pax_global_header00006660000000000000000000000064132526651660014526gustar00rootroot0000000000000052 comment=de72db00667eb1fcf0d3034bfc604b305443c7d8 mapper-0.8.1.1/000077500000000000000000000000001325266516600131575ustar00rootroot00000000000000mapper-0.8.1.1/.github/000077500000000000000000000000001325266516600145175ustar00rootroot00000000000000mapper-0.8.1.1/.github/ISSUE_TEMPLATE.md000066400000000000000000000002731325266516600172260ustar00rootroot00000000000000### Steps to reproduce 1. 2. 3. ### Actual behaviour Tell us what happens ### Expected behaviour Tell us what should happen instead ### Configuration Mapper Version: Operating System: mapper-0.8.1.1/3rd-party/000077500000000000000000000000001325266516600150045ustar00rootroot00000000000000mapper-0.8.1.1/3rd-party/clipper/000077500000000000000000000000001325266516600164425ustar00rootroot00000000000000mapper-0.8.1.1/3rd-party/clipper/CMakeLists.txt000066400000000000000000000077551325266516600212200ustar00rootroot00000000000000# # Copyright 2013-2015 Kai Pastor # # This file is part of OpenOrienteering. # # OpenOrienteering is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # OpenOrienteering is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with OpenOrienteering. If not, see . project(Clipper) cmake_minimum_required(VERSION 2.8.3) # Configuration options set(CLIPPER_VERSION_DEFAULT 6.4.2) if(CLIPPER_VERSION MATCHES "^5") unset(CLIPPER_VERSION CACHE) # source incompatible endif() set(CLIPPER_VERSION ${CLIPPER_VERSION_DEFAULT} CACHE STRING "Version number of the Clipper library") mark_as_advanced(CLIPPER_VERSION) message(STATUS "Configuring Clipper library ${CLIPPER_VERSION}") if (NOT ${CLIPPER_VERSION} STREQUAL ${CLIPPER_VERSION_DEFAULT}) message(WARNING "The Clipper library version is different from the current recommended version " "(${CLIPPER_VERSION} vs. ${CLIPPER_VERSION_DEFAULT}).") endif() # Optionally use externally provided clipper source dir (e.g. Debian packaging) set(CLIPPER_SOURCE_DIR_DEFAULT) set(CLIPPER_SOURCE_DIR "${CLIPPER_SOURCE_DIR_DEFAULT}" CACHE STRING "The Clipper library source directory to be used instead of a download") mark_as_advanced(CLIPPER_SOURCE_DIR) if(CLIPPER_SOURCE_DIR) # Expand variables like @PROJECT_SOURCE_DIR@ string(CONFIGURE "${CLIPPER_SOURCE_DIR}" EXPANDED_SOURCE_DIR @ONLY) set(CLIPPER_SOURCE SOURCE_DIR "${EXPANDED_SOURCE_DIR}" ) else() set(CLIPPER_MD5SUMS # Schema: VERSION:MD5 6.1.3a:4dcd043ce48de59714f07bd3ec7ac62b 6.4.2:100b4ec56c5308bac2d10f3966e35e11 ) foreach(line ${CLIPPER_MD5SUMS}) if(${line} MATCHES "^${CLIPPER_VERSION}:") string(REPLACE "${CLIPPER_VERSION}:" "" CLIPPER_MD5 ${line}) break() endif() endforeach() if(NOT CLIPPER_MD5) message(FATAL_ERROR "Unknown MD5 sum for Clipper library ${CLIPPER_VERSION}. " "Edit ${PROJECT_SOURCE_DIR}/CMakeLists.txt, " "or specify the correct CLIPPER_MD5 value at the command line.") endif() set(CLIPPER_SOURCE DOWNLOAD_DIR ${PROJECT_SOURCE_DIR}/download URL "http://sourceforge.net/projects/polyclipping/files/clipper_ver${CLIPPER_VERSION}.zip/download" URL_MD5 ${CLIPPER_MD5} ) endif() set(CLIPPER_LICENSE_FILE "${PROJECT_SOURCE_DIR}/License.txt") if(EXISTS "${CLIPPER_LICENSE_FILE}.${CLIPPER_VERSION}") set(CLIPPER_LICENSE_FILE "${CLIPPER_LICENSE_FILE}.${CLIPPER_VERSION}") endif() file(GLOB CLIPPER_LICENSE_FILES License.txt*) add_custom_target(clipper-licenses COMMENT "This target makes Qt Creator show all sources in the project tree." SOURCES ${CLIPPER_LICENSE_FILES} ) # External project definition include(ExternalProject) ExternalProject_Add( Clipper ${CLIPPER_SOURCE} CONFIGURE_COMMAND # Check that the license hasn't changed. ${CMAKE_COMMAND} -E compare_files /License.txt "${CLIPPER_LICENSE_FILE}" COMMAND # Force source file timestamp update. ${CMAKE_COMMAND} -E copy /cpp/clipper.cpp "${CMAKE_CURRENT_BINARY_DIR}/clipper.cpp" BUILD_COMMAND "" INSTALL_COMMAND "" ) ExternalProject_Get_Property(Clipper SOURCE_DIR) # The actual library build set(CMAKE_CXX_CLANG_TIDY "") set(CMAKE_CXX_INCLUDE_WHAT_YOU_USE "") if(CMAKE_COMPILER_IS_GNUCXX) string(REPLACE "-Wpedantic" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") endif() set(CLIPPER_SOURCES "${CMAKE_CURRENT_BINARY_DIR}/clipper.cpp") set_source_files_properties(${CLIPPER_SOURCES} PROPERTIES GENERATED TRUE) add_library(polyclipping STATIC ${CLIPPER_SOURCES}) target_include_directories(polyclipping PUBLIC ${SOURCE_DIR}/cpp) add_dependencies(polyclipping Clipper) mapper-0.8.1.1/3rd-party/clipper/License.txt000066400000000000000000000025651325266516600205750ustar00rootroot00000000000000Boost Software License - Version 1.0 - August 17th, 2003 http://www.boost.org/LICENSE_1_0.txt Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and accompanying documentation covered by this license (the "Software") to use, reproduce, display, distribute, execute, and transmit the Software, and to prepare derivative works of the Software, and to permit third-parties to whom the Software is furnished to do so, all subject to the following: The copyright notices in the Software and this entire statement, including the above license grant, this restriction and the following disclaimer, must be included in all copies of the Software, in whole or in part, and all derivative works of the Software, unless such copies or derivative works are solely in the form of machine-executable object code generated by a source language processor. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.mapper-0.8.1.1/3rd-party/clipper/License.txt.6.1.3a000066400000000000000000000030301325266516600213660ustar00rootroot00000000000000The Clipper Library (including Delphi, C++ & C# source code, other accompanying code, examples and documentation), hereafter called "the Software", has been released under the following license, terms and conditions: Boost Software License - Version 1.0 - August 17th, 2003 http://www.boost.org/LICENSE_1_0.txt Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the Software covered by this license to use, reproduce, display, distribute, execute, and transmit the Software, and to prepare derivative works of the Software, and to permit third-parties to whom the Software is furnished to do so, all subject to the following: The copyright notices in the Software and this entire statement, including the above license grant, this restriction and the following disclaimer, must be included in all copies of the Software, in whole or in part, and all derivative works of the Software, unless such copies or derivative works are solely in the form of machine-executable object code generated by a source language processor. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. mapper-0.8.1.1/3rd-party/clipper/copyright000066400000000000000000000137341325266516600204050ustar00rootroot00000000000000Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: Clipper Upstream-Contact: Angus Johnson Source: http://sourceforge.net/projects/polyclipping Files: * Copyright: Copyright 2010-2013 Angus Johnson License: boost The Clipper Library (including Delphi, C++ & C# source code, other accompanying code, examples and documentation), hereafter called "the Software", has been released under the following license, terms and conditions: . Boost Software License - Version 1.0 - August 17th, 2003 http://www.boost.org/LICENSE_1_0.txt . Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the Software covered by this license to use, reproduce, display, distribute, execute, and transmit the Software, and to prepare derivative works of the Software, and to permit third-parties to whom the Software is furnished to do so, all subject to the following: . The copyright notices in the Software and this entire statement, including the above license grant, this restriction and the following disclaimer, must be included in all copies of the Software, in whole or in part, and all derivative works of the Software, unless such copies or derivative works are solely in the form of machine-executable object code generated by a source language processor. . THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Files: Documentation/Scripts/SyntaxHighlighter/* Copyright: Copyright (C) 2004-2010 Alex Gorbatchev. License: MIT or GPL Files: Documentation/Scripts/SyntaxHighlighter/scripts/shBrushCpp.js Copyright: Copyright (C) 2004-2010 Alex Gorbatchev. Copyright 2006 Shin, YoungJin License: MIT or GPL Files: Documentation/Scripts/SyntaxHighlighter/tests/js/jquery-1.4.2.js Copyright: Copyright 2010, John Resig Copyright 2009, 2010 The Dojo Foundation License: MIT Files: Documentation/Scripts/SyntaxHighlighter/tests/js/qunit.js Copyright: Copyright (c) 2009 John Resig, Jörn Zaefferer Copyright (c) 2008 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com License: MIT or GPL, and BSD Files: debian/* Copyright: Copyright 2003-2011 Bas Wijnen License: GPL-3+ This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. . The latest version of the GPL can be found in /usr/share/common-licenses/GPL. License: MIT Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: . The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. . THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. License: GPL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 1 of the License, or (at your option) any later version. . This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. . You should have received a copy of the GNU General Public License along with this program. If not, see . . On Debian systems, the latest version of the GPL can be found in /usr/share/common-licenses/GPL License: BSD All rights reserved. . Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: . Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. . THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. mapper-0.8.1.1/3rd-party/clipper/download/000077500000000000000000000000001325266516600202515ustar00rootroot00000000000000mapper-0.8.1.1/3rd-party/clipper/download/README.txt000066400000000000000000000000741325266516600217500ustar00rootroot00000000000000This directory contains downloaded Clipper source archives. mapper-0.8.1.1/3rd-party/qbezier/000077500000000000000000000000001325266516600164455ustar00rootroot00000000000000mapper-0.8.1.1/3rd-party/qbezier/CMakeLists.txt000066400000000000000000000025731325266516600212140ustar00rootroot00000000000000# # Copyright 2014 Kai Pastor # # This file is part of OpenOrienteering. # # OpenOrienteering is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # OpenOrienteering is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with OpenOrienteering. If not, see . # qbezier_p.h and the private headers it depends on may be missing # in some installations of Qt. # # The implementation of the functions declared in qbezier_p.h are available in # the QtGui library. # # The headers need to be reviewed when switching to newer versions of Qt. # (Last review: Qt 5.3.0 beta.) set(QBEZIER_SRCS src/private/qbezier_p.h # from qtbase/src/gui/painting/ src/private/qdatabuffer_p.h # from qtbase/src/gui/painting/ src/private/qmath_p.h # from qtbase/src/gui/painting/ src/private/qnumeric_p.h # from qtbase/src/corelib/global/ ) add_custom_target(QBezier SOURCES ${QBEZIER_SRCS} ) mapper-0.8.1.1/3rd-party/qbezier/LICENSE.GPL000066400000000000000000001045131325266516600200770ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . mapper-0.8.1.1/3rd-party/qbezier/README.txt000066400000000000000000000005011325266516600201370ustar00rootroot00000000000000The files in src/private/ are available under the GPL v3 (cf. LICENSE.GPL) and were taken unmodified from the follwing files in qt-everywhere-opensource-src-5.2.0.tar.gz: qtbase/src/gui/painting/qbezier_p.h qtbase/src/gui/painting/qdatabuffer_p.h qtbase/src/gui/painting/qmath_p.h qtbase/src/corelib/global/qnumeric_p.h mapper-0.8.1.1/3rd-party/qbezier/src/000077500000000000000000000000001325266516600172345ustar00rootroot00000000000000mapper-0.8.1.1/3rd-party/qbezier/src/mapper_qbezier.cpp000066400000000000000000000001631325266516600227450ustar00rootroot00000000000000// This file is part of OpenOrienteering. #include "private/qbezier_p.h" #define MAPPER_QBEZIER_VERSION 0x050200 mapper-0.8.1.1/3rd-party/qbezier/src/private/000077500000000000000000000000001325266516600207065ustar00rootroot00000000000000mapper-0.8.1.1/3rd-party/qbezier/src/private/qbezier_p.h000066400000000000000000000201771325266516600230460ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtGui module 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 QBEZIER_P_H #define QBEZIER_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists for the convenience // of other Qt classes. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include "QtCore/qpoint.h" #include "QtCore/qline.h" #include "QtCore/qrect.h" #include "QtCore/qvector.h" #include "QtCore/qlist.h" #include "QtCore/qpair.h" #include "QtGui/qtransform.h" #include QT_BEGIN_NAMESPACE class QPolygonF; class Q_GUI_EXPORT QBezier { public: static QBezier fromPoints(const QPointF &p1, const QPointF &p2, const QPointF &p3, const QPointF &p4); static void coefficients(qreal t, qreal &a, qreal &b, qreal &c, qreal &d); inline QPointF pointAt(qreal t) const; inline QPointF normalVector(qreal t) const; inline QPointF derivedAt(qreal t) const; inline QPointF secondDerivedAt(qreal t) const; QPolygonF toPolygon(qreal bezier_flattening_threshold = 0.5) const; void addToPolygon(QPolygonF *p, qreal bezier_flattening_threshold = 0.5) const; void addToPolygon(QDataBuffer &polygon, qreal bezier_flattening_threshold) const; QRectF bounds() const; qreal length(qreal error = 0.01) const; void addIfClose(qreal *length, qreal error) const; qreal tAtLength(qreal len) const; int stationaryYPoints(qreal &t0, qreal &t1) const; qreal tForY(qreal t0, qreal t1, qreal y) const; QPointF pt1() const { return QPointF(x1, y1); } QPointF pt2() const { return QPointF(x2, y2); } QPointF pt3() const { return QPointF(x3, y3); } QPointF pt4() const { return QPointF(x4, y4); } QBezier mapBy(const QTransform &transform) const; inline QPointF midPoint() const; inline QLineF midTangent() const; inline QLineF startTangent() const; inline QLineF endTangent() const; inline void parameterSplitLeft(qreal t, QBezier *left); inline void split(QBezier *firstHalf, QBezier *secondHalf) const; int shifted(QBezier *curveSegments, int maxSegmets, qreal offset, float threshold) const; QBezier bezierOnInterval(qreal t0, qreal t1) const; QBezier getSubRange(qreal t0, qreal t1) const; qreal x1, y1, x2, y2, x3, y3, x4, y4; }; inline QPointF QBezier::midPoint() const { return QPointF((x1 + x4 + 3*(x2 + x3))/8., (y1 + y4 + 3*(y2 + y3))/8.); } inline QLineF QBezier::midTangent() const { QPointF mid = midPoint(); QLineF dir(QLineF(x1, y1, x2, y2).pointAt(0.5), QLineF(x3, y3, x4, y4).pointAt(0.5)); return QLineF(mid.x() - dir.dx(), mid.y() - dir.dy(), mid.x() + dir.dx(), mid.y() + dir.dy()); } inline QLineF QBezier::startTangent() const { QLineF tangent(pt1(), pt2()); if (tangent.isNull()) tangent = QLineF(pt1(), pt3()); if (tangent.isNull()) tangent = QLineF(pt1(), pt4()); return tangent; } inline QLineF QBezier::endTangent() const { QLineF tangent(pt4(), pt3()); if (tangent.isNull()) tangent = QLineF(pt4(), pt2()); if (tangent.isNull()) tangent = QLineF(pt4(), pt1()); return tangent; } inline void QBezier::coefficients(qreal t, qreal &a, qreal &b, qreal &c, qreal &d) { qreal m_t = 1. - t; b = m_t * m_t; c = t * t; d = c * t; a = b * m_t; b *= 3. * t; c *= 3. * m_t; } inline QPointF QBezier::pointAt(qreal t) const { // numerically more stable: qreal x, y; qreal m_t = 1. - t; { qreal a = x1*m_t + x2*t; qreal b = x2*m_t + x3*t; qreal c = x3*m_t + x4*t; a = a*m_t + b*t; b = b*m_t + c*t; x = a*m_t + b*t; } { qreal a = y1*m_t + y2*t; qreal b = y2*m_t + y3*t; qreal c = y3*m_t + y4*t; a = a*m_t + b*t; b = b*m_t + c*t; y = a*m_t + b*t; } return QPointF(x, y); } inline QPointF QBezier::normalVector(qreal t) const { qreal m_t = 1. - t; qreal a = m_t * m_t; qreal b = t * m_t; qreal c = t * t; return QPointF((y2-y1) * a + (y3-y2) * b + (y4-y3) * c, -(x2-x1) * a - (x3-x2) * b - (x4-x3) * c); } inline QPointF QBezier::derivedAt(qreal t) const { // p'(t) = 3 * (-(1-2t+t^2) * p0 + (1 - 4 * t + 3 * t^2) * p1 + (2 * t - 3 * t^2) * p2 + t^2 * p3) qreal m_t = 1. - t; qreal d = t * t; qreal a = -m_t * m_t; qreal b = 1 - 4 * t + 3 * d; qreal c = 2 * t - 3 * d; return 3 * QPointF(a * x1 + b * x2 + c * x3 + d * x4, a * y1 + b * y2 + c * y3 + d * y4); } inline QPointF QBezier::secondDerivedAt(qreal t) const { qreal a = 2. - 2. * t; qreal b = -4 + 6 * t; qreal c = 2 - 6 * t; qreal d = 2 * t; return 3 * QPointF(a * x1 + b * x2 + c * x3 + d * x4, a * y1 + b * y2 + c * y3 + d * y4); } inline void QBezier::split(QBezier *firstHalf, QBezier *secondHalf) const { Q_ASSERT(firstHalf); Q_ASSERT(secondHalf); qreal c = (x2 + x3)*.5; firstHalf->x2 = (x1 + x2)*.5; secondHalf->x3 = (x3 + x4)*.5; firstHalf->x1 = x1; secondHalf->x4 = x4; firstHalf->x3 = (firstHalf->x2 + c)*.5; secondHalf->x2 = (secondHalf->x3 + c)*.5; firstHalf->x4 = secondHalf->x1 = (firstHalf->x3 + secondHalf->x2)*.5; c = (y2 + y3)/2; firstHalf->y2 = (y1 + y2)*.5; secondHalf->y3 = (y3 + y4)*.5; firstHalf->y1 = y1; secondHalf->y4 = y4; firstHalf->y3 = (firstHalf->y2 + c)*.5; secondHalf->y2 = (secondHalf->y3 + c)*.5; firstHalf->y4 = secondHalf->y1 = (firstHalf->y3 + secondHalf->y2)*.5; } inline void QBezier::parameterSplitLeft(qreal t, QBezier *left) { left->x1 = x1; left->y1 = y1; left->x2 = x1 + t * ( x2 - x1 ); left->y2 = y1 + t * ( y2 - y1 ); left->x3 = x2 + t * ( x3 - x2 ); // temporary holding spot left->y3 = y2 + t * ( y3 - y2 ); // temporary holding spot x3 = x3 + t * ( x4 - x3 ); y3 = y3 + t * ( y4 - y3 ); x2 = left->x3 + t * ( x3 - left->x3); y2 = left->y3 + t * ( y3 - left->y3); left->x3 = left->x2 + t * ( left->x3 - left->x2 ); left->y3 = left->y2 + t * ( left->y3 - left->y2 ); left->x4 = x1 = left->x3 + t * (x2 - left->x3); left->y4 = y1 = left->y3 + t * (y2 - left->y3); } QT_END_NAMESPACE #endif // QBEZIER_P_H mapper-0.8.1.1/3rd-party/qbezier/src/private/qdatabuffer_p.h000066400000000000000000000105551325266516600236700ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtGui module 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 QDATABUFFER_P_H #define QDATABUFFER_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists for the convenience // of other Qt classes. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include "QtCore/qbytearray.h" #include QT_BEGIN_NAMESPACE template class QDataBuffer { public: QDataBuffer(int res) { capacity = res; if (res) buffer = (Type*) malloc(capacity * sizeof(Type)); else buffer = 0; siz = 0; } ~QDataBuffer() { if (buffer) free(buffer); } inline void reset() { siz = 0; } inline bool isEmpty() const { return siz==0; } inline int size() const { return siz; } inline Type *data() const { return buffer; } inline Type &at(int i) { Q_ASSERT(i >= 0 && i < siz); return buffer[i]; } inline const Type &at(int i) const { Q_ASSERT(i >= 0 && i < siz); return buffer[i]; } inline Type &last() { Q_ASSERT(!isEmpty()); return buffer[siz-1]; } inline const Type &last() const { Q_ASSERT(!isEmpty()); return buffer[siz-1]; } inline Type &first() { Q_ASSERT(!isEmpty()); return buffer[0]; } inline const Type &first() const { Q_ASSERT(!isEmpty()); return buffer[0]; } inline void add(const Type &t) { reserve(siz + 1); buffer[siz] = t; ++siz; } inline void pop_back() { Q_ASSERT(siz > 0); --siz; } inline void resize(int size) { reserve(size); siz = size; } inline void reserve(int size) { if (size > capacity) { if (capacity == 0) capacity = 1; while (capacity < size) capacity *= 2; buffer = (Type*) realloc(buffer, capacity * sizeof(Type)); } } inline void shrink(int size) { capacity = size; if (size) buffer = (Type*) realloc(buffer, capacity * sizeof(Type)); else { free(buffer); buffer = 0; } } inline void swap(QDataBuffer &other) { qSwap(capacity, other.capacity); qSwap(siz, other.siz); qSwap(buffer, other.buffer); } inline QDataBuffer &operator<<(const Type &t) { add(t); return *this; } private: int capacity; int siz; Type *buffer; }; QT_END_NAMESPACE #endif // QDATABUFFER_P_H mapper-0.8.1.1/3rd-party/qbezier/src/private/qmath_p.h000066400000000000000000000052001325266516600225050ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtGui module 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 QMATH_P_H #define QMATH_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include QT_BEGIN_NAMESPACE static const qreal Q_PI = qreal(3.14159265358979323846); // pi static const qreal Q_2PI = qreal(6.28318530717958647693); // 2*pi static const qreal Q_PI2 = qreal(1.57079632679489661923); // pi/2 static const qreal Q_MM_PER_INCH = 25.4; inline int qIntSqrtInt(int v) { return static_cast(qSqrt(static_cast(v))); } QT_END_NAMESPACE #endif // QMATH_P_H mapper-0.8.1.1/3rd-party/qbezier/src/private/qnumeric_p.h000066400000000000000000000150151325266516600232230ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtCore module 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 QNUMERIC_P_H #define QNUMERIC_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include "QtCore/qglobal.h" QT_BEGIN_NAMESPACE #if !defined(Q_CC_MIPS) static const union { unsigned char c[8]; double d; } qt_be_inf_bytes = { { 0x7f, 0xf0, 0, 0, 0, 0, 0, 0 } }; static const union { unsigned char c[8]; double d; } qt_le_inf_bytes = { { 0, 0, 0, 0, 0, 0, 0xf0, 0x7f } }; static inline double qt_inf() { return (QSysInfo::ByteOrder == QSysInfo::BigEndian ? qt_be_inf_bytes.d : qt_le_inf_bytes.d); } // Signaling NAN static const union { unsigned char c[8]; double d; } qt_be_snan_bytes = { { 0x7f, 0xf8, 0, 0, 0, 0, 0, 0 } }; static const union { unsigned char c[8]; double d; } qt_le_snan_bytes = { { 0, 0, 0, 0, 0, 0, 0xf8, 0x7f } }; static inline double qt_snan() { return (QSysInfo::ByteOrder == QSysInfo::BigEndian ? qt_be_snan_bytes.d : qt_le_snan_bytes.d); } // Quiet NAN static const union { unsigned char c[8]; double d; } qt_be_qnan_bytes = { { 0xff, 0xf8, 0, 0, 0, 0, 0, 0 } }; static const union { unsigned char c[8]; double d; } qt_le_qnan_bytes = { { 0, 0, 0, 0, 0, 0, 0xf8, 0xff } }; static inline double qt_qnan() { return (QSysInfo::ByteOrder == QSysInfo::BigEndian ? qt_be_qnan_bytes.d : qt_le_qnan_bytes.d); } #else // Q_CC_MIPS static const unsigned char qt_be_inf_bytes[] = { 0x7f, 0xf0, 0, 0, 0, 0, 0, 0 }; static const unsigned char qt_le_inf_bytes[] = { 0, 0, 0, 0, 0, 0, 0xf0, 0x7f }; static inline double qt_inf() { const unsigned char *bytes; bytes = (QSysInfo::ByteOrder == QSysInfo::BigEndian ? qt_be_inf_bytes : qt_le_inf_bytes); union { unsigned char c[8]; double d; } returnValue; memcpy(returnValue.c, bytes, sizeof(returnValue.c)); return returnValue.d; } // Signaling NAN static const unsigned char qt_be_snan_bytes[] = { 0x7f, 0xf8, 0, 0, 0, 0, 0, 0 }; static const unsigned char qt_le_snan_bytes[] = { 0, 0, 0, 0, 0, 0, 0xf8, 0x7f }; static inline double qt_snan() { const unsigned char *bytes; bytes = (QSysInfo::ByteOrder == QSysInfo::BigEndian ? qt_be_snan_bytes : qt_le_snan_bytes); union { unsigned char c[8]; double d; } returnValue; memcpy(returnValue.c, bytes, sizeof(returnValue.c)); return returnValue.d; } // Quiet NAN static const unsigned char qt_be_qnan_bytes[] = { 0xff, 0xf8, 0, 0, 0, 0, 0, 0 }; static const unsigned char qt_le_qnan_bytes[] = { 0, 0, 0, 0, 0, 0, 0xf8, 0xff }; static inline double qt_qnan() { const unsigned char *bytes; bytes = (QSysInfo::ByteOrder == QSysInfo::BigEndian ? qt_be_qnan_bytes : qt_le_qnan_bytes); union { unsigned char c[8]; double d; } returnValue; memcpy(returnValue.c, bytes, sizeof(returnValue.c)); return returnValue.d; } #endif // Q_CC_MIPS static inline bool qt_is_inf(double d) { uchar *ch = (uchar *)&d; if (QSysInfo::ByteOrder == QSysInfo::BigEndian) { return (ch[0] & 0x7f) == 0x7f && ch[1] == 0xf0; } else { return (ch[7] & 0x7f) == 0x7f && ch[6] == 0xf0; } } static inline bool qt_is_nan(double d) { uchar *ch = (uchar *)&d; if (QSysInfo::ByteOrder == QSysInfo::BigEndian) { return (ch[0] & 0x7f) == 0x7f && ch[1] > 0xf0; } else { return (ch[7] & 0x7f) == 0x7f && ch[6] > 0xf0; } } static inline bool qt_is_finite(double d) { uchar *ch = (uchar *)&d; if (QSysInfo::ByteOrder == QSysInfo::BigEndian) { return (ch[0] & 0x7f) != 0x7f || (ch[1] & 0xf0) != 0xf0; } else { return (ch[7] & 0x7f) != 0x7f || (ch[6] & 0xf0) != 0xf0; } } static inline bool qt_is_inf(float d) { uchar *ch = (uchar *)&d; if (QSysInfo::ByteOrder == QSysInfo::BigEndian) { return (ch[0] & 0x7f) == 0x7f && ch[1] == 0x80; } else { return (ch[3] & 0x7f) == 0x7f && ch[2] == 0x80; } } static inline bool qt_is_nan(float d) { uchar *ch = (uchar *)&d; if (QSysInfo::ByteOrder == QSysInfo::BigEndian) { return (ch[0] & 0x7f) == 0x7f && ch[1] > 0x80; } else { return (ch[3] & 0x7f) == 0x7f && ch[2] > 0x80; } } static inline bool qt_is_finite(float d) { uchar *ch = (uchar *)&d; if (QSysInfo::ByteOrder == QSysInfo::BigEndian) { return (ch[0] & 0x7f) != 0x7f || (ch[1] & 0x80) != 0x80; } else { return (ch[3] & 0x7f) != 0x7f || (ch[2] & 0x80) != 0x80; } } QT_END_NAMESPACE #endif // QNUMERIC_P_H mapper-0.8.1.1/3rd-party/qtsingleapplication/000077500000000000000000000000001325266516600210565ustar00rootroot00000000000000mapper-0.8.1.1/3rd-party/qtsingleapplication/CMakeLists.txt000066400000000000000000000024071325266516600236210ustar00rootroot00000000000000# # Copyright 2012-2015 Kai Pastor # # This file is part of OpenOrienteering. # # OpenOrienteering is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # OpenOrienteering is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with OpenOrienteering. If not, see . find_package(Qt5Widgets REQUIRED) find_package(Qt5Network REQUIRED) set(CMAKE_AUTOMOC ON) set(CMAKE_CXX_CLANG_TIDY "") set(CMAKE_CXX_INCLUDE_WHAT_YOU_USE "") set(QSINGLEAPP_SRCS src/qtlocalpeer.cpp src/qtsingleapplication.cpp src/qtsinglecoreapplication.cpp ) add_library(QtSingleApplication STATIC ${QSINGLEAPP_SRCS} ) target_include_directories(QtSingleApplication INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/src" ) target_link_libraries(QtSingleApplication PUBLIC Qt5::Widgets PUBLIC Qt5::Network ) mapper-0.8.1.1/3rd-party/qtsingleapplication/copyright000066400000000000000000000036351325266516600230200ustar00rootroot00000000000000Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: Qt Solutions Component: Single Application Source: http://code.qt.io/cgit/qt-solutions/qt-solutions.git/tree/qtsingleapplication Files: src/* Copyright: 2013 Digia Plc and/or its subsidiary(-ies). License: BSD-3-clause License: BSD-3-clause This file is part of a Qt Solutions component. . You may use this file under the terms of the BSD license as follows: . "Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. . THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." mapper-0.8.1.1/3rd-party/qtsingleapplication/src/000077500000000000000000000000001325266516600216455ustar00rootroot00000000000000mapper-0.8.1.1/3rd-party/qtsingleapplication/src/QtLockedFile000066400000000000000000000000321325266516600240710ustar00rootroot00000000000000#include "qtlockedfile.h" mapper-0.8.1.1/3rd-party/qtsingleapplication/src/QtSingleApplication000066400000000000000000000000411325266516600254750ustar00rootroot00000000000000#include "qtsingleapplication.h" mapper-0.8.1.1/3rd-party/qtsingleapplication/src/qtlocalpeer.cpp000066400000000000000000000151111325266516600246630ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the Qt Solutions component. ** ** $QT_BEGIN_LICENSE:BSD$ ** You may use this file under the terms of the BSD license as follows: ** ** "Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are ** met: ** * Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** * Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in ** the documentation and/or other materials provided with the ** distribution. ** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names ** of its contributors may be used to endorse or promote products derived ** from this software without specific prior written permission. ** ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qtlocalpeer.h" #include #include #include #if defined(Q_OS_WIN) #include #include typedef BOOL(WINAPI*PProcessIdToSessionId)(DWORD,DWORD*); static PProcessIdToSessionId pProcessIdToSessionId = 0; #endif #if defined(Q_OS_UNIX) #include #include #include #endif namespace QtLP_Private { #include "qtlockedfile.cpp" #if defined(Q_OS_WIN) #include "qtlockedfile_win.cpp" #else #include "qtlockedfile_unix.cpp" #endif } const char* QtLocalPeer::ack = "ack"; QtLocalPeer::QtLocalPeer(QObject* parent, const QString &appId) : QObject(parent), id(appId) { QString prefix = id; if (id.isEmpty()) { id = QCoreApplication::applicationFilePath(); #if defined(Q_OS_WIN) id = id.toLower(); #endif prefix = id.section(QLatin1Char('/'), -1); } prefix.remove(QRegExp("[^a-zA-Z]")); prefix.truncate(6); QByteArray idc = id.toUtf8(); quint16 idNum = qChecksum(idc.constData(), idc.size()); socketName = QLatin1String("qtsingleapp-") + prefix + QLatin1Char('-') + QString::number(idNum, 16); #if defined(Q_OS_WIN) if (!pProcessIdToSessionId) { QLibrary lib("kernel32"); pProcessIdToSessionId = (PProcessIdToSessionId)lib.resolve("ProcessIdToSessionId"); } if (pProcessIdToSessionId) { DWORD sessionId = 0; pProcessIdToSessionId(GetCurrentProcessId(), &sessionId); socketName += QLatin1Char('-') + QString::number(sessionId, 16); } #else socketName += QLatin1Char('-') + QString::number(::getuid(), 16); #endif server = new QLocalServer(this); QString lockName = QDir(QDir::tempPath()).absolutePath() + QLatin1Char('/') + socketName + QLatin1String("-lockfile"); lockFile.setFileName(lockName); lockFile.open(QIODevice::ReadWrite); } bool QtLocalPeer::isClient() { if (lockFile.isLocked()) return false; if (!lockFile.lock(QtLP_Private::QtLockedFile::WriteLock, false)) return true; bool res = server->listen(socketName); #if defined(Q_OS_UNIX) && (QT_VERSION >= QT_VERSION_CHECK(4,5,0)) // ### Workaround if (!res && server->serverError() == QAbstractSocket::AddressInUseError) { QFile::remove(QDir::cleanPath(QDir::tempPath())+QLatin1Char('/')+socketName); res = server->listen(socketName); } #endif if (!res) qWarning("QtSingleCoreApplication: listen on local socket failed, %s", qPrintable(server->errorString())); QObject::connect(server, &QLocalServer::newConnection, this, &QtLocalPeer::receiveConnection); return false; } bool QtLocalPeer::sendMessage(const QString &message, int timeout) { if (!isClient()) return false; QLocalSocket socket; bool connOk = false; for(int i = 0; i < 2; i++) { // Try twice, in case the other instance is just starting up socket.connectToServer(socketName); connOk = socket.waitForConnected(timeout/2); if (connOk || i) break; int ms = 250; #if defined(Q_OS_WIN) Sleep(DWORD(ms)); #else struct timespec ts = { ms / 1000, (ms % 1000) * 1000 * 1000 }; nanosleep(&ts, NULL); #endif } if (!connOk) return false; QByteArray uMsg(message.toUtf8()); QDataStream ds(&socket); ds.writeBytes(uMsg.constData(), uMsg.size()); bool res = socket.waitForBytesWritten(timeout); if (res) { res &= socket.waitForReadyRead(timeout); // wait for ack if (res) res &= (socket.read(qstrlen(ack)) == ack); } return res; } void QtLocalPeer::receiveConnection() { QLocalSocket* socket = server->nextPendingConnection(); if (!socket) return; while (socket->bytesAvailable() < (int)sizeof(quint32)) socket->waitForReadyRead(); QDataStream ds(socket); QByteArray uMsg; quint32 remaining; ds >> remaining; uMsg.resize(remaining); int got = 0; char* uMsgBuf = uMsg.data(); do { got = ds.readRawData(uMsgBuf, remaining); remaining -= got; uMsgBuf += got; } while (remaining && got >= 0 && socket->waitForReadyRead(2000)); if (got < 0) { qWarning("QtLocalPeer: Message reception failed %s", socket->errorString().toLatin1().constData()); delete socket; return; } QString message(QString::fromUtf8(uMsg)); socket->write(ack, qstrlen(ack)); socket->waitForBytesWritten(1000); socket->waitForDisconnected(1000); // make sure client reads ack delete socket; emit messageReceived(message); //### (might take a long time to return) } mapper-0.8.1.1/3rd-party/qtsingleapplication/src/qtlocalpeer.h000066400000000000000000000052051325266516600243330ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the Qt Solutions component. ** ** $QT_BEGIN_LICENSE:BSD$ ** You may use this file under the terms of the BSD license as follows: ** ** "Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are ** met: ** * Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** * Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in ** the documentation and/or other materials provided with the ** distribution. ** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names ** of its contributors may be used to endorse or promote products derived ** from this software without specific prior written permission. ** ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QTLOCALPEER_H #define QTLOCALPEER_H #include #include #include #include "qtlockedfile.h" class QtLocalPeer : public QObject { Q_OBJECT public: QtLocalPeer(QObject *parent = 0, const QString &appId = QString()); bool isClient(); bool sendMessage(const QString &message, int timeout); QString applicationId() const { return id; } Q_SIGNALS: void messageReceived(const QString &message); protected Q_SLOTS: void receiveConnection(); protected: QString id; QString socketName; QLocalServer* server; QtLP_Private::QtLockedFile lockFile; private: static const char* ack; }; #endif // QTLOCALPEER_H mapper-0.8.1.1/3rd-party/qtsingleapplication/src/qtlockedfile.cpp000066400000000000000000000137421325266516600250260ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the Qt Solutions component. ** ** $QT_BEGIN_LICENSE:BSD$ ** You may use this file under the terms of the BSD license as follows: ** ** "Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are ** met: ** * Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** * Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in ** the documentation and/or other materials provided with the ** distribution. ** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names ** of its contributors may be used to endorse or promote products derived ** from this software without specific prior written permission. ** ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qtlockedfile.h" /*! \class QtLockedFile \brief The QtLockedFile class extends QFile with advisory locking functions. A file may be locked in read or write mode. Multiple instances of \e QtLockedFile, created in multiple processes running on the same machine, may have a file locked in read mode. Exactly one instance may have it locked in write mode. A read and a write lock cannot exist simultaneously on the same file. The file locks are advisory. This means that nothing prevents another process from manipulating a locked file using QFile or file system functions offered by the OS. Serialization is only guaranteed if all processes that access the file use QLockedFile. Also, while holding a lock on a file, a process must not open the same file again (through any API), or locks can be unexpectedly lost. The lock provided by an instance of \e QtLockedFile is released whenever the program terminates. This is true even when the program crashes and no destructors are called. */ /*! \enum QtLockedFile::LockMode This enum describes the available lock modes. \value ReadLock A read lock. \value WriteLock A write lock. \value NoLock Neither a read lock nor a write lock. */ /*! Constructs an unlocked \e QtLockedFile object. This constructor behaves in the same way as \e QFile::QFile(). \sa QFile::QFile() */ QtLockedFile::QtLockedFile() : QFile() { #ifdef Q_OS_WIN wmutex = 0; rmutex = 0; #endif m_lock_mode = NoLock; } /*! Constructs an unlocked QtLockedFile object with file \a name. This constructor behaves in the same way as \e QFile::QFile(const QString&). \sa QFile::QFile() */ QtLockedFile::QtLockedFile(const QString &name) : QFile(name) { #ifdef Q_OS_WIN wmutex = 0; rmutex = 0; #endif m_lock_mode = NoLock; } /*! Opens the file in OpenMode \a mode. This is identical to QFile::open(), with the one exception that the Truncate mode flag is disallowed. Truncation would conflict with the advisory file locking, since the file would be modified before the write lock is obtained. If truncation is required, use resize(0) after obtaining the write lock. Returns true if successful; otherwise false. \sa QFile::open(), QFile::resize() */ bool QtLockedFile::open(OpenMode mode) { if (mode & QIODevice::Truncate) { qWarning("QtLockedFile::open(): Truncate mode not allowed."); return false; } return QFile::open(mode); } /*! Returns \e true if this object has a in read or write lock; otherwise returns \e false. \sa lockMode() */ bool QtLockedFile::isLocked() const { return m_lock_mode != NoLock; } /*! Returns the type of lock currently held by this object, or \e QtLockedFile::NoLock. \sa isLocked() */ QtLockedFile::LockMode QtLockedFile::lockMode() const { return m_lock_mode; } /*! \fn bool QtLockedFile::lock(LockMode mode, bool block = true) Obtains a lock of type \a mode. The file must be opened before it can be locked. If \a block is true, this function will block until the lock is aquired. If \a block is false, this function returns \e false immediately if the lock cannot be aquired. If this object already has a lock of type \a mode, this function returns \e true immediately. If this object has a lock of a different type than \a mode, the lock is first released and then a new lock is obtained. This function returns \e true if, after it executes, the file is locked by this object, and \e false otherwise. \sa unlock(), isLocked(), lockMode() */ /*! \fn bool QtLockedFile::unlock() Releases a lock. If the object has no lock, this function returns immediately. This function returns \e true if, after it executes, the file is not locked by this object, and \e false otherwise. \sa lock(), isLocked(), lockMode() */ /*! \fn QtLockedFile::~QtLockedFile() Destroys the \e QtLockedFile object. If any locks were held, they are released. */ mapper-0.8.1.1/3rd-party/qtsingleapplication/src/qtlockedfile.h000066400000000000000000000063311325266516600244670ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the Qt Solutions component. ** ** $QT_BEGIN_LICENSE:BSD$ ** You may use this file under the terms of the BSD license as follows: ** ** "Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are ** met: ** * Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** * Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in ** the documentation and/or other materials provided with the ** distribution. ** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names ** of its contributors may be used to endorse or promote products derived ** from this software without specific prior written permission. ** ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QTLOCKEDFILE_H #define QTLOCKEDFILE_H #include #ifdef Q_OS_WIN #include #endif #if defined(Q_OS_WIN) # if !defined(QT_QTLOCKEDFILE_EXPORT) && !defined(QT_QTLOCKEDFILE_IMPORT) # define QT_QTLOCKEDFILE_EXPORT # elif defined(QT_QTLOCKEDFILE_IMPORT) # if defined(QT_QTLOCKEDFILE_EXPORT) # undef QT_QTLOCKEDFILE_EXPORT # endif # define QT_QTLOCKEDFILE_EXPORT __declspec(dllimport) # elif defined(QT_QTLOCKEDFILE_EXPORT) # undef QT_QTLOCKEDFILE_EXPORT # define QT_QTLOCKEDFILE_EXPORT __declspec(dllexport) # endif #else # define QT_QTLOCKEDFILE_EXPORT #endif namespace QtLP_Private { class QT_QTLOCKEDFILE_EXPORT QtLockedFile : public QFile { public: enum LockMode { NoLock = 0, ReadLock, WriteLock }; QtLockedFile(); QtLockedFile(const QString &name); ~QtLockedFile() override; bool open(OpenMode mode) override; bool lock(LockMode mode, bool block = true); bool unlock(); bool isLocked() const; LockMode lockMode() const; private: #ifdef Q_OS_WIN Qt::HANDLE wmutex; Qt::HANDLE rmutex; QVector rmutexes; QString mutexname; Qt::HANDLE getMutexHandle(int idx, bool doCreate); bool waitMutex(Qt::HANDLE mutex, bool doBlock); #endif LockMode m_lock_mode; }; } #endif mapper-0.8.1.1/3rd-party/qtsingleapplication/src/qtlockedfile_unix.cpp000066400000000000000000000066141325266516600260710ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the Qt Solutions component. ** ** $QT_BEGIN_LICENSE:BSD$ ** You may use this file under the terms of the BSD license as follows: ** ** "Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are ** met: ** * Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** * Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in ** the documentation and/or other materials provided with the ** distribution. ** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names ** of its contributors may be used to endorse or promote products derived ** from this software without specific prior written permission. ** ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include #include #include #include "qtlockedfile.h" bool QtLockedFile::lock(LockMode mode, bool block) { if (!isOpen()) { qWarning("QtLockedFile::lock(): file is not opened"); return false; } if (mode == NoLock) return unlock(); if (mode == m_lock_mode) return true; if (m_lock_mode != NoLock) unlock(); struct flock fl; fl.l_whence = SEEK_SET; fl.l_start = 0; fl.l_len = 0; fl.l_type = (mode == ReadLock) ? F_RDLCK : F_WRLCK; int cmd = block ? F_SETLKW : F_SETLK; int ret = fcntl(handle(), cmd, &fl); if (ret == -1) { if (errno != EINTR && errno != EAGAIN) qWarning("QtLockedFile::lock(): fcntl: %s", strerror(errno)); return false; } m_lock_mode = mode; return true; } bool QtLockedFile::unlock() { if (!isOpen()) { qWarning("QtLockedFile::unlock(): file is not opened"); return false; } if (!isLocked()) return true; struct flock fl; fl.l_whence = SEEK_SET; fl.l_start = 0; fl.l_len = 0; fl.l_type = F_UNLCK; int ret = fcntl(handle(), F_SETLKW, &fl); if (ret == -1) { qWarning("QtLockedFile::lock(): fcntl: %s", strerror(errno)); return false; } m_lock_mode = NoLock; return true; } QtLockedFile::~QtLockedFile() { if (isOpen()) unlock(); } mapper-0.8.1.1/3rd-party/qtsingleapplication/src/qtlockedfile_win.cpp000066400000000000000000000146611325266516600257040ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the Qt Solutions component. ** ** $QT_BEGIN_LICENSE:BSD$ ** You may use this file under the terms of the BSD license as follows: ** ** "Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are ** met: ** * Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** * Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in ** the documentation and/or other materials provided with the ** distribution. ** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names ** of its contributors may be used to endorse or promote products derived ** from this software without specific prior written permission. ** ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qtlockedfile.h" #include #include #define MUTEX_PREFIX "QtLockedFile mutex " // Maximum number of concurrent read locks. Must not be greater than MAXIMUM_WAIT_OBJECTS #define MAX_READERS MAXIMUM_WAIT_OBJECTS #if QT_VERSION >= 0x050000 #define QT_WA(unicode, ansi) unicode #endif Qt::HANDLE QtLockedFile::getMutexHandle(int idx, bool doCreate) { if (mutexname.isEmpty()) { QFileInfo fi(*this); mutexname = QString::fromLatin1(MUTEX_PREFIX) + fi.absoluteFilePath().toLower(); } QString mname(mutexname); if (idx >= 0) mname += QString::number(idx); Qt::HANDLE mutex; if (doCreate) { QT_WA( { mutex = CreateMutexW(NULL, FALSE, (TCHAR*)mname.utf16()); }, { mutex = CreateMutexA(NULL, FALSE, mname.toLocal8Bit().constData()); } ); if (!mutex) { qErrnoWarning("QtLockedFile::lock(): CreateMutex failed"); return 0; } } else { QT_WA( { mutex = OpenMutexW(SYNCHRONIZE | MUTEX_MODIFY_STATE, FALSE, (TCHAR*)mname.utf16()); }, { mutex = OpenMutexA(SYNCHRONIZE | MUTEX_MODIFY_STATE, FALSE, mname.toLocal8Bit().constData()); } ); if (!mutex) { if (GetLastError() != ERROR_FILE_NOT_FOUND) qErrnoWarning("QtLockedFile::lock(): OpenMutex failed"); return 0; } } return mutex; } bool QtLockedFile::waitMutex(Qt::HANDLE mutex, bool doBlock) { Q_ASSERT(mutex); DWORD res = WaitForSingleObject(mutex, doBlock ? INFINITE : 0); switch (res) { case WAIT_OBJECT_0: case WAIT_ABANDONED: return true; break; case WAIT_TIMEOUT: break; default: qErrnoWarning("QtLockedFile::lock(): WaitForSingleObject failed"); } return false; } bool QtLockedFile::lock(LockMode mode, bool block) { if (!isOpen()) { qWarning("QtLockedFile::lock(): file is not opened"); return false; } if (mode == NoLock) return unlock(); if (mode == m_lock_mode) return true; if (m_lock_mode != NoLock) unlock(); if (!wmutex && !(wmutex = getMutexHandle(-1, true))) return false; if (!waitMutex(wmutex, block)) return false; if (mode == ReadLock) { int idx = 0; for (; idx < MAX_READERS; idx++) { rmutex = getMutexHandle(idx, false); if (!rmutex || waitMutex(rmutex, false)) break; CloseHandle(rmutex); } bool ok = true; if (idx >= MAX_READERS) { qWarning("QtLockedFile::lock(): too many readers"); rmutex = 0; ok = false; } else if (!rmutex) { rmutex = getMutexHandle(idx, true); if (!rmutex || !waitMutex(rmutex, false)) ok = false; } if (!ok && rmutex) { CloseHandle(rmutex); rmutex = 0; } ReleaseMutex(wmutex); if (!ok) return false; } else { Q_ASSERT(rmutexes.isEmpty()); for (int i = 0; i < MAX_READERS; i++) { Qt::HANDLE mutex = getMutexHandle(i, false); if (mutex) rmutexes.append(mutex); } if (rmutexes.size()) { DWORD res = WaitForMultipleObjects(rmutexes.size(), rmutexes.constData(), TRUE, block ? INFINITE : 0); if (res != WAIT_OBJECT_0 && res != WAIT_ABANDONED) { if (res != WAIT_TIMEOUT) qErrnoWarning("QtLockedFile::lock(): WaitForMultipleObjects failed"); m_lock_mode = WriteLock; // trick unlock() to clean up - semiyucky unlock(); return false; } } } m_lock_mode = mode; return true; } bool QtLockedFile::unlock() { if (!isOpen()) { qWarning("QtLockedFile::unlock(): file is not opened"); return false; } if (!isLocked()) return true; if (m_lock_mode == ReadLock) { ReleaseMutex(rmutex); CloseHandle(rmutex); rmutex = 0; } else { foreach(Qt::HANDLE mutex, rmutexes) { ReleaseMutex(mutex); CloseHandle(mutex); } rmutexes.clear(); ReleaseMutex(wmutex); } m_lock_mode = QtLockedFile::NoLock; return true; } QtLockedFile::~QtLockedFile() { if (isOpen()) unlock(); if (wmutex) CloseHandle(wmutex); } mapper-0.8.1.1/3rd-party/qtsingleapplication/src/qtsingleapplication.cpp000066400000000000000000000270121325266516600264250ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the Qt Solutions component. ** ** $QT_BEGIN_LICENSE:BSD$ ** You may use this file under the terms of the BSD license as follows: ** ** "Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are ** met: ** * Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** * Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in ** the documentation and/or other materials provided with the ** distribution. ** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names ** of its contributors may be used to endorse or promote products derived ** from this software without specific prior written permission. ** ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qtsingleapplication.h" #include "qtlocalpeer.h" #include /*! \class QtSingleApplication qtsingleapplication.h \brief The QtSingleApplication class provides an API to detect and communicate with running instances of an application. This class allows you to create applications where only one instance should be running at a time. I.e., if the user tries to launch another instance, the already running instance will be activated instead. Another usecase is a client-server system, where the first started instance will assume the role of server, and the later instances will act as clients of that server. By default, the full path of the executable file is used to determine whether two processes are instances of the same application. You can also provide an explicit identifier string that will be compared instead. The application should create the QtSingleApplication object early in the startup phase, and call isRunning() to find out if another instance of this application is already running. If isRunning() returns false, it means that no other instance is running, and this instance has assumed the role as the running instance. In this case, the application should continue with the initialization of the application user interface before entering the event loop with exec(), as normal. The messageReceived() signal will be emitted when the running application receives messages from another instance of the same application. When a message is received it might be helpful to the user to raise the application so that it becomes visible. To facilitate this, QtSingleApplication provides the setActivationWindow() function and the activateWindow() slot. If isRunning() returns true, another instance is already running. It may be alerted to the fact that another instance has started by using the sendMessage() function. Also data such as startup parameters (e.g. the name of the file the user wanted this new instance to open) can be passed to the running instance with this function. Then, the application should terminate (or enter client mode). If isRunning() returns true, but sendMessage() fails, that is an indication that the running instance is frozen. Here's an example that shows how to convert an existing application to use QtSingleApplication. It is very simple and does not make use of all QtSingleApplication's functionality (see the examples for that). \code // Original int main(int argc, char **argv) { QApplication app(argc, argv); MyMainWidget mmw; mmw.show(); return app.exec(); } // Single instance int main(int argc, char **argv) { QtSingleApplication app(argc, argv); if (app.isRunning()) return !app.sendMessage(someDataString); MyMainWidget mmw; app.setActivationWindow(&mmw); mmw.show(); return app.exec(); } \endcode Once this QtSingleApplication instance is destroyed (normally when the process exits or crashes), when the user next attempts to run the application this instance will not, of course, be encountered. The next instance to call isRunning() or sendMessage() will assume the role as the new running instance. For console (non-GUI) applications, QtSingleCoreApplication may be used instead of this class, to avoid the dependency on the QtGui library. \sa QtSingleCoreApplication */ void QtSingleApplication::sysInit(const QString &appId) { actWin = 0; peer = new QtLocalPeer(this, appId); connect(peer, &QtLocalPeer::messageReceived, this, &QtSingleApplication::messageReceived); } /*! Creates a QtSingleApplication object. The application identifier will be QCoreApplication::applicationFilePath(). \a argc, \a argv, and \a GUIenabled are passed on to the QAppliation constructor. If you are creating a console application (i.e. setting \a GUIenabled to false), you may consider using QtSingleCoreApplication instead. */ QtSingleApplication::QtSingleApplication(int &argc, char **argv, bool GUIenabled) : QApplication(argc, argv, GUIenabled) { sysInit(); } /*! Creates a QtSingleApplication object with the application identifier \a appId. \a argc and \a argv are passed on to the QAppliation constructor. */ QtSingleApplication::QtSingleApplication(const QString &appId, int &argc, char **argv) : QApplication(argc, argv) { sysInit(appId); } #if QT_VERSION < 0x050000 /*! Creates a QtSingleApplication object. The application identifier will be QCoreApplication::applicationFilePath(). \a argc, \a argv, and \a type are passed on to the QAppliation constructor. */ QtSingleApplication::QtSingleApplication(int &argc, char **argv, Type type) : QApplication(argc, argv, type) { sysInit(); } # if defined(Q_WS_X11) /*! Special constructor for X11, ref. the documentation of QApplication's corresponding constructor. The application identifier will be QCoreApplication::applicationFilePath(). \a dpy, \a visual, and \a cmap are passed on to the QApplication constructor. */ QtSingleApplication::QtSingleApplication(Display* dpy, Qt::HANDLE visual, Qt::HANDLE cmap) : QApplication(dpy, visual, cmap) { sysInit(); } /*! Special constructor for X11, ref. the documentation of QApplication's corresponding constructor. The application identifier will be QCoreApplication::applicationFilePath(). \a dpy, \a argc, \a argv, \a visual, and \a cmap are passed on to the QApplication constructor. */ QtSingleApplication::QtSingleApplication(Display *dpy, int &argc, char **argv, Qt::HANDLE visual, Qt::HANDLE cmap) : QApplication(dpy, argc, argv, visual, cmap) { sysInit(); } /*! Special constructor for X11, ref. the documentation of QApplication's corresponding constructor. The application identifier will be \a appId. \a dpy, \a argc, \a argv, \a visual, and \a cmap are passed on to the QApplication constructor. */ QtSingleApplication::QtSingleApplication(Display* dpy, const QString &appId, int argc, char **argv, Qt::HANDLE visual, Qt::HANDLE cmap) : QApplication(dpy, argc, argv, visual, cmap) { sysInit(appId); } # endif // Q_WS_X11 #endif // QT_VERSION < 0x050000 /*! Returns true if another instance of this application is running; otherwise false. This function does not find instances of this application that are being run by a different user (on Windows: that are running in another session). \sa sendMessage() */ bool QtSingleApplication::isRunning() { return peer->isClient(); } /*! Tries to send the text \a message to the currently running instance. The QtSingleApplication object in the running instance will emit the messageReceived() signal when it receives the message. This function returns true if the message has been sent to, and processed by, the current instance. If there is no instance currently running, or if the running instance fails to process the message within \a timeout milliseconds, this function return false. \sa isRunning(), messageReceived() */ bool QtSingleApplication::sendMessage(const QString &message, int timeout) { return peer->sendMessage(message, timeout); } /*! Returns the application identifier. Two processes with the same identifier will be regarded as instances of the same application. */ QString QtSingleApplication::id() const { return peer->applicationId(); } /*! Sets the activation window of this application to \a aw. The activation window is the widget that will be activated by activateWindow(). This is typically the application's main window. If \a activateOnMessage is true (the default), the window will be activated automatically every time a message is received, just prior to the messageReceived() signal being emitted. \sa activateWindow(), messageReceived() */ void QtSingleApplication::setActivationWindow(QWidget* aw, bool activateOnMessage) { actWin = aw; if (activateOnMessage) connect(peer, &QtLocalPeer::messageReceived, this, &QtSingleApplication::activateWindow); else disconnect(peer, &QtLocalPeer::messageReceived, this, &QtSingleApplication::activateWindow); } /*! Returns the applications activation window if one has been set by calling setActivationWindow(), otherwise returns 0. \sa setActivationWindow() */ QWidget* QtSingleApplication::activationWindow() const { return actWin; } /*! De-minimizes, raises, and activates this application's activation window. This function does nothing if no activation window has been set. This is a convenience function to show the user that this application instance has been activated when he has tried to start another instance. This function should typically be called in response to the messageReceived() signal. By default, that will happen automatically, if an activation window has been set. \sa setActivationWindow(), messageReceived(), initialize() */ void QtSingleApplication::activateWindow() { if (actWin) { actWin->setWindowState(actWin->windowState() & ~Qt::WindowMinimized); actWin->raise(); actWin->activateWindow(); } } /*! \fn void QtSingleApplication::messageReceived(const QString& message) This signal is emitted when the current instance receives a \a message from another instance of this application. \sa sendMessage(), setActivationWindow(), activateWindow() */ /*! \fn void QtSingleApplication::initialize(bool dummy = true) \obsolete */ mapper-0.8.1.1/3rd-party/qtsingleapplication/src/qtsingleapplication.h000066400000000000000000000076171325266516600261030ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the Qt Solutions component. ** ** $QT_BEGIN_LICENSE:BSD$ ** You may use this file under the terms of the BSD license as follows: ** ** "Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are ** met: ** * Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** * Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in ** the documentation and/or other materials provided with the ** distribution. ** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names ** of its contributors may be used to endorse or promote products derived ** from this software without specific prior written permission. ** ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QTSINGLEAPPLICATION_H #define QTSINGLEAPPLICATION_H #include class QtLocalPeer; #if defined(Q_OS_WIN) # if !defined(QT_QTSINGLEAPPLICATION_EXPORT) && !defined(QT_QTSINGLEAPPLICATION_IMPORT) # define QT_QTSINGLEAPPLICATION_EXPORT # elif defined(QT_QTSINGLEAPPLICATION_IMPORT) # if defined(QT_QTSINGLEAPPLICATION_EXPORT) # undef QT_QTSINGLEAPPLICATION_EXPORT # endif # define QT_QTSINGLEAPPLICATION_EXPORT __declspec(dllimport) # elif defined(QT_QTSINGLEAPPLICATION_EXPORT) # undef QT_QTSINGLEAPPLICATION_EXPORT # define QT_QTSINGLEAPPLICATION_EXPORT __declspec(dllexport) # endif #else # define QT_QTSINGLEAPPLICATION_EXPORT #endif class QT_QTSINGLEAPPLICATION_EXPORT QtSingleApplication : public QApplication { Q_OBJECT public: QtSingleApplication(int &argc, char **argv, bool GUIenabled = true); QtSingleApplication(const QString &id, int &argc, char **argv); #if QT_VERSION < 0x050000 QtSingleApplication(int &argc, char **argv, Type type); # if defined(Q_WS_X11) QtSingleApplication(Display* dpy, Qt::HANDLE visual = 0, Qt::HANDLE colormap = 0); QtSingleApplication(Display *dpy, int &argc, char **argv, Qt::HANDLE visual = 0, Qt::HANDLE cmap= 0); QtSingleApplication(Display* dpy, const QString &appId, int argc, char **argv, Qt::HANDLE visual = 0, Qt::HANDLE colormap = 0); # endif // Q_WS_X11 #endif // QT_VERSION < 0x050000 bool isRunning(); QString id() const; void setActivationWindow(QWidget* aw, bool activateOnMessage = true); QWidget* activationWindow() const; // Obsolete: void initialize(bool dummy = true) { isRunning(); Q_UNUSED(dummy) } public Q_SLOTS: bool sendMessage(const QString &message, int timeout = 5000); void activateWindow(); Q_SIGNALS: void messageReceived(const QString &message); private: void sysInit(const QString &appId = QString()); QtLocalPeer *peer; QWidget *actWin; }; #endif // QTSINGLEAPPLICATION_H mapper-0.8.1.1/3rd-party/qtsingleapplication/src/qtsingleapplication.pri000066400000000000000000000011101325266516600264240ustar00rootroot00000000000000include(../common.pri) INCLUDEPATH += $$PWD DEPENDPATH += $$PWD QT *= network greaterThan(QT_MAJOR_VERSION, 4): QT *= widgets qtsingleapplication-uselib:!qtsingleapplication-buildlib { LIBS += -L$$QTSINGLEAPPLICATION_LIBDIR -l$$QTSINGLEAPPLICATION_LIBNAME } else { SOURCES += $$PWD/qtsingleapplication.cpp $$PWD/qtlocalpeer.cpp HEADERS += $$PWD/qtsingleapplication.h $$PWD/qtlocalpeer.h } win32 { contains(TEMPLATE, lib):contains(CONFIG, shared):DEFINES += QT_QTSINGLEAPPLICATION_EXPORT else:qtsingleapplication-uselib:DEFINES += QT_QTSINGLEAPPLICATION_IMPORT } mapper-0.8.1.1/3rd-party/qtsingleapplication/src/qtsinglecoreapplication.cpp000066400000000000000000000123511325266516600272760ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the Qt Solutions component. ** ** $QT_BEGIN_LICENSE:BSD$ ** You may use this file under the terms of the BSD license as follows: ** ** "Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are ** met: ** * Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** * Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in ** the documentation and/or other materials provided with the ** distribution. ** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names ** of its contributors may be used to endorse or promote products derived ** from this software without specific prior written permission. ** ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qtsinglecoreapplication.h" #include "qtlocalpeer.h" /*! \class QtSingleCoreApplication qtsinglecoreapplication.h \brief A variant of the QtSingleApplication class for non-GUI applications. This class is a variant of QtSingleApplication suited for use in console (non-GUI) applications. It is an extension of QCoreApplication (instead of QApplication). It does not require the QtGui library. The API and usage is identical to QtSingleApplication, except that functions relating to the "activation window" are not present, for obvious reasons. Please refer to the QtSingleApplication documentation for explanation of the usage. A QtSingleCoreApplication instance can communicate to a QtSingleApplication instance if they share the same application id. Hence, this class can be used to create a light-weight command-line tool that sends commands to a GUI application. \sa QtSingleApplication */ /*! Creates a QtSingleCoreApplication object. The application identifier will be QCoreApplication::applicationFilePath(). \a argc and \a argv are passed on to the QCoreAppliation constructor. */ QtSingleCoreApplication::QtSingleCoreApplication(int &argc, char **argv) : QCoreApplication(argc, argv) { peer = new QtLocalPeer(this); connect(peer, &QtLocalPeer::messageReceived, this, &QtSingleCoreApplication::messageReceived); } /*! Creates a QtSingleCoreApplication object with the application identifier \a appId. \a argc and \a argv are passed on to the QCoreAppliation constructor. */ QtSingleCoreApplication::QtSingleCoreApplication(const QString &appId, int &argc, char **argv) : QCoreApplication(argc, argv) { peer = new QtLocalPeer(this, appId); connect(peer, &QtLocalPeer::messageReceived, this, &QtSingleCoreApplication::messageReceived); } /*! Returns true if another instance of this application is running; otherwise false. This function does not find instances of this application that are being run by a different user (on Windows: that are running in another session). \sa sendMessage() */ bool QtSingleCoreApplication::isRunning() { return peer->isClient(); } /*! Tries to send the text \a message to the currently running instance. The QtSingleCoreApplication object in the running instance will emit the messageReceived() signal when it receives the message. This function returns true if the message has been sent to, and processed by, the current instance. If there is no instance currently running, or if the running instance fails to process the message within \a timeout milliseconds, this function return false. \sa isRunning(), messageReceived() */ bool QtSingleCoreApplication::sendMessage(const QString &message, int timeout) { return peer->sendMessage(message, timeout); } /*! Returns the application identifier. Two processes with the same identifier will be regarded as instances of the same application. */ QString QtSingleCoreApplication::id() const { return peer->applicationId(); } /*! \fn void QtSingleCoreApplication::messageReceived(const QString& message) This signal is emitted when the current instance receives a \a message from another instance of this application. \sa sendMessage() */ mapper-0.8.1.1/3rd-party/qtsingleapplication/src/qtsinglecoreapplication.h000066400000000000000000000050251325266516600267430ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the Qt Solutions component. ** ** $QT_BEGIN_LICENSE:BSD$ ** You may use this file under the terms of the BSD license as follows: ** ** "Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are ** met: ** * Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** * Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in ** the documentation and/or other materials provided with the ** distribution. ** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names ** of its contributors may be used to endorse or promote products derived ** from this software without specific prior written permission. ** ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QTSINGLECOREAPPLICATION_H #define QTSINGLECOREAPPLICATION_H #include class QtLocalPeer; class QtSingleCoreApplication : public QCoreApplication { Q_OBJECT public: QtSingleCoreApplication(int &argc, char **argv); QtSingleCoreApplication(const QString &id, int &argc, char **argv); bool isRunning(); QString id() const; public Q_SLOTS: bool sendMessage(const QString &message, int timeout = 5000); Q_SIGNALS: void messageReceived(const QString &message); private: QtLocalPeer* peer; }; #endif // QTSINGLECOREAPPLICATION_H mapper-0.8.1.1/3rd-party/qtsingleapplication/src/qtsinglecoreapplication.pri000066400000000000000000000005041325266516600273030ustar00rootroot00000000000000INCLUDEPATH += $$PWD DEPENDPATH += $$PWD HEADERS += $$PWD/qtsinglecoreapplication.h $$PWD/qtlocalpeer.h SOURCES += $$PWD/qtsinglecoreapplication.cpp $$PWD/qtlocalpeer.cpp QT *= network win32:contains(TEMPLATE, lib):contains(CONFIG, shared) { DEFINES += QT_QTSINGLECOREAPPLICATION_EXPORT=__declspec(dllexport) } mapper-0.8.1.1/CMakeLists.txt000066400000000000000000000221271325266516600157230ustar00rootroot00000000000000# # Copyright 2012, 2013, 2014 Thomas Schöps # Copyright 2012-2017 Kai Pastor # # This file is part of OpenOrienteering. # # OpenOrienteering is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # OpenOrienteering is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with OpenOrienteering. If not, see . cmake_minimum_required(VERSION 3.0 FATAL_ERROR) list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") include(FeatureSummary) # Project declaration project(Mapper VERSION 0.8.1.1 LANGUAGES CXX C) if(Mapper_VERSION_DISPLAY) message(STATUS "Custom version display string: \"${Mapper_VERSION_DISPLAY}\"") elseif(CMAKE_BUILD_TYPE AND NOT CMAKE_BUILD_TYPE MATCHES "Release|MinSizeRel|RelWithDebInfo") set(Mapper_VERSION_DISPLAY "${CMAKE_BUILD_TYPE} ${Mapper_VERSION_MAJOR}.${Mapper_VERSION_MINOR}.${Mapper_VERSION_PATCH}") else() set(Mapper_VERSION_DISPLAY "${Mapper_VERSION_MAJOR}.${Mapper_VERSION_MINOR}.${Mapper_VERSION_PATCH}") endif() set(Mapper_COPYRIGHT "(C) 2012-2018 The OpenOrienteering developers") set(android_manifest "${PROJECT_SOURCE_DIR}/android/AndroidManifest.xml") file(READ "${android_manifest}" current) if(NOT current MATCHES " android:versionName=.${Mapper_VERSION_MAJOR}\\.${Mapper_VERSION_MINOR}\\.${Mapper_VERSION_PATCH}. ") set(android_version_name "${Mapper_VERSION_MAJOR}.${Mapper_VERSION_MINOR}.${Mapper_VERSION_PATCH}") math(EXPR android_version_int "${Mapper_VERSION_MAJOR} * 10000 + ${Mapper_VERSION_MINOR} * 100 + ${Mapper_VERSION_PATCH}") string(REGEX REPLACE "( android:versionName=.)[0-9]*\\.[0-9]*\\.[0-9]*(. )" "\\1${android_version_name}\\2" output "${current}") string(REGEX REPLACE "( android:versionCode=.)[0-9]*(. )" "\\1${android_version_int}\\2" output "${output}") file(WRITE "${android_manifest}" "${output}") endif() if(${CMAKE_CURRENT_BINARY_DIR} STREQUAL ${PROJECT_SOURCE_DIR}) message(AUTHOR_WARNING "In-source builds are discouraged for development.") endif() # Build configuration options if(NOT CMAKE_BUILD_TYPE) SET(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING "Type of build, options are: None Debug Release RelWithDebInfo MinSizeRel." FORCE) endif(NOT CMAKE_BUILD_TYPE) if(APPLE) SET(Mapper_MACOSX_VERSION "10.7" CACHE STRING "Minimum Mac OS X version to build for, recommended: 10.7") endif() option(Mapper_DEBUG_TRANSLATIONS "Debug missing translations" OFF) # Used for some Linux distributions which do not provide the polyclipping lib. option(Mapper_BUILD_CLIPPER "Build the Clipper package from source" OFF) option(Mapper_USE_GDAL "Use the GDAL library" ON) if(CMAKE_BUILD_TYPE MATCHES Release|MinSizeRel|RelWithDebInfo) set(Mapper_DEVELOPMENT_BUILD_DEFAULT OFF) else() set(Mapper_DEVELOPMENT_BUILD_DEFAULT ON) endif() option(Mapper_DEVELOPMENT_BUILD "Configure development build (loading resource from the build directory)" ${Mapper_DEVELOPMENT_BUILD_DEFAULT}) mark_as_advanced(Mapper_DEVELOPMENT_BUILD) option(Mapper_AUTORUN_SYSTEM_TESTS "Run the system tests as part of the Mapper_Test target" ${Mapper_DEVELOPMENT_BUILD}) option(Mapper_AUTORUN_MANUAL_TESTS "Run the system tests as part of the Mapper_Test target" OFF) mark_as_advanced(Mapper_AUTORUN_SYSTEM_TESTS Mapper_AUTORUN_MANUAL_TESTS) if(ANDROID OR APPLE OR WIN32) set(mapper_package_default ON) else() set(mapper_package_default OFF) endif() if(Mapper_USE_GDAL) set(mapper_package_gdal ${mapper_package_default}) else() set(mapper_package_gdal OFF) endif() if(NOT ANDROID) set(mapper_package_assistant ${mapper_package_default}) else() set(mapper_package_assistant OFF) endif() option(Mapper_PACKAGE_PROJ "Include all required Proj components in the packages" ${mapper_package_default}) option(Mapper_PACKAGE_GDAL "Include all required GDAL components in the packages" ${mapper_package_gdal}) option(Mapper_PACKAGE_QT "Include all required Qt components in the packages" ${mapper_package_default}) option(Mapper_PACKAGE_ASSISTANT "Include Qt Assistant in the packages" ${mapper_package_assistant}) mark_as_advanced(Mapper_PACKAGE_PROJ Mapper_PACKAGE_GDAL Mapper_PACKAGE_QT Mapper_PACKAGE_ASSISTANT) # Migration of legacy configurations (before 0.7, affects developers) if(Mapper_BUILD_DOXYGEN) unset(DOXYGEN_EXECUTABLE CACHE) endif() unset(Mapper_BUILD_DOXYGEN CACHE) unset(Mapper_BUILD_GDAL CACHE) unset(Mapper_BUILD_PROJ CACHE) unset(Mapper_BUILD_QT CACHE) # Installation configuration set(Mapper_PACKAGE_NAME "openorienteering-mapper" CACHE STRING "The package name" ) # These value are used for the DESTINATION parameter of the install command # and must not be empty. if(WIN32 AND BIN_INSTALL_DIR) set(MAPPER_RUNTIME_DESTINATION "${BIN_INSTALL_DIR}") set(MAPPER_LIBRARY_DESTINATION "${LIB_INSTALL_DIR}") set(MAPPER_DATA_DESTINATION "${SHARE_INSTALL_DIR}/${Mapper_PACKAGE_NAME}") set(MAPPER_ABOUT_DESTINATION "${SHARE_INSTALL_DIR}/doc/${Mapper_PACKAGE_NAME}") elseif(WIN32) set(MAPPER_RUNTIME_DESTINATION .) set(MAPPER_LIBRARY_DESTINATION .) set(MAPPER_DATA_DESTINATION .) set(MAPPER_ABOUT_DESTINATION "doc") elseif(APPLE) set(MAPPER_RUNTIME_DESTINATION .) set(MAPPER_LIBRARY_DESTINATION .) set(MAPPER_DATA_DESTINATION "Mapper.app/Contents/Resources") set(MAPPER_ABOUT_DESTINATION "Mapper.app/Contents/Resources/doc") elseif(ANDROID) set(MAPPER_RUNTIME_DESTINATION "libs/${CMAKE_ANDROID_ARCH_ABI}") set(MAPPER_LIBRARY_DESTINATION "libs/${CMAKE_ANDROID_ARCH_ABI}") set(MAPPER_DATA_DESTINATION "assets") set(MAPPER_ABOUT_DESTINATION "assets/doc") else() # LINUX and alike set(MAPPER_RUNTIME_DESTINATION "bin") set(MAPPER_LIBRARY_DESTINATION "lib/${Mapper_PACKAGE_NAME}") set(MAPPER_DATA_DESTINATION "share/${Mapper_PACKAGE_NAME}") set(MAPPER_ABOUT_DESTINATION "share/doc/${Mapper_PACKAGE_NAME}") endif() if(CMAKE_CROSSCOMPILING) message(STATUS "Crosscompiling, host: ${CMAKE_HOST_SYSTEM_NAME}, target: ${CMAKE_SYSTEM_NAME}") endif() # Build definitons set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD_REQUIRED TRUE) if(APPLE AND Mapper_MACOSX_VERSION) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++ -mmacosx-version-min=${Mapper_MACOSX_VERSION}") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mmacosx-version-min=${Mapper_MACOSX_VERSION}") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -mmacosx-version-min=${Mapper_MACOSX_VERSION}") endif() if(CMAKE_COMPILER_IS_GNUCXX) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wpedantic -Wextra") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -Wall -Wpedantic") endif() add_custom_target(Mapper_prerequisites SOURCES # Extra files to be shown in IDE INSTALL.md README.md ) set(Mapper_prerequisites_FOUND TRUE) if(Mapper_BUILD_CLIPPER) add_subdirectory(3rd-party/clipper) add_feature_info(Mapper_BUILD_CLIPPER 1 "version: ${CLIPPER_VERSION}") add_library(Polyclipping::Polyclipping ALIAS polyclipping) else() find_package(Polyclipping 6.1.3 MODULE REQUIRED) endif() find_package(PROJ4 CONFIG QUIET) if(NOT TARGET PROJ4::proj) set(PROJ4_FOUND false) find_package(PROJ4 MODULE REQUIRED) endif() if(Mapper_USE_GDAL) find_package(GDAL REQUIRED) endif() include("TestBigEndian") test_big_endian(big_endian) if(big_endian) add_definitions(-DMAPPER_BIG_ENDIAN) endif() if(ANDROID OR big_endian) add_definitions(-DNO_NATIVE_FILE_FORMAT) endif() if(UNIX AND NOT APPLE) # set(CMAKE_INSTALL_RPATH "\$ORIGIN/../lib/${Mapper_PACKAGE_NAME}/lib") set(CMAKE_INSTALL_RPATH "${MAPPER_LIBRARY_DESTINATION}/lib") endif() add_definitions(-D_USE_MATH_DEFINES -DUNICODE) if(Mapper_DEVELOPMENT_BUILD) add_definitions(-DMAPPER_DEVELOPMENT_BUILD) include(EnableSanitize) enable_sanitize(address undefined) configure_file(suppress.txt.in suppress.txt COPYONLY) else() add_definitions(-DQT_NO_DEBUG -DQT_NO_DEBUG_OUTPUT -DQT_NO_WARNING_OUTPUT -DNDEBUG) endif() # Subdirectories add_subdirectory("doc/manual") add_subdirectory("examples") add_subdirectory("symbol sets") add_subdirectory("translations") add_subdirectory("3rd-party/qbezier") if(NOT ANDROID) add_subdirectory("3rd-party/qtsingleapplication") endif() if (Mapper_USE_GDAL) add_subdirectory("src/gdal") endif() add_subdirectory("src/libocad") if(NOT ANDROID) add_subdirectory("src/printsupport") endif() add_subdirectory("src") add_subdirectory("packaging") add_subdirectory("doc/licensing") if(CMAKE_CROSSCOMPILING) add_custom_target(TEST_WARNING ALL COMMENT "Crosscompiling, skipping all tests") add_dependencies(TEST_WARNING Mapper) else() enable_testing() add_subdirectory("test") endif() add_subdirectory("doc/api") add_subdirectory("packaging/src") feature_summary(WHAT ALL) string(TOUPPER "${CMAKE_BUILD_TYPE}" type) foreach(lang CXX C) foreach(i "" _${type}) message(STATUS "CMAKE_${lang}_FLAGS${i}: ${CMAKE_${lang}_FLAGS${i}}") endforeach() endforeach() mapper-0.8.1.1/COPYING000066400000000000000000001045131325266516600142160ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . mapper-0.8.1.1/INSTALL.md000066400000000000000000000135611325266516600146150ustar00rootroot00000000000000## General This document is about building OpenOrienteering Mapper from source code. The general build process prerequisites are: - A supported platform: - Linux. Ubuntu 16.04 is known to work. Linux is also used to cross-compile for Windows and Android. - OS X Yosemite (10.10). OS X Mountain Lion (10.8) or newer may work. (Qt supports Mac OS X Lion (10.7) for deployment only until Qt 5.6.) - CMake >= 3.1. CMake is available from https://cmake.org/. - A supported C++ compiler toolchain. C++14 is mandatory. Mapper has a number of direct and indirect dependencies on third-party components. Direct dependencies are: - Qt >=5.3 https://www.qt.io/download-open-source/ - Clipper library (aka libpolyclipping) >= 6.1.3.a http://www.angusj.com/delphi/clipper.php - PROJ.4 Cartographic Projections Library >= 4.8 http://proj4.org/ - GDAL Geospatial Data Abstraction Library http://www.gdal.org/ - ZLib Compression Library http://zlib.net/ When building for Linux, you may use the distributions' packages. However, openSUSE is known to lack the Clipper library. For target systems other than desktop Linux, the recommended way to deal with the dependencies is to use the OpenOrienteering superbuild project (https://github.com/OpenOrienteering/superbuild). See below for details. The recommented integrated development environment (IDE) is Qt Creator which is available from https://www.qt.io/download-open-source/. ## Getting the Source Download a zip or tar.gz source code archive from https://github.com/OpenOrienteering/mapper/releases and unpack it, or checkout the source code with git: ``` git clone https://github.com/OpenOrienteering/mapper.git ``` ## Compiling for Linux (without OpenOrienteering superbuild) The standard g++ (>= 4.9) compiler from a recent distribution should work. Make sure that the required development and tool packages are installed. For a Ubuntu or Debian system, install: ``` cmake \ doxygen \ libcups2-dev \ libgdal-dev \ libpolyclipping-dev \ libproj-dev \ qt5-default \ qtbase5-dev qtbase5-private-dev qtbase5-dev-tools \ qttools5-dev qttools5-dev-tools libqt5sql5-sqlite \ zlib1g-dev ``` When not using Qt Creator, open a terminal, and create a build directory, e.g. as subdirectory build in the source directory, and change to that directory. From the build directory, configure and build like this: ``` cmake PATH/TO/SOURCE_DIR ``` When building on openSUSE, you may want to add -DMapper_BUILD_CLIPPER=1. This will make the build download and build the Clipper library (libpolyclipping) which is not (yet) provided by this distribution. Now you may start the build process by running ``` make ``` ## Compiling with OpenOrienteering superbuild The OpenOrienteering superbuild project (https://github.com/OpenOrienteering/superbuild) takes care of downloading toolchains and sources, unpacking and patching sources, and building the binaries with respect to all known dependencies, using parallel jobs as much as possible. Superbuild will even create packages for Mapper when you build an openorienteering-mapper-...-package target. If you want to do development on the Mapper project for macOS, Windows, or Android, you can use the results (install directory, toolchain directory) from the superbuild for building the Mapper CMake project, for example by using the toolchain file from the superbuild directory, or by setting CMAKE_PREFIX_PATH to point to the superbuild installation directory. For convenient development in Qt Creator, it is possible to create a Kit which uses the corresponding toolchain and installation directories from the superbuild. When building Mapper with this Kit, it will find all dependencies it needs. For setting up Kits, see the Qt Creator documentation: http://doc.qt.io/qtcreator/creator-targets.html Starting with Qt Creator 4.3, it might become possible to simply open the superbuild's openorienteering-mapper build directory as a regular project with no further toolchain configuration. ## Cross-Compiling on Linux for Android In addition to the general build process prerequisites, you need: - CMake >= 3.7 - the Android SDK - the Android NDK The OpenOrienteering superbuild project will download and install this software when it creates an android toolchain. Qt 5.6 requires at least API Level 9 to work. Apps built for the armeabi-v7a ABI require at least an emulator image of API level 14. The build of OpenOrienteering Mapper for Android is done by CMake, too. However, the cmake-generated build creates a qmake project in BUILD_DIR/packaging/Mapper. While the cmake-generated build only used the deployment settings generated by qmake for this project, you this project is suitable for convenient deplyoing and debugging Mapper in Qt Creator. Note that release APKs need to be signed, and the signing key cannot change for replacing an installed app without loosing the data (maps) stored for this app. To facilitate development, debug builds of Mapper use a different namespace and name. ## Binary Packages and Distribution Even under open source licenses, distributing and/or using code in source or binary form creates certain legal obligations, such as the distribution of the corresponding source code and build instructions for GPL licensed binaries, and displaying copyright statements and disclaimers. For OpenOrienteering Mapper, this is solved by either using (not distributing) the Linux distributors' build systems and packages, or by packaging with OpenOrienteering superbuild. OpenOrienteering superbuild collects all third-party downloads, patches and control scripts, so that they can be made available together with the release binaries. Packages for macOS and Windows are built using CPack which comes with CMake. Android APKs are build in the same way, although not using a CPack generator. These packages bundle all 3rd-party components (Qt binaries and translations, PROJ.4 and GDAL binaries and data, etc.). mapper-0.8.1.1/README.md000066400000000000000000000034621325266516600144430ustar00rootroot00000000000000# OpenOrienteering Mapper ![Mapper Screenshot](http://openorienteering.github.io/mapper-manual/pages/images/main_window.png) OpenOrienteering Mapper is an orienteering mapmaking program and provides a free and open source alternative to existing commercial software. OpenOrienteering Mapper runs on Android, Windows, Mac OS X and Linux. - [Mapper Homepage](http://www.openorienteering.org/apps/mapper/) - [Manual](http://www.openorienteering.org/mapper-manual/) - [Downloads](https://github.com/OpenOrienteering/mapper/releases) - [OpenOrienteering Blog](http://www.openorienteering.org/) ## Reporting Issues and Asking for Help Issues and possible improvements can be posted to our public [Ticket system](https://github.com/OpenOrienteering/mapper/issues). Please make sure you provide all relevant information about your problem or idea. ## Contributing ### Translating Translations can be edited online on [Weblate](https://hosted.weblate.org/projects/openorienteering/mapper/). You can register/login with your Github account. Find out more about translation in our [wiki](https://github.com/OpenOrienteering/mapper/wiki/Translation). ### Writing Documentation The Mapper manual lives in its [own repository](https://github.com/OpenOrienteering/mapper-manual) which contains all information for you to get started. ### Writing Code For building Mapper from source see [`INSTALL.md`](https://github.com/OpenOrienteering/mapper/blob/master/INSTALL.md). Pull requests are very welcome. - [Issue tracker](https://github.com/OpenOrienteering/mapper/issues) - [API documentation](http://www.openorienteering.org/api-docs/mapper/) - [Developer wiki](https://github.com/OpenOrienteering/mapper/wiki) ## License Mapper is licensed under the [GNU GENERAL PUBLIC LICENSE Version 3](https://www.gnu.org/licenses/gpl.html). mapper-0.8.1.1/android/000077500000000000000000000000001325266516600145775ustar00rootroot00000000000000mapper-0.8.1.1/android/AndroidManifest.xml000066400000000000000000000140421325266516600203710ustar00rootroot00000000000000 mapper-0.8.1.1/android/CPackConfig.cmake.in000066400000000000000000000066551325266516600203310ustar00rootroot00000000000000# # Copyright 2017 Kai Pastor # # This file is part of OpenOrienteering. # # OpenOrienteering is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # OpenOrienteering is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with OpenOrienteering. If not, see . execute_process(COMMAND "${CMAKE_COMMAND}" -E echo "Creating APK package") # For Android, we don't use a CPack generator, # but run androiddeployqt from this file. set(CPACK_GENERATOR "") set(destdir "@CMAKE_CURRENT_BINARY_DIR@/Mapper/install") set(KEYSTORE_URL "@KEYSTORE_URL@") set(KEYSTORE_ALIAS "@KEYSTORE_ALIAS@") if(EXISTS "${destdir}") FILE(REMOVE_RECURSE "${destdir}") endif() execute_process( COMMAND "${CMAKE_COMMAND}" "-DCMAKE_INSTALL_PREFIX=${destdir}" "-DCMAKE_INSTALL_DO_STRIP=1" -P cmake_install.cmake WORKING_DIRECTORY "@PROJECT_BINARY_DIR@" RESULT_VARIABLE result ) if(result) message(FATAL_ERROR "Installation failed: ${result}") endif() # Create an androiddeployqt configuration file via qmake project. execute_process( COMMAND "$" WORKING_DIRECTORY "@CMAKE_CURRENT_BINARY_DIR@/Mapper" RESULT_VARIABLE result ) if(result) message(FATAL_ERROR "Creating APK configuration failed: ${result}") endif() # Copy the Mapper runtime from the CMake project to the qmake project. file(INSTALL DESTINATION "@CMAKE_CURRENT_BINARY_DIR@/Mapper" FILES "$" ) set(sign ) set(result ) set(final_message ) $<$,$>,$,$>: if("@CMAKE_HOST_UNIX@") execute_process(COMMAND "${CMAKE_COMMAND}" -E echo "Checking if we are running in a terminal") execute_process(COMMAND tty RESULT_VARIABLE result) if(result) message(SEND_ERROR "Not running in a terminal, signing disabled.") else() set(sign --sign "${KEYSTORE_URL}" "${KEYSTORE_ALIAS}") endif() endif() if(NOT "@CMAKE_HOST_UNIX@" OR result) set(final_message "The build created an unsigned APK. To sign the APK, run: jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore '${KEYSTORE_URL}' /path/to/install-release-unsigned.apk ${KEYSTORE_ALIAS} zipalign 4 /path/to/install-release-unsigned.apk outfile.apk" ) endif() > execute_process( COMMAND androiddeployqt --output "${destdir}" --deployment "bundled" --gradle --verbose $<$,$>: --release $<$,$>: ${sign} > > WORKING_DIRECTORY "@CMAKE_CURRENT_BINARY_DIR@/Mapper" RESULT_VARIABLE result ) if(result) message(FATAL_ERROR "Running androiddeployqt failed: ${result}") endif() $<$,$>,$,$>: configure_file( "@CMAKE_CURRENT_BINARY_DIR@/Mapper/install/build/outputs/apk/install-release-signed.apk" "@PROJECT_BINARY_DIR@/@CPACK_PACKAGE_FILE_NAME@.apk" COPYONLY ) > if(final_message) message(STATUS "${final_message}") endif() mapper-0.8.1.1/android/Mapper.pro.in000066400000000000000000000045031325266516600171540ustar00rootroot00000000000000# # Copyright 2017 Kai Pastor # # This file is part of OpenOrienteering. # # OpenOrienteering is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # OpenOrienteering is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with OpenOrienteering. If not, see . # This qmake project file is to create a configuration for androiddeployqt. !android:error("This project is for Android only.") TEMPLATE = app TARGET = Mapper ANDROID_PACKAGE_SOURCE_DIR = "@PROJECT_SOURCE_DIR@/android" EXPECTED_VERSION = @Mapper_VERSION_MAJOR@\.@Mapper_VERSION_MINOR@\.@Mapper_VERSION_PATCH@ !system(grep "$$EXPECTED_VERSION" "$$ANDROID_PACKAGE_SOURCE_DIR/AndroidManifest.xml") { error(The version name in AndroidManifest.xml does not match $$EXPECTED_VERSION.) } CONFIG(debug, release|debug) { # Don't let (unsigned) debug packages replace (signed) release packages, # or the developer may loose maps. system(cp -a "$$ANDROID_PACKAGE_SOURCE_DIR" "$$OUT_PWD/") ANDROID_PACKAGE_SOURCE_DIR = $$OUT_PWD/android system(sed -i -e $$shell_quote(//%%INSERT_APP_NAME%% --> dev/) "$$ANDROID_PACKAGE_SOURCE_DIR/res/values/strings.xml") } # Additional configuration from packaging/custom_install.cmake include(mapper_libs.pri) # For the generated .pro file to be used in Qt Creator, # we must install everything and replace the dummy libMapper.so. INSTALLS += everything everything.path = / everything.extra = \ "@CMAKE_COMMAND@" --build "@PROJECT_BINARY_DIR@" && \ "@CMAKE_COMMAND@" -E copy "$" "$$OUT_PWD/" && \ "@CMAKE_COMMAND@" -E chdir "@PROJECT_BINARY_DIR@" \ "@CMAKE_COMMAND@" "-DCMAKE_INSTALL_PREFIX=$(INSTALL_ROOT)" -P cmake_install.cmake mapper-0.8.1.1/android/res/000077500000000000000000000000001325266516600153705ustar00rootroot00000000000000mapper-0.8.1.1/android/res/drawable-hdpi/000077500000000000000000000000001325266516600200735ustar00rootroot00000000000000mapper-0.8.1.1/android/res/drawable-hdpi/icon.png000066400000000000000000000271131325266516600215350ustar00rootroot00000000000000PNG  IHDRHHUGbKGD pHYs  tIME 605 IDATxg\ŝ;D@Q E'` 6kY6ڀ klb&MNBPN#iфtSU=/n !a>9g>}_VK⮻:vRkm~5 @k x]szd(Z!oMOUߧq@#@ЀaxCqRa0"Z71X$PԢ8ɏF1 NKR Xb-_?4؎g٤s1MsXJj*u- (09Lm3f܄:B(5&M2 @ !:gxİp" V!7oGW}`oC̛8|omoB[+e5X24LIxEfiUp\1o,Kæ?@k[maA6D/[KʲcwԾ q,4 T25S2*o!dN;ʄhmj*vXz2f`{k{e }?-Qqy^hg?{(ۺف뺣k=BTlA"}60NRާ1gD޼ƃmǒbeCO%l/&teCQW*#ub#eL`ӯS5 Õ2Ji։>RFiV+oUuUܧߡrLT(J#FIE7md-[#n B3c::q2~ܙ@1JThסꭵ24P "O^D_J0hj):i'Sܲ%_Dqc؀geƈiHT0B6ĺOO A\"B1eNgc(t>7.]ȍ7:( F =ؙgV- b;)\*c"±(%s6ni2-bL>i4/T&/Md'޲kS߸۶NB, (ĀVS h?yH"F{֭{6\׿I=9%)HRJcz+}zOCL`PDh0J9^+鯖q Z^'Tvz6asF`[]Ao\O!}!La >/bEĔ8~B-EoS[a:/"og}D5l;/fLᥗ_֯y~ ( !I&5iT| :R)EEΤ։wɈTTLJ裚3 8cj2""RƉ"gQ䋬^6kQ1%ߧut>a╳4ȐRu oڴ)z@namnb巯>F\siZ՟.zFqq1pb磦tҔb&6W+ ,˦BuBgJ^?&52tK[s1 Uݡzg#Y|*`U+ OdіwJ#~p#տF80Du{?^G &E*Rn Ey&OacQpmP ˳wx D5[c4 lYwi$UEcc \9X "%eOW8򆯂."0-j1+6KˬKkpv p 1 p1H64&^H<WBʲ,Tg/pstC G R+M-<#è4u)s\Ef^r9S$!f- RkHQJa`&M,ޤN\tq\FV.Ŕ DzM^XVX+ƭi; !HS0(;9]E:XuqxEKl\-\ӏp+,ړcy;DZ?ŭ f0に`"y݈r&Z4ۧ0śġGqO޻-N{+L☋n?ǟAo͸W~3>EN#m6Hz\>wYmPhMpiДIaעl4L(4W;r1L 4xy3}aZȊ<b0g{i42XE2yc,߄)=$I :Mkrjq# ,Ҧcی%cb13ڪ~Gp+y/qlK۶@lmZ7* 9},Il֍9tr ӲaFGfy̙`Ba(ьxئB uAђIPKBI(MUAT^";P4zi ÐB=vWOB4*%ct˶YyK(usNTbZhaٔ0y|\lHerIB!~LaF* `iMmXZP{gcֱ_ב2$Be0:?Q̫5 Ϻ[LFd ,%AdZYmo(Nfsa2Zx?жe5a&p4P2fdO2e-J4g~t$Β^ҚHq^-J%=)ͺk5|2}4YE<#L/xyY?9Qpf9D LJ0޶n0f&O#!L롆 8)xv6Au'˵`&WƆԛ_$ɖZ-phc5B#À1s[^Nekd?z;ӎɗQ]E׺KWVf:Rrwsy_zʕhJ^bUɚ'V*3y?\fq 2p ީ^_uWY1sb~G2nCh0qY}0]gߴgM=݂!n9p,JFd2`M aڛ]۱,/؃9Gpg湿=ĉP:!K4JPRq<*ɏ)?Ne Q QMb.51Խ.wcYO=ge0q"WX.dXw*[Q,Usd3ǯ0PJޭm<ץVx嗹pl)chڸ!Ix;x0˶ڽ{x`n6/Ro}s#8L#Fci/1-7!~6t K7ͯ?}U#%( qqeYdsMr7p]wk0|N88;x8 ~2j6z7̊Gngsprl۶eòw~z2=^>u'Jfq[ة,K* ?Pݐd.J?bٽד8lTG'FV/ngtI\t5o>L:^5o2HeXx'N@X? P\w/ 3W\C&O_E!j(Z'0^Yl3OLCT 3{& #eJL9Hk\0:,a.//A~6Qr0rXܟy:؏c.vxp7.WEضT\9fΚ( q|s2uQ3T*bYnM#g?WLN[_N;2FMXQLיD B>elYg/UG8fZͯ?'H)2勞5IP)="AhSJ! vJć1m3Y|5tvtEa`ۻw*eXQ%/?[Eɩ 'tZd"AQ":>zSP^ꊈrÿaԆy;YP-5Һ~L:O\+U,A&G%~\W_C\fϹ{r?bxhZVGȏGyCJb:.⅛wL>~0vp,C$G1eLT|Xw+"~R,)Y[ewŸ~j M&13ѴLԊ=؎נ'h„ \v\s5M7?piT/(1;<|lY4Mgp539YV$m)(+ī@haɺgP+ż=4cIu0j&$Ȳ,*G,Z=MK m?Fհ, BO߮=(1iqp@kz5,RJ8""": t ضMWbNfƑg‚h7(W Dc VmhްFsqpE2uV` IDATX =@Im(ܴ-o<ڧe#7~,JܷZ$pļK'yŴ=7%mk17$/m9:¤X AP_R$#b td^:ڥd9X8v(;j7Xl{%60=o@i:^Leީgs'9^M+Nja]\-IJG`g8_=5X/-+׉IH!R`4})K[0T%O]"!Tvي5ػFE2 wk{22hO2DTz(TDib+_ %_Hec:YJekzi"b`ⷮF5p\^]^}HMeNq` R0 S*c҅6z[C|֥TGDF~(KLƤ[Y]<ED}۴ڏb/=PqI Ԧ3ŷa.EW0e-~9@x6^K'ms,&3v: 혈mK׶1m^'T1Lp\/:Ym&YId&a*!mmmзz *z'=*ZxeOEK$-ZH=D[>&~O8@6owEl[jfa#7ip0@GġA0cc`eaibYib}ˀzq k0aw )FY"v&ǒſ9s߅+P+ 123}U|qXG;94P*a i8nEs Ʋຸ{wqmq,kA52̄}3eĶe/[ [UYi cRMmpX&0 ADQ@29޴0SDFjjcH#th,S>1hh #IӚwimB( TrC?IңlL&C|.HO6HӤ3iReY&6 CŬ _F0\DɘCnGJG*nOlx1\ ֽ3WK7^´EaF۴y(-Ga ּihm^)< drX_7ccfJall&M:&Rh)I7dG%&Rt|v ד):m.3;|։3+h(* ~&p 4/>vKO_m8^+"B(aX}7MSZ%-˒mKqy0U `䠖^z}}GؽS&MRf̆G?2=ry?ەm8IENDB`mapper-0.8.1.1/android/res/drawable-ldpi/000077500000000000000000000000001325266516600200775ustar00rootroot00000000000000mapper-0.8.1.1/android/res/drawable-ldpi/icon.png000066400000000000000000000074701325266516600215450ustar00rootroot00000000000000PNG  IHDR$$bKGD pHYs  tIME 6SIDATXŗTչƿk\23$@7E*#E[cѶZjj-mV `BKE(`( L2{C?mﵟ>󾛿?ioo_Q]]=.L6KvT_B hȦSȈ"OpyXDD#oZh` ,+CkM.#Z_/%߶=)etuu4Cɻۗ; ?8gΜxށ{4WSx<AQyL $r42tK?`!bD{)kbx-4 -;v#Gl4µ#Dg.2`LOѦAMi[,(ѴVr}}DCa^}y+内( R7 m- l=@5˟#՗ S(RJ)Gx@~T,=q\t>K+#o)8Չ2-:PEN`C%.]`HQ{t[%5~e_y+}?B*uW^yeA$zŘ_}56Yλ'\^Ai2ORGØXB"1v'8ò,,!CӬ˩˛5 \ۻ`~'\ze0JnZ>cU+ݏ_HyI(K5;oژ DHKI$)rïL}OYm5kw~YS™4qx%]L}-T\8kWPy8.yǴuÁ ӛ4ޘ9i1@✆y+H'3>ػɷKeP@$Pݿ|ӇE* +aoq19xxw8Fh\%Pz@YwgrIOHyO_-^i|wH;D&Zk2`]xZӲ\hS:i9e00uɜYӆT# DϿƕW1O;E}s9Rҕau&<)}x7)̈*Wyo^\R !5C5IQV/gl/ϲBm>#xǚ5kE+pZĨ1[1 KՓw\քg,6 ]@O ZN]&J⺮RH#Gk8qR׭/[[yuF ٸ}Kn?yZsn-\; 7ΉïAy(cDRD}NC+R#oU1cw߽ӦQ^^2 xlڿ'xg?A* x׃ &~sVy wǘ ԏUQgؙU"lC|>u+2&O={hjj<;QII^{6/f\_SCMM Z{A_w7j{>YV.ȝ<[cĄؾ]{#Dx,zB}e̽>PRk,S> ͼGRs(rs)&[IJM2'ęK7RJZ+X7ӆjuh`ws՗c6= #p:"p+ DC!Ad Zk!B!B o'R @(KMtޫr| ej6F=0?րPiG? sȘ7ozɒ%%qصk&Mb H$O=5kH$*ꎶıoZ2L5tIH)řq~Aϟ?_pBZ[[Rс'| Bǥ=IENDB`mapper-0.8.1.1/android/res/drawable-mdpi/000077500000000000000000000000001325266516600201005ustar00rootroot00000000000000mapper-0.8.1.1/android/res/drawable-mdpi/icon.png000066400000000000000000000133071325266516600215420ustar00rootroot00000000000000PNG  IHDR00WtEXtSoftwareAdobe ImageReadyqe<fiTXtXML:com.adobe.xmp IDATxZytTE^{w,@a /a9z7AQ FQc(p !$@B&$$t& )[U}믾.$I|)?/|>n5Q[&Irwh@Ej` KMp82N񠭭MVKϿJCD֨ϧ21gΜb6tU*={r8SB vF4lQ*!cXnwh]Ӆm߾haB`#4K TZƞ8pk C9HC4^ 1EQIui`4?I3}"&O Iyll\<~I)k_aИa;v UVANn NeV{U{_7~}< d-v;2Sq4UϭEc ͙&\Y b^)H#*'h5] jAcLP3Lw}Kw!$337|w ;onf5I*\\ 5jր֢mĭ4 MA5.>&mWɂ>' I*%ŁZ+l`cDN3o޼OyRrb۶m!ȿxUȃcYT ]1R_ :]꼃$ U?BnVhT5ci(] 䌇7@]H?y[~:IȢ 3n*"xc.(Ѹ;>d`5$) xPsBF7P_566Hc6 > ҰKt"'ڑ#tok[ՊV Ԓ<]L$@BonnmeQ<ܢ W~>H~ V+=+;1F &h~!hr10Mƕf, zaL~<k_A4UHIN‘p R(ӧ xaiQcM :g4QS頋`w:&`)?^(c(Z6yK'T23zým"K񆏟̱$I^{oF -`ZNO%&M=[PF]6 k=Ѹ<*& Ы##w\れr$c$ち ;bcga%~FH$u l7ښ9 Cz|cp5bL`*RV\z9tzV4r~\!tsy񠩱7 J)AÙ(MKU<G *0r;aJ򆀀K #tZt,NE7H:# Zہ*zְA}YިClHu?GaT ~L2Ay>檫S^5ɕL 7 x|rm det Ҟ[1rFLsNQS|_/ Art,hp`o? ' "a%87y`ȬYP-4ҋ)wP1,:z>COirgCF#2-}GB7S[VcL*lR?+hllİVbM63/,:y/^fkمNl^wBgAcM"Pe 5T?0CBWFTrO X& b`<̍"^3M )PrJ `)>{#g;,+ $R0Gw֘ RҀEp(:92 L=x}q!{:KyX&^J< ,@se似 {aB3U9.<.J BBlH%`i_=m^k BOHB Q~~>Ԣ+%[_R[A{vhص8K(0ӽ/\{b,U_w~جA1S0*? >h<:8#G`bؿ?6>&%1V .56FqnjEEE% c3HK]B55%XK{0bhՎ@67wo7 ϝCff/Lʙċ AO6ncYΝ%4ɞSw)agOz0~0M=VG"s&)PAvqaշl˅T|clڴ6~a|n-fgRI ZsG͈N}toYf8Mh#+!جj!KM-p4ΏZS]]%U>ׯ5/<'ΥZW6u&w-Jhp5g>bspki ߹%q~"v|/ʏmd4F/|?,f?~/~ ܅3:͆3;>@Ɂ$pD3է 6 VUBSIjO4 Kh} 3 ZnhӦ?4|7:}-G婟8I٣wzV~iQ}H2c$ xt bBo<z%.'.1o{llNJX?RuA(۬"԰Z1g !@\ۻ_߮\9-~ c|"&/G/~HnsVܸj@ 7ByPD6;o%!$Qj tW 2òS!ߴ\ȇ!scܼaQx{,\whr?bސ2NS K>AL2%6<͓Nl> 5qbYSV˿##ւ䝤Qi}GT ҟ& b{fs`ɴv9&) LUQGys#&M e&IXe@X |t1 }xZC.@Ն]{=x?IN4lliB= Mļ_cIs.]Ͼ}c`]*z&FVJ1Nn} wFŽ1>5X qsŸnNYCjX%zX~\QQDBB& **k]E&o(9+s䗗:M:ÌCiڃyk3!K8[ Tߪt[b#h#}T, s_/2!>ݒxzA32ou 0P:U (2민u]<o\~rO;ZL)))RcV`9!cvyD!rss?a ,3ɿ `ऌ)4V6wSi k-..LUZCyy[F'G)g1 B`ROAFS۽\;lz۝; g m|sZV;sg_`{WBRlTg܆q!<_V/Yѣ^UGk9jRs1>x;¨$Iz柲ྡྷU;;ʴo?,D#]IENDB`mapper-0.8.1.1/android/res/drawable-xhdpi/000077500000000000000000000000001325266516600202635ustar00rootroot00000000000000mapper-0.8.1.1/android/res/drawable-xhdpi/icon.png000066400000000000000000000412371325266516600217300ustar00rootroot00000000000000PNG  IHDR``w8tEXtSoftwareAdobe ImageReadyqe<fiTXtXML:com.adobe.xmp Q>IDATx}xiWKllccmZ@1IBB7=7p!pI $` `cl{oޥ6sfv+ɶ $_?]I;}s{ _֗jEuu5TUìp8Qs |~~W^Ä _<'C. 7 #''9=Aqg<>fƤy Xޱ^N5^IFK.ׯ{ѣG&K4YRR2>fىDx b,VTE.j4a2`2$|W(tX %UQLFl2;l0;o%hz:FsV5kaE0,f"?۷/0.ƗJc sP&zXpPD rs{D$`ۅfY&z #Oo56!X1&Y#2ݙu #V`Ct41|,,5ģp9]l>йi/b-P1 ;.lCːui0_4 N#Zڐ4ycc%% 9XVF`jt8xG7iX<Abc? 9Hrs`%b0Ql6;?y[dOZkr4KSc` ]^/\gt-_Ӗi:%KExHJެI7j#tz! qtZ?X/lۄ3Xʶ! -`{(R/?m^YBYҥ+ 7<ٓMm DIJYH+FY ]`ޱ9cف|5)dX $$n7|Z!tr|pH#cPJS0'F&~oҙ`LY#283AT ;jPh{w T@"V.?*ۅׇDgP+ Trvˑ ;Pw1lPMX lj_>r" DP4#N\HFF6k[5btojX{lKD#ǁ=W󏵩 ʹr`}~y p?i\gggϟk׮|[3gΜAS2 *2~aW+ 8;"?,ѕʪEsYȚ>Vt pŒq㸟 weK%kDюL'O>}ܹ_wȑ} b2ic (?|WAVlF "tnmONr&Rf_xa7lD+1d#ظ+II[ثx46+\4e/{ä2=q'Ln$HK2"w㽵pzC| Xv?z.G~]F_!%dZ|$.*{Wf΋AFnrAAEݛa%tнaqsg!|ީ6 d`EyYas&i D#iG2U7w(I%=qg{7|maQYBTp n|J$(@SSƐAQs]G8GT`&B|~9lEg>d"!}&3}]~`4[I$G˗aRCqՓ~κly^OJl6"P>,Q!Un-H])D In4FgXr=?n!傝qE?~O}Pd|] ͏x{(x*D54؈C?}J>+i&ҾС'!ee ?['nm\hu֕K.]G &j~>Zeh bLxN-g9e%M<>VvIj[7Qp 6 4iMo8urZ8lF4JGZD2j?](Ϝ{pq酪[xNsGF#"G\׈A pP|v߾3}]طhA%I4ǷxD i!ZjW@dL-%=n"_V6j5KNޚawL/&#qD9LhQsd"eB63K Gf0RBxt4g[0[#AF?sq -e8N53~trK pbƠ<:҈Onh{gǯX]OVM k,0oECqGH nWy)dښSlp^ VHD [vg CҾx 汁Vh\F:մB "sk7;Nߥ{ 0(ω;BKޢsQ4axr8~-jM7: } D0}4 ;al[e J}]#5>mxLk<9`qÐ:ld k,E^ԀpS{N)p[aﭒD5?}/&sPH#$F=:hh8gjjnF6m/DbVf=|p{<: M`Ӿ=*IWD?r*Uk@`8PU\w WMp= ҳ́n%'h?#7F:E GhZK>Jj#o(\#IҠFKÁl0a|Ţ `Q#rgkyM=:fl\a3PE7/R]s-A߸!Zd^jnP \.T`[:.wVJA&<x?9' bd16Kk= fpE~8lCXl =h9;,n n`;V8B&2<1L4gv5XKq 8B^cXX 6y>gbvt [_EƽX&+n2 [ƍdXtg]rG:WߕeΕ"+1(_ Z<3rʔ#{ƌ,(>BJ QNb K?{x$ꆷ\u'Br v ~.:x%iTG1 $k}Fb>p>F~5֛t:D^7oQm0|~lW(yct@Dcm:P ye8;| MlGո_jjՃ!5VqNCd=ǻЙOQbW`;Uk Hw!6.&39 M+( s80P; \|>YE%cQ{4S'I]mp{Pz0u(@J5bfDVaFϝ 5G~av,*T&GQ2dO> 0䅟"o(ƢƏΑE# =ZAK ccg1k@2[:?YVtj؛ ktU˴]@s{G,br\MDT~5@%'v{`7cizm? {sz}}tp3yC9W,m-M;qh+aUcYW9H!o=yy~z-ck7X\1л9߽o,Dbj0X?BV'9,-ęJY% y !2E6wlZTL/IU1Rxnn/ gOBYa?bkv" n;$Lña6ƹȓKI.h>|3O3n}G#J G#e0|9 HP ƞo!lV۬ POrK24b2g;l8i&z İTMk倭8Qc\q#4QV7e^>F8yyAkTz1Y4&tdPW,upX猁])`̡b j3#H1CaT ل܂{ǜs [㈄X4jaS?s3T] ai]r UV$*$\nzY,挶l#Jr%"јxlCD`;pfSFSHJ;&4;|1m;X1z6 ;%L*FPd@BzԧD*m~}Yr#\TsBnL0vGב=Xwyt\knjx$g,ÞQWَ0mt6-LKİ̶DC1 9zGsNBV*946~@d Z1|-pZd ]9 HiIR*Ū. dm+>͵\!i݃SCXFm (6&N\LI6N%޻ N4!"zFWnhh^hDP ]z@$<GKGފ cD3~"_[J,dxvɝ_/ jfSt<DUE9tmF(s 9yP$dLa0Wt(ӇJp\IEvtD+ ]6 Nk[uifD,\wjG2VvkpNӮ%=+Nd I@Glx8=!3s; ":=[Xk("haI"L<ENg̣h!eLrVY烕p<4<Vt?s^"$$,f%NLG~QA"Imyڸ[ o(Ab"0iJ&K[;y9;&&!c O@4"xG|5'5J6}Pml@wc5b|M<0ߖX v_;ҍgMsb_n &d[Q4MF26~qt㇈CzO).5dx;6MSdMҿ62%@y40 3b=Lo o9low:q`nn$?e\1ohؾ]uZ>뙀<N~1ZlAǑwcTI!aGN`RA ߔ˿Ac`qf!{[(E埡fJNzhVMlxC^?^,98ll߾wsYt 0QYշ]2|rQ0rHWoOw ,:"mT1lX 1d0=CP8r F O`ZGǰ9a ą'v۬H¤;l(UO _U-G;yl]( IN$ !;¨w]0"f./nMRz<Ÿd"Ifjޅ5o#R/P$gͱ!' 4< [WΥT^qY(;YZj@w<v6:wN7 2 /EӍ#ro3!P3%$+z ?u#A9܈7ٕ*(-+LR TAS" #Ko {kv @5طvc;+ø |\Ɋg! 5j$; =@nshi'vccHY<.O>Љ3z ڷ1!kV. vBZ~2F(~VM1P‚qgvˊ{˖+׼:{ALxʺ-+0~'t~(a̹נh4Qx$(`k#*n $uZ`szPYh񙶬?tWZ!- Ci^_EZ-d[/ނJrW8sqּyhljkxҤ`w8D> Xϰ%ⅸ+1+$uL]?L>~h(Y]=7+wU˜.piҖWB"ZXD/dxD5iJ!1xP 0П~)rYVk )Q:8m)O $꿗&?%A۩ .GobEmro-$Qp"fWl܃deKjRq"E֭%ۢW)_؈疷ͪ;s5{'34 r^p5F5Q1H߅UU.Yd6&A=itJ'NAy8v|FD]hܻnCQ j6.:U{wnCdV&.lZmNO'l$KGsjŇZE ?p^w`c{H+e.* cs!A#κ;궭Bb 2F |7iA jm8L=Xk(++QNrOpOAm'E3RȈ0ob捿cqfv/uI4@A tABnXy(w:͙=PG#zCXWj|s5TY)p#[4U2vC9@LI_K4Æ=ڍ<0r1-M%BQ{0mt'9v͝rpU5 0qšҶ:^’glߡ\[^j֌ ̎E54+_ S S185e 0#GBy>Ռ1-L|-g\_)1#p%Q{ׯOWg~L)>KLISՎ*r Yi3sDzw i]z7*MWD:# */)ZMظqWƷMz6bWiw08ۂK`\#EAaqLA8i#طyt[G`|{H#-y> 0edkK𣵵wVQYn*x`ʕ9N< x1zPL{4{( =m1:*ө-w Mlg6P8dLR62WӤ3Bb0{.__~˗/3_E/^>f8 É #G͛d W>,tc^ F{͛w&."a@v}EJK91sL\0Nc?䈷;߅-+_=\NJQ2R'n˯%V2jP8y_,a 3 uxWdRH?\,\SNk\}J26f&5Xk7aVӮݍCر} mڴ8a-ʽF|D]W㪫ң%ڈ\k#E;ui}#Aɘ$5{a~Rd"ב0v/0i-I7_2Q3؇g}/"SpbpbHӽ$Eu7(R JuE7SŐ1l\K2::: W,܊[x<-cr5a jBmPN6^bo2 dj8UL_^ds3 I| j%={6na1.EI"C' 3~ fV<ChLܞEui;o%Hhicȷv%MzN]dx[mdzN$$Eo=_zYf`=\1r[DW=%V/*n&9Lb 1BԄ+0>{+@%Xxs(>W[JhB"%4sB#-j7r!(u: c)אrR5ItZusfzQ#-Cg_Y1? O=Ƞ% W\}V̘1Ch:W3+.?Sa.,+A6S`%?BwbϢ|"~oPR DHݦ.dY#PwBm:9v:?"%X Fr.Bx U䲖MIތa&!Z=>'dww19NwWܿ7On2 =+ǯnXx3:w@? =In+D"B71Sa "W._Kj6ϖI G. (uS牋sR=(a,qea xŗpwɲ%f/\P ',߀eNo=uO/E/?y C0I|m8 mz_gי\Py ޛ.EKh!ZMPSuM6{C~WQ~Ynh!}^c }!`鿳A2,mcC7Nccـ6'w TN4ZNXY> gqIJ#d1)};ip]vMn| ^naShd$+o,( fx={Zur!ܬuƴ77c/<=kloYCy%(G̙P{ȵ #ApIE")i&w2KՓg>{!~KQA9Cja:"O*8u>!:~P~EW3jҟ@.[$; )û6&׆eo!Ku=b$&Ağ)6mDX\L$UFcGB鹾_]{`rCjG v̐q-ϴ>pบ}"Q-(7zu#GQÊ1("+? a Wֳ߰FGIK$̞%Άzk2`1k 7^0k"nwX~*y"D% i}lxM$;bӤn&xS̉Xbmܤj 9,΅:@a 7܀H(_w%]G'׭y&iclڱ[w]}Pam ^AA9bt|1@ @8'lI:s e)Ҡ硄Ja!z<*!RzSj2;xSW`L9 cB8(o:^"{PLو5g'T4~BD7 шAJjIapD"l{avEVpP{k2B:}" RvMlq,bo<6ׅmbbgCa_C) g>J+E{ìu*8\92% j/`'QTgnh{\6Izq(9 F!+HrLmboJ?-7w|JAo G*!@?OUӋw}f:#ʧ ?yl +&mq_Z_'%Vpq^n!@"}^rv}L\`G%@,y#OGN`dJG ? q^vm6 = c~@ ck2w=-+V9Qx?/C6 ֵ*Z7>*n`)mG A+q#[SU.^Evr؆ B{Bv:}CQ( b>2}um 45Dl{88hR GO0v”MͲ"wIk٤6 3B|gΤGv}Ov $2ncӈOR8x9 v'/[Ǥt8L~̾NUN_PAi2=yMd-oeW/nv=}KNxslNـG`1 d1NͲkVYU~2y˩ &;H^[Rԩz4<썠,; $u=)û o[ga,L &O>d3|])xj>va{"k!%ͪ1Mƒ 48&x 3?|j ́NT?H>'6ǜ[ t-cӉ‘8 RHܯӫ@maS1 og;AY.OpEӞi3ȾBft06} VJ?DӃ>|&ԇ??~E9',>p;,i&5x% +JBlXhuaa$Ҟ}ў\܃:)jf6mwᒇTȻMgM iL5\39PRqY8Jy+x;D #N1 o"ß!8sP>u"_;^M6`-pQC߱+cN1atbC>UQxwY :D'IkaDO߉5vǟ+CUkmUdqݖ1rZp$P (ػ$ۼw%9rU}77M'5R7y. tCI!)}4@̆#Dv$")ݳ8]`5<=UxnCoΛ.Rp$dIZmpW j7eftً݂!|yPNq+yCvڷE#d޼Ry)g""If/;^{;lS8OϻȎZכÙ]DJk% &hhZ%|ݔ;IֽmI=F^'1A~gC|b^I:K~ѽZ{jsyǬH 8Q$CΌo].༺zۨYjg~ ż`t=9q'}DQRU2)zgTq;aCɿV !xy8UFPMi}$ H{P d@=v[\ז]~BmnʵuYX|(}Jo#$ 3so{]}Wu;.Uɓ*=p[13xsdO{n:/n@IbHĎ 0W8?X4Ju:RY$' &6$= ~5ީiZm7l7.1\yED8;[rEƓK\ /* USM2&li0쌋1"i<*1H#N);妗 Qo J6v"\Ehl؏m;a5GQ\jrp>g\ྼc=Ogۀ.'))\rœ&ma&ROv Jg LJ ;|,y){(6;Y999dg3d8J^NxFпWpJɈ_sMLxO|3ьƁ-ZK/I]_z5a߸maTPćH5$C:}^iȗגcE ufxEn0 ~tCNHF #",Sy͂IcghժUN]k֬YKA%_%P_R'ک7O{<ɠPHDRYQ82;<cF<=5]JV-BRq ~&#x Lv~W貧3Q{*'xݨi) G* }zT}AX^@g^m8?7?9eyI_rڦ_ڲ]Y qCk:wy)&[>ɽ~}ѓ{ WoaӅ or ۻõ-d$gMDt^;tu##OX>VߵWz >Qω!C:|&HNb, 8 P¿0OV2o2x?~>K{'I@ɿ0in$ŎTG] YJIENDB`mapper-0.8.1.1/android/res/values/000077500000000000000000000000001325266516600166675ustar00rootroot00000000000000mapper-0.8.1.1/android/res/values/strings.xml000066400000000000000000000003321325266516600211000ustar00rootroot00000000000000 OpenOrienteering mapper-0.8.1.1/android/src/000077500000000000000000000000001325266516600153665ustar00rootroot00000000000000mapper-0.8.1.1/android/src/org/000077500000000000000000000000001325266516600161555ustar00rootroot00000000000000mapper-0.8.1.1/android/src/org/openorienteering/000077500000000000000000000000001325266516600215315ustar00rootroot00000000000000mapper-0.8.1.1/android/src/org/openorienteering/mapper/000077500000000000000000000000001325266516600230155ustar00rootroot00000000000000mapper-0.8.1.1/android/src/org/openorienteering/mapper/MapperActivity.java000066400000000000000000000167201325266516600266270ustar00rootroot00000000000000/* * Copyright 2013 Thomas Schöps * Copyright 2014 Thomas Schöps, Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ package org.openorienteering.mapper; import android.app.AlertDialog; import android.content.DialogInterface; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.res.Configuration; import android.location.GpsStatus; import android.location.Location; import android.location.LocationManager; import android.location.LocationProvider; import android.location.LocationListener; import android.os.Build; import android.os.Bundle; import android.os.CountDownTimer; import android.os.Looper; import android.os.SystemClock; import android.provider.Settings; import android.view.Gravity; import android.view.Surface; import android.widget.Toast; /** * Contains Android Java code for Mapper. */ public class MapperActivity extends org.qtproject.qt5.android.bindings.QtActivity { private static MapperActivity instance; private String yes_string; private String no_string; private String gps_disabled_string; private static Toast toast; private static CountDownTimer toast_reset; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); instance = this; } /** Call setIntent, as recommended for singleTask launch mode. */ @Override public void onNewIntent(Intent intent) { setIntent(intent); } /** Returns the data string from the intent, and resets the intent. */ public String takeIntentPath() { String result = ""; Intent intent = getIntent(); if (intent != null) { String action = intent.getAction(); if (action == Intent.ACTION_EDIT || action == Intent.ACTION_VIEW) { result = intent.getDataString(); } setIntent(null); } return result; } // Static methods to be called from C++ /** Checks if GPS is enabled in the Android settings and if not, prompts the user to enable it. * The dialog box works asynchronously, so the method cannot return the result. */ static void checkGPSEnabled() { LocationManager locationManager = (LocationManager) instance.getSystemService(LOCATION_SERVICE); boolean enabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER); if (!enabled) { instance.runOnUiThread(new Runnable() { public void run() { DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { switch (which){ case DialogInterface.BUTTON_POSITIVE: Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS); instance.startActivity(intent); break; case DialogInterface.BUTTON_NEGATIVE: //No button clicked break; } } }; AlertDialog.Builder builder = new AlertDialog.Builder(instance); builder.setMessage(instance.gps_disabled_string) .setPositiveButton(instance.yes_string, dialogClickListener) .setNegativeButton(instance.no_string, dialogClickListener) .show(); } }); } } public static void setTranslatableStrings(String yes_string, String no_string, String gps_disabled_string) { instance.yes_string = yes_string; instance.no_string = no_string; instance.gps_disabled_string = gps_disabled_string; } public static void showToast(final String message, final int duration) { instance.runOnUiThread(new Runnable() { public void run() { if (toast_reset != null) toast_reset.cancel(); if (toast == null) { toast = Toast.makeText(instance, "", Toast.LENGTH_SHORT); toast.setGravity(Gravity.BOTTOM|Gravity.CENTER_HORIZONTAL, 0, 4); } toast.setText(message); toast.show(); if (duration <= 0) return; toast_reset = new CountDownTimer(duration, 500) { public void onTick(long millisUntilFinished) { toast.show(); } public void onFinish() { toast.cancel(); } }; toast_reset.start(); } } ); } public static void hideToast() { instance.runOnUiThread(new Runnable() { public void run() { if (toast_reset != null) toast_reset.cancel(); if (toast != null) toast.cancel(); } } ); } /** Locks the current display orientation. * While a native "locked" mode comes in API level 18, * this method tries to determine and lock the current orientation * even on devices with lower API level. On these devices, the screen * may be temporary in reverse orientation. */ public static void lockOrientation() { // ActivityInfo.SCREEN_ORIENTATION_LOCKED == 14 comes with API level 18 if (Build.VERSION.SDK_INT >= 18) { instance.setRequestedOrientation(14); return; } int orientation = instance.getResources().getConfiguration().orientation; int rotation = instance.getWindowManager().getDefaultDisplay().getRotation(); if (orientation == Configuration.ORIENTATION_PORTRAIT) { if (rotation == Surface.ROTATION_180) instance.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT); else instance.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); } else if (orientation == Configuration.ORIENTATION_LANDSCAPE) { if (rotation == Surface.ROTATION_180) instance.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE); else instance.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); } // If we read another value now, then we must reverse the rotation. // Maybe this can occasionally return the old value (i.e. the value // before the requested rotation takes effect). int new_rotation = instance.getWindowManager().getDefaultDisplay().getRotation(); if (new_rotation != rotation) { // first try didn't lock the original rotation, retry reverse. if (orientation == Configuration.ORIENTATION_PORTRAIT) { if (new_rotation == Surface.ROTATION_180) instance.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); else instance.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT); } else if (orientation == Configuration.ORIENTATION_LANDSCAPE) { if (new_rotation == Surface.ROTATION_180) instance.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); else instance.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE); } } } /** Unlocks the display orientation * by setting the requested orientation to unspecified. */ public static void unlockOrientation() { instance.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); } /** Returns the display's current rotation. */ public static int getDisplayRotation() { return instance.getWindowManager().getDefaultDisplay().getRotation(); } } mapper-0.8.1.1/cmake/000077500000000000000000000000001325266516600142375ustar00rootroot00000000000000mapper-0.8.1.1/cmake/EnableSanitize.cmake000066400000000000000000000026001325266516600201340ustar00rootroot00000000000000# # Copyright 2016 Kai Pastor # # This file is part of OpenOrienteering. # # OpenOrienteering is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # OpenOrienteering is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with OpenOrienteering. If not, see . if(NOT COMMAND check_cxx_compiler_flag) include(CheckCXXCompilerFlag) endif() macro(enable_sanitize) foreach(option ${ARGV}) if(CMAKE_CROSSCOMPILING) break() # out of macro endif() if(option STREQUAL "NO_RECOVER") set(full_option "-fno-sanitize-recover=all") else() set(full_option "-fsanitize=${option}") endif() if(NOT CMAKE_CXX_FLAGS MATCHES "${full_option}") string(MAKE_C_IDENTIFIER ${option} option_id) check_cxx_compiler_flag("${full_option}" SANITIZE_${option_id}) if(SANITIZE_${option_id}) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${full_option}") endif() endif() endforeach() endmacro() mapper-0.8.1.1/cmake/FindPROJ4.cmake000066400000000000000000000130771325266516600167100ustar00rootroot00000000000000#.rst: # FindPROJ4 # -------- # # Find the proj includes and library. # # IMPORTED Targets # ^^^^^^^^^^^^^^^^ # # This module defines :prop_tgt:`IMPORTED` target ``PROJ4::proj``, # if Proj.4 has been found. # # Result Variables # ^^^^^^^^^^^^^^^^ # # This module defines the following variables: # # :: # # PROJ4_INCLUDE_DIRS - where to find proj_api.h, etc. # PROJ4_LIBRARIES - List of libraries when using libproj. # PROJ4_FOUND - True if libproj found. # # :: # # PROJ4_VERSION - The version of libproj found (x.y.z) # PROJ4_VERSION_MAJOR - The major version of libproj # PROJ4_VERSION_MINOR - The minor version of libproj # PROJ4_VERSION_PATCH - The patch version of libproj # PROJ4_VERSION_TWEAK - always 0 # PROJ4_VERSION_COUNT - The number of version components, always 3 # # Hints # ^^^^^ # # A user may set ``PROJ4_ROOT`` to a libproj installation root to tell this # module where to look exclusively. #============================================================================= # Copyright 2016 Kai Pastor # # # This file was derived from CMake 3.5's module FindZLIB.cmake # which has the following terms: # # Copyright 2001-2011 Kitware, Inc. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are # met: # # * Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # # * The names of Kitware, Inc., the Insight Consortium, or the names of # any consortium members, or of any contributors, may not be used to # endorse or promote products derived from this software without # specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS'' # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #============================================================================= # Search PROJ4_ROOT exclusively if it is set. if(PROJ4_ROOT) set(_PROJ4_SEARCH PATHS ${PROJ4_ROOT} NO_DEFAULT_PATH) else() set(_PROJ4_SEARCH) endif() find_path(PROJ4_INCLUDE_DIR NAMES proj_api.h ${_PROJ4_SEARCH} PATH_SUFFIXES include) mark_as_advanced(PROJ4_INCLUDE_DIR) if(PROJ4_INCLUDE_DIR AND EXISTS "${PROJ4_INCLUDE_DIR}/proj_api.h") file(STRINGS "${PROJ4_INCLUDE_DIR}/proj_api.h" PROJ4_H REGEX "^#define PJ_VERSION [0-9]+$") string(REGEX REPLACE "^.*PJ_VERSION ([0-9]).*$" "\\1" PROJ4_VERSION_MAJOR "${PROJ4_H}") string(REGEX REPLACE "^.*PJ_VERSION [0-9]([0-9]).*$" "\\1" PROJ4_VERSION_MINOR "${PROJ4_H}") string(REGEX REPLACE "^.*PJ_VERSION [0-9][0-9]([0-9]).*$" "\\1" PROJ4_VERSION_PATCH "${PROJ4_H}") set(PROJ4_VERSION "${PROJ4_VERSION_MAJOR}.${PROJ4_VERSION_MINOR}.${PROJ4_VERSION_PATCH}") set(PROJ4_VERSION_COUNT 3) endif() # Allow PROJ4_LIBRARY to be set manually, as the location of the proj library if(NOT PROJ4_LIBRARY) set(PROJ4_NAMES proj) set(PROJ4_NAMES_DEBUG projd) if(WIN32 AND DEFINED PROJ4_VERSION_MAJOR AND DEFINED PROJ4_VERSION_MINOR) list(APPEND PROJ4_NAMES proj_${PROJ4_VERSION_MAJOR}_${PROJ4_VERSION_MINOR}) list(APPEND PROJ4_NAMES projd_${PROJ4_VERSION_MAJOR}_${PROJ4_VERSION_MINOR}) endif() find_library(PROJ4_LIBRARY_RELEASE NAMES ${PROJ4_NAMES} ${_PROJ4_SEARCH} PATH_SUFFIXES lib) find_library(PROJ4_LIBRARY_DEBUG NAMES ${PROJ4_NAMES_DEBUG} ${_PROJ4_SEARCH} PATH_SUFFIXES lib) include(SelectLibraryConfigurations) select_library_configurations(PROJ4) endif() # handle the QUIETLY and REQUIRED arguments and set PROJ4_FOUND to TRUE if # all listed variables are TRUE include(FindPackageHandleStandardArgs) find_package_handle_standard_args(PROJ4 REQUIRED_VARS PROJ4_LIBRARY PROJ4_INCLUDE_DIR VERSION_VAR PROJ4_VERSION ) if(PROJ4_FOUND) set(PROJ4_INCLUDE_DIRS ${PROJ4_INCLUDE_DIR}) if(NOT PROJ4_LIBRARIES) set(PROJ4_LIBRARIES ${PROJ4_LIBRARY}) endif() if(NOT TARGET PROJ4::proj) add_library(PROJ4::proj UNKNOWN IMPORTED) set_target_properties(PROJ4::proj PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${PROJ4_INCLUDE_DIRS}") if(PROJ4_LIBRARY_RELEASE) set_property(TARGET PROJ4::proj APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE) set_target_properties(PROJ4::proj PROPERTIES IMPORTED_LOCATION_RELEASE "${PROJ4_LIBRARY_RELEASE}") endif() if(PROJ4_LIBRARY_DEBUG) set_property(TARGET PROJ4::proj APPEND PROPERTY IMPORTED_CONFIGURATIONS DEBUG) set_target_properties(PROJ4::proj PROPERTIES IMPORTED_LOCATION_DEBUG "${PROJ4_LIBRARY_DEBUG}") endif() if(NOT PROJ4_LIBRARY_RELEASE AND NOT PROJ4_LIBRARY_DEBUG) set_property(TARGET PROJ4::proj APPEND PROPERTY IMPORTED_LOCATION "${PROJ4_LIBRARY}") endif() endif() endif() mapper-0.8.1.1/cmake/FindPolyclipping.cmake000066400000000000000000000147061325266516600205230ustar00rootroot00000000000000#.rst: # FindPolyclipping # -------- # # Find the polyclipping includes and library. # # IMPORTED Targets # ^^^^^^^^^^^^^^^^ # # This module defines :prop_tgt:`IMPORTED` target ``Polyclipping::Polyclipping``, # if Polyclipping has been found. # # Result Variables # ^^^^^^^^^^^^^^^^ # # This module defines the following variables: # # :: # # POLYCLIPPING_INCLUDE_DIRS - where to find clipper.hpp, etc. # POLYCLIPPING_LIBRARIES - List of libraries when using libpolyclipping. # POLYCLIPPING_FOUND - True if libpolyclipping found. # # :: # # POLYCLIPPING_VERSION - The version of libpolyclipping found (x.y.z) # POLYCLIPPING_VERSION_MAJOR - The major version of libpolyclipping # POLYCLIPPING_VERSION_MINOR - The minor version of libpolyclipping # POLYCLIPPING_VERSION_PATCH - The patch version of libpolyclipping # POLYCLIPPING_VERSION_TWEAK - The tweak version of libpolyclipping # POLYCLIPPING_VERSION_COUNT - The number of version components # # Hints # ^^^^^ # # A user may set ``POLYCLIPPING_ROOT`` to a libpolyclipping installation root # to tell this module where to look exclusively. #============================================================================= # Copyright 2016 Kai Pastor # # # This file was derived from CMake 3.5's module FindZLIB.cmake # which has the following terms: # # Copyright 2001-2011 Kitware, Inc. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are # met: # # * Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # # * The names of Kitware, Inc., the Insight Consortium, or the names of # any consortium members, or of any contributors, may not be used to # endorse or promote products derived from this software without # specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS'' # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #============================================================================= # Search POLYCLIPPING_ROOT exclusively if it is set. if(POLYCLIPPING_ROOT) set(_POLYCLIPPING_SEARCH PATHS ${POLYCLIPPING_ROOT} NO_DEFAULT_PATH) else() set(_POLYCLIPPING_SEARCH) endif() find_path(POLYCLIPPING_INCLUDE_DIR NAMES clipper.hpp ${_POLYCLIPPING_SEARCH} PATH_SUFFIXES include include/polyclipping) mark_as_advanced(POLYCLIPPING_INCLUDE_DIR) # Allow POLYCLIPPING_LIBRARY to be set manually, as the location of the polyclipping library if(NOT POLYCLIPPING_LIBRARY) set(POLYCLIPPING_NAMES polyclipping) set(POLYCLIPPING_NAMES_DEBUG polyclippingd) find_library(POLYCLIPPING_LIBRARY_RELEASE NAMES ${POLYCLIPPING_NAMES} ${_POLYCLIPPING_SEARCH} PATH_SUFFIXES lib) find_library(POLYCLIPPING_LIBRARY_DEBUG NAMES ${POLYCLIPPING_NAMES_DEBUG} ${_POLYCLIPPING_SEARCH} PATH_SUFFIXES lib) include(SelectLibraryConfigurations) select_library_configurations(POLYCLIPPING) endif() if(POLYCLIPPING_INCLUDE_DIR AND EXISTS "${POLYCLIPPING_INCLUDE_DIR}/clipper.hpp") file(STRINGS "${POLYCLIPPING_INCLUDE_DIR}/clipper.hpp" POLYCLIPPING_H REGEX "^#define CLIPPER_VERSION \"[^\"]*\"$") string(REGEX REPLACE "^.*CLIPPER_VERSION \"([0-9]+).*$" "\\1" POLYCLIPPING_VERSION_MAJOR "${POLYCLIPPING_H}") string(REGEX REPLACE "^.*CLIPPER_VERSION \"[0-9]+\\.([0-9]+).*$" "\\1" POLYCLIPPING_VERSION_MINOR "${POLYCLIPPING_H}") string(REGEX REPLACE "^.*CLIPPER_VERSION \"[0-9]+\\.[0-9]+\\.([0-9]+).*$" "\\1" POLYCLIPPING_VERSION_PATCH "${POLYCLIPPING_H}") set(POLYCLIPPING_VERSION "${POLYCLIPPING_VERSION_MAJOR}.${POLYCLIPPING_VERSION_MINOR}.${POLYCLIPPING_VERSION_PATCH}") set(POLYCLIPPING_VERSION_COUNT 3) # only append a TWEAK version if it exists: set(POLYCLIPPING_VERSION_TWEAK 0) if( "${POLYCLIPPING_H}" MATCHES "CLIPPER_VERSION \"[0-9]+\\.[0-9]+\\.[0-9]+\\.([0-9]+)") set(POLYCLIPPING_VERSION_TWEAK "${CMAKE_MATCH_1}") set(POLYCLIPPING_VERSION "${POLYCLIPPING_VERSION}.${POLYCLIPPING_VERSION_TWEAK}") set(POLYCLIPPING_VERSION_COUNT 4) endif() endif() # handle the QUIETLY and REQUIRED arguments and set POLYCLIPPING_FOUND to TRUE if # all listed variables are TRUE include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Polyclipping REQUIRED_VARS POLYCLIPPING_INCLUDE_DIR POLYCLIPPING_LIBRARY VERSION_VAR POLYCLIPPING_VERSION ) if(POLYCLIPPING_FOUND) set(POLYCLIPPING_INCLUDE_DIRS ${POLYCLIPPING_INCLUDE_DIR}) if(NOT POLYCLIPPING_LIBRARIES) set(POLYCLIPPING_LIBRARIES ${POLYCLIPPING_LIBRARY}) endif() if(NOT TARGET Polyclipping::Polyclipping) add_library(Polyclipping::Polyclipping UNKNOWN IMPORTED) set_target_properties(Polyclipping::Polyclipping PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${POLYCLIPPING_INCLUDE_DIRS}") if(POLYCLIPPING_LIBRARY_RELEASE) set_property(TARGET Polyclipping::Polyclipping APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE) set_target_properties(Polyclipping::Polyclipping PROPERTIES IMPORTED_LOCATION_RELEASE "${POLYCLIPPING_LIBRARY_RELEASE}") endif() if(POLYCLIPPING_LIBRARY_DEBUG) set_property(TARGET Polyclipping::Polyclipping APPEND PROPERTY IMPORTED_CONFIGURATIONS DEBUG) set_target_properties(Polyclipping::Polyclipping PROPERTIES IMPORTED_LOCATION_DEBUG "${POLYCLIPPING_LIBRARY_DEBUG}") endif() if(NOT POLYCLIPPING_LIBRARY_RELEASE AND NOT POLYCLIPPING_LIBRARY_DEBUG) set_property(TARGET Polyclipping::Polyclipping APPEND PROPERTY IMPORTED_LOCATION "${POLYCLIPPING_LIBRARY}") endif() endif() endif() mapper-0.8.1.1/code-check-wrapper.sh000077500000000000000000000060731325266516600171670ustar00rootroot00000000000000#!/bin/sh -e # Copyright 2017 Kai Pastor # # This file is part of OpenOrienteering. # # OpenOrienteering is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # OpenOrienteering is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with OpenOrienteering. If not, see . # This is a wrapper for code quality tools supported by CMake. # # It adds these benefits over direct use of the tools: # # - It provides a pattern for filenames on which the tools are to be # applied. This limits the noise in relevant diagnostic output and # cuts build times by skipping files which are not of interest. # - It allows changing pattern and arguments without forcing a complete # rebuild of all sources handled by this compiler (which is always # triggered by changes to the CMake variables). # # To use this wrapper, set its full path as the program for each check, # with the actual tool as the first argument, e.g. # # CMAKE_CXX_CLANG_TIDY="/path/to/code-check-wrapper.sh;clang-tidy" # CMAKE_CXX_INCLUDE_WHAT_YOU_USE="/path/to/code-check-wrapper.sh;iwyu" # # Any other parameter is ignored. So modifying extra parameters can still # be used to force a full re-run. PROGRAM=$1 shift ENABLE_CLANG_TIDY=true ENABLE_IWYU=true PATTERN=" \ combined_symbol.cpp \ configure_grid_dialog.cpp \ file_dialog.cpp \ file_import_export.cpp \ georeferencing_dialog.cpp \ gdal_manager.cpp \ key_button_bar.cpp \ /map.cpp \ map_editor.cpp \ map_find_feature.cpp \ map_widget.cpp \ object_mover.cpp \ object_query.cpp \ ogr_file_format.cpp \ ogr_template.cpp \ overriding_shortcut.cpp \ print_widget.cpp \ renderable.cpp \ renderable_implementation.cpp \ symbol_rule_set.cpp \ tag_select_widget.cpp \ template_image.cpp \ template_tool \ text_brwoser_dialog \ undo_manager.cpp \ /util.cpp \ /util_gui.cpp \ world_file.cpp \ xml_file_format.cpp \ \ ocd \ src/tools/ \ settings \ \ " PATTERN=$(echo "${PATTERN}" | sed -e "s/^ *//;s/ *\$//;s/ */\\\\\\|/g") if echo "$@" | grep -q "${PATTERN}"; then case "${PROGRAM}" in *clang-tidy*) if ${ENABLE_CLANG_TIDY}; then "${PROGRAM}" \ -checks=-*,modernize-*,cert-*,misc-*,readability-*,-cert-err58-cpp,-readability-implicit-bool-cast,-readability-braces-around-statements,-readability-else-after-return \ "$@" \ || exit 1 fi ;; *iwyu*) if ${ENABLE_IWYU}; then "${PROGRAM}" \ -Xiwyu --mapping_file=${0%/*}/iwyu-mapper.imp \ -Xiwyu --check_also=*_p.h \ -Xiwyu --max_line_length=160 \ "$@" \ || exit 1 fi ;; *) "${PROGRAM}" "$@" || exit 1 esac else true; fi mapper-0.8.1.1/doc/000077500000000000000000000000001325266516600137245ustar00rootroot00000000000000mapper-0.8.1.1/doc/api/000077500000000000000000000000001325266516600144755ustar00rootroot00000000000000mapper-0.8.1.1/doc/api/CMakeLists.txt000066400000000000000000000063601325266516600172420ustar00rootroot00000000000000# # Copyright 2012-2015 Kai Pastor # # This file is part of OpenOrienteering. # # OpenOrienteering is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # OpenOrienteering is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with OpenOrienteering. If not, see . message(STATUS "Configuring ${PROJECT_NAME} API documentation") set(API_SRCS extra/code_overview.h extra/mainpage.h ) set(TAG_FILES qtcore.tags qtgui.tags qtwidgets.tags qtnetwork.tags qtprintsupport.tags qtsensors.tags qtpositioning.tags ) find_program(DOXYGEN_EXECUTABLE NAMES doxygen DOC "The path of the doxygen executable") mark_as_advanced(DOXYGEN_EXECUTABLE) find_program(GIT_EXECUTABLE NAMES git DOC "The path of the git executable") mark_as_advanced(GIT_EXECUTABLE) set(API_DOCS_REPOSITORY "https://github.com/OpenOrienteering/api-docs.git" CACHE STRING "The git repository where to push the api docs (only used on initial clone)") set(API_DOCS_BRANCH "gh-pages" CACHE STRING "The git branch where to push the api docs") set(API_DOCS_PATH "mapper" CACHE STRING "The path where to push the api docs") if(DOXYGEN_EXECUTABLE MATCHES NOTFOUND) message(STATUS "doxygen executable not found.") else() set(url http://doc.qt.io/qt-5/) foreach(file ${TAG_FILES}) file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/${file}.cmake" "file(DOWNLOAD \"${url}${file}\" \"${CMAKE_CURRENT_BINARY_DIR}/${file}\")\n") add_custom_command(OUTPUT ${file} COMMAND ${CMAKE_COMMAND} -P "${CMAKE_CURRENT_BINARY_DIR}/${file}.cmake" COMMENT "Downloading ${file} from ${url}") endforeach() configure_file(api-docs.sh.in api-docs.sh @ONLY) configure_file(Doxyfile.in Doxyfile @ONLY) configure_file(versionfilter.sh.in versionfilter.sh @ONLY) add_custom_target(api-docs COMMAND "${CMAKE_CURRENT_BINARY_DIR}/api-docs.sh" "${DOXYGEN_EXECUTABLE}" COMMENT "Generating API documentation" DEPENDS ${TAG_FILES} SOURCES ${API_SRCS} api-docs.sh ) endif() if(GIT_EXECUTABLE MATCHES NOTFOUND) message(STATUS "git executable not found.") elseif(TARGET api-docs) add_custom_target(api-docs-repository COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/api-docs-repository.sh" "${API_DOCS_REPOSITORY}" "${API_DOCS_BRANCH}" "${API_DOCS_PATH}" COMMENT "Preparing API docs repository" SOURCES api-docs-repository.sh ) add_custom_target(api-docs-commit COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/api-docs-commit.sh" "${API_DOCS_PATH}" "${CMAKE_CURRENT_BINARY_DIR}" COMMENT "Committing changed API docs" SOURCES api-docs-commit.sh ) add_dependencies(api-docs-commit api-docs api-docs-repository) add_custom_target(api-docs-push COMMAND git push COMMENT "Pushing changed API docs to ${API_DOCS_REPOSITORY}" WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/repository" ) endif() mapper-0.8.1.1/doc/api/Doxyfile.in000066400000000000000000000044251325266516600166150ustar00rootroot00000000000000# # Copyright 2014 Kai Pastor # # This file is part of OpenOrienteering. # # OpenOrienteering is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # OpenOrienteering is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with OpenOrienteering. If not, see . PROJECT_NAME = "@PROJECT_NAME@" PROJECT_NUMBER = "@Mapper_VERSION_MAJOR@.@Mapper_VERSION_MINOR@.@Mapper_VERSION_PATCH@" PROJECT_BRIEF = "API documentation" PROJECT_LOGO = "@CMAKE_CURRENT_SOURCE_DIR@/../openorienteering.png" QUIET = YES INPUT = "@PROJECT_SOURCE_DIR@/src" INPUT += "@PROJECT_SOURCE_DIR@/android/src" INPUT += "@PROJECT_SOURCE_DIR@/test" INPUT += "@PROJECT_SOURCE_DIR@/doc/api/extra" RECURSIVE = YES STRIP_FROM_PATH = "@PROJECT_SOURCE_DIR@" FILE_VERSION_FILTER = "@CMAKE_CURRENT_BINARY_DIR@/versionfilter.sh" SOURCE_BROWSER = NO VERBATIM_HEADERS = NO INCLUDE_PATH += "@PROJECT_SOURCE_DIR@/src/printsupport/qt-5.5.1" PREDEFINED = QT_PRINTSUPPORT_LIB QT_SENSORS_LIB QT_LOCATION_LIB PREDEFINED += Qt5Core_VERSION=5.5 Qt5PrintSupport_VERSION=5.5 JAVADOC_AUTOBRIEF = YES QT_AUTOBRIEF = YES BUILTIN_STL_SUPPORT = YES EXTRACT_ALL = YES HAVE_DOT = YES GENERATE_TAGFILE = oomapper.tags TAGFILES += qtcore.tags=http://doc.qt.io/qt-5/ TAGFILES += qtgui.tags=http://doc.qt.io/qt-5/ TAGFILES += qtwidgets.tags=http://doc.qt.io/qt-5/ TAGFILES += qtxml.tags=http://doc.qt.io/qt-5/ TAGFILES += qtnetwork.tags=http://doc.qt.io/qt-5/ TAGFILES += qtprintsupport.tags=http://doc.qt.io/qt-5/ GENERATE_LATEX = NO HTML_TIMESTAMP = NO mapper-0.8.1.1/doc/api/api-docs-commit.sh000077500000000000000000000023361325266516600200250ustar00rootroot00000000000000#!/bin/sh -e # # Copyright 2015 Kai Pastor # # This file is part of OpenOrienteering. # # OpenOrienteering is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # OpenOrienteering is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with OpenOrienteering. If not, see . # Usage: api-docs-commit.sh PATH [PROJECT_BINARY_DIR] for I in "repository/${1:-.}"/*; do if [ -e "$I" -a "${I##*/}" != "README.md" ]; then rm -R "$I" fi done for I in html/*; do if [ "${I##*.}" != "md5" -a "${I##*.}" != "map" ]; then cp -R "$I" "repository/${1:-.}/" fi done cd repository git add -A "${1:-.}" git commit -m "Update${1:+ in $1}" echo "Now call 'cd \"${2:-[CURRENT_BINARY_DIR]}/repository\" && git commit' to publish the updated API docs." mapper-0.8.1.1/doc/api/api-docs-repository.sh000077500000000000000000000021721325266516600207520ustar00rootroot00000000000000#!/bin/sh -e # # Copyright 2015 Kai Pastor # # This file is part of OpenOrienteering. # # OpenOrienteering is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # OpenOrienteering is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with OpenOrienteering. If not, see . # Usage: api-docs-repository.sh REPOSITORY BRANCH PATH if [ ! -f repository.txt -o "$(cat repository.txt)" != "$1" ]; then if [ -d repository ]; then rm -Rf repository fi echo "$1" > repository.txt fi if [ ! -d repository ]; then git clone --depth 5 "$1" -b "$2" repository cd repository else cd repository git checkout "$2" "${3:-.}" git pull --rebase fi mapper-0.8.1.1/doc/api/api-docs.sh.in000077500000000000000000000025701325266516600171440ustar00rootroot00000000000000#!/bin/sh -e # # Copyright 2015 Kai Pastor # # This file is part of OpenOrienteering. # # OpenOrienteering is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # OpenOrienteering is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with OpenOrienteering. If not, see . # Usage: api-docs.sh DOXYGEN_EXECUTABLE SOURCE_DIR=$(echo "@PROJECT_SOURCE_DIR@/" | sed -e "s/\//\\\\\//g") DOC_DIR=$(echo "@CMAKE_CURRENT_BINARY_DIR@/" | sed -e "s/\//\\\\\//g") test -d html || mkdir html for I in html/*; do rm -Rf "$I" done "$1" Doxyfile 2>&1 \ | sed -e "s/${SOURCE_DIR}//g" > html/doxygen-warnings.txt sed -i -e " s/\( ([^( ]* rev ......., [A-Za-z0-9 ]*)\)\(<\/a>\)/\2\1/ # Footer of class documentation s/\([^( ]*\) rev \(.......\), /\2<\/tt><\/a> on / s/\"${DOC_DIR}/\"/g s/\"${SOURCE_DIR}/\"/g " html/*.html mapper-0.8.1.1/doc/api/extra/000077500000000000000000000000001325266516600156205ustar00rootroot00000000000000mapper-0.8.1.1/doc/api/extra/code_overview.h000066400000000000000000000117221325266516600206340ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps, Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ /** \page code_overview Code overview \date 2013-08-31 \author Kai Pastor \author Thomas Schöps \todo Review and update the code overview. \section core Core Map is a (too) huge class which represents a complete map. It contains dynamic arrays of: - Colors (MapColor) - Symbols (Symbol) - Templates (Template) - Parts (MapPart) which in turn contain objects (Object). It also keeps track of the currently selected map objects and contains an UndoManager object. Furthermore, it has a MapRenderables renderables whose purpose will be explained soon, and contains the settings for the georeferencing and for printing / exporting the map. Most features of a map are represented by Object. All map objects have got coordinates, which are stored as a dynamic array of MapCoord. A PointObject has got a single coordinate. A TextObject has got either one coordinate (representing a single anchor point) or two coordinate (representing a text box). For a text box, the first coordinate gives the center of the box, and the second one its width and height. For a PathObject, the coordinates are interpreted as a path, i.e. a line consisting of polygonal and/or cubic bezier curve segments. To define paths with different kind of segments, the MapCoord objects store extra flags: - curve start: if set, the coordinate is the first of 4 which define a bezier curve. - dash point: used for line symbols, can be equivalent to an OC*D dash point or corner point - hole point: if set, a break in the path starts at this coordinate and ends at the next one. This is different from OC*D's hole points for areas: in OC*D, the first point of a hole in an area is marked as a hole point, in OO Mapper the last point of the previous area is marked as hole point. This is done for consistency with hole points for paths, so there are no hacks needed for combined objects with lines, areas and holes in it. - close point: if a path is closed and thus the last coordinate is at the same position as its first one, the last coordinate is marked as close point. For path objects, the coordinates are also processed into another form which approximates the path using only straight line segments: a vector of PathCoord. These coordinates also contain information about the cumulative path length, the parameter value if generated from a bezier curve, and from which segment of the original coordinates they were generated. \section rendering Rendering The process to display map objects works like this: - An Object is created, set up and added to a layer of a map. - object->update(true) can be called to force an update of the object, otherwise it is updated the next time the map is drawn. - This update (re)generates the ObjectRenderables, a set of Renderable which belong to the object. Instances of Renderable are the individual elements that make up the visualization of the object, such as dots in a dotted line. The power of a single renderable depends on the actual subclass, ranging from a simple dot to a line of text. The constraint is that every renderable uses just one map color. - The ObjectRenderables are inserted into the MapRenderables of the map, where they are sorted by their color priority. - MapRenderables::draw() goes through all Renderables in order, thus painting them with the correct color priority. \section gui GUI The topmost application windows are provided by the MainWindow class. It is just responsible for the window itself; every MainWindow also has a subclass of MainWindowController which is responsible for the content. Currently, this can be the HomeScreenController or MapEditorController, but in the future there could also be a controller for course setting, for example. The MapEditorController has as most important members - a Map object which contains colors, symbols, objects etc., - a MapView object which defines the view properties (zoom level, position offset, rotation), - a MapWidget responsible for drawing, - a MapEditorTool current_tool, for example the path drawing tool, - a MapEditorActivity editor_activity. Activities are used to display stuff that is common to multiple tools. Example: the TemplateAdjustActivity displays the pass point positions when adjusting a template. **/ mapper-0.8.1.1/doc/api/extra/mainpage.h000066400000000000000000000031431325266516600175530ustar00rootroot00000000000000/* * Copyright 2012 Thomas Schöps * Copyright 2013-2015 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ /** \mainpage OpenOrienteering Mapper Developer Documentation \section pages Contents - \subpage code_overview - Class index - Warnings issued by doxygen \section links Web Links - OpenOrienteering Mapper repository on GitHub - OpenOrienteering Mapper issues - OpenOrienteering Mapper developer wiki - Qt 5 documentation - CMake documentation - Doxygen documentation
Qt is a trademark of The Qt Company Ltd. **/ mapper-0.8.1.1/doc/api/versionfilter.sh.in000077500000000000000000000022041325266516600203320ustar00rootroot00000000000000#!/bin/sh -e # # Copyright 2015 Kai Pastor # # This file is part of OpenOrienteering. # # OpenOrienteering is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # OpenOrienteering is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with OpenOrienteering. If not, see . FILE=$(echo "${1#@PROJECT_SOURCE_DIR@/}" | sed -e "s/\//\\\\\//g") if [ "$1" != "${1#@PROJECT_SOURCE_DIR@/}" ]; then { cd $(dirname "$1") { git log -n 1 "${1##*/}" \ || echo >&2 " at file $1" } \ | sed -e " : start N /\\nDate: /! b start s/commit \(.......\).*Date: *[a-zA-Z]* \([a-zA-Z]*\) \([0-9]*\) ..:..:.. \(20..\).*/$FILE rev \1, \3 \2 \4/ q" } fi mapper-0.8.1.1/doc/coding-style.xml000066400000000000000000000036251325266516600170550ustar00rootroot00000000000000 CodeStyleData true false false true false true false false true false true false true true false true false true false 4 true false 1 false 4 DisplayName OpenOrienteering mapper-0.8.1.1/doc/licensing/000077500000000000000000000000001325266516600156775ustar00rootroot00000000000000mapper-0.8.1.1/doc/licensing/CMakeLists.txt000066400000000000000000000165471325266516600204540ustar00rootroot00000000000000# # Copyright 2014-2017 Kai Pastor # # This file is part of OpenOrienteering. # # OpenOrienteering is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # OpenOrienteering is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with OpenOrienteering. If not, see . message(STATUS "Configuring ${PROJECT_NAME} licensing documentation") # Show all available providers in Qt Creator set(known_providers ) file(GLOB provider_files "${CMAKE_CURRENT_LIST_DIR}/*-licensing.cmake") foreach(file ${provider_files}) get_filename_component(name "${file}" NAME) string(REPLACE "-licensing.cmake" "" provider "${name}") list(APPEND known_providers "${provider}") file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${name}.stamp" INPUT "${file}" ) endforeach() # Select default provider if(ANDROID OR APPLE OR WIN32) set(default_provider superbuild) elseif(UNIX) execute_process( COMMAND sh -c ". /etc/os-release; echo $ID" OUTPUT_VARIABLE system OUTPUT_STRIP_TRAILING_WHITESPACE ) if(system) set(default_provider "${system}") else() set(default_provider NOTFOUND) endif() endif() set(LICENSING_PROVIDER "${default_provider}" CACHE STRING "Provider for 3rd-party licensing information (${known_providers};OFF)" ) # Software and data used directly by Mapper set(third_party_components libpolyclipping proj qtbase qtimageformats qttranslations ) if(TARGET Qt5::AndroidExtras) list(APPEND third_party_components qtandroidextras) endif() if(TARGET Qt5::Positioning) list(APPEND third_party_components qtlocation) endif() if(TARGET Qt5::Sensors) list(APPEND third_party_components qtsensors) endif() if(TARGET Qt5::Serialport) list(APPEND third_party_components qtserialport) endif() if(TARGET QtSingleApplication) list(APPEND third_party_components qtsingleapplication) endif() if(TARGET mapper-gdal) list(APPEND third_party_components gdal) endif() if(TARGET printsupport AND WIN32) list(APPEND third_party_components zlib) endif() if(Mapper_PACKAGE_ASSISTANT) list(APPEND third_party_components qttools) endif() # Licences used directly by Mapper set(common_license_names GPL-3 ) # Required, because linked from About dialog set(explicit_copyright_GPL-3 "GPL-3.txt" "${PROJECT_SOURCE_DIR}/COPYING" "common-licenses" ) set(explicit_copyright_qtsingleapplication "qtsingleapplication.txt" "${PROJECT_SOURCE_DIR}/3rd-party/qtsingleapplication/copyright" "3rd-party" ) if(Mapper_BUILD_CLIPPER) set(explicit_copyright_libpolyclipping "libpolyclipping-${CLIPPER_VERSION}.txt" "${PROJECT_SOURCE_DIR}/3rd-party/clipper/copyright" "3rd-party" ) endif() if(LICENSING_PROVIDER) message(STATUS "Licensing information provided by: ${LICENSING_PROVIDER}") set(third_party_copyright ) set(common_licenses ) include("${LICENSING_PROVIDER}-licensing.cmake") else() set(licensing_warning " This build is a configuration without an external provider of third-party \ licensing and copyright information. It may not fulfill requirements for \ redistribution of third-party components." ) message(WARNING "${licensing_warning}") set(third_party_copyright "
  • ${licensing_warning}
  • ") set(common_licenses ) endif() function(add_copyright var name path) if(name AND path MATCHES "file:///") string(REPLACE "file:///" "/" file "${path}") if(NOT EXISTS "${file}") message(FATAL_ERROR "No such license file: ${file}") endif() list(APPEND ${var} "
  • ${name}
  • ") elseif(name AND path STREQUAL "-" AND ARGN) list(APPEND ${var} "
  • ${name}: ${ARGN}
  • ") elseif(name AND path STREQUAL "-") list(APPEND ${var} "
  • ${name}
  • ") elseif(name AND path AND ARGN) set(target ${ARGN}) string(REPLACE ".txt" "" basename "${name}") list(APPEND ${var} "
  • ${basename}
  • ") install(FILES "${path}" DESTINATION "${MAPPER_ABOUT_DESTINATION}/${target}" RENAME "${name}" ) # Generate output for manual tests from build dir. # As a side effect, make source visible in Qt Creator. file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/${target}") file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${target}/${name}" INPUT "${path}" ) elseif() message(FATAL_ERROR "Cannot create link for ${name}, ${path}, ${target}") endif() set(${var} "${${var}}" PARENT_SCOPE) endfunction() foreach(entry ${package_names}) set(entry "explicit_package_${entry}") string(REPLACE ":" ";" entry ${entry}) set(${entry}) endforeach() list(SORT third_party_components) list(REMOVE_DUPLICATES third_party_components) foreach(basename ${third_party_components}) if(LICENSING_PROVIDER AND NOT DEFINED explicit_copyright_${basename}) if(explicit_package_${basename}) set(package ${explicit_package_${basename}}) else() set(package ${basename}) endif() # Exact match string(CONFIGURE "${copyright_pattern}" pattern @ONLY) file(GLOB path ${pattern}) if(NOT path) # Pattern match string(REPLACE "/${package}/" "/${package}*/" pattern "${pattern}") file(GLOB raw_path ${pattern}) set(path ) foreach(item ${raw_path}) if(NOT item MATCHES "${package}.*-" OR item MATCHES "${package}[0-9]*-[0-9]") list(APPEND path ${item}) endif() endforeach() endif() if(NOT path) message(SEND_ERROR "No license file for ${basename} (${pattern})") elseif(path MATCHES ";") message(SEND_ERROR "Multiple matches for ${basename}: ${path}") elseif(system_copyright_dir AND path MATCHES "^${system_copyright_dir}/") set(explicit_copyright_${basename} "${basename}" "file://${path}") else() string(REGEX REPLACE "-[0-9]*$" "" name ${basename}) set(explicit_copyright_${basename} "${name}.txt" "${path}" "3rd-party") endif() endif() if(explicit_copyright_${basename}) add_copyright(third_party_copyright ${explicit_copyright_${basename}}) endif() endforeach() list(SORT common_license_names) list(REMOVE_DUPLICATES common_license_names) foreach(basename ${common_license_names}) if(NOT DEFINED explicit_copyright_${basename}) string(CONFIGURE "${common_license_pattern}" pattern @ONLY) file(GLOB path ${pattern}) if(NOT path) message(SEND_ERROR "No license file for ${basename} (${pattern})") elseif(path MATCHES ";") message(SEND_ERROR "Multiple matches for ${basename}: ${path}") elseif(system_copyright_dir AND path MATCHES "^${system_copyright_dir}/") set(explicit_copyright_${basename} "${basename}" "file://${path}") else() set(explicit_copyright_${basename} "${basename}.txt" "${path}" "common-licenses") endif() endif() if(explicit_copyright_${basename}) add_copyright(common_licenses ${explicit_copyright_${basename}}) endif() endforeach() string(REPLACE ";" "\n" third_party_copyright "${third_party_copyright}") string(REPLACE ";" "\n" common_licenses "${common_licenses}") configure_file(licensing.html "${CMAKE_CURRENT_BINARY_DIR}/licensing.html" @ONLY) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/licensing.html" DESTINATION "${MAPPER_ABOUT_DESTINATION}" ) mapper-0.8.1.1/doc/licensing/arch-licensing.cmake000066400000000000000000000016771325266516600216020ustar00rootroot00000000000000# # Copyright 2017 Kai Pastor # # This file is part of OpenOrienteering. # # OpenOrienteering is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # OpenOrienteering is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with OpenOrienteering. If not, see . # Based on Arch Extra as of 2017-04. include("linux-distribution.cmake") set(system_copyright_dir "/usr/share/licenses") set(copyright_pattern "${system_copyright_dir}/@package@/LICENSE" ) mapper-0.8.1.1/doc/licensing/debian-licensing.cmake000066400000000000000000000023241325266516600220750ustar00rootroot00000000000000# # Copyright 2017 Kai Pastor # # This file is part of OpenOrienteering. # # OpenOrienteering is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # OpenOrienteering is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with OpenOrienteering. If not, see . # Based on Debian testing as of 2017-04. # Also used for Ubuntu. include("linux-distribution.cmake") # A 3rd-party Mapper dependency may consist of multiple DEB packages # sharing the same copyright file. We reference it just once. set(package_names gdal:libgdal ) set(system_copyright_dir "/usr/share") set(copyright_pattern "${system_copyright_dir}/doc/@package@/copyright") set(common_license_pattern "${system_copyright_dir}/common-licenses/@basename@") mapper-0.8.1.1/doc/licensing/fedora-licensing.cmake000066400000000000000000000023421325266516600221130ustar00rootroot00000000000000# # Copyright 2017 Kai Pastor # # This file is part of OpenOrienteering. # # OpenOrienteering is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # OpenOrienteering is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with OpenOrienteering. If not, see . # Based on Fedora 25. include("linux-distribution.cmake") set(explicit_copyright_gdal "gdal" "file:///usr/share/doc/gdal-libs/LICENSE.TXT" ) set(system_copyright_dir "/usr/share") set(copyright_pattern "${system_copyright_dir}/doc/@package@/COPYING" "${system_copyright_dir}/doc/@package@/COPYRIGHT" "${system_copyright_dir}/licenses/@package@/COPYING" "${system_copyright_dir}/licenses/@package@/Copyright.txt" "${system_copyright_dir}/licenses/@package@/LICENSE" ) mapper-0.8.1.1/doc/licensing/licensing-html.qdocconf000066400000000000000000000020351325266516600223320ustar00rootroot00000000000000# # Copyright 2014 Kai Pastor # # This file is part of OpenOrienteering. # # OpenOrienteering is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # OpenOrienteering is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with OpenOrienteering. If not, see . HTML.stylesheets = licensing.css HTML.headerstyles = \ " \n" # HTML.nonavigationbar = "true" macro.break.HTML = "
    " macro.copyright.HTML = "©" macro.reg.HTML = "®" mapper-0.8.1.1/doc/licensing/licensing.css000066400000000000000000000020631325266516600203650ustar00rootroot00000000000000/* * Copyright 2014 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ body { font-family: Arial, Helvetica; } body:first-child { font-size: 200%; } blockquote { margin-left: 20px; } #buildversion { display:none; } .toc .level1 { } .toc .level2 { margin-left: 10px; } .toc .level3 { margin-left: 20px; } .clearfix { clear: both } mapper-0.8.1.1/doc/licensing/licensing.html000066400000000000000000000043031325266516600205400ustar00rootroot00000000000000 Licensing | OpenOrienteering Mapper

    OpenOrienteering Mapper Licensing

    OpenOrienteering Mapper

    OpenOrienteering Mapper is a free software for drawing orienteering maps.
    http://www.openorienteering.org/apps/mapper/

    @Mapper_COPYRIGHT@

    This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License (GPL) for more details.

    Third-party components

      @third_party_copyright@

    Full Text of Common Licenses

      @common_licenses@

    Trademarks

    Qt and the Qt logo are trademarks of The Qt Company Ltd.

    All other company, product, or service names may be trademarks or service marks of others and are the property of their respective owners.

    mapper-0.8.1.1/doc/licensing/licensing.qdocconf000066400000000000000000000021671325266516600213760ustar00rootroot00000000000000# # Copyright 2014 Kai Pastor # # This file is part of OpenOrienteering. # # OpenOrienteering is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # OpenOrienteering is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with OpenOrienteering. If not, see . project = OpenOrienteering Mapper description = Information on Licensing naturallanguage = en_US sourceencoding = UTF-8 outputencoding = UTF-8 outputformats = HTML sourcedirs = src sources.fileextensions += *.qdoc # exampledirs = # headerdirs = # imagedirs = # excludedirs = include(licensing-html.qdocconf) mapper-0.8.1.1/doc/licensing/licensing.qrc000066400000000000000000000005771325266516600203720ustar00rootroot00000000000000 html/licensing.html html/apache-2-0.html html/gcc-runtime-library-exception-3-1.html html/gpl-3-0.html html/lgpl-2-1.html html/lgpl-3-0.html html/style/licensing.css mapper-0.8.1.1/doc/licensing/linux-distribution.cmake000066400000000000000000000066641325266516600225710ustar00rootroot00000000000000# # Copyright 2017 Kai Pastor # # This file is part of OpenOrienteering. # # OpenOrienteering is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # OpenOrienteering is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with OpenOrienteering. If not, see . # Linux distributions provide packages of programs, shared libraries and data # which Mapper just uses, not distributes. Most dependencies needed by Mapper # have no attribution requirements for pure usage. # # Commented-out items either # - are components which explicitly need attribution, or # - are transitive dependencies which were checked and are recorded here for # reference, but do not need to be handled explicitly unless added by some # provider file, or # - were not checked yet (marked by '?'). # # Based on CMakeLists.txt component names and # - Debian jessie # - Debian stretch (testing) as of 2017-04 # - openSUSE Leap 42.3 # basename[:package_name] list(APPEND easy_dependencies libpolyclipping proj qtbase qtimageformats qttranslations # qtandroidextras ? # qtlocation ? # qtsensors ? # qtserialport ? # qtsingleapplication : Linked statically, attribution required! # gdal dependencies # libarmadillo4 # libatomic1 # libcurl[3]:curl # libdap11:libdap # not used here # libdapclient3:libdap # not used here # libdapserver7:libdap # not used here # libepsilon1:libepsilon # libexpat:expat # libfreexl1 # libgeos_c:geos # libgeotiff:libgeotiff2 # libgif:libgif6 # libhdf4 # libhdf5:libhdf5-10 # libjasper1 # libjpeg:libjpeg-turbo # libjson-c3 # libkml0 # libkmlbase1:libkml # libkmldom1:libkml # libkmlengine1:libkml # libmysqlclient # libnetcdf[7] # not used here # libodbc:unixODBC # libodbcinst:unixODBC # libogdi3.2 # not used here # libopenjp2 # libpcre # libpng16 # libpoppler # libpq # libproj* # libqhull7 # libspatialite # libsqlite3 # libsz2 # libtiff # liblzma5:xz # liburiparser1 # libwebp # libxerces-c # libxml2 # libz zlib # qttools ? ) foreach(dependency ${easy_dependencies}) if(dependency MATCHES ":") string(REGEX REPLACE ".*:" "" package ${dependency}) string(REGEX REPLACE ":.*" "" dependency ${dependency}) endif() if(NOT explicit_copyright_${dependency}) set(explicit_copyright_${dependency} "${dependency}" "-") if(dependency MATCHES "^qt") find_package(Qt5Core) list(APPEND explicit_copyright_${dependency} "${Qt5Core_VERSION}") elseif(dependency STREQUAL "libpolyclipping") find_package(Polyclipping) list(APPEND explicit_copyright_${dependency} "${POLYCLIPPING_VERSION}") elseif(dependency STREQUAL "proj") find_package(PROJ4) list(APPEND explicit_copyright_${dependency} "${PROJ4_VERSION}") elseif(dependency STREQUAL "zlib") find_package(ZLIB) list(APPEND explicit_copyright_${dependency} "${ZLIB_VERSION_STRING}") endif() endif() endforeach() mapper-0.8.1.1/doc/licensing/opensuse-licensing.cmake000066400000000000000000000023521325266516600225150ustar00rootroot00000000000000# # Copyright 2017 Kai Pastor # # This file is part of OpenOrienteering. # # OpenOrienteering is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # OpenOrienteering is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with OpenOrienteering. If not, see . # Based on openSUSE Leap 42.3. include("linux-distribution.cmake") set(explicit_copyright_gdal # Copy! Package libgdal* does not include this file. "gdal.txt" "/usr/share/doc/packages/gdal-devel/LICENSE.TXT" "3rd-party" ) set(system_copyright_dir "/usr/share/doc/packages") set(copyright_pattern "${system_copyright_dir}/@package@/COPYING" "${system_copyright_dir}/@package@/COPYRIGHT" "${system_copyright_dir}/@package@/Copyright.txt" "${system_copyright_dir}/@package@/LICENSE" ) mapper-0.8.1.1/doc/licensing/src/000077500000000000000000000000001325266516600164665ustar00rootroot00000000000000mapper-0.8.1.1/doc/licensing/src/apache-2.0.qdoc000066400000000000000000000264561325266516600210710ustar00rootroot00000000000000// This file is part of OpenOrienteering. /*! \page apache-2.0 \title Apache License Version 2.0, January 2004 \break \l http://www.apache.org/licenses/ \section1 TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION \list 1 \li 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. \li 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. \li 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. \li 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: \list \li (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and \li (b) You must cause any modified files to carry prominent notices stating that You changed the files; and \li (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 \li (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. \endlist 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. \li 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. \li 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. \li 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. \li 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. \li 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. \endlist END OF TERMS AND CONDITIONS \section1 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. \code 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. \endcode \hr \e Reference: \l{http://www.apache.org/licenses/LICENSE-2.0} */ mapper-0.8.1.1/doc/licensing/src/gcc-runtime-library-exception.qdoc000066400000000000000000000070541325266516600252170ustar00rootroot00000000000000// This file is part of OpenOrienteering. /*! \page gcc-runtime-library-exception-3.1 \title GCC Runtime Library Exception \section1 GCC RUNTIME LIBRARY EXCEPTION Version 3.1, 31 March 2009 Copyright (C) 2009 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. This GCC Runtime Library Exception ("Exception") is an additional permission under section 7 of the GNU General Public License, version 3 ("GPLv3"). It applies to a given file (the "Runtime Library") that bears a notice placed by the copyright holder of the file stating that the file is governed by GPLv3 along with this Exception. When you use GCC to compile a program, GCC may combine portions of certain GCC header files and runtime libraries with the compiled program. The purpose of this Exception is to allow compilation of non-GPL (including proprietary) programs to use, in this way, the header files and runtime libraries covered by this Exception. \section2 0. Definitions. A file is an "Independent Module" if it either requires the Runtime Library for execution after a Compilation Process, or makes use of an interface provided by the Runtime Library, but is not otherwise based on the Runtime Library. "GCC" means a version of the GNU Compiler Collection, with or without modifications, governed by version 3 (or a specified later version) of the GNU General Public License (GPL) with the option of using any subsequent versions published by the FSF. "GPL-compatible Software" is software whose conditions of propagation, modification and use would permit combination with GCC in accord with the license of GCC. "Target Code" refers to output from any compiler for a real or virtual target processor architecture, in executable form or suitable for input to an assembler, loader, linker and/or execution phase. Notwithstanding that, Target Code does not include data in any format that is used as a compiler intermediate representation, or used for producing a compiler intermediate representation. The "Compilation Process" transforms code entirely represented in non-intermediate languages designed for human-written code, and/or in Java Virtual Machine byte code, into Target Code. Thus, for example, use of source code generators and preprocessors need not be considered part of the Compilation Process, since the Compilation Process can be understood as starting with the output of the generators or preprocessors. A Compilation Process is "Eligible" if it is done using GCC, alone or with other GPL-compatible software, or if it is done without using any work based on GCC. For example, using non-GPL-compatible Software to optimize any GCC intermediate representations would not qualify as an Eligible Compilation Process. \section2 1. Grant of Additional Permission. You have permission to propagate a work of Target Code formed by combining the Runtime Library with Independent Modules, even if such propagation would otherwise violate the terms of GPLv3, provided that all Target Code was generated by Eligible Compilation Processes. You may then convey such a combination under terms of your choice, consistent with the licensing of the Independent Modules. \section2 2. No Weakening of GCC Copyleft. The availability of this Exception does not imply any general presumption that third-party software is unaffected by the copyleft requirements of the license of GCC. \hr \e Reference: \l{http://gcc.gnu.org/onlinedocs/libstdc++/manual/license.html} */ mapper-0.8.1.1/doc/licensing/src/gdal-licensing.qdocinc000066400000000000000000000301001325266516600227020ustar00rootroot00000000000000\omit // This file is part of OpenOrienteering. \endomit \note Depending on the actual package, a particular copy of OpenOrienteering Mapper may not contain code covered by one or more of the licenses listed here. In general GDAL/OGR is licensed under an MIT/X style license with the following terms: \quotation Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \endquotation \section3 gdal/frmts/gtiff/tif_float.c \quotation Copyright (c) 2002, Industrial Light & Magic, a division of Lucas Digital Ltd. LLC All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: \list \li Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. \li Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. \li Neither the name of Industrial Light & Magic nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. \endlist THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \endquotation \section3 gdal/frmts/hdf4/hdf-eos/* \quotation Copyright (C) 1996 Hughes and Applied Research Corporation Permission to use, modify, and distribute this software and its documentation for any purpose without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation. \endquotation \section3 gdal/frmts/pcraster/libcsf \quotation Copyright (c) 1997-2003, Utrecht University All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: \list \li Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. \li Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. \li Neither the name of Utrecht University nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. \endlist THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \endquotation \section3 gdal/frmts/grib/degrib/* The degrib and g2clib source code are modified versions of code produced by NOAA NWS and are in the public domain subject to the following restrictions: \quotation http://www.weather.gov/im/softa.htm DISCLAIMER The United States Government makes no warranty, expressed or implied, as to the usefulness of the software and documentation for any purpose. The U.S. Government, its instrumentalities, officers, employees, and agents assumes no responsibility (1) for the use of the software and documentation listed below, or (2) to provide technical support to users. http://www.weather.gov/disclaimer.php The information on government servers are in the public domain, unless specifically annotated otherwise, and may be used freely by the public so long as you do not 1) claim it is your own (e.g. by claiming copyright for NWS information -- see below), 2) use it in a manner that implies an endorsement or affiliation with NOAA/NWS, or 3) modify it in content and then present it as official government material. You also cannot present information of your own in a way that makes it appear to be official government information.. The user assumes the entire risk related to its use of this data. NWS is providing this data "as is," and NWS disclaims any and all warranties, whether express or implied, including (without limitation) any implied warranties of merchantability or fitness for a particular purpose. In no event will NWS be liable to you or to any third party for any direct, indirect, incidental, consequential, special or exemplary damages or lost profit resulting from any use or misuse of this data. As required by 17 U.S.C. 403, third parties producing copyrighted works consisting predominantly of the material appearing in NWS Web pages must provide notice with such work(s) identifying the NWS material incorporated and stating that such material is not subject to copyright protection. \endquotation \section3 port/cpl_minizip* This is version 2005-Feb-10 of the Info-ZIP copyright and license. The definitive version of this document should be available at ftp://ftp.info-zip.org/pub/infozip/license.html indefinitely. \quotation Copyright (c) 1990-2005 Info-ZIP. All rights reserved. For the purposes of this copyright and license, "Info-ZIP" is defined as the following set of individuals: Mark Adler, John Bush, Karl Davis, Harald Denker, Jean-Michel Dubois, Jean-loup Gailly, Hunter Goatley, Ed Gordon, Ian Gorman, Chris Herborth, Dirk Haase, Greg Hartwig, Robert Heath, Jonathan Hudson, Paul Kienitz, David Kirschbaum, Johnny Lee, Onno van der Linden, Igor Mandrichenko, Steve P. Miller, Sergio Monesi, Keith Owens, George Petrov, Greg Roelofs, Kai Uwe Rommel, Steve Salisbury, Dave Smith, Steven M. Schweda, Christian Spieler, Cosmin Truta, Antoine Verheijen, Paul von Behren, Rich Wales, Mike White This software is provided "as is," without warranty of any kind, express or implied. In no event shall Info-ZIP or its contributors be held liable for any direct, indirect, incidental, special or consequential damages arising out of the use of or inability to use this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: \list 1 \li Redistributions of source code must retain the above copyright notice, definition, disclaimer, and this list of conditions. \li Redistributions in binary form (compiled executables) must reproduce the above copyright notice, definition, disclaimer, and this list of conditions in documentation and/or other materials provided with the distribution. The sole exception to this condition is redistribution of a standard UnZipSFX binary (including SFXWiz) as part of a self-extracting archive; that is permitted without inclusion of this license, as long as the normal SFX banner has not been removed from the binary or disabled. \li Altered versions--including, but not limited to, ports to new operating systems, existing ports with new graphical interfaces, and dynamic, shared, or static library versions--must be plainly marked as such and must not be misrepresented as being the original source. Such altered versions also must not be misrepresented as being Info-ZIP releases--including, but not limited to, labeling of the altered versions with the names "Info-ZIP" (or any variation thereof, including, but not limited to, different capitalizations), "Pocket UnZip," "WiZ" or "MacZip" without the explicit permission of Info-ZIP. Such altered versions are further prohibited from misrepresentative use of the Zip-Bugs or Info-ZIP e-mail addresses or of the Info-ZIP URL(s). \li Info-ZIP retains the right to use the names "Info-ZIP," "Zip," "UnZip," "UnZipSFX," "WiZ," "Pocket UnZip," "Pocket Zip," and "MacZip" for its own source and binary releases. \endlist \endquotation \section3 gdal/ogr/ogrsf_frmts/dxf/intronurbs.cpp This code is derived from the code associated with the book "An Introduction to NURBS" by David F. Rogers. More information on the book and the code is available at: http://www.nar-associates.com/nurbs/ \quotation Copyright (c) 2009, David F. Rogers All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: \list \li Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. \li Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. \li Neither the name of the David F. Rogers nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. \endlist THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \endquotation \section3 gdal/alg/thinplatespline.cpp IEEE754 log() code derived from: @(#)e_log.c 1.3 95/01/18 \quotation Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. Developed at SunSoft, a Sun Microsystems, Inc. business. Permission to use, copy, modify, and distribute this software is freely granted, provided that this notice is preserved. \endquotation mapper-0.8.1.1/doc/licensing/src/gpl-3.0.qdoc000066400000000000000000001053641325266516600204270ustar00rootroot00000000000000// This file is part of OpenOrienteering. /*! \page gpl-3.0 \title GNU General Public License \chapter GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. <\l{http://fsf.org/}> \break Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. \section1 Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. \section1 TERMS AND CONDITIONS \section2 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. \section2 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. \section2 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. \section2 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. \section2 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. \section2 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: \list \li a) The work must carry prominent notices stating that you modified it, and giving a relevant date. \li b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". \li c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. \li d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. \endlist A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. \section2 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: \list \li a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. \li b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. \li c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. \li d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. \li e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. \endlist A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. \section2 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: \list \li a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or \li b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or \li c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or \li d) Limiting the use for publicity purposes of names of licensors or authors of the material; or \li e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or \li f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. \endlist All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. \section2 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. \section2 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. \section2 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. \section2 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. \section2 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. \section2 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. \section2 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. \section2 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. \section2 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. \section2 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS \section1 How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. \quotation \code Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . \endcode \endquotation Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: \quotation \code Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. \endcode \endquotation The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see <\l{http://www.gnu.org/licenses/}>. The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read <\l{http://www.gnu.org/philosophy/why-not-lgpl.html}>. \hr \e Reference: \l{http://www.gnu.org/licenses/gpl-3.0.en.html} */ mapper-0.8.1.1/doc/licensing/src/lgpl-2.1.qdoc000066400000000000000000000642171325266516600206040ustar00rootroot00000000000000// This file is part of OpenOrienteering. /*! \page lgpl-2.1 \title GNU Lesser General Public License GNU LESSER GENERAL PUBLIC LICENSE \break Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. \break 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA \break Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] \section1 Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. \section1 GNU LESSER GENERAL PUBLIC LICENSE \section1 TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: \list \li a) The modified work must itself be a software library. \li b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. \li c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. \li d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) \endlist These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: \list \li a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) \li b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. \li c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. \li d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. \li e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. \endlist For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: \list \li a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. \li b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. \endlist 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. \section2 NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS \section1 How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. \quotation \code Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA \endcode \endquotation Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: \quotation Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 \break Ty Coon, President of Vice \endquotation That's all there is to it! \hr \e Reference: \l{http://www.gnu.org/licenses/lgpl-2.1.en.html} */ mapper-0.8.1.1/doc/licensing/src/lgpl-3.0.qdoc000066400000000000000000000174541325266516600206050ustar00rootroot00000000000000// This file is part of OpenOrienteering. /*! \page lgpl-3.0 \title GNU Lesser General Public License Version 3 GNU LESSER GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below. \section2 0. Additional Definitions. As used herein, "this License" refers to version 3 of the GNU Lesser General Public License, and the "GNU GPL" refers to version 3 of the GNU General Public License. "The Library" refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. An "Application" is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. A "Combined Work" is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the "Linked Version". The "Minimal Corresponding Source" for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version. The "Corresponding Application Code" for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. \section2 1. Exception to Section 3 of the GNU GPL. You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. \section2 2. Conveying Modified Versions. If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: \list \li a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or \li b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy. \endlist \section2 3. Object Code Incorporating Material from Library Header Files. The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: \list \li a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License. \li b) Accompany the object code with a copy of the GNU GPL and this license document. \endlist \section2 4. Combined Works. You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: \list \li a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. \li b) Accompany the Combined Work with a copy of the GNU GPL and this license document. \li c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. \li d) Do one of the following: \list \li 0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. \li 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. \endlist \li e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.) \endlist \section2 5. Combined Libraries. You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following: \list \li a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. \li b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. \endlist \section2 6. Revised Versions of the GNU Lesser General Public License. The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation. If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library. \hr \e Reference: \l{http://www.gnu.org/licenses/lgpl-3.0.en.html} */ mapper-0.8.1.1/doc/licensing/src/licensing.qdoc000066400000000000000000000246121325266516600213160ustar00rootroot00000000000000/* * Copyright 2014, 2015 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ /*! \page licensing \title OpenOrienteering Mapper Licensing \section1 OpenOrienteering Mapper \e {OpenOrienteering Mapper is a free software for drawing orienteering maps.} \break \l http://openorienteering.org Copyright (C) 2012-2015 Kai Pastor, Thomas Schöps This program is free software: you can redistribute it and/or modify it under the terms of the \l{GNU General Public License} as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the \l{GNU General Public License} (GPL) for more details. \section1 Third-party licenses, copyright notices, conditions and disclaimers OpenOrienteering Mapper includes, uses and/or is bundled with various third-party material. This document provides licensing information, copyright notices, conditions and disclaimers for binary distributions of OpenOrienteering Mapper. If you want to distribute or modify OpenOrienteering Mapper it is your responsibility to ensure that you have met the licensing requirements of this program and of any third-party material you are using or distributing. \note Depending on the actual package, a particular copy of OpenOrienteering Mapper may not contain code covered by one or more of the licenses listed here. The following information reflects the versions which are part of the releases for Windows, OS X and Android. Linux packages will use the distribution's packages for most 3rd-party-components. These 3rd-party packages come with independent documentation on copyright and license terms. \section2 Clipper library \e {The Clipper Library has been designed to perform boolean operations on polygons, and polygon and polyline offsetting.} \break \l http://www.angusj.com/delphi/clipper.php \quotation Copyright (C) 2010-2014 Angus Johnson The Clipper Library (including Delphi, C++ & C# source code, other accompanying code, examples and documentation), hereafter called "the Software", has been released under the following license, terms and conditions: Boost Software License - Version 1.0 - August 17th, 2003 \break \l http://www.boost.org/LICENSE_1_0.txt Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the Software covered by this license to use, reproduce, display, distribute, execute, and transmit the Software, and to prepare derivative works of the Software, and to permit third-parties to whom the Software is furnished to do so, all subject to the following: The copyright notices in the Software and this entire statement, including the above license grant, this restriction and the following disclaimer, must be included in all copies of the Software, in whole or in part, and all derivative works of the Software, unless such copies or derivative works are solely in the form of machine-executable object code generated by a source language processor. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \endquotation \section2 GDAL - Geospatial Data Abstraction Library \e {GDAL is an open source X/MIT licensed translator library for raster and vector geospatial data formats.} \break \l http://www.gdal.org/ \include gdal-licensing.qdocinc \section2 GNU Standard C++ Library v3 \e {The GNU Standard C++ Library v3 is an ongoing project to implement the ISO 14882 Standard C++ library as described in chapters 17 through 27 and annex D.} \break \l http://gcc.gnu.org/libstdc++/ \quotation Copyright (C) 2014 Free Software Foundation, Inc. This library is free software; you can redistribute it and/or modify it under the terms of the \l{GNU General Public License} as published by the Free Software Foundation; either version 3, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. Under Section 7 of GPL version 3, you are granted additional permissions described in the \l{GCC Runtime Library Exception}, version 3.1, as published by the Free Software Foundation. \endquotation \section2 PROJ.4 package \e {A cartographic projection software.} \break \l http://trac.osgeo.org/proj/ \quotation Copyright (c) 2000, Frank Warmerdam Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \endquotation \section2 Qt Solutions Component: Single Application \e {The QtSingleApplication component provides support for applications that can be only started once per user.} \break \l http://code.qt.io/cgit/qt-solutions/qt-solutions.git/ \quotation Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). \endquotation \quotation Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: \list \li Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. \li Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. \li Neither the name of Digia Plc and its Subsidiary(-ies) nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. \endlist THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \endquotation \section2 Qt Toolkit \e {Qt is a C++ toolkit for cross-platform application development. Qt is The Qt Company Ltd product developed as an open source project.} \break \l http://qt.io For version, copyright and license terms of Qt see menu item Help > About Qt. OpenOrienteering Mapper uses Qt under the terms of the \l{GNU Lesser General Public License Version 3}. \include qt-licensing.qdocinc \section2 Zlib Data Compression Library The 'zlib' compression library provides in-memory compression and decompression functions \quotation (C) 1995-2013 Jean-loup Gailly and Mark Adler This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: \list 1 \li The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. \li Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. \li This notice may not be removed or altered from any source distribution. \endlist \endquotation \section1 Trademarks \include trademarks.qdocinc \section1 Full Text of Selected Licenses \list \li \l{Apache License}, version 2.0 \li \l{GCC Runtime Library Exception}, version 3.1 \li \l{GNU General Public License} (GPL), version 3.0 \li \l{GNU Lesser General Public License} (LGPL), version 2.1 \li \l{GNU Lesser General Public License Version 3} (LGPLv3) \endlist */ mapper-0.8.1.1/doc/licensing/src/qt-licensing.qdocinc000066400000000000000000002074131325266516600224340ustar00rootroot00000000000000\omit /* * Copyright 2014, 2015 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ \endomit \section2 Licenses Used in Qt 5.5.1 This section lists other licenses used in Qt or in libraries that are supplied alongside Qt modules, not provided under the \l{GNU Lesser General Public License Version 3}. \note Depending on the actual package, a particular copy of OpenOrienteering Mapper may not contain code covered by one or more of the licenses listed here. \section3 at-spi and at-spi2 This license applies to \c{qtbase/src/3rd-party/atspi2/}. In OpenOrienteering Mapper, this code is used under the terms of the \l{GNU General Public License}, version 3. \quotation Copyright 2010, 2011 Novell, Inc. \break Copyright (c) 2012 SUSE LINUX Products GmbH, Nuernberg, Germany. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. \endquotation \section3 Big5 Text Codec This license applies to parts of some files in \c{qtbase/src/corelib/codecs/}. \quotation Copyright (C) 2000 Ming-Che Chuang \break Copyright (C) 2002 James Su, Turbolinux Inc. \break Copyright (C) 2002 Anthony Fok, ThizLinux Laboratory Ltd. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: \list 1 \li Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. \li Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. \endlist THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \endquotation \section3 Big5-HKSCS Text Codec This license applies to parts of some files in \c{qtbase/src/corelib/codecs/}. \quotation Copyright (C) 2000 Ming-Che Chuang \break Copyright (C) 2001, 2002 James Su, Turbolinux Inc. \break Copyright (C) 2002 WU Yi, HancomLinux Inc. \break Copyright (C) 2001, 2002 Anthony Fok, ThizLinux Laboratory Ltd. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: \list 1 \li Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. \li Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. \endlist THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \endquotation \section3 brg_endian.h This license applies to \c{qtbase/src/3rdparty/sha3/brg_endian.h}. \quotation Copyright (c) 1998-2008, Brian Gladman, Worcester, UK. All rights reserved. LICENSE TERMS The redistribution and use of this software (with or without changes) is allowed without the payment of fees or royalties provided that: \list 1 \li source code distributions include the above copyright notice, this list of conditions and the following disclaimer; \li binary distributions include the above copyright notice, this list of conditions and the following disclaimer in their documentation; \li the name of the copyright holder is not used to endorse products built using this software without specific written permission. \endlist DISCLAIMER This software is provided 'as is' with no explicit or implied warranties in respect of its properties, including, but not limited to, correctness and/or fitness for purpose. \endquotation \section3 Clucene Core Library This license applies to files in \c{qttools/src/assistant/3rdparty/clucene}. \quotation Copyright (C) 2003-2006 Ben van Klinken and the CLucene Team The CLucene Core Library uses a dual license strategy for the source code. These licenses are the GNU Lesser General Public License (LGPL) and the Apache License (Version 2.0). Users can choose the license they wish to distribute their software under. This means that you do not need to abide by *both* licenses, but rather than you can choose the license which most suits your needs. To rephrase this and to make it perfectly clear: \break CLucene is distributed under the \l{GNU Lesser General Public License} (LGPL) \break *or* \break the \l{Apache License}, Version 2.0 However, we are an open source project, and we encourage users to use the LGPL license and participate fully in the free software community. Dual licensing of the CLucene source code provides open and free access to the technology both for the GPL community and for other developers or companies that cannot use the GPL. You can freely modify, extend, and improve the CLucene source code. The only question is whether or not you must provide the source code and contribute modifications to the community. The GNU and Apache licenses allow different ranges of flexibility in this regard, but in the end, regardless of the license used, we highly recommend that you submit any bugs, incompatibilities or added features. Note that this same license does *not* apply to the CLucene Contributions package. You should read the COPYING file in that directory or package for more information. \endquotation \section3 Cocoa Platform Plugin This license applies to parts of some files in \c{qtbase/src/plugins/platforms/cocoa/}. \quotation Copyright (C) 2007-2008, Apple, Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: \list \li Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. \li Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. \li Neither the name of Apple, Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. \endlist THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \endquotation \section3 Drag and Drop Copyright 1996 Daniel Dardailler. Permission to use, copy, modify, distribute, and sell this software for any purpose is hereby granted without fee, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of Daniel Dardailler not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. Daniel Dardailler makes no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. Modifications Copyright 1999 Matt Koss, under the same license as above. \section3 Easing Equations This license applies to \c{qtbase/src/3rdparty/easing/easing.cpp}. \quotation Copyright \copyright 2001 Robert Penner \break All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: \list \li Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. \li Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. \li Neither the name of the author nor the names of contributors may be used to endorse or promote products derived from this software without specific prior written permission. \endlist THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \endquotation \section3 EUC-JP Text Codec This license applies to parts of some files in \c{qtbase/src/corelib/codecs/}. \quotation Copyright (C) 1999 Serika Kurusugawa. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: \list 1 \li Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. \li Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. \endlist THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS". ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \endquotation \section3 EUC-KR Text Codec This license applies to parts of some files in \c{qtbase/src/corelib/codecs/}. \quotation Copyright (C) 1999-2000 Mizi Research Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: \list 1 \li Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. \li Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. \endlist THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \endquotation \section3 forkfd This license applies to files in \c{qtbase/src/3rdparty/forkfd/}. \quotation Copyright (C) 2014 Intel Corporation \break Copyright (C) 2015 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \endquotation \section3 FreeBSD strtoll and strtoull This license applies to files in \c{qtbase/src/3rdparty/freebsd/}. \quotation Copyright (c) 1992, 1993 \break The Regents of the University of California. All rights reserved. Copyright (c) 2011 The FreeBSD Foundation \break All rights reserved. \break Portions of this software were developed by David Chisnall under sponsorship from the FreeBSD Foundation. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: \list 1 \li Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. \li Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. \li Neither the name of the University nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. \endlist THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \endquotation \section3 GBK Text Codec This license applies to parts of some files in \c{qtbase/src/corelib/codecs/}. \quotation Copyright (C) 2000 TurboLinux, Inc. Written by Justin Yu and Sean Chen. \break Copyright (C) 2001, 2002 Turbolinux, Inc. Written by James Su. \break Copyright (C) 2001, 2002 ThizLinux Laboratory Ltd. Written by Anthony Fok. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: \list 1 \li Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. \li Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. \endlist THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \endquotation \section3 HarfBuzz This license applies to files in \c{qtbase/src/3rdparty/harfbuzz} and \c{qtbase/src/3rdparty/harfbuzz-ng}. \quotation Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies) \break Copyright \copyright 2010,2011,2012 Google, Inc. \break Copyright \copyright 2012 Mozilla Foundation \break Copyright \copyright 2011 Codethink Limited \break Copyright \copyright 2008,2010 Nokia Corporation and/or its subsidiary(-ies) \break Copyright \copyright 2009 Keith Stribley \break Copyright \copyright 2009 Martin Hosken and SIL International \break Copyright \copyright 2007 Chris Wilson \break Copyright \copyright 2006 Behdad Esfahbod \break Copyright \copyright 2005 David Turner \break Copyright \copyright 2004,2007,2008,2009,2010 Red Hat, Inc. \break Copyright \copyright 1998-2004 David Turner and Werner Lemberg Permission is hereby granted, without written agreement and without license or royalty fees, to use, copy, modify, and distribute this software and its documentation for any purpose, provided that the above copyright notice and the following two paragraphs appear in all copies of this software. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. \endquotation \section3 IAccessible2 IDL Specification This license applies to files in \c{qtbase/src/3rdparty/iaccessible2/}. \quotation Copyright (c) 2007, 2010 Linux Foundation \break Copyright (c) 2006 IBM Corporation \break Copyright (c) 2000, 2006 Sun Microsystems, Inc. \break All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: \list 1 \li Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. \li Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. \li Neither the name of the Linux Foundation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. \endlist THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \endquotation \section3 ISO 2022-JP (JIS) Text Codec This license applies to parts of some files in \c{qtbase/src/corelib/codecs/}. \quotation Copyright (C) 1999 Serika Kurusugawa. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: \list 1 \li Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. \li Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. \endlist THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS". ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \endquotation \section3 JasPer library This license applies to files in \c{qtimageformats/src/3rdparty/jasper/}. \quotation JasPer License Version 2.0 Copyright (c) 2001-2006 Michael David Adams \break Copyright (c) 1999-2000 Image Power, Inc. \break Copyright (c) 1999-2000 The University of British Columbia All rights reserved. Permission is hereby granted, free of charge, to any person (the "User") obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: \list 1 \li The above copyright notices and this permission notice (which includes the disclaimer below) shall be included in all copies or substantial portions of the Software. \li The name of a copyright holder shall not be used to endorse or promote products derived from the Software without specific prior written permission. \endlist THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF THE SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER. THE SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. NO ASSURANCES ARE PROVIDED BY THE COPYRIGHT HOLDERS THAT THE SOFTWARE DOES NOT INFRINGE THE PATENT OR OTHER INTELLECTUAL PROPERTY RIGHTS OF ANY OTHER ENTITY. EACH COPYRIGHT HOLDER DISCLAIMS ANY LIABILITY TO THE USER FOR CLAIMS BROUGHT BY ANY OTHER ENTITY BASED ON INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS OR OTHERWISE. AS A CONDITION TO EXERCISING THE RIGHTS GRANTED HEREUNDER, EACH USER HEREBY ASSUMES SOLE RESPONSIBILITY TO SECURE ANY OTHER INTELLECTUAL PROPERTY RIGHTS NEEDED, IF ANY. THE SOFTWARE IS NOT FAULT-TOLERANT AND IS NOT INTENDED FOR USE IN MISSION-CRITICAL SYSTEMS, SUCH AS THOSE USED IN THE OPERATION OF NUCLEAR FACILITIES, AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL SYSTEMS, DIRECT LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE SOFTWARE OR SYSTEM COULD LEAD DIRECTLY TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE ("HIGH RISK ACTIVITIES"). THE COPYRIGHT HOLDERS SPECIFICALLY DISCLAIM ANY EXPRESS OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES. \endquotation \section3 LibTIFF library This license applies to most files in \c{qtimageformats/src/3rdparty/libtiff/}. \quotation Copyright (c) 1988-1997 Sam Leffler \break Copyright (c) 1991-1997 Silicon Graphics, Inc. \break Copyright (C) 2005, Andrey Kiselev \break Copyright (c) 2009 Frank Warmerdam \break Copyright (c) 2010, Andrey Kiselev Permission to use, copy, modify, distribute, and sell this software and its documentation for any purpose is hereby granted without fee, provided that (i) the above copyright notices and this permission notice appear in all copies of the software and related documentation, and (ii) the names of Sam Leffler and Silicon Graphics may not be used in any advertising or publicity relating to the software without the specific, prior written permission of Sam Leffler and Silicon Graphics. THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. \endquotation This license applies to some files in \c{qtimageformats/src/3rdparty/libtiff/}. \quotation Copyright (c) 1996-1997 Sam Leffler \break Copyright (c) 1996 Pixar Permission to use, copy, modify, distribute, and sell this software and its documentation for any purpose is hereby granted without fee, provided that (i) the above copyright notices and this permission notice appear in all copies of the software and related documentation, and (ii) the names of Pixar, Sam Leffler and Silicon Graphics may not be used in any advertising or publicity relating to the software without the specific, prior written permission of Pixar, Sam Leffler and Silicon Graphics. THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL PIXAR, SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. \endquotation This license applies to some files in \c{qtimageformats/src/3rdparty/libtiff/}. \quotation Copyright (c) 1997 Greg Ward Larson \break Copyright (c) 1997 Silicon Graphics, Inc. Permission to use, copy, modify, distribute, and sell this software and its documentation for any purpose is hereby granted without fee, provided that (i) the above copyright notices and this permission notice appear in all copies of the software and related documentation, and (ii) the names of Sam Leffler, Greg Larson and Silicon Graphics may not be used in any advertising or publicity relating to the software without the specific, prior written permission of Sam Leffler, Greg Larson and Silicon Graphics. THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SAM LEFFLER, GREG LARSON OR SILICON GRAPHICS BE LIABLE FOR ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. \endquotation This license applies to some files in \c{qtimageformats/src/3rdparty/libtiff/port}. \quotation Copyright (c) 1987, 1993 The Regents of the University of California. All rights reserved. \break Copyright (c) 1987, 1993, 1994 The Regents of the University of California. All rights reserved. \break Copyright (c) 1989, 1993 The Regents of the University of California. All rights reserved. \break Copyright (c) 1990, 1993 The Regents of the University of California. All rights reserved. \break Copyright (c) 1992, 1993 The Regents of the University of California. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: \list 1 \li Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. \li Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. \li Neither the name of the University nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. \endlist THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \endquotation \section3 Native Style for Android This license applies to \c{qtbase/src/3rdparty/android/extract}. \quotation Copyright (C) 2005 The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. \endquotation \section3 PCRE library This licence applies to files in \c{qtbase/src/3rdparty/pcre/} (the basic library functions) and \c{qtbase/src/3rdparty/pcre/sljit/} (stack-less just-in-time compiler). \quotation \code THE BASIC LIBRARY FUNCTIONS --------------------------- Written by: Philip Hazel Email local part: ph10 Email domain: cam.ac.uk University of Cambridge Computing Service, Cambridge, England. Copyright (c) 1997-2015 University of Cambridge All rights reserved. PCRE JUST-IN-TIME COMPILATION SUPPORT ------------------------------------- Written by: Zoltan Herczeg Email local part: hzmester Emain domain: freemail.hu Copyright(c) 2010-2015 Zoltan Herczeg All rights reserved. STACK-LESS JUST-IN-TIME COMPILER -------------------------------- Written by: Zoltan Herczeg Email local part: hzmester Emain domain: freemail.hu Copyright(c) 2009-2015 Zoltan Herczeg All rights reserved. THE C++ WRAPPER FUNCTIONS ------------------------- Contributed by: Google Inc. Copyright (c) 2007-2012, Google Inc. All rights reserved. \endcode Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: \list \li Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. \li Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. \li Neither the name of the University of Cambridge nor the name of Google Inc. nor the names of their contributors may be used to endorse or promote products derived from this software without specific prior written permission. \endlist THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \endquotation \section3 pixman This license applies to the files in \c{qtbase\src\3rdparty\pixman\}. \quotation Copyright \copyright 2009 Nokia Corporation Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \endquotation \section3 Poly2Tri This license applies to files in \c{qtlocation/src/3rdparty/poly2tri/}. \quotation \span {} {Poly}2Tri Copyright (c) 2009-2010, \span {} {Poly}2Tri Contributors \break \l http://code.google.com/p/poly2tri/ All rights reserved. \break Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: \list \li Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. \li Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. \li Neither the name of \span {} {Poly}2Tri nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. \endlist THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \endquotation \section3 QCrashHandler class This license applies to parts of \c{qtbase/src/corelib/kernel/qcrashhandler.cpp}. \quotation Copyright (c) 1998 by Bjorn Reese Permission to use, copy, modify, and distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE AUTHORS AND CONTRIBUTORS ACCEPT NO RESPONSIBILITY IN ANY CONCEIVABLE MANNER. \endquotation \section3 QImage smooth scaling For smooth scaling, the \c{QImage::transformed()} functions use code based on smooth scaling algorithm by Daniel M. Duley. \quotation Copyright (C) 2004, 2005 Daniel M. Duley Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: \list 1 \li Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. \li Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. \endlist THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \endquotation \section3 QKeyMapper class on X11 platforms This license applies to parts of the internal QKeyMapper class on X11 platforms. \quotation Copyright 1985, 1987, 1998 The Open Group Permission to use, copy, modify, distribute, and sell this software and its documentation for any purpose is hereby granted without fee, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation. The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of The Open Group shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from The Open Group. \endquotation \section3 QtCLucene Library This license applies to files in \c{qttools/src/assistant/clucene/}. \quotation Copyright (C) 2003-2006 Ben van Klinken and the CLucene Team. \break All rights reserved. Portion Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). \break All rights reserved. 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: \break \l{http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html}. \endquotation \section3 qtmain Library This license applies to files in \c{qtbase/src/winmain/}. \quotation Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). \break Contact: \l http://www.qt-project.org/legal Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: \list \li Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. \li Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. \li Neither the name of Digia Plc and its Subsidiary(-ies) nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. \endlist THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \endquotation \section3 QUrl::fromUserInput This license applies to parts of \c{qtbase/src/corelib/io/qurl.cpp}. \quotation Copyright (C) Research In Motion Limited 2009. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: \list \li Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. \li Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. \li Neither the name of Research In Motion Limited nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. \endlist THIS SOFTWARE IS PROVIDED BY Research In Motion Limited ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Research In Motion Limited BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \endquotation \section3 Secure Hash Algorithms (SHA) This license applies to files in \c{qtbase/src/3rdparty/rfc6234/}. \quotation Copyright (c) 2011 IETF Trust and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: \list \li Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. \li Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. \li Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. \endlist THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \endquotation \section3 Shift-JIS Text Codec This license applies to parts of some files in \c{qtbase/src/corelib/codecs/}. \quotation Copyright (C) 1999 Serika Kurusugawa. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: \list 1 \li Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. \li Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. \endlist THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS". ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \endquotation \section3 TSCII Text Codec This license applies to parts of some files in \c{qtbase/src/corelib/codecs/}. \quotation Copyright (c) 2000 Hans Petter Bieker. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: \list 1 \li Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. \li Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. \endlist THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \endquotation \section3 Unicode Character Database Data Files This license applies to files in \c{qtbase/util/unicode/data/}. \quotation Copyright \copyright 1991-2014 Unicode, Inc. All rights reserved. Distributed under the Terms of Use in http://www.unicode.org/copyright.html. Permission is hereby granted, free of charge, to any person obtaining a copy of the Unicode data files and any associated documentation (the "Data Files") or Unicode software and any associated documentation (the "Software") to deal in the Data Files or Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, and/or sell copies of the Data Files or Software, and to permit persons to whom the Data Files or Software are furnished to do so, provided that (a) the above copyright notice(s) and this permission notice appear with all copies of the Data Files or Software, (b) both the above copyright notice(s) and this permission notice appear in associated documentation, and (c) there is clear notice in each modified Data File or in the Software as well as in the documentation associated with the Data File(s) or Software that the data or software has been modified. THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THE DATA FILES OR SOFTWARE. Except as contained in this notice, the name of a copyright holder shall not be used in advertising or otherwise to promote the sale, use or other dealings in these Data Files or Software without prior written authorization of the copyright holder. \endquotation \section3 WebP codec This license applies to files in \c{qtimageformats/src/3rdparty/libwebp/}. \quotation Copyright (c) 2010, Google Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: \list \li Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. \li Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. \li Neither the name of Google nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. \endlist THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \endquotation \section3 xcb This license applies to most files in \c{qtbase/src/3rdparty/xcb/}. \quotation Copyright \copyright 2008-2009 Julien Danjou \break Copyright \copyright 2008 Bart Massey Copyright \copyright 2008 Ian Osgood Copyright \copyright 2008 Jamey Sharp Copyright \copyright 2008 Josh Triplett Copyright \copyright 2008 Ulrich Eckhardt Copyright \copyright 2008 Arnaud Fontaine Copyright \copyright 2007-2008 Vincent Torri Copyright \copyright 2007 Bart Massey Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the names of the authors or their institutions shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from the authors. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: \endquotation This license applies to \c{qtbase/src/3rdparty/xcb/xcb-util-renderutil/util.c}. \quotation Copyright \copyright 2000 Keith Packard Permission to use, copy, modify, distribute, and sell this software and its documentation for any purpose is hereby granted without fee, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of Keith Packard not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. Keith Packard makes no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. \endquotation \section3 xkb The following is a list of copyright notices and license statements in \c{qtbase/src/3rdparty/xkbcommon}. \quotation Copyright \copyright 2009-2012 Daniel Stone \break Copyright \copyright 2012 Ran Benita \break Copyright \copyright 2010, 2012 Intel Corporation \break Copyright \copyright 2008, 2009 Dan Nicholson \break Copyright \copyright 2010 Francisco Jerez Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \hr Copyright 1985, 1987, 1988, 1990, 1998 The Open Group Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the names of the authors or their institutions shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from the authors. \hr Copyright (c) 1993, 1994, 1995, 1996 by Silicon Graphics Computer Systems, Inc. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of Silicon Graphics not be used in advertising or publicity pertaining to distribution of the software without specific prior written permission. Silicon Graphics makes no representation about the suitability of this software for any purpose. It is provided "as is" without any express or implied warranty. SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. \hr Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts. All Rights Reserved Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of Digital not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. \hr Copyright (C) 2011 Joseph Adams Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \endquotation \section3 Additional Credits and Acknowledgements This software is based in part on the work of the Independent JPEG Group. Portions of this software are copyright \copyright 2012 The FreeType Project (\l{http://www.freetype.org}). All rights reserved. We are very grateful to the following projects which are used in or distributed alongside this software, and to the people behind these projects: \list \li The \l{http://zlib.net/ zlib} Data Compression Library (Jean-loup Gailly, Mark Adler) \li The \l{http://www.libpng.org/pub/png/ libpng} PNG reference library (Glenn Randers-Pehrson et al.) \li The \l{http://www.sqlite.org/ sqlite} database engine \endlist mapper-0.8.1.1/doc/licensing/src/trademarks.qdocinc000066400000000000000000000021241325266516600221640ustar00rootroot00000000000000\omit /* * Copyright 2014, 2015 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ \endomit Qt and the Qt logo are trademarks of The Qt Company Ltd. Digia and the Digia logo are trademarks of Digia Plc and/or its subsidiaries in Finland and other countries. All other company, product, or service names may be trademarks or service marks of others and are the property of their respective owners. mapper-0.8.1.1/doc/licensing/superbuild-licensing.cmake000066400000000000000000000042171325266516600230340ustar00rootroot00000000000000# # Copyright 2017 Kai Pastor # # This file is part of OpenOrienteering. # # OpenOrienteering is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # OpenOrienteering is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with OpenOrienteering. If not, see . find_file(LICENSING_COPYRIGHT_DIR NAMES copyright PATH_SUFFIXES share/doc DOC "Path to directory with 3rd-party component copyright files." ) find_file(LICENSING_COMMON_DIR NAMES common-licenses PATHS "${LICENSING_COPYRIGHT_DIR}" NO_DEFAULT_PATH DOC "Path to directory with common license files." ) if(NOT LICENSING_COPYRIGHT_DIR OR NOT LICENSING_COMMON_DIR) message(STATUS "LICENSING_COPYRIGHT_DIR: ${LICENSING_COPYRIGHT_DIR}") message(STATUS "LICENSING_COMMON_DIR: ${LICENSING_COMMON_DIR}") message(FATAL_ERROR "Both LICENSING_COPYRIGHT_DIR and LICENSING_COMMON_DIR must be set") endif() # Based on OpenOrienteering superbuild as of 2017-04 list(APPEND third_party_components libjpeg-turbo liblzma libpcre3 libpng libtiff ) if(NOT APPLE) list(APPEND third_party_components libcurl libexpat libsqlite ) endif() if(NOT ANDROID AND NOT APPLE) list(APPEND third_party_components zlib ) endif() if(ANDROID OR MINGW) list(APPEND third_party_components gnustl ) endif() list(APPEND common_license_names Apache-2.0 Artistic BSD GFDL-1.3 GPL-1 GPL-2 LGPL-2 LGPL-2.1 LGPL-3 ) # Map component names to package names set(package_names libcurl:curl libexpat:expat libpcre3:pcre3 libsqlite:sqlite3 libtiff:tiff ) set(copyright_pattern "${LICENSING_COPYRIGHT_DIR}/@package@*.txt") set(common_license_pattern "${LICENSING_COMMON_DIR}/@basename@.txt") mapper-0.8.1.1/doc/licensing/ubuntu-licensing.cmake000066400000000000000000000000741325266516600221750ustar00rootroot00000000000000include("${CMAKE_CURRENT_LIST_DIR}/debian-licensing.cmake") mapper-0.8.1.1/doc/man/000077500000000000000000000000001325266516600144775ustar00rootroot00000000000000mapper-0.8.1.1/doc/man/Mapper.1000066400000000000000000000032001325266516600160000ustar00rootroot00000000000000.TH MAPPER 1 2017-05-12 OpenOrienteering .SH NAME Mapper \- orienteering map drawing software .SH SYNOPSIS .B Mapper .RI [ FILES ] .SH DESCRIPTION OpenOrienteering Mapper is an orienteering map drawing software. .PP It comes with predefined symbol sets implementing the IOF standards: .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} ISOM2000 (forest) (1:15000, 1:10000) .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} ISOM2017 (forest) (1:15000, 1:10000) .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} ISSOM2007 (sprint) (1:5000, 1:4000) .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} ISMTBOM2010 (mountain bike) (1:20000, 1:15000, 1:10000, 1:7500, 1:5000) .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} ISSkiOM2014 (ski) (1:15000, 1:10000, 1:5000) .RE .PP But it is easy to implement other symbol sets. .SH OPTIONS This program takes map file names as options. .SH AUTHOR This manual page was written by Kai Pastor . .SH "REPORTING BUGS" Report bugs in the OpenOrienteering ticket system: .br .SH COPYRIGHT Copyright \(co 2017 The OpenOrienteering developers .br License GPLv3: GNU GPL version 3 . .br This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. .SH "SEE ALSO" Extended documentation for .B Mapper is available from the program's help menu and also online at: .br mapper-0.8.1.1/doc/manual/000077500000000000000000000000001325266516600152015ustar00rootroot00000000000000mapper-0.8.1.1/doc/manual/CMakeLists.txt000066400000000000000000000210011325266516600177330ustar00rootroot00000000000000# # Copyright 2012, 2013, 2014 Thomas Schöps # Copyright 2012-2017 Kai Pastor # # This file is part of OpenOrienteering. # # OpenOrienteering is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # OpenOrienteering is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with OpenOrienteering. If not, see . message(STATUS "Configuring ${PROJECT_NAME} user manual") set(Mapper_MANUAL_QTHELP_default ON) if(ANDROID) set(Mapper_MANUAL_QTHELP_default OFF) endif() option(Mapper_MANUAL_QTHELP "Provide the manual as Qt Help collection" ${Mapper_MANUAL_QTHELP_default} ) set(Mapper_MANUAL_PDF_default OFF) option(Mapper_MANUAL_PDF "Provide the manual as PDF file" ${Mapper_MANUAL_PDF_default} ) # # General target # add_custom_target(Mapper-manual ALL) # # Creating doxygen input for HTML generation # configure_file(preprocess-markdown-html.cmake.in preprocess-markdown-html.cmake @ONLY) add_custom_target(Mapper-manual-markdown-html COMMAND "${CMAKE_COMMAND}" -P preprocess-markdown-html.cmake BYPRODUCTS preprocess-markdown-html.stamp SOURCES preprocess-markdown-html.cmake.in COMMENT "Preprocessing Markdown for HTML output" ) # # Doxygen HTML generation; creating input for optional Qt Help generation # find_program(DOXYGEN_EXECUTABLE NAMES doxygen DOC "The path of the doxygen executable" ) if(NOT DOXYGEN_EXECUTABLE) message(FATAL_ERROR "doxygen executable not found.") endif() set(Mapper_COMPRESSED_HELP "Mapper ${Mapper_VERSION_DISPLAY} Manual.qch") set(Mapper_HELP_COLLECTION "Mapper ${Mapper_VERSION_DISPLAY} Manual.qhc") set(Mapper_HELP_NAMESPACE "openorienteering.mapper-${Mapper_VERSION_MAJOR}.${Mapper_VERSION_MINOR}.${Mapper_VERSION_PATCH}.help" CACHE INTERNAL "The namespace where the current version's help will be located." ) if(NOT MANUAL_SECTIONS) if(ANDROID) set(MANUAL_SECTIONS_DEFAULT ANDROID) elseif(APPLE) set(MANUAL_SECTIONS_DEFAULT MACOS) elseif(UNIX) set(MANUAL_SECTIONS_DEFAULT LINUX) elseif(WIN32) set(MANUAL_SECTIONS_DEFAULT WINDOWS) else() set(MANUAL_SECTIONS_DEFAULT OTHER) endif() set(MANUAL_SECTIONS "${MANUAL_SECTIONS_DEFAULT}" CACHE STRING "Conditional manual sections to be enabled") endif() message(STATUS "Conditional manual sections: ${MANUAL_SECTIONS}") set(DOXYFILE_HTML_EXTRA ) if(ANDROID) set(DOXYFILE_HTML_EXTRA " FILE_PATTERNS = android* toolbar* USE_MDFILE_AS_MAINPAGE = android-app.md ") endif() configure_file(Doxyfile-html.in Doxyfile-html @ONLY) configure_file(postprocess-qhp.cmake.in postprocess-qhp.cmake @ONLY) add_custom_command( OUTPUT "html/index.qhp" COMMAND "${CMAKE_COMMAND}" -E remove_directory html COMMAND "${DOXYGEN_EXECUTABLE}" Doxyfile-html COMMAND "${CMAKE_COMMAND}" -P postprocess-qhp.cmake DEPENDS preprocess-markdown-html.stamp "${CMAKE_CURRENT_BINARY_DIR}/Doxyfile-html" "${CMAKE_CURRENT_BINARY_DIR}/postprocess-qhp.cmake" "${DOXYGEN_EXECUTABLE}" header.html footer.html style.css COMMENT "Running doxygen for HTML output" ) add_custom_target(Mapper-manual-HTML DEPENDS "html/index.qhp" # Sources to be listed in Qt Creator SOURCES Doxyfile-html.in header.html footer.html install-html.cmake.in postprocess-qhp.cmake.in style.css ) add_dependencies(Mapper-manual-HTML Mapper-manual-markdown-html) if(Mapper_MANUAL_QTHELP) # # Qt Help generation # # Qt provides a broken Qt5::qcollectiongenerator when crosscompiling. if (CMAKE_CROSSCOMPILING AND NOT TARGET Qt5::qcollectiongenerator) find_program(Qt5Help_QCOLLECTIONGENERATOR_EXECUTABLE NAMES qcollectiongenerator-qt5 qcollectiongenerator DOC "The path of the Qt Assistant executable" ) add_executable(Qt5::qcollectiongenerator IMPORTED) set_target_properties(Qt5::qcollectiongenerator PROPERTIES IMPORTED_LOCATION ${Qt5Help_QCOLLECTIONGENERATOR_EXECUTABLE} ) elseif(NOT Qt5Help_QCOLLECTIONGENERATOR_EXECUTABLE) find_package(Qt5Help REQUIRED) set(Qt5Help_QCOLLECTIONGENERATOR_EXECUTABLE Qt5::qcollectiongenerator) endif() # Reproducible builds need a modifications to the help collection file. set(source_date_commands ) find_program(SQLITE3_EXECUTABLE NAMES sqlite3 DOC "The path of the sqlite3 executable" ) if (DEFINED ENV{SOURCE_DATE_EPOCH} AND SQLITE3_EXECUTABLE) file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/timestamp.sql" "update 'SettingsTable' set Value='$ENV{SOURCE_DATE_EPOCH}' where Key='CreationTime';\n" "delete from 'SettingsTable' where Key='LastRegisterTime';\n" ) set(source_date_commands COMMAND ${SQLITE3_EXECUTABLE} "${Mapper_HELP_COLLECTION}" < "timestamp.sql" ) elseif(DEFINED ENV{SOURCE_DATE_EPOCH}) message(FATAL_ERROR "By setting SOURCE_DATE_EPOCH, a reproducible build was requested. " "sqlite3 is needed for reproducible builds but cannot be found." ) endif() configure_file(Manual.qhcp.in Manual.qhcp @ONLY) add_custom_command( OUTPUT "${Mapper_HELP_COLLECTION}" "${Mapper_COMPRESSED_HELP}" COMMAND "${Qt5Help_QCOLLECTIONGENERATOR_EXECUTABLE}" Manual.qhcp -o "${Mapper_HELP_COLLECTION}" ${source_date_commands} MAIN_DEPENDENCY "html/index.qhp" DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/Manual.qhcp" WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" COMMENT "Running qcollectiongenerator for Qt Help output" ) add_custom_target(Mapper-manual-Qt-Help DEPENDS "${Mapper_HELP_COLLECTION}" SOURCES Manual.qhcp.in ) add_dependencies(Mapper-manual-Qt-Help Mapper-manual-HTML) add_dependencies(Mapper-manual Mapper-manual-Qt-Help) install( FILES "${CMAKE_CURRENT_BINARY_DIR}/${Mapper_HELP_COLLECTION}" "${CMAKE_CURRENT_BINARY_DIR}/${Mapper_COMPRESSED_HELP}" DESTINATION "${MAPPER_ABOUT_DESTINATION}" ) else() # # Direct installation of manual HTML files # add_dependencies(Mapper-manual Mapper-manual-HTML) configure_file(install-html.cmake.in install-html.cmake @ONLY) install(SCRIPT "${CMAKE_CURRENT_BINARY_DIR}/install-html.cmake") endif() if(Mapper_MANUAL_PDF) # # PDF file generation # find_program(PDFLATEX_EXECUTABLE NAMES pdflatex DOC "The path of the pdflatex executable" ) if(PDFLATEX_EXECUTABLE) find_program(MAKE_EXECUTABLE NAMES make DOC "The path of the make executable" ) if(NOT MAKE_EXECUTABLE AND CMAKE_HOST_WIN32) set(MAKE_EXECUTABLE make.bat) endif() endif() set(Mapper_PDF_manual "Mapper ${Mapper_VERSION_DISPLAY} Manual.pdf") string(REPLACE "-" "_" Mapper_VERSION_DISPLAY_PDFLATEX "${Mapper_VERSION_DISPLAY}") configure_file(Doxyfile-pdflatex.in Doxyfile-pdflatex @ONLY) configure_file(preprocess-markdown-pdflatex.cmake.in preprocess-markdown-pdflatex.cmake @ONLY) configure_file(postprocess-pdflatex.cmake.in postprocess-pdflatex.cmake @ONLY) add_custom_target(Mapper-manual-markdown-pdflatex COMMAND "${CMAKE_COMMAND}" -P preprocess-markdown-pdflatex.cmake BYPRODUCTS preprocess-markdown-pdflatex.stamp Doxyfile-pdflatex-input.txt SOURCES preprocess-markdown-pdflatex.cmake.in ) add_custom_command( OUTPUT "${Mapper_PDF_manual}" COMMAND "${DOXYGEN_EXECUTABLE}" Doxyfile-pdflatex COMMAND "${CMAKE_COMMAND}" -P postprocess-pdflatex.cmake COMMAND "${MAKE_EXECUTABLE}" -C pdflatex COMMAND "${CMAKE_COMMAND}" -E copy_if_different "pdflatex/refman.pdf" "${Mapper_PDF_manual}" COMMAND "${CMAKE_COMMAND}" -E remove "pdflatex/refman.pdf" MAIN_DEPENDENCY preprocess-markdown-pdflatex.stamp DEPENDS Doxyfile-pdflatex Doxyfile-pdflatex-input.txt postprocess-pdflatex.cmake WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" COMMENT "Running pdflatex for PDF output" ) add_custom_target(Mapper-manual-PDF DEPENDS "${Mapper_PDF_manual}" SOURCES Doxyfile-pdflatex.in postprocess-pdflatex.cmake.in ) add_dependencies(Mapper-manual-PDF Mapper-manual-markdown-pdflatex) add_dependencies(Mapper-manual Mapper-manual-PDF) install( FILES "${CMAKE_CURRENT_BINARY_DIR}/${Mapper_PDF_manual}" DESTINATION "${MAPPER_ABOUT_DESTINATION}" ) endif() mapper-0.8.1.1/doc/manual/Doxyfile-html.in000066400000000000000000000041061325266516600202570ustar00rootroot00000000000000# # Copyright 2014, 2015, 2017 Kai Pastor # # This file is part of OpenOrienteering. # # OpenOrienteering is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # OpenOrienteering is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with OpenOrienteering. If not, see . PROJECT_NAME = "Mapper" PROJECT_NUMBER = "@Mapper_VERSION_DISPLAY@" PROJECT_BRIEF = "User Manual" PROJECT_LOGO = "@CMAKE_CURRENT_SOURCE_DIR@/../openorienteering.png" QUIET = YES INPUT = "@CMAKE_CURRENT_BINARY_DIR@/markdown-html" STRIP_FROM_PATH = "@CMAKE_CURRENT_BINARY_DIR@/markdown-html" IMAGE_PATH = "@CMAKE_CURRENT_SOURCE_DIR@/pages/images" IMAGE_PATH += "@CMAKE_CURRENT_SOURCE_DIR@/../../images" ENABLE_PREPROCESSING = NO USE_MATHJAX = NO GENERATE_HTML = YES DISABLE_INDEX = YES GENERATE_TREEVIEW = NO HTML_TIMESTAMP = NO SEARCHENGINE = NO HTML_HEADER = "@CMAKE_CURRENT_SOURCE_DIR@/header.html" HTML_FOOTER = "@CMAKE_CURRENT_SOURCE_DIR@/footer.html" HTML_STYLESHEET = "@CMAKE_CURRENT_SOURCE_DIR@/style.css" HTML_EXTRA_FILES = "@CMAKE_CURRENT_SOURCE_DIR@/pages/attachment/scribble_1024.png" HTML_EXTRA_FILES += "@CMAKE_CURRENT_SOURCE_DIR@/pages/attachment/scribble_2048.png" GENERATE_QHP = YES QCH_FILE = "../@Mapper_COMPRESSED_HELP@" QHP_NAMESPACE = "@Mapper_HELP_NAMESPACE@" QHP_VIRTUAL_FOLDER = manual GENERATE_LATEX = NO ENABLED_SECTIONS = @MANUAL_SECTIONS@ @DOXYFILE_HTML_EXTRA@ mapper-0.8.1.1/doc/manual/Doxyfile-pdflatex.in000066400000000000000000000032251325266516600211230ustar00rootroot00000000000000# # Copyright 2014, 2015, 2017 Kai Pastor # # This file is part of OpenOrienteering. # # OpenOrienteering is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # OpenOrienteering is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with OpenOrienteering. If not, see . PROJECT_NAME = "OpenOrienteering Mapper User Manual" PROJECT_NUMBER = "@Mapper_VERSION_DISPLAY_PDFLATEX@" PROJECT_BRIEF = "User Manual" PROJECT_LOGO = "@CMAKE_CURRENT_SOURCE_DIR@/../openorienteering.png" QUIET = YES @INCLUDE = "@CMAKE_CURRENT_BINARY_DIR@/Doxyfile-pdflatex-input.txt" STRIP_FROM_PATH = "@CMAKE_CURRENT_BINARY_DIR@/markdown-pdflatex" IMAGE_PATH = "@CMAKE_CURRENT_SOURCE_DIR@/pages/images" IMAGE_PATH += "@CMAKE_CURRENT_SOURCE_DIR@/../../images" ENABLE_PREPROCESSING = NO USE_MATHJAX = NO GENERATE_HTML = NO GENERATE_QHP = NO GENERATE_LATEX = YES LATEX_OUTPUT = pdflatex COMPACT_LATEX = NO PAPER_TYPE = a4 PDF_HYPERLINKS = YES USE_PDFLATEX = YES LATEX_BATCHMODE = NO LATEX_TIMESTAMP = NO mapper-0.8.1.1/doc/manual/Manual.qhcp.in000066400000000000000000000010661325266516600177030ustar00rootroot00000000000000 @CMAKE_CURRENT_SOURCE_DIR@/../../images/mapper-help.png OpenOrienteering Mapper Help qthelp://@Mapper_HELP_NAMESPACE@/manual/index.html html/index.qhp @Mapper_COMPRESSED_HELP@ @Mapper_COMPRESSED_HELP@ mapper-0.8.1.1/doc/manual/footer.html000066400000000000000000000001531325266516600173640ustar00rootroot00000000000000
    mapper-0.8.1.1/doc/manual/header.html000066400000000000000000000011731325266516600173210ustar00rootroot00000000000000 OpenOrienteering $projectname: $title
    OpenOrienteering $projectname $projectnumber
    mapper-0.8.1.1/doc/manual/install-html.cmake.in000066400000000000000000000022461325266516600212240ustar00rootroot00000000000000# # Copyright 2017 Kai Pastor # # This file is part of OpenOrienteering. # # OpenOrienteering is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # OpenOrienteering is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with OpenOrienteering. If not, see . file(READ "@CMAKE_CURRENT_BINARY_DIR@/html/index.qhp" index_qhp) string(REGEX MATCHALL "[^<]*" matches "${index_qhp}") foreach(match ${matches}) string(REGEX REPLACE ".*" "" match "${match}") string(REGEX REPLACE ".*" "" match "${match}") file(INSTALL DESTINATION "${CMAKE_INSTALL_PREFIX}/@MAPPER_ABOUT_DESTINATION@/manual" FILES "@CMAKE_CURRENT_BINARY_DIR@/html/${match}" ) endforeach() mapper-0.8.1.1/doc/manual/pages/000077500000000000000000000000001325266516600163005ustar00rootroot00000000000000mapper-0.8.1.1/doc/manual/pages/android-app.md000066400000000000000000000170531325266516600210260ustar00rootroot00000000000000--- title: The Mapper App for Android authors: - Kai Pastor - Thomas Schoeps keywords: Android edited: 3 February 2018 redirect_from: - /android/ - '/Android Manual/' - /mapper-manual/pages/android-index.html --- ## Preliminary remarks The Android version of OpenOrienteering Mapper is not as self-descriptive as the desktop version, so please read this first for important instructions. [Device requirements and recommendations](android-requirements.md){: .subpage} [Preparing a map on the PC](android-pc.md){: .subpage} [Storing Maps and Templates on Android Devices](android-storage.md){: .subpage} {% if doxygen %} **Note:** The [online version](http://www.openorienteering.org/mapper-manual/android/) of this documentation may contain additions and corrections. {% endif %} ## Using the Mapper app On startup, the app shows a list of map files available from the [supported storage locations](android-storage.md). To start editing a map, touch its filename. ![ ](images/Android_UI_explanation.png) The map editor is slightly different from the desktop version. This page focuses on specific features of the mobile version. To find out about the other symbols, see [Toolbars](toolbars.md) in the desktop application's manual. #### ![ ](../mapper-images/arrow-thin-upleft.png) Hide the top bar Using this button, you may hide the top bar, leaving more screen for the map. A similar button will remain visible for restoring the top bar again. #### ![ ](../mapper-images/compass.png) Compass display toggle When this item is activated, a compass is shown in the top-left corner of the screen. The red line indicates the north direction, while the black-white lines show the up direction of your device. If both are aligned, the device is aligned with magnetic north. When the needle is close to the north direction, a green circle appears which quickly shows how well the device is aligned: the larger it is, the better the alignment is. The compass works three-dimensionally, so you do not need to hold the device in a flat pose for it to work. Note: the usefulness of the compass depends on the presence of a gyroscope, as mentioned in the device recommendations section. Attention: the compass is very sensitive to magnetic materials. Android tries the eliminate the influence of local magnetic fields, but this requires calibration. To do this, move the device around in the shape of an 8 while rotating it and while the compass is active. If the local influence changes, re-calibration is required. #### ![ ](../mapper-images/tool-gps-display.png) Location display toggle If the currently active map work has valid georeferencing, this control will be enabled and will show a dot at your current position as obtained from GPS or other positioning services. In addition, in case an accuracy estimate (HDOP) is available, this shows a circle around the dot which indicates the positioning accuracy. The chance of your real position being in the circle is approximately 70%. While this is active, your track is automatically recorded into a GPX file in the directory of your map, named " - GPS-.gpx". This file is included as a template in the map. You can simply show it using the template visibility control button to view your track. Note that track recording might be interrupted while the screen is off or the app is in the background to save battery. Keep the app active if you want to ensure continuous track recording. #### ![ ](../mapper-images/gps-distance-rings.png) Distance rings toggle This button is active if location display is active. When activated, this will display rings in 10 meter and 20 meter distance from the current location which can be used to do rough distance estimates. #### ![ ](../mapper-images/rotate-map.png) Automatic north alignment toggle When this toggle is active, the map is automatically rotated to north using the device's internal compass. The update frequency is 1 update per second. This is quite low on purpose to save battery, but you probably still need some replacement batteries when using this mode. While interacting with the map and for another 1.5 seconds after that, the map is not rotated to allow editing without unwanted rotations. If GPS position display is activated at the same time, a line starting from the GPS position and going in the direction of the up direction of your device is displayed which shows your viewing direction (assuming you are holding the device upright). ----- #### ![ ](../mapper-images/map-parts.png) Map parts selector This button opens a popup for changing the active map part. #### ![ ](../mapper-images/tool-touch-cursor.png) Touch cursor toggle This symbol activates a helper cursor which allows slow, but precise editing with fingers only. The cursor consists of a circular area and a pointer above the circle. ![ ](images/touch_cursor.png) Touching the map anywhere outside the circle moves the pointer to this position. Touching the circle simulates a real touch at the pointer position above the circle. #### ![ ](../mapper-images/templates.png) Template control This shows a list of all opened templates. Touching a template toggles its visibility. #### ![ ](../mapper-images/close.png) Close button This closes the active map and returns to the map selection screen. #### ![ ](../mapper-images/three-dots.png) Overflow button Depending on the screen size of your device, some of the symbols available not fit onto the screen. They are instead placed in a list which is shown on touching the overflow button. If all symbols fit on the screen, this button is unused. ----- #### ![ ](../mapper-images/view-zoom-out.png) Zoom out / Zoom to fixed A short press will zoom out by one step. A long press will open a menu where you can jump to a fixed zoom (cf. screenshot). #### ![ ](../mapper-images/move-to-gps.png) Move to location When positioning services are enabled, this will move the map so that your current location is in the middle of the screen. #### ![ ](../mapper-images/gps-temporary-point.png) Mark temporary position This records a single point at your current location to act as a drawing help. Note that this marker is not saved in the map. #### ![ ](../mapper-images/gps-temporary-path.png) Record temporary trace This symbol is enabled when the location display is active. It records a temporary trace of the location, which is intended to act as a guideline for drawings. Using the trace directly is rarely useful because of the inaccurateness of location services. Note that the temporary trace is not saved in the map. #### ![ ](../mapper-images/gps-temporary-clear.png) Clear temporary markers This removes the temporay traces and position markers. (They will also be discarded when closing the map file.) #### ![ ](../mapper-images/paint-on-template-settings.png) Paint-on-template settings This button allows to select the active image for scribbling (using the symbol above it). ----- #### ![ ](../mapper-images/draw-freehand.png) Free-hand draw tool Allows to draw paths free-handedly. #### ![ ](../mapper-images/draw-point-gps.png) Draw at location tool Sets a point object at your current location. Selecting this button first enters an averaging mode. While it is active, the input from the location service is averaged to get a more accurate estimate. Touch the map display anywhere to finish the averaging. #### Symbol selector Displays the active symbol, and opens the symbol selection screen on short press. A long press of the symbol selector opens a popup menu which allows to read the symbol name and description, and to toggle the visibility and the protection state of the symbol. mapper-0.8.1.1/doc/manual/pages/android-pc.md000066400000000000000000000112511325266516600206420ustar00rootroot00000000000000--- title: Preparing a Map on the PC --- The Android app cannot be used to create new maps because it would be cumbersome to setup the base maps there. Instead, maps should be prepared with templates and possibly georeferencing using the PC version of Mapper first and then transferred to the mobile device for surveying. If you want to use live GPS display, the map must be georeferenced. There are several options how to do this: ## Georeferencing when you create a new map The first thing to do after creating a new map should be to load a georeferenced template. If you do not have one, you may use for example an OpenStreetMap export in case there is sufficient data at the map's location. To do so, go to http://www.openstreetmap.org, locate the map region, and click 'Export'. Then select an area which covers all of your templates and save the export as .osm file. This file can then be loaded into Mapper as a georeferenced template. On loading the first georeferenced template, Mapper will show the georeferencing dialog. This may look a bit intimidating at first, but when loading a template, usually little needs to be changed there. First, the projection type should be checked. If you do not know which projection to use, just select UTM. Second, the magnetic declination should be entered to align the map with magnetic north. You may simply use the lookup button to look it up on the internet for the place of the template you loaded. However, be aware that the lookup only uses a model which may deviate from the real declination. It may be advantageous to check this in the terrain. All remaining values in the georeferencing dialog are already initialized using the newly loaded template, so you can then finish the dialog. Then load all remaining templates and finish preparing the map. Non-georeferenced templates will need to be moved to match the location of the georeferenced templates. ## Georeferencing when you use an existing map In this case, the easiest way is to first create a new map with the same symbol set as the existing map. The old map can be selected as the symbol set source in the new map dialog to do this. Then load a georeferenced template as detailed in the paragraph above (use e.g. OpenStreetMap if you do not have a georeferenced template). After that, load the existing map as template in the new map. Then use the alignment tools for templates to move the existing map to match with the new georeferenced template. When you are finished, import the existing map template into the new map file using the corresponding action in the template dock widget, and you should be done. Attention: especially for old maps, the whole existing map may be locally distorted. In this case it will not be possible to match the old map and the georeferenced template and the only way to georeference the map is to do a tedious undistortion. Mapper does not provide a tool for this. ## Preparing for free-hand scribbling ![ ](../mapper-images/pencil.png) Mapper supports free-hand drawing onto image templates using the tool with the pencil icon shown above. This is especially convenient to use in case you want to do only (or mostly) drafting while you are in the field, and draw the final version of the map at home on a PC. It has the advantage of being quick and it only alters the template you draw on, i.e. it does not create new map objects, so Mapper's map display does not become slower. This may be relevant on mobile devices because of little processing power or because of battery runtime. If you want to use this functionality, you should best load a separate, initially transparent image template in png format for it. Suited images can be downloaded here: - [size 1024x1024](attachment/scribble_1024.png) - [size 2048x2048](attachment/scribble_2048.png) The images contain a red border, but are transparent everywhere else. After downloading a file, load it in your map file and move it so it covers the area you want to draw scribbles on. Make sure that the image resolution of the template is good enough by drawing a test scribble. If not, you need to scale it down using the template settings. You may also create multiple copies of the file and load them as separate templates. Direct drawing onto base map images is NOT recommended because you will not be able to use the eraser without also erasing the base map and because these images usually are in the lossy JPEG format which will introduce visible artifacts when saving the file and loading it again. Note that to view the scribbles on a PC after creating them on a mobile device, you need to transfer not only the map file, but also the affected image template file(s) back to the PC, as the scribbles are saved directly in the image files. mapper-0.8.1.1/doc/manual/pages/android-requirements.md000066400000000000000000000035061325266516600227670ustar00rootroot00000000000000--- title: Device Requirements and Recommendations keywords: Android --- ## Android version The minimum version for running the app is 2.3. Write access to SD cards may require Android 4.4 (cf. [Storage locations](android-storage.md)) ## Water-proofing For obvious reasons, water-proof devices may be advantageous. ## Stylus For doing any serious work, a stylus is a must-have (because of precision & speed, and because of temperature during winter). You should preferably look for devices which come with an adapted stylus such as the Samsung Galaxy Note series, as general styluses for capacitive touch screens have rather wide tips. ## Screen size There is a compromise between ease of handling (better on smaller devices) and overview (better on larger devices). ## Battery It is very desirable to use a device with replaceable battery. It may be possible to use an external battery pack for devices with integrated battery. ## Sensors OpenOrienteering Mapper can use integrated GPS receivers. However, their accuracy may be low, so it may be advantageous to connect to an external GPS receiver. There is no direct support for external devices in Mapper, but some third party apps bridge the gap. For example the [Bluetooth GPS app](https://play.google.com/store/apps/details?id=googoo.android.btgps&rdid=googoo.android.btgps) provides a "mock" location provider which can be used when Android is put in developer mode. The app can use a magnetometer and accelerometer as a compass. Almost all modern devices should contain these sensors. If a gyroscope is also available, it will be used to improve the compass stability. However, these sensors are typically not accurate enough to be used for measurements. They can be used for convenience when not relying on a compass, but for accurate measurements, an external compass is recommended. mapper-0.8.1.1/doc/manual/pages/android-storage.md000066400000000000000000000044401325266516600217060ustar00rootroot00000000000000--- title: Storing Maps and Templates on Android Devices keywords: Android --- ## Storage locations Android organizes storage and permissions different than PCs. Especially on SD cards, write access for apps is restricted. You can choose the following locations for storing data which you want to edit and to display in OpenOrienteering Mapper: - Folder "OOMapper" at the top level of the primary storage volume. On most devices, the primary storage volume is a part of the built-in storage. Files in this area are stored permanently, until you decide to remove them. Unfortunately, there is usually only limited capacity available - this memory is expensive when buying the device. - Path "Android/data/org.openorienteering.mapper/files" on arbitrary storage volumes such as SD cards (since Mapper 0.6.5). This is the only convenient way to store map data on cheap extra memory cards. However, you need to be aware of the fact that these folders are removed by Android when you uninstall the Mapper app. So please carefully save your changes to a PC. OpenOrienteering Mapper for Android will create these folders when they are missing, and scan them for map files. It will display warnings when you open maps from the non-permanent app-specific folders or from read-only locations. (Top-level "OOMapper" folders on secondary storage volumes are scanned, but cannot be written to.) ## File transfer You can transfer files from and to a PC via a USB cable. Android supports multiple file transfer protocols. - MTP is the preferred method now. There is hardly any interference for apps while the device is connected to the PC. Via MTP, Android will only show files which are known to its Media Scanner. If you cannot find a file such a recorded GPX track, a reboot of the device could trigger a rescan. - Mass Storage makes the storage unavailable for apps for the duration of the connection with the PC. (Android also needs to terminate apps which are stored on the volume which is provided as mass storage.) Unlike MTP, mass storage does not depend on the media scanner, so all files are always visible. In some cases, users suffered from files being damaged during transfer. This can be worked around by choosing another method for file transfer. Remember to keep backups and to verify transferred files. mapper-0.8.1.1/doc/manual/pages/attachment/000077500000000000000000000000001325266516600204305ustar00rootroot00000000000000mapper-0.8.1.1/doc/manual/pages/attachment/scribble_1024.png000066400000000000000000000131451325266516600233750ustar00rootroot00000000000000PNG  IHDR+bKGDIDATxڱ0 CߕD(y7Rj%`0Owfy:G@@@@@@@@                                                                  &@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@0                                                                @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@Vf|6G~IENDB`mapper-0.8.1.1/doc/manual/pages/attachment/scribble_2048.png000066400000000000000000000515041325266516600234050ustar00rootroot00000000000000PNG  IHDR0 IDATxܱ @ bh'Kԧl=v' @wqlv@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@ad[+`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@-t IDAT`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@صA#`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@`@ vaXǟt8 @  @@  @@  @@  @@  @@  @OIDAT@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@  @@ ;fy^{p@ ~ ]IENDB`mapper-0.8.1.1/doc/manual/pages/color_dock_widget.md000066400000000000000000000073101325266516600223040ustar00rootroot00000000000000--- title: Color Window authors: - Peter Hoban - Thomas Schoeps keywords: Colors edited: 26 February 2013 --- This window can be shown by clicking the menu item Symbols -> Color window. ![ ](images/color_dock_widget.png) #### Introduction Colors on orienteering maps are defined in the orienteering map standards, ISOM and ISSOM, as colors from the Pantone Matching System (PMS). The recommended way to print maps is to use these spot colors to print each color layer separately onto the map sheet. This way colors printed later will overlap colors which are printed earlier. The color system in OpenOrienteering Mapper follows this analogy: each map file contains a prioritized list of available colors, where colors higher in the list overlap lower colors. Colors can be managed with the buttons at the bottom of the list. To edit a color, double click its entry in the color list. ### Color editor Today there are different methods for printing maps and the color editor in OpenOrienteering Mapper is adjusted to that, so there are different settings for "desktop" and "professional" printing. #### Desktop printing ![ ](images/color_editor_desktop.png) The first page addresses "desktop" printing, i.e. printing maps with cheap home laser printers. These devices use the **RGB color space** to define colors, where each color consists of a given amount of red, green and blue (this is not to be confused with how the colors are actually printed; it is just how the PC tells the printer which color to print). So on the desktop page, you can define how the RGB value for a color should be calculated by OpenOrienteering Mapper: - **Evaluate spot colors**: the spot colors referenced by this color will define its RGB color. This is only possible if this color references at least one spot color (see later) - **Evaluate CMYK color**: the CMYK value of this color will be converted to a RGB value (see later) - **Custom RGB color**: you can enter the RGB value directly Note that while defining RGB colors is enough for normal desktop printing, using the [overprinting simulation](view_menu.md#overprinting) effect requires to define spot color printing options also. #### Professional printing ![ ](images/color_editor_professional.png) This page is divided into two parts. On the left are the settings for **spot color printing**: - If the first option is activated, this color entry represents a spot color and will thus define a color layer. Enter the spot color name in the text field below the option. - If the second option is activated, this color entry represents a color which is printed as a percentage of a spot color (screen) or a mixture of multiple spot colors (overprint). Choose the referenced spot colors and their amounts below. When multiple colors overlap each other on the map and [overprinting simulation](view_menu.md#overprinting) is activated, the program simulates the effect of overprinting real spot colors where the colors are darkened when printed on top of each other. This can be very useful to increase map legibility, however it is not desired in all situations. Sometimes a color on top of another should erase the lower color completely. This can be set by activating the "**Knockout**" option. On the right side of the professional page, there are the options for **CMYK color printing**. These are similar to the RGB color options: - **Evaluate spot colors**: the spot colors referenced by this color will define its CMYK color. This is only possible if this color references at least one spot color - **Evaluate RGB color**: the RGB value of this color will be converted to a CMYK value - **Custom process color**: you can enter the CMYK value directly mapper-0.8.1.1/doc/manual/pages/colors_symbols.md000066400000000000000000000006751325266516600217030ustar00rootroot00000000000000--- title: Colors and Symbols edited: 26 November 2015 --- Modifying map colors and symbols [Color Window](color_dock_widget.md){: .subpage} Color system explanation and working with colors [Symbol Pane](symbol_dock_widget.md){: .subpage} Types of symbols and how to create a new symbol [Symbol Replacement Dialog](symbol_replace_dialog.md){: .subpage} Replacing the current symbol set by another, useful for updating the symbol set version mapper-0.8.1.1/doc/manual/pages/course_design.md000066400000000000000000000100541325266516600214530ustar00rootroot00000000000000--- title: Using the Course Design Symbol Set keywords: Courses, Symbol sets --- [Templates]: templates.md [IOF Homepage]: http://orienteering.org OpenOrienteering Mapper doesn't have a sophisticated course planning feature. The Course Design symbol set provides basic support for designing maps with courses and control desriptions. It contains the overprinting symbols from the ISOM 2000 and the control descriptions from IOF's "International Specification for Control Descriptions - 2004". See the [IOF Homepage] for details. ![ ](images/course_design.png) ## Loading a base map Open the base map as a template in the background. See [Templates] for details. ## Drawing the course For simple courses which consist only of start, controls, finish and free navigation you may use symbol 799 Simple Orienteering Course. For more complex courses use the individual overprinting symbols. The labels for the controls have to be created explicitly. ## Creating the control description sheet The Course Design symbol set contains the individual pictograms as well as symbols for the different sections which together form the control description sheet. The pictograms' names indicate the column of the control description sheet where they are to be used. The control description symbols are meant to be used with a grid of horizontal and vertical lines spaced 3.0 millimeters. By pressing the Shift key while creating a new element, you may easily align all the parts of the description. ## About the control description sheet The control description sheet starts with a heading which consists of blocks for the event title, classes (optional), and (in a single row) course code, course length and and height climb. The course length is given in kilometres rounded to the nearest 0.1 km. The height climb is given in metres rounded to the nearest 5 m. The heading is followed by lines describing the start location, the individual controls, and special routes. The description of start and individual controls uses eight colums which are refered to as A...H. | Column | Description | | ------ | ----------- | | A | Control number. Numbering of controls is in the sequence they are to be visited, unless the description is for a Score competition. | | B | Control code. The control code should be a number greater than 30. | | C | Which of any similar feature. This column is used when there is more than one similar feature within the control circle; e.g. south eastern. | | D | Control feature. The feature, as shown on the map, at the centre of the circle deÞ ning the control site; e.g. clearing; boulder. The description of each control is based on the International Specification for Orienteering Maps (ISOM 2000). | | E | Appearance. Further information on the nature of the feature if it is required; e.g. overgrown; ruined. In certain circumstances also used for a second control feature where the description requires this. | | F | Dimensions / Combinations. Dimensions of the feature should be given where the size of the control feature on the map is symbolic rather than to scale. Also used for the two combination symbols (crossing; junction). | | G | Location of the control flag. Position of the control flag with respect to the feature; e.g. west corner (outside); south foot. | | H | Other information. Other information that may be of importance to the competitor; e.g. radio control; refreshments. | ## Specifications for Trail Orienteering There are two variations in the use of the columns when using IOF Control Descriptions for Trail Orienteering. | Column | Description | | ------ | ----------- | | B | Number of control flags. This column is used to denote the number of control flags visible at this control; e.g. A-C equals three control flags to choose from; A-D equals four control flags to choose from. | | H | Direction of observation. This column is used to denote the direction in which to view a feature. For example an arrow pointing north indicates that the competitor should be on a path/track to the south of the control circle. | mapper-0.8.1.1/doc/manual/pages/crt_files.md000066400000000000000000000050641325266516600206010ustar00rootroot00000000000000--- title: CRT Files authors: - Kai Pastor edited: 21 January 2018 --- ## Introduction Cross Reference Table (CRT) files provide rules for assigning and replacing symbols. They are used in the following situations: - replacing a map's symbol set (menu Symbols > Replace symbol set...), - replacing symbols within the current map (menu Symbols > Load CRT file...), - importing geospatial vector data (menu File > Import...). To facilitate the automatic lookup of a suitable CRT file, CRT files may be placed in the symbol set folder and named following this pattern: SOURCE_ID-TARGET_ID.crt where SOURCE_ID is a symbol set ID (menu Symbols > Symbol set ID...) or an import driver name (e.g. OCD, OSM, DXF), and TARGET_ID is another symbol set ID (either of this map or of the replacement symbol set). Examples: - OSM-ISOM2017.crt will be used when importing data through the OSM driver into an ISOM2017 map. - ISOM2000-ISOM2017.crt will be used when replacing the symbols of an ISOM2000 map with an ISOM2017 symbol set. ## CRT file syntax Empty lines are ignored. Lines starting with a # character are considered comments, i.e. they are ignored, too. Every other line is a replacement rule and consists of two parts. The first part of the rule is the target symbol number. It determines which symbol will be assigned to matching objects. The second part of the rule, separated from the first part by white space, determines the objects this rule applies to. This can be given either as a symbol number or as a [object query](find_objects.md#advanced-query-language). When using object queries, more specific rules need to follow more general rules. Example from OSM-ISOM2017.crt: - 406 natural = wood - 408 natural = wood AND (wood:age = young OR wood:age = very_young) - 404 natural = wood AND wood:density = sparce - 408 natural = wood AND wood:density = dense Example from ISOM2000-ISOM2017.crt: - 102 102 - 103 103 - 101.1 104 - 102.1 105 ## CRT file editing CRT files may be edited with any text editor, e.g. notepad in Windows. However the .crt filename extension is commonly associated with certificate files. You will probably have to select an "All files" filter in the editor's Open-file dialog, or temporarily change the extension to .txt. When Mapper displays the symbol replacement dialog, modified symbol assignments can be saved to a CRT file via the "Symbol mapping" button. Note that this dialog shows the assigned symbol in the second column, while in the CRT file, assigned symbols are recorded at the beginning of the line. mapper-0.8.1.1/doc/manual/pages/edit_menu.md000066400000000000000000000045321325266516600205770ustar00rootroot00000000000000--- title: Edit Menu authors: - Peter Hoban - Thomas Schoeps - Kai Pastor keywords: Menus edited: 21 January 2018 --- #### ![ ](../mapper-images/undo.png) Undo **Ctrl+Z** This most important function reverses the last change made to the map. Repeating 'Undo' will undo earlier changes, one step at a time. **Attention:** While changes to map objects can be undone this way, not all map changes are covered by the undo system. This includes changes to colors, symbols or templates. #### ![ ](../mapper-images/redo.png) Redo **Ctrl+Y, Ctrl+Shift+Z** When you have taken Undo too far this will reverse the process — again one step at a time. --- #### ![ ](../mapper-images/cut.png) Cut **Ctrl+X** This removes the selected object(s) and moves them to the clipboard. #### ![ ](../mapper-images/copy.png) Copy **Ctrl+C** This copies the selected object(s) to the clipboard. #### ![ ](../mapper-images/paste.png) Paste **Ctrl+V** This inserts the map object(s) on the clipboard into the map. They will be centered at the view midpoint. **Note:** When copying objects between different maps, their symbols and colors may be copied too in order to be able to display the objects in the new map in the same way as in the source map. #### ![ ](../mapper-images/delete.png) Delete **Del** This action deletes the selected object(s). --- #### Find... Opens the ["Find objects" dialog](find_objects.md) which can be used to find objects by contained text, symbol name or tag keys and values. #### Find next Finds the next object matching the last query from the ["Find objects" dialog](find_objects.md). --- #### Select all **Ctrl+A** This selects all objects in the current map part. #### Select nothing **Ctrl+Shift+A** After this action, no object is selected. #### Invert selection **Ctrl+I** This action inverts the selection in the current map part. #### Select all objects with selected symbols After this action, the selection consists of all objects in the current map part which have the selected symbol(s). --- #### Clear undo / redo history Clears the history of undo and redo steps, i.e. all undo and redo steps will be deleted. This reduces the file size for map file formats where the last undo and redo steps are stored, e.g. the omap format. mapper-0.8.1.1/doc/manual/pages/faq.md000066400000000000000000000107351325266516600173770ustar00rootroot00000000000000--- title: Frequently Asked Questions (FAQ) authors: - Peter Hoban - Kai Pastor - Thomas Schoeps - Libor Pechacek keywords: FAQ edited: 5 December 2017 --- * TOC {:toc} ## Navigation ### How do I move the map? The easiest way is to hold the middle mouse button while moving the mouse. But you can also press F6 and move the mouse, or use the corresponding toolbar button, or use the cursor keys. ### How to zoom in and out? You can use the mouse wheel, press F7/F8 or +/- keys, or click the zoom buttons in the toolbar. ## Drawing ### I want to start drawing a map, but have no idea how to. Start by reading the [short introduction to o-mapping](mapping-introduction.md). ### How do I add points to an existing path or delete points from it? With the path object selected using the edit tool, hold Ctrl and click a free spot on the path to create a new point there. You can even move the mouse in the same action to set the position of the new point. To delete a point, hold Ctrl and click an existing point. ### How do I change line segments from curved to straight and vice versa? With the [line edit tool](toolbars.md#tool_edit_line), select the object you want to edit and hold Ctrl while clicking the line segment. This switches the segment between curved and straight. ## Advanced editing ### How can I cut out a part of a map to take it for a new map? First you save the map to the new file. Then draw some closed line or area (symbol doesn't matter) arround the border of the part which you want to keep. Now when this border object is finished and selected, click on the "Cutout" icon or menu item, then press the enter key. ## File formats ### What are the advantages of the .omap format? The .omap format is OpenOrienteering Mapper's native format. It is XML-based, which means for example that it is relatively easy for other developers to access the information in map files. Additionally, it is platform independent, error-resistant and makes it possible to keep a high compatibility between different versions of the format. ### What is the difference between .xmap and .omap format? The .xmap format is nearly identical but more verbose, even suitable for direct manual editing. The .omap format is tuned towards minimum size and fast processing. If you want to keep you data in a line-based version control system such as git, you must use the .xmap format. ### Can Mapper read and write .ocd files? Yes, Mapper reads .ocd files from different version (8 .. 12). But currently only version 8 .ocd files can be written. Beware that some details are neither loaded from nor saved to .ocd files, and some additional inaccuracies might occur. ## Mobile devices ### Will Mapper be available for Android? An Android version is available. It already has a special user interface, but it needs a lot of work. See the [instructions for the Android app](android-app.md). ### Will Mapper be available for iPhone / iPad? Probably not, due to distribution issues. Technically, a build should be feasible. ### How do I hide/protect a symbol in the mobile UI? Tap-and-hold on the Symbol selector button. Context menu with hide and protect options will appear. Hidden and protected symbols are marked with usual signs in the symbol list. ## Experienced mappers' corner ### What are the most notable differences to OCAD? * Whole objects can be moved by dragging the dashed magenta bounding box when [**Edit objects** tool](toolbars.md#tool_edit_point) is selected. * There is a [single tool for drawing straight lines and Bézier curves](toolbars.md#tool_draw_path). * [Another tool](toolbars.md#tool_draw_circle) combines circle and ellipse drawing. * Addition and removal of path points (vertices) can be achieved via [**Edit objects** tool](toolbars.md#tool_edit_point) using various combinations of keyboard modifiers. See the status line at the bottom edge of the window for dynamically updated help. See also [Drawing](#drawing). * Dash points can be added and removed using the [**Edit objects** tool](toolbars.md#tool_edit_point) after pressing **Ctrl-Space** on keyboard. * Corner points do not exist per se. Move the Bézier curve handles independently while holding keyboard **Shift** key. * Edge lines of double line symbols (roads et al) are inactive and cannot be snapped on. Mapper does not support cutting out parts of the side lines. * Partial map export is done via a tool. See [Advanced editing](#advanced-editing). mapper-0.8.1.1/doc/manual/pages/file_menu.md000066400000000000000000000042461325266516600205730ustar00rootroot00000000000000--- title: File Menu authors: - Peter Hoban - Kai Pastor - Thomas Schoeps keywords: Menus edited: 29 November 2015 --- These controls provide standard file management tools. #### ![ ](../mapper-images/new.png) New **Ctrl+N** This shows the dialog to create a [new map](new_map.md). #### ![ ](../mapper-images/open.png) Open... **Ctrl+O** Shows the dialog to open an existing map file. Currently supported formats are .omap, .xmap or .ocd (versions 6 to 11). #### Open recent This shows a list of recently opened files, recognizing that a file to be opened will often have been previously opened in the recent past. #### ![ ](../mapper-images/save.png) Save **Ctrl+S** Saves the currently opened file. OCD files will be saved in version 8 format. #### Save as... **Ctrl+Shift+S** Shows the file dialog for choosing a file name and file format to save the currently opened map. --- #### Import... Permits the import of maps or data in other formats. Currently the following formats are supported: .omap, .xmap, .ocd (version 6 to 11), .gpx, .osm, .dxf. #### Export as... Permits the export of the currently opened map to another format. Currently pdf and raster image export are supported. For raster images, Mapper may additionally create world files. A world file is a small text file which defines the image's georeferencing relative to the map's projected coordinate reference system. Note that these files contain only six numerical parameters. They do not contain an identification of the projection, coordinate system or datum they refer to. The filename is derived from the image's filename, but with a modified filename extension usually ending with letter 'w'. --- #### ![ ](../mapper-images/print.png) Print **Ctrl+P** Shows the print dialog to print the currently opened map. --- #### Settings Opens the [settings dialog](settings.md). (On OS X, the settings are in the usual place instead.) --- #### ![ ](../mapper-images/close.png) Close **Ctrl+W, Ctrl+F4** Closes the current program window. #### Exit **Ctrl+Q** Exits the program, which will close all open map windows. mapper-0.8.1.1/doc/manual/pages/find_objects.md000066400000000000000000000104631325266516600212570ustar00rootroot00000000000000--- title: Find objects authors: - Mitchell Krome - Kai Pastor keywords: Tagging edited: 21 January 2018 --- The "Find objects" dialog allows to find and select objects in the current map part based on their tags, text, or symbol name. It is opened from the View menu. ![Find objects dialog](images/find_objects.png) ## Querying objects The main element of the "Find objects" dialog is an input field for the text you want to find. The text is looked for in the following *textual properties*: - in the displayed text of text objects, - in the name of symbols assigned to objects, - in the values and keys of object tags. ## Advanced query language With just a few keywords, you may enter complex queries which connect subqueries for general textual properties and for particular key-value combinations. The following expressions are supported: | Expression | Matching objects | | ---------- | ---------------- | | word | having "word" in any textual property | | "some words" | having the phrase "some words" in any textual property | | expr1 AND expr2 | matching both expression expr1 and expr2 | | expr1 OR expr2 | matching expression expr1 or expr2 (or both) | | key = value | having a tag with the given key and value | | key ~= word | having a tag with the given key and a value containing "word" | | key != word | having a tag with the given key and a value different from "word", or not having a tag with this key | AND has precedence over OR. You may use parentheses to nest operators in another way. You may use double quotes to deal with keys and values containing white space or the special words and signs. Inside double quotes, the backslash character is used to escape the special meaning of double quotes and backslash. **This query language may be used in the second column of [CRT files](crt_files.md).** ## Buttons The button "Find next" will select the next object matching the query. The button "Find all will select all matching objects. The main windows's status line will temporarily display the number of selected objects. (It may also indicate that the query is invalid.) The "Query editor" button will replace the input field with a table for entering object tag queries. Pressing the button again will restore the input field and make it show the query from editor view. ## Query editor {#query-editor} ![Query editor](images/query_editor.png) ### Editor Layout Each row in the editor represents a single condition in the query and its relation to the other conditions. ### Relation The relation column specifies a logical operator describing how this row is related to the row above. Therefore the first row has no relation. There are two possible relations: **and** or **or**. **and** relations take precedence over **or** relations. Within the editor the **and** relation is indented slightly to show this precedence. Taking the precedence into account, the query in the screenshot above would map to the following textual query: **(highway = "residential" AND source ~= "bing") OR ("highway" = "primary" AND "source" ~= "bing")** ### Key The key is the name of the tag which this condition applies to. This field cannot be left empty. (Otherwise the query is invalid.) ### Value This field specifies a value that the actual tag's value is compared to. It can be left empty. ### Comparison The comparison defines how the actual value of the tag is compared to the value that was specified in the editor. | Comparison | Description | | ---------- | ----------- | | is | The tag specified must exist for the object, and that tag's value must exactly match the specified value. | | is not | If the tag specified exists for the object, then its value must not much the specified value. If the tag doesn't exist, the condition is true. | | contains | The tag specified must exist for the object, and its value must contain the specified value. If an empty value is specified, any value of the tag will match. This can be used to test for the existence of a particular tag. | ### Extra buttons for the query editor The ![ ](../mapper-images/plus.png) and ![ ](../mapper-images/minus.png) buttons are used to add or delete a row. Added rows appear below the currently selected row. The ![ ](../mapper-images/arrow-up.png) and ![ ](../mapper-images/arrow-down.png) buttons move the selected row up or down. mapper-0.8.1.1/doc/manual/pages/georeferencing.md000066400000000000000000000151701325266516600216100ustar00rootroot00000000000000--- title: Georeferencing authors: - Peter Hoban - Thomas Schoeps keywords: Georeferencing edited: 25 February 2013 --- - [Introduction](#introduction) - [Map coordinate reference system](#map-coordinate-reference-system) - [Reference point](#reference-point) - [Map north](#map-north) - [Related functions](#related-functions) - [Further reading](#further-reading) #### Introduction Georeferencing of a map is the best way for aligning templates (such as base maps or aerial imagery) and GPS tracks. In short, to georeference a map means to establish a known relationship between the paper coordinates of the map and the coordinates of a geographic coordinate reference system. This way, data which is known in a geographic coordinate reference system (such as GPS coordinates) can be transformed to map coordinates and thus displayed on the map, and vice versa the map can be transformed to geographic coordinates and e.g. be displayed on a world map. More information is available on [Wikipedia](http://en.wikipedia.org/wiki/Georeferencing). Georeferencing properties are set in a dialog which is available from the menu **Map > Georeferencing...**. The dialog is divided in three sections: Map coordinate reference system, Reference point and Map north. ![Georeferencing dialog](images/georeferencing.png) #### Map coordinate reference system This section defines in which kind of projected coordinates (real-world metric cartesian coordinates) the reference point relating map and geographic coordinates is defined. Projected coordinates are transformed to map coordinates by scaling and rotation. You should thus choose the coordinate reference system in which you know the real-world coordinates of a point on the map. In case you just want to load some GPS tracks, you can also just safely use UTM, which is widely used world-wide. Other choices are: - **Gauss-Krüger**: this is similar to UTM and widely used in Germany, but is being superseded by UTM. - **From Proj.4 specification**: projections are internally handled by the [PROJ.4 Cartographic Projections library](http://proj4.org/), so coordinate reference systems can also be given in its internal specification format. Examples may be found at [http://www.remotesensing.org/geotiff/proj_list/ (Internet Archive)](https://web.archive.org/web/20160802172057/http://www.remotesensing.org/geotiff/proj_list/) and [http://spatialreference.org/](http://spatialreference.org/). When selecting this option, the specification field will be pre-filled with the specification of the previously selected coordinate reference system. - **Local**: this enables you to use local projected coordinates without a mapping to global geographic coordinates. Depending on the selected coordinate reference system more settings may show up. For example, for UTM the zone number must be given in addition. #### Reference point Settings in this section define the reference point, which is the point for which coordinates in all of the involved coordinate systems are known. Thus it acts as the anchor between the different coordinate reference systems. In case the georeferencing dialog is triggered by loading a georeferenced template in a map which is not georeferenced yet, these settings are probably already pre-filled with sensible values (assuming that no other map objects exist yet), so they do not need to be changed in this case. The **Map coordinates** field shows the map paper coordinates of this point. To change them, use the **Select...** button. The georeferencing dialog will then be hidden until you select a point on the map (left mouse click) or cancel the selection process (another mouse button). Changing the reference point on the map will not affect the other sections. The next set of coordinates gives the reference point east-west and north-south position in **projected coordinates**, for example in UTM or Gauss-Krüger coordinates. Unless working with local coordinates, changing easting or northing will update the geographic coordinates. Easting and northing are given in meters. The third set of coordinates gives the reference point position in **geographic coordinates**. Note that after the selection of a coordinate reference system other than local, projected and geographic coordinates are linked together, so changing one will also change the other. Geographic coordinates specify a location on the planet's surface by latitude and longitude. Latitude and longitude are measured in decimal degrees. Also note that according to convention, the first coordinate here is the latitude (northing) and the second the longitude (easting). - The **latitude** specifies the north-south position of the reference point as an angle relative to the equatorial plane. Negative values indicate the southern hemisphere. - The **longitude** specifies the east-west position as angle relative to a prime meridian. Negative values indicate a position west of the prime meridian. The **Datum** field shows the datum the geographic coordinates refer to. This is especially relevant for the longitude. The second last line in the Reference point section contains hyperlinks for opening the reference point in OpenStreetMap or in the World of O Maps directory. The last option in this section determines which coordinates will be recalculated and which stay equal when changing the coordinate reference system. #### Map north In the **Declination** field the angle between true north and magnetic north at the position of the map has to be entered to make magnetic north be at the top. This can be looked up from an online accessible model as soon as the reference point geographic coordinates are entered, however it should be checked with a precise compass if accuracy is required. **Grivation** determines the rotation which moves the magnetic north to the top of the map. Grivation is composed of magnetic declination (the angle between true north and magnetic north) and grid convergence (the angle between true north and grid north). #### Related functions The (mouse) cursor position of the map editor can be displayed in map coordinates, projected coordinates or geographic coordinates (decimal or as degrees/minutes/seconds, DMS). The coordinates of the cursor on the map sheet are discussed [here](view_menu.md#coorddisplay). #### Further reading - Wikipedia: [Geographic coordinate systems](http://en.wikipedia.org/wiki/Geographic_coordinate_system) - Richard Knippers (2009): [Geometric Aspects of Mapping](http://kartoweb.itc.nl/geometrics/) - Larry Simons (2005): [Magnetic Declination & Grid Convergence](http://www.threelittlemaids.co.uk/magdec/explain.html) mapper-0.8.1.1/doc/manual/pages/grid.md000066400000000000000000000030641325266516600175520ustar00rootroot00000000000000--- title: Map Grid authors: - Peter Hoban - Thomas Schoeps keywords: Map edited: 25 February 2013 --- ![Grid icon](../mapper-images/grid.png) The map grid can be activated by clicking the corresponding button in the [view toolbar](toolbars.md#view-toolbar). Settings for the grid can be changed by clicking the arrow to the right of this button. ### Grid settings ![ ](images/grid_settings.png) #### Show grid Toggles the grid's visibility. #### Snap to grid If activated, holding the Shift key while drawing or editing objects will snap not only to existing objects, but also to intersections of grid lines if the grid is visible. #### Line color The line color can be adjusted here. #### Display Here the display can be constrained to horizontal or vertical lines only. #### Alignment Here you can choose whether the vertical grid lines should align with magnetic north (which is assumed to be identical with the up direction of the paper), the grid north or true north directions. The latter work only if the map is [georeferenced](georeferencing.md). If grid north is selected, in addition the origin of the grid will be shifted to the grid coordinates origin. Furthermore, it is possible to define an offset angle which is added to the north angle to calculate the grid orientation. #### Positioning The spacing of the grid lines can be specified here. Normally, at the coordinate system origin there will always be a grid line intersection. Using the offset values, it can be shifted away from the coordinate system origin. mapper-0.8.1.1/doc/manual/pages/images/000077500000000000000000000000001325266516600175455ustar00rootroot00000000000000mapper-0.8.1.1/doc/manual/pages/images/Android_UI_explanation.png000066400000000000000000007655311325266516600246530ustar00rootroot00000000000000PNG  IHDRYabKGD IDATxwp}y^Pz%;E5Jd˖ٺ7&q83w7wfgnvwI׫8q|cYl* )R$EEH^޶%$H$cg)8o۞/P@P@P@P@P@P@w ( (~M] P@P@pI0- ( ( =1;xpP@P@P"a"7#M  ( (fMuv0=W P@P@Fݐfy Ӊ|P@P@}c{f>_ P@P@H՟ϤoFc%iQgxH3w&AP@P@Ʉ}2IW'~&d.p/lRe "dzQ@P@!JF,#"(..M 7_9nwIW=L eL3?OP@P@VAe.^r9zzz$jTU-ܤݰM"C}2zoqR P@PoTUEQJJJf}r$I翀9}a^:9&Ϸfqy/x ( (JN*Bx4MCUUtv:}̌*37OygInɪfEΖlb\ךUd\.g݂(L&AE/ }pb6!L, 9EEy.Ʉ(h(Yo}IUY~7ͭױ;[,3UU S]!9L&S Vn iFԙz?O9x ( n84q?5lfՆ %%%7\JWWdYp\~/'(Be%>uI@#t띗 144ĢEp:hA8@oo/b1#3b2HR>}/ mFiim;$I\rRl6]nQD"ap8^MӈFU.N"퓋T%*~=.\ 7A@4|>n$y+nn:|I^]l>կwZ,N$: *ɄEΟgW}->aE!qN&I"h㿼?a&Bv7 2DFGGd2`XpZL&2 & 4g LKMM t^|>V:300 tuupΛBڵ~?ُ?cDK{zGdD_%9͡Cekb2er gΞ=ryfn݊b1D./ٳ r(۷o' 4B5Ľ+Vp8}mڵ={/j/uvE&_V;\+K\i:J-Nu:fN'MMMر5k`6 O   zt2x)$I$32$mmߏixR)<6 I _`u|{ߣn;h2 ΝرcD"/_<5Xf W&rAN>MGG6l`ҥl9'׻](\wBq授Ɉhy~ -ł(SU)AtFbIg~Ҋ@Me+ʩ 80DFbfnTx|U%~f$ GwIUs5.2ccc166墪@ `D" tttPSSC Cmm휐d]׉bd2(--… P]]}͘˧p^rK0DEb.v-ڞ=2@ێHIy Çd ki霒w]ב$X,_|隣zccc8pJXx1=>,< }}}8qxmۆ绫:gΜa><<[o޽{E>ouÇEUU^}O'vy&wE&Unq7燐Yb6lƊ޵T2wMQU⊂e˗g1 _DbaݫҟrlkEfqYΩ{TNMCf8 cl>###v;}4+V\׳T@`8A4@7$ z"s`Ț5k8ul˂ lSH@sʹ=Mhu4VuL:Eg_8 55 Kik\`2-)HHw_sWQp;{x<]oP˲(x<Xp!DQ._LUUpmt\+b\{NQQ]]]|>|>ߔ5U4IR,YMP<} uwvƾ`( Z$B,?8Sz A߻Պt{[(4]UU t: |7ؿ?fvZlrSOpWUUSTTW_}Łu~sWAUUJKK㴴@QQќB 044Ν;ٻwAS;wl6ȟt]gllw^4.}̖[&H 0_ ӸmGoۼ|6mkyK3ݨ.v'Xs9:J\ EIFH˖aO`bN9Bۙ3 b٨ i>xmu]gppǏرcŋrѣYwab>^- DQ@ҏ?knaCN-o$ξhf7c)d;JI`u);ׯ_& CeI$vä7:T6ˑ^r WT9{aq >sIɜŁUUǟx]5'4 ׻MӸ|2c֮]$IȲL6f^TWWSQQA}}=1+WqvȲLii霤 S>cdxz Mضm1R<СC|Gb1cp8|HwJKKoƷ'{%M)i~Ӽ?j7ȑ#Ioܸ+HpNSYZJN74Ћ/`tMCtxOv]k 1A2Ѓ,\~Dۑ/qly<\A+b]WIs)ϙLgTc.+a.4Dm u*+ @u|>O?4/8r ~u>ݹ`ᩭ矧~qyGMCt4UeyJB!p8I *+#pظ};::p0t2oMEu-M gΜEQYY _p9/^̪U())jc2aŜ>} ]ٲe `JhnnraÆYDf$rxVlV3kN_D\8S S}^;_?M:]ЎH Ŕ>N_B#VQ)P/v(ikk3& Ο?ϢEA-m\BD@v[1-]q,R/ES1*jjkdeU ~QF/f躆diփN4ehhʈ\td2I0$p8^ڰl|y3DQQ"vAII /_6ruÔz-HEFFFhkk3D7+WHR|>hrFFFnuPZ0`zɻ(bad Xuh9;1e2H.z6k@V+r,\Z4q}u]3ȹ˖-eηq:c2!I)]gц v}|4UgeʊȌ }dc8B!t]Ct7lE}xvZ'SYSsKRByfn7T'NpE/_ΦM \ni!rzlv;555\.L&---l6n݊f̈́aZ[[Yn- j(.v󵯯d'yI%FG8V^xa5rvfE&AH[ # *9=lfI"4!q;l8,=wۣ(q.M K8я4OT'} lNlb]!tQ"w 52b g̬\h{G.k~$rR/js!J"(883Qz"JJ&kۙLUU$ ͸Sdڈbx^JKKFS[[Kww7~͆ cZwM7|Ik(I<(8FjB5fW# H'蝝 :/~OcrUu#8qpy d2y cFҮ{C=y-**bǎyϟ>8Ĺ3gPiJA,F^=eq4t21!#bƢhJ5]CҴv ? `ZhmniٲYyA DX~=PEQ룭:6mt+Ɇ,Zٌ6VݴiL6멯' Q__oԔzl89Ğl@nj岰fM555)da3:ȪUU,YR6S(jo_ @sWA&nՌn[t]rpBX ҥKAS&adQ~*ϓiVmFt{ASnl "qtŭЙbV,/N5E0k(Dc)9׃)&dҢ<%IXz5$G:bOF6%Loƾ1_@O86r+++r tEQ$KH& -[vJ%D"3 \iiu䮴s8f\7u? bg'fLi?#?lrLWb!//G枴O^"=ccc|~慼 'rTTT3]f裏r/9~"(DEEݜ8q[D"( n{^>*?7EQHRFjY$!Q[[k.7\d /կɜr믿N[[LfBb4+lٲe6Sbvf;|e`'|W^yJ:~3>y;l'?!8AdQarʐM'㚦3Lye%.L]IMoMM8sYJ}~>~mج'۱crFr[*-*$I|9ct q"XhJBr9;vKȲzcvM)7 L&}G۸&HL$d(R_=GZ4} l]ױZ,]۷O}v.]jf2kAbDM(1, ^ {$ao:>•XHy|vMCuTMnc,Y®]흗5LE>#B4W7É'nzd'|rGAH&ttt( @iB|$ HPň8E"/ҥK7… Xss3drsO$B!aÆ[$of˗gO> SO+P]]~Ƨ~:zO6USUŖGEetUCWucN׾}|>XZj4 MU,޸MEz{Q&' pFQUEQd2t2u혻xA03V__Occ#pT B5U+v?#T& 8||~v&N' XvJo>~m=:oBČ-fSݘ{l̚ƢӃu,kdS di)} |z&Ig[ 29&5kﬢ!{Y Ib4`Y6 ?F8@CnE@ pC"I%%%SWW7qoiv:oH$F$ITBl|sd ˞LDmSQk,0Eu.]Ν;%qjZ^yÁ\ZMX^IG={|x띏kyjx=}~!ccc)++cӦMF!LjBlٲCq̙O.cӦMFʝ /qL&%Z3 `6QUY#oedQ^^N:xM&EVkժU|{cӦM%I9Hl1t]G2p{w`6\.$oݺ\)L:lSUL'ۼ߄;S\.ƌv;.D"Q`L"GwruIQ3q((9JXQK83B܄2|&o yÇ9wى,lذa^":VtB~fQN݆t"^l6TJ?4H.`a/e&n:HHpH4>{@m2h,I,j,xx"͇.=l>;p8,it@ `8 V+&DnVjFEE5_\\`~~?XbNIoϸ'scIJ?o$Ӝ?~F I<-d^d`Аᑜ(½e Lk0M6"x!̷:0JKC<3~{yI+"e25- d2ɡC8z7?PTT4v"ÞhF" TNrN;v/_/̛ohizjoxF-~wDeYf7\&oGa2~x^ 8z޻??:;V&3~?B&C^ZNv,LkΝ6fIw,z?׾뽩v6GMӧ_.5dY#nN6^ͭ3Dr9E2j4 `(H}P$" ?_A*#cY+ѯ e cwm*\.g^xu͋g4$|N;f]Pdֱ.߈}N׬uӋe&a~4J6-܎( G߾O2#`9E;݅l]׀${uds|"gIFeds)\`oLI 6"hXn[A# |/[={xfDQ9’%Ksn~߸kۇf׿5}56}wpTUU͋Mٰ[G9һv2f !UTpO(7t >P0~xlc5!(tTUAQUU-[@ǎ3:%\.gL&#b*CCCq(K.5y~'s~ASS֭㣏>BQd…T4fI;N^}Uf3:m۶y^5]Q}R!m}#&%.xW@d_Cy O?ΎtQрeg)mh2꜋k&pe~c|,FD~_M/(g8eB r /:,MNy_O$KD1f+xL͗g珿55@ #c\2ڒ)´${;0U2 cv*z&|<(&m]E7gq:ɂ hLeb~2ņ˱%MdqtMEU4UAtů_yN&%λAqq1_ظq{oVH&lٲbW\aŊBR[`yOKn{H6!95oO׏7bSZZ`Ev;I}!~l55W5'mM&v&p@B%OQ09MΟE pЃIm9Mg(2荤4[l2?~4EAdе,.ADMg$&Vՠ` )T9 (MY4_|o6ht)mp!N'?<ſ}2y饗PU_CMj&aEґBy]C*h ճB߁]ASd4̈́yl:R{z0Vm!Ѩ`ikk3Btp$BnVׯG0I=J"$LbP`ʕ+455a6hrW}INRdd,\fJ@Q5b4DwWd22Xt:u@\u{FeYX4􀦑Mi4ŮQbv_1:ЁX4&3;9{Ķul^YeJenaXf ^X,FQQ R):::H$x<HwoeiKX-BNJiȶA hk I 1: %ȘɅ$ w4Ivfh$ƢДFxln`A})(Jg{Ƿ5aY\NW}UZs,[o@V[r1::JEER["&Nd3YMqYG""˱{n2 c1LvΞ=ol6 ڭ#=O<Č)D(r 8q5 6`2rݻ0P*K.R4:y[+$ϧ^Orr~Ŵ z(2M~)ocVUM&SiT A *7$vc09mkp m™7kT\N'O=o-SO=e{wGQz{{)..x'뚡|V3~z)ӉC8wիCQ +rC_6@݄HlqMefxNki\!c=<'Nl۶۶/N\`##Z4EPQQLhXwl窦v$S \vJ04=-\(.ԥ>d(,̛oɅ ErE!N< Ph4ʮ]r|>JJƯU8ĉ Jضmm̋ >7jkk)))23OX͹ g(O$)]##m6l00OQtRA:8H׈E"^}{Q}pj2[G@ ,mػ3=֭[ohƋ/Crci„X4B 0T Gv\.0,/6X,|>a[VDQ4Gҋ ,  N)Xso 2Of׮]cXpb=~v.q}Ycω{>'|` y-[vλi6"2,ǎ… ꫯ"Ϻu|3E~afyq 8AבFG1BW,'S##8O0 `t$h ,cSU20hN&zu4AC'_ aqHDQdդR)?b͚5뺑.ɓ'Y|K7,$xz{zHOٰa4"2gϞIUUm+Ff~ѩ8,U7[*--% xeٸ?|PU .{kS/NJ?݆1H$|r9p8%0ct]7ȑ#Ƣu"yvde$qIEE,\°NU^lg,MBe6PAI  \O>Iiy-$I^{!*ߏe顾Z蠽MXf 6m ccc:tՅ#_f9IKrYe i- wL1z{8sEQ))qGn'dd 29F(Un.ԉwq] ˆ4aR5" F#Q;{Rl6+Wㆇ5y`E,L(.Lb__!RYκ6$#wKD!P&vIt4;Ky+Rk$KSYT:O]M&eeeDQFFF(.***]]]$ .(`fb1=@"JJJ}:u σ>7 B##?{$y{Ns:LL$A AH (Y%YZ^,ez_*]uwW.[Wk]%HZ2<LNsC9 $&AM fz{yO?IBDe墯nvEGG/"Ӽk mj:ܲ,T* cj.\pֺu9gRWfgQqKhDB-R2B{;?s:uWnrdP{ߣ?D]cPv=_ IDATVݡr(XEP@$<Ǐu? h;w.˂$I .EV v222ص6J R~zC 4M|>P|>,NO:݈ I[ ۺem֣g׮].ccc2JZF$ӟ4ǎK_OիW;;+lg_}UTUߴ{DQ>-;v)۝~#QhF,FK {EpiXHji^}̕+d.}| _6.k8vMX7ڵk|F;5rur8W>fo@~0WNE&pf곮sY[fU]a3Ŵ S{]Էީ^ʲ NsjoofnM+lY~OsRFYHg\13^8XL Q*=r#ܿT*Mvn:<ĎȖ[[A$] "Sh[~q.^7@IY\\Zn砫,//0MrD0$8<a+ NO?퀁~\.iٳiEa\B.^3 hgyIΞ=˙3gq>n4RkJRj5\.Cyga̷"".\Rɘ33]kԴ[a4iRJIHG?.UE$BGP?wobT0 njZ:kpOZT`o]LOOqfff ߿R?Y9z(l>}H$┛J$,"P) ].ӌOpug'uP( Et;[oZ#6~Ξ=L_VD"|Gu(,GGG?׿u.^LsVNpb|#qǝ?vغI͡c /pmv]] wp$wDQ$p!Q&\{, dYξ&S/"dt@V=D:EaΝ?ޡڹs',ݻv~{xez댏squs+_Se%@OGӌo~ D[2 "NM 2w_'Ɗ4v.^@Ud:^Ό-1 +r^E ieUettcNt|v8.) |y,sy&O¸s D,JBO( 3gxf_'؁WumljEд ;Ȳ,I^g~~Ԙ(2mm~tݠRR7t ]4MJzH8B[[CCC^0lc``,ɓKj5*=j޽>_s؟TU0 z)>8A p,h4G?Q ϲ,]]|_?3HގZe~0vΝ<9A]>N^fQ#mxd倚l6oŋVx<4Mi_DBV'14ˁ.<{̽B̴,d,`.UgBG;>](2/reê!ШO9~b׍-|~EazzکEYSjȱ,ˈH @u:::8hWXdbb R#csD"=ʏc<_W硇"8 ac2eYahe>G\F5)nB~׊ IPu{\ssvhEY!Kx_}0.U]۲^R A>9{ȁzN*bzz1ff,ȑ#q|>S'l|>9,׿dY>8`Љb@:Q,_fnn.=ʃ>t۱Eq=kkL[&VN^OplTسb?<%ˏ(<TUos=s=f\B!gZ׾5^}U&&&ѣk, EUy{_t&C%`Z&$FDAP [p1>/ѳc28}4ǏG?(sNFFF1_vVx 8 b'G*.8QrԘI;ZS0AO=4@'S,[V9_׺rOml_@$c9uyK3i"Ʉ WLJb+KAӲh~ >|c6!$ l6K&!CAPUթi6sy )J.|>~}QR 0$ Yyꩧ:uB@RA$Egaǎۆe$Q$:(ИO8Nܿ я~L&TjYCZ (DyŹ9Nfi ]oԽ9 J;x#DxbdY8w׮]K. O( VuT*}cXBaZoȿCbG$A/,1Ka^Z_$ ?(H ou:'XQ2|H$c=)Ҧ x]>+k#7UU9z(@3gP k[(c=ƕ+WHHА3w;~wtIX GF=ho􆢸e1`aô;5ѭn2yO<'Ns ^uNVޒ;U@=zx<~ǃlcǎ-?"Ban_`2uO<  L榧v ZQau04:dSm1T**eek[EΞťHƗtEr >3d2%#lgC4[_2KK Np;5VKgU[hwJݻ89[oộs@f[1Ǿu-A;aJ7|i] @&ᡇDk8d=X ڌm-s~Pl~Z_*@ efJ[!fJTs|ാ'RB7k4JXuixB#}m]+[ޖwʩݖU^~ƹEt+je끻|حT{Ui%V07ch '>彵W?l˶Wi;E^mٖmٖmٖmyO!Qt %vv8eK-۲-۲-۲-eY*'D4eQ&VͶ-۲-۲-rb׮];s amhd2LLLi;hkkcpppo˶l˶l˶w{nz (pʺ3??,;w\mٖmٖmٖm`0s{[VihF8 Q Br۲-۲-۲-۲-۲ [=dMcڵ- $e56f"IMK0Ŀ{LD n7'K$1YW;A2j=ﴭ܄`ݴ]u {nf׽f.Ve7wo6iO@akg(@Dfr,(.67]>[D^rr|tKEQvV(ͱ\iqޑEĆIbOaic("H>EN/4z*Tݻw o;Rp)ػwڦG{X +zxN]d{it4MREL$D4ι.]wjEVVCo>ֿ, h𩪪zo0WN\9)uVRT:ũS'H&nn\ny(7A*".o/Gw"A W eׇnԫy^>Y[G"sssI^!gYR'O"lI>gjj Ogg2x뭷ݝf1r }J'ѧת薀X8  Y;{BQAh;[ͫEio7-q3X,255E6%ze;;; HT*E{{FWV`l @Jkw_ n(Kw%e@7tz{x"nuE Xv 8pǻnx<mT*o#T(cb2x=$I__rmg϶<(o 9~2XB^mR[o񳅦QdQ> _쳈DIV#ˑH$rj5 @$TU% N0DUU(ʆ eY,..1m* PUyk*Uڴv<ݵJ鿢H|HJE3ppo8~鴛!\ * $!I.k(,NfffشT*LLL ===Vpp8a0::+JTO 3bt0v$?ы%Dfd:wf5z#q-_aP(J%t]w ֆ(ȲL^wJT6K:~8d! hydСCGU-vT:fiiL&CV4MDQDQb1TUݒ(dRarr:DD"wMouu'pAM{Y΅TkU>ǎrDQd~az&'ۙL{{`YZ%JrCȲkݑipMaJC$:]tȒL*baikW tww( ji&&&RP.ftuumL" 7OnpMsal߾R2;O}Cu.Աըy²``,NaIZeiiEj۷`0Bur\rUUp6}WWZA?޼pe~C}7EE_][$%{GjUK(rirHw`!`XP3`v1G&WLLLPVy|XER4M`V0Bb؆rE"ƭ]י׻5`\~$J0U1r @>/%$ U,4䭹)vx\a(v$(zbcaTUrJYD"EZFP NS(4 ޾iV\&DT*1778~;QJ!vPU 4ݛzٽOLLLP*B8AzN>'JqyEn<Ϧelί]שT*y2 |RaȲa x^\.צ$IiZ,ΪqF@,q+!E__)ҏ <zoUU7eJ àH$d2)Ju7 sI  tClX =E,,8WZKmM5 r$IkVl6,M9idVM__5HH8r J%(D`0耰 )a`h'b?tףcjdQhOw3^CF Mσ g[*,b$rN,bii'cziA {?V*qw`!2h9f+EOQ}SIC* dH?6zR.Tv{yro78:xzy:ν8<olFu,|M__mmm@^G4#A.#255E$T*Q(D"~i: 2LNNL&ߎ>8{qR[R6M|>ϵkH&~q\hF&qDQvRAэ߫:R"DD"ATBQnurdEQbq֞6 :4 `YM6m|u 'd_Ǒ#GO~t:M__.}ktwwӳ,e'nִ|l6Չ:F%j(p\J'gvnO0nIeYe2QVm-,o(Ξ#@>@*,,[oؿS.p'篾J{G{MIiL&@2Ӡ IDATUUG$Ai,.-/稛#ET"~v˗$@ @"4Gp<,n(.xL<-.ôM#s GERd-$C TK"* 3"UXEPp"+q:::c LMM5iF,=$;QeٴTUPxۿ$HF=/]èÇx'LW!9D,#`RjZ@O7z;HÜ쥔'K {4~s J'=""jJru\|>74#oTr\.G0$NqEqo~q, kճ|>ϥKqdYfiivUױ,`0HOO ?={޾nc+t~{177G\C[[UU0MrϓfcC=>*l :\M_ ) 9xu>_ 8p g [\z!2nUSY%\bd2Avtw ]72%` 薁(XDQ</K%Rp(B8ctX,ȲL0t:Kr"ժZ#X(L*>5:Ͳ͐rٳg ڵ EQn$(273_44āCmJ3UP`zz(^RɅk,jsd%l"H&N;wDRԦ+eYfvqm&ggxU9\^m*Œv<]( -|5(%QDA,z[7M ũigyHR3?}黆a8}e!FeF;n:|f2BЪx ])#%KKKC֭YQx -Z*CG˘SW8`α[)_nWTSLdq*t082+`>^!t~nI6T*A$^L&)JP`f Av܉,..n+Bgg'Νciiz{{)hF^wH&Ǒ$%~S+ EEX,R׉D"qL^/M( ,..H$MӘFQf=׭t~9Su)O_4RVY\\4Mz{{ N6n]J   fI&\vͱ-ٝ;ڒy2C"3V#dYˮ^SԨVj˞5?HkUihu>>s?/?4urىx/bYBЖլڽ9v&dll UUNoNoo/nJB:Fex|͎̀\N$a0<>NTb^w]Q;yzJؑn{U*) [`&]]]sl­S㪪:YyEMXa˲ yz01M6?}AgťKeY5>o}'|@U-i/HӔE^p8eM " . aY3Sd 薅(A<LE$}Ģ 9ӹ\˲r*!zEj7HUQűm} u5 1 Nu]_pWqL40LsSx]5Mc~~]vQ՘mwMKWr*d$! zB|>Ooo/`ENL2dh wbs(nZ͖eKy banP.n@@wQh#-O;q]CWW;FXuL x"<{,$[V?`ّ•NuwttvoHO_F\.cn&sss/ ЃdK rX,FVADu#F->ȃQ޺0ApigEd 1Ew}m@Wy4ț2D_;9se˲qE-AͫB^hP_˴EXHF7YZoa(;(.Iw~sb4Q,-rhFWF8;~=QTM4 uS#bز$ YQUP(DRaiiεEEEb;w/kT "weHsC \!#V8s GS\NJ,`fS;vZ>z{{e٩so+uۺSՖȲ4d2IZeN#300'z*L8L&Z8jγqj$ bS~l? ի~\.TH$z;y}mzŕ:owQj<%LݠFgg'LNNuMр2~!fs*`!꺁^;#ӴR͏|a <#r9^z%9|LQkޅ0uAv,LD0(aa"$6"$P`|bH$JGkmU,qkkϒP٧$IY*rwz ){Ϸ~ d6Jetܰlp\tuuzeaa0[X &IHv*\,rL AM'""! <;|e,b  7, #&.YbgoM*[ͧ V%aE{UEFpۻc6-XJ9w~م,|ѡn:۩yϬZsہi:.(\t1p6 L py*rj :N&Jw̭#W6! @:FUe)I^Au,Go{OX`T-!¨i@wk_n4,W9XOwxo9lRp4ghAOuo|Mji(Pыl6Q޽Ǐo>yqqߏwXH&x]ut:M pa~c ꚁ{kc!F6Ewt~Ҋ犢vht]q5cP8&ˑ1 cBtufȩ_Ŗ6F]Rcd}6ms\6 S,[6MxP` K3H?FlU, eǘt+#2.˩Y\!]mf% MOO;@aY%|qTL9` xuDΒFAB,vL"gkx},n-+P.dS))ccc0?l&jRVj 8Ů.~^/CCC$ R4V*dYt]_w4^&JfTU~AʟAp~֢c6ӝk;977ەj,IZA{#s<` x RCӴ wM?hΊ4FYz%H //|)uۼ7EU,,52ݨ1LQj t AEA d6 e1:I8w7=wk(:T)]Cc?4QI( N~[69U7 Lz- nͩ2ֆ6M{L&) :Dl:MYv76(\oǽg2voR"nSX&`8 /M|6/Mj]nDAX5ʾV *2vwcA fgH 42/nsGٳ1ȴD٭h(255Ǝڼ7ۣ=d2逽ap{΄̨.~[NC*#FvE oP*(n!Y sDI"ˋSvu(\.j5~v޽l=WcLi,lkkk0W4}8&H8Yͮ.N4ŋ8pfjD;e /H$fg;6 (5lӴl.'aYN]o`q~h$ʇ?׿Ώ~#~{Sf\'o:C}?W^+7,߱e3 A0Ơ* 0YJ,13;M[=GqI..]H\fxpd]mvHR,..:l*Q(ڶ TJB6=6p1i ZnKKKT*똦0ͬLe4 JZbi[Z"|F"0vw7~3^yƘ%:\r]ٵk7OGT*9WϷ@}3SRnOpIv}% z5T2O˳)r\:E$3338qɒ͑#Gٰc(k1ѷ%{SK0ٱi`S"F:Y;CC<Kgg'?f$QPULL26v>❝u0,K-QD Q9fH&7@V͓op9dD↬RD=nT*o͂f7'h -'*Zun43sej#RVd2啽pd2ji&^}4.ILMq7@YgeW/_~ӧOcvڅ$:zA>so&_J2b(Hggo »خ^a .EyQPTn8mڊ׮]sr!fȕ7 ;w\DR^)ݵgY$I"/9|M;S&%0nNuS7uqlOke 岁͑J8uo>۷n绕HE癟geffT* ^A6,d:&e⹕ΧiDQ/>{krn񲝧;vowM.RZΝNCuN>~3* w&/ӃM0Zkp_װbβ#cO-&ks+ȑI?& rСM[ž"< LO7jp CoF# #JRkWj޵h,F"u]$K_Su;֖ePTUuK}}}x<JT I}ōc5 xIj SǮն WX) .LWSdܪinN$guwu~a~_2<v(NÆ[6۲,ժ4$IgΜ^#c/C7>>NOOvN=NFCQ6[z^Ӳ,CWE c;9vv{֦ȳ9W͓~bi;|t /\+BGG=@piE@Othkܛ4&H HfѢQkg8IJ$}(O*_$ :Q{zy,Z=shB'z*]Ap!Ldqq9܊ʁ.M399( ;W^\.mxIJ[UUo7|>(ssseX6[ֶl*q7MdDbL1^`$z\0XHє,M\"[$I[0 O.sul?QW1=D`lql|>O 'ՅoԳPY-F,Y:uuk01uz{{ٱcǺeiSF IDAT[uQ 2ZM'(|f z{UDQ`r>K ̔/9A4$_4MtV¢(K*/g͵R@ЏEWH[TVql/fFFlywN$,I  $ڴ6-^mt x)Z o!8.Yl4}l3Xeǒtyit<Ϲsu;;6::j(,i/[2K*uZ{0rUK|f kЃX95FanYFk*fߦLP<2Za&3.$~-2NL&co^ڎpyI K2ˡ( 555v6ktT*޽{f:tD"agCQQQ wbn֭[ffI"L(޺X oXJBauf)k~hhhɍ\^l  G:~ͽB$)|>$ 0e1JG ~I8B/gVX'qU t)l5MCpڬL4̓wd5Jb7~y<=CȲ^Z[ڨ Cd+3D"nLQRpx}B#G fflڸiY=V__mm-dyϼ;,쑑rNӮ>^ɸ/3p7i, U)j\݀#]U>_T%-]{l C1~ۻ,ǹp5\-4Yf ]]]V2@6e*E!Do/#-2p8x<x2>6Š0H%'*$3 Mm$1ڇ$$# dE~!+ju=SO8*x=Mq0MTUg"a`t1x] l[1(tq9\ DŁ!M;Ht󆕋rN5 92OYFXg4M"D"A"pJg liiY4(1T {NPwX˪Pƅ4{6pxmBGț4AYф ˧edQ@afbg} p K|ս,(b;?mƭV)$Tnv pؒwD)TUm'&&o+_ ccc8l*dϞ=TWWw*, &fI&K2)]}>T EQlvxb|&9kiP((*IG% 裏ڕ鎎x嗙bƍAFFF0M?e fggn!f;]rq8LLhin"Xl zTSST>,ܹtSU)r``0h'TU%̞={8qoy[ڵ|>D"%=4OiIvR9UCSu*zB`QL֜Chllo9vo}+>oI0 ڸG|>?\ci:;FxFdž"XI}}`?== O8464yY&(d'ϭgr^jRg4Mxgm۶Dl*s=GXdl۶ͮ+۷õ^K{{Eח_~9HA0]$]ra%o [\.7!ԗ߿u!2:taE+W B сa <1*dqUeiY=5ܳQEmʢ(PbH,tV]< hu VzÔFmdZ꠨ޕ٠/%`JS<tTho KGY\$8>``4+dr׵ ۡw+`g^t _N[Ò$4DID;YUUe7Y2|3g퍍#Pd1@&011A>'J6U-d26-&&۵&ˍ܁xH%wBk8~NMC VrhF }13kT6 xn7>.9)4Gew22s7FkΊ"PKEbɇ{Qѣx<;+_ PYY$Ilذk|;ANyGxgxB~Kr ijjz&dl=󉩧B`b)b yPUǟMuFfggF֒嘘S]u-sX,Ƶ^kW9|0333lڴp8?ϑ#Gli2%'͒)p[M %:QmkLg !h5FJ`U9Q:]l6s=G0믷bG E~Rztx|.Okk+uu4{[^MhHDE ]H.[XQWl-}m@ @]]$d3Y`pٲۺ3<<ÇmO+WKTc8vvٰaNt:m+,9'_z.:-$0"ք ) Ü5M+X a/r /Յ S(رc_۷SOzjfzz*O&jj(^{-"H~nLO3CU"Kh^rD1f` (eD4T]&*42(y M+6|;Im͚5DQqR=7ouIrlbHUUmRT__?ԑbppJEaffmOMM ZU Vg46mZtvԄ \r$! ˾Ku]g˵Injjj[n! /sNzZZZ %QjI=,%YL@7y(]0w41Lc>U_ ''') $I}YۉK^x>fffUr1CRZZZLNM$.\.[hhnn!F_ҴyjOsJkWJiva8N9\?K2'c$ITUU(D"p8 *-pQ;FӶ5ttt٬a8_Ɍޭ2uG}$\dw(8qqvڅ$IaO8Ga޽Igg'Ln# p۷jR0~Uy%z {޹oxL^0T @(!L"NJI0Hz]p􌫈/Ųw4% RSSC8^vs0I4*,щNM Ԅ}lh=dska&*k,c|9pA㪸xMiea0p,-kt:bf(;>U Yg@t{5y9G^᠐gN[kles,SV|><3s Lֶ 8obGyGEƕt5􎶲{tgY$':Bx6Fx"8RxCW@nh4քfp8Ԕ$(".V\YEQJ*^Np8|֬\itvv BEH$HR[ àH$Bgg'뮻իyʋ%@( uuuhĄnb%H&''ÞAtFrhjjZК2>oQ\.GCCye{ee% E<>99iKA,LNN288h;ىbÆ R1 mۮczzt!JطU, LJ .8"$_@jhz ˌ0Q]]֭[illƦ&-G,$.]NAz}08fS $Y}%--xܞej :ŚgPX,"I2 <Xvo"e7+2/r^T^j(7cG;V^0MOǤge-oltRnހl6d*8ҿ`).ihk+MM߲z3ʣjWXYnv4ihhX*B;111a YX,$UAVP-Q(Vٓx-ȢC*ru!RP5'fyn" ّ"|=W!=gQf$Ibff.![sjQ /K %I"rrO<SSSh멬W_SA`ժULLLJxgfر^; r٠{hhL&CUU-]x*p@\kjjXbuh4 ZrET" mFJE&_'N!{9V{8G iXQW?Yg4Z2~R,rKsafzc='Dزe MMMB!~j҆ PYQk=;A>qe4]+:"l?@s[  y5wS=EEJRWWg;Yg Mp2mmm]&VCԔY( %Ξ{FD"ʱR'kyC4T/=P_ejB[3x=^$b/K D2…WǴgLdrrdj68n"\SR0f|l)e +j=EpWS=8kpU  \CEf39FŘ`1Vi:EzUY1 +n)XV u`׻,}ںu+/`>pM7F(bHiq***ezzD"aQ{nCh0[u222B2%s)RUUu̫u)TWW>V< PSS 7@mmbSFtMC5ɉRkٜϜ93ˇM0 w+O"```Y|>Vnhs.r1q{(TM&Q )(]Csi4(}Ys?K&]9z(333ˊAt]?xYRFxӎlZ`s38x* Vi8].~ [kHfȊ4 yV+hU.{Հf=&JAp2ar9))B!ffflT*/7D'H033C4=/Hdl%0&$7É29 f{8駿0^' Q^+ &g(?j#UeGVGP ٍ\2ι\ qLOOcs#l޼5[Z+i&~?{@0dlٲe(  kzN5`z1 ֚ i] ~zw؄秳C9dYֶ%I.JKkZ[홫~:1;;7 Xc ]' 8g*:iY-ˮgzyBrs9&"KʜSG.㥗^bÆ K/bd2̰e˖E[]g[bsuL&C2P(؎e糘P \]ܰD\6 eg/0 :;;f\.jjj>VxnX[8c:N/xdTy{% YQQaS-.rtvvvŃOׂceoAСCv?DUUռ楮*[a69KgaFGGikkyIT{?^?~'N, Ϩ Wj3@(E@+Y.\l}!\ l/1t6GQ5E Qp;dI$TWƅ5b\^Q^b<&Z3+8 g: UvzN.5w݄ϔE\^5}|>= R){*_gu %:ǃ"+r; ;-4/2.0L"tXԁKO]l׺PC> l q0q*7-@`)q X?~X*3}徔u/[`%7~֯rZ?o.se\WE WU W&uWƕqe]]g<4Qo5p^&\EQXʸ2+ʸ2+ʸ2<8_L[`dttH$s͂D,a)LLLЀl s\ck,TҼTH2r]&Gh^{V: %=KWLe-crgzY,'n8/I|akE## Y(nBPvN<}ژXV.%<''20ͯ$T)kD69K}<(̵_8V 2OJ&œ<-FJqMTы_qrOB)֚ _&`(& )\In^.%/Cp9S:`nܠ:11a$ʡnZk .?@ )BK q=Z-[PQQad8xi:\qA5`fRgj ٺŗ,̙&$CųRF GsIB! ?8"D4=5T!4,yECщD#\M4Md2 ݁W1LUvRN.>LK C~7auiwxi Ƒi<@:Bl:,@ pF?08r0^~I e| sL)iYgLr^#lD]v'hkn8#c8 5Я ;8i'']E([: HdC֔ ԋ  Me4>eÁ_Qו~%{?|}pR]],f0-%O~Z.z.?3~mEE& 0h\ĠCwfqWO&/>|7~Gq\W̛|iy6mDccEո=u 8JxB҂"k|;EeV?٧7>B k0'Pz|螏7[۴q׃?}C|@q77O}V>O=}Gb/Ǿ W7գ+Lmv~/xۉ T nm`qs\~1hkkcݺuLibbݻws[e6W+_cڭ|}F/r<>˛}w~cL|%>^ [o=gBc?GjHF7Vqze_j eW g;NkWhI뉌 O\no~Gpp7_fKAxꩧ{OOfi<4}v?0RE7Ͼ^>GIlNG=alތ(K2sNzzz*۰XfX(08+tbKq$@KS#"STx<0nE.n~K?>8t:}ڏ[8Adh]0j]"1/֐$ Y7 b B:G_etfw6xۛo :(``|Qnj$l.L_=Np6.Qv*O{xӎ t]稪&J[=m{ H.<^'yS1nq!b^S RZщqsjN@v%?AGEu4!X4uJ ADǘM T,N\[Qh>A 2$ I8sLJjE#EE RJ2YU% aM-x'S<2kއ?-K#|? 7H (+"ccc}݇ +Wbwx_ȧ?"HeT@n\g<O%nް[v ™.Tv(|Wy]!b&zAףƢÂA8: P x-|ɜd0XWl_P)X~$Ē߾;oog³~rlٲrg@`߾}LLLBcc#UUU%+e˂PLm.T*$4Հ#"#MdWIXuP3i?c:NrYt]`W8Q|"Lq>VG?g+K_2x547֓.~[e[3{WCC4 GT$1/aJɟԆ){1d/֮Be;d]򞷽 E/../tNu3;~vշ(nI.'uE!EC|??!8LfQ|@ = 7O?̓>ȡC$ֆ[Rf p@d8N$IڀEaE (?2A4P!9rĉ|ITxtL|^N祿VY#1n&L],R~_Y4MVĢ|KWo}CcNGu[Z\K|evPj:zx7ƝjSg)ne?m儼Nc}͢?$t^)q@lnQLj8n\.7B' `Lp:9 M"N*qFȧ TT/9dJtX16cTm'<\L@&W{"btbaqaFOAF#sP%?K/EKBukysu8$r;QQddYi6m-rhE^|~7~( &yx3ƧGDXciB$< ֭9^gv49 oZ Eu׎X҆wYgeW̧hssn7LQ>x;q=ȧ>):V>Ǣz -Gz.oή]?ρ#>eNtQ`(\.d2CQhF&5+0&xB .NH|IzբA&9p\Y4M (X!xS+va4c4P(R4~oL˙"L>Nڕϝw=85āA|6>1Ak D2%i#/^fPok|/ H +SgIEmc*NWfA( q${b $Ξq;Jh -j=r(V0@N! ]X{ ޅNaUUPUV{Pd`dC zxj^"uuu NW(T"70Kyy|{p]:].;?6 ?"ң:3P|҂E8|ljV7/N\D=Cd l\$va޻ߵ bK:ES0ϸiE)Yk9^K7m7~K>{w~緹QUuQkUuA ox}{?~uQ__R|Jp\mI;s.ee#feu5P!h/s)Rivf953fc@2ajneJ&;sf8 pU E/Ѳtܤt:p:>Gh%@U~Oͭ[yӵ+ns+ ;OLq[0 Y)?G.ʌLkANCRgGq|*ȹDx{p.x4)sg&cj%ZR)buG8>0A)rQN̂$v{__ $g(>oA:Ŕ sp]YFe8dz>Ǝ7$HȐNNϱ!^9to>B1l"IM]HF ڸBu#}2yQF йf=5 cp&"׿륾I.:eW_}T*ŭʪUN&*3>!, Oyw E?Hh#!."]Eߍ A8z>qJ}E˽uazȺA,%LaUЃt#ÃR%:X$TcSEZb+hs] e{`Ͽz}nV>'[TxOG__7WۀzG_ȱ6`b :/Pө%+0v/S^}Nj]TH4=Yd]&@J= ڕAHgdEV4UOMMH$/~?   x^E]/AH√h9 Lj0.e0O#NcEMχ(Ce>i$u΍h +L$U3|Ǽdtvvy#9}$trfTdd6'.:*[mycÿ} it:Ďkhm/wS3=p[ W)½4=eO[VW,'w+^܃pW()[njgEQo {Eu T#ſzkAĺ?b+RKbV#_}?s]wsހl|I/ٶm_wѣGaƍ444p8E2 (X@{$7{>>{[)"\ʂ6FL&5DQ, *( 3pLJH*uGmk:⷇%/ipx.uVS@vrxoqo\( d2y>o3  4s j@ʔIhJ9Tښ ։"=Ԅ=|oae3}U*؀q Bq=-@HB~!z6ؔƸwYNyXI !e豭ivgn9=3L9[dݦ|.:cv];wh=}1RI2ߍ5Ⱥ.kҧx_aA&_eIgpyu1x?~HwC- (|Cyc_}9)q-WIHL˪UO~Ǫ^J]c3 TTQ]@sk-m,gPVCT\8`5ﻠe~hXnr lO&aaQ~"2NdXґ%iTUZݝ$~~Fk}fhhnڣ9>i&%![X~(F5lSVVFGG?,N6L(X&A67AtxL#BaA)iIq̚5,$dժUرg}͛7qFbcc%!!M$~7$hit,;$̙3#3Ɖ*}fP6~ߏW[ҰY! R\32\)8_z7k=Tk[#0* I{pIV,2:%w3KY8'g6Ϟ ^=@(tNZ:ˉ| /W4YysHB_?QğFKa+p1?A<e嵨đ@u!wtvvW踰c<I$kj(/:O]HTDQY/,9W* Ν,~i_۟qygv;Ƅovqݝ_݄oGϺ{B]̘:9'،zj5i>`ێxB#2eb^/x o{y[yM9,[ݎ5cv=z4VO>aڵ;lذ233INNFQou '1ȕƏa0ba`-*((ѩV7))b@'Zp?ףD̘cpLQJWoPaͬD*A|Xns>l+ai褹MMT),eBl6vH3?UP}gWW/gČ īΛG欓WpINEudYGJTp-7i%q~(RéKd$Q^Q/t@BC א=&Ken7B-HF_!CDbnGORR⠯SV^Nbl$kTґР{Nt h,PkQy%y`MR 2. b6hBۯ!_Kbc`M9 t`4-l's92 DJįt]Hfg.ۄ{wr$ZHv!K2l0M`g +  p9琜8n&%铆L@hG60{L*1aty_5]bvq۶(ںU՘7}ҰoPS UœE?gZnzꖗ0L5sd̞GVT5Ɖs0{X>ڼ ;WdI,]?؃͛ǔ)SXp!^oSvvvo?;#{9zUIPT """Fvad/D7q Ȥ?ۀ=AAfXcDܺ,nBtC2hanCd@]6JN7 YBa}BEV'2erV{ )neqd%ܲkoO8%8(WS;Gfތ8N !F(FLSA(p.!C 7k\ ~5G TbeDnc6ِ wG'!snQ 9 }0yZ#$Wq4M faY(cΙ1-H5Z;``gxӗx&/BUnL8qB]ԩS?~kKDUu5Evq͹`1 Q]G6?#Ť sp-x`aZG8x>IuvE;1Q?De. c}W^}o)cǎ I0Af3 .dҤI|oc޽ד㉊BUo|fH=ѯ@™b)X4eه}д$ͤh3V޲sc!..pH*)u 'ځP!H ߊWB>ذ)n$$Vwqkҕ,; MU%I w wvqe B<pp&d~n3s) ֧P&" >+ŵ4ˡ$8Ig b,SZVa h9csbU('ՂdRZ Ƽ^/bɐhUXV[+F!}൤O[f0@7n~,Y51Bl{kJ3AH Lo1KOE5!Qfᄤ#U aSMJSƍoYR\VErr2ö )**B$N:$2vA84^Z͹a1+x#7*^{Kn z&Ea۱Y,L{ oDYf/:9 p '/_ɮmD U\a9Icsy qI)~8!=kxx8seҤIl޼{2HOOgĉX,o|fHZ+K"C1A XK %7ɨ*Ohr k|?|灻.Z_.aHff&O~Hee5c`4Iő,KG+5uńc6)!={B0}B:3בNHW$1Q_$QXRAvZtkWP"'Spe*;HFd&qPĀA߁ESP_$ǚml9J6А n AHOşIoW?v=b6,eYBrbX0amȆV8lv DLaB@Z19ﰐ{-i׏?ZæNTH,I7'_DuM6bM|2W^Ug,Ѡj6q3s5u(V O<ˏKt ߁cߟ}kڑgxuM/w ag 揜q .B{ʖN8wyg|><țodTC9"?"ANWȊ4Cɓ'i&KLKzw`b:ND5>6""†Vt$ưh+a+n3&fpե<1p8{tvy Ux\q9cC5] 9WvG^UՀ"1K`H6wX^+HcB?̋:j\\@qA[dddEmm%6ļ8MHԖ5r",#%!}5nKj)E-?g&Sb"4I(*$'QՐ}u [,~=2l-WAGA蜆ΦOlb(R^RR{o_T ٤}~^_!!?~!0}tN;~Yn7o <u$!kl5b$ܼg @V )V1cFe2*=~ubȝ>Xh`wX ll_@~I?[Gz ~p<ut:Yj\p> vbݺu0~x0LHij# -~b!7 U06Nt< S @~/^d|laX"Q9x 0Ltvya,I>PD0 hbb]hlKD7k َzC@-0Ovr1<~ڼJO$\0Ėh;) qm:a#Kn!wYiԊb68^MjY;,IHJCkR pnZU[IKT([Hn#+Gie?yqќ% j/EfDV,bL@i.1@2u2@嫐r֡s>s9;^ --a6c/wވ6VZ)eڴiak#m6v;]tVp7Fa>+fc[wTcne`X^|=4۲9|6!(¿N;g^C7s摓baF?yyCUUyyydee>to&p25A+rތ`Vcz.,` qфNϫlc Hx IBJ jn7qtg {2gzn@FXhEB`3<#- pf$:1A$V6C2D)A}"[q8lpuRP&& ̎,vw 5FޡvIg"{W:***aRx-~nr%[)n+Fs['->b%\dd^Vh ؐd2~.p+ m;S!qT j'3Q)}lH?x6z?t?=P!Ѷm;ȮdI xV_gĥ-SJ5A\ĭw^2,!V` HSK,@u?:V4o Xt cƌ z0 ǃl+`ɒ%۬_={PRRBvv6iii\:<]9#$JKKQP li'#2& Al&b<!!+5h U1b{]]$qMWs鹧ŧ锔W;ބ@ \-.R&-3&y#W.(.\6N\ta k8*?^/ɘQ&*Umസ>啵(IݠrAЊFyqGj1UltD8U!|t( 3+HG0dQ A&7v?Xƌ IDATBhA!:yiZVp/+ I E$13LW8k4&K;$qeX0 ]^| 1ёx{ӊƞ/aM `ʸb{(q#xC礟C,P=xvOs67~gč?ZARBܠ ReYfݼIHH@asL&۶mqg0nܸ~k4OsT8fEO#fI]K̆p\$P\}<{Tf%6x qĨ_>;;TUeʸ\o\A/pɵ`Y5_౿g[جVb"i3uL0vyk/co~17m -l!^+Xp!1/v GIek(ӳw^c6v;2J^\-O&o1c26<2#q0 + 3$K%Jʫw"~ V1ZHN:eCfm//DJ@b9#_aFT&xX v~-uV^zƧs̜rƖ.&eI`Q_@Tv7*}PE4EaJ4p:1Pj?Ng1tuujy}44wb ?'[UTlԱxR:VyVYY9FG_Kvzl_v[7ʪ4&#!:j+﻽Ŋ-vp%bc~͛CP!MsC&3-/X7n,O`ܸqdg{uVjK8+p44koeiÖ+I---TTT 2^{mHՋ/QvP"¹?s'Ǘ,`5fSMHi-:J&H!awCuN7TjkhjNvV fiPfpm?}wס$!Z|8ik{=^2SNOf >lw+Ν煂Bhideeܹsyx'ٱceee呝ݫ j# y<~?> PXZAYU-.׷ksZlQ#NݴpokkEҰ嗝9hm^@ЩNVގk D`5(!$Ȼ337, U?&2ˊeTjK%1Ê*O'. *…Ȳr\9|maq&ӐR%I=\P<AKf+(slwlzjdDJMmmVd]fk[·|{_TC;u]$ )$AU/@ ,S蚊mB~?$}Hjs90iגrŝIQxihnJ~p JINNbqL2I0e>]kݝ<0 9אJBB° Yٳg^+Wow8Ǹ{::dbڏIv;q;VkflA' -A ǃ`v:Rz? 5)1!os&̈́>g Rz#<GJ'#%EWK.e9x A 22et̝_T)**l4qQ`hdgepEpڒ^ K*:N QgAQC~N46ъp\j~zw1oqNfF6'J| ,4:b-O̵W奴T22nUSOe`*4vTo4,3zLVKhz\?$C܂یd`C lv{@GDxadrҼ!hIE5 Jsp'k| L: ?=$222Zò-Ir8묳vY)(,;.; (`dǞzsfe2Y/ɯrE|$$ OxIڎ =Sq>ű 2wZρʃ3&¿_yZ9H]ѐi{Ė1?Du&̔D~y%\pbO_ELL̐gz>d"##￟p]wQZZʺuHLLdڴi0wAKs.b2䪞 Xe^yN"'30G<. 8N;H~K}05%cwal!Y9c{tH[on S{qeh[%bKHH57qbVd 8&ʗ$v7Ee;HI#/+I̾R=1V}@ϗ:c&`M7#9t֛ DPŠ{ `CVGoG@Pta 4+-~V!&2KƖ%I2xu^ꪩ1Ag$AYU3x5J+0ܝ^_G>|&Σg2\~V/.䄕B'PqjjCu0-YA?`Oѷl=EE1dg]Yȇ1ٙS^dZؗO}c '/:E9|>t;餤 8cx^)̿ vIa݇I71&#qăVw 0 #Wtw˓Gt.Zڻ8c#. Hu xAܝ!plVS{ )>G{.IT몀?{=>aJ(w7@ېs-f> ϖ=k)eg`mYGtTUm gM.jH=baԨQaNkjj!//s?L&mقM֙5y<&hSo~_rd&ƌh]Auc[X8{ ]E퍌PvG'SM#o1W6a9EG;_XBח_ -ޕ2 y)iQr֮]K+*>,^0[oEIIIopT6DDF]4rrr8Yb/&//H oP:s@=BfYA ]_oB4TȨo荬lr/H8ɺOx_%!j@4Ho`0*+O^P9}Q  by/c@i!0?P!$I4,Ԧ$*`i5Nn8#h*HDC}3ް.X {@mP"cw]{{jRԡv" LDڸ*TOyYC hk9XNZHe9.2SbX2sFN2P3 qځ,_\Θ4{H^R7ѡ&_C5DA&M&L,KCѻ cV&%U$'<,l{OA\YY`+~:KVzQM;s0oRΐwÎ3F~E=M^ fJ>xfqﶻ1;'+x3[OEB|1KRC\lJ- LBb2 y TU/_}kU{yOhnnF!OUU1 ^x3<06o멪!IҰbaa܋jw9GiZHlkS *$eLf EiPZdEB2C9=di`=,('\C'wƁ_ ;KDtۆ2)Ĭ;kodg:N݁]33ބհ +dB/Gf§;3|X\a`Ug}W,eEee-dg hJ#\aÂV"Nt1.$pj}*6mؘԁ# F:aȸ]c~lOD^z}o! k7`ryr ~W jxqf Xym VxC:| Bzjg i k"~8kTMCg5 r2 !z>޼rZ;PlŦXHy~AZS=VcD}}=eee,\ɓ'QT>ۂYqGY;tn:c2t,NwtlC>ď﹇6A/Gt+$~w|EܮxjGn`P' Ysx>N[u3w]{163Oޚx!\ o~xN=T4MO>>@ 0l^.  PEt{{{߈ѥ57a>9$b:{z9Ϟ->ꪂ$*dR$)Ė4>Z>ls /(y 4Fu8䠎l0tvπ1::iT͎mԘBE j<3; K4UFzRqDDEw"w>9ȒЬ$j]H$!.zHJAke? v͌+hŠHe$I~p^@ ȄϽwAۃa w~v"%65B%!l f+[8o߱?C~tۿغ?=ȵz?=,70.'s3&g3e%m]N 356T|XCx}0g2^d4bKGBޟIIL#19!ZZvV#B*_C~EyYAM&^~{Oش?oSY|9a i4 ә4iׯ磏>bTWWIzz: (o,fw!FTf ى"Cl2e) jj}$%"eBA܆)J:- r]{< ҢDޅa"5iQ_ИFo82HeMfr8aF:fBJ|b".)v`~$zAQ 8OHsMۄjh͑k!eTcF3kXRXl2]<=v;cǎ=jhFYt)3fm, R%Ķo޾h، wqۭ!`0'z%| #4}'J@:+5;[nye2Jg\vW]tA BP4`o_ U8MćɎx:,X3$iGҥK9s& ,cڵTTTNvv6qqqi fe;;:qZeb_*`s~'NiiIBԁ S;֙ _ 1U!*NbL؇o/-a͔kduxL~x)U͙ĖxG hS?c̸tt"CSAIqpY6Z[hl꠼jkL|`]o8BGX<Ŋ$PEw5B9at<y- A{:S=D}]QuLA+!AcرzZ촘Gib1[Yx}>l\JX ".i>E)&'=;YP5?\9UV=@NfL?nO̧$hҐ%t)ێ;GAIHB n4Ug̘ N[Y{0mVDfM<^x%Zrss{C8|v/^̤IXl?ٻw/դ1n8"""ؾ}7 U;5d:"_ttttg"'2w?N';= .>/kfĂ?AD&BU7& ~A{K!İ~pBWuy G*d0#]qٳDXv~,k.  l.< 2C9亢hv-N{rpbfwDzJ@5~\^.?`0юf5Y$Ѯ~b'Vq vfZ?III ޶;iVZ0ȅt؎ͱ\(3*# tzUbn啈اqKUjU<+K66ꄆA0{q,z  wZ<'v$+V naD^zw#u׺z5Ȉ:d  AF0MWRY[mqvOg-`h*ǯcǍLQ45!1 ʥIt0_-= yyY]  ˴1r@[7\tp:;Ձ[nϡȑ#Yt)>  g{B9MV,pSg# e TZZڧ@k038;UlAZHڠ\.ptA]u3YʈtÜ3) (Wsͷsɜ1gal˜dƻ30QC0|pQlo>W}]fUlj$4ký^F{QW/tL~;iY y\yuqdy]hk*%DS5 *WۛRn;@avҖJ!C9 L^׫[n7/1~G*_ܣGY?߯)[_%jDT6y; )f^#ֳ mYb#pEgI8[hSY-f'&iITp4tYef$#KVۉ?O%^J8~2-VrOupfyfxWx}L~`w}Grr2sd2IgsQu}UҺn2k\ሊ"I蚁!=6K]#QdMinmcQ\Ao_qxPUs !;|Zu˘cPl? ^G_qT\'#X;S{ M+ͬJއ\c`(7g4bx[˛KH?d&S w?Ko/a͆HO?ÇX>kx@2]dIIE@UeaYMvN'O 4USy|eSwN@礦(!" |]-1ӐqN?}K/_Nry)0 ~mKejZ4wd%:I]e=%pQmw!m瀏$As34wYANbQ upe7IH MC-`gܽnl.^VYkS:) gܐ|r-R+ "j A E5]eK9X6Z¬ lG4/#rl 3Ygޭv'W\ j #ŕlZc`M$bE/ $ ښ5"a݁$I{oq  Ĺchnc11$,f6ONNJtA҄cH="u٢m 0ETBgn&ًi0y]U5w͝(nVe6xߋ0 )b1 }m%>ZʬIiȒnXHtH؃BAgBo=s+&CR)Æ;)|,4>ϏsC(Yr%\xJ`"O=9/^Qyx?)<@%/f⒦. Aϻ_扇%w˲Liy%Uuta,Oއ/=8$ʫjtr1rْ~ 0=Wsݹ$ŻYĝ,_?^Wƌ?9%1!Ͷӹ'F0૷!yKԮ '{Q$I(]QK|y`ۑٚ/;Q&=LN}444( ,zltw3Q>Ԭb<ک\ul_dfn޸rҝր<&cM8 P$!cM W֓zh2V؇1dp_n$oT~0T<&}+:>Eg]lF3 v$Auc;+6V3zIImcLSðǞ==ێ_kk|/TԈWVU5"͜~̙u(K3#(Bif6& pjb/'JKKgn;2 2}YG*|+IHLaܹ=-{$uƍn# m^Z:4uIMS;z-BN'#ifl6c6lv, V^ Pl[ kXAy+_sᄓ9 NQUx4x[ii05QcԆq.&l=57 /q|l.>u}l(%Işra Qv&.EL oLUâ F)Urc1`65+I'>P @F&cH:=~ZL܍42Adj/;ʄB!"i$ǑΨ!Òcx"9TTe @! vC#sY?Y1bItǒKB-j[`#,]#;% wYT #EK{@{"EsO7+ un# $^2/m9P5wB6 >`+ !1 s9䞚K/s˃OsIX>:! C'n YI0({F:l\~BSP/K_Xlb˜Ử;뾗BtF /EZ@'cS}MEEE?aU(cQ_uMXbRb=%!ۡ)JC7 Pכ=>`l6t2 :nIhij杏`Όax(;hhb`-<݇eͳ,zOB[XFU0#yט8nP~ՆcŷJWA "#5+/85Jl<}¹Cl,vI11~uj72Fc{Ӂ({wy0S:3O< vO'_AYVylqcez=\^^[i $̲>V~`d;ǝ[|܅ Dt6)2IqNl8.p`:G>B    |]! ˩[Klf3f‘I63ogꤱS\L ~E|߉rp:ĸ#@A8zhBnNZFUtt9jrl J34qis,d)@xcl)L\)OhAԕYNfBw0HnM;3ZW"eAs]:T '# i Őq8hUIJ$tGes̃0t8`D./{.~C$0_.[ǁ0)28~|R^\::7{x?_3rFKJX[\K[_YWԉA TN'`ś+y7&0Mx}AJ & 2 $e#+-ܴD6(VJT^ʗ˫x u t7{'{C\n+T4 6:_~9f{wGk/Ueqع'8s("aE'4S%r&@&+11ΪNo/kڴ ob^ Yyƫ9ϗ1t]lXЉ!h<ߠC)AEfsAY99uYx }U|7L;̴ݲNI[ L2A.8)QbVDxyWlcv$A{ ͝I{[k5`JNǤ(hbb۫B4TUEUa"J)//m_bgS IG_\gXX[zsN>)n0 ~Ƽtt Ou8%6TP^]wUUC*(4C^0.E R43N KR|"$FdX'Og¸;:~O8-9}_? DnkSNӴE:cZ(rɋՉ7 fbl\9W?K/Ȓg\uË'hΑ<@\x˱(EB?_awŘ6?W^T׷u)2ͼG%h ٥7p:[.H@S5dEjo. u 51ϸc}Of0$<r2;}l q k( 1aƎgZ,DQn֯b++Y~3O攪GX51D\+ّѣᔎe[<~){T.K+h5cӯcִCX,w}}Yz=Ȳq\,Hc§ldك0d@NfERQEѓnQUEfȀr29i$~u9n]{-'Nd1G6q[vC̀d( H ̲`$ K֩Q&1ݾOHe$֭'=ɵ]ECm2B@Eq/GS!>YLv_T ڛi{wFG\&H$kcRl^̈́ ?}!{8חP\VɦJ*kwݵfTͅlb+K"=~OoKpdn*4v+eci9eewRUWL|a %3,1ID4h쇝(CcqwJUinWۍV`@f{B0~x71N,u/`ve6κq10?,sCRbmmm[˒DAZr,q;O9m#~;-MM@ .袞-_Dkk++˵<ӱ jWfʰ4N&a,nv<{T~(O:頦iX-f2R ();穧 sW[!D"f3N'VTQ? ff jr9FhC1!)BQ\\.ӏ.uY108զGTӅ|oc84'qıc8Q IDAT4L2QC"D) 4UuśPZɧ+yRbVqИ2~ '9~I#Hu ̀{]'q{}݈Dt\{<JavyYi&#wٙjNRy7Wqs%_}g_}7#d&gj5oCdыj 79 b ag_wNcNlljb`~N-CU9d(k؝60ʀP:qZ_]BPvv ^^$6m/a !9ӆRY| RA ={6yyy2,^„!9 -L%*\Qɟf jג!]5nYJemTg\|\0y#7n9tl$AE} ->>hNR4{]Οm\k.w#d38e̞5pK X' 3\O[F ֗ N+\y6 ʪ9#whiJ^VGL$$e`ʚz+(cce=|\Ï?n!^i5؉D"Sue !(f@[AuVhT1nx<}L&Sb87w/ ^s\nBPS]͸ɣ#ZD]@T^d'n2$KޠsmwMc*,Dgg%¬iSIG@4-ugDU0ƔM!!E%Ip;l$ctd:۽|_/ n& EՃA~Ny9s(dN|-w-d=քa@EM3Br2*{תV\+,{ƖNh%$xS >{xkFح#+2J)IېP >٧/cgk9b0~qAXe-+12ZWr^Od^zB<>\)+V}'_~SHMJX,{m줢!'x"NW$Jj*8{8w9Ee ,d$>!R;)hrtI}cᅮ &'5ԑKBr8zX/qkʆ^'_.HtwC_wt>^*>B].W;rČPuݓf$~puk-,rd2ce/sȻ(^6WV2>]IbMJ=]B3*8L(+4 f{TZ$:4}!TJ˹[-UWWQ{.Q!?ndFFF݆ln{e}L t]cwU^GTڊvgfKHPp[Aa|>?9|Abť%½``lRk5ރ"H"#OR`nQhkwuĻlV\['B >F|__ɗͧח@ƇKRNڏ6y7Jj~*} KWlb+WQVa҄~Iw_z#OP5s,{1'N܊+d8cqe+mj#ͿHDW2jp(((#;iFAAAu0 VXAfǎ&Kulf֨Lb>ZuU;?+Kg`@QvzdOo5x54)S\mnLzjq7JK+8mmߗX$~07/fqg˲ >Ym,i]/I2%! Mױ[#ɖ,I䱺IQC֗8/HtCxΓgL 8k6N<Ԕ+vB{TFG#)#Y )nyM9 ~+8$iQZT Rq(KB_&'Q?2PXEU5&3w/g}܎.>unKV~ x *HI~7B?'*B08i2FÆ͂IDS?t]HĈ#ةKw77q\j$Iu1) ȮlUXt !zwn)JƻA{'X@WS!(0&tI%ͷ#F '59ÈaZ9n,'vso.GtIOOcRujjjx1B8)I<,eСq;%. w,BDMvv6Itvz٪ͨ߭y~BAX0L눉!..HJJ"))DVk/@J sYsJ s֒I]=GR[Qdd!;KI!'+.赞ݽ7NhLSG$di~e22.W422s8Y2:.4a?YZ G"Q 58V&+bCI}{n6)p9|Jj".A\ffNӷkj1#Ob13Fo 3O vd`ٚ4t?fLʲ-gNڲ3+|t-=$1jԨ=V Xj999qX,>6o7_, 40}5-\qҸ˶ ͡g)ĭW^L(:BHa6;݂˞!6bV@a܅ QЄf'2ax%'a1յd&9}:Gyq&4J 33|BPzm$|#t67g[Cłjp\CVViii,= V$ˣ !0L#H1yAM<c6L`Zذ ?D3HMI(?43($33-GjkQRW1o96Ԓ]٩1١Mhq^8X[?Asy3.L G+7=./x-vYbO\tËaͻ4U5-X@(F}ỉ5^D^VKWlNԅAqE r{GhÒgϾ;Ҽ gH/SfcXzd$Ib{10óVxl m> ڣJ%%%ʔ)S>|xw9zꁤ&ԩ޶aðM{f6 @F[}Z@hKH$:D|c Y6n?.J!:0kylw RSHAA>!ٺERLXl6ʫe/ew}JN8HZ2hbKwKEQP l- Bx^:;;xXXzk;|T,>%lG뼻m%r?U }۰ǸxWxב^hĖn"mE> UYSf܄Ku|ARR2 Y (Ȧ0A *Pd52;.4.$luj8 5MvAsU39YijeIB$d!6iڰc#Xkiu8]b}V3.-sGB,VObs ;$ J*'QSys0!E^Nh:!(WXF.0`c{ 7C!Xmf9_|̠A(h]lN>~߻|_"*)-- ۠] VGoldrI*zC)HoN:/E^sn;V$cEy\~9,~~ 3>6.dv.ײ<> fJe99ĔnYg㡽*9]aw=IC!$ !me{t?] tvvI[[x<0uuu]6ڸEzn5]''#kSmEa^6q]~;Xd:$ow9bƄ_=%IdR8+(Ys!GO${")];Ae7z.t7g˟SbR w=ʸkc6_RUGvAf_0)#';6u nd]q> r)|t6~N^V"^:YUme ֗W[g'Iu}x^_pD#=̀-BPZL^VNջhu׸Y e4^bLͱ{9L,cRRuh%->&LGHի1L}Uzz~TJ+)MӘB.1Ow˪QU b'$I"99y+Ɇ%35k?pX]i!I>#9ʹ[A}m'$p8vy58/IN`cz|>4M}aQ>Zؓ%NDeW\jn$餝H@ ׹{T 3uXfb;?dYk6p_oByٖaml؇؇]KPd_awUoq r)'R0(b1;\N!wAbc3~d7NXLRR(J/OrpDXnGA"N)Yzt_ D;l.Ve:770zc9Gn> D^V_v N˾+^NJb *:a4z0_)*Ǐ㚻>fa\t<7Bef 03A(J# hܟ(oD794x$ExőGdZ[[)++#==3ʬYZN 2ߺg?j|YvO鎦>{eR +vNwB4MtvB-.gՆ$!hgP>/J6 * dl& @200oq|ݺAczr)Oןs..BjKFrMWSA(>寒<)v[>k-ˎ:k_"U']T.x=j8*نq)0/lASgr饗v}:q# /A8tdF ~U$8cmi]4M`4xHp{@G1SILgĴY/^gOC?_uw XUW6CeyrMmfa20ܱ۽PcfN&pMoqߏ"!ŀPO\;Yi-C:C8CwIx!1?Yw- d$"T ;pB#2wNWPww}<+ n>7{/)M0]0!uH8ւ/00H !Z24M#+=ECqqKʑ%#O#ZdŴ0,G.`X>.@)p8?e!)Mm$ kT }4)tp88^z|~¡0jGP atHXdl2}KHvlJ6A2y%\tNͭnQ0 =eX_wc08d-ic\_UF;!#_A0m3Z50d$!#"U]02ѵ,*!KrcW섺<: sz4&Ea >I5eBFFcʭ>247JDz@PC(E7aG!Q=} FMsIJ"'e#356?87S$5-,xsy䞜ÃEu5n2^OmS d&1mPfM;d߷`˦OszTV9ҞYPXk iʏ# <>l33gdM4mҽ).,"(*W^P@ddʾCKtI$;g,?&3$$]4m93v%wK閈EJ z: aTY3P/g&DhX,F,>F)I)IIJ^26/c(>e_BW>$s<Ǔ\[X>&p4i{ )@1q8M n*$b&A UUQ%+幫k0 "0|yt&>#$Z;[l !Dk-Ni\dA&B v3[نJ ;.8M'. n%!D:|"+"((7A$!ws5Mqg1 e{+#I/ix>f` -`K!PjTU/KBB۷o.S\( 6Po'{䁰_z綻*? )x\liلW Fd]) ZFBP\bVSSᣦ~yad]C6 2&1]i̩|pֵ1Bje$If(eԑofCQ,(eEJ@e:Ҽz!)a)Qkdj)ͷe$DX0.WDȣd~~Vmd#ܲ' x{W2*ۅ9X-Tx B@`~׋nYGa=}TK@-|1 f)Ĉ#1ʼn1ݴE+ݴE+h Llҧ4HC)(Q.aavXVpop"Yƥʌ=?&xfeYYx[ X~||56{\_&iRY[Nyy~sIeF?MMM͘csR\T0SmV|iV0_8$~|6}9Gs0rjM5qBvvlBVWPWUr:Ҋ2030p . +sSCCC̸  D$ UUQ˲,Y nsaiLl(8B?L(A$+Hdea{ݰ"Nx7,aAAblnnނz<Mc9 _țOP.:[[ymYXFinn9ִ,j֔Q"2(w~)#2]Ka$G7px{F yNGC#Z7N:tZznS@C"=aRY2.p_nf׎ɖI60pnb^H]l?4^ lVX1NSt UUԧ>5(b/:9dd!&|nFV+vD$16 {M9jŅ6m_,d Ch!c.MI+z~9,2a1϶Nw_m=|:'L$Mg6sM."ذ5G?8{A&3-S*$В}}_qd<cޗL&`0D7DbG)Ȋ]g -h;qPbBsK;uU;G7} 2_!fx6 !67cO0MH$Bv O?=n>n뮮M,Ý&8?qHҞ9&Օ+v術*̗QQ8RMYi0T)>6k'Nڭ[;iS:d'鉇D8Tu3zʺC]j]roC/k((oWn3qK̄^XG˻ bΜ9ٗ4:TIvhAA;LA;$y],˂>mۆ(HYqH 3lRTT@,ҴY%8w_ضmj̮ffDQ-K;Ԍ{qQ!] _1e%}EN|I(3lBeEErܹ̟?YGbMXf ׯg֭tttO4%LH$k p8Fe8((6Y1UsG}iB,'w>lkn#R]]MeUU3* +.knsNn],46Si-b49켿[vB‘(ͭ]v-=CUTq̨,Gw-ɇy嚫3gqW⯨`\.,>Kt]H$%\Bee(  nZϠOlKVx2|쏐/f}S'| y$'a9f!x 'a Yb|7PX{}{|tӛo[8G$~'lH,MWxT<zOrig<8sk^cGk+bvU߿kJ$;hXrX`麎/X%K IҨmhh7eZZZhmmb0GDbqtvű,DCQoڄ$ TJ~I-`֡Ǐp9rg$4As[͍ky4H(**WR}>K|غ?Û$&V/d743b}"ۻK](OsG']hDEsf˛k'd֬̚U* M&7nd,[lTxx6mDlsqI!H$<ڛ,-PQ%$x}vl%q`|'z#X"'D2W~xǝrG~8px<QS67x#%^UN\./p7Lmq %pdS) +!̤ew2f_e տA[fsYPau7H(]6*KFQ>lphފvz82йKZod6+)-Ś+<|5[Z3t$-Msi6nvZ… Yd Ҳ _xu3h袣~:{ꥳȶ~y*džI;L}mFE4`F _ƌJ?,42z4FGwmݴwǦ77v\ny8?0<3\۷1w\***zY) OuO<1h4K?˙I~gRvEaŋo`,?((*"qn~!eYwFaqp.۾6n͙Ϣ +}2IyyGs ǿ/rbal0Q5  L0PAe >) !9̴a."%pvQ>pRH[w F2e05@[9i70nj#OJկ"Ix 2{Lٲu555ʶfT{R$G8PIR )2Z} ?@WO?G_Ҁ`ʦizz{ )s$F2>@e&Ʊ_y|M?%[YU;l֧1M0 l6++) SrD ^o?AiE&|ܦiưY\OsIp%gK֭[GSSx=@ -[Xx1'p˜d&7n$̓&>l;CV]OMR/ !hͶ߹c! ?4XҏnGeE!dAas? 'Tp~;7(fJ+<1Qj dv-S*H>n.|Hv1Ly#0`tg"=̻Q:7J0IC#I8vRDH$B$H$J2gûp} p(DhhBL#] 2n{&bH_q0OPAIQB$!!Id楿QI&O'\t; 7pٷ55|t$yD1@z3sVc)4Ӯ@}dAz²ah @3_= wy'PWWfckb8c(,[n/jҾ$ʚ!HM#OVkWD,.Vm6~}˝vQz' !OƸ۹瞻uVʬd˖Fz./%ܷohUU%}0lg}lBO pyVLIHߣ ~" aRiv(F ѡQ/ʅ@ M%HT$UVVr V3*] TT)ňFTT1BmTWS0 ¨6+V(;צ9븯zO4q\Qv3cF nLYgm4GbM,_k! rk8~b|_pƙgtRfϞyyz2;k===tvv2|>OJēͩeI^F1^|m5KfyD<"q|i=/=-*SB1dY&Jp89О4v$1-͗2ڼNc]mSe$~~q)nQŠ;ESg$ H$vrw4js[2pTX0#[ZURc]@`A ;R#ͨ$I"9]|,3{f-EE,Z(?̲e˲rbv5wrƶ8 =m4IG3'lE9j zeW\Xt.%1!AxJ2!(2)Bt݁:sׅTV`&]$شٳG-A\V;΀P(aYOMhmmefMiG)c$x7@UUUUUb k%!N6MӎiH]Y3*Ү?wR+o:.Y3f٨nc\uU /RZZʒ%Kg$I`0Ȃ 8SFsu-?Y-$&: Xd Q}`v^~5x9~{.?pGQ^ﴠDQ֮]K~~>yyy$Ib8v2tY+S7 8eLgFFI2e>HM\}" ܥ4pe2@.0pd;0! n{9c?'Jܜn>BhYr+..YfQ__Oee%xc/_6j0mmmreJ)@  =q]c3Œg%Ɩv91+FwIljSSda] -   äR) k׮]TUU1k,jkkvۃj_J`ܝEG֛ @Y;Hz)r #.6? h42۾I Ob,&")L]QȊ~f6l?׻uha_xᅜ{gaƍl߾KH&uQzD", ][~wFCT_0p9 zjdrdʕGJk IDAT'}^]9`!:S k9|όngv}JW*@AiNp5PWWGaa!֭p`ZpN'.kLz/uRZB͜ɉE (% Uilăe(q>TG^SBjxA@ īo_ ncOřW?@@V@IT$wK 2d,-BQU6ooe|%&7.p/^ZD#&3GFaHI>y2F$_2!N,Kw+):FM]=Xj3l6Ng} Q>sqĘ},#i{ooh$x*Lq|J ()(Vz32G,ѐKLTͦt+t*]?e:=ytÏu=|CVHVlO#Iw!GG.Mw I"$<SS]5!~1Jb j%~ۭ̪DQz],mY3&g !H4zxi9&6l[g'D8L$mWncml:'hс(ۼr*a;ngTcLMѴT.ŇK" ].ʸ;TIB^DΫwѰg[4uUBHB,"vr4&qQ+vvoXe%m;Z1c=JK{ ;Zٱc-; V #ì> tmg4STV>6O$DAv9ßoe r>r>X2"O܊Ta`T2v4<|"EOҗͭHp}It_컀<ӃL:N\"Ŵf 1U+P?L$S)GBnV6DU Mƻw?_Woūo׼I'Ȝ9 lްo_p}lZ|yi(kx^ހ+-a'ֵ'X|+\z, 6Ÿ&_|GF7 zhIoݽA'̚QEc?D y"XPt]iJ$sJ5vȁ5p=>]e RZT@QI!hށ!V]S( RF޵1ᒰL!HhH!p9>{(w߶ MIFMmݘkc9jb"@gʼn4y~+t5;~?vl,e'3ȸ>LJ4$Hphwo E2G(N L$@vHxUQDY2CXH1$+kTY+;,EF"ra'O×*$UO+KP!Vzg@~Z)YDxOa)s&3;ϛIÌJV [<56࿟Wol͙;.~v-rvX0D KT!  %fsC' Mtq6{VـjR?>yꪉFj ܇9B 8p)7W]Q>+mT%{M )VzQ}ppprrQ,)=AHHiMLw 5֖vfZ4@N>yr1cOcۧ1|,I.y2Ceܚátl(&<"#L4#H YJzծPP!ցZ'A]$n[I0 L@7 LD4ޱGBFRZf C@R*,YJy2T)e$S|aLE)K6ۛL~($:::ywzSqqsKY2/NU4bQxש-R 4qs*h8N&k cZ~= 5j2zvr8p9TWir䡋ŢL^4c=Mߏ =gu%ڟk?=|S'T\^ܢ:HRx+d0$ pY8/Y^hsAgGv4ijj"Lyh"[ 0&`^YSO\ Î.i1u4q- d w3X(B5/}:dbGyab1 P(  tE4R?Hi6ѱ~j*US̚JfV)4R8dۀ&t4t#/ ݖ^6dddSEeT(MrP IߔH,3zL<ʿ|,h97s.̫b'9*\t=s w چ2,=nSUjnH]q`zҤr? I ;^RȢ}"Ǚ\ KHH$Ia$Y]}l \xb1~WPQmƼЧ +SMS0L(\x&zfF7[;}/4ܷ7Q]]W-2iA  _{X.gsbt~ EXao8,YTJkeuNeDF!Tx%!e'%sw;찶bb!ߣ*~┨t /o$Ilݺ.N?m9:ŀ,ѶV -q1Fpcx%DzoEaqd;.+0I8-3oK݌j+H2+\}o }#J(1cn =nDII>3gTLZ&K)XEanc`Y  DmCθ&pN>´ p9mݺu|2S,Ģ&@:w:*gaY7JffM0aϿ# Ҽz 8%Im4Vd}`V:m23vFq\l7fC!8mM4`8@Iq 6m-5F^ӴDeN:u i<-aUŔ44)7v Z'yhiԈm,TvÖUTՆlc#m#I݃YQP*@`ۉ IcQm̈́gw$Mh@'I""d<"A_,peŦgoHL&I$- ifdas>}b ΝKyyyn4k0nbfj> |PPP@~~>NMAA%%%zx<Ȳ}zjE[P, 0O;lGUU@fcѢEKl4EQfL2k*& $h*:ph Au34pŁ$ttvb!ϳS=YQYYIQQQvB4ld2 Mٶme=^|n7. ˅QQQY',A0Lt= d<$441nwy57r%lUy͍TV͠ρeYEss<]uX¡3Z N2tM|Oe"H*\+pFrݹ@.rݠ7M 0:B!~dpVlz_ÀPCb2d26 P) x!vv*O}.C(ӴJB2nL$J$&NF_Fgkh);==َ\LYjaY( v9js\YLGaSQQACC6mbۥ!ޛ?.eFz죫&eZ;;1@f M&N37;>BL& BTϬ"Ͻs.IAl6۸(Cˀ:cihD"0Z@n X]]+o_ Rj*˩#2hRഘ,5yC1{ᡔM\5jI55\rҹ)lD AF\Lxuuoq]I lF /?<Q ؗ"0B I)Lv|,7|<eg2}?^ȡ']ʲeF=e˖H:fUszv;p 沫}&v]OKKgɳZ~YOEna/Ѳqu'Gg$$!!6,gp갍zc& .h%J%])P9T׈ Pb*bwpP ݀"+9>"]`l2/10`(B Sjb9a ;3uh4J("a _]mtuڻ;x7;}";_|' gw;$66wV͝=-jEL |3lXa!L;NNsS9! Y.kvqU\J$9 3 3i:WUwWuGu3=hdS[ݷsOxs~ pA.瀿WCeխY0 4-zr~Ru1.E9JW*#?G*u.7.o@N??5-)ZmF "#:hT*E(tҙ]UUzl]YQÂW5KUԾǎ}!A =[$NQf) 0ç$68p_b0 R`$dܔ3wnjkkrܹիhjjH$~ϐl)2}=!^E5[*ֽC׮0_~/ko\yf<Ɗ8~GY$qdB>ۍ;r0"]²*6^ KLvn21jC !t1R_u#6*xR壦 o-m[O~~3RFzI$#I>Nӿu':oV4{/EqhNhĨIx +M0U/x*~#g9;NwX,ƃ>H&^e-@;:MȊYZp V ^Ϲ/}KVdٲnWҙyڔh剥rx^l6Sf$9U>r/H6Jy%oZYF|m_1MW܌9|I,Ґf".4BLW˳viZ[UTY=31qȘNdTg2dҷ'O(! (R_YV^3tVgXKOݠ2;E{K,o_k <5+[Sq7Z_W}r,Dw7q4Y1Ox\ǚ66pU ۿ}ICI)p7%~|0-W_D.\gv[~La3K Nz|⧃_~;)$2Bu8>޾*J&h9.ΑJ zʒwL ~]C9Y-ŷ&#U:Z懨B᫡ƌ~(>̧v snq4(D#YѸ]l6rڱR6;n|>OizsygPQnoi((w$H > EM[h騦6#[  jF'8ܗؘH$v1 OUUk׮iƵ^__g]spC 0F yHPL&B9/p7MP8L[S¸4 ^eMY0lgRW]{-:NMq_s;ֽ?c 0*K`xys&&c|=4IS۠xYFKFUL`Pp3XA>[/_u ]۷|E0J xU L$b:E.`SǙ85H],2&lB:R頦ڍ׫gl_o{IH% ril"6Itb ^kV'$ͮ944a;j8aVQr&: # ?նpV?C;p/b5%ށ!n崃 5$ Bq_jcf*EGG@`q6BF 兛PD f\ЈZaAX$LdX|I$f'&&y<@AP䉭࣮ʻleGu1֫84-r"~=aW.W(k">]85'b.uWn]Ht2R_ͦM ǼWp`dH6o(U0M(XɾwEt+>HƏ{.p/1UK=t%LlzXE2aɁ,Zi C(`,eK{s9q_ʀo{]oqCAzq41r :E8몳{ ɘRɚ6~M4k[D~@'7P ϠEy6n7. N吉:f zB@4Qi(/9Rmis{Q(y쉝m3$"ԥ6f꺎!BdF99G<`ࣈ[Lb vb^^|e&^@.0 n7 ܇X$u^R'_$ Lcqxh*}nxYA"J9:H4m5˔1; {#KvE~Ƀ2lپEϲe^dI{p/DVd+5na:̲Bͷg.U`Z"|6~+//m@ۏ^TEAQieQ(;.I.`lH&o z ) ;v@u^/.iFaS^9OF(`|{ZAVF؀hS[gDlmwʭ[9U] (44";Xn?EB(nc+;! ,{,2ZKS^ggF U0J{vyyuBô1qipQ^`i%J^\Avg%ۡwDwC+bRtB\GT@ [~s z Ӡ|O߿g|TV阦m_Jem$r8χ(G "lxXKHAJ Qq"yleO弈O%IbttemM/$sP qkV"30D*eUwN}VСɅ ػw/眴☘u}g|S3ꋝ,vM:$}q?g3qn|"7U1(۾Ulbm7c[{9}:ej[Ҥ^. FJd*3944D4$%I[Y2saS}}}s~v$<>c*QlLUr;S!DTْ{{ [^Mmue˟O"E29cUӱa2K TR)E@aF%xTV*D,?!<`H}CDyTCi7VxI/ĨMaó?E?[{'6Xͪengvu/**M?I$CK="jq/mf!܊a{-bi,jVs'zuqd,7#/=ڷ`V/Qqlm^(2Sl=e+g /ߗ㹽<˿|M. aښ :hneEk'ZzxvsvS`HNAG:s.8k Ndy BV$0e0^0++ +I;2NWon^۲,f=I%!$uk4^=A&H0,߼g|s} eI"Kwq2-5ֳU(" (N\ l ţ[s|:6;CÃ4~mxyyvHpIxsRd^XcDOfi%*ho o"ٝoesq J'&"s{=4lPw߮ٝ+XyB( x=-,rbi -uu rd[*)h(b4jD٪=k4$Ad h3KR) g<&&&ʓ"MlQx\Pv;P`lALCb12<)h B d (qBEy](R3N OnfD҉"J7TQ\4t_=TH$S*,j3tg/d|8j_$cw]˖H$(JAjkk93$ Y) 2<2ۆFCR*<v'I%X#Q]){fYzԴ̣K ~\*'uq08²,6 ?qkW/[h/7SU?E:xpۍ58[-Cu,+^>dp`*X0pWd ˯xL„2ݩw8O'^%KP<2OK:eIw˲PUZ4ʹM]46.PMɦ3.SNsKe&,Y]EUH%/YԵ`Wۢ։3SZT}P}kN&ǎݣl9$R|8au ϷÊYTa.پmS[6΃c9R+ŒKqv-pgL2|O?_#\.OxkT4q#O"$ktTwٱ{K/߄sF,/ d/}&G(]CBKK KP(+,Gvɶ' 7TbU]X2<^1!ҬmW YD%hI/0f2H`lH'.O0*26e*,*=%N  gwUU_6=mdo S!$&#[$QrImW\vjLOBĶ{ :`,PZQ$[8všǒؚx IDATߒ}㓼u x `*fze$ Al"i|BA| UUM"͒fμ2YS<'wdrr`ϊelaEg++inn,,0j7 OZ5OsSOOȧm;9O-Gcf*Ŷb ~6]w"?r#VC~h=y-8sT+f29迠5I$rͷs\{޴dU^\LD,eſd 1S85>e^PjKAι?Ӵx)i†S\7MX$#&f ,U~eGf 2eUYeMՒL¤T21,gPeժ62QN@PE7s1:+G۹\tZpl#v[3Vitn\gg}|A?6߈jl~8S8ŎC5uzãBIKC dQ5C YrKq`Pߴ`P|da^v3U%RN;z[_W։ Ad@< @K%dEB4<nry?c}q8L"2͇eq SSSttt{64M/_~@Dz$qN?fc'!#`h;RYOgG炟,qvqi6@frf" mp6Y]7Wiu_]-\yi* ^Y(Ƃg.0{oe홗P. a)UWar"v]UQdybw~|wx_mEƸe)`EJPehU^9]l9h~ BSq @tt EQ"%eRBi< Sq^ 3!4nګ34=po^E9qU M/j7S $Qf`] 1-֤|}[`#ZKE&65v}3WĊ&e$boͭʎjVu1zJs v,LI噒'IW5n\^qLLV$dE&>qjHZWYDz,Qv[LtB#:c&1hD"A"uuL3ue9Bۍx*&l3A8?ە@UUPHeۗќa֭[7%QZ-wS8c/wL+Gjy ķg+!0-I-?n\4N%<70ƚ͸k|SY]a pعήP(DOOk֬E/dl{6CNCC֮b!:0BO_xWnykwvEkwy1-s_`Y6ͦS oaTW;Z _ ú3g"'շs/02UU \vDQ$12m܏'(9xIĿ2u﹑jܴaɵEd:^6K5il`e}' K*<ϦZjAX&C9Qr}fA'jwܲd' HeRnT MeΥI]גmܸF311gۻIٱm~?RPyNLa׮]slwvvIWW8TUEQ ˾]Ys04uֱaÆ9zrr~zxhg{"JwkڛYDkc}UU4ML~&SXot]m<_cvvfErGy]l;Ly[Ze]~4y A1MIYWBiCvK4e$dmӋ(uꜵ8O$H4gc!LdmxsFȴUTAsBK#*{ɸTHg232MHLM"9B>N"=Ed)vpd;dYFeEs _%r?,p;/CLE$"U2Yv666oqϞ,-WҖeOK_vkVtnU7<e0 &"F 1#!2,O&1"d(xr|**;RQ5 LtU![.cˀ3js-o=_$eM?v,T U;T\N#$0-7Il 6eHF%b:ٴA6"NܠE8*.Q9:Jh68;w)BeYx^ x?(JW@K(J;aKx(L}{Z̧<wr;L-#E8@Yc(#*YB HyUlTЧsLHc&-nQI8-I ZEz{{qL˲9&BD"A,#ɐ)Jyf+*~?>׋TBfP-Bs|>/7'o, `ddFFG=:O"sTy<.|n~`OuKuOʏ}dj`ꕞ$Fy\@Wgr'a-KQ,y<9}ldꈣ>,Bh)qHPfLjwLqzK'InuǷVy8?p$Eܖ=&)2.HIEn?Fe, LdܐdOCie1\5g&1V{ٰvyv1&'LL OD'W*UL*JֺqkP#TA 1)O~):3Œhl1%$"{4gTy۰ K_ؤj74r tNɄI:!$ /I sEZX}Vn>l$\zk[w຦4A`X׮ _ZGHLcUU_5e:gQKPX`-0H$2wPUznNKa[rM޻<݁V.\HQ4T[l Oppٙ( T׋j36 !V IGl&G,"HLd%$p)F Ä Tٍ VkgEUqFs 3<mŸ OhbsKD9Ԝ)GX/;ǂyfT7(4kI0tI6cX䲂\2Xi׉M$IFB:\2*:bfo~?HzAlྲX/ld&O2fרMӘ>0}P=9pD,'4XSP69qbueͶ?M}8}`cy woJ8W8jl}@YcP|dYbw>[r(f@kÊ +j43ciP U 8>K Dcc# 466x*!4Gf2U[SC}]ݜdl6K,6*c8c,;)K.{8ɮO;0QY"G`l/ ltm5\`016`Ak$$(0sN#:ϙ9}k~knFzaxTjNրe &/xﻅr,I(s.6QR}y+Np[G:7 gJ}q*bSyto.R099I.k8qIXvyJlTm,%[AjT(P5UIu.tt\ Lo unGw<'>adhh*e RX/UHf,'י\fe+'(Ƒr }G>g4+Pjxd7] $6RdU@PdBD%9[q@[HE]{a̶k%237B(jnDbS.Ar nZP(5z5UQ5UUu YϷcp%&s$ 6W*d7L.7 t]΀DC*011#e^sywMS%W9!F~_|ԣHn0C3mBA{,6m*r-l Gk^Zʮwv9>9G]'}b5Mk4}jMP]( LBlux<= h=[b{{-2FTXlNeYGΓI&$6p:c`|d}!!Sԯ 2-R> r˲/4W DBz+"A+yQݮS*DU=aYVYYYJ+ʑQŎfƽK]UI>%ut[DHI5zX?i׫|ϑ(ٜNbA m7_*B~B!mp`Ӵ jAf6ߗJe (~j6s++tQQ00 (.1B RYS9yV* pɂG&Dt[&⒈$4Yu.>C.poBa6&T+&5reĪLbUbe%JԷ%dY]>^`>!$K? 0kǞnIS w4Gjr9A?,s?g%cQ$Mr"퟽Mn8oGYngo{gI@K&;;B[{X7%vѳOLrpݲ,9&68[MHdu7Amj52 lllԵw}ͅA󺺺cddaC{6wqZ cST*baaS ,.̳@b'Ftƛ1ͳyi _~?}^wZJW;06ҿgAe gk88'FxǛ@&grvos/<,?G-2mOfߘ|~Mi:B ۄOș]#@2.,c'L^Ù*,vo]kCZey>sIX:EҦ3gB$ɩ Vn>5n;-Cq(Mv z56DTzd"n6DHM?=vu(`; ֥0qX?0T+a֖UeŹIoj-+]w֓$l6$$F{1`gk;SIYO: 0q͍ h4}].I$S)nW p߽3fl}K ‰+]<3;r 5NUz4agUdoȃ|u L~;K !2,mg)>p?z(.g2.n-rL*"L&fTUVWWy'Z`u]g`` FGGellH$/6dJQgnww7k\˲XXX`jj33|[<}l6`?rh|C ӴD IDATZmC,& ol (ƽXXڨҞ(LBP( e7 ?v|^7${pd;>8Edrrxou{wρTF" @Ev=~0&I,Uݏϳ_Ssy}mܶ"71?wgf917gxd3'uyDBDb!ƂDGHal.D#!S6IM&Z ܗқ[. .7iͅq9<SQ%'4'Z䡘X_6X]2X_Hu&K y"⎁T KDf(P_9mL->]A\n!D׍:إhIY'.,>M6oxk(/dq$|<_w .1t&yYq1Atӑ$1=HMUwuYgܟ6hJ[f}}d2I>T*qzoCCCp qډ$c||%dT*1;;ӧyzjsLN\*qxF986zД`jؒ geqQT?|◞v\m|wje(@$NKf} {|^D!g]k=}Ll %rT;x\8 KNo%!NϮ26ܿ^g i|/$qՁC$$9YY[3"9 OM>ZWYD_@(&5QGs+:T-t uKm.d".Rou@Kvҥ{.ALe`L Bfm`mƊDj]%dR%XXݠ6;Un49mff~ IVp齨T*T*"ȎBAVw9 0egU˼3r;G dU$,3 Iѩ֞9AT?J0l^U8CIޚeÑ]CIv{u iRTx<˦r'臇ᘓH$ 4e2Րb1Dml#lRq,8UO}lz/|AN>M0Ga牡v݅vjlT6-pō ~_9;W-0bY6gu]^jQ-Lb/ܞo-.//7Y;{oxu `!F%j%_ǘY~ f%I¶,kImmvt]S\%74YFuy,RmfzaMSzM|xÈR022#.IdUgw>4M߸ܺzj[ϘH #E$^2,1TjuS, a4ސr _?}X| e‘6TEo6‘.gI~?`X,LST*rfT>RPVѣěHvhh6<OlK&dYgZ;:x+F:WX]]aanNN{f}v(0MSl>^dzg b`, &gao/ m9ey9r)1 9 RS 4k4u + k9r"jl@'_͓l I8S Ru) P_xT_ &%OUػ);g#0쎳@@* U{UWDk\xصXU<×ø !( HO<[yJ%FGGwBLn9ZG.?sυ:U,;C,h%*|GyoJ+6Ua8+.!,35HcMp{UH7l'zxrL.#ɐ(JTfԱcZg``0 B-!SB]Lm=|>_W] 2 x|E'djfi{~D"I8# =|n›BppЏ$5cUL u{Eݰ~jFbY9=M}{ 26D,]ҒřaT2{_c_z6[6H  nc+%ID]) *LB}Lw/3pscC^ b[/[+ V,'4ܦpNem#a:h{!Z 78Mϴz\+c腄aVHT2$K) rd%M&Uΐd4rr|PҦS !-U6J&b=VΪ JxU|*ꯊJxU q [@}b-YPͺWm1|uHLhi$25ø1C˥; DӘ_^_~ @Xmy3&ep8L$i-0_T( ͐ `0HgggKT$i jooo7LmPH$W_Քj5X[[8ua ٩6dtvNoW;ݝڢаٴ,LzTivlAJTcwaEzB|bI,f'7x(<2e& 'Cln]3md{s#YsΫCdN_MbBIXr/~FŸ1Qq#hoJ<,,J)VSeJId*I1[Fg!! gJNFdnM;J_w3uEۉve5O{9Z|9v6-B` 7d3ED;jRxqy7WuM~oU,Tٮo;%uVާJ5A@WM£:3JeӦXZp۶S+c#f)r?'vV۶p\dwqbtuu( a4,ˬ"2@u% [@a͐)00 |>O&!HAm> j ׇ(;`;%I"V7Ԑ$iZ() % |:yWW]?: C!{N4Mr^_xGn&1O<c܋mASX-@@S VjOA!n^#ojc9C)?>X2R%(`> WlIMy;`ׁGx)TlF Ȝ3s !(|y*аIͅYzwwb#\ {ИqmXUV®(I"k xxN`˸ BvKՈgdF{:Evм u&,d!WUo3,11A60,yoseKYȯ0]bDzm$Iov5˦j0ojo-J+]㊣hx¨Pi0qHZI<}=p000h XkS~~W]S4Mau=ށ4h`I_bۻHm/_O%ܼeߚ:H6 U~v2į\nR˴`DFw KP% g+4Hlѡ^FuG9JdddPt7JfB"%f.(P/Lvi[8[U(n [fTPd.Gw)l 5 E-J $I蚆E _LUu4|E&g]`r~zfL&C&9/T|>tuua6$Q(P%@z Y9qbiu7@fwm޲l}pcKQ>mT>up7A,L]5Ý[uP^H ܋ w=2߳P۶95=0W]uc`v4dT*E"`yyz[o 3/!L05 J& LYET"ɰѴlL=B9p@ӓ>FwnsH#djlgg'-k&kkkLNN233ýqSczfc&b7JW{S[_Zv4{@* -r MSH{L.x4RFF5%uA~wIa3ǮLW/L`u $CB* iv&/sbbk7TIe_h3J@P+_e&lv3rˤ+YhBhwn3EbX%J남[!Jr4l ӆaag&˶<' $UŲկhy5WhJβS3 L-q|r%e駘c5 }!' t6`}AgRȶi x4o}_Sm#JfC t8N3ybԏq)K-S :;iJs<~z\88=T*zAr|jpd C288,"ˑJH$ 2 e _eS%=ɸc2kTF<:>TŦVE0xY(+r%e(bVME^ 8+UCS(&fZJhVO ֿg~yykl[o?z4H3|U7v\Di[&5 @dMA s- H%474hO`*9 1+N w2HG#} uцoʨ*(geα,DuBCLG6g'MS~BlT0lGumnZA|t&U3-UaT҇~ddٶŐ׃iٔ*U$lM(|7m wxS\\ypC36NX&3 L-2̕&V)`m-#_:ɧ{5 YrVvBD/ZU#42*!jUJOar$mzlL,IZFmS>L: :|&Բ,n; i$Y1t'aM5$;Yp ~d h.ʄA6%NA:P(`FTOOOIj28FFFm(B6cfz)p}_bzz۶`l^b}re_)hLP7yl=_(a&ia,Be[_[vcScs"*65IpM4ժ@XeTmGy7~6m_ta׵\\IYMy~X1Y4~m)[^d:\2)ͬ/)'s IDATn{:/.E8lXq^C]nU/{Zf n'x9e. 8Yp,>_faiY|{94uQbs|rwA/YBPKjlZ%͒dHRMvV133[>r166 ގj6\(dJgX!Lif897SSSE"twl(> <<zEpev_]\v=8 T5ǟr@ij/W,Nserʨ`%H_4'j:::n (JY,)TUK5SP ޫS:%IM0ǐze\O]F71ͩt9&kH +/ᾶLx%\+:|Ux btsˏ@J>F ]\ COaf~ *KiN1'f5%RIM$?.rlr{%7Шh ,D߆i'H~7@-Tz@'x)S8 kApLf@@nSkG[Mpy @*`Ї'֋gmqǹ̴mc&[p܊"kMLbce/n, ,0mn :w¨7!߮,I,,/܊Erv3Ͽ~䍯\|d\HEQ۶mM ۼ1MnR=BW >q$_ >Ȼ7Lo{?Dc#45G5, \m)-\#a*v>"cضVי]`t^Jʮ۰eY,L-.CK HWǦiϴ"to[KFu>~UrC{{;DQBtvv2BRcʷ>CHԪU8'; $E hvjH$T'g,*އM@63UuRP׮Mk=|@=t mls"P Q i뺴R!A&iQ)[xvB ylۤCo"=ό"}Q,F W5Z~:̊䘯̢߆2ޓG7ɨ)DFP˂QEd4M` RRGW+\$<sngLfv@ͨnI膍0* iKyra~ ,IL,w.%AϼFV8(^=4:5OkkzW UXw\Y9 J,|zDƏ7\#6yzieMBpwnpclBXf"s^7oͽr.:W([wTM72@l^Ɔ\YOmYmㆡ}ulV)ef 2|c)gRa~YίQ2*X6ĂrZBP*YYh.7Ȳa.ۣ$qso36ݒ*XEhS-nt)z3>WreSx!{ Vq:62ibwn2[#Aqq@=_vONaiJsO fC\婙%^u8fR`J[~F CLϯcZ:#t&9Ю(G0/b noez~#GOF__߶g yCLlj"[(zq݄aoLj3(bCB P3$Q.) E%5ϑIx䩧8?3Kh'AGXa T~6>?Ƀ"BXql& "3tJ>;H-6{":c1uGSDY8R[tx=|{Z"̃Ez P$ɹmO@S0, PFJOG06Uea$ TMp#5APwkՋO6cQm ۤf(.I*؝̢Tao<ڣ; ߔBEAvS,z:jzd7Ӳ#-58dž`ƒƇ~-8$E#sX2gZ!_E-B-Am:cQg[*_ةE^+#bLʫo#_; ڐCl˲Pd;{4T]}ťS{b4|V&F%r"O7y\. 9!h$)k-5[C,ˢR ,..RV[Aa'122Bggg 3$ -6,S(,M>"%|>q^Rj`_e/tU>tu 5[U? nl]EC^Jbb[?pB8AXn){M/:2p];Jʸ J4Nj^ZpOS\'?kh[;^ }9SeC*3Pլg\QLVpn_;m..qUϏ&- przi<$TM"aMĸL /6P,5 a7R]֓&@yS˭ȸ3 r,+mxbSJTloRXd)|.7Gs\ Or0>pDh[6<0ȏqO tԦS ۆ`KBȂJՕ/9{>'Ȁ5]DY?~T"ߡTp3/78thj.\+YAE0H0@}^!|,XdjFh04d2R)QXP߿;wiӦEuRûxFApHEdj7\t+Rw9E&_2k^`|YKl<+NmvMuT7(&$Jo=sIz4OCT ceבUl1+|wɯ^I.Cg_c2Gs|)5hD+fQ`XМgh7x8]+^Q4p(ʻ{X'.s_r,7?ٟd߽hnjgxQ,z B476+庫hil窧Uldlp~F3m475ϸs.~ !f[[[h' ~aRB?0DKcݢ :3;8ũWD,m/ MϠT7,}Բ`jXmYyu7a*6b@%\zHl"^Xx5e/`+nۿ0dÖ-3ש+ @} :u+/Tl'zV&n@I[:R-1距QieŮ1F4mD.D׭ p!8J? g)RؖE6/WMW{rpDaXǵԋLfO} _)%tR%Tlkr+m˲X c 38:8ÌO`ٰvUܥ7<'i_C1z%r_]+^Pywg38&b_m&J`|?sU_JֺEk?IXeĈu:O|_H$)E8084DCu ke%pxG !%ȗ,'fQaħB &GuL& ~- e:3vEQQuI( =#/7!Ul&w1oJ%3Y챜&2LI,Bσn8o|LU *V lAbKWŚիZO?4ݟiWtaUxtu$R ;:GND4X-.ExJ\C]kjEd˯rכP,`d@ 0pQӴ5TF.&5S,3YGm߻c fm]FkC qRlL\¡^)y<mTE1jbTF)g4$"a>V_Ԛ%&K2z-iiO˻GY|7c2qOy"w3KT3uzl_^r1uUQp mȊSɅ z84lƆ9*|>bE2vٹT)%C4pr=T07p/Ă*<Ǒmհae.qð U}~{>(:8#֡h$_ޫ+!ԞAJCY)H[SH$Y98~C$ѱLGM lSe^/*@`Yj J%2.a10>?R].yA30(8wۮw/OU $qU`w?tCǍ@WBSEK2spFOE-ifu{ws{N/#s~Fw&X?k61O{6bre'tWȽj8T@IU%)xZ$OUSfAbz :Vڦ*Jh=Mbw<{{+m:Wtr۝R,B044O~Z|>i:6#~",#[*7*t8=:)*9m\4Zl'OLu&L:_$+ɗd&Y'Z/?Y`x~#LhjjZtL׬ތv3IEăR T*Q.Y}.wU8YݲAQٛsu]^<-%Lˇ*{F( jU~V12x*<<ԜO=)v߽$z ֹ÷,eIL\]sh -?KE-9BHiiڼqrlvsɥ e2 L\6K&!;%SiD)Ε.VZ]ӡP jy6``RgxO  |pu窫&J*<3\veDQgŽ'xӉ!khQн"s;@ MRT||EUtYyF\I"qV oΆ2,ʺAY7v1{ãO?qiZXjۛX5+hsZtn3MY8k<66&;E|ouO} v.$k/XE]Uxy?+Wֱ|7U Rv1|l"OY>׽ \FJI|l,p%. !PUUI]G*myߧc'Y蠁{>~>~ò, pYa,b\&K,^A4@ lofG RN?M[H/&80P?zuR GQf 5mt*, tpOUyRǟ͛0Ms;@|j@8Atq+=of|TUENN 6W3/;1;0?{"sۅ&dvl @%amLRC}98¾زb2beG+kWv5+۸y 7m? ͸q=&qÇKp9nr}\gc/`owl( jضMq˙{)sw(ii8 jy%3q=> E*1:UӮGKӢ+B˕Ll헅^Z͛a?`i m Y@!F4Bli4UBp+7@mk\&9o 1!ilv|e&}}}߽CmD e4pWrnb0ij 6cS+\(l5~m?cjjZoU=^ j׵r^Y,aϲ@?d6Xml%@QөiĐB046A_o7o|OFG }CTUDUWRd a2k.g;ν6Or[na˶|e3Ud\Ąs*]5q>/R=޾q*RdNm3g YqIoL#W],BS7ZBjZ r;ogrJ>zc'D7t->,DmF I&4j&S띱eAụi~M%3Yz!Uyx+RKWnt0h]=ͩuHMlŠ\5DG92G8?^Kmm-L&yg3Z٧:hc/Wjc?H[zm}!!0 _ypW* T֨|<r- e+U0YW%Jž&܆CE,'H!HM%z=|>& g`JgXKU?gxܷ`M^Vf,TE&֭kgɻ[$ ]vQ AAoIEt/h9Жe( RT'#k{oB=c~dyFPD]byz))R")J Eڛ(HI9o`Q=FC# c JKӐ[÷~>/u EܚE/ߚ˟㮿,"F/K*}}÷x-?|FWQSA47kOS[]7p;yf%- tC]P{ctNLU o2 䇓tgV yp;<@Dnˏ>S܂UqwlJiM+*U'R2> j`¢ /e5uOpsBʅ7+|geWBwWaJpw}籭{93LPWGmU5UTUQ[]IE0%y }^sL2>YI|;_cV.\qq4Uv^:s1ݱ2v R_?>ʺ(9p>/+ZXʕ\ȕl5+.뎃SϿ'<ٽ }R8 .z)dlj%d}`|N{T"H *]s)ێ O'+(oy/ը(&'{hJ%"-uA!]ks=m CU(2N1<6Af!R\v:/5fliGzA0LSpy <FMI˒)$yK% fBeP(rM,)W.nN;n T+R{5mM$m7^X A6bptIF&&dpd > 4!HDUFJqcI7ߛ,Ӳ] d_W!(򂮘e nlDdْAY:nJ$Cj,dnQy,2] L|1 +-QtʍtsU#Kt E{{0A.l[! xb0ъHH8Pl²9S1Om}/}͔vR0<"],p(ڕA425Oud&d5+)x=n?L0[{DbAcOkݫŽYK-o‡hz_[~,"#}Q)a3u!~sq_7d2ݻnC[Srt%L#^VY,-S)!9eR*kK`s~.K%jQUe"ə ]}}M-p\{EDWNQN}}u5wJ`ϟ@ W{mPB.-bͶ_72X`'˔&@WTDENZ̲IqL}M%ţTaE00jݥ*t4ڈ)0I:d|2Tx8x,,c[Yq)R wmB?@M\=kNtLT`bQnnXXRfH[p7*Y ^z:KpZWq,wL٭<?̮42M=Qm+Xx@~-A{\z^tE|!OERZ3^cYL&w&hã<64SPα!eS96-C^Q^{bҤTd*O'cSm0ǩ}ʺ>=Pxl?|heiXUmKdI:~ N7tPRƓ:J.š)w ?} c*~HAJ)9ϚeNI >\^eێ;EsC59])%ضΒH;?.E|RW aq j~y= PLjޚL̽§0R2<6hmJG6?[WJӷۂڄ i1y6)JϷN|,u6~~ )Y!x"KvxZ}ۅdY)ky7S*딾#V;2.TWii4x8%R S&ТC2:9N("h[:}fħ~Â_$VƆe`ژ L梦*Jmu6T7^Q柷2bj*NGk]+hiajaYj o[[l22B'heAY_ƴ҉w[XyӋDyt^)m.i!4F{c})N&W -0:ablW# rF*Vv4-\~5bQ[4"%Ǒ9ש&ru'j@fѷk=}a6_iTwW\In⹩hЅ,3}*?fa fs뼔瘜8'2 |B=LIHxi!(3BZ,gQ ps!7<)o}ضСy/(KNLTbF wVƲ nU,"=Ik(G}NUYMݙSI[S=P((:NC3#G9]:V>쮰-OOpI-ܐ[ljճ,J\jƃj#$A#G2.R|OL ŖH)D"2lSofpX1ԝ]/y?NۖK)y-\sJl8S"y -1ꪂ 6KB ;M.nr  7\frݏhJcI@ veyk 4RJ>>@WWe!@Qa` !TX$|riA{s#eHg<׹oW'ѝ{nzTMeh{0 I~vl"uֳ.S(}y*d4HGK 4Rw)j 5fpPU][߳wIU_q2&Mi6_BFM>kE % Omh:ࡩ ۚ(cJY2MAvQdWh| լF[jSoZw:Z*X6BF _':[c~ENݖ4(Vݩ[3})O0fic\֨8pǽ-bz6]S 01>=z }08gPxۍhʺUN2::$VlVEG&-;0_:3>]=EJ\4n:/̥̅ۼt*[~npMcUY@(:Aٓc!7)pdxآiB0ԫ#B!suMp\ }Qٱٺu+l+VFEE<DE*O}9eYr9~5UKE,H C6T>gfDPT˴=K41`vbYL"5 ~Wuu5x=yO$ۃ?㱱eBLޟ`z~ +>qʟ72&=7 IcHȭEb.בB$L!bNX&=_] >xz4]Q;H)8?JC=S 66vյtSlB08:"%ъ0B8DWq IDAT;ƿx\q6q"Gc4w8 !Imn/:O}{Ͽ1lpz@s. CYCC}:6===!***CggFy>atvvvZ^rܬ 7{eG6B}}=+V`͚5FMM,5UJ9;;$"mb0biI9q۶͊M&"9M&f7e}+GctԊҕM|ft@'Zn/JfR&,2r|N/I9.X{Y{e&{a>?Oh`&8r͔jTҔH׽-IX66SB.-(lc^cՠ }y-6}6MG N+Rro m}V2iZ%ٳOơqj+1ń"̔ ̇ow'o taDn~wXRE[S- Q| UUp .U nU#H`EؒuޟR S0 ROw5XQC <^QNj;{HOO ߎ>z%HC)kt0tcq"%|kᱭX[Ƶ]_WRW][sxܨhw~>5.44fF =>D"5zɅp?B `n99b-l"U**M23~D ӏ>9):q\t>c>Sy,B4***uEQ㌍99X ]u1زe˜|Ekk+---21>FK:ò,\.uѱivE r6iO2H3K$qNiAM֭#d༗jTL{˼eYͦk|; ٔC. X(Z B)=KK,>K@u[xG_#oyvn>*x[߆lPKO~f,gVubRh^ ! feC)S@^CYMn@C} >\+lƖ`1\r9w6#SUgkr1lu'yw{sv\!JS]G]ʳ\@L R>c9&gl\f/+ic,,T$7!Qˋ;{9;/cy,թzxhd'ՕQ~%ۤ"{ʈɿ$.ma^ܱ~sʆM[k -M57S_SE4*}?{'<888J&5RU ߚ<ؓg~#֬$Q>̜*~1jmԀD 1 YИ"'t! AA!AqBD01txBGP(FUهBQy`~9 z9]X\\d0 f͚5lܸET">R\Ro߾yX$̾C=l碵xuW=(ʲixFl9H3FhpH+ɕZ800پ@!gQۨr~.KhrEh[H'-rYhk- h4p˷ EWr4EUX $g8Xj/1k]6 G.ozͮ~>x@ zd?AJ 鯳()1/^V\gM C#}N\Y.#ltSF sH!H$S|^58DcB=.P,tqwl P]A]uua+E^A/pC(%gZIJ2kDM|3K,x71 >ޖ̥GM(襫}ݣ2^ 69EVח?tj32>qTz Q{\Hؙc|W=B"#ZQi0" IwYH !|('BQ;m>Ivc3L(NfXɪ-\J{j^pUWReC^#qa);-I8f7B8a05335HNd:StnO   }xO>'ɐ(J%J2??:y՛p_. :XGsS $Y\W|.T@5i9};JQbFk/B7H%ŘGL(H3u5U9*/X¢>µ>IQ&'mo=sJIʖo*A9; WӴWiR6@M }O]kVF]"XAۤe@tU(%[VP5E饭~ɪ_dhdW}LwL1:.P P RS GTF4ky_i'UI ) tUp?1p"VTg{#ZRX9-f$TH-RqIry$9e"19Fo*.Ջ{x^<^w(<؀Y{`0H zq|>|>l8'@\]]]:xǃcnfFgm Ld޽۷K7=T;ۚ,e9WR3[Mتp ɸ6\toӼB^Ų0mXD t;{d𞕃|TElɄYM&(pќ%Lqk#Ž!Η!"%yه ) !ihBX]T{]5uyH_|->b04Ģa>4ԑLI ;4Py$cI  5dE͍5G2V2#yucKzyɌ._cFN{I #הIgtL[S d*!XXtųThm!ڴ&2De$dE6?!ny=K7ssׇ:S;M'$SSvЃF$,EdWe/(xijyMtQ7e38`X®mAx[iRx榕VlW&ɗJw&Lv9tCS;Z!b{\2L30LOI3$SL?=DzdfHC]hNNRSrpiPYPYI (li\#4LLf3tО=_Q׍zy1éU=ロh4J?222Wm{<<Hq>Nϊvg+VYEaTxN4TU%rjV v|Nنom²mr"\o߾7gr`|fP%8Q¡d- 5B!c|lC"HHBш3CUU;?vUMtϠ43]յ^]~ǐkm@A2dz1U:=1=PT9:9m(H9=Ieta"Nrwv»%?[s#=>6Hֆzu[Ze||l6<}}} ^*iD~νmؼi/;n&rOk[ nPsmLeJ&_yp\}cΗ Uŗ!I `Ϝ;쒉$`P9% ُL&Ms}S#NuϪ,p0`ЁوVّu5$6gݏI8 |&)K]!$Fi,J*~VXi4)RT, Džh8D膁A<>Ta%#+V|K)əy:Ǡm8q|_eiiH&$IRdpaNgY-j [8=||309& 2;D0fz~ \*RUxMy໾ -77q]e3ƂM4";Oq,_ ZRSg= cI&i:q2-16rJUOrol>`2@QV2wQn~%ܦVU-BW[ k&sE-ȕ)̕/f |*&U]zՠX.3fh|1(FGEih*ޣҵC $,n$^ ac`6nc6ILOT+7kYUq4"p n7Tf`}j˲8t`W6[ThI|뚦=Nݲ>nBШ}N\n){uIbۦ27=v2(x`snN`]2BUzm BؐAr)M`osޒI⇞!T]]/bef3<iio[6[!Dpo/AظRZ'?_R0(4ţӾuy4MET8HUTLŤJ*UtۤjWc_fNRGrnvG{\ץ2WISS~v޽f1MC04&BW2iDcbb!O{mw͝MU7I988ɡY勏>vl`=\oVŠٽmSߕJ$)YAV:O%u.4ucR"{%_a,iq ҅C|3(tŻhND&(v2ZvR|.iϯ1%vqΣ]m9ưmr"'G8vjc#QDHM1Z{Lti&P5kRvLl:̥ FuUt:2\d2XCp"PpH$Q4",א++( PilދNr=מ^(bqZ3Keoz B$FYY\CB2RX۽sb4|'67uӳm;_'g;og-H$keBa/I %wY0($b5 x&pMfhj:1KL)RY^j31U~1^ghh~f.N2s*zA^A ]KBdM(``C|e" ] P?ͯxYK~[y9tݤV>յ*iqefIUkEZhao{1¬6dSSSLNNL&djj 2 iiԜsFmjH"*\[:$yr0c> qWnIvo~R7i]uh6.66~Qp.76[Sx5a )/ Va;]Lw(8%-[m۶F!zQr`}~HL&}[mgj|y*R|^YήME TEɑ~D3|C(eAjU8kQ =4NRP*WoM[)Bqm~v/cd0M[74mY6?'/֡sZ_Jsery^j~/"%8s@' [ĶƜs1ȗ37r`M^+:si8'yQNN]%Ԝ'R$Ѹ喨DQm.wFVR:HHA~$5aIM8)BrnUDYcy˵e,l6/=bY}knpя~EQud2(ccc=z1ҪjO(B$!7: .c64XCUnSm;DBi%Sw_@4i!@*JJ ~%!Hģ5aL/ KyTl3QVlr sF"~j KTiJ6E#+!b,a Odu|QY< *A܌ޚ:|HgݩK02yߺa:erE&S߷Gk+05DMSP*'h YR*smS*Ï|vd}uT],ۆi\V܇Pp$5Fȁq2KC.q-.C$n+$Rx|w KNBܴ>c>B,L4 Koj" 7m|hLyEiZyk\[TIjN&| _o{ﬔ M7݄(H)1 T*pfjjL&CX$3x1r IDAT.M;eso'.mazވc xJ(\&;_?Bɟ&X&d&1ͻ&,| 4,.4M["eSLJ"볻f"93c$v{ .gx^΢J`K}SZG/۶⒴idXеɚh}>&W,{[EP  FMZ% QgcBV.P[S͘Ĭ-(լ>kLMymKJ/+ Uy]=or 2mC<Iaa!3V>lwxM"d6QHIta}Mo V. mqH3pA6NҲy!eʪ✷i! ̵0ޞ0?jP@ }0ȁf+ػ ]8_])_/x+yw=ЍFqīG{?rE(p?CN[{x2Gx#xo=mlnv;ZG"a0hH8!- ˲l,²,3|0-r@djiƦfa ɥe~t04{ Z&rx?$ u7_T>(^cSMfd"œP*q~l= 'dhx`&*T>?W$ ~afff:g4MLDWcK$477FރrQ/?FG'+w5\F:3eABWڤ 8 ф'9^=Q/*C ^Z,p\>ǥ >/^,ۙMd|z9$vf0Gn4'֨f`m)IWeNXH!jx ؖsq}ڋ)leSm ҽ#0}X TzZ B@&gj Ϩ[5 y<.'Yb.mS牄$b5̥eY|;?gC^{׭"u#r < f;B<& @O;=] v\eNT2yd;1ZHd %Bťe,.X\ʲɲ˱_ ==ͱoèx4">g[sf(Je>M붒,8PX/ '[*x^>S5veQw9ӒvSclR,50 FGG U@(BVY=^Fgg|(R1糂f6!Km]SL7J&)K`KZ}sowV޵}{wlkZD7Ui+Y>`]@҇|kTQnzw5TUАWu Cgii)&'x[09Yfgfhmiٳu gfӇjRӴ.jzH" p a0]?v>b"A &!$d-IjPM F{a^> Jr|0ũ9V*TSPUck[XĶmzzz6mTzsE.{=S;=z10p"9ZYe@xrkTf)$|@s$BɋOy|.<-8V,ml˴4U0$4 [`="$|4o& |V`SB@`łh^mUrUԒwvVHK)_X&ӶmCu;oy8x${o>jITrsW4]sbϽ{^ V|~wl+pCz\蜥dn9XTZL`ufk~ٹ:{M'7]ӷ{ мv2MWC(e`iBLA.lo|{K)IiGZ^JOg|Mn|Hۙ Yոgev>oe!'9xgy#~ٳkvogN O +btMۍ{Eјf6o޼[ G8u$عmسWbt8 *ظr#_*U{^y?=xWܶq{EQU$ׅ ~zixy 6rGN%9|2Ɂq95h|>tc>RiڪR{H===^Ķm&6Ѓa>e5o+z*:/nKhd|j iLj i˵jl(js{a H$yeLNM)_#x xE'BJ($\'_@JgH6gq{(rQܺgُD6i73$̳w ~5)M*V|poe$ƖfΖyb"i$,+pFm/0IڛYyL&]3 ;D,v5+RK^W@[{m-˩*%B i]$5xc|ߥmE TjʤHtIbDj|8& 6W+q|>O8pK$]t/%^-\LS,R*wuQ`c~YpB)2~UUIa QJ%،8KEM(Xnr h3@0#%tFU6UЧgMPqOsΖ -c,Xh1yV[͢1;߼Mqq[H¾@6 ܨ>foi?%B\uс{q{=5'$ S3tM 6/ʨD,e?Sg3 է$gcMtN%P/aL; K|{?`SF~V׊:Iڼ| ;<~{hD W&+w;i3)TI-Y&&&XgXֶ6Jôi^Zz i۽ʇ^4\ 6Z5 !O/khWm4/– "(\ Nڶ`ev܌tiէp$X㇎ 2( SV0Ցz8Lj.ŋu#=*&pL%P2,rrrSS'x~Oe6Z;hip[G"}>/ETlkN5-D vmg,!غ[{ 7}9N28:ǩY'Y\ʓ/TM2<3 5حH0Pp(D8u\ť e6 !fx*pc L/e.r\lLź;msT{^_ڪ獅a6'O$JLcS1Y7US D/6MzFG6^ v0x0,ml!\ba/,qb0Z<enVylѪB7([/nI-]XXҴ]$7J֫䩱Y6t:tGRQOgɆWPٴG~)KP$/J%|ꢁi}U?ޱ^|CǎuSȲu|S4UjU}mW?|Gqgüⶽ{Xe[4++:3s,..r9%H4&XP(f£MHtln7Zů4P7}/k D0tf9~ko!C'ҩ&^U,Qu]gǎT0 laAmmaD]~{!P0)12=&='W2 [ #CHX4H<zz6yG>{緩P5I lRI dhml%ʼn*YRSK|r~>KLYX^D]ft8IXB!|^oTԪ:b2Jɇn$nssڨouj:2%,3Dz!%2/.1a(ZS5>ׇw(̏u, D<rbG`&oۯ%73c'7aܟ`6 &n9|13˄NڲA qȷpsڏZ%=`rmf|J_{K] V KN`M֜SXV1`V:'HD7ΓŁrq枣S-.%A{sb ( 6fdh|M]=ǯOJ\:rhuEv jRBc3+47IFJL/o1i1) e[6m,(D]/eZycJۗ9p$_|#772Zs(JLNN299EQ"84Q,.${$BGGCܹi "U(W ^uN~4'shhonW]p0w$i##΢*^@ @8^X?p: 3lNfl5۴W&|tuY;%P0Dj1Cr,SJigu IDATeVc2jpG߆Q^s+0/EƽrF(ODuN0]YX,9I̓'5@r6M2R9 ,qy^t EJfӋq׵a/B|<~vlv߳ Y3]nA$<* Ӏ$4\G7b<\y"RMw227$5]iؤ6"}7FpފW_ '4G=TkSk/Qm f}D[ڔtS*{}1 G9A+6Ko?Plǽ IvL;~IDHޞZV% !H/.i +aӼkMt2;. ȵ෨240jar }ݵ:BmQ8\i}7HA"u ȴTŝPxs"]|?{[U旆Ʉ\NeM*]a1Cߨ@eY,,,`kP((;7X_&m0m/"YI-Zȑ[w)6^)z*Rrbh144w. ׋v ^/`X,F8n$ٳoKT,Nru4={fW 8T,:zTt:emWL,M>_(Mep9 }E'Tw]`n?=v $gJ36ft2$Z }|ڊ"S>@C}@܌>)%C8ʜ7p_/Bc5:3 !ժN\X.; r|@jv &ffl2E,F?ǎ^:ZY\\o&(]&8*ĞB@A:-ƕ5gqOx!&b\~lnń&d[?)=ݝP.RV ]36~F_a=y1w/{KXb@w*qJ\-MКB.P4g(v,ư-ܾ`|q,glںMjQڔbզT(UtUs)=Vο^dvv \Vͬ;f]H))W bi HF.Bض_0YT*T*NC(J9cbϞ=twwDZ, V=3?zu Jbx'X0ۧn5eX&S,'ef'e$Qď(̘2$a6v}\{ pv<ɵWD >כ쾢1O,p\jZ #%+@)cWuŊs*rEN%PN{822J!Zd߅\yB /2eNB uBP*rtP*/}TS,z!Bn躁n#)K'g W082ǭ*,/Y6ﺫ_I"@{4lo:{6cr4`ED  ⚅ p@hB{AIaj`@''Z.bt|[@>vA?tuuiZ[OA^: ln~`!cp$x{$[ͼx˕T"JEM@"=Xɣ׾K{f"!?Q]1H)qi5mG"xW ǯ|Ypˬ:'d- ,!`jqiq_[Y2 3yNrl(S) Ms|xbceBj 6"x+Lu 7 \.G4%oxb(,jsk@3 \k"@=k+ɣԧSk.q zq$喝ɤt1{ HS"Dcqk-Z,NR6va-tS9>c=]N  6g5^8kL* a[|({W氲fSj6oxf#qAd5ݯ vgDwV>GS[׌C)$CSl(K.ڜ@UWW\ZΓSxkA(3.:ZD>;mDA/ЅR?Y|䞇a|߾~Y~b_9J{kٞE*;, MM)p9]q.^i>9>Mni|].Vbwߘ:i,-7%xd f OW *o]No{h4*- `!6$@BH&M!BnB @\J :41nX,L~ze?Ȗd˒ ys{o߻}L27> 5N4~3iwQk摓]-n0d(S> ccc}ݵ^]ioog׮] uVƣe8{KnL>waoL_Y#jBڎ' :>z_tSǖF.;h?~+_"N 5.43_믡{g6ڛ׽=puU xO6"MhA5"A[sy{a[#ok\tVJ  !A4!H$Fu_Ķm2 mmm?k!v=ѳ)cSʞ->UX7eTYK |}R8kKAmjl`mW h ȼ`5-8'`eS C~)Be .=QP gaZӱ:G HA&@m8kq7 &uUHEJ zI&]0>DGkK)t#^ ~ϫ? #y;6G8(" #uIaf7||Ϡ9 vw7Vmg^i 6ot4|zq 6u?]=^C,,e^'B}\' qa6rکN3 GjH0 qx\DV7F Y+<T˙HUY.`Q*>/Lgbj˶TV%ײõ#000;ضmtwwiSSt6}Zi`~1*b74s`]jsVbg)֠P ^0dG|x*֞awS}X}44Gh D*tEU&uşN0Qi{=_͖S JA"Ja*R "3:N"5ql6/c;MeA&:C>W!q{D *4ITºt%mnDk)ho;w!b ߄T #wHLEOese󫯸S 3O265_ tDP`ƘKs%4L>ɕ;#|Kdm+v-O")RQ=WP8ElEb%o ypBXVp4a. :hr/E/iؒi؍SeRX>r߇ *)rZ*> 1LGNmi-h@:`,\`xzeO3"8KXhAհVB4xW3uwc3?=->"p@hD>ё@]FnqbS麊R٦v_5o({7-]k֯b=;2?7~t%:ZA׼rßwE&WZ͕XV[|iRB"NCT'Tg}n.(ssDP)c}#~:45Gh%ے# [bi@&|?};|s+_z#{v)+Ɲm;ع<-/>U#ڱȮf.݃/@0ϒf*TlNe]FuK+[۹Q fؑM{_"?g++I>T%XK.xXmc~ 1%$iho0i7yl%uۢ:€{>3Eieb&BbX{fأ~P V(2;ŵ>U;a5vXͧq=7ė6a믾R@ljV,hi?wh eW+7\]Pq1'BzVKhU!ChU*S>@(t:(JnmwGm ??\.322<> }n}ɵLڌT\*?MM455HSsUxqRw^v؆ <碙j]ƀ j)BHß7ҹ D7 <4V*-ZC4!N<4' LM: 9/ OҀ%\#uD^v}/#)JwAC{b`e+yNy>Kε+4tuRu5Tޞ͵J/`LnZ*,iX./(ErU;3P!ꍑʁHs-soq9;Vsv=FubݳB`P(Lγ𠅣q eyĢ[{4G)ESftGtPj#'+,un+ vQ({K(TOc]q-7/z>+$3yRLd:G:#_s0⡥ % -{ IDAT]-AgIGFg&?48?y钑Y] |CI$pZM8_mWYR253Kc(Bl Ҋt\* .nNQħen^ٴvSkrhR`el n&W ɶ&wxtRP,UXL2eέptndꚸ G^!4MrdtqW^SC[ B.JecppX[B\wMjy"l:&JeRٞ?|;o|\rvlm)9p13??5lg *7ekۦ Ъ KPYyKYh1P[@6Baʃ#B%{8J P:,(EYK[y: r-a"4K4tt>?T$+tuu [;GC< 13YҙhR8ضm%r\|y<~~Q~=P) lmwQ0z4g^ 2\/y& .C2_0pD, A^>=AfCSE{ɤlDDU>פ0lcJJ` sAWAS\y çig[BcW&ISyBE']l(*`Lv4mR,>+{:|9:[Q4GpЈ V.geW(|;/R.1B269?|ګ䂝lBJhԐ@ jG)ET&_,Q("KK,--әYgiiaLzڢlmmk -[1fk]l58R29;Og}RAzBb$$\s&}\83vYمZ=o]pQ5hXh*ET!W,++d % M.049"sTb|.[f8Ps<P(077G$!zG"\g~a :/zqV콶s"$XW\A{=jj f%lk#Y)j-KIX0dWvFH㰰.i5|'ü>|`O(JOG"|lH.^N_ff Cַ`tbi^k8z 9K468 N)5%B@c~ƍ)/hB^&x&0}-CEv~ `0R@Ÿ<ְu08v\K῰̶;F.nAi.Y+-xtI '`U:}AOAUl)J!ۜBʸ )hkEZm9@`wvvt#^#J>+تF'E^"P]?-V389>5HK캝`I#_og?Ν;Rbg>ayZm> i b9TlJu\, Nsp|Cebn崿" K(c:PfڳD( QϿooaϸkt˔5PN]E+]79{vn;aQ8H(vrͥ{(Kpxtϛ?Ǎ A* wU3+U 0XByyZ4!{փ CIt Ҥwne>7IQj dB:ޒ~IBu;RJFGpԮ?WB'cQl_0(p5z"78)%RnTbe߸}VL5aJ$ 8B @0!H->5ɴFGty&zY}pFWt.§0,#jSR F'l =iк`2h6\bFgA+Fm!\_Bӗx2ee7R:vu:@2?Hs" s%@_.NT A:SL]shldVf-gXJe=|cu'sxd.{q{V,G}k ߭/~o'gNdkHo;[=i'.`naёi=( )IɹCO;(#ql_<g=PS7BH̢H:&Ԛ*gt3;{[VDٲY<4ytn4MH_歯}>ٕ2M~vQαX`qqihn/rOկg;6ݵPUS)ً8'"  ^Y{`k>\SՋ}&ġk_'r};r[}. ZSW 5Tgx,l'\Zf݀p$* 4߈SBt!xb9L/hh܇Gر 6{&>$r=p1wuʀ5IM.L!W@OP%"l{-y^y4!ȰW3Ngw$QZ$JA d%GǘU, \PI!й$`kTjY(!q,M.o`O*=#c5~. ZgToݏӈOݵMb'<3VBGuKrfrZk]2F7m>׏s.2pgoN= b"w6}S3S*3?mi.1|ӥul2h-L'hdUBy\):c6nEҸd w-3e'}.maRi2yws\JNϷɢq, M\>aRu},7l&]8y 8$0JyQMu gag.t0TR 4$ %) &~~3|뷓kaP7XfƱ8E ЕN')Mgƫ_ƻѮH)Wnl1;$nz-(Xbז6O*Yݧ:q"n9 χp]\BjBkMpp|wWѡM#av^WrGnَ|(HC^De.-'eWT7`_M2;'t?;K]ӳNo'/L ^-CZ9tڭ֓oO ۸񺫸߼?,Ky_ѧ5qr:z7">fٽkdcQbep*ulOhH3Mya=2Ȼ}/lZ>;)+S)WlxMikpRn֭bhq?$}'R ([wp7qMSy%q?c|r47$C;ii$p\y_s'kdWR?4GgO24>lڸ##)ӘhA$dR5ѥMЧk}áZKqF- 'dwL0wAXF|n\0J.Q(,%,', ,*,$ +ܻ`ۏfKsM:YJ۷v?+ _w!өVJ/AGq3_:cJ8kBgU u]<Sޤ7`z\]'Yԏ5J.9t]rhh֛cuW)ni$),,vv^w(eXUm(c; gmW Qz%Jhߡgu%>C״[=4b<[2YHLVKkm vśT:K<. 3HLĝwܱᐏT@,:RcsIQ,kk^P 4%MJҌs*Be{4{z|,"tX't$ s+ Z=c{ws@Yi;Dٞk9g`~fGKC›666_&m/!b){~lWq7/gp[' Wz&O!6 Q;kN]-Q'LL-s_>^܍V)0_I,LIS}3mMljfkud\~Nx7B7M4obGs۸wbK:HJYB2!"d h `p {4| ʸJpO YDjiw0Re*mmPLD/FN/1 &ZZ@KSm 66Pm|>U-M-`ܟW>g)4ML>3 !RJBsc!N+.nR0R=ߓtξdXq]ڛ#`H~-%h렋Dl&c`xĖΎr!t^!аS/ K4r~#KIi{lӜ(W$`iZw9D)umm>r~6Z(sbI,\Z8JB'\hi$g+:7W6;M"_(h1<. ߯!)9XNq.Z@w ?7L,:r~uqʕs2H$St-֕F)0ޏsqQ]mm.tq㊮\؏&RJ 3C7.Lk!qr+(r>484n1S-ܒC9pF&W_!jvω_/AX(jF&h.Ae$]pމOlzJ'|1lWgރGO3Oo` R=~0b*\ۮOFk`~@mą{Zxo'h&.)斒-&YLXZId,TQvTPnSslj9VDql9l+ߢɋx4R' EGţUAE۔\Lgp]}һI)/x/VS"9-Ԋes^_]]ҩWe"anSS&tl6 uV^pjy[ Ôsob (zoJpb`T zwT 6%&y(%jHt'݂(ZwsQb[*/BboPk4܅\z;y{-Y_WIZsH,Q 6"pHVABq xjLXo&! JGW--l:\PXBP(pK(boxU Σ/bw'iSu4!0^q1%?%*OBT{\=ނ[.Vr,SiW@o247ֽ6E=&]Mi^-MH[<10}f"MܔI0H͕~W!@Ҷ &UBo0LI`<_S|O _\yIW^ cS ]btbXZ)2 k[/zc{Oκ-(VזpO-s鹸.\L@TTre\ERAdKIͲQ$a޷\# |UQUS ZUZ- \Rӳtvvs ;{ܟu&ƭөo/T,V8E-,6fWvLcBѲc ]X> ^YQm!NyK;O ܥ$ 1h.4O ,pO^fc`*|9PuĵP[YWۭ *#42!?w>|w<28)zP.hJODg>ȍq]淡>UHH41 ΜRp7i@EJIŵQM%Wu csݔ:w_ vS,wo'Xȟz_3$5MF_ țkwFĚ`. rbm*2ņ<\@Ohwg |ײm\{gY"!gUo5\I9o{c;JcDaq*z͚8sL 럒ae`;P8WK0ց /)/P-zVWJWB[iusb)z8܏&|Q~9r~|>fgwWsrZOCGC qKn^ay F"&}k W]bɢTQFcMC+՗U]ض 3A *skOlzI<_Ty0af%! 2twn| ~SOq4mZSB;,6Ζ̗X㞯tUnØ􆘼.`[dHKc]-+%;`4ys`D% "yx:,\g$. _HDjpN5՗xK$Ho Q_cr]MƷ_GL3g`&7JEq.vZvaI}y?W?|qǸǸC(`>R'+p pi(G$D4=@K] cRƓ|2.zEy }=jsodޡ5P!_}"R>K2VŸА/UW3 (BXƓc!!HrĢ!K](SUZn,m&gi.L]?+ZB@2W+w?‹ ^t|^\8pyÑIr'(#ل2^O AT_)<í= t6ll&#(YI.-dppףO;@Pu7O^;~ Z\$KޜV\)(qrdT{BO(IH‚1,ᒼNp|` Ƒ8qx &,b]6,hɡ{:wUusF3#fVl?=;鮮s=}'+VSY8k}Gwη[P3]ί*K5b-ի[2ԃPB(:ql 7tl`ԋ)- ϲt}0pדDy.!NҖIӝ'AwGtUϣy^r|xң\C?1r.^h\>ɼI[6똦9ϸky( k$ mYjΒo$%LO8VRk)l^* h UQ\=ɧ(۽PSjB ّ2-U)8kR6%L١i{(<^OF)OԙNꤓ: cUFJfk6٭ほS#V{nwu='eN$ʡVL6EyfJJ82=MlrZ&8è˄f?u*L2+RoqdxNzD9<5p/WIbPFAaJwt5WUahdT,D2>igv@w ֗@h6̈(gp߄'g1qP\7bj5:û6.J"l]ɖ5ldK_'{ B0©u`:-\#&%>w;Y5la/a@Y`20o5ren%%*@K6-F]S$qm_4ONh]=Χ9ȩx݌kmo'EK,(rAzd:/4K.#uoxkj7ÇzL2:m1:UR7蠣6ڳY:ɵJ&u@ @(   tEQ%0, bl|ablig~*WnqZw\ɷ&X&Z;svMGbXof9x_$Irn@SUV!/ Y1[,i`XGôHtu5qre{CEUwsJ.QU9ɑdR ?%  veN}T=$|ksLˎe*O95~yOTz+e֤`2-#fsKkybj/Z RUj(!ٺ6s'qh`WuhP=\$𦑨G R P'H'b |꥔ Nm6ab((h謀FA!Rq焕=q5 .hg/JI(bŒ_KeE8B"\ ɣ{NFT!B`}pboi>~௯ؚRATal| q =pѽCh$x*4EK:[T8-An CY( N=rzH$ W?=@,S]ZFAoSг- N 1/qm2P {Z3Vu΅rBc,}w:3ufXә# NDH"R/iyewu n~o#%Ƌ'Śm|!A &z+DsP.SZjsNHi:.JcPy4Lض19pb8xZ;7^R<9;I,ˬ ?@*P0xPvWx`_+x  p뭷rWw^[>! I"$cadO'ɤS 8Wlu=WW:ptlmLgVo, ӲYb511G;ۿ36R?u6ޖ-b;P8#ФhpOgL!Âu"T0$Lm0蹒r _Sqn.hR6EH: SyBb}A^7'Ph/)25nr]-qF `1$Dׁ߸;> FM RP<%\>?%n+Yrٓ6AY͡kRՈG#(bIADJ?Fa" )q pf<._`w߫*HUR9]P_y `bDEe2]p| SȬG<HH:W^ 4ˮ \$;#ԯd:m 5ɯ6ڳG=|H߅K1.xg>OP(I8'K>`C\*][8\an DE_S EW5[\HI'2ϖKhu~c&6xpg<9Pb dҤi"?8?0ٹsgU4M<33y= ߣZ1](B2Y(ltl2Ag:MGgBbOn*\3(zER.)V.1jq]1.}7kwBMk~K߅5=d^Pe-KE>wFWô.yw{3wvS38>Þ1#uDbb;jUL! bnl`Hס;ھn{rt6?t]p3ޭ8UU[jw4kߡJ*Aypw)k9E**Q)++ՙm QA"-A&8)282՝[bNU8ޑbe2 }douڅC=H> #EGsshuj>Qֲt3E AXCwRɅ}PB T2{֣.ي0Br~1{9:ج3װz\\+zi'#7Ұ,7G1oXZBÿv' Jh8r1 UK}rvNJ[ !)=sZP?㼾Ml6]|ZޘÙ)vF;JH勏?;zzrlY6JNZ՛,;CN^Na2iBN f7dpCS<0z=GƱ{)՘)V3;w!g$4/BxB@ {v8n+0}MF#6PKX/wl[hr#\^eQSU& |-rj.1 >{^5¯M Mkz(s<$o (V͗ɬK\Q$!dbAp{OmS= ކF1Ԗ y.3U|}2*6F^:I&ABz&cnEᢋ. oxÂ{חzy(EĢQz{{(Ѡ0h A^X(06[`C(e6?hhc5lj=^gOZ>Ǹ\];kXcŌ{Wvi/Pɗpbs6!].*ͼC.ن~2_gU0=c6Kȅ59C%p.Rm94@Q }q y΂kT=*eMX\IȎ<vDba 8_,!'ƈ͚:tM[6Yga[QDcD /ͺkKl!Q1s7TVӓI?}Bt|%Ԍt BoTJ?ϡ*hnoΝ;IRc~CUUTUʥ5}}+w"p_=f*J @Oɶm  ĚnӰ<&N`Sm*g`S(|+p8( lٞEUO`hA!_@L9җ̡/S)i!+ >)ԟ{e;(3Ȗ"yjtEғR㼔f<\GJ rllz6t! vSIbїM/XpC#èRv#JpiVqx˯џ?%{ gd](W-b(qj[r7Iӎ+ekӖ`|E025K*T RSN)5+4̿=DSwFk - -t*J>@7u ᶣn*GzH c^."=Q" ATA~egM  cO[dyf]kÁɾ%O.t2!{r>Kg"Bb3[SZV O>׾wYfTy7nliOgkz I=Gf[jdWB\L/s$fS|*DV^1W"pQ])gSVjk>/I7(:==3S(tvv̗& JH$,zN^T*Q,ٽ{71?;;;ټy3;w/M^sg;OpWF'i& SmۦaXtR2[]]ѥBL8\LA0h6mvl_TTB#a2\TB])&h-~ Rat\X㉋TM&­SH K#'c7؎MӚQk6m !}Ҏ2st2~~M[x[,\C>.TmU {8"]PwAI ngW<1[*P! S7wrѺD#!}Z.3Y28Pi&oW+h-j^|ԭmץ71qMgK<Ϳwrv7^ޡ||mO30Fҏp8r|D ᖟ Z˟!O[)C*u?1']J#%ʣ%z'%'WH<ɽh);v"N"$TbIT$MVo MlN᝟qlR_=)0.1{y4lS x8E3 `)p0@0H&r9z{{@4ߙ H)Flذ{L&d2T*5/ڎP SK%ʭT˲( w}{ XuqsO*B4Eta0l0L.#9:xzw8E)wk}a4&,N"諓O+L0GT ֱSQz*km.W@yFp &ֵmBAD:#? ;Jx9tZvs9ƽn,tlI R888H}ԬI& a|ᓿǛk|t(TǨ @PE:T5:.(wbG#%pWqBP7Tvqy5-ZմbK_"GI!V#4!b0=\:voobf} 츸]Iq6ٷg}˧JE%aN0FiTK6)Qf( B16E5MAzfŤ6U,6pm"]I%㣭G9 !Pn8EQѽq|OBW3rxE ='6Wl.-MF"hĥP%ϭ^UJoo/^z)tEQp]p*[la֭r咄D"A*bڵ4M*ʂǜb`hhsmͿG[[e˖-[E\ u$y[saZfBDVHٮTsI~orE9gL{%$R w,Atma3Qm+Le_y<'(0R'Wx,4Lb2Թ((lI^vzS!TD J*s<.d <QKw0p;*-\pfuQO3F-fqc lPaRQ1q1h(e z<^bo}>jEr*gVvl!i.BU! !Ù,Nc'N#LVжӳs-I[%B)%woP*t#RChddP5$ t$c%<1r[maȥT:=!"mXtbɖAW|jɬҋ9#Zg=qqE f:]u+ᏧChm?Wg9}$!9/mFau ੧zP&Vx<?ksi Z%p?1~ aM !rZ%ROJX%HP( UaIRB9ЩLc_bMC#(HK6m;8l&Y,9+۶1M =lK[a&FAgG孮(CDȤUArQz!DT2Zэ"@4bO5IߣT X]нhw7,B0LEby(y5Tf5ci6GfKEZNM0 BJTJ%!IH(cf5!א?^Guu.GZ-DtJ18Ou<22F϶o@%; HXU_rqWC|xsAUwDzܗ[>OpBШ(ov":*7 ]3zA8 Vv$c 3*~ixz51Mt2Z (W%v,3OK)f8} g0yBdYٜX$ ͺR AA;xzgp,I{&<`bjb[ZӅoc;TturAQ "Wr=:ԊyǸ+>|x0-_x!4M5/瓿>"RJeЗ-y4! ]IEy̦\Dž_Yq5=< <%A&dR(pwTkdqfVaW5q5/*7,t=\8"0K4}94Z9#!*u@AaQ5֮kGBICCC\voj"JYCN+9500hs ۰G$;E {1vlC5+ K=Ω9 T&&O7̝[cKUUP*yu=ctd&cs\o]SIc$1:ڳ\uOFowl¨7VDf!хxB Dew7x|x9}ztg+(G822xd|JLTǶ+B(hskdX"bL[v\h=~ ۩r5a x/YG* #Wa2ajd&LoC߶~4Qr /}?~W7$)lώMPon6 sxxc 142qE٤F4$NqR. ܳ*)hr71ƥ׆wW(],SZWA4iZkzN`ܥD#aTistdGGʼc?0N əWu16(n0|$\Q \Yc ߩ]+uG<U]?l`Ou]ZäѨik B ۡ0S'׾7Q_?noò^1q]*[׶::͒?αX u(cm}.109!q@"Ie5dSq[qHtOE㮔u&Jq_]RҴmhYG N`+JZ#&S#m!WHB–JKAtos(B0Ulҵc(ʸ_F}0&nbqLx s]0,f+Ì< /u-z%a;tF_w-XѶ p]JF[:0XMUxw?FSh.rʗʘ4Es\'ݕzm<41q^ *G:q.lrdUF8 қk_U%)+t`yO%.GcM<k20LPMe`hQIv~;m3ޅRBQbG&3[,Hɵukғ!Ta]Β;3{ :[7ml7OT{q8X_"/WИ9˂wiK ``Ġx$)^<^bs]\Šnq"AcRGjʪ\\thuA⹬ 0KhܗY_p]o]%hm.W6IF8$a+n0]ÿx+^~1<a.In5 ۾MkwSyM2 #M$wu6~&ºdy$sӗUee2MG[6LEY(UO9Pogbc+oqK#l4H0$zn)RW8>#qdG6(' muPIBH3V^B.<ǜd 70K&`H=CpR,Ph윏 `J+g{$B}(սNJI$Um\wĤ8di{64qJBr6{Eug@=+՚zvX؊脼o\=ŇULLAGq%nY=xqGױuH =mH猆 t *iO> q90wPɶYJwTƿ;m/(RcƎhBl cآX(T4TDA33 fgݙ{=y+r/qXeq1-E yzV-1瞔vΘ@(%H'p]$I>pRjwxozV=wg>{p4fZGS|[l1_o~{x!n1F ^{{6GV=jIE;prZHhfQAĘ]Ե!7oa.+b}! t.M!@EO`XHM~uӖTnf*z\$?ʭ#6"ډAd+% |4VY)A!te+3,J 2모A.A9TNI& J;qf1p TWEV%^~_|تl&s_uC\q;>)?+܋ %h˲IS,ZN#O%q:\T& JO;[j)++Sf;č PQ pxP,#IYJS^˲%u BReQtpSošf/iY(=·!g;\^>8u*|U3o~ 4>@z`:DE<ŗշ/au?Y]_ռ{1d߇N<]cخY^ʚLo[e7NceA]1 H4ٗ]ҿw9}{ӷz(qXdeؖnK ސG)W~z^T&BNbC&0$yw+-v4X]mq#0J<N[DYqSU(nct_޻SAiiK.r{G9uX6DYl!Nf %H6R0HRq>@Ԫz)[ZIqPkK;ˢ&Lc}u_lo#y񊔸|^Bn;a Lf{)E$NT00v14#k$m^@$|nWvFƽl0 믹>+n=Pџ&[iJMdrS'{ 1 7"%x^Wr//6PXP@~~Mz#f~0_e%i*MӰldR'OMx^P^-iշ19{yUյ90Y\?ntZoH\NRtuz"6;}f=< |d2N[8+=D5m ɒNbP;Cme.ʈE}[v.}Ԧ:?_.ѷ8GhsF*SZsd"EaFN~s f3k9i5Ժ,o*|X^UOYq!# A'ַ@̿;mHF խmfKn6 ( 6'1 KxZ3R^$["ר9fRf,UQذww(VbIzE B((b|97RJ+ob]DZ.TUAU\-TMC\e2=s⵽ӯf7,EV[~Q7eu;;53mn?/êYŬHSlDաʗA<θr<a%젍gg!n*qj8 2t1y8es;;²%M~(/mKHEy_әz;W$W ;nckধ6s|I$a6QUQ>fU7~daC"d]1W D`w g xVdG _+A:YY#4e\zީ|nV1oZLç L c$rSrpop.fѫ0DAXʤ0^aUU$+x浡\#qvq"٣QUfVLpνz"SpSq?d,Zg_}õMd?`yM`F+ymK8>kj8kzj"XrGYF`Ǟɇ,oe$xSR\axAyI h(T8}YXsխ&`MN,Idp TX6˪vvjJJhA>2=DSpXmϋŔQM]'x"NqA>͸ Gc҂!5Nuπ}T7Xmݡ=GiSD p4!' CWr('6?9TMJ-s{c`lZ57Q^mYDpH7n_Rb6ica۱^@]}#AiWuu'v&M~ڇJҢֲN{4 /ݦ222MfNډvtCg\v+7s GZ{Ӿ LΗtR*?> 2ا;f`ƛ=̲k ( cGrޯåB2j+K:-_˪* DjTG2cCC*PT8}q*.aЀ|F:c )[]'acFU>^Cٍne%ض)T[kOgFF p󜧶1^*,OH>=ٔꨢ#PTA*c#/h̉Agjg7J:l H:S8~˕# e/a^\-\Ls:Na~P< !yB CA^75R\!n,ҁķ1Wnp75SK1 پZVNdp mh<۩qWUO/!^άnvhX0 vU_g3會tz13M*?s.fpFƚQ,X{?p>C *Nɴf*`Ox5t'?ǦHJ% v4FawGrn2hnܻZW5qӽ+54̮$B>vQܭQM T‚a(2kN~]VɳnG!6@AUN6YgPU"X>Ci[fEQX_@$?Dij)|RA~qr:Ͷm6mɜM]Q\_K,wg4Y gM<#BKVBOSUU؀GUnM%G=i;&dDWa$NbF蹱ۮ}w]}yۭv(OKl*˂U|V^_Axi&).ޝ{Re*塗G7pDs8,v2/?6^h>_ѽ|&n4} f6op_˳ZnV䠑#,j0g9|NWM}:I$f]@g`+ N" B_8xDPS~} oVk-%3gszI$/^ ]F2Pѳ.{Fi,5M:Q.ܻZW4EiQ!itj]MTսPZlj@EZ&xy^#sR1o,!)<EURlIcMkXPGQAvc{ª*̞)ljJ5杏8c[@RTf jyۦdʉW3s[ iΩ0;j7{ f>[^#C?JrkdO/Ѵ+`Cu1j6:=| o~gLRgJɨRIl;z ['۪ܷ{ ݕ3O8:=hl s f4EAh*yfkWsoVRo=+&2sޕmB& aXH^>5SVޓkن݂`qwp=.=L8Ʋ,;?yvEeM ]QmfHRM6jSMe Ghl ={ƘW"|iz Džh(d%3qFcsPCCT Te3<{*U[ Lɢ)R4;XN:wknO?CmCfWDE*i`&l30`)Spwr6Ie2wAM}%NMyK8= Ise4ÔDXqXqIu9V9pX|oڕWl |vdi/b]L8m2|mf,?xwNcqƸ+KviIf隦9'q|T헟aIƨҤkn f p@ߨGjKc*TZ8A)˸KbWpYp:dPkkZOG4I{=L:˹6X5\pmCǶY3ˊ eaQv@?iM"{8 %j bKY`YU>&h߽SdƈAp@6z'Ȯ)'+d6x0Q~cxJ*FrzWJy~ nǍ{yqLiD[H[FM/AYŏ@Z7x{'y9"~ >a Uv~F@!P%h_Fg;*TЭ Щ]as]|(b2>ڟQɽ,Jm_9g!H&x\ݦ§Fpմsƌ;Azald,NSϻb#p K!UEaUttԵ̹jHpTWaX/||{Q]NQ%) >fqnփ6!Pǥ+HaU!՜@ڙ|$o-`Me._O$Tt;ģΟn'74q9?`du4!|ɾx1G Τe]2ͳ5M,Y6͋?jݤ![DDLPSu|8vT|M%nQ4<;UUU1uT|M0 ?miw֑Ngvm%5 ]@btqMp9 53e BOCSn%Ql{'ހb+yȟ϶mb81 CCsR&uY\/9HhZ!єSDP"yτobd76FO& e%y>?Dyf.tw z"EMC#u M4ԇijRʪxg|%;vFD<_` +㾽2h,Ɛ@jnP mw_ƙC|Ψ0I+݁XG^KawyԻ,Ie U'R)8sD5FDZTS"cnP0:T#v:ҖĒ(.&խBmC޸I"L9$,]'m+3oH$Zz64qw=72jn9YmK3 ,J{`ܒhkF^Uں*@ٌxR A7n'=*I&QQQ<*#=(ZVi;l G"Rt\hjj²,]5LF)))i * p8ܡ0 *nLÑY`M9=ׇC5vW-/s-L$ٵEVqJf5LPv&M;3tZ$-hkiw.in)Z*#sMҕ`Ye$8#pi;lB"5I7W#U|G("y)T߶N $J|`g,;)(B"PȔpE_.锁"U.g: %suȎ]!`*FйGI'#jeOUV`X!'x8YKLؿa}<6֬^'RZ'#c±c"f^bK; ە<|%{Ohjy>C"Ѳ}DQmDMaKyGUJk}Oe] Z-Wyh d }xKdU/UMZ'| GA2I"t:rL=JWoRlKwrͅH sG \tQT85U=y]:0ԅYOm'wHfKO>h& #K\/t4ڲ wb6l3f̠OXm_硇bɜp ]s5|u] W^y%W9N*8Ks[jwu-"JQ^^)¯~V r=|rlۦ)SpA^f.89s&S^^΅^!wk{g}6GuNM+BMm=3Hř} lo}T;-Wߎ [zQE,g NlgIW[|*j>nZ 8~-Yh˰EmHa4- %c7~1Q"pYa@P1<hP P& F|˒Xxiwk+ќ^=e.=lpWPPeǤ^A}c/I~ Q7[L4 0mv6!Sxj$ IR/`OΥvp)]".)FJ׫;O;^M)n_{d1g,CGI_N|JyYfwb"pdׁ^S`Pą;EPhrqjk(aSB$c^bF3w, ^Yǝ!<@7/UV:WL9tb|xz#[{~o̔ߞ ~w6O<E).6}2[9.4MA (..`awi=C*6?IT7y,0ĭlv-´,t %I[IRQlj?ICP2Tqoqp8;呗JRH)ۀEQ߿>ٓYf1m4FI޽|M(wEQD"̘1Aq@ĉYt)'Oٳg3i$2e .W8q"irgp8xgyB̌3x1b#G'| rA1g=Xt]gƌ曼ی=z*Qӗ6vď aюf+/7 ՏIUZT8&`-&DS >n㋴HQG>~Ki tt Mjk񬧱[ǀDr#爠H0ӷ{y;= Z=y9ۆD<7,x+&l̤$v*BMv:ѼW-"*gZc%B 0P@>8|v8ǭW%ro9z~|&x$D"}3^z@Z^ۢ4P8|204p"4,#ZՌӱu igNl)m M}"Y\=n |.ݲ,.ྒྷsws)Oss7#_^oGN=3h N̙37oy믿Ξ{>WT:bTPY5j1ۋxa3N:x6(>'Ƒn|IP7+kWKv@UR9roj(,>}`ݺ-Ui \.W.xRJ(xٽU:vCm@ v;ۢi+k!}4$ZZsS7g5w!22Eu1-#vқ_#iPhh>d7Dfѭ$Ȃ/p!$$lLXRH;mL.uswqo7'q lbRJ>/#᩿]Gf"[0\N<.{ʢmƓ ZkI]zn*I*c4jJ`%vFWO^MEѲV;"? 0eռ(L6^g#@;<Ͼ>_ ұ" 0,wHPfp8HH)x=zܑ<\|tFjYT,EaCVײ"&nۈxX!N"QKZd{'e3 _IX&,j㙯f6aXؐٓ L45~x:,Oܹs裏xٳrrL~ӧ3i$?|.]7nƍ˽[n|ɼk̚5aÆ1g-[ɓ}s0`r 7|3o|w;eee;0zF͠AZgϞYӀ4cMaT˕JT:>7[q>i8_EIB+CnH&!|3F"`pt>p00)I ۲`&lk"j sݑeY}{ǐiT(#Hv)Cq#2nR-[.sqyrQ9xom'Siv]]àcr&%)ٿ/qoSw:\T (4P(Zwx3),vT)3F+X _3bi!aM C!_!Ż mB4š!j _Em2'./q  ?+?Eϵ=U?s;!xwg].;}CdNH&Í8`^l&J_Ű pnNWzF[$JF*Hw!XOs ѳ^'%8"+e# ;޲U!ZX @Ge@ϒ&5q)uHvྣ[pZ^^\p~!*ww\\O>=ܓo=/X,Jݛ/4Y`Bƌ#Gv`V}es:8 )"d*͙g$BV NzGki3j<{Mxm4F_ ?9rP19QڼI`nޭlذ(ÑOufRмWR&[dx:R:ipc!{Y!vїh)'~P VN*㔎':VC @U6mnf슣*^.:t$mB`K ua3\w5\&NY 6M3S8o:C!@fJ(76spf ixcTd g)̩e" w)`@1ۛ俭d'Lk#H 3i#m ڌ;?_.f`1v/>͝*Zy{nd"S,*dvk8v HX2:=̖u^㪥]h#$k-FRD$@} i(Hnm{NoأQޚ2#F;֕b._~1 &@Z"`ĉq]wrpI&ضMaaa|><x)%x!DGӴ \Q ȔbPwz&+ ঒8fk2eX,6-w91 yGUo{n>$@ ȊuQ׊}]e`"ŵX ("L2@DDѼϕ1s{=9y51 4ݱ#)6ƌɓs0po<ʪ@;F8ү`9A4ʴtOP@PU 4`fe" %nB!-F5 %d LѠ-I L`qw&~g2U䧲a" IDATִOb6c C*:MdTg0y{%V >*UiʙVq;2A{RC}x7֬YÃ>$6%%EQشiSJKK)//gРAHD0 -[4;w۶m#>q4'vd/S"=ob+n+`ݑ!2b%jJ7}bݕn@W`O3ڂl>f d|"8cx7ŝhTTAzc;՘vf15=<(Hڅ.YEWKbdpJX*c"X.TG \'cm M B3(A0C! qG\۔D~Q)YnɼO`w;Yݣ$+kHqcG nݱTSSE1:P74rOa mj^ 2'^{?2:kjxHKJb1qY Coے਼n3n^K',bX@odzXpZWmi{5U$ |AMEb"~T+;KѰ[n/IA~a 羅^HNR"Z_4{HJ*A;drޟ-|t^^Eyu> ǖBF9SYsm}S^\e6-Z LVI(hax wb/AMJtҒہ{Ddq?l̘1O‰D2_S_::*Hi&\C#``p7U|PaT2$N&fx4w"-?&gB\|M~rn %ؙz TQPXwP( QȗɊMEUf`$#-o+գ U"¿% 00Oi.*&4!>\WHt< )ts>i-/:ep CЛt64 㬓GveN͋ᒬD4sqX(%(B?K(SZQ|:*٩2-\tR'ၷxsIe<.%MיX$Ig7͊l@JU~Yb}k;%5,b)_}c6llJk8nH? 綗>NHe8C芎l5ż 2l JR&A5x~QUWWwtہ!6ϽKee%W_}5cƌaժUL6ٳgsM7xݗ_~ɔ)SHHH`̘16x]#55믿ɓ'sM7qvYb?0礓NBA.]8q"s;䬳Be>c^uƏU9RMyV,Xkg|k؛ -ZXag/GĽW`fBa&:5V,/ر9\+0cWUa޼ N;-@t6v}{#@BEd;_E&$#>e:'}xJ:&#mJ Va6(D}X,TVVuV]p8ߛ h*cQRӬ+EUh:Ȧ<0хIHuk*$"{]ebC2zZ! -q&w36yN>_߰LV|2KV[Z8{0|hozP>AY`w~9g>k7~-3 AþzK=-b:~VOy~SOyn@NyKKµMRB\;p/FA6v*30dHɏ .bE1 8{P&RtEn%cF8xol۹O{Yw-ĥcиW8FoO|ȍW9nCm­n#I !|hh@7{dbZ4J]י={6:]wcƌ[oeL4p7+sΜ9TVY.IVz^{5>l&MD wvSTTDy̘1{w{Y)**sF~VjF]]]IXשEd~~jJ8a@FFTwcb`v7afӚXo̚L=;}H^xwO$ h}޾5 ni`j ?4,V v]q8l>}PR`/P+CNy*:m@$$}/jzް8bg!% ѐOXrrr[78G9H0Sfcm$Ŋ uRU/BD5If&f&ӿ[V8kdAyn=ݧٯRhӢKYL> (B?`톥|^oI~A6z ed1\rɤ%|DzH1Xv ߮oƟ>%Wwymqw]뺺^竵Zcc{; IpͿ2߷/HG MxCK;Ll 2#?k򿁂j O7#N% PS-WM?o>OΝtNu/#*(ޱhh߷O&tURehfա::t`״:0w\$''7Oz*k׮ѿv|M֭[ey\wu f@9oZ[9ٴi;ҧO222\%\֭[4.]ЧOu֍/&w҅I,@nn. ,pHHh$+G=RCjC+VK"䶱u&c7ddpo~mϞC^D# 9vo/nаڰms1ǣ:BʀpD(hy0֫tLD@r"K>lEU-pE{2~țBEΏi(## HKEXNPU3Q _,]W^'+#חOk;5V/#$* !7#|=% OgONihAJb-Dqo[@U\9!0ծA_:' §f㱗&_.+;ڠr+˹jg)uhBW_OLzdoZKgŔ >#S^ooKZdr.P{ M u2BP&gYOjj]W[$̣SQ3\wr1>ʪ<'Շqn+4w>FJUk7ѫKxn B\syT:%Oe&鶸62EZ50#%!,0V`i_۬Vkm/k׮M@{cphof1`:ða8ꨣ0t p8>|x-R Ϸn:v$["v u bbϕ[R1[?}$bH*- 9'{K߸Rwuyϩfٲe9A(~`ǎ!I)r\gs`~A 50m<|U!`߄/2E xC|!^ 7H>.D>aN.ټ]$i6ťd%`6g_S$0 tcαHdIb:?SLxlj)TK]| JyRYU%D%Sro˸-s0m]L3:&(do B&J{;xb;&$kLbn&J[lKW#0LF;9n} 큆~g)XikLjq|fRZfsrƿn&}/hnrc߀!5Sr{yZA9 ;a6w2"{c!`I0U?8U4v 3-Ǒ=E$ǒ,ǒX& ILobۗ+WRRR^ S ix^ˣC! $p:fሶ!ZE`͖b`XHniRB*=,\-kzHJ1E ,Zl"7d*G^$Nb9.6VQ[TO)nBA 87J`R)]HE%6;1˨`嶐o󍴑^՝ү^vZ ՟mUCpra~Sd*px䓓Z㊢UHJ[Y8&Ii*_]]:kwqT_~xkXFra`(tNOgtd%3j™;82SFʚZ]y/b'a疫.kv>3u({u>;8*hz§ܼwwt*U=jgߐQԇfi:'ɪuX,{m}oŸ™Tm9OY,H,!S{b*?Ŵ ˝2))a[FRmQl_( oèt?x멩P[[YO:`-tN0Pzl>*J8 7t ش%ɬ, dS%Cn(>>JYguukI8Ph۬ۢۢۢߢۡ|6aeYpC\\^Gwa"6[LLTFnbD"0?Hmc-}v$GLCBI&*$R:fbPCNi<{%kiؘlKILrdF!c[B=D/D݆i4֘;D4Zc3(*ܺ< `"0Lg݅ N-clLtbQ,%gKz%#U }?j-;HHLK-º~QK\~ _j[;55M>Nb D8G(¢9i"_z.EEFxzfI䌓O챣gS/G ϊXonHG_/ usoDḤ )19?S'f.(٤v\ Cگ$n.* /.gnvDhCہsvpήؽ8Pj!YBB!@|G+6qر!?~K`uor<_nܳjG 羏众kHOcH*BD׋PVVƖ-[u^ #GR'Jʨ,D6|J\-$h^ab3-H'2 S ej&(HLHaWn3d111l6t5=PU]MAA!PLL  5SZ4@Ηfyd@(x17B\;NFHhD(p0PCfbz ~i&q6"7(+LmI-%TTWPopǢ*Ȓ/nXUZFA9UU ],$InQԻ$ F{I^I߲,SEVod dpbHyo*fbQILL$99?Kz1>q=o`R4/$I\Q~1j fCHO7$Q1xEl,S/sNjn:q*A\.޽"7zZLqM&  uBsGS7NW%cTPR57]gwۿN۫ۋx{TFd99ǁEPy)] =S AĦf=3tQB!\p!,[Ç u z+j (lg5Ctqc59fV1b%gXHJJ"99-[ֹY9.}zGرUVѻwoLx]{t%.W{kږ-[زyz4Mʫ*(*G^mDM(.*`ڤ+91|e;'ٛxV呔019$$S!9,8^Qf>E{R rali|@ K E,m]?Tsn$I!Sس{7wu\wYQqډYz7{(R-tN4pWq8h7AyݎLf8=3*GA[vB*++ZTr:@(DmNX,?c&999lݺ5ATU%..0~ՠv;2P׮]Om>[$ICFtIA}Tddx7{ ulZ ?+>&tϲqdM1;ā^^@ n).ĺUS=%.,&1! /~~b8lVv׫V30/ȿV ܋`0Hnn!1v )lsXT`~|q7]כhvm( EOE`UN ݂f6r2CZ$8q^y&y|:S̐.dٚešbPPkR#\|&0ə) G444*jVT#mCD+ù`/ޭawއjg4Hg jյ(mX,JNVzG뢽^`&&6nGr T,ܾ"D@0SEFРod0󩗚\bS=/ 3y袪"@=ePcbmM ɐ*W\[sܰHK!3=@/ެ7wK N7 n"IhQUpg@Lݻw?䌻{jZVc]V*kjp?֠ ;vڭ0AŴab[w7*Tkx7n&Ļ (4JHK__%ެVTe䦠V$lv Z1qa5N|0bdTI`ӰΜ|zfе~w8t IDATicfKߟ [@Ce:Ei{N}[1M2jo%>vi3puߏ;-DB aaT~Ovz9P|!{ A ~k5ޝuKKe諭nSNCa$):imќ&r~Eޟ&t1AZABTY '_0dJ*Wu'p_Q\V-gٺ:ѵiۓX;.XVG [bzZȮSqLr\Ud+NQ+ݤuh\N;qqɔT!h&"<9- ٔ½SZ/ ~)@\ͽ=n\sI\WG[:T$AA[m=nwӫtHR]^XT4.Gx](=ҪAg=3X3nmTUm[ [_r4aq܅fgABq: 6QFѽ{w222xgzm*vv(wMlllft:?~<˗/os=toL8<,X(UV 5h5M{!==E!rM0d+D8;0bӆwiL4ڱyT4 ͜y8~7EM0
    O60~zN:$cȑűa.]UW]Eaa!r VF"; }K.@ ۙ?> ,;(jҩS':vH||A6l`„ :iӦܜ /}SNi/2| RqO?4]tQ{Wn رc}Fe(--t9 p:$]$ ̟FREZLG`Єb*r#׵ϐdJRF3Ν2پ+?Nzb]YX 1:u8%^*:2]rb$1t$1>/g4}6MoHe-1O3ɴ@|s vevm~|>HNvnG%Lbt7GCY' c{} ,+nۅrCYYBtiTVR\ZAޢMmFQQ&LW^i__δi=z4DNI 7b4Enh60ö`I\~tM;[;ׯzj܊YM$>>< # !4-Zr"߅B!֭[Gjj*i$L!˗/gΝL8I#ɦٸq#ݺuk#0Rnc7Ң$RYr+rhC݈wO⋛|ia4y涴}((Jk#usӖi)-=x㧴zo~-aPi8&)ЄkHFZ!T:$55PmRbUةw`keU* \+cWLOt 6{2ׅRH 84~E?X $Xe6*PeEXd$R8ö. Pdqo$ٹs_eUԒ%Kزe ǠA+0IN#UV?R K/.%*k=TURUagi u^_rwaTx()D:@t,˔}{8ԩS!bs ƍcӦM| 8!| Gaa!ׯˀHNNn"WfϞ=~\.999 M0MIaݺuѫW/[ 0M4n"x  ۠( \r ˗/ft=+I?#wt֍.]4Iرcуx$I2h Xj55580 ^/=۷oݲeԩ{n@*>(III\pmf/_N^^w'OcH܍7RTTW^^tڕ]"IR3g֮]vӿTUeڵ{w(Iׯg۷/IIIhj\zs9|֯_Ouu5СClWeJKKpЫW/233Yx1qqq?- %%%] V+;w&77~!>Ǭ !PU۷yfjjjp:tܙ={F@mիWG#77X:+Wχ$77\Mڭfe4noqa Yf w4MRSS2dHtsxC- #1n2 ɡa4=72ȝ DeKV52(` *kXX!٬-!FetB![/b4)XDM&"Νh)] jGLd7A7))a[p\J`jEOq%ߐ7*#Z\E+Û7orHLLdӦMdff2c .v4}8a%CpMﮝ̒,$z}VTQZ^IYEUVTRZ^IiEud`bRVYŞ&loka㎸cvs璕Enݢ, 7܀fc„ ̝;+V`nO>YfaJII >,se۶m>h((2۶mꫯ[n /D12330a7|35E&%%ѥKC(bi=*ׯC2qD&LiQ7'xO?g}#G"Idž ;y7/Ȳq̙3ׯ=N(bʕ\|ٓ $]vpBN;4 ;,]uqr饗2k,͛ߏfI3g:3f`żx< )$%Kp]wdt]GUϋ/Ȅ {[|G}}QΝիp\xL>={̮OaV+}O=-,Ə-BJJJ4i^;3O?4:>.ӧ3|p~mLjfz!ϟOuu5v;/f̙Mt~Y\;<,Y$=裹k [@qFfΜ{GMMq۷/g}vߕejx ^}U6o=gС\wuuY,ъl2fΜɢE 9Ә:u*M￟g}={ϴi4^.tr:`#H0I~qS"f3kcuCtoƢl ؚaf3ɉ866>_ 8n|HT[:T!LoP1 옊8`uR,8-nUȸT"R%S#J4 #_tƆnYfѣG~iشifppgDصk>/k.LB^xB0w\/^wKJJ O?4̘1={rEӦ/iW]uUEAyy9K.c%55SN9^{z.nv222pѢEL2޽{ӥKLӤ/;vpp)xWxǨՅUO:3fУGyXh< ,\'m,鳿k, K.ke׮]L<xxWxG(++'fa&\,[SO=#>'(-))7dќs9X,VX#<Q8q"VӧCAA3fn#%%1cƴBPVVƴix75j{.vᄏkOc=ƈ#p\[~otFiTVVrWqFnFaΜ9̙3!=P .Ru۹뮻4hg}DH^Sٍ k3lG`._LMMt+XU9`6M \1Tz*}cd'&$" T>G(F0Ee1$&aDf~.4MtI4#.:Dޡ0=#aZY"+v%c  (YVVjn7q%$IvhN tt:CyP(J_$~+eNYtANF*KC~n*G}42rss 5k| gy&@I-ZUYk_ѩSNvBӴ(oGaEEQ8bR:ulT PYSZNN KtQiz v}/ÙLڪjc4iӦQRR‡~ȕW^#<¨Q7nucZ/_. !YYYy̛7Iؾ};-_~s=Q?ܴiX`AG|}>o&f HMM[n΋tl(f$C8j㡇"117|^zE}SSS4irJԍf#Gug&11!D->8&NȪU5kVtGCYK0 ^xt¨Q춴f,n:>s?\s5k \veMʉl^}U:u,ˌ12>#vMᝦgy;v0vXfϞe;ӹ XhQtIjj*oV4駟Ξ={裏?~~YCŸ7>1qڵYfqp80 ;.??1c`&1˖-N`dd3h7v( .'//x޽{#IvL4Ҿ&I[n婧qơ*Pd.bs'Xa| >￟~!Iz*YYYL2ɎMXt)YYY̙3':7I:u*_5Æ p_qF;8|h?ӧcǎgaڴiFSNL>=L Gw>AP~.tr9L/WoH49dNC@$᢯neZ\HHV$j yB{BOu;EEDD~)z1YHy/o{i5L]. $]Hfh&cb&  3 i"0@F)ܧ}{j&!"@Vjy狱Z,crv:-)Y0M;0ضmɹVՊbj GE-GtQ z$%%a~}~]3xj)**%kw̛m0oò5k%]袋hl2ɾ|qwi7g1`fy̙3/odeeO2dVX^˦M_~yUil?nIIIy 60rH.bJJJ7oBN'wu]v^z) &&I&qub+vT!ز}77DXnТJלtHYtHNb;Dn`a`x(L4RRRx饗X`sK,᫯Go߾q:jj*gq.@ $Ip H梨Xrssy뭷Phr:=iӦEƁaiii 6,*K'2gy&o~)6m"99z/ B-P(ĦMO(++n //Yy B{ BHe]O;;v,iiihQGEvv66l` 2^Hǎ BhFVVݻw磏>]ױZ꫸n.bbbOJJ #F`Њ/0aB%4Ml6[ϟ?s9WUPUK~ze9̹??=4?>r駓=?!!q/6yg+W3q&nÇG`[v-- .`ȑX֨ѣbl޼=z4kO^E0vX oYg)33# ###:~v@qq1~?؉h4<.]J}}}tn^z1f̘Owq<y?,)!) sR.8afآcQ#xxϼnƩH2`wfr7_wTela.4 EEԈQ0Fן=&j4(& #Aea+la[~ef ,;}3ܹsyy׻a̧'[Y;JCļIeX!ka FxlMӰj1i:/o_i~&uiLeJ9icJL׉i:1]'jG =e{(`h M(} Yk!}l$b!]` u-1 I0$.+啖T@޼&FbPUUEUU6l'$Nx<n7. ֯pX,F0_C iy 6hh4(Lw?'L#<‚ 839c_»_+hxBc2EORZZjX:>sMb{%77W_}! r= v̚5QF# 2?8sE4̙(b1ӟyG?Ox' 3p뭷RYYywĞ zih_cB>< IDAT2 //axY1% ++!ـem+.) UUq8|{c֬Y[W^y??dSEdY64M3h+,K(W_eŊ455Fimm5mK,i&&OO@,Y%Krq2o<8dYSNITỦ'Ț5kXr%g#n6S-Y5p. O:W^yee 70?9/… 0aBY,~t]l6*TWWøqzlB?sA$jt]gƍs0_|@>ȓHEw^>ЫׯlFc-كjFE>Eb1rf ~9ƌCyyyP/4M3t2B%x-MȭXt+SqeU6off.܋fX̧_|)nS\?;/J$]gPVNZư(C7Ec0m?NB%txI$ڍ[z`H P 1QunѺ.dUXl[y E,䘞"SҝhI#as ̶`-1.o t;<"V5Y\&iDՍQUPb ClOkjj2_NgZ iii=fwfP(D8fa۱l)Fż|8fذaSPP@(iƕfr8* -4dɒ%\wudgg֭?L:n|L|>grg\uU8nf y||嗔0{l222x5k.2 Xb/BN9͛\ƒ}/ *ꌉ0|ptRN?C688*LvYfnTbhiWQ>N/q.:¸q8q"?m9X%ee˖qRUUfn$eX:ָ79yc2ˣؠ&ֆ KOA#~ غu++V`֬Yx<~Wn?端HLS/_Nkk+〟O"i5{nt]_{=&a=;Y::: Do%^f`deeU\EQ4bT(mmm,[,|_%%%}V4 gG}Ĝ9sjGzz`D~+{9nrss;wn52k@ΟfiXbc%|Xõ(Z*fQ~q(AEwE'; 98Lwiy+> ֦"` 딯bB[c'Q8Gh|v K hZ9f',1Qgivoy߇\LnY8l)\tvt0[F"ZxL&1 f۶mlڴSSS磣vmۖV\.<@ `xVo (p_oN"^NYqEz1Md92I\(&SM2ui͛7sw]wݕbњ0a~;/"˗/g֭,Y <3,Z\xF̶m۸ О8{.?-[;;'|³>c>/t`b?&CŶDD:Nɻ'k2S7TUGU5b<lGg>qlDį6J7˧ra B1jUn_"*JVQQv'pl xl7uupam&pHu*P+nAը G=a[8r 29?\ yV9Igqa6ՆX2ޫvED|Եb{L+BzFa.//jfocb0|pFɬY Nرc7nd˖-ڵ˨\WW z{=}„ |>~?PEQPw?\ɒS@]NJ (.ȧxH9+ӍffbZI6eWpO6J+`ҥZkSO=ֈ˗s7r.nٲp8ɓ{B6mڄ]AzX{!N%g d$&f8A$"!HS?nm6?87|3>/RsMՒ?%[6mĚ5kꪫ E[bKF04bNGLUUb~:{,k׮O? .H$W_}qw@] ~|7(,,Ow;:::Xp!\s5=,⺮a?{ Ot]nz lذiӦ,ݯח >͛7θqR  K@XͿ9r$/f0gN>d6?m۶1uT>z0n)0d$΃6x^c7l0\.DSbC:t(׿;n2xihh`\uUb1bDE֮]K(bСdffr2sLnk=c0 ~1[&V8)DtvP+osKo3 N N8!ټ~n1NG ٲwX+zcxAR&R4v8bwZ@88Hq$|7P_dPd0Fc$PH ,O:z7ӌ7)q0/-{l mWm]a: PAI|KnNBך`I}m8iqϫ#G2zh. jjjjزe 477C: LOMX`07DofZTENcHN6CrzM7OwOsrXq뭷o7fgge\~TTT${oʲ,#IᆪWkZѰu+2qo\5ikQM?1+&u~OCQ_5t_X,ɓ)((`ٲe?aʔ)fb[[VBUUF+(HtL Db$KrhoogFQYYd 9#FRF~?ϢEO~BQQQJ?39묳1wDG`&.V_ 6pw{NCC\r MĔ?3ZUV93Oo (|;vay`ժU}Zib\r%H0w)YV^/_}/#Gdʔ)nvTDdDQf/fX"k㜜\yWK/pB^/ƍ#o$ ^8s9s&:/';;V/^̢ExSB׳SU/~ ̙M7DSSÇ'xbyMgmӧ3b^{5nVN?tt]?6xgʹix;930LY'|2UtlԨQ̝;̟?m۶1tPB+W䩧fԩޯ(L0iӦ;pwԨWGNM4\/_]we0|z( hn˗ca>|8/2YX@6{O28f݌"(=&q&d36k$饨JL#BD"T o'jߏWsḂ_r©IOj@Sp`JZd]"'R7v$I b"Fe(3Fckƶ(M"(C1Ȳ يl & pM,3h4@ӄe"e66cs4ZYgbvĈ2;#*|gJf+!vD\'SZRHgg'޽0I&// 233)/ H>YdggZO~uTH-Q y3(wĀLTɄd2@}f.d5[zؽ@`-g[r%w^z)7x#fgʕ)ncٲe<{=2e yyy)mӦMd2_y~__S}SE J,W Sb'.'Ǎ]kq8kf|hSlU c\s <4vމpiӦq7#] dؑk(9;3?ٰrws]wuVv1cR&GOS,QK/?A'Z}󙸟.䚦qr  zG?Qu3#9$EQژ|DLsr7?>GMYٰa+Vଳ2'@~z} >}?k9sX`os1M='&]UFuy~}݇b!p1p9o#w`8$I<x^,Xᅬ1hJ_0iҤ&w DE1`0Ȍ3x-܂& Ÿ ;S:^/?0r O=>,W\O?2dկbo_De, ӧOg)`6jJ dbܹ} 7@ff&H@ E]7ߜ*}~x^~_xYp%[X,ȑ#;y QZZʜ9sxXr%x<8 }Q~qmE Ǐ7C~=Cp2X53cB#Yi *uǠ ugKN0xV;`_EUEqI<45[^b]GXm&;|K|I'4M'![}GL"0Ή ]#]#!z?Ս!#hhD ;;ܼ2ecNw`-c*v_,aZY͘&f3`h4F(%qTDZv}]#MM؄դM7MI7a&7BJNyohLJow}@24$Dkw8ɓ&f$ ٹs'KIqA>99v1dH>Cˣov1W{_^;v=? d(( լ_lOa*ԛ,b1 QQPQQ}Dž^9M7Dvv6K.`̙L2ύ7\UW]d^`ҥ̟?R-i)r$;ΚF,L_i"tѨ'*: sگM0L\tEL8]vQUUE{{AXRRf3 Ib_q!2wFbݺu477Oee%L<Fnnn Uf.c3f cqTTT9u]\|l۶v(//g)%o˭ /1bDJX(b::&NHuu5FHRw}aϟ+F_UXX[oEFF^D%A2e K.5/:th}rgכ1cCeƍ455 l81m4~m R8{Kپ};x^ʨLIP<Ԣ*> p9kE]رcJyy9dffXDO:$.\ڵkihh ;;1cưzj~锜h4yY~=;v --c=N.\lFe(~:iiiJUUӹꪫ83ټy3wvSZZJEE^wcƌgaڵvN: ә/ƌinn&''ѣGx3g|.'cjkkeRJAAq\ee%K,1~?~1NjCs0<5xS((?ᡨE4u+a%M_EF|°Jʕ\?bJ?uxn,FQ^C;v+n q98n,ZY IDAT/`tIs%F"lt=yX)F"ΈfC䦝Los[¦>ɗ|3B0fb⸱ 'iAÎaw`LH7'_B–М\@U5J̈Ct!DQG ڪzj?eێwH 23c%?BaB,+o!7ke=Pjkkӎ@06v5vG˩klΦJ)))6H2WUQf%ٸ ddȿb1K^+mj% O0uT.}_}_x***={6 _= @_5\,?gbn0h9y|hXTYN>XW|L^J,U~ZdUثɞRXXHaa!cǎ5b CݭZm2i$s;Y6luɓ'$$:n&M 3%}2v;'p|m$ȷIv_s1)Knט1cR%r1uT#6OЃeeeq-`ٟ\.N=^c&n礓N2`С"S'QqcС(dtq*QFLN9c(ҟg1ctmyXQQAyy9˽UUU:::(((h4j30DZ^/C 1\&撙(dffs3+baÆo ~u=e$ߗ;vl '{NѿhKlrrcu< B22::* 5W &Qf7W(QDA4AUbՆj.XCp0H[FeXMEsi+:~)=WBK,LhKxR4> 5Jf2?Ҩ66r_” 'pę\pH椹u݆bl2!K(nw^Ƀҁu?@G>f̘OSN'XɳgNնyGØJצ9!UUUuY{F?,ZY;wQ t͚5\q1߰2/_[nɓc}ؔL&or|x=(z/;>q~Ft5)G cƗyhͳH'_=f '̚dgs]#ۭL_=o g5U[U5dI${,f%Newg?>ϽON'lY)1 ~JnS}|5̦!.8sgNܳrGN.VQ@t$QD>z0`Y%3V3 ]xBhHT@=Mױz^t m 3E2^^ 9Jr 2#YĽ^9aQʿhO%$HV VkjVyqSN<.~ֵQߺ۫ٸ__Ŧm;hhl'[v_;r5߿؟RQq:5j;c%%,G q ArstRG&'̷Ih#F￟G}|vE0j/cZ -`*x^L³>K/D~~>MMM~$I⡇bԨQD"qW^^Ή's=?Oioo5\~UYxzzF{*x )\>A EWYӺ?~ /Д\aQlwW5^^zyf\$i[Z$}eJ\?u>"Z,Lc`]UxwXQ{[8nT%ξ3 `7*+!#ue&6EnN1"1Mήݻڹo[X AQk0FqH$LQt. omRk18~pƎʭH4JKkœ~B 4]tsv&Nu%WDhC"P,rt?^^c۶m2i$nFI1or\墋._dƍps3vؔ$XO<3XhW&##N:.;ϢIr. WQL2SS^$]ٳeueFldIQ"0%#G瀪*\rf U;<լ1G3i dHiZt _}@ ڭ`YIQ)k1ۧk#GvLXuv8eHvSIǏrK D4_C~)BH 7pkT] h_]u"0nWY 4(.`EAq52rH.]|=$ 'W\q\saVUՠ;m>{Gp %h֬Y\|)|PGB!0cƌpDA9HeDɀi(/̢{j^ aM8MTЬds<W,@YGn-GXnO}t􌢨tvtӋg""2R'mRPd*HHĄ/zTDl r('hk.\ lPD>t,VIXD', v@ѣ fÁiT=T֕x2@CDĂ(V"H%QDIhz҂ԕdT;P g%  C,t]nIQI) reѼtgQdv @ hJLPzB4hZ%lmfSw47HB}~h,6h$,+HO4MbrrꡰT4pQAA,NYNW~j ǹ͖m4ZPFqq9>x 0aõp?Y;$AbAJh}>MWx=t.󾴊w%3 |3x gN4$֢N6|f4dC_ߡIYeO]$3a $ FQIL'(l 3*Seg 좖7c2&fXZdpk*0"+=L'Bk: bSVeA1:Z5r e2g#+щ5! BTphM `9lX Êl8`u@Zt-F.%3Ɍll6c6h1eAhieEC0nn&HYA\Oh1J~b::;j@ldQ ak`mx L)e%S-;j n(lk؃`H II*IӌsΚjLz'y!۲_n_04ѻut$If{M=&NfbQJwE l"SȔCHU9ŭa2AcB4,RRX'݅x|CA;4QU]吱[DV4(vќ/}9,uZ} {VFTQ] AgE W(B(&  „áF6Ҝv;6ٌ$#wHӻ$I|ٗqoܽ nW ԙhZ{!t@EŢ)Yp;]/_ovm@ PԢ)=p,e͚5Ȳ=h4ʮ]BЉ,otU5`Yn@8!] y|ASC}/$`$in,|m[:Z.0@QFHL +ھ{ݨD~8gߴ}[W{-zLξv %4x;61!$`T75׌$*$/tkY_SAНo:$ېۓFYu^{oi褥9 ȃP6IdPigِeH$BZZQ}£C8RH>U!x|:{l ǩQ%G?{7^z~HAxFު7.R wP` yxО  ʠ ʠ ʠ ʠ ʠ ȃ]0(2(2(2(GJCHO-^8GXC/פR|#' yL Z1yePeP$jvyi hPFB H*l$p8|Y5 a]G7gيA?tab1|>)ep8N$I:@JuES(N'iiiX, b~46b,WXEv# ʠ ʷ]l6O<=ŇnkzȨuEQضm˖-clݺh4j`28q"W_}5wVl_&`b(3 ?d9$]Q\{Ax5Χ**ȲLqq1%F=F8f {XFխfrrrGٜEQB 56u)=ÇD*i͛7XgQD"|<䓬[b=X2331bz>^/#Gf}{='pG 8pD"D`ѐ╲dI, Fصu֑Gii!`fv;EEf+&Un&Y,RWW֭[2dkF4&kj_[Kl*l 8  툇n .Z)I4<f:::eh4J}}=v ʠ ʠ ʑh4JSSp8L,60M&V耶yON; :;; ﮮXVF 2 1^;w$RZZ_U\李5LLձ[$JN)Igdi9nW3[mGu 466N^Z ;#hes7kQ] p infÿxv$`[$]UJJx"ee5.m64M#//x„E2a;gZ[[ |>JtPePeP@"hƊ+(..ViAfԩXLEoAGG'p$؈$Il6caXP͢A9zi466v[N!9nƏAE-~^:8wB1e*U;qdggVU6v;yyȲ5TtJ/aGvncrzl1%Fmm-vDi|>;TUAEXxs1L8\rrs{ˎɝf2vM{{;E8CzzaWXMl6Z[[ Nzz hAA9$3d~m$9sTKDCC6m"77Qp`۩ YZѣey aP\ׄY 9eb:ޜ̞M1TMτv2d*eey&H`mP(Dss3PUUeÁb EQhii={`6466bZh4&FQ)ePePbpdXZ&0R麎O>sc2bOl|r䁻봵! lfՖ::"-`Tq&no-~>+x21"Ju6ʢj|> T<8}{z BIq'QlŒeI\۹Jn*NJ+RSqRגXeKVɒ%R;IQ")A6;f8P-IIj 1h9w7xPT-+[iu9 n" z IDAT".YB@q2A_[= [S\.lj'H$V5z8& Egy( TD--+pe4MD";4nm^4q:DQ,np8|M"ۅBQB*rxL"r|$C{C E*~^"J,$ɂΒ1Z">dHaHS$B23 O'e @6t4駟&Le0T*YyrpodF"ﺋ%7݄lH$x7q:[d2I}}= J% C,S__ٳdYRV9u,#2\Battt`|>rYE]-!/QHVep\#"k4HXt`CIP_\.;;V}miR4 7v=ڬ>={BM7D @uBMMM`&r$nqMı$I$ FGGimmE4 R ˂ '0hhh s9PU֭[Ν;?sos뭷ZWZé_~d2I0rQ*8x lٲ6 wA,&b\q,a fyNrZdpC@f_jE-J].0Q2PlT# +(Z֎.$t$D^ƼJ=E84.%AC4b533Ù3ghkk,k訵0MM8v~dYyWZ'FT*QVF?lllll>½k;nN;Nnv__}{}rm=饗444X͠N' x.^S)W :z<7u'M-&UM\u8%jY̋H݄jB8FҘLѻ?`P*V0%*e\!DV0 s^> #vK BwgӃ$I< b1A`hhA8466Z ]$N:Xr"xNۆf^EM?s[vmN[nСC d߸jYE?N:&`3jK TǏi&1q>n`AFLNN%L<(1x.KP!]k!zp d *M^JI6͙fQG`dzJX&TMp9<dL*'^VPUu^0糟켵_\4Mczzx$ C?q5l6@jJTT*Q.Y`qjA˻oE~ r9rK.ЊYy߳w@ @SSǏc7t9?DR ilE2y7-i#333h6ZӝѳlYFͱ 3*AUJ$Wpx(d=:N X5Yh%wjj)A oy 2 SSS$ JU˺3>>6혙RNijjb…h"]E>WWȲCn7(B6eѢEq~+4M"b}uBbKOm>̛,twwsΞd.N=& %rddndMOt=s=ǭJccnX,(@/~DBrRzb1p8p8(CUU *i-roѣG B466^UUIR ðnUU6l؀'$+aYGy?;冷L&Ccc#Vb9ro񊥩Agg'7nd~VT( lܸN0z5^>}GsۢzYED4L61&''jk9LMMsN6nŋ/PE$1Ib>&"WUR 9(@ T*{nò>jH9~,XF4Ţ0H44MT*:lذv۩0 x {1hMRdd2,]V{2jN{{;k.nN'lH$###|WZ6l؀СC$IMXfN^½F4199I.c ijg||P(d9b14{!ϳzjex<#J15`brʺiA@EDQ1&'' ÄB!<$I8(^M ,Y/}Kꫜ8qʓ=dY&BGGnt]]kgg'r9*%YtT*Ŋ+X0Q4M, huuݸ\.tKK ˖-'#k"[ZZַ7MFFFhnnT* \._MܹsJ%|>477[6p4M<]]]J>GUUt]G$<~E40 ƨS(GUUN>(|͗eX=ߏCUUykv|0Ǚ@ @PEQGu,Yr<-"ɠ( . χ糼kޫaLS" ˱j*Ne Z#x^q8V6Fx<FGG+&3 c&B@  lk+F-6Ej*~mbGŬV+Ufu.k0 vr@MS,q\,[ IRX 9q6l$a~%uMs0 TU% h"%[v===E>L,$[vLMؿ?n*޻P(`0hvXT\.322B$![㵓pYȲ|ƵpNml~sõkaԺk֊UUEp:8q]ccclذᲮkGA4 ǃ$IT [\.===Ȳ<Dww7۷P\*l۶X,vE&&ٳGss;fR)+ގM|JL&Ï~#^x+\.Nŋ[BY?2[ll~K-y uAujJRDᠫ Ç$daݺu,O(]]*Q.-[ÚͥfxxA4eppRt"ʿM;_lOa֯_$IWD~?~G… /;uW] ~ŋ̞@SM}icccc@E<ȗ>A=]wuEU?SO=e5MC$ZZZ,dzRD}}=333v@͵5A4W\֎fjRXbv:KHIDgg'uuu;vzr… YzE5vܵkR=E5$I$nB?j]Dք:=?Oؾ}0"̙3߿EQXv- ,r]kx0 Q^>0 0zv/^l\{^ߵǹy547*~X,Ɔ @ pt`nOM8WDQhU+4wyLMM/M6]adY% ]\~ܿ~)Hsӹ\j񣣣~":::"s((8NHB^U /t:].\h\{>w, kջ\!OCo/W΍zyuv҂ 033C.7: `ff[oop8lm%$l2ܼyMs5g+&hCCC>#Io7njJchh^o#vEŊ!/\.v?En??IMnfkWkHDdccccs1AWW?<{b4QUymK._:>՗eƈFo{/hlln.EᡇO033öm}Ʈc|R(J<|_*OuJҼljA۷s}Ji~}@.c~4wLNݰkijֻr:^"bh~u%kŦdV\.u-v-#2 ,`ppkz\%Pp8Lkkfccccccccc AV\;'[| wAE0M0FC 躁][jcccccs"(ZvV7 #ӘPQ0 l1a /8hk tnpa}$I:R Wf:BA=> @˚G՜WkbX,2::8LƲ_xu+k ']^ndiE6?. %cO԰ܸ,0PcyCR(;pò,Hd-0 &&&(JDQB=a\~iLMMw^vѣGd2rogٲer-lڴh4zAT*B 9Sc#Ti26u}xA;n(PR*:fa2$?~t5pi41MM0MN^qA>LTr BR zE.eREcL&$]]]گ͇GEѣ$ zM˲… bǂ P.ٻw/?8x >6Z[[q\8Zi|_;wj*/i&Ϋ Gv4 ZhY;}gl^*(J_2ރzk>a=? CZ( . s6.IRrS,0 ׃DetݠZZQ^b\.G.X,b,z B!n7(ڃnk788hm|*b1dY_+qBaժ;w꺎墣TCu֮]K$0 2jUU&^::::HR;v??_"> H 2zRz18tMRHNb:สe\FQEn׋-m>FRgye_xl1`";dpfG$˚p8M\4Mn/T $Pꔫe*WN)|2~%S(eYcLdxx_~ݻwsiV|>0]]]|lٲkpCERzI$i"PVe6666j8(bQ3n&LD"ACCüfD??n/_,˖-u6J]|455#G;$_WxEYASr&*mt4w WGtI2ɓ:uifff0 ˅李/^L45NTLcNg+@UD LS1za$jI#4Ji\.$IDQ+u$ɉ L(T*e O,wFE:;;Yͱx>n + =>(K.eŊTU* ~@ $Iڣ0MT*E\F4Kc JB2T*vM޽Gy>V\IZerrcǎټy3}}}b1t]'Jq)^}U^|EΝ;ҥKijjbʕT*yJ: IDAT.]ʖ-[uCԋ9j7@[Y|zC~CEQ8r*8Nioo0 $FR### yfVXqjl>b0L-5Δ8pdB 9<6>$Q\֑OEx<^ ô6Kzӧ06UADwtBk\枭]E:9  ~_#r?c#H_d49EAu.֬Y3<ÿ# {/~-i,UU)J\.}}}]*Op?{,:jax,:z(LNN}YYr%r):ĪUxYf .T@4%a>Oʕ+yǒ%Kv+#z iqa4r7 W\ׂfd2Z[[FdY&Lzu4inn&H$HR޽+Wffׄr(Wt'رkq>m!׷q:BR)iwq׭]<}!7mTg_d`8c ˙fc$RZYGf7Kx3 EAUwneA\HӔE\*O~* _x衇"d2Ib^V  U7ޠ(D"z!QTO~Bu'PEabbIEEQ󌏏D{{-mlll>|\.G\r c֔VZ̵d2i A$^|E=ʪUYz57شi*WU6mW A`ժU=z_|qެEQ"iq;*(rCۛh_+iSSS߿)8vd2R)~?l6kE{{{ZzwAP*oߟ:_e|( 膉VuU-iERMQ0tَhNYԙ4?{,?yf'TKjrdYfr3/ex?y¯uBښd>5+HҬE(@&WG$[S* Md||{ ~nvN5hSSzEN'7C/DP0 N'~;?{7>G_t0t:?N-Q\1E|v;,jͱau38kM)I¬h)r3UȲNt]kpw\PR.9x\Q "s;}:Izqkq&œNI&x<3Jt:YjU8iTJSE馛V' e͵$qIAQJlݺ5kּǒWw4Yf [nettR(000ɓ'/;nɍ!(`*T &5b+7{CQtz;]QFx7ETU!",t:rPTf/QDQq#eK+= <.?db]:>i*㒝"T*?{ˉHeMvs~߱/nM+=ҵkul޼eppojLIEUU˲pXEQX~-DqR]+@b1"hT*uL%$Ib``jrX+zl޼ERW<ܼy3< !nJׯ看 s) %(qS<3T)>0=+ꕫnY 766F4ͪM0MEQ199jH$H$?]yu,Ow DTufE/.4UCV0J蘀vtFׇ=M'PT->Qxa|Ǡ\)j&><^DO:( [E'3oGXb93/嵽Әxqe*Y!H3l\x½R055@oo{A'&psfX,0M󂴐K>.$Rz+:JB)/t,\0[C|r0 qF|I_-x½V1M&.):}}}455Y)4$TAR8slF$a}f|.lj2 m9ڽ,T* 4nE#@Rp8ShAx'X4MdY& 筂Zc&M T?}vCQM("$"J"( nQ!"QD`6\*E璋CEDAD:4ML =4<؏69WX(;_`cu8L(*A~\0'gQT޷>ø )I,+9NqD"VdX,R(^UU4MPrl|l6˹sfO+ @UU8mmm֚z(pt:}9s箋SD:缜5T1rr7rAw5; 4Ļ0GϞ=7 nb7|3۶mcٲe۷/~|[blllϹkY-.~-W,|tTJ`'֛XH}4T4TM\Q)WT U4L$QD0L*T UʈeIr n0T&c{:l ]p8&&Z4ԉOM oR|,HjBarGujEL@$n%Hk[Ax{>gMI0 vbst]gbbbH,#ˡ*bڬ#ڿOykT$t:QP(dEellll'^*H$HDX?irڡ8LX,vI(bP(fEl6KPpX"^Q|>%wRauC/qC_# Ik?hYFyOswtRJLL&c``'x׿KZA@ٍEd:2Ռ=ri>rLT@e[Ri|~%&8p-:VHeƩVxL^Crx\N%n(LUZp:[Hf&>w}sv˕7Ig;NtTLSQ{:X?p*Şz(r 5䫯f:OHT+e q/CCӒ[xp8*gϲn:\.~ dO!",:{7_|޾>gϞ? J#(^WWGoo/=0ɧ?iT*k.;$3u'sCCCVN4%Ͳck^E0\Ί Νᢣahj"{rr!nKCCCLNNZAZ@_2E!7hR'};}+omPu!ӧO/~`0YM:X,NTg?[l[oh,I@YubHKK{ZL0T*t:) d2/6>;LNy`9.I>A],r7,jDΌe_>dIMq(47|zk; >0M^~sc3LLAx{pm >hP:ulP0i ?řA>{|QJ07mh4W':$HLNtb~xfs kV4g J%y6xiyٳ[n'&p6M+Mb$HHTU9<0L>O*`Ϟ='-[* D" T*! , F">-Z믿iq8qwqP峱D{Z%Ji,Rҵ{->gsm!k( .^pln7,dػw/˗/Au{dp*iZ'D'pHEV=gzFbÍFN>__388ȽދbttYV*R-dɓر[#kE\.Z[[)ȲlEkyکV$@H$b Opw[wym `qoI>kvtii-<ܟ"$Js-1>uc'xQG[i:L ˍI[MkYр!R*)xͼf]{ljlA+QW_,A੝# hNQ,ϫXT;,YuֱgvMKK ^{Et`08ruU’D(;}oT"C馛Xd'.n5F"GUU:::hnn Ef.p:$Ib˖-N&+V8666-ri\h[Tk߫ [SUb_jETU/^LGGX:~ټy3/L5."'N'e:::XxEv{U'h@p1\.^ۃ JT!8ܽ9Χ6u\j-ImruF>UQdY0L|^-ع+ͮH 빳xěf jMXSσv/$hff/۹{<~u&~|tQ 0kơO}皚x[1 8/=$?`{ l6k cݿR:СCVÇsNFFFr*mlll'L$ɐf-7ڣ&VT*{$Ii.Rn 07ofxxUUq\ttt022£>ŦJ裏222BGG. UUf̈́ U3_ǨhN2=i],[ ?8+͛cjjJa9tduֱrJN>MP!X]^/EKD"/_n 333cir9\zfb1/_N$Ə0 jNΦIey*UgQ@7uf 3?2AП!bQzb7.#(zƘ4[+I#cY(fMA=3Ƚ[[YS_tZ51Ma\vZfʕ{<~33N,Zb 3 ( 9_daN |scʕOd9N[|KSS`WF`|>nibA㢣6666gj$gΜAU ;̞ 777#˲5;?:͒H$GaٲeV۶m㩧ȑ#lڴ`0Hww7;v@E~>+vNZɓ?dǎ,X`0(9r6mvIkJ|/:݈ĉ?3/7x#MMMVs#ŁE~lݺl6$dX,i455Y)H$Z[[Yz5oA t:ijjPy2 2@իW5c6P-PM:|'D|A\ADƋ{21V- k΋ ze 9+gFڵ477iiiPy>e˖QVe7x#¹ BXdjjbhͱyK綶6lK\LӤ^z|>o$122B"/f-+_ ɩSXt)]]]Ν;fÆ tttu\o^c۪<ŗ[iQ/QR݁Z*$@Bޠg}1,Hj%^-]!4[%M۔&MiڦnLN|;g_uҤMHGIl>w?/dhhkײf ɓ$ ~mږ|,T*o~^V uVbW\Au"'NP(kw^9{ f ]gWWiիWTGu4IitwwM j 4- NkchZ554a TTPU>~:𖆱,M9jvLR׆8w! <5"akJC,H2VP0e*IQ8~K" ?u95 E`zotDbxx4tVegϞҥKK޽Y` p&ڋ4-׊UUUtuuz䧟~r!ɐN1 ￟6۩оZ}j"\5H0>6e^_4uu; 4Ma֯ )E0A!zZCJ(c <>o555w][ˡCꫯH$Ίohf{9v͆ +JD"l޼Q/4H$룹l6N*^xa!!W!B^%&3<=0 1}#N]]`t:팱꘦I,`0aΉ'8rO=~v+*O3ii(l6QWWǹsg߾}ܹT4M`wqwYhG}6H$?9.Amm-֭cӦMqZ xٹ} ,GNFf;\hZd2d2WE!AKK < LNN:Ajjj#ȞUxOBU^I=EZ[[QUd2LH- NE\u<Q__OSSӜE`v|:eYoF9x d.S~u@ @.\x/rKnpo :;:H\v UUeQ__O4ehhǏëJCCs|CUU}}}B!0ǎc2;CNlK@p8nk~CWE)6plLSSs: ܎z-:;;yOioopK<Әa``ӧOywqʮ? >|^/ׯgbbJ1 Moz*?O3ӿaD"{)eYgX,+³>K0tV-gw晚r&oEEK}qAThqzd + rk |v<p ]pMY JA/axcD#>7݇l ƻv*Ì\IJL@`:QVeL%!jO?w=}R{\sQ844Ka% !].t^],r,g`/>oު8a*yhX[EU`5=:a`&p7nwf;{_D!BAuus"lU'/frUB!BbH(b?6,Kڋ HpB!b\UAxk)YfRܿHB!Kc]/0w(/ݿp8 6õyK^TwqB!^W^ø]aϕvy|yip\*rN`+v_wݯ0/]!B+|rypwWˇ˔vk1؛{L5WdBpW$ !B+}Bp祗Amw~!0}֑2LF!B7)Bzy}NnU 9=(B{vwp !Bt]8R%̱҇Tw07KBܡ3p0 B!b%)7+MP]p|B}VgJTۅB!j +CWRhs P)^闔)JYpB!b5waR/ חr@pWEFB!XM|ō}˫7]TYuu,pڅB!j :w sT`p/+$ !B s/4?T!B!VR`tR@/K/KPB!bqAfuVBGYm!B!Vk`_L8n%.B!"LCe$ !Bqwp B!Bܡ.B!B!Bjy(T#IENDB`mapper-0.8.1.1/doc/manual/pages/images/color_dock_widget.png000066400000000000000000002756151325266516600237540ustar00rootroot00000000000000PNG  IHDR)/bKGD IDATxw|Eߥ7R@ J!"J* ()"RLB(AiA4B %#d͑BHHx'w{;{3;{B!B!B!MB!e<`\x!B!^^,+B!Oh9S,)s!B!$/ 5 ^!B ʓyG5O{!B!{ϕG5/߀ޠhОhp/B!9Gj(z& !B ?;Ǡ|w\&\zcNB!xA|=cØ9{!g} QCR>B!xr g<2euk=wA.Ӡ?#B!(sYM'e;f B!xjقG;>'+Q^=z !BLi'<Grm '0dHB!%=7%s1`ѮOΛyCGwry\zSSذq3۵B!ʕ?p<_wwuw>t{7H7YjMYH<&ـ~~ oظi /^Ȉ*foy!B! ˗+ke{Xe4`^/[ BX}xTA3^փcOo1n<ʝ;!B˗u=!yS??ZjrMw08v>y4ڵnk<N 5t}wͼ_rV9fЩët*?Wvԩrhߜ-?/Mzz:f8:R !qġ [#{B!x1}:es?>0߱&}tUKl l`OkhJ ^i,ό 0w_.banNV-(MzF[F~}ys''3?ѿXpeJ&Jɞ)B!G~ij̘>-G 5?`>a,nGy$IyQ||QQQf<3HMMc9 ߏ'+-q&S5/Lu׸<"2 Zj>udB!2y bb@ v|+J}p 4o3y3Ν ޽{$MÆv e$&&)B! -Z1 ^Z`>߳kk+lmm8?/L5ny>||7&F)k;=ү@_(L: +2\x13f~%׮]#==ZuQ~/[a&shOt||,mJߥn 0?bbnI)k;*WCs?B)cmi:μDʻaH{yrC`}L?*Ue?Цu _zun,Z8 ΟEiߜ)_>̂/ѥs'~9SSS]NЁ( İ((DEE~&(+kEv w}#1eϭn o[ Og1ig^Qd*1мYS^ k֮yhx*y`iS*ռY'ӧТe[Wtʎrܘ9kӦīFmlRv=vޣst~;eTIR/2o~zvmE(0i4jk;{`9YtkM[t`ja5GB=u毴.?`P)pv?<`[rc>/JtN~i߮-?R=TTƉt\ʗ+@Tt4͛5sLSׯO ܩKKKz!{Vg@]^ưCdhT3?CzzF}ǵr̿v˿/"^Ԥ;ud{Sͳ*>>5)^mO6166fϿ0|ĻԮ郷n,zn9Ԯ]'ښy \:###n߾M붯R/7'**njsb)cW^bс/>$oDXJ^o-g̸ p4 wcaaN6պ/W.9ԩPlMvmi G[~\46oʠk$&&RûK%mٺDf} ynsۼj;9sbӆܸqU?ewVlmg}cOݑé^(|:{_̛φibH; d@a?`kk˄c)!!6HM_Pa?->='w;CCOt܉r\83>5Boп??yyiERzz EJү;>›oCP~ HЪݨQ#5MݺuYnAI6sfMD زyzҵ@Ҫ !MzŠYu-ٳ>R}~S.m۴o߷h+h臂 M6m۶ܾͧ>C2퓙 8 2fmޢ9׮]gSxWHX7ʕGi̤j*a~+V!-=@ەUyÆ /ϙ0ގ5ƎUv8yի/\2۷n*Qj>L2'Mp ?nlGaw4kެk/J=g?lIII\ryG֭IK3a0fR֥;2Zڷoŀv.={;n)өZ ˿F+VQ:rE>G}߯X3+Qݻ&< 111*V@LLsWop)nߎŹ e(YIHH7_!ww7ǎPLzvDBBBiڶi͙gIJN $$$мy3e7oFعsܻwr$(͛5 'Zdz.Q2d)?$ʊ4iǼ_˪U?2ṅh4,--nԪY_΂ycjj#o/o/ʕ+Ǥ) =-9Miiب 7kWVE$v,xmۯ:I͙Ş={7` KoV^M_ڼy39qdbugXekjjw˗Jִh\7n>b'hѼ9Ыw5c###߿>7Pcc*z۔9.YYKѫW.e4oάs3{zU}==GVy_OCj49):t+Ks0W_suu%##g2ɤbaa(|h^#c#UEҒxhh۶ufռYeR} o՗:aZԯ'\E(jon+G!55CCC5mzF:1Oyp K믱},g4yh4j]0e͒FìPOȐwkVjW@@*KJJteԫQ;EtGgoCϞ=055%>.]_:8|ΜZ5ǦQ^㽴ɉ33vZ}aooO|\1l5153SӔ)غw337':*:vtރ7zT+IVtà!ܩ#mڴSZLΩӧܩ#\|%<:('$ᄃSXY[> |8f*Тy3Y-fm?ӲU[ Nopyf|:}?O llh?ևh*9.%lAA/F0_t=HOYi4,Z0&M[u:/]%K2u:8\[YZ_:}O$%%R^T҃۳t2nܼ!.+2gL iRX/\ŋ]ׇ6C˲l״oF]fE7zz{f͜g*:S׷;̤)S8p04kO_+:~⯗xїOdؔh;^ď's=lmlUi)5榦{jҸ1M7927|BB.[yLoYoiڼ%C-;_9g|֭߸q&ҬYS%4nԐk2hP222*UrELҡSE‚ʕܙ4q<Æ }qclLӧa31f_ΦW7c;&}YwxN@YwzuOϊ&2/< #Ё ԭ_HQOOs¨\|P!HjOϪ,\0_ CHzxIZd石az3nMaieEClI@ ?@#_T1HO^^2F{ܓEx<Գy|j.)} HKK+ QH{om߶HBڟz~LrX(ҍZi/BHRRUWZ`|ysR-xE8c/4Y8(EiBYߺga;~sQWe!B!./}0!B!I!BQ<=Ũ1_!B! !BQLy(~~#^iBK4/B!_GW4VV6o еqF_-!_`}ݡlٲcllWBB`+bukˀ`U !(FE׮]dL:VJJ ϟ'>>V3ܿpݻ@Jptt|YYYe^"nM9bLMMh4QBz-Μ9#JѥKi7mڄFʕ+Oؠh000lٲݻW']Z000@`ffFj;v,wQfҤIn'h4NaaaRطo͚5zͭ[tٵkSF \:|0.]w:?34e7w\ʗ/)[ҥK:_pVZajj33ܲe3p@fȐ!>>ߜ具7p1͛TTK˗Y`㯿jժRBvxmٿ?ܺu+VЪU+~:v˗իÇ:uYfc;Vϟμ+Jb!--aÆѮ];ƌ7nwa׮]j չsgvʼyX|9;vرcTVM'kRjUj֬SNeoaر̚5 &L@6m8}4$%%Ѻukټy3ǎ?ښsN޽oٵkK,A2bXj3IW\ݹsggg%%%7,,;wᡞ4hZ<==uRL*UDDDbnn@||<+WN799Y ' _1/VVV4nڵk?gŅ>}0sL5 9#Gܹs93gpmlmm9{,ׯ_gذa\tXlllѣ3gΤTRynώ;G񎌌ի$''chh;hZ\BDD阘兾>TREMOOO'<<777vFFFxzzh\~Ujo7UǎS78J<`$%%h˗/g,Y'N̲epuuٳ̘1 BBB@#ӹsgƌ… 9p#Gё^{M:(l;̏$''络@֩jIOOY@(>nݢRJ@Zl|- Io>"##*={===֭[cׯ_ʕ+̘1CWZ5*T@PPo&ڵcƍDFFݻwپ};-P]v:E74jԈx¨_>K.Z7o8;;cccCZZzlZmݺV~wtY4]q1abbdaQ*˗)W:yܽ{(S :<'555lja㔔'FFFj,&&&jОiE iYUh޼3 J*EHHHcHHHPZV-{(ўu;̞fwq.utt9^Ϟ>::{?;**r=tQ|| ׮]?NNN^3+V~S煅qU5o???.\ evڅ:fƍөS'mt޵kFIGj'u}}}իGݺu}}jmm .%ʊ85( qqqr"lϞ=9r$SLߟNϤIx׹{.ӦMJ*|2;w}L4 tB~077',,=z7(gɒ%hтѣGcmm?̎;/1Zf 5kd|Z+WسgO'^^BܤD0eS'NPWXFڵk߿?cDEE1j(ߋH.\Xu;#G'ƍ͍nݺУGONϞ=5kǎc,[Lc͚5t333u+ݻwgܸqXYY( &LW^m4]q((-ꅭ^L¥KEQ;;; `033lٲ\x}}}.]3LTTaaa888AZZZ.W 拑!C_ͧ~%5kԹ>? 4{Ѻuk,Y~׏]v1j(ڷo۷oO>O>-ڴi#x999I裏HNNJ*| 80ߴ+Wfԩ|?ǽzjV^3o֭GH3n8̙#&CEZtӇ5k?3vX֯_۶m D:|p޽˂ I&lܸQjffݻ6l:ut̝;A'ۇRom{ァx ,XT銳{add^Ot޽\P\vׯc``ˋK.qU066J*QjUΟ?Ϲs*Wkaa7/^$::SSS"屆7`:p31!K++_7%꯺Mmv]v\~ޛV괗:%mCCC~^NsaܹDFF\_Y<>v4BaccGzAҨ?ZEE(h-( 65oT4_q}n$ HN ߱ D=B!)"Ңu5kЭ[BONO4%`'=BH{ƍ122ҹy -X'M'/a,߶m[m!1b#F(uW^>c4q,/V!BgKh-B!Dғ"B!xz1Ͽg^!!B9۫h߆iԈpbքB!x`Ew̛ ! FڋԳ94kZ$|@Қ8I{Bڟz.'rB!ŔB!BH0/B!`^!B!B!%/FTE~MiB B!x 9LBB\XY[Qhh(SNeŊXYY=u^Fb͚5KkBQlܽ{/޽{Q-J*nݺP~W"""tԉ>UV?&NȦMԩT(Q̙C޽0a۷gڵ\pM6,aLLLرcҝ9sSN'ё~VZ?RLϟ.## 6222ׯ|O^6l˗Yn۷gҤItޝٳg:g߿$ٿ?!!!W^ޞ+bggGjHJJ"&&F=鉭-ru{{{qss#22R=zyQKIᣒY|o3aV\ɻᆱV^wɆ h۶T(q"""pppЙ訾bĉJn:z]JvZ|}}T:OOOK^뵒BOOcccRSS133S`ll vJ722"55U]&׳g_&qV#ˆСCдiSz=o))?ʕ+ciiIΝuExvK)FâE8}zgJ*q̙ח'OBf%JVCL"## sMnԡ1IWJj׮ǥ۱cwޥgϞKx"::@IOO̙3?ڻ9Y8+GaO <==QӧOKq (ONNV{+/ڵkG~:i9}tƍW_}ĉEǴ8t?ǏיVʷ~ @^/(P666Ӷm[;&#J ggg*Vo իm+44T 5{Qoƾ}tMp?{… :ߵk%v d N===[.S^=֭%Ƙ}HCQ θ8>YeMdV~Y<< +p J̘x8@ZZ_?Sz?`b }]j׮Ͷmشi9n}7|p.\HV7n\p~Xŋ :ڵkSzud/ǵ,ǏϑYfX'''z㛱nݺ1oңGONϞ=5kǎc:CJ"===EAQlmm144$-- LMMuPXXX`aaŋ155L2ISTTaaa888AZZPlY.^v\t {{{WyQXFFFmۖ^bB ۷&M``` >>>;ueȐ!̙3]2w\:t(qqqXZZާʊ}NJJ t%̙޽{۷/G)p/%IAaGİzjw=z0sL֭[Yn͛7gŊdddHӦM>Jʑ 6lvڼ[ٳ'ߋ(.ݻwY`4i҄7I]Vւ[f uԡr:j֬?رcY~=l۶ ///lBFF]v}̌ݻw3l0:uDҥ;w. *|=ԡRʕ###7n^^^./^$::SS=UV;w2e+rtt,ír屆7`:p31!K++5- >t -Di/O{QWB>LÆ 믿SNӥ`ooɓ ζmbhhȯճx~e}hqqƎu=QcJܴEXy7*z AAYq8>pt7@'>O5Bk͚5xxx*' ::{nB<+O4 u!^uСC vڵ4iҤזlܸ###e!$B!_Eݸq1b#F9+B!`^!B!B!By!B!$B!BPn6FBڋԳOzeKkB'EiBYʺH0!B!) B!`^!B!B!By!B!$B!BP B!Dqב$$* H0/B)##k׮aoo3PBo&55'''D@BB`+ BHLL ׮]}u߿3… )SF*P谲4勤DQFamm8;;ӳgO,g_{EL~~~:̝;cjjJ֭tR3v튥%kkW\̌?CZ(v===hԨK.EQ"##yqss{{{zɩSe9F.6֩S_ PT)>cS|<pիΝ;Q8qBg[-ZDZxNӧUݻw;m͛7i#G6+̭[x"ʕˋTNuU'OOOuޘs=133‚wr)lmm]6NNN;wQ1%%E}^111DZpppܹsϴ /^HDD*Uf͚pI ]O*>>0ӱ(CfceeE4Cڵ9z(+Wܹs IDAT93gpmlmm9{,ݻ'rIʔ)Àm۶q u]jբI&,\gRZ5vލÆ ܹsQT):vW_}e5k9sлwo&L իWgӦM曄ұcGʕ+GϞ=P{oq/D;RF 5ꫯϒ%KFo߾;vҥKO&Mxwtxzzl,[ӧO}vx N>MjjN0O͚5y8tPRbEiqUX"&&&fDGGu ?HNN厏'##$11\nkkk`0 ڵkFښÇsuV.( є+WN=q155RJ*UD_HD:Ǻp`kk͛7IKKwwwc dR&..N-MZZƸ訮\p8 )S nnn88::CTBTTѣGQ___ J7HQnܸW_}0\|={0ydv… ɓmWWW6ǫQ;w.ǏUVܼy+WԩS:_cbbB&My&̞=ݻw3}tlɓ樨(,,,|2:]~+WЮ];u^jըPAAAV%((ʕ+{G}|҉ɉTZ-aaa>|cFFF̚5m@9r䈺ٳg9}4gÆ :u===ׯMHJJb~QQQվY񼥤:C (]p=:8%%SSSeS8Orrrat9;gfaAAQCQr5̛-jYzݼʬڕnLP[PB\RA AAEYgs99r.<>3 \GGGRUU%m$\J\sޞ:uRwބF- LK.hZ9"ZYYQZZ*bUWWS^^nfޢE ڵ+-Z 55R9ÇQ(t___z*VŋIVqq1 ;;&c&55|Ν;ǹsIFsLSbcch4"I Kڄ裏Ι3Ν;n: #F@P0k,O$::'k.8t@_~C`СnݚƍO,hvaiiɨQOqtt$''7774Zf͚ň#֖-[Fii)O< ̙3L0s7CŋΣ>J07iCBBӴmۖ 6̙37nڲ~kmtlڴ`VXɓOFCTT_}-Z`ԩ̛7R)Ƭߪ*wEEŲZ ˣK.9III!//OV8]T%I{P(hٲ%ڵ5 &ɓ N<Ʌ pvv`0PUUEMMiWFCUUֲQQQ"b49s m۶=t:C~~>[ӓK.BQQfϭvt:akk+wYVVV%:qvvF'ccۿ)JD߿?gϞͶO4;a>((?HDAA?3ӦMCTٲ^˾}x:443fpA۷/۷ogĉL8y^5ͭ!3|ϟOEE 'Of̘1l߾]sNQQ\p'|UV/#F`` #Fo?Wۃ+J&LҥK_{_~ٳm۲f^{5Z&22 &{zYl=P\[olV#ƬnDcii)a\ͅ ?СY$Iptt$//R 7՚ŋ9q'&4??L!˗6T*ju^W+)1 dddiʍFl^SSCyy9-[Wp |ln穨@Ra4\II UUUj&pTTT`0[^ۓ)..ǧY^5;aHIIaժUȑ#?>l۶իWs%puu{Z`͖#G$##zB>-77,nnnn-{]ٳڵ+f̘1lݺWWWFo&+W$88^{5  /l%:$ WcѩS'xLJLƎ< 64>z/c#4~>}:;w$""9s1+4j4z=...|RRRСClݻ7* HMMRIUURI-(//'==]4M-9UUUfk7՚v#?t萙yPsdkl2}2 P(5<@I9y$Z:9y$z hILTbmmM@@@rT*$I"%%OOOڴi@֭ٿ?Ν˫Q ~~~۷cǎRH5gLɆ4f碣QTt QFQRRŋ)((O>O?AHHYo* JF///m&U}%Yf }lf :݉ {ҵkWLoId???Yxtu^sٙ0!C_ _/{?‚~e]r%/^$<`ccCyy,_}( dsVVV5MŴ)=4|QQ{졺"""صk˗/n`ƍ?Ojj*cԩ͍A1cƠV `0rzmCii)>=Ç{1l0y̜9_@5k>>> [l&,,-Z0m4}]x ;v瞓;ѣo;v b JKKqwwg„ ,\P3eJJJXt)ۗl-_MFFFp͚5?> &SOիWӻwoV^+BǎIJJO?t: w}W\G Zv*'O,! ?_kĥK` ڵkǧ~o!Ƭ`ܹsTWW7':m/]DUU0;ߩS'\]]qqq};w,,--qpp6PRRBvv6FW jf|$I$%%htt޽^͍RjǴZd[٨T*lmmeA ZIՕ˗/sY222P*jfaaAɓF4Yv҅'OhDұcGKYCj̅ (-5I}{g#>F'@lj"ҥKge@GUˀrj<$'O 5@ h=V T{6^ܨPP*yb{6 Lj8@PP( 7Mn<<$ŷ$Gy^`\;CbbM]s,_UEE_֭[jC ̙3gHHH 66zg^MNgii/^dƲz5.]WWZ卉`04^IJJ"66Ξ={_uZZǎ'$$Բhj?@jj*wfݜ8qV[ݎujbcch4˺>VK=ؿYxjj*ÇOOO|/_Nvh4!uԉ{άCT*6m$[bASQQB03wgEaii'#G4KCh4nݚ󟵖gCCCQ(( [Ckt:\\\9s&UUUr?HϞ=qqqA`zJ%Q(FC=ڵkڵ+: Z$IDVӶm[o~_ 720_5k]KsN>cfϞƍ7n=os̝;H{: ^xڷoϧ~ZgE1vX̙YnDFFaN>>|8cԨQ|:u ȨQС!!!$%%0uT^xu&SA///ԩ岹nRsҕQZZ*КtprrKKZrI˓'$5ڵ{ˣΝ;쌷7-[$++YqQQ111P^^NZZ111</kÆگ<|}}quurssef9p$TGw!$q9/_ !!!_ƍٲe :zJ`/_W^?~+Wi&^yT*YGeȑۼ^h4uD߾}e;|%QQQ||w̙3>sMƘ1c߿?cƌa˖-/߽{7FL`w4 &99+Wc,kMhZ}]lرc?>?Y=zGX={6_|SL䫯_~a ֑+fu}498r999][3ϘSDD>|'o,2ݻwgL>~F`?\wIRqq>sYعgϒɰa&mڴar?5kaÆNnn.mڴ`0pJKKIJJ}|w{B24J*** VVVL z=-Z@R59FA֮;::P(J%LRUU% ={Ҿ}{,--o^jX[[q(//7S47 SN(JzMHHnnnhтU )y܎, _~aƍ<rooo{=٣Ͱað`ƌ{ LHkl_^Nsu >'N͐!C8vvv1믿ty9r$...*nd^KEEW?:+++?>EEEuNO8AQQ9 \ƧPRR IDAT<~W,XCpႬe;D:w,-qM6` u768{,)))qq͛ǎ;+y\\\::uiرnݚ;ӧOwBESa]lذI=z4?^d'dsCC᯿:߳h"iݺ5˗/#00vXLXuP?7*9JKKVԯ}cR&ş=YYY3vȑ#ѣo-o Aj\ߦ3&j} 0 MS4uiEEEš#..8jjjHII!..FaCg:wS#N $cǎR /|EE3=L;w ƨQ} رc^W3k,/_ܹso4 d3N:%o{{ZС3f |W 0kkkشi7nd̘1ם%hSRRp ՛M˖-[PTt޽8#JŨQd͌1VZzjv% 5Zرc9p|q8l?D֬YÇ͎;}YO#۷o_~ :CнxzzŶmsdee$Czz:r_~__Z7pťgXX?0A+ӄMTTT6eeei W? t:YpU(//7sXPPVm¼,ѣG|}}QTٓ=z`oo:^]&Ҕ)ȣ<fr܍X܋4"Cuu5999DDDk.yyd.Sҽ{w~'"##nʔ)|g2k,ZjEzz:/BxL4ݻӹs:˷~<<hZ-Zʕ++YBBBϘaӦM\O2o<***^<,YDWJAII [nEVŋ4iun5Mhܸq|jF{Ѽ۸7vڅ^ח"Μ9}%T44M>|VwwwƎ[k駟fɒ%|<ӌ59s搜SO=-Ν#!!իWZQh4>ʁn}fΜ믿N`` ̚5 ~i,X1cs!"""ܹ̄SC>}0gz-֮]KΝl!?iӦ iiibkkKFFZYzkc]ꉭ^'55777rrrSfe˖dffCII yyy~RD$$I KKK5ۓИ:lhٲ%ԩSʓۑGSQ :b3~BSȷ 'oIJ+wH+]ԩS%;;; ,,,$iРA꫒tIŋs='9;;KJRrppcǎϜ9#=V%)77WڶmHgϞ$I@jjj̮Y]]- 6LjӦdee%J=nc=&t:Jj׮!'&&J;w&L [dԶm[Rԩf͚ZuKVVVRQQ|cǎR>}[rwwV^-Kw#J^^^RϞ=%Z-Ϛ5K5sC=$:u$YZZJ^^^ҦMjcڵR``$YZZJҲe$xk5޽{˿%R)͛7,^C_J[,,,$'''233;I]EcÇ4u:?zHV$I _H=zZV%///矗.]$I$t:6GK?, zgݺu [h.i4iРAɓ'O8! 8Ph4GʣRj׮F4uTII%$IYYYR||k.˗射Z1RRRԙ… Ҿ}]vI 䰜)&&Fz^ڹsgfCe$I*++>,ڵKv͕c\cҾ}[ѣGٴigF׾h4l&N] y@ #6o|@˖-oy׿زerrr(,,lvt4G0?m4P*hZ<==3f b w8LKKBRDʕ+$s{\|M|||Pո2f9"ٷo ~Qe|y'Xt)vvvݻVٳgӺukѨ{>֭[jڵk޽;SLa̙f/]yfLBdd$ , --={uVtJ"))ɬ,˖-[nf/j;FPP|z^aժU̜9^{72df󉎎gҥKDDDpa+ܢ"ʕ+y'dʰuVJJJxgbҤI1Z=?N@@QQQFJJ祗^bƍX?&Mt޼y3'N䥗^"..3yd6lpOŋqtt'>>+++Kvv6|ر i&ϟ/zྡqXTUUa4IMMe޽̜9LPռO?BȾ}8Ǐرci8R$88,H˙8qh8=ٳgdذaN:ѦMv]K~剫4ZyIff&[e˖ꫵ%%%DEE%** :ǠA2>aÆ!I{칩kmۖG\YHOOw#ЩS'J%{&$$777ZhAHHӧO?...~~~tziz.WWW 3 66 F#$P(Xt)f}Q9sܹ3֭CP0b fb 8KKK8q"vCq%퉎_~XYY:֭[3p@ٸq#~qfMcǡEpEbbbGERqqYSV9}4m۶eÆ 1sLƍGii)߿Zt:6m"88+V0ydѐ{ 邛VZ5deeܹss/_ˋ`*++ׯ˖-# h4 /2ݺu#;;^{ ^oWeFz)Rl+s]e^}_7r3fȃ>3}:ӦMcMo> d֩CCCt:_8q"DGGc4%˺#77?q-[geȑ|7z\{_~n͚5ZMdd۷oyl2׿m%֭[6w^*++yG(.. ;;Vĉ޽;aaa;fڵk2dH*sckr)}YڶmB`֭8qoR3m+**0 FW^ۗ^z8v7ރ0백ߟ '|!Cn#GcٶmCeذaDEEw^ xMj1 n0qrJ>hdбcG|]w xooobcc'33cbee3}pQy#i}ƪVǣ`0?rFs]V]Ӈ~MvY]]Ν;2>_~BC=tSNNNp$$$Dw7boo/oGRٳiJUɛEP}Ɔ::r3WhZY2+˗fc3_TTĞ={&''vՠOpp0ƍ'55y1uTٶ͍A1cƠV `0rJa /8\x1;w^ 11R_M~`֬YtؑϳdӉ5O<Vb͚5f{I׮]Yh hCXz5C5_ ۙ9s&:2k,|||x駁+Z֭[fi&>ڵk3|ᇴjJGA۴iΝ;۷/8::=zu>lٲ~0Zhixw+رCvV=z4~mȍCFիݻ7WW^cǎ$%%{駟ryt: ᆪk׮fy<#899P ݫɓ?j!CZfLBII K.}˛$׭[ѣk٨|rz= 0>@vER7`Сj^x/^ \񚒘(l ]v~`̙DDDо}{~'34_}o/< K.k/^Rɜ9s(((ݝ)SzmVeS)kiݺ5deeJB4EvUUϟR]t󰰰 (('Or $IBҢE j5qQ,--]&sQ]]N߿˂(OaP=ٗ^Xz=O@Ǚ#f]{mݻ޽{%FYh}u/׾^; n_]:¦9::ӽGϛ~]<$tŃQ2$!| 3T(߽{}ʀ?@%PyHOLk@|Yv-۷傼)6ގSܨP^Ա IDAT@p!45 6l`ҤImڴA Ņ4qAֻL ey$I>vʄ D ]@~~>gΜAV\ٳg)((|a߾}ƒ@uum/Nki6=>]jGڸq#VKnXd 555 cǎlfԨQ V;v~C1zh\]]Q0}t60m4P*hZ<==3f IKKBRDʕ+kM0sssy7AVʘ1c8rg߾}( ~F'`ҥٱwZfϞM֭g( c޼yfq>#ZnVe:uJ+..橧ޞݻsaI5khZ^Za .Dri.]Z 7779ȑ#ӧOrppu?DPPZZZ+zNhhg,YbNNN̘1,l׭͙3gHHH 66d댧qrr|q8RSSlLLLhl^o,FSNH\\V~{>'O6~oiii;vLPo_o߿8&YŋtѵkW:t耥GEE#==] 00<,XP֭[NJ+t9ߑrM>~777VZw}G~;w.C[lI||}Z>>cf͚E۶mvbO?2v$$$o]gxCP^^)((`ƍ̘1Jt??D֭[GTTIIIlٲb? <<|AZ-NNN jϟ?ٳg ,еkWHNNBO:7ݻwGr=뢦GGGz!uM 0 lÇQ({W /^bTYY)n NnpssĉhAAAh'///t:ө,8PR Z 11m۶G1rH{96mĴi?)kߎ=J>}pwwGƛoIIIY[l{XYYѱcG ðaDVj8p )))r۷tR.\ڵkygy'3{&:YEHBLd#HUZKmiү^](nRZ\mUBV UJJFBD$[:Lfs3f Qy^+y|l|s>C_ ,@ҥ [[[ΝKuu52Z.==ckk7&M2xM6 lllK.&-`d2vyy9׿puuՕÇ(=R+#=z`ܸqM;Ç 0o<_TTij>޽{7nÆ cƌ$''S^^kkҡCqssW^\-%NwߥCEBBBP =z 661cưrJjjj8tuDGG3d-[ƓO>%3<\m:u*Æ 1f*++!(( g")^'N@1433#<}H/{aV\x"qqq_sN^{5Vٳ'...lذ[DbbbV .\hdU_槟~::<2zj4꤭%g2N:G}d6Q*K 4LAp!*++IIIߟkrIz;P]]mvvvX[[fW:u 777iCV|||qɓ Ɔ˗/K sYY2LR& 8T*UϨE&5jh4K3z]ZZ&88\NϞ=m۶4Sz9:t@hh,5QTUU5S3...ǭ$220<==޽Q_pk׮bnnΉ'(7~5LwppȂ:h ߿?aaaӇ#F`~a:CBBk֬1P### =b233ԩStڵcɒ%js5+5Jza)Ri`o۶-?dҥAGuسgG%<<\XzAjjԈX޽(Jl"M~LԩSXXX`^k߾ɼI.]$/BBB/r]SSû󥙂=zm6[&Mtw( NC&xb1˗/gV\?,)h˭ԷhV\ٳgeÆ DDD0m4FMee%$''ӵkWB#**˗ /\wZYY?_ĉ5kr\#S:7}t dztR*++yG߿?ϟg̘1̜9IyLj#xqrrcȐ!F՟ꫯ=zM]f57Cnns>}j:t(=]taܹ|駬^:l2n28];Y]]MYY:tTWWӵk>ҥKFI˕ ^cJ;Frr2Xw;;;jjjh4*rbrssˋ:g/:99!ɑ.)n*ceeEmm-R[YzwkZΟ?/ڵ}QXXޔNii)&teee!FfbS?Bril'h|S**Vll,$''3b9BYYKO9ݴL8r/6VZ 777I[oxm֜IͻO؊Mݻw[oIznݺT*Mf0kQ[[KNNNhZ NGqq1۶mcrr1cưx&۵cccطo|׼K <KKK[ƌ޽{Cڵ+K.套^W^zW˗/zj^}U,,, fwHQQ>(~)=< .dСTMl2(..6pW :u(j;?aƏOuu5̘1gggxٺu+=ȝN#?? ɺZWWGvv6]tnmk۶ h}JKK)..6hLRh!sΜ:udd2fffhF-z#Z&//,d2 ڀ`;;;̔\IF;a՝J`UUUF!++ H JhӦ4S\\̕+Wx" ͢t.**V9PmI4U322xꩧMLRޭ2dEL?5rٳgZ乆ߤ> !r5^ILL}\wuuuF,4F``56+ߦfȐ!<,\ ^}[-2hNҝo߾駟(| QQQ߿^z}J/@J Ϗ;@N'ښ+W2h 222xwg駟huqqaʔ)޽xf̘!Yvum3)^{p|AFŏ?qqqڵ|۶m(9mvYc4<==߯W,,,>`ǎlٲ8w7~F[t |ݥ@QQj`!UEEݻw7Pkkk늣+NԩSӹsgt:///x":tҒ0j5LƁx{{SRRBAARRR"m۶`ffFNNu-R?.Dg}5 iiid2BBBw[9si6ۛÇs%gΜPTĉTWWoJ/^l1}}}ĉ&lTWW#R)>}`/O=T}uu53g롇bǎhZ &M7(>#%O aʕͲBI;ooo:t;$#Bzz:Ν3arr2_5+/sspTng5DSAwعs'JҤ:ydϦM I6ׯ\Buu4`TTT*777xt,--SXXu J[_;u$)Yjkk%kkk#S ŭ j-K,7#$''#/_&++KxYZZ"133CR5UYYi0_*ڥ>R*yz)o 0I&˕+WXz5?#QެYxG`Μ9'%..cbggGzz:#Glr1h &Lٳ9v#Gʊ_~?}YgѢE 8^{vqЭ[7̘1cG&33ѣG_7=z0`^|E.]DXXOhyΚ5!CO :iӆ+V0zh8q"ocgg'6>g˥!&&~y1zh,,,P*#ƎZߟ 2yfDO/LMM ̟?Y̛oɺu@T^772c }iӦ O=T+>?䫯SNXYY5:z9r$J ꫯRVVFvxꩧ5kV.z:vȺu;w.| p5`Ĉ-*3O?壏>BPʔ)Sn(NLJݻwӻwoqvv&,,3rF+VgϞX_[oŇ~ȟBo߾]]'''# /\C-?~J̞=FCΝ _|E***Xx1ݛ7)H_~%YYYرC:_3ϰpBƌ#f? q;8p pu<4iӰvZ֮]kp~ݶ2k땃-?3/Æ ͍w}`xʔ)L>]RF ۛ5k׾}{4 /^DVP(ڵdϿ)Jj794$Khh(gΜ!-- V s/_LzzνWoƆΝ;KjSAzP888h0Iee%:H+n]t 333%e^TWWҒRZܵVӧ ;;L+P IDATw_%+e)%`u`_¥2 |y[>;8bo;w2h .\p݈|CLL JիW ěj/;￟ o^lW^^Çh ˹spz)cgo_:r(fJ7+N+iuZiLF޽8*3PmWU@ P _ t:oNRRJ!Bnĕ+W8yQ݅>pv+w,pw762/8wΝ‚6n=܂ֆ-4NGAA/. \@(w l/ nqqqR0[ dM^ ^WJ4" @  @  @  @ B@ w7|8#F`ѢEXCrٵk~);w#FLii)3gEpKʢx"ǎ#** kkktVɓTmmmg6 ])((M6d2T*Gnn.DDD`ggנLZڵ+dffbnn=Qޗ/_[jjjpvv@ӑ >>>TWWZ&,,Ln.׫ KJJh׮ג FM̛7@޽O=SLa 2=z /@vv68;;3rHϟiii1 .0`ޑ#G:u*D.ALL Wcǎ=KX[[{IL:M6IV%KСCk׮]jƠA`޽ ^?f233ٷotlް) T?ccc [n:t(zؙ3g:u*9u)))FgOH/ &`Xh^ R>?'55HtB.]#==|M>c#E h)Pϧkkk>>>J^tqww'##ϮociiIPP2 '''<ȅ  \.jJŹsZ2_ZZѣG HJJ",, wnVR]]ͥK*Z8s EEEd2#w ZMqq1yyyxzzH]]dggT*bKp!t:MzWܭ 7/~@_p%;w.۷o_gݺuӇ:͝;;wf=jPBTTY͛7~Yrűo>V\x"qqq֓槟~b 0fLx"˖-Ύ[FC]]t:p^y~@'--AѧO{F3?t  t^{5% ))}=سgGTص<d2 7޸Q9S$$$`nnVE!Xx1;v%ϻv bѦ-?_.sF>CqFڶmL&c…zj~WƌCbb"}aɒ%&,F رc$''J~~I H憣ѹ kHk -jjj\,--ke5ʪs\J_uu5R\Kqq1FAǍkyy9(6zu䬤eVs:vH>}HNN 22Iru zTgϞ' '''X :C=tSS6GGX[[cmmM>} ?SX:ڵkdٓUp1Ô)S +ZMdiiiҔޚZpս lQQp0-- LFHH[XXHZZ] JnYQ&Ή'0333rYkUXynJbbѹٳgsƌǎ***8}4?<#$$`CBBL>>ڵɓ   ;;@9zE]L&3uo׮YYY&jI96Xq1vJJJ?)Zv!۹s'2^zIڵKQ޽w***xט7oh4)͍P[[+^7X[[-]\XXIx~~> Aw}>ZBDut MɤP(20TcccӪyGGGI9 {(J̈{QNRy@z7T蝊~s,:*B{x 0`&M"66+Wzj~G^{5Yf裏RQQ9s ..sssJ%9>>>q{M<1cxgÆ ׌5 &oKMM G 0_|K.&5(wqͩѣtQ m׮ ?~iy͕VQ9,GCVK||رcYbL<(y?/ kz͚5kEbXYYaggGvv62 KKKPTJerFCqqqWjʳ `M~~>mۖ\jM6pI:uDEErt:t:...XXXV7Oj4P(pww`ooW!,--8p eeePHHH>`ڵ,Y uƦMxG)((瞣|˗K|ӦMLɌ3ٳ7n-bժUt҅M61}t;СQQQ7$@>>>޽޽{cnn3aaa_#G EǛY:u*3n8{otlǎ 8sq/^,g˖-L6xٺu[\].nɒ%@c<ѻwo̙#*E ̙3j!00`oJ`2(NwC_X޳j ֩16$aaadddJd@kKKKiJ맗S[[ˡC 79 88sqjkk177`Nހe\uՁ} pT(xXHGΝ;4h.\h/͙3O>=@ L"w1d2ъ'7V%));J-g[O:r(fJ7+N+iuZiLF޽8*3PmWU@ P _5UC {ZJJJ jo$V$h=ܨRڐ,ޥ\nRHPI hms6țaΜ9V w۷eK_Sr'$6e^ @ ʼ@ @ ʼ@ @ ʼ@ @ y@ @pGqë|Sxۄ>(JN E O YPoƻ "',$@ @p"y@ @(@ @(@ @(@ Ak\d@pݟ*P O e^ @ ee%ͺGp&"PFRSSy7Yj B@ `۶m^t)++͛7g(A#L<#FxΝcҤIwLeyVKvv6j jΜ9CII uuuxxxxC2 m+ ''___/_Nmm-,Y~JΝEI oI $ӦMcÆ ,[ ooo.]IKKcǎSUU%]?СCM*܁Koɞ={nצsssRSS>|8#F`ѢEXCr|'''6mđ#GxWprrg'x{{RNGFF...P]]Mvv6j0隂rrr(++ٳ<_ZZKJJh׮nnn e( KXXݺuСC&/cǢV17 o6EEEu۷ogR//#@;Yz%_^f۶m<쳒9x ;vK.>3fh4lذ^xA;v,'.. &4z8~8K.`ѢExzzzj,,,ӧJ>?A6lٳg9}4 <ǏpV̗rQi?## i?))0\\\޽;r/t钴_UUEVVިT*:Μ9CQQ2ȝEӑ3Q'ՑMvv6Jѐue~t˖-ΎNkϞ= >W_}%Ko>&L'<=)]333d2(=/?:߿333"""ݷo[n ݻ˓Y8urԳ׮]7{ 11caa\Yׯ{mTDJ%5 bƍZw;GGG)--%==(d2/ZSSokkKϞ=d>}RSSr J+++JJ WdDFF"(((hPnwwwɽOOOB'!!sssZ-:Lŋ xYp!p՟7!!5ke^ acX[[W=ZO>aȐ!t҅Ϛ5k3vܹ :Ԥu눈0ZF~zF) sssoOOOrss:ZXYYQ[[UWW쌕)(( 44HorJKK%K?+.\hrinqKKK2Utm6&O\.79-9BYYPWWGHH9>:u '9c#ʼ]vKZZ~I%B?UUU42d3g48W in;BAqqq>w1zhNʫ ĉҥ K.5_~=&x߾};5ş7@nݤ{===%_j=yyy!<==9s}sR?VunL&#$$D -,,$--fV蟡j;3#vQk'O6xHHsJK 4^]b^R瓗G@@tcǎJ˹ٴi<6n:bcccK=[zU>&&]vQWW\ ܽ{7111Cff&ұ;wT*[򴎎RBPPݻwGTbffFdd$ݻwMZZ7'z#˗E h5&RۇZpl IDAT&77x~7-[fZ'ҫW/xyqss3X`֬Yرc#==#GhzȠo߾L4˗ӯ_?L[laF$$$)%͚NvڵkGHH3g‚6mڰeN<[o%]R8uӧO7J~%KwM~~ѺSLaݺu<쳌;+Vɓiȑ̝;QF`9B|||(\.GӡpqqZAp+@vv6888TcmmݤLB;O{{{BoQ|||ؽ{7{ggg¤`ݻ3~x~mF;wd2RKKee8*<{["bE M`a{ty*7p@,,,ضm[[SSgW^#d,ȡdJJ+ݺGFtX:议iACsڿfT2I{P3t@TU_T5@-PצEWD @pSPPʚ5kZ< 2 Z762/C˺@ ߵlܸKKK i[޽{Ӿ};F&!楗^BRݒlܸ~펒I ʼ@ @ ʼ@ @ ʼ@ @ y@ @ y@ @p )HmBLJ%'"',Z(7z~kgQKkAs:=^E^ށp@ R2/@ e^ @ e^ @ e^ @ ʼ@ @ 0Y @ 4™BIMM7dժU( @ h!T* 7}_py<<< Zee%DDE7s=geeyf>3Q@ hA.\Jcǎͺ'2233QTd2quugRUU93gU՘5ky*~ɓqrrB.ccc7FbwhdF[]]tMff& \hA~-:uŅW^y /@LLwm;LIIA&k.igtoE&ŋMm6(_޽/Jbܸq899rtĉDFFaø|t.>>ggg EEvJKKILL$==]:VRRBJJ ۷cǎQYYh:gϞeϞ=[vvuZBڴi@UU)))$$$ę3gj& Wmϟg$$$p1ZBFF'NO~~>޽{ILL$55j4._Lrr2 $''iM%//dINN?4ĉX[[J׮]qtt'OJxx8AAAܲ:|07oևVaח˗S[[Kvv6K,_~ҹsErLɉM6q^yxg)++g/$44￟￟~V\.T~Lڷl}<1???qƱsN/_V套^5k3< xr SNeΜ9 TTTp vW hZΞ=Kjj*={l0: ډ6V\\FM6h4=jN:N3x/UiII deeѩS'رcDFFXUo˗Y JEMM 4*++ ܜ ?NݑdT*RSSqwwϏ\RSShtP(piiӦ *Z:ud#P*6==2P(j@Cll,aaat֍CT"KƎZ~{"۷3{lر#/2/ICF7lٳg9}4 <ǏpB}Yj555DDDHR4d %Ν;&99B<<V:u 77FCP4:U[[L&6X\\,)z~{ppp@憙555TWW 8찶6rv&88\NϞ=?{U aaE@ rz)2QJhe뛕vR(xE@m`68Y*yk5kgzat ooot j\.iJZ-2jTVVJeҨO>@jjϺPSS#]W@}}M {dd$DGGK\.>fٌ i𑟟O~~>:t 00]~8::@@@֭cɒ%mNiΒ%K_ҥKa޼y3Fv:vʠApsscذaJ m:Y___i˗/gСx{{ӯ_?ƌC\\-b͚51j(N>-\?^ק~jw*;w.ݛ/6+ln+Wd帺γ>ٳ뮻#22Hn*nNAAz FS[[Kpp\Nii)III۷E8YB<<<Ʉdbz*++Aepٙ\]6BAcc#P(P(%BA^^f)r .f7QSS# dddp|||lt+* J% d2ٵZHӱ~0L$''-n\IQQQ[Lii)}fB._1=Jee4d?j(;|03g/ gΜFWqO.jtܙ3f߲qF|A:$`voV-y_4 ={_~ &w^nV+&&***jܹ~#GGa2Hzz:2pZ- 4H"-- . j?aϦ3gP*?rgΜ!""oLFpp0UO+?Y2 - 1z䵳8y򤔰w)9r^z٬ ,e,Y111#++KZ￧Gvcg͚Ÿq>|8[lg1,(bDXX.EEEV6}v|}}Vk-Fl`ǎ<裒md2zz͛Ƕm۬&Qx{{3h 222pvvZƞ1A\d2+IIU"ZVZjkk ...VuuuzI^m;QQQ$'';eeedggKKCg,=;;V+y-Ϙf>$@H׷ٮ[z/˥^/z)~k׷j]ROhIȷ+1_QQ}0 qF~gVZe%׭[OS޽;|Kkwޝ'Fl63|>)**b|(J&!!Dvv~OmrGGG)L4??''' L&$ƪHIIGQUUEee%jDYY.\4ODTzJKK)**ˋ.]XS(6bK.dffVQdggT*x-^ff3899a0P6zꨪ8::Z͠ȠSNb0OOON>MCCjlM^^^J^SSCQQqTVV^v {-PՄɉd6I?yRy{t҅={0djDDDa&M>}: ǏgϞ|L>f|}}=ztܘnŋyqrrG3Ʈ`ժU2]Æ cҥՕ;wooo-[ƴiӬٳ:I&Ddd$-& 7Z;wMrcΜ9zz| y̛3rHvwaYqq1k֬ٳ( ۭD?|I<Yb∊ҥ WfR@p˓B_܈l1h4R\\,HRW^Rl%2  :ήH~~>FCXX4p5558;;K555vÉΝ;NCRѥK]jp)..FTxCCC9{,Ν '''|||$1oIΦ ggg޽um#___?2;˸ʍW8WUVpץƒW@@ߵ'77GX@ nF#ΝWW6OO6p PXXL&SNW} ޽UCדO׮]qrr߳A8HjwOfW_}`:<{&sfavfsϙE}}h?***~1JKKtuuua2~h.ruuMzib~֬Yxxx Q*0yd諶gy=zT*իm:p }l6d񸻻ӿ;&mjFǏ#صkwIr9>`lٲaÆR$88~&<<>d얫IOO'11>LIIU?+I$F#8px8@nnnqy:D||>ѣG{#{'9r$_|L:U|8991tPzo|@jj*ƍߟɓ'ꫯb6ٽ{7;vD@p3ڡ=Ν;dž عs'ӧOg\Rھg^u͛ҥK[u^{͛73n8zj}hh(]tѣR_y~ϟ@n e<^ׅ\|||ڵ+...$''SRRbU\\\.˫MjkkL&ÃG>}rfbP([ڮRHJJR٦:٨$33SzHDD4sRUUšCٙݻK12 Vj|}}ǏL/ݻwG. ҥUӧh4IPP%ݏ©Shllls(إ:tLTTTNΝ IDAT@7j*T*SOemۆFaw[nSNzjK`uTbccygm466矓=#}ȑ#gggF޽{ѣL&K^0<̙37%;пFٳ裏hZ^xVoѢE |- y{+V@^^999y~!!!tEثЀ^D* N7m.P(.iZd2j6BF"<㶥N6:t 00]f#& ٌL&cŊt֭2'N_~dff{gN#))3gp_~ϗ'xtBCCqqq>C/r <ĦM$O& n41oq5ٙF+۪<ޭ-=yCCCYrZsQ*++dIjKlr J^GբP(fDҬgJ]tYHPH///d2YYYJרTf3v@~jCqIF݋lٙ}t:[n :dAg7㤤O?1{lf͚ŪU___O?M^pww<BRR]Fo]v7߼bfϞMRRK.eժU6Iy|||8|0t:Xh˖-cڴiߟO?+Wm6d4vh/̤Iߗ16d-A@@< ǏoU׳{nLĉٱcWMϥ ŒڱcV\Ldd$QZZ*.P+AR%Z-uuuVΝ;3`ϷJ淇f*7Ã^'%%+j\8p ḺrQi`Zqww'77#GPYYI``}i7yJEXX~ذafqgΜW_}ŻK>}HOOgV+uT> ?;vcǎr-444tR^|Ej"5̛7HFɓپ};>>>Ʋk.. n ;ի)//YكgϞ۷pŵϹ{6l۶mnl@_;SI{)Up]x̝wĉ gРAVeIKKyh7'..UV`>VFa׮]rz={cS699׳rJFdHghWɓ߿s1W>}())7hqZ-?~;wqGmZ* ]Zy322*bʻ]WW^Umm-V^֔$^]h4Kl6S^^nNC؄\:Nkg] 8=z@tt4ݽ򥥥T*)y՞n.[:L&;ƕ%FudW{={qh|EE+,,dƍ%\fݺu<ߟo͛7ӣGf̘ʕ+5jqqq%@|l{1OhhL6ò?~W^yc|<#L:kע5kH'`ڴi>}2i$Vzء+2X6l֭Ϗ0a/_[oń 8q"'%%ǣV'))k< 7׿CI;Ο?O޽ZIgܹl+yYt)SLa&a >S&Nh׻3i$^~e6l/6lYn=СC9rqq/`׮]V }]N>\.'88իW3}t|uu5+V!Ci&d\a?F# 4 aaa솻VT*<%%%fu]]]ݻU~ڕgggg)n%%%RbZ&22R8dZ3N zAΝcQXXHSSNNNhZ%P(h4={V ر#R?ˣA ىl%|}}m”;2;˸~#U;΀bUk4\(.ֺsm[ hrȑ#DEEI L&֭qo-)))d2iVJ\gG%S^޶i֋wNhhXFf3'Opk8UZh__fXw@ { r rE ~WV7Ĝ@ K,K)))A._U̳@ h;3/A;E&]q)֖4o$ @ @ B @ !@ !@  Ufun> @ E 'YW[T݃ץ/ m:n f#@ ܤ1/@ 1/@ @ B @ B @  G@ PYYަ2A1/cѢExyyi&~'}]8@pqOOF#ΝWWWqћa6F_~Aq}++ˉ4vw\3+bbb$$_j,:tp͏9s }߱s .x b9998;;JxnDM|cc#˖-#""R,\… dV{je˖ѹsgJ%GٳF?wwwϱcǬ Օգ`R)yԛK/T*_D&&vRL&ٙ]2e}?ncWFFvO<VL&:gsvmsU1Q(,X7]t>>ojj"<GZZzQVVFZZ8q>:OOO/5ϝ;GRR񤤤P__^LҤIIIt:h$##{@jjjd2ÇCw]xz=GСCL>^xΜ9cusHTUU1|pON׬YܹsYd ̟?1cƐB`ٲet:N:{Ǵi$Cyꩧ3f FjU]wO?kO?M݁Ӭ:qqqgoӳgOV^MSS,_hRRRdo&QQQVڵwxxMץę3g=&tssYRߩs=?˗vZƍѣG a׮]Yrss?~|8zbÆ <3~aaa_ѓcΝk/_~1vXN8ҥK%1ߚ7חロZ5 n ҥ̀vwwsQ\\Lyy0 TVVhdY *RSSҥMDmm-555իUu!{8qFm󔗗SU\\\.H3)))熸VIff>11T*.\ $$D~:triVgRWW ;w{<{,T*t͑#Gd2RիR__Ovv6ҵ&Μ9Å d"d*ia6k׮eرW۶mˋ-[W_qLJ~(XiJ{eر^z1vXΟ?/쉉,];wh"ˆt:rt呓Ýw)}B.]ػw/=z 11޽{իF'|9sٓoƍ9rJUt%RRR;޽;[la֬Y,[yIH׉'8pUg!CޤvhKݥ&&e^k^s=ǝwIll,UUUNCV/=fBB#GĂ3#FT***8y$UUU`ùF+V_~fϞٳ 5oxMu+bbbAuܲe  b˖-RΜ9Cee%zs>?~zAPPٝwIVVEEEK0$&&ٳgyy뭷*ʬf\\\BG 6⩼F+ܓ ==L&]'NCPHJurrr .HǨťEL&ݻpN7444fT*...TTT0ݝ\.[oet ooog5o#Z\.VΩӢSNҌƥǎjπ۷/I+z+-RVVF=ns1_^^Naaմ bȑ3}O?g}Fll,Fw㤦ex>}:C aݺu8::yfcq1'|GyD`CPuV:wLXxdu -..T*?cJKKHNNF&x\)`֚tʕ,_WWWyg={6wuuuuDFF֭[ox;#GP*tdddp|||lڦ777d26ۍF#t惄+8rNpⰮ o߾L&;fTVVJuihhqvv/7`Q(466ꊋ ^ \RDPGcc#fZ|\ DеkW<==!ۛp˓wVVgϞ466Z˅TUUQQQApp0~~~xyy]@!o2f9| :u:m{=z[n\\\𙜜233eܹv777%=)**w)[nEQZZʂ Xx1>3>Laa!:0-ZIJe˘6mO?eʕl۶j*.\@vv6oV۩5R*++yꩧx'?_M^^~-999,^DTh IDAT!!!i^n ((( %%4v3<}s=ǂ >^z_6IV..yM&YYY8p@J\~}+m1Z///tN3[< 6(u9-T*1Nzzz2{lƍ?^:B.**;#7o=ɓ'}v|||e׮]JtԉAdա_,\;Xv-UUUlݺJKK[rpihQ766RQQa3T{3Juxncq [mg"[ZEPb彯CP^cwwwiѣDGG3p@gZfggj%JR\kSS3 ///:vJjqr]f/g O?MLL r\Jpr Fbƌ?0w}8::2|.\+F`0SO=uUMNNf޽DDDΝ;oxץ}Ν?NDD(ywO<ӦM~O>|L47xܺu*JIMM%<<???~oرcYd  䡇"##{'xBZ͠SN9ŋ3yd5jFիW[ڇ;0a׿JRRRlC.] .&Y⯛sx԰dn6 ǎ/wy$*fϞ#<ԩSYv-:/Cotؑx@GPfy4 }eW^kUU7nd…Fz[oœO>)3c , ¦MlF}VK)S0~x鼗&;+… yGY|9֭cԩ|̚5c2`mƳ>˸qf֬YV׿Ν;yIH&MO>Vg4hՌɥ-Һiii63gb ~mbbbxw:u* .]ʬYo=z4[nY~X{7%sNsw- /#**'JnRMC %F: ijj VK``tCMMM8;;/|VRRl--? ~-Xyr&^N.../pqr9j ]׫W/d2FZUN;{/% 4 aaa7\GMM Β :w:JE.]i蘜ӥk鉳32 ///.\@AA*]Ǟ={b2T*UV !77WJuttn>edvq1Fw}*+qh8Uupznvq宂Çl#;ˁa/7 9\3)GL&fx&Dufp׃*9BTT|9ldKx|;֦Al&I5n5]  @# ~sPWWɓ'yEc7 $66VyNcc#QRR^jEy{C? ܔ?";wnt%%%%rk_ m/ƒ+!7B^ 6*\pir?N&숅=@ !@ @ @y@ @y@ @pCqK'n> @ E 'YW[pm.~- KkA:=a/?΢@B@ 7)B @ B @ !@ @ @ Q4@ fTVF%j-B ܬ]GGGL"C !.\??6.] o: ZGee%Du.'j6ݵa6kq͛ -- R6oތl'7YYY׷\^^\: ZGYYVrAIJJ`0׵MH󍍍,[J%nnnDFFpBavxWdTTT\qߪ*r9+Wlձӧj'b4MOҽ{wdС|g5vRL&ٙ]2e}?L& 66ɓ'[cժU6dVǮ]pssc֭vBo߾VkkkyGÃӧSWW'mOKK#::777nʤm7nDR;(ܗ" " MIӤy;vR;tKvQ,OZVZv,14 BDQaA.+,>?+žyyyf3|))FRSS{y>Lzz:Z>_ӪU+z=Ǐa6)))&]FFΝl67ԩS7n'O-lIaߣhߗ/_RdeeIxSvYYYnݚ:w .о}{)>大SZZ* ?tP#GD#CZ`L>^xΝ;'{{Gx~)d2'Haǎbгg&{0?~{{{"##$''{{{dJKK1Lc28v`4駟X,`X(..&??^`^4O-2tԠVxD@@... rrrX,7899\eS .PTT-""B^SSӧi+Vo>>sx Fń Xd ֭{饗^|EiOハ#G… R:믿Nvpsswޤ8GVsڵF󜗗Gbb"nnnj&M$imۆB //OsttdÆ dee1l0\\\ bܹ2_cj{nlݺ")ѣ(Jbbb@`Ν6ZK.ɮBY ۳gݻwٙ.]f7[oѩS'b۶mR\xx8?{@,-ٳKРp>ejkkYp!<'O&993gPWWǸq Z޽{Tw?Obbbޞm۲|܌/׏0m4 ٺuHdJ%{xxߨ%3?SҥKٳdؼy3|Gf֭[yט0a'NdŊ|R8y$ƍ# GyǏKɬY%h www˗  >qڵ#**֭[R*`[5 """}NNN\zח:PXX(zrssiӦ [n$O- I&55C!Ӷm[iӦ 62IiidB?hҍF#5555J:HYY~ I[n UNpphw:o 7qF Ɛ!Cnzܞ={7ߔfp}M6… IHHȑ#ٱl2֬YëJ׮]yw6lre222x ԩS}ؽ{dHk^*u۱cٳgpaqqq fѢE888n:ƏO~~M;ǏT*%[TFɳ>ڵkٿ?g& D֭駟fѢEۗ={0~x>|8|X, pLbԨQ( ^|śoy Xdggo>^{:aձ}vz)ƎV%::Iuwĉk/^LDDD{k a-K 7택-nx>III !!Çqlٲ;;;٤> <4h*:t(3~xBCCIOO'66 cժUٳ>RPPliTK+aVkmm$8WTTRd7(iKKK%]1͒Ņ{BٳgSKÃ}RQQAVV{uuuI}R}]FYYl|.//ĉnݚ;R[[ŋm:99-VNQ( fވVիCw0_^^Naa$xފ0, 55cǎIgɓ'ҥ :˗/gӇ/̝;W:א!Ch۶- "'';v4(̿[qIGر1c0x` Iߵkqqq/}vW^]{}4۷ogر԰m6L:ei#""xGիZbL:EDD:+V0bV\ @\\iiilٲF(%))YfK/Iŋ?>ÇgԨQ\^zq~G^}Ujjj\#Fm۶ ӧׯ_}̘nUwMͧT'> ǎ;~3a6//+Wbgg?,衇d򗿰qFYq*((M6˓O>io[9L\$1wcmvZƌÒ%K /d̙ >j?Z|9#FIL&.]DvdZr%V+苋qwwV}W8ʲ4 BA~~>۷MO"nZvvv899Q[[m0PR׼HrJOOg͚5 z-Ο?3w}___NV=zV+3#**سgRQQA||,<>>/JzIPP;wW^ܹϙ3gn-Xt8p{Y]xٵkfo322d"44)\_~hC-I{W777oNll,6lYuTǕ+W@V7߰w^RSSr|ެ0&&F22e fg,,,즎nnnMv̎$99gyW_}7 H@)~mjF#sep裏d8,^Kqq1>>>6R1IST];Zqttԟ[Z%uԠT*ϗY%RQQ'$'ҫWWcΜ9B **J+Jḏcjxyya2~3?bi8VP0n8RRRnk8//.]>t777N5 IDAT:eڞ={ri0pqիp}{QFyflСC% **<\\\lt3h׮;wogڴiRԩSػwjpX7:DEEq:u$:4(yxxйsg6m|۷O21**H^u ؜7_y=Cs}/Ɍ38pJ‘h#..NV hz.\dc{{Wq\]]=KUUU2Z)SxzzeRd&6vvvs t:\bc+n7O-IyN^ ETK^5[QAYYo"З'lFZN\\vvvӧ̜9˗/MYY& &`ooϢEXx1...c4ge<$%%a0xѣcǎ{ꩧ0`|2$99!CpBڴiCAAaaaM嶒ի>e/^G} /pGӽ{wJ%-#''{$OBBSLՕ,ƍgcIJe˘7o{ٳs 4ӧӣGz! D?G=* } DTT .K.HJYn:&Nȣ>*̴0Ko(j9gކ {ƿ/x Lƍh4MHHvh4(0 RTTOL$O-Rjg'퍃F777._p} enE/$)BCC9q(Jt:/~& N'v¼-jrJ>SVZmڴ!11QzxO?eѢE^R8q"&L`ѢE899n:<==֭nkfw}3<ĉg$''>Ezz: ,`Μ9˛1c+0qD-~~~$&&m6FuGy׿Eǎyc,[{ BCCy衇gܹf|r:ulc~0g;׿zիGa\bT*۷)DS~O1s̑E̛7 +_4p@cnȀV^O۶m#,,]v)3giuhر OϞ=3f^yW6lV鰳!YQTt֍\9#;wlKJJX,6~)Jh9yO߁JEpF#tڵMob#c@ by;F?Iw+4D@pbu`mV:NmqxyFf^ puumg̓@p7"4@ @ @ @ ! @ kLH5'"',ZzۙxӚ@ ڋ@ ڟ@Գ(k10@ a^ @ ¼@ @ ¼@ @ ¼@ @ y@ @^@ Ə?D-oVOO5={¼y7x"w9&/ҪU+\\\ҥK4{j)ԩS#s***Z"4zSO=ҥKtرx饗3.;;uedԩO ?z(=zhnbp>Ç>}:/555;wN ~|2;vwޡl%33S-\f ={]]vQQQ_^oܹ1r&ԩSy1̚5 ^ϖ-[1cqqq0i$VXUf޼y,]Th!*W\!77#33XFh4R^^N׮] :'OGǎ),,ɓWWWI nnnf?=TVV="HYYWt:jjjPR|EEJyy9mڴW6٪%$$J%P( '''Y_l=+**BRb_l૯_'!!'gO|r<ɓ'M6899ѺukΝKeet3g$$$p5=ʠA*44I&I۷'))IY%vO>$>>>0rH.\=уǏcXO>ӧO'Hacǎ@CZ?εkפ>}0uTZvYYY 6 ;wL[y#yyy$&&ZfҤI򩧞"88Xv޽{Q(dggzuuu7k+Vo>>sx Fń Xd ֭{饗^|&ў={޽;tE&\Z7???,BS/׏0m4 m&>A槟~j}uV^{5&LĉYb1.]uܸq#H+VɬY!UpZ]vеkWz Qqq1vvvmSt:k ۛplbI,..`0۷߿ѱO LSSSIMMEדMjj* ##CZ1I[W4˓/^H]] `41RXzzt)*#Fʤ'Neva, ={VZ"->CF}נӧO._LFF6lCOK۷CIځ_|6mڠhxg={6}p}w 8-[ɻ7|Ӭ|'$$PTTĦMpuue…$$$pH޽y뭷8{,]tl6cz͎;?RܹshZ^Yɓ's=ł Pռ曍ޣOޡ0`ݻ)**bΜ9 A͛1 $%%1b8@bb"YYYtoHny:oSO=رcjDGGcƍ 6!Cܴ.كo&&I,ܪRSS9r$>,k׮e̞=#$$SNsݲ4a0dfYh*Սm~<`a'%%>Mo>i\2lXؿ?Ǐ'44tbcc ,,>>q%.]r5+qww',,|Сmۖ̀HIIaѨjƎ+[PP 3Yp!aaa|R`MwFF;vӇ'O}^>p]ta߾}\zT"""jyY|9#FR `V~ ZdtLnMtҊZCͱɉZ\\\pvvUVKX_qɓ wqq9BhX;;ÕJeо}{  X&"BsU%www:ѣ9<= L4鎴oGϷCBB dL2>aÆѹsgG}<}ڵM ѣȼf3FVkM?)޽;˙3g8{,Gnnpjt9vX}MGjNk~ܲȐ{hh()))leݺu4M (۷o'66 6ؘx58\BEEjo{1͌?<4A -сXXo|ofJJJ ?UUU2Fq7.\ȑ#hZBBB/̫jZj% ^ =L8Q*8;;cX:t(lٲ???>#I3 mo=ppphtG9mLOZZ|||8=\6cbbX,L2̠Al҆,,>$''3ꫯ6zu¡hz.**YۊZh42w\ϟOaa!AAA<,Y4V h.5???oN:e״j맫?6b2d”B+s***8qfw?\o3gP(DEEa& :u RNp4iv풜3xbg̘m&_૯¬[Yh}8z= .d2ɞO{{NKwvv ]ӡd+WMIwk\ZX,VS5 2冧'z^*--ER axxxHixx8z"44RIll,zOPKcǎ12ij%KF||<'Ofرh4o.;f̞=%Kпjjjdz>}g̙\|h0LL06lNNNXyIL4+W<33f"88Xrfj9gކ h4C3 baѢEL0%;EEE,[M6R%77tf3KS`qss͍\T*dvkSj%((F#7^XXhڈdN:ӧ ͞kł7Fd|8::2dZLWWWXf | k׮ݻcFeiӦQUUŃ>o- ;v駟&11nwРAڵ uV:t耫ӧO/ALL 駟h"V^Mii)L8QaÃ|ݻw3m4Yy7HLLo7ofڴi16mڰf"##IOOg̙3X|ypssgaĉ3|peӧO_Ol[//f]1j5dʕ|駬Z ;;;ڴiCbbb M8vͲexppp 44z郎?3gҼyl]` pM9f&n^'xc}̙#;7xٓ1cH;#jÆ ?'L&.]$322Ro5MMIW߁~Enn.ŨT*&bT*&;;'OJ}xxӨQRUUUoWpF?apO|Mӓ>&>~G:th;^ K.wޑ5w^]v#Gгg[*0c6Ƞ}6R"33B!Aze}!Kuڇb%MNk2X,9}OOUT_[l,A !֦АkSpQ_[v@ hXXog{XBII vvv2I@qi "y,]K'J ۅ)q`s " @ B@ B@ B@ 0/@ ~Wn63Lh"',Z677kśftz D=n af#@ A¼@ @ y@ @ y@ @ yw IDAT@ @@ w(@ 8V[ެ4jz#y@ -WR[[K6mNQRRBpp0vvv< V?Pٻo3'߷¼@ 9995;]AA:n$heeeʄB.^`^z@~~>L޽|{zz4h4T*sgĠR %%EUرc;v,8::@BB=wQTŋcf͚JBPHv|tZ]^UVz?NZZ= l6SRRM Ν;ln0ݩSSK%;;SNI{4>|X*,Fcq5hݺ5111t4 .\}Rx}IOOT~С#Geu>s***Z"4zSO=ҥKtرx饗3n8uK/D@@߿$F4o`L>^xΝ;g3tؑwy:INN&66Lc׬YCϞ=ei׮TTׯߨ;w.AAA9rV\|@mmMɓ'9r$G&997sQv ԩSy1̚5 ^ϖ-[1cqqq0i$VXUf޼y,]Th!MmIYYϟG "33PTٜOₛfǏcooOdd$`ooO@@,]ii)& L&ǎՕF#?0, ˝橥RVV&:jo2@NN&_'''Ykֽl`0pEDDkjj8}4X9UTT.˖-q}I'No˗/gӇ'OSOGii)jqƱ|r8s GB||<ׯ=zyqAhժqqql޼c|r)3VbҬw޼yر8֮]kp wʔ)Ӈ?888HqӧO?{,g̙3\zooo~'سg/̙3oߞ9s0sL,f?>;wDNxIHH+Vo>+ rc===ׯ `tԉ[JEFFy<<[neÆ L0n:{lVXA۶m9y$W& Gy;wJɬYC%h Bɓ'  >qڵm۶*`P\\`w޸N… 6œF '''4 555GGG:t@nn.(Jz=騪ԒرclDGGMpp,klVaJKK`0RFjjjHMM@RѧOF#eeeRݺu#//:um*M^N>bgϞ۷\af2b __WTV0##_|={PWW@@@/"_5[lرcח{=[l?{oiVؿ?6mb֭\tgj^Z&oYx1{eڵxyyȑ#y衇onݚݻw3uT1ŔSXX(6jXr%vvv<òzH/ 7n'6M6˓O>֭[o帅Taa![YެvZƌÒ%Kd̙ >j._#FL$L&.]]v2Mkyy9ΒyV|F꛶JX5U`BP/9Z''Mn7O- ;;;ggg\jTVJKK9y$///SŋV \bs͆sRi񳕏Foߞp!bcc anVQΡC=z4ϟc߾}TUU"[:|0&M#ѣG%Fmyłld2\#66իWzZVj5**c}}}mbH鍤0}t;& vJJJ$ZYng>2wqsscƲaÆeYN8\BEEjo{1͌?<ɩP EEEt}jjjpuuڐZBI:N4oooi|ruuspJVS"99Y:V:==ffj Oǎ̙36  nyf47?VT*U^4p 8^F-`X2e fA٤ j[W"##INNgW_ 1/+**(FڏBB@Vc4;w.ϗG}%Kpf9 ^Q(J34J%EGGG.L'qr~;iݺ5555(J%G֦ryjTTTp ի՘3gΠP(aJ%̱cjxyya2$%o:::WP?SP*MI&k.m/^Luu5'On4}ff&DEEQYYٳg1c "**ʦjZiӦ On4ުˣK.O}G&+Fɉڹ&**#GЩS'ٵ:tp[nDǍGJJ .>3 dؿ3f`6Ƿ~+ GFNrƵ~WRvVz.\dGۋݪM5TVVʞ%ʳF\^/SNRd&6vvvs t:\"3i $ӫW/ə866^z5jrJeee@_RR/bұcG@0Yd iii3w\Ouu57o/d…+CXRRFKҹsg'44{`|}}9w,y o$4ر~D 2Ak.,X֭[СӧSRRy &&FOYhW&Nب7jwͲexppp 44zHrkn~j<ʕ+OYjvvviӆěO?fΜ9 ͛gs xW/^8p173q `<}111ܹm6صkl9sHcǎ7h~zɘ1c;#jÆ 75ut:dQVT*ݺu#77#GHΝ%,_R$::lN<)wj n|F#tڵM^֭[Q7%>|#GУGz6n8_>S:v<ڹYo70NTR-̗}S֯]K@` z~( ʖ ?ŀ 0ֲEԲGe"c!2V~k]ɓ'dI\> !U~kF^aaaMqn]v=\sMܽ=c f̘!gBJTT[e:g|I_+ѵײnWteܸ }B!MwuͣӼysn?/a^!B0}40udB!8K2< ҋl2B!٧~o ,]Y,/B&= Oq5xeddb0a@ӑS̢vza7_ "B}y]eyWnFܖ&7I`۩.s&7 gӮ<޽ EQZd> -¶koWmեNTMˋѴi׎DFPbp6>!FO_Jx',\hS.Lp'a6Y|9Ǐc,BQr۠< NSc1X4k<0.QxnAeھRsѠVD4¼bad2ߟũ̓6a_#O# s `ŨXaÇ]m15_U_|DGGiǎ7sN B!h0Y9EDEd剏'qQAlܓ=DGrx[df %+׷~n%8Ej6lkuO;+`[/lFYuBΩl!{wOhh4"LLp fCS,ڲVC0df3ۣo79u:/NjI;I@H8s)+qX6Mr2oZ5 aPi;O>z}5߰+!B\Vy+;'܂5x=˓mE$̏+"06NYNiu1ya0ąDQt( hZ,6E%;ϼDl~h"!-+o^{0}^wTVoFF!X,<=Zˆ*|}ٲz\ OO/:ic7c29'IrSh`S"zԿsP+|QldFjq3wZHP@, JmHmp;t~X)Sra7ǧl6{KrRo{ܠ믷ftEB!D/IrK0i4遇у_hfcx{Tr MD6t^9o=fA Cl؞fE͊ +%{Z~RAQ+ͯ l64Q-}49+_q]fǏ"6VzL&^>q*QQQuBH ŎiӶGAA(t_&\7viUf++SRbl& y30jD5 #4ȋ&A{-i4=oC`Z*pe^aGpȑ̞k=3љg&]Τ{Y͙/Bsu:K,z{` z=;2u4AGيA;,d8ʁfuuIDAT侮&hh<ٰQ0g>MU|iflg:+_s]JɄz=zu/YGrO^!#%=;zCoI.00N?ߞhyX5m2:~l勍9Os]eڬ6 =l/n[{n|7sWUTW6G`n<.oj qn ͤ$hГ~ӹB۔gspM҆yoYacwɭ#oUy§#^{!BMn'7JdgfSU|tdži0s,KAijbZjVl͆fCӹ\~ӴjWՠa3_ڸx{ͣDN_Ԇvͮ|ͽEQh""|==1YhѬ5(=ztw/hѡC{m+..l6Ʌ{2K"a^!u)hƐ> , يfEt`2[X\)Ft70̦~Xql{ESU,f3,3G2p4b&S'7FTfrI0aGGan B ޞxۣ_.5qGKjgVkO`*Q>&Y25B!ԵbYn;#{m :(.1ڷ{)(,M=H e\sEyk[J;< z%M@UKg^Ql;DVVV|}g\ P\*Au$Ib1cXR^ހCNiPPt䛇cMDLF_c:}je.Awێ}iԳfzBCBh#FNd[0z_?ͬ9,kGڵ^ѽ;3gNo q w>vIx^׀Zh7"(0ܼ<[Ta|])a^!nkp2-@Dd߃yd( }维]jzy9oPX0}7Bjg1`0`04]((eKM۱:FF#~~~hƀ4^vMkZ$ű~YU:ˢQc9-۳=c%rs_4UTi~]\wjd8N~qSy &иqcWӅ}-!2Ҝ Rt ~~`X5tLA.s !>xxxqA|ppjp1y7t:TU%88{|||]voaQFB9l_g^!AعĤ E=R6>)eǎ9;z&QQ.Eѝv*t:4.lB!.02i&rf@vVH\XiɌ jנlƍ/v5ׅĕ !B _B!.B!B¼B!B¼B!B¼B!B!B!B!0Euz_64Ϧvq̼iC3w-;}IK=~%u^ !ϣ}/~uSYY ZNO]'NvVڧ Q>[?"==]5pb5|gߟ|R;wf]B!aOlK]?X?MxytDTtT8^³_ >Qt֓=zpKL&"_yl <9_|EJNDDEi30-N-\=h0QĶJ`3tҝt՗SqiwM]lKLx~^<mwϫɾxĶJ <9}\]':!n!8,H6n@QQO<5։)D4ᦡ#׶_G&C'",ƌÕG&Ӣu"-Z'2jx9j}Ytڃ14nҜ+z9S]ϡ+~k^D4K+YJϜ5.ߕsbsHhӞFMOofBQ! GY|ң{7fl"##sפ{I['{֭9x0͛75ԴcٹkǎчQ:,'M2/y`mΔii֬)wO{mՃn`{==`:]:ǟ0q~\5pm׶fo1ѦM勮 bٻwZ`MZ k?Аl6>1Ϳi4u޹s_e:w,舍d<3iᦡa-nV;k!##oͣW߁hF}U||9k6ǎgt:~+1L|B&Lƍ9v6Çgt= n݊>%s\._>Lc<\ad6ƛ>n :l|wL}K_-ou?p;ラ[o'?⴬xڥ3֤ IB|</(f Ͻ0;︝)O=agGcY|]ڹ3={twX+{v'00WerӍT<;Xsc=鮉kެ 7m1ۧ7R+?_p{uJMw+8?]`; ?Xn=i~"M2 :vEbcb[vRuԑ,[#co}VsߢcTykG`rlޝЛ"E!`6M*$&&Tl6 wѝ}cT裡3gMDT4bZRPPw|OLxo?\:ŕunj͚s3k,+m:ny8-i/6t}z_Y)ۿܼjۧ{MAAAYj}Ȟ=w~p%%%L6] ;഍XjՎ]vvxڥ 7 ֿ* WYBzɳ>vMR+|_h! 3o2ٶ};W_5u< z6ju8]gPΠO!"0(^%glǒT Ʈ0=k]wa|0>z{>̈[ocM6i<<<6N}7ȭc|ZjIrRp'ʒKshݪ%̈́>?j;U)_}Άxy{a6*))vX3u-ֲaB!j֠=/yX,8xst܉qCs9}4m۶%El\^ wC<44ۧ=ݒ3~$BEQxaϗYJƌ>>@rrNДOks:}^_kQÆnUӟšusz͚{oϗ_~@hP_ fp] ݋7a]QUޟ˯F[ ub16fBCB?;c%899@P`鐘ѣFU<5ew~yɌ=p, ;M͜((,ST =ۮ/^ǬsHOO'))ؘ y}F1bP|]ϡ)mq<3}&w}8,~U;畢(|`>O<=ǟx~/Bqi oouk]ѣ9QG%&McxwޛNP^ƐaeoD7o.;D!EP@AcaR 3`-[4@p!A=v!Ĺ6Å4k֔P;s^$C{ B!.5ge!gO+-N>ye!B|y/{՟ 5l@+!.g^dgEDvBKYUuܮM&A^!Ba+ UP5wB!NüJw4ʘJw>MvB!VX{jIm=5u !B! X¼n\/><x>ewXO̜m !Bq)^{eKql)J<|^;fSq&ʢV~a. =3Tx^'B!ĥL/{ ]M~T l)_*) Ἴa^/B!.PCb/qz U68啨8| axT2F!B\AUU{zaJWU(Aʙag^!B\~ *GXPJ\ 8xⰛE B!RWgٰZ%̫2^0_jB W\y#/B!rZ\]bw˳ST쨐CoxJB!r PyfGVṪ=N{ j a^5B!ދQˢ W)b /B!$;#T({Š_5״HB!@_Rk׆ٔ8(QxWp B!ĥ(; N)n^^»B!z5w|MQB!] _B!CYϝ-a]!B%KB!x!B!B!B/Б~JIENDB`mapper-0.8.1.1/doc/manual/pages/images/color_editor_desktop.png000066400000000000000000001050101325266516600244650ustar00rootroot00000000000000PNG  IHDR.ƷbKGD IDATxw|U%7=tЛ`/t ]W]{,kok/kAE,HE @h!! yH$ -|r9c̜ """""""""^[ z """"he$EDDD:vlW91k?qWwLxϤ("""ҡ`hZ4EDDD01 ^a΍118Ⱦ;M˚hupssMm("""r`Cbio MG -<,-E.¨yHl`PckM<(c{N=ߊB! Xs6l\.99#%ٌlbi0c]6O?[[;>"|jkjxՊP/[oУ{/;nN $MmyluTqW#MO% e%8v uu>`q9 0`\MsH6jւ#fwK)Zf^QDDD@ Al6++VD܀an%$8Ym(xc6z0b2eۂ퓍'hV9!-(VVV`Y1 C""""AAV pyǐ-wïwubӤi1 @ ؾt QUSO}@ a6NۍluB!6oMBbI-πns4hj%( hhbmʊjFe'sdf]ܸf|!V2]3e]dӥKb٭}+*Xnk֬%u1LcjC0-&s$nimQIH7yck}wa0'(VU2oFX1>Va qbv*Pn.ð0fh<~?@ 2A"[.o)h8غe3u;R)/ڈbkH|f(۲{L4^MaQDDii&!ߘM\ZYoasn0DN74 o:<{`{aqGEDDŕ?#ڛi7]]s!PK(NȐ 8%,5{w9qݜ{P'\f07xi6 LJ-+/ͤ޺A7^#/dc3䷨uSJ+`xcy;YIeE UNBIg[;ygF!EDDCP42^oRlsD1e  '\OS~ rLo40bu5C vu v;յ{yoX֟?קŷZNXx͡-|(@y^!0̱Bլ1w:v$KX|ɽ2{xru-56,` =fq/r <[KIPŚe$w)LJsmӱXa!J3'OS TDPeո0zƧp5?t?hoqķ)n6G(jQ̐]L-ʣ`Syðb}[5l(b{b+o-uՓsf4nWH'b=fzLfBu7'o 0Fw}ެc&GW{(C<0;s7s͘[Rlak*RtD,O3]SE=E,}$?6]"1o^ˬ6naLPf~-uj@Ƥbas3G2:7M|#)rbi<.0嶳|b}ͤF0QAqS6BiOi0Z2LTDf),Jnܕ[G "-H/ټ}1Dmm=&_k7>hY]jͼr$6gZqpljŶf2Yjblo K˨\N6Kj J5ї)SiM-s2[ vC1q$Z c J$zuθNMPi20UjR$$80oj~XVyX7M@DDDEPll6hY[`-Fu|+ ɤ'ɽ_ m XTVV񸛭/LmL&k-{56Sf_I}i)dMiy:#E55$E^ܩ #7M&g:{ Nwxu8\Cc[ ^#D Pʆ0 x™KLheU!;b•ލXcHz;/NvY>ұ(""bxDqǙͿ*4hZYPg['; :@M%#,:m5^h51fmV+[ qwl-=IFy-xn >+ın,+dhٚLfuUCzQ0)5~yE5xrvcݩƔyøh9 T-aֺf0pZfLbdI_Xه+w{V`F`NN>ǺP9ϙRtu+?i#~ {nP+ +M3a1a֐Y.|MbL#uu&oD;apᇵ]L&(- ծF ?3ݝ&np0xG9F a@"௦p*J5@1Ցv^B` )E^zcĸ~@<~.5[sTR]P+)3xa`; Ex/4e*tE~?WZğy]:;ַZS >2ZS ϛ^fv1bfsCa~h_ kBAjZfTW $77YͼS]EMiVqpGx 9Tm63_l&9)7ޚ窗p>c!`^J!delrP_Sf"covf* h'Ն;_EDDdP>UMu[ cmWlZ( 9en[Y@k6-uQaub|Mj! jjp8mh^/&ݿ>L-&hM=;U3BDX}=wh2[54"ͩzldgNfa4emzl;/\OMh'/Y~,}#w=nSKneGWXAYڠ|scsرYi `x""nc|>*++YT_'dmΰ?lPܻm,V N6  6,g20X.Ni.ŌfKMu\v G.k5r'""tDqndmZZ[2v+ͺDDDDpPEQPTPQPiZ@n}z@DDDMLku2DDDDDAQDDDDEDDDDAQDDDDEDDDDAQDDDDEDDDDAQDDDDEDDDDAQDDDD~5ItDz ջ""""{@#""""(""""g#saea%鐮NcsgHfn_ H0t$Hb6+'>+UPVV9Z8u$HTZ]bh`CY_ ("""Zlj/999,XYf1yd~Gʕ+@"""GGGynj3_ 33n2N#F 99r-[ƑGIJJ $ܞ?t:ѣO>:IDDD+enB]]]1k,z)zIQQ111,Z ϒŊ+ʂ +INNfQP555^@ I'[u73f iii >s1s)T{eܸqw}y̛7W^yEAQDDD b¯v+quG_bPot6 }UGֿy"""HLL$>>6ݧb4 M9N?xnf;u((oAyJJ ?^YOyyyxd2 ŸۗKbQ爈Yt3sL&MĬY ^ncڴiztޝO>9B裏eWX'ԩS1c<o4N\r keرyL>ロ>Fvv6F"))I!""" {キ2fI&1iҤ?=z4a>lhfϞ콱c2vXu0:,"""" """"~S3e8VJu􉈈HURYa㐮lp#ybw\1jK* 4:'"""p:l<7MVP_.;6'>_ku$HtHDp1 zEQP=ϮQ..T늈iDQDDDDEDDDDAQDDDDEDDDDAQDDDD},׮%[һ)>!(vRc+(^qFb<EDD8nHP+JٟqS>TWWQxGH :q -V')w0gԩSut/+=z%+}nĐÎ y^/-~0CrdʟU0ggniƍ޳o""O^+))9ga6)*.b39x7x7{qی=[y?bYIL5W_InoBxQ<پ%''wpƙgCRR.LDD}#裎 O𣏸﾿!x̬l"b8Ù7eeu9$$vIpKg@0dܸ *+ŸM>fPS]5\KRr*HF7|sٲ9cHM 9%kʪ_27ěodʟ5f= `/68 LnN>;.^{eLGq8{TOBBӧȑ#LDD~Aq/5r$cF^w{ĉ|trss9~ p˭/y3g,:w.epDGG߿ / .ﺋ NAA, 0[7qqC ͢"|!vN_7| G3n=钕?|Znvz@Ff6t~X,\磮뮿itNNwNz5gu]r8h!v$KLLO_/^;oO`teʽcˎ=4F8?mLpraX`2e]C!:w\ǂ_p-锘sϿSܹS[o#G  %%[rS4h 2EEr aoݼ4y1~ݛ͛)dRC$$$Ƿ/OC9Yfs㎈`aA^x>cnj^~%sf}K.&>.`0#=%_/ `sr3f(yɧ6bgΠK 6l\JJJک+eKO=869s>?@;ftC|Aez zbt?s}=nX?D;8v|+&ݽ%0B~e555|X}ɛ6 IDATƢE_ضuԑDFFL0~7[pNjk[ǣ>N `̨Qϻfw裏 `¿Ac˦$ӣG=(@Ci [7cs)ܲѣge l!)3ÇbCю~?.8<&|#ÆǦ|<ÇtAq."֑ ' cQY={m<:a eȡG̳q2#F '%9zUc<=ݺQeדKQ+dbɒS~DDdXYZY>fƌ\x`@Vf&ݻ7{t9\|\5kkx7'S"yk󨮮Sfۓϴi1ù⪫x8ygT =f3#GओN.I"5526 Nf8ܿS0~|\~٥sY~ѼI͖Jdd$gD, ';i٦ =.|h P;ڇ5q]ݺucWQDD:TP= 0r, 8W_~?f#,o >ǓO=Mtt4}kd21Bޙ:J:uJsᚫiY/>ۯ/3f|?$3 7njyRRg6ls>lhu+Xfn7/ eeU{KtTYY<̳DDSYYEukᬳƛ&2~lܸŋU?ED:L:v5L\ݵ\ݵ-~SOSO 7{}ꩧb?(1~tޱYGbfŋ+8""*M܋FIWgӦM<3lڴ Jvv6~fٯA{HC;N_&nppV5ř뻻:ED 5t?Z"" { P NbW))iԩ_~B:YDDn ۂYqđ,""ԩl,(Bi[m(""d:Ef((((((((((((w񥗓':;)i;lO> `Yo9,\hRҺ0r2gdS|}=~?$wG%EDDD7Ǝͨ#TTTҿ?s_|%9 wd c?zrٹ=HMϤkNw7onsyEDDD~A{`6ٺ'z [&̇Q#d2r3N'"aJJj Q~o@iYii?<HL-<75H ]p䷷Y0~hia1xwTEbojGU5@|@aYDDDDZ("""" """"("""" """"("""" """"("""" """"("""" """"(""""֎QW,ҲRcHNNgϞ1C({钞qGqGEzz>ߟFm]~۞7p Xj5:~jjkYr`P!""[fD3dx"<>Kr:蔘㘣rm*ZJtT^ E%$ǵ?Rz}XV f{ZS_WORrE?HDDDAqCbS}̙t≻0 6PT\B]}=& ERN$'uSZZϿÇ`2~u=UլZ.DGE!qob|rvl3$b.X=Zf1 TUאܹ]2a:NzZvłB|D 6SQYE= tdmmWbMnvvzaS>^6YtJL%)1. $'XךuoHO%s_|l7lw]kS^Qj%!> /9 x>>_!ac>E%%x> =-.i!od+`HffD ?Z|~?&8")RDmmVԔdSSZ⒭߸z/Ԕ$2R][(2q:뽬YrL&3qtikoZcu٬|P^Q]] 4sIDDJJߧwem)RD\lL#F4v;~5yYvztkB'5%ؘ(_qqb6(.)eDz5k58vL arkY6Lnk?0&Kvͅ|r8݆aDBB^V]GYy]plZF޺ FG>+W&%9Ąx%BKIIN"!.Ϧ~t XV X~#?.inwsWlW] rp\~V+N={`2O&QPW>OCj%1!~{zY1Ųan&R:53clgLtTytTE%WV6 nuv[=7<ؘՠh6pwzr~]U]MRzy%߲a&rfFFz":a׹qR\BiyNAc1DE|AT튋߱is9]3/(3_p`s\|2K6ۮ;lW\\,&>fǃb8**³w]km$iz/y7PYYE0l1lv'uufjOnz, ``ۭTW%BaA_?:!l&**2YK'~>M&vő7 qgIILTdda>:*WUUvy<XV՟d~U$'u&!>NpDDDAqdjk]6Åjzu.[Bn9lVŲN/pD(j>˚N'Q{xsHX&`aGֲI#U5X,fs6lbPP?@jrJr ӊd'x<ӻ]:fb.RLjJR5 D`;ץ((1Gç3g;ELrjkkٰqGՎkV+͔l-eS~& IBB^=&oW" aZejué٤`XٔͅX-R"%3~-lؘjn 2Y۰Ҳ2:wM ۀlnp4p57RTILL.}{nk֮#d8v23J?&o֭' _^&_ B1D]ٽT`݆|~".sB>Xn=+W qqt^G[mT[vxHN$$r:w]- ~F cgRDDd_3D)iScvQV\ފg͞ vXr%n ..]n߶P(Ă/G:%&ADDDL&ޝ:Æ{䛁Qy Pxh|@Ew vу= EDDD$MYaj19C"""w@DDDDEDDDDAQDDDDEDDDDAQDDDDEDDDDAQDDDDEDDDDAQDDDDEDDDDAQDDDDEDDD7jŊlBii)qq$'uG1C(ӰٛOω'{ޘ6Mں=w3'zzU__y_Q~Ž~%Gg>grqݤgгW_ɂ OaʟR`]n-坩bŗ^WDDŒMqqwEDD[0k,8(Pltt ~3gjW>A :Xb:sؑGܿ_39팳{Tϒ%K9ܳGG}SrsrtgvݸiywƖƠ?* o?z sN۱)?Fヘnu,YQ%"*ĤngO<4=zC[ook@ gCn^Dē9ݣ"""{SQ,--%>thII -[n333?>eeeHmOx=j$#G oLrR2wLdqÍ7s5mO?uu\СQZZJ}~x8Ǝr3h@^7b`2(..a谑 2iCax4O{GB\|?`$.|6o}7IIy'6r4?-$3>UW\΄'6/g㣨6vM雐NB5 ti*E@vX@J׮  QzNzɖ5 EQ||3d3tLg-[&{= ׏ΝOCmseАsXgfΚM6Kyye:v)S9r8o6vvܵa U${WzՏs 7^bvvz¿. Hv}o];1ϧ:跕3≖_@zFW]y˅3f=U3x޲L~/7\w-=8l\z7dA}vL{z5wXٻofi0(z{{ܾ֭Z1~ ;o1by ORv:AX&;;B~6gݷ$=#^~^}2=i|C=ZYAtTT?ȨH-xoo#n˔ii߾&=)Squ2}:={rݓ dzL93[JDDNFq6-,,^zk׮C?;wҷo_BBjNdjkx@Sk]ˉoY$siiN~rFwl"ILjCiiG\גChx$!Lv/ n ___zop d2IN=mfu]wټe s')%#GPPUUUV'ǟ|Jjj ׌6ka8#n˷WֿGHغuuiiظiuG9YEbdd%ŇK/UW]7Ԝ^۷/=Bc˯1Lsx  ѹ-Afn[osMFYY#~uV,x:[OJY,Q&?6\ڻw/zɴŒ%<1 e= 6XO̟G~qgdK/;mip="""Fѣخm;6oDaa{ދ/ٶu m۴ip}cFGoW[-[rӯo_RRi׮m8]5M-gɒ/*: 7qwO?!.6nu;?C)j{rkwRRmNhۦt^ڹ3K\WQQ߮]NH[KԄ; q_.`Zݡ`|Cz\LIIaɗ_yU$YK_~6[PݏeƢ\60+G>O5WiiG]SYtipMի'[m{&MD%7#;<+X~))LxLl_TWWo_߾\0f niO^TVVy100ʢ?w^vZ'ͧO^\.{vZ't.ׅlB7c9ùۈb}nՊ={6޽;n|"##7qJʘx]'m Ajz op{rbkmjAAޖ~}31LLduL<3 ` #// /˴λ Gn|,#NyeDDDFsm?__zŗK?guݳ2ػg7eKӻpVX7g0悋_+{}M|,>?B /wu㞉woϛ?dĨkw5sϿaqWұcZJjzSs[O>]t!LsӳY 8{07o@87O?s'$=# ->r43ga'^y5&Nb dgsXڦ9g޷\y4ÆaOod}3NJ]ƛEDaa *9ɗ˖)v/\Km[0jX~'mNRva=z1"""(z7mH#DwX Mvlڼvm6XbW1IZrrstRPP/>e@z:/*ârΰb.RRSS>u*QQgr*eeڽ=BΝ/(`ٲصk7=Fѣ1 $#3e9+Vwx99Q#Gp߽Ӹ;h٪-SNXO= @~~\v͢b Sr][L?L?s{O=MӱsWy=kva'!A۸+y9G/BOdÏzI^=3|>`11nQ#GΝz,]%D˖,Cxsӎ-DDFPQQ (,,ḃ0+**X ]1oܥ_ɲb=h߮-;v$!>2=^z9***2u:_a4_19ɓ&yMRf>Wݻygt:≖_@zFW]yşZρDEG1t`)Squ2}:={rݓ:dl׮]ӧ7}槟ɧquп__^->⋣Ū*32xwXg3 7M.4k׶-LXo5k~fE[^ӓ:vL!#3|ETd{]wE3Qu7ɴoߎ Cc0{=|MDGE>~Eũg[hq]7Mz ].^^^jne7n$cg7¢#ߺu 𘟖66Q\\\r ->M&"#9Zk ~lՖYp}:p:o:qǼg~5?|wVW^`0лOzy5k@!:w}_Hp(ng͚bdd%Ņ\#^HH06[(c6)?}:LnW_ɣΥ]r*6o5bn{3MTWWq]ɚ/prΰjΦM,Cagm"tҙ.}vŒur@DDDծm;6oDaaQmڶi`1,ņ~6[(z ~Yh_ (0>6mZsñX,L:^ţ)YO}),ҥ37nl/O׮]r-ӹ Kwk˖EN$Ŵi\ =z!(8ە H;St2 ӫWOnFii)L ƌa-2m}Ӌ*Pobho;wRRZJxpy?!5}s%3mdnBBCѽ)?՝72]Ni2k0gL&r+}%**" _׭e׮_ݻ1xٌ:vCrvri< y> &OFΧDEGs.>3 Čӣ\vU\zɿؼe+S֜FisكW_~Q{ȩ|}ݫ_,6m-&dS\Tȶ[Qª˙5{ˋ׌l~Yxh&t؁֭Xr% .f\~%|xƌ>oWf8kW.b&O<ΤUR"{_Z|D&OW\Fvkh:y[s%Dg#.6 ݎ/͛sp睷cZ}w~)S3slrrs晧uV|'wrWb2y1t̞|eg_ngAt|~{Leфl|xL=TDD$2@)iCm4ހ%?;}x/9j\ǽQl&##.0"#xYDDDKj-\XG %SaKr쵓 pFy{vmҮM[}9DDDDN"@DDDDEDDDDAQDDDDEDDDDAQDDDDEDDDDAQDDDDEDDDDAQDDDDEDDDDAQDDDDLgݺu瑛ʐ?f j#9%E!"" JH(/#!>Uf 珽;֯[OJ¢((6yy֩*BrW.]믖DDDATr:ersTq""(rE\QPzb=={mz0DDAL~^\-1=RDDAyVr=t:qs:z("" k4o^zU***0DEFҷYLNZTKR2r(+h4GLdff.*.aFUX Zr؟I|\4A oVv.Yy8V?bx ')(bn)*.&_%""M'(-yvncsѣW=qj2 '"FUu5ҳp8Lh@yE%GpPQc^ڶjⱾrr9y":>ddcڵN`o'+(Zȑ4SAAAك3ÿG~~oǟ|Jn=ډ~cEEE\=Zl"iÈQ i0h*ب BIiLVN.fhEc2yy˝23HJhѲ^fv.!D 'y UUi:9I'/:* kF?A'.a- 5>>/@Lc^q ` JIi`Fc9UUWSUUM`Ә> fG@3'{iH=NYY;wY1;l>4aC0{ի'˗_{oVG9{`C fq [T\BAa1- v&Cײ=A?G}˙M[Du=5YDDNՠjշ,jȢұc~^3EXCd֭}zW0&*))-m4x!u""Dbǎxgq\\5N'~ pם{,gQ^Qs dk~D9RlWmce/p~ӶÂٗNYY9ё_~?ڎ?AR<Tg""4ju[ѽ''‹ XIIIf͚iWv '~c{jzk׏R\.EII1:al޶zm6m6SX\JHs**juRrbM+-+8f? oPP&آ>ƥ_F2l&}} d\qeXV6o1G9opZc3=3|}(t0yyy,,"KLN^>v;jk7ɄolT{=(/t&"Xazo3AڻNbP hrHS ]x&ziӫWOfn8ӀڰxqDEi AQDNDN||MQEJPc.j !4Fjj*NDDO:AEDDDDAQDDDDEDDDDAQDDDDEDDDDAQDDDDEDDD}u֑Gnn*C0-[U((*!T!Ӛ5?[Xn=) ""qZNu\Żt9ZzSN7ˑs븗Qʼn)qDDDAQD (셶BY J8"VR})"" "rO8N\N'ep)(Hթ窪*=&۶md2؂s c)jcKVnՏ(,V^P()-nynST\L*KDDNP`sǟW3ITVVmvR '/YDG6,vC։ ?]^NPa;j9#i49+Vݷyٌ1 /I?1wxI@{@QQp#͢bhǰ{G%1 A}&˗~} o1qX⊫MQQQi\BOB*((,:!I$DDDhz_x% Ġǟ|J-G$*2Q/ 33yՏ'MfzFf f3%TrbA1? ny1oݪw?_[k~~5;v 0 }b iӚy{OqWЭ|Yp^נA_߾ؾoA^]s=٤fU;DF1 (N=?"&._v-=50k``l۶R{`[3ٽkwm۶-ꥑ|lLDD(zCBBuݺ:hpy_-b` wz}}}p88L}orf~?ҵڎϟ./M&)3iA`00vhyy{u;㸖OMIෝ;`z/lذlHooRUTUWcTr˽O54pf Km@4((H#NƲe_ sȊ+lng6`'!|^0ĝwS݋iifzS1#m=__ga6=(/thE˅h].p:3DDD{P f7_3sl[} HtT#GI IDATי4y2>:ܼ<. pcXxxg $5%[&t30$+7݁Տ |-/M'(8L^.s AAQDDPc5\ AxǺ%KdQ8-ZDڀw4hpݿ^Z]w4hO>fȑ@F ,Z!Qw@IT|R{^pA#/Dϩo~}w8RyENxuEQP(R%Qt: 4iP==B r"'E騮{R*AqoVˉԛ]p<>y.EiX9nb, a6={oGjcP\RJFVeFl>~MIirI-ouc=[|i2%+7݁G\Lo$ŚzC '. A&wMBBOΟGUU'$m֮VIIj2 '"FUu5ҳp8Lh.p8hn#48=}uHyFcá"'/YDG6,vC։ dE0ߥD9[!((nΠO\~٥Sv50N<\C ڵJ$6*BCPRZQpkÃefHDPYUE#=YI'ط?O<4VOzy . ,>%uN.vuTFTU]MUU5y> fs*߾sHCq/_N˅`gC6t3^z|71SaK((,eB{l@F&{cxٌenǀ@b#ꚞ\kd6>WƠQ""r.]:sr壏>?ݎhCya 9pvRROF-,*fDxyiߦZʪj2rع{[&` Yp\w;'Es-M!(w#&V+:L6nij=_oP4kNP:},> pwy|rJ6hAQ1yL!A xiEAQEx` (0ʪjgp8*jGS{X]mG{IǻLDDFPOEE;rƍzOIIf͚iTSIyE%$)$5Gl0PfooRBkޯjV_]'%'Ҳ:?|kAAQDDHP,((`UT۫IOw˙7њ`U _~Up=={ 6&Ļ$,Ɔ =9;` F/jyƎMdD)՘CyEG"˅An~>kN'Y9[0 WC{d=(/t&"Xazo3AڻNbP hrHSXל? DHH0R;k2vhv5f99-\wx.j\.Q LKsW,~!xˮ Lbb vŃHMhpv9yTUUc0eBsigAYy9y8N&!DE7aN'Yy~Rڠ57J/.-嬙g0z1((yl9 iK~vc]_2r(X`-"m@UԻ 4'W5'|ȑ#U""2XpqC£Jکǥ@9P T89t 5"r_c\kȩs("" -T !48NB&CmlܼI!1N\N'֯'4DAQDD&&9%u_U?ˆB S"""wЩgQPEQPEQPKi֭[G^~yy Bm-)Ki@U)_ݛSDD<:wꤊ"$$5kת"DD^FEn}DDDAQDDDDEDDDDAQDDDD fEu}2()-r,,p+*w 0@LT`UH#zO .yBKx-nPL?|HLjU;v˼ƛEDaa *9ɗ˖yk]BTls|mނQ?y[o#u;|_ټ2{! ؆G;pͷo~w~x}Ϡ!o{ {/(u:ٗί-ݟ(ST\¦mvf6oROhZ&=0$&đGU{H#z.]n`MHO_?@˖<|v;[m籹ѫk~Xrrs)((db2q =_yACaтw9gP`1]|))L:H3Xj;~`00~ o׎<֭[OaAl۶m\0v 㮾ҲR~]ft2///2曯r[oWK.RBڎ ^{ST\J\tb NqWT}An^;vmX,A`{UTVVW{b]_/_΀Onv?O~DϞ=8>{&mۧ;ս\=1j>FҲU[y96|w ݺ~lv/w͸=qRU]5?u<.n~wlݏGNXD+V3EDDkt֍]m믗3ټqF)SXEEEeF#TTT`" 3!{[mcŊ<96m[oCrr{"#"-WPPțoMaa?62K}8#a9;7Zq8kKJb obL %)--#ߊ``0o̳"Tl 1F\=mcFÏ f݁UM̳+((,E|,5MM0|QL*&sjl*M&˺MӚks((ކ IMIq?_~=7xCrV}~21E ޥc|AAtp;89NG8gtYrjWmxW`\vϳ=Ϡlt]nP\\?iK.㝷ؠ |||{^IxU8^XZV$"""؂+v***0|ÚSZW^͕Wc{oOv؁{UtүYu+1L{ [s umΦMIMMX.';¢"Zu93=1Yx3ۻs 濯޽L4FC=0~nwz\Sh fݻxPyQK| /< ?>@@@?=? Zt؁N:;oo>Oz;tHCT||mڴ /@bX9{\˖-ywyb6{n<ӜvZPNXՄZnŽӧrK#WyEf ק-f"mxyyKvn^F#m[i<,2sq8MfFaxž[uh",o4@EDD3@)iCm4ހ%?;}x/9jԞcѢE a?b/9r*BDQ&5. """"("""" PFS""( eM9!6nL-L!""$~:֬]K^n*DP[(a6ɪ QPXLϞ=NJ[AD{orշ|͊Gb=v,9,r-[Сi,ZCdOpWT~ϏfaDFD-i}TTTRDN߳'|5?n'>>1L'>;摞AII). ___pnӖ-ث$qfbhx~|}~?SupPUUMBBs㷝WToThDRg{">6Ç3\ض};֭h~]wܵ lLlh{Ovm=ǣb6mBTT$qqT>\;zq׶lʝwo!44{wطP{&OadffRYYEJJ w/}=zp]~~n'##q@{rEBs, eeڵR m>Iu??_7'00]Ŭ;kFG+igk}l.q߽{Vlݺ2y9t 60alݺ6l(>'믿Yؽ{aaa<0~.`,.SO?C^^ڵӧOQۡxz.(($==XbcCCB `˖m?NLtTlQXT[y\?y`yyJwc\.rrrx 8t,xLnv\ٳβ|r"x9;0d0^9 ~f3. Ӊ``mTWWы={e6RSkmݎB6vݽ}Aqͦ[1ooopG˖xt4߳/L#kM㱅p8̙0^t16bp`~v5O>8 -ظa#SM#88}zg`Mׯ/yyr̟83m /2b(~gNŠd":*{OVV6QnNtt1c_|"VP[^W_u~ۻ9?̜YÐaxAFqz=l]. AQQY9c@qq EEE$cm)+-ϏjBBB Xtbci+?Ys`Ƿijg7mLvq]Ft? ѽg/oNrr{ii̳ر7̚Euu5w?^s 3c8q= ?/oҷt@UU\zPk֬k׮!SOg$D"hkkK>_h I_ml/Idم7))AYYiߍx||hw|>o.'c+n0B3f!IR?\m-XP7oM 64,fKy *{ VtuaْZ]]-|Q6hYv0 '(עnyhv5;vıo#,Y+W?yxLdvZ h7j~v{eWF,--زySkkkś~ ZLxTT:ƒ( {o)AUeaƔlQ]US8 fAyI͓'̞رs"[Jvؼ18x/pǃG_ /p?9LQϦн(SE z{bKJK( *B}}Mt:P7@4TUV@{3Ieƀ߇9p+n\+Qϳ=+_}<(FT{sbxx[ߎ>x<464oq!obpze&Z۷=Á />׋ۛW xg`T]m Jn{{|Dd=ZlY3A|Ҋe7Qz/b] &JtçϜ'ޤ]֬]Ɔ4 5QqI5߿t2-BE_Y_3 Wp3 K,sڃG^'!5٬.ׂ5KR,˵ꯢ<ێ_ƊED_kgYWE1:U$+yEFDwΘy#P6T*ς_l kX~;|@MU!ЄDQP7uV, "KѓI`cw|i2W>Cf464&&1&xBUU477Getvt`nM uŭ8/_QpߏP(ĿwFW}>,Zt۸ 1<8`↢((ga/_zE} Dn gusN"ZĢ%"""*n[9ٮȢ%"""*>lI4ٛSA} u$o'zsDDDD39Ms[Dfg}`y}Ra2]P􆈈(u7P3eILG=3$XfSH",mh2z֦@h2n6u=cH4wCOMOmK,PoW7\DDDDcL;@yO0щ#>v(njt3[ fSs@;D1+jKZm"Gbo'bv>(sF<=[ R[3(o٢œfZII$R? ɠHDDD4o֣PZ;m#]P4?E!"QVUC1,[Edڡ`جK `7Dv;e?$cG ܲ.,L#eTiU1$"E"""lS _Ub)qB1#!QEjW̰HDDD4=t4Ƙ)(EcR-¢hnIdk"QnflEc` zKKء`Zڈ;SP$"""܄E uU`1,L̺,6h IQ""""^@zl3s!N9hFljZ^#L1/ f鼖hfin*Ahvɬ>F"""Yu<=IENDB`mapper-0.8.1.1/doc/manual/pages/images/color_editor_professional.png000066400000000000000000001437751325266516600255440ustar00rootroot00000000000000PNG  IHDR.ƷbKGDtEXtCommentCreated with GIMPW IDATxgxU{nzG.ҥ(v],(HUQU (M"RCGZ Ho[B@Jkl93sfwx9 B!B! !B+KAO¡B! UW`Z B!U;Vj:տFuB! z\x0s* B!U:0*ʥŊ(Bqax\ҁ|Q!B\X:VE%)VbU+aQ!چDyRyOKk+˹i Q!BCbX<:;XplbVʜzv][N'CIIY#G.B!jZSv(*ZE&-BBl㝳~֖ Kw"Zvrs=$'Ef2^B!NoYC1pqzTד"QlX՚ܛn"q^0XhtB*(鋅D[Q jJreQS*B¢1U <!@)@)E[Qs7ŒAj,?x|ZMN;wBՂ@Qfjd$LYL9KFf[FF_"$1z>d'ײX5fRO&#NJu4VLKM}>'Xv4&vWB!JPܽh/vs{AR_f zv:23s&S~NDž24 oZ&y: Y9t0 7h4Zj+KUii< n.s:oʒllzsPtj 4ex`7N(a<`*xPܛPQdPBQՂ"Z϶"Eb~~ /(|hhs-r{4i1i S-؜Sc_@͚51Xk\ Wc8 !5vNMKD38BQɱjKOclѧ.!;FΟ>xjp,LhsweUd9jš*5fÉbCAYvI#bAQw ^+&Τ`q*c2 2bl͡#;3V'4Jա#;# NT :| 81C['+ @M-?=,b!P~KyX`r'l(j<̈́,^6SS,F/܊Z6Ys: *+8KN繋٣r,Xabq\=))*Wg`;YxXgW,O܇6fT9\9I>}ⷤ&Г Y0uVebջaSXgiI 4l&5 -c1Ncw|17 U5ny-jI Jq, [_{uX5*pdqhRf~<z8ȱS2o ^{c~&Gӟ۟@f50m˙BlJ^:˳r?)lG ݟ{uBfV ?x'R! AQ]έt7Y./aEn,s%(;ce__ƶKT֚nf/ (*ʉDY' ~Aἶ?5gO O[3dJ5z<ɶ$r$YT8jEPcWPnu|dbbӾ38N7ujQL۟G%#;&I~|Cݸ #8NFymthp` 7KM1qg8ӒwI\72}-e?{Z4Б:c1qyUep]I˰(z !W6(VjXV ^QSps{Z@-#<ߨ&2)1Yע"7DYT׽8y)婵Z1x{c N@Ew;La*̢ǒ)&t')9'zVmqQӫ4QPBq%bC`e\EF\;tNP /Z %2nAFpa< ,pQi@FMVV6f[9*I}yJENN_̠NAU4YeQFaJ$BE;^s # w5Vk/)Bqۛ*E#S 5> 1Sr`Q * vEAQQ05v&r"=ũ`v -|0ՌGIAlM`!5rnܼֈwޑ(B\jQ,=Be :56VS(yZ$c"ݓ7n ZkQE :j9[xhpmrmۋ))%ʺ8AtUZbVnJ׀ 'Dk+$%I9Pӣ Gys O/ש\,Z}ޫc\jDѠꦲxs8I@f6Pr.寴t{x܊#㧳}hA޼3!DVRW` *4+֧+~^ j#AQ!A񲸨<ϹgA,=U(8[r M?J'fi)@q:@Puyf]aEeGQnnus땙J"??Lt ( jfm¨͘jCQNSBqE9;=X0֩mZ]qSf˚˘|XQP"=OmpCӖ)nwJ:o =V.Sօ80v k90Uwfb8u:5f=Yx~ZK hȱicwt {(ZZ=fѩ0^ƚz7spXSj/7#Ɩ`(*uJo=kҜFi-gg,AAi&2Fڲ} Z {wC`XHNNFV^|D5yxT|W7g,d-?_p⮵`h+UzUy̺(zllj SiϽDJ!WO>2;K3 yETJmUU(& w'XB+O-/|;zt:6 JVVy _Sz8{W4)B!nؠxy˻-gi#:eq: STjZ Fo**ˤQcZ=[nY&Re7bvd`BQ%5n϶x5CEQ3gCvEwB!5`i0ǀQ)Y`{ Z fBQ%0~ʦX B!|Fv5*wCfq8*J>=B!d B!B 瞓B!EQڵkYjTB!$(V5 d2RԪU|!BHP%''ɚ5kXlcƌaΝhтݻwK !Bˋ[nmdo eĈ%1|Gٳn5j`0&##;vкukW` ((^z… iҤ FXM&;I!WV|nnnh4+5}RR˖-O>!..SNM||[l!** F#;G!ׄz{wK2j(-[… q8Ĉ̛7Yfѯ_?bbbӧ+&%%h"״Ӈ9sdLBJJ!B\5ҢXIO?4L6cIFN'$''Ν;'F)sB!ZԀѠcԯ%Ajy}$ǫ߮dTy' !Jj@<.B4}B!.@F= !B B!BB!Xň]!B똴( !B B!BB!(B!$( !BF+UP?xlΜ>#!??<< B DZC[Hekn˦ 8DE!o=L^^999Rl6cvw';;vq5mނ?!!}HGFBJ%]Y ǎ)<,QT!B!$(kyyyE!NERՄԪŁ2B 999PIHDNZ. !B\mW8rYO!B!\zȩ_~a+㏤2Bnqa򔩲/Qrr2wُjxxz1j+q[UW]r=_?vUm6,%?tL+y3 !x^GV-[ٱsu}?nf-ڡa<$$$\yY%1s7hۖjZ}Ulvt ޽ %wss#66@9 !ԩ_faذdfejaNz*xwԯWŝzNNN&22OMpt()Sٴq1&Ÿ+W2uurXYGӈhrAF?^tӵ7wn6v%(Ub4ӼyK֯/*[N/o_\ٽΝR6F;k$#=ŋ8v(?иqS}ԬoCq*%֥s箮@A~Az߱c'mڴ#Z0:_~Q2J-[iӦn᭷޾B L͹޳>xe˖3e%Z> M$,"{n}LyybQ+4;ZfY?Dv=?yTP-8~ ^D|ԭ߈Z^ٱ5Obnn՗R+z 3b*nnnh4Xh~~O̙ }~7㭝8qDƍ?o矿s{ϞҾcGt:;16|8'ƍ#Ff*>x}6|8/Y| " x' zeΙMvmXhvKзOo_~Gx 4_~Eb\v;/^L{2_G9r&͚q`IX|9#gߙ2yeڱc'RRݷ,\0ǨѣJLgaeINNfu;%ѣ3w.F;ώ5׹x{yzj9 !u1CvڱfW^`I|1(fxVرټy S'Oߟ}W %!! z^|ik֮cλY/U DJ 7mbQfMI^y]v;zrE[ou=޸i̞_~ox[NGF1v8>\P Au]ǎ_ѣx~sum[IMMQGAFfw!!5[W `Rkށ^bWBugddf+ܲvCFFFSPV*wbcc [N$+Jtl,^>rnRfνMV7q㦢˯߲ϗ]N\8?OM}ƒSq>>.]Rʔtq,! ص8H^^oyéY;hrrr8rp4O?Ş={hӶ}E ,>gIDFD\p[n]6F#-7c˖wM-1l۶W ظ1];wp C\Fغe}Vv܁l&((m[0udV:u}vۻ'&WݗmL&6^ocoz_;zxYz[& Ận-B!$(^5[nzp0So1pV_PO>[ڰo߾sAQCx}pNX]Q]|i)l`6iڸ 11? sM6` X>f͚wq`n&7bcbJ4j ({N:1aD~~j5ӧUfёQxzz+Q`bmټ];_-(zo;?cʆ Wޅ@( i.A:qqY`!]M ˖`ş+JeRjժ;wp>{ZY? O>w~,nnnxzz^F Zn]`Xشy3]tpvJ 3+Q?rQ|SKz>KzFAՂ2|BO?$&O{ yUWcGVX*N#--_ /S4^DFD0id||$6.X r啬^g<n:뉊' ֭GVsV~Nn/\i^xw{Dvml$:3O?^?>Bnn%ˬos :N. 2BڴijѭkW^>>tS~wfAo@QP!DTWQxJZU$0Hl[nQk/ha}W?z)aEQϙw=z OOOԉJbC`!999ѣ{w=^Oˤ 0o|%ݺv%%$/ |uA3lrssykX_@nn.M4fQskrssi߮;5rbh4\;KZ}= xz[ڶ@xD8_5sfL[;ʛA!(!@.St.v?,ISSU*(^m[7E})~TP. ]7mvo(EaѢy\wvA BqEyXuO>„ 8|:h~{wvqۡBH$( qF#ku޽{w !<(*Ruڷ#Q׶_UB!OE݁aZ(u BhPt8}bDB _Nj_*}jI|i dRBq]M\žw!{2,DUy3rKk,W9sٲgh'|P]ojB!BB!(B!$( !B B!BB!(B!$( !B B!BB!J\9k֬eڵDGsG>R!B!ܐ-Ylڴ/g)\ : o@>ˋDJ |+Vz2_LN'GEl7 R:riӺn>G/BfqM-eG qf˖+9>y_B޽X|9u FM nW ~wce0j Û&[8{p~mӇpQ/ee;{tyW]N勗ouζm˴to?< g>!絡Cٺu͞ĝ9tK0xxxAnnku߽gsz|,޽n]۶5%PTL|r??@W_ҭkIBTR庿zt/={tw=+! jUTj*/`?Yfup=%T*f3>wy\T*QzѴO8ܰl6>8ӓG۷?VVfM#33 ROP '&{0lH/>Μ>%(ܹ=BqMNHx;w%v:j59} \ɝwÞĝt:V']DQBCq7s.;FVv> :w~0l蹮*Ճ]O:Ye˗mv7nĄq:Ԩ^K|Q2iojΝi|txQ7nTf}r:Fa/e' qDbdDG@Rŗ_QV(}+LۨQC:C!77~\R~_F5H?} -Ҧٵc;vl),F WqO2g[o%-5_vUv^|?=)G9v8 ]738u"ɓ&oٻo%ow)Gt/K~K]uw][VF 9z ;)l]8ro׆( }{wb_)unX,n٣Nh42p/'O~Z` =Ha,ի{%Z5Ə[r~6tw !Ǡ0r06ů㙧˓ ѥ[Oƌ-[@?>06l`u4k֔f͚Op*5}Wz}lBNNN!pm[GRSѝ5P֗[g2*} ƍUYFMoDx8AA@in?DRɧQ_ȭ6kA|yG1̞3#GcXxg LZɾÇ %*2EQ2y"Lg04ee)ūQ|4u Gԓ0v;X Lg=x_Ke)u>:T* }ur)`V[zp:YFN+Zo _ti,(#=ng`2x']][232,?" ;[rND~/B*nI( Zࢾ,vf( Vs'|Mlٲ͛g_KTd$~_Z\\(q6̥ӸQcF#9,\DԲEvAJwΞc2(ur9w^ \Rh4bc =~tH:$H!s>>tV>x}0IBB"_g(< \_F?~3$CԪ7 ӠA}.>,;J)(޳G:<0{5kִD;vRzA5jjjT*^*ҭknͭ_IS0|h4b ]-[Eej[x_-7߂} 7 >wv6l@m|~JwB#q{ovLt Z͑GQ+!_gaT*AtY>B|8uZ >:_-3ϱkWY܀)_D: ^ZffֿZoo/:j8s&~w[*!D OxX:FCZx'2xo󉉎fޯshY%nxyKPPP@8~;^|~Z9D˖-Pl<6eD}gqvT*jt2|:c"#"'F FԓK6bEEFn=؟f}{EStT70rsp*N F%wG>,^4ﻗm۔x4M09"#1 z"y0dgg}u7v s]ed,Sm]}/UH+T*۷jB9Z@SOTzTò+{pM#^M4f 23mlztvݭ3iT*ep>K">yujI!qs֎' x ݲ `E7p_q| }}8~~j׵Ҟ~ B!D AQ\3^^r, 1<}6!BbTY:s*^{{DvBQE B!E!B!AQ!BHPB!B!E!B!AQ!BHPB!B!J2Kbb"'N$-- ?__'{L!# 8BZzǗ`x.J(oեW՗:͛O^~ΝwdffIeq_nQfڴC6Ԭj]8~dr<WvpHeT堘uԥ+qqY &77j֬E\t3k֮%*Eʫ %4< ѝǟ|,jgo|Ŋ+ol:wմlٌPUh`4TaahޔUjg.[0u:J=n6g.˲SNbYM[Yw)/4"ު-]-u}4Ov=U6,]@^h n{SG{ n~<|qo?t:111TDƿ˛cʧI?ҰA= 14W?.Tpql576~ɉSU>IؽEwA$;'@qDGUwU> ԩWDH|衇[naƌaۛ8w&.62δb8|gڴțob!^?\I={J?~wB@xxyC+,z];OtɎ]d\-ڵBfq2ddfa0RCh⪩-'Nl,nJtt4lڴ{')'O^\///n۵dтy yu0FɪU]޳Ghx^L^^޹o4|7{77z90i2P؇m[ QՂy'*هeif=}[| ?50{ҨisN*˯J^DD2o*_fϡA{P=tng>G퇏5yJ0);/Oz4֭wO]ʈ J/ ͳg^tI< FFF&w{?ucX >Hd&N\cdfeS7.P|%vMj|6oY~#`Z[Slں6aVN?6nơGKLfq^v>mfFo‘c%޼+q%v۰={:P=B땟ώ]]-vrs{gkmJ(n'::zhh(W&==H[5r0>Kmېzveu6))'+Lk}zƨ)7q֭[gL#<<1r4>ޮrV}+/b Y"88>{1|H70 `=z{fϙSC޷r &Ӭl޸0s7||}1 5MzVV~hԨ!|5JP~?Wdԉ$Bk׮2<f|Tbc8O.ĕ<Ѱ^JO;vwۻizklH:]Op 05fw//>>*ROwA]:w ]9O>yDDg۶|w П<<ӻJ-* IDATmTN{1hw˖mǎrbdL&*Mڥ {"0$y3ol>YĤ$vlO]C @V)Cf:V\n]KlҸkзooԿTrr6H&"U+q* 7WJLA.WWAL%\NH V$\ϛegm}DfO Wg' ;3)WS6ՕߏOiGs89Z$zݸ)#$a4QTTDFF&ۨ젠 #@Ppa222,q/>Ӵy+F|'?]?䒑Q_fM4p}C0fHڵ<;99Ҿ];8} 1+c-[ى{UUĮ״4T$ϝI*8;T2K5 Kff(m(u'yyL& vIHL"! (J)6Uص$T\Jdfe"ic`Wj{%VBm1N3'OGꂗ;*yj5V=^J"++B!Vp;j݀英degᎫs Tk;YוK2|po/]^ܹ3K,^ BM(((豣ӧS$ 5W{SMhKbT*j{<؛&R pON=}/9s{y,[g{YΠGd$ 2<(JVXƶmۙ:uѣ7wvV*[9_Hک+H.^ݰǻI>7O/* [j<~ *&ڪHJreRcZe+Jr .D'G#%%VMA\~y\i!QpcB%)$ \NHȱxyz_5mp٪OىD]7 em!A9ut/}o> %1--sg ޜ<wt4Z@  С[zrqqf9~666|ŗ9x 0~܋4mF۶mjUݩSGVXo`ѧ\{4ފSN[:zin.V󞫦eVM˿oњ4)%e5a$ɿ3GĒ]+3ظKUͅWSʪ~qq1yyyxzh@բZQ2^R$-=ͫRٹfAA}Ot:-w2x{uZmFcnڡБ~SRRBfVzs@HP|}X%WAVV:Ɯ< [0sr0L8-eW&>Y|Ug$kC!degQt٣=;Ӯ}Ge-5ߨ$ٽnVř_۶a2^-[2}T:u#;#;W_'qqh@h'ugyat:DEq}CpwB=77&;'zniٲ܏1"!dž ѲEsJJJ8xzFc̋/ATXbRxxv-Z̀ACk37iժ% @|'̛1͚qe{|}w[zQKhH0))WiٲUi\{__}ͳcX2a;\Drޣ;۶E󰦕nɓDv^m|> IK؉XOIq 룱'1 j[[yy7)gfNw}b.ơP(p4BT*q4psshbb0 ?҆ :.'p9> ^CPKzn^Oz\O3x7ΎB U\\9{!ٖ(.םieFY9(JlllO(}W*$$&QTd~Ӄ$N>CCC#zμ[n)*:9\[bNLJUKpQژD:Z(j5:DDq`0Z$]MI&+3sgƊec&uZÃ"VCFn'K)((ĻAOzў?wf0ۘ#pwgΝY,깹csϖ ̛7 MO5,)J,\ #44ukVbkkkVJ%SxBk&|}:˯LǟDRҧwo>x7tiopWEU+i̚>)WRˋO}Bjh:#?`UF"{=ᏍnVNnn.qݪ\RѲYqIʥ ( 4P j…N9KII *rлgϗ.] Rť\ODTҠ'F^DleT*z kCL%RpG^^>(FATVۘqq\IGG{{7 !:&8BVCP1D\=B,_EE'$ :ja6QXXNEPs+Jp!"gΞCgg'|72QnNz=]}̲Ÿ8wkE.I+I`Ӓ.[[4x0OG!11ԫ@rvq㖗%WK?瑇H$wgΐ@jO/j%uvGP깹Jܖ`ڵ '7 @]n@!`*s(I|=Np`D$?@pPP%Hj7rN"H$DR!*)]1JAH$bccC u)E H$D"D"H$D*D"H$(J$D"H(H$D"D"H$D*D"H$(J$D"H'N$--dY+Db%nn8;"!H≓')..YpY#DRC/SJeQ"Ŵ4:w.kC"HngeD"Ԋ=rY"H8*H(H$D"#$DOqiHMMOwp$:(J$䎓ˏ?GigGpp(AbeᓅH\r Bw$6n"CG c~FGڷM>w38NX^? <ŷρ9z,Æ=6Ӆe{QhF|H_Pu?Э{/\hcRv7 F7bkË6"xgˎ/Qk(^|-ZDqq1̝;6mp+%%tn݊J">>Kҽ{w~G@tt4=cǎ%77cǎ1c ~:Z*9sкukk5M"9}!aU*feё`N9Cpcz~~>xj8oLGHcQ_4_^}L&Ξ;y.#ûA&6e|2l۶N@nN.1/2olZjEZz:[[o &"K>](;ؿ@^z6DDDЩS'q~wrssꫯn):СC~~4hE)tڵ[n7 r)O 3ui4$+; $&%UYcN֮^ɜ0x@[ pišɕC'>!x;BšupQc-__@oLfWgprK_ ~Y~P Nkg@] `K/[aii]JE3fҬE8n880wqjљrs|&FoхOFvx7gW;WoߎN:SOúդbR?p\&2+=ȠxXz%Zr m6كYΠwرxpruF.-[ҮC't'||;Zv SԩSZRyGQ9[(͒L&EZ66r;D"ˡ.nP_,WYOO1/OO\W& Xry,q/̸k.Ҭi[.wҧO/vj֬]c oՊfͮyh^J% )tփm۰5$&&K3 nЀ 3sfOqq  z$oO;?^@8yL&~c#=3wt4ƿ[2y~~'^8n]ЪUKd `_?K\\<4??mw{:B?Dt >WWP*;v>`L43QQ9-rrr &Le⿄d"''3sLL& IHH`ٲe3D"ԐtUsrrP&.]3+[TTěӦ3v(ޝygAo#""ݲel۱BQ F>.sykSܥK*vvvTcuj֬]8?ݺ$3ef[ӱc:v3z$]tƍ*DG{cccC}8y-[ze{>( /8(ɓ&Vw%$]m[ 5&NBHH0} >{P(x}d^xrkfyKE_bΝښ_ӥK[ƸF#3f瞳3c f(}jP(}899Y'|B6mh/H'pqv&j چVsk'JeD0yTvIvN6Z FE%55nwܟ.ş}FLL zrrrȨ2GHNIE"2221 kh4ڷoǞ* |@RAJwڍJ}}חukVѼy3s BVQk,^Z&??1б3"ݻ\ҷo~6 Bq] ֍WLqaޙ0RQi޼99:777\\\,F^Z.ܕ+WoT*͛ǪUׯEc>|8cǎwwwsMhҤI,D򿀇;YV̨rg;V2yo8rgw3gGd{Q&ۍ-_3=Ϭwg#2\j_Y:4 `j)*f|d1BIII ]:_kn, 6%$]RN֍}{ظiBy-Wrgz=Sȑ#GHLLa<==Y7oNYhAAA :\$<<??[R%`Μ:MFFz~8w6J( o(? wX"YFx.;JppPxJDI"^gӦ^VQp!4`i4m۠h-WoxZhZ-\uhݪl5_g=:ֹNyfhќ+s%}E+̛-ş_:OW$PNFİw5o~=zb @Æ D"=;Ӯ}G*{v[ddM:͛ٳ7/<,8{999L:~|R6ŕ-ɶm;8q$aaLL֖.]:STT^K#"BXlٲJIy5bs}D{+$.Mh߾mwe@~ iiixxxecu۸1ǟdAӛ!q}CyI=z!p!8?J";'G0g<|5XFƘQݗW_?;oM]N {b8=0gi<;v 7?DoӻWO/(s6e -+-9AXV-N Zjwf  """Du:tHG agg'6m*,YbgΜ9EEEg͚%T*8yB(&MTezFQܺuÇ+ &D"X;/~U]B<~L^999"''G^'?Y)2R/%)Ax/F!b WڿWөBRznnۚ {|m' ??RxiT+ 7& zpt4V_֛~p-E9bʤ»AT*N~~/>H]ۋu0~FAۋo*/~宷is宧_"F!\]]ԩػk~vFWK?7_7wPGU۷+wWL3֭ÅFCtEٹ] rDŽwBR @h4(;F]Ҷ}8pppvvv"44D\\UNxyzM9氉DΝEZua*? Ī߉`3h^eCg[ܠÛ?+n*sv:-9ᲵZ[4x0XE֭cpD"EVAպ|:Cbbe/vvqãfDrv`ڵZT+sd>y@PʜJsJրD"H) H(-D"H$(J$D"H꘢*_H-H$rH$RQgg+kC"HnVYD"]jaN<ş[6rOI$j\~*$8X C"7E9H$D"{,D"H$(J$D"H(H$D"D"H$D*D"H$(J$D"H(H$D"D"H$D*D"H$:6f$$% ;AA'$.!snS,9zvvҿ D`p(6*[ܼ֯w38NX3g1wdH*VQ<}!aU*ё`N9CpPP.] )S:d0j|OW&p%9|oOUɓ[ķqc0IW櫥R^z^z@ҧw/fϝGǎ *χ ƞ={`ҥ5շdSyc$>B$ZaQLH^oڿ@bRR~pss%;'EX[z gDӨWx#Gq2%()ϽE\:* Zd2{50{LG]$Pϳ><3h}\GG30:ɓO 9@PTj-*K/[!` @7$?6>8qN]#}deeYıeVuozuƻy'1)y 'Wwnt5iLua*q3666۷O|Qgҳ4CHIO`ïѺm;t'B`yͼ$C7:i"dUSƿB}o4z#~AK&ћS7X~+MBot&YK~ް:\Zf999hFLT >{erssy~ WwO6nڄd'Ww "l)Y-k}ݻ!qfORσGI|ذa:t&MФIk4;}v+3Ayk>Uxc4p:,\@ZZ:{zi~IWv6A{pA!M:8ҼUkVZ]iwptǟşѦ]z3ލ|yٕmU-^ `ѧۚ*3rrrxqx4BotG>|%v4h<\U(ݬE8:^ 1s{57J9V>LPHS W-ZՍ]UHINaÆ %xzx0x$%%EtZ^4Cg]喲rssޣ7NN,| 1u f?-/s"V3s{|mb Grr ]m6a IxĪk=j BFF&͚5d2̘ Џ$>3mq`n|7ʵjO.m-w؁|G{3ʍyJJ {I&>>>l߾4?b)zG[פ)Sسg//YL=77DEz}iѢ9_}9z= }K_W_]ٰW~tz>{sόe͸yL/'N[.jղ\=FbB"_|J_mc˯u&ysgW9g z$oO;?^@8yc??dIqB4>U?.]̣ÞٱcJGf&zڱf*K&sXM=kP92 u'LyT[[[#%''GXMq>GH,\\9 SAXbP"6"[7s6mß|}]BR1G J%mlO s2-ҭ;oO..Εc*۶n8oڮ @ڿ\fx[jwob SA b>W]E¥r* u+/LWY(?"NJ%bc.SAh԰ڄ*Y39̍Κ4\0CUEv&|~VU[׮{-] 6ss. 5.0/[899UvFWK?7YbęSDž W)ڶmc' z*LÚ6ާw/ѻWO#;v\1}TQMdx-h4ϕ]ONN1W_/|}+7seg@|g58'''ѷo_ /RSSϓ &ZV4Ψj1ola*#ڵ[f ;GE,f GGxGWVƎ%d 7+mT+1Y(Jq9.ұjmW.7_J*cF\or~ǵj4oJߚf qwtޫs0OZ << zfU9ze `Yzvqv>)8WcLMMNs>s1Lzz <=1 U۲e Ə{Ѿ}L1/ N Nt@ŋ۷S:;;A6k΄&S;J?]P\v ġC{ ӳ|Yo![WM䑞`UتW ۃĦ[mWk\OFxq灯 99dddT?((t)2>ណ.5m|l'YYY~ 4м-9%g7|9nɑڱ7˿*lmmj)Í㹫+Юܹ3NNNVז`Jmk܋s)6o܏WΞ=Gzz,GFv-},T*<==1ݻx*[Լ'{=<ʰVfECcǒ3=ϑ#Gy0 dWnqq1mߎ~üo P@n܁^m7rp;q0]:sN:ysfmڂ+VVFzSeyZm-@F,J<^ڌFF]|<<? nĊea4VXєQ**,yпt ?mvSR̊=M,ds>N;^qZJFW&L&89:֩I|ҥiӆ(h߾=K,zުU*:tCx7hq/Ҵim۶A֨ M~5mřA7ͷӫg{ywt4Z|jj*,,d}4 cdի]k[{թI/`߾}۷BIfw᮲qmشyK5SZ3fO̴zܨ*}k5DyOb='"Gsvڵ'贴4I74gdd][ݺqo>lRɤor͚Fqq1>p9ώâE0h^|&Z価Cm#) ~gghi 8,M0?yk:tbyч9u7NٱchP>wgutNMMcFгw_^}y<?7O`5½^=acc#">VʃOj"!!bQfqrrG "Ə{QfW;g_"F!\]]ԩػkGz½^=T*ECooL{vn!NzѶm_kuh%c, Uޕ펇5 SSjrY*Ǖ>TH5%ROH$=ʱP"}V*7r% ['Y]\eH@5j4~NY$dꎓH$.55f'-IƍTTq>kS[,]-w9==D_II nOJ$N6 s`ڴuSÕ䫜8s'p>:¢;?=#Q9v,,]N9I̤d*5>L&YY_XX{|Hpru]N, Y-[ܹ3z~a-o4mZMfظqcq?ݻhfٕ`Ȑ! ZjÇbbbjզ')g?s.^X^xO/+s&?u$ubij6շdSyc$>BUy:1cлwo^y0ao@h !C={6}СC9//=zȚ5k8tǏёÇӧYp!#FKٓ}jg?$ß|xmg'G t2g]YPSNR󥤤K9EXK.srs),*"THO .=NN\OBB" zؓt%ghѼ)vvvuqRTd𞝝-Z掦|'Gn.4NL忰h(du1:^h0;}vɆӣ{A5f,7nQ{/[ß&R,..Dtٳg%,,l p611ݝ#fk*|mrq… ,X}xzzW_akk=܃saɒ%\r%DEEѤIñcǘ9sfׯd֬Y!ؼy3+NVE-ڣhJK0V?:.4&77DQaAM䄫 M)=AXG#FF hHVA|| ~P(~nt7乸|"jQ*7FSy[" z5{;[[snۿ-ˣj;3/ӽ$u:Q}Rza$H=#Y;vЪUKq/<Ϫǵ F{! ʔ$&$_u7tS\F IDATߜΏ3u|!>>m۶׮]پ}{m۶ ???4ibֻwoΟ?Obbb9~~~ܹoN@@<4nXv>[G,&٘\e-7X(p{}eTAAՠR~e߳,">FCaQ!Bʔظ8[4 \e?y|RӼYZ:YYF@UѐoTfUnzVQd2ݶRI}Ow}!hps<]IIM#5=;[[ŜNBb"ß|R?#j_|Tٱc0:d+mܹ󄅕988Чw"ٸy3k6lUIBBBqT濲{&Lo߾t:z=+V`dgg3h "##'`ĉSlQo:$鎋{Epw^.@R )zJE"JobD/A - H#zȥ@" $|lgfw YTdqu> 41\fc˗Cd\sT=}~.`gVRɝH 022""2.Dqm*W((v ʩ%m۴f󖭌.^D̀Loo'"2[ xyzrNX۶mG,[ ___ZI_tiN:ELL VVVܽ{Ν;|r Df͘;w.ڵziFN̗-1b_f116$YwD笯OlanNll)ɺxnfZL0P*yr;:03{JeodQjTLM9}֋BARr2wS1X[073ȈȻIKz]bPYL`g?1]>Ѻm{^F`At _ϨR2/\;=qywJ͛7ӫW/VX{oS[V윜|rbO85jK=رcKcǎ۷Oŗ-1y9=z.t)"Uh5ܺ9j[;1c#nATbR͸>_c##OнLrJ fϜ>[w"RaiaNLlm&'}ω:P(ȫHqE9 ݺvao9v8oQ/t-߯sh߮BӴ~ܹsݛ/2GРAGjj*1k+Wuܵk˗q9:u4qnx98ydiիG}94Ū hRo\FP(TF\:CobaaN,'˲KZƙؘX._ƿ7{uZIK;Kd=$pmLto*{/]헧K\$$P)=07'6)ܻo Q*&M%cZ|0~ԩT(_~l߱;v̚3Wn|w8Hn]u_Y{itSLjժԪU  ?oߦ8p}1ct5˗/{ӣGüyذa&Lx6j( DjհJ*\ׯoѨQ#9)yWW]]]=y[ qJ5pgjlLEry8;줷rn9ZJrt)BiY{U[lIhw4,-q-Se&My =-6V$<|+022YlطNa`h\AE1նy0,d_Ys (;{є)Od>r1XY6˲cLjf͚z׮];C5غu+lذJ*m6|||r]9{aذao{{{,XA=VZիWٹsn?@>};w.}ߖI 88>;w3Agwdg#vmZd\1EHyGTddۡV-bAˑBK!K2AheB!BE!B!B!@Q!BH(B!$PB!( !BbP@!(!!DG%!vTxy>6%PB!(HLKKjZ/x[ש/K(Bh7m!*j V>DlB!DͯR-Y2b(9}ظcmeMj)B(}iXq~~9ubtփ!ѪEdƬ1bЗvC6c&+Yt!(b.-{on1l[ں@Cqwp"[ǽ{ٴyj.K/x5 !DɈѤIK@)A-/e?tQfz>f4tٛ49IB*ʼnZp?HJJD/))cʑ#GЩ iSB"w> MY\8xCs֬ᕯ|RJƍcڮ]Z*&&&TV{MOHH``ccÐ!Cx׷`\\\033חk׮vƏ?JժU`(;w;Cl\,g(mN>ٳ&iy#&*U`y9vޗ_/WUU6T6?Qozrr2}<rXX]/\=#"P~8 aaeKY̘9[UbL +[z8S + M̱/®#gk1tJ9S:ݕ7tg͙KP;8cƬ'<*Tʖ.Hrr$>ce=*bfiMJٻŐ.Ѷ}GT6vt4OGUƻ}̒-[Ս[U6$$$c.jn~lgwX ػ/uxT\e{ i>,.XZmن3gO?ת-necŭ119ZNow+(rn࣏@;~[̛75jpʕϝ;GZ*;k׎ 8M6__a˗#شiGc:te˖1`޽ W_}ELL 'O=[C _\UIXڽv:31̚9=ILxX8?Y۶еL6_Kղ|0v<ݻueƟiܰ!y;wҼۧ+ Ƴm&=9;RreV, >G(W,lX#Yh1?y Ξ>|ӹKw,_Ə?t F vj~#~@Q:K?[];:cz˿Oч9r0QbҴ/FFFlۺsfO?GS:+WtQ`σ,[޴_6nKuw'2"R7IQL&*J;дYOҦ]]ZMsVVWlΘlƛ?~hO^ r^-ZWE]/^q_租~bٓ޽{3g~nݺ:͛G^4i[f\r7֭[oooZn/^$**S2|,,,`)|(!^~/^H2p@?|{JuhޜyV:vhOrr2SNc!-|q&'}HV-9} 6oa?ѱC»ix;^?//O֭]Bu( &MSCǻrԈ1<״ySzzb~PZU*UHMs`߾>|Q#s', SSS:o=k?%oVn1B/Zy$?׷W]Pm9pn9dĈa̝6[Z|>atzhܨQ+%%ig0|͝7S9͚R'sϖX^AiӦ.e۵ QF͍;wb 8333Z-feЀܼuǁbfETmkK3ntײg޽L:jU}89>;ȈH*Vz]mDxxc> E IMMe>i3x罾>y"Gގ4}{r kZA 8cÇi޼066iӦ9r@Vtj VѣGѣn޼י9sfknnn9r޽{/_<Ν#&&۷o'OjժSl-/Eށ_˪kupyzrNsX7)yfpx<~\Whe҇tͿWɓ4oEfr"˗A+7l-V4i^C }_k˸Xƕ#G@q)Sjՙ0q2^̙ E[i3%66>I~QcnnΞ=鷇dꔏ9s&wq\0ak&aiae˾O͛.]s|/'O8f̨_ gi|y2}d-f-Z/'\DݽZ͈aCIMKe^ ^Ƅ 㱷'"K'm۴~7>t-|9|8=:} lė/eԈ4n֙B䭷٣;c?guzԩ[ӣ{7͝ի9*^xyzuy3o5nLMY8a\|'aaa8::srr",,L7K9-Ͼ̬ڵkG&Mpttv̝;0V^ٳ۷/׏x ? _0;{;fȠ׷?_|S133#9%901NIf k߮J LjjjȈG9I͋)寓uCghV<￴2u N;666XXX0j-ZBjj*,XȀ~}qvrʊ"zDz_-\*Rrzu'#LWUlK-|,kH~P(Xv-#66>QF1`n͛7ٶmׯ_gƌ1 ҢX<6NdO9ccͷ{ފ+`eezPR%T*U}uTyiִ ;#G`Ѻ^?pjԨ^sv"$˃e'y}5f~kKʕJ|!!ILL&SNmjԨKؘ=sϣ4o֔OڿbnncO7F>ޭ+j7j9m۴f_ټe+ݻ=~ǛӧPBYl|qed߾Yu}ȱcǩq=BI*UȢOw7'O[E!:&+o2bh2nXY[hf%'UHjt yQcDZv:f͜Α9)) NNNg{n9<<\jDDӲ//kܖ Yv-.]b֬Y۷Ð!Cطob61`<1>e˺Ѣy0mLږ7ΎlޘUeА޸'wޣfUnVv=fϜ ^N/.^)S3r03fݻveMIÆowfGSݫW\w~Ӧ ps^.͛5c}իUhѽ[fU 3DDDRÃnܼIgg ĒϖҦmƏى7oQbEfG MVɂ|RuBMHIIaͪ'Mf-ܥ}Ktҥ|Th-RRR[oѡ};Att4, >d򤉺mXz #^ן:O^>]:jannk)"yzpfccSzꩻ6ZXX"Q..etoO{yV?h:}|ٸi3zL>ktR:MK??Kea طoB`` ;wd@zw: 7|3\]]qwwgΝ/^HhhnYygĉ̘1;43+H Qvwx\(zѯoV ٳfpwgNճŲ[fts\|YoE uŁ{4#|0d\]\1}Sɓ4t?[okN^~:Z^vvxVKٶu33g὾122ã-;P?lmtb> >Mz߯d8C=~(*r "#4訰]t9_q6謮f_6TϸE+g L g?m>6oaƬ\rU"ܾs; IDAT1c9r(U}rA:@z]Ĥq i.o߯w7vR'wocR7V{o)S?-[Х{W*Uh۶- WwA@@/_RJ,\???ѣٴiݺucɒ% ={'tRݻGÆ s'?CժUٳg?~]}]= _}kJo\Ie<kemys+JvNN)زy_#98MC| # HR3- jM(B@Zzyo(^@/VOoͭ3RΝ;JDh(X!Dq`oύ76}&k*Ax֏%󜦕YB³s.NXiѢӦN#P,`%E B{۶n.qAzxu*J4 vv%b۔{B§VqɈWR@Z]2EiQB!!!9t`QY~Z|ٝvvvx{K(B2TJn= !B B!B!BE!B!B!@Q!BH(B!!GQ!("!!DG%!vTxy>6%PB!(HLKKjZ/x[ש/K(Bh7m!*j V>DlB!DͯR-Y2 B!%PDnB!(Ң8dP/_m=Bb%88.zBHQo'5%ԔsL@J(Xz[FJJJ QDn>}h32} 4h%PB 6b$jnݾ7>66RNe>rdR ₙ\voܸj5cǎGÆѠAE(~r% YPX,BB3st~ٸ3gbW,M|r3gիWgҤI3O>ZjժѤI4iB۶m bʕ8qBvB!~OMWHBBcooǴSXf-!!q&KӦagFղpb<*TZ q'!!1cqvqZo69/7ahb+Wtǎ F:zpvq)^H>͛7^z1i$Znr 7n|׮]CеkW*WL JϛQFѯ_?j֬)kF$PB*6d0իWchZǛ0sؾm++TmNܾs'?p07l`_f- i&(J.Gp"\zȨ(7o͛\~VZyyyƑ#G9iii +[lJO|-ɓ4o5̌жMkۏV%jԨN6mػw{ŷEs_X)"K?rHbcc_~a޼y_|u^:(ElB!Dvmhgb+uwlߞHNvחV-ؾc'III~ϛzuӞ%}&###T*W\a,]ұcGԩøq8ur>{>(o= !xnʕ-%!!!644-g&׿kС};9v8ۧ>>xyz_}v~ۺ+ҷ@|Tݼϛ+ܹ޽{pEBCCs3f ڵI&lڴ 0=HKK/88<*xyyerY!:ahȄq|{ƍ{,l.sF+٪kxa{1tV^î{ܥ 2yDrzW˖ciaIݺuEf̙ ۵ER|dٳ={A.]ҥ +V$)))_}vOhР۷oXoooj(jZQYh٣n;{^h >sdڷo=wԴP$''τ pss{k׎5j~z֬Y#d!Zu`dlldlB:uJ/(r ߚ6訰]tܹDeBQXlB$#^r7_>@FsLS*ܹN:#RSefi<|NI@21h Z B!3Vr&?'B$(BQ$^@-JF]6yE!(jNx(|  !%#PE!xyyrC)۳gwۡV-B*L%zB!( !B B!B!BE!B!B!@Q!BCҏBQDCB&**J2C?V*}lJ(BQF$3^b{wvS' 9_":d@Q!(14nB2U(yZu9|h4 B" _8Q[#d( !BI ^Sn=._7V|CDd$G)\;w'ۼ QonڳcѢE4nFCvۛӧ|rV^;_|+WYEGQ!ȿZ^o޿wƎGT2b=zĔDDFbkc?opq< tAJtG ҏ9!CJbҤIٶm6lwɓr>kXBGQ!+oo/||iܨ ÷EsV|mǎcu̚9#7WS={r)>YXSF7><<\7Yg222BRq/^ҥK9x ;vN:7SNqLK( !x.tf}SRo^oKuǏJ*z .eN^2\]]qwwgΝ/k5Mݘ1ch׮M4aӦM}`hB(hG//?2BWXLL 'bOvW>tW^ÇyfMvæ[ :KPY 2kkZaݏ /۷o'00O? }v_Ί+FV[V*t OOOyE!psuC4nCCClmmV?n]ڵk1x/XHNd>r1XYkk+y1rX:˱9gȱs] >x,Y½{hذ!  99&Lݻw?Ff9` )PTNA K(A" [!` DGS%>BQXlB$#^r7_>@FsLS*ܹN:#RSef7kd4Hd Y>'$ H?DiQB!x&Jn԰QֳB!ī(ŒB!DpppLxU*J4 vv%bg!j:y\2T(9(j4V@QZB"EHyGTddKlϞNkgoZmB!īK2AhrY!BH(B!$PB!( !B B!B!BE!B!B!@Q!BH(B!$PB!( !B B!@Q!BH(B!$PB!˰נPH. !BHo͒B!([B!9YYYI !B`2B!@Q!BH(B!$PB!( !BHQrqܻwOr:찶ãEF%QI僥ff88If!KʯPYAzK"#4訰9 %ժ+{:?h)u!+T*/զ`o/!Kʯup$2,G@ f Z@?Yb\\'y#G>mJXybQ61w֍{wq pK.QFMlD֭e)1M⅜  !99)`cen޼Ebb"6>VrV !^얭[ꫯ9} Ϗ9gahh7}W޻wprZM1c:Y?x ۶m#""d|||5soոTt 7_3enf%66֤. T*ʕu~=P(XYYQrEz #<"TpwsJxۛŋ?Ғ6mZx"`ݺd|BCo`ooϜٳ޽Z%K>e˹>̙= d~ŗsLL,aaḺRY7^mkK.sNerqq\NNKt>zHo|rrngkZ޽K _?֭æMر R{x|>|'G'>t!>b?ӪuN:\bF(rjh4 /]&%%( q&.]j\Ą*+RSSu˻uaaḹbnfJDd.^F,,)_<"(&Oe5;{;XSzͅpm;W_~Are9rӦacc+/_5&MhVA2{\> fϚI*UX;:vg윣{Ȩ( qvṟn[T*K"#pvr+cp)ʖIE"E]'P9_7nЧo?]cPPwqbccҫd.;oٲ%&OÇF)f,KzqwsEA!!s̞eyjILJJafi|Q*yj<ȣG1s&۷ "2[[bbc3Z/+s9+W"""FåKi&9seG񔩺Ʉf/k ( z9CCCL{%vvx4n?V---S8kk+PB6c-̳FŘ(144,=_3U mΒŋT"/^O~9ʤGS3SHMKXKd&w͝C&oM¹>+?LMMG轰)a&zyhhhgܸq+׮Bҥ^Eʽq%{qo//lي..Vq &۲ѣGXFE] ׌E`nf%r ,133$ԶWS\]S p80P*ysy HMM}*wmK~sMs|[SƏǑV9 WoٙΟ?ϐr/ _`gфCC|\k qBA)(*^@_fɨx9e@~|ŗtԙqrrTPz,srtѣؿ?o];co͊E|;]Ux庺8ǵR ssT 99s33RRjXT*Ϩ>:99rJ%*ڌΎ54sss#" ̌ _^BV=Nd]T*KKc~lX:׏7166:`|yEK8=Nصc;SNc|(Sƅ?P6`xF_|_}hQT|\rҥ/ޭkn"?.eܸp"`oGreu'HRSS124DEmkF!:&wjꂹ9ZҥPQwީK0XSw0Ƚ(U! :wҰa~XPti& ̚1D111T*<=N_.=Uhl|XYY1j5lc066WoۛÇP(ݻ{gIh^ʯrs)QQ\| et6[mYىu=`d:J" @a` DGUM%PxyzEeRd4o4_z%6__R٠3xyyJf!^E^g*嗭 x1g<d 5c /vB5^|}a_BW*YגW~_-oMK _:+Z/iQ=;_H$/5F&(y׌gn=+{uy_g;m>R~=@F2TJFm/q[H|0B<+nD~")XjȤz5!CR~)G(lll≯}eH _oߺdB/)*"{-;_X[l)_""#I|Hɴ|0 RB)z)˯SF= " B!D36I %kB!J"YRV!9u:Hג3^:( !B@ߨgt?4i9!Bb(!`5ϭg@B!ijlӲ Y췡vf1`.K!ڙv~>:8 %S!;~1>ζ9ޖ[Ң(Bvs[@ 鿸B[ѹuZAEQ3~)`g fY!<9@E.B!/3)̖GY&f-ٗk6K06h2&g:f0xBJ(Bl"d 5w-_&s`dfh%) n@ن6Q7%5朄(/<4_E(Vj辰yXsyJ9RH"3EigGb)Ea?vj_c$P0hcz|O?/ Cp?8r*zZO;E#kxBq O \E,7b?h48fGs0V8wܪֽ.OGغP9 S:V-]k=狌آY(<7J^%֎pG,G ~)@՝k؎*Έok[H7<°]~xq±& ^:/N9}.tKs@x`[NwH>$D#!mb IENDB`mapper-0.8.1.1/doc/manual/pages/images/course_design.png000066400000000000000000004164741325266516600231240ustar00rootroot00000000000000PNG  IHDRU ңbKGD IDATxw|EƿW{PB = E! URHADQTP@񥈕&H Ckg69.*>총yFj!B"D/H&!B"DI"D!BC_Z-ZUkr$2"DxhUVs\%C"x˞ĦMN!!5IexM@^Jz*߈n#6f"qprG/cر؉"D<$"==](JzI%&D?"G BĿDRA*%s 2/B(Dn"88Xl",Y7|S$U2-JY 1D!Bf*f}_K "D*qn;Xl-"D!BxTݵښ/u'&u;~]k< }"DTݎ#];BYVT$2K؄ƱYoڼҦ|Nsz[šqOb 9ǝ@"'/%Vve}HTihJdXǡY\:Ӭi_)I<*?ȝcgql$7]D;R[aIոDoTOP榑}KNLٝ[hJ,\qh=^3P ^9H/)=27+!8yN/!s~R?;)*'enhlٻb &OԪ?Rky}7MYA_f$2ѪUXд7nONگ]UeG$ϔ$CZQyx ի$rK-M!珍ǝ@27uQ2Gdv.XcX46O澕] U!䏔fġU*pžQw{u`SozYVSx4NXz]"w0YwrT*/#;#eo)+ACXܞMe*I&(;$ԊnX)\'T\QDxaVW{?U^ƒki-\vLc#IG]>6͉YMC`]e&% <2GǽHL+r#P椚&6ZRkȝ HSãt||X>7[g**Usn8sRKǭǹ 4BR}ŀV{0wbfhg&a)*[/}'޿R D[Y7s\VUJidXeϜ} ڭޏf+ɏA1sMm 5S)-"W9ꦕp>  [&qD?"={Sbf*eN iZ(sRHbQBeS+\P)/7j $MH " 5P:Cܢ5zD$}9$Дj8Wsky* U9i_xqsIw 2+$!Ǚ$TҲf}zTل tRܸ]|&ޱ .GމH* @$|Zy߀0VK5VrhBKTTv"**G OfDFFqO(J233DP<6P(z+JWEJEYZ2_a 7wiarv$RsdBu`JR>o@$29ցMo$u@fdliz,i[fBQiNԂzsqWEne dYa^7Mz8Vu䑦;͓Щ H89C_W{&~1 eNAX5¾aWB;U$/ͳano_dzV!]:Uy Fz8@%~yf8Pdr{fa1f?G%Q/ hŸ/EQ1u0d Z f K':2~y m}a@]ܺGIiZ >Mٝ[wj6 YsHMҗ#єg{-,\E׎`죓Z}hs>C~ 3 >G4e%g+$hɗ slOP/<ރS|OJclM`3"[Jn6J|}ùlߋ+IX9hwbWbt8h2}O4R!7BIyA{Rb[/MF(RQSC9ܺOwħҳ6a=o@B4Br"OX #z\DJϒwz+6AͰ luP3,JU"ʻu6Ԛ8pë\oI=zy(̮ߨq6Ao?Kn.i4}1HDmr,",y'mB"WvŦV8wK~O6h޵\8qP^K |/EfD󤪨aÆ ̜9:upM>6oL~~> ʕ+?4j7N T-Z`ɒ%4i҄۷o?xb4 &Myjժ|ׯϏ?`Vڵk)--۳K.B֭+9_\p: }X3PC)/Qx V/5k?B T Wes,݃q&griUH1;:]؇Df k0ATv;Mи7b@%yFCӧAEBIylMxrYG"D*'?%yf*>/,5TeI$x} 0v2>Σ}_UI4$r+_I4E(/U* jn@E9f.*HЎ2%Mcѓ MY OBU^"ZoigBJӮH=x 'уnխm۶}4R.'nΩrt>;o\kzbHHlBMKZXꕧ4-uI>2G.ވ glðo Fݰoحp&l5Z*S鵪2R $ XҔQs3xݤ\Ejmo,p*i돕:r =eДs{bnXgma,8@fVVvƽJb?3[U$پAgT4BHSX`ܟJg4=cuK@cڄByB}U(I8g<,^X T1o<֭['/'U&L ##'rNN|w,]@bcc9s93f k׮@x 2d.aaa0rHElٲEOeƏO~~~ņ‚K2mZ`^#!!aÆq!RSukaIJJ⥗tfB' eOXlڍ`9B`N_ZZʨQزB+#YlPmȒ*% wņ(2dW*ȭtvVw*^H5TCrӪ4z6FIƽp 0^Q$_B|}"[bߠ "pn3C30,݃L4Nҫ&퐪j)GQQnI:UnUʧ^ǍFQ[kHz|`Wхv;NHz`4Iq1NPwO9Tn'/ w80. .#73JfTZ4%2˚e^Y L2SdT@e+̮1 ;KHj(=FQ1|'';ejk){BVU#d$Sq$fYMa왲ҾY|QAYY2߿@zz:*+C I&4lؐsr ~g ܹs%22[[[fϞM۶m9y$ ,`dhϞ=4n v¾}qJ 2332,3㏙:u*yyy89D_8C/B:3 ~mVX _p!g1i$bbbhԨ{!88ٳG~:_3={6O?4mڴɓٓ Rpvv&22&gϞ̟?߀Tl߾dՎF5ӦMJGB.] @Z8z(VVVhZ'zS# MOFHFy2['RMW]y7 fjJe7t 1v{`TedNR+~g]nH& Dn]Ufd5{#^u;톙m?Uܷs[8iZ4 3xdxǁ1uLG3m5!煻KBggWM\+~jժ@A}p.^ݹs $22={w^t^קNgT^ruV%7״T|2lPUD"#h艧zJR!`ɩ&\Y^eee TO+[@@'N{m;}ޕѵkWg<==Gvv6oNygH$ :%8ٔQf'W5ʪ8⢪]DlhpʻQXΈݐu@_I8~I]f,;AxXF7s6EyW;RƩU@"ʉELL ڵ3Z6Pw9e2wGDDMSnd}*tk7oMSnTŋF۷6tuu5 ^^^}*ԥr HH*/(& L2WOү.f ALސtQC4Lgm|PSxPxIiLN擪{0~mO l'ӪUFV݉fN1[&Gfz9r(v+)|$;=Q_q'?kw'vc`mԶJhT @u}md ֬=@Νٸq#O?TPSUƪU.]%DQY닕vvvѱcGf2nqDɘzs΂GG}dTEFF #}DrQN,,,矍>SVX=SUn$dҥ4lh(.WyB-? ]F*š޾>)\/͛7 ?B}d#kHmNhиgʢzRe[H\ʋyfKk5jnQp~&wݤ?oKjjc H]u_cC꤂sJ=TY^] 92;[>O2_Ћ Vy)*&c)='SA ɌV""7淣(Az_Gf@dߠ3w=h$}5T}jUA&߿[ʊ)uRx@dv. P\ћS7NEU`c(ٔG`yoSHNΦe9ȶ,3 ƃp WM"Qѷ[ךQ!ʐ$3Zض(#?c ǃYϻX{a(jH2踨I[=,:OYf<7w5*r6#wũs|!ZEzBZ EerO@5]c,]T0@y&w5-ѼysxwXt)~zy~ },^?p@n*dddw^Ν;'QF}v|@ 5ҝJѳgOxC>|A ռyL' 6`999hZb$o+O([wMJJ6O'XGn dZ-+Jr§.^;-p M`STw9Qb~l3 .˸AP\:1RK[9 M'| gitEo=Reiu@Iu)lZ «3^U5! ZԍSضZ(uqˌ:o{L~# >9.GaS+\Gs(vS?nH:#vuڒ}!4nossEi5NI¹nXm[ȝ=+ i(EE$خ~'MD7^wf8Kz<mЪ`:uK^|EoٳgŅ͛s ^dȆ Xn.\ 44KRV^͈#ؾ};)))ӡC맷Κ5l3={$:ss=G=رcQQQܾ};;;BCC۷/ ~ƍ㩧2qZh!\ ڵkr9rR޽rb VsiCS/_f˖-8qjժŰa %K0`~puum۶ 8PO[oo4fxxP`µϦ6mwEѿN8APP:tIU9<#ޠF*E;=)[>\8ǭx~w]SVBnfr6x.,fx>3@.ήXy3*u(N~d'g YCiա5FgD"5mSf&)p:lĶnZuk/Iz2vW}Q{9XVhV[e&ܽL(Aim }ʻ.~V)GjPZ2l\1c{AAAF +SNF6\.W^1Y1c0f̘jNxxxi:T8pss3#G4ֻwoJJJw[`@:!?4u+>/,ywv;SgI`ߨ/*w,vy\\y`߰+fB"3uݞP<8苫*6V0vucRcxkMu3oFvj׮e͏ hB7 ^=f7xݎ$#3>|S=a,݃GҪIy"D< ì_ݻ3vXw[o4\Z`"x 79{N6-/֡*=73=MO Nμx[%QPųz `7_{xgr8>ߨ/$m@8`׎%8R}xzߨZUji{)]xFGTGB\¥jϺ]pF:)ulluD7c~irȌ ܒW?KIJ䖸vM%lmK/nhu xv&x^"D<իqqq[T*i֬|b$@*mZQTBҪ(/P2+5N^ncVVcħ:K);*D艅{0VuMzYUEEJӯ.CR"s5>O]KÔe& ["w&ѽ}QO/3n19d&3Zê++uHTiJtmy& =]:^)+4 i5J2'N55Fq8*?؅vWF$ ZDà pi?\3E n5b:&UQwev2 6Xz3x{'tVw<Ì[nRJ㉄___֭{_VbxxxQ*"cTxPz\_!B?T ųD!B"KM B?*o"B7oԩSFCjJ"T>;m„ Fc!BԩSF?"D!BT!B"D<?ix}{JH({J"DR%B=L5Ru!!BĿ Q'B"D!*"D!BO"D#(((`ڴizז/_wbb" ,KvZy^ÇSN-Zwoܼys ӳfǎ͝;TqѺukoVɓ)))ԭ[W{͚5-ɘ8q޵(KZZM4a׸{=nݺT*eժUzǽ[?B+?SL͛FaǎDEEqBBBxgh֬2\pŋiӦș"DxqQ-nz$pmۦfiLԩSZLmڴ1߸qc:uoԨ^SNջ5=zTeЩ*Ǐ2:uB?УGvҥKٵkߧ^z۷׏MJ"A(!M(&Χ[ӲC $H$3%x6Fd_cUcn޼IҚv_я4)L4~`wb"eNj{Vd5 pZ#ʑZ"F 9??AQpvw< ýKt{dJq(@enŷFE5;O(?Cnf|(* d2É֖~rJ\\\VVVk׎~ Ftt4tڵJR/鸻3yd|Mr^n _(ﴴ};vdӦM\vl\\\Rս{w&**RR)OD"Ɔ &ohtRu___f͚ŴiӘ9H$ .퍑[ɈX֙WmT1+8;2~=/nֺN2ZX9! iHmng^@Sr˜uY)qAIL91E(NJML?#e]^Îgm8u\m6X#8 FI-+:kLP+8T"$$Т*-[MV< 2KyhJS_WRVV3a#[gq_4q#޿HhYakm:'jdRSS/DGbe*m>ȤN3>VPe}11q7ԨXD  : \K y4G-^uEIphևһ?:iӉt$i裊] ĤT0f.~7v͚5kbdiذ!#G$--B^}UZꁔ=99XTu(!ʢ[nl2Κ5kXx2~:~@de2;wCXYքÇSõk޽;YYYH$>sUH4kC 9x4VcorɜH;xzwRoIJCRo+-Gk_AXҔ63hGK:т`C)n>ܑ4}]0~H'"o [*W`⳸+ml#E]&˵pwՒg+}}'owgJth$%,] EH"F$֡v,iy{#hԨ߮]YAD//29`4Oklu (v1؉:dKhh@'f劕1̤Ӝ1yc28Q>G gljcؔ(=^(TaDgӘ+RK!O}ېV\92;K~6<ʥ=|70F;v,:dff 'RIff͛|2ׯkkk>,,,8v}u_LB???K\\3fOOOZQQQ8;; eDUnk]6h777@XNڴi#666tTӠpLL ݺu$nk׮o߾~df%-pCy)ޠ (;NzR`cg}*),!;#ZDo1LOEzVbXcKxG Pve=BM|1C_6pJvR/*qU:UŋXfgń6G9%^)/ʢbgI4 &xl?Z{o9W{9Ypdy(CYl^wy׏mW!ɯbl0G I{̍,y.SVLq37oiѝlY8ŋL.єaU7!ww؇nW( (JT=^HvEE}!YGn,@Z={V%zC0?{,|&gccnZeTV_._\PEsQnDJZ5wΐH$ĉ e/忣8z [C׮]IKKC"fF-GKR%.d|RN;SSu60gBZ- Øy#JK񺏵*n"?} uf5ۅ c"{2SXzU;SVǏe2) H1] xIYWKptյ"lBn#mU\)>ZTICK c}s_NJEt4Qf'PBf9qQ9}&J~4XoO1ckݺLǏQF_???\]]ٱc/3g :HApBHUMB'''!̙3ٸq#̝mmmyg]P?4VXYtnW/<++uffrӦMf͚'!Tܸs"WnSa6lؐQCNLd3_ -4-A%m;yXh醓xjKTPuisJ?J./`56BXeg($-QQG =uOKDtG^yN^-t;?ѾY*>}!]hQ ZdKHOOdȑ}Vl?ZRsʪfKk=0y|<6r&ӏW"FLǷE c\4Ih,meO::v~{%;>K˹s? 7>C_uи'ؼba mY i1XиQ)ېV{%n-KR/~sQ_jCd%8VR9:: Yh^?~<7n $$ J%Ç穧tDL:FeN0JΈ#ذa;vEd,Hi&^y֬YC||<3fr={6:UW͛땷s΂֟|I~G7@ӦMiii101xDR(4M+4mlȵ(;5R%VSy˩*@ h"W.IZu9TR~qv`3=`d(J(P@նz) ~>:[%|ވ%y6^U#㺯ߟxk dxPL^|٬¶TȭZu㌭Q>ٹ#(I*FZ>_EFO޹C<; itSI"Jdݩ0tнOD7<9o;Ly,?ڲeՒ),]W<Cw>e. +2 1:4b$Ƥ.x /nN/_YB6oOD2S_ xdܼyra'~Ĺ]qdĠn^>|Bql̂|f6nc쨿84덺$ԍLH$XȻe˖SRRVGGG 7{{{̙;Ȏ;*X ܯf]=gnMfϞM=*_-[`6gԨQZFL&ߟے-tɝeCUڦK.l۶ݞkR8pK{/'hۺw=.%>~;&0e|‘Dg;_1l|BFROg}25#N58s3Y\L]Sa}'*˵1‡Xr%97a ]2g6.H9]>y\1-c "L@؄7lyŮڧPsg}"P1t?8/fZ ><ןڗz'sj@ŏߕ2zJ&W.YMxF&)O͇@z'^|35 Crle@6N|:I|R%\JA&2B`Қd%0ۇR~##NRWQfuI+l顢M91.ݺ7$Th&Tl?WP䰑:itP;QvwZVX\4Ɵ?PKyчsѨ(8|guZ,ϩ$eYs$VJwiӶV"Ty.NVbH29*EV<Թ.ߑO ah?LΚd2&O>Q%%* uAFZAP} ;}~?Uo#9lblӃ)U^ۂ7QQQ%TNbĈt~;,vޔP]zP駟&((V*kH%dG"BdiAnS1k: ϢqC5#͑2|}uxkڠTPDNWVF2%hcw`sY]gs~UBoIAXgѸf̘ =KЙlN=#z mEsm=x ;e㍥0v xrUj®$hƃPN=k{` /M8m6/;/Eo~"KJF'4էS WzN@`stOdL&ښmW8/m|+" 洓uAľ# r)޷̭^{ԚvÙ\\5W!;;͛_e6ovߋ*JSSNC 5Ty d\ʢo?(Ͷ'8C@140iE +xӣ"Ke\ 0;iHt̕ױU-J^s>؏ذ]c]>ca0K&R*qBTYt):uB{;AEuLW,U6LAF J ȳ?bp:AooJ<(f%\Rd(F'Q^߫k+s<Q!P梵+v,-t`.+9N}GX0:H{w¤3d>G<$ oG<48=(ΝDwΨ8.8j&Z5 mF5bǎ}vET:]淨>v/7f;Ԙxc(ܹԹ~*!}&AD//~$jµ%Z6~XE~?,*#M^Ne0k\}$z 8"#M[6OӤr[#bCgdf]Iz'nxq*!Uq< <"@QQ+i%wfO? _j`xO$je.A)j"|0q,YV':oq,Á\@_`2¢T٭RtK%(3ZDEc]ۏ(NKse¿A;qX5BT=X1Op?{u1boh,E3ڒ.HrLBwf0]OyZM;V Evw2u;T8:r/L eJ qqfh(ӄ WJ.O낶}GuA:11w͚5+-BlpW*ًd%{V_d&d "W l!BqLu3F_'VdpaT}؀7S`YPOCit%*D˶Ci\BE#w0$\x|`o&T^B18?:))nT])Sq3ϥR36. $2ܛ>g$e%"|uD7(>߸?ylfȮ(a+vBXx- <-5rC0 GRE?N2ʀJ1r֌#y=@OڼʻLFkԕW`|opg.kWNT5-ެV.\*TD]I(s4 )(ȡYfҲ.j<Jw4s;| 'r꩚Y3Ip:rw8:BwQ2McJ9ʥm|ܠ7RD>!D7 "XY<׽d/?`YK0in؆1RnA#v.$!Xyʥn'v']߇JT6iZ.`K:t-x GP4`+:BR9]k3DG佷V^w_c]ReKsQ%Io?%n 'v±d/y) 5񿎶(r-?Reڵ\>[H wx9fA=_F'bb;g Vb֫qa%;xވT:S=?/oH@0nr J%b% /X|K)>? }hٲ6&~I45"Ty;^k+F ԉ HR.;v`̘1m1MĮ5ubO1%Xֹ%!*aQ?k%QEN,U{Otfv>`]x28mkud-_RhsOnOxwFξ4-OpD!^8 @O=78mDזQDifP(]Ϋ=Ukw_0z[X^ybbb~dւT ?ѪtM{rSٺxqA2 ZVUXP I͚/08yʩAuɛ62'Y]bFJIN)M3ϟL z`& JZQ@m_<!Rj9~DQF[tR#?"z{**֬[Lqt֍}яORZ'zB$t:>>>9c¼#q }JDQj=+Y9E֬Ҏ^ʿ852{s߽}^-6U]zD"Ԥꫯ2䚌6A (|SaI$CiXK-(*Yl=RB_$ɥO$A9NAS# ,܋{oooN'NÅ=H݉8~T*\-i=$+Zd 4{~voGNG{+CǠ:A”|EU<>؇M܈"}PKΰ '{DiѲ#^AϞ=m360LzKɢ "FLz%M*`Z$ATTUBH՝C-&ـJ)'2Zr9h4x gO3.&ST ]F2F||A5w#aZBvrlNMEj9p;d"\<]B\-/< &$G)e,r'jo|"(8C#]@~.g^Ap=?H֩#Zxa{@Bbw_Zr])e.bѢ\!P ?mk/ʫ},X8E_|R-.<9e/g#т0e`LH^*TT |ulKJF>yZqWTȵ1$U gy 3T;eiJRMJ9v7sc<R@؋]$"+%JǭEzO @!4>>>j$Ih4kK 7ZlL* ΐ"""'22P|ar^O%j$0ld2N:EFr&/{簔 %r6W_69swE} M5&;{*WiZMhp~6ph{TF1931H_+OP  (Tjvo62=JfDpݮn+{ePf5&ZhԩS֥X+@]vɒ%K8q^ܧs|S߽g4SO ]dJCaȔVȈK?/tICwO԰cSkZQ<`T5jao?N&M8}U[R.\x0۰KAt"&G=md籷j=1> IDAT~ʶ`DyO^r  D)y+15x<'Ւd#ky^twN@ўhkɫP*T;6x,&4~SϥV./#"~uD׫# Ϝ9իW3rHQA81-INӈ#z{I.?C-OY Ts7o~߈:$IzD*JOO'12s 0[+s$Y|9AAA;bolIs]hYL&BIKϕncFόZGp:6.rWhVawلꤠs]a9߄ܹ ] Ik?Ii?LR'2cY(/tIpV1]ג2pmLͷ̉x={VLe9YN}0,vF?EX-_L/t[*Kth*,> \g;DEEhD wei==0$D0"58C"7l3U[y n]dk(H*theTQ<"/!e@װksbSO='U<裘L& vǤjٲe <\תuKR3<|W+V0h W^Tm޼Gy亿ݻ7V"U?|ɕ%9ݤ+6֡Ïx3:^6s)5ݴ'G$95 {DNg6:?$[ %ػGWa״|w)!VEMҴiS޶]ve֮]KFF&'NШQ#Ǝٳtvm$L)o+%eLl+d 홍NZ-CD·S=ZFC('NHxDCp")r)z<Gk?r 0CVСC|2˖-c̟?J;yd,8#b{\ >˰%w忍LCPP<] N d7%_,~Δ̗ԃV,UNٳgiڴ=atmuATpG؍Kav'1{K+0H)q,D*KFJXu*EUB[}{z horr2;wd2oܸ{?ylܸMocǎ5kHՑ#G׷<ՎBr+TQ":OEDO齄U$I\t \ҥK?~<7<ۀ(KLFƁ.Cɕ+AKIjZ \*7">e?aAycЭ[7uƬYشiرcL4͛73|>SEz6>y~ f!Yгo6$\flaXћ%  @Ć CY&UF]hsRfa4[1,fV;`w?Q#SDրR-!N)cԩe076mmB6jҾ!M7<0ݜyG>Vv| /a)1n2u(Z32,&H$fg}1(xyxӧiڴ)5&QG1S,R)#}N`Jj)Q[ٝiKVEVJe|ӽ*?#GFTះQFq…}; SLa;}bb"#G#wELV1MU?V2Kn۔ j<9OZ`j4v8yu3dffU ,wA!W:{^*)"ʀ(<ԃLRCar%!}䰑dzY~ptg)4oK,rX0-󼳔 zu 67ڤu{~E 2N':vg}ڵk(4#-GT;CgE$le>|'t,]=u@Yd6A#BKM yfM&?@䠥x7~d<ښFEqoAJJ >Y[YbK,aΝkB` ;;wC&PThUUJ( H#zi ~weI$%%qeN9͉ݧ{.OwExBbw!Ad@m'6*RoĘSRolr͍-1z;VAd|+|J uw*d^ .(\Évo1cVܤ*556zC`κ@^1S8k73rPri2j5TQ፭A &q ߤq1ppDEfz+gҤI̘1_M[oK/޹5~L>I믿G}СCi5vnݺp%FMak<~86l_fl6|uV;CWӦMfݰ,ǏӮ];rssi֬ƍem#GW޿T}9HթKnRCІsUv Hg+!7}.RլY3bΗԼ(@o~]0ϡ JDpRDRx'T=G- 4JxNmHIx;м*6!eNBwP\0~-GYv8A͕ߟcIJJ"''2֭[bq:uPfMéQc8 . SŗGEBNtSRVdkzrQF˥a&N'PRgc0H-4Ql%FY>[,@V8VA?~jUʉAI 'yz;YFew(DE!O\;rw֎TcI\0#rQ`\rMc&LlCn3cB̙MֲEQ u\FoL3 :VCToϮ۲˗+_vH}__S/APKh(ԍ*#Wi7Or0@:}Ō/BHeLY| I%g@<<<@arq]B@Їk0H? ?_|EUJ7"$$>&֭[˯%7ߠh֬Çd2jYh3n8._=9ٵ$wyΝ;sAFAii) b߾}\͛_;!U;b1E~rH=!#xi G^.xҗ}e1x]4쁯Gԭ[yj5EtŘk~ ʀ(78!yw:L,';_J|&)#ij'6|~ܔX%ux]rCu.D]+|ٲeiB߾}9t.b{lݺ3i$hV+ذjAn5B |~^@^s37ӲeK&Lѣ9{,Çg޼y+lݺQYx1ݺu˃-m2c ij.).)!d[Of#]_Hj"¯ÏFF`M[=bM 9"сq,d(PTTʕ+駟lddd0{l n!Ms AY"XEr8NHӟWDAHzs92 gȯXlB/V "6P%/L46"aɹ1 C]"~SE$ y>ݧyquk֬˫<~ˮ!d7Fk/:ìasAԨc[(N4V|MxY)rћmT,(T [wՙ3gG\ZJWf̟?:Tu_VhoaȐ!x zo*ٽd2ryQ+e]x…[i ( V>׻TaDˍapb+zm_`"SnLj/"KgxҼΓC"ݺu`4)rEA| *{< p`E尿nQ}>_ {;iNʗȣz+W(|0 wȑlݺm۶1|x1|&>َ4fb*%6,L-!߃L3frĒiEL襤UƵQ2?&?%e LJo)#&m4f9DAmn^zգf|jԈF33ggϞ fqj9{CP`8 >q^ o>Ҏ()*U_ RN@EqQy&R[a T3a&zg m1>|ڶMw518Б{=t{P ((J(M&' فDnrPxeq61@IaI46:]ʙ, t܀Vzn -/Ԯ] 6@VVÆ \s ^%-TlذSNp8=zt}nؿw jN;6'HH+A P jId^)e/~@ aML8˗/C-AbM,.H5jӘ\:$-5F M$M@ֹX5[Dpxĵ 8.G^~lɒ%4hЀIj]k~恩 `[_U$m-"^{~%ܷȊGFU䧤OAMݱ+3mW;>>2L;9vr3IHM(ѻ?h6l]vm6mn4Y%w#b鸕jn xM Nr_K KI;I^jC@?[?Cl32idA%b52d XD#цMo~Yd᩵k ]OQ=B lػ΀ &Qo^S5o}`ƨK4jԐ;w]8VUӗ{KHAJ"%!*DM-bW0e"6 ZDoN.SŚ=V2e zڵk3tlvx IO>$gرc5޽{WraȐ!,^@ǎiժΝ#//LƇ~JV6 GbOc#Vt +wNqحj~ent|bccxŧQTRBL2|p^y)=a^`P[bܰpūaZ{/T Bb9("((( zTPDP"E@@B !=Iv幮v=e͚y)6J=MW87+[,d#sv"&*ēky#*.ȧK`L9Ms9HN{'mBy2Iq] 7x}oG2x0_}aö "v!3mڴakTTTpQ($:W5Fr {gv0svTނ!%rzQ2#T2H@r5";&j/=)_-[UKOlSh.h8ycs|iMC>&)%G*sT RHIn \R@jX` TSBaO71&4^&__~2zq*bccy衇~h4zj :/[Cb(**(%ڂrWd\\x$+&`aYO Q8r6& zCqiL $x{mJsGۼNܵȴfDn ?#olݺŋ3n8m=DD7IiٺqωI48"NZ^EqQ}?r`v Ji2vm;p9ڶmKZZ8qcǎqq?ޠ8eJ~D눉%MciZ BVS9}jj8P k]::]Gބ.s{J@dz R絛K o*nUN4Nn EvD5i_+cOЦ)߆wF-UyGjI)-&Ԃ&ff߲iU5xwO2wVSQT/ .Sk=W!h"L/~v7H~_R9Ou ._11BQQQ^ʊ۷/PvZv;JeCΝ;q:h4 3oE/n}s۷/)))8qLL&LY#p{'N@cH&r[U¥K !./෋˗ж[*/畊R&o7IzP׋_a T_WđMȁ"v'ފ>?%Pqw xyoĕJi D?_cxy;f fժDDDP]]ӧٱcq|~=f h* - oӷ* QwԲkS L^v- mM6$&v⡇CHE[w#'SRT@"9WGJa# 5Gģhh44i@1Fu>,XWEH;eg*y-> xgaʳ"JfR`R`QPږFss:#Ti(+褸\.N>ѤC$>&v`̷QH;g,HJJA[ZYbSX?R͛_o^]k[͚5zW>::/m;NT*ԩ:]>gأH{-qo\)Dbh P D槠p9fI>F^z&ŹG9ѽYJ yw K!%w sϕcNvT*3""Uj >c)ݺ>yo {Øx+eL\"! {sD>U 2'_[aðl=Gr-JT?poG"Z(YE\IYVg㦭,7R]ଝ3Gٹ'{EC@VFbhץ *tͣZRI_!>[正Hօ%Ag ·"c4Y,\ҡǗ UD+vy؈oF kpO4RٜcŜ]W5ZHt:l m93;WK%$FOV RWVgkЖyjVPg,R(jDЄ&"U2uXk,7ch7m#9CHbP%m!YdI}`J"H.kh G̨:NgY.1&v-UR-7rd@+9l;P8jwΧjv.rb37=&S~,=a9Y?48'"IY>SFEYY~;ci,*P Xה]PwIM#yЄ&Ru=8z(m۶N*'U8>RvDJ;n'eߑW*@@sy]탱Z,Uo)CniԱoOe/-Bn YDDnıxKȧlk)g[ay+&bAjSu1UGsaJ.q토$h";k2-ch;]xꩧXp!ǏgѬ^???f~}[[FXFE=5Dϒ٥&Uu.un5nd.M@"z&a VJQZ -z^QD"S#C]D<ىNCd"*% )vQYwt?NR BJn8`9 u0 C~3NmȾT*f&=]veȠ!Ez'9e'+yK KAkd7 1j+vxA .p"Tө\K Ӆ\CΈR!% DA`zٴ^ ):[l"\Kvw ڶEuu Ņօ)FuK웼bB̹6`((RZ(mnФs{Z,Q^HҥKl~XjM_5k8xwyNE~ϟ'22s~LШ9RCjر!)Χ3alxnJYTc\'x(Ǽ=ԽՁf.G+6)#hOgYf v۷ogǎ$$$/G+:IN8Aaa!w6kٸC$0UhENxG0$hk_ؘ؛%9ɀ)$0rR$'Ixׅ%k$C/klkS2] r2pA+MRkkZNΧ99[3L!66 wprp҅|YVd_p|bZҎ`=1r^Ndr_Gf2v(ȆЎȎ(Gau{D<5R6,AKdZBN7KWkoʳ!uzS#,FOiKV֍9O_X=!2dZ/)(-˴Z~6EvEJXhdOnSӉᨗǑ*DBٺu+ӦO[I|0jvlzf5/2Ͽ7w'NԢ%T Lf7k~vp N(݇]ܫU ];)Z,%.Q|a/C6mbʔ),["&LOA9r Jz lhL.nB&=M>xݼ7b=< }3bݬ}T➳=dq`}>;@9_HUډ ~:%VXNq*zSSǁ=VJ ]B7ÉCVNq^wy`ԗEЕ:0\`G[@*xS+p-5~kZ9V|jPW(i#eXU'$$ԿONNKGeƌ h۶-7n$,,OױcHLLLMMeܸqY&%%ٳw^:vq;X.\Ȍ3Zoi`"Pvzb\V%mr@[/ueuO J 8uq#W@3S_csǾoHOdZovLlG-MwmR%8W-LFES!e2֓4zIw?>,^ADV$ 5aQ⇏'G;íʴiHNN׳~لFO-߁6} "2Caa!Kڹ3qQ EH$A k OF@ @ 2BNnp渝?+ 7 -hhK츕RjԔБӧ%kǒ 矚9^R"U<Ѓ dڴi OUvj~`,8cj1Hżl5j^f9#TN~,'K෇a~*zVe eeFƒqJ*5ĎfeDĚ m ?lV;QY\<1A3/0nj*Znn={ 5!j*Jvѳwtm"N̉*NNIVOR\ 8 8"Z;8*E MhR>Jě(P{t omXzrٳg_SCtwbuͻr˩tܙ{m۶]ՎD~ _.q DU,K<>,??)~ J=scmD=ec(.?ǜ4sQh[t/l/L##_` 0]vvZKFFU͝UTTB?k7|M7fF5Vnjg">iɩr+Ne䧗fP#փ6+aUL!ůjv$p&rDąhDS/k7ʑDUGh[8sڵ"+h[8䈤(|Yi3 f D 7xYl".+?蹤n/w} kk[Ģ'Q(DGhn׺*%F!*Rt2U 3R\!CPʡօgq esJjXrsIqI9EBxp8nwR*Qkh4jZ- sD{`sQWN dR M|mرcwKЄ8Lp or7uwǁؔyӝDGG3cƌko޷& C>:t={ݻuF`RvuL7* 1]mCgQ]Q[{@ MsI"h+J{oSmdzLni~D=Md_) PysQT"PVIAA lWYy9e$_dMLhEEhDrrrHOOܹYT+69i6ƋYhEz+Eggw?6"]L W IDATzO+ er;jSvQ{Cj :r%,P&45u8Jsp(U~ Wa5e_^po88ԡh&6zMaLG>nȍ~6QZZJΝ?~<߿Q? 99>}PRRΝ;WCJJ }o߾Z"4{+_Kj*+p5[뮺ks9FS`J[iZwݻwO\)w?=몤yȟ:phG0QvwߦսX%p{GQUG"pK/2KU~!ޣ6mUQ}+*bJ9'@"EtX5ܶ{ͥ*ײ8iMt(>DiF? (CQx"7P^c'#3+ .\d4ذaCD\t8-3j((((DyNfq\{Qyfrڪ\m"$ u^ $8 rlz9 ^۷ə MR+[$; CTDB߅Ty!6:ʡfFS5 7?~vR1qD,K F{ ?u'SB.j@Ց>gťDuH+oMTg$f_0Hb sg= hNiRlyqV` ^7 OJpǴçxTqHd+)#=c;0wMck>>>SRRӧ^^^!C0x`}Fn3n86mZEG*\kI,5+JKTRn6Xxq#F߷T `Sͅ x'i۶-7RDcQѠÖ쫦߶~8kEJԠmԎB#ӳ Xwa9wEϟSu|LF)Kb}\oq M2<lԥHĔHzdJRJTv"( kpՔ)Yv6T lIhH<Q:$_$uV233IMM%%%9zE%9deۍ0)6Z3qtZT8uθX^%-x$7W!!J WBRDW+g{(4R&% $E>Jy6j)mp+#~z<`8m˚29EYU9C'^afyz ǒ?ş"&D䝷!,$"* vjwRu+b0wc[Cn=[\ƒ=~w-(0h: k/w]wSˁPo]\*P(<s=<3L:?[XՄ#T֭m۶>틗}1_-Z0i$Ak5j[ne̘1MJ8!I*.Gzzfof|F{B 8U%߿E7 l 4 &0~xN:hl$-t"cJ`Hծ]9kg]3R2-h o5c91p! >!7,)fEtٯ>-mmK,I}1rMq,xW(8qV,ϻb= IB9$!eh[ߍ? NrrruP sXAs| |)NΔX9{D϶ok9\ J:ao2-9>LQw_TxD@$^|.:#[EŰBFW'յjyPno2j{lB)qMH.ҵS7LQ}t=cp[=b2޽{Zf>SZLI*n n7ōd;JfyڌfF6ڨNx~Mx;jSRr*UG#7b_I/$TCK}C `8N&O(LOĕcr Dž l)S%c5rNl)QDOV<0Մ[`w9z(߿bYvu;N:i}55ɻ:,*p5Z{)a ;JFی2j;jٻKmgJTM\WnZ թ'J=N1-m؞\,=r>/<>2)&Bc8<9"xȃ9_fعt,YҲeKΞ=7|Ò%K !Acf׎ Xe6yx<2=Jpד'[ilFpZQ&iuD"^=GTyrB^Q)7w+N/XT;MU/W0e `ƍM@*2aF /ܹsYb:ÇKT՟`hP3f ӧOaBkb%"SL'XMT{gSmfP| 쳽O`э'Tx%l }l/nvu[oŤIhӦ }{G4 Ԡ>ũ*FĪU3hػw/""8FBۼ.XNl6u^AvQxOnDA Q3l"޽wn/U`KT-1v58K`;-42R~IXn D݅ͻ2|L4H)`Æ \ӧO#"۶mc۶m̜9*}OsOyy GDBR;OɲZJĆOT:A9sjsh):*ub(`xT_NThIkvpt/p{\zcKW3Z%muuu}SZn8$e3%W En:ڽԎ@ҳ׺%O ֊؍ͰjZR뛈.RUXEr*+Sj^nťmf7||ɓ'3rHǛoIVB뮻裏Xh-VSL'XMT xE̲e˘8q"rmExn | (kk9 4S(nJiPKgK)D&!y$q(ƥm\_Q/tW?Kx!QutC`("R Hfd#^ "555"GVuGh:qX,, %%%3zh򨨨 '':K5NpJuJADQV)ˤ(d2r"^Va=Yz ܹ/>fҤI̞=@sBէO|}}SXNb޽MT9_{ ŽķxnD5 =Ob喝Բh"oʿ`r?|W$ *[n%66Ao}jVm?quVKEUk:Ͽ7Yȓ<ϲFY>Md.ioLFaղ2#b7DǼA $O#z.Z_ nDGR{ VNAע텾@jPF Uj;wm%-QQze. crOCG؅`C|iv˖-gܸq<;u"10zEdVIc 7V#RH@بb)j%ǼW^IHd6' 1b(Bkq&وHZcVL$O֌J 8y?P6쫊^W}V0tZ: F V}qQa(1TrL%FрIhңSʑ&J#EU^D*Wޣ ׶J5!C+K/o2nܸ&ߜP}V*ov>}:2u55R%AJZJa+1}(_.pzO*[nT:UL`=.{G{/,X,Cxxx>O% {+\&.*AB+:zFK.uhug @-X"k=FtѪs  _gvr~mV8< WU;ys1vR{f7>8b_K#c^_{g|( nIfmh> fm>iu>DB"A/s돫bi腏wپc;n>OV|ɬǗt.Gg!YV5F!Q@I iiYQV69঵ˉYV\JJ.@%qOo T pUbț@/:2ֈ?W]Ty]z,=YENۅr!]R`!!CnwmF;ws-ߘPk׮CN'd2̙DIe;E$rL:;#\~#8@qI0^=q HD"oaРAݛݻw_X 8FI2~xz!T}X|!,bgPaG7EG7n2E|q'SGW.9IDN߂&"Iܛ9yO˗qXJwCO|n bzvC;IQ*Fo^tn%f:lgu\()c᲏pZF+b1^R?_G"Htz:A\%N)FaXh/2*KD\BuzBm۶5YIVlY~n۰@*0ڴZv`0e DRR_U>qЯGg83B)67_R9s>OSԆ7=n\E/)de#^DLYUj:9IՉ/nر ~ZVD 2Bȸe`*$|LRƪ):voτ׫_A"¹YqוQxPy`wĜt/AxTNz FVI1('+L+ZJf*h_k_?Lɢwywy~qWs{WM.B.CuY_[rICɖŸ* >I}N }#^t:۵!M+|%jS_)gc6UN]aCU%XK,ϥbJfYYT >'bʂ oز!Q(ĵk\f~ bW1e}HBE6mԩS䡇bܹMASBIII,[/#TM1THU(QpI :ٳgc2X`2S |L뗉ް``7=zb 8=z8/=<ĬJCIՅ LnS_mn,ZD&L!(l*Y"ЍȎD>ԩQD?C=]N|h]/MӷxQtaA'CPfͺա|j+ S@79IPuNKN Q OxgK!td1qI*_ :e4GqWzIYtJ`Ν ?} l6Sr_#=2"JOU}-3J2NRCQXX$Vm6tlKD.d2ONBƆUKM%qPE <`B"=5*|AG!(TH*!o]ʔ7mH$L>_|558( "Ä PP(p sn Oӿa4QMŃ4L–-[޽;k׮}M 7%T<,_Ai&BDM 'MMM K.ˢZz_6u/؞ Fܹs}:-*e2ZbL`A,l:ND\}C Bp+{?=EWp0UUOeuyͱ,a u}W>{2 .ӈ1Bm6_Os) Zsart7ѣk?lÃ< 'QPP˗/s>I'td+;xUn_nA%D&#Gj:YJ ?` ՑjbH~V*B =&LI̭`cu&w<=IE% l&nEL=5@WSRH 0veJ1)rJ@"\ IDAT}ȇED $$Q%cҥذe T;_N {V*R# ct8̞]{hذ!cǎ{ `Ua+\M"KUf@θqP(,]5kҥKcڵ4jԈ‹ vRѣGl޼;]Nqz5+|UKhDO{PkY41kP](fWoNQp [kr0/[t ?@yVF4|9qG#kی"]>;cdG&w0@:N#{O<S'd~ +;ЖjlQƲ l> or<:4h1 *Lӑ^/y [h2TcAqWIؽ۫tY_DU/SHu)0uh#$GFʤ}=We-[|n6ѷ[!VUHj",5 \)Sq{K,a% -b [L=@ }1:eUi2" " 0S /'O<̅0^mb:Vfĉ\x.]?U0M6o\M0x*BTƒǁ c̘1TRClq)ar,X֬Yرc7nO>̜9!%۽G5暤 / wz'5n. S*:<"$ Yګ(.ҡP )PJG'Жl+ӱ6jRT,y=RmPY7F 0\ܹs+̟CuhwdD2vM\\Çgt҅F#G/$UJ}YGKY,C (!k/uq<z鿯#/,aFkbĝ$nXLy汿Y7eˮg}Zpb4u~;>b[P߇o 9Gux|EQVXLur?m0Q9E*j= mnZ6'mv"h2JXz! %״i&Fٳ'mڴw,14V0V1 9?Gw5l YIO R_ʢM6l޼''Dܽ{Z[9ڵF+\.U߇ qQ/dլY{O>݁E=nUh٠q,KXl$lC:@k7 "00wJJ se֬Yqn?Nrd(ʽKhS%mI2QHrk5Yx}{ퟐ~z5_7Z2|~ȵ`L0k_Uͩ:G}Pu Uwx nD@lӰȺEvב/j9)L i&91ӑhD krE鱈* ¯ +0rB֐¸f%v82f=?<@)|_yw6ظq#gڴitԉիWh>!!!<~7ƍçuor9C.u?΃s*\D|NLw1-$dcuf{|%i5zJ2>>L6!CT(PX c+CCZ@~~?xJIu4 0g С-MW,K$ڶm?ڵk5k v4ˋ0nܸ}EB݇q)Q4 R@$/6ZэxwfehЍr#l.c~yKu@o\@H]QyLҡ0ܸM*uc+%y7<5&X òŰt''!j&>vqr+ku XCجM=Hu&#)|tY$@:ɸSiKj`mvfP@=<CQF#lz2:wwwm^HLL,]Ǘ˚rS/pb%U}k`i#2^j!9*^VڒWM7Y:'WՋ sUMVd]BfJy}GH h5ngѓcNzWW*T Uamm]{ sef:dǓܾ}ŋcƌÜ9s8q"ƍʪ|`A[ +<IľX3<IeǵCm0h6XaWsv L8QF1uTN5fCW$[[T5tBrj:Kwә?堹 N:t\Jb;iGH?mӉZqˠᄍ\@' 8廥.8AO?RVCB "dDOl6#{ N`jurp9ΓI?+abV-"պ]lǜoyv%IEE]P/$NcV!dS ɐ8L`{vEѳgO6l ѣtX~RO$sma1WfX3$ձ`bJg"'٬zTEҠ""u~@fdV7,ۇyn$GA"EU=:bӧOGxG~knj3ި"=4/x+m͓'Oj>~?]3Hyyyƾ6z*`,!?I㹠RMh"ŽyfΝYnsۛ>}¸shђE+*@W BD[z!/% b???֬Y=ߒIK9HݺuYJ&?dуAvyG !Q/@ʏgT=eXwOKbV $ ѷqkgtk 6 ؍RÃ#؎!$Etj[B 1ȼ̋ۈZU`?T}1un6[]*_2h ALTT0j'&|GXВb ;O7G/$AqV2LLl9f3WJ~\Ch;yOLA,ѽ{9Bd 4r0NhZt|i,PjEdL#C/D pcmmX^ݫTʄ&6Va03ihDdȨ!C/| \HHfff1j5ܺu]ؿ$V111z6+,,|mZXXȃJ6F)iGFYI1VK*CYTf .98(Mzs ܹ3ǏK.VR8 Kcf>U%Y kĖWHlp;wBBj YӞyaq ra]6m:+3gV>L'YCȇd"#s"uĮBĬ x62 j(dl`;;Ѭwq Df,e70o @\zJ-%.}d.DD[Aܳ=$3.Wa+ epcɼ%ɭ e"&|J!ǑK67GNATzb>&?֭Z"nCQ'#TX#@l뎉S- dS黍4moL{͢1iv1ʲdvC) kN9I,# pI wYns1UرcǼy&3go:իÇquu׏LÆ ={Ç1|i͚5cÆ ԪW x>> 7o.7E^Z`3gά:2&ْup:X^]tu'YSW9O^ÇY%֣8% s#D"!*=Erǁ{Ye?]I\Q{5Xa|jU~ MeNɹu+j5 lp1,yƽ2t #1A20qx RXan,ƢMYČ&s7 d4F_ئ.}Ym3¢(bV( 5>;CpL6w&&&-[m۶r) .'$ !B̐#K@NaM Nq~ܽBO󑛗']:ؗTo9yդS$VUj mTUUB;?OksWly6wڵk|uc ٻ}?G9~P32dJ2P>SC%΁3% Ҁ.dlYTI  *߿ߘ(.? luOi]+Bңgl삀ÈD"Zb6XW܋)NN է:p4J=2Vn/w!3$힍I6+M[o2ɏgh>Imߴ1 V+P߁9Xt`;t|T:D:]^W#G N"^2o$zy$v5@Z2wJ^ݒM%"ϬAT oj>f̘V0w/e111|wL_1&n<' rtB܇|rq. r~F[l:[&8G`]@LDr M,3\.*ӢHUP*ROMӴ$n0{a)ȲX{mM4aʔ)ˤTRc$O֡J,T%VDf͘B?QVI-,؂V#(Z|NaÆk֬oc<|hGO{p.` 2$K2ԣI1,Tv[#ٓ-ʝ|Eŵ u3ި岒JϲBΐs;?#n=RbUGȿjt~p˞"E_+NTR ,:K* U(z @a|(` IyؔG-TdJDrK̜ 4&* cGkECT& f-.АRٳg9~cjAyO!ˮ +Ux<-(:t(M3}|{p7LؾhH!Me¸Cgr'~,W-y9r E"VFRHâ[AQp=é8YRaMBb4DB)4żyOi";8^K/R'_h>-ɾwKrdl]fjFDD͛7gǎ"NPZyV͛?w Ba0*#{Cq!;kPnE4BLʔ $A$ IDAT3CcDbea_ : )WPMb8f9NR=d :LN.wbn.[".':,T UBHkC@(B 2)}M 17-݋LRۓ_'44e˖i&XK14@9 en|iI3zXĤ+!U'NӓI*O oXiZ'4SMð"`ϠS,:~O;=E:Oo@O8cD?s;aMƟ?Is7Lg{N*7j`V#H=;pv`Vbꅲui'sok zߙM+˹4>{pĭȻ1`l0[~^ "InfQÌjz\HrH(# \!Wb @=cUdž풊f_Gn&Ej"Fb*Ra8\@6xj~2e|!Z:${+0*#2ӌ\jBPE+/Ơ+.}i%qp[/5 Q!z0셙{s|!X`x]{qT6C?tcpԦ:NVuu7G[6 6ϛ,mZ=ΦXOҎ<>ߠ m:m< ~8ĜӦtR$&׉<&e@4ɏؗ־vXٗBn!YAN݉o]zpgcY>($-lғu'k6LDaV_jf ~ȅ .2k!m{Zд|lq݃\+VߵTݳ]*T UMPR 쨙6=;Gy{6jǿbSxyyUl56DI4> $ČK?BսtBBU@KU? .I׮]3OSZ > ^kL?B /Xn;6cՌ36S9v˗/ o|=dg9@ҕAq:z[E;w4#ăhMBOD/%G x[&x^]Z҃GpFRsͬbKj?=_|?8_l)Րuy;IgP_H;Štoߎ'lX}I՘uºX#X6US̝5fR?N drA'17xo5`$UЭ j ݍLꕮ <X{*QqUhHkýlQRj}C ,UOIVU U]K1W<ʤUoywqګSxTF8ZcG?_@-k׮ݻwiӦ bRK,PJnXJ׷~4iBj՞a E@/oӖݎ"Khrݙ#F{dOnɓ'Yyx1iaGKMʧ q|jRb[M29w'gV!Q K.a%+LQQoG?=I]Kَk*Ƨ[9x^;4)O֬`R o&u6>Fƹ_^=ߠŠ+[VG fB?r H ;" ;Ç~:>.6%RO,qM9y8 g(xr5}y>UF,nqCVzIfks. -yLe[Eb*}܋|k 4PgF*V"$=!ǺHV/!Nr:>pUZM׮]iݺ5.\V["XᘬӕQRRRh4U7 Um>78r3Ⲹ|zǏȸ+Q @~x2czY =E'4 )̙3ψbMF&M~0/۷}p;LhA'$v.0ofӂTo{vϪtgb塤SNjÄٶmWOė1/H`Dh*& M⎙$1p#'9cf|%NֶdGHԂΤ M?$Nqnr 1G~= "~  <߱-xyylY?dPCQgIn?j]1q!hlQn<_uH2qacu{|}֮ňhfo@lj 0&w'txo{%n"VґfS- KuL,g񒮞ѲS/8z('iii{ӦMiӦesKKKٲeK?qrrzXZ*Tx ~qFN"`-ąp I{>(-3K=210͛7 O>!??ٚ:5]F\\}y1HPWJ%HF*XaCѡh4?v!"Ji?]QifX ㄙz1$>"Γ`ic<]̒5c'-aˆ] Qu[0sþT1,A_T@Ү9&&9/ݗE@oQ`#td̙ܾy}khZΞ=bIЂW^Raq,Ľmڵ׷6kVܾ:bd~z OE~X=L2ٶmC aܸq,Z{vڲj zEWgu}+// )yR3|pH1Ûzv6L%e}o|GA.\`{I'ɪ&ۘz}"sLbcٿ7nܭƘ=AatyyH XnQ DFr/n )'o_s'=r-Բ N#֐}}/K:g0vyV%d7(v']| @,2/o= ǖkJ{O:ECq~c &dHM4 wåPάD{5ݍkkC&??[UEW*5QKm ѣ@[O"DVt2I-`$Emo]aΏmg崻 ܀xyv;آHp'BOg'!9Yf%\J "U/IMMGùy&u` VRʕ+,ZnݺLfΜG,g6vINNήƎwU~(*"Ҋ.lgg@ '^o QsA,--IOOG$S>KpA; "01~t҅LBtt4:ubС%0EW5j_5?=:^}֦I-YbT4CGkXc->0f8d]۶m G Ceh֬a>תSDv.YnJ] (Giudg3_1ʐ^]ر#ԩSڵk{;m ztɈ#OYt!?oۀ=za(z#uh$nHuhb]=g^ÇY~=={ޞ$2aÆ#z%<-֒'/$TŔ3 fݟRӤq褥B-Wݡ&UA$Ǘ>0b~ЪTUUa!Di%">'5}.p=<)u؇mdfi3ű[ j]>- U>zrlu'bX_#fG˗ԩS̤weֿ{ѱl߾իWf͚<\fIIIDdd$2  MrrrIQH-,)nݺLZ8Q\#3d;'jR읜>ݨt X`l&O'"(ʴ6L&f0 "T ԹNYt)XXXt[rr Cq1ŋmggg%>~vcAo)gjЙXu"VOiaӒukו͛͵_ ? gN],o>n7G"賚6LJ0*6ӱ`ycaկɻ߶IE"e}qVp@T.L hPAP Sf\=K^\qp6X);սٟl3^+̤]pttGGG,--Q*j QҫW/fϞ֭[accX,FѐFttt +:wLc057G;uFi׮rԑQ:rH"kKy Sŷ#ue}eZ!S7T[ۉ5Vk;P.>? Uz&"@?ҰHXB=yGӪˋƔ(+ 6,EH4I{?u֪,^HXYxc xs`iiLKLL 33,**ۙchu:]U"U _T2pr_Р\i?5< %J< 3C'Qu1`c6=fI|Dֵ=ĬAW*/}*$X&&&̘1MrLGdzڋj C9C1`#dm8T&r>יĪݟ2iBz!CQXHxY}Y6-,177'P&3Yn%MAҥ7kt"f ck{m'bjG!O^xb%ʱ%Q#XGn7;zHӤ%P?M@ZGe0|yo҃S@dk8sIߚ$L&\?Շ7^f IIIr-f̘ZfȐ!lذsѢy3N{U{Od&^!$@ @!Ҥ*""*]EH`RH!ws]̮k5k=-k܅ɿ 秓zNo1_r0OmV]*? #rRiڳD23d/=E#,IN7 O*4V/>%(x؉Z*ͨE$98- \5,B1a0W֘ tVNRg^ճnc֧Oϭ[[nt:֮]K^,t^jR?^db(PHXwYe` z&syz5^~=W-o#o2{ǹ|4i(y翣$ "VqjN5O__fժU3#F{n] զ^$c+Ztiʊ9"גX%"Ϭ'b@[G n r-]r.arpiQ/>wDރ9;0 I>~{Rp$akWCS DH&H!vx,F hب2s &g7; ۡqi@ߤ54h=W"dHLCPTǴعs'5",, CI>ٿ~ " V](:AM[QSDs՞cA/6zyuQL]",_P qY)A`I!K(4ƥsB3_` 9(r23ǨBfQۊD_/'5Œ w{ܪ>Nb/|5AsMd&Wx31117}qaz=aaaGݺuiРݻwǵ,a@WVu[Z$WR#VC:]Ha_tm+|1@,FsiKM\\\9qz;%%j&MVV`„ 1Lwׯ_g߾}̐!ChӦ ]v͍H=KHj \Ė\XlLxNګ#… KOMH G,ݨxkJ+D"ĖX l|_x 58 2O ʼnrTd( G֞'r-NgLxk,M\̽( ~Lڝ@ J^JTU5,o ?A7oC Ņ:i&\±cSի\~M2vXOζm۞H ח5jTJTTaaa5kh׮z*6ȨG$1{,ƎKhh(]v[n4lS֯}r]^R;1lp8W:J ,jwI/G$3P9;'x( F躰d=q_WsjWGEvEkɘAJ8?!;t&'k(;xu^,|06Gw{4 |F2q8u) " stb冣dee}vXC*cƍԪUӪU*cmmM-ʳ 1""WjcI7~UؓHeo`_,=kM昤2cB;0ϟ… z+رcѣNPF yט2e  o޼I۶maҤI_O?ѡCЋ Vټy3{faL4O?b.Rsw5˴JV?c==*BF'NdǎL6 KاAg,BfH@J61~6!< \RP"T`om=.0Ãdyޥ vraCEZByIc…Pݬa/7g  %v8 An}8 0[ua>LZyѱcG*Y^%_lj_m0$Gѵ[W:j} |2& +++d DZVPBw-^~bBJsrdY,jbJYE*3r-ipN닷z(#TF|W=˽ YTbkt$= eR  k,~AMFX'(J̤L>ӫ~4h zcQoJ7aHt: $&= `mm59E[qD΅8QPb#ShDA$XP/&p;]V i@g:c2h֬O%Tڵ###+V0m4tBDD={ŋXԮHyhb&VZѲUKR?8ǑQS(,,dРA <>o3OzNJzj#]H9D{z`888pi6m @DUq O(g3fd-0elg ۵z]nE@uwPXTn>k˸#ﺏ;~J(2q1E 9#F\M>~w3b0 b4/FeQ8Sd;Jj(ƍ`嚯i߾=˜fڄpx,ȁf3lPZ?TJP=%W3) YOvfb||8{{Yg]9z~~~h(e<ٻw/m-_dCigcz"lLjd83(*`&EEE0n`MLVٶ³>mcXRdœVWR;$zd=\-2QƷv3vE٠O!R4%cg"JIy˽&aOy׬=җJ1Mn!No,H,s͙dXG/7vr+ Q.s ^{f d bk4lؐ۷ӡ$iI:)d^^Y˿wkvM8:UBRnF'|r0aMꣶM@qHsW84~n>AL|E.z e?:{Υ餪4wh;i{!5=9v~Ll0 bz74Ʀsg,8%ɹ8QPٲ"W8z=;w vīQjTƏ_麂 `ccCÆ p...bt&ZIb}.n#&E-CBpss# FHF[}y 5QbM6nR-_c4T.޺uJBÆ ٳ[7,d˖-ixDq~Mxܫ7f84;}Ʒ׳ew7X>#3pc5႙(i>=X8 Wҧg_v0艒 W05#ϱ߀6'e'@ept/lB@Fa}Ի2"Ցp_ڋѮdǢ+EhcmIJ2( ._zhJ]>W /ýk \{ԩ=eīk}ux l M4g7:u 0gA@Wpŋb (PL`\X-7} ! iV,A;u8S<⸍/]ΡDrByks~Y*INJTj*k1* ȹi84O`g]I:OEFvkI5i$Z$=EOzg0hKO%3˟A1-\&+]OvB-Np;]'.ūQjTţσp7RaloƿFCv@' yAۉci}-[#ZadS}4A3{%鿬%-}{=q[nѶmۧXr%&Pq!n6bK~M'fC |OiN)55ỎDW#sIK@kÂ9ap-eTDL&N"))(Ξ=QT[g>|!fTfZ,R&A/ DC$=^5 Om<-70"Jr g^YN[, O"e,C1R~w9og}4.h(! {e[ppж j}Ћ5NJhՇ :e=؅:]Whu.RLES{0.{rpzY ^ F7D-?"9 7pRZٷKGaru{2^^vf \`Ča %9hNM#*b# _Ihr:Fsb@v˿UEd,XeZ48lq*CnЕ;a0l숕5XD׃#Ƅ{DтNIǩ=gX5}=Qok]GZĜ?:o aزmx)G9 X1q?o{6ضd\&. HMi۵d_ Tce#bn^ZA2FAoB,hޒF^o܊5u⊩4–%.z mBB'\>4+^`"+J_h?X k\y@ 'TR:h4t:RSSK"-- Z_fZZZoSVKZZs/+hLMMEm}0_Gth6V⊹,Ge,/tΉ'2|hѰZIkh]#93irwKo2F33t|W3s*L8JodN>Çw)e:Fie}VGmNbC ޝdVX|d}_X &/LYR>={G(Rpndɹ!Z ^ 1s\Tr2<ɥJתSRNbəܾFr,pI8T ,B;_Si_C A.G 8u~\9!Qʄn3)*ĉiK.%Qл"v-)-oY'Է,lqlG#7Z~uF;Kv'ӌ}%RRURlEd<Гi`,dEFFܷ!`2}GX;NFO; 6jdt% % zկI^v,Y*{g R)&7SՋ]n*e˖KM&Fo4 4ookt_ݧ:t4""_٧ f?J*$2I#t\pǘ+^/[A# u*j*?/ӞLFqr(ޭb5N#bn,C$-# I c]gvvv=zo#F M[4AE2| }߃2Y 6If$*i7Ĭȿ eb8udGs{&uahK!<菁phg ~I(vZ9)W8(;IgqPl8u"רw'8n'_S9>r1OV tc˖-y;w.&Ϩ]{YQ\]]x"=#K*\=57ly{wwwlÆa6ß3&M::B_ @jѨ5l*I\!",a 9^ǥS> A l' _mIFfyL`(4Pa1@ .Jŏ)DG?9, /_fΜ9=zokgppp&"##ixڵ+qmFٳg>m޼9_}u_Ν;ԮmpܹgcOϞ=[>`Μ9899HUY(Q^OT Vx~H89MG|ep$b+OA!7^%ZK\AP,#eܹL>.]!t`I)ȝQJ&>Q ރAV|ߟ"oDDҳ'cvW@9Z7zҰ4YM$fd0:.5؆3^+✐Va^w;+ПE1AcF8 nPQfϞ\.gʔ)|u_R`"ߢ[nl޼Ya8 B`΂yp+H8OD>pU >sT,^lGh3HcUZt5RkDQ3+(Tgs3bi(Ts*sSMzq@IL<]0 D" o7_5TPZ0V==)`T1M`(!d#A'na(FF5dypMfbJ~ݮKXְ7!$kcԖPwgw<\XZO)D6%ڟI~CLpuuҥK}yquwƾJMi-URW̵xw9s&ӧOg֭!LsF1HpG[v^*=h3IV0$X¨D_&V|Z+m!m LF1K>PɌal5&ƞS!_`<>HEB<^)7s[JL<'OsNFܐAPJBTjv0 uI֜Z͸1ab9y[ tMxkcLB`zSuこ?++|z⢖NJB&қ&'EV/O3U,Ni\IۓBtX[ ifҵ{7Ēg?w)s;,QHd4 ASTMQji(%nDM&zn=F1آ_ "]u?hڌ ns e /y<==iؔ0:#h e4E`H{=\?MvHHH7,?:; .tެ̈́ʦԽVm(?ԩSܟm۶wRH=lր  Y`ԂACcͬ׼ͨ} UZCޭ~6\gCiK7061MYo0u:˧u"]E'qL.iwrSk* ۀ@]b#C$U;r3 j}]&~Ew4$ҮB4<JU/a IDATl\Ȝn:.g;spqqaС,Ŗ~ c2Т!28>/y'|-Ue"=A^ײBKQF"YiDݍ#kKd"1:C6eS%_Lw7u+7_آL}9YHb2 B&$YRn X{VըF5-U/{!pSB!;;i+v~>1?JJ(,TFV ĦI?l*긚 _m`)bEJ< 6MfQ&۷"ϥ;[ n:XVa,#F 4Cl}FEUo}:>KDomV2F L{Wq 6 Dr2EbvЂpF3ksoA~o,@F!{.1s)I 'ϖ0y@5jAͥ4 FI@&jς=$\!}ϻ]Oz!S4󗥔i4LE#nx;. ܇VvВDIlF&@)-=d A0=#iۻ%e|lI"51=$A0[1b*gϵ(\2"-rK6٩07m8jҾܽoob ssDmRFO\J蹈W5QXl ,_ӮJl*K%B C=a,h2R^+kgʏ"Ud]I$ٽȜ`yLaʕWYKzF eՄ꡵ `i{dddpkG:KbK^MOc%qcti9EUR6tVrszɋ~>6CG(>LgpA|םܑ؇>?>z;:}$0̞GձhleHYi+A/M& k=s{ \;3|WJ<8җ2^>3Ukԡb.Rp)ϊ >PA嘧Gsoرa6bnę-OFs_fjӇSa+DL̗jTL\~e˖j׿B饨,B9&^d  l[ w>j==ݏu)Ay9(ڵoG4\iJ{. zprg8'fԩуSi5ex*إ3u`(A] q\WDRk+ru"dD@}3913P4Cڄp) s^r 5v8EĦg5`YHtuJޞIʮ0{O{0=+1ߵL\\\HLL,8yb+PԇPk٢M$\!..mGјz*bZx{6tx.^@d|cd bޖXz]zt}ȭ+x75juK&dŨj^oni` <&rzp=I#Iq:*C܊4A"&,fl;:Wv՚NOZ )hF \pqk{Ӗh"kWrA&96XwhJMܽ6}Ê72X|TjTT(6D81bavx@n?OZ”v8\DLT:uh%W B^Dll,m۶%--Z`LRev4Egwt W03g>QBՒ"M6Qa=6nȤ/Gd[o-EhG& N-dU3po7K+`2VUxk%'$2Ie2j>f.K31{_K6Pk"FÈ- o|+Y{9(\Lnu,zCF0[I*'OСC3n5{@sNׄmz2V):A[MI7/B[XXשB "ssU ,9Yx*:ɞ5+]3N?MdnGRo<ȼJEeͼk]'.OZ2Н&Ka+L±]BhHH|PF̤LJj\l<=l }2A"pp}򺙩D[Ȋ<({IhCVZv⣵bTVdT6|˥iilbƍ?@=!+ه1 P.@>/4Ј[0XCGը =KSm轱> ,+и{ #Q17dL_j0ɘ#_^ rvFlK K,3wFۦJ.pp~Te# ca>iR;Zqq7n1iӆĤgD"^I"f,1qqqux2/_.crs6`iYL m$W{`&&paЭI#VadozSKԤ}M#Ԙz:op\eD 7C,Qbi֬sJB+:OZ|aK_Zh,L/dbVeGIKs#`+Wch/ܺ{ų;Ϟ i^LׁQKO'U))"mrQ)u :[>9m8TU*; "@|hG6AndlfCRrn/r#3?Z;Wa1ӧjբGߟ ҬY3n~q hdƍ 4pƏ͛OPy&aaa?9EFFΐ!Cؾ}; PұwޥGH-͙9L e*bYp_Scs3Sg#++2IJ["pttc4W3&3A`&iW&tj숅#Q M"!XtkBOxu$1ʨ=;+.P`X|C #D l5e̮Ĕ4C}ƂY&#yY !! ɓFP\#EdR(DW")&K<FD(OIn>/9Fur~ҸLi1凝9sÇӸKjPz}@_-r 8+\?rn2"tiMfb͢L\哟cOE; AK̞Wnkt&ZfnΈ$UZD"s2ZvRRED#G'%YEf3YLhĄnMwJN:JlUdWfi- C!TǨ!F͏?wZ ٳgdnC2zhhժ/T A JBbԝb91DJkI2 Y (c~CȾ; e 7] HMPC0KR/ MEH\@3p #ʋw.\Fpp %K*Z *R tiA UN{^`ԻCٿ -W9;)FҎa׷y1r<3^RPDV^ f럧Soi&#^nUSBedO/6k f~g8Xp?=_ϊ{&Buq&6OGǙo dee}v֭[ż,Z۷osi:v\;{,[ne֭fc`޼ylْ3gᅲ;v0a$g&M[n޽<4((3f㈕F^zƲsNwԩSKY*lnsmPy'JH'Nd58zҨeJ3+l}8{,ݻw֭[̙3H>EAb(**R!);5rLeE|M1UabTBE0.C6d㱯2d'Tlf|*2vҹkv~vt'azd]%l^)|(?0okg^fpH}n*|f83=>!Jm4MEYT\&ya#HТA+>20"&q۴!~yWDbq(&M J_xnK7])oXxR}Brq嫨f5{1w5O &+ %#>_PU[݃/MYtUO2,o# i[k\h\2Ҕ(Ѯ `N鎉7;d4P,s1ÊWZw!~4WGL7KĬPp= < ""^zѣG#{+?ʢe˖t޽yŴnݚrݡCׯ,Yy}hXqh1۵ Env+ ]4l!I; d0w%PB.k )!---y@&!m煁@@(㷑{ zi hi+vZAL{QY⼉Ӹ ά!VH =>;|Jfn3L!0AdT !5ލ<3*nb!&=Sٮ4k#'#DN{%`_xX Tܹ8i<1u]_M4aڙ#.\^OA1hРZVcǎѨQ*SxYjUrٿmcH߿PݻW[k=IտjF)IR)JuhKAXT / ۷/qqq<58F;vˌXV @Ǹ/!ž~< p-rĩ Dun3\|@=~m<@M.UCM:.] r6 M !u$F=6!C^%XZ'-` `.G4?FaF#B ݎYsD꓉Pr{v`h;&{l Ǖ>Ge^"R:3/c*@C.J67?̆F!&&m~K3lG{,FYBln"h3A+;(̳Vn8KvEuO_1j'i* "|{ZK;ON  9qsJ =WY@NDbk="<c=ʖ-[EEEw9/^d >};j4oƖ-[ؼy3999-Ru$ >H1}e`nH$aNһvZLÇAq9$ ڵ/n*Р=0e+Pu vGKEWXጬ QOށ xmRWw7 &;?$UnAF3ՆG^emC{FWOFȔ_e \ۯ\ C\pELƶFt2;)&}!h v$ N 2=Li#q-<.Q8W] R9%ﰛ2-!ް[ E*K1>;^a[S|kbB5Q!P{CRRݻw'//O?T;L6#F1rqNDS#5@r)z1ksnyXˉjh@p?dBTEfBYU]n+T7MMJxv IذaׯXrW`AĎV=$"".qlޫ:XmB.N<۠3^d!fȞ>C3ұcG6帒4MHxbƸ=ƨ)N4 d56ĕɿ.;3t::''KhZᔎ, 9%"J pӝڅy2KxH(2|.q߁$FғL(TBNw&3ńGH)A"D&"^gg+XdgO SyvQ1o8u8SN(kHMMGdfS|'Ee٘?>-bܹuM+9€F IDAThذ!?3zB5rH._P(Zt)F~E*(hRZq eQx<+qYz$2O|NI<ڼ&35U*ݻwgz͛eҥ:u6mPJEzu//;yyy89y%MñO)@yX D,a?djm zBOD\}M7߅K7}Lgb^TGe#+d()jdƿ@2JXYB1xth ܧXɵB/1rЗ%V("7@AOX%".q mgee5 ?L4󮏙&b'T`:.thϧ`=c0{J -S$L7,&Inu2M1c&cGH6^?EIz7yvf.(M,_l!r~b=x٣Jzs!w'h>0H¥&9V|Rg8J) difj2g5XGV`1ۘ6ߍ9(BA< yf&<^\! 3US!bG5Qdeq R=RRRڵ+Ug}V+>,+V࣏>bΜ9\{eذak׎;vVǿه端:R?6nHll=_/8p]zRu.ogd'#bmhq߿"wk(D">R%|Ṫ"wUr :tMhӦ !i3}' *B]WW6ـHQuF&F"NXQZ@(WÕ(Z hPMbMvx  L]5QmSպN^$7gW1jWj#)},Q(e^gψ=}ܦ'x˝p|2SBͽ=_v 3|NJvYEMj"3zBOWP\\L=ʕ+y3f石qFƍ߿GyÇW_!jԩ111lٲ]x}v֭ 6T=( Ypn;/#Dzo#pF{?{g!cn.E2|<ң1l5_f۶mDGG{Q;wСCqH1a^x_n*E*[pmF v6}hbZ~\:Lo%.MоCȣD_ǬF,vK%VfvjR- v{(; R8Jr_,eVv7a#ʁ#ֈɦ2dZ =f'Kl?R$Bg`BRkBAXCڲ= iKkr~{I3YAbbbhOX^H8b*o+eœ+fTp 5(Xw*]VDŽIRIӉS$6QqQ%! n^;Rgמzg7 B}4U|N2 &CvX\}MфbQRhA*9uVRgw1%52E3lɒQR2GMEh&"qxtL2$i޼9'OV'w :tVb̘1w{U#;L2///ƍWzM6׵޽{9w/f|?(1;@RgH-k6D eıIO!UDŽrKU e3XA"?t?"~lִ gL #P]nL`!eEs1*m=]7o~Di[GYAZ;'&)&C }̊AXI*29x(3-&Dc-:DoRFmi?9 >RRhWL7/JO΍prUls&#|H٠m&-ZONuUj7V7 df͚uO}|U5HNwOj׮]UXJ4|Zh]LuԨQgF3<{w "ʱY7_ŗw|z_ES8VJ1s4xQ>yi 9ROUiI}l Cgc%? rl @oKJ6}I,~줪߲pw+oNB K_]}d$cK WsYV /x,(&s=y#1k@"9<|IpqQ! ܁Esm$x%Lͫ^JADPPP}C @"kIU%B.}bN>͌3E3R)ɸ(0CکT籈#v? \U!lN1kdڒXrQ]Eԭ8.*񟼖c(3bnYU'!BNڷmGZZv  uMzWL~qL.1vNl2-[VA 'ky~K̝;>*Wս OKoFAh#'$5%kT* 9x /CdC& NǏ{w3\WH=d5GI&٣3+*(//Da eNe{'N`;nY֊~#Lؔ%qRCdddV$GrucٺbV'E.$c뀻)BZQ˚:9F[Pxx-"GK5]ذaJÇW.EvgNu)چӭm8#8r:;s|8tMs9/H۶miҤ QQQFppp_EBȲB4](c',W\O>O?%",;T^^ΩK'xhz}ioTlSڄvOuKUO">Z{(c*ʕ+(t`ɵbBCC BVA&)IdehS4%;[S%vZo$֜u8DB/Bxi?W2sL͛w M>P=)ɄD"-"3ddJdO'i4m god^ahFո}O>j2e\-R@Ցx`$zGgc0]Ԕ?Lctj@#(@\}}ޝyjq ͠%H/ c~ 9s_#w[("lP3JZ. 8Nmz`wgї<҉xyq&(^3`H WR3ۡ׸ aIf)蔜9KHn'Cnho" [ϴP W5\'"\±]jә9b+f%[oWY8ӎ᷐{J*: KyiN|=p r$1NgAp$]!ܚbiFKP1 _KD戧"<.'@6mjmW0Lh4I6mܓD_Qؽ{wJ4͔mZTTDnh#bPRRiq=*I9wFN}ꎓsGqn(_LAr9r$K. "8Ƽ$ZCpK| K/yUҥ7 aMշC\{5oDtEXp!.2B52Wh;3sG1 Ϫι4{*#W@ŝsX pkS'?Ip\j&nF[.O.N%2EŁ=g82g׸PJ$p ǥIA Z>SzO/ߤ5ήtڕ 6Թaww9`@*Ϻct:W8楠XFςI(W)RDz(fߵZQFŌ30eB*i0%!IXerȯ&PBU[Vl*yNX*SijFӑ{s8>\;v@(#l(;ՄZD>S !P7Hf\fD P4ܦD1+z)Sc:V AH&fʱ+Z\C3bC6%^xg{ayr4Z2)c꾭8ݗp%a:=1e9?ǜfRLGo1Ǒ'v2MCImaKhfwo1 C};SFE(NwG$n'TЭfzFn٣U‰CZr2jF6ҤN^.#X\AE#@Ru< yYbccyznذS#ϟ"UO<~zqqq9rmm۶1i$5j+>>T=cn+gϞMV+F*7Iѣ2dȋ!} IDAT=z7x"uN|}8Dt@ոGCdu6UnoZzî>)}ٛ_+ |z=fܹs`ߊ,U[Cww}z6@ræ+ܐUt?UP Bf V}6X V3A,ŵug>H햀"AM ˀ(SPP/;Fɲ .?X8[݇u/X!)zٽvZawL: |_Cĝ5Pl^ZA a@+\zkNxS ԵwvvSOsI8ñY-HSIS<օ~:/68G83Ռb™[女8O2Yt\7$δ (Ac_V^FDu<_0?׹tNnEO{e뮼S?M)\h5 #%_l{<j*6)`",u2W< %n mb+;>rjP!QҏdKi;DsSYdߩˤ5H*%7!;Q6D  Éhd_" RJ.p]fMI@% Fm"uҺyeaj6{qq`鯉;_S;'0[: |cK&)5Sk \~# H=G=QO.l߾3fξ}qWlLjN P5[}ȑt󾙈HK߾}ٲe aaa!l=,u*HAyܿ  Rr~]G%TAEhv,3GZz8Š*؞Xlh'T@1ߝB #Ly[6q!ݒk'wNDf z5Y&2R\M[XX$CQt{(b]\bBf)&@ A"zQڏ_aX^Ǎ(l6pTkWgJG?6 B\k[XL FzH@jc%? ((( 77~z$BBBP(PO ,ԧ!Wáa'Tq1 7NBзo_ppTijjz,f2Oخ~+JkZ/r9\r=>B1!Hd2qE3@pGBMڟnO Nbît;o ɐ"6ƃKxNPi sex-q|gRaDB<4^̤C?DwD(C}OPΩ!& )޹sX^UN@LonCd}{B؇hv\@HC)! oޒUQVl!#DFJ:'U`4ZpVJJ6-ӡ*Њ>&IH,B%(Z\%W0IT!N_G`egi>hVTR{HfA}FAOߺ?'=i{!23w[?҄lDv0l_zSNaXYrA}v&OLqq1...]t=I}AԂFfT[⏠C)&}`0-V% SQ:25H_WķO&f?]k{7lؐڡ>R!n][X`c%mZrkwVd#S#Ǹo/ﻟw|ȩ${hFi7u^( ?B_ImPc+H] ֠̀+Ac!2ڝ[>}VӮ5:#O)_2ڮ\EhcwU\ىU"-7#'ҵi R5 X 9k:y &1dX!w7kƷ`!TRaIDewKLEٍ_ 0+E~+6䭄5!RSRRҥKkJd*Ϊ=Gwf6|V&(=P9222<6s!$7n?3-[dĉ׏{6֭[ѣƁ2ط?g8Eq}s YoT-sU}BU~i/tθJmSΌM546-sC!`{$Nؤ qcF@HgX?ϭ=H-LS&eח:̘ܳ)ο FlP |XI8 o^E,߳!>wm8†Y- F v2*  يdk[ju#;~JxwӪV4 gq81U?pJ>Yz5=z`ʕ4hЀe˖ZzRHMMgϞj-<߇I֝ZqvvkxUhb&^uZn{8}$̩t҅,xt:n^hl68@~C[t5e uS[{rb=n@Ǹ+7PY4E@ OfОQ`|2yjۤphXt΄I^c2Q5O'rQE8p*"e>qc6n5hvcqȂXl@.-e&/Ox~LϏbƓ˱RXGs-uO[y-w>\p Ï nql=>͓ 6ыFpzY*D,DxB6!B>Tv#4ڝb]sgH$899ѥKΜ9buh4lڴ:'sqZ+Rȑ#QT]JU( Q*|T*^迼SVXqg 0BWGdd$~X3T8$Xή%OVVݻwGtc9or M iSˀojO؂cH#GY_5V Y׿G8=!vIgss4oA`C<3wu-t~]g9`Db?T%O:< /[ % 505yo-eXG|K./3v9ZYSoBMƳnqE1gn6;81ns8"#Ҧ->6(/i&7HNE.]3w׿^i <;7[] Ѡ|֭[Gv툍GRU^^NIHH`̙ y)m6sN C[\:?6gw83rXf3M6%VFɑί^kVu<訸UH`csdzQwZi+gDZۇOU W\ -Z(;! Qqʴ3oWhBDNM쾿?gzId)J#]iPT@DlH*EKHIH N ! y}ʕd9gSu{mqXF cE}總5' JKVTg@G~?$@ pKHMjiH GA6!) X/m+xO73n\|h+l+kWETMI\rz#xnNЇL`_-{2=ǒf$NRb_E"FLzruS3h;bZ#/|$2d<,Wr@Xbnt"bg+U(U9UTΝiӦ?~ LfAf֭[jng¹s8tk֬aŊW\b_s…r?9T`Zٿ?111`49|#[-[f޽L: ҫWrу 2uTO^^?c_Mfp) ҥKrM&+r~[Zuu,O HŌ3gpEjw}KoV ,h*yɒ%X9[VBZF2c~<[5BΘ1c>WAAdd~WAbkT_aݾԮQpGJT^^⮂):/?1oBr|Wg9l 5a"}1. K_'5FH6$8pf.nW< 휃9ۓ❕LFw<;-nk15(]jō#\$pQ:7~ԞדqG0ﰎl$#fi5"ыp1")3T*%/8rqM|r}+_W8ẁR##;…ZVJT."ä4nK?W5U`28pȦ 0e;xϧCjՊEѠAONJ֭[iذ!999*͛7᭷*mӺuk4h}eDٳ@߾}_~QɓCӦMx!@ 1Zյ*7ydCYz]ޱ$^{xww*A$lO]> Fחhzlۗ[MwqsPb*\T)6;wei.%U`C95g; H2HƖo{cd)odD•xFǞ"N՘o#pI`` ]taƌIՃ|߶o/qlڴ *5KJu6Jw@/8Hܝ f'<<AP֌FQ{>D,f| KȎ\2˵Nl3:0n8^b'H|G+iOq= E|`ǎfL3WLcC/Lf15 yi#U@̥':2n9V%ov!4n, ^A_/'~YۑD"~n-{jBJ,1x_(2ANGy9iyxx]V9"~HWtS;",8/^]CXXΝd\U#k6= Tt@΋d/:Ц}ٓ#Iw8BCn.\̶m*'1s +Q@NGzN̷Ȋ;qy3v6Q!4nř4WV'h5(%.k{Ўm)K$Qf%ῃMJ+vu44?6:-;|Lp]o ^9varr5]pHX1]ly}bW>\tMr ǒCv^>q 7pWJ̟f͚0騭1!L:̝|": tlv6fjsmcK9I@t')#c@}{ {OޢRF$5LKxrv}D߾oe.fIdoҠ2m۶̘1 .nuqiZ/֚M=.X﹁2h=5B+Nt9I*3iɈ{Hfc7;W9[W! IDAT<<ѳ|/U]v;ƍcǎt:y?%~뿷 r8? }_ÿ!GA=Sn!?JX7PVZ?2}GHDB%U`ڢFƆHyd< vW!1TF`|D򲐠jΣ50$sa[Ӽ^.19ιO^&?2Y$m.Tz{سΎ^enT4b`0 =ޭ":Ħ|V uak٣Toדq~sFd@<3??;=`XXz5bX ٳg1uc„ L0BzAϞ=q$$$`L͚5Yr% 4`sހ[hIV~T. Sqd`+VH"XAll,۷'9 F=[|L1=?.gHĜ)=sFNo~30U =5?5,  =UyZʸhW"X%X=̴iJ̗{PIIIaL4\Tjj*v5kuƍdeeU>MIIyd_ѣ >TJLL ۷oUVL4 B֭y7OjR𨬊-,vy3 chXu]]:eO'g$$V>bZhІ2^AxwnIοF,_%+~?|J[}F̐> 'i*gO'>OV " GW_zasSWl{5V<+ѭ|Vvȕc&Ft+^xɋx%cpmBYmΊ#%fx"bqSpkH|H1 }`_<;A@\BBUV\ 6 [!hR!7KI_ TU[B%P (T"o̜=j #P+)FQ`)wU%a2"B5bB*ƨỏd$rI9m =\ڧJ?~>aՌ5GZjq>#>Ah4xbhԨR'N{D"n3{lT* ⫯׎_?ri͛WJٶ<==9|0YYYqEo[oO?Daa!=0IߧU! -Hʔ`,sDQƬ;F".K{-׆ݐ.A<>?78լȼjU7w{: ]Us/L~)OG2vcWǹ3b9BJg2|wGַ;4Fk+KuEs8}>Gvn"o-D2@ m2)"E>'1 cr 9k-bۈa$ U o*Fٸt3ڵoƯlPg&JQN}x F(F˱Y+W7n7r (bB7@ @9R?!c͸d +"-$Y,氙BA9WL2g8c$,nD^m%\_%g 3E2tCʆ]AhCHWCY2 d[5@VnW@ծ'v=0paH "|zhPܡT U\ʖw8YX-̦&; OJ d[FVkuؿ LƦM֭yyy̝;=V^O>Yi٤Ihժ6m"==#G2jԨrdw\|uqM ĺuSR[ǵ;WTmpǽ[Ay/8?JFõeV4Ƙz̭ 3@IdD=ű/Gm /;#)w,%&Ir_<&2 ˗-gdNa@\H}}^e]G/q4V>˗s9L0Raеk9͚5+WV˜3gΘ1^xm~\p>TB.NJϲ4NK˻] K&eD$Sqjb ?}!Mn Y;>"kG"-QUL bbG  DQ@lg"'^T$XPo @QOm!d^X@،݄ͩՁ}l!::+B1(OU:xp~E-*!U;v`K/JjA"y!rp2 R/&'\MT: ~mGS;S{d %Дd*Nb5,; *Xw8A3|N0 a ڰ[|pXOָ7yQplcү= ޴>}@Q!XI pI{wgq㗓4-BϏ$Q3لE7c-b=VwwwqqqAV#ǏVAף#lb Bͺ`֬Y<0F<:MD|| V11"*3"t(ځw`ƺ|ZЉ0x6MJjYmhVJ%s\#= A]ߙqs6z&W1@62`%M5\ k}|5";(rK'2G5FkT=fݕ7+")ʷsᄑ 'He_TBD!\<3G ֪ΩF5QM<={~$-H\VrZ)!n%of,9H̘!'dn0v#Xd*_DEpرfb7;'FA@Ian1bbNƐtS[??N# (#"1/\bvvS|Bqyߌh{\+g'M\x kѫW/N>\.^OAAK,aʔ)K^ H1 :NǧV`(G;]- )o.%39CNԓzLp AQ)H] _Ȱɝ'* /7ڮ-Nɚ.t &(5;. m1ŷd ?O 3>|d>%ŧrB@i\`AĒ* s(̭1JBFsoC$XCMEhR ɨJz7{ \@&cQQ͈,lRTm˯zdFUn)àU# H5<=NÉz>Tm2Bg X6hjRUjTTmEΞϸ2>¥31;{،4/od+$ EFqsh~Ī9+Uд)8[_DO ;*qo i0ZA/ ɍv?2`>C7vlڴ4222GRѢE * =BTt4v/ǏX^zL=l 60y$,"Mt1|SȘu̳(P-'D|;V07oX}F5QM[hǭY?rɩoR<|41 ✸jc059 g)l!7&_+/"4v'"!Ppo6u\_؍:Αªj.\8av=!1+BGW.&3GУ@b$倞`[ (dHe_<+ypsx؋O0H8Օ2ʥ|2oIKE˛I߉OQY .܋-S9lݞnn{݆:(.QvZp3dɫ\!>qEȦ_AJs FM"u<ȨWReTNkIyzBT1_aV=X8aS7낂2ۖ9so333xMؾ۷ywKRfff .|,cKrrcӤ$ /^?Bۘ:tpZ UMlo Y,׮V/}uRn=_ATSCx\GHB^oSP4EtLIh¯R/$~V ~zԼ\\A|.*q9,s~ =Kn*y2qH\Q4Ž \Tjٳ N4 Α#G.~3@Crr2O?taĤv +h\WB1A'8$5d`]5UE8Cz1ak h`_A@&BAI]qg{xr5`ns D ĕ*&yS\_Csi= ^n*rn"v&d4ͺC1Hl25dVSD>nۚNV]n5t*U+N~MvHR۪ow*UVKxz_qҔm4ۗ?%Jӕ}Sd4cǎ{z~w$c~RtMB豎iQQc~#GyjXmLo߾]]ϐ*+$H6 $-Gvhb+%po><7ZƔ/vm؝Ysyr0;Q,Uw[M'mxDrn-0)\q(Tx; }AZEym'7>A/@za{.!xAUCf|9g-'X쫗t_ՄKPi_nu!>>k۷grւd21``Ut*E%YL>W8IzVoF$`u7|g-* )PKӻfA(vl!+ִirַҐzVtJvo1 qѤ|6Mp;J c1ݺBmXrRDbOwǵ\׎J.7^F. IDAThn<nNvD@PL 1u]ϭn\Ft+բr&9>R%98˳?T)sVZ6O:Ell,{yl3::[o̙34i[oի `21%& WӧPp~DwpZGz,Z}fִ/Wgybw.Fwys;i5 j׷նm[-ZT q&p>Pb/:9{aK@+Vt ,HH kdq6юKw/IfF"зN1ܬQjTa?C'vln#E[d)_Eyx$jq 5&oO6%4!-;IqaF,F*rA8b0Jg'8JPf:❹v¨W+aI7h.y~O.>o95b7¥8KBޛH0y5[^^%s}wt߻womఁxXn+V|Ŵh}+ 2(,(;OAr~.y=DXJMl=.d'ц.<PR"' XLAO3wgLqs;4ferDLe'8Mæh@qW7x> o!|JEN !aыbwk;eƈdtwaǮKh; H 3ҕ05~pO3 6;(؆x3IOzc׊=SMQjT7YT*M^8<ɯqi²p kr,zգ:!8e< S:-! pm 6YmRJ]D\/jX͌|i._XK€Ҝ+As5I]H1<Q*%9{ճ0 u˪ DJ0̟瑳S_AXhueaUIy}ƝzM/6?82<:=Ovܟ\>A7"Rk(ɬJv]trdz>=YxuvᄑLUe yy|*!8jn\1iܸ1cٞjeԨQĖtFRU%=JҍDV?G21p/hxrz|Lazc+ް}h?9o*^c˸]J E"!F6 rW5@E;&0{m ʑv)Dwq7׿FOT#U/ 5́Cs{hL[: 3"+$+{o1ia:{6%U]2ߡ&&*33 2Oi!2BS ؤ VJ ryHGCIIS &wzy|-izۄE%*&"cQI01 ,x_+FlܦL#3&:G+=.5^̭ 13=fM-8vD6X ]FQO`+ʔ9#.xP"2/ b WKZu9Z2{^fTZ+֖'3b34t䆩a9s@rF p8H2m*L؞l.>@fnljW_L:uB 44ߞbalذ>~zZhk.<==K"z5zܹ399ʹ+Ln>w". IVa KEAR6 6 N~46@Ep_ ՒZfRQp\o1oR9AU?}Bɹfrl#<ہ1.ʏZSڌz_6f@g%JΞO1g&<UXKb~)HdKi3}Ks(r Y*o_~!M#NlV}GRꇽ1GjW|] Gg%Ä*YFIgCQ[Q\5㝠3@*:7aM0|%᝸[rK"/("P瘑mȊm8DH #ʕeB:4&7#5=VyL|@]vFE%ҭ0݋z|i~[QG!y@ܚC*l[w 9ep]YY[slR)-jRH~ ;Sl6K_! D PF@B`׍K`JWz0t3Hxً\gr+T,4zrSJF35;i|HzF|YȾn*<J"!.l}ʅ?xF}^\*i$S$}ԇE=<ڃ )yd1'IA}Xh8K_ o;-=:|%fcĉxzzM1?Ys2)x^Nvy,]pC *Ȋp!?-+j E& $0B9`+(6V<]$]$UrJɩdҌNC&ףF4Gi_}T >u{-"%S9@%}L^ pԧbSJD #ߑn ^]_" 0F .4w*ׂh&cUD,,*1m=X:6^DJ 5(T"̦2[?Tk8ugzd i{G P.bý" (* bC A@zR!Lc !&Af=O$3{s{{w#USs*Jd qa@ɍ*T57Kr5j_5k֬aȑ~-ۗݻw* ΋/xÙx#ƨ99ǹ| `B簾&@2D 6>%>tt#g &q(liBAp,}scN6>TT]h ^z첱dlnWz|r/P+XbZ6trÈϦpկGYD\\Zwc釖0jlͷDw_tv.Yr3y OdY_Hy>gxۗcrsͿk>9MD1b"*(9$`=63M ,*&GuøJGD3\_?J{U2U3߿=8^E9+h!HYh{ DF䈹~=yI#y|pD6hUB "^r9HqeGYL'bte"RCz}%*<yr:s؀,,Ant#yȄ^tJ qJ!/\+mRIK |=:iŗ]_QLj9UMz|mD UU?agԿ)qQ|2'N1g:vz1 *t|>v; l6ۯXvv nw51b:Nll,v!`u-OFPL"dӡ|ĴzNm,ʄ uY9!(PK9N󎷓$5ЁO+CxqQt<#6#('uw&wv-,[""vGy- F^?t3𗸍%[GV&-,,|1xdzAMz;#KG㮪$tD&G[\g!r(0I"^TjOe!򴉬~a (#D)[&nj5_k\%J[i7Q$0iM N&r wFvgw$xc)( O$qV̧a<8 j$ BMJ[Bn20[?E(ru8gM(VB5q?<AyVbK)j_OPOP{POO: ;^7SP lzM(pNIAĴ?Vb .Ұ 3[ӣXGfYxB(W7俶)xپ};௞Gl_:?>-BӲeK̙w]ݚ5k>}:/_&!!g}ݲe J;v ۷oC!`k.RRR0?sm,7'0m8-z񭞭Cǯ.RhH~nSbؿBV"i({kFLL|^SVM$n8@ţ[Tx$"SgBxRSF5ܦ ?RDF&~t,AޟqxU=&#q0tG8+ʏO/B?=Yd M^[EɼiRTt0}BTz0g?&:dgPu.0Q!#[гtxFQ@d&7jHAq s3:K/t<> >4΢ 2 [~p@A<I3~k'Hz96^%HnH!)蚩8{G$e P.axSZ]DY 5iߕv,AU>Cj@INrO"E1/z)[0(TVν$@UEEj1b^PYnyj@Gkpz_x1?]U NkGe*59ZE0O/ݐ֐+J ɬ TRNFnplG9ou(a<;/&q~\!ॡbה9un ;OPeiq9 u}Ջux>]J8nbbB47`YYYL<'''tMF\\\3f O=۷gݻ{ر# .}<3L0 rQŋӇŋ_iʡ㑍4*GIs28! \pp=Uv09nx ѥdYېX=(L.QrNl Zُ Gg=l䤇Zi [SnAT4Psމ̉6BEVƒ[E+8ʊJjc]\#PItNgR%l<cǎjҶm[NΝ;%6صkWL&S#Tg풔x-Y0Z}Yђ㋽zJK[x.ZF,G"#9()"ak%bOczED]өkZ4#WIqMs&.ғ\7#XSO+ `,Y?;}ǯһ|.q20=ИwM$,f%{̇1p7yI*Bf7 AdPaeQ('_x)k^z'@W&ekz^*6#hk%( :i=*\L.N/RoDHU~RDSPA&7&D N| y* IDAT?6p]@nt5T-7b/f߾}1| 5~xf͚@ `͚.\PpXII ` Սgy'xm6%PUROǫ=5&Fqg *7+][djζ@AO|f )EB "vܛx/ۼe)LITt-@ At#S‚?xemcJ~ۻT%uWk< 9h0) ~lOSlF̭bN+l?ffbD'K$H 8 Lx@pv֍nQy!ݜHX{ݑaSPFU 3?o`4*HU7n -%$s K\1Fȯ.igWTE.ߕrrTTN:UGV жg[S";Cn?7E{%1@(?SIerW3:,8.YO*LN̽eS!m& OMVzB̑~yM~e-0>NAU6_qjx]NZh?_ʄxL1 b:/b%&J;c`3fݰFOB]%p <RKpq4Ż1G^{Q#tn5P,U0cyUjV2C35| RZ}W mI oc=j!>)\z{ :p(g81 Y.bs]"|A/$VD=TZޗaS 9%%e|gmoU?24>&7cgsvQ:HZT |~VRZSf}!FF0CZ./Xz>Bh&%g00mN/d:N gBBIvC871q~}`T(e I(Sz|siq'ISo3@AfZ%". (THq[ ==ѠKPb VkJ'2j{*=?91:VJHJ&ƃg?N~sdmx<7^}տ PٳoTW9Tk֬A"4_A՞f=N>(Ws_HL U~SsxY]@.Yoo0!0ZW~H`&(;2EO'nťaW\=R 1;H%k.厡*^x=1@RVq zY/b2$Ne sMir'C#%*x"xZ1}BI\QU؃]_J"7I{xl -7Ʌ}gZlyq1˜0ayyy@lįծzjj8p%KpC_ӧO v@U7 cek4yVON]fmH5{9[wuM#nnMۂIƹd<ϥ`=@kp7㐩օ} {}E';4j ^\ \d^K}|8DDD߱P9jBh" ^]l,wcQl( dZ f%ɎJŬYټ;J"{zcڵO1UJO†M#0L{8t-oC~NcH&t"(xvP`ψP >xGIy\NԽ ]4̯u@d5.;ZTM{M"r!qwuYHz S`|$^Dik v҉R^~}OB^q] F-[ʺu6lX|ݺutڿa;X~=ӦMXv-aaamP]PGՐ_}TfY1QE1@6_\ƸAZ-k}p&Ɓ8~8nW*$|/d6 $ l‰~>OMa6֕)KdL} bzߦr{\^5E\[bzms9z]#NQIx^>Y/q3s3p@n\0@ޮu,< S`HĪ ׊< E4RB4`3l5B;waf]ǛY$\%hU1 r¤x%~]ia8wk$b-en*T\Zܤj||>H A٬;oCW0L#+Q&u_סSIcڮ#v},mih8]}>|W~" m$^O~̗ ɴ_G~ڥC̑2>ߗqq`_2Г{Sp-m|&\#>$v)`)(l%%E exٱM3!q>:YGe̘1X޽{^ӧGO8tP-)SгgOƎީszSY7R^>AѧϢ߻9=4_B(U"Rj*Q^:tߧb“9er'DɄdh46ѣ6}e yl I)J-HBud-^ ?VbWP!)Y2WI#r\T b/AĞ20#S=VuHz>FEE|Ell,M4Eu^;v,n%KpB/0`@unݺuVfΜɐ!ChҤ K.ås3f4UPUir ;5 UTݿH9qARt v-*Y8.[irl NkY0uF$s!g{ ? DPBA3` c:'!N#-PU 0ʔdNoEkk-lf+~.II^91VPUjO34-ɭ. ,L{"ǐEl/\wPBU FCԵBB@lr#zZ܈\^|>JeB?%q(vV`WPnP ݕbD{Vc~T \j1)!#(JHM:S{)ml[3yY hdjGϘ137qT曹曯\.[N*SNR t@UaOvT2aXx-bsU:A|DBSU-PUs3^Oc'&'i3o_fr*u.7Q"ϽCp1~M)Y?s<6A7ڮ#vBY?c>-<:ɣhV y!*CQoY<K+r@TZ,/! i SRIi_wk̙M[$W!#(FADGߎ+OSGa + E\Z7sKu0OjNZFEvxE>B/ @$RgkJAyV~!\ y|!RHxSNgn_N'σX2 koWBTj)JJB%@Ye(G1ZmMvhQJAb]tqd'_ E,g%(ϊd-pGه j)D HMxW3 lx{rc URӡr!~vk )k@Ik o["O:a°W.ίO~Ĝ59yV M?."Lv] ]GR]zu6agHg 8ܹsXo-,F:§ߞ⦛6}vD Mx UZ +.G/nY7H=~&oG(B?y3s9rAZfŵƲ ~ ((TIdH-55&urɸhcSN-{"f-BLLfa] < IDQ5ݯ"BV_987h ✙"a=hv$v/k"'O',#TqDL"%_Fȥnu5x,:'[P0`SPq^OAjjC{ɆI93x%a ~d=::ؕQ 2Cwb\sIwYT5g̴V~xUC"{}K(+xB¥ S<4@FQ[%,!$%`iںέͅl:m;.ԻG$h |ꩧs^"mFPכE$ Y~iwSӯoi/-y\6p8ٹs'gG":El@Gm$8Q{E =ȞN”-u ID"V5 l$ärouV!MMT2(g'j"u›0;r_iصke6s2sslX5*v=<VP D";BCl]jvC9'm?6a-?KH&,L/@ޛs*qđbǯtË\~w,rq8|t3Dᴉ&@r ].W3$by9b& чu.2!R]+Aܧ(/$+ hENPljmx"{{YSnf;5ƛ -_p/kiJ[e!8JHe pIEBlRA*U؂\HY,w@I5~L;`S.z!YTN*T z&mO5iӣѶqoFkFP{7Ue d)5u2pBφjKYDێynel//2۶mcСHEf>~Wc1!g>W)Uq($A f sz v!b/@ߥ-MS9IMr!1XB<ЃkF:&>`5̢E(-g4B=9l o"]37zM:J84b.zBTuU2x9˜9#zza0^s_G(vg[':xl=j!âdI#R^L^ˁG0 ÁϨ:?^ @ݢA=Ǡt"EaB{ -}琅'M<%ꄃVVM+ .Q]`"2yޒ lbΫ>YԭnzlFWf l4L͇Ĥ l~':ƣ7b$O]|yVr%v`"|Y^&rz5Vy=SUy QZfƃdff\3O?1U`h)nRh^8 >\ D%h9)#vfleuW0P.qp`Grk>A],R^gEʇKt8!<Ӣ\b;gdI©dd0XLD Ԕ>IpmZLe3ZQ] aO%)j-a c٢og&s=hsl6HJ+233j*{ IDATIHH[ʣl6233HMME.t8\t|WZVV111Q*9N'yyy-رc۷L&?S%L_e4T,Uʥ2O𓯅!!) _VDwլxpQ.;^M%p ;X:)[D+3/b|UʼŸ\.v*``F s>//# a)UgwPuv$B=]{Jr܌n{Č{A['220F3@M@:6 .1T TcZ`S T*/W VZAS5KbxUwmlwunQH х>aA]@]$ĎB8U" KPAVV8ke!{%F"Q{!$ٴio>SeGUa%Za)JZi%{S#dOMZS0cJB>I'q\İc*wC ض6d2R ӷc8ŷLBA(GjG7(yd^!煐"Dʠ _SOɆ9vIm \g>`giO(nW^g%w(袭-:郘"Ѿ]#-!Mu&*R$YJ&?6Õ#z5`F6M;J)"0H/`O:./%m5IuR] .&rɅ@,RfqbkQkD2 ѧo;G2.v"/^5 n6N6N/Ua2rb.\S)Bb xVy6:'c 8WEqpV g%8ϊ+^1UMuusHh9)|?Y]npQ\MY ?9Kozjm,yeDo2i$K^WFk2d[lA$v*7DiDv]$E iDG(j^,gpkw5 }4D#Gs0  BL$8}6ȲdZAzb ;iq*E $RQ)vXv/2ˇ@eB ,Ka%K ˬ"cHM.<2 XC%T$I(iᧃ:V& e3zGO9i;SK6*0*(jE]@ntйj:ECz5dVz6Z l )=9"CK=#R夸"y0ͬxo9w ߤ qo%`)U*RF$*(ï[.-#6%(TĢi )#9DR"v/lPa[ڢ‰#U`.] KќeU|;7&šz]y*P$NvKd[>Uu/eY"zD5;CuίJQ G"sV[*b~şMA c*W"^fTFNpP @f#p k{V}.|B5^I.v lD5#( (q{HO ~/əyGU3d2ɤz J/*u~m(]+=@ަ'Lo-s_z]gΙ)sq%O2mc⌓Aؿ{noJfa53NNϥ;"l4e90>k貲co[Z(D\w&0Dr%XQT%)pq8cuղb"zz+xl rb;6GI mΥThe@29cE66irxT= aBeg#E%*700Cni58gj9U-[0l0zŋ <#F`2裏;v,]veŊQ2@`ŊEIկ1{dO+ꌬZ9;挾7㭯@;ײaKY hGڸ x3ƚmLJx[ {|wTm]Lذ_ʢ]t[*r KG5(o%O_෴ק]X$k:_RV.q(B"o źV/@l*rs6yR0M[%wUa)U? ͯAկ'RycachO(VYZڥ!ln:fyILJw[-v% 4^~j–jtb : 0L,}}' ;HEarRvXw5f)@yb\/Enjm;ڸBAǠl o}6Jņ=zy+*^WO^_ڃ$йd}o#E)k12[CTY r$0oS@ږf&9[d:]V"wwA%FIR[~^Sif^?%=z)'UDIB,X Yr%\_|%KpQmF~2e W\q.+:Q!oQFQ[[O9=rE2- nr-ܳpaNN!<+,FT΅@nC#!Y'/SI$ 5>؈NZh9$߆~RO#A =[K!$ "_BB1DȌBגk~ "bo~)-hj<P70ﵡvSMcm 1aRwO]e[lnvy|If͚u¾z"޽{tRj.p(4@Ě5k=z4Fbݺu(EKjs]Fm+UۉTXBD$x3bb O'`Y/5Œ70ѣrC:db`N%CwӸM?ԫGiuJ7m4|< _?zCI^bY3̘qԯ~ ۞{G7yu\ѿK7|I!(#O)^8XDѹݱ=`uR.P4 )p_N$B# GVX2$O%nV]J@x QCAi?5QCO4B{)C }F(hMcHRPP%\NW.(Q&OOL|* DRBe/$oM= ZhJebr1 BLrf=kǍ^j_ojTN85W'X38,i*HE>KAR!^ mQǣ V?r+HH!R8ZLP&9K$Gl>G ^~lb?[Qn+@"zrmHA:k){b/Hr|?Ȉ Lf@gPȢL ^~ez)}.>oرlܸC2c ^{dFF~~>֭cԨQKb!Ul<Z eˁ_ Tfs냔PLIygKul(p0R^ef ݙT1o|"Gy˃.B{2ni)5} .]Yl #iEm$Yw+}*jA4<6 ˫ ޾b2e&>K8Wcݨ$c|-Jͣr>-&$SUcULy6>`KTrcKj}zpH (ĔPZ|4tPմ2mm)mtD27r 7K!8^\2lsΥҁTv9SMsUma5SI')$)p)NtgW:*_nWqe\+LCCzb'$%[*# b:HPY(-DxJ ^ S "2Q Z?&i+o~\w"6S>Ub#uK3^~@*ݔvy3TCR-S5$]Geeez̛71o2qD.R.FF^^XIP3)QB;<=_dFb &3Fli1g=?2ew\,)2oy=3Cg0Mc?]:Gv1UhJBbb":KCC}UF/a-+LXE?`s;W'M+gBkDFb:%KGب|b8xzq8Zekŏ8S*BGm^)6DBS!cK K>1jC:KAq5`upʨ&G}h<HcK=UK@46?VC1ͩ* Un;k),PT$>1^kJBHRŇGarM$D Uo qCz?Dy5tjm),~}^MEO4uBT")A&<#F_lc„ L0{qEB!.6mZK/!//ٳg]v-o&n3}tT*UtrD) ^;6,:TE4<} h6^^YH WFzv9r!eᦉf>6]ԲlŇ =$])ѳkx̸ݑz\&MbӦM;cΜ9bSKw'ϧk!DǡBE kdo@ 1*rx2;JJO>_@ܨKڿ8kɢѪyh֠XR~Y\})sR[hP㟩n![sƄ 1U{$HuG|, KVy .K01.2m{tkU65jz[A'r`MUsjjPGQP"«9q=AH"(N0d\w=nF=-ҁF;k HEk=$݂=AA]g-G(Of_GXPN;iGϨ.ja3+G]W贾LZRlGB"Vo͍7xPsaǎl߾=b'x rHdAaС}ݘL&xWɡ2|+رcq8C1nܸh@jjj9r"y]M( V_C LqZd,"/kjkّ*κced'(ƚzn4^;n^G;OMo76 . ~Ьqi"{| CQL`…c; *#_kl%(uˑ ǥ^bΙJz5ӹk"hyr1IZQqP6P)d*~vͷg#7SrlV Ґ!eҼzʇ:;p1SFJgGezp`0H,&yG Nz6|h=MASE_Zķ܏84&>lC%z*(1(î3{)qB@&GZW!AE#aM wb7+"ُAnl(~ eኑaG3 sHD;acު$%* Yjr>*_BE[P&Cpe R\Fv<\x<ȝB[3ы< $l1HQX<>J-_?r}qK(m^t9}H r'H}/(vB&ѽ{_ Rٽ{wt>G?om[,.007<_}n!|L׮]=z4 Iնm(,,d„ X;vЭ[7n˗/g֭dggsueC_>""V?ŌJ\pS|N! UvD O€ZXpFo-]] ܻgjB#~K _lƥ}S9sY|0 e/^Eֿ!Ө?}㐩HBl9};웙Lw"՛cH%h.)r(as^J5O<M$_40! C[)~S[g|H9FD[qѪV/UP]h9xAc3vrL#lZ`.~5 JRBpSX#+AQaIUQH nw[I-H4b<12 N214A8 2<2$*b'); HXSXSL B{rReIVb,vWK(Oj()ń$"§ M>q>nAVK%{93oґTi禾/빩oiTXt4R_~ollh4j&rLL,l2FɡC>|8^m{5" v6D"K.eرʜ~\|to׿ls:ڵqBX,&77eG'5kְd-Z#!!ŋSXXHaa!ϲf>_g̘ߟ+W0sL{3ѷo_V^ ٳYtiطo:ujFIY#~( ^/OzAPV~;f {q{>q M%7^ޒfdPj/埇Ԑ@T/''+pTŕJAkNDb 4]9G"|"?MĦ''}pP*FHysǥk$qp(1QYB;89X9i[yiTcKNWR *c ˭ɏw_h&o[i|; &ZZ~:k])W7(chWd DGtCG օ[ &)l~TMBuPP;F*$른b%V\L(~d6?F/;LT MӰtXB PJ6?[)s2( A͛7y}}=஻N/]q,__~뮻3!,Z)c6N}GyNܤ 0gׄPg%>FJd80;IlGD~::#cT? uP:Z;2()Ie[vvrEd"}l\9>gÆ ᷑Z#!s#r2f VSr2fC湲||[C$ѩS6=z"><u;A3Dr~R[rm4*7 7GFʼφ6+kCauvf:oXM[*1h5ڐ+Dfȹ6ú&ƛVۙr]Bɉ!v}b*;~y=_Kn`*}T[ ރBvAZTxt2lI6o .L8uG'Ŗ@WƖrf%N&C(VUdBؓ|x|:)6xWD]w+m~ q@fGNzztܙ{K*i=AҶD'Cvd ݴMV?IZȔ['&_,;;LAL$ aa۸k{$B.=W]uD!&N֭[Yf Æ ks̀!|„ aB_r9D'OB: @=*"K$C:;ugײfsWD6,ʌgKxzʪiL,&n۽dRJ-Yb {Su@CNb1T 0:Fin6hsjt4`sIe!{]^kKYH*9M&ERw)LƏMh=XRTByToj꽘(ז&JۄTb2Kh23^O(Pl~F  D. TDّrUtrB6HL uaC "Z/F/.=%V<̄هM'4tTS׽Ǐ'T ";qUT2 13eX`%%)2_OS)]cmQӊSsWy3.B̞=<3Ē%KXv-?` 6i4G>}6lϧ3p!,Y_@Νˣ>"T-RulQB]κjrU#|KM)o-;ya\n~z|狆soL4IDI]xK΋:2|H͇Qу] M5!a1|:r=~FJQX\āY(,6qȄ+tqaݥu\M]deaX /]?M>:S!rrO޲D&镛?4H48R.3t%w? !4i**{PX|h|HA$ ok| s1nVmKLږfr?l`LͯcB2wN .KP?b]U r nh-{m/''={ޗ_~I&`bCu2wP]/G$X.:T+pbw~U{1ȐhBPZ%#!2mwq{(tGAFYtteG"3'T1塣7{X7XLC5dd>ULrr_")Iv6ʱ3V; j4bJ]Lk67q$Bg .:|@xbJ]nkƧphT Tê9UgJ[B WY'DR[I \sSpJQ׎OŮ[%ⓤ4OTuol,$mT?ECy>ʋ}t9sM*A [_ u$ﴐ}M4e0)鰃=ᢀSALE(B{nZ2gtq#1ѕg櫯g甗s7rJxh#mT^ Buܙ;wRPP@EE={$33w3ym]t$&5tĈ߿?"ϊyn (W,8&6RU]CFd2}IJ4kԇ_N%?sHYE.3hv2Cx{C Prյ!Or$D)ŽsDj qWq-ǎ`vW 7#X J·#D1 Boq9^;/IJw6 CDk9&A?_'D3o{ؼa"DLߠDc xkmǎ@rrq\3&f1cD~F(qs9jXj[m(E0e,A5αqفzQ`MH&=n̡w iEqtJšǬo~Oy ;kЩ2ŋ3k,6mĐ!CSRRԩSٴiƍ/gРA#Hf|ᇼ{$''ok+(~^4n&8.VZD"GI NLvBa3)<{P埲K(G$iߙi۹]7ۀۊ<.7%Aw |=XFsz_-&v5@T0rHĎ!oC-<~!O?tѭ[7nFnᆳnE5TIƯ=NXɉo aU7*OI@8ĝ;,;\!H$K.K.m۶QQQA  >>={( %UQDERp("J"(v؁T}|G%%%QREQDgٳECSEQDGFFFtwf[/(EEEEt"wDB*("(F4EQDEQD%UQDEQDE?PA6IENDB`mapper-0.8.1.1/doc/manual/pages/images/find_objects.png000066400000000000000000000424741325266516600227170ustar00rootroot00000000000000PNG  IHDR,DgbKGD IDATxwxսl_dYc0^lꥄ@ 5@BrC tf%sؕQ1gvgϜwΙ]B!BB !vS :B!qi}PB1[}zB<]ܭ *$.$xbH"pPG}HB18G>4\ݬDlhd$D8ԝ;MNB>N'snOGog1 !BOQOSNz;3 ug 9PxĬYH&>]UU$ͤtB "¢bve:{ۍL2ed:lV1:t@~t@wښjy~*+v\.WzK!-j>SO?\_FXEKj(sJM7M*nLG!#]q]V3/NwG@t@_n5U[q  SzG!1۶m۠j+Kw>ώkF@O7mZ$B ɤe˚(ɶ2ǡ `ԓsMB ѐCeVHMб[P{査:`eNwG)E$H1iY8Ot>4M֊fYB!zqLl =#|2OF0vF\H 4u4G9dB sƖ0!7D"X]uYv 9Y.˪2~xƏKnn^D"I]]k׭cݚtPhC_!ɶA)P8轴sj7|Fi}-ȧNd^p4gupy8rd.%׌ţx8=bYHqwXhz!3ۍm[$ 2rFڵ1^qKL .^Wk+囩+J:w;HⶏE>[k`ߴnbyHEO$b&<έ师EqPZR+U^NõԃtMjlL(rxe&Qfh$G;UѨ.MY8y$ lqrp۶1M`^DZ۔*Rbi N;n ^u.QGu]޾+O,׍Ս=ſKypX׎[^"8.κY>j*RIR FH#Ш&Lm}p, ۍ}Qtj"TVKX8u1UT5Rs ya_ϑgaS7v7 '݋)Eנz Z2i:hJN[p8‚hDZ>S.hn.yw^pSJ0od۶;-Dz, `}fhћ-VUrC0k}c5"T78/.v^8Z<Rcl*ݏd2 o]/rӂH_tj&={0C| O5RGoEGo׽u{V&uƯeNqo> T P{ob`E7^K5aLO֐ }NQh~rtMjen'utJl C7m+c12>S=(6X#d1H`7bCbжia~#U1>? A8N k.kȑx ]-z Ν;۶QJmmMsY=cyJ*.qJnrUK)lDriB"i: 3tX[Aܪ@^G鑎Hm$ ( *K%d"1[."u$IM'87{O 8&/Wѹl20I6lcRF#H]C#H4./k#ژw)~lrlI))aNe w򋇾SDMmmߴ~9LݕtwII߹{PBjHē6 ^Q_tVxtjPWiݻ.bU נ0/Լ`) BŽq^!Ec4!s?$HbTOd4\dYĕeZ6_R%+@ 8.eQcin/9NMa[*l&hdQW[E B'=PIem7PЁ+J),cI4 2@44aC7ZoPsu@~hԈ _sr\|Vo07`:- <$Ddx:Sڼr>wy7EChT ?w{+>r"Ǯ55= ۶HoEUʖ|{Sqa޼.21T@NDPXy)7| eʼnVRQ94t4崻>oBhwHw1⾦g{eYuY8(N'Ȥ_?<1̂Uaw'&\ͿģuXݢ@iZXe͒KiZeugf4_v%7Jng\vV p"NF?W},w:7 9F[m13MRa/'NQe*}N+F W"w\txbzSQssŒR+>&;kOM}]h?=`j} "NFwe|b~C6[F%ߝp΢xof*L ]KUhm,RĒF ~^E!\/M9p\FSwFU.gm,//ٗh}X;n֣=g8ar}_ꒈf}Flu4j⹝qc'miqvY$s8t>[K7<Imt?zVUoϣ'|3XU@ (pDa#d 8(Oa[GGm1liF7.=j#8#[Zky폩ASIb+-$%v1L<,_4}㤧F?I`Ĕ FKc9v,Yl+;5 mdWʼ([땑%vИvOZ=y7Oᮓ<9ޢ;z14Sj~=}|w%&rI1 5~|{#>TPy\TmY sokt.Kw/tŏe]'}bVw6) -o.Gt/Fח1>0~ݻ~oĚ>=`q/j1ђ#4k#2(̯yi s3+< 'lKt or~\; Y)CgdˍTⳃv ui$$yhEY=?f^I_=.7n+!O:@|3Ko m,, D^G+Xfx |-7^u5@~ ]/*0t~:k5+4rpX\ ~6.Ecܽ8纽8{!VFD dƜ{ryI8 MqĀ`n)9h#sRՍ-4ݍ77þCf:_miYyY4Ѓ.nPv={{^w_qV'4vH$ l-ިa_ȩ@ `@ G l"ߣ̣KkUCuo?\ ӨMQZloۧ:NKf !?Obyg3w<\Л.iOC5R Lyݦf:gkQR*+J ^S L `Eem!/ L}z c.JIVÝ{ҧ'p pٜ'pRTPqn|^gYlޏ]OO^ 'odBF{X$\ĨZ;Yzw5(J~_tY Tdu={{\elb<#g|_\ ͣ3,_(ODH4gvr)P%!d=(!rrߟl-jv2 JHd9Tu}lm->˥?<5w5XF%X\@xF:9V"<d:L'=Rv;g)nڀ:݇)H¨P75q2hSܴ8J6.So*vekcZiة2غEY=8Rv֪xfmٕ/*LjNs9DzW*'=.bQtvoQcvaB/Nǥcz7va'rKW(D?p$֪yn:Fw w=wXqKؐH#Zh>]]LeY-f^ "MNrycv & > %}wI ⢟P3@{W`UwoYգ.;uQmGca'ޛ.v^s>?>&c6JI ^Ҋcj7e1fݿyZ cxj JBL-Q;'uZvo?Z^-F75nUu$]]o)Yv]1bq>!>Sm(ä\yesĄ&rTN1nd*9`ĉw`:>Y]NUr2yF8@n 5It8 '}= 98{a4j5(O"غu؍Hf'G96h:b:E BTjkzQJϾtX]#B沶R ~w/9tdfJԳi:b(t;47߉u1}<㉲aɋ] ۍL9tL+xf.瑻_C2y7 el4kIVc?J ~Ud{M>Oo]ߝ ) Ac* O -PܙI3P lKu8IRѣzֿ{F-mçuX.=hލT׾ |.wp0& MUBk d#Z. í $XJSl|A&ϞWbu]m? uxm{4c^x ߕ8뷲nEez5Q-9zȎnܯSyzeg=pr#QͪcSY=Fb&,p=ۅby9/ 5$F #7pa&+R$v׎:Aԝ}qzȁeRiJF$F󍓦͵DAnW,f F4ժ5F& 义t4m J|4ء?קvt>Nak$4<.lV'&H}w5< T>5<8Z>|}Fz>3?BAD4_;/Q@cۮ(݄:v>u63"[Ɗ֦\nAӈ:Vcd[t,#Hzi?3p VD'o՗]W5{"A}G\6hLJRӘé\F4zN wæ'MW  AId8y^ŽX3l.CuA";0HspԤuy,ߒE_kNL&`%YY+h4!xVd KqaI[XItMqa)6Ե@ݿq^yғθlhOVC^p m?mK Œwm\kzCu1S2r^o9xR$ٴizK]iU_z,#x?7i#5桿.0hl~m/\ZokLM$v w|{e֯w).i7VyK hF}:[]WrQ]=yYJC=Mi4@EKOq{$3~#iygݨ%=lyZevG{#ǟhݍ> ;[j AϷL3|ߣp멓lа0'p@=PYiD<11 ia&潸%f׃m,D"I0 +C &'x]%b9k1G@s #YO3H}E.@Ӵ.3 @KLl,ۭB|y^I5O/ .WV7B1H!B !@= >8V!ķ$zгB ayE{,]S!@B!$BH!B!$BH !B!$BH !곯]dCٳ:|OŊ- ,][) 5f/b9/;he!>c< IDAT\qr_' BL+4棔#w朻_# !ȴt]%#鲣bHP _l4m@__J++xR$VY%bv~$SpB V q'<J1 nxnLJPAef_@@d$W}9zx=(k}F 'w7_ d?,ˉ ط4̻l2g˲,*޼ٳ qhTWwqXn\^R$7< &r[Y|-#BB9}K;Jel}2+[Olt3/^X0%r(+~:?]jݳ//9J>w4sں/U-(9Ardy/'Nv_b(QMS`\0|J ߭|wge#.:u48K*ĥg3<_M_8Kz+e`̙Oq!~њ-@r Hѿ#I  Shl Iz|\m`LlǤvcиR*}Ṳ"vEYk-9Soj7wc9 ]/Sam23t9Fs}; $Z#; AÛ_BIi0iBi48 {dk+jޝ;:샯CֶVݛz?6~ >an4izwbH$QC{O*hzN䃻82{tpCGe:\vnzj9uŠW$]k]ȳSpߟsͥg bu6q: !蝁J_^BF&׾-{Oχȱ8ha\p:r>>|6?î%73~ $_e/;pW.xGK23}\N'?G Hk綖&-=ECAuu {"o\ -F#0 oy;+~"%q; 4 XEUl9$0#Rt9 N18}rܸa޿|4zG}1)$'ls'} 9#6ct>а d:1:_F@Bo\~\:uCK< !yk+%|FdGGs&I !K$ U!B !BH!BK,8!DZv-p*iTPXHN($C#|4M9Hc>` k֮cL !M!̌RB&ˀ/3ECC_uu|&'@B!!B;m`A@NwWO)eڭIQ^:?YLMÎoZo0_U!$Q\0b/I(/Ϲk=eGs=SsclvQ>*SpBN~SN`c5}xCԦz=}no9 !(QtozqTN+H( {s9m%ϖc*R pZiw8r g7`og#gh$>Tz 4nG?{sbɲ/ϓxқy?P\͔l;Wh7͏?䠡qL~§&=1\͹uNr z4%r3|})Ujuer H1F@Bsoy4/y%F@>A-=jܸuFH$o6%Uqlܴ?]cﬣA:'fC-$) hBJGh> ڟkcSZ6rGSg* IvC?ʪhviq[GӀ6HA3aY˳њOz*G/p׋")|r$IP/yDCuv)8!4:a=#e>/ \x0oFa4SEr"ZpO?i2,9k^#> Ak綖ir[[/YÎsМC1v}5k1aBH&fY,;2sY444H1UWWDH1$(H큋i2'rPCyI73J8:SJvrP#$Bv,65K Dׇ[C.⾏밷]鶗5 Pۙ>8^ϺH !Nx~5k/{;[sh bgsǶ\'im_]$2jgx;x_yÙśG].N8m [e<^頔‰oc\^iŵ6Jٔ?x㦳dIG8}~Aƿrk٦P*ʇ~ÂƱ%da^;m4 ]ʗu/qRJ̭Q^ tĵ<)RVz87\{{:'>C## !~ʿ>42 8}q?Q:VWi(:K?Ckly;9רuRύ~/j|Vbs&zpPdóby=hY.ʎ;N}+>䎱/ЪԨMz_9yb&.⚩:9n%R[JYBkfaglu(XˌgѪ`Ϳ=9E%-ngl>`7r,x;}0 7(@ ߟIHS(M}-=|”՚(kXf3QO>!׶QEK(PƦ2^tuO=v[{R##C5?Ϸ83I. sTͶi&aBF ]{;{x_C>M#t,4=cSư үe&ʯE-$)n ±3l}T9y7zkvANX8 &^O $ws}y[榍qfx&s̿oMxoNbd5$`ֱJObPSRW6#r_UT!܀]Ml5Pm!}i2'bp=:KK/?pƯ_dUM#,\p&?O>t,a%P(k/~t\QIdǼ3hnƟx#߸ry^;-͸Ot]<:dwsmb%X5O+bY3F<{8h'$#/q /,ѯǔP8f6'i'rY}ss}(SMCgݮoÙ%r?"X֐h($s1F~nepΝ~(3bڃ=?]c-֯]C 7YLV^Ũ3}K.ދk6xW~ڹIKOѹ୭\݂,YaG-mB 1//|ٳgث-#q_䎗lmxYq>mfj mu.ysWQ ^@#^d 1 ! ߔ yŬ@L77-*ଅS}X@B|4rgUjC`ٍ3J_0?-$j -4lox+ jb(q4Mǁ7:㐗'$rbLy*1Ba(|H ! 'z>CjkkAx4Ä %Cĉ  !B!$BH !B!$BH !@B!$B !@B!$ BH !B!$BH !@B!$B !@B!B !@B!B !BH!B!$BH!B!$BH !B!$BH !B!$BH !@B!$B !@B!B !@B!B ! D zI !Mh]O uskZIeUECjL !M1 @_A'N1r%# !Ġ427)Q#`ĐZ/ !Ġ6VKӴ^[?XN;O%+*+vo:7֫/#//x l3{-iVM6l3Hc,BuUcbruV*(*FQqQUj\2n.' yosAz)I2$H5m"# !Ď?˥< 򨪬smU,dXj%aYUU|ǽ^(p8iMKF@BA@h7@Վ˲,>` Y uuuWWWc&֮eqhTT88qT[H!zqmƲ l1۶;.;k{QOuMMIp$W$ʢ=MNN.J9x 7$YYY #Qv}'MB rݘګzx,Ʋ>Ҟ^XH^^ :F MGγZ?|̤mR8\9d,@|>_9sXj%mS_@" H:Ζ-[(,,$̈́)c`ςBsAl~sTW(:. ''"pT888vW" HR^^Ƅq㨭òl Åa@"Υ#Fhrs(..h4F4q͏B!Ld}B PIuu Hd瀓)$hSl]X!''{[doqjk̙Fr Ԧ `̙~vW5r( $It]Gu^/mǬ躾%$D7s2V9iʔ`Gl;PP۽^Meږj۶; ԩSyw%vq݌3`j@Bx̶mtTb۶4+OBPWHWSunY/3؃%K' v{PhR׷{$)588M8qsL]1  QSf;8dd"I<"xjkhxq]=Z/ea&e3z6n 3}ø b%$ ⺕f膎kLs@4hJmZ>ue]0$ɤe[(0 p膆w^S(4 4=brrsњZNc@B5|:騮U}Aۻ^. ˅R5}ZAfv:]aE)Ev(RʗOBBHb;Z VczWW&$ G@ǀzI !` 8h3w8C0r%$4~?e(Q&+5Mo^zs8Cn$F"***zzh86\pI4XKH1BXKtB!$BH !}O9ia!@gϒB!B!$BH!B!$B (!k #TWUIctCAa!9Ǐvvb4i%Y l,]]L я af̜d2sY444HsHAzvҮۮ@B 2 u4MuUHA@7~Ow?Ƈ>ߞdwat ۵UOe;b-h>$ǀMR}Xcrׇo|qOo t\0_>73d7H׾%*$J>[E\|~Uv0꭬vLǿm64vm-*z]%PKU^xk8fZ ,wDžx_hmz]ߝ6@o8qVJ94|r7gQJ0ğ=]NIRo3;KZG|HH{7v+/f)gq=Q{pDNGlMG ߢX}dgO ]Ÿ|_՜²:o|B3?է/dsCe1ud$>:n>iAͰ9򋽿W1n]۴CGvzR%u,殧"FM#Itۚ4ipe'1PQs$k'D=O#a9PSm"[ 8\qu8knvU{qꕿ&SW{ 3bQ:K)>i5mڵM[ޟVxSn(cS]|w:Ϻv[{2vF lF亚n + C4rCr %uD*{e7d` Զ) =PLQŪjPѐ5]u$?H~X/9 y`+]@Yc(ڦ@l3kz(A(}RuЂ(ǜgi6ϯV8YW1CWRI$SpB I9s~Źƽrm#hiv[OV8 mOL(_oé}PG+.jG ;knnET#t_t/w`ML\kI%i{:{LFBnԷߜy][?^K'ܼtYҒx\_,镤UIr8IV0zs6՘^ hl؛`elu qJZ+c ǁ2E!k:JdfzZeѸ?؀#kLͼn~hlG8nkv(:y9΂رY&y *4:GhP*2 nؠ ٨eTQ#hC0p\r">IZ+C(n@Qȍl (RqC3#7hAD:{  b[PWKr!Tx)I{$-Ǟ`*Cл}L9[G\:|ƳZ`,Dz\؎O炐pѰ[-7)d~5-v)h3Wn66jCGhG(EvЬ J0V JkҚJo݊&TBrР7^| V+`]_l@hC>|L&pI7BՀÄAԋja '_͇" [DqAj@q1/c4 gĥh-/5O j0Rd>fP|>=6/0LUpRa>JWu)T@2ʠAР14|%8&15B'u宵yq3"p`ҰF AcjLV2ٻa-<h]7JX0Ԇ a`t܉ 5ulIENDB`mapper-0.8.1.1/doc/manual/pages/images/georeferencing.png000066400000000000000000002172071325266516600232460ustar00rootroot00000000000000PNG  IHDR0NbKGDtEXtCommentCreated with GIMPW IDATxwxU{E(P"(*HDEAH*E mlW~svf왳 B!B!B[SB!B)dЖp.B!={y!B!Aonw!B!~KoUX.B!D]E0W$UnNB!x؂y^ҺúQ0 B!B{@^pɻRS/>C_ pn-)";~I_( t.l626bXPP@Z%B!fٸzJnPW y/*ps{Ӌ bJQRgfIMIC FႳ^MLƆ3{t˺RR҉.Gxx8^Z& ))\83gQB!,knNvyB9'ΡT"5.MPOKݦ0}M\(fShC^.ۏfͥغECNqssl6cww7jծE ٺu[uۗeёGɂ ^^^_|ߎәԸ5:ʸcRk !yB9'oku5/2-g4C_(}|qxll5kd-ZT'2/Ti߾ 6̌BX`6鴴oߖu6Y׭FzFZ@wL9XXIqW01q˧TE 2S1f!XŠ#F_hfP{1̲)|ΞNX϶8n cwvc5M^CºB!GP'7[sr*To&[ }RRY9Yo=Y͸::cͦN۶1l%eXh44n\M䫫4<@9&lߕlř ]̧OY8K9 w-IL3 E!mhT9w528{t'?} ub +F!2.ɡ$H9^lG* ax"+W !BAq h^t 2NPlZu#D5+۾bR 74Z fpy ҳc̳ `c2k`֭Z B˒x[U@7rxNgbuB[ kΊ74]5xz8z#ɂM7 O']fM}L/oc]# <;-ZR6bC ogL*{}&۝ : X5̊MFz|(NnrEԛnsJ {/3ޝá,㹓3t'nĒmbS%SN:Rhuz"%l7y j7J+Ӣ##9lAC;&|!BqP/}(EOH%l&X3 Bf7z3[ٓ.9ab 8;u`4fcdѵkBzX!6FA\uUFř2̃}XU6ZCnwplFF&OZepV~kc3Cy|HFk*m` ddkeQ X<9/;9AZbU놿T;oNaxi9gd'oIX\hHAA76]{.TuP&lZ=:z`pdH8Mr4mߟŠ'cF4O'-ŕ?3+IэCҭA^ȼԌIS&2u+FGͧĺln"yiQopz+!uuK1vǨKQX,Dj-ز3tʻ2ezn~V%XԢ Tk^)bْokNU6\\U*jg|]R9F]i篠  c Eڑ327ޛ*"3G.b+[ z2>DoFLS7OJ@bZYidcU?uK I2%TTr4nMDH Y=B!^\yzKԋ51JS6a3OVaJXYʺl7ϓ`6ܞ7* +N9A=MAz$4,/ھ7N*л촂FDQs*MiU4KtX,}ܽWH(ZӬD<2~Ԝ_9T#~xm9N}ZCXY%S&Dٖtsg/s-U;->I=\Egmӿ\c̜ҕ2Ift':㲢 /TNu_T5]@9ވєG&Bq'Au !o]L*Ƌ0*TOŖo?bX|-G1yY )& jb*lT/[٣"##;_]jT3zFŸkYw=a`>i  W͞t<=w2HFZX\Ǖ@o-+(:-%# 3/LEA1pBŊj8[qdܞy*wE}FST霼l /mk !.fq-^Pl;Q;?%%TJ_\sGI@Vhxuj5`3S!zCxiܵuF 5u6G: 3h J[/~?V9r5ghZG4\ݟdr׫T*3Ѩ5+rI30iiz FM8DjV]sA^-`2G` Z{`V+ 7$E{3 T f%E6&m̙ddfғO8V[9EŝH|4V1Po@Isr*<} ɉ)<^\KJC)yH/JK".MxJuΫ#*nX!6G.wZPilO2S#@Opn0"ӯ`H%ՃwFtŔ3F]b[N3Zbcq8X}[,!.66_] {O+l,佫i 5Sײ& 3t.z5Te Wшzr%Z@>6v2( Z2Qqok ]\ țtj=oԡo3 寫Yh\=Q|FXw>=‡U- 7V4vaEIg#z?:g&Gx'H&֡}#LYWM8'H+US0S_FkAڍ D]B!FP+n+J7hّk0 11$"&llTjgؙM3Ĥ$1(Bƍ=dT*YYY$&⨫TjpuS&[{oRfV@炳Z!,z ӮkuJF9NěBr5 v /CZ& ܝ$mWn4-W!rF/5 +,~2IT%3?U4 SK'~yP_ԉ$lbm+HֻAMQPTKmryVObѻjFRpwVXܢ.qߢ̜ҕ49,BqvPq;]Wz؇y\V\{32:U6:7no-j{nY* }αg)zw9*Ϝ3`BN&'R4(t ^9q5 !(GMf%粘-L&3l"Jc[dQqj,=L. ztZ-2̸^Ga61Lޠe]͕8lj e=!]OO<ʼn'h4p[ݎ8|O z=~j՗sHKK#330dzg^{oo/=qqqtܝuq {vl_}Mjj*w7w[Il6'$?gqNQl..h4j>p*gDʙ3gysxFɵk|{1hHLd__Q?ΐa#V GA˶t2>>ޜ={?EOsn\]]c߾ۏu?n:';;ǟOzz:r9ء=o >'5Wc6p*U3y VB|BKtJEt(Jk~YGjj*z;ƫox̉Io2|} (B< iz<ӿ~[SNZ0jzC}-x$̆u?Ӧu+v=_իU GG3gycdEf;u3'g=(͜)Dsσ;u둝̈́z\5kT\xCu8q^Ϯm[8ycǼ^62x 'bXrc([cpVXƯRߟt<Ӆ cϮٵ0fڶiͱ#8vM4&..(xYOCj&Nqor`nOO6nS+;cqf$W.giBAǏ|3.]L`` ř8/EE~FvM2e~kf_koضe#ΜǛPU kN:mKlX3ˤNh'طN3Nfv9q^|6+B74y(”3%xzzпߓx{ou4\LejԮϦ_7 ҿߓ۷v0bP?sc-1uԮY"а~=cy!pO$%÷':cqr^5WWW^jz649^EnSOPL5 W,c9|xi IDAT k׭g,^ӧcLx>%!99ȈԮ@Y̚=[2i֭5QA+o0w'3`i!B<A=g͍z>փ%Vpuu 99+W8{l( fVP)&U| //cri)NZ vlP0S؇51a̜Y x}}}8rhN( 5Wc}]N8=:}n̓cgd0jsxyyb2zjAjh4V+7^{VKJJ*FONټ*Wo۶l  x`&6իOz֩MeVLƽ2O=36rtОtȊUyk{<ݿ|\\M7yݻo\rqM&3'N$&&oB!|lfs` #M~~$y]r..LIM%*2oZH>}Yzu=vr1X;wR_]~aQBTJ\|_x??_@5,_EKJOq:w@F Q'J l0GZPTubkΛ ю}؇t:)^}hԬ%gtqww'  EvTQB!CsC:hR,a olLIIٙ,Y&e63V3^^dggS>b{{<ҝ%}CtdfeQR% kֵ3!4͚6a_]>4GR,f# ƍT*bb*ugRR%N9߶L ɾ/KRr2^^X,"ٸgw킏7IɸР~|4wKQ )SƟ[`"ݻv{.=n /8ڱ߼NѸy+"_=[TTهC8[,F:  $":_7o3t{'!TQi}&NvFyo"u4!0$2ehش?Y̛S:eʆQv=~Iu7lEvSQSض[ZڴDtLNPYPV]ѶCgv(̕+W?nU38" 8*5HIIq[,z=$UjԦL0C"ޣ'N,ߡswÉ,_qosGU4lڂp*TƌP~c|>߾_zf-ѣt}1U'?oߏ>f3?4^|U*T"B%,.]vY2lKhdyCY[̈GS:t{'Οw J~im]Qh֬)͚5fԨQñ.,, EQvG8x`&OBHH=y,", ,E}O6iӪe|5o5F?OXa# Օ!kĨl۱Ǎ%?_ΛOG{w6X,_#IMMj$%%ӾS˕9x;w&((iS>h4 4dL2tԁ`0Xr5/ 5kTj*7{lD8l۾'?F0e$vƌ% ]::s%fWWWl6S`syw[Toѻo?@%*wElӦPByq[V6ѵK'_ΤSփMb09nfdɎԫG&SF?,Y~lظcUԭ[Y~O?fM8mؠ>?,]]11h7wYthߎw(}.Euwww:vhh21k;_ڬ]V1|{Tmоeqש].z sDEF2i*İhBN'$`XhղuɷkNj>v'$pYyGN=?f˶;mFU8:vsx#ZZUVZM֭~&O %u Oy2aܛoҪM<'UV?B <ҽ;o6tB!ŋ e>ݳCVL*Ϝ&%%V-[ߪes־tc].'''4Ǿ}Kbi,OnHu!qc믓jcK_G^^xѣegga2v=NNz_{ udefS9EP^=JpshJirL.-[syq;9;䴙^o(v۩_Ѱh|vŗcG6nbM||E1yzz6;eot  xǣҗNUT".66[@<=q R'd>EEfԮU/ٟV@Qpuq%99I!U!?j@/tܘ7غ7?ڋa>sL^{%ƎyߙaMe--^tܑ_~dʔ/瑙ɫ/T2ޏK/]cQ:99_̥Q(1ۘg{54 &~@=9{<ɫDn{/..:u}q-OV/>ϤSqvvUfΞ;r]91JǮفggX۪+߰ժbSl[NF鈌MV/~o嫯A*1|h߄zq׫WW.2xp4Z--[4c=n̝5ΝCs.D3`,q=8>:BXX(ӧLnƫ\raGM,?s ))???yAA|)ej&?h4R>:Og}3O/45..͚6YӦʹq) B!H*rkW䔉YbbbŲѠ~=ibl+:uvL)Bj5mZ@幝dFXrQ`5oo~P8i2mZ^ 9!u^\\zNK:y4Ji^|E!Wuo[Mo wK =B!DW+p!\^(6jJƩ !EP(6.xpyyxrQ*W3JE!pPl=z//vԅx.ǩgؿ?II!퍗'"% !YI#!04B!ԅB!ԅB!.B!.B!Ŀ"Cٳq#4B?狧QFٳqㆼ]}}uJPBVkQ4B ;s3g;gQԴlN6߷v.hզ4BK4`unOMMe("ujשKP!$]zmOq wԮqu!B3]Q{.BpDܴ.tA1sl.]|jSٻw߿}/\Șo\?Δia41/n1EnKJJbG!D)sr#GЫwJ}K.e.nol$?V^)Sȸo`Xǜ;u[&&&bB˜>}/}EVVV\o؈AǼo~oݻYb0BfBB2`{YVXl+عm۶zݿ΁nX]֯Xo .^ćSTL4pΞ;W}M2 ob{ËSN߷`xo$F FŊ1*7]gjqFU?Wc8t8+W2~~=n,3f~Bzz]9?ڵkLHH "11ѱ}ӦMkwwwԩ.3gݻ m3f !!!B6j9p}>AHH(nDgp[_*]}|w91O>8M4ĉƛL>+U ,,Wb40~=/_׮2k -Vfcgdd0QDD?e/xÇĆ HOO#xoޟ4E}ψCv}iii<3TTskaXoʢQF=lyyΞ=JU tuNTT<~%Yr%O=4UTa)[,lذ^w7+wu۫Kps߇ҴIc6iL^c]X=]..4L`ڌ .kEOzhؠ> ԧAz|B\\]XxNNNp@px9"cxn $䙒ghֲ-aQ rZL13"0kժKpx9v̎}]Gb'R6eʆA8q⤣̈GӱK| yo"u4!0$P:tXJ>OY <7ճEԬӀZ?~izGPh$Ujfط '4lڂ F2eGsi1337| =pvèYADD歎Ax>IKK+4nD`` 0y4Q>6oq{LiGg'|ӰW*UE@p 5kۜ :oǚ_n{f`o|Gc_uFM6l{fϞCovʄ ڵ+zb̘1?ׯ[Ff͚уL_R!gPDFٺu3sN 8%KSLEW_#,<w/*Tbo [oll,|Ъu, }+8W^7L)SqrvٕiӦ( 63>|i۶~Y_nЩcS;{/ߕ+WY}9Ϗ~Lmc9z 腷6 c?Og/Q=v8rZMʤSC.:}s˻ߢbL|}{wWEpG J#Ғ"( ]k]kwkbwZ*"}?W@յ/{gΙ{3瞙a]8IX$A:uhODEE1v$:tC9`Oi}GO=)&Qw1u&ڴN`FXӷWOXd;v g07OKSL)mHbbZ@~ivM֭3MeUKV֯_O%h߾=W3B|@=+6l$&6qcǢeښjRz5tuشy3]uxbxxx({ZV\\+̙3055n{c:KK˴8dpի׮][uMmˇB`̚5cFS`A,YJz XYY} ?ؘQF \;3gم52&W:䟦1, +/\Qݵ]ٺ] +Ueԭ "O,--3__#C#yOypw{%'0}tF Nv퀴;wdӦtd_S_FGchh|2ml?y/d@>a`OǓ't#M7Ri8wD~;; +6>D~}zQQSt!f bbn\n C.\$`쨑Z 7ꢥE.P.GKS+CjOIN~Hz}fRRRIHHH|pvrd/?gH;wV{2A.\&=c,9`m̙3w ԯed[:YN};wn<==+rqʕ+3J*2ƍ7ih,7/_'O| c`Ϣ 033e5̚=[>휪9urgƏϯ:es7xFw\t9f{'_|ViyRXQVݻ*eٲy6nY3 *-տK~]ّ;wn..*y޼汰߫\-,qOc<11>s響[Anϧі[hQ>b-!Ov[ja~ݲeʮ&hkkѰA} b ̚3/u&'޽hjjbo--u{= sC D[[Q$'%eXǞ.䎏7zRSR@;W\AddyYZݻwʕCT/Q(<ׯӾ];*/G!w׏NOo`H^gW\`7o7|.:dr5[cg({c̨9Ol3@`x6mBF rΝ(<( ?2ͭ :giڢ&>!1c'Ph֩Ԥo? ]]]*V ))нkL.UOyrW6F=hִ1D>_os/`iiG=gIIGmӚ9РI3 |ܽwg''JhD oj֨Ny|_8μExhܨ7I9Z P]]]cO!p]M939Vŋuv*/Gn==թVNo/O7&v+8v~IPDҥ100a„ ̙3133ʊǏpB]vcddŋ^:rB2LfR++KƍCT*;'E>rqwJѢE3wfϡL( ӿyW(XXu]tܙ3o|ԥOXX#<<ZdIի4-hͣDDC=TZ366֘r lڼ ʣuu5*WD~}=flZ|GrRa=Bvu vlĘq6"x1mfx.U(~\!K?# 1hu2j֨M:l$tGSSU*3vzzG^?Wx2 (Tȝn]:ez]@jtڃ)@]eFömNACʕIr,Y4{{ عm3#FaА`fA,;!1r4_˓cee|66L4'~&P7ݺ}3gҷwOa74'ҥJ1ahavu&'*hPܽC.קUr'&&{^q3z9W\aɒ%DGGcjjJʕ3f9""Sr](Q{]jhժBzԱ.̜=AACx9&&&xz'!1ƍƜ9s 6,,,APY8ڶ᧩ө#gР Gbb"NNԬQslܸ^бS O-Z4g D*urB?1^^Csm:t쌾>mZ?}F[[~fPP!w·"}@]-juQK5m@'R?|Шa} LϘ9svOe/տ1x0.w4l\6B|C֯YEJYj< @dO]]]vfY1k9xZS_N$Y,=B)8:ؿ3a?166''"-B|>d !z8;I!|eB!BH.B!@]!B ԅB! !B!B!Bu!B!$PBy6 ^y֬]+B/=POJJbDFF'￟S óDiRRR̳b*9*'B| 7hն=EuD՗GFҰIs{C !>k\\QW/BES8>!: [OaArݿߐݺУ[)!#d_}#1,7ztЎÆװ y-1kٳ:W"[QijZaou6y-9t2ٳ0kիpejթu~' мe(CݥFB(\Ke܁1kɍp \3gf9c,QZ>55igQNT ?RB!{d-vvz*V(G+L1S ^!u##C~YR ף]VrwлTWηu>]'Oo2*i Qη ڽG MuiT655_epws{缓3oݻuaЀ~TcRB!{w~UR?w3s~$0((8|~~Gqo∍}gЀ~̞; ظy;P!ķLKKjUcg(׮a @||yRRRB! Iח.:m®]iFYxoޤev;R0[FX2`-8%;v( Sz7ݷ?Gy_H*p'Ww:w'OT>P 8p[^( .^wɲXSʷ."555r:x0UkZ3 ]iѪ-\QII䊕# 7ڵӷ 7ar\)Sn[Qܛq&eeܾcCB!>raFF*XxkG ݜ?N8 EZ~cc#eܹs?Mʊ6~PSSS)m!B|&IokY4\=7J6ȥy+s\o2y'Q|۵sGu;a-^y;vhǵ0{4lFJJ 3޽zrܾyiS&nxݹuN`@ZK(Ջz8Og0},"##ϟW)SxFX47]YlܴYenj){B!g򟴨?}ӧ1g`9{}`fa/<<:t$''C}/^0ooӮ{áGHLLj@DEEsgfjJ>rg[vڍ@MN~:ۤtm-ݗ@!{ Իtϝ;wٻo?AMM~-^3Sh(_'&% ]9Tu]UB͵9K'm:tT'(oT6yYU_B !B%P044IhҼ%_`thߖE<8s,hڤa8;s++ AJJ Χ_^…ݕttt4Ē@\oRR2 dl+%}J3l޲ݻ[OU4u>11kյIhkk^"B?xTJe*0~dbcc޵3ܹsZ {*Wɶs}Ę6[0vD;a_jKm2u:68RPXg+8\ODMM];Ǟ53㟈 ōOA[[~}z}q-P;[[*WPQO5l,{B!F]]'OYd/tkĘOɝ[>%i9\HIIFMM [[RSS?vǎ`AWT^<#ffFFĚU+FJ`w)[}wYoMMMVX <{}z!BeZ-=W#k& D?Wә`CSh-"B1k9x"SSS ptt@]!v!'5{>t_PSSو g ,!!t`o( 7{ϓߌ!_!?~Q(?O>-_u!CbW{|8|h_!?cHi IDAT)}o$PB:B#yGRzDOC.]u!YrROt}t ԅBɿs.,X0; =_Q6l8zI2111R=Bg իF%N.mێW(Zu5,.`2_w3|1ku2~1Zg?f2u:&M#&O TZƍTR]]]lmm:uW,^7,\֧+Wba1&s.v7p_>`acϽ{UX~{prr}ڹ#seʖ+_׮@A^?iP˘?_N|l~=FGBbbg谑<|<օ5V&}:ŗc 8ݻal(get72Bbo r(6mC>|PT)ʖ-Kٶm qqq״,x וx)1N.2Q($''Sn}}xiW[F&89`*]l GM70[nͲɛRϟ+WX0gASY7\ +k[LBB $ԩK< 6&<<3SS^xeUmڲ->T_~#ŽΏ;G@rr27:мexzf%J%E~FL7,l۠1yQ%ovi^&y' ={)Y<6)={U2z,%J+ߏwɲXcneG) J@-\ 8sO8%y-i=ٻ UΏOi_,Y9:tc7,l)[ޟmҺm|d߮ &мysQWƍ_ү]7ooQF iԨg~ŋ%o޼hjjbffFHHUVQX1 ёŋӦM,,,ȕ+~~~~MܵsGk|p,?]]]RkjbY^ H6hE _9.W.]444xJ7W_@.]U&׭a1xxNTV{KشiRN]zJzƴi Ls1,Xn WƖi*Mm*bcѾCG|J fu*ߎ)?sve,ZX޳5k~Zf̘έ۷)U 7@ѣЫgϘE||RL2֭ZRp!ePQ'c˦ %KY<-ONIaTo+aܹs~U /YF^}PU)\zbƍ IJ ۀ(g֭[:|q&G)^(˗.x:qu9,[5;v&**J{l kV-vP xđ8ri?M4o?6o\GM0xw`Ԙ9s93}w댙Co! u ;vvv=z9r''' (L70ϻwrq&Nѣ#Gd݌9M6/_>~YnxyyѱcG7o+V[6 >Wjjj̝5OЩkwFˠ(V__|m߶  w  Sݍ6[~֐>.9)ؘX.@NINNV͚*iP@RbFsN3ʕ*ӸQ# _;xvŎ۰θ\ >777/[FjG f9ܿw_9?GG*S# %ĮU S# \VJnݨX"Wq+FJ*ENZ GSg oo*Ti&܁&fμ'ܱ2'ʗKka\ OR̝IǪ,?))q'ѡ][BF SvlΊRfAگbz0ytL4TZ++K9ܹ˲yg9WBս{ԭԔ~ ]]] sTZC(U҇c'NڵԪXXXPJe444R]l#Poii߿RjUlllPׯÃ-Z(~R8{,eʔ@__j~Xb_իS^=>|l>CF>8;9R~WCCӧ_::wcڿo_(TNa`d_,^ʥ_ve~Mæ pqv̙3lr~~$''sjXX(j)ܻ/忙>]~bcc gϝS9|0'OҥKPX1 7ihhHRu>oOZFGchh]:ud֜y$''3},Zl*2w1ok!] qIbce ##CJp Ӯ߸AtS*0ird={.ir_)S"#Z >/2~Z|Ovًu~pnݺl5J- w}.A%;;;ns*yɐ'11E2ݴi y!,&ǎ+ ѣ{W6nLݔe=< saΟ;í\M5yдu1iV7SzzՕqlol"111GJkMIR$))}?7߰Vlذ[6SXQ%fL4&}<tОX ̟/ѫGw fW}qd_ٸ~ EdU::::kk|쇈Zȕ^q 5[3|KUN.6CɣX>hT5ĄN9CB_Գ?iח044}L9ƍ`cc Ocp_$$$0h@?}aee1HoJLLaZ~;[rCGZʴEpQj֨\'$ 4Y)^&%% R++ɓGeO3+V'Y1=wM./d̨rڒ?~vڥ"""__JҥKqeeH,^3gdTRm: ڍpFodƴ)-!,;[9UΒܿESS0rh(_}Y0^1 ?ʴБf͚ 5.;aee~kz/NYו7o-\MMM&NL֭ɥÃQ,Zuu rxѭ[݈@=62Pܱ=?{qsٳ( 씟Ӻe tr"AT.Y uXl9OFӭK 鴴ЯǎGKK|IJJ"wܔ/Kݷ?Ocܜ G}(Q;4Y9uճ;̚=y a`O](_Ηsg3!iZ"7PN 9ø ,N&(U҇5V0iT:u펖燹9'Ob˶<{3337kBL.|[n<{ӧcʕ+Ǻuȕ+cϞ=tڕڵkcffɓرW AĦwQ104`H:mڲ}Few|J̱#?a@K߼XrE!#2bJ[Ѷu >wթMY.2_O0?/޽`AbZIU׭ʪmV4i8uL-j]WK5m@'segܴ3Bo6i.!iUTV=Zg͘a:vr%Bt<^=TO泴fy4ii ^u8 <}xK H3?mB[f_CϜ>-iERHtrZnofR! SBǹ yualjmQuVAvg 9KG#Ŀd*<+#)uuuRSS115+GPFjj*я`rN(RS166| AΜٟ$s8HMK.WH |2.G.|.]g۾_ͤB',&Ϟ#) Q`hh_!?tFDg[*cdh哛I+7I %]_B!@]!B!B! !B!$PB!Bu!B!B!B|9B|n,?)8~K_{Bu!DE=KHa97Wsg'f BH.ȉg\M B⽒{yp>B !rJ qGRa!$PBIRB !ₜLZ#;w3ɡ&& /?{3|($'%D>}2LGZ/a!$Pڄ߼ɬ9}!,Q2'OٲmBcr ELL BBFAAB_n^fmZjam1kɭ۷0i y-&L㱰O͛.k;_Fu9BM-ߗPbEɃ͛7޽{ FMM-PT ڽ{7ʊ5k*LJѬY3LLL022bŊ:uJd:| ..:߿ }#4ttt(R{U~]7o94lؐo9,I+W CDD .T tѢUL;vLIIɴ?J*cffF@@{׿Sv@r2}22lذ Ӄ3]3jjj*?ԩS&W\pA)'O2LR&v`zfM-[k &vvܻwDFKud<|[[j#օݺHm]RR]v% pm ē'O'5kK q̫k׮lٲ:ХKFA\\ O#G$007o'O^ʘ1cȑ#L0CCC*WL||eZмys7/8ccccԩq~J*qq'Nĉxzz0}tʌ3fڴipulmm`~~hͶyqH.P.KG"*飒.2Nlݾ?/^GaSacmōp>|_ACKM;:E2bܽ~:ʼqqqǦ[}R%}1E<HNNS?DEE a?3gajjz˖9dZ2;Cs.8::0lH5kTW :6)ؿ/?4W!i媣MݴV={1vD];;[:w@vm <|K 4J~Yh1&Lfx.${7JKK/aaa̝;WP!_N R3gŋ\K.UYjʕ+Nxx2M@@ȑ#*zjj*-Zt?g٦N%˗/GKK *ĴiXhn?`Ϟ=TP!m !00 Qrr2\tIz(W/ӻwoD9dy2Wn,Y$gZjQV-/_ҪU+ȗ/__v)'u-TŊrz&cccϞ=ƍٹs'̛7VZѩS'-[ݻ;w.Ǐ0ρyfn߾Mrr2>>>t҅+Wr Ԩ[.GWWnns1sOr2WGѣT_EO=VZͰ!E~e܌eK2w XdAé_+eJӾc}cXq#Cl6P¦-[Tɏud.NtS$>>sg1vt:$Y9qu9,[5;v&**JF@5ϝ k V{[~Gđ>uci޲ ~l޸M0`[:}&#tVƌ%ߙ[ݻecǎuVL캥P(?~6mz/82y;P ~۷s}vFAvؼy3ueРA;wi|!Uq9&&/^3mDD˖-We;gWO9 QQQ$$$d\ݻw߻ D*UVC=}TASSŋsʕ,kkkvƍUV.]5k0j(V^}[yjr֮h3>zccGyb IDAT\rB̜=gk`dȘ,[ǎHAWW~Y5׏[JLLb thזA\ɟ;w2c ]cc#LMٻo;?=~Zpu/B螽|*Ӗ*wqQmG;zH ^Q&c% ްEEξh^}"s{=;;e Nt⮮K3g͡_ƌ\ĩi x\N@zQܻ]C5D1ߘݻwyfBCCs}|۷y[iӦddd0rHw^^G1j( kB 0rHUzӯ_Δ)S8sLC^DDD!""===֯_O۶mtŊСC޽;G*V^^^\pݻwӫW/@1aݺu۷qy,7\KHHёʕ+sMY&>>RSS!,,LCx*~=C {+Vɓ'ӴiSerPVX9}#G=M'O\pAe2K,ω'999Ϻ\\\Utl%''Ӽysqppߟ{o>0@Y2̘6AQ,|QQQ5^ܹs8.^E[[ʕ+rB=e7nݦT$Safjʋr|k[ $$$("+Wox)͛bkkC;O=cXh~~~9߼yҳgOY=ڵjժT^KsvޭR6<<ooo?~|?d݋ͻĭo߾WW,nԔ:ׯٰaCwrrR򥡡MCUGFҥJq^'g:ux9sdjժEtt./^d۶mku^x*~`ddy-ߧzqIe=|7o0bƏԩS?gϦRJ9NgϞM%(]4,Y .`eeUT__*kӚ&Mܥ=Q744\9^CՓDrJ  ߗmw0zx 0crCKK//!))]-me4jzR r6A9~uQ֮^%'f?qJU=mb/mݺ-Zh":wk_++IQQQiYCZvݻw^zX"s,Y#G(oE/Ç6mhkk3n8ef ؟ жC0m[BWOp6iM?yW8.Ltt4 LԗoѣqssÃ˗/+;::bbb챻uC͵cǎL>RJaeeŠApssVZ,Yz+W˚aooOzz:#F}HJBSSŋH|12217b}<ӧwOf͞+כ[[[k8i ƌ#99".E MHij%5

    ^4kHn]ȐXt93fى% S/q3vqT(ώ߷|f'===>r U##OjAM -fr//TȎӦ0ytlgg'}JBulٲl۶!C(cJV!Æ9ȐÇ0ɹu6 41=G~۷o3o<^|Aɓ'>cfΜɳgեbŊ8p@9 x*~+/^̃+WݻwSrN6nȨQHOOhѢ̞=>ڵ<}ѵkWo߾T| %"2ceȣ"#&Բhl@ݺrLyY9橩gnjת)bX1,|3L,l@\+6ۿD H2_!SA(ܞ)=gyΓ$I4 bX Kr>_Ղ bX Iy 䛴|DD 'RM #CC.?#B/ Z_2 n\Ş&"bXǰ TQ<$&67_>;S3Sp%KÂa[!n&osq_>aA"  "QAAA$   uAAAD.  "QAAA$  QoԽ͛71 #CC盋+"Q!$GMMEcy:|`/Ze.^8˃qvr+ bcb]W4aQ=<*qA_A B  H"~ uA&:H uAKtr޽/e h4Aį |&'8ys=w' .V߿?iҿ=ںm;'O+tmX%Gmy)JvxBBB066ؘݻRf޽!)S J>} #,,޽{&:˖/NJlǏxdvR [{?~L4hӺukLMM166LJ3gΈQkbaեkӴ3)^ܕ[sBX`B5/_Fӧ~gܭ;v3W 4nՌ cԓiԒK\&cHJJ2urury a޽,\ zMbb"k֬ڵk4nܘf͚1sL-[FÆ t%KN&LJ\:98 *<udߐki}eL:njCBBK2cҰaC>|_RD ~>EeYzk7m’E ޝT>yJP@:uhObb"we՚u\VP_Q/$%%--ME>D iղ=wUrwu t:wN%1|(Vx3gϒkK`t su >IPK6a<~ACOTٕh1}zv]^N_M2_j &fmTd1Ke|4n(P!;IWWW(_N ݾU̶6IIr--F:xş*e'UTQ\KK*, :X"#JIddd(W)*2BPC2664ew*<vX\dee%iiiJR!Ӈ2#HE\%--MZaPWϥ)G -^0O̝Wƍ$}}}IWWW\bϓZ8oJK͗"#gKBH\.jHGWYCeΝ>!x{I֊gi)=wG~]F*V$˥EHeHe+xڷm>$ggI.Kk1i*_[nBr k2n%T4cd弇ᷥfM%cI.K%Kܼ*EEFHHNNҋgkzJrpp֬Y#ժUKҥr^ppTZ5RskժU ݹsG9yR"E.\XjӦ7nHnݺC ,Y?|I&IO>$I:u$9::J)))$IRrrTP!)$$# 6HakRRBGͿ7דJ(.XC+̓~r.^Xrwwo.JTwhhHgϞ+[RO:.;[*I}UʽxXY]211ߖ"#3'IEtuu%=ֵ?R3rr˵i'*Z5}%[[ISSS֖5fN*u/#rJ;zr)&HRؑ9>$@:v@iT׫+UXAܽ%JUT^=g׫+*Y2 ͑d2ٻ:!-R.uP!;iW"{{i!@o# e636̵53sol8Y:kQ/l˖pvvyDDlkV-gn̙q&*q&~ 011a%lX###NV\LKO;U˗Ϳgٔ.Aݳ;kV.gu>8""^py 8/mFeޢͰ!ظa͛5eڽ7ϛHzܹ3v :uhz˖Odžuk0v4KWlٺÇrbA&xry G1d`n?bddHnבreYb)E|.?߿|dcc/Vf2 ѥkw^z,_[q+ͩ# ;?M7i߉5}ٶu3mZd'rϙEݫ;ֱcG7nLssrrڵkܻwHݻG*U>BbŔw/^ӧ99t &~>}0ytڵi_B\xI9m۶T(ϝ;wu+\R %&&ǏUhn-8v<HDdddp%*dO~zlk=ժUݡ/oFK[.9%90r0;* ٻwG0⮮,[L_d2Ǝ^=amm̈́S(\ؑkV8Xc +Wgj*Ӛ4jr˷6ߧdujo/J*E@@vGѬi >^?:'ϡ.^&3h0*Tt]g =zĨQ6l"~Es籰QGƕK*ߣy]uuu ؈N@={f~whA/WjUPj|ѥs'kxio>re5]Cr;;[]@p$&&wV-_¸ غm;?(.gbEyeΝ?O&_.^l,T\QyrAƭxVb 3SSe;|J[\z}(Α”ѻgwn߹C^,Xm [ Ɯ9sEEEZn2ݻwsΝ<-_:UGH~\ڴIFwF||?ߟm.ۊҥJq^ŇK?yBn=)Y.̞;ߟ~UCCk+K^.دWZE ݍ>4o-[$g ɨRr[edHoИ*;V,[>?Y IDATsS&LR'ޤrMTwsMenM~D[.36GSS;y'ǯ"#IO@HǏ[5!526*g؈ÌS(V-Нjuύ6)نtd%/RKK;&%%#{;|eyЄ\MP (H-ҡgjq y 2k\BoX"H۶m㯿" @9-55Lr }ٙE78Uҷo_ۗNUxB9/G2kZ2e˖-PY~/_TVe#a}iSè1t]iԨ-+Wont?WW\Bzz:ŊE$l܌0~jjhQ)"<p7l~ٸfMԤE &LB<}R%K*:;&--;wR*,o޼qRĐAӫu7W8vUrY]!]4t8H]:ڳl\Z2K\ıCtc#{uLF`޳/[Gu6EQyYZXP{HNN77\]qُ^?._v4iԐ%JP~1,YڦTܼu[evmcG|x9ww풔s(_l\ٲ>z,3O644䯨\8;;ć ٓaXnȺ?A`` nWʕ d6o\rŋg;SZ5=zkܻwO9sqq{{{ٳgr۷y1#\:݋L&z:<KÇU㟨#trukUz$?!Qcy] '11$]įƺ@È3n&&4  ..{ ,QWb;hF&jZŽcW[[jfN\o\K'MCWWժr=$I٢,Só:111D}K;uG)_iPߟkW*y<n:;~һWOVLMSj yi$55ѻgwF0uӭgoZhAp~4n!G0h͚SΞ?O|ѽL8.N'Oa@}2mE_ڥKN,Z&A-],ym[R>@SS3ad455Ijj*zzzx$~= XYZhR4?+V`=Z~lӞ00WS|˗+ˢ%KYx)JZlΠ߰ m;Ӷu+tt iXZZio`nnΣǏ<C~&N222(A'''իNJ+pwwL2\r5kХKΝ;G:u6mݻwe˖7VZ1i$.]ƍYd= B>}pwwݝCLP&D:t(FFFHiݺ5߿? ;w&88e˖KoLtǭ`͓өcw7}T(O=5{y'O;wYf-#^fr3G8.)dg?cG(^5EF-_6aL2jUBzFjK1itBo+++<<3k<<<ʫܱ=t#55㳵aq 6z%1DG:rXq+] MM-n޺ź 89U˗53e #22SVVVVʓ9 U:O9KMۉsپc',@&Sښ)}GKKΪkYb%*U^=!(^ljiݮ=fޕaCEڵ5s,M[(\u/;$ ۷SDqKV"#_ӷ S?iMV޿a?da$B@zʫY~###[K144Oxdy71c'O},)2n9wƌ#{Od2ԬnҸ!gϟgҔix/GͩR7eڌtMMM ;RKKKN9annN-٣ȺiӦy Ջc۱~zIF077gtYz"66YfjԨ͛ RNJ+?ԢE f͚_lYmƐ!Cظq#Ŋcǎ.]ߚ>!ÆihdȈT/;<]_>}US}@.]Xl_֭4l,?;Nq!gv9۳l޲ vįNmztJBn<]jMINNؘ):?I100CvS~+Vϳ10ǭt)\>Бw5iԐ .Aau`nnFzz{KΝpqqf qqL*N-,,ض}#-- [:uhǐAh S'hRfΚ1C9s >zg2y @ GEF<7T|r!e-&eBl@ݺ9~W;wvNMM={vSVM\?aOvR=|ϣG?yX ⁸Wl@2e$ w7JH{IW]kqqA$8s{q (Jϙs$I&U nMb#}eN>-[ٹb}3ӧ2wB>MMMqaՊթ-NSKߌ'u*\gL⽡A__cG+ڈD3 _>{j!"WO1*4qF555222053+G^)q+M_M"#CC.?#BL/RF7_ _A4IAM_###\2)W+|ͤ m.H+5C_AAA$   D]AAD.  HAAA$   D]AAD.  HAAA   D]AA  HAAA   D]AA   uAAA   "QAA   uAAA   "QAAA$   uAAAD.  "QAAA$   uAAAD.  HAAA$9w C\YYL727zx _sϳѓeí TΑnx捈l<}s]\p׮[fP]\uhoЃ=Ra9/^|Î`-_=|ˬ]CXاoÔ#GPk;+C{tk2;QJu;u]2HV- s33MPHQ, Ln=ر342&&+Z=}=ȟAJ9cu!+읬hԔ{1l!e+0rjk“̘ϔiDP' L>cbaͦ:y$ZXX˯: jiB {)>ˍ_}.^˺ :ˎm1jDlewj({'+ Ykz~ͨT͜42Ž8}rRr25GOWQ#cnfӧ<"uuuR?t?q[[^DP,ٷ{ggu%sI275 '0lH* 2viAax?N5Q9Z,Wj?YW~#'Ӹqr-I9OQDƗ)ƼrRRdhe[RMi2e\D[[\;E5Kʷ܈,\GI)--8yJ ?sࠜMD)%Ƕ}Ƴg^KHy;C9uZ IDA7ݻDD`̨kӚzuҙ1~P)7mL qźPaq]?]`](\ĕ}W38d%˔S)[G G˱쥖?E\Kae@`P/Y9Vv.Ԯ[N+{-Ϟ= WZ'0%%aek)wc 5`mH,_J\ZZA-Pʽ<yե?.S~#q*ZFQ˯0U\ k;GjvJInX*Lf-rʉ 6[Ј"0.D"a?<[ٮfͱn†#Vjօ(Y3fB$zD39A-?6I4Jdό\4`JҖXYZҒa# ԹfXYQ9WfcE/o)w ,m.p'[ocrڶN>K}D̊FW]Rtš&Z~fqU_`2a3Or9ZQ'Oנy+l(n$&~ S{fɋꘛe/cJ1׿on:DF;nޢ[9WKF6 %ElLp+gF&N^+uIc$%T>Ϥ-FbwTT8^S9o1K[kW`r]l4hlΊm/zlڲ~ \"͚ÏҽǏ 11FApqZi=z{bj<}=1ܸy Mw3!˩ZJ>"Os3N3.j׬ ^5{׃\xŋevRp䘜5A%ROɔd΂EzʄlmӱKM;AbZq غMJ:/_Q"M0k˖E&:;ހ^=ⱶV$E\q4P޽:XTת́yP66=ܹ8 khKY4/"5(+Ctg=B$)L}:Ot){GFTX7i8S2s=%%+AGGb } "OƳz8@ EO ̛=xQ>bEx⥢'%J(C^ c.\!UT9vm`ccͱdddpX '!!1&`m爵#Ǔ'O?rW^e9z,,os]#G_nݺMRR/]Lν󵵵\".\,pŊUrRX1e%\TĥKqrk 3x]=aǵhԔ"qiMbbȠrTejUS8sFqog6:>4o-:H>LU2\ɑO2iipJuexV+x(łFF!]HL_~!9}<7I}RI4,2,m%W*>q[G&lݦͿ |&_ i-[ֳ75Jʌc*Rb*z$kLQ^52Ps4֯ycy==|h5 RBglBQ2ڴ;Q~Ch5JLˌT._MW4 L|R+>ĭt IcKemtt$ J%&IRn-SjdAfѣEDe]o[$':8N96n/hѨa'_M_E5:bMf:]\Ѥ˕M{9?&G]ʸTE/qzZ߿ٵi۵=W1$]OmCu^Y۶G-#ufMGݶ&k'+d;1U uup".iLCDjqp.AD\q9P++KcbXv=ԨY:u²hն#)9'N{4n%>56'6AKKoo3Yz5S&q ~1q4ttt&55ѻgϾ߰ m;Ӷu+tt i6x4}δj]FEѬi'QV\!t/@Ŋ_~6-V,Z 1S\Yv۫zԨ0!!>T\% =>!8yJ ztKZe7JUǰ8YIũ1eIޠohJ&mDx:M$aiT/IOSЍj#c8ҫtD@Q[T{cb1{oc]{O ^>_?vvvv\2 `.n<~~}b ?$uÁ_2o.ӷWVi {Gt̋W_CZ6" bpuM!&V7u 4fWF_/4QP*KG2yK>,zu2_  o+o}|`}4SVY**~Jɬ9z]1,=2o.D)4cJttҰH;?d6eb4e$t{ C.qv{Mw^g cHH~ TE_? W 29ɼhj(Ÿ۵҇LTM+Y.TB|TyBC*: hE=y$UZ6E0{RHonܘKީeS]B ,E/̈Qc9un -8&!78 ͘bx$ %cRiyWjB! $B!_ "B!G"]_B!I.B!B!B ԅB!@]!B!B! !B!$PB!Bu!B!ėF]@qmŸ "2##LMpppB!>iQRSSE=_7µtٳo? RQ9Xa#ŗ'=PiB!'9%3gsreJwhצ5(~8H-գիUMV̞#:::ٮ;))qq/-Uj8et\^&]vU֑6n+Qc]<v(hC s{o?aEG7oĭ۷ivMU[|rB`䩸@A{fϾ= kw+鍕#kqLܸq-ZcY :/_B)VBwUܽwO%Kut់_o?{srei٢ /_d Lqe+Tơ0H!FE}^*ʖ2oXj kO<f͘-W^e1i¸,e+ь= w*vvb޽ܻMwÇ4?. s=ll ))]ƍItҍCw`nnΐAYXFb࡬YH`iiHLLdt …!ʛ.]{g~/]bȰGhզ=={tc`?C177NfϜʹ)(VԝGn{\rmӿooRy2bFN¬X-ZsQ,--ukx`҄q{^;uv<{Ɯy}3#,MFMM Hcc&#5- c[:rÆb^ ?,nƄ;y679K9a̚1R^^)SdƬ9o/O.^̌Yߺ\DZuQ*+-@SK V0dHIҥ{n:KyJ! #::E݈Z89:p8'._!44森{ 8"..ԩ]Z5SjؼeDq+97jj.5Ǹ !(22gϞQGprtOԿre ħbn߾˖m۲ ԯ]Ŋs^|ݻ>kԨ^-v\:QlW]R,y;[fr^%d|*R/C5U5tTv022az=zgZ~z{{h}kΝt|7j^*l>z_6lN ޞ{3vD?N\\s&9/_>ʔ)쒒1ӱi^ '.. ߕs'CBhݲE[7R>?_v Ill,<5[Mt*n })\XyXr5+V+/#!@=R LL[i֤[ȧTRSS388ؑk2dXQw͙B{>SBtKtRt ꂏ?'OҚ ȿ!- BAg9~FeK^Ny$%%)n K}IK+MӿOeJo*Pe֭[EK2,e }n5me+9k}XgzPY.'G~Z2K$d?죚2qQpuQww̚v,t^y>xPy.畦VSBw*g^=D;z$*2%-oj``A#!}҇I[h_;豬CO˗/iղ{]vm455<ѡXQw<eͪeYҨedBNU֏ޞ+/kڈ_]iuuu(Qgg<%`c'$&r䩷vX<50Hſ疧zzzvɔ[8elqGGG3m3qvrDOO/OiZ%su2uۭs](Rą~}z>fLnRN adhDtL4.ι橥aCX[[xs=Jx{c`hY~FtT$ׯ_\ٲ]͟15zqqx{y,ht,{/G5%MvtuuvFː#˱^s !ķN%*wQM@+"#/iؤ9N&OMlđHHLڎEҬicٱG!133(ߦ?610'/_>aB!X-q@l8%$)H˫R=˖P!kLMMy&Md2R3g^*hoWPnݾ%(VTt! vٳ爊ܜʾ:ג1ێ4dEݘ?wTBot}B!#]_T:B!H.B!B!B ԅB!@]!B!B! !B!$PB!Bu!B!B!B ԅB!@]!BQK!nݾMDDF`bd R9B!g"-B|^L`^ԃQ~#\zOW= CS̛=ff\y3SS9 !Dv"# l%&%1{|L@6x{y]l޺΁7"..,]UGEEc-3 -?^j?/2)U YzuMMM֭[*?Z2 Z[ɇԮא;^ҸQC|+{>lۖml*= cXXXn0y :0fԈLN9Xrr~(Yg2m= 8mmm|+;w޵ # ſz-:wNf͸q'Ls~Y77j8ce,_;fm)S[qL2 g'Gj׬^ܱC{.YF&ӳ}.[@SSCNCC2.x"T@rr2T@Z5oQ(EKg@*k=r8Uդy6kW\+,Y`\y -7e@Ԫۀmhݲ::ܸqF S@6&&Ƙq=0ƬB ԅ˗޽".`jު<**XZXPvMtۻZ\W`o݂:By-'6ҵK \WU*̜>ˆ٢Z_= (pu-an7aa;ظ8*Ub[Æ$%%HZ5"һgw 3w> -@]XE2r8FGl\^l}cB&;Ŋkǟ5N]B\A a¤)xyE-S0et:w펆vT@;~[Z6kPg9_%l^%F%#W4'ʄܺs_(`466;6Ԩ]{{;̝yٻ&[q\[!";-q@l8%$)H˫Ң.GNP~់\Q(ܵ'CקL!_ ԅ9EgB! $B! !B!$PB!Bu!B!B!BH.B!@]!B ԅB! !B!$PB!Bu!B!B!BH.B!@]!B ԅB! !B!$PB!Bu!B!B!BH.B!@]!B ԅB! !B!$PB!E]Ӻv:gϝJ>R!B!"OE# kY><=zg>$!Be꛷n8%-9q2$b?t%N2"EK0|h9B!ė߻w_:xL\lGJLII#I!BwǏ(_:|![9B@DD$ ޳Us+JW+W+۷j3 k\h6K/.Rv-F68ѥ[x) )]Ḙl,Ŭ9HMMU&7j{qO̭lp-V](ٷ_ ›eXg,srC.< O!B|@=,#`Ƭ?6oB6ٳLUqM ġGz:!!$ųTY7>G?ؘH6 W=w>A{rMCFO?(y+&4cff#Nf*Ctt DG- !Byhצ5fs7nd%ҫG7455҃gAo%߾ή?RcOdۣXQs+D||9B!{SJFBz: hE=L`IA!B0o9b37^/D Hɘ@Z_^UB!#B!_\!B!TE!BB!BH.B!@]!B ԅB!TB!o\yͩ;aRym $Ds !355iQS* pyoYu!/HWUUg) !ݵZ2O{8f;ʓ(VvIZB*mR;>%Pu!R)-*́:ԝp Gg*$ "_zAuxXxyg2Ϻ~ ԅB!@P+i{.B!x3PwE +LS(@}㯿efV]+az.q'O%KY-B%R4ۗdR۷o^"s/U(2hތUC& PIʪ`amq~K,l)U= 9I76 }Mɤ)Ӳ|nmmw^|}}‚VZ㷦%00###hРw͒.99ŋ燩)&M6M=8x {$&&NB|e˸w^Ɠ{h;oyL~u|~F'Oqpg$''C6m1l IֻlKDnAr5_dvJ͚58p <`РAx;Gu֜?uahhHϞ=[.ϟWFFFRfMi߾=#GH"槢JEE?q/+P@\EĹ5]||ժUZj,\MMM1Bo1R)@= 9Nyn] 6e+VP@Zx ii _?`EL#GE&*סX1yV 2+We$'%{NjԪɺ_3mL?^I10vSY?~''lFFF?_u-`ٴiמ5j/O[NAPdBB= S6e굜9}7g#}=۶no^,\J97xzySѧ6-[QWWgCiijRݝoju<+-F+#Hx13ga҄q$''3a:u`̨N/ϒTP>#Ѣm@Gn߹= U555ϟ[n 6EޚRR2'NS <|cQ%];J|oίʶm۲䄵5ӧODr5444`Ϟ=hiiHɒ%yݻw'44?C*Y! ?8w}w7:rV>J)éS!)>} WWW0W)ၑ1!!'S> >}J`tFz2/2di i(l!ʔyޓw*br_9ϟck,M 99h>zDDD$~>e.i($$$0~$ڱgaa U6IIs"3T-|89:pht^N=.ZGz,v>2 :_ʈC{,*IE/Mh֬ .C9-S ׮]y:ä<2= KISpw--[*Kz[BoL]H }@o_YEOO/|^DD䘇#?pժ*??p0N ֭̉[3o<ڶm嬭y1ߟEJjj* .TBdK-'$$$4e˖%}`(?x BLT45^ԨY}IXxE\vZл4!ũS~3&UAYsѸA=:wh_Mz1m*=hּZZ> !6ZUUtttYr9v Bܙ3hڈa#GӦ]{:whϥKw))TXRe0g~#~.H_T#GIIII8xCSSڷcނE4nޒzbaaΣǏqrtLRhhh0_ODCCHNNFWWc\rE٧]y6A%Kp,Z+?Ehij2t@FعkOW,,e¤lۼbEJ%FIbܹsomm166I&?/Ž{vǎcѢEzjrkצptڕŋNpp0;wFWWW*\!r;vӧ6h&+W۷Lghh0jx Nbb"VV :Hw2g|,Z>=S?KϾ{4IDAT7|r= (pu-az_r"$ ;PHZK1mLY`j֐+8v,Y2k֬ϒ~/3p,۶mW^ԨQMMM2eTB 2!:*C)JF}ICȹҕr\^GGI 49˼WXfm 6Æ+[*w3^&kqZ}ߖFMd%Jp-g .SPh0ܹgU3gLVDؓGr߷{^4o%!_QzF0gVFIUUUk;U*ggZ7JLLΆsѰqO.u55*Oʉayot5o9b37^/D Hɘ@(G}B!n3gu޻a{B!-;K_o'i'M4B|dr!whUUU%-- S)acBjURҤ@MU4%mL$PBp:8^Je!HU%?u@ƥK0#; *(si 8,F0߀[p !315\~|֞\y/򠤍 ts~aR!stJBoH*EcT B!@]!B!B!wqQԏ_ !(x_xykyW!*jԬ<3ﴼC?Lx^ x?#ٵ{cƽo1_:?d 0M) 0hXk "طYͬSySv c̨{ceШIsVB2xc|n$&]})^y?Mm; /O/fϜ=J/w5 .Eg{0iJ}ʕ8ӼVGY(fy]ѵK6H&֣~bbbh԰~T͛*AA?ٱDG[ΟݺhӪ%ժUmiؠ>KVسw_2ʖ)C``5a?x7|Iěloڷ{[+s\f-[Ҭi^z?{=_~ @…Xt.(S4vt:vcX8dqѴA6H\\1s5L&*\DD$u MjPMbbb]^r%]Mn;w&K8U }zWÏ?Ң5WWW6o9UƖU*sy{BZ4_fWILJ"4,{*g{ӿ__۟%Rj0~1>>w """A˓Gѣݸqs[.$ˎ h|sزuaC?ݛ%KkUe^{_hh4j!@:;{&.^dT#gNỽ9+Jqd aCٴyKwg>͕pq;z$uԦf,Y2bc,vZl߱AEb":"~S\9VZc \{`׋I_*;wShHF 088w{*/k'OeggprrbbQK`08PߟNS]N^=Ybcyl2\7uS:YY83ߏӦf˖D1q4&MƵxխ?IC/Rv$Wxzzǘ8aDDp={k\{f|{]nᄄǏƦ_)9 \'~r+ꆫ[ay,_ɔ+y֬L6֫䕤ciӦ->fG3f,7@{;mO÷Xqoqӭ[w8ěo.rjIܹ+{޷on䓡^""Gz|GGogFGϞ=ر {U+WXk<cMؿe_~!e+J.{K\fYk׾Gb|kRG~޺Ȩ(&MoEpBzysE6;܅0uݣn7į_D!l… i gݺaјysXaYߝ;wR¿zu>#w{k%,s3U 08::ҽ{bbb%v/̄ ޓ{vz?KٲeYc \tRތbiTVUޢsX~ %K3ԪUU+PHk<ߏVˋ^Ͳȼ3; ...u,g(W(#G4y2^^^lrFC҂KbQ:fm_CvY.>>> Jewؑk;k0rKزe O͈ym6/vbY.ժ]Y͟ϼ9s2]ߊ+кe+ԉ:2c,:w~`9\V_Vki$66;w;kh߾۷;wj֬ p#ԨQN:ҬI4e'&&pB&MР~}6m1x qDD!K;v@\]\,KX#>4zqN8Ippe6[ST)Z4kΩSϙ7gL6^|ӧ)7F6>s.wfrm=~Wp.lsٵ͒KINJPBy»o>6nCGv=2CՉ\?:t ._eDrr2W\#fj׿]v? ^euz)k~yh,9C?nO#sg9q &Ld/))3)_tF|s38::fYי:m7n"9|JJR˩Z 111L&~Wbcci2$rm_쯚! ڵ4nԈX(7.Z(RK󚧧G廸' []hqaprr(\Ԓ""R^=8#iڴ E-uef<M3ߙwh+8hQl޼2Y3 if^///̘1`Ť7A=ڹJ+w>,=3/6|oRJe͐ 'W7WF#77qvqd뛝l#kUTf]n~bŊ8r7odkӯ.^Ȁ/=u!bCۇ~gQtiS3YݻwSr ܯה*/OթY:va^VA~֭keTĜٳ=-ZmZѣGylQK.=z=֕/6l`ѢHFnsm6unuN9?+[www[ڵmqY}=Q~ȆO ;ftqȞ={>=gggAgLϸ mm6y__"9 #D ,4juݻ#a[V\Erضm_l˗tsN:;w|k׫Gː^1EՉh4#ERN b>8::2jM!!!LN 2:ud鿉ZՃ9{/z=3WUDDHLρv.4):wf)>|Чpw/_gϲXZjif*T3Yln>ۧ899L:B ѬY3n&'^ĝ͚ݱ+ϙ3Ֆym|Mּ L>Çk0\]6:+WѴI ducn\r#FRlZ#仾jK#Z|i#'Mf$%%RTi&e1G3dDF-f(LѢEy՜8y??.Gp'C6.w}:wٙEhզ-o}c;u 7n`+#|2E%(0O/4/Ng׆ {OO"ExR<<<:d0͚6c3غƾ:mZ]sAFXb%͛TI4p Js`l7y> /`ߞ/^,An_ݐg%g[o}lӖ@]jol޼֭Zz}PT""7S~X@PRߜGh/y;zA=r$#yD,""}q؋v.CvC^G ɄCm0Lx{{^s^ED$oȉv.BLvek;݂'G!(0{(&#GzzAmv #66V;[xyzRREW)?w}kPy~uUHޣ.""""srXu t»C6$"""""wzEDDDDr6^]y8PﺈH֬9]<ut;39nנn#4v[1[g7.""""rG7|7:w}Qȝ%)A=d;l<7Xxǔ p\W (2Y )S2l/""""N ߩ=I)u!!eSq7(LVn%WhuS{Sd= """":ǩA=yU(7׶&~eRslRUN]dAݠ.""""(ȶ7lu[a=SNhu~JX҂UO Yt"""""-C_V<}zfaUS6-lIu)XLV&2yݟS!:R~7&Ҟ :xڋºg{3;M:LMnrA=}OzEDDD vUfN߳nm)' 4{nätA]DDDD uH{[hZL{֝bn]^EDDD uEG{elnH֣u@WHm &GG l;A!]DDDDpǐ;%ufms] nw[<)=OӮ`.""""b;ߩ|A=i wгgZtQ`%T+/HVhɡp."""""""""""wV߅~-IENDB`mapper-0.8.1.1/doc/manual/pages/images/grid_settings.png000066400000000000000000001534411325266516600231300ustar00rootroot00000000000000PNG  IHDRx_+_bKGD IDATxw\U\6Qpފ;jh3sԲ4K3GffZffiYiޖ#+™w {{=t=B!B!DTxz!RgJ !0UB PNʨw!~/PH.@UyO%,BmX7Wny{B!r7\[C8!BQCuZVE;|,?UB#doeeЪ|B!`|#ȥ圵5]n̰=up`ۉ9yMrtl"!4hJHPj5jdAլʫW+9Vsf ɓi)|헜={ VV$bٱXTX֭;`tv&2224K ۲ Yڑe5vSv;szl6;iiJB!):t`ȷ^i`G^k Т<ϼ{N._Eb2I( !xt[vNXf <֫sz;H7nXZlʖBHSZTj=t#-ܼH:k0gΚ9uBB<:EQd!xvZ qqޭ}k07Ιst(f/7 &͆J:'ggj}K!Ξ,0 7η՜W8g(j5IL%hPڟ2j,bSz:'pptۺ_O&4,xzxkZl$\Ιӧ9y2% NoWBQlhq֌`9 Z3|P$Sqs(LjY2.Z N@uP֟dK9ϺEC6quujbnnTFX06oޚo]AMGҕYl( 5o֓DIo=:gwi6#b(S :-vvՋH('EmEFF65#cYYM lAkri140--:BOǔfn&*&S ͂blsj;CXs4K*ZlZ Î82ÎjEPy﵊dv-)$$Zx8igT;Vi z#5E7;swOB38!(Z|d?۽7۶-k9; ׯ'3aYi 8?7mԘ]/n?NyPHHLyhPοKF^Z_)[]Z_Uz^d)5Hѽ8T@H9s$sߖ8Քu#\.vp`  QuN]E.*Bֲ]Ys5Ow9-DtIW4&ilΥ(,w,BQQ~28ϙ=ɦsii&1X,Soذ1nn`0P$ s|ûްx8Rzٌ*ycrRakIJdƁ '/vD8L;%s''-F_tw!ު%% f5NuAC)/7l$^:Mc v.îSϕHvŌݑ#[Ů#ewvQˈEʷ\瑌F^`PcAKECjeLV;8ϺK8 !b8Eu8g|~l2[=% 2ZgR;\@IǒAsF7J#=ۮ$bt2dUf Tڵkc+Vn`6;(QqquM|o ||]uڜIy~Q WҸo=_K6-Iox}( Bpi|&V&c?^dxswNwbW_q鏚ICk5g) h%.`īH!Z^D? Gӈ~S)<-Io+ML_N!.ço$QEKS΄bԫJ!mtw;] ~r)Rt6%Xf.E R^c=(UgVpb-ɐr_'zlm'uHcਧi Ic)NrByO(ktyT&洛oZL٬?2j A`OK6Jq--, luK1\.p͡F:cz;$ B[Һ$pa)3(t~oOlBr˙v>Ľ|9+Nwa{(^2ުCd~|לUiAe93(1j6X/;Kw3q3 mE1Í\֖WhT eqaґjVtuv,QklOFYYQdku>/ᬺ5:T*-6<GBml"*x ;]Mu d*T*Ux^@rVbVW& xAbGXV͎S.^$T;o|| gOF~D=\BlRTtOBf+JC3qiz}o sf)䆏%E-<+ηەQ%2T4JvMoqs Þ?G>o OWZU /`M&1Qh!Dawi\^g=}fj=`hѼ?juOʲZmxXR+O,T$gt3Igt:ml6;aaT|[Z7?vN}QbQRүXfÀ):Vz oW-VÕXj- $:Hæe A6ޔq VS::%j'6E_Dɼ1t$XSd9ϮC1g\Ʋ, VpsR0:ev;p"%%ep8yn'!<ȖsoYmX,SePFcKoSc7{gt=:u-V\]\pswCףZX,IIIEoݶή8g136^J,B<-Ζ 'l:v$pӧSTjZ NFm[˨QcIk]FS*ԤKKr[aQo9ߓpά\/D)wtu !l8 !B!B!@Y B!rIB /w.N!ppB!$B!,BH8 !BY!pB!B!,B g!BY!B!pB!$BhUG'9%quq%<|$21N1OILL=V!$QFQbE:t۲ǏW~GTD`5煗a Pˋra(ڷsaf͚?5tggƼ>ޜ;w h46C6_Vʞ(ngɸj=U!$dfϞͮ]Xz5WVZb0Xd y_^VM|g…X9.--qy>dOĎdgp84mܘ8Ξ;SVB<$iGFh4f0v-e?@Q 4g /[f߱eVӅB¹`FEŊ9tm?~^}Q!ԬS^n ˚5;\X(A !D}P3>|0fߟ|˚f:v펋3c|ooΝ;υX4#|B!-gv&OKe;Nlloy{]z8ŋ5#Z-۶`Ԙ9vS&MsٶnZFANFOf`6ywd kV,ACYq^Ȉ<+-S{r9>!...8\?Ïf2Q=G3d/B \lV D׮]Yp!Æ `鹆խ<==z\^xiVk݋ȈlGUazG̩S SeiԴ9'O$B\]]iղ͢b42u#Lq_\|x8Z8g\xW@ra4i֒+Vf[?-ggڥ3~ŋ瘏l6>3 ~ k׭\!2[UVtRFő#Gѣu-p-[4?GӉQ|<ˇK_:."&7KZ 3rlnx9* f*+ɞ={sѵޢyϜ=KJJ ՕZ!$ 0Cbۙ:u*Fɓ'q=vm݂d&s~s-dtb7Yy ~]qo4c0rS+Ɩqwa2}^!ηջw#F ?ty_4oM퉨Pգn;;h41kޯV ҥK?pUͥ^tc !Dpn`=z^zѴddzhִ)~~ŹȂߢhhذAR2e? tiΞ=̅X{jT;q2=|"U*WB<޽+1ND<7sɞ1eLf3NL*ԱCz6iDQ4lǽM1//~Z_*5 ^=@_^.]9l!DGG} bX\ɗ IDATԨlm} İp77W*T(g'Nl߱O> NG>7r/ SLf{ǟ(S&ƍR4Z1cZ--[4g^Rz5F 22B7:6Kb^L#FQBQZEzw*#0_?~Ĥ;Zw7w>1n-_w>i:w{?NpPB-1H3,S4 X[ƠyZ*dF_|99?LpBjI4iEQde&zM6o\'-g!͵WQ`߃<",(ͅ3 Y %a ]TylV<:|gz%11QV B:Vymܴ[hEQgϝL ,By,8u +VjɁQΊf}Cm m8Շm;:h4ym9.~6ԩS^A̝EF`}z`}}|ur9!n_g0Lmׁ{ү__F+lӈukf|Kx[@hx$> !/gdzTҼcl߱3,j֩_ "*Wc~ 55oI (M3۷ogEL} :vSǎ6:"GUpL0&MȬ'99aÆTO/o<֖}e[昘?$>/I|rxz=ۛ%FZ\xEQp8L!aS eV.*-[|TJsywL8)S1kƾ&1﫯dO%s9y>z C Q$/uvƌ_q_> w[QWZZ:wӓO?d݉y3YZٳgٴy r¸| rd.XQlͧe˖4o,Ϫ(8991z(||}Xn=O С=gX5SZjß?n///>&qޡslڸZ͛cggcx{{( &Nb̏xʗ/_|I]},YRv֢74d4j؀,_cX,:}}գ׵,gn@zV+ߛB}le?`/i׶ M4JT-ޟ6Mʱ\s~ɥXzEfx+Ff-Yb%2hղ%KyseO?%ٞ\n9==}:oV͚Z~Zʐe_K T¸t)ɓ ƿT0CINNs9=~ 4m(Ǹ7999QvMٛ*T(U]r%< (®ݻӏmM3;99QNvޅ(޽ eR ^^^ڕ^šC9t0Qk0c\~EQ8~8))~s ^>xyPܿIɜ9sukK9ONF'v;vwҤq1^^^RɑMqY/Xb߿6eaJbv/%˅dJ>ݨA |Ì3`4~YSqu4m${ӳmG>; !,,4P7򁸸qӖU[33kQժfz7VRGrҥi8H||B4B{x,·R]r*~+jǢ 6f3ﻨBTj>|qqeo^Z{żxػwj5y>t² }}mTEĤ$6mj L4H&ZĜ:͐Ast:ONQXV\\\h԰mkFp=ř礦2b+bn;o>Oňa/c2ywdVL]gCض ^/Y!8acFbMmׁQn ]jj*#_{5[vPfMڵk /DB|<٤j|?KǟWG_λTZΝ;( _Lb$''FaawBqiSlV+'cbxa>NU+\O?,`o;8p;vۧOa0E;>BnOd{[︮W^z0y˵̏ggAӨa>1ocǓBQ,iI\Qժg>c&ڷLHZ1cZ--[4gT!'ׯeڇY|2k:uQNηf[o[oL5X2J((8;;e1MVUL0J(ر˖={`ϣ( ^y^ϗ3󹸻S12!/TY/SOkݪz99J!u7Цc aOO{=ȅZfU4o]i|KR!) 01( J.zYvJYQjS&& >',hYHYYDq8PT(Zj5ÁB݃Q!" ymfSY!rS6,Od޽ -",y -++A2,\[.(Br-[Α#Gs-d ˖-b( ߏXb@?@[{7ۅf z=ԩS~fyyQ.,W9 !rm9ՒX,Dr~,5%ˋ&Wϻ"slleʄ0Z;\JH g/+ek!roIKM%%5?SO=mϭ綾oԸ[lʳ=vwsNZ4lPO> yxw7l˷.pAuB)Ggzg7nj֩OPr Y6lٺ-eaɏTVR ͷbXp!/|lת"55*QxnSx ܻNΠx'6e2[d9Hoo58]8$\y}Ӡaclٚ9EVx?ݺ=.]ҭ}[yp5_-1ow P\9nޤ?ұKw9۷u~*FxyyF6 П ظi Ox /sMQBηZf Νq%ר-[h԰ sLrOQn-߿ظ8ϛVeu{g<*TCL83?f»)_<_|%:wQdIyAAA?Vv<`; vڝ븺uЧOB$ z=##9vxc.X4_/!KggZss7{<ù|pʗvlܼ;v2p@ڵm9sZt:v;za4]g ә\ostuw1oyӤqc>5F ޜ.k[NKpsuuV4nXtڷl63}t~- @5Yj?!Kvmo.#-X~s Oޅr?8|duإ;ᑄGcN֥3][mvZF٣(4-]ۧϜaڵ/UZΥ2׮Ln-m~WP<bcQǏ7탗K̙3gPQoG3fҪcd 6M0z(2?pwwcQLba+[_c ^FVEwiJ, %SraaԨQ#˯kh,ZBs_~%&Mqqs͝=fՂ9.[w79X,(( L4;e{bذWPPxšݳJ*2h`5'5-6K%ƿ)SCա2ۖHS~=^O̳Lh2LݻuY6lڼ֭ZZvEӡRp88|ftp0%K޼ `1䅗y{7bcԱ (d)c2x={VȑjWJCv{**KH1o֔ bsrB˖eI|ޛ<;v5(k 3?$ǴQժѷOo>6 r;9[C{eƿ3տS=z2Yf-dV\IӪc|p!L}իyay|-89_2B[Ѳz9hmP<3pW]ve=PBb iX*!KUfUwt o@ 1$eyf2pހGR]v8bc6bH܃HΝ﹁x{{ӿoo  {@Y>'|3۵m^(B'Á-PBcjKY!rCJ"uB63),)Z'Nw^e{ Oʖ pB )jYB!B!$B g!BH8 !B!pB!$B!,BH8 !BY!B!B!$B g!BH8 !B!pB!$B!,B g!BY!g8'%%1o=ml!3gye̚= +VT-2+lp!eB`lPR5|KТMMaFXruXv!4h܌cǎ˞'"O1={پc'w᯼3M7*+ZQqB!|YQ>SjԨ̌U̚=dg2xg$"D@pp5m…X?ٓk_ 7ǥ2شy mu$|E| ++_eDլ_ jo+-O̩S<ӻ/C +_ɘfϡqt UDA `\v-gkѦ^%,{Br^v'cb ٟPV=֬]G̩S dOm߁+U?p]wӻ\vu7퍢(:|S)2P9oBVÕ+W2h4h9v8 LݿŋiX._Bq__V+];;{ z=^^^\|?fe,8 x{{(^tp0M4FQ|ճjl۾V5ذڷmg?Ή pӲWTb'33;^ 7nY| ͛Esh>Gx"iYzуXgv{2_ΙͮXbB!o8>sVy'xS|׵w>VBD `Z*\p!2uj|Db^ ޳u7۶ &&&z֩ frF=B!DAn|((BDj%$\Wҽ[-Je0ӧdܢcAF WOm]EzTժY#ޘZ}{C ÄB9'Z4~fysfSA٣={t@!\Pn$R~|Aprrbp#*fХSG3]'n}uZZߏKr0ٕqYVl0խ"'MAQu_ج,~Ynظ #SK:uNFiHKL-12珟8 RXXѨi'豥Ҳ e=0+a=}΂P k;ʔ?M˶ހACpvuì-n^,Y,:v鎭6umP|788B:{)vۯ>r^ղ1[50խ"V#OnFr.5vܾ};[9sY:VTɶ|퇭6C/!x+=礛7QLuB{IΎh022BebJRRs $e:w #z<|ٱs5m]$ }hi/\lEo`godž6{|}@OX֬]ϗ_ʖգn&O+Vf'Vmۅru~2Eu>rmu`[f[r̜zzzh4ϻwׇIIuuQ..*{+YLj`h`#u n{}A^oV:v^byV;j$XB_sӬIcTƨfyN>k2wt5,G9===t~Dnb93@ZOQ;022$zNjs: qqW]?vRF~j*~>Ԫf!Mڅv|G TVժrlNǰYuxbj""#ǻzݪU*b*vCe;!qWr`.ll9-9?/V{'zN"R\Y/N`?qref1zx:2||Z(Q_oΞ=;z$t eЀ~Yekҥ >R/U!rچ Iu㗉 5jΝ###n$īX#" N=SX(7mj{UɉW庬n ._ɓOHS|uo""J+٩4@xZI[[⟸NKK s3ɾ`"|kPљ+Brmu8;#YS XXA/E/11$%ݤKxE!L n%稫/'OΝ;x%lbvI`ˆߟ9s樗''D:.ZBff&uXd2K?|,GKvn\v)]1۲6ZbjwMѢEY~իV<[ٚT0rPϵ-C7!څ5rM4cPTٮ>@{{Ϙ!ɷq.]{͍0}lU(OU/)Y07Yj mR%Ojѧo]\YWn$3#C}ߣbfΞÌYs(•+qj<[= >9j kMZZŊڤ2x -lظ_-xox !>;)R nlݺcG{))){ǎf[f<*SDGu-]WFuLMLX24443jee544X`o t Y=Wc^^pq);v%K<)|ڦy;cڏܹ{Vm?cȰ9{!OoϜE`4l6YטYl .#~GZZ/fӬi~>m>!q&1ǎmP~[2zz}9Ǐ hi{ź_j߻uYl9-|F̞1z}ox !>0ik(Whܸ7åE'05}Yb,υ?p0II7],#Xآ[(Ebcck?GH-z?+Wpz=իWIKOg򔩜?񵷧xxU####2]JTTUbbk֭GQlcb0k\ݻ'b!$k}7ȳ!5}ٱgO} {T&==J 퉻zm䩷kk+J.EB-6oCXg~qBk|Y\˕{fbV'hCC8s8?o+Vp=Rkh<͙N]kO٣C9'Hxq|7'Fs)fХSG3]'n}uZZߏKr0ٕqYVl0խ"'M1 p=>cٮnظ #SK:uNFiHKL-12珟8 RXXѨi'~~9CӐ-eݹu+#GUM]Off&Lť|EJ:I_v9sY:VTQ/KOO'SW*xVRDDnW/7 iF]_pmyW !眗7QL]^ebJRRs˅GD{2;=j>ع 󽭮{+;ƀACOcOp5ΚVa#"5feiPlu}hY[ k8`Ͼ}mmqD<=SϯRًfMIҩkm}x"۶G1i8JA/P&O^TcǙ<۞x͝ w/RًmJ+V32cZBӴq#)WΕײGoۊf!CmZĭ|9Na140dBHr~fϝO&Q26Ǜs;9sYY ns3 VM?"Gvv-Vc'9sԮglmlpq)~}?5?_j@&B;:H.KOOgڌY|ޣpjek3x=xc),AP.]fxyz,8;tm֤1%JXͩ1]^چ I7122zfGDڡz^P6nPh󫌓W^uY:A\|'OӧaوHLT*<==^)Nrm.\ݻw^y2^1gbu+Sܹs@9g\! |+哓oabzfI`B;Βw {"ExA*{UDb-lMs .]6on\@ڎݯ=z<}-xOFF-{{N?ƭ[#III:y;c##s+kː`_vaF@&~T*>5r]@{{Ϙ!ɷq.]{~A헎6C hk5L~=>>ks`o6ZNoΡCp+c`_B! olX[!v! Bn,BHrB!$g!BB!$9 !B!Y!,BIB!$g!BHrB!DJuYn$t&Ɔqpp Bɞ{ FQ-Fhܸ.(R8۸~m~j*]{d`Ĩ1ۿ_gޱQcI _$1:|ZkEhQlllq-[S鱗bjnPޙ?ٞ4#"zj2iL2/μP=wmۣȑwBs~ڽo_? ,c``H o_vٓzJUj`amY [z׆Mʤow8ڎ&-8|z34 iK[\֝[ػozBZVs5J!>s͛T&.21%))#"t͈O5v܅wV= RՕc!''vsgMGK0324ILMquqa}-5pAjg>}668y"^۩WEp$NQcԵ۶P=lȈFYc'm>={0n(˷bnn#҂ F ع+G,k=z"zN)̣a`ܜ/m{&ɩ4 d}s+_~@OOO]]" *m\v)]1۲6ZbjwMѢEY~իV<[ٚT0rPϵ-C7!څ5rM4cPTxu=}>c$Nƹti_hyՓ5G#gRсV>װ mۅҶu++ӧi֤UpM̘9k)))|ugQ3gaƬ9uuʕ8Zl.`!_T"E֭8v(/BJJ /paElƣ;EttYequqɑkTĄſ.CCC1Fk^6o QVCCE QҩKw_.򪄋Kݱ+YMIO6s~ݻjCs/ՆgԮU!F4%g»Fu ~Z2˗.ƍt9G^Ƭ?ӨQɧø?ViM7cXFOvb{,\~x5m@')>n̹sܸ~\czmiHK9lB|܌L-w;O~d>ɣoQ*G{{߉ΛkT*._fxT Y!;Ac[9tn%ܜZ~ W!ߖKˑB*$!B!$9 !BB!Y!BIB!,BHrB!$g!%!BbΞvmnܸ!J@_AHrBP!M=$DlDp/΁rKJrBNM̀:j兊{zVfsBP(ܬ: BזQޫJrBQ IDATvY3sYC㭶}j̸ ع+W1d]Ο]G)S&67̝_`5&N1oB9mflS޽IOK#=-޽{X[iyI>Ӵ$'xxU###>X?_zw~vS=wmۣ lc[1sw\KLLbͺ/uM k#bb \p@Zn=%9qWz|<~>,YVVV,^X;s~4 >[nsRSSyǓypBV… ̚=;{~5V޽{M?Тypogn͙)g+Ul?.ҭK' {ǻ[6U`Ẁ:9/nHf͢o߾=www @:uy#((CCCV^ӧOCCCNCC 9i JXhs˽mD@~6.:<$?OQ|WttɅ֟Qt6 ڎLarGëZy?L#SKL-O֭ϗ_QIII߀pr)M[pQtBZf%lQN<.s)*W#%lh.W+>uJ*XcWʙpOۇbURt% O=Vw~><|>?mb8g|<+Wʖ55/m?T<ʈQcp.뎅UI*WVu7ʶi8>JUj`amY [z׆M9fji%V14oK{ʺ{`ݻc+Zs+[\*jyM@zXXcOѳO}h)5|kLtz[ɨ̭X~z`neKJJ ZrJ;FͰ*鈭m>>锯X ԩ]+akcÆc>K};6m0`իDzeˈaժU/U~Ŋ;w~zwѼysƌ/\0ӧO'(( fΜdw7;!#"t͈O5v܅w2ڶ#YF yII7 >9=+.]StYezEΝ 4s3Sf͙G&9GDҥSGׯ3jx:uA[,-,ϠC@/|M>f-aiڸɔ+ʽ{h4#CCfLJT=Vm>cg9-+&zzznOn=m n{}A^oV:v^b+6O۹k7Ceץ[v߷}pvr ;ϳk,--0n4̝r~۶YHS]ooԔJGyp3)1BZQkNa񤤤0bؐw;~-B__~~l89޳L>p g>*UhѢ9 k/]LBHLLR/?qgfؐ(Ă_ѼU[݅իW%zN?[bcc9<#GTsuu֖hڶm壢pttI]O>aʕ\z lunݚ֭[K{zמgϝO&Q26Ǜs穓`=Z5 `Ƭ9:~YX+FFTlmT*\\CY Ӡ'ݣ2&Maߖ==*]mmڅvs8chh@Ə{o/_a^(NU}ai\v-׫{NNѤQCtuusc~ړzOz njSX( v-.]̐# Wlw5+++q ^,aogG͇W䉳6oݧ?8;yfGtLGzE֭d2m455=nts:|h8;3w 444 @CCa#G.T|ݐalfZ'%JXP?ٱ=jFFDة^/*z'];Zgkns <'gYq`f:u Uٳq}ϖ REׂhO̙nJG:ywWlt=M53gΐ#ձ+ŝ;wx1V>7o,^}^yn_l{ -!33s,[t_Ջߥ=x@\n^k|n(Bm(^83+V1mƬ~rMi.\ۏ+} *_ژWkk6":4oՖfM͒Q]]] b_v_4k*Q#Xw<ĶQXb235U{J«dɒlܸQ=… x{{TyooobbbyM6(CHyyt-i{|7m ڵkvhmX MXШA}ӗ+qWq+_.ǐiΝXh ͚OﯰzXkCE(TH}M/JԯWnbnfYsHIIᛯ{k?/,k++T*gΜ{*!YpEl '0vҹ#s,Uv|WOMQcFד=*V`9̘5.\G͟۞kf`o}3 H]LIgűIckHZZgLs_~6e;MLL믾`ԘRߏ4Ξ;ݻ߽g/͚_Ŀǎk6uѷo_z쉻;BBB GWe˖ >VZ1zh<˙={d 9t zz%666Ǘ=qLTa. P")>NԨA}%e W/)&&JpJR|rE߷}%,… +&*QuN?l(څ [9y찒|eON]CJ~bR)_KIS*yz(m.w|^Q+:::JՔ-.Weܧ+IqJwJb---D.s8:8En?޻S-ZTWB)'Q/~1)~>jO^=ä8eJI[[pŠ2{ƴӂymmԉ=|޽bfjhjj*V+IqGESSSUJ+ÇV/')66J޽ԯ'0A133Sttt28e߮h哺u}}}EGGGqtpPϝc4 )C P-+e8+mZT]bRM}KW}Oy@YΝ™yKRVm/Uʥ1J^JѢEmmt)Ge1겉ׯ(#Umx}N8+W/_3n+Uyc*%JPttteJR竼(ɓ'Zj):::2aEV|31ԭ[WMJ֭nݺX~'_{@_s= 7(0_ ?̷Oz8i:Iqx<.pqKΟ|~GBZjRԩcqJe b ԮZwN.pH'Enk MMM|7_|E!9.yӮ{XwZ!N粏w fMpt ZZ# Z* v &ij_^iT* x-=*TLUo !eؿG!^-i礁ɱ୵WnBΜ=GHH`Xe/&sB|=} B!Y!BIB!,BHrB!$g!BB!$9 !B!Y!BIB!,BHrB!$g!BB!$9 !B!Y!BIB!,BHrB!$'=wr$Μ$$ Yݻw8p6'n޼^4~7[R"ԩӌ?{$Z̲Wrṷlqǟ^^T f%l)_ǎVfwC͵ޝvshkcddD||<+V&##0fO.epmbΜ%93Bzί_zz:gϝc/̌ !## qI߃!'OE8@Oe_9Q#=wgdl\&7ob̸ 9gOl/,Y'Nd+?g4bNuǟmCNa ,=uQ%w\׫V W{[IOO'>>GAnRD!o69kjjk1wtDx c+]&W1j86 ƯBRF>cOAZ^E<~!gB7;w ų?rJphkV5%{-[Tk׮Qz5V-_ʦC8ȥ˗sMΆ=z,+>H˵]nn( f offPt)PYr=.]$gBGHFJBX~%U7oaccMۇý}i֥u󖹹yuw ʺ{`_L7Y_0hPlhivzu)_koS:dmk𑔰u`䨱rF !xɹz*yz^1Z!b&XbĜxzRtss#iڸܸHɒ$H̵7_ŔIq(aɀ~߲`׾cGd҄qԯW?_jsB|4r]a Q+-@I,!+8.]mm222KVڬXD$#S~] 觖""abA#1_"[[JXZDZZ<=X8B k ! ,Bc$9 !B!Y!,B%9 !y1gr;67nܐ`NRa$g!+1kh¿VC1Ǚp|K Z❖|\(H^l*=g!M(Hboۼ"Y4Ko[ݺ$g!eh!>,Y~t嚳rX\XRXnYhrFMCW߫v?9? KK/XVQoDv9Y#SK.]]]J* 6oCXg222SK&1c2rol{&[k(^ɩ.NΘvڦ&&oZcKzR݆O215!22As~Z;>:8aba[E&NV%+VΞ-m02ª$̥3G=&GVsqA+ ׬ů.aƘcfnANIIIQsV2=zIJ5% i  c޽"([ ( L4NePGTTz'OQ~Jo`DI;n#Aeb!!-rݗA Ƶl\!=O$<FSW(;wkzeIw!qP(œh֢%^43#l1\z%p7ݴh1ֶ屶-Ͼ}I{WudCYy8;;queOF~i =j y{ܾ};7n^?w%xQhB #+C9OSK)/]Z% (PC-σU JF U344T۳<-di$g޼lnųk7]s!!vh׮-ǏEKS_}Q.[,22UF=+ӦMeʿ_{|٥RE"$))e*ʕ++}ɔ553@xο+|J7;OW؄ Ϸ{_:C66Чwz)6N;РacvM͟?DmV9ٲ>7>$dd0v`ia+WY_;yn;^o^,[Oҿo5wd҄qLJ퉿@YBæi#n]|&y Lyk󙇩>}V(Ԩ^ƍ1a\r7$''M`E`!+rva_2qdtuuiԨg*C=6g˥!Dg)00̌ 232 ̳~s4?+ȑud׋igU]t {J[9p}4~RRRPTҠx_wjgsq]SGl٧/DV]]]*8VD\NBMM-ϔL:::cy'--^zahh!ܿN>+++tuu!>>^v;#-#111cԔHĒ%-z9/[X>uĄ[7KCwݻwX~=NNNONl9'NЦMڷoϬYO>رcЫW/bbbXh 0z|ˌ ((P<<< Yf-ptv m};дy+Y/\(P>MVqtv̊0TΜ=Gwylڈ[YWcQFΝzީfV՘7Ïo荃 e-lQe+T>@/+ѣ &LfԬS9tLn?EV1*-5jeڌY=6NXF=~ͪVZTRE9=6k,YjM6e͚52{sBB6m]tϏP֮]˿oaaatڕZlɺu눋#***OLZh5ڔ,Y???/^L‚iӦ,\}9+0=]eޣG*ZАE 0IatڍE#GxFOO‚ᘚp6.c+~: ASS}0뱜cڔ*OGa8;9q!! Ӥq#V.[aS9p79X@[[ƍw^-ZPӢE  s*]t&N|r>pqqƆ{'80x`ڷoO||}Z슉;`*E}uQk 4lm1=6NXF`NyUZRrzl֬Yj*6mʚ5k055e$$$iӦѥK eڵF׮] e˖[8233iѢhkkSdIXx1իWGWW M&;s笡ANXIJ] {~?L10nWTtvbŪtC077ŋˬSqr7yUfy*U*zD.[_3_^^k[eWbddĉ'=f,V+kk+uuunNV.۷fffLʃ~#mu`cZhO&is4;]]ӷ?=ҧÛ43<|+W[gܪVʲ?|~g $%@Pw;4n"Gx4mڔHOOI&(ӨU_'O;;;45177ƍC\=?8?p 3f'&Oݿ(Cc:dЋ )!1]>œֲ1j=ޮX*2pp Z~ PGSS ȗ#۞[~`ղ/P n "32BթY&ƍڵK٣vJk׮){Ǐ}<s޽z*_}}}n''05zzzOSoeO!==(o#\r570~nL<hӂ{bi'ߴ---8 O\oٵ;V-_]y8pSj1?8Ɣ2u?n~]ym IDATMAVf&66̘:Ȟcsg?wCo׉'fHJJw~eMMqFp.{oh E`GOŪ>#32hN266FEs~uuu)c\WBk=4x`^I Ev6NWB{/p75[7oJc"WƸ 8j=y LAqFVO_@!,B B!g!BHpB!$8 !BB!Y!B B!,B B!g!BHpB!$8 !BB!Y!B B!,B B!g!BHpB!$8 QŪ5HC!$8'#G}g:dee(KwĚo-ܾ̦-( |-{l9rN,^7 )pM[up3B=z 3XUMlS,PuUUWW 051)g>ٻ )}۳O_6o.Tk/cdSGJɇEwfQfCڴS¿#s}6GGݟ_etH ؿ/-p&&lߺY{C۷˖..\O^]v$!bSg#D Ъes:c ͌Y9rsqiۉJnU)gi))w?q25Fj֩GY *а'0=#Ss=>MVqtv̊0ֻiPr64nG skL1 bi$Μ8y*OuXJζ3rܻwnG7م6x֨C* V:x7k˟vL<gW,mYsD>Ꮵ7p07oRYO^WO¦Piغ-&>-ptv m};TrM[|LBbⓋWطs {s5Μ=#SsL 3T~9{J;UkϹt_:'O6uމ>UۣiV :L.##IaT\3+]M;˯025ʕ~Col01Ž*3f-!>̧)ujʳl VZoQz5.^=̚>' ܸy<-_! @F y!3ls `h`HX|=_eʐ9ߋ~GG[[fhkhqgƚ1}l MM͗{)_ysdz,g6e2fb%Xj5:q }'v>G ɉ XZАE 0IatڍESԕ"h=Gh4߿τqc8|(/^b鑝s17}&cXW䩿=gKE\ř7XFŚ˕|3ݟII Mɒ8QWWgXչ};9:N{<+@~ٵQ#GS=7n`o'Osu"#/)SǭZO18߹BRRU<]^T)8#{OS,,]'##IS͏*(x7n9^O[Fޜ?J+:;+*@@^:4mS Jm*PJOS4n.fbC100`|5j$=P'1wi]6_l͚˕' r͙IMrprCLE555MD}033t4o,733"п/#)q_^kжMke˗0},T~k'n]mڼ>MeWW^NͩWU#æDF,}9e˾mJ*OSo7j=l,ć< e+.!>ԞeMM9׉|w> gf ([,5rWDpșE'HCCtttL0 TA뤭ҋC=z:ut,^0cłE/ܟ::9#1?mҥUtKf>,[Y(M)QQW&?BwܥsGl,z%KNF i԰ÂFr5*|YY,³jtuuٽg&F ^͓!_g12A!$8%K-7$M--{biۉ?FD<HNO<ҁG@fs5 ,-{7%~X(-_{2aZEw.88yo$ p?'!E??)+W 0jd4w~X[!(䰶cc6Dmdv\F dˏ3kTmUL>+++tuu!>>Wp\ĜSSbb"K蝿l⃵w~,, :Xe~}|QuO|p""" "44BBBh֬'ODGGӋ+8KӽmfX;!1zci@yGg ͧ^غ-&>-ptv m};дy+Y/\(P>MVqtv̊0TΜ=Gwylڈ[YWcQ;wpe5|vQԵ?nBz 0ũ稌\t쇹5(('''tuuҢnݺY͛=zsb ?=g{H...ذw^^+=@ll,DDDp=FE7nC ٳ1www &;9+<8~0V_fIr'c[4ʕɩ 9'>m24o|L116>dŲ%hjj`QDn s#>>*ؘJ*]3+:Pf ve ~m-<*UJeNI ^JUbts>wKիW033Sonn\:sppQFӡCEİ}v =8c9 ;EL'>z*UT&w7 9rh\6m`e8;U )KbM8:ʁ{7@}_ՅO>nInӧ~?|7o֮fY6ܽ{W`)]!$%%)p @Ϲp{s.UJ/Oʻ@1HZ5_;%"++ MMMy1=!1]>(3n[۩^4 MN f<}(sss]2ڵkTRCcyZZZddd)ѹ}:8DU~ III-sS$'jBT_-223uNdݞU:s/d܄I|=j$*ituu,6 QX[[ckk˶m۔Ϝ9Cbb"{9D.[FVvm?{ҖL_b%3ˡ-L|Czzzl!eһQ'׭U>\s8A|H9~+#$x8n@9p7l\c'ǧy+N*>y*hX jjj,XQdXҍ,YVmҮC.]̷kV>[=_wfS䷍BGgʔ)ӺukLLLؾ};%Jx#OBFRZ__Ti}}F,VjA[-g h:7^],/W|;ɗW[5kg!9? sUWg۶x7i\rL̓4^i xdN '7 J%9?kn.B!:8k<-g]B󨫫M2o{Bb@_DNMlN}:VVVC||M/ޯl9㧦D",Q oQsї7_~Es ~شGҹ/w` ߷X|ۏ9C̏ݻ>C7r""" "44BBBh֬'ODGGӋ+8KӽmEֽl԰eMMY]Temf8ۣP(;!nUciw*yŝo'*U ή125Og:*ojԮK9K\ܫF6Dmĭju,mqpv1x(۸'v/Zbfi'af ߹s˗(^wkN]P(2u:ó&ѡvzx2rm};)9\\\a޽VzX,--޽{5ݻ3n8 ٳg1bZMIDAT 6Lv;s~WT=Ƽ&{tSVK*EOېs/Q#˫W$f6mB>y5iJTTgdd:uz3~yWLŊԪY]{b`Ttv&r"iꍚ&L>d-|XT)DC116ƳjUbts>wJpScQ)KW^Pk+NhԨtlllhѢ111l߾]sq{EԔ ͷ]m)Y$ϟ?3K[,m.Ƚ{xk..;4jؽhӮή8:rAM-PGa*r5jEff&uо-ԬS1&x_ܼu [ v FJ]޽+Gay.\p @Ϲp{s]>Ν/\`0&OGX^yH.֌jZZZ7툲e˲ol޲"Xysh߮-::%pt+󬧧/ć_===vŧʲӧϐNa s\s=m*ݫ2oO>444pws{y|uWBRIۀP . HbQ\TărSSPvv炠 r.?~.*[ i4-)4 mO M&3I7L]9.ׯGI| C4ۨY&֭x=z͛R*gKsN4mڔpUtL#,Bq8+WX,fLdz݉([S's[<д *UʵqcFGշ?ݞ|2yz/]3pXl6]WHHWK;AB.Q8YDDD,""" g(EDD"""pQ8YDDD""" gQ8(EDDD,""pYDDD"""p) ,\#GtgϞ՞nfWK;CBڌs6wZmL>n\ 8ıd&~u^B h `VQmƜM43k3B픷oUTuMS>/gAvn+Lz3fb`CԄNj1ytHLlj׹C{Y3vw7lĭ*c-ٴis'u=Ih7~lڴSg1lPt>%dZ>@*հbh@svؑ={Hh݆jkPJ‹/q\Z|w7lC\O_RNzmQ{[i!nң1\sNbްsϝXd)]xǙt{l̜ݧ*N`˖-$uIW>0!8U?rpظ˹ȸ}cgΜ]|GX#fL`Ջ)'Z_W_^ƍYv-+W~^j,'=u~կOXXu7-os=22=3g#wuig￧=+9sUyI:q8\p՚1n5z4?3lM$aPLse&N'SDӅ jwy'OtrΟ?O ysAfq{u|=z5Q`k86&Sumv, YXM%kދ& 1˗q_fZDԩCR6>2eX,(gʅd8r*2oG`I}-.=NF%++̬L#³{{#^{}TZz,:::oX4SeӿvV8,KX{,Y1:~{q9^|*M@A#t;4 XrVuɄZV߶mu!b eR8Kh[]`5̞WڑعSI~N4}5kԠ\r| :okG^/noG{6nOzJf2x\_ ;ogqەM&OLΘ)s=T9G`=aǹ/$m\:'v!CR2~ng+-@8qc.x/]7%PviP>slfݺmZ f˵ڌ&SֳideiF2fc:URt࿮t2p2]8ïkkT?(@!.u!@ApFk䓞 ?Zm.*!4%>IO+T9$zyΟ=r!t:J[W{?)܏11N'fWf3ILLvh3Ο9OTqOXfbjpD$efֈ-}mw^odBW[޽{6#0mU~CnY9d9 ZcynhmsEΟ?ܯ!hڵ3$$L1k~=~o?{kҰz ( g9Y*p6kw\"""E>W8]8{ l+bU9](;=*hW5w EDDƆW~*C"""rdq=/""ҾgezLAm&OYDD`vs]e\S%<@$pk]fr.jNXuRpM=]er_ӳ^ϿJex59] sd]WfU+ED3Ug&mG;}d*U?6=yùGغ7#DD3ξ:_|*fϟ=wFxvW #ե-""z6|d# :U1Tui|sGΨrR񿓼 ߵoL>U;M|oV>+0h>GŬYDDJkH^ճ3̆GW]1YwWw(YDD4Vў#}t}.鳂bܿ]n))ē@ @ ]D'>۝ƧtR45Q[IVēԁ8@ MScIc$r(Tgńw_/ 뚎|O)@  1c'1iȲdb̘1,5 荾;]OiE$5e5j^~E$bVP(%@ @KQUO')9H,Iݘ6G:qjĬH(L($@ @ =PU M0 _/Y InC45^Okoĩc a[d@ na@4!I2+W,kNv<̮hivM4J;6$n`(@ @ۡibAT梩A,iqjLaZԞ¨a$YQ-8dYC@ 5'hj;hS[IjC8IѹѦ@0㠁YLˤ ށ"ljs)o(ÕauhQX8٢Ժlټ 6m @ UtAK5MzuўHj,^nf2ȃ˲ܮ^_np(p*N!Hf *-YOsi0LL/qqNhz1|>- Yw? AՎg{QtٴK|!@uwL gC a֋i{aPNœ2tH4P0K!!C`hI`$gJ(-M9d"0az,F4EQ Y~? !ՎOoh3Զ06˩EHqZАeϏ?"Q[]KU?8 :nG_ik;vאQ^Y/hT3H?uHPSQIeZOX|j(=jkTWW40[{lW 0 V1MgR%rOk҉cU{StqkѵB\O6 91voz|p :b24i_|e3@O~՞K"hF˰fgp헲oKR.#SbJ߃gpX 'p2m+v}$}25aϽ; ߺga`=p\?X?xti-8v Dmaɘ[x-h UoB0 c9a$se<֍a]@ B8띆djөgS8!f􋣿) -Su*]vkr vk_|}E`V HiVnւ ۴3c6muf[[M(FGf5dE5QSY??قd'9p rx;ud%ىHfOuHMXXSqWF]lDMm#R+@ Xe|F2B ݨ{ͦ-H"„ qkA]1:Um%pcj";EYLV" OU9Hqv3ۖISHJpb@8a$xAÎ<5 =O! TCBfZ!)F5^ )TVW{fn3" oU[ylRB 5Ft˟cJlv]I I5HΌ5p Uqk6Q9_ݖ c7wu]8i0lu̜gVl$Я 'Y䌏]vElDSzm+^*;?tƮgt@  \,5Lqp)S(arJ< :[p8Fƛ宵>n G}GD_%6kVVVp}H^dˢC[6}m !Վ_M Ρc#ȶ_g籼& pk04wa,yߘg.&g^jM b<2dЪ꾛y|S2s;S3HIyxj-ƝJg]2^=㯾))$ _>㙧_a4ߴøÀ]{5/øvTqDmK:2uξ*JV~O-v~~DS9TI]K~"soMd'/Ի+2|ѭW:v 6~<o#fq -i։VLX7q؟4N6WKbn+Ԕ6XU,hNqo yFH2RO۱s ժxjծdשχhc+1Y7vo'=AH31}whΌc2+E4Zԭ5+eqFWAG sPUf>gu}8nfFͰ9SP[&2d[\3Sv x@ vp[IoSj}ZRe ©*򣇃 1mq:MmJ4[hTEUNhTuh EڵՙZ88nL6ֲ)5oø kXh3 1hR@ kDsse76m\d"o\{(ZTSbˊPBqm]IaİLm>6,_8g!DدE_Na l!6,_Z;{?OJ_cC};~_-aEg$#߬GK!od>VUB6\< {KTNbĈ~$l} F2çd (ZYלʨU )Z+ O\?wGJDLIm`62r^f{ 75a" Na |s#-=D%iNBke.Nl:ҠzX.¶~fiA=uwS_^E'rБctL&"^{o5\M# {]`&X{5.0&j (vfcH IKJo#Y%w5Q!Svŋ@FOɓMRO\u$ OD%ЛlKY zr\O,&;⎨$I"!I?̌g:0 [lu|B%H2Vڒ0jCZOW 6FaiO|2mLZ݃'4"FYI܆@`Hd'I FE'k1)c-N,6$ta+ [Ȁ Ӛ| p:O+rI1QB5͎٤.X ~%vseA&SVP0KHF܀! ,ho Ȳ WMp&'44X0~r]| W;dzt!,uϏ6cC–}ڊAr׎LL:2膌!ۉa/§3[`džh_rQIiOHI& ](rYTv  D%3@tZ0ŷ& s8rL_SI!$IЮ!^'>NEFL&~=zl>ުp<#elw~5ƐIVbM&K\#C$I `MڂIFluPuA &XEbhSQn ۋglPk +};*zpFfz%;C#l8K3!4NM෷XǟɅ3Z y(ˠ* fuD G&/LdjhJW$8qƑ 鍵0:kHأE MJBmƆA'; D]>nbØHxln3Bd,~)B*ASwR' #g "+6j֕$X^LpL3:(=eTJz1QN Dt^v9EE8 `'؈S'uKDp2+ Kja@ = qs|2j0"z$10[v' GA^ufb{YYloPUM[ʣi8:f?@b歋oOY\3|&6*}ª}8$e_;'ۂ >4\MMwO+(wW6H QM>q7&!`Pc_UWHBv}2tId;\1$JO׆F_zrdb(R?{]ZCf'0B=.CW܁U!ڷy[78?w%ZE5?ڋ IF[ڊ7vgsu7b޶kHQ*!Q,勊}8ly #yQߨ>^z̄agqcH`6EMdШIv5&@ vp$vY _{$ J 0t,a?y5c4ipC@&`".\:[55ҰZ-:JD0֍+Ѯ&i][ۦ`y=fO!!ʖg'`K-Zc[]ؐ36b'JTҦ]0[{Z'za$tz`Xu)$$)x0$TD kف]6^<kZw{' 7Z.dx'Pv<) !To"ٓǀO g=HjY3-xu-ɶ6-I ^Fs7M5,X<`hT%! qDhJ>,\Wku`6+-lƠAHԮxjXnܧ:CDFHX)q QbwH"wcHqiӰMC$\6EP#XS&+d'Y !r^yք(řPVZ,wM~{t2TDN*TI"%7etNx ̹J(>9M&$N~6o&L+L$PK ,T>Z$'2 ~v*5 xT@&Vp9lˬвLF;ٮ]uݙ40^Te~/Uq*#d w~9$D/j* oÁ'6^L&j&۴+@ ^8:_||軟Ca F[co4MPպJ|%T7pc̃v܊ >"E1k׮XE@8f֭ȲǶ:Y a;iVZʨ2xkhEbI'ذ7,&'[b.~byfGBT"c~W; dF$I Ur<{m=6 䆼1NJ3˦C>!D|:@c0]ѱǷ\NYWceYAcz`o[u݉ 7ɇ~P+aG|z?`n'mZfq:.K$VM`o:0R_ͷmۮmAcgSUD"LgP` Ԩ5+-|q`Z0+J‘(qN'xL4%xXjkw"; &%uvg4VQY֦DήɎ@ĉ@ GuXU8uI8͘>S©AᩪZH$IȲP5vW[@ `N .=5Nu Ֆ@ @ z}*@ @'@ @ I'@ @ S@ @NkKw% @ $e-Yd@ @ $@ B8 @ _B8sgZ@ )Pp8qe|aMtzgz|;cxw9&}‘sO8.'=)'fZv@nnl+wɑGb3 n]k(9t8kHMImM_ [oK[?jHqF@&}>3~X&KGuNn;}?|>SgHfM3>k~\qM0~ 猟7iWMv~oӧM%=-4, IDATxź7|qc2U?^/^vQ0x'r:%\~ W{M?>{u$eE?7')'1e$= ѣFŖ5=3nٹLo:~;@F?e3nOlyQq1#<}O: z?u*ev{Xr?C}XV&NЧq3q7r#y|~l .yk~Ï[{+.瓏>[op`A}"'?\t!X0 u߿9S9r卄Ө#;yݷ3 ;Dג!33ygzܜo۫?uyYYy/b1<\{?yxEuGV{Ӟ}Q78EYGl6/qˍosmwi@ ݡϧefh_e;9z6g~*#`v>S]xA{uh;3M_,&Oca{$%%qmo;~YY\}Tx _w5iir۝ 2| IyHͷ;&ǯDUUOʘ1ư3rĈX;e2H<@  l2eݙit俦}?#Y_gMw-ٺ|M@|O>bҸc:eK_.ZLQQq5rĈ&~-RԪL4qLA~>SۗW^$W_{c̎'ZX?*3{1b8a< | E8d%΢E9x>o70}~MOk֬/Ė5`?~l 3~7q 7|+[zښp8sxPU|SN9L]T5z*⯖pR8dCFͷx=A3dժMyu~>@iiM?06trA3pGnzfL֦M]'(+~ ܔɓ450_6u_$IjRުe~ءx{uTʛ8*;߭Nt_yMR/.[FeUv{x<>Vo@ J8h뇱Zm3.飶-VKt:k=Z] D"%!IޝwY=4V\Im6 ϶g}.WuYYuWgz0w/(/gJuMMRQHOOgɗ y'x⩧y2=Nn7¹gC>>6r-7dfu7^-foL2C :f›où}LYp'pqsٴy3/!>vF  ~Ga6m49V7-wċ=|O=_wgәZhB1͛ʺiӸ8gQ,o=vv!6z@[ N}# 工(('Ƚܓ_.BӴN;>>Ꚛ^cǓO=Kmm-&Nhw*u[lGQG5ȃ?JuM Ə' qt:7vLL$r墯8h恱/aP@k:Q墯uu6~b!bƍb9G3ٜpҩqΉ] U \P7U_,m+m8_-1 `ݺu!?}C W^cŊz~>dbtAs-6/fԨVɓ&2yD|Qnvnj\DAoљ[4.kI 6⧭H{|<{[&M7^3ONcժmJH ?G{ĄD<^C N,sypͷ*/>tۏӧM+f[i#G ]b!==iS_ѳb13mT4]^;nZ,\{?[IJJd}/ǟʋ0vfz0^vZ2y\q?7IƳn+*vSR͑.ŊdzyEUU5?2Ӊ0 F(wvIXm6ʷ/y0 _,dGģWeHK`2w)?\nN|~,{_`6oyG8SIOK倃 Ǻλ93/E3zHtCߖt:l}?ݥnO[MGlyژ3N?G{9_Dff[mp@Əy@ .\y?$G{zڄx՗gkFUVsQsZH4Q#Ü}pҩ,]3iG焓NƛoeM'tPש)t?$G>}Omj9rp2228 9nɼ{~Mh^e֭[ܓO֛ol (ge֑9عoʫ/= ?ݡnǝڛUyXXPU^ 'xmm@VKJnDJ,t2x, ʃܿ|q:.%@e-N UҲ>y}A D>^Iç"S HK/r;,)@ OSRZVk*Jwm}묏w/2VY1_=.wU[%W B8 n'.HYw8 @ ?%Ҳ2@ /GӠCD @ !CW"@ Na1@ @CY @ B8 @ N@ @З{@ 7J4@CJw Aqq UU1lظ ڭcN$fY7~g+8w2F(fIڵ5 bql6F#njc5_| MfִKhbf3N!A{֤m$v;}޾$&&2xP!)))n[\RªUkHJJ ?qqqJ]'r*6o.YtÖ-E=yCg@n.q f]QUp8V$EA1+݊M6xܵFvZm??৬{O322st:v~?JK,i>Gl/2$ib`VBĤ?[2p &ioK>t( \uĕW';wG6mC>ʫWsƜKdU5l--j 0MӐe MtP7:aʇ"*1ͽ2{DB" 0jP2ՊjBjZ* ]O,'-5#F첶, S^QUQ 6Y_SSCrBZz*6|D"&ܜaX!Zi{61b,:a`F{rK`K%/7R$Fb@:d6nǟ%m]$$'0ixRS,2B֒}-aԧnĖ[װ|{02)ș-6nڂj#-_ʺMcT5ZSYUMJJb`0I E1FiinĮwr0ZڷEk(PUUe vl]Q,v;e!2UlE_,jkl--cUȍng픆PQQ]Iee%֬f$N_ot{c/w2tTMe :Lbr2G:j*5&mn'!!Ť4f lظ, .I{Φp`Vk\s{ܔn݆G4޺ .d\zRSSr{$d@bc|]m.*"))V7)) ] 0 6nL|\ ȲԤݩƣpM0t`Z8fv1 +`IP%"Me-1֢$E*1?FUK85T_~]ʴSf$8 }-;r,R M$IDXQE!))-| E[ӱpR5tMCl68'(((h1b!//J itl/dl)*&sXVRScW7[I:`a (..pJ9(7MZ7BݢZ$l :]> G'qQUwVQNfĐ3fCbq^g֬CV, .%5Tz#XSmLQC/d߉I饋g]7%E?5mS\|$Rqdbc9/~ ЎBTՙ6C4EU:Q(QVZ￯fQX8Պj͖JFfpb1r8YYY(lt]PTTL $ vILLdР>/5P^^0 "[0s,}ѨJFF9 l)*&vʲLIu+6i2:C[5kk"ZNݏ?3yDhΞH$Iu06^.K~SC2 A?.ol0R(pc4T#[œ;fᗋZ-N$Q$&&(4]g~ǒW{_-˿)BM{ e$2$bZx>;xORRRBzFFqw{< ˍeZS<‘Ii2S':dcZ:]. '))djjk;m #=t))يbD"TTT:;>``M!gg('z6#77F MYFeaez(gmY3SX83&n^X>F^v# 3ڼKfU{XdKi. 7H4ftfbj*Pǃec?z=]؊'--ݎ,TVTRQYI4Edz搜c6ldKrss%Q~ʽPvttCW4:=nJza *zm;N2IKKeÆZ/na`d-&Gjj ~23)-+d+v‚rSyyE.&%%RQQYC^A.0yVw5"%${%B2 $Y I+pQݩʲiM6M )P8%Iuy_ԵSJihzۛxyÜΈ"LfZP̬LJn#]l6]پԴTp3G8%++[JKNM&'2mJYzuȑ#Ȭ%%[4UxV UUQeO,Dii)m1/}Wf<KWWPZF7~!#=TdY㭡|#nk|_ ']YbcO`نr,d\Jb E4dHOrʭx=g4o-~Yxں U t؞L-;U[Vbb6j~$&$P5k1[蟝j]\CBBn KIV0999dj2acRʐe+Ï?>{EjzzyK7n";~7DuSf3NںY=PTTLRr"lܸ E11xР.WU5+mjrZgdffCqqql ݉E䞦Ҳ2.\Dr%i IDATf OڧmY@SXAn6,"lR)WHmjMދ}[CGE Dn.:YTı^d6h>= pFRd76Zl.@+pS.^CdpH8|J{eQTR,4 \Tl6iLMLs{W_;?*{\ݎZ]RL_zQ-ۛ( +##LOOoc;6cݨOlvI TUZ ,K,/`c̛ܼoG>[۳/\#u 'NR6PK(WWW4BA eiiD"8.\dhhP ԱL0(+?>몸L&}t >S,PUVKcuuAt2@M3 e7ضYw"O֨WSהo R<-W\xx,AP(sxVC{؎ҨV(#3#mSxb7 }nhFqn'Iׂ(; \qtuP|8qk44k9ƝuNmPAU;Ad>f4kmǶ-"66mHKcYEiBl$BPJJ<#/h4EBi4úە\5 PػG+hi-2ɶmR)̘a蘦&jD"c~kK+ HWDCDL ҴHr˗033M0|sSC;s ?L6[/pr_g)4UQ@ @/_Ʋl~+oqf6}}ޣOb `%_а8s<$igiQϿʏ}lk da>E4[o\' 2:C%4D}t}@*p&GΌU޺7VJtl3-fi {s᧞ړA̍$渴 ׶yg%tEK8j5lq\_15uJ9Ə$chQԚ% ?_G_~w-"GBu,4s_:©ph6t ?,|rY$C\$ 3:#P)p+LHBH3;hZ,FI$wKEQQ(288Hf._mjO()121;oy>]ةIǶ=:_[uN BAEE%U^FEFGoJƹrkضcdn9v̏k躁a\2_;p.GQQUzݍ4 bAe&&PUٕh$**~vMMUD}>ʕ hU#M;JZeQnl (W*D4{i'?nKkZ˕7䮭**UνM۱Y^^In/nwp{lok3t]:vY__G zIve5>T&IS*mrTUuS4QUHh6ضͅB|ױHj8 zIud0Z: }}],S DQDQ$Lbv$U7 R$|}=gl7 zC'Oc5{(4uZ$K{jsP6qmw"N;-!gzLG]6aAv4Qt%5A$DdĮ4$VM:˾%gWU=8~ebnj4 |W }ϡw048 `QEc,-/ (]izEnuo89>JmNp+D""0,A(@$uRUzi~1h7n JiS,DsˢWVU @F%MM&'so~o,͊ c*3ȱYY,>AѨyc1 5H8 FGH"KN9^xem)suN/zJ4(RE J"ȢtzO=7NJb-'vcOP$Pm휾祗_*K:۵¡R{#3xʮ]cp`k7n{ritD܍t봏[L>Lj*s:qr+r9~?mO#ʄCC{ Q{ ݔ]iǯmj{s|NwÆ  VkB!bBRip(-]׹M97el3ݾYn,)5&''ܓӧv$7ow%1 Ei'F0w=N>m}ʕM4Aՠ51MEpӴhhMQB7 ll|kN7'4Ϝ<'^Zd/pt܏m;lP  𙧆Wh{/tS<܆,ˤ(e&ǻm> >si#ngp|UQX_ L"2GffBUUDQX,JN$ٖZI=E|>:vvIDj@vVVWqw:ٶCya` *zLTUYu_9NNͩ( E9uĶTFo}>Iu}wzbstw (hNFJ3]o5NO?Q筯wiVCs7!OY'9LGi6ic;m" q4æR϶XQ9Ξ:(Ȳ$IH} Hi9rJm5M|.Cn?DH HCXbccD2ec ssZ- "H ځp8Dk5~GeltdO :CTnw$%[ { غF]G8zU$24Jlv>s'ItMG5}]|NЙVfnR(9}$TATd?sn~k:86;//-~ Ne:|(x#gI?~޹T#8D"IHT/c0[O,eh oٳgI/_o/QdtΎ0ާM dYf=j(Ws#2FF* (a[6rZdIZz|χ(>uLJ?$Gff3C::_aP,iiP4I$ _FQNF6,#S77KIe&c#qeBP;b&BEI&|uX(+c,'N>|CK<+d|#]-0z&A{nnnlxЈ$ޜRq"N;hE^N!h6FEibZij!ݴ,,pld255w^|KYB~+7KFö.qr-ǎAkix&Q+HD忷b#_/)ab~>;o6qj&΋$qxI3jNdd'ۤs|ڪP(td0=H2@e6i5ƕ( D#6Yi+,Inm 6. ]ި$CN:ɯzL9?pr?w{yE8u1~q#J-W_9F25~NwܱG ˟GQULäVƫ/^!HtU_VoO31Hܣ`9UC fR+,a5_eq&o]Zdn!=+,,,P.WaѠX,RED۶fH4W?8s4Z??:sҴX­UQEEV֨댍 GXXDzL7㚐NquZF8A h6&lۡ\Ғ[V QB4 ]Iz&%lF>V0-wyml6G6Eu:hQ.Wd>m5M/Eq& Z4T:6Z] +5M2CCP-#O^{b׵m{8uy$w I0MjA= U-{DzڪzN'V-Şj\^QI4vHKp0ͽm-ˢ?åK^/vlgKmߊmm@8b+pTԛM$Y?iZr9bCC$qzIf|Q ݤYSxWyOhչ[ܭ`dY&n(]¦? Yc3f~W0JApp!tM'_*E' cSdUbLFZ4=pPQWQجV8َy[7ݰp:A4ux7z=kn7:G(um !!JdaYbl6aZ-&cc,/TUߡGlwK˔+eRpAZEQBq[;mлb(G(࡫ݭн$b6-["&x @K7m'vc [jd&lq[crr-) mrNNGvm]e |ۯޘۖoIBΞ}itCO$[DHQ,#+2O=$W]{ xO}=3D{y;$f_䶴SE};NiM!LvHwx| CC؎ct#j(b{QD"n*mY&L$K؎UruzN  `&vCڵ(ky?=( fBտt||P@lD Q@7 p4ݰPU3/q0NX3Nlh4 PUaWfIO~Y;sza`;泈`*@$2AmUK,\RJ]q7q9u$o&.^dfzqw]oMU>_7l=JR^o󬮮!K2L466͛K+ʕ ~+N;Ͽ@:@? LOOv#O˫u7=/ !2׮]S>S.WH㬯oN122 l,V[a hNrkl6@?~x,~sQ&F:ds94MRZt-Rt_ѷ! &Ri[*iL%8bhl4zحnD_QݦOQEJPm0Ev{=Mƶ'۲jvSLܖg֞zXy`JoקT BA9y8ǏsDY$%P{H8ϧbb!NgsK*Aާm`Nm I]wz Icb[֞Kdž`(L_bseTk5 =ʤGŶSֵ}!un|K_s~eJ~ֲuoªI&op$6 EuΗ~,<9׹mm4{+u^|*oy7jFyꩧxϓN148gk(}vA`z4ų7)״>|l(i; ڶM`uuMvgd29szBWUn.-j̶H8B*\q Q֋ `c'1ma8.͑d0&o9JFzL&C `aaǏa-8UFQb(풧f3@(^olSs5MzNdhh@ ub(bG>HG?r sDgr*jmhphW|mi:T-64M\F6@m.oOcڶ׻!ُzI'א7 Eke}4MILӤZi'\/eYTUdEE.ϡ:Z0F qߑ8XM:麁(Mn;d8(c;[6[#NR{c`Iv @)]aVҥpM .E s  ®,FBw"L(rinD"(4VnAi-) mr_t(Mvq/e4[vC$v` =Nqܧ9 "z6%q 踲׎q\[({wzOܑ|r ?*̦ N>F D%QħE\[8pЁKݮnMi >TEqCZ&fV |[o,%T5|pSePmh]:Uh46`?{Dqteb(XUQ_G:\.w7XVŵ%z02ʎ_(45LӢ]h6]AvahhX,k81:j8S Ej:i"R{r{ C!XXXddd:B2ЍX^ZZbzz٣ .Aih_GQB`d"viZ`ee@ ľҩ>`7[4M$I"Ϟ q y_8ݾY|Pwdx}܍8A4񙾞R,A`;u8MLmp&& Sۦhh4i4R^2 X4ʋ/&\,=vn%9 9ujJ?OAUTb{sB$+2nbZ& #avԫ}Gq47~|1J+X(mÙ?׉B:q'8$рBiXLIQ}*S{=.]H2$Q)D+K Y?K?@ιwn~6m-;$qN>(2<}})Vz&-%QỞ8DZ-!v}vq@0ƍ(=1p8d'5P(:88]Hq<\bL?xw_O+qn¡ckk02P~?^X\' JBAlbhh &cwt##:gc&xӲH&=UrnE'ʎo%0<jrJ,eb|9" ݒt-tٶu 8.9EVGA,>Q$y-q{q+GDQT%ٍӣD.yrӴ놢a^ƻ]+1/yfNaZ&sWџs?ypP=!­w&0v.$Y'{iVy$/080Sö!_z;A'BHR<z% ЙsD"XaZP@ @"ghpߝWfgrcmUmB;6Gu{$+$⻏^(LM1409.]CHD.j675d2sq*EQFGXG$Wvs[9"0d+TPV)mnR.W} ( `nH [^^a#ö-4K|똪21>E6MJ%&,H(]O eQo41Pc#(14^ctb>h(L< ~璘A*RWv6$S)z N8veC?ma~~ߏ ~3 c[6cuDQ@ ld7mhO画}ǎ(Y͛"?[') ah4d  ܵASG8Guf{W/G,=F"A)1 @O*yr[ Ie}>E!LF\PMsQbgdhBHZSU*4IȇNQN< n AkG},`  DIZ͖QQe3  ߳3V 06>?^c޽.3Sɤ3H$ݱpoj-pgbK7 ޘM«ӇW{{5? b(z1m{%qm;6cp88tb]h$T7moO(]W_C聓{;(!Ӵ(71ﳧSTx4,KHPbjb|.υsb=@Tէ288@Ѡlrm~~R}n4>Hvx-bracŻd"Nqzj*3GEx.pG%2S_ey ,J2;υ ) {%I ''  \W1w*4MA9~(GffHoFgW*/ځYfa:316C#j5i4tMG% `A.h6hN(Begl$QmQ[aTK;D@zA^gummCaB࡞qW>yEU\| I3f+o/K&?s3=߼ &7n FQݍ4M|]^z'?Io,'s>^̓%MMP}*&?qR*D^G4dYqdjX<,K~Υe$;@?cc#ϓ$s ~[8O-,.RxCضMisUNE{'/rWdVחWmw6J٤P,R7u APP(H*$8_kids9t]GDFG\PPUtp֎ZFV#迫B42,09>$=ϙ&j Mױ-q|J4FzηG-q:̴7n~ l\O}{_#Q=Z(uKI6% P((Kj5J ?؍eq5,"I pՕ.^=8;8i6e(#1ꬬSUpɉqׯ/pEt]GVdΜ>|q**of{.q8yDϑ'qdҜ<Ÿ; ,/#XhN Gu*|:Gf5>u'x?QU8-//# P70t;lpsi}4'}+++(NCSרT*u,x7I ~}49{]7My]t@%.]<~1$mݹv9rTXMQg\4|碀iZ4bi/} QeL0M;W}4n\b'O`ppY[^4nܠ[Q._ig\jeZؖ,+~B kLNNqh4'N;ذ,IiYHnL {%|rl.G$bjjߏmۮB iX[[T$͓H)+b ho3Oc{!N??ooλxC$:11O_k|"{Zl "@BHZA$QѨJ&PZK/±Yxӑ gbmqlZNT2EYyWO=';FFY??j5W bPcP}(ɲԪu7fzbl.G,EQ;F?ΦYuZY]iD"a D7t|G 5~<y5#0+kضSO<,8fsQEw㤪 D"!9PRPD {h{7h4i޽ņa iFD"D"aE0-fI٤\)3wyD";}Ѵ,:QDAdyyŭP}>u4Mv3l h-ɉ=l6G8ػ6 0ܶD|>jO4RPTb[ȍ2gذYF%ǻfuކ\6GVgdxx<][Vw.e`0g||d2=E>gu#G?|+H8z1:6؏8X,ʙӧ63 Ȳ:RgGX($ Rwsi? ffI$H̅m=4N"o/:S8ݲlْwgY$dB)-(-)B).m!JZ +d؁NŎw[;&-i~<όf9sι=k,xZUUB0=W[W[wuuӛڵkGG&LNŚHTAzyHRtuwcۨO(+Jx=(ʹ724#9EE8 1# ކTT(W?y-fY!%~ט,'(Ζ~E!Pˉ=k644n/H% ƨvN$c9GfRUUE]]-F%Q%e8ʹ,S3ޟ` 7mftt_ރjY?םoUU(xJF.%&L[$I|sn12:moF 9#GvxwPV^vqkXZ,\s'V6sCqaV45QVZJ 0o~wyǼzZryގh0BPY|f,kAOw$I"L"IP( I$P_G$366jaES#O$!bA566(*fHEÎh1#r9˸qJ:M[{#|FE|Ŭ_AS&;@Mm-plv[HE\.'=}Yx,J.ꮢ(HY AS_3_7FY%yS;:H?JK<Y֮ih0PZZ夫C҂iFFGsŹǧ-N4Ox"A(/=!h21hjߠ')..˓ "DDaSSEUH%S!DAP8đGqYpBK1d2 up8p:os8$I88OF)))"D0[&;xDpbZ"(166v^x v#INuS^V~Fp$B8:gLpINrS7'-܂dO>y:LePTGrǺsO}o}络hc|;哟L^CQHVEEdJ~TUbxS{{; QdQm:')gF2:/Dg>G4ydIhn^E,SE=ycDuk0 Glazd.3+9.aimms֬it"e}]N'O]c20"(fMˢsRi2 eHD_?(]϶[)--EEx}RIڙT JuU5x` Ŵ$a1)Ve7=2헑$?@yy&۟H qn1`p9lXbƓfϝL (/+em8ysOg> m<gtllU}C20o#ǎyTMR1$D0_ANL3=U666%6g}MNh'{d6c -nr5!N9 LLh̛ h1VF(b Txܞ59L9OWjF㔗^V:WNUWU;ҹ06>C?>>̩ήba1N^y[ eRPWUawg~.ۼi^ йn2=溺۷.zxn~;XzNup BdLfRG#xXd6}}}yN.֭FP;Q/D 7np.Q$`0iY N<%cO01d*ʼn'ٴq`08ry' -%쎘!2ux^TUS97Q9~$֮wޅԯa H"  "˗/d6]ӳMFk/ hfe5J2l6Ϻ I2t却`0 vKEoKS 3ugK/miy~Yj8]8\.'N lD]}=v}N8v,Fet U=ij=ֽjU܌xKso2X̖dlXM9G|&8dbIr}ttB\(Jii)l60 ƚΤ5y屚^QJ\\&9T9JpidÎ:Wl6++FFYnrnILfS.圗lvDPTB*b$L280&HLf ̪ʪi p8XMo&J344DUUU(N31S]UD0`=T AG`"@Q7|hSGB8Rl6[6i)%pao..- qeBY^⧷M)@iūWfrJ^c?Lly&,oZN |Vun.N\^}=79>|/?Rh4̪w:mq ~Dx"l+e;+#?2MB{E3_2 vEQ1M_Fلvp՛\`4jRNF qڝ@VkN(l]FRq82cXrPשl>CsgYpiMd>)5f*޽/v{hm}3wo_/7npz8~3/6pKvޟ.,+MQeEV0 \s5\s'}04~?NV6x@ۼ:>EE^N)T1Ύ.ma 뛦ԶX-;;A߅2>&xߟ"YVDbƍT:(RO0bBi x.cBknv~n9B%{h~}رvMk<"Wvf~NzU3XH$BX,ΓbʕvٳZ,dҙG8!NcÕN # (dҳ<_' P/!h To@5|X,ƪlvdNW- N7yyΩ 7x#wzr7;Qھ|ar*z^WsSKK ^?'NrEem7IV^=wgǜىsxdH$פZFX3:z+c:NQY^A2$ XS6●=$ehYZ32 rQM&9~$Nv͚iۨN$vٰa}nRڤSUEGUU.2*+g|\_ۜ9I4;x=44ld2166F"&6c#?<2D`9-:W\qF+B 0AxJ`ڃ'bߴ|f2MJI&y'MҤP(L]]͜l6c6Hg20XP${8|0D1mj HzYQZ-~y F‘ A"`<ݍ`DQTl6h_>8`Yڂ؇Q{BgD$""XY)0G(YQb<l6QYQْ_,'8rX.U sUNG&/6 wKr ޟ /g<k.s , y;~^{s-w+_P`ulV3ǎjb28v(vo]ںZ>q0;~ͮQ*+y~&"(H,#y#GF,+hM"2cX||i|>ZZVc4G~D+bd\Nd*%u$Q)-kH}mWpݘfFcΈZ,8N$ Ȋ;q$m;>`ll@`1FFFҲ\v 'I{SVR($S) ji/+f2p#saqg$D"Ba$YΉ13R4<`heW| :?Ofze};`bE^/Lr1 ߿|]?Szz  .:z)TP?`2 !LL%DQ\B2"ɝ/yy_1::mV+Pƍ7˗!K##c(B8aϞ=< vH!{  cxۮcYC=$a2 (J(bxI)?pNXV+#srUadIKȊk9-C»#wy-S6 U%21: p͖3kX4YpE;QUdr-od[{)#>ƑO?FSc=VgDQc6[QH*lA 76J$PTHYY)/pl##O` $ &u~$#cXє/*208@g),f3DLfd ro \p"H2dҒ0d 7Պh9$IBl6ckБH RHi14yMNM:TE:::dˉ`t҂NYd`pr1q:|Ei2F.NJ`/Es=_NN·Zvl6mTWW!==^gT1[k8r.TVV{SgpTWTD|bZ(/c`pb~"ѹ FQ108HEEy}BVdgUz܀b$ A0O?8PUCMM>m[PZZeyy C AE5SQ^(xB8N֬ii9O?o~ƃh`MfJ>{mkkkAd2by'h&~u~j*4Cd6|IȤ3 3OۃGGG'+0- (\w (bZؼi/,V X,x(/y st+۴վiq L&Ӵɾ0YVnj(ym٬ iv=B‘(PEԿ$Iftta-ӌX,f'U[^fNV_[[CiCNJKKH$tvtRSS`< l2_K l\Xm#zJ"VVeh6yĩh-`(CȊTOw5.E燝n{oo?Ǐirm-x=䩒)Hk䥗R x<9p6GzAK8<2B{[W\qׯw9w}Ae y' "NkM&2BE"($p8Ȥ3ua:V^E:B41(X-iQ"̫"Yw@YYU;UEETWI‚0_DQ_L$bfxx͆`hbәt,rZeb?.k}B(Ò2fڹNOqH/U/KOO$Pt{q2>tx<M|⮏RQ^>c()< F"l bVr10=z;\Fmg)Us ml@D$YX,sm7~:üʓNgj߲is)$Kx jq9vш(Mf :;ر}+W~F&.݅㡿Uʲ}EFDKA&#V"[D$ 8)yII5RB0TZK2LyY)IR "(b4q\d$iιB?6mUE%RNK-y "A`4  -i!+q1l6Ѐ"+BQdJhmm]3.c+L&YV_OS\~Bl8i;"_[ ٸlb=? ~1{ĺk]ybQ)//MGG'ǍnCb8Pd*IyYkZZ0[̋f3˗7ގjl2#,sJ:ɩ/olպd2i!&VTeTWW11̹_ TVTظblE)d7τ֖7*(47aאj N G06G ڜg5[I.^o.a!υP%c8-&>֯['? ǿ?ttv/|y&In|?O~p/_~=?_2w~+?ѽlۺџ=3A]W#7mݪ7Ş{?{fϧsIL/KUOgG)ڝr 0dѯƳ0l#0;XvDzɁpފάx~û'?MMy|@lܰѱ1;:ug8Nu5kִ/kvΛn9q뮽5u>? \5}}|kۮ1:6$Iz'7u.L&OAПg3ΧOނ ;J\ 2]q"V~(,v1%ۮ^Mj|_`-G_je˕3~Vχ`tMۚ* px2l߶Y9q$Bav]}մ*N89Xkjn~獼?G?{au. t+I?zyp,W[dwڅz%,"窥76iY7Ʀ%l7}?οǞ@Ews*?kvl߆l,fdjFfO&Zu*5?'~xS$V>/ s34Wi6=paba^G BwǒMjj'H#%] @[[aSv'w`0vq\GEQ,[;x;r_|O|/w|p)L&3V5sߏd2鍬~{564v 9-.'t66Hג2L疴ӷ٦NL[7|bGS| Ot8:7;︝;yן|䣳}M}㗞FoP x:+"VM`Z7,L GX(,~~IYn8eft4^}5^}u#kvq镢sp NZJհU9\*JKK]OBnV}l4j-'ww}TVV^)::D?)L_00z~g>a3Ţ!+a%{T5#Вn*3FYY}]j;ο[\.Rtt.!t%1zeˆD:+c4VWN|\:ă%ffH}w.lˀTٷOO Ng(~q:;#()Mze,յo/~ُ9Nڼ4U9av8nO~O^{O=8\UUEEEU -נs-~ PjB+,tpT̞Twl{y}vUwwѹ&h`:h7E9kn8燋m uHYISzE,Ɔ/yQ U]\i٥蕠siNMbH ѧWBLk1$o,jΊנr[wѹ BKu!8XZU=8A  ,0u~L%VN#4^hrSnՃttttt.qRd QՕf[8WB(mbM'W8jɶFgg„ʎӦ IDATe"tttt.`>]Ny xt6xK+lqQ" _ݼtƒ8.ɶ0C~r9$V"tttt.P>ϐHd ꆓ",hmz%j䪜^a1y1~4$jSS1 **E!tttt.TM8={$6颩 <~)A%/nG9׭&qiѨRwG8t4 ÕrI,w_::K9 #Rzd}N@z̄qS rws==g<`Q[q ^gxL2Hǰ/,$dTHin.½K6sU̡?ER3d~ȗ ͒W;KaO_::KUe?دhqb*hJ!z?GhJ]a* 5x[uI*M||CB9#%] Ҿ>Υvwir/){Hzޢ3ƢDieU3/כ<_e*_x𧖄R2bYˠ=ǀOg$>tRs2?Spo%?oj`2xߧwʊZBzSzA}7$z 4@|St+y8-2$fv\^7,tzY ? rOq[rĥSW?:^t'{v-E ڷtI{# "zo֪Qr_=4J2NTl窫s7sr8%c#+%BN:lcԋJㆍ?eρL\t$ٶhx77;R"jddH?i_`h 1蒺K'W~ <~kͷ[=)2B6!IaMG{o MVޏaIt\؉zʮ*2w5;.U0E!;w_{,*.u;_\( pORIe)}T>1PZ]0en0x V?g}9Nx$5J:nqyPQHhCQ/ 1sµӥ8?Yoߒ:qz&B$B A4P  &ErŖ3minqK60lLKe3u li7\;󎏣:33EU-M66B RInB$$M &rܛHbƸ{/mIUw-3?dd*VYm>fv9sΙ{Z0MW'P3F71nŕݳwcÂ.:٧AUȹ4=?&Tvn~@Nzl3}4|8L&kg~z;CC4UR0QN=ܔTi$_e_5yDŨ7῱2H}v.}g08輇umIi?aSݽ#>R_Ee'`zh;]Ls$nYE0T2P{ҵ]lϕ|du mWsv}L_w=z?Ooؿ@R5EwhЋYs~$Ŋ:;o~kQ%W`_N6ۢ0t / }^rH0.%xp3cZcn f/d]obqfw 峎>V?~c%-o' n{g_죏lH?xQ܏O!si'lG]uOާy8䮃5\ql-ǿ}2]峾=1ͣ^:7;6:Oai9e(p+4ɵ%72ɘfסcþ~OleruL* Ȱw1cկmA˭Eu_G82&]Uwq d/P4e=g6+9Tς!ri츱@`})l2D\ɗԯQ_ Up!=lݝenbe/wcvѾ8\)!?qk.Kai떮v9 xT[ZY(E]?SI^>1{,=]֓ww|GȘ&,<־{Mulতilܘ\13N3Y?7}QqbbWbfv51N Tϲl*fOSu;-OdIWβGKO7;ޢ25#L4L/>o"?)@*[>1q{wD^tui߽gM4v`˗rU>VFRR1{yumGGa4J[qE_nM׷v 8h&UXlGLD!sqL ܅?e?Nx^&TM]  CFPJ#{Џ[&n-~ [G(<?=Fg6_)|]ke8{e,al]ڊ'S|>ww]V'w̆M[1=x 9z0^;Y Yb!SR_BǙq%70Z[}\ϣ9;PpV0V:Bi=6|漇fF\)Y FL}sS:rx) lEtZovֿ&P Ug*S[N/rg7G!kvh~I9_*f^}&iɦ%-㶲N'$ґަVÚ+pJ$bq1FkɊѪ4bFbS- #A;>~P(@#[ߧŷ@-`33H(يÖE̴ 擟uVgz¯W/</u =:Fmx偍ƚ#@HɿΑNc3*b/6n"a+ܣW>CqE:},J8N'2Ø %Z 쓶%p2SYXvgQ{VsHM$BQ49-sX㠴.yhd@ 8u8rHwh&U^'Zq?M_"O.G15?"JXp0$mwϒٴt2M6PqΣLMgT|G96znp=jh@ @%l5׉6PvC"Q-Zicrzo)Oq[A^bqŵ6ΥTV *2^ǚ0zb|lb B8 N 12$O+(<:hDZac3b1!HQi/AD`BDzJ뱓x-7&^!x…Ú\~ 3#vآgU{XNAXDB8OM@J H l{cYhZlIENmϓ:Cm Wݔg!ΡLa љ"锤'ùݵm;(Պ B8Hqrv'X s2*{޾m_61TQ(G)=#w&U\i3[RCr }֘TR#BտeUO7Etu~cUUU\s5K @yp) TQ@ 6r@X,j6EK9}No5Ty7!zW{hmm{hGa@5HT5K6#Z(@ 8IEGFjI:ڥ6tBpܐjj \R6fff19;/$".ڕ6Q@CBNFE!\uچۻܶm䈇~x28,L$W<2::A3@8҃e;XNV.Q;9F>RJ oNeziV>z=! p#H3A)B%(,BR%LTt9BB?0%T9,zA0(0+rGBD/S٢@Ԣ7^Z.~~ 1K:8V"2z` )btphFFQ}|"]QtaQ(*a !lł*ٰ2d!,%ԮCey&qʔ)bʩd#RU42MxdBa m?sQғED܄S~UқUbJ?iJA-j$8p#7XueDQt?K㳨X BI[LGg d1ХAIHDA9BHuBrt! ),mEu;gXJfXP桀!I 拂tۺ?xɗsy94 $yM#$Ƀ2w XshycǿK몿7Gpj7ت%w涶 Q:R 8-MGpz}(2,MBIH]6ZLdD (A)J\n,kW¸d܉} \9UW9ΘAoI"qX5+쓷lN6NUqV7Ux| '&|Ov?p!C[)ɘwM׺Ij:3אA_w!]>|=jV!c:沈Yc0ڑaO=U\H@7Y!X 'C>*cfc+1k޶Yh:֐1{ϓ6a1J(5[ZH\hf/Cv@qa+BSOǮ _Q@vA?jG9Em@p[syL.FԌ{j1enW4D3&aI&d1e 0l+2F$յ7_>.#>4BqR%ۅU,,TNN2$7a Z YU.ou߹g Z[/MǛ|i5l5t~5)P񃷨z"w^ nóYonb[i3d̽__c<*~W_sPط TE}vw-~dFDaR )= p Aޑ^fIJ뱑^)z$PNaiF/ ɩ,CEs! a̬yhV@u,E&P?)cN'!7R"Moj ɢvy_ fb_q Ot Oկ.!xp3%_yGn>4xXC.f_GI?KQC7$aSgHtmS]9QW'o|yIXd 72W;g/c\N~cB{f/c5rk-GZhc^Ecu\㦇X|6͉aHtf|E$wwo}i̅'EQԚ!2n_iRplkl"KX{; 5v~a^uۙ>}:wy''NiVg1lJ`; .1?' Anufu9f![7ET[bLҵ6tuQ3@)s\~tӧ_?J%jh&rǀYIE\JZ? P2 pvY߮YVTD_&쟉OE]Ě5k:ϯ]~^xs9gD?,aunAa VznY+ pN9s+##{Cn,/@-)UsRc$V<+P4?y߳v7 |v!"Pz<D"DXP۷wvMz馛hooRy`2䈃t\Lk(@ N"=~řmLSH1˛lqUM.#)";((HAw~ayjkkyG|,2/@5Ԕ+6R)* 8 n4# B N巨SEHys8d<Ӻ#tId4SwnZ9$0iXqFvEA5O@ bkՅ ! IDATA}W:s:`߀) ɽ, zfؾ#N0a䷑i))(% 1o! w00ݏ~ff]E-$I"o|QO… GtX{M H]%TG)ߞ,`l@SWan`bhr14ahTAr<以\9nt ~iVMR*3AE)GzJ7kW2S/@ Ȑ);. aB9E֣{\F)rd\J( xDA־i%g˛{#j/[HHF!N r{}q;VH5OE#AS;ȂI0M1 C>nFaNTE`%"2z^6&FgJ`^c!dܲV R;##g`!ޝ}~&OXvd 35HHb1YmV5< c^D&n zXQmS?ާ||_@18͏PבwVqb=K)k,k,uG,e"-G2D?| \%5?@-`-RPll/lvxH]JʧZ &e==+g{MSXvGU)M s6VFhpϦCjE' ] n*K`8xoVm%_BSEq)DpGǬP`BpBaQs lc#EM M GӴ# cHUEe>籖whȈj6=0gQjЛ6&SG}\+&q*SX8 KT ̍!5BJyAS>gbg ޣzfn8lSpc  n%NBUƷ^÷ʣM 6K9K>)dI.1=!oN[@) H|n /ЦR.** 8ƇFDӹoX&{^!Nn醄"]H`E VwGYg@ZD}CZZA57;WQb$P;rP7Oa#J(U+oŷF÷F#*|j )9LstpL Y}w?Ua?!G#ɷwP%n+FSU&omx,V3&pF~Vܯo V֗촾dd^$jn|DԷg)z|GI]z{3IV+cx##Z򊏢6pv_VI&Jgr,l@'@ ep x87 r]ݏ5|'4d?N:z(fD½gQT76!zTPز`o̡ 5_ K5kP]] C1V~PƚBo*a$ .qf_~7)١m [fJgf,'ɽY@ dP3N3Uk_&̧CcMJG3(%GQ-'2[Uu~N-D`Doo.2sUׂPLD5CF3['oYbk4&ưg]WY%?g71TN4{7.^nOC,EJK֧Zc_h~΁KZև,=Noɾ4@8/,2gOx"???DxuE5MiLo+A+`3VZ1b3ֹy_37Vn^)r1 S=tyzԖsiNQyw7$ ';PsLj[`>Mr!^E?9\5@ Z FM-c, 7Mҟ WahyN vsu niu$oK9XSϿyC=B2Ft.`i l: GD F,PEAe ^p&? O|3C1KgyM,mVde;C@0hM)!ɥtR ٕ0E K`crd1f. @53ʤ6)+f*+9D+cOLOҒ{j&D(Hb70 8yӋaKvf2Ӽ<*xv.rG$!Hy^P+D"v2R qYJ54 Yg2+3Nv(E$ 4͉=b'YpLx~W!]KP\3u5Vy"W7S(ߤDDӧw+T'ߥS-/Y L@-ǂ/{{m^ҖmOz!d6}+dO+=2qk6|.KŔGdC8". I6 ` E#-[r+OXV:̳f e%r6튇guLT;$ܜѿQa&(jQkE5Pr 9Ț NѠD4 a$m2LU&(iяX, ԫpJ@p3Id;xVnCϜfMҙh.e\N[MPP "$E:R*]_?GRmbD_;K/_0"Hӈh "MKבžyV+ݟޱ)YE2}b֊(jnthu9~;eר_`!۾{- iaJ~ԆsE*G=[!NZ#o{#" Ap?6 ҏ~ͯ>|,.kv5[is 5X3? һhx^ {`D>bݎ\E +TdU}7 sFh:a{[P<+n,zדLVocx 4EMh2<2:i|4-vd3I9[1#cZ e% mzbm02T|hm8ߠ2/RrWZPnGϜ)'7Ixq<,@p8s+ϸx8a̽v~y@'^˸_oF-kQGOz,vװLg~o;x-Ȼ_kڴ {,3-lJR \!(+Ոx< W_$W5 =`􌃺҉`<]ǵ0k}jIIp X+u wm`wC+mjWm߲Qpm+[U {wA$,TAK/d(b฾*e;e`=@.sB*ӧx2>8'WѴ1LC%{|$|IV>+qI3һ:D7qͼdP `֭_Uqq9t[|9&L895'9Q4*qai^%þ]OlR4fbhO}9M<}Uƶ0ߦr7Q 2 yV||2[uF{e{e1#ퟨ~JpoP;'-/)"{WiIMa%!T\(AJ%_8TH C_n!֘9 ZnyE@d-͆Żm9жU_˞AiiiArrilltb;q㞴א512DڎtX {.Ysv9׿**CS6v%q.b~砸s'md.G٧n<FYz~\4>6FC^'P̨AH ԡ75ns0 F4,ɠi(vJ 5+k(lE؋P3K6^ [Ez)GVt^naқD_QzOϳO۫<,ڽwXNN<]vY$1P$@I'G#g(Ao =2=G}ߋ/fҤI]qt9SOu{޼yfowYcQU5zr?߂ '1f%[Y;τsg=}y U}iP,M ӈQyJ~s2E=u39֯_OeebiFXI $P3 o (Ѣ XdIW5?.ztgv;77H:Q,]E'pjIphP5_YtZR, a^U.|%X)Ɍ‘?9ueʢJ;9W]ֈx6ns޸Ǝn9a 铦[ anL6(Ǩ~F|E2o":?%?iCDӧ,GplB8Oȫ@ b$J1dDr( pmòĊ*b`>d-"$MjpIFrSBw2h # Yaw+MަuJ"A\>@EVU1m#ۇвHݝA GhNbuIKx?RMO9p+vm۶%ր@9=5{d=}(@ N8LfYpil֮'6WĿ8iPx< 0N+HY̐wFmYDc>9Bm^2KoihCL#qQtpmi۶Y9~ Y<9+F_CߨQt+c+{v.%kInO322ې%4rz~{xs(@ V8M3OgM@,su?}O/X$c[(?i%,2ҽxk_'5<ۘ({ɺ(mCZFh^rPIš_t![mHRhOKz"ށk ][U ԙd縉Cud:A(ET3u?ڌwF.b Hys1e!b0|W$}i"GT)ˆ@pKAcL01sAlK͢<2΄dBV+1?:!h⍜8ƦÔL>Qߧ6\šϝ|a~2qjm6(GunNӊ7iX:F5i& 8FEI{C~|{v۷/2: kqoXM3>}5Q{K_Ot*k\c4>ϗy6{\r 7xc{K[Y-[uNDK! '%ggr1@ou8ODT}8ɐJk0 7SmY$~=-v'k%e~jzpM9SHc ۈJom8Ƌd̘Cspؽ`vG$Au/ h|$91CoQW028]\{&E< 1v !"lQ@ 4F@Vxj7࿒%)1me1ļ{Oo,"-䜫Sv۸3ԽxF<2gCOwjzY5k>fԠzm[6ٶƷatB9FW연in'RbUY Pi}ih%$r$L3ߘJ4( l+"8]`]" B #f ya>XQjjU\_Ov†p5 ׽Fշ0UL{Ⱦ,8_4}V1ًu%!1)^sDc'RZpu?GۨBκs, R'A5S CC_gP,J~u&|jFNQ ě-m(@  p,> I@mO)pӹp W\S[z7b~1EYFci3yqyP/k۲"v83| .YqL e^v [q[g z/42]FƌӑJA8M)i:ע_o49-&t| =ŖLfL'݁bOO: QyQ@ D3騰R Y{ ~~7f;՞"~܁?򯧾\36knVfI^gɰfQ?{u:;cԳ JuQś} N>cCV IDAT8-EXE|m+[?65iZ&zQmcwdyf/ߨL<Lxa =BQyMA{>6G#QMskqM@Č(fᢥ iitn=g&i"K> ..{;Bt,1tڄ8f., ts[tEpqWbS&zN-W3>aA{m6O3hz"6]]KC~5=#h\Igg$I&gљ9͑ ]q;Brw>X~Z#uT?Ί ^v 2꒞D3N]Lj.sQ1M+W5H]șuʜm;Ŭӧ[E!t qOG_п ˯HW;XV}"9X_Ю1= `y`B3g~2>gI$5l%ت6 deHTMZiZqL̿7u/?+#ˮN}L1\(M Pp .L5жc?.Rppy~S,puʄwL3k.gkSж9NaWeSs23:?)āoZ6-LS؈&Y!&E4jR_T4IB93>{=P-w};F9}~6){hׄRL(n& A_Â;πpcTfwbd>j yOp hKA  ׅgg)&{*kI)`F(+mbڵ6tTo>nvJs*ym̩(*n3.+J^~E2^׭-(&?6]0pHYMjbוpw3jT]Gk9WFVq{ʽZد @ NiZs!J9ZjPr ) yPRC%L}3Mᅤoڋ(jc'r^2A۶MdF]8_MuT>`eײ4 ąaq֚ɉ{^̼3>"GFF%[zNv6-A/-L'ΨqÜINb?,\]Z+>G?͗HIKknf/@lL;Z=s/ews\e  ST?0F=E|I?#!,6?RIx\0SL5m1Rt jgD# w~O_nxl2 ڧG[ptIa{ ?z>G=ԱVN0 ؃wh 支дo?BIwQxT.>̊8<7[nPi umfvWi0.g$4~Y4$rgvL99rNm+s&TEO g{<<(1,0v5Y&U% S;sO:jZ(DÄ9DOQ-/؆dR%[^K%k!_[ɚ537ï(q.2c )&c~݊Ac=-KeL1ʠ܂¨#eC™ 0\t-@ ^]AiU;iS(߀}2m }.IRܨvR/eK&4 6ڀ*ōIF% W4rI)xÚE͕?%skgΉL+/ kȜ ?`I3"?{UϹ'ɤBHBDžݵguW^׵ׂ],DD@:@*$$d2z f$}<03Ssa %~3``P0J.1fH9oqhēC4NFDРA !N/)|,s S=S},U!i9lR3~6IO_bM1jIuGpR>cWJkژrk߭?Dl<8.3_#1 DM&򔜍Š&{ dǔ5ѻ=}DcoYPРA qpnO#}fk&S=  !U{aR%bQJbO*cL/5i;˄$ |!S9bc1'C] ,OE5ٲ俫lKÚ{Ӎu@sZ"1`L.#d:?cX{<)I\׮?6;`iHS1qY f# ;W!%ӃZB7@iwU_i~ܔN&}j؃8brp*bhN”*55o+A}-eʕ?O0COKQbxᅗ;t_-Zt }.\G#Sȸs!&[#* e]o p54喴I$H]a+FZK¹.++D'HL߸!ă=G )H^BDlP0M%R y-}%:;}8䓻>ӽb}u͛Gnn^ B^^=ӦMHʋyvol2\)6#T9|,_ѝudB %"ĉ^Ո]0 3'LxH MBvt~qRUg^1 ԛ`Ĉ/65}^A;k{;BdO&ExL1!Ql7օ.kX9[#Bu$Š#2AEm)$GkNf'jeJU.(.נV_j{h2w-’rXߣM1L"]&V!6=QV8 PGҾ#X`3CTDG b ѐLFF;2kI 2vD3."0ZSĩWz:mGa6 ~_9*.`+!#{~𼱟W~~<(dTtLk` {x 5 2T;QtFy qHS1ocqXK*wau%X G !hmwIbM>V.SL svv6hE":>oK#٤=G4}Gw *B$F鋐n/~K*t uHa6 Lf{-}tkP.v/Bem# cLI6b׬Ydv ^{omsIlWY 3"l1=

    >;>oFisP_>IdlʑgRֶc ~LMRc#]躤nҸB~13*auF?pJ'9dp=9y*/`Sd\}  qhæ2.TS d1)E |J$hSǯlhH=4hР'N:ԫA-ؗ8OǑq۷D :o~NfaIXpZrsG߾.t 2ɞ@ 8W wu>G-~%: 5}t ƸDLIXRoȡ_1&o'p5W?sUS ^ĸo1аQ O1U?[nI;}|Aī4 fՇx9u 4^A1YZB'6Ԑ42nM%D: "۰c;]HEtPթ>Fֵ4-ngy>nn{(b5[OODUMH/y[yoz˞RQ)˨UWkT0b" $WgLt[g vˇwɋ$whРAáL~!O%f4 X~6 Qv(Z 1=1хr_uNr"6 > qC,rbV8p)Ĺҹ3j, O/-kZZ5mMl&ʛ(|Z0)x2}d?S0uNۻu؛'f6ؿĜ:juWUl O>^T]}=Yvc0Ɠpdl]P1)*f? in 4@9ӛ]>7/Cbǝ{lyk;7&tHEOZ% KgYq$u^YQmSK>sVQȁ< F*NN +&3WH[/(ٜg܉0Z(y&R׾Kܤ 0F0gof-#;.Da+rCߩ$]Z 31ޗ @IS`n$;eWSDfFgbgG40ƦHbL_O |坧x̫{nc?7okqDr̖XP/Ihpب[&euj vro4S3(yfV湪ﲗA Dt7S::Du ~vϸm ~dps(<*Nh@w97K Yg##V{ G^n6s5?DfNgbMl?G֭Z̓sO!+oBzlHJr|lՔc6-ӵ]ބ (KaXO|!$[9y9;G ' $iРAW{}8ݯ]ByTIzO=)ᶧm--NFxCr{1]kOĠJwݦ6~)8>6詮OovO9sR߰{':}3)x?W dчAsSE ET+X,$WK*wbZX5Va?tzv ʖR׬YKi"ACOFVf?&NFU %Rm<*%RJ!X㏜tw_o_4n!|$򫔁.j|)LybA(}T>B1|#(U\Hǒ @~X%r7T,T{ojDčDr;laU7kp5۱M in*$_Cĩf6-DekN=Vl3ׇ9g§Z OQ/ obK/(eΜVVVnX1WtWQ f_:܅bG"=NϾ aF.i.|[6]lcB]Ko1UeBQGF" &@  ^oh ˰oXCq'0.uSd^=UYwƵ(& zmע IDATS\ؿ%&k~} =h'Su+%c^eMC!^6j7Q t@Qg!nA T)=1|;;o,a \u"ܨA\ p;K}Nxl6"2c0kJ*Ƥݢ`Spݍhm:Ofe;؆2⢏q-fWY>J)zo-5M q.pƅ7u^>Ua N^d2aZll6"""x.9o mLh yiT 4hЈSp<;RI WëU }Nj_b;D 5W49{5r*"d#J3G2x&ۥ^@ܸIčHiQ!1llבKyHF_{|wf'D`;bT&Ixkk |q~h4.wnhh 77|IOOTJVǫz<3qWgx7^C{dkРA!cEfE݋Y.{_Sa[.ŲKs u`)NE BC>|"h{88r_4$(6H/v >ˍ{$TI(ڽ%㬞%zBlAe7 }W{Y9$_d? uWfصk?#h%=R^fAr h4 MCy2&kenK4hp %"Gy ;&pܜxQ> ! 7??ӡ&=F߸4{u f#O' f3VgG)F*jhְo<+:M\4M]xŁ>0(|wF.UoHuaIC3$*G㽋IQA_c 񞠆fݺuۗaÆiOrcaRyB~~aF\01yXPR{˜UU=Bu%-Zןӂy8Qdi^A`A?q'΋}EN=M 3AYY˗/n?tC<ϲIfoKĤ'/aꍆlnn 4>r [%NFg g]hD<X=]>WQQ 1*J(¨_L_.TKG%|J1{X H2a!5:=q# K7Z=V>|!ve(fk;vz'_Ҝhב3 N 1aS 1l-~P-?ͨs8cȩBȎVZőGIbb"8Xa!gA=lo:2)q4:7}Lq$mhРAF)!RT;>ϳs)o;Ncmۨ ;N!gZZv(WֵW|RhF ;%XDW*^T"g̃Αci\p,L:S;FFc!a1Vo!# $r6q) 9͎pw >0^ojx4N?|٬Ⱥv5lX~u,[K[vx<<&h,@1+֣OXcݺu5}Gfq_UE~-oʠ t Ա& Gl.\ɨpjРACoҝRɼG<]ZF6tƬ CFBmL^諕>kVI@p|~|U]fU9QyӤFr8成rJc+bwNj.o/8'efI]1]|-WOǜT+!P¦]/>}ڐ-H8:\JT;,DqM `…PJqO>[ۼkN\ƍٿ?TԺû =4"{>\6i%Nsךqn;ߗ[norjc*)kT+]SL2Y1-8I؊bK}O8Q46 M4s^%W}2d?ئUHCs>ڵ+ՆzVH͂VRuȬ! ~?'qP.iyUz)uʾ2Y3rl:)z.Q-*"R=޸qcN ԷH$NLHGIO?D]]ճwuϒk3ӍP܆ňB:l֯f'2X,lٲ'x۷w Lw[w^^o&a+rqw0i$.aZ -v8\~a-rYgz{JxbLGu 鑏Pg~.O &-W׵'KW}}r;KU84:!SgC<| I dwVg;o.f'W9ʇ3ΰB'|Df j8%%QhfbB/'j۱U|Ap`t}=999x<u;1\ypCc ]KBBs~)aʿyB||lܸ>o.;F1e1rHJFAaaaX׿œO>Ɋ+xy:^曰.-[^x9s/j)d=A G #xu{! rbUQӺ!9%_.pV_˞Uw|Q\]$/dAc+Z+.1ry7eqNJ̝ۍ2&&yݭ 9T}14QD|3]Z%aNOcZ~8ϜNQ6fv>—/n@C1m48߇&(ޫ-|hGeĉ!t.mUQY-DUUnz۽yOyG2>y[ʶwӓqT0^͝-,{cP%_Q 7 …^(Ǒ 6o;æM0`@X}{ [Y|9&L_ʂ Ky999M{7شiS~=Rq|9a@.@[=CHgǵQ&tVkjIˊmZ'Z ;i4p%^d%z"ﲑ4R0-1cBWP:_?K?{*/`d\{ O$6ү[Wt=^o=LS ds~Ս(vJdƾq]- MP$'@>4S^7{3'eUYW恽si9QG_PJcJzg4Ic$/&O 4hǑ*z9#OmpBOٍ^oBWLK:8@ 9g5r~NmEF05EQ1z{o_DžM&-!Bm ;H˥!YV yT9y _zԻC7S4|g:UTTV[G!Fg"v|q]~ C|s9Q㠟L_d};4h FA݀9mWH ''tYJg ;,xr#qV1ƾ 'tۇ=s_fB:[9$#O^4R#Z(7֑{`DaV`JuڦIT,YY3~9Jѻ3ݶ1kZCw wC'(//cqYL뻑49]YQx]$ n;ӫ!͢kn/rkOuZ /D߉I{:檉5=}'G =U uF:S=CK}J]nFʀKk<|m}UByL pdT)c_Q\S A'`6 .Fy\]_+xl]S!Č8I(5hРN1)q ~rQ/֖qz( \*(N8 I޶:l R2@d؜-j|lġO~8Ha X`I2Y+"-lg%W#Zȷ%UE3gIT/&OܛBGvi`w.P=PUENdC<}%:{D_ȢwR9#N%5X&I 4h1>d0کP2<%&uXVO@ ˌ !9+dv YJJh,V4Ñc{,JF߹mG2%e?&@Ov0Y Prw+/E!R5 k!m$#֭` ŀאyz\b̓cBUC02|(I*jyp(uFL l&e3X[O<_5hРA#NI1. ŖqZQ*VI2!,|l9K[sDSGH5i)&V%jLZk7;XE+aRB 2D|y>9PAu2Ž\AOG Mknpb78ɳ6%T>  q> n=^1>'8BPk-^OqwB?M9N>r+p Vq6Wa#sr^T]ĩS2-;["[RfmV@Q|M@{TҧEDmBԠA8CD +-.S%P]aB4_?qR~ \`Mmj\0z1_l&!׿Q?LY37>=؉fI jYZЯUa_ɴ>١0@Q׾A>zYbbVy"K}W$¯b\*S IDAT7ܾ3g$77ruY'5m`qra7 5l/\U[aj(nxNrB[9P\cʹ KщdA qu\'Cg%m.:Cnz$:whMz;rS!T @$K{ETȳWQZ3'.'nU{;Om:֖0Vt B=#f-+;R<=)}& _T"$D)!R4|.ǜ_KUU-jRQqP7$%RCm:6(Cx8{X7taxaf`\A8~⌣!#$~j|X}o}L\z XL@+W+W+O#N]^jedsSC-w ijC@LH,*ͅȍsocTqK;;D^Kgm9WaF5, `<^^2#c 5'H.2, &ߗ8M8!? 4lF聈 LJw]|ĚI=SgCC.ׯ}RGuࠦM8p:bnqɣp=,X NA7qш9x}ȷÏ['f(`'gI<^zQrE\|y8yidk7_>AIk'<{y"ķ{?*b;k6puV N >>9~r9].?hĩ;3e,E NI` 5t\Crdn ؃}O`; s7$R(jf_,<gn^pC3cka) $<*qXUùeot8crvbo-/nRbx:v./if0V]nB"4V(m1ķ\ |%::[$Dװz-eXC{fY5۟-9 L;0wv_A^i؁b #"|4WⳏFU%fiS+*f: /p~"#L |48' bFޟΝ~'n =}4qR]Q J| C!5 q`k,w0ib}c@y5rO%B7߯6#^IQfSE9p<¸\nGb,s1rbط1û="ePL=;M/JH7swfkTٻxj+??⮿"8E8xv3(yBpiG@r 'vVCH4MV"}.> bķMY L8D\ԡR/g3SwLa䵟bKc]&N8ۥuFan>1?GϿ[σE<7#/f\CF:WGo= (*TS2bHaF\!NqcAP\_m ! &k2QHHH~|a37ܻY+g˛8}Z PYk6v'E$t*{3N (kOE33݀90pg 5MJ]?h!{Ǻwyb $z">2s7WyZnX:u@d'(P/r6{*L8~$䱇~.e?ׁزc#/;YE1&4t'uB7aiMH9'nr÷tqA;-g͘8 8kآ/N:vX<7e=||3xEs ð{HSY5Eo͇=H =Ո ڤIU#_&xҤx*⊓@Jur wݦn#/8Hj.m>zB"γ6)M*jcTTa6Vo(`ʅOa5!`5\q& sNRh;N= jS9!20{u[DbZ',r$@6 =Jq rV8n` jwVrrjE˶ٴѨksa{dX$E5 Wn7*[tr8 Fѣq/7QxpƻP^@7պ)URF!eb庒U  g ?/ц8u,"{8Yj/El-H3fy*?0=$S4W|> f~.fӏ|ZJ~Tknۧ8TBhuWa 65]VV]&q_qDY2NT3m 7 o&"Ndcq–(&$UmlB 8 [lr.%1{:UABItދ+vqc'v~)ɽ7Nrq=cn ]j;Vc}, ̜9s}ϻL!@*pQ=[VS'xOSoV <+[,l5T**e9UQ)J2JPʰE6D&QQ>9`,K ׶3y?Bz> G%FB3;,݂P̉瓵yD^7܂r⦹3։ U2m|?o.[v$<,av?%¡v/~)tV6߻8O#>Y ]t6( ɰplq?ͭotG+8c:MfC:CLctOS\n)/e5,C@cHݞYsMrs2m|Rӱ7>Έ%^ZSϵ.r$5uj& dpա'7MT^wrَo [ 5%!"#ߐ6டC{ C7n_Bn86P |uۧo׆(%Zj Qlu6XQF rx|{r]P}y8Kɀxd߉85>mji6S@tƓ!3iT$ew-,͇Pdh,Y`hZl*uȝ}d%iN^!>Pˆ%F7穪Di&ҤEC _Q|kǥ峩l-U! !5ƹg{^YCe vL~|_wNb.FÈLU|1<#Mf7"ni1V.$O {]g_oP OMDzsK o;Jpj/o~ܻtg]Wqz' O>&wZ)|!hoL$^_}co%}C%-b )_y2I]do%Vg #j4$ G;rg;ߙT U)88ZzXQ c1<n9^VEPmu}y5Ôa(Sq[Tz&֛hGװQ BGk8陥NB"31e`ӱبP>#D_Vt>-uS[KcXg t7ޛm E?. Ңz8)Ҩ@Ld27 AGw7&!$Ӗyn겕pQ% wYinçQkP7r}-dYgf÷x{NE`pElڑI3'wXñSyDEX;b;?'9N:]!1} "QUJMud$v$·eI=/;بP)!>=~]?:W FYo/57:?:qiĀK.\<6$}ε3-1w{\P yݾ@OE CmF.N#ʱZ?BqmȔ͵ )%,ߜ`g?!j(ud+yҎu{Nlo')0]D SQ 3~d{/rjBۏA~[dI! SL>_jԗt E&aٺž謪+8p<@  >}1aϘ1I?Me׏oӤa =‚ÛH?'6J 5_w< _jjombM*{O̾kn~t{oqmq\pivd=a*Y9GM ?Qԯem]n?rCHb^*b.WEyvf,a~l^'F!FO# ^[O. ÎP8aScjjV?VQ0ƌ/ }=^'ĩ!]78e:>'usiftL4M{4(48-YYNy$32,cSݍC-q8Izw0$pFF1cr.^ŠmaPy]pX҉L /@qVXX/K?vw7im3SOEX})/LJ}5gv^=k,d5_s#Y~Db?wӯw1el">&jl,ߋˉ &QO8]vnE";_ak)w0^ΞM,S|a}{G'hqEe-rѮ|L*峬^,-j>pl_V[a 'pOqjoڃQ%Ԋ6k?s{1Dg ME[ >rOcGÁ3OIma`M%)DF^iֹq- ]~S>xcA _r.GUIMB!y ']:h6څᑿ 6[G#M[``4ǯzLQf#HE"lVno}1mz=sǓ_Ttlg~5>YSc~c>: [P>a>nATII@ vd!Fa3:UM-Ƭg#_YZg+wh=u59v&{ RE0?p]oc?g!+[0c$0Qv5mA mݘDWhy%U :䕂7rpIIuL\ZOk`-SĄfY\9Nўg"ƛg6M-TJp=NAm ㈪fuvVvbz~>o96D1q0?)Yȓ\}k y)%(gpAy8l\@ V(P̞!@ طsî%)Qwoє3;3i^qq)|5_ !'O]@EUA~MK*'(M<;vJpKSSm lx5++A>~xnnS>v.C'NW*̱,m _qи. 6ASΙȿٜ}k"G+S8[k"r1 k &ȿFt)$v m&D$!,? ˅HEAGc& P0N8(A16J:0U2 و3A/2%$D "C4B,Lw&yA+_~ӱ?x7i~qBi #zQCbס'ߙ4y31[H c!5Fw?"u˛< =+5Ûz̢[>fIń!\*>o#s &{O8(@I pJCX퇡hJ$T$vb\ O! .3{~oG;X@} BF}u&ƜAD}>t*ڟ}m"M-9ߋ˝' طYF$P|ݮ'C+9STLW_֌6O I#+ 烎^7>ν5y^xsOb! NBJϾScpB}B(__F\LN76_$9~Qv<˳]l=_n-%.u1.D#D#2fGJyJyw}7oպꙏM+ٶeqM???jkk;~2XNڭ~ ~eȞƄ'uyQVT)ejˑ#:O*H/Pc72+5cNO;cA<{{x-}O7? c7l䪝#is(; aYPZ]^`E8w!R :z16eV;UH)Q/UwoH)Qۭ[Ypݍ?jƟ~z4Q{DKe4r7iCBdA1S'cA0ġ,GO(*x.-/; Cka ˻3*H/fwqVڧ`sǓL(»pEJxz ҨJ{tW ̓q{_æCn@x`st^|>]Bj7]{n+ }u%0|Z?$p-ұ7LZnu g!x#^c҉IK?enC~Yv6#ojW8PflO߂ۭ]J/#B{m:tn&6 PX臸m*r樞\i ^3F8@}~q#DYO^#c'/AbK$ˆa"pdJ;uhj2\]_3?Yӹ>n}X&Rk n~ ICjbljjwN( bjRMӷ"kJ{Uzކ.={b݄2tؒu6l;4ay 4F7&:q,8}ޡQjɄDE"l6RjtS"&B_>o9} {'7776 ǺUHYV_k(1Ů\|_[6E{%{{Lĩ`wSz/KMe3!:\<_j^`6"MG;I=ޞ9@3:t\8if۫8R+ Tl1 e ( |KBR8u9& %{" w5Vj!{OJdo%V為KH,p-1l<~=5 l?|xpa)xqf'#5:リcߝɽƋ|k$ Z͕6%LyC>?I׉n!㘈!psts'tKhq#ʢq0~p|(l9iɑeΎt`Oq03J;qɈG!]aWSmZ qPYwv7?0&wmf _ rJm*,` '4@C:txջD?v!$BJ7ouigiIUч:1&ӏdRpe U{?ߙ ߙګl-gGo@nA 1j8"WJ;J-L,ì έXn8_|:DB]fxL0@rG&yMZ}Ą4{vg;m"AcIK:t`xFI(.*kttb- 0($SR{/]%ȼ2DQ9VCU ڊ4,j ": bÑ}A2͐ 7N QV\K#|.B019" VBzI0#W/GiFxn&-oS'I8y6;{O‘sP7N 549Z.SV;5% JP s7l:)00a\At8H)9zN6 +'c{}FOj^lJ}yy< 5Vv'ZզVqg[[""Gœ͓5#Yr/NqkVmA bq01x`݊hBT7LJA>eAjx DmZFĩAJimZ czS_Ax[GH_En{nN: V68Zל9i e=nj{qn *ЪgCˆ.q)F̄G"& !2kF!{;|_rS!I `KshucAӆ!|ͨ]DƮ;_8x.'=NNic٨O_+5IzxSqxX2dsMN]N<@W76 >w, fw ]*CRQ~6lll*%`IgGZ|8z䅮 &> "Cze4u}:-oĄ!^ 3nNw:theu⤣;a{P6y"|X½ڲ٪|ycYV  O!d;ӌN"Oۛ`f \SsH6U.g/ r;>Jv9{JfUXI&"_PC~{ym&w =X_8v'r w;9u&[QTk2 i9Q53~3#5 S-01vT7U(u\B1Љ'F7U=DϺ?2/<9I)k3Q4R)BwdKj 0gUj5PZ((GCn16r٫%0qmPja~c F;`BY\_ ,YY } Ev_suF8i{C骽>Bb%}zcF2Cm+ʀ;Oa+ 0nѪ/ʢWTHͱ-՚6 Tg|W:@j"&jR2=mֈa"\7q4ZF7 ؚ-JE^?k"MMty6]'2!QKwq^т8U@R*#lE'Hfg򫻑onG=vU; fA1M#̃!WF,BĆSyp@ 'y'+Ƌ׸&d;^&x`!btBMT_fگ'7?C هecɱeiQJZ%ح6xk!X69<Zx`[-4|g Y֖Uu b$՗ّ eᄇ9ˋ%Ѥyޙp$6>VV7^lAb_ɥ("D_>m"|T.Kq8"m ZԺzmnU[:Dy5Vtz H!1_.D:ؠr\6?1,To#:txwj4^4K0T5_Rst#'wPx]"e1 P(y`|R4 <O&ƚ@*9Kʻ iCQ?َxbE_;eN~zӆb ڄpl:I؂֖W8nO'd+^^т8'aŌwwEh <O#wez (j{A X[HZcÙShTdǮmiG ~`@$B2m ȏÚݖ{r ٌWu/:YlR_Ccu)7g==\UJfŚ- ZoxՂ( j lA"&aLpɣ }B"&fsvCNr g C{NA}khvX uA~{بgRn 2i놚DI:@awLMm6fse߳ݨ% ǮG,|Ϻl0p?^ 7ћHzmy pb0ĝ ͩ~,K4ǠC]##G5aN띎ۭ4`/ϧk*@00ˊgZ7^هf()pd*k=B ҍnL2l-P3?}Ij/HENZߙz=}dKog+%4 )jAJÉtN? 1ѕvLËMu!6{2DV\Ao]jĻ#E>Ap'&suȢjjxn!N GLeǴ0vf824*}aP&O:ęc!obT(bIܜ{N@8LU GM8I 10 1 ꩟k =*K4a Hjys>:tvM>WЉBx0I/5"fBCr7fP>X8RQ_Yvނf#fNPL rwz҈SQ9aNRޟ@`@UY`=_ Z [UɊdԃ7 GĆÒ4B-94lL)Uֳ4,0/_N\¦AmE8Z~#naz IСǰZ4+.3#p%]ܒ%KXrevmJ?SSgZУ6O$\n:{eTPvʌE!u}:-kXC/YZ ێigx.7P\-2N%nY\ŅO~ c'(LKŴp||->A)*e8(G}CX5u4co": : ӧs$8&8#-(gan(I1>Ƚŭ_޽J}tňepRJ9‚n1PT7T]{ P6#ڜzPʽس%#{?y 60La> f#61?12pu2,wd¹B7u>trYƉ'Bad(!`@b͌lwL\/g|lE%*%rAMaѸy 6 rV$vT*qPjTjQ"b|2* CZP!w[5$QX|{`hۦlj}q,!'N+{goSpy]F޶TNWc3_CPdw ,~s׫=l\eX+]qὐEN ]Mu/q ٶd1{ytwj[w>[Ĥ!?`=fFhk5V-M*ig`)d;UX!dE%۬Zm[r['b n͇wTa_~Pb71Vgxbtam[`W6k(&=5i$ t u}v G;XPE#;V3;XpR1?0_}rJ^}U&#xOF<|! ԯqYVYS=~3Q}jЉ/+dJgRU\,<8asvLq,8qKÈxFTBog˦wmމRN'/TJ"~_9[ U3F 'n( k6DحT?btLG*fD+!urXO G,2m$ /!Cc.&'Hs2*;4::" 0>JpuY~N3$I+شPav@vCNe=R%w!7@^SD`=!M6ه$pmS?{GquNXն+ci 0$ RH B' @`z1$75:Ivǜtrʩ؞^Ҷݙ<|rl OR a?wv7`|LڽֱE7{W਍1#ڢ&'i@wQiͤqncX4|ڿ ^\':P#'NcH0!zseh܏!#Hq.t.1]A;wubq'&S~s8uZc@c$1݊8 B9u򫡉"c(dɁƿ@rp9]e sYB0d7*ֳFh2-Qi)o] OuOlڍ}/ n:399և4l mͦDREWUiq~07mV֧ tߓϦ@qQn*$JWaEyRKWAKw$㭏#p:[aA_gS:]p2$R 0!f6FԶȰ)7ǭ˲G#pJtm׀. &NIdZ%p/>,CP32.氘9,& 6cQN>Za8%r4xоvu |vɆE%[(g=&1+ eAGӜYg{WvZh7=IcHR<(W{2TM2;uY%6ΒǙxr\ L<#NMVѓXot*<&dPͅer4P ans,¥i#wθXP҄W%t|f3P(H}b=Ub ~(KK'v<8q4Qz/"_) J!} t@)t&@'ou#߯Bnةl5O#]6jG!6sz0.8N=|XgrQ\t9"s<{S$x}2^xy _6頁gq259Nk$?C\=qOJQ[h5DM~ :H+&I%T3i:p^%v<- A40AY _Q0J)dBUR4^D`hAn٭D/;#=f4` s*ec.C ou sk:=N8Wi$IPQㅀ#.8LWtqtgEpg,|>;e?=܉5G'xr8€QL@%0r3B$$vٮ\Y13I%[6*G\,Wb'tBh*? >Iq8𾻏q#5xutd$$N~gK9a>܁rlo;GθCM, تbi+x\x(X08'L}(+k׌*V?HwnxygAKjeN8T M r&c^ Hd&NLe)YrB ՑT[%joJ? [ucAMShDl5nY8 <,֫0 ;@0W$!01`lZ@7<+EXw8m%;yß`^YtA*YK0n,E>o! ؎IRl9Y2BK] os.qz7̈fPی/mL9Q範rRPY֙j89`. T|N&qj7a}9V{,PQqh5ϡTjC/[~ʥ< *ktnpC2%֬o: _aLLAz~_ѠdTӤ`8%}' Ay˩˩tVvtEA|b=H 26">{&ձK7/Pƽi^J_ZƾO3bKJVNe;ₕ}#?J^mލp{4(/caYAֳ!6>]6q ?`Fdɳ(t2917M|>G'AMJ)7߮SC8qYS x]rG\6mW|ʍ%N?%ȣ \tpDK^ê4H{&'x 7E俘H-pmn']#`I5-pA17GFڟ %48SXđ,HE\»)g;Nk*} dqȺ֨h정>' 󞁄G 8sPۆOݟ8좒\xysАH 7*,Y#}~%uqk; j@?cı c0{x ,Je5ho4NF'q4wP?+F/a~g_P"w2IsDYn.Qsf;!0 Wg*٩'ci^ë\; =9P\5RPoyR ($=JQE>L#2Dއy9l-cېR12Q;-$4c>hMm7h=$N {bs?YwOe+FUxn=-4u8QK?^% bpY kxs 9VL\]}M#g|54+7Vɀº7@>SY>|l+H`8_Ƀ'[O G3`u6Np$w)EkGmA8X pF\_ 9ʢ~Z gC8H+pJ'M=q(W=9Av3?5߅Hҵ< $ 28 B Z*&vR1,kXD#͏ H?H| ˗]x_(Ad >'!$vZ{lML@)蝻_u[܁ix ,6_,/3F^{$^rI`G+ol`i|ӏUK!exCad+S)se1l\ GuɆ_G5\Ӽhq:vDhS| 1߹C[H9.VtӴ[Yz[mO_WvϟOG tJwΓ&Nca~IR H2^]Ɇ题3**= H0V۞DlU.` "_p;iOu4!⁥y[Ia3g!렕&f "_:q_ʺهQW ?143vYdYM7[edxn!yb0o-RPldu0鯛qb駵ϒQ9*ƍ[FV?N\l%f8 fO꧳w6@Cdlsˍ/Ӯ ~\,L=4d<#P+@G KnGitE"Ms4q p#kPK7?(#g:'.K9Q.QNSĵ>w."|Խ\ӟ¹;Bӑ)vi'eS>r.ªQ]m%N]jx}3em!$|ǚhKtYjwa3B.VWaH% N] IDAT9SƨqC-b^MH6F'~֦9EOܫZ6h(tZcYI7J;1U5 'Pַ#&0]cK-sl>A[FRP~^QpNvF9ooe}/oU#d~54&^/A{q4nU|︖O={ìʅes?"|]vf167,.a]SVR"wûۑV@MORXr"5$1;4AhU!t~<8P:6n7nwr<Ӯ8/#ӕcOQ|䔍=|59`_s&CYd[Tw52bA絒ôi2LU{/*ߢQ§{ɺ [@"hȹ'K%81:;φpmML8:H3qH/N;|K}fra PzlfXlkv(By7JC[pBIa4%2GoX`ɑ drH0g_J8jbMG 5Oeiaqp r> nY:aA09& 0ycHѷ:h>[Zq pս!{Cpct/qn!\(Dax $0H?3Q줂y1S_ UpQȫ׷to.Au4IRDD, _&R"w6ƝXwAy5"2!`L8~!CKxq\Ry,N~(IGSDw y?7|~g=!%0NV,R~=ri65Ռ -\r%QVcl^kh4|B3i?4͈Nm:M4 ?Öؕ)gp]YlN/:B?, Rh'-^F G}dPiBᲉmf!d^8ïbS-ZM,@ ?&$P[ 7Fnݫ7cn!؅|no"7\QDӥƸ5m'y~މI,-t7(ݍ9ɧ^PՃ\>,Geg# k T1שLEo*|2NŹఉpʹ_ M0B#WChD{%)~o^ǭΑ%йMI}{XVSSLY=;JP34S'8L*i&0S.Aэ]GIhyXy:%o\;v:dCj!"R1 1tj 8!Sny}x 7p0{QI#%1!0*xJșeyP2Uq"G`_; ȺfEAL(C.*E,*%vi(3DthhL)\+'`J+'k˸~}Demr쉏FS򴻿*84y =͎7Z';T4?>01Z"ë'q2 UO!jD@!Flݣgz!7 HA"SݐBHaHC Ȉ"Q֡ 3D{Vk'Տl@#&-êKv1s-F+B+V=/mEX(`68St1%QǙuXwuKJ=$u߲8,E}StVcJUGq(9vVAv5q)9]ɲ&70%l= 9.|_ vCr܇/#E зso8d6&l9#9w[݊[pZGF  Z)Ki0k*3a#l ,bu8aZA]w]w5|s9oc]X lK0ϭ=HØjĈSVĩNHv{eQbeKJYs eNZrY=~wƌرcK?3AI'i$ PF|`߾> dbXsZ:Yy .b]w**Etd<Ĭi0#fLqLHRq$N=oIcjqz}Z~MCc* E4Ml޴3>;@':;n H'QjR}:9NI<$qG/1wE𮔸J4y KIF?|eM9Q :TIHl'h22 am܅ؼY^ ]Sx3 9*UIBq.x/h6"̑K/jk ok2vݹWOW΁P mU>ͅj0J'_l8 l>uOš5߃2wxjgl#(m][:*P]{X7f}k`͚ߵ0gMބ_w֬Y͵\%\BYeU<lﳄ>7p;7pk>5[9eJ:AOPzd'FAZ;>%D< -A] 5ð홃';nQ2 9G\ߌ(|$Q g-ÐYӄ5#ΐlL˂ Da( qSp2fɅb!Ncbay.K0Ip T"eEQ]Mo@BMa; hPDʙA՟!{jP˹'kT?G_8"sLk+%OGŸ#{7! #a"5L8MwR)_&b\6 981֗~o-JƼѥB`o!aW;1|)D2L"'M~RŕdyZs" (ѱ44W)R},~3{ [nT,@  ~k |-pҟC3{"J(`j'q^R&<^r1tϫ(:?qȇε1+˕  /D/bi/TĪECCJXnJ[J_H9^q:wtA iĩ;pĉ3qD.YN߽/Uz J,;x#VAN[鍤S,(!璦44?4Sa7?P̆i' %2l){Iy̻ZއFC2<;'t|s? EC_GCCaY֐daɞ;EHv_6%ص.W=H@Jh~Kr7[) *;w^ߴt@"hSiJ!!~&yY&O-b'^_+ԗKtӚX>Ni#MU5VkH]8ŋW?( ,Vy24o(nE?סnMؔKg*щ6gWNnCqGYƂ֛`ǟgsih$ơqqj"(@Awm{^<[$!Ӊ]&(4%OжѿYCƲD !\J"Ҋ./-p(׶رEOv'myZ?T]Ug)prڣ<,՜B>QWzitH*) H$1U8cՂ$-"u%_frۇR' JkK_אYLr%M嫔$ 1?LNPXq]*'*eSӕDYu"MTB%ñSX*woo.YW;Gޜ]8\#r=q@ Aߦ@㕅m}RHҶ[ع> [0iIqyn{NķHӕ#u餻;o#tE{$ cC2aHYI7/U~V,I]ݵ!@*՜cVJr|B]BGZ'/wBAřrfs`)J/ؤ4Uݭ,MͼLi4UEZmeFpZշ3_0 A6 Tp-P|)RA}iPٽPI/}:v*X4*h!3*ɔT#)Ssفqo{ƫr$7^eT??mACFqaj宖 6|`9(RqVy)%Sy>I %zF7teiW(nWqj'9ά(*aZ3߾l]44q 8$M9NuSscba%JIAn'L Cd"ǰX[6J7;4ihh`KjrPqdT=/^m8&m#nݨMʜP.v@tVaWưrU\m|MԜה ec{L_l|U&N゜c нY2~90U=[#ή IDATYJa֪F B["ܤWCC;0j8(iy=M#G3J:TH9_?+G՜sUtGo"PLrkԵ[Y:CQJXI(JjP+:E/ M!8YI;IL$k ٴ Hj==*غB2IsG\NW~µRt+$tgQ#H?xiM nu9!#,^r[sERg{i߭8xLwoxb%]IὯ@9^%QʃWQs8{:C}.(<e-ܶGr[P(AhJ|4rIDf}73>Hj|i4HY.{Y3\"]~%r{jUpig*j{{/R"]!E:OSmRCE8%煒RbqH#P_?fi8J$Pt|}j#u9̸M444|8'C5-oWQֶN)*hn[6OYa hj[ Z>Szk]SHPRR6H[*{MIP5S2m<K[$ iE^U:T9Nbڷ#&,hQA캝nn 'xWW<.ߘAjG٢ĩ`a8NWVmi8Hp?UNgOQ_]`߆S^P*iXir[[qKnuKM:~ǃ[^EQ2T oUd oldY˔8(K]oDsR ŭ7i$ ^r, B%U@Ǟu-KXuE>Qtk1B8{a8Š&@03] %Ֆ& ¦,FJ@=jnRO&:;_" gOFT:[2Vm^'V=:Kůʚ+tAXG|QxgH@ Lz*1$/[T}RB:VDdƟ}`tN/7x~Rx~@"|?+E 6b163!) A eS y``MPp5AP~r[#y ShSOQ&%,T^qlZb־gYMےp_8h\c8N2Y3q`yKcEc H&ԁQ~.kEef~M񞗌g0e9N"%3OkҤqbo 68f<5 ?voj Gk$=sD{Ti_׺r CJ|TR 6Bcj.w~L}I\Y*NSU_ Ԝ=)TX:0z9oϱv}%Dp%QuvvOw|y/lSIr'mϽ4/i .މ$#[E, u/Paߠ[BỮ_wN'Jlqĩ o)){N֤ICC`u¦D@T:gϲ7ڃK׆oI jkT4"qUgeH[eUy8N;x5(I^}@/A ,GDpljs 鮱wȂ37<"J iφEd"50*$j=F4nta/s-*C?<'FID CśhKi35=o[ecw`(w "* $#`H1HCb0d؞ !0ԱuMiH@y![f`48{h11B 2{3t*su>â/ܳG544m6ަn_. )L-Z=c4cS1~h%"}#![OD'z ?`Os l`y_Z{1l6RA-_|z!&5kVs-r W#(bgiQ_z*\-<y, Top_㧔1[>KX*)g/\w;<=]wf7f ڵ0gM34PKo0`"P֫Jjm`ߚ8΅l c`p5]`؝,>",7]{8wsoK'< ;/P?bL" >6OrzRbyH̛cdszݤcۛ\&3"+iwX`q@l{s۝uY[{%'9>uX⽬Wofw㞓M%NIPt#TSA7~J#%#\F3TSͧ 8)d&LK :vQΤ ?'sq&%e4$۝Mnq\vlv.M [QO$'h㯯6a HʣRm ־^pȔ8iMӍ2"y#dFMS.BtaOذ3R(Hr$l&]<Ƌ-8P 4T5-Dؐ8-A7L]YiuZdmx"4;GˤaAӤ&EY:Rd\m41`l{OA⊊aϳH|joʔ!IO7Owh9'i,f9ʼn[$BfL$Iաw5i_> C˛oW?ӇRV7GDΩTwߧ"-ؐNg*enAr).%8Mݼ5|SliX|D`P^(A.P@3{g7tś8pG1a1jJG> ?9]wnH .k7es͖>R+qX2F8<#`ɸBTj^RHi+&eT_Ka E\gKD*IKF$ iIe$J@ BbIulC[gWx,nvap#-`FoZZ8Y8 +zR* Ӳz0ԲgJdXI)X eZQ"UYH ӌ^2HI4iY`I"e0-eaƧG1-%fRJl !lmi^Ǵ,Dg'y5s]HL4 S=*),sLPdO6DtЪ[q/yM>Fʤ1' C}d}F[19`=?zTw{0DVr Q.H%׍@Zhtk@ Q]n)eG0!nZZW[et@F)=^KumEU%7X%{!%-5PћueD`bTJ{8q6v%iVlg4{M8zDɇ[!*Q0T"}"*00i,S jH5`^K]4#X{d&ebFkHj M444&-N*onqNr_m.fM[lI G/`lL2G/l2TFC3]4[R}uR{g.fmXIpIi+dAG8 '@V??^HVx_D* D!.XR#< z9Ft@=Wo@1h@.GSYXE܄3@cHwSQS@XE)DGM=H:aJuruΔV˨@~ u~+J{<,5L#i0?eA>qd-u}ITG鹎jLEʉ]Dy+ ?JoxDLoE=7b>"X]ò"Zo=foz"uNӡ?PcyG4ݼ^t:Z&s:| )$]QO'BQ{|i^*!6}_X Ƚ|6 1fb{h/OEm/h{ч3/BUam5 fn ?GN*kCDɊD2HDyAn\ O$8>)KgPC`zY޹K-QN=)a<\PeQUyM. o#ZއA@mI j⤡ bQ;.nIH@zņ JthRD) RDEņD@z Mz'nߏ,d>ٝ{gv9s>jE诔SM͍}Nz<00}Jp]M.7y<f_w4R Øը@p_P/ ] R}$YL؇eQ y\rQe^ŗ+ph܌2pH4fX=sz*kldf䬛 M?nB2vg㜛?wjE*X2~ٝ__O|M>OԮ$O [QD$@Hq2KZѨeϏ9UHКX=L^ŴBp QiOTi{][2j'0q\ Y _0Ȑ5stkn8 n@ 9K:v^ ?\< I f&531\W΃BDVf<6zQ =Y'~ EcO+\t5%7F,6)w=ͬ$,.dzJtb@T smYTI2oGNVsI5YNxw|T輆ghṗêHNM&$ m+A|.PV#7L/ϯՅs`_]-~Уhtr(E7dd_ⴻ=nF4\u6ㄇ#^zW!w5' ΂AsEs;*5Bp:x!ݍdv).Q:<$ p INxdR5f'8ۜ:TMSd"&E1&:< 2NOV{!iOT2HFA.ղ𨟎Wt4FQ&}5@p?b u3|l tk' &kgMs@MR)vqKOT F9{9=Apk7(N" \U&Ľvn2P@p"+7xeyyd$ w5:w2K5f ,a\0皻Bpgj I,#TȒJFeYȒ JF$d%ǽurG#q ͑o?f]?:e{ϫ=Ah1|J-3w7e{ߚɊ# `61J.مhθ+θ Zww1E&:2"L"> .by}/+Tl_e͗-ٺƑl P-ZeZ KhT]hF( IDATJ-#I*ԲEV˲m7_TTRrʪZjgjYd-~X-d,Y}NWYij|ngyXC·GMAMY Yb)鍟N)w[3n"4\s6ټqKGmW"RS.,8Wp$. 87c:?%8\==hEB3ZРEj4 *(ٶd^&?#&1``M_z0#V;h80 @f'\Q@pS5EI"K!*McU/4SN 'sC2- N&>,bl$`@vfQ5UKp#NH[X/-(J9D|ؗ?T 嚃RF'ݸ0|ǎ\"@P !.dBQ>CiWj@&zh})Xܓe)PJ*a6mF眯~Uj ܍ ~AңG2Osɒ0ӽ4^H][+$8>]t6d2a6mْd0h4"I?0ǎs g찜Sِ;_يsWG͑Sxul(Hq󳛛hg$~eѮ$POWAZ'k_ ^GC*F+#cr*< *J(A5 yl O*}Aѐ.ىT`(4AZcsQ+sG$@ Bp*d* m430\ O~bI .Jr'H)q i;KO #H2ϑܕLSF;1PsW2zcuڽ)d2Y SAB%1ƹhʑ(U#X-G'9d~ :ze:i~檎GgTT#8o;Ea }UOKN^'A9q$Mrm/@ "x/kq`=/~ZzUG;qPs׊$1T U}əw4/'@ ȍ,al$ENw 9|B~ ~GQ z@ (2 ƛ$ dRk0cBFSMơFsWݘyw(]!8 w0h1UQdȩeыdA9 0XnRfcl;1'#RcgR9>PQ{P?t!!89TrI‘SsՋ4moOT@ 8@gP8z.ѮvF2W.ܬԯFNԩJ_=A\%jK}X">>>$&&rYj׮ ֭[S\pLTlf4k Y9~8nnniܸ%{ۛΟ?_riZ$vlcLR$eGi" SBOw MhKLxs;!nZRq#jj3KH\+t}Q\K:VnLũC]<]o:kO >4=Suoa͔'0+FG|+E 0I&3w\|2M6eɒ%tޝ!Cp)v͛iٲ%[lI&ԯƞyqyvBq5#]}_jζ-[a1>|8]t $$;vϜ9Lcf͚e5jdVͲֵkWjժE``*U`ȑK޽efMh6ֲ9vgJP*!-P.\aKs\p#t6Mu(|-/O!;N,ˢpmsu0\IM[*ZDJ.yUQy% -LլYs l+T`iҤs7 gggׯoӻ7EriL_3Si/OуYƄjqf Or6ֳԦ)>e?ӕnӝhT%'(⌓.%)cxݚ VpE."u3MR"{~M!#3!0BӼL_Ɣ&Xy9Xnӽ'(k=_L(~|Qj% [ Ƒ4ӻ00d9GTLXG9t魅Tڔ'.Ip> \_%cQE-0.\7P1Hk g v~<:}EG 'r/s\,?N`e%K5ݵ+e+.b#tes%;QC@o6$}?&ջ~svONe})v F`Q''YVL aZbUꈂ|x7W~mҦ'M!<~'NeWLRMeMp_5ޯcEa 8t$ F*9UafGZE=͕D A>:yxkW3OJd[H8Ъ(h]MZZN bd|/8$3\^6H#4wǏ[Ea }yI 'O҃'nf~⨴k%@!P Q0BС+ގޫ^XwV]P{zq\X1:jvNM{u!m_۶[@d;mҸ=ztSIRǒ̊T@:ޥh& B nowO5$lZ|<ᚧS3QЂ{>{seΞ=kx9Ne}\UF;AbI @ 7@ YpoI(@  8TS=@ DWR.$IAR(g;Ο?'Bp{gT"i=z!Ѿ@ \ ܻ;" N/_/@ |BJLp}@ I @ np!FcаaQ>] I @,ʝx|0@ ;)8w4sk^;&ͷ+H*oh)eX˿W|+ >ѣeRRRD(\vVW_s~d)>rX1L)qNOfG1},-\'1Lbܳ1.Y~ ˟bcǟlacOX|xi̞3ݑھ}'[n+{ +WIGCMJt"(ȏKƹ牪Ze˿,v́n}'$$Ӫ_PJtbqVy-Z_weXO|5,\oUFDTU{v#>7"V*DDѼU;>rTl3fq)>Mhު]]kZ+ƈ2N{YzTkݝ{]i%bJ{H~o}2Ks?{oKp̙ m;QUO@a*Ӭe[F3gMYocC6xw$/^Mϗ_+bIjD߱>Q z{,u4!(4*l"i"^T~w[+Η_ƍx|IKKˋ 4oՎO;?+<UA =0޽xKLn{L.VƙgkY1ԫN̞?6kK$-EQ|׬CphE=7m.k׭神/SFm}Qw3g' tpcQ~f܄I6ƻ'@]*DDѴe[OzojM<}FF ŃEfffKOOgaT!b4ݺE*< 2yB[Ch{9DJJ O> c={Q#3?UҫKt:|9\]H8._FR̛^~s+ߟ?ƍ$&&!2^^$'pQ=Kx~;jќY3#2NMq*.>\d2\ݷYIJJfx[BgAҺUKK矍ش~-UV,ZU|[y8xKXnvΟ@./@[87K\/OO^]؆ 0񽩴o"`k^o_Я@"iР~yhѼ&4͇OlV6|$YY|2#{78t0 ӓ ݺiZ2V咛~L.-j5̜G[cϾPT=j{vn̘> www͙ ,h4YԪۀbkӅ9˯WB@H8k^={K{ ._;cŮ]xy՗ygh; 9Y>iУl޲~I{!=Piz'GLVaˡlgFb׮yq6maֽW}IIɼڧQUVf-ruMDMlq7V 4nފ_@߸+UpK ,kUߺF?({.SMϳ c.ZnOHx$5;bwVuoiи}{w̘e>*t?^3q%UVAIHH໕@]B#^kZ˥Kia#Fl:=^fe1~{=P0ۮ_7mނhdOxyⱎ 4u1ا2h0V'juԓ/V}ߪBphENBy'Xw<|#Uo ++vM {G당֑GyTQbbk1bnk(> 4Hu[>ϫhܼZ&ΜU(>[hZ\^c|:v|^v8*VG{{ھ[odLP1QТu{2y1|B'_ٿ-ᄋ M<݉ʼn~j&MGO0etFkw_~Z5ͩ+96U0#1fH07ONE5Co{K9zvjdxxxŎplڴ-cԈѶM+Tl*~AXʜ=w\L&?(LrS@uY#r[VSW,XGRykОO? mgBbaavq噧;ܳO3vHv%.]s+Wۣо6`ޜY=/6Srɭ9|O=-컕Ӣu{vF,b9$&ygh3??c'$qy_ V|cٶ| |s".^~~͢ $!>ÅoknbMDx8NNN|wn¡07md}j1U|R{o!$$oO':Q~=V|k׮1|ޯMPbVDDs f͘Nz' F`]1ֈQ,[w8~$v3 ɓ9,-[S'Xb^y$00,?(.̘>@v%_MROُMb۶5bAAA<<>̳c߰!?qSw⥥1x@*V`_k *{Xup=7e$%&kG/W ,Vy|p>Z%99vᇟVMT*|qxsQ|}9{,OOF EQxnlڼgggjլ3g8x0;vg;bbDIDAT3Z'K1~M6ukh \]]7aݺdkZUfL#==73xk۶v5i' SE.]>tɩz3Gn]68ܑEdt {wnGͫyTrt$QAxxNN3&:\.^@:Y[; )eE i{ #C^WBB=rmO }ȷ2x`5lk^͍;X>}dq:|r J}NU*`k2.Zt:izܴ27RTL&3*?%99}ZMZ5IE( Κ?HJJ a)DVDpp0ޛLy0qL#"z]HUTl߲Yƹ~ݲJIM!##WW" uz`sgի(BtTT~a\|%qVо}[Ea<\rf[IرkӺP咓[VW-\zş}JYd㙧;S1z{,#GK=?Mm;t6 #?t[g&V$<ء=S.Sg_ZiխùB^,|!Fzt`T*ׯW`yGVDkׯGLl-X/vcg| o!<< 'O8I~8s6=؁w@F ٴe+_Ci܄Taႏ$;G$?~ m[敗{Zvmt3fpx:1 L:W_Ÿw8~~~IQv|}OHSvƽNGV#2Ξ"++$zSMgUij5kWFl 4n߾][ƌx2^օ/viMg_6휇ȑGW.LPN@:/F./Jvmk:^}1KsCTBV-G':cˢOܪncV-r F6[rITd$#ŔiY kڷkK^eSZON9C͹t2wbxոY95|"ղ/vh[CSRR?= ϘQ#e9nezFϽЍ/чhͱ~;^}Mk߮-.\d <ء=~fyUlL2RRRرӲ٦ubsxw_bkաbt >KJJøoÿǎѸYK}H^U s.TMaz,C+~?={hݪ$ڴnhs^.'͛5S "ɉ'ILL:)R0[Ϡ"2gΞ'&< 6o)+Mrȏ!r~_~: lj"ZǞ, jwIT-l; a,iڤ1gVDAu[>Q#.{/w7lu]7l׮^=?WLasuJ js:U&Re|B8M7VZ{e]@e/TrrI: >{KBa8y$)yM;v4vZ=kuh.PP*t+U'!*{rr:I9y ʼnT7|(Ejo'S MZ?8FZo~e瞡A4j؀Y3sV8t2le֌MRrur<i݊{vrWtz=Ovz|GFz/v풯@BחHIIa *+\Q ȴ2kbVag 9{U$B=4[{q|}qss3|˲󳧧'IIe>WJ+"InFE#{+g a?ֳlv4nVcᱎwӻhތc46[Y>RX"6oOp'y^L{oR[f >63r|d0AS5&\ܘsYn?{>>0Y~ƌ.[i|}"!!!o xIc ='Ԧ;>݊ ص éutl,)Dr?#<n$ZjY#!!$$$Nzqtr dמ}h {dmޟt|kׯ3n${\.qqmuM8/`vԬCyӳxyyx'3{]Q#ǎg&z}Mrctz3ǎ̞3vmHrJ ˖-GRѢEsj5Cg{pqqMV ;}7.Ȃ9{kZKճs?30x@xi԰ ԧ}4S-&jժE("#|IIM!JAdJ,YP8u*g|J|}}ٳYKMW_fy 2.o[kj_Æǟ[^tn8qzv&'cFCһ8y՗mDbdt|6-p[ՉF᭡0=45-[4`0Fͩ[6? ZIiצ5=zʡGزiMGP!"c'?\ZҼzo y˖fFGJnvٛo_9rFK9#mZbFpjŒdy(j}UJ yg:? ...==)1SYlzs _~M^a_r|a{Sx㣤3etT#k(>Q~8}"JVZ5k`V۷777 l'NlB(6!nr(t+Оo]FYIJJG9۷;vo>e:'\p16u6_څ/r=>NbǮ4kޔ0֯Ǫ_BhhL̡Ç9~.|lMq*7]:?9V-!zuZl;N 4J⧟WUf̜MTt>>;YDcx8pEQhXCw+Yr6YVКglݶqz^~ ,lO=8'0n$y|_ //O>;eԤq# {qe.4;浰4iTO!<)#LJpQn 3f᯵sB+ҴIc]^'g'h4J&Mo7ǁfKbά4nފg0cG>+GRc7q7nAlluV|V"@ube,s<=<뽑$n]_`9z,ڷ˓V;'2|t:BC=e1qfBBT㹳0x/t{ &OGtBFˋ=^FM\r6[mU?]t뽋/7a{T77&sGF Y2M}@Y"ڴq(8~+ޣˋ/G>1lq7kʪ_~㥞`뉋Ǖ&(T ^^^|4gϷx{lќÇqtEa*__ݝHڶmCW_,ysf1wX~7mFEmVsػo .rȑB-|}|8|(_thڤ1#j3[h&NfμOHLLߟ}ƶ0)N}۶iiSxo|DEEҪesгK܈gg`,<<ܩY#h3=֩M/1sGh4ZWƗZ'-]^x[seLf3!!o6.MAo`_kO **;[CL.dO8y ^/ֱY|{TqwvS B0FM]&^x!Q1Ĉ (EC.KF&ĄB뺵h8==vJOSosY~qk mظIOa䩨>SϾб5g<}VSP(ѫ-Wq@^DaZnʟܲ E2YpKJߧd.(Pož4Aڵ{f<9Gר51 IO׈ôb2eeemڲU_Vh,P鰑ڸyJJ_N5ԣGwZZ Fg:o_g>WVMuuuڶ}**_V(Ԭb-YhcVy[TY-_YOm5%%1m&=PRy%%WkvVWg~,k{K^+OuC>CR\Iy.Syim#ٟ1ϧAlXw) 0@aVw' -ЌiS7vk܄tCJoӘ;GCJ{;nZ ^ݻOC*Iҷa&{vO0`PCKi}nϞuzޅs$IuKzIJ Hj Y!*Tn-TIJCf uAֵ=͘Q :}(=U:P(VSSz׹sԽ{wwXHs8qRC MHš﫨 Ǚ3ZdnД p8z]/XV.\J~_Sz#ٻwoUȴϥ/yViYi Jj^S"81sxcޅS,^75(r~Sc44'S=֪g}h҅=Oc*NRl)Gz.ERbYyѕ]Y0p8&^1PSt-^&s٦g+;V¶>B.Ɲr3Jd+%&s6ͶOKaJ2Yԙ /0=- )uݜL<To*7zV9qld2qu;[7AcQJu RPT*֨*D'%פd-e'NYyM*@e6M}Z{Ԓ 7 A݆إ$cꢤsNSNSn:d˯ 2!zDBBԒ  ZK.B&Ӊғw9=zNYyP*@ui""jHLQAb40Lh.\v$r ‹'NO ͋|OUDC<\0@s=Ĥsь.Di-gq)pHqA4vz-/ Dj2C[ldJ>4!,gcUK2`20+\:Pbk{*?9GI,l 4g݂b~)W,T(ŏjSuQ˧dRaTR|rm Lt>(r^;3šY}wBq#+\03OLjq[KJ<D̆t}N2&9؏oNy E]ǔksl~l;9& ɶoH2x>:[1aVFz ŘV YU g{gśҼ pk3?}M1ޜ=STuv~^4&Pk(i 3[h3TOo֠:sRjatgs?v̟ՊnY*=ũǸݏA%R R|L u)ugsUŧc M?_9L{Ʌbpz/O P8=捡oCZ `dӕ'(<)@Y]E!:ɈlHfDբ|z|Lu9$#*E!>>EFdzhc+W?"pt%Lń%LNE :1m?RNk&q,WN5QP2Nus(N_ $K '<85VhPHm!‘׬cLH ''a6$b1@Qq-Ap''mT[͏^bPf,@ģ(r;0ac:EKbWvWG *%^ዉF{W,LXsVQ1ovm4nU0w.ONu/oãWcB>.z\[YT[R ̣%89ekKHw7нP4nFgߛ8p{ԸLk%mޕ*F&OIKCc[5*Sա /y}f[S}FuoTY6''qMcwN$e?(Ģcl1zZM[F%(JP˗Ѫ2ʝ꫈K`JKZz OG-ѸI]Ob:!ɬ'1. cRr) vI艋Ɣd7J#XtN$k0Fb4YufsN66;QQ'AQhl$S&Am;8'9ZىDIp'18jL[؝";p )]5DAxlf#8 6uI$R>Bش7[YF&l0aƐv(d-GC8FѨ1M\z.h%۷Q4 GA U:ۋbAM".NT}O`1I3DS+;6b.k񹴥N8]NXH߈g;39YK/'mߓROGjkuO)N9_3tZѨxx'QTm]c!ҨpդX#Ih:M6uZF\~'GG]thZIJJ"::xtzmZ%K9FuYZ F^Xq[{y9֨Sqܨp)8EQPTh4j5ge^d n\reK T,T1 oT\5j4-OyiKA  $$ 8 ?@d >mJ  'l7A"N   8 "N  $ 8   $ "N  $ 8 ‹&NbڌY\ z?:}~Cu &O͛?Gd73lĨt> qqi^6l*'fؾf=zvC}ҵg>⫿=/hRMlydvî+ToAe*i_88%%%3ԪoQZ-7[9azg.\m_Nu6}?#1w|fΞ+"<74x@W_Sѽ#G ̙߈x(/ωQC+!$>#GŚU̜>WZV-y(d ~.U˷e+0w|O3r8Jۯ0mڿƙ_t}'(\#߇-n߹'T˷kcm|9_r5,;Z7s6^M߰ooLxP,0q'e^6ǎ'0, l*qwܹiG0?IZu+L՚uq,˪Y>\~g{~=CZ3kL>JUk[5=4t8e)X(zɽ{lFM[@H̜=ɔb0cBU@BX{>Uiwϙ#n߾c SJ Ofͺ &c|!kףPђa֜2DZ'_}S瘘NђA-Dn=yVq=عz @Y*ݿO|ְ?lێ{~"""KnP(R^hШ)'Nwԩ߈b(XUj|<̹xNٰz4z!/5m8{{;F{cڌY U :cU ?K?]msHHHƒEcH40uL:vƎm[PTL:'NRhcy,dǏT`IV~xgݻҧ[*`Aj|b%o˯NP@<|HWRr%}{1r8fNW_XT*ɴ|JF}RnEF @!̙9www~,cOߏ{eMi%z=»Q\YJα'ù L:7W7k8HoZL><ㅋlY]ÆQTٻot#h4֩'9v >G…9p3gϥBr177W>Z>p3tZk{2c5Zn{h4w^}3jɈCt93gsKJ(Rp䛯R.:nFϐAV ?[Hƍ|̜6ٌweJɢ3''\]{,!L2.{wT*ܴѹ[6fLc7j ^^^jٜ ?ݺƍ^t%c7jjOۆG|L&>hz/tvw޽h8x0cޟPfϘ&ѣ(޽K.r Jvm_.v-[hղ9?rMfgݠ~=Uʜy;k˖ƽعm ^^^,Y/5-[i˄ۛ&V\96$%Ō3z\"ɦ͛קwi (TA4o S2 w{.|:ζr2ŭwL),.-7v6ۓ>k6ɤ g8T@J Z*ٷCd*N&ujײWV_#G)bިaC n\zlTpmVZiYgUwQZUm8=Fpvv߰~} Vlڼ :www7k3ZqGQ`'CBS[m˭4e:Yl Фq#Ea䩼ݯٶzZh+m)iKN 3fOƎRO/5֭ی4*Nŋ~9TgcOp!E)ٙZry6ӼӴIc&cmV-Rlt^A8p+g{m+/\+6hĕ+W y&xa=wÇ@Lϳ[/EUO}l`ߵݤΎjժX3~JY \2Y ߗ /RV]h\B||<&M7 eFrg*|ȗϚ3ʃ (R'=#şrlnrh(l$=u x`!::&Ӱ&+WnF Lgi?>ݻom@5r]Yڲ0L@Bo 1!!pyk{_X4_3星RUkf\qúiz% A\xr-H>5|vz!d{:c՞o{kִ oKK/1|~ڳÃJ**Pbb"OR՚)Anod{4mįO Wpss%_>wSkgoGRRRv:q/0lٴ^3)fjKASy(^(_^!cmݏfMU/YʖMRҜ~N]iƏVeYi~F7ӰA=d2a2h4$&& sRmZm8T(WX% ;~R\9_h3h߉|de/;ء(*~A@B8::rJ(nd^y *ŵ7WVfbE1m2[6m_Nw~ Ȟ39 R -S=R/)fz4?u V IDAT8?!T\ {i\O Ȫn:u|km?Rߦ#GKV oAj5e˔sZgjvs;c%j#...+V gggs)-%7ʉg9_!O6OE (T{Ӻ|y [~Fƍ|^}6y^N>CF iQ9١GPUzޞQ#ٷo?yPZް!9ڨR2-[4c4>bUtҝCh00uLʗ+K)3psӓkׯ'2mliP\4M+PnPT'},@}ŋZ*/Xcoo})]hZ6)fj[68::Fay[vvܻXqpp`˖Y^^^Ore˲d,d5Wb`H|:bqԤ9wF7 UݥѯOoլÝ;wо]{P8 ˗C5ʔEXcШi @^reNI^oZo~;wqaݿO…֭V!6B"EXd)nnDDXDOiwcF&Fʕرs7?lW>UTPO}ʒĝ;at|CU\1l(S:$G0Lqe!x ]K88:peڵiM)9^=+>[NeڔmӚga3+[md-)\8;aa̚+UPNI环[oֱًG<ݍ]?nc ؼJM"m sb⅌4 GJ #Owh44nĴ)Gٴy 11xzzҹ!Eӳ5,l%..v>v9[oѷޭkՅ6DŽS9f?__Ǝ!l\\[ؖ2EhP!Κa 4p,X%K?řwn,_Ys>N-Vi1k.;v[Oի,d)'NF兿_lܾ'LW(Biذ~KuPx17z;wѥsgj>>TZg+ڶaܘhSoʗ+˺2u :uF|ۛQ#[tܑ?a?=ݻ5Wg)n#K}ĻCӱK7>er)Mf >]y)Z˖,EfOUNmẔ'>s6*Vȕ8ZISp'DFFkTZU}sѧZ-E RVqR2AAprrp@@J[nߖҵs?56''ѐ#x( gg'JގChRtZ-o|qcFduq]ISRK}Rd?'v:) ͤ)~9q_7>#8 6uI$ 0.KگFǵk ;q+?gwP_²|J>]bŊ|<!߇t yB^vA"N   8 "N  $ 8   $ "N  $ 8   $ "N   8 "N  $ "N   <){.s!,ϖA1/W 81ci 8)ɢfH20Y,/T#aybkhe/! C a^72ǒG:C /.B'hz`!]WKA韀bu)b-Y+R WSh BN%O4;:|Djo&K,`AAqC}J dq˗C-_*k1pPLc2}+\wK~:Jbm߾2eʠ)[,;wڦ)..^z憛}%>>>mS[A*qJ4hݾ_qY|)}zj](7ob޹+qqq=fΜI Ͱ~u֔)S-[/̹sazņ Xd -bݺu/OmhS ete™8vDMM&?&))fRo֨ŋ=vwX.t9+k>s/ay?qƬYp5kfϞMNҥ ӧOgڵܺu+l:Nҭ'+^S^2m<X$\NBhޔ&_B׳-;x˕tHHuޓ`883i4t;:,ZJ޽{I%Ջ>}0bĈ OFjt:6lȁ͛7Ӽys, cǎl#3qzWpLXFuz!g 8zrJh߮-oNHp0ʦ[X3ZL ֯:kY׫W֯𑣙ӱ'8pL4n֒sS:$3Ͻ;ʽCٷw/lk񅅅mLJ0~&;aN[o΍7moT ӳGO\_&,h>aamИpDvu3=vmЈbPxc2 {_hܴ޾xꕶ./*#l2[Ǝu<řcdӭ'' 甆^UjٜooOԩ] Ns }o$'az$'ЩYǡ`pqv~;~+LJpmV&ݻZߗ6lUeYλ'=: {NOҸK\|Rٻ?CS[LʕwOfׯSDq%ڵkk.F#OԮ]ۺ`۶mcoߎ(ԪU+l<:e,\ |i"N ㅋyA ͚5_VS6+UK 8x7oݤT` -[re˲d,d5Wb`H4Xm;;;-?Pz5*WD6bw)LGM&ڷkK||<5jקZ*,[(OL9-6Zǻ!CW_oҳgO/_ݻw/aD>>|4:ڎ+W8^~~Œi1k.;v[άZISp'DFFk}/|%/_#Fn:J,Oҥa>3}]zJk1<,Wgˠ#>\Y z/! *)AAIADA'AAqADAAIAqz9t3g}f;|?! f3ٲuBC<}lڼE^v*wˡte/B8h(&?YwǛA-EwǍÃ7oq', Z-%/ ӥK gڹ͚6w7X*UIm4d8u7`ԩ"%|j^oD`pY}xM{vFH^i*x`&M!))&̧?R՚xflnSQMђAxV݆ܻV SBe|Ѩi >F~7zR՚ ӳR^֯~:899oRC#h ϝݻS*>_à!i߮-̙dd̸;XmHyp3f!2+V2zx HUq.'GG4nc&OƉ'Y|{zr)4OfϝO1q8Jdб ?;ƁPԨTm 8!nn^R|%ڷk˛= @Y|Nb+WiPՆ/5@2ٴy 6U\vf[LJQ#[PQ3n$ƍSg̤כ=;z5̭[?iJ),.4iZMʕ0$%Ō3otrlq'6o_Vh49|ri x"4nįOSAcF@5f)CQFLmyˇ#YeWe6T*_bXaۏԩ] N-jHNIL&sgc0I~jתɩGޭ+ /rZ\t^9wu9vh?)6v٦ _@F/prʅ[or=vͮݻi ѳGw@OU*Wbm;_^i͡G8w7[RA)74u<ٙ }ʕ*Zýݯ7l}EaْEY# aa,ZE qQ6mBLL,t:`Ƞw| ?[ !!ݯ31k7~KѢEWv6A(+t)t>~N@DD$e+i7TZEjD=H M]bҭ H02qϠ(L66aA-t]ڼ:ZfM0{4 A'///n] A2E>6( 8   $ "N ¿6[R /8UgPT&-EF֗W(vśAgc}Sv(fd2tq8#Q'`Q/E+H_"NΝ#(mfJlb`X)bFu:* ŢέȯBQ0AqDGGcggGTTNg~(O+$Uc EF8(ɘMPGA1J~MA?bAQE Ro*%:&u,:EU4t8 8=2^'**rFQ:*M4p//oL ¤RPT6*? fdBD^o>&NL6֭)a X1`Ÿ$fՔ; e׮]/Lcضmk׮}fqZv-=|cX'[֭[mzL30[0.uOi&SرsԩSyf:t`A9FE! f*yq';̔ gFĠR ÈO@uUPrL2,^f[2BA%Jн{w{||嗔/_4 EeooohРW\AÆ quu%1$0 .K2eXn]̙3ԯ_'''\]]iݺubaΜ9Dj2j( SދuW8a!0(vܹ{ EMS}6bŊ+V={0qD*Wĉqwwgtڕ;w8}4aaaZ F޽{2d.\`8*TիpvvJΝ;۷/GаaCݺuΝ;3p@^{5prryrڿb믿7|ZfҥݛN:dF#C al% ԭ[{s1a"""X|9-b=5kuV:v숓-[̱^uزe *ZM<?YfҥKiѢ.]ӨjT*)"NQ2{)@5OL:tvV͛7ߤIWׯL2tWɓ'YUlZj@fppp`Ɍ7///(]Mk￷ӁV...6ݻhqT^f_۶mY`'OJ*ܻwK.ѤIkggg6m @Y~=͚5m۶\z5oFðaØ6m70ek7n0bĈ\ӝ;whݺ5׷n03gSNKټy3ׯgРAְ!!! A[/I{rj*W'NY$3)½{*T YhZd2/dN:{nسgOsvhݺ5]v}գ`lذjޞ:ud'NNN򔔔dMGfԯ_Ĺsx"=q6a7n̹s爉ɱ&L|@ Xn-\t8Fvvv8;;õkLOZZfRn*ɉʕ+,OzR6݀iae˖֭[v "((:!3nܸ֭[Pf'O䄽=2>ԡCOBBmڴy溉~@ӱo>fϞM~y4nܘdBCC:t(+VDV3zhvJhh(]vAdAqIQLǍ{Ξ=K 2lOHH6A IDATٷoCѿMfkqa뤃UZ)00-ZuV|L߽{ Ntlڴɦ;rȐ!|t 77g*rCn݈PB|gkn=D;V*JQETRUQQ{ϨR+3y~$9reR8Ϲ9s}~ЧOuƜ9s8q"ŋgŊ(AFìY;v,[n{kcbbX~=ƍ#%%www͛/#FА 2o<,--)[,_}WWWϟرcYjŊAxzz;Yw&һ2E0 #ܹߌ?L&L$$$Sf(u;vev?)S>|8k׮%,,2dNR[/ѣG8;;{nj֬)gܺ酦is1KtDZ@<$)e%J_Yr >| o߾ѣh4FIƍ%0 !)o]pz6es޼y̝;///~<BΝ;O˖-.B N/HQ,+13ntov^%-6J:Wt-$)êecu*&%-m~X { Cb|8x5µ\aQ*8ʿn_w#+:`e[LuQOgnW~nYs="s4: AC/v\|IHNwm9)qO҇+ߘ tXء$lM{mqj`2Jѳ Gã2w_%&1Z{ӻV q/dLEmABTt~hjKN,aÂ%4cK}u (]X]}" Sw6&on!/nk¬=(۔F4*ĤWvU{2aQy!ߖӿՕW1 "{/?q՝)hNsD}~녇 Njə;h[S}?''1%e/jpq<1%.Ӆ4)aK6ƀ:nydiHG$ǵۙo:CKW!^xGyma)u WJLyG C]W3C=7`p/:I=Bzt iOIJI9ӪTaBDhL  1%v顧7rO[O lp4R {t\7D^ق G<;t-+#lL77C"$ ys,LjV7{1)lLuq6fIWmQ&'ysѢISܰ`All3Z3bh=uIj il%٘`k&t{?2~q/>RmӹKe5]0ap3:W1$u 9BQs=7nLbb9s [L2l޼Zl< f̛7իWӫW/y' !r\Nz>` 6că;J~[6*Y סCXbJΝϝ;ʊ+EQ]*nnnJRR(8;;+(ʵk@YhǼyFܸqCB]vl٨>0| 5'O3~Fӫv###VXAttt,]-ZPvli&LXmQDFFINNfk׮ʕ+̘1{嫌>ulܸ ʕ+͛7ܹ37f,_??|ȩShժU!!!xzz>M6%,,wr _NӦM%K<6l~|Uԯ__M5M( |=B޺zN&,i?ʊ>}Dzeضm 6 KKK>|ܲc+WmҺuk>#>\ys~OB NVW^N iӦ 6l`̙3kkk)ɓ9O!åK(Yܜ}2}tMF׮]qrr"##U>uuu f׮]SV-[ ݻwAmNfZnydMlA !xӿ1*E4kLm|lܸS|=8v SSSϭ[8xpm533lC#88S2o<Yf;QEk4K0 #ܹߌ?Af- :SAʛx,ڟ1}t9BQvnDժa d,Y@"d, ̿o>+۹-BWW2eʼpOE!󭚑B N/ߺ2G!$85kp ֭[`y*ٿq-PJ9B!խ[WΩ $-H1Mw !xS^(GQ!1: !RB!-l13 !x{'޷$  :::t֍'OɓBHpz Dw>I^vooowNjjV |caaA ذ!{˭FwygϞ͒%K:u*fԨQSD _h)QćfΜm 166QFn0}vʔ)!e˖6Ibll,XYYaeeEϞ=wrhذ!gϞeɬ^}bgg.ԫWϪUX`0zh}̽{hܸ1̙3nZLJ-[ry5@Y`c9w%t.'=@01%ˎ-;VɯJʡJW>J|ߢ._WV g^[Vj֬(oNVW^N iӦ 6l`̙3kkk)ɓ's|]LL .]dɒjK sssә6m]v sssttt|kwj=i̴Țl16NvW4k Hb駟qFBBB;zhQss-<5|D doZj5oxzzboo nnnl۶MM %<<\_V-vMJJJdݫh}v4 5k֔w"WoӧODƍqpp 22ŋK3f ߟSNn֭ >5jlll1c/^\ݮ[rر#Ǐ?dɜ~T2d|Nu"HӧO)hMT\@Lq,$I@JƢiFZNon)S!o7i/fΜ^,SSSbbb^KB6{7;*m0}tEqoP)<BZNo\K1MNr+rBPηjFB!$8*UPJq!ϥ#U m9\tGc~~~ɑB Nd_FA??rtBӿKѠh'**4EQf'OsNQR% !WCjj*$%%2$_2'|m]ąB^ۀF(b``.Z)&&&jRSSՙgvbȑoׯ_gРA꤈/~w?sJE N38Ht\"o$rr%ˡ+)pJJ$ñpX 7ݡ{VV]Xd/Ghh(3g$..mN[do۷%K''g˯>X&Mu~wu^1!gzIIDťhDRKP Jࡷ05ǭE Çr2nݢiӦڵ GG\;qL4:uɓԨ>>ajjJ@@@n͛7SdIa͹n/""Ν;ccc1e˖޽{\FիWZsjժzzz2~xFE144LJ۳OEcoo)m۶ݻZ{###\\\7n?EQ;v,...;P5k ...?[7:ѣٶcnܸSX1J*%&&&?XG"E066aÆbڴiܿ\9b9?L… Ύƍî]ٳ'!!!Q~}>̡C駟d߾}7ʕ+3n86m}oֺVp,Z(ƏOݺu9{,zzz>}&MЮ];ƌChh(cƌ!666ȴzj&O)_<7n Gjؼy3wޥ_~|j?#ժUSr֭[044`˖-ߓ]ve߾}L<{{{fϞMz oN~h׮W^޽cɆ 6l 4RJS|yΝKB8uMݿy&Lw}ԩS)Y$ .Yf\t ''' *8i4k`:ME!%M!.)4@TM_Lsݻ9rD`RJӡCn.{ iӦ~ymi޼9ꇲ-jbƍjp:x XX<]QF8;;ڵk)S ]tƆjժRF 5'Ne˪Soߞ#FPT)VZFyh4 W_}oAcddD@@vvvZP\hk֬Q?oܸ)S9100q///6lq֮]˺uhӦ2`ĉ̛7O]J*ԩS:uuVuTb Xb;v18*URJPfMvŁr N?71qD|V6mbڵ_>tFrJVKߴ&ۢѨm4E!%54SѠAOG= ɩZp:vKV@Ŋ)Tڽt5:uꄳ3VVVL6(u3gm8sQdI;:uĞ={GFrCkV\WWWŒraeeř3g:hذ!F-%%ӹsg)QC ڵkjɓ'yaddѣt1u2Sf sK===r˽{ҠA찳#$$@ҥK2tP͉%$8]]]RRRHLѠhtHHN#)%TbRx'KtGIs˫WҼysٲe ;wbŊZ### }wy.^BSA45'q?*~0*ID$DTl2ObK&&>EkK">13WQreΝ;5@ԩS<~*UŋիקL2,Y2[;wsytttXx1: clذwwjDGGV\={hgtuuPMKK˳˭sΜ8qO@2ez*&&&(QBkGU\ݻw%$$p˳gϒرcZ**TPM_w!=<<033ٳH"%i7S2 d4 #^ h4/FEEi}XevۗӲeKFEBB_5*U>@GGOOO~G\]]ZL:UҢE z쉅gϞ͵,+VƆ… sUbbb(\޿|||y&;w.߱cE%44S|yZn ɓ壏>O>!44QFѷo_ёk׮ulڿ?ӓH^z1sL4iqttTLԬY++++̙3&** mYիW' ?eԨQRe˖EOO'ңG|8p3gPLFѣ111ߟd8p gf̘1߿ŋStij֬Ɋ+ԛ(O6MJi9RSHHE^uHHJyVٳgZϟ{ޞ.]гgO|}}ٲezO`hӦ UTaTtr *pQܹ3-[ɓ4n8זG}Dj0`]vej7͚5ƍ|gTNNNԬYQFѸqcƍGǎ Q+UĖ-[ e˖L:h֭5j*g˗/3x`WNǎ)Z:ʊ/N~hٲ%~-nR_l2bbbhѢC _/B:t(m۶%!!{HpZ'NP^=Wιs^)=F̙3ӦMc֭[]XK20[4j玳}X.ꠈ"(jcξoeȐ!:u]vY*x#vnDժ s1KtDZ@<$)ezB!$8ܜ:*(/2044|׿fΜ ^#F[!x?-BHpB!$8 !$BHpB!I!$BB!I!$BB!I!'!BB NB!'!B NB!'!B NB!$8 !BHpB!$8 !BHpB!$8 !$BHpB!I!$BB!I!$BB!I!'!BB NB!'!BB NB!'!B NB!$8 !BHpB!$8 !BHpB!$8 !$BHpB!I!$BHpB!I!$BB!I!'!BB NB!'!BB NB!'!B NB!zf;nBUH !x)ҭ'BB!I!'!BB NB!'!B NB!3;D\xؘz!11TBˋW~& ^y2aҷ?q8y>”i_)DzCȻ18ĤoJw3nOƽ 2EWWcccر>r@LNIY~Cs-HLHdo<|q6mFQή^!/n}ܸAQ/~Yk/:2*['ϸw&8=ȑ#)]4ϟII̞;ub쎋GqZmfΝtoMϜʭgSXqlmS~۽6{Sp777hΞ;Gn2|`ظy3]>z09cgkˎ";卵 Djj*SLO>տ2a7kBmeك3gp4oE1R;^̛^}s밶szxܝ;ws`쭲Ѣu[b;ӪM{.^~=<>톓'ż~jWĠ)Yu5d؈Q.]΁}l֠WZj06`m:jmGql)_ɗ+VһoKɍvVjע^.ߐΏPj 8RN=֭ߐ$22)僽;5()S2Nn4l,\syfB)U"]qpG>t96ٝyZo%(J9qO's΁tR?S4n {'7r===Ԯm{_AGGǏ#}V]6<~ISٛwO6b$&$2q>1;mK~^?7nBTΝ144zj.[_4͚6 ܻwK~DWG~Wi׶ 3Qcoru{ Ԩ^~_&%DDD$g`O?3bߪUرk7{ԔF {'Lĉ2o,lm%`7f%yr:|؅^袣?+QsϯyH"6{{?xף2dH~Y$L|k׎UV1p@f͚7(I3-@ڵz:7o18*D5ߪl۾ip:zJ+`nn͚6V9ǯNm@WGOuk`%ܿw]6SHҿnܼu@A>ҙ?҂!::sssA0&z^Z4oʢ?SX<>$UӪPLi6lLp:M+_G@==]FϨceioY7˵aܼy1Wps4o.+W 1)s3jpUT;va&z}Cͣ7zzz37+FjԨ^:ͮ]{Yzԯv-}3U˧疍 u5ʕ+Q#)?~y’vmPׯcy~v'''3)|q`ldg=T!vSt)oҜ.PTlА͚`kcC մfӸ?cGoU8|[&z϶Ws~λe!115Vbmm vm*%mZRu6A3fz^ދnƍSX1֯_ȑ# sT^yks\GnoۆDRRZG9U\x+aDE=^:Zԫ[/K|ѓЋVXnʕ+1v7;a䆋{1bbb/g~jՉE #QcQj \cccL[YYRΎ1=66+W}\026"55Toљ1ظy իrr 1/{IFd)_ yú#qѤ|ofM4ٽ{/g ::;wQ-#"hڤ Ȉ;v`m:ƌm;]ՔCCRSR,]Y{VjjZ79wjլNec~708i8i?K4h^(O/_nh]'Y'GbmLWb^D~ʥL<^zd|a}?^lMx"kk… lܴy ̟=ڶЈ2h@34}9?]BB 9)B177ysa[0{r頁Bl۾#qJ||<;u|ViN~u\]\*Yo'M`uuBr\Μ=GDDV-ava=nYyRl<==177gZ<133SycB  A+qsuԔ /^Lk)lg#"gAߩ4lXV-)CJ-,,r,O% \U}}&r?O.l)VځZڲwv1g5ShQ|\]zO2ϥl;zisU(_/:9p9 ݺ~Bڵ(ヷWqϫٳ/'ȡGhz}ރիϪ[î{п/UTw~î{^Fmn߾Mv[@ 166fsYR>  !1P\Y2 .L]L>m00ЧilS&M|98ӺUKFB|͛z?sLL vA__C0{c'`mmEʕعk[mgLLLؼy+5juw-Bzx8ڢ}4W׏d^N/zݥLf2thkء6o1ςPlYmMpweRBy~X# H>ܾ};v`4iњ~Ƈ|1o9Ԕ/z?bkkKDFE[N~ʕ+Q.UHRSsܷ?x͛VA_Ѭe]>FNbbj˗iК… g;=}~y׳G Km jNy~k*;~Y666\r-ϸ{ׯO_&&&l; ,dM:.Z>ʖa?ѲE#cIDATʯKٳw#=zѣlشlmmܩ#_~a񒟱0T9gg'O·SXwN<|Uƍ!!1>>%ʸJ@;qSTX;vJl;ƌ#^QׯοFFJJ DDD$e˔F_\(VX"O?[[[>h.}rӦQZ5LJTͷ*ig}ݍQpϥm;zmwޙ{VƌQP!Ԯ{.}Ϋ|˗.fcHLLʊ+{hrxd/=0xpV~3| OPa,-(^VȘq8s]ͻJu[ B@%n|7c]:}("^0 X<D HX -/ӛ2/$&$rğ26~NoãKzXRt X6mZtJ'B\?Nppם}9#S/z={V'NKάoM{"[ 뉈Ha=%\(DDD$""pIDDN""" 'Q8(DDDN""pQ8IDDD$"" '(DDDN""" 'Q8(DDD$""pzQ\9N<=$hݺvH?jgVWrMJ;"{Nǃ1 D=qq"G: M";,StuFZ>"g1}J] trcƌvu=5Z(Y֞D٬<']*_PXY]i@rFejP^z2>ٮʩ߅7n<ΙK ϛKll,dKfVf'\TUg_llh܎r0" Ngg 6^Hgח_#0vx+~:Y3-_u\K|ɧ:g.FWoYF'pmضm[}!4m֜87Iٳ9eֵjj܅Zi;4=UڶkORFtޓ?| ""eJM%cF؟qk=g)y{sXi8gN{W^f-zm4{ӹ7=&L0-=iӲHmqf̝3k:t q#zdͥm߿7@JF$7jıcJǂm۶ኍÀ~Bj҅\9]b.\i[ßFR$^/<ACdd$ǎem,\-[d}L4 +/`%=O #--SNs`Yg0kdNJYl9EJJ { K3LeYyw#"f0 g)8I^9veǣIq-u*}[oI>}HMuؾ#G`!јiZ|q/\S}{VX/w•3vx$'0~GUK]Xx111![:=[kc4lPi[/abv8HNJקwo6mJj:ڵ+3gv2uLie-,(dΜ9L0}veÆ YC.ee;p:fipxpixln$}IaQ~igΜ?>Tz,bcc{]~}L͛_~nm{k2''>?ɬgYWTTwVU:\9]/hYvv2u*o$$gKW㘦ɷ~ӧ+z8@nn=/LXz{QQ%bj2HmYOis^MvmoÏmW ^׃3Y  gL'--}.o *D~ɲ,L3+؊ l͚6O$)]?σኈxL,B\|< ݽJ|iYwEԐ_Xp_;"j:]9  [Ǽy3A]Ӈ29$U6ej?JhѼ9|շo}۷)詐;s Nji(D rp撟O~~>|xb* JC+;ѻ N0r3g[+Wcvn;|lW}}x?{Nιs:xss[Zm׮LǩSn0!"##1qdvMFF:1;zm;v` .".. G<8NXɒ75:1tȓ>G>/pR8rʩx(,GD^dHNlnp`ق΍sX,? ױE6ZlŽK\ܖGt:YJV.[F\\,:vǟ0 ,_?)}PBcظq襝!dn~׫:vi]!?'@.sr\ (Ob&umKmAK6qBD"E?NuIHTcw)!e C;ZFlXIBBvH\<ia3 Bb7O{䊋gᄑtбdV|PË́iݻ˥!&צk36Ms^b,bnF2[Cpi!U iӦvHaבS!y"%0$me^۫Un.36Q8H]wN""p w5 ,[+E+(UN""P2*(&UpvC""RʐP+'T[PUIHY5 '&'""2!l< 5T=HU<~K`PYɓPgrprS~/䀪zಗ,Q%K,Y|q:}Fu!XR**Y%(u^[ +`1KbQIx*0~ID ' LSn 2d UQ6Ʒ6t ' R *NB *&7F^n /4'"R'+H6+* `JTِ$<~8IDN1UsS +'*|d4nϷS@ԝʩ}CN&UTUKE[ ,%F$u6r%??6dbbbbblV)itؕ^'j"2ڵˌtRyRM{mF&/^d0{Xj JZ,njĤeX.{9 r]&)6^ jaD,.;nIu2/0PU |x Vd$0@b[3y:-b[46yy[i3b-nќDvP"䐳y H"HE?(lbbr|ZUcו !J#R3jW~T_Rq/)zkrTGcx uG A$d$ҿY?V٬5'p=t&6AA=]8Hu39zډYw鬎9ݵj#AH1e׭u%#_9T WKmFF@ DඤADЇPMEZ_5t6OcnN!X,U2#!Ku00 t]GUU,ٴi+Wɫ.4Eޮ](hb8х=5;MOZ.;(Q3y/괢EjvF n>ȺU44(Ivێ@G/ IRPࡰ؇ƂjEzj]/q8^ٛ+W?cDE5M^QopњsksU_/`u8^| <{C$VKxB2yJC%n(W.,(/"G7E86 :҄rIe\x,Z),($?Op[ dGXF;*οkRyCS˨ iѝٸz+݊{{4a6+!%/Æ A uv#i(B~'0o yՇn61x 4pZp APz>,4 cj `HneȋU]ˆiruhƄGoShM-90ѿLGŒѓ+oF%u,9I}Rk}Wg~a;W݁cpSRPX#mܯZ i@V\v2OT@$7nk K|~J?p'ܤ'(+yț> 1 nw AXJ"dbG"dv b(PݔV;Yi1 gr(UԸu㪕bw7-;! 7F^{`_:(֘Ci㎎T56~+)[8 4nN(0L7vکUoУQP qc{v 2P I7)ICX?$&w@f)$80l]%M_ /B$ug0 ˍyk4͉Hodtt֥洹mK"aPq3_ލ_^gO]'ذ3^{9X'z >5sZ,dA3(.72bCS o7^z/zuŽN䒫{Ϸ$bڏF9rΜwR闝F] ?Shi޿p'k֝p`SQ?3! ^_/]UI"`ǫר"] $w<%j^wU-uGcZquqR@ؓƢ!a$7Fہo^]J3u/qnBkxB &=n8%KinJM{ _s1Rj<^WI6> }Ixhcng6̑SQ۴FY\n{GW|~| ^ϭ^s%T#w v *=7쨵oE 7h 0ֽ],FuҕOF] и+6*v:NG6)! ӳ]grY1j,kv XrJ%^Usٱ[NjpRr.0FTf4rf1~Ur! ߕ꒦i4hAF( :'c0u{u#裹UE y\NTUCt4-2h(,,"9v;*Ut,ξ7[lZYr*5"a5Cc i\uE rSy-U 32B6;?sîMlsѼ"مLfŊЪhT#7;W,9c\H Uu 6VYm Mn`a"I͍AUv?OƉc;EXma`I;oT; >j6 bDE_.֭y hӄN*':KT '\/S^o#/U65ݏ$I5Q?1sݲxN2t+\HqXC\&I~I..ˮj[ye#-LXm5 GV66dIX=$4]ǝm!F%8ނ/ˊ($HrV XQUcy))To:%Ԅ _i$+R✄$5熡}>\o9o>; ^ō>o ~$xB@d _!~A#$+R(Drf|d>4?Pa$[*8K/U줫N:zXHI袎XbP]VnuuCnu8^i(k㑅(nb}.bAQWF@oY;u"tpz#gL0XJe8I@ *uRUu*cT,j=pS 7%0"QFY" E碫Q3\<& Wz"٫fDl H<='4EfL]24Z;K-fXVbbʫi}$xQozW!;ʼn_B4ݪ qJjd"+wl\=ɏ`73Fis85}!7)x;|5\?e7NN0]KX@˯_ݮvSai].SQ\Ukyok_.Մ,!3$qEiݫYPӅvwH-* /']D/9_ 5 cKCX6t35_;3,4;$Q$MOɻR z>.#Hh7jlx$N H4W[K/q\ cCI&/ħkͫoj2v!^EEbbc]ey Ai-`/\Nچ B3?5*G| hW_dcYhXrkC.٩Y-;([D3sK8ݚ1tnVZT,B= qgsrl:Ap슄]̗|pڵBrh{l. ) +@uyq-oG *E+sc;fۦ X:s>~+zrmZCw*XDQ_S0TWiku"{!'1_PX ^}%d~= !gq4e qA*I[: Tjt"o*pahD`ǂiȾ{wgi\ l**ck='lHp;`>rF}ڐ9Y2pTIٗU+ Ԛ̜ HAldF L.cobGv1c ~QmoM_78O!BTErw:woxw79ArHqj.?_O~.Y% hX $={\nVK4MUR|'I0ظa3VR%P\A #&P]VHtPV\l %a'-BU#{$l8J†8z&-ASeAC]Ɛ8N֙Ι6?^ᆧWbu3센ŨZVvKp#a#-FSoΈx28Yi'oġ #B[! 2Z{nLSiw7b :3Ȗɷ|H EQ́j`%]VKr6V]j8kW4Pս6(>@<>z#=;ѤS[[aG]HزүE!=IRZC$vp IDATюp{PCyz4ìU?3& hJB(HGzu6i4%g1 j|HVpSvL\/NNǮ~e?Py(erG |e<[Gs.ܟyaHڦEڡI/N<ߊ_>۷'_W8fP4jrsCw.1 m'ɬ⃸>g{6ǥ?n^5ޚW[\vڮ..\pHl8v7ಡ7(-,Y uЩ<$~rw}kGoUۆFߟ7_E 3W/w}r-=aXc8˧ܸċ<˛սJ*11OB'PKȊbqҨX*ظl;R/s7szWR8;q?,W8/MqS:fn /?;vimIoҎގ jrJ" ʳvלؒw<\* :!r='#{7XcK?fcCHkQ4+x/Ǡ,BlxP9ݤʄ>N kÝ@j y<4!΢s㐖 4 s[W'=&W˳yO$\[68}bmK_7O '%6Gl3z^ Dz1 ӹʵau_u -Sp$bw.De<7bq$I/*}cЫ;3;0(ЏfI$ymyI޹sc-lVulJ;GZwΚ\j[:V䗨LDh=Og_^͒qioEtz{p/޼|/o=Ϝ.{(2wt;7\<{7F_>UM;5*"'Ͻ?y|f\v":a!gX%e!͐grm }wɗg zw9{Ŧ\}|&[? R||Cp,[_j>2_-<\: HjׇkGwjB Wrzrk:ͤ:n\_/0fw 1wբ7~p"nS_aȉ19̹p;;h]B{[7'V>BOs+"S> S~lxK^C Z'sU3I@4~ڄڇd/9sܦ B/KfG.Ek^Ih¥KY~aÒf^+xp2_ ^[L!Bc׬YzwGΉ-dq. WqϞr!r=vA/E\ߍlvrS @ڱvlv2_9)Զ ?r At;߬|91$d3ة083/ ^$ƸIi9?! /1" w/)AӴfcq7l:֙MtɷCHh#Sz4׈X=w$˩lvނ3RTY^N\xAZ8 8uW=>[f:;[۫Equϵ}I{&|#O;fWG<ܝ B|9.s7=:iԭ|6-׌$C9Šy[ 5l/ ^Kb|Osat\9g_ #v 5Jel ET1\ZkSΜ47grV$dl'q.XITr֫{'RŚJŷL^H!yOz[7~prW#ނ-cw=4|`ژrY'XOf{ys(W^,>Ǽטo/f]H[{D/'_)Sq{㮑|B )v#떀Ś olcoI@v1wuw~%r;P A`s\z2N=W2l>: my.|mZtҥgCKռP޽~̌ wxtX*M?$m"púNjuao͔rK3{+< [J.M&o qUأP=Hm ) N8} ~<(knCeuQHAt;&t%EQ5VgtT] ;:  jZVZr>t41'2ekpc"Fǵ^_\Hmw\.\AюB՞"owLW; jhF@rX6-"mIW(ZPXGUt %qJYDlnjB:Y<1 _ PCE4?OX'A7\)$Zh؆=Ѡȫy gdF$eeZZA^HG $G۞S{$6%%^_"Uh\MOEpOo?D^-4L'߄ :"M8'al_>}5MM^7Cp7c2u\(N}{K?"T&qTo=%o}t].p@h?%2bKPS@*PٴۇÎnЕI'ad:d :ΏJCTiBǕŕ[{4,lF4q<1mWDƪE:=ڷ-mAﰵ 42m\2ꔯ(>~ʅQq`CFT|ѫO$7]bMQٷ5|9f g 5T0MlES*bU*RCl ѹq,iNRTS:ݜ] PYhZi4A3s !Oa{p}]=Kqڝ|wi8;^oYts.E+ a*eI/X-Ő%'2^a8;\iy%U W?vUT͵BWuWzJd=2ǰ+pAXiu\iyW~/Siqd|卼~\7r})WE/X1tx=: Znu|2؆Cӟr(Xci`bbzr`?=2?J4{G"shk%cD-\Jꇹ=pcb5m3ʜh'[4,f}^hߑ° ka.o%xo? $Igd8#!08(3ˏ q,W+fWVCkK&!+/`_ZIkn;:`p!>sbVQnk.;K]@J?;6+x;:3W!5K'!-z%Z4Qԧ2I ujimk`]HJnonG-_0mg]N /M}'W5|ѽFšɹfVI:e4=+cu$W=F6 Vēx|'ܜ6;Gw&I6}qgrQv7 CX}soe3E/rV#[W29OiEj1|y.9S7O[ 8;zv"V. Z޽y9)y⒓ nqOI2};:c?L2_&K\%>D"/ߓ/K#9edIV='` kާB/靉|pIWRSѤѾ0.CԲ"b٢`S!}|9!T|2_S#^u jʫhOTg_pgCcTxm^w!;!&AцxwTRKp=+ݧdYaun8_ӏRaqg"?XWپM6˗-W<6E~8g,}5_ZKr^bjY5gl|v0}o "FmL|z1jJ I(z7ԑ:_{_仵hD:IC[<`g5V0qq2w/mUsw:J!"˜,wTILm|')! h$'V#B0#֭[xJ/rG;㞱7w>X|yF~}g (G0 >g>S5B:V#?gnU0ejrXÓ>-P95P.c#Im1\~}dꣀ?/}o4FHNJSne259mݳttxߙEtAțI~_ACLa ~]?V2q)#GvFVd0 %34&&‚|ѽgo~\8f amx~zh9m/0r$$d&2qȑzjy1-eff)cIښB010iMLS#_&d4&&G7yƛFx ٛ6l0_̵M@f'mԹ`[29Fb? FJ}сܜ%:OU7=CSԘɛ?y ޑxU:|%O>+uռe ,0'V-1>i</yyf:$Kvv q>k_yk&Z3?*b={4NK̠!6ylޒC#dpa c‰$iqs (޼. 21}ʍ*(Ep\ۋEBJ:>ߍw-v@7l.a>X ͛sòxWؙi䔐$齯fTB`孴HE~03Inu7ǴҔTsbmڔ￞]Zv=:㜎(n(A6ͼލȲ5'?Pp}o_I٘J9D2GsfIa$a';Ϧwd$g1:߽,`݃IOJy_SX!\ÉUb[sՏ&)н f=JR'PRrxL# o&/."[ZsA~#)Ν ?Kȳeޕb rB>zN/XUޟY v׬c }9XOEsxlJ.I+ #Ό\#.cW/ wh|lzz4K!&k-WNYґmb=5bYBb̓iy{CJp nTCǷmhR95OqKB?yŷU,:V作Z&✛0ZM'~HyldG4ȸ=#v.goA[?>/2 xt?{Sm>??,e#%!.?/~!0i>}]c4 c8rg9;Cu O;euƉM1"_wp77zYtX ;[_IX@ւD_w# 2Ɛw//[sLV *n-'/bXxb"z&VHD9˗X~}w lIvץwJJ/kv3:WZqHlm5[:uR[$QIAa6><]a p3SY  .6NJ <3|:|#H|UWx0g76RXi:2JLG 3LNǔG$JX&G.2=nhh-m;(?%RC ; HSTDDXPP^l QbA (B/dG !Hyٹ3s-vod%D:溁8؃ї-2< e'6,V ѷ5}~+}A[-k?MM O=BLZ`$ߴ.#rG=c[cV卾{cF&,,| eGP]o%VXvQȾ>s'f:KX.[ O677nnL6Zu}p>;NrIN`KDJ́z3#sd&@?+Ty/h)_8JJ ~W-#;߀`݃8/ؚD/tokk@G%JMqZIkZ&xljNgYѧmS#s~m~[p[cg@#'5#PT6.1EAך=o[WdG2 4=~]';8n;^gw=U#Fٳ^w%qia2"j9=NXQSͷ9aG1y玃^_r !s~yNvĄ;5Bb;MM'!ףM;0n?N9b5:әl7EҮ"LG'Axu< QIU$)[Xt˅:dnIE^1^)z h:CGz]&GZ44k"Ln7ޠ73f&M, ʾenr>%)[ho䌈@Gz'ݮý@*tz!vnƄ]*ڛkH5׍"W-/ɣåB,@1ϻbar.J1lX*dx% 2LpKxq?3,Yw4M.7}Ȳ܋_CD@{o g kfm'uy(T"p=(N>ZT/*/?#SX54#@.IӰ ZҐ~aN: 2D^: BW3 U+e]{{&CU =M9ӰKf:*hY{Y2=ykE&**2_ f v<{_\2c>hNd54o *%$%ȋ 5ovMMPjPU={KfFdIQI9Je!"/eOThvlʶ9(r xKTV_F2鉪(D*NxzzV5(%VZ7 `JN8}j޸qnRʂ eLZj*m۶)qߡCج|Mسg/&#82"3 MT OJrr gb˛f[~w*]((1j}N۾b˖-$''KU"xJ˚r,Ɂzܺy#;cEW1vlj͡kbߵp(lV+iպ-II̷|mɋZv[ mHJHziiifmB|+OM)(tx3Xrb`/'wĒ-?D]ώwpBt9DSP4>r5E=E2kW3r w4Ĺ,V&g{af􊙆wekzށE{fػ'NDfyI*dkP04ޙڞOH_k޿;7삣95MEC#gLZgЩuuHR󏶞`ĞҖǢ$,[^/Cy`͙qW#x/&fU#m6,W9q)s6 [y{+lIcOǗqөn7¨_=yzlΪ-^)}i2apٽr(WCiAxdnDӀ<7NڷlBh@87sF<~i`-Ƴ.=ߧJ@W &oKM%xvSְCqhr+>"B] _[RMOe,9U>sAǼqٓ8PF,oX/lː Z ]_hΗ^/qIZK]<# `O?ۦ(L0c̻}=6s|(FWv`Q>ٛ/3Ku 7C9oE18A̶tioHY{93ray|}~&NWJXmwvfżY8" OTP雿F{b="_M+gmw˻soHd擸l_|w/asveȰ磄YUۚc:9[xî퉬k2O|E+Q~w SJKOGY{ABp"6lP⾎;2z%kDPocΈ՞ڍI[T1:X6"&F0:0ٱJ{>=0*2~r4 n"іW1wpFCߙuΰ`,?Fǧ/̄ܐӆP=Ћ#,,#FcgY:)Lꂿ'M=ÊVg8k󥡿CAspnI]M̝ 3۹#y5&Ĺxq8>N` evR2ĕJVG|Ћ7f $EAgY*gY&žz{8wOh'8a .<5" 4 ]X kfy4T\:o:  uf5a;@I߿LpTRÑ$+m+}` aaa%wG,( #(َu LވɠcGp"@@=BQ*hA҅sVݝ ?8fcגfI%+?$8zfbkCl ( }~ZFK`ڴwsseڴf=B^:_ kٓo̾ޞü\|4 ~:tMp?YCyڼnLa~xNkW{=4O込p+M.x;E3OIZ]1d65=?^|:0s<jM 9j|gʴfdzm$85zcP c_a*SYdM4Gr.%WSc ePLף+n.ϻ s]]¸4ٓ_W:b.U7s>Ø8Qiaa {A8yŗ3'lL>+7D™ _kEag$^uB#?Vi;E?o/uՙ)eJI;u6YxE Ճy=D?=X;SKaM 44۴Lh#!]DzxD! ɺȣ{j0~al(|Bmܙu){"#n<)W>YxXis.3d-iF92rCXr;(؎i4{+?'yA^+0)9ɫMxMsKԚ=UeOоSW*ukcA2#ڷ׾?]njkT";_"NKMm65O)@&y X[E]d2EDᷧsǭXq~YqxAN*Dkr"Ul|>ny{k"oKNB+ΘUt[6H "_~T3_yAZ)DAj'//Tb(R@ן2MDKl6i*:ZCt:4MG닦jY"\+Y!"`4{n4n(\8,F8ʞ={%3#], Îm4oERz/i) "gl۾s5SH||%E+֯[#Ujъ۷~z$C__}|jR[!:㝛 B#3lՌwfWH8{F?^4tdZ۪AAYVOvڶ \-*Y F]љuFvlkbن՚SO[KQ3C۬VZEܫ֪u[ŚIj} P)|ZZZOHHXT+#/-"L*T&b ȗg%[ߚVeEV؟ط $P 浕:j˼xb߂ "_^ul_Nž֢,(Gelh}b)L\vqӪ-n]P}[\yA''uI ^wq*,[ydvΒAb "/"_>8FHɇ ?c?:F myjGgK _, rsk^zN(zd{EK-Os:@ڏcX>{>zū ~ *g?W9ۘRW 5G wRPڎy,r~s? ;ib_|8^eZ(,wekzڧ>l}~,|4Wg "Ҭ87rg) E1|2ly U轈2-iyזg{DGP,AY\of4W "_v*{>Nu0;ܞF/i؋MN<=4%=Ʋ/L7b_=;&loϐwY|'cl겘#9}ŋ~:%r:uO뉿;}9F5 gسj>m5aY&9nVcuY0ICmDBqt㉓_#n}c7⪈}-Y.}r^ד;s$X;>c&p*>M˟r8eg<҅>o0}K*y}3SgjY}M}S ڠX2qNK)1F_$*w=Ss3da mn `!X_:ZQEƍJסC{FYS4 =Т-b^fR? D?8 :5{i ѓ_"&FntsO2ig;=c',^A\HZ{oAj7e?;17b :-}Wy8T(St}xz,Ξؓc9ቾG޾=e=DpľLw1;g# pj6 =|C]:7bRp-ԣtuj1bwr9<9 gm s&?7%d= |o2/7WZH7o '_R-sק|ћ肯AgqAGO5"T"9U+149mT\ ߻2mk f^%Fs'f:KX.[ }[9p2;٧FN8{+g3X{2$} \i o>? ƶqZ_p}Rt1x`NlF3Pudq(1Trٳ^eܣ __zZt7A^ൿR7;NgYnWVi+A<LJ6G?63f7vfoD?ʦ%&ӣH!Z3qC;o O.q h٧XPnuoyV5_fezl]yk8\I3gH+rfdzacthYYZҵ>=̹v8߽dHOK'55UTqwwݍFUs_sjz\|b%qִ|X2eQJMQDz7s?fh{8EpZwu~Ct~#tvcorˋz~CW &[wlk]3ݓviSw_ٴ1g|0t(vX)v㮻8^W:bF+[3gN/ !лڵ+؄39;ǚ_;pl  dʫ}TU#8p *G+~s[f|2O>|/$7g2hpURy%Yx{guZ҆~c?cf͈SQErI\x #[cRCxcR_-V;CGzP']d5MC&~8v z;hfd<¿+|>-m69_ƭ?'skIO\p7^DZE8 ܛahCS(t""l6o%7S\Os,gyA*ĕ>ˎҧ6Z=ͿĻGꙒwTQscW`uVcW9R\,x6cTt"UZ0 D)fK_Gz'ݮý@*tC܌ 3U7א‘}W<GD%p+:45,*Pk|,O{ IJ/(^03ڧR~1͓Wºz3b~eE%xWf*WO[JTRw|ԡ7ՠ1 >ܓ^ͧ6Q'-oR&둅|ÃQ;JU){nH} WƲ{Æ~~AB8~Ȳ츨a^_l];6lvtj/PSB&-\ Y~~eożq w7as0f9ʡ &PVC!)Ԋ0ONQhp}TP_vQ^'w };wv_~kl=qK7Su"p_9]8X&DZv0{>RQT1}|j} ejI&BOp~8g%Jlgn_`#ikDo`!={TC+"gAWl'4 }iҶr| Nx:zcлbxz_r>dfKmQ'-艧!3Ɠe|P3r: kJCoM!MV~z܊c//吷׶y[:|z =@tNtNx9V\Egg3۶o\bb}||jъHmQj͎m[k} eSY7O3;f3)٫Iz SpTvԣίlI9 /atG&6*@Z8RΠ۽ſlIҡXӶh&;&\PKbWQS(+nGq:ו)zEdeeHHHH}dfVRV ʢ۟طp|4d2Ο I} d5Dvu9_,+/ s+Fї>#ig >6Ӻ{KHݾ \8g7:5%enDX[~:ډ{_ ?'X.)j⍋ t7AC;9e&rλA@=E["l8pN\Gf<(F, NW4/0y[Oypi$v}zG݈pڙ+Mz!΢ɯ7p doE^g$MT?ݓYDGCp[_BU:*Ҿt2xecYȉpҗ9J%kR(jM EU2AC]^{ n_H.Qj' Sb _(\eDI?Yjcp׃tB?/o/`.kW*zUpww"`^L]-7_-Ypwԭ[ T%=q<<w}:)ZWEO^#iv=]_f3|=L|;2![EAib˕h*KzdmVVc 䦦Df|.HU55[owi+z@fΝxo~GC*ŗGʳ#; yX;zH: 눋jc%8UOxRaKTkǓ ){]/vl53$W{N>ūk9h0\;]gaN8837g[Ծ㛸caB[HE-,C寳޿;7삣95MEC#gLZgЩuuHR󏶞`ĞҖǢ$L_^`=^K2M@^x筧Xvgn3@ >Fb٨H=йLٞIo6sÛxlҤ;Q[uq-?Pf;h낣 nƏ'sh{9rL1T߮9^ɮ-.G:8157v]!V}).Hv>f;`k ϶uƱM.>ŔeM& Kѿa]kaҭ [}7\g*ɗ6uzaovK^xQ{὆6Ea:AcyG1s}|^kXlܸ}:gȑW몆H?'[HN`ɕ;,,{r4Ia4vɾRª58?ןۜSxq9[bok6'UycYhc|r;? (>-5,yce]2iӓei,wĞK OXߗ}vζMgٙ M*OZnYր ֥ꦃ$-8qz֧kfKCD{Dvsx]?9?7h{v;f8L:gXw0 ~՟lSfBqni s(njMXXE#.y\Idݭ)o˷bVx+Vf?.;= ׫I>ɄKף qQmS~~5+l Qx)s|p'EA1Ҵc h3OLHgtuZ'"6Px.7 :΅F'r.~;h9WI&%ؘNb(o]}iG?l2F%رfo)40AuHNkزsHq/]<'n݉Ȑ h6v-iTCno+ 6Tk q½eoL Mk{77WMZ`=s?Dw.drN3RȂ9k`i, _923\Nn~o Bw7uV^03xz[AAgtkwr㘕Rx..ݻ3Lr:ڐWFE/}~[&~w]Gǘp:Vαw)4VL,i?AzC= L^^gp%luǠو_Giv> (!aW6o&?7:vCݾ`Y׭Sw|r#!GgoȽi>á05~o/o'DVsښx 7L_DL,ٕzvvh?w]^++aeYo^A,hK]*iA^+FpL O^m7n"-=[ކ翊N/+~"$$˚U H[ wrp7iⲱG$#=OH*5xJ۶m$Cj(SL #K/:9@.`b5eQ JH*@MwyM.'M}c@3'ߟ)J7ht犨dkRRUm`;X -K0ȬEDrH}K~ |Påe#S-ø*%UK%\/TzH}Ƌ "h|oPAf3)5tNCTbMUlk?oA*MFvMMP UUٽg/G,D=U)YjU}+Hh2Q4i UH]\㨅#pXoŁoϸѫU,  i5*ھnSϡ)`xлzKF^ٌwn exW63޵UJKY U,AyAADAAAAD^AyAA,AyAADAAAAD^AyAA,AyAADAAAAD^AyAA,AyAADAAAAD^AyAA,AyAAAEägp.1L͵ҲU4{v,t}|pwu~zDAxEѭMevm4nڌFMy[7o#_Bk}7AA* hZڢ[!--ݗx Us94M+?w\/yAԢEyVwZ.b(ev_5Tf?8K/F @/Ry'd(gYr ֬2mLI86%՞{% PJrl̮o˫h%(vd pֺ?zeO{K#-;cIM%+7-*œAH +n;w(3aUs^ rٮ$]b7c`F$\;L߮ڮ*œA\ZEW#nEW537s\, f@ҿX,YSN^}ihUS4=T 1AٱVT4T͎]c.7]aP^aw0}-\,a!&ni(:N唲_,#Rcn"46{.u IMÐnSp0.{6qX4 :܍pڛQ^wAJȗ,vg?!CKŦifnν _Qcrë]}iђ1tYUEb ?XD 8IDAToɌ{jC:-*+2Nz%mb6͆ v͆W5*Ӵgt:N)lj^ޮ6rܕFkiI.l6B' Tʗ|X4V+6 5'kv\dgc00( v;YYYdeeYC;hpϜggM/UU%';\guB23m=LL&L&F%"/ B1T-^ƣ LEQtyJ>.QJ1m{, K6\TUh4bj`{telB PHD bQJ)ARlUNERk KTrh-#(l%2_L2&1l#ew237_fHq@q}6;P8< gaDa)-us tܾ5y""٦i@B6 'v!*N'NgKƓDqя+-- iiip\R"/RH)!hѶz""kw=!ߡ#mz\];CLy5ݼ&Ő'""+|>TWiž .EQ*|>_;.<Yu|7,Ҧ{ke=-be؇m۶Gv{P{g>x=wŐ'"غKx\}xGDDDDD y"""Kl&0QIY]""t=CDDDĐ'"""<ŀ%S?jkP]UbX3p <5j$뚀2䉈bx!?B#~@A@b]8]ODI_'Rr0Og]\WvDD1fX֕!OD# tRdkʐ'")upyh|s<=%dDk xUkJp냟aj#^'x)mcĊ_LN;23n} \躶:|xpYƚYLr㉊mh} y"R޶߄[νK+/wh7j2Lo^ջڦBt?=ܖߗʐ'" |3 +Uwcpg{4\|5NlkD{$[{$T4,d0f_U@BAFF&\`h|[H=?V+7NH_&3r1vPVny4oGss.$=m UvDD yWl]͘\0"nR*2,=sVg0_G!|o0*ⰿGv՛&Hys 8p>\Om!o9}Tr珱x4T~uG6fe޲5~,m5;Êeرknk~u y">v q$+}݊7d.R^:G _4 Qv:X 9EGípg}=xӐHEŸz!;յCTԾ5 ü>;}+SI\" % \qBx7mGҺM?rKyh^ uDD<.yO (m P A2ٵ_FZ=w/O݀c8 ߉^ub[&W >9.Pj̹d-OTbegTm& MA]pw)n ; 8\2p4M*` q5ر"I 6]d"QG!3H4: xqqڀ? ËS79p܋D蘴L\]#C"c lٰChޑxplzWK`5stBh:%gã#9]ODdCP1RQxR\Xy [;Gls7/=eWw&઒_?Mx| }Nw|^{;4BAVsAѿýkq2(6Vb:l:` pF7?k2nwju?;;Klrl9Dx|p=2gŘx CȖNގ!c7pKF308/cg=MK1(*p1,_\/ (N=5N@۝SEKĕ#H9u!^]}8 ?ـʀ?޵ΌD.87wŃŨExqaiX/KMXzNW߃ULBf?Xx'L -ZSH9v}QoMdtb) .-ŅJ[q됵_X:j7hȝi@#f!mH*ڭ 5y"JNbYRUHо'^ y"bS|_!OD4OKN͒ Xǘ2䉈|PUB^HQ Ǻƹ y"xn.ߍ3 @oFZx=zx=b]Pz" X֕1䉈(.zlI>CnbQpW [>Pb8{`WODDd?cˈ)v x""kA9d'a/;gDDDG}l5G0"_ƒVW׳'""At]8zzN2aiy>g'ͳzFa4h@߷Gn2bv?,𼈘1`Qyrc!lx4j͈էX} !0A1AdfAUJq:A 4< 'AT=y7/M26fw6U/L>d!vDDD*:Ζ}NQz=qao{}tg;yf:1C!]uمb !]<Qa/#y5J؛4X#;xe#S."Bwƕfa6%0Y gunR',V\QrȚ/: ~H mqr'tM5XHjb ![Bu]lUX-JVeM\l]X 4dgCkE+g=jY'GȀCH  v;V-D̾hNfÊ4TpA:~儱bHyWܡkZYHA @}1T#k>Ul`r:/͜- ~gw}3c7TLjbAax +|=<<_]0`@?y;w߶uk&Lղa9:jl۪'+,ڳg/<90xF<1.6Jt=K1w4 B=YnTbu"YCq X{}Rq 5 ?g(6ow|Ti>>^y&HH}!r͚rf>/q^#**kֲ}y2ztMq&X,d ¿mbƌ2eKѶuj4xp%˳p 'UIsR5Mϑq t?}z;jB.BBUwڅ-Zp]O\l,>|;nMÏfoЦMk>w<<}`27OfΝ;'s ԍ 3wV#*Qd|׮ZTΎϤK3fLyjA88;8d@g_r˭@1PMv] 54g{Gq9eԫ@11q޹ ɿNFC d-8r` MC ^  z4>~@Ĭ]Bouo0w+6xnZ2T/--KJB@/, =//[r2~V_j< .=@]ӂ]9I9 4\tu5؍Wwnۜ~fyfAD AD AD Ah:y]y" y)QZVʔAxz rr99<"Bw(Jچ^VR1Ǐ{5%G>& b+)NY8tPC1sz_A գ|0?7[q>>bc:,~zr6|)(cD˻>'%)5sG{?1׿!,T-7'bmMfonæ"w\9%p{_8.{0ك/nە$r?@iPxh#D~ktsXdР[?"ǹHU/RAmVZ@9bD/h#lF4?kiܴ#oِ:f?0E1|c:ײ'hF?3J!7%N $M iAIA $E;-:rr K0Fd'SW9`g-CuXOqNz#Z ӗkQ*J2T^_YXXȚU+p)n;] ɂq_,ei4>/s {,^_0 ~-Nx\q߼ |ƅ$<-+fqd'|ԧ ʒp?(m]fqZrY7rt3PԯcJkҴwm˟U# `M'HNC qYҥ٩W_|rBҾ^GMC3Tp{5wGiTe:xܸ>KFX8_'븉>j')=vW`Q阢C9zusxۺ[ s|,57~lAV^J֓_BoLAu^қ&MBz_L@WiIcm/7޺_yp<:?M9 FFzjı } CYiJe{qs P%@}ǯ>z*7o+J8^qAN#D AD AD AD AD AD AD AD AD AD A(M'm3f鴕C\@Ha"F۬u;f]%Gq|,J=oI[)?1 k؊r­dkEGt:)O8y'I<_u[_(:_/s$͹Vh fEyvơwŧY4Sp O[I!TT_9kQʉ+$i%m 4MMw݆>Xbb iាoLgJ3)!}B: ҂#['Pl%_Dpq~X)8KlWʲS߳u_~(Q^gqhOl,txaKJrΆ]I!޽S~ k+85t~+ c=q$|._?D_rI L`Gh_^@ w9>b5AFcBcF`"'BTWPth3q7Hș|ɑ9w &_Y]y;s11E`aLY@4t٩$%Ϫ2^KlW,vťOQAqv nk R3kK\WvnNةv9 ,¿ b'5?>hu@]Þ'=V:FBp7yt UXZݳ)(SE:{_\VV 5ʉ)Ek\\/@ٚ/l! :CFp\[Zc@3L]J?(NݍotGX_'t4%LMJ>MK\_#n-&k_pSF޶_ݶ_!ɵV=@LH߻wog]zO6o,$;hM7f9wL #qFqS'qغuk|||h׮g}3lҥ4FEEy<Η^zk={z<ιs?\{o߾ΰ+Z_[n^y RSS=7el69-[Vm#G믿x Fֆ1! w߿DʗƖ-[ ._~wutM\uUnaTTy?_53gty睜{7Շ=7f=z8ǧ&VfYRP夹9:D4;LYgE~*SsAh@bjSZY7ܶ BΝ&O\ /p߿%ss>ckfF77-4,?W>U=_'ۮsPf> :+O+ dn* ݶ#""ڵ[XΝt :tdz| Vɶs%) ) ) ) ) ) ) ) )pUمSCuE4fwI9٤H P+ƏFǭ^Abum躎MԺngut]Ǡi^6Kn|(J26/_S}&T2o|Wxqb F@ @ @ @ @ @ @ @ @ @ ),,䡇b׮]l߾~2^4͛73bF\F͛%:֭[y饗xٹs'崌f͚EbbbzL͛9xܺ˗/g۶mѣVz=A-j}NJJs;::kߜ'ԩS۸q#˖-sns9օx /￟/%Kx$/w=Ûo1qٳ'eD Oqqqߟ9s8îJj}ɓ'{<۬]ֹ=p: ^H~~[oW˯^t)?Sѣi׮cƌcǎ9#xV+>6{7xM=˗;ü|اf͚ѽ{w=zЬYZ_xeXU1tPE w^)z^u&o1A֭ٲe +V`jJTѳgOz6}tTǒ%Kk:,XOdH3)   ) ) ) ) ) ) ) ) )p:UمSC6fwI9٤H P+ƏFǭ^Abum躎MԺngut]Ǡi^6Kn|(J26/_S}&T2o|W=5d p=bmg<0q4S_Ep|T7y5/kvDy3l5hG;-. 5K`}w${S4f\G`1Bx-8r<:r=pNNqg!l6 Lp ;o{f.e3u yc276:s lݝ$]L=zwm o ~\.= XGr IDAT㞝<2ɺLFK #snĂs.p)s4_l6W[b2CK_ x._LFlLdi|5=IhW=@H-c¸zn)uy֙9\&. 42|]׹rL_&9-omD4~8-"ٺ;Gk6{ؾ7!qVyҍͦ[X.?6_;I@ B`qf=%ΰ൙Kݎ[|+,Z6w~\-r֊39|L2[\lAhv?Q7vO%łGmؕn2ƗNaqQXDGz ݖ/޺ZW^З}+&E|t^T7E""E^khUN],Z^xϳ`yQf̘3H}6m`ƍ̞=ۣC^^Gz׏7ŷ[˹Otv7ql=lGR3a?n=vw>K?}"C=v}ҫW/:vm׮裏\,O^qfee㏻=coBBBHKK#00Уyݷo_zUkgG{ᮗӠk3/9X7v>FlͲO hA\5Ls=F*iĂ?y?fguI3®J3i>/vvn!}`#bA BWx;ݭu$έ4d{3C9UȄt+- 4.XѣSuN,>tl'ڟJxhadf0~s iFXh;p}:Nm2Ͼn1UDb] f0M]jERNv6)9s Ԋ㯫qW.gttcm6t]1hW$8$CM<[n=(̾N.vyy̛7 u1S>}rY=mHd go'{<{yђ xO@$ 4 " " " " " " " "B/j{mź )4q,]DRG+_MFh47oXdbOak!<]J -kde:XOp0i`հ4'cmD@;_H+pNS6 eY_`Q\&ho2'uwD`OkĽg@qΝ(I^S:ϥxޟ:oi>ׄ0xv0|_fcGNOk>j3NJ:wDd(]8i-?|F]cf:IqA5ESɰ&@?6mx6lA81pSP%!4P=8~dA:gky3awO>5`EՊVy 0Txb}`v©sw{FrF%=a/s.0,T@wXl.Pa也ov5;VW43*1 ""{<{ a7 ͵ /ŋy\1&%퀌igQy[NzT 7u.݌:?:7x˚y>?x1oZ]Ze0G^TPy&1##][ hO4@8ijSz^ F<$ϧL/|_ey,FU8H-UUE@1V ]z~a:,=T+7;=kQPM+ZÃfM&ȭۛCG ,ˆ^4_;zY݌z.Fx ?;S0 R/q:촵Tf?Ãj۟ EpCIp M/M*dY바e.XH_*j<^uCq? Ss1]5ژaHl-A }uAˉLb- CiwTn(ngԪF=ܿeu @+34@^2?d_p44+l*Prf%`-*aQ?}Tvr^E⻋\Ԗm`ի.~7:< W5. 4V% Z lvm10]dzh+\b__tB1!FְTLo ȣE%ۋTO{KDe9dE +$YZ Ɔ[iFܯī׷PLEB~,VQg۬ K<u15P^G482Jv`vSGJ h8'f$CDKe!o/gUϲ>bWc^l6m|T pF,p}^ Wy-ȁu̱vj9%x+ߓE' ?5n WK}ʬ*B^+^E{?0/$@ׯG^aA06r ɞ::!m#(<`XSy׆C_(-wOZe~73 O30>~CIX*BO1Lã3db˹t0X:c1ԭR"k | Gn~$+>"m.燨0*HcT#Z9nMON]goRkR ?r 41 B']wUZox~Q +摯CTUczþ:o'/fVB9ƙOF+(otxW׳Ma,[w޲\C1Ua6N@T;Qԗ(WF6,Ջyd%/5USZds_̵ь(بɲZ%iREie7KԹpr&.z, =rMJ|:y1+7 P3>gX=VV3-j4O3*M4@8鲱Pc/Z{רޯ{>T]z;_z H."]yAϧdgNFG+ÐWEbQ;RϧT*zGFQ~ד8Y O[ S?mgc=8ǃW4CӢ]3WmDTKѣG`` ]|+Պl߶admͰ0’lXq)(7(×O7+HVnTc;,%X{=Tc5yDŽ_7$q:)3ŵC:=6 7]aPh{zj?3(_4n&ѥhKY.6r3*{1ʓӿct#-|3wx̛7=yd:|6X>`Y9?ι9k+ݪ@J>Au9A18BQ%0X gB%֩ȳTGR,ϗ (0IW@0IY=^u.xQ C( qcP-FƇ =^ԟ!r}f>Aő*)q O'Bdceƹ-)OPU!8[Rsly\Y^=_%} TO J >a::=jw.m3'n--TL1K"?8:F@P;ȶ%c33&4M|< bpGݣj'K/ԯ9Lsj j8GBt}]SsUOj񛲾v#rF9!- nq c)+V@PhkWCT5"tAS"*$ >&-GT9186sD3)1uij$Ӿߨ{]`pg}0vjR5>_gjVG R☴H)=3VCYD~fjx\UDi r|%E*W-(|V2AU;ܽze-Q

    wD 5-F&D,$}g>M5Ve)K # u+h agB0: lUVTA١4,1oضg4%j >厭pA嶍.qUe\AhjQe99~z#˥Fj,n+ j*dU8W(׹UjcƨFm j!u.U|&ok,_qU!ʭ.On׺X}@&ٻc:#ot]eD*q,_&(p15nS e9K@Wg2ǁ.ty` IDAT:Ք0br;*xUWCMS 7MS{&{``ULF5*YtSX\npgEַpg ~m@f9:D1Va/[g*2ǀVα~~ܵɹ2 kUVN59rN|T;ǒ,U †1 @K|\._ 45dpeo-CS0pTw rh{3]kjH]ՐeGv]vՔ'*sv;JdL7Ko1`A^rnTl'|+}X6y5~2q#SxO0j"48U{y.0ƼIJwB@2be{H1}[.u󵲩Y1VTwxݲ|i^ 739fb MKa_A)<%Q5z. +bR9[jtβ|ft?kYgמgq_Ƅ)t:(4 leM6 |xMF]d7[(6,VR/$2J _ChOKX5歍Lo$H3ɳ;bwPj_XJIY‡K2$2UӮ _+)~j4[IW>61:zQgOPI\fˮU:f\@ ©1 r\s MȲpݍHXr0$]M1?EFOVrd`j\uPUuc~' Y^Pt /`CB-2EAw# \ߜ ]5`ڶp&s {w( ǦIsHAh\hPIWxB˗PD[j}ΨF`aڒi> oDÊ(2+RI;(u+"=Êȴ]::G RZid1fA"a KHBޜ- !~˹=K7{V6rE&M?CŨ64ЩWY:,g ذ;[ͥ>pQөNvJ WWgq"A7NQ|4ɁAU υ8HA3gD1$V( HtdQ)()W=]T82g/Dt_4HA8tp>4KQ9ݭ8Rx]hOIciABlAWZnm&|!>]3mPluI+ѫ *<{3;f}9 z\#Ya}9Zhp:a#k?0~ălPVOTV[+Jٓ>в56NxKŹ؛I}l`],*ݳMC :,Oۏ':ּz>ʵ}gty|~)G5ާ, "6 v~<俊5qs+*6pܓ<>AOkdܓ˔s~oX;sOMg0ȼ"0@7S{nZ&S O-4yuIzWU{8Lq զy[=ϫ>b0x6&DtS(mz>Hgt8Y3-f^HCGM@7=jj:y3ڃl:u9 šcD{w{lC)v] L9lN?8*7SR,CU㵚t {2Fis6dSԤ(h rJV*,m&r((!oxOnmiLC\9A"lqXm}Dž_˅=νF_V]sSS }jǂ]8٣|\# CԽ837_4?wb O,J'>

    =ߕm/p $j]ޓEA:AA KV'Pl>k n;]*\mU>k nll n \uE?VK?qu+ Y|gU-_?g֟ղs_{S}-rAN5'㒂)'r g'J >"̉&0,˿=#&0$$wI%:/E!H=P4޹H#_s!Jƀ:|䑕fWR͋|8477s &N={(--%..SNaZ5jn]v1c t:$&&2dV+L:c#FFSS'Otyo >4 ~=@ͺqn{·+Wr=Ǿ}^z 3hƌ޽n#KbE())!;;[AA?8w_7y 3yeyůOJ3t9L5SC!oLb&$bB"LdzT뚲Ѯp ɠ0 z„gi( ~/Eh?^[OMM{7j t:M9[@2e7FZZZx7W$T2.e*\C a-OrK{#r>=Ld: j?7g^lgÝ1k*ήf`mQA 3BL_c(6ӷ\stX \8cOsUXˎ߰Ag۪cnvٲQMMMe)=Qf@ ӗu4G9C=yx Govws/֭[{] ˭rFU5;k0cgmO~&) 8a좦_ W?QboLZ+Z(\RCy~,PU$Kj&hjsRww6]a`y0==?6&њ1Ae#˳c/9f>Z3loύc. 02 5d]Ԯ)~PBn0$Y /AA U-t{79f&Ar&r bol%kaeЌG:ȸǂg\Xsw7]a` ͓RDQP-Ƞ ٴȯ ?̦UEA$VAkb%MEOsvpfPB+^R 0=l?™Vr DDYⰶ {. UU9x)L9g%`ڽKϺ68 tD$Ů@D @ @ @ @ @ @ @ @ pr)vSD A|ټy3sayGUj$UߎỸt1_ @vvvvA>ʃ|h#Jz#orDew1?G},*iiiܹYfqŨnر,XCEArz}>СC㴶`Tv?=C=^TTđ#Gۨk{),,[n i7>>>~z.]Jyy9˖-: },_g"ۨڍd6EewܸqrMKK1H-  ؂ \#{ >'ӕNzeeKă x~P{~~>߿LBlAoHIIާĪ]#Wl W=/GyЕ2Q[ naXZﲮÓIҘ|lkK3AJIIXNt[N< {e{ \ҔT \#DjPD$E8 H.>ɻ!CJ u^p.txC||=H USwg]l . 7+'D A\ե.;|Bɛ 'Z|;>R#x]! WX(C9n"B Cdz}]:#<7z Aq@O' 9x } A,EIDAT>#0;;} ![z+A dO݀B H7?Av).Hx P^ %9*=$0F%@ AқhJO[Ð!P;MՂ W {Ɏ԰v߁x^ AP;P$CBpB xޥGAҫ8bw}X PD ‚ W")-R,EA7OQV /{AX{`س\)(&]4b&(µ.WDD,ADQAAp\+IENDB`mapper-0.8.1.1/doc/manual/pages/images/symbol_dock_widget_menu.png000066400000000000000000001257771325266516600251720ustar00rootroot00000000000000PNG  IHDR-,>bKGDtEXtCommentCreated with GIMPW IDATxy|Tי=I&+AJETT@omu_7jQ^F;@!@M {&dr&103뜜̼u3s@!B!8)B!ĉJ!QU!Q J)BL9rQBq`2sBiȪz8_h*=BǪsa 3DףB!NĐ=o* Pvu_ X!'kn8dlbvXeB!gv sރ՛ʰ?[C!ďэ7/H>7[U鯷:Pk2[B3n;xS>.0Xuka^~!?b!ByD݇210Ԟk=XB9+=x="z=b#*sn` |_1[XzB!~ B֮@ؾz{:e{k׮ s9Nll,wu'ի}z_b6裏7o3f̠]v;qDq8|7n杔1`g}6~B6ly睇IOcӦMB(X,~aN=ԁ;=ۣ%T7Q_Mu}5ENѳZkn>_xTU;zbdl٢v96O> /d̙|ǸnV\Koq38k6l@II ?~ f'5\џ0wB"3<øqa >a3:$w׷DZOsJi˻VfcҥL2;쥥\~}6//ѣGl2>#VX#կ~?cΜ9{Ĉ<\y啜viZ/ .ѣGkm6fϞѲb z=r &MbAeV^^g„ ڞZ w'mtMRgHk8~?>鎬q؈+͝;|&O 8s饗p'N=TV++W`ذa pƇ~x믵O>ڵkyǹ뉋Ge̝;_Wh"nfHLLdڵh;BS3Љj3+hx^:;;x=TVRUrx$fouk=wUWxھ{mmm,_Yf7x^ :bbb<:ƍ#!!3g'|g}V+3qDVXAQQ_|1=vpEQxꩧ%?Pz"c?cK  St:t:]Љ8?z*s{B!Nl/< 7޼nh;xkvpn\9뺯]?kT ZmݺEQxؿ?rlB!]\}Ͽ~ !pCqFY B!z !B!B!*B W!BU!pB!B!*BHxK^iY!Gʕ+y׵Ǐg޼yx^>3B4uΜ9(v 3fPXX(-/9?//^x!!~ٲeB!$\ gʔ)N㏹뮻ػw/iiiM'77W_}U{n|B5'1WtK.I&rw>h} 0n8 Bz_~%F1JyǙ@AEE_v gg7x#:f2e 7p~1_9999q=3frJeB סQ|0=0***:tIOOp6;).-7\YYYx^Ylgy&~wLWβed BqȎڰp p|>~?>ޭϏn3eMƼyXp!-bڵ/AQ|>ŋǏ'&&DVV?<;|暠x Fl&99z  d"--_W}ӧOGQE.^TT̙3Zp뭷![BHT5? hQ@ ~/ꫯ2u!f˖-۷/ >sn6xgT?ϭs=FƎ ٳ5kf{k'2n8 dffRWWԩS?fr-<3u !O=\c}%p }~@a67n;w<ׅqEpbZϢE^￟oiӦiLҥK/5--\SO=;#lŋK !I |~~*>ۍ=׮.͛-[TTDcc#ӧOsK/Ą  55vNun޼:v;}GssBS蹪z@ гwq{@N9ynl٢BF#.!ףX,C-Ázz뭷x'8K.Ѧ1˗fdBz?ho`t0OssvQ[vءkע=z}7==ʕ+5kVд7wܡ=gZvbZ>|lB!WՏrvk߇FU9Xvpmnn?i_Yz5?v2әgIrr2_=ar83~rUW* AvZz)L&7p<6kᩧ{-]wEDDO<O?4vo g,Ywމb!!!T_k袋HLLdɒ%_pws-vIII-S!NbJ _`?cMZ?)M- X.<<(;L .ޣXZ]!D{o^p7v7|o*N/:V=j=׾&BCC!B!~lI^p 3a!zB!I'bh.^ŋK !8t B W!BU!pB!B!*BH !BU!pB!$\O==B ף?hxPUU!?p AQ, W믿>裏D!ď'\YYY[O?￟Z<̠ !n3eMƼyXp!-bڵZ"fΜj%%%[o~UU'xTl6'Ohz]VX)b!''^xA$!/\UUvQ<QQQ1uTF#1=o&sOuG{O?%;;3gp8mٲ6o 5k={6\pVb޼ytM|ᇲ5 !\? ҳ”@U *g+o#{=nV1c0}Q."?0e֮]o^*[BI)Cl~T{laGea۵^<ݻioo箻b…yfNg0#//O&!&\H@UQ~PSwyA"##Py[,f͚Ž۫7-Bp/innk //w}|(ӱll߾n]݆شizقB=nwtoz0k慷zZp˖-ѣGS\\̚5kYt)?0W]u+|QWWڵk>b֭uY$$$? I `ΝXO?ʳ>K}}=aaa\QFO<͛X,TTTb % !8}>Uq>8x׆0!@ $>ibQl6iiiUW]~׽ܹSKTݮZ,5;;[}_:fu޼yj P?٪dRcccs=W k^~e5>>^X,jnnZ\\XB닌ꫯf\q\;M6{p饗pyi6mvʔ),]/qח_~ɚ5k?~<SPPkBcs}_c2[ %2&P[|^BB#L`4vY(//_p0an+Ok_z%&L@DDt:,yfX,X,N @YYl%B!Ow\ΨhJqo3+W2gN|7nHJJ wqjwrԼ;\~U!8"lMz{!v̝֭;W;)+@s9p3c jyVZŲeؽ{7r UUU,Y7w٩Nhh(|#??ӧ3||IVZ;7IOOgܹ|v^~el"[Bc=1#inX(rh f0x{nɓxYh'))W^y+={dN, j=ק~xO>w}{'| ƕW^W\![BCq_9أU0}1[ğ j'!]"k\! ƛ mo. x OChm岙H !I8?jg !??\%XBH !BU!pB!~ZBqR2eʉzB !B!B!*B W!qT~[Ʀ!fɚB!:ƦF SJU!P|>_UU`0B!z(^/^?[QY7͜:ire=K,7dݘL&FW_u]'-S~  IDAT(FшLxX,l];;;9xؼy3zB!NHGlaC{{;^wrj<裬[˗3peqe#pj{91ok|>3g$55Jxx8gϦ*E_wvv6 ,-D! WEQvZ߫ʌ3 -ϳ`.r/_ԩSYb~)\r }/"L0RcʕZ?_!7\ !!!F-ln={VukSScĉ-Zč7f̘s=_]wTvɜ{\y`0d.R>Sǽn:L&g}l!B!_ {<Bjt"**jaiz2vӧ=?}t GFFrgpWt:Yz5k֬3j"\V+F󤺆kkkՆ ~/622hnzLp^b ''|z-TU壏>bƌu!8]=C=koo?_|W}1bZ*իW3rH|o!//O{ngժU8~!:G=b`6{mWϱl?j*9,X]}ݴs};(N?tVXw/_T׋/^'44_|Fn6me]ƝwW_ɓIKKC! WNG ttv(l۾ٌ0`ذa/>`ɒ%z233孷J g&;;K2{e~Guu5yyy[̠7|3wy'--C! פA( FɄ$1>ee̺pրuGDDxb/^on-ڗ뮻sXfԨQDFFrWȖ!k_TUEף*:N VEQp8iiiaӈ9+jXBq5;+;Q?v!!!}[/Xϟ/$ ׸xO?b }K/-[BF'@!pB!$\B W!BH !B!B!$\Bp,+7z\1WƅBHUFsSv{x!|2""  c\i!}~whiiaQx=ޠ2Fw.:nr$22R!'f.8:;())̨Ǝ}v aȑJgg'lڴI&aZBõu4㨨(KOFF{졶׋l&--|>w鈴OHHǏd2t:YfVqpz %33ZBp5LZzXuzECtt4:: fQRR$''DGGv2fcZp`LV+l۶ɓ'c6eBÆkWpz<:;;n;xPUߏ墼UU:fjٿ?l6<fhE!< &LP:݁"***xE !õ+@;;;zz:N 4NPzڊlfA3 >D6l@uu5YYY Z,z=2,턦gWTl၄QYYIee뮩`0`2!߮pfv'55{뉌DUU\.)))!8>=׾ê0 n ޽{蠲t:& @ss3uuu|>L& Ztذa8NmYõpt8z&*\??5l0x<:\Q8yyyIOO'==i#Gdȑ}NS#F0bĈ^RRR$LBj0XA|>,f3^'' !pIUUFɣ^qXBL OK !pIQN8I֦Bd!B!BSr\ׯ_/kQ!Ieʔ)'vB!N62,,BH !B!B!$\B W!BUo>B.[ss_'7PXXHss l߾w-餴^骪R^^!~ZWWǺuhoortHuTWW@MMkMM ^wB6lڵkꫯ\.uuuzTkiiȾ}iٳoTUE"$vm[g_u̼2t:\bn3rHE9?`j(++r)[QBQQDDD0'ر8l6I~- V r1~9r$6lyq !>~^Ԗ-[ddd`4vKdd$'N?ƚ1ͤ8RRRHII9󩬬$>>~#Jtt4Bpݶ^ύ?vKlll6l ,,ѣGX~=cƌ;2e N[rg`6֬Y@TTƍ`ىh$338_PgHH7j@rr2|7vF-**SO=***p\f3iii477؈#<<BBB:^/[lEQHHH ==?UUUTUUv !--aÆ ](-- HBBiiivk5$$:|>w鈴OHHǏd2PQQJffԄh$66 mb6-// RSS|PWWn';;Ţ@qq1X,ri˩( 111dee`mصL`뫥bZ[[t$$${6:سg\0l0vE քp= !\CrrF12'gH_:z}pEEEQ__cRU-\N'~l6-8CJJ xޠ!î^(8v駟CMaa! 6 ϧ#::ڠNS 鶶6<|cĈJqq1{aرA}NJJ ݻKݵJxx8 ر^Ottm֭[%--MG  333hua[RR$''DGG0eeeTVVjm۶1ydm;Oxx8cǎg-kDvܾ};nt:0i$Em۶OVV~םg˖- FE m۶q)h;'aO66oLLL cƌ&}Qp܎;X,8zDD@)Y8ڟ/P]EEܵ` 77"+HLL>rZԤ}hE=VU{݅j!Η_~ICC A<!CUU---x ***hٺl6ꈊv:;;{Yj6IOO\Q`0PZZJRRRPMIIɐµPC5%%%$''kkO٬-c TTTNbbc\~=uuu}euu5cƌBvS^^Nvvh4-餩SO=U 6mFEEAp8xf@UU1L|^[dd$Aޖ{'҆Y%%%[IHH`0r}%g]x<֭[*>}HmU'MzMfޮ +EKK AhߡmsyW m*==ZZZHJJ"66Vm+^?!%ď6\gϹװtKMM DDDKTT DGGrHHHpРS(}֕Kss3UUUl޼dQxjjj̤Wt_t+]Z)""|Gee%L0Aza;:ժ+;60aBv@u ԆCY_֙=2%%hEeefm#!$\!=h'Ä'Rܹ}^'&&:n' ^߿ zC&jDDDPQQAII  GE!,, `555i_Q@/4z}/Qǡ0 >D6l@uu5zフ֢N6j$]CnC466+,,<mkk:l_u@fx_%Jvv6qqqlڴFbbb\!zྴQQQATTfGuu5h'MĠ騨`ĈZnڴ UU5p͆&>>4JKKtڐ^?5:;;ihh ,, UUimmEk'栳,e?~t:TTT}S\\hNhz}^O]]!322شik't?:555 L&6i2PTݫsUUq\Oi$%%Dee%[n%55Lgg'Vݎ(iXXȁ(DEEŮ]pl6^/jClܸm۶\q8l۶4턦kÞ#4{ Gz=`۵mPE1fN`u-D =?8TUǃ`f1vXM鈏Z #$$шjaÆt:Ég ***`0hg ǃr( rRRR!..l6&;wj?ԣINNFUU(++j2z`f޽}رc3gF# 8CL]]>DBBt:^f"<y߿̛ݻwvmܬ]5Dގwx]WGkhg!-N"#"0[\SN,F:KT}wl6FE F\BL׉@UU =PwttPYYmd2ߜu:ޮѺ.Pv~v?Zmu~HEUU),,$44>_( 8NƎ+e{)ܾSvr_5O9 o}z>))#F`Z|wjjZܬ]4""2֭[?~Q=~oNDD:ޞNv?ZC;vgr?~u9 6LDD|ʋ;\^F)3dffPoZY xO9291cr{ijjرc6tf3Ft:9dCioo񐑑AHHxEee%A>::J WqrAKS=%&$he%gfCׯܴi֢^za?~|N@0[PUKK Nǘ1cQRRB]]vlm/R<zx222B{˖-'>h}ٵk~1bDii)mmmFHKKzq\~mJEEGhh(n;||7sgqfye/++M  55eسg(BDDyyyJffԄh$66 PH }X,j݇= [lMBBv}8bZ[[t$$$usr222HMMK4RVV`0Ljj*Æ c׮] 'o~n-i<5רx!&&uuu|>233֒İa|ڐq(--I&( 6lF#mmmcXHNN!<<@ Vo_F##GQVVi(uVbccIKK{Wt\2IKK#<<6탱`_}?~&iHЀh$;;UU ;v`XtO+++LV+l۶ɓ'z^{;v,GxW8tlFhh(. Ţp}(msbu׵#`IIIx~wj[ζ66oLLL{QE['nB]m'""@ 7\ ɵ_>nEJJ2QQ X?znԩGeoө5A=ISSzzMhoofJhhFbheZo;2Zl6;뉍P탮_RRBrr:@LLLЎEee%ÇzQQQ444{!P=$$dYMLLLh tmn~zڪKٹcƌѶSMyy9ٴRWWGnnv]ײu롶{hhmPםlNhFQ赣2onnn;g4xPUH-pZv- WJ IDATٌXHCc#-ͼM =cBIkh `4[[[!BfDyy9mmmZXql ڈ2QQQڰ];NGptJJJhii!))XEцKJJ(-- ~fúuvDTUt:RʚmM{nš6h.iԕMx_8J6m ??鈋-7NDP(x4-;NܽdYY033il^״_sʷ/̸5#Mr;ZJ*@ @qq1J%>&TMI^$WP(4}}}s8B<HMM@ 1܋9B!cr -- 7n܀D"l ahh\D.2 RQM_4Vw ?G~~><&&&044t@9| ƫ_ gftV+zz2JE01yafj AZ:d2Xûc=zO©GqrrrT*afT; P,D"ٯIbb"_e``CCC!33YYY(((N>LDAAB! ===dgg#==ܚYQ__HO~==zFTx! annwwwn O$999HOOGQQѲeKnɓ'())c``D,ѣG\pR؋IvMJKK ;;;>m^NMo"-- (++#lddd@ldff"// [7Jx)rrr`kk >SSSÇBQQ V333TTT ##>ӧOaddD~צ/j_ ƫt`0 \ `Ε`0 \ `0`0 s `0`0 9W`0εwae0 sͅRĊ+7n8qke˖aӦMʽ{0sL}v|w沉q0Z nnnXjI Ξ=ӧO7 ÇMsA$&&jo(Xv-JJJ46mZjBd=<~hK_p׮]Sw6V΁`dd7oZx`εwGtGV}4h,--!JѺuk3YDC@`` vlooWWWMСC>>8uꔊ, 2dx7TVuuu 'O \Dm";;[Efvv6|>mƥEFF\ڐ!C/ծ;|g\Դ#G"mcĀ!==p t 2 V[&CJJ, ?JBRÇǖ-[ DkkkZA6b0ɝRBǏʕ+aii [[[ehb{AϞ=~<$$YYYoy!$$W^U;zIcذa~wL<VVV0``8q,Ywww$''El۶ ;w899) ˖-51c L< cII abbeeeXh~m?^oPPPe˖[ny&ttv'|u/;n݊}Ν;AII :w lٲR1~:Wعs'͑333r-,,鉳gbر]x}|>Шqaʔ)@{{{̟? СCq=<-gȐ!ppp@TT|EBM4w9ԩS1h `ڴi8v,Y'''DFFbܹѣ:v쨕M4Ķ FUm* @@u"={F}p[JJ*͚5!DEEф ƍܱеk׸ .~:M6<<<={`2C!!!DDt@u꒚J(66AmXnpڵI&iܯ_|A">|ȥ]zO?DDDG!~:ܹsɉHOO֭[///'ZyYb1eddԩΝ; UTT/UVV֫ٳgJMLLh֬YDDHܹsuND@ .#LJƌϧm۶Gcַ>͙3vP~z-۷/?a#"R@wO4 ק>LkƶjL`IF_m8UQ_kЗ8-$( n322;#իWCTj-_~>v؁ xyyaԩ8@Ν!!ѵkWEPG\\b\sqS%55Æ -j*4JŋѦMXYYqi:tI~ڵs1gN(,,Tу3֭栠 !!!999u-Z_pQǧI6hjj SS&GwHΒ%K_{طo@3l0?~K;y$zꥱNL ʪ)6Xb0+RŹ* 8:D۶msD0`"##k.lܸN>ׯsۭ[VX,FhhJ7orS"->>ĉv!// ;9[[[#55F߾}h"[ {===$&&bȐ!D.]1cGСCUVXv-QPP777iӆm6wUi5N:xxzzٯɔ)SyfaѢE(++… ѱcG;*y=-["11˖-CF`Xx1R)PQQ$̘10qDlذ;w.{:t@ 1b$%%aĈ\.9RRRPTTsss};bl޼Bk׮.\333t]]t @=>޽{7FKΚ5kЩS'HRL]S~M777#44Ya}6m? q4[@Ӗ[Ҫ/B˖-5k֨'͛4x`!]]]255ݻӑ#G<4a"PH4w\`rvvҢLJ$ ݺu;^RRB3f dooOG%"gϒ;ꒃׯSnH&S.]ϟ'=zܹs~m#CCC ,A~~~diiIdnnNSNOryΝKZJ%YI(уJ%4`'PHmڴݺu+YZZX,6mPRR7LLLHGGiTVV۴i h"|222W^$̌,X6ȩ,TXXH~~~G"ZnMaaajF?#tm.M]@ƍI$à &-u`M4j9Wpm\1qz=tyy9v؁@(J$&B\\ttt ___HRv) 0apEטٳgj% e0iUm5/P rUPVEߗ2-p<}ޘ>}:{]_۷ >)5$99eee8wk'%%a͚58x ***؞={nݺO7O1zfυ pڵW݃\.Ƿ~ 8"Rwmlذ%%%Z$9RRR4vڴihժ k;89={4ٹgt*=^m7a0j`cӦM"vvvpttDee忶\]]ѢEf5tPzKRr?td9puu;hxd9~R>9GJBp 4IvڅĈ#?-mnngϾVu9s&fΜl0;W%J ݻwqʕ;j"YSLñz:y>}{FFF߿3x#mV%.\2x:Sx*遁J066ȑ#zuI'|cccH$#!!;6o~x\kLL c\,b޼ypttÇ1zhw!!!·~ ===̛7!!!z*|>0m4̘1=z@^^7nLDzz:BCC1e;(((P=zٳpvv>} (,,>z~:AѣG ;w.W_5X ,\~zkrk<Ipp0?@ff&@[bر6lkTVV⣏>q!R[={6dav?>|}}CB&_~Zhmjk̟?ϟΝ;annDN<`̞=6lɓ'  x%ѣGɓXb,--atBee%bbb0e 0YYYXxF;b04UNW>9밍E<{;*((ඔT5kQC۷/=rϟ';_NR?~?y$[n iÆ DDtA@DD@W@j׮)J./$ ""3fBBBÃۯ't5 ]~ĄNNpBW:| uҩS'1b=~tttȈh˖-$ٳgTZZJhDDsN@*29B(--K2e ب&9#" 't]"";w.9995;qK.Qvv6} IDAT:ﹴ+WP(kh$(++={FFFFu8((ݵMM:fkKuٓUC!!!j˰ŋ"""%%%daaA'Nڎ6'sL0 h u?ԯU_Çia%APp!y^JRk9CLL >C.mҤI_y?tUxxzzD벷l///ō~Tŋٳ'wGիW5[n}(Ν;C,C,s:wWcmڵk"88#FQ Ƃpqxyy!((+ѣݻ7Bal YYYMfuss<|/_׮]ammҜ|8N>wB@ee굱ӧOv-C$=qrTP.X7Megg8|0郾}"** Ϟ=Cll,7%MٚH$ 5U&;Ѷ9r$~"##ѽ{w!TVWW}$Zeee3]Gnݐc/ nݺjTjvMDDD2lώW\ U**燂z_P$wav6bl߾DoooÇ5FBBBM\rvvv5kڶmΝ;C*wՕqǏC Kc=> n@WWWM__dU+?V{k׮_|0EpwwǏ?C_~ݻ7Ұj*TVV"$$Dc4-%!/ɓÇȑ# ///(J\r2Ls#F`ҥW^X|9 }}}?>FF=^INC]}V~Q> <&L@iiƋPXXÇC(ԩSXj>CXXXx=k,K.Fdd$~gxmjҡ6[͞={ annq5/ZoBBB{AOO2d,--댧a۽{Vo.]qɓ'W_}b,ZyFlM[nPZJJ -[֬YC7nܨWΠASJ277aÆqe.YlmmIWW̨SNtq7YYY1 B&̬#_TLvvv$HOOZjEv?m۶5m߾]mcbbcǎ\ .T ";w.9;;>D"ԩ;vN@|ӄ ʊB!ܹsU~uVjժ\.+VQFFՋD"т ҥKNBd2uܙN8233IGGϟϥ@CnJ$M66ISM}rԕ1Hvkr9}U{Kڵ#%'''ڸqJڵkёtuuݝ٣rENlMƍG&&&C4yd*++GGGI$200 ///`㩹m[]`aa!M0LMMI,S׮]mm;b&hhr`yU?  ۸>cizrرP(HL8*S qssC_0e<~8vB 7}ISP j+1R/i\ӧ>7Ў˗/իHLLd5… xX`z+/ٝ+G-н{F4QS)_Kff& lܸ)`εwemFҘ^cL)g*`0 9W`0se0 9W`0̹2 +`0̹2 `Ε`0sĝ;wf ksT*b 36ĉ57B?ϟWɓXxk#篤l">>DAA?~ԩS$xxUVi8{gk7 |HNN֘022͛7\ ÇD_랔TpQZm{A.o}mmLІd)j [lr׮]?qX$w .78{,3*'N@PPBCC2d̙9s&SsDP(*2wŕ+W-~~~ڵ+ƎCɓ'<Сb1\]]yfXee%aggPTÇc˖-D"5VZRnJJ  Lccc5ٷox<RRR o۷/R)0m4mcBB ɠ&McԩD"AϞ=W'֭[ѲeKH$t .]Ā!==]+=ϧ,X+++.OͻՀx2dZl T cccZ7xfffс)-[E"nnnQ#l8zj888@&ǧΚ˗X ̘1^7oZj"cӦM\<==o>5_(?3x_uG @@u"={FhΜ9F %88TҊ }'DDt 4o<:s }(""JKK M:N:E;w$2770 &r WyyyсhϞ=ԢE C >}J"k^);;rrrܜOǏ$iԩjۙG3gЁޞ 6myxxhܯ_uV=z=|PE'|8uUmGE^^^\#"s\.{Rll,͞=&SݻwEIϟ;wBS^}Cry>|H(::|||[nt _M6.]Jד.YFkxѸ ":x \:r:ʞ0aR)ѵkHGG @I111//'''M6@ EÇiҤITܭ[7__~>}H$Lb'lz0$  /XWC*[/5|s}p[JJ*͚5\)>>BCCӵk׈gϞ>}PHHѤIёۯ }}}ψ/ H9#"W駟_~Էo_魷""˗9qW\IFFFZ{ݺudbb(z:,,,hĉuRVVF4i$EzrFT\cccUҿKdS> w6m7ʊVXѹ־HSwҪvZÇ\#ח*++mw}Аˉh߾}$ ;3˿~@W鿀mBqsY/Mek[^&"{ۿpg6H/ǵkYYѺud``@??wԮ];R*gϞQ rwwר眜m6E_s}9JB6##C;Xz5JedUO/^Ddd$ڷoCTTb1޽{e:::"''ut`eeM7]xmڴC011 ǏJr\\rssahhiWBjj* [[[aժU~ŋl5~~~CD"tjHΝS -\]]1{lTMbٲeãG 33AAAM]77fjOΌ3֭gذa(((<RTP{{{ztkۄ69ilyǷ=mk}:n8s1g.> UIϞ=۷o#??8 @BB ,b+RŹ* 8:D۶msj߾=]8kRT%|_,#44ׯ_綛7oǩ;iTVVj||@!!!NBnn.\b1\\\T֭[uoHNNƮ]p1/1B2^gH|CICӉ7nƍqI}E,O>8r6mڨ_hL"h|t ;v,K֭[ ҏ+:w{p!k1 lumO `ڵ*IHHyi:Iƹ UTTߴ HKKSy'^ gggr[zcƟ,.CN<ck.ƆSJJ FM UXX۷o??<==^gռ㯽_T+++ùs縺#??j /QR Tcx<#,, 666pvvnܖ-[",, Ç_Xhz-ރ1dXZZ6SL͛E .Dǎ;p&L]B,c\~kעO>7oe``'''l۶ 055ݻwUX[[#55F߾}פK. qXZZ⫯Bqq1-ZҥKANN֬Y |Gju"=w3f ߿>>$H֭[%6nH"HEADtzIOO )44ԥ}T'֭[Կ244$XLδo>~:u֍d2萹9u҅;Kv64a255%XL]v˗/p=bdԿ.`fHu\85c @@tQ"":{,.988PDD}7dkkK:::dbbB\:&OLb4WWWz7Tr)//'>S=4"i *i68n8211!ɓ'T~V^Mh֭:J`_u`66?~<ݻwФMy D{V?ի_4mFִ}v""R*frvv&PHfffԣG3gD 6ľ֮]KKgϞ.\"^Q@sE ˫!Q'OCcǎ BDb-AGGm,հo> :%%%H$L!)xwq}6%`4͛¤)(PT@)gTVm@Y_ʴpnn.Sx{{c='33wƍkȣGCddd`ĉ㏙ce0^3ݹ|hݻw-ߌ(L:m۶w}r :fff8q"V&v:::xwf;cǎex0`={`ư,0 +`0̹2 k;"`0Vs}d0 f0 9W`0se0 9W`0̹2 +`0TW.^@A>_ICϞ=hXW(XlѥKA|J vݓpر:=~Aks=r|&!99DEE;c1;w{UGy Tbb"p!ېpuu}ۜ@`` bccamm|r$%%gϞMX9R077g`_EqVQqY6 k?")&&<\ZTT!H憨(V^ d2ԩSi&8;;C$g֭hٲ%$ :uK.[htbؼyr,XVVV\x@@x<x<.\"’%K`ggPGGG=zN=1|pn???\_ Hbu?{,Wv>}Ln"X|9H^# <{SvkʗWgٙ9sD]]]M IDATaӧ `hhQFTkbL:H$ٳ'ى`w/"BYYYS'N@1bZ 999XzJO>֭_|wwwlݺ}ŝ;w`ccSGfXXO:t(d2S*X|9R)kHHHS'ODpp0fϞ 6ɓaee'N`ɒ%pwwGrr2Zhmй0'''۷+V͛Ѿ}{ťNϜ9R .`ҥ vsK.~W,^\^mۭ)-<==sIIIիӰ֭[QTT `ȑXt)M;w`h۶-fΜM='OĊ+`ii 6{HLL;K1sxU `q=iP^`q;t9rPZZݛ:w"kɒ%daaADDeeeG֭㎗__Ϟ=####:uJzPPQii)ݻws‚&M={`}""r :uJMM%_L&zuM())MF&&&屲+VQϯܹs璓J' >\m=+mw}>c244r""ڷo B*..&""3f ߿ZŋqII YXXĉrss`6'sL0 h u?ԯU_Ç=۶mϫl?cG׮]5s1gbb룰ݫG@@@-G$K.W{<..QQQ\b1bcc:;w+G[BCCakk WWW̞=jC*rSXl#DPPPͭY듣m7l0̂RZY"=rss5M\xNK$m s*[vwQQ\ym (p jEPQ#AD @s4FG'5[Ltz!8$q A׉=s+]Q" F!:uN۷.޾ݝce8qℶ1k֬zEEELj#̎᧟~^}HA8`a+İ{n<<<,h4ү_?3wwwmFBB͛7M69*ٿ%&ҿ㪧rtܙu֡b֭?0( uoy5!p:uĎ;(**aooO?R|˖-qpp~̝;wҪU+ X޶m[;1ڶm*+}͛7+lfĈ9r@,YbqÆ cΝ|[[[BBBشi 66vttz\TTnڴiǓBff&C yfkSNfV^^Y@KճxR/W^a/={6>RPPٳg>}ʂ xw[.~~~$&&eQF\v%KPPPi,ܹs޽;aaaّСCquuϏ`ƎKFF^^^ܸq;ҲeKZhe˨SwܡuܸqWҼys-N8p &Eb J.](((fZCtt4QQQk׎K.1jԨjW={y?yw;v,4m]~~~1~xn߾+ܻwshc/σ˗/sbbbСk t ~W233>`Ba-\;"YLBv?! R_bذaԯ_S?rƍcܸq5k!|YB!*BH !B!pB!$\B W!BH !B!3}BCsvXN`޽{ٽ{7n۠ҥKx=5׿ggg&M$4!jzΝtRa8ݼyco_ڶmO}=X=ڭ??RWz-ZPTTTcU&bͤ˳OCu0'Nd2鰶͍#GǺuV XL333 ~,W˼ F||jG jՒWJFpff&-Z`ŊqxW8z(oÆ roo-Bswpttߟݻ믳}vrrr/׿{1e\\\0LӇǏW`00ںt-/"o999|̙C 0xxxtt:S:w}V->!׏|{㏵rtޝw}4͛ǭ[XjŶ ʕ+|g1k,8vjĉt҅+lll?ɓ'ʥK1b'Oǝ;w޽{SV-ߏ;#--lj׮ٳgvm+k7Ν;wx1b'NS:t(M4!!!ZjqMYjOUVZ8;;?re\q1|pSXXȴiӘ:u*[n5ԩS={6]t!11aÆaooπO]R$t 8 ccTeR'z}BCC*((PYYYѣj PvRJ)ƎkvC)@mڴI[\\\TDDRJ/B .T/-ZH999):zhK]pA*99UqqnҥJeff*9sj޼^WW\p}??xԉ'RJ 8~K5iD{f;𿭏 WJ)uMeeeTBBRJ+V({{{u ^Y*@YF[u̙*U)9ڵk3fTXlmmG}kݺׯ_W/ͮ;kkkî+'''5e2!!!S&N'6&J3`  KvI>JV }:ߏ`ɉN:?zjzY}: 2LW:L[\~GGGF#F,ܹÁ*]]O>t:B;VzAQQa!Ν;kŋ8p@>7 "44p- !99;wCcoN߾}~u<#AT͛ҥKٳ'7nԆ &]rt,S^=}U~~>wܩ5qi*\(ɨѣG9u7nܹs9ѻhe˖8qB[~GN:6jU ?;//q==SNѫW/mc^Orr2tU64$}U9BRR$$$p}#L&@g=9~43f ]t^x78tΝ'UzL666j [5!p=/28;;W>:u %%E[ǁ TYmrylmm0[z=m۶ 99~+ܹlΝ;tС%&&رcmu ]vmLJJ:::Vzdzxbbcc{{zzanʀ۷/ }Y07@˖-bϞ=>|X틧'}? q){Mljժvt:O#g cܾ}WWWwscǎzfϞMxx8gϞ%<<7|e˖ѯ_?f͚EÆ _hٲ%رcˋ7nhOn-Z`ٲeԩS;wкukڴicv|ΨQHOOgܹL<ƍk岳IJJښ{GoR;wL`` dffͭ[(**b̟?]2h F-nb_ӻwoU[&11:P\\ѣGz}兇3|4h/AAA,\^{M{r/b[d|||zSNeΜ9ol޼] ,wޡnݺȖ-[o:6l޽{9yOߧNhzKJ'L0AիWOF9bVfʕUFզMuYRNRrttTFQ7M>}rqqQz^۷+ڿTA5i'o/UÆ {g6jݺu[խ[W ռysc6Wngee & (kkkոqc5sL_R򊲱Qvvv*44T)Tff R666~jΜ9SY[[+{{{չsgm"%/_VVVVjں 6(@9±cHOO`RSSt|gfB<[vyEZ 4h̚5W_}U;v,iiitܙ;vTzBU4B wQi!c B!B!*B\t ^/WB*a,\//z)233Yhcƌ!$$I&h"ݻw?~<8;;JFFMLLcǎF<<ڶmƍFqqqxyya4qqq!22RBRf͚EjjM6h4vZ- >cÆ \t0-߽{7ۗFͤIؼyVO+Wd$$$0m46mmO9y$'Oۻ> lݺѣGLJJ J)} /3n8Κ5k8s SN5_nn.ݻwgÆ ̚5˗M⊍eԩ 6o Fbb6t#Fo߾ٳիW N!SBIЕ,}ڀ306&J=I E999 &Tmw}?h<u RoҶG_~*,,L)ѣG6mTX.\c]1cFvR:|RJW7sLռysKmݢEv}R***Jzuu}䤦LbV&$$Dyzz*}T\\RJcǎ)@%%%)!x =Du`0t́%yX$ %yYL+?'33:0e?@Ν1FuE?~h$99Y+m7oK.gϞlܸQ|@Æ z&nݺY^zs5mܹsу"8}4YYY'004# 4Põ6BNh4pB|||aR666klٲիW͛1'ԩSb]F#F駟شi??NUۤ͘1cҥ ԪUQF~zRlٲ={j AڨLiǓLnn.]veBLJJ &M$%%m6 (11^Oǎiٲ%KܹVZQvmퟆSŋݻB r߿?sΥ{o`ggGzz:C???;v,xyyq -Zhe˨Swܡuִi츗,YZH 4jԈ!C0arss {v&)) kkkG}ěo ,wޡnݺȖ-[opppΎM6ѭ[7 HLLCsQ1L(B/_&00@HH111(B!*E *)!t!BU!pB!$\B!*BH !B!pB!$\B W|^cȑݻWT!$\7o_1ǴrJvZ_.WB9sFµt+jՊcRTTHnݺ!C}#22듑Kr!Ə… z{N"33E1fBBB4i-ܻw)SdO>?~\^XXСCy饗N:رCn0tFF"##pwwgԨQfeǍG1c-[d2a0g͚5y5￧}K/_mb޼yX[[ӬY3o.4!sE)ENNf"55צMb4Yvm_6zh֭[76n@Ϟ=|_}#))x{{k?q'OP.]bĈۗ={zj&%%5}ިQ#شiweԨQqF,XY|>?~< 0ѣGv*oy7n$22>tRZl)4!s+Yjp0galLzrrrR&LPڶC)@mڴI[\\\TDDRJ\5k֘_(@Tcǎ)@%%%Uضk.Ç+zTrrRJ)___5vX|\\TFF_~*00P]>J)|oVSJ)tReoo]yTPP8p]^lLfׁ@h4a|4e2Z{ ##?L:t)S8tZyɄ?|}{{{3h BCC ڶ6lH||<wd2ѭ[7u5k k׮i{=8}4|GAƍw… S… yKkeV^MLL 7o~~^O||<ҵkW&O{ժŨQX~=J)lBϞ=JK  x EEEݯ:\]]IMM%&&ݻw&xX[[mse-::u %%E۞ǁ#]& x/^Lll,w`ҤI\|$mƐ!C&%%dP\\lvQ1#GȒ%K&xnIӦM~k[qRSS۷/ 4 ++UVի1~xn߾+ܻws>;={6={plmm2?3tЁb==& F1d&L@nn.aaa|Ν#%%<֭[Ν;ꫯ{:88`ggǦM֭Oի4oޜ,~g5j$6!scŊ)>YƆ̞=k׮H۶mIHH5k0c f̘o/vqOf̛7ӢE zM͵2W^O>ٳX[[ӦMlق`L6u1|p|{M4hk׎zyt:7oIII`\ݺu#::ZmBQyrAWg:@ﳡؘ̈IoIy&7&%%i!xJ-$ Y{%K.p Kc+i;x :9sзo_ V!xvzezzV;U˗ `0BLL4B<ʆ*\%XK4hРOBT+* z@ZǪGB!p-*U W !xYN>lXBWx'V!Ϥ|$\ 8L\倭8Z}bEq#`mb*X,V߇@cBTuU!ز3-lQu>b֪P6d-kٯ`!OC[xg WuUX_C%(3lJ !kiqe?F`)luU EU!ZPYRB!*T-ݶ =Z S!b>h>BK+ ),PX)pܹT~a9u\\.$?NGIXXQQ-P[ZRbŠ\A[8r RNҥ x0RN'=]+jI LBŹ`4iѲ-:@ȿ0tE2Ca͈8)?vP"A %Q(=;a,열D#,)ݻgR&SԌ@ /F#dfH\x.G,hy !P=Z_sz&&&P1V@ x2bcC4D~# Z_,>f!ڐFt:y ` 95 :$PXXYZ"J@ /c0}fX :x3ȨBabARXF iShN?7l-Q+k \MJclr;+,敘BPP CՑȍ׹r*f#%SE)awč2OB\9t.-?IRPD!Y"!×$i䀼LH'b4tz&Fw)ܥ:Ժ z JaO \%ER(4/^BTT#|}}z23u ,,Txzyի7j &C&}tr 쥙%R9;wHH7⪖HJtNEˀh+/ONEHQb!BРBF Bp6f`HŨbg>~5锒weyy  MD!V`c4L/AOff&r .]`(F !TSII`0dbT`xƇPjuj5 ]=ر2):/ 5n4-lر$2 |Y06-VJ ɻ>g-^0aiHbb NO/~mZʞ| E)ӺHBR QQh4Y"萋LFZؾ}Wav# )k޳7AmgV2Fq%FhK\dTsz>U9#0fFh$R1rWN,gd^/")㞋 DzX%@_:mtxYʂzK09HOJV<;vj`ddCz=* /Ol^gPqRxQbUʖ4Gddh0 BDhA{h: ,J,HGh`a"i F# ao%=2 IOJHdh#o)2HNBg$R\T$eiDjiK^M⽿J8V>kހF)vr,Uyɠm rO{ETV 9ᱭ*n* $X(vRrh 9T҉ _{ld35\I JU B-Rz &QXhh>5#7|y6ς)kϤL_Lad-tsRAj )/wwW4 (-[F?QuuYwܽwϔyNywbXg"]fM:.:CFNV}{Ӥd)\?_BRD!J) 蓸~l+˗mE+S/; JjE׊.`L KHФGOZU nleHp,@Ti' NVCzǾYzC"=DUy|?Ìl? ?O7R=cĹUz',2ǦoXY,u:5<Rn槅+8BԉT=G*XΝYd=ƴ!K.bI,=JW0lYY(T⵮90$hD}NF.kx6@ xBa OYȂ _j3e)̋٧n~o|oOOsD8(ȜB~rR)U2<:m]MS8KH 1v'䝉Wғ9$K&eIݦg$-60?."qr|AĴp  Ys{qlO2Aj@m ޅ =Dd <ґl.JKiw.r uW9P kvCeaK1[Ђ@ x%sDbPQ,Eڮ =QTs7^cQIGC!9/>"9(UzpNhtp@"!%ZD?Nt $W~ y5bxI,I nt +贙YŔH0"p']V⟴aqA5l-C'$kҒ1J8^5=cS*dF$tZ]𲕓.Uj&:3Sz`2ܖ]ino WX2`j%IUh ֤E/z^Bm=d )6gHѦ%`m KgR*k%Ҭ:ȱ5S JGk9R>q@lw[sR2R㘛MV +# n3HR_ɹާ逆cdǜLi}ܦ! ͏  :kr#@L3I[?@.7x2^^^TܡHh4ܾ}T\^ RҰZٗmBCw2PXx? !olA*AI:2ӱZ (۬/ (>X*;Z;\Jӂt5aT TbHĚ8шR2Ӎ`H: X"gd:6O>J U[O4o9uFZ(eq@`'# "YACRKF5{xzӕZP)Qfbme JBAff&Z$RSP"HOA*.K() RH%8ZJ0*,f@ ErFreX,8Sp YAuD"A*"˰P˟YX^jVCR-EȾM3˺WOB:6OD2@ " BĂ)si,5y% /X@ Ă@ !X@ k@ xͯDbao_sK*)@@ B,@ bA  @ X@ Ă@ !@  @ Ă@ !@  @˿Yɷٵw~Ǝ^g\D=LZWܹˈ ŋP/.~g^c?,=F- ٝ(SI/+Cc_v]|=W.ckט5gII/̇/Jڏ 6={- ܹ/MGĂV]/7O_Kմ˖HOOsx{߲a*&ONl .p+Efz8yn]0Ӂ>=|n=w||}Mw;!jY uf"|:mfeČMѴnÆG-j ӇZզt:;t"|%\<|pu\pє&!!Uj`쎽;weނEx_~Z:x_a&nyǎv۰q4'($WOڶhd칔XO@`Eg5gMy~~{;x0Kdz \zՔ~Ԯ'rS`5ow>UkӇL:h=99>D@p(tҍU`F&|jl߹~{gwx{߰zwG~,]QMqL ̘52~ Gdf?qlDBB"K@p(n^~ԮIl\\״46ryѦݛςt ڴˏ4eZ}8^t7:v۟2s ishݚ2e+CHXC~NJJJ7nepaT'_8y-Z2 o[n~dc<}V,{f_k[_P'9``o ӇrcDQ;M9, Dd|KTH5lPh/#aWxGoh8 ^;޻nNll,cO^}ٻk[V#.8?Q֒_3hgӋȆ ȠW=w͛uNGͿ=sVVV &N 9b8eBYrؙV`^^^|a /Q\\\HOOuؗ*ż93dh3.e:׭T*p!.\HJJ \vhؠ^G+96}V[0A8::snƎ@ZF7s܏eiXZrX:wƮ[JVfLCܾ}G JٹkmAr96{ѺU4o`ٽ{?6nb1ԯW #r FZmɒeh5߃_~o?1c9zsfNɉ/Xtw~>6Wg,ZL68zp/Oh<@ͪo+~[Q>|!ѭRJe~v :q[{exs%&MCؾe#ryW>ptp@3m,t'R*34mъfMtB* Sn/ƏС|2d(nԾ4׶1m2~;QcSʮɯ&MaڌY !U*W3g6cfe=QԸiN@BB"ߧK Mf2f| ӝa  Q$nfėi8ʔrԭS;1*Ut.W]#ߔtPU]:3WmaQsY&2|ӅJJlڼkһg yYQ-Ǧ-[,j cٺqB Yn=mZBvKOsX[[q!4b}XYYQjWՄy{;[;ڽmN֬[G>rg6ʕ 7qhΞ;G8v8͚6a׮=FvKD4kڄ;wcN5l <޿ToV5']A  3eNo{ pb PƇe~&j6PT$y?Ϝ!11DEEҦu+BCRRV].< NN@{|}|/8XI=e0k<(ŸY0aC{ZiO=Q\%--O`OQۗjlٺ 7rj}z`E899QBy?ABbcgn9s+UN?cODVӰA}233z:z*{{x.c #24ƌBڼڔCvLiiؠ>Ne^+U`aܹs׬mUTa twޣlx zdn?@l\~~$&&r<={PV]ܹs'qww#fϙGf{W777nܸmiՈ{~899=Q j*DhG"!1W/XDZZ>`vo{l7F//ObTw{9Ю[ ?nnܾs@WjS>3f| [Jɔ $:{Ar-MYv~~OLi dZFn7Q[Im4p-Zst~#V\tۼYܰqvϳ'l+6T2bg 9JƍTcp-`᳎fEK9mm|u+WT,ۗbL960wR~>{`0pb&ON@? 1{wBI`t: ZL||ʢP(Z?uD>2#GujעT)|Je_%K%<<{(L[ܬg}>!i8GY߹SGMdZlٺ]:ZqQLLeq̞;G899fvW^c)accM5x\@G5bmtֳ5fFlشW|lvԑ;vٰ4jCXv)899ɾpt`lF|9/F&%5*+淟,-}ʊm70b(>9iii888RnlʇvvlX/Fah4<=6tp> "%5Ȇ:ii:19 MDll -C);;([7{={#iP^oKL<}P(QÆuV*6s;883y ,KK5nnn.}1\[?-$dOOHńPK'o`ӦL[Ek1ry~A8C`bЉ3Oec¤)j5(DZwv )ٯ\St@h]W*9nMd2$))_-FTv: cүY'N^nX?YRʮG~oOQ|x)/%00} }++= !9 !@PB,@ bA  @ X@ Ă@ !@  @@ B,@  @@ B,@ bA  @ X@ Ă@ !@  @ Ă@ !@  @@ B,@ bA …Kk+kBBJZU ))T^D;|1~$̜- Huvo8}gg¤)8wD3~$֩Mڵ*xxvvvEd2jkص{3q ֭HN&CÏ?ƒ_m~gg\|?矺 Cwѥ;^Hųz:FK瓉rҿm5 ǟJ:t(e˖ٳŦܵ;͢[TU;v#>!mLPH8_|963O{մnDEZuKph9NaF#3gϥ\*xմUwvӶpaoѥ˼ѱ3Đ^`vF߰P{7惏jIOOcAS8Q\{gw< 2f\6ˢ{EgJe6G6ӗj5xɲ{N:#G5c*~~ݷXDRv̘5}q( 3Uߗ.2~$9}IЉ-t\tq1IKKcO]zKޡ$&'cmp8y*s/d fycg>@FJvMn]zx!ѭRJe~v :qIơhj2ыOTTچL*C*}Qcr1̜/_1+}ԥ˄c9t0 wܕW2j\]?Tl8baooɰ_E۪L>c(W6۷P`>[nޞ R)͍ ǑϿk%xU гw_O̵76˞dْEȤR} m2i:Gٰ|ry}ݍwqQfL/ΟgԘ+ejݺ *ba޽Ӈvw0}\[n;=VG;waͺun#Gٻo?{vn\٬mllh,Q6< #ؽg/:v >>3gammŁh8{aeeEU(h Vٸi3ua:tV-kwjת `ooOfќ:}Ti7DTHrG|aCpqv(W !Hh8 DQcۻ'nnnyʏ{NGR>h̜=C.Yr%6mkݳc-&qfjTSeE/.^J;L9+ GR6mJu6NbcQe? A.Ѥq2,O}L>M~AGZF7ر8xF Фq,*2eB(S&ժs8Hq}T*-H=(0is]#f>E3mτIS  E|OUvmʕeuDE64w Ohv!*2wխիYn]vXl;"M6%((UV1tPΟ?ONYm1]rA)UʎժqI8= >T^cǎ?;.[NQPbHJJ~n~(̽{8Y(;,Kmy/5m®]{0޻reiִ ;v`Ν4jtw djӎ_W6h޴ k׭ 66K.=Vwԩ] ^o ?q"HLi6Nߟ8lxZSnӏ#G+W_6/_[F7gϞ`0Xq3[EÇ6?.f"I$%%X#}/9Ӌ.Pv=_[|ˮ{L>s<@ٻ^pB9pѮ-TY#Gq9qIcΝ;o:-9m}R$))ɬ>P!{)w^ȂT*磏>bɨjbbbk9whmr´ 0d2)*˗ )6Ͽǟ fD6OzZzs%BmVkWakc'f\DN<֭iِ2!×#?g=LXp]߷7͚6ae[տbaaANi[\~{akkC"UJ%y(zC]FTd,_`W4~cnke쨑y(JemnޜO>6Çiݲ^X|y~'wݾ(JZ9@!Wz~'bw3{<Ϙźտ\ʂчyQ|FFZL 37xѶMkF B`Q]\\طkk֮eμ,Xsg͠mwErLVbʬ-Jx'/^^G.~Hd[n ;6 6N @ص{)MF#NJ E(>4'OӃF*Q[`0gnܸAAy^9k*BC Vi4"aCn߹YstDhVD6cGn?q4WܨaBBY;6nLP(ϵl݆L&#"\VU(={ٽg_VrL>ڵj|bFW>(Ο¯.88Ӳe4#Q"Msxh߸Z~.[۬:ŵ}77WΟPd{{H,f_NN@_D+VȲsf׹sh4 d +U$\'J۽ΎhP>{ 8HPPnٜS]{=qS E M۷o… t;KF&bii_O=Z -ǃHHLŅ Ơop,"<,;wұyҔ`ނE̝5c4dhr Y~Obcc%mVyC*iؠ>?(F|B>]R툙4WWWTάh٢Eʬ9Tj]-LՓcƑKRRRغm; 2swj>6-٧|K/1f\ ={4AΞ;-[n#\Y FN aܘQ#o?ZM˔oݺ-*h_ppɉ7njjKE6tڝouʒK.zpqqZժnG?{D+KJJᏚC.3ytcMظ8HLL֭Eށ?m۱kK~۶d,;;xVj*D5G_ ѣGDDDmO.fTk֬~XYZ"JM&X$2-I=6{ʆ3`lF|9/F&%5*+淟OZqQd-\bsU_PTe8h*Ə ٲm;]:|Ȳ r8fϝO|#x}))xyz2ˑnwS #o3vZ]KۇYcS crLNj>XؔܚIS65=CAV't 2\ѕVmΏ;WoUF-\͇mw]VzXۗBlڲ|ZRƱ<ӢgY=zɕw}rehbN`n=M 2t#o߾kS5ܼX:_fАaq,̊F8s\60q+bTڂ?nSc54dˉAٱ&-01΁=zz >_\cS *~FٹQӰs@bfE&\t9~ : |vȠ}.TrɶoBP^ n wImQa[/2WY:8ɾ}<6 2{%} #6e kwdڵ{^Uk 1³JuvޓMϟ?{gh"kSϐ1K}Nj֮3Ʀ4iC.q +l-)獿moNÛE滄wqGbjF"--]!(H6R7sk{ $fTrbŪt HL-(7n( 7YBj*\]]dǎرOOO>Al VZM*tsD7˗NjYe9F:ArrM:W^1fDеK'\ʈQcqvr)III`8ꍊ ͚fz&Ȑ SQ('cllm̴DM|9g޽Mr~>>,Y[ocan΋ɜGCCo9rͩPW9f%uKi/eօ5<) g7M/OB2q#إ;V:ԮU 6o>T圝[h߮-! LrNEP ss3~Yeee=Kkզ=bVtbj΁(++s(0-hK9O0svL+LMKk|6?йcsܺu3ͻw$'?+l+sÇ̚*%bH._sOrL#177KCA򜿐ÇYd!*,[}8S&Aİ :WPHW?'1= +KK.^d̸9|:7;BASvt֍P֮]K~?{M]C]r\Xx̩ܽGYԴ4fΎaX`ڵm +;wa֭tIVT6-z&k1rX<9|?[711t ٍΝ=V,SCr*3YljYKꣻNع{m~h@eeիWqrt̷Onrau .x W^^ժ#}'Zl)5}% th.{>s͚ǟyk,+-]=2-I9w aK\ZzU8x(֭8|(%8s/_DGGSJe454d7jH&Ԩ^ˉW=3ad:ol;Q,(Ц]]u}[ǎSCAU䞈 CR['HNVz!KrYѡVP0(YspsseޜOxwey^ PWSѱlzr1:::.P!El0*8/N`ZCKKQЯF9Sցed8r]:u #OM?߬Ĥ|96o%O7n2qB]ç?,(@`` vvvlڴ.^HXX_Μ=˓O{ RINNDru^ׯ9z,njpsOO֭^ٳ$')r /^$[CIշ;&%%ŋ+ߓ'OQ֡,Q( *Ńe&Dcn=~LɒYo$',rNԯW4jXjo$ [cRGۻrmYZX33SLϿZ*ڽtOnߣNk!,_ Oj֒e-(we/;@Va?x֭ظi3n@<Ġա=>m-]BRv_[<5YXlK/ׯ З?EgGE64_Μ=lX7G[nSٙ iѬω1ES+L. sv-&NٳL>6gˤb CQ/U mmm?y";fnfJZ.i,eeezI>}BKK/vjZ[jhhbgk9r7~(,@7;@(khh$4}7W~<%5ɢhjianAԔHqe:tRl +.c_tЎǣLMYa##E}.W]ff;222SȈeV̝5!9 bɤw>fFO˗ڽ##%%Q'(0_gff8g٣GSK3{*-ǹ6o ƌo^m +SRXSSUݤJ{vJP`m/YF3w>7m[7QBGGeķo䪣(?+1WRƾDRDϹe9=|̍7iB5`İ1jBPTy44s@cO]'ӧO zUlbd^HMe:;ehiiɞLs85AK IxNZԳ$i4gDz>skTTT%6VY2?pϞ__/zTZj*T13ѡs̉Ǟq썋c¸1_ܾ썣lY455qX $?)uu56&q-Za֜& g̜ó$*{y[썶6y^AXZ䖦:գV1)C#iMR ߒ_/^ʱ%?5nchڤ15kC8g?ݼy!rR$33@_UDtܹ={P8} hjjʲ o ޾yKp:mOoC8x(Np 162b~YkŊ#6v;U*{x*?ʊ/^p>ddDW~1_Ǝ*5W#===+ ɶŒ ^HRRߦXO 2u:]ef#lm3U߯7JqE2եZZZ8ֆnܻrN$=λLY7j0j֮Cִk$$\,?䅫K%lŧFuCIY~38UyVݿϤ)SqwsrM$EsYquvvBUUѴm MM=|?Ԩ^M!y ˗U^v ߿HuML$OFhcllo{-9f")C#Կ/gN|̊oQW`,Z:%pvv{())affJH&NaccOjT5+5z%5r5; 5-k̝#{3@QYvǎM*l(5o7'?NqR25]]*Ѯmkg΢A`G`_Z5+ɽ!нkgoHhtu?oͻZ*lNĦ Q]Vf~lFQcIy w7WҥMdӚǏa(v;)ctt]2׶Mk?HP٬DHY}|Vzꁮ.f0wBJСgN6ik׈7Ơs3}cʖu`Ō3QWW 77WeUWWc/k ]Dccc|Wض}œIqϾƲ =nc摔 5 4*/Ǟ>r4tCII #CC}y1dܽC=ѡM[`Q"6VEAzc̙;u55:k˰!yE 2iT4W@QB/{F)dBP4߿hÉWI~YGN mq"7)߳gI8Upa_s}:?ǎgXt0V إ@ H$22mmmѡ'իU%Es֯[K޽7!#G%&^iV[\ѕyMJRfΎ;֕k=&沙޳ZHL-%NWM_ϕZp222h< 犮@bfE&\艳{7kImQ}ӖTԒ2=Cv˗72ؔq$u8oߑӣgXSK[jթǍ7?:M`HL-/#e.ZgM-RÏ68Ƞ}.Tr+;?T*e)2Ut$n\2tN0*mAWVY+;s\+gh / %on4i.SȎۇcyM-\͇mw+o߱`!M+ n@r06#&K.Y:Uv9Z^;g $f:81iTMَgۯ@ 3 IJJ VOOOv;$,,4?˖`ŪT͓O nYɣG1 csmcr4b-`Ԉau(+hڢ8DV.^TUPVʱ!u8zvg?~#066^pB%#3}ܱ#OL.݉?W6ݻO.ܱ=! x%?JZiG=bŴ=v<ڄs n7L23fѿoo\Ϙ} P` 6{={Щ+ŵ]+ oо][B6 99rmfE$jr$9s=lmrF:ԮU 6o>T圝?$\adham /ysc(<733_^pR:tٿNbddTh{?ynݺݻw 8IEaiaDDMށjPbll̐A1Hx1CdV.[R̚1 k++!rr.*2,ˌYsܭ ΠV ځ@ /@||<ݺu#44kү_??4)ˉ,_ 9+1 Lpwse=l޺;ʖup ӢgX#EP˓GuEJBjUmuX[X'ddd냻ܹ$KrY7Fjs!!̙;ݻ2h`jgo\?qeXt1JJJii錛I:d5s.#F5Y(L/U nTS}?f֯rɂK[TTܰ07ѱl ̚pDܽ6?*EZ5)]_sv&AwL IDATA)YRf޽ǔ חҥMZś[cqrtiQ+(gNᡰO@  00;;;6mDDD/^$,, oo/;Ϝ=˓O{ RINNDru^ׯ9z,njpJO֭^ٳ$')_Td % 圝_.ai԰>ۇ!V`nݺ^Z'iղE`^HϷq?/^:jj44+W]Zҡ}8]lK/ׯIN~Y'lcKBT)eeezI>}BKK/.vjZڵf44ae97B#;@3 _nɬ9P}VCC&#R]C=jj}P~NS V,]̑ǘpA ء'GC#m[7QBGGn]K5r 4FFF>-[2g|/\D̬6+~F ĘQ#7m)(L1*އ} o{S4o< ͙>[ϜUC]W^}nىezFBz_.?+X "Y($`nnEڝ9/iC9;ehiiad#8+![@[[aOgϒduYllQQQ)PVĘ/}V%c6mIjZl*57VjofΎaԘq sS͛7i(G;;;/Ξqipv*ˁJr%rV6X(Rmjj>MB80k_`jZ^=Ɏi*_ j(bE2NN92%%+W5c5WzJw(|)VW% ځ@ /.qqq\t6m>rW'C Lvm3w>[^H$ܽw;[[<=o=ƅק'NFKK ?_ӹv=ub򨨨0f5 4oʀ}[ڴU.Fbb"5(!88 ]424lڭ'-[4C]]ak5Ez]vKx'}gFSSww7|}`@9g'?]f&CPWg! :uu jժ Ǐ1Υ? ^CZEfhyRC<#ǠWw7vc^G~,X[Y ܹ+9=+T`[we/R)oS }6̝OIݒ$L> TU I຤"rrv ͛س7Z5b՘ի/ׯ@KKKM"YR_&&~P4166ۋ۷m-9f")C#Կ/gN|̊oQW`,Z:%pvv{())affJH&NaccOjT5+5z%5r hm[S/Y3)Nr*e63QWW 77Wٍ}%\~55u˲f2Բd-^q3gЬI S*G,YbŴH$96KÝm[~c؈QtUU|}j8; 5-k̝CKncHܸz:ctt]2snkڤ1W]#f|FccmK@^̝3EH$L;FdA{W|=?wzӦLmkւ}2tjb" mۓ^ɒ(d;_J_ZWyفQo%~A);P*pbU_&n:VbTo}iۉݶW|W}u66eYzj@ |?Bbڴ뀃SḼ\^)4C @ |QTVoSSib>lܾ}{"4140`-B@ ~f._N>ZXN3rP<еG/:o7loH̨ŊUޫ7NZRQ(oܐk#y B`aS;gFKZZ\YcS c BBA sp@bLe܄HYUoΥKRSK[J[ЪM;|Ȳ% Y*U@HKKgI̛;׮1dv߼yK,Y8>{h24Xv~ 6! YbUxӡSWv٫PO2j06oN99}0Z#CC:w!KR2h ֭e^,Ξ;6}3kkW-azD ɠa2E"lB/9 7e|1<̐A'ʜpuꡧDz Xj=v\x@ d|e%uYtz\%7B> NN ÂE\]*\ŋS˱yk,~ 78iuK0DUUa#F3l`JdH:o!deܹˈc]+@R Uxyk,N4m===jsi<=<(YR du޽ǔ+^8kP---&GMc@>`me%K4 LazyO&Ԩ^kn%6 c;>++KVXrV޸zZ>{5­kYzԮӧ=#Pïd=-WD"ak6ASSoʹ_<~Dv̔tZ*dffre\‹֐+[K.:9x)7oݢCn8Upց搜26k"33g[&!"o߾UHN2LѱO|@jjWc^rIR6ƍ̛3h46UToUUU7jji){%##UUU@ wPD:whÇس7qq4jX_sPBylmmx2Ζŋ޽"OII!UڅFj89:PzGCEE%߲G䤰88 @/_9;~~"9+WB01аA} 7o SܜCMM#G1sڅG c+ı}NVX*kC"p1.$$Y`me135E__W(w}&M+0nB$a-wK.҂qݶ1,~}h !ܺu'Obgk d Ҥ9G][@$ _aU=njjthזaCr$K$$̙M34鉞ż yz,Pא-@Jd-O(e':/-gI8Upa_Oټ%?Ocٲ̌Ue nsg!1z&W@JG@S%%ƎHMlorߜ=w;[[R)'G@ WҨIsT H ܾ}Ysr԰eْ @ /C@ |eM AdA H@ dA H@ dA H@ dA H@ Wj?|R")R UvqŨ@ fR^UP&2+VfϿ|ߺ}a#Fٴe+M'3~g,X}(gĨ1<|P(C2333~"<^wҔ,\%d!tuu%=r=w+W߿>%&^ayn'Nrq:8WdĨ1+Hbi@B}<6 2XvyOqgh½{evރWH̬R]h2 M $f8Up!jZtS.] 4i"6x*!M\/Umk >Rv'em6iR!{ߗBY\ѕ)S7TISpQi *z\Cs M~֪SkZɓE17^~͠!(XBq칏ZKwq,c[&eeNCv|r4Y >yJ-L)o+s34gh¶;xVTDyjf!%%UVꊧ';v`ǎxzzVuxؓA˝5y 5beʰt h'bb"!Zӫgw&M'88ccc!#3qԱ=ux1Pï3o;vHv̔~{KЩ g:O>%Anf%=bP0"'q1s)̌W`O@MVZM╫fϺR *_Os̚1 k++!rr.*2,ˌYsܭ Π!=m /4e*6hTUUy4A`~ЏˉW9ׯ_3fԈ\ԾceJy,t7BO9?YPƌ'O9;e x!ǠFܾ}8 g_֭߈ii+{L[nq!MD2<~] InZP_} PАY|9J</.K󷤤Ԯ-bYF6mfZL&GR3wFA^TUPVԣ[K"333fѺ]G;E=/̍={ÇF`ld(166۴aFYbdk[3G츗CH~̸tҝ{ kۊ"eee8DeLUUիSL5g=ճ210ysOYaCS ΎT `ێ4_c'PE 梤DZ())1j8wD"ɵuQjBP~*ܹsV(\F{zs]+@6bKll6ѸQC, ֮%@1^$hŹʭ +Vƺ+ ~}ۏu0u}9v5/5z*NE=/?[cYl&|}ɔirIx憅oSSQ/**iׁkׯccm]XSVgJQ˓;w}Ho*)jӱO.CbggǦMŋ]v qwseYaܹWkF%VvpmΜ=ˮ{d%8xHv>7Jӓ?t_JC,O>^&˄ɼxLrrr=uKTZy deXYk="1 ~ybia'OdL2'OS]\ק܍Ϸ>s66 yoM -/]~TmTF{ʍe)dpMBsY\,ʘ(OvAo.(K ihK]pVo}a#FY%{I-@o[${^ۘhjjզdVMQcMQHS$4i¡Cc|LQ,UeeezI>}BKKBST)b7odt֓gIIT)cFSCnI24 r5󽮊OQB੩IZZL;[VX\n{8U_'z̎KYnH2I\qSy<hիF5Uk|H~) =w%(6,};,t[2 M-=V._7-+/T*Wuu<| Sd>ȈeV̝5!y08bv5%2v\t~נq(r) Ҩ!CGbK6+{x\PTȥK}ºPWWIhcى̕gQ89:EdƤbE[:8\Q"}XSVg%KҲE3VZ07/xm8.]D6m>}~Er2~9~>Ӌ'\~ݺd%)R~#ZiG-(]D7j(Qn娨(SX1/]It9W9\]*eK,>5]Jg=2g|BH{vxyz( *7nիWwҙ&W,"c?V$&^aҔ/L}ÇFXzТY3$2nB$; J$n޼Şq ɬ9squfr}R1<5ڶAGxl*jTX4 -8Y( G1^ אiѬZŴxDh\]*1oB_#ݧE[NYb6>7X[Y ܹIZr QQQa 4kʵ7h4*c.6o*RQe/Np 162b~"~ښ)[8cmcc}1.$$訐&u *չwMCLQ,HyA)DZNz|_g²w<۫['H!wûҚ~RC gݓ=Rjkc#UWWK}jT>{tOVgURw7WTGGGZ|9GIK߹!ޕR uuKʕp/TC]]j/ק4}u0f}{˾~4^Z'(PZD Fd\^IT"H;ul/}p;7JIK6>}xWv|/k?e }r.[Pȡ}R/O;(>?\ w H/H( HWtH/* @QMP-@(餑K#dH!4nwvofSvQR*4l`8mN@IRJZV7fw5j֨!ݸ)5 n KjJUSZ4nl~yYVEjִdjj*IիU=HWTNK}^0[2)^+Sw~T^]IRIRԤ8)14daa!=JշԤ8)YzIUXqn\3)Ri[x쬓LLL$*RO?h(j[:}t%Ի׋ddd$Y[[I>>ѐ+w7FJo |]jԴIciߞݕ*ϻ_~AJZUڼal//Lvw ݶښўwJ^R>/?Pl{'mZǎ>L7oڠ@O1}LΝm[Ef<$6I@y}, C#[xsϣP(6dȔ_Kff&={t!ÕWY~#?~]dSx3Ψc~AKeOw|KF uL@P5k׳zzj֬ϗ#[@'!Dv @ !@  @@ B,@ bA  @ X@ Ă@ !@ X@ Ă@ #22 ^^T@ xxd3 Y5l@ǃM̵kLiO<..^߀7V9zy m/k=7̙'W[߈ "ba066L8)((}=Q|z 999O\a箟)0e>]?s92RgGsj̿7lLcG(2e \pn?܆Paltl ;.>^/ReaO)?..^Φzu s7&ISߛ@Rr(U@  baܸq2o<4}'`ogGVVo4?p92\]IJJmuкT/Ӧ ?^A?@bb"o .kҾSW {zZ^ ko.3Q2>uqr_mӋZݳ[ARXn|^wW Ww՘pߝ:/@X_|zE銈wWѹW'/zzpt՝!bMlmU7ɧ »}6O Ojxз_!_ jmU7ѨiwtUFwsy:w{5VÓ0zxf\HIIe<ע5kR~C6}%oo Ztޓ+WJVÓZ^~junK54oɎᆵG5{Ku<}XrV~& kc\֥$Z^0?@ll6RS? N~&oڴ? _R&n1moݪtr?8ߵO+X8<ע5:WwoXjS$~ATIv9zTϻymjyR^=q_|Az&88}Vʏ[Vk,SSSS$Ibż޿7oƒxXc9vյ*Æ |"ԯ999n K K>^4[[;166eb]ys>,k׮q!/IX[[r} `7?w6ǎg)899ѹSmCAILLei޲ Ѷ ja'o3:0]ɷnѩk$&&2i4}a!ϰ!ڥWcb<=<Ĥ psw㧟w3c,oujj$$&钏177gjW=z Nscͺ 2ѱ27յ*[ڌ))~(};12ئ6Uky{իW׽R,X+W1c4^)/I^~>K?[δ)zկ_?2|oVL2DvU}SLҥKۗƍWڏT177g!|lz%K?_WjXYYr)C!bojyQˏУGȸ]a8'N۫\p/^$&&!IRrY~uZq!]^=UX08LLnD󖭩^ӋAdgg(]gΞ%-{3o>VZ5!&&&hIN% Ц]G]{ܙq);vv@5i8>[.{FCvmq.y&""Vw:{E˵ >׬i LdT$lM Lffǃ|ҹ#} `舷8q6iVr4nܨ}{YGpfL1kZj{ 9,0833PT4hP .pi||eP;0*e.?U."#((( **lC>"33z/Ă#GEaffƼyȏt,--:x0a=-=Wcbxpvֱuضځp( GRӷ̔B= C6WM>`Ɗ766BVym0޽zӏ߲qj,*.ڔZ5k} )w?ZLJ6]?~g?׬̿2oؑC11؅%}i\Oʒjt(>WXX<]PؘMҾS7&;}^Ͷ"I? 5k*ݍ#cل>Bpfl- 7w I/*ۃRg}T>]z z;{ p!=JD!J0`y񸺺>阛`mmŠ/yG7Vuܜ&]x<&MGzu 7X *HSRS K$&&>3gQZQZr/)H%ygF5?__cfnVtxs5fffeWASO JQ^E5;{&;p!^h//O6wBQi)~ݳcccY&8bxԪ)]Ԛt&ٴ~-3Ocn(| !!={coѽ[*^=_dߞݴxy>]B.Vpwgϩb]xyx0\^Q#qrr$U^}?Ǎc s9ؽ&7bll9ko$--7/'| 2ځ|5WqHD^_#5p,[9=_~1oBufl,j֤apJcPP}Zxq'/ii .[Au033G ʇ搙ŗdP* =ϖ1p@џ2y?*4ϯ{~㧟wj9z /beiů{OTğA`jjPtZu ɥSesH(II΍7Zڹgd;c _j(?$%'ٲe:dkoϫ?r5{ujҽ[2eh1'1nhlmp?R\ʁrP׮*nhML(f]-[>|yC3 dV`|Ve%* }Dbb4nԈy?:xgd116 in6n?׌W6hmL}oC m۴f3} wJd-3nGDBɉ-Z5kڄwD(ϗOޠ 0fۨTj6l̚u뱲ϗ7}OI(㳂 1aujRzE{5󅒛/L~J7jt؁qcF'қo`]CAǰ7G^>O6I@y}, C#YIsy BBb"Æ קOBB%nO\\<&NfqB(qLD=KiToeJڷk[8ѣ4tvvv ~c# $yd@ 2N@ !Ă@ !@  @@ B,@ bA  @ E<788qR@  ^JЪukQF %D^OUIHTTHH*B" I ,@qBW wQ;A$/?=1bAOT((D$<c@ Ts¥Z &;|)ra/xS~~~4lؐڱRoV^KQ$AQ%KљC!Gd?v7åZ l We.^N`s/&NֶT*>_opVڬ߰!FP'Uhס3QW y/4jڜ*vNԮۀWڣ|q^K]p IDATA^=v 3*YTK fZ\+^v5k7fĆGGN<ɼ95kJ3ޣS/ԋ1KIRRVju3vh!666=ƤwRՕ7G +cJߊiPTT1sϖ3x{{vzz?N0XrLM8r~2-(uZ'MN#11INe̸|f>_'ȷh٢9)i}syrYno|zd钏L< '8x(/eQ|p>G1fZ'v^_={1|(]t&!!f9˂jdXXX boPTLY(zJfiX( KxDp29 @vW/D_TM8;;S(!!Q/3g /S3S_Sx{-[ٙMw9A->fȐrFQ$QMI E=Ojj!!!lTW$ANV/Y{Ӧп֭ˮvmǷ>B UEM_qcP5tUK̵,X\gΚÐ1cA3 >Ϸh/~=ZCDE]ayr|/{hР/СC׭[&M`lbBII)&" mKo$55m8yyyfm"$P(8t(- wBj'xj֣]ʈW¼b72*l3>)/ڵپ];̝ϙghSƫcbb`,=FfVffdddp9"TZlQ꾇d\Uת瓑gΜ%##G_bJ_Ou .>5܅! |b#ãgxnםI*a.>c4i֜ѣGA\|<˖ʕ+l\J2h1|(+?_=uSIKO/&&&,h1Z&11V𑣜p)O߫>V7%gx!!5r4&&3saffFS/ : o9B+66y T>#"5{.}_郃RvƆMqZ;[["帘(L?gF4sQ/@**e}tܕ^վ`nnNDD/莣cu^?խW|G_v6v4i\ꤘYx>++K5mϗ_(T/ܔw'Yz iT*162*={P]Veͪ_m۶`goO.dwǍe;cX|>DBrQ,]1mwd/пcFJbÆMY+K+|ysAqH(˖DR2hMRoZwN~M*ԫWWv?z[X[[XrVV|s͛5T| f!,Mh0[`:lJw77^hGu ?w'3w>եK_TYAwĄ ԩIq7+'iаt[ W"2ȘKxy"EYÏ }p]3 łG'K‘T~A7 2y@>sH@ѽHbfA`~FSŅ VYJmDy>QPBΝ-_udboo/ iǧD Xx`E?c6cF-_  !'M` "B,< 9,<%ljLtB,MժUiԴ s!Q.=s6E'@@ B,@ bA  @ X@ Ă@ !@  xd޽"#LAAIIIO$'OrEQ!111x{{3~RvM@@j@S{Ғ;w7xF* :t4͚53fgZ& [ƴi ΙgN6mpqqAVHKKi $&&ベY׮]CRロ->>>(J_.&//j$R֭[hZ W^ĉpA^ +W =z,ǁؿavv6gϞ!ǏZB&44TΟ'O R©Sth99~,z=>|ÇFnn21y\]|}zcK7+t3x` ĉK]?t[FTRhժ!!!M6addD||}NHHǹ_0`gΜ9hZ.]J˖-t:7orV^M58wSLƆO>Dnݺlڴ KKK-[;ûK&Mصk}B+ʕ+ݻ+VCbb374GN'׋RӱAPt!==]vBOEk4E>Daa!:(^VP*xxx I*Dbbb‚< oРW(`eeE`` DDD၃qq B %%+Whd7rpssCѐ#/S;w<122"::0 ->>#IRq'--~Gn߾-@Hdd$qqqԬY\>'''bcc9~8:n,}63$!IzKKKj5XYYRpNє;R2bkk[f\x^rssX tR R/̙3hbbbURY3IV38_jA066y5GT;F HJJܸqoooʽsssʌGxILLF$&&Xa]622kK&&&Y (e3" ʧYfݻ^9۷f͚=pBt`\BCCiРAò.ؘ'VVV޿o%*8;;sya kkkRSS:IHMMo[jU HHH͛8;;WJlDPP666F˒ާ,J 𜝝!!!gAnnnT4)3 23f [l7`Yy睇OPvZ^x4i^^^piݻ7ݻwgСjO*BE4hЀO>%Kȍ7߿?3gdҸqcvw}'ۧOFi޼9yyy>&<\]]IHHҥKhZ(((okjj=/_F$͎呖F~~>fffzrss h _Ι3g^:jZK9666ciiҥKǂ,,,֖p򰰰I*< ww}U $xo N:|wL8[?`iAiذ!Nb̟?$y#b:uؼy3'NdĉdffҰaC@ _z%BCC>}:ߟѣGSTTħ~ʬY/[nÆ #))˗3{lS(O+DEEKjHJJٹR\z|LLLC\rys#**PKf:DGGCAAj///#::k׮Q*899 HNJdoTڽt1xyBqGL*@w8q+kk5m.,C x;rHE)YYU~YXX$I\8/G8&Y@] w (bς@  bA  @ X@ Ă@ !@  @@ "9$rY x uQ L\\00! bbbf޽j5ٳG&I+W333xw+UTaڴiӧ۷o'(9~8p <ȉ'HII1~MѣGFr !::7o~ H\zKXW\y(JaB,<ܾ}yQN20uF@@;wLJ.]p.^ȬY߿?;vW^a޼y,ZHX@ɓ')u=++0,,,@FVV|=&&V?NNNpڵg./իU*^3 (~[:o,|ZTf˖-|lܸ'xbt:7nDTSfM>cV^/ѨT*:t޽{9t'Ooz=]tܹs$$$`llLƍܹ'rh4ڵk.\͍~7|B{9>Ν~|ݻ_̘1ד;v;n8كRk׮,Z+,X+WB2|pNBØ={6g`ܹL<,}]nJFFM6eԫW 60p@ 01)6q ٳr 4o`6HDŽ!**k׮RAPPJ;FШQ#@vvv>{}o>vkƂ HLLd…߿nݺ1a.]Ç9r$:=zaԩѣyy *ߗ+ٴj [[[6nHnn.ӦMs=z###Ξ=K\\_}Jݻw{G=0ʨQj 03gZ-K.e˖\tɠAGrU֭[EEE?#Q PXHOOFFFF˄X[[7~IEKjwwwj5DFF?ܼylHMME`nn. oooLMM"::<<<* ###RRRpqqR#I^‚(9LJEvv<@)|SSS Zjxzz˗QT888`mmMJJ\7z=RxzzRTT͛79<76h7Y:uxyyT*GPPPPggL,ǷX}_7w7,e'wdw7hmș6m999U`Zl)7B={$22Zj`6lȦM FX+Wϙ3Ν;3w\5kٴi=zӓy1yd.^H\\\SNm6v!w[`sQ~l߾]t m۲gon 8qTmڴݝYflٲJ秕]tw>;v,7ofZRYpܷ155SN޽pwŋTR-[зorWQlN]2m0jTG_J DFFOݺuo߾]=gΜT*\~]ٳg%11ss bll,w~\|$j5 (~ QFqu]Fݺue022ɩ)ٛaiiIbb"ܺu,oiޒ''ݝwGxt5k޽{CAAYfիWzjBY믿VW@@NooopwwhԨ_eL ԟIJJ 4tJ{ػw-44Td=8X[[*'$T= Beƒh4M)%xe'(WwIXZjyK*g4n߾q 3IIIcddlh4dee98h4ȢC1floYf ;@)SЯ_?$I7%B? s 6 +++Ν;gfڴi4oޜݻ3p@4 .]_Fղyf?ŋqqqctرkƍС "&&???WFbt҅iӦԩS_>/RԠAm /FȈvѽ{wJjj*ZO>,M6jժBՕ.]V%..yC$IDGG233 :өT*rssuvvv)JRٙk׮!IROggg GBB&&&T*rrr(,,gdqׯ_‚}0t:dgg(DTTQQQwXEp OW.ǵd9sիVܼRXB,̠A'xd{{]w}6-55:6^{-YYYRBppiq8!44$f39999шZp`bPRR^'22`Ftt4NVʨgȑ)^ѣߋPll,RaeFJ尃G[FG_sٰa֭c…/_Ndd$֭C1~xyYf jUV)O8bcc7n\JJJկ~ŧ~N'? /"3o^6Ge}3fϞ͜9sz L8Qz=&L`׮]=6mڴ OwErJTB΄  bݺuXV,Y7g}Z[j*RSS!00lf3|իWs}qwj*X`~*++ٳgk֬?~ŋc2XbyYl b\- r%HJ󚚚0LTVc2zt:j@/FQp88z(ZTN'%%%dgg3|pT*l6J>>J1Ѐb!99шfCՒ>uELLRw?~NRRRPՔ(CР>\.!$$Fii)'N`ԨQ[SNQQQAbb">>>9slƌ`J  @Û.x%` LRt&FLDDGf6ݦ-Ze˖r͔)S\>˳>=fxW>SRSS1b6m[nQs7*]wCeƍL2nIix'12e 111L0"6mĊ+^`ҥٓرc?ƍyᇥ]Ax~Nt^fh뾢#((--|kk+EEEhZf1be>>>>uvNm1PĖ*7"##rVUUFXXcǎ{h^;rȯ| [ݝrFz{{{p8p8xy=,_'z\q9> hO z\.eeeׯYdd$hZz e[-Ͽ+bo黔jZDp:dggpZs;bcc V]***6lX-Ӈ Q7vXPPP@mm-ghu &z%,^7[*Wvm۶1y>庤/@9r$7oJ8z( ]+K~Ǚ7o|xebrRp\X,>?^ϯy~~~׻Vv2RaXܦ544RSBKCCEE`0n`nas! Azbt;Pz?CCC`0(u3C///4 mmm a[`ׯg֬Y{ャ]jϟ!qFFZѣLK<"""0vVvjkkm.ELL dgg p`3}_ϡC8qǘ䘐@II j@\.ZByn_*(q\]$=)//ZzϏL&jJEPPAAAى/v˥ܝ]9rZP_0>>RoEGGz)a:t(} .#99_|8!!!!z=pؿկ_ 6n:s=<Ӽ{\{Jeee%{w| oX~=Ǐ ZƏN?fٲeeѢEtܲ1ͼl޼[o'x>HYfƌ޽7x 6pif̘qѯ[|DFF2jԨ^0LJP՘L&sWW 8|||VҒrQ__OHH 8Cll,&##VǕϹb!;;L\\ljƠA ,,^.9:D[[dDEER9rJEFFTWWˀJi/B}f Fcʔ)|ԩSygxꩧXz5fb~6nȇ~M7IׯK.eʕn뎉aĉ 23gc0?<+Vc 7ɓOٴi7t{a9rCرcaȐ!R usuvvݦzl6wAAAAA^p{ߟR*++$**cL&9YYY>},nHyy9*XVΜ9 FCpp0pK_Ǚ3gl;V)HKKSNʔ4:bfYeffy;f<YUVc})7W_}5YYYnkϏ3sL>Lmm-2NNSSK5h &;vrNmuF{&>>(jjj0͔kjܬlJ+dw^[[_uF8,rp\tuu]x !RkolTUUMR4Ltt2p0k,ǟ<<}/~ :+/Uk4`+///ݺ ^y{{+[v333w[c%eWpy4 tvvMlJ18v'8 IDATDVeWh= B|7[aƍc֭J"l۶ɓ's㏻=g<3u]믿kR\\̠A#GuVf̘mܻw/ӧOuǎQ0!..޾؂햔pI\ʠDJb@bb"nٿ?ﵥ󂃃1tttx餹Yhnn&88X ߟe/Hmm[P:^MM j+3CHX=X`ׯg֬Y{ャ]jϟXR]]M^^fv; = fڔzVr-nJK٦uuuqDbb"$;;h4Mz~ќ>}G^رcc0Z\F!aA`С|G,\{d0xKZ_pp0٬[BCCkxꩧW^ᩧbf…,\Vƌömۈq[wMM wL>KK|O‚eR$< qvQ.8/ #$xEa+)T~m(*~j~}P@QQNbĉƸq䍔cill$44k%55kfmm-'OdС9X0:t(V+&I 7kn7q#fJ˗۞l"dCxEpB ״܁zSvCti?7GC}Qy[l!==@FF~?T*DžNrUUU[WV+c^CCgΝ߿A޽{qپ}; }}=Ǐr}紐^^^_l6.Vgg'SNQZZ*ԲˍlnӺk&@T*5)R=QPk|aЇ'yj.+Wl2ZZZ<0}tfΜYv-ӦM 4fWxbƍJb޼y Myy9c~[[999l&''#G*W]]]h4IW#a/ׯgÆ [ z_|9[NILL_f͚5UDpp0Cm{l6rrrRynExhnnꫯR0w\X,1m4^}UDgT*]w[lW_eŊ̒%K۔%$$pwtRe/"#??yq  ɓnkjj*F⭷R-^uqiN<̙3r1i$^{5cr{"vޭL2e lܸQ n>(6m`ܸq~mT!55bzRSSQTEEE)))JXt}nSSSCtt[h'&&JũSI||< b6l HPP;v=j5Ǐ", :PFa߾}u!PUU~)//jp8DEE,!Fr?3'w/jgfΜ9=L&N\uz&L]*8___JKKEk.-[Iĸ}v}Y"""ȵ^K^^TVVg֬YC9~8/d2b e=Æ SN~~~\h"~'p 7pQǭ[j*RSS馛x7q\Jߺu+7|3*H~Euu5<?ӌ37h43c :$122R)=&L&yR՘L&ܚ5 Vv<ш?UUUnaát 0˅^25mv#GOFF6B @hh(fmEڊfcРAT*(--h4:>ĉ466///GEՒ餤lNpp0j466^<.immeĈhZZ[[9v$$$Nii)ND"@~0 l6(,,dR+aimgOILgU%N@HԮ'cnEЄOrUN f t:X, 0N-Š+zs F6ƍ馛4icҥ\:&& &PTTĦM‚[l,Y駟`ԩp‹ nFbbzv7sqAFř3g8x ˖-SnVe e.Ş={ؾ};GQZy;v,999 2DJ^:>7Qzl6UkWWYYYT*0`@!>**<ە}UU&Iv+ZMhhGat줬L eee477Ogg'߭K%88XnhhǰBmm-Vwrl6#FP0L&JX# @Y@UU}"6lr_RRhTP__qq1111rcV%,-WV2)ߕhmũ+H\ e(Y6}X"~R1W+t08cq:pv,z ''jeΝοO=ٷoίjz}^jj*UUUnW=%Fuwo'---J ė5rHbcc裏5j}\s5Jŋٵk]!~Q݃Ǝ1ԩS'1!!N' 瓛FQQfDV+lo9s #22 ,liifL\.%a0ߟ:t:]&F[X|KLwfF[CLXXp8PչuUVVrV+IWWWZ[[KWWn0eAAAgj&e?qYG{όBSZ1?#|zTL'w3˛}n;ʎ lSdd$UUUnӪzbƌ<_Ύ ?ve?8e`؅*վt::::>z뭼{{?Er:u*ż[ڵmƥSwEѣGɓ'0a@y4 Je= SՄGCCC'FsEUU2/zQF1`, ->>>5Jy=1c(aaaԜ`ZSSCXXXeYVt:/Bjkkilln+ǥ6l";PXX>^cǎaZIMMeC|B%t9IشDZUҚUy͸*Bm!9u[i(:p9i>]K؍_}7n[nU*ζm5RѠhzL餥 /PWW9??=ݻQF]P__6-99u?*DEE}Gqq1vbV~~><&L ==JNRUQQQZɤ+M)))nZ]m*X,raX.x^-ͽnS]]Mee%QQQj59DEEs]vݣC ?EGG466^ԀZ/\hkkskhmmn+CSUUUUU)Z\\_0,xyy1|pj5ʅ>644Rj=gWWDEEa20]|. |Z}FOǩMʥ/^L˥CHXw*sNO8j7.GϽl`֯_ϬY{Yv-̟?_)\>,W_}5#G30uTNݕoc222x8p >| nf̘9sX,DDDb XdEQXb >}<qUW'>Hyμyx'kV 8p &LgĈX Yf qqq(;w$''tnV~i~swc00 080i$|A*++2d 8)߁Kuu5yyyDDD`6ƞm%q8(++# ZMKK eeey""$$\.`466bKP gT*JJJpرcc0Z(D~~>~~~_*L^^2nKLL dgg+6%%%DEEqj'???*** 00k [3228x ҿ:ĉ'P8?^ߟӧOSQQ (ۛ3gN] w!,4ZV{Rן=PoN΀gזⰶ=_СC裏Xp!P bСCZV{7f:3<òe˨U"v{Yp! .1cưm۶^w^|IF]w?OK.e;L>]yOmm-< :/| _m6z!6mbtMXV=x fϞy7IKKcӦMEK/Q__OXX?$,|Gʣ/SSS7n߱Bee%@DDQ[[KTTEΩSlhZErr2TWW+#>|8t:1 CNN*_) }p88}48mmmJRRfFhT+(((q{`28p *vڋ1b?gf2qqq{gA~‚"۳rhoe pn+CJB[7Uw[ Ob3ܧ׳a֭[… =/_H֭[Nc$&&/fV\!88Ell,ƍqމ'9s&\.&Mk3Ǝ /#))gy3f}Cz)egÆ 444œO>ɬY5w\X,1m4^}U{s=s=Gff&C{Z!55bzRSSQTEEE)))V6ѣ/xX,\.z .'N܌fCVFPPӉhdcǎv!zI/xs,bp9}B!_xD6{l̙cXdĉJ0aݾrx dĈ4551zh^{5{ݮ墫K[RhwQQQTWW#0o<>C>YbHe=k֬QۏcѢEt:V\ΩS_~l޼{FÝw ӹ0 lܸٳg3b Bee%{a͚5ߟǏxbL&+V2rXIDAT[oo[> _+}ײ&/N_dsq87a ݨ?[2.u[lLf˳>HQQ&[oUWQQO?d&N})[J”u:ѨtRYYI~zW!Z= W5,ܚ''whTЇ%Mwtyy,1 r_s_Xee%wyr\f  `̜9͚5s*ߧZZZŋٵk8 Rя~nG?իWSZZmmm}cjj*UUU\.xfn6,XOS%ߵ mnHII!++zBCC=OTT555fINN%tRRRB]]6Mia8pd">>^>l[OCdd$UUUWNUUJӿ٫2ŢOHHHF1r1uTy뭷 f_󼽽p8ꢫ>s /0ydەVp8p8JpPPFO'O?|D`PA7ͦtw}}u:DGGG^^AAAt,,,dF#mmm-STTDWW2B )W6M> ߔk<{%^}JeC>'mK¯>JzzzWeeeر3|pYf qqqPXXx $))˗+RSS{p1ٺu+Vw}? >W^y_~\.V%mGVn:~q-pK Qccc&//f3v]NNGDDFz=FL&f3mmmjZ=󣮮ZP\. [ +[8?yT.T KUfGչ0Rk@>B!e4z^{{t68JxAB!r !B B! B B! B B! B B! B!aA!B!aA!B!aA!B!aA!B!aA!B‚B!$,!B‚B!RگrE%%4<.nH wŸ/6McJ\qQQhZMEĀim~,D#1F4M**/VELepg`F0|3ws̽,ׯ`0 HCdd+fc:1,@ ]+~^0}D y, Ą^q"$ ~M;? :S0q}`Z+0sD L{ PmJl՜[LjB[vNJ 0]-[3AYΐ0S6XׯC'@˓N `؉A&xe k ǎ#GYLmjAjJ4 #l/>PD3Hf,*#&f mCFadT 89 d|7F!@fFTVM~)Če5 =eJ]gnhBLqu KIY6lD\l,27M6(pO֧wSMʒdlA¤Iؐ;ziiϢr?v O%gIJn/;N_fňGpH[t X/[}va҇`a8pꟉ!Xh|Aa#.\?i;&O~&9I}seaã` 4'OvD;!ㅩ/U^۶mG>} Fd.x2^}5t c8|" &¾}`sN.Oy O>5oDvVetC01Ӧ!5-3ZrgV۫Zv3] AX v:Çסv%{}?۷8FQB n^Ҕ% +i,PSSu_ɿ|Ξ?r'NĦl08[t+Ҡ-1E4{Gƍ2 B^ :cubC-4=;w=wG+V kb|q  %гgObFF0bp\ >ka4*/%&buFq޹ 6` Caa! 0s }C9Kqytvc /.Ƴg%/۲,?zꅬ̍V/_瞟bqK"}a4Q^^NW u?vX,]۷o# EGBkc>>> VaB\[7M kFGڵke(++ȑu)))AEm,[+-۫p%… PȞh$|MŶ (v?n߆~cb$86֭[_Zm YQQQx?yC(WGV$Y  Ma##q TkXmYx1v^{]vx)ur@wVAuM5, my[sSDGzM[}B5fCN9KW;z9PS Vkb_v~WbKq6΂* Pہ:v{ IdY9u=.]ժ$B\h>)عk23`<_`LL b-7A@X:iHN ħ?1cm~ٳx)`'d8 OM%v#k]=qq,OR뒄2b0`ْd׿y΂MZ&!׹ +;q1Ė7^Wkbf#\bc8L{OFl\}L!|U@QQujd"/ j1|0,\02V# Ѩ6q0{LKt_^<1&F&d恂#Ga5(:sqCy8x0M-y(YE ѱ0|D],m5 P\DBt:)܍+Ӱhq2CH]̲E0kvsyS66m /o+J.]0lޔg(iNj%ּ71tl۾P#h׮  d;w`y&ڴiݻ#HT;FXo3' lBΦt=k& y[[ބNC=1stHg>ٷKR0&f1>Dޅ.{CÓT)}x\]UMIa3G ѐk!?2Ҝ.F  "!ZZ4!v h uFn$,'{? ,nt>p,n] spfYnBpB1OI$h d B<+bXY5GZ*=)5FP㦶¿65:<lztI =E!2Yx0ubXzs 59>=Y:]2J.\ĭ[PVV/ܡ;`胂йs$+fc:1l8-|q+~)}:cFtΆPU,B!CͅbA$BHCP,B!-X BZX6Ɓ!ݢIY BZH0V}씠8B2B!ރRܩ6QgBB.jL6D_ GB!bF4h}wT,bB*V8gwGgCY B*FX0nÂBǔ,K>h CH~d^&Zڥ%;yB!ylo$TR"6Jw`Գu=_]$t0J2 CaL.8 Bk.6F0e:PsRwł7hG,] !uDz`cG, QP>WB(F .B!D]wAi8 {pYh B#R,!⿌F8Qz\PL켦0' u (!u (ECXxƜJ GG! l`O(;ᐳ`(HĶkV$H6bB! ʙDCb: Z'  Ă(!۱ FC.JlW.HP B'͐?Pw&BV4(!#J n%;'$' !B\#=' )GrǁBi1AܯXhhBqHpD8+rLB!/4Bȃ!&\S8B!-P B!B!8[7SIENDB`mapper-0.8.1.1/doc/manual/pages/images/tag_editor.png000066400000000000000000000161471325266516600224050ustar00rootroot00000000000000PNG  IHDR_ bKGDIDATxw|Te =` ADH$ ]UAAUeWײkٵbYt/r* Ă *-H/#30 3ɐyz^Sϙ3;眙#@}1xz@2sOHIF<@Q=Ϩs hvTQFݨ>f a&0"r=}4 jC յH#4B[s E#떠KKBWXTeU("i0aJЪ"^u SaWU I ?dy@1,+?!.HUEuT:7s c)@3i󏁫1A!7;* #x"'6((B5]h+H)N,"詰GMUWa.K`@ >#P5"T[daӘ_Ӎ_.2-|>']V Mh֬i۶IKӍ7P_ņ @HxCB#\uqvM0zܣWcN74z<|=WhӢkթq*}>E6xzq:ֵk@YYfN KPXSVÈ"ij_a7ݦjb?B !.g/bW '*Iωv#l|i۶c69KrGnggei䨑NŊZx~3.hT£V9#\e`\0$e3)av:尸5vp?u옪^uS+t0p0k9U]>^蝼u~tNSNr>p6'WG%Z:y>E[UА~uJe+X'ggJQ[U}5=6mK_.7=zzg%GB"2pOڒi jVu#axs~޿K[MTOJ$ytzvU]6Ѯwn׭ {okvVyoќ'+>I޼I1I[߲K>!ѵO} te`\^MM@,9f$'B+p ?$"_fĨ?aW 1&i))c|*;ߩoҘnYb{{WZ{]ZAܥRPJUj[NYj}=ڽk5X]4(&GÕ7￵S'֬U;rȕEШ!,jm`Բ5e9ӻzghTRT![6lwdGC:ثYWiUZz}B.+wJNQ޲<tPzGr\jږV Jh8a?D /dGvt>SՃ!䷵aˏڱe^,)ɚ.(,w6廫3Iյk#S]5Q]yux]ڞ=*DUr8q)hז7Uw㻪tRڈ6," O p?[.ڇaDy_|*tQ~]*v} kGe_;P[_xNvWU?TeePsyeZ=~-3%_|Mm _i$;`ݐ]}S{ڿLQP']oA6C5L-Y;[K=i詒v[&h` ,.ktW;|0E=]?|+sŭ;C~[M=_/j4u>3TlMVƵO9f}Kj䨑a$G}"N7<3$t:~RMX5afQ\ kqzj +֩1aY%I*T,Yo*U!/GC )_A5P)?2/ϮYN +aC߼@]ݜi੺Uf:=~!Q11ⷚO)ӻWp5J򬶸, 0hb+z䟔|-vĕӴǵ&<+oPM74en\8S?:YuүuhvqZYu_z~/b1e6m=3>ӝSfk;tп踍\ˢe> ir 5WĠPl|=kUlKiZ=g x KYI7k2V+gqn9sfEWTOژԵ W϶6Yb{joU-뇲?^d˿6=^zּRQnO1SЫ*` >UNObҫtUUz=D3;ROLSu:j)yMpt忲X5 m#z뵻ܐUe*(>KeW髺nH[%09NՅiJuҘ'ʱrd_f5S+kKak'뒙K?1מeR%|cSt=zԩVӔ)vs~,[s?{KS|Uwd?؇!6[4ebUW4j.եY}^\^ ;`&$}z%O6.Շ_4%ӮSݳKCN)񚑔i#ݚ{}IΈ?Seaէ]PޘtxmA){W*ר}@j;äթӏnznPn4M%}H=;]~ʜVM>u~֍gOay'tMc_׎WvM>c&epS~QT/`+3[_S꜡C͚T>:Lw$:0ڼy *_}5,+>UDGOheiw-sK<~4p0D*/'>i}t FS rsMЩĒ*: 4T`|Z{ hUڶmѭ[7q RAemn[Zuy$9THJ# jրGI @o3; PAbI;ms@MK>Ml >:;C:땉i:Um_`U1k.XyNJu:~?=0 QlزQFʵts|,4/EŚyCwu0B$Q):;vO*tuJ|t>:lʚI6z-Ym5qd/]Ŧ"-ѢKO)=i$ɪާAIn蛯5%*OI]);ߥyve.J3 G:WAvؕۧZE+ۧ귧e^=hUmХ~?5TBeհtknN?8m8#.?swz2Ir+JT~`A{p-ZS 0"|}z6T('׭Nr6e LP7W^Wߣ/,t^q<բ5-K4w}q譵1Uz49i4hCROG}.)ՐasEY7 8|&\6YS.)V< $9$9W.lwQaukPiR XVhzut;θV%pƽD ʾOR+^*\R$$U}?z:M`M3!QϊR53Y49"yڸiT)/%`XPOR|2ɂh} 0xwW \aTh w ޢ|m;@CƫjtN*0AUXb3Z: 0@`@`  0@`  0@` $j;=@ƹ#9hɐ@`  0@`@`  0@`  0@`  0@`@`  0@`  0@`  0@`@`  0@`  0@`  0@`@`  0@`  0&fD?\HQ^9h6mɐ@`  0F@QTҊoviߡ2IRq<@;T[#;'Jv/[uHFWJR'o:eWeUznJ/bKSW:CsUdާ#'}ZC}y#.nnb(\ss6jqS7JiN 0 tn7\w.q)>Ω㇨j=!7O\nP~3F[9J H~atjoR~QbcQIZ9Ok6gj;ML9UT_mUN_XN`b]/)á]=9kߣ/7i,QLCk7U֠4-]s I@9TT._X!&î]v];đzt^TPRjr[*j &b*p+>!ifn=6ƪϿ?vi3Tfm" HKmԿW !ab1dZ@^JeZeXd-I,/ު_/ɔi2}nq!+ϔ4eҨ!iZ~2pZ-'Ըx}xrgi׌8J Z73(4̺%TJRuD*H" hy ٦v= fVfmìMM`xC#Th=f~h9IJR -~#!0~CABv:AA Wjhc_@t  pC"L` l*eNjRP%ك‚(h* vG4"FŒ¨n8"80D t#BnT C@Xa  4CGаF jQSH*K-b*\px S!a+0(70B?7r_SH28( hwTK/)\QIhT h~Q]1,?#>!(/(ݎt:eQC+ZNTI cm@V5BԿV[juz^m6\R/ZqH@}#6aIENDB`mapper-0.8.1.1/doc/manual/pages/images/template_adjust.png000066400000000000000000000752001325266516600234440ustar00rootroot00000000000000PNG  IHDRSCbKGDtEXtCommentCreated with GIMPW IDATxexTGfNBBB $]Vpš@[ܝb "łP+A$}wsI(;׹{F;s}g@ 9@ OH BX@e O@ x2_doG}ѼKTO@ &4Du_4'U.T |ڴMʟ~n%O&&%w# &..fo |9~}{smO_ NoߓfMY(.f;|nnٖiF4lPZM= :2KQ( ry˟ 0v(y&0jWlX=c1lʗ,vћ R ڀ\.'<<"Sx esslJ&^Xn=mwϳprrk<}U+TۡCL1^^4o$/Ӆs܄4/LEH"4-4ADu7g~utXz%j BXhq&1r/\~HJN{tg׫sÉ P\9jլկGJUX|%gL˶,T\-[rLbZ+Ӝi~˟%}ѣ̛o`~y֫[{S6iܴ-ų'%'h2j =uIrM>033aZ[cמ=4/{ӗ5!%oT?_JQ.\H縹}1ԕ$Z-XXXdI @hX..rqϿx++K*W_Yv=k֭Ott[-?sٮ7T_ڍF1x5kU^H8p74nzۡP(~51^OfLL֟Sa=FF <"ꊩ)׮_mGqS/0W& dZO& $'Gݻ4`u`A8:QHE<=\z: 9&&&^9&&&ٳjU*ST)V|Dz+Z2$!XKDP0ytڶiŝ໴konn{¬#.rvp&oc8T*5 Yh(_Q*lތEKaieEO:w8Pl4 Gre˰kjլ 2zujJ%Çbڌh4ԮVN]|Vy'ݻf͚8GzAzH~177kί-^Æ FRvV} s |}}ЯO{||tލk:?ݝLmj]nܓbKO8}x_bRN.Yl m۴:Tj5hڸ#.~ش-&8 HyqgTƇJC>o'uo.\HP-* ! ڊ.{M2tȠ|/P RLj"Btq_G (@ T  S@ 1!@ T  @@ 1!@ T BLߘ3`А| p,BL_Rk{' RLyie(V ;ӽgozawǏ΂ܻw+۰ GE|0FZrS7̷07rTZ۲M$$$-z<^>p(Jr᧍QG1o] Sf]p9nzcԨ]O/ RbUV^e}q7j˥O)W٠q3Nu.CAW] zNw-:uOJHz@ g& Ąw7m҈ V ܵCGPt)J2y4~.Z7o})Tȅ?m@./]ӧOY[J%'NqygL3sviټ_͠[L9x&O%O^oǿ͛5ٳgL18~`ǏD!W fV'@ 4Wt: ˂t4mJ~/^Zr%lʩg('Oqtta(Lo|)M4Ưzn^,[xcؿ/oܠJ,[hCG`ɲwBO \x ?ڵjdzUq5t}b*V'O/: !9RחǹSǹ}*8Cغm;GMV۽u֯z5~]:dY*W Џ3'Qח/Q$&&L Ejj[NY/<̭N@iPt)-u_ٹ LJ ʣ1dccc͠9{2?h+i?:ϑUNP(*@29zx8G@PPdתEBxDֹ$>\.U߱lJV$I$%&· k{qqqxiʖApQC˄<~̬9P7n1uL:o=ҧ@ڷmKЭ NI,u+B`mӊ;wiצ˱NzsUN8Z=S b?ڴwl OZƱ@ pw!&&;;;:vhG~}c_R/_pu-Y3P\h9y4KV22Wn|XCxҥؼNA]ތrkřg2cn߁;jX'@#,m/K[%ԧO}TzM"Þ|4eEK Ӯ Wk4Ħm1> @ 6 HIOqo@  ܙ0k1BL@@ 1S@ b*BL@@  S@ b*BL@ T LZ'))WoGkvqh0Jn$'#%&"IRtHZnݺ'Bbش/a6\ Ra- AIHu{8g>)K! "q:II†!#Z~sm<\(^4!aDl|0=hW_G%'#ݿOw"atGL֭+Z'£Y:c)^FF*4&(^h{n&$&ۺYs0ٮ_.2*4tɳbJx,C'~$i]sYsacmMoӡ][:/"z} J\F\$c!))LBc\LsS-g?n'O25EJI1lT$%$W41R)g/ܼu+$<{^/+-?kͶ1y-thߖW |t+STVY4nF_4^ˆ ]N!8D$!9)8bIL!\z^ҳ!0܇މ-?o6X;qӖZYbןI|"TS/\ߨ吒z=2ccy^ 1_65X#Gѥ0N8:Q|e tK?t> +W(iW!(:wcmURR$Ӟ<'#.^\_ '>^!>1bXG:[+;|9r,1C0yt>E&;I&ux#D- @v {̂jm:O[$Ɉ V-F Ecnq?~;BCoIf-zhF "Gx _}3]2˱Ƌaxƣ7t܅PTrb%2mzp$kv"2_hL( J"Ʉ&눋%&6mrN̟Wd ^%N(1{ zrBBBԵ;)))ԪQ\s:}7o `B¨S'q/FJ#rvX[3s4* :NR@2 Sk- `miBHiG 8slB\2 ~DI<{|uB< Va7OqZWJ  B>|4ӏӲE3fϝϼ2|n wڴOzd"څx]LLqnB[&vmZ4zŻѣF`mm~L4kFFFli۶1iȯ9ȩc9z@'إ;uaؾ|Ş%4w)Ssg`㲝w={w8ɪ4kژ:k1wL.]kװLR=vH\)NIc'055RŊoTVcc :\.G&!ː9Sd f3Y>I/5Mjs:b16aϟccmM:Ég/ !!44ueꌙcGS^]̜NF ?i -5Çѥ]yo(JԫKRr2,c̗#ѭ UTf颅(Jw~'}K)aagq _ovF0/T.\$÷V(͛A[kVf ?ZAiKѯb^^x{˫Xp6`7_Qr%D:s<#aÆ;wG>eKŋ4hGg7h3@F 9r$qqJ,AF 9}:|zujR7'X]"YLJ"É".1t FJt:=I %Edt8Q1h\ m,\=fdDЯOo/ @ӱpbt鄣#<[o S&7nz͛5!**'N{>׫F3a .Bllo]P##Ѯ~%xK\x ~iZu(.^$s܋>}l~i x#[]x?`HƑޙkי6WܸM?iFǘ7{&54`܄I1{w92''G5m8֡W1Lɤ8|f|c;8ٛ"ՠ+x{$x-0&m*[k=}T 鑤 S:.${1>X4pe}*u>‚|ýpaʔ.TT8ȒE `ԮY-,~犈@z /`L*II#%$ >xHOE I\|F ZO~\u4+c+ُ8jcZψTjPYLsb㐇1LMMY&?wz޽{|٧٦˫w,ZNGߊ-LهDtl,훖Ac|S&afbSGEO } ;zcf IZZZЫG7.ZB6 "kbbBdT'9z SExbff@]?q2eJB1Y&bjjʵg$Xt& fKI!3HNFei IIHIIH(˿&rPLF?#ڹڍF1662\ IDAT]/\@{4'!?z1Ryʗ㋁Y*NZr !PׇHRzZ}!^֭9gnKriCZ̝MDm`fk"l_1$kS{RRcllL.C&kSqvСY\ :: }{p(&&IPT|5f&LڊʳoW~\ƐM?q2&Nƿgw%WJC1ml4 ujBr'nc1mG:v "CJ $AD)Vff Z-)ȴZ? ^0EEGs)t:]Q;9z8|5ժУ{7U <G< '+η;::ri^w*]$ SӶM+ߥ]V1->StSOpppjd 7yڦ={pqu#1>D _ d9 !KJF[6) "+;jo&*+ ''GZ4O]]nҸ!BCMlGΜ=};KVУ{WjլjP;w1c U(fNYM$ nݏI sT*5:;d=,OԡV))b_YGl5y{OPf V*ff4i;;Lswve@>j*/km`ͺDEG1cd>CR Y+K mPSSSmɚu9t(1k,Ft!Uhl퐫TBiBo je" A%"ƢQEFF5kQf#Ϝرl'vŋ)^܋sgӺՋdcc5M5K|v=lk-[^iS$n<|fMi+ XG~q#wi԰>%KRlvkֱ{>>|D O _X|@r6-%mȲ)ALևQm*@}GU$2ɿj<;?iˋ\3qhszl(`灵r?CLl >1}J[?1HZ-_G%;9>$%z=QQ<*_#aekV%)6s>r N3$1@kmMle|8\q~Fq@ls$.mDU()UByV elg:OS̽4 \Z܆vhk>sRRGGNPz$ *,b?l0VViF$><͟QXY AnmN.'~],:wH(@_䄮By:w$LhQ^nZ< ZHBB8СN.SlAL&ݸ:b>%V nj0)P(,1j eKW(P#עGt"br͜rY~nugkWkol̟qeZ- \~{ 荓Vٯ>}JD 6yYiSHTGGaHT&®(? JIڷ?47 MԼkv}% 'ylen]J=fL-\R;8 M~ڴFT c >>1[As7u蹧hW$ jL>$I3? kMUL="r Ok|%ޙmV%δZu7?))%|matqw~o>9ӹf-?v~ٻ^de17MjLL4Y:% 2p5N8Q|Ν:R׮_g谑̛;Iҫ͝Ko0ʐ_0  s.\ĈCqssӦ#>|ĪЯO<ݜ8q;u_ݷ?;%aaa 4Os9v|(IXG _LӦ"<"wRWo;^ ?h4}-}+Y$5G…d5|yl)ÈQcK0)Ym  ߧ4ߌH2(T+ժ#^JY;1y/]Ë5k)3+4fV=;Dϸ/_cd\z2uD{6˛~ҥJÀ/P; Ӻm{`c柩TG,m-͚ݷ?nŰ-@&͹}'ؐfۨTgW,-Y+#%Ed')]7nB1XՃc&))9js 63oDϸ1{Md7eL>thl"3-ʬ2->ISQhQW|*u7ƽHq,mp(ƈQcͱ˖,F1l($ Fcys^^O(ɤS3=x#RhQ>{fuʔCAlhӮ#!!O^-@eJ0R*fNĦ[?q!Ή(XЉ۶ DDߧ+Udú5+}gHs aZ֯]+MdΝ9Nd7o֔-g4kڄqҥL-?Ξ⏳(YD-Sӗڵjҿ_\Hw,_kW($:._“'OqZwll2 b0,Z8-یr!ϋ/S[7٧嘯1[TI̛þ=z,W|Ǫg߳GwM{t뒯gLL?w6[mC8<߭Zshԯ8V囯Ǡjn*C؎; f挩<{4_}($$g01lߺѣF4lNm9lYMӿ7{cw+07{EP̟}pyC؜y(V(zvٳgN1ƪ˹p"F|>&m[>aV1,%%%KV$|Z[fLSpaU@||]cPn ƀ3U7&4,igѼysC*VM[8zŽѥqvvhbTX+MY/@%tz}6Μ=c9q%K`ޜkЈ /QD R ffԪ]v nݺ4i۷6c [v[:Q5{.#IHI i-JEP;'02d'OYiܸ1-[`ȰXZZ駟P^,ˏM?y@Agg}1o-gϞTF̜=n] zx ݇RRYn] ,H5iC˖--Kuо7mkTT)秞OVn:̜3֯ӧ[IBBa|Liٲ!{0{μW#vچgj}"aa)Vǂ88dDEE\KJhhaK!gr=VF z نf-~5kSߒeϱ oR~(JJ%7VmzE5hh(S- ?;lmm155}ɦ.$''ZƍpBǎ'Q "^TZUrΑ#G133c/Y['XZZ0rp͠ - Ó͛aaaArr2111wzSBn|<3>]i'c"llLRJ~T*4Wkg4Bս[ʺX+P*(eI+[1lHLH:uOCi1RƩW/ϘŤ$&ѠA=*V^yM\_Z 177/ZFJej7INF.O]Vԙ$Iܽ{6mg2a7)7ab}XdwYp!F!222_}(=+DEE92m3{%pV\ t-?mņ ?ХsTǨ(LLL qF Jqu-gID*XZXtK-f$Ž&ynWl3}8=ztci@iϰqttѣGrJF.2:|͕߯b}Xcbb={Z K"` ,j*HDRb!O KKϳi琐̜= s3(fбC{GGGN<͕ET2g|uZN[x_e ܵ5kbjbL.^:.4::uX11Am6e2QQQxxxқytLA͈e9-RMd)Sdd$ߌO]QGn]?q27:zJ^^]GժU+HijY57ݽ0ko[nݾ8y7ՐW9/>^4_-K d¤)III8;J)')㛛f:nѱKK6R=(`ƴ̘5ҹSn߹Ͳ匟0 Zcrq1t1 }N8:`)xgS[p/f/C;>m۴h"=\=mUL2%KΞ[$r$;KɓXl9*zt盯2rp e,]-V.g֜lݺ-unΎ-=ҷoڵ3λcS &6Baۣ{׬}̌($ڴ<>.4,!CG:?wvDwtz=})l7_kO `׮gLL, @ 6 Hy9<dIz mATVg>=+kz[3kf5?99];ޫ VaQ=J5W#ߪ?G޷❉']ZjYj-Z௿'K~)+/>}:Kfر4jԈ˗/M:u?!oǴl}Hx`h%`hs(xQI$)"qN8*=It&Ie$] 1[zɣT|Ie$-gþC.H*'iӞ}v%Gz|,<~x\T6N/ϽR}_e >xHJ)Wcnj%.6.K~d-; yB F=}BI!B+o9R2} HNwww.]ĭ[ ֭[TRǏФIC&M IROz),ᑑdWbE^6.1c J%˗ښFq£NlT*)\pG%g?ro…Q?$v}\ΣGĜiNChÖ>'ժU̝;0O\QQQԪU  l(\0NNNZϟ 0\;y$*:n:fϞ͑#G:thf͚EѢEd߾}̙3ǰoĈQ|yzMժUܹ3ݺuYQNNN^&>>777*W9ۊZ IDATkҥK:up9vڅ^'**'OCJJ ׯ_ѣG4i҄7gY& !)q]ƍǘ1cppp6N^H_~Ȉ*U7IH <3gpU-^ı@Cx?e˸>e˕ų'm۵%6.[;[$$<}XMvm-K}0p@ZjJΎ }Eqq.,U)JQ, vXbDv1Kh؈Xb%;cAEDEv!lXXb@ߏ<3393 … 8:w/j~l۶-BnܸAzh޼9ƍKwꑬnݺ8qEq]5k&D-[ƞ={֭۷gvMVes˖-ȑ#7n'O0M΁>vZ` PQl96iʒKyU sL݇*o>;cǙP(PbII?|Ξ*п_ǯs;p-~L2gׯC̝;=z ܹs9r$' 8P< {,f"22}`lľ3x7nǸRNpGF#/~:u֥Yf,\0ÀȬ (7ݺu9rDo7776lȼyU'n8:;;BʶYHo:4h͛7gժUIPDqWLLiBcc#4tH&&0iDj׮Eٲeҥh9yn+W.VSu(Eyeϟѭ[W֭?LH'I/^HN={6]tI)Wf͚ܹsxCd;v@RQV,acccT`Aܘ={6/^jGcy]\\-ZiӦ̝;7˽aHyJF<=]cڵٽ{R ݻWf"6RD vU3\qWkZhvvv8::#VXA||kkk_ۛژ1cjժ;wNw%(T{Yd ={]j߾=ÇEQ1b~)syW&Nܹsɓ' ߄N|8nnny!'((M6[!!!ocnn˗/RJ˫/^dҥ>C)/RL ɔr .dرt:t#t~̙C||<...|gz3gG۶m?fʔ)oǎ#<<*UݿtR:w̔)S022bĈDEEB;vnޅ _ЫW/:t}ΫW2k,uZ8q3>QFPN?Na>}tׯϞ=q̟??[-ǎ#665k߳gOϟf˗Y`=ޞƍǏ3g׮]Ȉ3w\8V\72l0gQuԲӧL>(ԩÚ5k077^~tժU=F3f/a2sgMUIaLcԨU2\'D%ī;yE4;^3s߂mǧzo&׮[p4=M3 ^IhSS9S!x7ȟB<B SҼB( Bw\B<3zRG!0=s5JJAh QR !0u> / GJ1^wo~}0y F)5|߂mwFB S!0B S!B!a*B!a*BT!0 L2.]Z7oҸqc,,,ptt/ ...QQQ3ooo9vXϙH۶mQTܿ?wʊ-[ݿg4i4oޜ .䉃yM>St_vqk׮ERv@J >^Ftwio_R8tPݱà &?( ӦMhѢnԂ)W\_v]Y9!!!tؑ"EPH Nw{~,X''':vȃ0U7n155CzP{!""=zh={ngG4hJ3ڵ+O6'NȢExEe<o6mp֭,znoe޼y(رciժoߦ`4jԈ'O-3yd=7V P:t@V}h4hit999/=&&ѣGSX17| ݻw/ӧ3j(f̘Aj駟h޼97n54O2{l&OӧOsfWVYVː!ChѢ.]JG:t(cߺ7T `;|@QEysk~a ;122"$$${+W޽{i&_0?~?C9*Up,zŹshݺieceeņ TR}C1<9gMqqq<~X7)ŸɤIXlYԭ[J㉋#>>;wN $,Y$_}{eذa13f܎Xv)Ue^gϞajjJɒ%z*-zC|C5MΝ;=`c?r{e„ >w}GV2<_ `ffƖ-[(V fܸqhZyF9;v/Yfy` 4`\t EQ ٳg6v6iNێ^@/_βe˨Q]talذ!K۲m6֬YԩS%L_ŠA(Tn a 0e˦{B|̙3 `aapttԝX"m۶r׏۳` ɉ>ҒӧOqFʖ-Kڵ3핽&qգy73fLJU|޽~e-[l!<<(Fɸqd+W>|xRĉ:t/// bӦMpu֮]KժU122ܹsL4-[-r9L yӻw{U.'룒@M,WϞy^wƍn]z(.'d7MG|;fL;3զ<'hmP T4J恂Bpp0_̴iy긪s'!!QqDB=4yc'ۏ_~g6zZEEIx$a,,,(Aa,/ӣw_7o 7ʕ+̙3XJ(X8Ya\TiAS)y5L+4 &M@YZFQf޽z޿?D *pv-k):~֍;ٚ'B)Tؙe+V_է&#FI3+Whךr*X8e+#GŸk׮SEKR܍N]{Kh#p[:~M'O7E\Þ} nw'PF-h 5mC才eo(Sb%]_{y|< ;z:}+]"N(Y,}_>}A*3Te{2m y剷: &&e˖퍏۷ogбc,g}ܻzo>rڵ^^Jhؽg/_fDDD2s6h}(YDIř ˳oA:wpnݾMlchh;q(Qܕ7n05'?Ξj1|(NNDDF2j z$K_rܫXb)FFF#6lĴ?0e*nkuQ<Xn=m>j M0CG0zHv"4_jժ+N0?>J.ƍ9r$W^cǎfywc}tEw_پ#t}Μ9y?jKttn|"K^ӷ ԭ?]zGP½,3f&:_1k\Z}؎7}8[1c|nC&\rϟs̙{ܜ߯ΩSβe<{*U@-Y7z'NW01`4 XXX0iҤl%j4i;ץ{OӜL33QjժfzEa 7jO^(#wal߲۷ӛw@?rJ+2A|Тϟ?H"޿)'p||knlmZ[s s4.U <1K-`U<nIDATf~m6biNtM!Cpuu+Wѯܷ[oоKHgE'OB?<_juۛ[ a|ئUKx$ Ínҽ[֩M)[LeJ1q8lZ3g &vP^=f%q1JvĄ*^^?ϗ='Nez #jeɢ ,JsJN&Ɔ={p5t钭ewKxx8ݻuţc?kqacll% -G[oqrr`?yO,,,hҏ1c/S73y3~$Z &QPՔ*YKShQy޲3gwXXX8|(*Y'Op~NnYh1%Jg}lٺ_zjhƗcaːr|T&0p ֡%*##zxk7yVBh9w< \^}B4'6lHÆ ܊P| ]&X|.L?aaaaTPm7Qd4CR]?c3iR066w;.Ka cɨ/K5]{O![[3nΤ)h)^ܕV՛K3%G"""*oL0>mb/=s:RRu,|7oǎ'3U&pZ\\s|G Yg 2oBn߾)˗c111Wx JV fQ.e7n4:{CD+k<ӫG7\"x̌Gfuڴ)Wn9zC盺{^n޼ŦMx`vvutZ{3bVQҜ]|Y0ҏU2T,x_tͫGEuoO Ӭի[m,nOa`nOT׳g'R+qs/Mnݹw,BߦԷ@]_^2eˑK;W<ʔšpgαw=|5 s3,-p/j˩kA 'z~<}AЉ8(FtM"o߫OvF]7nNzܹs1cF5qqq}n-һ&lrbc c59]S <~Çr?Əmu[!W 'oQrI^ &X1eyv pLXZaanaԮR=n8wTt|JǮ^F6m)d{2|={q;?4xEG'llU.ϓޟ}8p`zEѝ+SHI4٥.@pp˗U E/L?0*U)D!;~Bml/ SSxcuj4}:O>fڴ̓Y[HHHMx)G!; q߿ng=_'=z{~O y9hG)AgSߠ!6v89e( ۶o5=uu5ɹ(6YǏv:-ZRʭ46(QҍGu*!C)^Vֶ)[_\hz0_a]QObq)bMԓ瘘13Ucnf NvҸ }C03EY<V<|&,E^}!M~Ou֭[Cxx8_}5:%A<3?@\lVy ?/[J۶n]׭[ϓh~0ZmxYءDŽ39Xdoi9NN|3jDDD0|9h0\;c)h5jՒpO5jrQJ*M6eÆhZT9LK??^V-=zĦM8::U,]8:]p ѢEs-]iRo1ȏS9z ё֭[``F!`|i'\333ܹ޽ytʖ-Kdd$6iϯ43S36lHJxzzFLh<&hjcc]U+ c]`ڈ=ξp۷`"-YL)~vs+Eㆍhղ%1ᇉ0w4ur*Ƽr8xG{`54ҟƖv}[L4Yo]KӰ~YZ*ד[ue<}=vԯW_̥n:zeYZ5ԫ@P=fΜŴoV嗹9}UɍѨQ#^֣ KWlmmYd14̰NߍOY|.8Sh޼:t+Vк0m[*Q+ӼY3-1W皾\gR^-gY4g=J|;r% sNFFF066Nd,aщ\ {Zmܝ qv$yN*U̟gݻt]s=w>ɥnVKBBOdFjƯp\yuussCp<=+Δ?ܹk++y]\\*Z>lV6WYr3gʰF_CIsA1uκ.\VK}o(.e萯4cfnnHEO?a5|ݷlٺu`jffWYö۹u֬[۶#&&SOSP!233{k6u777Y쳩 C #p42e'E 4VFC)g+T)S;`(*V Xt4Z&պxxD3TkkVhn\Zt+V(x6SnܸٳW吷\da* VXͺj4-_Sǎzs֔֙3g(V(? 2*%(wj(Y]ǮP]{bbq_LA̳8j;Kt }CzWv>lWd},]c^^,X l Lhhn{ ڵaÿ&p滸8Äٱs'7eٲڽk2A\~=>VV ௘LǺYf&Jb￳w>__jE CSVÇKwر3(W0z莧g%SO~kֲu6>S=zĀmcN _~9ϟүjڤ1L ("""8} j5jM .7r%>ԙͺ6aÆڵk|iG6m d̞ aa(vfƍ;XDˋ̜5gpiex z!$CftZtkVU]iT(/ onݍ`ن}t[>)ﷶfǶ3tp:| ~"$#:'Mѣ JOʊ ѱS'n/sfqyI{ދMc[Ȗ#Fc=!!Ŀxap,^9k?foxn˖.̜O;v^,^USXt[3[ǰCQb(BNy}="Wlr*V@ҥӬvZ.\Kf-$ 덌nxtèٹ}%KL)~HZ:wۛU+W~<ʖǫJU϶Yb9|C,Wz5oBhh({S.[`kx5Ç7]vgŊ_~w4l؀_n*/McJ:g̞=¯ ĵXY =wi } *?BVt@ fQ.e{ҼyCWFGGsJ0/bQІ(@ll,GRÖJprraT(_Y.n;۷oqVwQn5y`r_avmϕwA濞ifmԔGQY"EvmI_BlQZ?b!2eddRP!Uұwajkmå˗PBgTr3hZ.]U*rX0u/΍nri=z$9KBdRWxc]U;BwB0}-( O$CBd=T-i%'a !D>2<3 B4IzQB!xaI_V>gwR~!"1Ŕ:X ]0?~B6/0M00rǡ8ih,&iҔGM <BԝLHRLIS? ךfԮNgT6"hR5JuA~UM))z)3,9LS"82z,lL|BrL0xf PKN4H9|'E&&)TB8פ=PTJ4!@&葦 Sg*iS50NP?KMI5Tjob"L;m2TSLΗf3MNq#ۜhG*R!D^ U%UTN R%E6K==RU:+I=OTa*yʾPդЯf7 W%LyuȯM1OwKK,ܟCy_%{!D>TCCc(\UY $A*oєifu *-ozް?r4=V O!@iFy[!r5_z^"?+͗BHؾ``B*B!D+"DM#+qh+b!*NJƱe%nҘz A3GHzRo)0M#f R+$F#z/WrȤfR+ZiL+ eǾ}ذq-M$9i4mE$ ⦱U T#*F|mv@fԋVӜ| *2L x^R5NLjT>koJii zׇ$9j AOEE)( 9"rwtXuP!762+BQ[ȑ"Z N@ 8Sz}|>Z eG;JoW5d5,׌;V`ղ(* 9Yajqj2d_"!as Ӎ 4LZ#rj׈՛eL m|#px>Uj5fH#>DŋHM:ZK.U4X,V\nXRXљAvQ5RSQiiɠReiO3OL_hF}Ic4,+TTX'ڊQΩܱq/ZmT`C+ 4˅,ټjzʕkl5G, AnB Ża2j;fܧPP<>#Ȏ3B:)BRZaǞgX6:K>pv |DnN h0sBh3E0vC.aH640{xyG1J KGsY:o<ī̪dh'r4(]^|;J gpd9&QHq5($#& 8NIS;9Ui/zwXܤ558Coob[$==^eժuVp|>z=i)ItFؽpQ`ё߄Eo1ɵKIi1.YhԑinKE)N 0h#nI[1`9RCV R0iS'n!ΞzVŌ$tyw^-\." ^Mjt8ncY@dJvIArn]IX38phL[8-xIҠ :2'2Ȋ7BFW3(hj"vE4P| E264 r4(Tw>.DB My~Y7LBjT: ]} pLZ{HV۞@pɱ:KNXLT8]nΊg@U$cLnfM*=aejHU]^}tl(hl%''r9\{Z]s|>:INI& `Q3ww ׎ 1X%5Lz)\ ]0{[v ٕky71uGP"z]kz?W5<Um_,|KФrSU+ .AsyH Q̖';V)!7YD9sgJP:5<{1宧:|s0=Kd{­R},2h Z8hK tB(3此]T4^1ϋ#573(Ȁ[__M_g{+W # &ofj Yzy^5^d @v9hj2ki<~ibRNOj[aaax<^^_v䲲Z/_f՘ћ{~3I Q(xR@C9unD_ggxxd E)pi |~jz]cbSЪe"'FR;]k[ఔb,_^Iaܴ&?j.XdJ]Ev7R [v'Kŗ0;.@߭^dž?Q\dDžSgt,"*o;?REYʘu>b"@FW# ,CP&DGygyH@ [0,77M㍝x )ٛ&: >n;~+ƬSQZUGӞ\ xx'8( .N2lmC:9A%|Զ9'IVdQs^dQa9@y^ F7Sz AQ*_eԄ|9'sh2<9OX so䪶c+_eoE\Z#V13{)@c)@exO{5iBAu:~h$ We>@LN)\G׬%IoD;i@L= d#͸3ãPi weq"iF@[K7E0TƝs6jhLAYyPi(hzD.̀MNt} ٞ_jxD'՘ b"Q$QA~*(JË죝)hG*^TID/غ\$l6gF9Z0Eq(>\P\6ӣrJxɽ| 1'Li;m^UąRsLWP* t&t!`"Po^% ˡRLJ*1ݿNPQxqE6 ]+o/xq νSh3>1؝ jo_])J(hXGsѡY7)4zlxBN`}FU p+2*RdmQ>Gk"LejKE 7QjJ/bh̦H=MeeV{ziؿ 29&&Z e'*@4=2ȶk)=I,;W@b}VwTL d ֭LU" V\[h{2.G(I7;>`ɷPQEd}5c*`Š}^|7>ҙ}tw؁u1ORpU?3-:"n S-&5If&E%K՛%e$JJqEr֖vfH̝ߎxk:ŧhQT1tٲˎFǭ(5u9?8ѣ̜ҕv4*]!ɇVVHJH=Z\A8( uJ6zՃ<24 yЮ=5Jb"ID'ϥkX)qC"C"s*X#Uq#($jfm~[ee[u(B^=WEy9$p8(- ^*=3vr"0peyk/xu5.ہT#X;2V[ɛS~(wʢu,(b~Ԩ [1F8$]%89X > YQШ=G}yղ=r\>ډhJXQ>)*hvJnuB>}owStĹ VUr-a՞F+Dpso}(qa VS˖i&HT,zhUl5v݂z@Iar.u f.WOśx5Zܲ%p1,lQO!5ZO&=ZOB|JtG쑌q'y$jI*Vak;ЌKaV~xJ* N"ʨí᳕k ƀ[Uu{IV5fsUgi6&b-/cn#vx 3 *҆!5BK0\S* )QJ:v/͈vhCޱn<hO& TUfg2WkR p_us kv|`{\RpgLU{-St{=;t|Cסhr{0LGjx<n7lvtzm*nلY&%?vkkΠBO^Ō NzDلhd *)jM*j}y:W 8UO$Ш1 ղEU=(IT*45֭u2U.lFClUm*2_4?p`COj,G~.V+k=O gJdrJĤfJQיV:s9@$&@ b"D 1i)&bQ brr|G_Bt|I%~*N BL@ D BL@ b"!&@ b"!&@ 1@ 1@ @?SՄ&!ZLAt|2|_/W,[rF_w-t+Yso`ҳy6Z5kבՄCe; ;kmq׽d6'39w;TfEjב,.r/`4A#\p,$~9_;o;=&ŗ_sWrג_+S{ j2bb6ⳏLa'n x\9Z;\fNoW]Ui޼u/+Wyse&Ntyd ~p2mڴfڌY 6mX^R 7G&fϽ-Fv}r>$15ge9|8ݔ4Zv8NϜMKJ#39} lL<ڑݔe5uuӷ1le5!#g`yyͷ6cfYt|2< K^KHJˢG~<‹Ȳ K/Bjf.yOȺ,yi)6ldӺ<0~GӦMr]wp٥ӣ{7L&Ѩhڶixdg<鏒ݴ8:w}',?cl睷EILL久O3?_ظX=Ï>f#3ꢑ\rE-hߩ>87K0M?p[o;Mt?IIIuU2EQC!?3j٢Na߷/ZNK>|> DQ{:́7oތԀ @jڵmßHaG~#& h$on6[g@/ 7hXZto`ӷߑo~dpȲs/,UV+o*׬`?y>oӶMZjŜY3(++ggu#~a6@`f X-;w"?g[ov{8缑8N֭[OFz:O?;oGg|ser:wEZVƎxgXrM˯s. ,,tZLYRb"E@Pj.(, X]3ѽ[j!3#fMuo~Ǝoɑ#u+:U˖!*(**"iyϞ=gӮm~7z?Ag޻KO=͗_}MB|<:f5ks^P"V+]vեKBt?={tlkӦjJ<0Ǐc7b0ؼy ^A-۹99h46l쏪rr ͛5姭ۂnWZZ<3<dž50 u.7*э5kj<oGW rj$I{n}]<!ԪR IDATK]L`ŗ1yAۇB6z8d6zkiFFN3wo^똿m;ҾS7ڵh֤I:>6 : oz[\}$sIjBnVd7m[oϹgZKYp@-׷?z^/cn϶a医u ࿓m||k֮cE̙8hu{0'yrRY)Cjqco!62(..232:Q>3>S=:Q Dxw6? jZn3L?\q 4WcXik֬et:rsrۻw`g3BnۆV#/GV˧Ϭ9Xb0ء}cMmرs$'%3r6֐uܹkw@|4g`dggoglVMrss- CgNGVqlv˂Oꞕoʴ32\Y0 .8@奯`IJJ/<Yg1|Pzţ.bsK6y<gNlXwz1vA>T}I4')-G^̟ھvz =LZĜ8?V:;`'O ]t|2׬ y'z:p~&K^s^$fЫ>:Ï>OA6oEBJdKKkK곩( GHHɠ}n\V9P/i3fҩkFi3,ҲHHɠG~|Wdž|3ѻ9HNyOԲѣw?R3iֲ-O:0}lnORj&z>LVHHɠ]ֵ1}XӉO&)53/l4g۱X,A`"vBjf.Myדˮ }rz67W^c$eѢM~`%i S30h(7o ^\Rcn%#n߉u^2sody߯f9hcU| >byv} fde/¢"aaF͘U\ǚߠRXR&=o}P^VFV#S`O[9gI3|X6wʨFRiљsq_~/Î5g.vSf@~UY&)1|8t#Ołd l3v6l`{iެ{'#ß:gP >^'.㎻Cvi:Xfj+/vm۰s.(QQ̙5#0)Z L}x -7c髯qWHNN|\[nC]%+;/_>thBlL >~?^t2h9z1xxw&r'Zg*&QQt ym:tfE#kj ]:u_>YˠԭgYa#Vma#cv7kƬr 3푇UZlyaKl3:v]0:mۇ*5\#{%';i3fѢys$1d $IbG7v }VeJ6lDdd?+p֮Yz`C?666nΞM7\σ'0i3BI^-ghdOr=wՠBz=#F\l,:vF1ޡhII#;++F]tyv|r {&tjΘ^/KA۶ |_ysG fݻBIC}.jѼy5.d͎t2̚fELy`_;Ν|gssN;Ыgzm?|Jn:uҧ7VɅԏݻiղ%K^~~~iFj1iLo&n&1\Cm[X| ;`pP:'u֍~ ϿGRj&?s.п ^=IJJe 1 uFҼYS ߯O п/^m?BDD={tg*:hڵJXn,v: sص{0_sVQv_~mKF$%%n={?rk%6&&7mZs1AW.}5DfnsvnlC}мYSQݻwcydڌqҳ`ZO8j4$%&PRrɔҹ[_l޼͛XNEg@dR^9wp{C:>bРЋj }Ռ\7gT.99"T.>)ŗ_ӧYuFo0fFs9Ø9{.+V`X,f9 q1|АJHjU*5x,~q yQ[]5-}eK6nzΞN3a"ӧ>p\qu_/ WрxȨ}j=Oz` >͡Y&xؠY}w: ΐX:;wv{ؼ'Zj @몔iڴIl6dbu^cn"XɊ+NVYn}gkm@VӮm@oZYpeeޝaCW_GҭkRCڏ,\Yq6ɭs}X6nZMmSrX+3>z囚kK_bSXR,K[Ljj ֭ZѥKgaƣbP,7χ+(EL&J~fР\xyjْ.;GN͛?wp4L~w,\@Ee%&j>}zʽUjaaaU7vL >? hӺe>.Ve½w3clZ }`2[ =&t81| C>0\~ܵGgaM7ׇ?E#/Dҿ_?|ʜ֛eSXJ 0$8Dq{LJJNV-yҹN+Z1sjՒ/>쬬W^zib)++%..K/}<Y#POxx8׌g옛e^|ǟONN6/EEń٣sfy /87of֜t14ÉXyᥗٻw/Z-[{xt GJKINN'/b=,z~1O^')1^h/xANvv}&ii<>w~DNN66}tz^yuZnŸcR.O}=OyyfZlAT?EȻo3&!>>~',h=woi3fɧi*<:k>n"q OkR p. x^ W-[~Ibٜ=pBHkoE֭0˯’ i3B.2Z Æ <'~Jᣏ?aOrIOO;s۸q9gXK ,'3%U !&@ b"!&@ b"@ 1@ 1ƯSٵ{7%G( :28rsrD+Lpbj*9Y ۇ} Uq:% <WeXmv0/(PXT5L'?=b6J\ l'v3#eѨi$F.#D=哖]/y ȊBB\ ɉ/WTrP>r0۹QOfZJ/t!IY)DOy}>VQ$AՆ 7+'&7}RHjb6ЮA4BKJ)-r$鈌0@^>)I Ma|4=1QCx<^2SPT?>h4zTS0u~L1Ȳ̞yHIJ@Qلth\Ÿl߱~ZNNY޵{799Y I@Pdgd{^r\F"+&XZc르XmvROtנ)fYYUtzINL@ӞQm*i 7STR#T4Ⱦ¢6;Y ^EG(>R`ГZ)m.,pѨ")!EE#=n|>)& |=y r9x˅CVӲYjJÅ8.t:- 1F"=p8x>$ ,Hzj>(b;\DRIjrvU8@Q 9[ؐȈcCm*J-(*Hi9^J'#5^רseŔUT"|LYpEVdY@^>LO!&*͡B6jRQT(9x^$IEvF*fS*(҂A{)Ivx:\H ѠiNҦEZ-eUZ7"h&)!. +/* IcVc> z=iɍ^?Q]JAQ ex<H >fSGJq:]jb&yWj4$%qoU(mZPi 'Ç XmHDdԤ@1>|dYAbR^QmE/[Hi9#$%`))-gjVt{A"INr_X,+&'rnjE:6׬}vcEVcٳ q%'b;8xVCTD8JՀCTd8b8L~av8ؽp3UCIRf|{̪HHYE30cžXU@:!p8t: 7p"HDyy A)M!ꘔݮ=Qkd(2 ٽ ͫ"l CVi1xć\S),>Brb.@]JMdlW HEndE("<@g씖U Zaa@T{rtWovfZH~Ii9Z\ЕU3P=kR_&G#-'ed>7AyE%ٙi_. JHOIjBAQ UF D,^#l:ǶJlL%x^6 >ǢVpJ (.,&.&*5;5}ؾRb1Mb D0J*X1fSXm 11Ak&25Gx4kF^ϟR^QX3iH**wm5ɔbmuIE h4W(pAQЀ^6)lBQ'БnܯzДe_ zs࿛Ha qp:1!iAAcxmb; 樲QcQ$aFUOfGϿYkHu.O=k4j4M`=fc0%$0xcD] N;*V JJ(9RFR!2>|\f;0!3PZwyuT*׋I||v{1l$=KRѨ8!EAU5;HMN hΊWRIDGxLZU,HUªNJ'd_^V'epq }mK* ^GNfz*گ_̱K)I'/oW S HMN lW?UpܝbÊUIlPYiawM}ǿIW:WʞPDY"P)"(< IDAT@{)Pft-M'e:WCsN}ܹCm8bEDd4IɘeddR$%%\'ZTMMOtY'Ը] MJNȈ~oMLOH,LMO -=ؗuVjxI׉ivQBbRGE6S|r5Z-IEvR[G󎍍'=##rid}nE[IR._84v鷙)q y>9%LvX9Lç2Gz|y i_4o֬U耉A O\|q$) 숈&84bt+;!'$r?28iwSԴr<rb≋O~DT.v@rJ AW;bh`j+.v s3KtYubia)C#>!{%*;s3SV%2:nܺCFcG^mQ\q'm>!]]Yw6Tu5RJ%܋^D Iߏ|,/*MMHKO^DI)$%'穩Pd*4M`v6 TݼCl\<ѱqܸ_q$$&P( <~.k&YG&4jP#'N掹HLL ܺs͚yiNSW5/Gǽ(,Q*~+أP*~d4*S*d635ݍ{\ym]w'RĴz< s3*v/[wQKK mnfJUOwqv0 K slRznh8z,,j)?fI׉ wCtW5iz)NUvDp,M*jTRY{ CbhdSE1-l~V2! Cbll炁+:@MP(ʴ髬ҲV(BQ(`}ߋ!. OtLl6`a7|z;!(Y\r]P1XZ?қKK$"Ƣ0\] zcLLv5,u'tz?y'$rm|W}h8U\݇!^jZ{*nνd HsNT &/9[ÛHyBTt,7rP~ٳJj~u'k]<2G:SE D/kE!&B!$LBH!&B!$LBH!0B!@nZπk׈#::F CZM//) !IFF: E ڵT""a"D.qqq4__ BvٳRO3O5iH!('[3 ""q,[kkl8B $'pIk䂝]OkFDF˾iѬ)&?*}?őx) !ʉG̕_)޵qqu S33\]MN<))Onz#}jq7e2SpIv !aR68Im*GjkZlѓE|8[^oGbo{-^ʂF`jjJ*Uqo AAxתUhښ}qBBC=wiiyiZ͘Y%`ooSG) !&QXZZ{x+QY>OvjU\]~t jk:vy=u 6N88Ł߳6ptvq|f_۟Qٍ>/Kڣ7^yVcАW~._SԨU7k|:L:<  1 A*Py?g.ͳ𑣺)_ff&߹˗Pٍw*v4h<NGNхTD$.t֕,cC011Aղpb^: b*f͜NX~ SqrrNZl9zAC^qcp\N<Ť)SX"]pm:>Z܏@¯(\]]FJ%QQ?ShhNaEr50-ɓW \L~ر^WQwy=vGGG>?d}n͗5ϰ*>UX#G7ڥ =w+v !$LZ0rp} _obW_y&?n¼q78Դ4>]S0!4lPݿaǮ]uΠfyxtShڤ1Gۭ[%@7\G?*Ur/$Љ-3oGJNmӚZ>>tދ_vF] M5*r~+η ]:w"W_>|aP˓Y31k\^ $,?lZ2mMBJښ⟻MDGV133cXddde 2GGG,$%%sptvUHHH;xx&66]Bq>..{{$6gng3q_5YGVVV`ll ٳhۦui. P;/ccķyبe7f4:՚u{s+>vl/}4*Rܥ]JEf¹xڊmo.S!I`߁8V[:ltt4Wo6Y֎Kmū^fy_]Șu\rF ;M+++YϝJU^˓sgӯoopb])u%bcPcOPdjx6h{3?7*S{!CkѬ)ǎE֨ ǎЬib^ua0WbtVzbjjo6tnr՗|ŗx$$$ t֓C1x wTPe\ԯWSSS:]K+B/[$$$`"VK@'R/>dw~WX\[n?r4mf>Xd:::rm݇_>+++&##c֭5kǛ1GP(٣Μaނ4_ѴIc|3Qٝ_Yp4lP#ߏ`wHHLo!Y4PKKK֮Hk8Vg˖b>KHH07uWf1n _o̬9ŊEAطN_-Py ļ";l #8~hpigt "#t`oogGoA\:w6`_.ԬQ#_> O*dyl&@Bvu" i@Fv4. C~*O!G{&B!lҾ/C˼Y \!5Q~,)l3&BZRX|ģ!\Vˋk׮ٳDGHBk$j5U%L(UxI3B !&B!$LB !e@ZPDFDJaۡS'a" Q*UP(ҍ\۾([&sHΙ"M[B!a"LݸSRSѧff̘.vvܹsPT*I`ܘь3.5gΜ޽{>}:{$%%j*Z-f͢[nܸq Y⡵jB~yDDFaii'HΞ=KjըVgΜaVj+"" իތ4h :ڈ7cntfϝOuptvqUޝ6uPɍ:75-eddз |^OJn8T{q}Vm;Y {GchZ0;xVg  w9~}MZѥ2ǶB%oܤ{_wcDTT#Fѱswޟ3K> '~L}Yhڲ ?lN hذ^:˗/gҤIi5kְcK~Xp!?h4,_@Wn޺o`jkU4<'BBB@0d{D'Orʺ+Wȑ#DGgtqsuؘ۶wǰn~ f#+:r2~~+;[xRƙɾڹ3og.`F./Vu&z :NlZ}F#Gԫ|ɛƐTppfMNY?Ϟսwi|۶.^ uř-`ӆukw^oK.%55S3223LHHyQ~}_WĆ9 w>#3Щ#n>ݻ(9i*5֮oSnW γcOl\]Sڶ[ѴIcn'{Azጌ:Ñ8|`/keMN]ejhР>+?pn0km۷k?GcuhO넆޷mfggG͚5_zu:eݹ^^]լ0Z&>>K2m4,--sMf̘)SP|#˹ > ƎIٻ/߰ קMVuhOMYr ϕ|j&DDfhEܼySƍm"#^߯,%44־~L~w: :J_n};UD\\|^*aaSZ5]sgm;쎣;];YWhټY˰SGC߫܏jUy{`ѠAZWvYKcƍ$&&i-006mٳ(u6%l6iܘgB|j&v3_ÇsH}m۶/ޮѵK]ݶ|},ZBomoI=k&|ې^.t>&Y:LNc#V;99\ԸQC*88Р~}{7~K@Qy,eok׮X[[wUڴiCYr&ʟ[J-TJ0`߁8VgL֬Y;//ۦMסe8x.;\7ƾ{̴L?OvSҭ[ݫaխ,h*=sO8::ׁ+R:lzGg ʊBC=J=z͛CLΝ yTR ݧk-tqݺH!>oaТYS9LQg>͉ؔcGhִiӺx._A{__*V@l\7nBRѪUK6Ox ܹ{իE:uxN>KV\EMjgmذm0S 711h23;k! {y(ETտoZ##CΙ@=`| zPzuٹ'ڴn +eٳhZ5\j*BCC=z4Νӽogg|+1c##fN{)f`dd_9֮GRbffWk͘#0111A:ݻ}R[agd#+4k"*yDDDbeiwM| 6-`eDFE'?_\ b/9k68VK~u_|Jž}taRv-ψQP(@v5jH͚5;bxiЀBk8N~w5z, c .`dd7N8ARR͛7ի[Yν6b86,Z k֭GGG6lYk]_vC|bz^+:10\]qȨ<\xVyݷIAb۶:oWSRSqr+b>Ri2$dw^'@*ddwZ@`/<$8y$g" !)&DߗǤHDBHNjJ*[FEEcOhg!3&]{Dw^v#;x0.{333Q(Ԯ]cccԩÞ=y%11#F`mm5F"))),bBgO!66mogZd=ܹ˒O>eoIKKd󉋋`˖-TV kp=zO>,^իWӭ[7Ξ=7#F`|h4ƎKrr26l(4Sv-ZhNͩBeV^߀8zB}LArrn?MZR'W:vΖ@\=YǽIJJ 0ꁫGU ynNIu:TFqpr Ggw{E-n/]k8{YAC^-Ѩi *:]۾w۶S^C ݱY&j2h+ܹsWa#hи9iiE9ʖo.r?g5֮Xm%2I&ԭ[WXx1NNN_???6n܈K,͛|,\2x`͛_ݻw5 !f򌊈K^4l؀-l޽{L:pIYB=w>ǾQqufΚ7ƌڈt Ľ{Y Zq*]k1;věoM u*Je?#F–o6T*[ZLGѺu+b>Z]ˇƆ .2m{0z?weK`ffs2x+nًkgP*ܻwn ƒyC`ddDv8rkYIDATHֺ' VѣG0`@B~CگVald~,أreZl@jZ LػdܘѺq֯O֭Оb*ΟwYrдIcn'Iu.%YatD󼟞μ2b0oFjԨNճ7;~Bo:}#Gq^jZvKKK:u˗_-A4_AWK-$%%N&MHMMu,[ڵkg]h(yqrr"44T3S~EMC gԹ版ݣ=VKzz:qqqXYY 8̟g*p>j4nBYnko;XXD\\|.I5<^Jn]QT^Ftt m[#Gx'\L^Se9_۷ ǧ7oD0~xڷoO`` jB)i!a166'6f85\WTJvޞ4ٳfη I |v@(dǍM|fޜϿ122.pGw-^zvg2sB]?beiQSM(J7niذ!gϞ899gذ0]#oxx8κ5 !$LQ>5kc.LMMuG!--3{.;ZF_ٹo}]7uN #*: U5kp-zn**k=f-[w^222000 ==믿2bDVݻQ(hѢXB5앗/ |GNJPˋ&_]J֮̌'&&1GY_h޴ /ԩ_|Ɋhִ ZԔTݴ^SJ9x_nܤ>%^e}Nzu155CchhȤ3w>nՒtE/!161^', 6m&o !a&:&Mf&}zx=镅?Ey {yHƝ?>UVΎ7o &yfΰaXz5[xxxЯ_?&OZF2p@\]]5 !ʻgxڊ_v'Ng,Y28!R)3w^ITTځGʺsgs)~߻~}{3iVN]ֳ̢ سo?/|U",TD2)}UxZm%J0і09vG#)!jߡ)}c~sVLʭ$MG{ӢV[f??vmB]d|JՑݕ(aբ).SΌw~a9|Jj*K,Y&8T]:k׮2YG=G[3}w*K~B=X'XZZx.\@ezjׄxr_OxU NՈH Ba^e6KQ)UOING\pW_S;4?7i3f}o9Х+}_| 6|CN>Fŋt@U`gKU/O&OD\||i޽-[PގU~i q=ݺt˥<8ydڵX[[3)ؼy۷oCmeŀIIIẢ'3}vرcϞ/5uv¶0oԮ] VyYB7~a;U٫7Yߥ&BAh@іje$O&4ISBhenI7  go6m¿c'4moړNĔ6h ɓLyw*~'mwtԉt4-bk;v2p`Dv܉F%=-}{ХkW˖Ν;>ğ={_u˗]&&&Nw^v4mނkэסδm݆׃,ZtfQ#G2L75%K̙:|85_~~d1-O :) ( 2H' 4Z J-Z IҍHMK#&&{PUŋ\5L~TTmSˇ]:?ӵ{w|>=4iʱGٲy3tͧ^huq!Æ!u<<=K\fmhZ-*$''3{~:k(oիW#<<FÿKtt4O`` IL>@ZZnʳaʔ9_  $eFbUaRb`h2(x۴ !Ĉ!mQY7*h97jr-[M5‚?ݺq9pv2fdP;OTZZ5BBCq(rp^?q4 3c箟Xd1ժUʕya@ԄL223026\^cc̟o<|²9/!JZ3jh23IWe i  \ܸ؉2ØJƍHQ303ThtVqps711׮ѧߋyPNRallV>}<222rtda]|2&=A0g ??zEf\mm/͵1ann}g9F|MoKh4W!a"5&)1dIIIf9WAF>tC#P(=߀||Ǐiy`\yq<^.^$665j59wo=֐ZJIPѣ;zYsz莹!<}ϖ} //?o3m a I̚=CCCZnMFz:iݚw&͜bjj/\ qcprt֭w:=lɞDHDE[§bbb ZP)Ux #c#LLL1627 'q1͈FѴY3ٺ[ø7ߢAFyn\0:wFBB<!^UбSZ&һ{W yaffƵ@E tjy^?^IZߚI]vjZjכ61rh25*VH-HLL&&&KKKkDmMKƏ+O>]g+>ʊ7ƍuV=-Xv\||;u b}L.~b"a"L104$33Ln u8zR000@T})v˕/044V:Z.]sFDD0$&&Һ/.BRjiظ ҏ?Ga`hHʕi{}5b;p(Vuɰ";l #8~hpq'w~:g5bS*/thN CKMndf>3tN7NNeRũi=d HsNT cgs=m{)/!Ju.A<'T՗HK@TBe9r^HD4Srnh>˩QDŽuProQѲx9w!qu;J Rņ1UKyVZKֲƷ!QJ!0BQZevFJD!w+w}66_@ɞXNeMN 'gfWP;*DvSɃ5EyQ<&B!ZJ+Jf=^Eo Š ;TIߎ-!x:sZ FWnZW71@C!"A"OW«&E􅋢A`@I!ij(uE +gd E K! }/{K_E)?XcB#\hK& (B<5O ~ZBoD"m1UHU7}huD"7}h_Bn]L,֕mYDO*ݪ̧O)DR+gj xYK¦ *nDOo`~7jт%^"/gWǝd I$6wo`ܴX᪊D"+dqЭ{/Jř7n,[T֍DOC[FK7Uƫ˺G|80ֆR}n$]y<ŤZYf_Ui:}(n$;t@obquZdRu9~Y5;9:֢JeY7}/9)1TQtC ﲿY⦴2 X9s lqY70޽?2t$%7 %+\]O]@η`e uꛍxf͈O B"ݺv=KN߹:W~*psJV8ֵEۘ`%[ Xhw̓:>~o|iJƍp{7Anᒥhmڄ[nﴡ4mfSjT)ISnW{#7//G N͌i:lN}Y3%Їľ2+ Za ajA{;;寣2Brc[uRTeE  -rjSn۞9a_wq!6}oI~}| ƿ0sabJ!˗!'y4;w큷o=]=`~=x,<%%wuMqq ? .%)p_9spLjkt.f|9 }@n }x8ڷW͛##QƍfڶmA͓mY%!q3uл{:st -Z7oARR95BZwŝb_cxxƒ1 -Q=|x5.Z6wzZH)t|Zۗ 9#N|ᇝ}-Kt ΟS8y֮ pvrb ҹ[wbΜa=wʊ={(/X[[ӹyܒc3%%) 'a#p7|#G0i+|us.wY.^ώVdWvS4X'495o6FW_~NfV&Kx|U̜YrD̦ ϣ !))Iw 뛬oWJ^M,eKn9++AqIIҺ~:i5xVA>CQq E7t[jENxnhՖ/es߿NuX5cيOHKM2{FL7:Cp߼QFϿЦukt:J]ܻo\zw/-^G?׼ѻWOfΜ@۶mٷgXE>}e˖txcښz3qԯ# PΝx"O9@P^=%~nn~ßחݺ]k7??B_3"e˖lٺ ld1}󋕖,պT[ !.o~c筷fm@ff&I&f^S yWE?3C ?;wăT+'GW\\BfM+Ӹ C"9%ILL7_NSqK~___|r~Gnn.zٯ+,,…k&L>SepvvƣN?ay|ij4 BN-[xo|5lH̙3o{,ݻ?\ekR7{ k6K\Sk4y_hWc:jZh!pttdKqwwu_y|[['"e EEťBޠK %yV]PF`a,p)Oǎޚyyyye&[rBcxa R1ϼ5k֔͛GE*Bĵk̈Xs2eG-.s FrxEmyuB0͚rqY7U/;+qq,YVlRj,y|~st ύwk?U%.yBOEm]ڢiMy%_~Ϡdž2vѱCrj;wbi׮YڟZZm~7n,kޜm۷ W />ò+4x_|O/Oxrrhղ˖e nFRR2O {1c#\NJf*z^4Sa.zNyF)1;;;Ξ=Gaqcϩ닛+q -Kל4ּ<%=NG.)*."!/x_ofXּ9kVF~}Y#j+5oy5kX/ٯ˓#~݀#M6ٹԺvd9ɯL >,Apa~g[LJаAzt;w޸T5ɽճğwfZ ztXƌƆzxk<1Y7YӦ`俞 Z)?ǷI/-WO?щft]of,u#H>S t*= 'Yˮ e5l-/k׺T//pa>[7}SɺH$$1}'g`pF?3>t g岏h"yS$>ZmMQQQu3*K[ym[;Y7D8?se %%YDW/-ZZI~D֍D"V $DD"HK$DD"HH$ɽmɒ-*HׄǏY8NNڗH$-Y X8G5/H$Íw*#;;[[[lmmeK$I- lFFF©Sz6|;=T.,''qϿw]2~‹hа1z:?sWx748~LiKֽGYqqu'oͷ栳w… ʗHWӧOG,z70|z;td˖ l-?XĢE ٰ! /'~FQ_~AZZ\ZLF?79oٺm;'N`{ j/'QPPYʅ%%%HOOWD"EzL<Νi˻@\|ÿF>Y.<1o9oCyb0^}6Q_}˗X{ {q^L^VMʥw9F#?J i>'N0ir5ZMNNa 6mǏ͝.uHMSĿZFMN]NT=A>F&puӛC"E<;;e˖q!mƶmh׮#Fm԰!q1Ve/WϞ!ؿ.]"1z޴I~~~pQs8q j:N:gz\BGң{w|kҥKg1{6NN)a͛7mffΘβeYɧ} _x".\DK$O7w}"^~e-Z<, Kn:&%%\z̛=z?01st>3BZзbϞ2yyy\7҂WilݶpÏ߸Q#7kڔCz৛jm6t҅' cǶ-pɒ[ʣD"EW^i&"##aĈtᾩ,;nR/sys,x uSBCCyU5ex*[RRRc222pvv]t~!WjTT*NN\zU޽yO?By...tОGR%IRolZYY1~x&NȂ t̝;oQ&;-- RSJüTxi5o^a666Ko ߏ֭[cɩt&M'P~שS\PPӍ^N}~ϏuΝ;ϣX3-rFVjkGDr[O?=""??EEtpㅥ?Ts'*닿?;v~&6 .бcOL#CQRRZq-KJ*_~ ;vP n wܙVZk8%ŕdooGFqLF6 nݺ棰C\t(\k׮@ǎ|] e-)S2ISɐ!/~uϿKM1Ϛի;z=<:hYZͬ_'99`veHTJ,Rĺ^4prvVI..[GR klwg4uK$Cg8K2s<(K7K?1}޶%*[D"H'w%({ V+o=\M"HK$DD"HK$qD"cV+tGdH$;&xO/j%Y7:HwD"HH$qD"HH$)D"".H$;mᑃb0d(6eK$q!6k(epY5k&;HwaY;˚ ;z222BJH "wh4r)iӦYXII gϞ%==P1.\ҥKBFtϕN\\x{{ƒMV_G?pe._L^^xzzJ2׮]#..>>x{{+Ǻo> ΨT*ڶmk|._\iUqttJ z4vz*NNNHZZxVVgϞ%++KCOOOի@||$$$(Okf~~~dffrԩoNK/99rן<q9ԩ 4 >> *u+I3Rn]F#jh4JZZN#˗/D~~>jѨ`޾ҁѰaCe_zz:YffO! $%%ѢErY5v1\]] Tn¿rw R2)**"&&"<&:vYII 'OXq̕+W?hܸ"rjr)Uw "##pu T*i<<<<'Wkѐ˫mF\\F+++|}}IKK#99f͚acc#BxbB5FSn_Y 2߶JBVŋi޼9W\N:899jx"嬼!55X4h+F'NTo ///.]DbbbUz5e;~\\)))lٲ\h42.[w5;76{B)/[[[5%%%!2g :1OII ǏGA˖--]zz:NqҸ95!-- [[[3{LDeٳgtJqQΞ=k:.[/5}5meNN֤s?J6...ipeLFVիK~~2Ξ=z^X:ΜlkkkB(^&GYpzzzBh4 dA IDAT^@RRIIIjM4$ B222*"KnDfnGKB`0}ي]V[e9sbZjeQsrr8u5*窨(%==:uTK\^_*l367Wׯcgg8;;S~}=JRR(Evjxxx]BBOll,888NN:}U222Wn瓚}/M3 zCHx"/^!onݺ\tcǎ)~弼rSn]򈍍Ѣ?^ABB{{{T*~~~Wv>>>f簱-TUaggǙ3ghܸ1EEE$&&Sfvv64i҄|3A4=$$$Y^Zm=qcvs.DepTd>IIIQ533ǏӠA|||ĉ9sOOO1 \/׮]t:^Jzz:͚5nK$<<FC@@񸹹Uk޼DxiҤ gϞUfԩS\n]JJJG 5a49{,cÇhyUHAANBVSf^\xxU`mmM6m !t4h@FFO8"//"兟|OZZ:Jcߌ`~3_{2rUmRh޼98qJ7 44pcjbYt:۷WİÇsBM1c頤WhDRn]ePԖpcZ`qq1ԭ[W[nܸ1.]"55Vkqښ$rssO@@1L||"[GeỊn*ykyL'gge"#jN׻Ѫm;d%Ⱥܫot:8z(5 J5K2s<(K7K?1}޶%ުm;"}Jvv6YYYիW1 %DS$=D~~>.\P^#$$8qDr^KL&Zn-+."WH$)D"".H$)D"E\"H$<<;_Jj+Y7䎉xmM(Ju#HnNH$)D"".H$)D"E\"H$<`BYL"H_12jYO%nA5%^u+wgggckk[D"5HBBB8u]/p^^M6rrr=z4...0frss͎?>t:zIBBBڰaՕɓ'7N:+P""~i>#rss|F(Zhw}gѣGҥKYncǎU—/_Δ)Sx&55^zQPPP.-SO=ży8pQQQl޼cǎ駟+P"">ydJJJ;w.wgϞ%""{~\ꫯxw>|8O>$o6_|.]`ܹ1iӦ#E\\6l(^BBFCҸqc:vȱc0a< -[QLBFtӻwopvv橧"//O3qD^z|gJɓ'ԩ>>>h4x饗ʪ4w&<<VgϦD _~=hZ<==y(..O>akkO>$˖-M6t:|||xweϔHnUf _ 7{Ŏ-߉?&vت|8xB[ @kNS6qc~mmUaØ1cD֭֬Y#qEe_BBDTTpn_3ܹ\"lllġCDVVhРXn/^BpѥKgw߉ٳg[E `%Ξ={? +&L T*8vBm۶ @XBܹS,^XԩSGlRYױcDŽ6lؼyX`B!.^(ŤIā?,"//O_{VNNNC,]Tl߾]7Nȑ#B"_/O=@sS:lSVeE$ڏ=(,,M4ؿY$xDg…;w,' x?Pt:hĐ!CEݺuŋ[o%6l(w.bbb-ׯHLLT[ѣ›^/67{ @lذb}aaah4*{=amm-._,=*urqMYF /@wQQptts̑=]"E"^^zĦM$&&#FСCq/$?@zz:ׯgܹz|}}Yd k׮%,,#GR^HKKS^l-[Ņrrr0 CȡC, ׯPXXȤI:u* 6ӢE Xf)ϓO>ٳgyyWjx{{MϞ=㏕'iݺ5[lawww&NٳwԒ%K֖[lllK$w*Rĺ^4prvV!22zڶ+?+ܾ};}ŋ U«RMr- ( MOLmjND"OiIK$?P%-{7No\M"HK$DD"HK$qD"_W+tGdH$;&6_VYeH$D"".H$)D"".H$R%DrsOia_"Hq"~ 5쬧Mx{YDrE`ȠMx ~Y]ɓ̚5UVܹs,ZYfݑueԊOٹMV(**b$%%(^||<7nիlذ՟9sŋ{[2ydj^ӷ|E*"## ԩSte޽JeuNgϞ$$$Tކ _>L<,lܸqҮ={ХKbĈ: Eмysvi1]v͛uÇꊋ ]t_2o=;>>۷Z7o*lDFF:ub߾}Nё-Z0s̿Mǽ|2}}*::nݺNe˖,\*~ :-xb>3͛Ǟ={3gNv>}>rkdfflWV—/_Δ)Sx&55^zQPP`d੧b޼y8p(E0;Ƨ~ʇ~X#kܸqhтu1{ll³>k>^ 4H 1j(GEE0&lllD Ē%Kh4K?AAAV,7͚5NNNBӉٳgs nvlBK/ OOOacc#ħ~qF ˾}v(kY6m$;!(**? :N;vUVM!ضmDHHr/a0γk.Ѯ];h5k(..V-";;[L0AxxxV+~aqQ%^EN{ފfT7) Sc@O#hc۔U}K<;;e˖q!mƶmh׮#F筏T8w\GƋ/ryk֬OÇͱchԨk׮?_Ylmm~'%%޷oݻwF9[nf[f 7o&$$簶^z櫪 !xwuzzE|||ijN>ҥKqssxL`` ;vPGVӦM OxGݻwuo0g"##ٶm 6Gҭ[7Ò%Kطoh4*O\ɬ^!C0i$^|E bkz֭[YzS'?@zz:ׯgܹz|}}Yd k׮%,,#G(&nٲeWgϞsGrڶm^GӲejd9:rJغu+&MRUq cǎ!V";;T4'LHo"99Xrrr2e ZV#YYYxӤ3o޼rݴig}*_uhӦ w.W'jiR<>ٿ&嫪tww7ԩSfT7YKTIw̙3^%;;~xFΜ9sӧ L #,,SR~} Ri[laϞ=( ;m4"""/ knݚcǎ)L4(}Yy>RSS8qbaŊ$''3vX077 oUՕz'R{Jdd$666xxxiӦ*z-eɓԩVVVfV5ӦMc̙ѣGSy&LG}D1cL>֭[㏛^uÉaƌ<7 ""WWW:t-[oٴiSwqq!(( 1 4mڔVZh"}|A0aӧǏg̙?~aÆhعs'}]tQ"ZjZfڴi9j ~m9r$FRm„ VC 4{ aƌ櫺YSͨoܱ' CK܋S /\ z)\\\/4;nܹGh4ѽ{wqJ-(( 6g66a,BBB̦@U6ӳܴ2neLW۾}|%''[b8l00M᪈Λ%{9&ZׯHIIVDEEYbx91h ,t: }Y]~]L:U4lP V+ׯ/}Qqu,XSNMY1¬LSNz^tU899 wwwQn… E``͚5+7e5<L<,lܸqtԩFy7#F(׉oNhh(͛sNiڵ GGG6o\>WWW\\\ҥ kyywo߾*yPTdgg/(( 22@ԩUW̟?0t:hт3gm:˗ӧS_|zJ5ۗݻw˻?QO>G}Dnn.wF(Zhw}gѣGҥKYncǎU—/_Δ)Sx&55^zQPPP.-SO=ży8pQQQ`;vO??F׸qhѢ֭clٲg}Qyyf6mJ:zbb"cǎo߾D`֬Yb V\Izz:xlMkq=3zj{J˼~z-O2UV`vE`` {Oyڵ+k׮eŊ ~+;J6@0wH‘Z!*VQ*_A-RP|VZDkV ȑrl=g޿?v7%O ;;3LQcG[)z7ZƻjZҩ)˗Oyyy>ϟ}|qq1ַ{nj[oy""9r$=!9{/%''锖OHHYfI̘1n& _ >Ў;nk8]z5ߟ.\@pԯ_?oKW&T__Ljj*-X@z\__OhӦMA}' \(?`0аa>Y4e2 d2h̙TYY)=i&!B*h$O9VtRՔM?|_d effh$NGO?yoNDD=ƒJd_":|к\ @4aiW_Mfj57|#=k*cҥdXǏ=eddV%RI\s _n$H3gΤoRSSIӑd>HʀkZ-%$$ѣ :t6l@999d4IPP||<=3>Wrr2-\g{4 ZGC L&}dZqp[p +kȖV+%ЀkoÆ /8}4}Q(~KƏ"W_}ӧOԩS>gff"))Iz$ سg 6=\V};w/cƌJ=z_|^RDJJJjkDKBT"//f7p?LBCa͚5X,IMMG}$uy}7P(<^}UL07xc}dN~>5k`޼y6mlقQFaڴi(,,),,b;#w}r+c|x7m6L<f޽{;w.O>ҼGy [o5YiM||c >&Op=V"NGNJO/=쳔AcƌZ IRI ""Nת0|2^_VV%vK-QMM P]]]X=x o諯k⨩2kjjZT7m%~Ƞ={6fڰaԒ. =s!n'@+W9N2 /HFCΝٵk7|Siii>e/^  D>2&MLO2K/lz?[N:EK.%\N/b|vKܿ^oRiލ7Hcǎ_gzy|Zޯl1cF-" ͝;'N-nزe ,XL>#F|qCҜ9s`Zqy[Xlf3zjlذ999뮻ڵ Vڵk1nܸo_(GŨQ0~x,YD?tPff 2$zZwuF|+8wn݊~X*l6K˷~jٳADxЀ 2Ν+ t:[{O>(++Ñ#G؈ZZw؁liy!xĉwÇQ[[c,3vXgϟNcРAn6eff{\m߾K3Kؔ3g͛˗Caٲe?p|+**(//}:֭[3gCk#G`ԨQ0ͰX,Ogm6*** tb 3g9ܩϙBV]՘ظq#~ǸqԄɓ'-'sN(Jx뭷0i$i=}]0`$. ~i2l6L0ƍkȑ#1rH兵ތ (J|g>u<&9rVTTT90ԩSPT>eC&;{8p bccW]u}^{Auu5? ׯF#m'| ?U&&&W^y%jV~z ''k׮ŀ0uT)v܉nàA0|p;ڙuUW(--6uN=%p}'8|pRSSqma'w)q̙3Gpϟ?}ԩSC]XXO?%%%R=(((ƍiSO=A!//{'''l6ƦM_ w^}UTTT`޼yau֡ gfXZott42d4i֬Y̲e(..4 3=r]222駟6w\lڵkWX6ccc"Naa!eeeIï>Àwp]h zzbV'RyyyXW 'O)SPTTt:ΦmW[[Kϧ Rդjo߾t-Pmm-_\ZW^t\t=rM8 EEE}4|2tuבh*((h5sŊJ*233[ Y ta믿Li(͛IEzꩧ(11  JOO|M&Sn!Jf̘Ar9L&ΦH8pIѐZd;k>C%~QVVV+؅M"raqqq4zh*++ za{| ŋ/XW]u4 Ѕ@y¦/䲖)h:ƨ(?[Tha6[;tX#ɿ"?}oرc354L^?7pppL@l;ݝ;t#1gsK1ƂU}&˖-ʔ)S8cq38c1qc1!.ـ*r1-!w2X)1!cC18cq3g1!cC18cq3g1qc18cq38c1qc18cC18c1qg1!cC18cq3g1!cC18cq3g1qc18cq38c1qc1!cC18c1qg1!cC18cq3g1!c18cq3g1qc18cq38c1qc1!cC18c1q\);o9) P{cܝc18cq38cO;quҍs:]BcC}aNE^Ú.]( 4.z$ ~!~ dr=7n1hnq t2ك3|u4.Y %uCzm .NǐܡQL@7lMHK:]zM]S[1nySNph ""#aXB6޻ *QH˸\Nh99ѯ_u& }A]]=r"""`Zaw8Gnu!^)>UJU !eKٽb@gtdrdQ* W9VD"NˣJ"ĀTfM3֪[WW'ղ F!BT hD=C/~ F嵣qh A@MM.ˡVQ^^DHHPF8=ڽ .2ppݨo%`Եjݻ(# vHHMKCbBb(rDFD\sI-J8 F \tH*+>T"|8۝:J4%"**@u{!HEnp#eEl IQA-Ƣ(Bˠh^֡z98N;)-@PB:="&\8A \/S{ht|"##r0HfFddCpQSS FSSQ( )ddBYEv~Ⱥ:> rQVVוDoj ""[Ƽ3w>j@@ W"*+C NFTbh{]ԡzJ;EjJP( ·U`JHLD ^R \F o0Nwlv;?Kt4GHAEy9AD툌"vQS[۝> jgϕ!"€8TV)s3OQCնjSx v_~u4P$ ;J?/b[AOA:\/O؂ ynYZ&33ݾK4RR!kè׃g!> Q`'Ci)\N' QokDCC9-:MIR*"ؚl5>OpKܻUv&7rA\ީ;ѝ\|\.x[u M"!C;`0RA>W@wlRN111E* n޽b[jUey? +~&DQ z]ܳ<8xrvr9q42nATBR@ԡz vU55ZjVkR)U/"vr#))NBs啨JR9 PUUċ+{sWVR J4ݳyym=uu>$j6EAvu #+py>rGS ;wF~XV<2! {ÐC@ulv>zܜ+i1_N=J4#G+]DpGh>V~[pCi}8Ļ"ʺXjbK/\<&oun5ԁց+ꕝ {˸>W\[ Z+⮀\VСÚnX_Zjr۠i/o: hؠ#s@V˦&'dp 7%O; ]Ų[aY}]t)E!4Z 5VGB ) 2 r4#%9gΖA$'%#cftq5uUuGK\.CEo V/ߓN=P(䈈!C:ڿlYeNr߅7Xd-˻t߉g@oqgϜA\||SKR/$wA SCC cE!صkjjjN :*Ao^C^n^DShljw_^ypIߏph]-.HO,nlޗ]ԓs@c"Za2 d]}`|pb5{v S1ƺshSG1B恺Vp}/x3X總&@4r%Z⡚1: .^ ;e~yeRPзL:I2)[&y$ P>c]`-kjjl-˸ZNB~Rl yO޳2|_C1sqx]^-^ksg+= ]%1N~zwx Jt$ yWsW cT|3E,)xYw{CgL^z3\R ;ſ_\ly,pѯ{E"8c[A"o%9zi;@ [1+h{y'|X-q,H!],- qʽG sk^{Ǧ"e<C1!#!P &KYo_K;c$@{(ea08c, 5@»Ü1s@v6Yosh3XB7u4C-#kcqޠur1a:Ҍ1vCK1~f1c]jhW`IENDB`mapper-0.8.1.1/doc/manual/pages/images/touch_cursor.png000066400000000000000000000016231325266516600227740ustar00rootroot00000000000000PNG  IHDRvVtZIDATxQA@YC@A(%| (EU$@" և=ӪN>0j,4ME_pL318L318L˔xG4aiZ>$===dqDZ).3dy1s^zDJ6ť\"_m][D"T"kg]d]"$"wa]O\Jl6=5$~yyi}SR&~~~')R4Mv|ql}|$?vm} dJT]q&ƙgbq&ƙgbq&ƙgbq&ƙgbq&ƙgbq&ƙgbq&ƙgbq&ƙgbq&ƙgbq&ƙp'$ uӣ6=-{f%6L*jx?GaСw;1Ư#0J πwCϾ`#EL`|0jQt۷|*lc ݥ"+O=*2(:ﻏ\9q+sL,`]Z#<).\'qBbG4450+bq&_"88L31ć?{g*~ilbM3!_8L[8Z褜O7L31nikZ[N1BkdS`Nbj2}MS8 \WysUc, ei os+w)+ٛ%9W{%~~*: G'~ #ے⼹.[D9'NDW\318L318L318L31ĸ8܊ IENDB`mapper-0.8.1.1/doc/manual/pages/index.md000066400000000000000000000036061325266516600177360ustar00rootroot00000000000000--- title: User Manual edited: 24 January 2018 redirect_from: - / - /Home --- {% if doxygen %} **Note:** The [online version](http://www.openorienteering.org/mapper-manual/) of this documentation may contain additions and corrections. {% endif %} ## Contents [Main window](main_window.md){: .subpage} Explanation of the main drawing window. [Reference](reference.md){: .subpage} Toolbar and menu items, including explanations for [all drawing tools](toolbars.md#drawing-toolbar). [Starting a new map](new_map.md){: .subpage} First steps. [Georeferencing](georeferencing.md){: .subpage} Defining the relationship between map paper coordinates and real world coordinates system. [Map grid](grid.md){: .subpage} Showing a helper grid in the map view. [Templates](templates-index.md){: .subpage} Working with templates. [Colors and symbols](colors_symbols.md){: .subpage} Modifying map colors and symbols, [Map parts](map_parts.md){: .subpage} Organizing objects in maps. [Object tags](object_tags.md){: .subpage} Attaching arbitrary key-value pairs to objects. [Find objects](find_objects.md){: .subpage} Finding objects based on textual properties. [CRT files](crt_files.md){: .subpage} Providing rules for assigning symbols. [Settings](settings.md){: .subpage} Adjusting the program to your preferences. [Course design](course_design.md){: .subpage} Using the course design symbol set. [The Mapper app for Android](android-app.md){: .subpage} Working with the Android version of Mapper. [FAQ](faq.md){: .nosubpage} Frequently asked questions. ## Online resources [Blog](http://www.openorienteering.org/) The latest news from OpenOrienteering. [Issues list](https://github.com/OpenOrienteering/mapper/issues) Submit feedback, bug reports, feature requests, and contributions. [Releases](https://github.com/OpenOrienteering/mapper/releases) Releases, with links to change information and known issues. mapper-0.8.1.1/doc/manual/pages/main_window.md000066400000000000000000000015401325266516600211350ustar00rootroot00000000000000--- title: Map Screen authors: - Peter Hoban - Thomas Schoeps edited: 24 February 2013 --- This is the main window when drawing maps. By default it looks similar to this: ![ ](images/main_window.png) The **toolbars** at the top provide access to the drawing tools, among others. See the [list of all toolbar elements](toolbars.md). In the middle, the currently opened **map** is displayed. On the right, a list of all **symbols** in the currently opened map file is shown. See [symbols](symbol_dock_widget.md). On the bottom, there is the **status bar** which shows useful information about the usage of the currently active tool. Just by reading these tips, you can learn a lot about the drawing tools without reading this manual. You can rearrange the position of the toolbars and the symbol pane as you wish by dragging them to the desired location. mapper-0.8.1.1/doc/manual/pages/map_menu.md000066400000000000000000000026001325266516600204210ustar00rootroot00000000000000--- title: Map Menu authors: - Peter Hoban - Thomas Schoeps - Kai Pastor keywords: Menus edited: 20 January 2018 --- #### Georeferencing... Opens the [georeferencing dialog](georeferencing.md). #### ![ ](../mapper-images/grid.png) Configure grid... Shows the [grid configuration dialog](grid.md). --- #### ![ ](../mapper-images/tool-scale.png) Change map scale... Shows a dialog to change the map scale. #### ![ ](../mapper-images/tool-rotate.png) Rotate map... Shows a dialog to rotate the whole map. #### Map notes... Shows a text field for entering additional text which will be saved in the map file, without appearing on the map itself. This can for example be used to store information about map revisions. --- #### Add new part... Opens a dialog for entering the name of a new map part which is to be created. #### Rename current part... Opens a dialog for changing the name of the current map part. #### Remove current part Removes the current map part and all objects it contains. #### Move select objects to Moves the selected objects to another map part. #### Merge this part with Moves the current part's objects to another map part, and then removes the current map part. #### Merge all parts Moves all other parts' objects to the current part, and then removes all other map parts. mapper-0.8.1.1/doc/manual/pages/map_parts.md000066400000000000000000000021571325266516600206150ustar00rootroot00000000000000--- title: Map Parts authors: - Kai Pastor keywords: Map parts edited: 10 January 2016 --- Map parts partition the map in different collections of objects which can be worked on independently. This can be used to organize the work of different people in a map making team. Map parts are also used to separate imported objects from existing map content. Every map object belongs to a particular map part. Selected objects may be moved to a different map part. You may also move all objects of the current part to another one, or merge all other parts into the current one in a single step. See the [Map menu](map_menu.md) for actions which create and change map parts. Only a single map part can be the "current" one. It is the context where all selection, editing and drawing operations take place. The current map part is displayed and selected in the map part toolbar. This toolbar is not displayed as long as the map has only one part. Other than templates, map parts are not drawn sequentially one after the other, but simultaneously according to the symbols and colors. All map parts share the map's colors and symbol sets. mapper-0.8.1.1/doc/manual/pages/mapping-introduction.md000066400000000000000000000237421325266516600230040ustar00rootroot00000000000000--- title: Introduction to mapping for orienteering author: - Thomas Schoeps - Kai Pastor edited: 1 December 2015 --- *Disclaimer:* This is is mostly an empty place holder, not a complete introduction to orienteering mapping. Contributions welcome. * TOC {:toc} ## Topics to be covered - Suitability of orienteering terrains, permissions, scale. Things to consider before starting a new map. - Acquiring map templates (aka base maps) Good templates save you lots of time, so it is often worth looking for the best templates available. - Loading and matching templates After getting the templates, the map file is created where they need to be put into a common coordinate system. - Orienteering symbol sets Before starting to map, you should become (more) familiar with the orienteering symbol sets. - Surveying: classic method Surveying the terrain by printing out templates, drawing in map details, scanning your drawings and doing the final drawing at home. - Surveying: digital method Surveying the terrain with help of mobile computers. - Drawing Doing the final drawing with OpenOrienteering Mapper. - Generalization The importance of generalization. ## Surveying (classic method) ### Prerequisites It is very beneficial to get experience running orienteering before starting with orienteering mapping. This way, most symbols on orienteering maps and what to use them for will be known to you already and you have a feeling for the level of generalization which is expected in orienteering maps. Depending on the method you want to use for surveying terrain, you will need material such as a compass, printer, scanner, a clipboard, suited pens, or a mobile computer such as a tablet running Windows or Linux. ### Fieldwork Accurate mapping depends on high quality fieldwork. Ordinarily, fieldwork will usually produce a paper sketch with all the detail required to produce the map but often marked up with shorthand codes which are meaningful to the mapper but which will not appear on the finished map. Instead of paper the mapper may use mylar or other robust drafting film. Fieldworking on transparent film also permits the use of an underlay which may have a coordinate grid or an old map visible underneath (at an appropriate scale). The fieldwork will usually be done at a larger scale than the final map. With convenient rescaling of computer graphic images it is now possible for fieldwork to be undertaken at any convenient scale such as 1:5000 or even larger. This demands less skill in field penmanship than the traditional fieldwork scale of 1:7500. The fieldwork is scanned — about 200dpi is usually quite sufficient resolution — now save the image as a .png file in your map file directory ( .jpg image files also work). This file is then loaded as a template underneath the new map screen and the features are inserted on the map on top of the fieldwork image. The fieldwork must be scaled and positioned appropriately for this purpose. ## Symbols Symbols for orienteering maps are prescribed in the [IOF documents](http://orienteering.org/resources/mapping/) ISOM (International Standard for Orienteering Maps), ISSOM (sprint maps), ISMTBOM (MTBO maps), and ISSkiOM (ski orienteering maps). It is very desirable that all orienteering maps should conform to the appropriate standard, and that includes conforming to the standard symbols for the discipline. A full set of symbols for each standard will be made available with OOMapper, and ideally all maps will be prepared using only the unmodified symbols described in the standards and provided in these symbol sets. This enables international competitors to receive a locally produced map and find it portrays the terrain in exactly the same way as in their home country. It also enables local orienteers to become familiar with the mapping conventions that are used for major events elsewhere. The use of local symbol variations defeats the purpose of mapping standards and confuses visiting competitors. ### Symbols Window This area on the right hand side of the OOMapper screen shows all the available symbols in the loaded standard set. Use the scroll-bar to move the array up and down to reveal symbols not showing. Each symbol has a text (a part of the standard) describing where and how it may be used. That text can be made visible in the symbol window by pressing F1 while hovering over the symbol of interest. ## Colors The standard colors of orienteering maps are defined in the terms of the Pantone Matching System (PMS) and are intended to describe the exact color of inks to be used in offset printing processes for the production of orienteering maps. With the advent of inexpensive ink-jet printers and high quality printing papers it has become possible to print water resistant orienteering maps at very modest cost and with excellent control of the process and the colors used. In this context there has also been considerable discussion about appropriate colors to better enable color blind competitors to discriminate map features and it is possible that some changes may be adopted as a result. ### Color Window This tool defines colors in terms of the CMYK system or the RGB system. Default colors for orienteering maps are entered but the colors that are actually printed by a particular printer on a particular paper type can produce a result which is quite different from the PMS color intended. The IOF Map Commission has prepared an offset printed paper test sheet (Print Tech Test Sheet - available from your national mapping officer) and as a map file printtech2006.ocd (available as download from the [IOF](http://orienteering.org/resources/mapping/test-sheet-for-assessing-print-quality-for-orienteering-maps/).The offset printed paper sheet has the currently prescribed colors for the production of maps (Store in a heavy envelope, do not use copies which have been kept loose and may be faded). The colors for a particular printer and paper may be adjusted using the Color Window so that a better match with the offset printed test sheet is achieved. The colors yellow (open space on the terrain) and brown (contour lines) are commonly quite divergent from the standard. ### Color adjustment 1. Scan the IOF offset printed Print Tech page in any scanner and use a color picker to get the CMYK values on screen for each color. By way of example this scan may yield CMYK values of 000, .800, .991, .569 respectively for 100% brown. 2. Load the file printtech2006.ocd file into OOMapper and print the page on your printer. Take this freshly printed page and scan that print on the same scanner and use the same color picker to get the color values you have in the scan. An example of this scan for 100% brown may yield CMYK values of 000, .689, .942, .255 respectively. 3. The difference between those two sets of values will indicate the adjustments required. Using the example values the Color Window line for 100% brown should have no adjustment to the cyan field, +.111 to magenta, +.049 to yellow, and +.314 to black. Those adjustments must be made in the OOMapper Color Window. Having made similar adjustments for all colors, then run the step 2 print trial again. 4. In a perfect world the differences will all reduce to zero. In practice they may not because of non-linearities in the hardware and colors like brown are poorly defined on the color palette. Several iterations may be required to get a good match. The same general approach may be employed but using the RGB color space instead. When a contour is printed in an area of green on the map the brown of the contour loses contrast and the contour line becomes harder to see. If the map is offset printed the green is laid down without a gap and the brown is printed over it while the green ink is still wet, with the result that the green and brown blend together where they are coincident. This creates a segment of line which is somewhat darker than the 100% brown line outside the green and the darker color is noted to be easier to see. This very desirable result is called an over-printing effect. The Print Tech Test Sheet is designed to demonstrate beneficial over-printing effects where these are achieved. Consider the visibility of the wavy contour lines (and blue creek lines) printed against a variety of backgrounds on the lower left of the sheet. ## Map scales Map scales are set in accordance with the Internationsl Standard for Orienteering Maps (ISOM). The traditional competition format is at 1:15000. More recently maps may be printed at 1:10000 for shorter courses. Maps at 1:10k are visually identical to those at 1:15k. The ISOM emphatically requires that a larger scale 1:10k shall not be used in order to fit more detail onto the map but simply permits the map to be more easily read by older competitors who will generally have shorter courses and not require large maps. Sprint maps made to the ISSOM standard may be at either 1:5000 or 1:4000 and use a different symbol set appropriate to that scale. Printing of a map prepared at 1:15000 may be at either scale (and conversely a map prepared at 1:10k can be printed at 1:15k) with no loss of accuracy or information. Most course planning software provides for this re-scaling at the point of printing. The ISOM details the size, shape and use of all the permitted symbols and codes on the map, and these are the only acceptable symbols and sizes on a map to be printed at a scale of 1:15000. If it is desired to prepare the original map at a scale of 1:10000 (or some other scale) a new symbol set appropriate to that scale would need to be created with proportionately larger symbols. There seems little point in this. Do not confuse this with the scale of fieldwork which is sensibly at a larger scale than the finished map. Fieldwork is rescaled to the scale of the map when it is loaded as a [template](templates.md). Using a larger scale with symbol sizes appropriate to a smaller scale provides more white space and enables more detail to be included but this is at the expense of readability, and the additional detail is almost always unnecessary and thus unhelpful. mapper-0.8.1.1/doc/manual/pages/new_map.md000066400000000000000000000054261325266516600202570ustar00rootroot00000000000000--- title: Starting a New Map authors: - Peter Hoban - Thomas Schoeps keywords: Map edited: 26 February 2013 --- Choose the menu item File -> New... to show the new map dialog. ![ ](images/new_map.png) There are two important choices to be made before starting a new map. These are the scale of the map and the symbol set to be employed. While either of these can be altered at a later time, that is sometimes difficult and it is better to decide on the scale and the map standard before beginning. OpenOrienteering Mapper presents a dialog box to set these important parameters before you start. #### Scale Map scales are set in accordance with the International Standard for Orienteering Maps (ISOM) or International Standard for Sprint Orienteering Maps (ISSOM). In earlier times, the traditional competition format was at 1:15000. Nowadays the de-facto standard for forest orienteering maps is 1:10000 for shorter courses as the readability of these is much better. Maps at 1:10000 are just scaled up versions of those at 1:15000. The ISOM emphasizes that a larger scale 1:10000 shall not be used in order to fit more detail onto the map but simply permits the map to be more easily read by older competitors. Sprint maps made to the ISSOM standard may be at either 1:5000 or 1:4000 and use a different symbol set appropriate to that scale. Printing of a map prepared at 1:15000 may be at either scale (and conversely a map prepared at 1:10000 can be printed at 1:15000) with no loss of accuracy or information. Most course planning software provides for this re-scaling at the point of printing. #### Symbol set In conjunction with the scale it is necessary to make an early decision on the symbol set to be used. Again this is determined by the mapping standard adopted. Maps prepared in conformity with the ISOM will use only the standard symbols of the ISOM symbol set. It is important to resist the invention of other sybmols for local features as these local uses erode the international uniformity which enables competitors from any country or language to use the map in fair competition. Non-conforming local map standards also create confusion for local competitors entering an event on maps conforming to the ISOM. #### Next steps After clicking Create, the [map screen](main_window.md) will be shown. Now you probably want to load some [templates](templates.md) and establish the map's [georeferencing](georeferencing.md). Note that loading a georeferenced template will show the georeferencing dialog with some values already pre-filled and adapted to your template file, so you probably do not need to do this step separately. After loading your templates you can start [drawing](toolbars.md#drawing-toolbar) your map objects with the map [symbols](symbol_dock_widget.md). mapper-0.8.1.1/doc/manual/pages/object_tags.md000066400000000000000000000020631325266516600211070ustar00rootroot00000000000000--- title: Object tags authors: - Thomas Schoeps - Fraser Mills - Kai Pastor keywords: Tagging edited: 21 January 2018 --- ## About object tags In general, map objects are characterized by location and symbol. Object tags store additional information in the form of key-value pairs. In orienteering, this is useful to record feature details for control descriptions (e.g. dimensions) or for converting between map variants (e.g. MTB ridability). Data formats such as OSM and DXF carry object details which will be imported in tags. The ["Find objects" dialog](find_objects.md) allows to find objects based on their tag keys and values. With [CRT files](crt_files.md), object tags can guide symbol assignment and replacement. ## The tag editor The tag editor is a window with two columns, 'key' and 'value'. When an object is selected, the tag editor displays the key-value pairs for this object. ![Tag editor window](images/tag_editor.png) The last row is empty and can be used to add new keys and values. To erase an existing row, clear the row's key field. mapper-0.8.1.1/doc/manual/pages/reference.md000066400000000000000000000007431325266516600205640ustar00rootroot00000000000000--- title: Reference keywords: Reference edited: 26 November 2015 --- - [Toolbars](toolbars.md){: .subpage} - [Advanced drawing tools](toolbars.md#advanced-drawing-toolbar) - [File Menu](file_menu.md){: .subpage} - [Edit Menu](edit_menu.md){: .subpage} - [View Menu](view_menu.md){: .subpage} - [Tools Menu](tools_menu.md){: .subpage} - [Map Menu](map_menu.md){: .subpage} - [Symbols Menu](symbols_menu.md){: .subpage} - [Templates Menu](templates_menu.md){: .subpage} mapper-0.8.1.1/doc/manual/pages/settings.md000066400000000000000000000102421325266516600204610ustar00rootroot00000000000000--- title: Settings authors: - Peter Hoban - Thomas Schoeps keywords: Settings edited: 25 February 2013 --- In the settings dialog, the program can be adjusted to suit your application. ### General page #### Language The program language may be selected here. #### Open most recently used file If this option is set, on program startup the last recently used file will be opened, otherwise the home screen will be shown. #### Show tip of the day This option controls whether the tip of the day should be included on the home screen. ### Editor page #### High quality map display (antialiasing) Map elements are drawn more smoothly (and less pixelated) if this option is set, however the drawing speed is lower. #### High quality text display in map (antialiasing), slow This controls the same option as the above for map texts. It is separate because text display is particularly slow. #### Click tolerance This controls how close you need to click on map objects or object handles for selecting them, but also for cut lines with the [cut tool](toolbars.md#cut_tool) for example. Set a larger value if you have difficulties (or if you use a tablet), however this may lead to some unintentional selections. #### Snap distance (Shift) This controls how close you need to be for snapping to an existing object when holding Shift while editing an object or drawing. #### When editing an object, automatically select its symbol, too When this box is checked selection of any object on the map will cause the the same symbol to be selected in the symbol pane. This is often convenient as symbols may be selected by simply clicking an object with this symbol on the map. #### Zoom away from cursor when zooming out When ticked the zoom function will zoom away from the cursor rather than from the middle of the window when using the scroll wheel to zoom out. Using the menu or toolbar zoom tools (or the function keys F7 & F8) will still zoom to/from the middle of the screen. #### Drawing tools: set last point on finishing with right click When this is activated, right clicking while drawing an object will set the last point **and** finish drawing the object. When disabled, right clicking will **only** finish the object without setting the last point. #### Templates: keep settings of closed templates When active, the settings of templates (like their positions) will be kept in the map file when they are closed, so they can be reopened later while restoring their settings. In essence, "closing" templates does not really close them this way. When the setting is off, closed templates cannot be restored later. #### Edit tool: actions on deleting bezier spline points These settings control what happens when a node between two curves in a path is deleted. The first is used when a node is deleted normally by Ctrl+clicking it, the second is used when a node is deleted by Ctrl+Shift+clicking it. - **Retain old shape**: The program tries to move the remaining curve handles so the shape of the old curve is retained as much as possible in the remaining curve. - **Reset outer curve handles**: The outer handles (those of the remaining curve) are reset to the positions where they would be placed if the outer curve would have been drawn with the draw path tool. Note that this way the resulting shape is independent of the position of the deleted node. - **Keep outer curve handles**: Nothing happens apart from the deleted node and its two associated handles being deleted. The outer handles (those of the remaining curve) are simply kept at their current positions. Note that this way the resulting shape is independent of the position of the deleted node. #### Rectangle tool: radius of helper cross This controls the radius of the helper cross which is shown when using the rectangle tool, in pixels. #### Rectangle tool: preview the width of lines with the helper cross When this is checked, the helper cross adapts to the selected symbol: for each direction, two lines are drawn which show the radius of the used (line) symbol. This allows for better judgement of the result while drawing, but leads to more cluttering on the screen. mapper-0.8.1.1/doc/manual/pages/symbol_dock_widget.md000066400000000000000000000255001325266516600224740ustar00rootroot00000000000000--- title: Symbol Pane authors: - Peter Hoban - Thomas Schoeps keywords: Symbols edited: 24 February 2013 --- ![ ](images/symbol_dock_widget.png) #### Introduction The symbol pane shows all symbols in the opened map file arranged in a grid. Hovering the cursor above a symbol shows its name and its number, which comes from the respective orienteering symbol set standard. Pressing F1 while hovering over a symbol shows the detailed description from the standard document for this symbol, if available. Symbols in this view can be selected like in a file manager: - **Clicking** a symbol selects it exclusively. - Clicking a symbol while holding **Shift** toggles the selection of all symbols between this one and the previously selected symbol. - Clicking a symbol while holding **Ctrl** toggles the selection of this symbol only Right clicking shows the [symbol menu](#symbol-menu). #### Symbol types **Point symbols** are features with small extent, so point objects are represented by a single coordinate. Examples are single trees or small knolls. **Line symbols** depict linear objects such as paths or small watercourses. Line symbols can contain point symbols: the fence symbol for example represents its dashes by a point symbol. **Area symbols** depict objects with a larger extent which is shown on the map in plan shape, for example buildings or thickets. They can be filled with a solid color, or with different kinds of line and dot patterns. **Text symbols** contain font settings such as size, color or thickness. They are used to standardize the appearance of texts on the map, like control numbers or spot heights, but are also used for titles, legends and other notes. Text size may be approximated as 0.24 times the point size for a typical easy to read font (but some fonts may depart significantly from this rule of thumb). Thus 6pt font has a letter height of 6\*0.24=1.44 mm. Choosing an uncommon font may create problems for others opening the map on computer systems where this font is not installed, as font files are not embedded in map files. **Combined symbols** are made up of two or more other line or area symbols. This is for example used to combine the gray fill and black outline for houses in sprint maps into a single symbol. #### Symbol menu To show the symbol menu, right click the symbol pane: ![ ](images/symbol_dock_widget_menu.png) - The actions in the first section enable to create new symbols or edit the selected symbol. - The actions in the second section enable to copy/paste symbols. - In the third section, in additon to the possibility to select all symbols with the selected object, there are shortcuts to the two drawing functions [switch symbol](toolbars.md#switch_symbol) and [fill / create border](toolbars.md#fill_create_border). - The fourth section enables to hide all objects with a given symbol, for example to remove all paths from a map, and to protect all objects with a given symbol from editing. This is useful to make large area symbols static while editing other objects on top of them. - The last section offers actions for symbol selection and sorting. ### Symbol editor #### Introduction {#symbol_editor_introduction} The symbol editor enables to create new symbols or to modify any of the existing symbols. Its main use in normal operations is to create symbols for map labeling, because no symbols for this purpose are provided by default as they depend on the desired styling of the map sheet. Regarding symbols for use in the map itself, it should be understood that the default symbol sets include all the symbols permitted by the ISOM or ISSOM and that any new symbols created, or modifications of existing symbols, will be a departure from the international standard. There is a temptation to employ a symbol which is used in other local cartography to represent some feature on the basis that everybody who competes in this area is familiar with the notation. However, any such departure is confusing for a competitor who may be familiar with the international standard but not with your local notation. Similarly a local competitor may find the conforming notation strange when entering international competition. Thus a non-standard notation can be to the disadvantage of both local and visiting competitors. The symbol editor is opened from the [symbol menu](#symbol-menu) which is triggered with a right-click on any symbol. The menu offers options to create a new symbol or to edit the selected symbol. In each case the dialog opens with a General page which offers common options for all symbol types, which is followed by pages specific to each type of symbol. On the right is a preview of the symbol as it is currently configured, in a variety of lengths and orientations. This preview can be moved and zoomed with the middle mouse button and mouse wheel. #### General page At the top, the symbol number and name can be entered. Both are specified in the ISOM and ISSOM for orienteering symbol sets. It is recommended that helper symbols are given numbers in the third subset (which is not used by ISOM). Thus a helper for symbol number x.y might be numbered x.y.1. The description text appears when pressing F1 while hovering over the symbol with the cursor. For orienteering symbol sets, it is directly taken from the ISOM and ISSOM documents. At the foot of the dialog is a box which when checked hides the symbol when the map is printed out. It is intended for helper symbols which mark certain terrain features which are useful for mapping but will not be represented in the final map. **Use this with care!** When accidentally ticked, symbols will disappear in printouts without further notice! #### Line settings **Line width** and **colour** define the basic line characteristics. Minimum line length function is not yet implemented. **Line cap** permits the shape of the end of the line to be specified. **Line join** refers to the rendering of vertices where line sections of different orientations join each other. **Dashed lines** are created by a tick in the dash box, which shows the dash dialog settings. Typical length of dash and gap may be specified — these will be rendered with gaps of the specified size and dashes as close as practicable to the specified length (so that there is no fractional dash at the end). The appearance of lines with regular symbols or breaks may be improved by making the end dash a half length — a check box provides for this. Dashes may be grouped together with a specified gap within the groups which is different from the between-group gap length. Many symbols (e.g. 520 Stone wall) have a **repeated feature** along the line. This line feature is invoked by setting a non-zero integer number in the mid-symbol count. The mid-symbol distance sets the spacing between symbol centres. The distance between spots refers to the distance between symbol groups; if the mid-symbol distance is too large then the groups may overlap. The distance from the end is an approximate length of line projecting beyond the last group. A check box to require at least one mid-symbol ensures that the line has at least one symbol but may give confusing results if the line is too short for meaningful rendering. Setting a minimum mid-symbol count is not yet implemented. A different mid-symbol minimum may be specified for the boundary line of a closed symbol. Some line symbols require an **outline** which is enabled by selecting the border check box. Border width is the width (thickness) of the border lines. Shift moves the border lines further apart — shift equal to half the border width will place the line just outside the edge of the main line. A dash feature with characteristics which do not have to match the main line is enabled by the corresponding check box. #### Start symbol The start symbol will appear only at the first-fixed end of the line. This can be changed for a particular line with the [switch dashes / reversing tool](toolbars.md#switch_dashes). The start is the left end of the horizontal sample lines in the illustrative graphic to the right of the symbol editor. The start symbol may be made of several elements. To add an element click the + button at the bottom of the elements list. To delete one click the - button. An element may be a point (a circular element), a line or an area element. The default feature is a point element (line or area elements must be added). **Point elements** have inner diameter and outer width which may be different colours, and will be centred on the beginning of the main line. A **line element** has width, colour, end-shape and join characteristics specified in the current element dialog. The line may have any shape defined by successive pairs of coordinates, add more coordinate pairs with the + button at the bottom of the coordinates window and delete them with the - button. Another way to add a coordinate is to left click the desired position in the preview at the right. A right click repositions the currently selected coordinate. The coordinate system origin is the beginning point of the symbol line, the X axis is positive in the direction of the main symbol line, and the Y axis is up the screen. Curved lines are created by constructing a shape with straights first and then clicking the "Curve start" check boxes to make some parts curves. Better results may be obtained with curves which do not bend more than 90 degrees. An **area element** has a colour and a shape specified in the current element dialog. The shape of the area is defined just as for lines, see above. #### Mid symbol These symbols appear along the line at the spacing set for mid-symbols in the 'line settings' dialog. Mid symbols are edited like start symbols (see above). #### End symbol This symbol appears only at the last-fixed end of the symbol line. This can be changed for a particular line with the [switch dashes / reversing tool](toolbars.md#switch_dashes). The end symbol will appear at the right end of the of the horizontal sample lines in the illustrative graphic to the right of the symbol editor. End symbols are edited like start symbols (see above). #### Dash symbol This symbol will appear at dash points along a line; drawing these can be toggled by pressing the spacebar while the drawing tool is active. The symbol will appear at each node drawn while the helpful tip at the bottom edge shows "**Dash points on.**" This switch may vary from point to point along the line. Dash symbols are edited like start symbols (see above). mapper-0.8.1.1/doc/manual/pages/symbol_replace_dialog.md000066400000000000000000000034611325266516600231450ustar00rootroot00000000000000--- title: Symbol Replacement Dialog authors: - Peter Hoban - Thomas Schoeps keywords: Templates edited: 26 February 2013 --- This dialog enables to replace the symbol set in the current map file by the set of another file. It is possible to accurately select a replacement symbol for each old symbol. Show it by clicking the menu item Symbols -> Replace symbol set..., then select the map file from which to take the new symbol set. Note that this is not a way to e.g. rescale a map from 1:10000 to 1:15000, use Map -> Scale map... instead. ![ ](images/symbol_replace_dialog.png) #### Import all new symbols, even if not used as replacement If there are symbols in the new symbol set which are not matched to any existing symbol, they will be imported as additional symbols if this option is checked, otherwise they will be omitted. This is useful to import symbols of the new set which had no counterpart in the old one. #### Delete original symbols which are unused after the replacement If this toggle is activated, all symbols of the old set which are not in use after replacing all chosen symbols will be deleted. This is useful to clean up symbols which are not needed anymore by the new set. #### Delete unused colors after the replacement Deletes all colors which are not used by any symbol after the replacement. This is useful to clean up the color list. #### Match replacement symbols by symbol number As long as this check is activated, replacement symbols will be matched to existing symbols based on their numbers only. You can check the replacements in the symbol list below. After deactivating this, you can choose any replacement symbol (or also to not replace a certain symbol) for any old symbol by clicking the replacement symbol in the list and choosing the desired option. mapper-0.8.1.1/doc/manual/pages/symbols_menu.md000066400000000000000000000022531325266516600213400ustar00rootroot00000000000000--- title: Symbols Menu authors: - Peter Hoban - Thomas Schoeps - Kai Pastor keywords: Menus edited: 20 January 2018 --- #### ![ ](../mapper-images/symbols.png) Symbol window **Ctrl+Shift+8** This toggle switches the display of the [symbol pane](symbol_dock_widget.md) on the map screen. #### ![ ](../mapper-images/colors.png) Color window **Ctrl+Shift+7** This toggle switches the display of the [color window](color_dock_widget.md) on the map screen. --- #### Symbol set ID... Opens a dialog for changing the map's symbol set ID. The symbol set ID controls suggested symbol replacements during import and during explict map symbol set replacment. #### Scale all symbols... This dialog can be used to re-scale all symbol dimensions. It is important to note that use of this feature will create a departure from the standard symbols and the resulting map will thus not conform to the standard. #### Replace symbol set... Shows the [symbol set replacement dialog](symbol_replace_dialog.md). #### Load CRT file... This action lets you select a [CRT file](crt_files.md) and replace symbols within the current map and symbol set. mapper-0.8.1.1/doc/manual/pages/template_adjust.md000066400000000000000000000050621325266516600220120ustar00rootroot00000000000000--- title: Adjusting template positions authors: - Peter Hoban - Thomas Schoeps keywords: Templates edited: 25 February 2013 --- ![ ](images/template_adjust.png) This window can be opened from the [template setup window](templates.md#setup) and is used to adjust the positioning of the currently selected template to the position of another existing template, or the map, which is already in the correct location. The principle is to define two or more so-called pass points. Every pass point defines a source position, this is an arbitrary point on the new template, and a target position, this is the position where the source point should move to. So every pass point is a pair of identical positions on source (template) and target (existing template or map). To create a new pass point, first select "New", then click at the source position on the template to position. The point should ideally be easily recognizable in both the template and the existing template or map. Zoom in close to place the point accurately. It is marked with a red plus. Then click the corresponding point in the existing template or map, which will be marked with a green plus. The pass point information will also be shown as a line of data in the pass point window. Repeat this for each of the remaining pass points. In general, the more pass points you define, the more likely it is to get a good result. **One point** does not give enough information for positioning, so in this case the template is simply moved to this point. **Two points** are the minimum to define a similarity transformation, so the template will be moved, rotated and scaled isotropically so the pass points match exactly. For **more points**, the transformation is still constrained to a similarity transformation, so the transformation with the least error between desired and real pass point target positions is calculated. This way, not all pass points will end up at their target positions in general, but the result will usually be better because the information of more pass points is averaged. When all pass points have been defined tick the box "Apply pass points" and your template will move to the position giving the minimum error. The actual error for each pass point will be shown on the dialog box. If this result is unsatisfactory either edit the pass points (use the "Move" tool above the table to adjust the position of either end of any tie), either while the transformation is still active or after clicking the "Apply pass points" check box again, or clear the pass points completely and try again. mapper-0.8.1.1/doc/manual/pages/templates-index.md000066400000000000000000000004211325266516600217220ustar00rootroot00000000000000--- title: Templates keywords: Templates edited: 26 November 2015 --- Working with templates [Templates](templates.md){: .subpage} Types of templates, loading and positioning [Adjusting template positions](template_adjust.md){: .subpage} For non-georeferenced templates mapper-0.8.1.1/doc/manual/pages/templates.md000066400000000000000000000165421325266516600206300ustar00rootroot00000000000000--- title: Introduction to Templates authors: - Peter Hoban - Thomas Schoeps keywords: Templates edited: 25 February 2013 --- Images, tracks recorded with GPS receivers and other layers which are used to provide base information for the mapper are called templates. They can be loaded into the map file using the template setup window which is available via the menu item Templates -> Template Setup Window. Templates can also be "abused" to display information on the final map, for example sponsor logos which are only available as raster images. **Attention**: as templates are not covered by the undo/redo system, be sure to save your map before making changes to them! #### Types of templates OpenOrienteering Mapper supports the following file formats to be loaded as templates: - [Image templates](#image-templates): bmp, jpg, png, tif, gif files - [Track templates](#track-templates): dxf, gpx, osm files - [Map templates](#map-templates): omap, ocd files Additionally, templates can be classified into **georeferenced** and **non-georeferenced** templates. For georeferenced templates, information about the exact positioning of the template in a known world coordinate system is available - see [georeferencing](georeferencing.md). This way, they can be positioned on the map automatically provided that the map is georeferenced too. For non-georeferenced templates, this information is not available, so they have to be [positioned manually](#positioning). #### Template setup window {#setup} ![ ](images/template_setup_window.png) This window is opened by clicking the menu item Templates -> Template Setup Window. In its center is a list of all opened templates. In addition, the map layer is also included there. The order of items in this list defines the order in which the templates will be displayed: the first will be on the top, the last on the bottom. Each item can be hidden or shown using the check box on the left. Furthermore, layers can be set to transparent by entering an opacity percentage. #### Opening templates Click the button "Open..." in the template setup window and select the file to be loaded as a template. See [types of templates](#template-types) for the list of supported file formats. #### Changing the template draw order When a template is selected, it can be moved up and down in the list using the Move Up and Mode Down buttons to change its position in the display order. #### Deleting or closing templates Using the Delete or Close button, templates can be removed. The label text depends on the [corresponding setting](settings.md#templates-keep-settings-of-closed-templates) "Keep settings of closed templates": if it is activated, it will be "Close" and it will be possible to reopen the template later with the menu item Templates -> Reopen template..., otherwise it will be "Delete" and it will not be possible to reopen the template later. #### Positioning At the bottom of the window, there are the template positioning capabilities: The upper left button shows whether the template is **georeferenced**. Later it may become possible to switch the georeferencing setting using this button, but this is not yet implemented. At the lower left is the **Move by hand** button. It allows to move non-georeferenced templates by clicking in the map display and dragging the mouse. At the lower right is the toggle for the **template positioning window**. It enables to enter numerical values for the positioning of non-georeferenced templates, e.g. to rotate a template by 90 degrees, or to adapt the template rotation to the magnetic declination. #### Adjust At the upper right is the **Adjust** button which enables to adjust the position of a non-georeferenced template to that of an existing template, or the map, which is in the correct position already. [Detailed instructions here](template_adjust.md). ### Template types #### Image templates Raster images are loaded as this template type. When opening such a template, the image positioning dialog is shown: ![ ](images/template_image_positioning.png) #### Georeferenced positioning This option is only available if the image has georeferencing information associated. OpenOrienteering supports this via so-called world files. A world file for an image must have the same file name as the image file and be in the same directory. The world file extension is determined by the image extension: it consists of the first character of the image file extension, then the last character of this extension, and then the letter w. For example, a world file for a bmp file would have the extension bpw, or for tiff it would be tfw. Alternatively, the world file extension can also be wld. World files are text files containing 6 entries of a transformation matrix mapping pixel coordinates to grid coordinates of some geodesic coordinate reference system ([more information on Wikipedia](http://en.wikipedia.org/wiki/World_file)). Unfortunately, they do not specify which coordinate reference system it is. So if you choose this option for positioning, you have to specify the coordinate reference system in the next step. You should get this information from the place where you got the georeferenced image from. For example, in Germany it is usually UTM or Gauss-Krüger with a limited range of possible middle meridians. In order for georeferenced positioning to work, the map must be georeferenced, too. If it is not at this point in time, the [map georeferencing dialog](georeferencing.md) is shown as the next step, with the reference point coordinates already pre-filled as the center of the loaded image. #### Manual positioning This method of positioning should only be chosen if you have no georeferencing information available for the image. First, there are two possible options to specify the image scale: - **Meters per pixel**: this is primarily useful for base maps from a digital source, where you may know this value directly. Alternatively you can also calculate it yourself: if your fieldwork image covers 500m width on the ground and the image is 1200 pixels wide then the scale will be 500 / 1200 = 0.625 metres per pixel. (Take care that the number of metres corresponds exactly the width of the image file.) - **Scanned**: if your fieldwork is at a known scale (it should be — say 1:5000), and the scan is at a chosen resolution (say 200dpi) then these parameters may be entered to scale the fieldwork image. This assumes that printer and scanner work accurately. Note that if you do not know the image scale and / or if you are going to adjust the template position to the existing map anyway using the [adjust feature](#adjust), you can just enter any value here as the scaling will be changed again later. ### Track templates These templates can be tracks from a GPS receiver (including waypoints) or vector graphics such as dxf files. For the latter, you have to select a coordinate reference system when loading the file. If it is just a drawing which is not georeferenced, select the option "Local" so will not be distorted. ### Map templates This template type enables to load other map files as a template. This has two main purposes: - Loading an old orienteering map as a base map for a new one. - Loading a map as a base layer to set a course on top of it. For now, map templates can only be loaded as non-georeferenced. mapper-0.8.1.1/doc/manual/pages/templates_menu.md000066400000000000000000000012461325266516600216470ustar00rootroot00000000000000--- title: Templates Menu authors: - Peter Hoban - Thomas Schoeps - Kai Pastor keywords: Menus edited: 20 January 2018 --- #### ![ ](../mapper-images/templates.png) Template setup window **Ctrl+Shift+9** Switches the display of the [template setup window](templates.md#setup) on the map screen. This provides access to the functions required to open, scale, and position a template, and to close it. --- #### Open template... Opens a template file directly without going through the setup window. #### Reopen template... Shows the template reopen dialog where templates which have been opened and then closed before can be restored. mapper-0.8.1.1/doc/manual/pages/toolbars.md000066400000000000000000000665321325266516600204630ustar00rootroot00000000000000--- title: Toolbars authors: - Peter Hoban - Kai Pastor - Thomas Schoeps keywords: Toolbars edited: 30 November 2015 todo: - Split this page and update ALL context help in Mapper. - Update context help for zoom-in and -out in Mapper. --- * [Toolbar positions and visibility](#toolbar-visibility) * [General toolbar](#general-toolbar) * [View toolbar](#view-toolbar) * [Map parts toolbar](#map-parts-toolbar) * [Drawing toolbar](#drawing-toolbar) * [Editing toolbar](#editing-toolbar) * [Advanced drawing](#advanced-drawing-toolbar) ## Toolbar positions and visibility {#toolbar-visibility} Toolbars may be moved to a more convenient position. To detach and move, pick the handle at the left end and drag the toolbar to your preferred position. The resulting order of your toolbars may thus be different from this page. You may hide toolbars you don't use. To close or open a toolbar right click on any open toolbar or docked window. This will open a small window where you can disable or enable toolbars and docked windows. ## General toolbar {#general-toolbar} #### ![ ](../mapper-images/new.png) New map {#new_map} Ctrl+N Click to create a new map. #### ![ ](../mapper-images/open.png) ![ ](../mapper-images/save.png) Open / save map {#open_save_map} Ctrl+O / Ctrl+S Click to open/save an existing map. #### ![ ](../mapper-images/print.png) Print map {#print} Ctrl+P Click to open the print dialog. #### ![ ](../mapper-images/cut.png) ![ ](../mapper-images/copy.png) ![ ](../mapper-images/paste.png) Cut / copy / paste objects {#cut_copy_paste} Ctrl+X / Ctrl+C / Ctrl+V Click to cut / copy the currently selected objects to the clipboard, or to paste the clipboard contents into the map (at the center of the view). #### ![ ](../mapper-images/undo.png) ![ ](../mapper-images/redo.png) Undo / redo object editing {#undo_redo} Ctrl+Z / Ctrl+Y Click to undo or redo the last map editing step. ## View toolbar {#view-toolbar} #### ![ ](../mapper-images/grid.png) Grid {#grid} G Click the button to toggle showing the [map grid](grid.md). Click the arrow to the right of this button to show the [grid configuration](grid.md). #### ![ ](../mapper-images/move.png) Pan map {#pan_map} F6 Use this tool to move the map by clicking the map and dragging the mouse. *Note:* You may always move the map by dragging with middle mouse button, or by using the arrow keys. #### ![ ](../mapper-images/view-zoom-in.png) ![ ](../mapper-images/view-zoom-out.png) Zoom in / zoom out {#zoom_in_out} F7, + / F8, - Use these actions to zoom in (enlarge) or zoom out (shrink). The center of the view stays at the same position in the map. #### ![ ](../mapper-images/view-show-all.png) Show whole map {#zoom_all} Use this tool to display the whole map, including the currently visible templates, at maximum possible zoom. This can be particularly useful when items (objects, templates) are accidentally moved far from your working area and out of sight. This tool brings them onscreen. If the map is just a dot in one corner then the rogue feature is near the other edge of the screen. ## Map parts toolbar {#map-parts-toolbar} This toolbar will not be shown unless the map has multiple parts. At the moment, the map parts toolbar has only a single drop down box for indicating and selecting the active map part. ## Drawing toolbar {#drawing-toolbar} #### ![ ](../mapper-images/tool-edit.png) Edit objects {#tool_edit_point} E Using this tool, click on the map to select a single object. If the object you wish is not selected with the first click, click more times to go through all objects below the cursor. To select multiple object hold the Shift key when you click subsequent objects. If you Shift-click on an object which has been already been selected the click will deselect that object. A group of objects may also be selected by drawing a selection box with a left-click and drag. If only a few objects are selected, the nodes defining these object become visible. There are different types of nodes: - A square stands for a normal node. - A diamond depicts a [dash point (see below)](#dash-points). - A circle depicts a bezier curve handle, defining the tangent of a curve point. Individual nodes can be dragged with the mouse to change their position. To change the tangent direction of a node on a bezier curve click the end of the handle and drag it. Longer handles have greater influence, while shorter handles have only local effect. To **add** an extra node to an existing line or curve, Ctrl-click on an empty spot of a path. By moving the mouse, the new node can be positioned in the same action. If a **dash point** is required, hold the space bar down while Ctrl-clicking. To **remove** a node, Ctrl-click an existing node. To move the selected objects as a whole, click and drag the dashed box which encloses all selected objects. While dragging nodes or objects, there are means for more accurate positioning: - Holding **Ctrl** constrains the movement angle. For nodes and regularly shaped objects, these will be the parallel and perpendicular directions to the object. - Holding **Shift** makes the cursor snap to other existing objects. #### ![ ](../mapper-images/tool-edit-line.png) Edit lines {#tool_edit_line} L Using this tool, objects can be selected like with the [point editing tool](#tool_edit_point). If only a few objects are selected, line segments of these objects can be edited by clicking them and dragging the mouse. For straight lines, the angle constraint will be active automatically. This makes it easy to edit e.g. rectangular houses while ensuring that they stay rectangular. To toggle the angle constraint, hold Ctrl. To switch a line segment between curved and straight, hold Ctrl while clicking the segment. #### ![ ](../mapper-images/draw-point.png) Set point objects {#tool_draw_point} S This tool enables you to insert a point symbol from the map symbol window. This includes boulders, rocky ground, knolls, waterholes or any other symbol representing a point feature too small to be drawn to scale. Select the symbol for the feature to be inserted by clicking it on the symbol window, click the point tool (if it is not activated automatically) and then click on the map to insert that feature on the map. Some point symbols may be rotated, like the cave or the elongated small knoll. To set the desired orientation, click and move the mouse into the desired direction while holding the left mouse button down. To change the orientation after the point object has been placed, use the [rotate pattern tool](#tool_rotate_pattern). #### ![ ](../mapper-images/draw-path.png) Draw paths {#tool_draw_path} P The path tool draws line objects such as contour lines, watercourses and roads and area objects such as open land or different vegetation densities. To draw, first choose the symbol for the feature to be inserted by clicking it on the symbol window, then click the line tool if it has not been activated automatically. To draw a path consisting of **straight segments**, like the shape of a house, proceed by clicking at the position of each corner on the map. At the last corner, right click or double click to finish the path. Area symbols will be closed automatically, so you do not need to click the first position again. In case you do not want right clicking to set the last path point in addition to finishing the path, you can change this behavior in the [program settings](settings.md). To create a **smooth path** (a cubic bezier spline), click and drag the mouse away in the continuing direction. This creates a node with control handles which may be subsequently used to refine the shape of the curve. *Note:* While this way of drawing curves may seem to be difficult at first, it is important to get used to it. Drawing "smooth" curves with many small straight segments instead is **not an alternative**, as these will look ugly when viewed closely. Just practice bezier curve drawing until you get used to it. Less nodes are often better than too many. It is also possible to **mix straight and curved segments**: click once to create a corner point, click and drag to create a curve points. Segments between two curve points will be created as smooth curves, while all other segments will be straight. To **snap** to existing objects when starting to draw or while drawing a path, hold Shift. To **follow** the shape of existing objects while drawing, click at the existing object while holding Shift and drag the mouse along it. Depending on the direction in which you drag the mouse, the object will be followed along this direction. To constrain the **drawing angle**, hold Ctrl while drawing a path. The available angles will be intelligently chosen such that you can continue paths straight forward, or insert 90 or 45 degree corners. To **pick a direction** of an existing object, Ctrl+click the desired object before starting to draw the path. Drawing angles will then be automatically constrained to the picked direction (and its perpendicular directions) until you press the Ctrl key. This is very useful for e.g. drawing parallel houses or hedges/fences in front of houses which are parallel to them. To remove a misplaced point while drawing a line use the backspace key to **undo** one step at a time, or leave it and correct the position using the [point editing tool](#tool_edit_point). If the partly drawn line is discovered to be a mistake use the Esc key to abort and remove it entirely. ##### Closed paths To draw a **closed path** (closed contour line, lake, etc.), press the return key to close the last segment of the path to the starting point. When drawing with area symbols, paths are closed automatically. However, there may be an unwanted corner at the start/end point if you just finish the path roughly at the position where you started it, instead of pressing return. ##### Dash points Dash points serve different purposes, depending on the symbol on which they are used: 1. For dashed line symbols like small paths it is sometimes useful to be able to steer the positioning of the dashes, e.g. path crossings should preferably be at the center of line dashes. When a dash point is inserted in a line, dashes will be exactly centered on this point. 2. For lines with patterns such as fences, it is sometimes useful to be able to steer the positioning of the patterns. When a dash point is inserted in a line with such a pattern, patterns are shifted away from it. This is e.g. useful for corners of a fence to ensure a minimum distance of the dash patterns to the corners. 3. Some symbols (e.g. 516 Power line) require bars at some nodes (pylons) but for example not at the point where the line ends at the edge of the map (no pylon). The bar is defined as a dash symbol inside the line symbol for 516 Power line. It will appear only at dash points along a line. In general, it is enough to remember that dash points steer the positioning of line dashes, patterns, and dash symbols. When drawing, just try out how it behaves. Dash point nodes have a diamond shape when the line is selected (ordinary nodes are square). Drawing of a node as a dash point is toggled with the space bar: dash points will be drawn while the helpful tip at the bottom edge shows "**Dash points on**". This switch may be varied from node to node along the line. #### ![ ](../mapper-images/draw-circle.png) Draw circles and ellipses {#tool_draw_circle} O The circle tool can be used to draw round lines and areas. To start drawing, first select a symbol of type line or area and the circle tool. To draw a circle, either click on one point on the circle and then on the opposite point, or click and hold at the starting point and release the mouse button at the end point. To draw an ellipse, click at one of the boundary points on the major or minor axis of the ellipse, then click and hold at the opposite point on the ellipse and define the extent by dragging the mouse away. Circles and ellipses are represented by four bezier curves and can be edited like any other path. #### ![ ](../mapper-images/draw-rectangle.png) Draw rectangles {#tool_draw_rectangle} Ctrl+R The rectangle tool is used to draw shapes with any number of 90 or 45 degree corners such as a building. To draw, first select the line or area symbol you want to draw with and select the rectangle tool. Then click once at each corner and double click or right click at the last corner of the rectangle. You can speed up drawing by clicking and dragging the mouse to draw two corners in one step: one at the click position and one at the release position. While drawing, hold Ctrl to constrain the position of the next corner to positions which align with already drawn segments. For example, when drawing a small indentation, Ctrl can be held at its last corner to set it exactly at the position to which the line before the indentation would extend. Rectangle objects can be edited like any path. However, consider using the [line edit tool](#tool_edit_line) to preserve the angles. #### ![ ](../mapper-images/draw-freehand.png) Draw free-handedly {#tool_draw_freehand} This tool draws both line and area objects by approximating the path taken by the cursor using straight line segments. To use this tool, click at the starting point, drag the cursor where you want the path to go then release the mouse to finish drawing. The object you have created can be edited in the same way as other line or area objects. #### ![ ](../mapper-images/tool-fill.png) Fill bounded areas {#tool_fill} F This tool fills areas of unbroken white space with an area symbol. To use this tool, select the area symbol then click on white space i.e. any place not already covered by another area object. Internally, this tool first finds all paths, which can belong to many different objects, that form a boundary around the unbroken white space. A new closed shape is created that has the same paths as the white space boundary and is filled with your chosen symbol. Also, if you use this tool with a line symbol selected, then a border around the white space will be created. *Attention:* This tool will not work if your chosen white space is not completely contained by other path objects. #### ![ ](../mapper-images/draw-text.png) Write text {#tool_draw_text} T This tool places text on the map. In order to be language-independent orienteering maps generally do not have names or text information on features, but text is useful for map titles and version numbers for example. It is necessary to select a text symbol (which determines the font settings) before the text tool will become available. Two different types of text objects can be placed with this tool: - Clicking once creates a text object with a single anchor point. - Clicking and dragging the mouse creates a text box (with automatic text wrap). When text is edited, a small window with horizontal and vertical alignment options is shown. To edit text after it is created, choose the [edit objects tool](#tool_edit_point), select the text object, and click again inside the object. #### ![ ](../mapper-images/pencil.png) Paint on template {#draw_on_template} This tool enables the freehand drawing of lines, annotation and erasure on images loaded as [templates](templates.md), in a choice of 8 colors. It is intended for surveying with a mobile computer. Click and hold the left mouse button to draw while the mouse is moved. Hold the right mouse button as the mouse is moved to erase. The markup is saved in the template image file **permanently**, so it is good practice to keep a copy of the template file in another place or use a blank transparent image for drawing. ## Editing toolbar {#editing-toolbar} #### ![ ](../mapper-images/delete.png) Delete {#tool_delete} Del This tool deletes a currently selected object(s). #### ![ ](../mapper-images/tool-duplicate.png) Duplicate {#duplicate} D This tool creates a duplicate of any selected object. Select the object(s), then click the tool to create an identical second symbol in the same place. As the duplicate is created in the same place, the appearance of the map does not immediately change. However, the duplicate can be selected and dragged to another location leaving the original symbol behind. To drag a symbol select the [edit objects tool](#tool_edit_point), then click and hold on the enclosing box and drag as required. The duplicate tool is particularly useful when applied to create and move identical groups of items. #### ![ ](../mapper-images/tool-switch-symbol.png) Switch symbol {#switch_symbol} Ctrl+G This tool changes the symbols of the selected objects to another. To use it, select the object(s) to change on the map, then select the target symbol in the symbol window. (*Attention:* With default settings, it is not possible to do this the other way round as selecting an object will select its symbol, so the initial symbol selection is discarded.) Then click the tool and the map symbols will change to the target symbol - provided that the target symbol can be applied to the selected map objects (i.e. either both must be points, or both texts, or both one of line, area or combined symbols.) #### ![ ](../mapper-images/tool-fill-border.png) Fill / Create border {#fill_create_border} Ctrl+F Having drawn a [closed boundary](#drawing-toolbar) which requires a fill (such as a fence containing a thicket) select the boundary on the map (using the [point edit tool](#tool_edit_point) tool), then choose the required fill symbol in the symbols window. (*Attention:* with default settings, it is not possible to do this the other way round as selecting an object will select its symbol, so the initial symbol selection is discarded.) A left click on the fill tool will put the chosen fill into the selected boundary. Internally, this tool creates a duplicate of the selected object and assigns it the selected symbol. So, in addition to filling closed boundaries, it is also possible to create a boundary around an area, or create duplicates of lines with another symbol using this tool. #### ![ ](../mapper-images/tool-switch-dashes.png) Switch dash direction {#switch_dashes} Ctrl+D This tool changes the direction of the selected (line) objects, so e.g. dashes of fences or cliffs will be flipped to the other side. Internally, the tool just reverses the coordinates of the path objects. #### ![ ](../mapper-images/tool-connect-paths.png) Connect path {#connect} C This tool enables two (or more) selected lines to be joined together to create a single line. It is necessary that the ends to be joined are very close together, otherwise nothing will happen. It may be necessary to adjust the position and direction of the node at the join after connecting the lines. #### ![ ](../mapper-images/tool-boolean-union.png) Unify areas {#unify_areas} U This tool merges two or more areas (having the same symbol) into one, deleting the overlapping parts. Simply select two or more areas using the [edit objects tool](#tool_edit_point) and then click on the Unify Areas tool to apply it. #### ![ ](../mapper-images/tool-cut.png) Cut object {#cut_tool} K This tool cuts a selected existing line or area object into two parts. To cut **lines**, click at the point where it is desired to break the line. The object will be split into two at this point and both will be selected, so it is possible to insert more cuts after the first. It is also possible to click on a line and drag the mouse along it to remove the marked part completely. To cut an **area object**, select it and draw a cut line from one side of its boundary to another. Drawing works exactly as with the [draw path tool](#tool_draw_path). It is necessary to start and end the cut line on the boundary of the area to be cut. It is not sufficient to cross the boundary; both ends of the line must be on the boundary within a tolerance of 5 pixels (this may be altered in the [settings dialog](settings.md)). The cut line may contain any number of internal vertices and can be polygonal or curved. The cut occurs immediately when the line is finished on the boundary (with a right click or double click). #### ![ ](../mapper-images/tool-cut-hole.png) Cut free form hole {#cut_hole} H This tool cuts a hole into an area object. To do so, first select the object with the [point edit tool](#tool_edit_point), then click the cut free form hole tool. Then simply draw the shape of the hole on the area as you would draw with the [draw path tool](#tool_draw_path). After finishing, the boundary of the hole may then be edited in the same way as any path. Apart from free form holes, the menu shown by clicking the arrow to the right of this tool offers variants to cut circular or rectangular holes. If the line describing the hole crosses the boundary of the object, the area outside the former boundary and inside the "hole" will be filled with the area symbol; however, this **must be avoided**! Features such as the unify areas tool or the area measurement will fail for such objects. #### ![ ](../mapper-images/tool-rotate.png) Rotate object(s) {#rotate} R This tool rotates any selected object(s), which can include the whole map, about a selected pivot point and by any angle. Select the item(s) to be rotated using the [point edit tool](#tool_edit_point), then click the rotate tool. The rotation center will be marked with a small circle. Initially it is located at the bounding box midpoint of the selected objects, but it can be set to any position by clicking on the map. Then click somewhere at a convenient radius from the rotation center and move the selected objects about the rotation center to the desired position by dragging the mouse. By holding Ctrl, the rotation angle can be constrained to angles in a fixed stepping. #### ![ ](../mapper-images/tool-rotate-pattern.png) Rotate patterns {#tool_rotate_pattern} This tool has two purposes: - Setting the orientation of **area symbol patterns**, e.g. forest runnable in one direction. - Adjusting the orientation of **rotatable point symbols**, e.g. caves, after they have been placed. To use it, first select the object to be changed with the [point edit tool](#tool_edit_point), then click the rotate pattern tool. Then click any position on the map and drag the mouse into the desired direction to change the object's pattern to. #### ![ ](../mapper-images/tool-scale.png) Scale object(s) {#scale} Z This tool scales the selected object(s), which can include the whole map. It works completely analogous to the [rotate tool](#rotate). #### ![ ](../mapper-images/tool-measure.png) Measure lengths and areas {#measure} M This tool can be used to measure line lengths and area sizes. It will show both real world length or area in meters and the resulting length or area on the printed map. When this tool is activated, a window appears containing the measurements for the selected object. To measure different objects, the selection can be changed using the edit tool while the window is active. It is also possible to draw new paths and have their length or area shown as they are drawn. ## Advanced drawing toolbar {#advanced-drawing-toolbar} #### ![ ](../mapper-images/tool-cutout-physical.png) Cutout {#cutout_physical} This cuts away all objects except inside a given region, making a map excerpt. It can also be used to cut away only a selected subset of objects outside the region. To use it: - Draw the cutout shape (with any line or area symbol). The shape must be closed, ensure this by finishing the drawing by pressing the return key. - Then select the cutout tool. The shape will be marked in red. - For making a cutout of the map, now press the return key. - For cutting only some object, select those objects before pressing return. #### ![ ](../mapper-images/tool-cutout-physical-inner.png) Cut away {#cutaway_physical} This is the opposite to the above cutout tool. It cuts away all or a subset of objects inside a selected cutout region. Usage is identical to those of the cutout tool. This tool is useful for making training maps where certain symbols are missing in some places. For example, to create a contour-only region in a map, proceed like this: - Draw the cutout shape (with any line or area symbol). The shape must be closed, ensure this by finishing the drawing by pressing the return key. - Click the cut away tool. The shape will be marked in red. - In the symbol pane, select all symbols you want to cut away, e.g. everything except contours. You can select a range of symbols by clicking the first symbol, then clicking the last symbol while holding the Shift key. - Press the return key. #### ![ ](../mapper-images/tool-convert-to-curves.png) Convert to curves {#convert_to_curves} N This tool changes the shape of selected polygonal objects to smooth. It can be used to "prettify" objects which have been drawn as polygonal objects but are curved in reality (however, it is usually less effort to draw these as curved objects from the start). Note that this tool is still experimental and might take some time to compute for a large number of objects. #### ![ ](../mapper-images/tool-simplify-path.png) Simplify path {#simplify_path} Ctrl-M This tool simplifies the shape of the selected paths by removing points which can be removed without changing the object's shape significantly. Note that this tool is still experimental and might take some time to compute for a large number of objects. #### ![ ](../mapper-images/tool-distribute-points.png) Distribute points along path {#distribute_points} This tool lets you create a number of point objects evenly distributed along a path. To use this tool, you need to select a path object first, then select the point symbol. Now the action will open a dialog which lets you choose the number of objects and various other parameters. #### ![ ](../mapper-images/tool-boolean-intersection.png) Intersect areas {#intersect_areas} This tool deletes all of the selected area objects that do not intersect with first-selected area. The only part which will remain is that part of the first area selected which was previously overlapped by one or more of the other selected areas. Select two or more overlapping objects of the same area type and then click on this tool to use it. #### ![ ](../mapper-images/tool-boolean-difference.png) Cut away from area {#area_difference} This tool deletes all parts of the first selected area object that overlap with one of the other selected areas. Select two or more overlapping objects of the same area type and then click on this tool to use it. #### ![ ](../mapper-images/tool-boolean-xor.png) Area XOr {#area_xor} This tool will XOr all selected areas. This means that all parts of the selected areas that overlap with another selected area will be deleted. Select two or more objects of the same area type and then click on this tool to use it. #### ![ ](../mapper-images/tool-boolean-merge-holes.png) Merge area holes {#area_merge_holes} This tool unifies the areas of two or more overlapping holes in an area object into a single hole. These holes must be created using the [cut hole tools](#cut_hole). This tool is useful as when two area holes overlap, the overlapping portion(s) have the same fill as the parent area object instead of being empty, which may not be desired. This tool may also be useful after boolean operations on objects. mapper-0.8.1.1/doc/manual/pages/tools_menu.md000066400000000000000000000004341325266516600210070ustar00rootroot00000000000000--- title: Tools Menu authors: - Peter Hoban - Thomas Schoeps keywords: Menus edited: 26 Februrary 2013 --- All tools accessed through this menu are also available on the toolbar. So for documentation, see the [drawing toolbars reference](toolbars.md#drawing-toolbar). mapper-0.8.1.1/doc/manual/pages/view_menu.md000066400000000000000000000064021325266516600206220ustar00rootroot00000000000000--- title: View Menu authors: - Peter Hoban - Thomas Schoeps - Kai Pastor keywords: Menus edited: 20 January 2018 --- #### ![ ](../mapper-images/move.png) Pan **(F6)** Activates the [pan tool](toolbars.md#pan_map) which can be used to move the map. Note that key F6 starts panning immediately. #### ![ ](../mapper-images/view-zoom-in.png) Zoom in **F7** Zoom in (enlarge) to view the map closer. #### ![ ](../mapper-images/view-zoom-out.png) Zoom out **F8** Zoom out (shrink) the map to see more of it. #### ![ ](../mapper-images/view-show-all.png) Show whole map {#zoom-all} Use this tool to display the whole map on your screen. #### Set custom zoom factor... Shows a dialog to enter a specific zoom factor. Note that the zoom factor on the screen is not necessarily equal to the printed scale of the map. --- #### ![ ](../mapper-images/grid.png) Show grid **G** Shows or hides the [map grid](grid.md). #### Hatch areas **F2** When this toggle is activated, all area objects in the map will be drawn in a hatched, semi-transparent style. This is useful to be able to see any templates below the map. #### Baseline view **F3** When this toggle is activated, instead of the normal appearance of map objects only their midpoints respectively baselines are displayed. This can be combined with Hatch areas to see anything below the map even better. #### Overprinting simulation {#overprinting} **F4** When this toggle is activated, the effect of printing the map with layers of spot colors on top of each other will be simulated. This leads to improved map readability. This function requires a color set with spot color definitions. #### Hide all templates **F10** When this toggle is activated, no templates will be displayed. This is useful to check the appearance of the map alone. --- #### Display coordinates as ... {#coorddisplay} The coordinates of the cursor on the map are shown in the position box at the bottom right corner of the program window. Paper coordinates refer to positions on a paper print, from an arbitrary origin. If your map has been [georeferenced](georeferencing.md), additional coordinate systems will be available. Projected coordinates are meters of easting and northing. Latitude & longitude may be expressed in decimal degrees or in degrees, minutes and seconds. The coordinate system may also be selected by a right-click on the position box at the bottom right corner of your screen which will provide the same choices. --- #### Toggle fullscreen mode {#fullscreen} **F11** This toggle switches the display to full-screen mode and back. --- #### ![ ](../mapper-images/window-new.png) Tag editor **Ctrl+Shift+6** This toggle shows or hides the [tag editor](object_tags.md#the-tag-editor). #### ![ ](../mapper-images/colors.png) Color window **Ctrl+Shift+7** This toggle shows or hides the [color window](color_dock_widget.md). #### ![ ](../mapper-images/symbols.png) Symbol window **Ctrl+Shift+8** This toggle shows or hides the [symbol pane](symbol_dock_widget.md). #### ![ ](../mapper-images/templates.png) Template setup window **Ctrl+Shift+9** This toggle shows or hides the [template setup window](templates.md#setup). mapper-0.8.1.1/doc/manual/postprocess-pdflatex.cmake.in000066400000000000000000000024771325266516600230130ustar00rootroot00000000000000# # Copyright 2017 Kai Pastor # # This file is part of OpenOrienteering. # # OpenOrienteering is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # OpenOrienteering is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with OpenOrienteering. If not, see . file(GLOB input_files RELATIVE "@CMAKE_CURRENT_BINARY_DIR@" "@CMAKE_CURRENT_BINARY_DIR@/pdflatex/*.tex") foreach(file ${input_files}) file(READ "@CMAKE_CURRENT_BINARY_DIR@/${file}" input) # Turn internal typewriter-face hrefs into regular PDF hyperlinks string(REGEX REPLACE "\\\\href{#([^}]*)}{\\\\tt ([^}]*)}" "\\\\hyperlink{\\1}{\\2}" output "${input}") string(MD5 input_md5 "${input}") string(MD5 output_md5 "${output}") if(NOT "${output_md5}" STREQUAL "${input_md5}") message(STATUS "Updating ${file}") file(WRITE "${file}" "${output}") endif() endforeach() mapper-0.8.1.1/doc/manual/postprocess-qhp.cmake.in000066400000000000000000000027721325266516600217720ustar00rootroot00000000000000# # Copyright 2014 Kai Pastor # # This file is part of OpenOrienteering. # # OpenOrienteering is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # OpenOrienteering is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with OpenOrienteering. If not, see . set(index_qhp "@CMAKE_CURRENT_BINARY_DIR@/html/index.qhp") file(READ "${index_qhp}" output) string(MD5 file_md5 "${output}") string(REGEX REPLACE " *tabs.css.*sync_off.png" "" output "${output}") string(REGEX REPLACE " *ftv2.*ftv2[^.]*[.]png" "" output "${output}") string(REGEX REPLACE " *openorienteering.png" "" output "${output}") string(REGEX REPLACE " *(graph_legend|pages)[.](html|png)" "" output "${output}") string(REGEX REPLACE "( *)(index.html)" "\\1\\2\n\\1openorienteering.png" output "${output}") string(MD5 output_md5 "${output}") if(NOT "${output_md5}" STREQUAL "${file_md5}") message(STATUS "Updating ${index_qhp}") file(WRITE "${index_qhp}" "${output}") endif() mapper-0.8.1.1/doc/manual/preprocess-markdown-html.cmake.in000066400000000000000000000203171325266516600235620ustar00rootroot00000000000000# # Copyright 2014, 2015, 2017 Kai Pastor # # This file is part of OpenOrienteering. # # OpenOrienteering is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # OpenOrienteering is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with OpenOrienteering. If not, see . cmake_policy(SET CMP0007 NEW) # Splits the frontmatter from the body of the input, and stores these parts # in the variables with the names given in the frontmatter and body parameters. function(split_frontmatter frontmatter body input) string(REGEX MATCH "\r?\n----*\r?\n.*$" _body "${input}") string(REPLACE "${_body}" "" _frontmatter "${input}") string(REGEX REPLACE "^----*(\r?\n)(.*)" "\\1\\2" _frontmatter "${_frontmatter}") string(REGEX REPLACE "^\r?\n----*\r?\n" "" _body "${_body}") set(${frontmatter} "${_frontmatter}" PARENT_SCOPE) set(${body} "${_body}" PARENT_SCOPE) endfunction() # Gets the value of the given field from the YAML input, and stores it # in the variable named by result. function(get_yaml_field result input field) string(REGEX MATCH "(^|\r?\n)${field} *:.*" match "${input}") if(match) string(REGEX REPLACE "\r?\n?${field} *: *" "" value "${match}") if(value MATCHES "^\r?\n") # list string(REGEX REPLACE "\r?\n(---|[^\n\r]*:).*" "" value "${value}") string(REGEX REPLACE "\r?\n *- *([^\n\r]*)" "\\1;" value "${value}") list(REMOVE_ITEM value "") else() # single value string(REGEX REPLACE "\r?\n.*" "" value "${value}") endif() set(${result} ${value} PARENT_SCOPE) else() set(${result} "${field}-NOTFOUND" PARENT_SCOPE) endif() endfunction() if(NOT EXISTS "markdown-html") file(MAKE_DIRECTORY "markdown-html") endif() if(NOT EXISTS "preprocess-markdown-html.stamp") file(WRITE "preprocess-markdown-html.stamp") endif() set(all_pages ) file(GLOB input_files RELATIVE "@CMAKE_CURRENT_SOURCE_DIR@/pages" "@CMAKE_CURRENT_SOURCE_DIR@/pages/*.md") foreach(file ${input_files}) set(file_remarks ) string(REGEX REPLACE "[.]md$" "" pagename "${file}") string(REPLACE " " "_" pagename_safe "${pagename}") if(NOT "${pagename}" STREQUAL "${pagename_safe}") list(APPEND file_remarks "unsafe filename") endif() file(READ "@CMAKE_CURRENT_SOURCE_DIR@/pages/${file}" input) # YAML frontmatter split_frontmatter(frontmatter output "${input}") if(NOT frontmatter) message(FATAL_ERROR "${file}:1: Missing frontmatter") endif() get_yaml_field(title "${frontmatter}" "title") if(NOT title) message(FATAL_ERROR "${file}:1: No title in frontmatter") endif() set(output "${title} {#${pagename_safe}}\n===\n\n${output}") get_yaml_field(subpages ${frontmatter} "subpages") if(subpages) string(REPLACE ".md" "" subpages "${subpages}") list(INSERT subpages 0 "## More Information\n") string(REGEX REPLACE ";" "\n - \\\\subpage " subpages "${subpages}") set(output "${output}\n\n${subpages}") endif() get_yaml_field(keywords ${frontmatter} "keywords") if(keywords) string(REGEX REPLACE ";" "\n\\\\addindex " keywords "${keywords}") # Not working as expected: #set(output "${output}\n\n\\addindex ${keywords}") endif() get_yaml_field(edited ${frontmatter} "edited") if(edited) set(output "${output}\n\n---\nUpdate on ${edited}") else() list(APPEND file_remarks "missing field 'edited'") endif() get_yaml_field(online ${frontmatter} "online") if(online) string(REPLACE "{{ page.online }}" "${online}" output "${output}") endif() # HTML markup string(REGEX MATCH "]*>" unsupported_element "${output}") if(unsupported_element) message(FATAL_ERROR "${file}:1: Unsupported HTML element ${unsupported_element}") endif() string(REGEX REPLACE "
    \r?\n?" "\n---\n" output "${output}") string(REGEX REPLACE "\n?]*src=\"([^\"]*)\"[^>]*>[\n ]?" "\n![](\\1)\n" output "${output}") # SF Markdown string(REGEX REPLACE "\\[\\[img +(src=)?([^] ]+) *]]" "![](\\2)" output "${output}") string(REGEX REPLACE "\\[\\[img +(src=)?([^] ]+) +alt=\"([^\"]+)\" *]]" "![\\3](\\2)" output "${output}") string(REGEX REPLACE "\\[\\[img +(src=)?([^] ]+) +alt=([^]]*) *]]" "![\\3](\\2)" output "${output}") # Resource paths if(@ANDROID@) # Our text browser can access our images directly. string(REGEX REPLACE "../mapper-images/" ":/images/" output "${output}") endif() string(REGEX REPLACE "(!\\[[^]]*\\]\\()../mapper-images/" "\\1" output "${output}") string(REGEX REPLACE "href=\"attachment/" "href=\"" output "${output}") # Headlines: workaround doxygen strict hierarchy requirements string(REGEX REPLACE "(\r?\n)#(#+ +[^\r\n{]*{#[^\r\n]*})" "\\1\\2" output "${output}") # toolbar.md headline entries with icon - doxygen doesn't support inline images string(REGEX REPLACE "(\r?\n)###+ +(( *!\\[([^]]*)\\]\\(([^)]*)\\))*) *" "\\1\\2" output "${output}") while(output MATCHES "([^\r\n]*!\\[[^]]*\\]\\([^)]*\\)) *(!\\[[^]]*\\]\\([^)]*\\))") string(REGEX REPLACE "([^\r\n]*!\\[[^]]*\\]\\([^)]*\\)) *(!\\[[^]]*\\]\\([^)]*\\))" "\\1\\2" output "${output}") endwhile() string(REGEX REPLACE "([^\r\n]*[^\r\n{]*){#([^\r\n]*)}" "
    \n\\1" output "${output}") string(REGEX REPLACE "([^\r\n]*)" "\n\\1
    \n" output "${output}") # Liquid/Kramdown/Markdown markup string(REGEX REPLACE "\r?\n *[-*] *[^\r\n]*\r?\n{:toc}" "\\\\tableofcontents" output "${output}") string(REGEX REPLACE "\\[([^]]*)\\]\\(([^\)]*).md\\){: \\.subpage}" "\\\\subpage \\2 \"\\1\"" output "${output}") string(REGEX REPLACE "(\\[[^]]*\\]\\([^\)]*)\\.md(\\)|#)" "\\1.html\\2" output "${output}") string(REGEX REPLACE "(href=\"[^\"]*)\\.md(\"|#)" "\\1.html\\2" output "${output}") string(REGEX REPLACE "(\r?\n *\\[[^]]*\\]: *[^ ]*).md(\r?\n| |#)" "\\1.html\\2" output "${output}") string(REGEX REPLACE "{% [^%]* %}|{:[^}]*}" "" output "${output}") if (APPLE) string(REGEX REPLACE "Alt[+]" "⌥" output "${output}") string(REGEX REPLACE "Ctrl[+]" "⌘" output "${output}") string(REGEX REPLACE "Shift[+]" "⇧" output "${output}") string(REGEX REPLACE "⌘⇧" "⇧⌘" output "${output}") endif() string(MD5 output_md5 "${output}") set(file_md5) if(EXISTS "markdown-html/${file}") file(MD5 "markdown-html/${file}" file_md5) endif() if(NOT "${output_md5}" STREQUAL "${file_md5}") message(STATUS "Updating ${file}") file(WRITE "markdown-html/${file}" "${output}") if(EXISTS "preprocess-markdown-html.stamp") file(READ "preprocess-markdown-html.stamp" markdown_log) string(REGEX REPLACE "([.+[\(\)^$*?|]|])" "\\\\\\1" file_esc "${file}") string(REGEX REPLACE "(^|\n)[^\n]*: ${file_esc}( \\([^)]*\\))?\r?\n" "\\1" markdown_log "${markdown_log}") else() set(markdown_log "") endif() string(TIMESTAMP date "%Y-%m-%d %H:%M:%S") if (file_remarks) set(file_remarks " (${file_remarks})") endif() file(WRITE "preprocess-markdown-html.stamp" "${markdown_log}" "${date}: ${file}${file_remarks}\n") endif() endforeach() file(GLOB output_files RELATIVE "@CMAKE_CURRENT_BINARY_DIR@/markdown-html" "@CMAKE_CURRENT_BINARY_DIR@/markdown-html/*.md") foreach(file ${output_files}) if(NOT EXISTS "@CMAKE_CURRENT_SOURCE_DIR@/pages/${file}") message(STATUS "Removing ${file}") file(REMOVE "markdown-html/${file}") if(EXISTS "preprocess-markdown-html.stamp") file(READ "preprocess-markdown-html.stamp" markdown_log) string(REGEX REPLACE "([.+[\(\)^$*?|]|])" "\\\\\\1" file_esc "${file}") string(REGEX REPLACE "(^|\n)[^\n]*: ${file_esc}\r?\n" "\\1" markdown_log "${markdown_log}") else() set(markdown_log "") endif() file(WRITE "preprocess-markdown-html.stamp" "${markdown_log}") endif() endforeach() mapper-0.8.1.1/doc/manual/preprocess-markdown-pdflatex.cmake.in000066400000000000000000000261361325266516600244320ustar00rootroot00000000000000# # Copyright 2014, 2015, 2017 Kai Pastor # # This file is part of OpenOrienteering. # # OpenOrienteering is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # OpenOrienteering is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with OpenOrienteering. If not, see . cmake_policy(SET CMP0007 NEW) # Splits the frontmatter from the body of the input, and stores these parts # in the variables with the names given in the frontmatter and body parameters. function(split_frontmatter frontmatter body input) string(REGEX MATCH "\r?\n----*\r?\n.*$" _body "${input}") string(REPLACE "${_body}" "" _frontmatter "${input}") string(REGEX REPLACE "^----*(\r?\n)(.*)" "\\1\\2" _frontmatter "${_frontmatter}") string(REGEX REPLACE "^\r?\n----*\r?\n" "" _body "${_body}") set(${frontmatter} "${_frontmatter}" PARENT_SCOPE) set(${body} "${_body}" PARENT_SCOPE) endfunction() # Gets the value of the given field from the YAML input, and stores it # in the variable named by result. function(get_yaml_field result input field) string(REGEX MATCH "(^|\r?\n)${field} *:.*" match "${input}") if(match) string(REGEX REPLACE "\r?\n?${field} *: *" "" value "${match}") if(value MATCHES "^\r?\n") # list string(REGEX REPLACE "\r?\n(---|[^\n\r]*:).*" "" value "${value}") string(REGEX REPLACE "\r?\n *- *([^\n\r]*)" "\\1;" value "${value}") list(REMOVE_ITEM value "") else() # single value string(REGEX REPLACE "\r?\n.*" "" value "${value}") endif() set(${result} ${value} PARENT_SCOPE) else() set(${result} "${field}-NOTFOUND" PARENT_SCOPE) endif() endfunction() if(NOT EXISTS "markdown-pdflatex") file(MAKE_DIRECTORY "markdown-pdflatex") endif() if(NOT EXISTS "preprocess-markdown-pdflatex.stamp") file(WRITE "preprocess-markdown-pdflatex.stamp") endif() set(all_pages ) set(subpage_list index) file(GLOB input_files RELATIVE "@CMAKE_CURRENT_SOURCE_DIR@/pages" "@CMAKE_CURRENT_SOURCE_DIR@/pages/*.md") foreach(file ${input_files}) set(file_remarks ) string(REGEX REPLACE "[.]md$" "" pagename "${file}") string(REPLACE " " "_" pagename_safe "${pagename}") if(NOT "${pagename}" STREQUAL "${pagename_safe}") list(APPEND file_remarks "unsafe filename") endif() list(APPEND subpage_list "${pagename_safe}") file(READ "@CMAKE_CURRENT_SOURCE_DIR@/pages/${file}" input) # YAML frontmatter split_frontmatter(frontmatter output "${input}") if(NOT frontmatter) message(FATAL_ERROR "${file}:1: Missing frontmatter") endif() get_yaml_field(title "${frontmatter}" "title") if(NOT title) message(FATAL_ERROR "${file}:1: No title in frontmatter") endif() set(output "${title} {#${pagename_safe}}\n===\n\n${output}") get_yaml_field(subpages ${frontmatter} "subpages") if(subpages) string(REPLACE ".md" "" subpages "${subpages}") list(INSERT subpages 0 "## More Information\n") string(REGEX REPLACE ";" "\n - \\\\subpage " subpages "${subpages}") set(output "${output}\n\n${subpages}") endif() get_yaml_field(keywords ${frontmatter} "keywords") if(keywords) string(REGEX REPLACE ";" "\n\\\\addindex " keywords "${keywords}") # Not working as expected: #set(output "${output}\n\n\\addindex ${keywords}") endif() # HTML markup string(REGEX MATCH "]*>" unsupported_element "${output}") if(unsupported_element) message(FATAL_ERROR "${file}:1: Unsupported HTML element ${unsupported_element}") endif() string(REGEX REPLACE "
    \r?\n?" "\n---\n" output "${output}") string(REGEX REPLACE "\n?]*src=\"([^\"]*)\"[^>]*>[\n ]?" "\n![](\\1)\n" output "${output}") # SF Markdown string(REGEX REPLACE "\\[\\[img +(src=)?([^] ]+) *]]" "![](\\2)" output "${output}") string(REGEX REPLACE "\\[\\[img +(src=)?([^] ]+) +alt=\"([^\"]+)\" *]]" "![\\3](\\2)" output "${output}") string(REGEX REPLACE "\\[\\[img +(src=)?([^] ]+) +alt=([^]]*) *]]" "![\\3](\\2)" output "${output}") # Resource paths string(REGEX REPLACE "(!\\[[^]]*\\]\\()../mapper-images/" "\\1" output "${output}") string(REGEX REPLACE "href=\"attachment/" "href=\"" output "${output}") # Headlines: workaround doxygen strict hierarchy requirements string(REGEX REPLACE "(\r?\n)#(#+ +[^\r\n{]*{#[^\r\n]*})" "\\1\\2" output "${output}") # Headlines: Add labels when missing (no trailing '{#xxx}') string(REGEX REPLACE "([\r\n]#+ +)([^\r\n]*[^\r\n} ]) *([\r\n])" "\\1 \\2 {#LABEL!\\2}\\3" output "${output}") string(REGEX MATCHALL "{#LABEL![^\r\n]*}" labels "${output}") foreach(label ${labels}) string(REPLACE " " "-" replacement "${label}") string(REPLACE "{#LABEL!" "" replacement "${replacement}") string(REGEX REPLACE "[^-_a-zA-Z0-9]" "" replacement "${replacement}") string(TOLOWER "${replacement}" replacement) string(REPLACE "${label}" "{#${replacement}}" output "${output}") endforeach() string(REGEX REPLACE "([\r\n]#+ [^\r\n]*){#([^}]*)}" "\n\\\\latexonly\n\\\\hypertarget{${pagename_safe}_\\2}{}\n\\\\label{${pagename_safe}_\\2}\n\\\\endlatexonly\\1" output "${output}") # Can't use Markdown for images in LaTeX set(scale "0.55") # 1. All non-inline images string(REGEX REPLACE "[\r\n][\t ]*!\\[ *\\]\\( *([^)]*)\\)[\t ]*[\r\n]" "\n\\\\latexonly\n\\\\begin{DoxyImageNoCaption}\n \\\\mbox{\\\\includegraphics[scale=${scale}]{\\1}}\n\\\\end{DoxyImageNoCaption}\n\\\\endlatexonly\n" output "${output}") string(REGEX REPLACE "[\r\n][\t ]*!\\[ *([^]]*)\\]\\( *([^)]*)\\)[\t ]*[\r\n]" "\n\\\\latexonly\n\\\\begin{DoxyImage}\n \\\\includegraphics[scale=${scale}]{\\2}\n \\\\caption{\\1}\n\\\\end{DoxyImage}\n\\\\endlatexonly\n" output "${output}") # 2. Remaining images are inline string(REGEX REPLACE "[\r\n]####? *([\r\n]*!\\[[^\r\n{]*){#([^}]*)}" "\n\\\\latexonly\n\\\\hypertarget{${pagename_safe}_\\2}{}\\\\subsubsection*{\n\\\\endlatexonly\n\\1\n\\\\latexonly\n}\\\\label{${pagename_safe}_\\2}\n\\\\endlatexonly\n" output "${output}") string(REGEX REPLACE "[\r\n]####? *([\r\n]*!\\[[^\r\n]*)" "\n\\\\latexonly\n\\\\subsubsection*{\n\\\\endlatexonly\n\\1\n\\\\latexonly\n}\n\\\\endlatexonly\n" output "${output}") string(REGEX REPLACE "!\\[ *([^]]*)\\]\\(([^)]*)\\)" "\\\\latexonly\n {\\\\hskip 0.1em}\\\\raisebox{-.15\\\\height}{\\\\includegraphics[height=2ex]{\\2}}{\\\\hskip 0.1em}\n\\\\endlatexonly\n" output "${output}") # 3. Common string(REGEX REPLACE "\\\\endlatexonly[\r\n\t ]*\\\\latexonly[\r\n]?" "" output "${output}") string(REGEX MATCHALL "\\\\includegraphics[^{]*{[^}]*}" graphics "${output}") string(REGEX REPLACE "(\\\\includegraphics[^{]*{)[^/}]*/([^}]*)" "\\1\\2" output "${output}") if(graphics) list(REMOVE_DUPLICATES graphics) string(REGEX REPLACE "\\\\includegraphics[^{]*{([^}]*)}" "\\\\image latex \"\\1\"\n" graphics ${graphics}) string(CONCAT output "${output}\n" "\\latexonly\n" "% *** Let doxygen copy images ***\n" "\\iffalse\n" "\\endlatexonly\n" ${graphics} "\\latexonly\n" "\\fi\n" "\\endlatexonly\n" ) endif() # Liquid/Kramdown/Markdown markup string(REGEX REPLACE "\r?\n *[-*] *[^\r\n]*\r?\n{:toc}" "\\\\tableofcontents" output "${output}") # For TeX, treat nosubpage like subpage # Collect subpage information for sorting files string(REGEX MATCHALL "\\[[^]]*\\]\\([^\)]*.md\\){: \\.n?o?subpage}" "subpages_${pagename_safe}" "${output}") if(subpages_${pagename_safe}) string(REGEX REPLACE "\\[[^]]*\\]\\(([^\)]*).md\\){: \\.n?o?subpage}" "\\1" "subpages_${pagename_safe}" "${subpages_${pagename_safe}}") endif() string(REGEX REPLACE "\\[([^]]*)\\]\\(([^\)]*)\\.md\\){: \\.n?o?subpage}" "\\\\subpage \\2 \"\\1\"" output "${output}") string(REGEX REPLACE "(\\[[^]]*\\])\\(#([^)]*)\\)" "\\1(${pagename_safe}.md#\\2)" output "${output}") string(REGEX REPLACE "(\\[[^]]*\\])\\(([^\)#]*)\\.md#([^\)]*)\\)" "\\1(#\\2_\\3)" output "${output}") string(REGEX REPLACE "(\\[[^]]*\\])\\(([^\)#]*)\\.md\\)" "\\1(#\\2)" output "${output}") string(REGEX REPLACE "{% [^%]* %}|{:[^}]*}" "" output "${output}") string(REGEX REPLACE "([\r\n] *\\[[^]]*\\]: *)([^#\r\n]*)\\.md#" "\\1#\\2_" output "${output}") string(REGEX REPLACE "([\r\n] *\\[[^]]*\\]: *)([^#\r\n]*)\\.md([^#])" "\\1#\\2\\3" output "${output}") if (APPLE) string(REGEX REPLACE "Alt[+]" "⌥" output "${output}") string(REGEX REPLACE "Ctrl[+]" "⌘" output "${output}") string(REGEX REPLACE "Shift[+]" "⇧" output "${output}") string(REGEX REPLACE "⌘⇧" "⇧⌘" output "${output}") endif() string(MD5 output_md5 "${output}") set(file_md5) if(EXISTS "markdown-pdflatex/${file}") file(MD5 "markdown-pdflatex/${file}" file_md5) endif() if(NOT "${output_md5}" STREQUAL "${file_md5}") message(STATUS "Updating ${file}") file(WRITE "markdown-pdflatex/${file}" "${output}") if(EXISTS "preprocess-markdown-pdflatex.stamp") file(READ "preprocess-markdown-pdflatex.stamp" markdown_log) string(REGEX REPLACE "([.+[\(\)^$*?|]|])" "\\\\\\1" file_esc "${file}") string(REGEX REPLACE "(^|\n)[^\n]*: ${file_esc}( \\([^)]*\\))?\r?\n" "\\1" markdown_log "${markdown_log}") else() set(markdown_log "") endif() string(TIMESTAMP date "%Y-%m-%d %H:%M:%S") if (file_remarks) set(file_remarks " (${file_remarks})") endif() file(WRITE "preprocess-markdown-pdflatex.stamp" "${markdown_log}" "${date}: ${file}${file_remarks}\n") endif() endforeach() while(subpage_list) list(GET subpage_list 0 page) list(REMOVE_ITEM subpage_list "${page}") set(path " \\\n @CMAKE_CURRENT_BINARY_DIR@/markdown-pdflatex/${page}.md") list(FIND page_list "${path}" found) if(found EQUAL -1) list(APPEND page_list "${path}") if(subpages_${page}) list(INSERT subpage_list 0 "${subpages_${page}}") endif() endif() endwhile() list(REMOVE_DUPLICATES page_list) string(REPLACE ";" " " page_list "${page_list}") set(doxygen-input "INPUT = ${page_list}") string(MD5 output_md5 "${doxygen-input}") set(file_md5) if(EXISTS "@CMAKE_CURRENT_BINARY_DIR@/Doxyfile-pdflatex-input.txt") file(MD5 "@CMAKE_CURRENT_BINARY_DIR@/Doxyfile-pdflatex-input.txt" file_md5) endif() if(NOT "${output_md5}" STREQUAL "${file_md5}") file(WRITE "@CMAKE_CURRENT_BINARY_DIR@/Doxyfile-pdflatex-input.txt" "INPUT = ${page_list}") endif() file(GLOB output_files RELATIVE "@CMAKE_CURRENT_BINARY_DIR@/markdown-pdflatex" "@CMAKE_CURRENT_BINARY_DIR@/markdown-pdflatex/*.md") foreach(file ${output_files}) if(NOT EXISTS "@CMAKE_CURRENT_SOURCE_DIR@/pages/${file}") message(STATUS "Removing ${file}") file(REMOVE "markdown-pdflatex/${file}") if(EXISTS "preprocess-markdown-pdflatex.stamp") file(READ "preprocess-markdown-pdflatex.stamp" markdown_log) string(REGEX REPLACE "([.+[\(\)^$*?|]|])" "\\\\\\1" file_esc "${file}") string(REGEX REPLACE "(^|\n)[^\n]*: ${file_esc}\r?\n" "\\1" markdown_log "${markdown_log}") else() set(markdown_log "") endif() file(WRITE "preprocess-markdown-pdflatex.stamp" "${markdown_log}") endif() endforeach() mapper-0.8.1.1/doc/manual/style.css000066400000000000000000000005371325266516600170600ustar00rootroot00000000000000/** * Beware that Qt Assistant (QTextBrowser) supports only a limited set of * HTML/CSS: http://qt-project.org/doc/qt-5/richtext-html-subset.html */ #projectlogo { float:right; } #titlearea { margin-bottom: 2px; } .title { margin-top: 4px; margin-bottom: 4px; font-size: xx-large; font-weight: bold; } a.el { font-weight: bold; } mapper-0.8.1.1/doc/openorienteering.png000066400000000000000000000325231325266516600200130ustar00rootroot00000000000000PNG  IHDR2aesRGBbKGD IDATx}wt\չEeԛ%ٲp\i6ء:'\Hh ߄\B ZjbL:68c\`ܫ$[dui4}NcFcȽy{Z9׿}G)K,A]]`Цijl6L$i¼K3uvO? PY!"IMӤieY2gϞ{{ߢ~ʷsNZzu$˲.VUubEEE~eevaf4My޽F!nEn{pAA;77W%M )B#(দ& BEJD[uZUU.))DtP}0hݻ77!YJ^^p8`'M%d۷0 hYVVV̙3oc"릀ӟj\fQ }ꩧ:h…ؽ{7 `)%.L>]]]rٲefnnrg*uuuϱuVL=ffEQp`„ 8sH$xٲeX,t]5z &(+V%K 4ADPUŸ1h ~we<ǬYDcc#m߾7oFkk+,˂eYlY !fQuu5fϞl,Y$  !@Do?| z)8SJyKaaSNx ?>aH-f;'%g]o0زrAPg+ׯAۊw@ r .8|سsQr `eAq3൒q$;ڹ SO3.Ν;p]wqGK3s N0!X" &' Ka͇͂@ E4T=ˈv3/ŋwp8ܡiouglˤ7_p%˗/y3\yмY-cӿ1cvj 7lg9"]1e\{@ѴY(~냷#$݉<'- l"ъXڱc*fUJɏ~o5㯩G}TU-a-ˢyam3ʰqPZe88TZ8>cvP)4 HC%ƈ''{/୮E9g${:aDB @/w؉ׄ4L=f;Pqe;Xh $̶oan "$ڢE(;{|~ E\\$㧠săгc4.J y``=9׀T̸GfFC` G 1C ,>o,-DeB) zB{{;TU?rf_vrssip2,S#{lm W.E #T A|H =ƶG~FsNJsezeE``k50A,|@ȱ`zAAۇg:%N{V]aTU՘9kJ`L&u;p4A&O=܃@>Y!"Wq ėwڵi^ x*`Bc!T {Nf]YsP6w8mC#ssN*8, dC0SJ| qn(RJXE©B;Ҭ  C GaQvnkR= URGQok`@( ! M-;بv cۋ)A@8P52+BT!-,F h$3KLD ")A)ROJI&`ţq(|9$ eܹG7J t]0E(d:x K7x8 -,[ hܻA*`M-F(۾ G^QxFXL);])os+￁U@$) /c`ŋqg. Hԯ fOD!"ަf$ڡy)sI NDOЃ¦i(5RO D6#PA!R2N' }[?=pmf ?!Ӂ c+ȋߖT={6^z%a-򵵵)/B @/3덹ska/?3ÿ{=;l.@ !#3zx)eRBt ! !VVUUm}cΜ9.]TN6mmܻw3gϟ%p"\e N7Ny`O*El>vX,h^?N8Ao>Dc9[lAËIgBtEUAԅ ڣѨM49,ˊ*_z+z)rv{~iiE[[~eYHd… ؇X`t?o6> 7}R ?"eƐzMMoAk=<6ap`ʔ)4 QL=b@|YP#o/R߫MHiM S%i0 >4#/2}SOH!#EQΛ4iRřgtj0eee̙3s:pa>m3gE]TjY|;v<OE5 ou-TNn^u+lދ"̚9ڽ P]lBE!I@2A 0H)yUC*.JĐlGdNGy!(q::oaP2䎛 ,c{xw}7֭['p2( !ٶo߾r+=#{gggȑ#1cƌfy$Yh}"Eh2t]nٮ]# C7,ܹs`mذs"u<_zWGWHuh83n߈rٲeO>O<M kF@f tχ;^{-BDZ,F˒ũR"( (xO9XB]P1h0|u+_Aߏ_s 6mڄ5k֠/ң EbƌXr%Z[[iBNNaxrF[PP0Hۖ-[6/ND 32x饗( -EA4NEBLPԇs@ @XLva)%Kfx(iERjkk l6`#!T;X΢Ixڵ&Mv:r\$}Qii)~ϫW6L\ve6mNb 77Z;p r8^J#~jMMMFimmz@<CEEi6n܈X,s9ߝ9_]Eeu5?xn@} 0~8{,߿F^^ۇz"n#++ EEE(**BNNڐvBwF{Duio>̜9>6m}}} '&g=v}6!MJDa45EQ-*ܑ̣~; &7{=jlltiٝ6B !Ml3MwFII {9RE3 íj63 v20)3gl2[2\يS;&kga/n !Rv !\\ BܮjWOOO?Hzg­j3&"g$3QCD"F0dϏuvv&a}[l`֭yTSScf! WF$ "=*6M't&O ׋ݻwc{>8%IaKȑ#vp8`":;;qFeBxDmnn_3\DlY6Ϸj޼yk r pcTK7D(@r6<7oN\uUXUU: ,3SZgAiBu 0|0 2̰, RJC#q~ v|>g477ʂ@qq1~?ֆp8CʔAv L" !`&ڊn{($L W GQM$jiidfҗWTPP,((.+9T!*˲UTTRROD-)%(ۇsb-Lbv~f#EQrPSS| ܡ`fr2<xnwZ5S?aÆѶm۰|r466"#++ F‰'ߏիW;euiw6Y8lv;^{5ա۷o]0i/GRP`>塨WFuu5&O>,[ ;v@__\.q'cX~=6oތ,L6 >mmmرcӃ$ dee'OF~~>VZ+Jd Bx(//B7nV[[bŊ)SD O>-B[[((( 'D"?H$2X4с@Nr !裏>›o 8qrgFX0 9f׈#I ضmV\P(]ƛ7o&R`3}XFVVƍG8 Ѷmhǎ5Eqq1M8Q4i~'<n愡CO0AD"j*رB!??***V\\ldND<֌ ?Qs&q)M$xy]dT8SÐ@`@Ju֙KP__Z Xd Pi^Cv[PHւIFyصkFol6\Ad fD\E~~>/_ "O@UT &El&#L`'+(XjxOgegՍBRaA=WVV G*I nW^qO8{=(++M&fbqpWH)s9ua޼yH'ep7\UMHĤVQQRoո馛Pw Ƹl!Ga 1Ǵ}wXw'x{a1A!w6_y'mp  t6Ś>/.q~L`' {M[HJ6Î}r0_شiSPty@_O/U(--/ ݅QJxfOiK/k׮k?YK9XgaiO_;W^y%Z;P펠C)$55 `G܍ɾ^ »8HMű*1ڒ(&$CgӆO~ ᱫ|mQNN$ |InZ}ݙ!JRfАpӃwx7R>5\cR`駟~zvWW͛b7xe+ӳd[9wjxpW`Nؙ;`g̍A8NJii[w ڵ CO_gઢ31_7}k~_ 7| :n&x<A =X={а>;;QnNq W>8/7Ǹ|<ʡ~ &y{qOs5/^? e%pGi=4qY4# v+`2a}ԏ3e3qs#IlyF[[.$ 3r:p$Ԣ "EJ4Mo(Ji⪫%%x`J34?)iqr2ok}u!뺎xvSzqU>vqѦjOS)߀\*ck̋-QF,;(,g  3`VD=>`r5swg ?:::𷿿A.=Iq7ŽhiiIEBCF}=,ߎ?㡇B,C$1#y,p yP@u:  0owV )MI'Nwc ~ߌ-q: `"ބ `B-a(ZtL;m,G=7[="`ۓxuqlqq`?r5A}= KpCѣ0‹%3մ'!  e nnuw`y& ,{bg 8(%HխqoS< =̓p 5,CF7;y  bA.9G˂,>@*&rTaˉT0;17D`^.+PaHՒ Uá-n)%S-izBuכj0k,t[9 l bT;P!P&![&Ô)Sw^lٲ;vp- S|=LT A8w R5T `hWEXڀ1[@Đ lz0NHHUǡsA~~>R,LcSԋ0t'9#|MGaC=YԏRAB*p ̄<[Jl8ɘ0aalY cu8gL 4I Ǒ,#:c@) "/-} \$Ōq7[7nFRhhXdHjkiY1bÇ;~Q ځpQ'iG }GF_NvqꩧEEEX|9B&x0w>0JuWfZ<4xB"fYc9Ç1j(1*.Cp9s8‚FBҨRJ""/HҨl[RF;`0B]],˂i_R+AmaWz$ѩ|)C/@F1xtH~%nF$/4J5M,ˊ0b JJ$7NC}L@X?cRKa9_BO`uicǎŋ!RLқD$a(rzJ6 d$$^l/CcqD5!_!cY,ePHl$Q刡U WMR-2]l.BQ "+]Q rX'8 4~ߞ "+/TQ!C#bA2>;/#ȡҮ Ib![5Pef(*" @D ㏭0XD DV)S՞l?/]_ %@ap.q6eʒt6M 6 !}}}̝]]]ǏWނm1/*)K뱠 ǸqNN S,AfEшn0sr2rMp 1r51I8Y&*x$+jsT .?: ; ;10r5T<']0W1QfOme 1-u/-zʡrD ]M0 W1ƁC*xKCef̍D /+KgTcen+?pacԗLEt6,0 Y]8=FOG}{Иt"fD,UwcCԇXY;vR:6q\SPI$j+lJa[y؟c @+& %;MwH40 hCmj62{a1!.Xt&t&/O׿O!Kͬa]SJM6nH˲O!$T`)/n@hKWi);3c:,ۓO>իWs( I9bC!j c3f N'n6޵t(N Iw}hiiA,Cmm-~_q(О:/vP^Vƿ/H4gEGg O5`S,M5n0lbȐ!ضm"8ȂFnn.R1¤I'? uttGk{` b`BAǮb„ CaӦMIk&U0l%AƓ&N+=܃6&rT>BL vHf{\>|83E L| G4 '+cI)IZ!Ba$kԿO.9'PW# Mˤ5dk&T`0A# $ښ[u;~}x7ۙnML\p/^ "Hw??~Ӳ,[7nQY9ǎKFgÇӠAhҥlcǎ*i6m׳eYO<:;;y֭|RYYmݺ6mݻ 0 nrQZZb޽O8S n?xʕ+k.Rˁ@N?999 6VDp\J=z4˱e֢7n͛"4l\]]M555(,,Ě5k6sذaȑ#FlܸnF==p:AII < \w}`0ctUcǎEyy9z-iYO6M Xf<v4e(+W/ÑTU1cб[O?d2o"Q+`Zt NaI.}x'|f͚˗opD_utvvf+2.01F9Lb׮]r۶mD"JD\QRR3bp܌VgEhI!Б#GzKKK@U"ݻ={thWUt!CCS ۷o7u]C Ѫjjj#GB1>H$"lbvttN3{ԨQ+eon566FyBommRRR"D1&IrCCfT !UVVzkjjClX ---\WWg[СCCJͼiӦ(3ffKQ3|poEEo__x"&Y54͂tbK?Bi:4cڔ6+Ү(GVQ>3hH)e:UAx<[Bt,ʲ/k)T%nf5pHWUIY5BJYh۝6MuND}6 !6nf.R,k"tm6{Ќ1!DYUM1s3jYֱel6i& xe֗-!!DDODe鱈D@2sv1sHP ,":Μ9;vS&V,T(%"N*"q_=>W_bѦ7ߌ3<Æ èQ!D UUX,/ rss"*;;;a9sm}:;8EuUun )%ȼqѢE;hWZuQaaH$kDŽu]7nq;w.믋X?LۿoDnn.JJJ!D8fغu+R|gg'8wyrѢEr(//ɤH$6 ckļy܌x< ô``SUK.AMM P()=izw^~eW^7Mʃ>n韪Oꫯr5ܹsQXX/}ωx+EEUUUD<G8F4E<fF (سgU__?B(ʲUVx < |[-_<쳪nbYֹN!A̜mٜ~_|rQ2H$" ˲roF---~Mo[wnF/add*"fRfΒR$u$y]seYEomڵk P]ם6.[*"&jYV4'z_^?+ŒnIENDB`mapper-0.8.1.1/doc/tip-of-the-day/000077500000000000000000000000001325266516600164535ustar00rootroot00000000000000mapper-0.8.1.1/doc/tip-of-the-day/tips_de.txt000066400000000000000000000024601325266516600206450ustar00rootroot00000000000000

    Willkommen bei OpenOrienteering Mapper!

    Wenn Sie das Programm zum ersten Mal verwenden, lesen Sie dies bitte zuerst.

    Um das Programm anzupassen, sehen Sie die
    Einstellungen durch. Für ausführliche Anleitungen lesen Sie die Hilfedateien.

    Schnellstart-Tipps:
    - Erstellen oder laden Sie eine Karte mit den Buttons links
    - Halten Sie die mittlere Maustaste gedrückt, um die Karte zu verschieben
    - Zoomen funktioniert mit dem Mausrad, falls vorhanden
    - Lesen Sie die Tipps in der Statuszeile unten, um zu lernen, wie die Bearbeitungs-Tools funktionieren

    Beispiele

    Drücken Sie beim Bearbeiten einer Karte die Tab-Taste, um schnell zwischen dem Bearbeitungstool und den Zeichentools zu wechseln. Dies ist der dritte Tipp des Tages, der jedoch nur ein Platzhalter ist. Wenn Sie einen Tipp wissen, fügen Sie ihn doch hinzu ... mapper-0.8.1.1/doc/tip-of-the-day/tips_en.txt000066400000000000000000000056321325266516600206630ustar00rootroot00000000000000

    Welcome to OpenOrienteering Mapper!

    If this is the first time you use the program, please read this first.

    You might want to check out the settings and adjust the program to you. For detailed advice on using Mapper, read the help files.

    Quick start advice:
    • Create or load a map using the buttons on the left
    • Hold the middle mouse button to drag the map
    • Zoom using the mouse wheel, if available
    • Read the hints in the status bar at the bottom to learn how the tools work

    Examples

    While editing a map, press Tab to switch between the edit tool and the drawing tools quickly. A map with few well chosen features will give a much better map than a map cluttered with many insignificant features.

    Eduard Imhof Features that are not important for a competitor taking part in a sprint orienteering event should not be mapped. Examples of this are waste baskets, fire hydrants, parking meters and individual street lights as they clutter the map with unhelpful detail. In the end, it is the mapmaker's task to produce precise and legible orienteering maps by applying the specifications and generalisation rules, such as selection, simplification and exaggeration. Mapping multilevel structures (bridges, underpasses or underground buildings) is confusing. Generally only the main "running" level should be shown. However, underground passages (e.g. underpasses, lighted tunnels) or overpasses (e.g. bridges), which are important for competitors should be mapped. All line widths and symbol dimensions must be kept strictly to their specified value. Certain minimum dimensions must also be observed. These are based on both printing technology and the need for legibility. Minimum gap between two line symbols of the same colour, in brown or black: 0.15 mm, in blue: 0.25 mm. Minimum gap between line symbols and area symbols of the same colour, in black: 0.15 mm. Shortest dotted line: at least two dots. Shortest dashed line: at least two dashes. Smallest area enclosed by a dotted line: 1.5 mm (diameter) with 5 dots. Smallest area of colour:
    • Blue, green, grey or yellow full colour: 0.5 mm2
    • Black dot screen: 0.5 mm2
    • Blue, brown, green or yellow dot screen: 1.0 mm2
    All features plotting smaller than the minimum dimensions must be either exaggerated or omitted, depending on whether or not they are of significance to the competitor. The sprint map format should not exceed DIN A4. mapper-0.8.1.1/doc/tip-of-the-day/tips_fr.txt000066400000000000000000000064671325266516600206770ustar00rootroot00000000000000

    Bienvenue dans OpenOrienteering Mapper !

    Si c'est la première fois que vous utilisez ce logiciel, prenez le temps de lire ceci.

    Il est possible d'accéder aux préférences et de personnaliser la configuration du logiciel. Pour des conseils détaillés sur l'utilisation de Mapper, consultez le manuel.

    Conseils pour commencer tout de suite:
    - Créer ou charger une carte avec les boutons à gauche
    - Maintenir appuyer la molette de la souris pour déplacer la carte
    - Pour zoomer, utiliser la molette de la souris, si disponible
    - Lire les conseils dans la barre d'état (en bas de l'écran) afin d'apprendre à utiliser les outils.

    Exemples

    Lors de l'édition d'une carte, appuyer sur Tab pour basculer rapidement entre l'outil d'édition et les outils de dessin. Une carte avec peu de symboles bien choisis sera bien meilleure qu'une carte encombrée de symboles inutiles.

    Eduard Imhof Les objets inutiles pour les compétiteurs dans une course d'orientation de sprint ne devraient pas être cartographiés. Par exemple les corbeilles de rue, les bouches d'incendie, les parcmètres, les révèrbères sont des détails inutiles qui encombrent la carte. L'objectif du cartographe est de produire une carte précise et lisible en appliquant les principes spécifiques et généraux suivant : sélection, simplification et exagération. Cartographier des structures multi-niveaux (pont, passages souterrains ou batîments souterrains) est source de confusion. En général, seul le "niveau principal de course" doit apparaître. Cependant les passages souterrains, tunnel éclairés ou les passages aériens comme les ponts qui sont utiles pour les orienteurs doivent être cartographiés. Toutes les largeurs de lignes et les tailles des symboles doivent conserver leur valeur spécifiée. Certaines dimensions minimales doivent être respectées pour apparaître correctement à l'impression et permettre une bonne lisibilité de la carte. Écart minimum entre deux symboles de ligne de la même couleur :
    en brun ou noir : 0,15 mm
    en bleu : 0,25 mm Écart minimum entre deux symboles de surface de la même couleur : en noir : 0,15 mm Pour les lignes en pointillés : au moins deux pointillés doivent apparaître Pour les lignes en tirets : au moins deux tirets doivent apparaître Les surfaces délimitées par des lignes tirets doivent avoir un diamètre au moins égal à 1,5 mm et être composées d'au moins 5 tirets Les surfaces de couleurs doivent avoir un diamètre minimum :
    Bleu, vert, gris ou jaune en plein : 0,5 mm²;
    Pointillés noirs : 0,5 mm²;
    Bleu, marron, vert ou jaune en pointillés : 1,0 mm² Tous les objets plus petits que les dimensions minimums doivent être exagérées (agrandies sur la carte) ou ne pas apparaître, en fonction de leur utilité ou non pour les orienteurs. La taille d'une carte de sprint ne doit pas dépasser le format A4. mapper-0.8.1.1/doc/tip-of-the-day/tips_ru.txt000066400000000000000000000103461325266516600207050ustar00rootroot00000000000000

    Добро пожаловать в OpenOrienteering Mapper!

    Если вы вперые открыли эту программу, прочитайте это сначала.

    Возможно вы захотите ознакомиться с Параметрами и настроить программу под себя. Более подробно об использовании Mapper читайте в Справке.

    Советы по быстрому старту:
    • Создайте или загрузите карту с помощью кнопок слева
    • Удерживайте среднюю кнопку мыши для перемещения карты
    • Изменяйте масштаб с помощью колесика мыши, если возможно
    • Читайте подсказки в нижней части окна, чтобы понять как работает инструмент

    Примеры

    Во время редактирования карты, используйте Tab для быстрого переключения между инструментами редактирования и рисования. A map with few well chosen features will give a much better map than a map cluttered with many insignificant features.

    Eduard Imhof Объекты, которые не важны для спортсмена в спринт ориентировании, не отображаются. Примерами могут служить мусорные корзины, пожарные помпы, парковочные столбы и т.п. Отображение многоуровневых конструкций (мосты, подземные переходы или прочее) сбивает с толку. Как правило только главный "беговой" уровень должен быть отображен. Однако, если такие объекты важны для участников, то они должны быть отображены. Все толщины линий и размеры символов должны строго соответствовать указанным значениям. Минимальные размеры также должны соблюдаться. Они основаны, и на технологии печати и на потребности в удобочитаемости. Промежуток между двумя линиями одного и того же цвета должен быть не менее 0,15 мм (для чёрного, коричневого) и 0,25 мм (для голубого). Минимальные размеры точечных линий: минимум две точки. Минимальные размеры пунктирных линий: минимум два штриха. Минимальный размер площади, отображаемой точками: 1,5 мм диаметром (5 точек). Минимальный размер площадей, отображаемых цветом:
    • голубой, зелёной, серой или жёлтой заливками: 0,5 мм2
    • чёрной точечной сеткой: 0,5 мм2
    • голубой, коричневой, зелёной или жёлтой точечной сеткой: 1,0 мм2
    Все объекты меньших размеров должны быть или утрированны (обобщены) или пропущены (не нанесены на карту), в зависимости от того насколько они важны для ориентирования. Размер карты для спринта не должен превышать формата DIN A4. mapper-0.8.1.1/doc/tip-of-the-day/tips_uk.txt000066400000000000000000000116051325266516600206750ustar00rootroot00000000000000

    Вітаємо в OpenOrienteering Mapper!

    Якщо ви запустили цю програму вперше, вам можуть бути цікавими наші поради.

    Можливо ви захочете переглянути налаштування щоб налаштувати програму на свій смак. Детальні поради щодо використання Mapper можна знайти у вбудованій довідці.

    Швидкий старт:
    • Створіть або відкрийте карту за допомогою кнопок ліворуч
    • Утримуйте середню кнопку миші щоб переміщувати карту
    • Віддаляйте або наближайте карту за допомогою коліщатка на миші
    • Читайте підказки у рядку статусу в нижній частині вікна щоб дізнатися як можуть працювати інструменти

    Зразки

    Під час креслення карти, використовуйте Tab щоб швидко переключатися між інструментами креслення і редагування. A map with few well chosen features will give a much better map than a map cluttered with many insignificant features.

    Eduard Imhof Не варто зображати на спринтерській карті об'єкти, що є неважливими для учасника змагань. Прикладами таких об'єктів є сміттєві урни, пожежні гідранти, парковочні автомати та ліхтарні стовпи - це лише навантажує карту непотрібними деталями. Кінцевою задачею картографа є випуск точної чіткої карти, застосовуючи при цьому специфікацію і прийоми генералізації, такі як вибіркове відображення, спрощення або перебільшення. Зображення багаторівневих конструкцій (мостів, підземних переходів або інших споруд) спантеличує. У загальному випадку лише головний "біговий" рівень має бути зображений. Однак, проходи (підземні переходи, освічені тунелі, мости), що важливі для учасників змагань, мають бути показані. Товщина ліній та розміри знаків повинні точно відповідати визначеним величинам. Також мають бути витримані певні мінімальні розміри. Ці вимоги забезпечують чіткість карти незалежно від технології друку. Мінімальна відстань між двома лінійними знаками однакового кольору - коричневий або чорний: 0.15 мм, синій: 0.25 мм. Мінімальна відстань між лінією та точковим об'єктом чорного кольору - 0.15 мм. Найкоротша лінія з точок: мінімум дві точки. Найкоротша лінія зі штрихами: мінімум два штрихи. Мінімальна площа обнесена контуром: діаметр 1.5 мм і 5 точок. Мінімальні площі для об'єктів певних кольорів:
    • Синій, зелений, сірий або повний жовтий: 0.5 мм2
    • Заповнення чорними точками: 0.5 мм2
    • Заповнення синіми, коричневими, зеленими чи жовтими точками: 1.0 мм2
    Всі елементи креслення що менші за мінімальні дозволені, мають бути збільшені, або упущені, в залежності від того наскільки вони важливі для учасника змагань. Формат карти для спринту не повинен перевищувати DIN A4. mapper-0.8.1.1/examples/000077500000000000000000000000001325266516600147755ustar00rootroot00000000000000mapper-0.8.1.1/examples/CMakeLists.txt000066400000000000000000000025311325266516600175360ustar00rootroot00000000000000# # Copyright 2013-2017 Kai Pastor # # This file is part of OpenOrienteering. # # OpenOrienteering is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # OpenOrienteering is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with OpenOrienteering. If not, see . set(Mapper_EXAMPLES overprinting.omap forest\ sample.omap complete\ map.omap ) if(ANDROID) # \todo Support overprinting simulation in the mobile app. list(REMOVE_ITEM Mapper_EXAMPLES overprinting.omap) endif() install( FILES ${Mapper_EXAMPLES} DESTINATION "${MAPPER_DATA_DESTINATION}/examples" ) foreach(_example ${Mapper_EXAMPLES} examples.qrc) configure_file(${_example} ${_example} COPYONLY) endforeach() if(Mapper_DEVELOPMENT_BUILD) configure_file(overprinting.omap autosave-test.omap COPYONLY) configure_file(complete\ map.omap autosave-test.omap.autosave COPYONLY) endif() mapper-0.8.1.1/examples/README.md000066400000000000000000000005321325266516600162540ustar00rootroot00000000000000The examples to be edited are stored in the src subdirectory in the verbose .xmap format. The examples to be distributed are stored in the more compact .omap format in this subdirectory. The test subdirectory builds (under CMake) an executable (test) `symbol_set_t` which updates compressed files from a list of known files in the src directory. mapper-0.8.1.1/examples/autosave-example.qrc000066400000000000000000000003161325266516600207640ustar00rootroot00000000000000 complete map.omap overprinting.omap mapper-0.8.1.1/examples/complete map.omap000066400000000000000000045637711325266516600202500ustar00rootroot00000000000000 +proj=utm +datum=WGS84 +zone=3232 N+proj=latlong +datum=WGS84 PURPLE BLACK RED BLUE BROWN GREEN YELLOW A line joining points of equal height. The standard vertical interval between contours is 2 or 2.5 m. To emphasize the 3-dimensional effect of the contour line image, contour lines shall be represented as continuous lines through all symbols, also building (526.1) and canopy (526.2). However, contour lines shall be cut out for better legibility, if they touch the following symbols: small earth wall (108.1), small knoll (112), small elongated knoll (113), small depression (115), pit or hole (116), prominent landform feature (118), step or edge of paved area (529.1). The relative height difference between neighbouring features must be represented on the map as accurately as possible. Absolute height accuracy is of less importance. It is permissible to alter the height of a contour slightly if this will improve the representation of a feature. This deviation should not exceed 25% of the contour interval and attention must be paid to neighbouring features. The smallest bend in a contour is 0.4 mm from centre to centre of the lines. Every fifth contour shall be drawn with a thicker line. This is an aid to the quick assessment of height difference and the overall shape of the terrain surface. Where an index contour coincides with an area of much detail, it may be shown with symbol contour (101). An intermediate contour line. Form lines are used where more information can be given about the shape of the ground. They are used only where representation is not possible with ordinary contours. Only one form line may be used between neighbouring contours. Slope lines should be drawn on the lower side of a contour line where it is necessary to clarify the direction of slope, e.g. along the line of a re-entrant or in a depression.0 0;0 -750; Contour values may be included to aid assessment of large height differences. They are inserted in the index contours in positions where other detail is not obscured. The figures should be orientated so that the top of the figure is on the higher side of the contour. A steep earth bank is an abrupt change in ground level which can be clearly distinguished from its surroundings, e.g. gravel or sand pits, roads and railway cuttings or embankments. The tags should show the full extent of the slope, but may be omitted if two banks are close together. Impassable banks shall be drawn with the symbol impassable cliff (201). The line width of very high earth banks may be 0.37 mm.0 750;0 0; A steep earth bank is an abrupt change in ground level which can be clearly distinguished from its surroundings, e.g. gravel or sand pits, roads and railway cuttings or embankments. The tags should show the full extent of the slope, but may be omitted if two banks are close together. Impassable banks shall be drawn with the symbol impassable cliff (201). The line width of very high earth banks may be 0.37 mm.0 750;0 100; Use this symbol to display the full extent of wide earth banks. A small distinct earth wall, usually man made. The minimum height is 0.5 m. Larger earth walls should be represented with the symbols contour (101), form line (103) or earth bank (106). An erosion gully or trench which is too small to be represented with the symbol earth bank (106), contour (101), index contour (102) or form line (103) is represented by a single line. The line width reflects the size of the gully. The end of the line is pointed. Minimum depth is 1 m. Minimum length is 3 mm on the map. A small erosion gully or trench. Minimum depth is 0.5 m. A small obvious mound or rocky knoll which cannot be drawn to scale with a contour (101), index contour (102) or form line (103). The height of the knoll should be a minimum of 1 m from the surrounding ground. A small obvious elongated knoll which cannot be drawn to scale with a contour (101), index contour (102) or form line (103). The maximum length should be 6 m and the maximum width 2 m. The height of the knoll should be a minimum of 1 m from the surrounding ground. Knolls larger than this shall be shown by contours. The symbol may not be drawn in free form or such that two elongated knoll symbols touch or overlap.0 -600 1;166 -600;300 -331;300 0 1;300 331;166 600;0 600 1;-166 600;-300 331;-300 0 1;-300 -331;-166 -600;0 -600; A small shallow natural depression or hollow which cannot be represented by the symbol contour (101) or form line (103) is represented by a semicircle. The minimum diameter should be 2 m. The minimum depth from the surrounding ground should be 1 m. The symbol is orientated to north.475 -135 1;475 127;262 340;0 340 1;-262 340;-475 127;-475 -135; A pit or hole with distinct steep sides which cannot be represented to scale with the symbol earth bank (106). The minimum diameter shall be 2 m. The minimum depth from the surrounding ground shall be 1 m. The symbol is orientated to north.-546 -409;-274 -409;0 217;274 -409;546 -409;0 841; An area of pits or knolls, which is too complex to be represented in detail. The density of randomly placed dots may vary according to the detail on the ground. An area of pits or knolls, which is too complex to be represented in detail. The density of randomly placed dots may vary according to the detail on the ground. A small landform feature which is significant or prominent. The definition of the symbol shall always be given in the map legend. The symbol is orientated to north.-600 -600;600 600;-600 600;600 -600; An impassable cliff, quarry or earth bank [see symbol earth bank (106)]. Tags are drawn downwards, showing its full extent from the top line to the foot. For vertical rock faces the tags may be omitted if space is short, e.g. narrow passages between cliffs (the passage should be drawn with a width of at least 0.3 mm). The tags may extend over an area symbol representing detail immediately below the rock face. When a rock face drops straight into water making it impossible to pass under the cliff along the water's edge, the bank line is omitted or the tags shall clearly extend over the bank line. Minimum height is 2 meters. <span style="color:magenta">It is forbidden to cross an impassable cliff! Competitors violating this rule will be disqualified.</span>0 100;0 750; An impassable cliff, quarry or earth bank [see symbol earth bank (106)]. Tags are drawn downwards, showing its full extent from the top line to the foot. For vertical rock faces the tags may be omitted if space is short, e.g. narrow passages between cliffs (the passage should be drawn with a width of at least 0.3 mm). The tags may extend over an area symbol representing detail immediately below the rock face. When a rock face drops straight into water making it impossible to pass under the cliff along the water's edge, the bank line is omitted or the tags shall clearly extend over the bank line. Minimum height is 2 meters. <span style="color:magenta">It is forbidden to cross an impassable cliff! Competitors violating this rule will be disqualified.</span>-450 0;450 0;270 0;270 750;-270 0;-270 750; For vertical rock faces the tags may be omitted if space is short, e.g. narrow passages between cliffs (the passage should be drawn with a width of at least 0.3 mm). For vertical rock faces the tags may be omitted if space is short, e.g. narrow passages between cliffs (the passage should be drawn with a width of at least 0.3 mm).-450 0;450 0; Use this symbol to display the full extent of a wide cliff. A gigantic boulder, rock pillar or massive cliff shall be represented in plan shape without tags. A small vertical rock face may be shown without tags. If the direction of fall of the rock face is not apparent from the contours or to improve legibility, short tags should be drawn in the direction of the fall. Minimum height is 1 m. For passable rock faces shown without tags the end of the line may be rounded to improve legibility.0 50;0 750; A small vertical rock face may be shown without tags. If the direction of fall of the rock face is not apparent from the contours or to improve legibility, short tags should be drawn in the direction of the fall. Minimum height is 1 m. For passable rock faces shown without tags the end of the line may be rounded to improve legibility.-450 0;450 0;270 0;270 750;-270 0;-270 750; Should be used if the direction of fall of the rock face is apparent from the contours and the legibility is good. Should be used if the direction of fall of the rock face is apparent from the contours and the legibility is good.-450 0;450 0; Should be used if the direction of fall of the rock face is apparent from the contours and the legibility is good. For passable rock faces shown without tags the end of the line may be rounded to improve legibility. Should be used if the direction of fall of the rock face is apparent from the contours and the legibility is good. For passable rock faces shown without tags the end of the line may be rounded to improve legibility.-300 0;300 0; A rocky pit, hole or mineshaft which may constitute a danger to the competitor. The symbol is orientated to north.-546 -409;-274 -409;0 217;274 -409;546 -409;0 841; A cave is represented by the same symbol as a rocky pit. In this case the symbol shall be orientated to point up the slope as indicated opposite. This symbol should generally not be used in urban areas. The centre of gravity of the symbol marks the opening. <span style="color:magenta">Controls may not be placed inside caves!</span>-546 -409;-274 -409;0 217;274 -409;546 -409;0 841; A small distinct boulder. The minimum height is 1 m. Every boulder marked on the map shall be immediately identifiable on the ground. A particularly large and distinct boulder. Gigantic boulders shall be represented in plan shape with the symbol gigantic boulder or rock pillar (202). An area which is covered with so many blocks of stone that they cannot be marked individually is represented with randomly orientated solid triangles. The runnability is reduced and is indicated by the density of the triangles. A minimum of two triangles shall be used. The triangles can be enlarged by up to 20 %.150 452;150 -508;-299 55; An area which is covered with so many blocks of stone that they cannot be marked individually is represented with randomly orientated solid triangles. The runnability is reduced and is indicated by the density of the triangles. A minimum of two triangles shall be used. The triangles can be enlarged by up to 20 %.180 542;180 -610;-359 66; An area of stony or rocky ground which reduces runnability. The dots shall be randomly distributed with density according to the amount of rock. A minimum of three dots shall be used. An area of stony or rocky ground which reduces runnability. The dots shall be randomly distributed with density according to the amount of rock. A minimum of three dots shall be used. An area of soft sandy ground or gravel with no vegetation which reduces runnability. Where an area of sandy ground is open and has good runnability, it is represented with symbol open land (401), open land with scattered trees (402) or paved area (529). An area of runnable rock without earth or vegetation. An area of rock covered with grass, moss or other low vegetation shall be represented according to its openness and runnability (401/402/403/404). A water-filled pit or an area of water which is too small to be shown to scale. The symbol is orientated to north.-546 -409;-274 -409;0 217;274 -409;546 -409;0 841; An area of deep water such as a lake, pond, river or fountain which may constitute a danger to the competitor or has forbidden access. The dark blue colour and the bordering black line indicates that the feature cannot or shall not be crossed. The minimum dimension is 1 mm². <span style="color:magenta">It is forbidden to cross an impassable body of water! Competitors violating this rule will be disqualified.</span> An area of deep water such as a lake, pond, river or fountain which may constitute a danger to the competitor or has forbidden access. The dark blue colour and the bordering black line indicates that the feature cannot or shall not be crossed. The minimum dimension is 1 mm². <span style="color:magenta">It is forbidden to cross an impassable body of water! Competitors violating this rule will be disqualified.</span> The bordering black line indicates that the feature cannot or shall not be crossed. An area of deep water such as a lake, pond, river or fountain which may constitute a danger to the competitor or has forbidden access. The dark blue colour and the bordering black line indicates that the feature cannot or shall not be crossed. The minimum dimension is 1 mm². <span style="color:magenta">It is forbidden to cross an impassable body of water! Competitors violating this rule will be disqualified.</span> An area of deep water such as a lake, pond, river or fountain which may constitute a danger to the competitor or has forbidden access. The dark blue colour and the bordering black line indicates that the feature cannot or shall not be crossed. The minimum dimension is 1 mm². <span style="color:magenta">It is forbidden to cross an impassable body of water! Competitors violating this rule will be disqualified.</span> An area of shallow water such as a pond, river or fountain that can be crossed. The body of water shall be less than 0.5 m deep and runnable. If the body of water is not runnable it shall be represented with the symbol impassable body of water (304.1). If no other line symbol touches the border of the passable body of water, the border shall be represented with a blue line. An area of shallow water such as a pond, river or fountain that can be crossed. The body of water shall be less than 0.5 m deep and runnable. If the body of water is not runnable it shall be represented with the symbol (304.1). If no other line symbol touches the border of the passable body of water, the border shall be represented with a blue line. An area of shallow water such as a pond, river or fountain that can be crossed. The body of water shall be less than 0.5 m deep and runnable. If the body of water is not runnable it shall be represented with the symbol impassable body of water (304.1). If no other line symbol touches the border of the passable body of water, the border shall be represented with a blue line. A crossable watercourse less than 2 m wide. A natural or man-made minor water channel which may contain water only intermittently. A marsh or trickle of water which is too narrow to be shown with symbol marsh (310). A marsh which is impassable or which may constitute a danger to the competitor. The feature cannot or shall not be crossed. <span style="color:magenta">It is forbidden to cross an impassable marsh! Competitors violating this rule will be disqualified.</span> This symbol should not be used on its own. This symbol should not be used on its own. A crossable marsh, usually with a distinct edge. The symbol shall be combined with vegetation symbols to show runnability and openness. Minimum size: not less than 2 lines, 5 mm long. -250 150;250 150;-250 -150;250 -150; An indistinct or seasonal marsh or area of gradual transition from marsh to firm ground, which is crossable. The edge is generally indistinct and the vegetation similar to that of the surrounding ground. The symbol should be combined with vegetation symbols to show runnability and openness. Minimum size: 4 dashes.-450 0;450 0;-450 0;450 0; 125 0;1025 0;-125 0;-1025 0;-450 -300;450 -300;-450 300;450 300; Small well or fountain, which is at least 1 m high or at least 1 m in diameter. The source of a stream with a distinct outflow. This symbol should generally not be used in urban areas. The symbol is orientated to open downstream.475 -135 1;475 127;262 340;0 340 1;-262 340;-475 127;-475 -135; A small water feature which is significant or prominent. The definition of the symbol shall always be given in the map legend. The symbol is orientated to north.-600 -600;600 600;-600 600;600 -600; An area of cultivated land, lawn, field, meadow, grassland, etc. without trees, offering very good runnability. An area of meadows with scattered trees or bushes, with grass or similar ground cover offering very good runnability. Areas smaller than 10 mm² at the maps scale are shown as open land (401). Symbols prominent large tree (418) and prominent bush or small tree (419) may be added. An area of heath or moorland, a felled area, a newly planted area (trees lower than ca. 1 m) or other generally open land with rough ground vegetation, i.e. heather or tall grass. This symbol may be combined with symbols undergrowth: slow running (407) and undergrowth: difficult to run (409) to show reduced runnability. An area of rough open land with scattered trees or bushes. Areas smaller than 16 mm² in the map scale are either mapped as rough open land (403) or forest: easy running (405). Symbols prominent large tree (418) and prominent bush or small tree (419) may be added. An area of typical open runnable forest for the particular type of terrain. If no part of the forest is runnable then no white should appear on the map. An area with dense trees (low visibility) which reduces running to ca. 60-80% of normal speed. Minimum width 0.25 mm. An area of dense undergrowth but otherwise good visibility (brambles, heather, low bushes, cut branches, etc.) which reduces running to ca. 60-80% of normal speed. This symbol shall not be combined with the symbol forest: slow running (406) or forest: difficult to run (408). An area with dense trees or thicket (low visibility) which reduces running to ca. 20-60% of normal speed. An area of dense undergrowth but otherwise good visibility (brambles, heather, low bushes, and including cut branches) which reduces running to ca. 20-60% of normal speed. This symbol may not be combined with 406 or 408. An area of dense vegetation (trees or undergrowth) which is barely passable. Running reduced 1-20% of normal speed. Minimum width: 0.25 mm. When an area of forest provides good running in one direction but less good in others, white stripes are left in the screen symbol (406) to show the direction with good runnability. When an area of forest provides good running in one direction but less good in others, white stripes are left in the screen symbol (408) to show the direction with good runnability. When an area of forest provides good running in one direction but less good in others, white stripes are left in the screen symbol (410) to show the direction with good runnability. Land planted with fruit trees or bushes. The dot lines may be orientated to represent the direction of planting. Land planted with fruit trees or bushes, with a distinct direction of planting which reduces the runnability. The green lines shall be orientated to show the direction of planting.0 -650;0 650;0 -650;0 650; The boundary of symbol cultivated land (seasonally out of bounds) (415) when not shown with other symbols (fence, wall, path, etc.) is represented with a black line. A permanent boundary between different types of cultivated land is also represented with this symbol. Cultivated land which is seasonally out-of-bounds due to growing crops may be shown with a black dot screen. A distinct forest edge or very distinct vegetation boundary within the forest. For indistinct boundaries, the area edges are shown only by the change in colour and/or dot screen. A prominent single tree. A bush or a tree with a trunk less than 0.5 m diameter. A vegetation feature which is significant or prominent. The definition of the symbol shall always be given in the map legend. The symbol is orientated to north.-600 -600;600 600;-600 600;600 -600; An area of dense vegetation (trees or undergrowth) which is impassable or which shall not be crossed, due to forbidden access or because it may constitute a danger to the competitor. Minimum width: 0.4 mm. <span style="color:magenta">It is forbidden to cross impassable vegetation! Competitors violating this rule will be disqualified.</span> An unpaved footpath or rough vehicle track is a way for passing mainly by foot, without a smooth, hard surface. The density of the brown fill-in shall be the same as the density chosen for the symbol (529). To improve the legibility of this symbol in non-urban parts of the map, the line width shall, in the non-urban parts of the map, be increased from 0.07 mm to 0.14 mm, and the brown fill-in shall, in the non-urban parts of the map, be drawn darker, so that if (x)% brown is used in urban parts of the map, (x+20)% brown shall be used in the non-urban parts of the map. Colour: black, brown 0%(white), 10%, 20% or 30% (urban) / 20%, 30%, 40%, 50% (min.60lines/cm) (non-urban); the colour and the line width shall be the same as for the symbols paved area (529) and step or edge of paved areas (529.1). An unpaved footpath or rough vehicle track is a way for passing mainly by foot, without a smooth, hard surface. The density of the brown fill-in shall be the same as the density chosen for the symbol (529). To improve the legibility of this symbol in non-urban parts of the map, the line width shall, in the non-urban parts of the map, be increased from 0.07 mm to 0.14 mm, and the brown fill-in shall, in the non-urban parts of the map, be drawn darker, so that if (x)% brown is used in urban parts of the map, (x+20)% brown shall be used in the non-urban parts of the map. Colour: black, brown 0%(white), 10%, 20% or 30% (urban) / 20%, 30%, 40%, 50% (min.60lines/cm) (non-urban); the colour and the line width shall be the same as for the symbols paved area (529) and step or edge of paved areas (529.1). An unpaved footpath or rough vehicle track is a way for passing mainly by foot, without a smooth, hard surface. The density of the brown fill-in shall be the same as the density chosen for the symbol (529). To improve the legibility of this symbol in non-urban parts of the map, the line width shall, in the non-urban parts of the map, be increased from 0.07 mm to 0.14 mm, and the brown fill-in shall, in the non-urban parts of the map, be drawn darker, so that if (x)% brown is used in urban parts of the map, (x+20)% brown shall be used in the non-urban parts of the map. Colour: black, brown 0%(white), 10%, 20% or 30% (urban) / 20%, 30%, 40%, 50% (min.60lines/cm) (non-urban); the colour and the line width shall be the same as for the symbols paved area (529) and step or edge of paved areas (529.1). An unpaved footpath or rough vehicle track is a way for passing mainly by foot, without a smooth, hard surface. The density of the brown fill-in shall be the same as the density chosen for the symbol (529). To improve the legibility of this symbol in non-urban parts of the map, the line width shall, in the non-urban parts of the map, be increased from 0.07 mm to 0.14 mm, and the brown fill-in shall, in the non-urban parts of the map, be drawn darker, so that if (x)% brown is used in urban parts of the map, (x+20)% brown shall be used in the non-urban parts of the map. Colour: black, brown 0%(white), 10%, 20% or 30% (urban) / 20%, 30%, 40%, 50% (min.60lines/cm) (non-urban); the colour and the line width shall be the same as for the symbols paved area (529) and step or edge of paved areas (529.1). A small unpaved footpath or track. Not to be used in urban areas. A less distinct path or forestry extraction track. Not to be used in urban areas. A distinct ride is a linear break in the forest (usually in a plantation), which does not have a distinct path along it. Where there is a path along a ride, the symbol small unpaved footpath or track (506.1) shall be used. Not to be used in urban areas. A bridge is a structure spanning and permitting passage over a river, chasm, road or the like.125 200;125 670;125 -200;125 -670;-125 -200;-125 -670;-125 200;-125 670; A bridge is a structure spanning and permitting passage over a river, chasm, road or the like.125 0;125 -345;-125 0;-125 -345; A railway is a permanent track laid with rails on which locomotives, carriages or wagons can travel. If it is forbidden to cross or run along the railroad, the forbidden area around the railway shall be represented with symbol area with forbidden access (528.1). A tramway is a public vehicle running regularly along certain streets, usually on rails. The track can be easily crossed by the competitor. Tramways are generally not represented. However, if they serve navigation or orientation, they can be represented. Power line, cableway or skilift. The bars indicate the exact location of the pylons. <b>Note: When drawing this symbol, press space to toggle placing the pylon symbols at new nodes.</b>0 -300;0 300; Major power lines should be drawn with a double line. The gap between the lines may indicate the extent of the powerline. Very large carrying masts shall be represented in plan shape or with the symbol high tower (535). In this case, the cable lines can be left out (the map shows only the pylons). <b>Note: When drawing this symbol, press space to toggle placing the pylon symbols at new nodes.</b>0 -1120;0 1120; An underpass or a tunnel is a passage running underneath the ground, especially a passage for pedestrians or vehicles, crossing under for instance a railroad or a road. <span style="color: magenta">If underpasses or tunnels etc. are to be used in a competition, they shall be emphasized with the symbol crossing point (708) or crossing section (708.1)!</span>-125 125;125 125;125 -125;-125 -125; A stone wall or stone faced bank. This symbol shall be used only in non-urban areas. If such a wall is higher than 2 m, it shall be represented with the symbol impassable wall (521.1). A passable wall or retaining wall is a construction made of stone, brick, concrete etc., which can be passed. This symbol is suitable for urban areas. If such a wall is higher than 2 m, it shall be represented with the symbol impassable wall (521.1). Wide walls shall be drawn in plan shape. A passable wall or retaining wall is a construction made of stone, brick, concrete etc., which can be passed. This symbol is suitable for urban areas. If such a wall is higher than 2 m, it shall be represented with the symbol (521.1). Wide walls shall be drawn in plan shape. An impassable wall or retaining wall is a wall, which fulfil the function of an enclosure or solid barrier. It shall not be crossed, due to forbidden access or because it may constitute a danger to the competitor due to its height. Very wide impassable walls shall be drawn in plan shape and represented with the symbol building (526.1). <span style="color:magenta">It is forbidden to cross an impassable wall! Competitors violating this rule will be disqualified.</span> An impassable wall or retaining wall is a wall, which fulfil the function of an enclosure or solid barrier. It shall not be crossed, due to forbidden access or because it may constitute a danger to the competitor due to its height. Very wide impassable walls shall be drawn in plan shape and represented with the symbol building (526.1). <span style="color:magenta">It is forbidden to cross an impassable wall! Competitors violating this rule will be disqualified.</span> A passable fence is a barrier enclosing or bordering a field, yard, etc., usually made of posts and wire or wood. It is used to prevent entrance or to confine or mark a boundary. A railing is a fencelike barrier composed of one or more horizontal rails supported by widely spaced upright poles, usually it can be slipped through. If a fence or railing is higher than 2 m or very difficult to cross, it shall be represented with the symbol impassable fence or railing (524).0 0;375 650; A passable fence is a barrier enclosing or bordering a field, yard, etc., usually made of posts and wire or wood. It is used to prevent entrance or to confine or mark a boundary. A railing is a fencelike barrier composed of one or more horizontal rails supported by widely spaced upright poles, usually it can be slipped through. If a fence or railing is higher than 2 m or very difficult to cross, it shall be represented with the symbol impassable fence or railing (524).0 0;375 650; An impassable fence or railing, which shall not be crossed, due to forbidden access or because it may constitute a danger to the competitor because of its height. <span style="color:magenta">It is forbidden to cross an impassable fence or railing! Competitors violating this rule will be disqualified.</span>450 0;825 650;-450 0;-75 650; A crossing point is a gap or an opening in a fence, railing or wall, which can easily be crossed by a competitor. Small gaps or openings which can not easily be crossed by competitors, shall not be represented on the map and shall be closed during the competition.450 -700;450 700;-450 -700;-450 700; A building is a relatively permanent construction having a roof. Buildings within symbol area with forbidden access (527.1) may just be represented in a simplified manner.Areas totally contained within a building shall be mapped as being a part of the building. The minimum gap between buildings and between buildings and other impassable features shall be 0.40 mm. The black screen percentage should be chosen according to the terrain. A dark screen gives a better contrast to passable areas, such as streets, stairways and canopies, while a light screen makes contours and course overprint more clearly visible (which can be important in very densely built up urban terrain and in steep urban terrain). The black screen shall be the same for the whole map. <span style="color:magenta">It is forbidden to pass through or over a building! Competitors violating this rule will be disqualified.</span> Do not use this symbol on its own! Do not use this symbol on its own! A building is a relatively permanent construction having a roof. Buildings within symbol area with forbidden access (527.1) may just be represented in a simplified manner.Areas totally contained within a building shall be mapped as being a part of the building. The minimum gap between buildings and between buildings and other impassable features shall be 0.40 mm. The black screen percentage should be chosen according to the terrain. A dark screen gives a better contrast to passable areas, such as streets, stairways and canopies, while a light screen makes contours and course overprint more clearly visible (which can be important in very densely built up urban terrain and in steep urban terrain). The black screen shall be the same for the whole map. <span style="color:magenta">It is forbidden to pass through or over a building! Competitors violating this rule will be disqualified.</span>180 -180;180 180;-180 180;-180 -180;180 -180;180 180;-180 180;-180 -180;180 -180 18; A canopy is a building construction (with a roof), normally supported by pillars, poles or walls, such as passages, gangways, courts, bus stops, gas stations or garages. Small passable parts of buildings which can not easily be crossed by competitors, shall not be represented on the map and shall be closed during the competition. Do not use this symbol on its own! Do not use this symbol on its own! A pillar is an upright shaft or structure of stone, brick or other material, relatively slender in proportion to its height and any shape in section, used as a building support. Pillars smaller than 2 m × 2 m are generally not represented. Columns of pillars and pillars along buildings are not represented. However, if they are important for navigation and orientation, they can be represented.-250 -250;250 -250;250 250;-250 250;-250 -250 2; An area with forbidden access such as a private area, a flower bed, a railway area etc. No feature shall be represented in this area, except very prominent features such as railways, large buildings, or very large trees. Road entrances shall be represented clearly. Areas with forbidden access totally contained within buildings shall be mapped as being a part of the building. <span style="color:magenta">It is forbidden to cross an area with forbidden access! Competitors violating this rule will be disqualified.</span> A paved area is an area with a firm level surfaces such as asphalt, hard gravel, tiles, concrete or the like. It shall be bordered (or framed) by the symbol step or edge of paved area (529.1). Distinct differences within the paved area can be represented with the symbol step or edge of paved area (529.1), if they serve navigation. Where a paved road, footpath or track goes through a non-urban area, the brown fill-in shall be drawn darker, so that if (x)% brown is used in urban areas, (x+20)% brown shall be used in the non-urban areas, and the line width of the black outline shall be increased from 0.07 to 0.14 mm. The black border line can be omitted where it is logical (e.g. indistinct/gradual gravel-to-grass transitions). Colour: black, brown 0%(white), 10%, 20% or 30% (urban) / 20%, 30%, 40%, 50% (min.60lines/cm) (non-urban); the colour and the line width shall be the same as for the symbol unpaved footpath or track (506.1). A paved area is an area with a firm level surfaces such as asphalt, hard gravel, tiles, concrete or the like. It shall be bordered (or framed) by the symbol step or edge of paved area (529.1). Distinct differences within the paved area can be represented with the symbol step or edge of paved area (529.1), if they serve navigation. Where a paved road, footpath or track goes through a non-urban area, the brown fill-in shall be drawn darker, so that if (x)% brown is used in urban areas, (x+20)% brown shall be used in the non-urban areas, and the line width of the black outline shall be increased from 0.07 to 0.14 mm. The black border line can be omitted where it is logical (e.g. indistinct/gradual gravel-to-grass transitions). Colour: brown 0 (white), 10%, 20% or 30% (urban) / 20%, 30%, 40%, 50% (non-urban) (min. 60 lines/cm), black; the colour and the line width shall be the same as for symbol unpaved footpath or track (506.1). A paved area is an area with a firm level surfaces such as asphalt, hard gravel, tiles, concrete or the like. It shall be bordered (or framed) by the symbol step or edge of paved area (529.1). Distinct differences within the paved area can be represented with the symbol step or edge of paved area (529.1), if they serve navigation. Where a paved road, footpath or track goes through a non-urban area, the brown fill-in shall be drawn darker, so that if (x)% brown is used in urban areas, (x+20)% brown shall be used in the non-urban areas, and the line width of the black outline shall be increased from 0.07 to 0.14 mm. The black border line can be omitted where it is logical (e.g. indistinct/gradual gravel-to-grass transitions). Colour: brown 0 (white), 10%, 20% or 30% (urban) / 20%, 30%, 40%, 50% (non-urban) (min. 60 lines/cm), black; the colour and the line width shall be the same as for symbol unpaved footpath or track (506.1). A paved area is an area with a firm level surfaces such as asphalt, hard gravel, tiles, concrete or the like. It shall be bordered (or framed) by the symbol step or edge of paved area (529.1). Distinct differences within the paved area can be represented with the symbol step or edge of paved area (529.1), if they serve navigation. Where a paved road, footpath or track goes through a non-urban area, the brown fill-in shall be drawn darker, so that if (x)% brown is used in urban areas, (x+20)% brown shall be used in the non-urban areas, and the line width of the black outline shall be increased from 0.07 to 0.14 mm. The black border line can be omitted where it is logical (e.g. indistinct/gradual gravel-to-grass transitions). Colour: brown 0 (white), 10%, 20% or 30% (urban) / 20%, 30%, 40%, 50% (non-urban) (min. 60 lines/cm), black; the colour and the line width shall be the same as for symbol unpaved footpath or track (506.1). A paved area is an area with a firm level surfaces such as asphalt, hard gravel, tiles, concrete or the like. It shall be bordered (or framed) by the symbol step or edge of paved area (529.1). Distinct differences within the paved area can be represented with the symbol step or edge of paved area (529.1), if they serve navigation. Where a paved road, footpath or track goes through a non-urban area, the brown fill-in shall be drawn darker, so that if (x)% brown is used in urban areas, (x+20)% brown shall be used in the non-urban areas, and the line width of the black outline shall be increased from 0.07 to 0.14 mm. The black border line can be omitted where it is logical (e.g. indistinct/gradual gravel-to-grass transitions). Colour: brown 0 (white), 10%, 20% or 30% (urban) / 20%, 30%, 40%, 50% (non-urban) (min. 60 lines/cm), black; the colour and the line width shall be the same as for symbol unpaved footpath or track (506.1). A paved area is an area with a firm level surfaces such as asphalt, hard gravel, tiles, concrete or the like. It shall be bordered (or framed) by the symbol step or edge of paved area (529.1). Distinct differences within the paved area can be represented with the symbol step or edge of paved area (529.1), if they serve navigation. Where a paved road, footpath or track goes through a non-urban area, the brown fill-in shall be drawn darker, so that if (x)% brown is used in urban areas, (x+20)% brown shall be used in the non-urban areas, and the line width of the black outline shall be increased from 0.07 to 0.14 mm. The black border line can be omitted where it is logical (e.g. indistinct/gradual gravel-to-grass transitions). Colour: brown 0 (white), 10%, 20% or 30% (urban) / 20%, 30%, 40%, 50% (non-urban) (min. 60 lines/cm), black; the colour and the line width shall be the same as for symbol unpaved footpath or track (506.1). A paved area is an area with a firm level surfaces such as asphalt, hard gravel, tiles, concrete or the like. It shall be bordered (or framed) by the symbol step or edge of paved area (529.1). Distinct differences within the paved area can be represented with the symbol step or edge of paved area (529.1), if they serve navigation. Where a paved road, footpath or track goes through a non-urban area, the brown fill-in shall be drawn darker, so that if (x)% brown is used in urban areas, (x+20)% brown shall be used in the non-urban areas, and the line width of the black outline shall be increased from 0.07 to 0.14 mm. The black border line can be omitted where it is logical (e.g. indistinct/gradual gravel-to-grass transitions). Colour: brown 0 (white), 10%, 20% or 30% (urban) / 20%, 30%, 40%, 50% (non-urban) (min. 60 lines/cm), black; the colour and the line width shall be the same as for symbol unpaved footpath or track (506.1). A paved area is an area with a firm level surfaces such as asphalt, hard gravel, tiles, concrete or the like. It shall be bordered (or framed) by the symbol step or edge of paved area (529.1). Distinct differences within the paved area can be represented with the symbol step or edge of paved area (529.1), if they serve navigation. Where a paved road, footpath or track goes through a non-urban area, the brown fill-in shall be drawn darker, so that if (x)% brown is used in urban areas, (x+20)% brown shall be used in the non-urban areas, and the line width of the black outline shall be increased from 0.07 to 0.14 mm. The black border line can be omitted where it is logical (e.g. indistinct/gradual gravel-to-grass transitions). Colour: brown 0 (white), 10%, 20% or 30% (urban) / 20%, 30%, 40%, 50% (non-urban) (min. 60 lines/cm), black; the colour and the line width shall be the same as for symbol unpaved footpath or track (506.1). A paved area is an area with a firm level surfaces such as asphalt, hard gravel, tiles, concrete or the like. It shall be bordered (or framed) by the symbol step or edge of paved area (529.1). Distinct differences within the paved area can be represented with the symbol step or edge of paved area (529.1), if they serve navigation. Where a paved road, footpath or track goes through a non-urban area, the brown fill-in shall be drawn darker, so that if (x)% brown is used in urban areas, (x+20)% brown shall be used in the non-urban areas, and the line width of the black outline shall be increased from 0.07 to 0.14 mm. The black border line can be omitted where it is logical (e.g. indistinct/gradual gravel-to-grass transitions). Colour: brown 0 (white), 10%, 20% or 30% (urban) / 20%, 30%, 40%, 50% (non-urban) (min. 60 lines/cm), black; the colour and the line width shall be the same as for symbol unpaved footpath or track (506.1). A paved area is an area with a firm level surfaces such as asphalt, hard gravel, tiles, concrete or the like. It shall be bordered (or framed) by the symbol step or edge of paved area (529.1). Distinct differences within the paved area can be represented with the symbol step or edge of paved area (529.1), if they serve navigation. Where a paved road, footpath or track goes through a non-urban area, the brown fill-in shall be drawn darker, so that if (x)% brown is used in urban areas, (x+20)% brown shall be used in the non-urban areas, and the line width of the black outline shall be increased from 0.07 to 0.14 mm. The black border line can be omitted where it is logical (e.g. indistinct/gradual gravel-to-grass transitions). Colour: brown 0 (white), 10%, 20% or 30% (urban) / 20%, 30%, 40%, 50% (non-urban) (min. 60 lines/cm), black; the colour and the line width shall be the same as for symbol unpaved footpath or track (506.1). A paved area is an area with a firm level surfaces such as asphalt, hard gravel, tiles, concrete or the like. It shall be bordered (or framed) by the symbol step or edge of paved area (529.1). Distinct differences within the paved area can be represented with the symbol step or edge of paved area (529.1), if they serve navigation. Where a paved road, footpath or track goes through a non-urban area, the brown fill-in shall be drawn darker, so that if (x)% brown is used in urban areas, (x+20)% brown shall be used in the non-urban areas, and the line width of the black outline shall be increased from 0.07 to 0.14 mm. The black border line can be omitted where it is logical (e.g. indistinct/gradual gravel-to-grass transitions). Colour: brown 0 (white), 10%, 20% or 30% (urban) / 20%, 30%, 40%, 50% (non-urban) (min. 60 lines/cm), black; the colour and the line width shall be the same as for symbol unpaved footpath or track (506.1). A paved area is an area with a firm level surfaces such as asphalt, hard gravel, tiles, concrete or the like. It shall be bordered (or framed) by the symbol step or edge of paved area (529.1). Distinct differences within the paved area can be represented with the symbol step or edge of paved area (529.1), if they serve navigation. Where a paved road, footpath or track goes through a non-urban area, the brown fill-in shall be drawn darker, so that if (x)% brown is used in urban areas, (x+20)% brown shall be used in the non-urban areas, and the line width of the black outline shall be increased from 0.07 to 0.14 mm. The black border line can be omitted where it is logical (e.g. indistinct/gradual gravel-to-grass transitions). Colour: brown 0 (white), 10%, 20% or 30% (urban) / 20%, 30%, 40%, 50% (non-urban) (min. 60 lines/cm), black; the colour and the line width shall be the same as for symbol unpaved footpath or track (506.1). A step or an edge of a paved area. Steps of a stairway shall be represented in a generalized manner. Edges within paved areas are generally not represented, unless they serve navigation. The thickness of edge of paved areas shall be enlarged to 0.14 mm in non-urban areas to improve legibility. The thickness of step lines shall always be 0.07 mm. A step or an edge of a paved area. Steps of a stairway shall be represented in a generalized manner. Edges within paved areas are generally not represented, unless they serve navigation. The thickness of edge of paved areas shall be enlarged to 0.14 mm in non-urban areas to improve legibility. The thickness of step lines shall always be 0.07 mm. A step or an edge of a paved area. Steps of a stairway shall be represented in a generalized manner. Edges within paved areas are generally not represented, unless they serve navigation. The thickness of edge of paved areas shall be enlarged to 0.14 mm in non-urban areas to improve legibility. The thickness of step lines shall always be 0.07 mm.0 220;0 -220; A step or an edge of a paved area. Steps of a stairway shall be represented in a generalized manner. Edges within paved areas are generally not represented, unless they serve navigation. The thickness of edge of paved areas shall be enlarged to 0.14 mm in non-urban areas to improve legibility. The thickness of step lines shall always be 0.07 mm.0 440;0 -440; A pipeline (gas, water, oil, etc.) above ground level which can be crossed over or under.-530 530;0 0;-530 -530; An impassable pipeline (gas, water, oil, etc.) above ground level which shall not be crossed, due to forbidden access or because it may constitute a danger to the competitor because of its height. <span style="color:magenta">It is forbidden to cross an impassable pipeline! Competitors violating this rule will be disqualified.</span>-530 530;0 0;-530 -530; A high tower or large pylon. Very large towers shall be represented in plan shape with the symbol building (526.1). The symbol is orientated to north.-1050 0;1050 0;0 -1050;0 1050; An obvious small tower, platform or seat. The symbol is orientated to north.-750 -382;750 -382;0 -382;0 1118; Cairn, memorial, small monument or boundary stone more than 0.5 m high. Large massive monuments shall be represented in plan shape with the symbol building (526.1).0 0; A fodder rack, which is free standing or attached to a tree. The symbol is orientated to north.-750 -48;0 -481;750 -48;0 -481;0 1019; A man-made feature which is significant or prominent. The definition of the symbol shall always be given in the map legend. A man-made feature which is significant or prominent. The definition of the symbol shall always be given in the map legend. The symbol is orientated to north.-600 -600;600 600;-600 600;600 -600; Magnetic north lines are lines placed on the map pointing to magnetic north. Their spacing shall be 30 mm on the 1:5 000 map so they represent 150 m on the ground. North lines may be broken where they obscure small features such as boulders, knolls, cliffs, stream junctions, path ends, etc. Magnetic north lines are lines placed on the map pointing to magnetic north. Their spacing shall be 30 mm on the 1:5 000 map so they represent 150 m on the ground. North lines may be broken where they obscure small features such as boulders, knolls, cliffs, stream junctions, path ends, etc. <b>Note: this is a non-standard addition to the symbol set.</b>500 0;-1500 -600;-1000 0;-1500 600;500 0; Magnetic north lines are lines placed on the map pointing to magnetic north. Their spacing shall be 30 mm on the 1:5000 map so they represent 150 m on the ground. North lines may be broken where they obscure small features such as boulders, knolls, cliffs, stream junctions, path ends, etc. Cut holes in the pattern to create these breaks. Magnetic north lines are lines placed on the map pointing to magnetic north. Their spacing shall be 30 mm on the 1:5 000 map so they represent 150 m on the ground. North lines may be broken where they obscure small features such as boulders, knolls, cliffs, stream junctions, path ends, etc. Magnetic north lines are lines placed on the map pointing to magnetic north. Their spacing shall be 30 mm on the 1:5 000 map so they represent 150 m on the ground. North lines may be broken where they obscure small features such as boulders, knolls, cliffs, stream junctions, path ends, etc. <b>Note: this is a non-standard addition to the symbol set.</b>500 0;-1500 -600;-1000 0;-1500 600;500 0; Magnetic north lines are lines placed on the map pointing to magnetic north. Their spacing shall be 30 mm on the 1:5 000 map so they represent 150 m on the ground. North lines may be broken where they obscure small features such as boulders, knolls, cliffs, stream junctions, path ends, etc. Cut holes in the pattern to create these breaks. At least three registration marks shall be placed within the frame of a map in a non-symmetrical arrangement. In addition, a colour check should be possible.0 -2000;0 2000;-2000 0;2000 0; Spot heights are used for the rough assessment of height differences. The height is given to the nearest metre. The figures are orientated to the north. Water levels are given without the dot. Spot heights are used for the rough assessment of height differences. The height is given to the nearest metre. The figures are orientated to the north. Water levels are given without the dot. The start or map issue point (if not at the start) is shown by an equilateral triangle which points in the direction of the first control. The centre of the triangle shows the precise position of the start point.-3500 2021;0 -4041;3500 2021;-3500 2021 18; The control points are shown with circles. The centre of the circle shows the precise position of the feature. Sections of circles should be omitted to leave important detail showing. The number of the control is placed close to the control point circle in such a way that it does not obscure important detail. The numbers are orientated to north. Where controls are to be visited in order, the start, control points and finish are joined together by straight lines. Sections of lines should be omitted to leave important detail showing. A marked route is shown on the map with a dashed line. The finish is shown by two concentric circles.0 0; A boundary which it is not permitted to cross. Uncrossable boundaries shall be mapped by using the symbols: impassable cliff (201), impassable body of water (304.1), impassable marsh (309), impassable wall (521.1), impassable fence or railing (524) or impassable pipeline (534) and shall not be overprinted with symbol uncrossable boundary (707). This symbol is to be used only for last minute updates to the competition area, as excessive use of purple for indicating barriers is unfortunate. <span style="color:magenta">It is forbidden to cross an uncrossable boundary! Competitors violating this rule will be disqualified.</span> A crossing point through or over a wall or fence, or across a road or railway or through a tunnel or an out-of-bounds area is drawn on the map with two lines curving outwards. If underpasses or tunnels etc. are to be used in a competition, they shall be emphasized with symbol crossing point (708) or crossing section (708.1).-1500 725 1;-500 450;500 450;1500 725;-1500 -725 1;-500 -450;500 -450;1500 -725; Acrossing section through or over a building, wall or fence, or across a road or railway or through a tunnel or an out-of-bounds area is drawn on the map as a linear object, according to the plan shape. If underpasses or tunnels etc. are to be used in a competition, they shall be emphasized with symbol (708) or (708.1).250 0;0 0 1;-600 0;-900 -150;-1500 -650;-250 0;0 0 1;600 0;900 -150;1500 -650; Out of bounds areas are mapped with the symbol area with forbidden access (528.1). This symbol shall only be used for last minute updates to the competition map (e.g. for areas that may be dangerous for the competitors during the competition, or very late changes to the competition terrain). An out-of-bounds area is shown with vertical stripes. A bounding line may be drawn if there is no natural boundary, as follows: - a solid line indicates that the boundary is marked continuously (tapes, etc.) on the ground, - a dashed line indicates intermittent marking on the ground, - no line indicates no marking on the ground. <span style="color:magenta">It is forbidden to cross an out-of-bounds area! Competitors violating this rule will be disqualified.</span> A solid line indicates that the boundary is marked continuously (tapes, etc.) on the ground. A dashed line indicates intermittent marking on the ground. The location of a first aid post.-1500 0;1500 0;0 -1500;0 1500; The location of a refreshment point which is not at a control or along the marked route.-1340 -1395;-820 1380;1340 -1395;820 1380;0 -1770 1;600 -1770;1200 -1620;1340 -1395 1;1200 -1170;600 -1020;0 -1020 1;-600 -1020;-1200 -1170;-1340 -1395 1;-1200 -1620;-600 -1770;0 -1770;-820 1380 1;-680 1680;680 1680;820 1380; Obvious temporary constructions like platforms for spectators and speaker, closed area for spectators, outside restaurant areas, etc. shall be represented in plan shape. <span style="color:magenta">It is forbidden to enter a temporary construction or closed area! Competitors violating this rule will be disqualified.</span> This symbol provides a simple and quick way to make training courses. The purple line will extend a bit into the finish symbol. This is a shortcoming of this simple approach.3041 0;3541 0;-3021 3500;3041 0;-3021 -3500;-3021 3500 18;-600 0;600 0;0 0; The OpenOrienteering Logo.-12797 -557 1;-12755 -447;-12770 -420;-12873 -420 1;-12953 -420;-12973 -440;-12973 -520 1;-12973 -635;-12838 -663;-12797 -557 18;-933 2063 1;-933 1925;-920 1900;-851 1900 1;-780 1900;-770 1921;-781 2050 1;-789 2154;-814 2203;-863 2213 1;-920 2224;-933 2197;-933 2063;-933 2063 18;-8875 -3860 1;-8922 -3920;-8984 -3968;-9061 -4006 1;-9134 -4045;-9225 -4065;-9337 -4065 1;-9444 -4065;-9535 -4045;-9612 -4006 1;-9688 -3974;-9752 -3927;-9803 -3867 1;-9855 -3807;-9895 -3737;-9925 -3655 1;-9950 -3578;-9970 -3500;-9982 -3418;-8723 -3418 1;-8725 -3500;-8740 -3578;-8768 -3655 1;-8789 -3732;-8824 -3800;-8875 -3860 18;-4230 -3923 1;-4183 -3737;-4160 -3527;-4160 -3296;-4160 -1395;-5113 -1395;-5113 -3182 1;-5113 -3488;-5155 -3707;-5235 -3833 1;-5317 -3961;-5468 -4027;-5690 -4027 1;-5759 -4027;-5830 -4021;-5907 -4013 1;-5983 -4008;-6052 -4004;-6112 -3993;-6112 -1395;-7064 -1395;-7064 -4646 1;-6903 -4693;-6694 -4736;-6438 -4774 1;-6181 -4818;-5913 -4838;-5631 -4838 1;-5347 -4838;-5110 -4800;-4921 -4723 1;-4730 -4652;-4579 -4547;-4467 -4410 1;-4357 -4273;-4277 -4111;-4230 -3923 18;-13524 1118 1;-13605 1126;-13667 1137;-13708 1150;-13708 3722;-14662 3722;-14662 536 1;-14492 476;-14292 420;-14060 369 1;-13827 314;-13565 286;-13280 286 1;-13229 286;-13167 290;-13096 299 1;-13022 303;-12951 312;-12878 325 1;-12806 333;-12733 345;-12660 363 1;-12587 375;-12526 392;-12474 414;-12634 1201 1;-12719 1180;-12819 1158;-12936 1137 1;-13051 1112;-13174 1098;-13306 1098 1;-13366 1098;-13439 1105;-13524 1118 18;-11093 -200 1;-11204 -101;-11336 -52;-11490 -52 1;-11642 -52;-11777 -101;-11893 -200 1;-12004 -303;-12059 -441;-12059 -616 1;-12059 -791;-12004 -927;-11893 -1026 1;-11777 -1128;-11642 -1179;-11490 -1179 1;-11336 -1179;-11204 -1128;-11093 -1026 1;-10978 -927;-10920 -791;-10920 -616 1;-10920 -441;-10978 -303;-11093 -200 18;-11009 3722;-11963 3722;-11963 357;-11009 357;-11009 3722 18;-8649 1053 1;-8755 1053;-8847 1073;-8924 1112 1;-9001 1145;-9065 1192;-9115 1252 1;-9167 1312;-9207 1381;-9237 1464 1;-9264 1539;-9282 1618;-9296 1700;-8034 1700 1;-8039 1618;-8054 1539;-8079 1464 1;-8101 1387;-8137 1318;-8189 1259 1;-8236 1199;-8297 1150;-8374 1112 1;-8445 1073;-8537 1053;-8649 1053 18;-5220 1105 1;-5297 1110;-5365 1115;-5425 1125;-5425 3722;-6378 3722;-6378 472 1;-6217 425;-6007 382;-5751 344 1;-5495 301;-5227 280;-4945 280 1;-4658 280;-4422 318;-4235 395 1;-4042 467;-3890 572;-3781 709 1;-3669 845;-3591 1007;-3544 1195 1;-3497 1381;-3474 1592;-3474 1821;-3474 3722;-4427 3722;-4427 1937 1;-4427 1630;-4467 1411;-4549 1285 1;-4628 1156;-4780 1092;-5002 1092 1;-5070 1092;-5143 1097;-5220 1105 18;-2636 2346;-2636 -481;-1683 -634;-1683 357;-539 357;-539 1150;-1683 1150;-1683 2333 1;-1683 2535;-1648 2694;-1581 2813 1;-1508 2933;-1365 2993;-1151 2993 1;-1050 2993;-945 2984;-838 2966 1;-728 2946;-627 2918;-539 2884;-404 3626 1;-518 3673;-646 3712;-787 3748 1;-928 3780;-1102 3799;-1305 3799 1;-1566 3799;-1781 3764;-1951 3696 1;-2123 3624;-2259 3526;-2361 3402 1;-2463 3274;-2536 3121;-2579 2941 1;-2618 2763;-2636 2565;-2636 2346 18;2142 1464 1;2121 1387;2084 1318;2032 1259 1;1986 1199;1924 1150;1847 1112 1;1776 1073;1685 1053;1572 1053 1;1466 1053;1375 1073;1298 1112 1;1221 1145;1156 1192;1106 1252 1;1054 1312;1014 1381;984 1464 1;958 1539;939 1618;926 1700;2187 1700 1;2183 1618;2168 1539;2142 1464 18;5923 1700 1;5919 1618;5904 1539;5878 1464 1;5856 1387;5821 1318;5769 1259 1;5723 1199;5661 1150;5585 1112 1;5511 1073;5419 1053;5308 1053 1;5201 1053;5110 1073;5033 1112 1;4957 1145;4893 1192;4841 1252 1;4790 1312;4750 1381;4720 1464 1;4695 1539;4675 1618;4663 1700;5923 1700 18;8718 1118 1;8637 1126;8575 1137;8534 1150;8534 3722;7581 3722;7581 536 1;7750 476;7950 420;8182 369 1;8415 314;8677 286;8962 286 1;9013 286;9075 290;9146 299 1;9220 303;9292 312;9365 325 1;9437 333;9510 345;9583 363 1;9655 375;9717 392;9768 414;9608 1201 1;9523 1180;9424 1158;9306 1137 1;9191 1112;9069 1098;8937 1098 1;8877 1098;8804 1105;8718 1118 18;10350 -200 1;10238 -303;10183 -441;10183 -616 1;10183 -791;10238 -927;10350 -1026 1;10465 -1128;10600 -1179;10752 -1179 1;10906 -1179;11038 -1128;11149 -1026 1;11264 -927;11323 -791;11323 -616 1;11323 -441;11264 -303;11149 -200 1;11038 -101;10906 -52;10752 -52 1;10600 -52;10465 -101;10350 -200 18;11233 3722;10279 3722;10279 357;11233 357;11233 3722 18;14726 709 1;14836 845;14916 1007;14963 1195 1;15010 1381;15033 1592;15033 1821;15033 3722;14080 3722;14080 1937 1;14080 1630;14039 1411;13958 1285 1;13876 1156;13725 1092;13504 1092 1;13435 1092;13363 1097;13287 1105 1;13210 1110;13142 1115;13082 1125;13082 3722;12129 3722;12129 472 1;12290 425;12499 382;12756 344 1;13012 301;13280 280;13562 280 1;13847 280;14083 318;14272 395 1;14463 467;14614 572;14726 709 18;-17051 -1307 1;-17299 -1307;-17525 -1349;-17729 -1434 1;-17931 -1521;-18102 -1639;-18247 -1794 1;-18392 -1951;-18505 -2139;-18587 -2355 1;-18669 -2579;-18708 -2820;-18708 -3085 1;-18708 -3350;-18669 -3591;-18587 -3808 1;-18502 -4027;-18387 -4211;-18242 -4365 1;-18092 -4518;-17917 -4638;-17717 -4723 1;-17512 -4808;-17291 -4851;-17051 -4851 1;-16808 -4851;-16586 -4808;-16386 -4723 1;-16182 -4638;-16006 -4518;-15861 -4365 1;-15716 -4211;-15603 -4027;-15523 -3808 1;-15442 -3591;-15401 -3350;-15401 -3085 1;-15401 -2820;-15440 -2579;-15517 -2355 1;-15593 -2139;-15703 -1951;-15848 -1794 1;-15993 -1639;-16168 -1521;-16373 -1434 1;-16574 -1349;-16800 -1307;-17051 -1307 18;-17051 -2126 1;-16834 -2126;-16668 -2210;-16552 -2383 1;-16433 -2557;-16373 -2792;-16373 -3085 1;-16373 -3380;-16433 -3610;-16552 -3777 1;-16668 -3946;-16834 -4034;-17051 -4034 1;-17269 -4034;-17437 -3946;-17557 -3777 1;-17675 -3610;-17736 -3380;-17736 -3085 1;-17736 -2792;-17675 -2557;-17557 -2383 1;-17437 -2210;-17269 -2126;-17051 -2126 18;-14662 -213;-14662 -4646 1;-14576 -4673;-14478 -4697;-14369 -4716 1;-14257 -4743;-14142 -4765;-14022 -4781 1;-13898 -4798;-13776 -4811;-13652 -4819 1;-13524 -4833;-13402 -4838;-13287 -4838 1;-13009 -4838;-12763 -4796;-12544 -4711 1;-12328 -4630;-12144 -4513;-11995 -4358 1;-11846 -4210;-11732 -4027;-11657 -3808 1;-11574 -3591;-11535 -3348;-11535 -3078 1;-11535 -2819;-11566 -2582;-11629 -2368 1;-11694 -2156;-11788 -1972;-11911 -1819 1;-12034 -1666;-12189 -1546;-12373 -1461 1;-12556 -1376;-12765 -1333;-13006 -1333 1;-13137 -1333;-13261 -1346;-13377 -1371 1;-13492 -1395;-13602 -1432;-13708 -1479;-13708 -213;-14662 -213 18;-13184 -2139 1;-12733 -2139;-12506 -2443;-12506 -3054 1;-12506 -3348;-12572 -3582;-12704 -3756 1;-12837 -3936;-13032 -4027;-13293 -4027 1;-13379 -4027;-13457 -4021;-13530 -4013 1;-13602 -4008;-13662 -4004;-13708 -3993;-13708 -2274 1;-13648 -2234;-13572 -2202;-13479 -2177 1;-13381 -2152;-13282 -2139;-13184 -2139 18;-9182 -1307 1;-9485 -1307;-9750 -1352;-9976 -1440 1;-10198 -1530;-10383 -1652;-10533 -1806 1;-10678 -1964;-10787 -2149;-10858 -2362 1;-10926 -2575;-10962 -2807;-10962 -3054 1;-10962 -3352;-10917 -3612;-10827 -3833 1;-10733 -4060;-10611 -4248;-10462 -4396 1;-10314 -4547;-10141 -4660;-9950 -4736 1;-9754 -4813;-9553 -4851;-9349 -4851 1;-8872 -4851;-8494 -4705;-8217 -4410 1;-7939 -4120;-7801 -3692;-7801 -3123 1;-7801 -3069;-7803 -3007;-7808 -2939 1;-7810 -2873;-7816 -2817;-7819 -2766;-9982 -2766 1;-9961 -2569;-9870 -2413;-9707 -2299 1;-9545 -2184;-9327 -2126;-9055 -2126 1;-8881 -2126;-8708 -2141;-8542 -2171 1;-8372 -2205;-8234 -2246;-8127 -2292;-7999 -1517 1;-8051 -1493;-8119 -1468;-8204 -1440 1;-8289 -1416;-8385 -1395;-8492 -1378 1;-8594 -1356;-8706 -1339;-8824 -1326 1;-8944 -1314;-9063 -1307;-9182 -1307 18;-9982 -3418;-8723 -3418 1;-8725 -3500;-8740 -3578;-8768 -3655 1;-8789 -3732;-8824 -3800;-8875 -3860 1;-8922 -3920;-8984 -3968;-9061 -4006 1;-9134 -4045;-9225 -4065;-9337 -4065 1;-9444 -4065;-9535 -4045;-9612 -4006 1;-9688 -3974;-9752 -3927;-9803 -3867 1;-9855 -3807;-9895 -3737;-9925 -3655 1;-9950 -3578;-9970 -3500;-9982 -3418 18;-17051 -2126 1;-17269 -2126;-17437 -2210;-17557 -2383 1;-17675 -2557;-17736 -2792;-17736 -3085 1;-17736 -3380;-17675 -3610;-17557 -3777 1;-17437 -3946;-17269 -4034;-17051 -4034 1;-16834 -4034;-16668 -3946;-16552 -3777 1;-16433 -3610;-16373 -3380;-16373 -3085 1;-16373 -2792;-16433 -2557;-16552 -2383 1;-16668 -2210;-16834 -2126;-17051 -2126 18;-17064 -2593 1;-16944 -2591;-16842 -2808;-16838 -3077 1;-16834 -3347;-16928 -3567;-17049 -3569 1;-17170 -3571;-17271 -3354;-17275 -3084 1;-17280 -2815;-17185 -2595;-17064 -2593 18;-8496 3810 1;-8798 3810;-9062 3765;-9288 3677 1;-9510 3588;-9696 3466;-9845 3312 1;-9990 3154;-10098 2969;-10171 2756 1;-10240 2542;-10273 2311;-10273 2065 1;-10273 1766;-10230 1507;-10140 1285 1;-10045 1058;-9923 870;-9775 722 1;-9625 572;-9455 459;-9264 382 1;-9067 305;-8867 267;-8662 267 1;-8184 267;-7806 414;-7529 709 1;-7252 998;-7113 1426;-7113 1995 1;-7113 2050;-7116 2112;-7119 2180 1;-7124 2245;-7128 2301;-7132 2353;-9296 2353 1;-9273 2550;-9182 2704;-9020 2820 1;-8857 2934;-8640 2993;-8368 2993 1;-8193 2993;-8022 2978;-7855 2948 1;-7684 2912;-7546 2873;-7440 2826;-7311 3601 1;-7363 3626;-7431 3651;-7516 3677 1;-7603 3703;-7697 3724;-7804 3741 1;-7908 3763;-8017 3779;-8137 3793 1;-8257 3804;-8376 3810;-8496 3810 18;-9296 1700;-8034 1700 1;-8039 1618;-8054 1539;-8079 1464 1;-8101 1387;-8137 1318;-8189 1259 1;-8236 1199;-8297 1150;-8374 1112 1;-8445 1073;-8537 1053;-8649 1053 1;-8755 1053;-8847 1073;-8924 1112 1;-9001 1145;-9065 1192;-9115 1252 1;-9167 1312;-9207 1381;-9237 1464 1;-9264 1539;-9282 1618;-9296 1700 18;1726 3810 1;1422 3810;1159 3765;933 3677 1;712 3588;524 3466;376 3312 1;231 3154;123 2969;49 2756 1;-19 2542;-52 2311;-52 2065 1;-52 1766;-9 1507;81 1285 1;177 1058;298 870;446 722 1;596 572;766 459;958 382 1;1154 305;1354 267;1559 267 1;2038 267;2416 414;2692 709 1;2968 998;3109 1426;3109 1995 1;3109 2050;3105 2112;3102 2180 1;3097 2245;3094 2301;3088 2353;926 2353 1;947 2550;1039 2704;1201 2820 1;1364 2934;1581 2993;1854 2993 1;2029 2993;2199 2978;2365 2948 1;2538 2912;2675 2873;2782 2826;2910 3601 1;2859 3626;2790 3651;2705 3677 1;2619 3703;2524 3724;2417 3741 1;2314 3763;2204 3779;2084 3793 1;1965 3804;1846 3810;1726 3810 18;926 1700;2187 1700 1;2183 1618;2168 1539;2142 1464 1;2121 1387;2084 1318;2032 1259 1;1986 1199;1924 1150;1847 1112 1;1776 1073;1685 1053;1572 1053 1;1466 1053;1375 1073;1298 1112 1;1221 1145;1156 1192;1106 1252 1;1054 1312;1014 1381;984 1464 1;958 1539;939 1618;926 1700 18;5463 3810 1;5160 3810;4895 3765;4668 3677 1;4446 3588;4262 3466;4112 3312 1;3967 3154;3858 2969;3787 2756 1;3719 2542;3684 2311;3684 2065 1;3684 1766;3729 1507;3819 1285 1;3911 1058;4033 870;4183 722 1;4332 572;4503 459;4695 382 1;4891 305;5091 267;5297 267 1;5773 267;6151 414;6428 709 1;6706 998;6844 1426;6844 1995 1;6844 2050;6843 2112;6837 2180 1;6834 2245;6829 2301;6826 2353;4663 2353 1;4683 2550;4775 2704;4938 2820 1;5100 2934;5318 2993;5590 2993 1;5765 2993;5936 2978;6103 2948 1;6272 2912;6411 2873;6518 2826;6646 3601 1;6594 3626;6526 3651;6441 3677 1;6356 3703;6259 3724;6152 3741 1;6051 3763;5939 3779;5821 3793 1;5701 3804;5581 3810;5463 3810 18;4663 1700;5923 1700 1;5919 1618;5904 1539;5878 1464 1;5856 1387;5821 1318;5769 1259 1;5723 1199;5661 1150;5585 1112 1;5511 1073;5419 1053;5308 1053 1;5201 1053;5110 1073;5033 1112 1;4957 1145;4893 1192;4841 1252 1;4790 1312;4750 1381;4720 1464 1;4695 1539;4675 1618;4663 1700 18;17092 4924 1;16887 4924;16682 4905;16477 4867 1;16272 4832;16083 4785;15908 4725;16074 3926 1;16224 3986;16379 4032;16543 4067 1;16707 4101;16896 4119;17105 4119 1;17377 4119;17570 4059;17680 3939 1;17796 3819;17854 3666;17854 3479;17854 3357 1;17750 3404;17644 3441;17533 3466 1;17427 3487;17309 3498;17182 3498 1;16717 3498;16361 3361;16113 3087 1;15866 2811;15743 2424;15743 1930 1;15743 1683;15781 1458;15857 1259 1;15934 1053;16044 878;16189 733 1;16339 589;16521 478;16734 402 1;16947 320;17187 280;17457 280 1;17572 280;17689 286;17809 299 1;17931 307;18053 320;18173 337 1;18292 354;18405 375;18512 402 1;18624 422;18722 446;18806 472;18806 3299 1;18806 3849;18665 4257;18385 4522 1;18107 4790;17677 4924;17092 4924 18;17360 2730 1;17459 2730;17550 2718;17636 2691 1;17720 2666;17794 2636;17854 2603;17854 1080 1;17807 1072;17750 1065;17687 1060 1;17623 1052;17548 1047;17464 1047 1;17212 1047;17024 1130;16900 1297 1;16776 1464;16714 1675;16714 1930 1;16714 2463;16930 2730;17360 2730 18;-17051 2993 1;-17269 2993;-17437 2908;-17557 2736 1;-17675 2561;-17736 2326;-17736 2033 1;-17736 1738;-17675 1509;-17557 1342 1;-17437 1171;-17269 1085;-17051 1085 1;-16834 1085;-16668 1171;-16552 1342 1;-16433 1509;-16373 1738;-16373 2033 1;-16373 2326;-16433 2561;-16552 2736 1;-16668 2908;-16834 2993;-17051 2993 18;-17064 2526 1;-16944 2528;-16842 2311;-16838 2042 1;-16834 1772;-16928 1552;-17049 1550 1;-17170 1548;-17271 1765;-17275 2035 1;-17280 2304;-17185 2524;-17064 2526 18;-17051 3810 1;-17299 3810;-17525 3769;-17729 3684 1;-17931 3598;-18102 3479;-18247 3324 1;-18392 3168;-18505 2980;-18587 2763 1;-18669 2540;-18708 2298;-18708 2033 1;-18708 1768;-18669 1526;-18587 1310 1;-18502 1092;-18387 906;-18242 754 1;-18092 600;-17917 480;-17717 395 1;-17512 310;-17291 267;-17051 267 1;-16808 267;-16586 310;-16386 395 1;-16182 480;-16006 600;-15861 754 1;-15716 906;-15603 1092;-15523 1310 1;-15442 1526;-15401 1768;-15401 2033 1;-15401 2298;-15440 2540;-15517 2763 1;-15593 2980;-15703 3168;-15848 3324 1;-15993 3479;-16168 3598;-16373 3684 1;-16574 3769;-16800 3810;-17051 3810 18;-17051 2993 1;-16834 2993;-16668 2908;-16552 2736 1;-16433 2561;-16373 2326;-16373 2033 1;-16373 1738;-16433 1509;-16552 1342 1;-16668 1171;-16834 1085;-17051 1085 1;-17269 1085;-17437 1171;-17557 1342 1;-17675 1509;-17736 1738;-17736 2033 1;-17736 2326;-17675 2561;-17557 2736 1;-17437 2908;-17269 2993;-17051 2993 18;17090 5390 1;16864 5390;16623 5362;16391 5321 1;16389 5320;16389 5320;16387 5320 1;16169 5284;15963 5240;15758 5170 1;15555 5102;15409 4847;15450 4637 1;15450 4635;15450 4634;15450 4632;15538 4221 1;15549 4165;15526 4109;15478 4079 1;15431 4049;15369 4052;15324 4088 1;15245 4152;15138 4191;15033 4191;14080 4191 1;13930 4193;13775 4107;13692 3983 1;13669 3944;13625 3921;13580 3921 1;13534 3921;13492 3944;13466 3983 1;13385 4107;13233 4191;13083 4191;12131 4191 1;12001 4191;11866 4131;11781 4032 1;11755 4004;11719 3987;11680 3987 1;11642 3987;11606 4004;11581 4032 1;11496 4131;11364 4191;11234 4191;10281 4191 1;10048 4193;9815 3957;9815 3724;9815 1796 1;9815 1758;9798 1721;9770 1697 1;9741 1670;9703 1658;9665 1663 1;9608 1670;9553 1668;9498 1655 1;9433 1638;9341 1618;9227 1597 1;9221 1595;9217 1593;9210 1592 1;9161 1582;9142 1582;9157 1584 1;9118 1578;9078 1590;9048 1615 1;9018 1640;9001 1678;9001 1716 1;9001 1716;9001 3724;9001 3724 1;9001 3957;8768 4193;8535 4191;7582 4191 1;7424 4191;7262 4099;7183 3962 1;7162 3926;7125 3900;7082 3894 1;7040 3889;6999 3902;6969 3932 1;6935 3966;6899 3996;6855 4016 1;6768 4059;6679 4092;6574 4124 1;6467 4157;6364 4178;6253 4195 1;6244 4197;6236 4199;6226 4202 1;6107 4227;5991 4240;5870 4254 1;5738 4267;5601 4279;5461 4279 1;5121 4279;4808 4231;4520 4120 1;4514 4118;4506 4114;4498 4110 1;4220 3998;3977 3840;3778 3636 1;3717 3566;3663 3494;3609 3412 1;3571 3361;3503 3344;3445 3371 1;3389 3396;3357 3459;3370 3521 1;3404 3711;3293 3930;3120 4016 1;3034 4060;2945 4092;2837 4124 1;2732 4157;2628 4178;2517 4195 1;2508 4197;2499 4199;2490 4202 1;2372 4227;2256 4240;2134 4254 1;2001 4267;1866 4279;1726 4279 1;1386 4279;1071 4231;785 4120 1;778 4118;771 4114;761 4110 1;562 4029;383 3917;218 3782 1;186 3757;145 3748;105 3757 1;64 3765;32 3793;13 3829 1;-35 3929;-124 4013;-229 4054 1;-362 4109;-512 4164;-678 4204 1;-871 4251;-1073 4266;-1303 4266 1;-1606 4266;-1876 4227;-2127 4126 1;-2355 4031;-2561 3892;-2717 3706 1;-2719 3701;-2721 3699;-2719 3702 1;-2716 3707;-2718 3706;-2721 3700 1;-2724 3694;-2734 3667;-2764 3624 1;-2799 3577;-2859 3558;-2915 3576 1;-2969 3594;-3007 3646;-3006 3704;-3006 3724 1;-3006 3957;-3239 4193;-3473 4191;-4427 4191 1;-4577 4193;-4730 4107;-4814 3983 1;-4838 3944;-4880 3921;-4927 3921 1;-4972 3921;-5013 3944;-5038 3983 1;-5122 4107;-5274 4191;-5421 4191;-6377 4191 1;-6533 4191;-6696 4099;-6777 3962 1;-6796 3926;-6833 3900;-6875 3894 1;-6916 3889;-6958 3902;-6988 3932 1;-7021 3966;-7059 3996;-7101 4016 1;-7101 4017;-7104 4016;-7104 4016 1;-7191 4059;-7278 4092;-7384 4124 1;-7489 4157;-7592 4178;-7704 4195 1;-7714 4197;-7722 4199;-7729 4199 1;-7731 4201;-7733 4202;-7735 4204 1;-7849 4227;-7966 4240;-8088 4254 1;-8221 4267;-8355 4279;-8496 4279 1;-8836 4279;-9151 4231;-9437 4120 1;-9444 4118;-9450 4114;-9461 4110 1;-9737 3998;-9980 3840;-10181 3635 1;-10185 3631;-10185 3628;-10186 3624 1;-10218 3591;-10250 3536;-10300 3470 1;-10333 3425;-10393 3404;-10450 3423 1;-10505 3440;-10541 3493;-10541 3549 1;-10541 3549;-10541 3724;-10541 3724 1;-10541 3957;-10774 4193;-11008 4191;-11961 4191 1;-12194 4193;-12427 3957;-12427 3724;-12427 1796 1;-12427 1758;-12444 1721;-12472 1697 1;-12501 1670;-12539 1658;-12577 1663 1;-12634 1670;-12689 1668;-12744 1655 1;-12809 1638;-12901 1618;-13015 1597 1;-13021 1595;-13026 1593;-13032 1592 1;-13081 1582;-13101 1582;-13086 1584 1;-13124 1578;-13164 1590;-13194 1615 1;-13224 1640;-13240 1678;-13240 1716;-13240 3724 1;-13240 3957;-13474 4193;-13707 4191;-14660 4191 1;-14893 4193;-15126 3957;-15126 3724;-15126 3532 1;-15126 3476;-15164 3423;-15218 3406 1;-15273 3387;-15335 3408;-15368 3455 1;-15420 3526;-15463 3591;-15511 3643 1;-15517 3651;-15517 3652;-15518 3656 1;-15703 3851;-15933 4002;-16189 4110 1;-16458 4224;-16748 4279;-17051 4279 1;-17346 4279;-17629 4225;-17889 4120 1;-17895 4118;-17902 4114;-17909 4112 1;-18160 4005;-18392 3849;-18580 3651 1;-18586 3643;-18587 3641;-18588 3637 1;-18776 3434;-18919 3196;-19017 2931 1;-19017 2931;-19017 2933;-19019 2927 1;-19122 2648;-19176 2350;-19176 2033 1;-19176 1718;-19123 1419;-19019 1142;-19019 1139 1;-18913 875;-18770 642;-18587 446 1;-18407 241;-18054 35;-17897 -33 1;-17894 -33;-17891 -35;-17889 -37 1;-17628 -143;-17344 -200;-17051 -200 1;-16755 -200;-16468 -143;-16206 -33 1;-15946 76;-15707 235;-15518 433 1;-15470 487;-15425 560;-15368 639 1;-15335 685;-15273 705;-15218 687 1;-15164 669;-15126 617;-15126 559;-15126 538 1;-15126 435;-15088 329;-15023 247 1;-14983 198;-14983 129;-15023 80 1;-15088 -1;-15126 -108;-15126 -211;-15126 -1586 1;-15126 -1644;-15164 -1696;-15218 -1714 1;-15273 -1733;-15335 -1712;-15368 -1666 1;-15420 -1594;-15463 -1529;-15506 -1483 1;-15508 -1478;-15511 -1474;-15517 -1465 1;-15701 -1271;-15928 -1115;-16189 -1005 1;-16463 -889;-16753 -841;-17051 -841 1;-17341 -841;-17624 -888;-17889 -995 1;-17895 -997;-17902 -1001;-17909 -1003 1;-18165 -1113;-18394 -1273;-18578 -1463 1;-18582 -1468;-18582 -1470;-18584 -1474 1;-18586 -1476;-18587 -1479;-18588 -1483 1;-18776 -1684;-18919 -1924;-19019 -2192 1;-19122 -2472;-19176 -2770;-19176 -3085 1;-19176 -3401;-19122 -3698;-19019 -3973 1;-18915 -4238;-18774 -4473;-18586 -4675 1;-18582 -4680;-18580 -4682;-18580 -4684 1;-18577 -4686;-18573 -4688;-18572 -4690 1;-18384 -4883;-18152 -5043;-17897 -5151 1;-17894 -5153;-17891 -5154;-17889 -5156 1;-17628 -5263;-17344 -5317;-17051 -5317 1;-16755 -5317;-16468 -5263;-16204 -5150 1;-15943 -5038;-15707 -4885;-15518 -4684 1;-15468 -4631;-15425 -4560;-15368 -4481 1;-15335 -4433;-15273 -4413;-15218 -4431 1;-15164 -4449;-15126 -4502;-15126 -4560 1;-15126 -4560;-15126 -4648;-15126 -4648 1;-15126 -4840;-14978 -5038;-14794 -5093 1;-14694 -5123;-14598 -5144;-14499 -5165 1;-14499 -5165;-14477 -5168;-14452 -5173 1;-14340 -5198;-14217 -5219;-14085 -5240 1;-13922 -5266;-13842 -5266;-13682 -5281 1;-13547 -5294;-13415 -5306;-13286 -5306 1;-12964 -5306;-12660 -5253;-12380 -5145 1;-12373 -5143;-12371 -5143;-12369 -5143 1;-12101 -5041;-11864 -4883;-11665 -4681 1;-11657 -4671;-11653 -4668;-11649 -4665 1;-11536 -4548;-11441 -4412;-11358 -4260 1;-11334 -4218;-11289 -4192;-11242 -4192 1;-11193 -4192;-11148 -4218;-11125 -4260 1;-11028 -4434;-10923 -4593;-10791 -4726 1;-10600 -4917;-10372 -5065;-10128 -5165 1;-10125 -5165;-10124 -5166;-10118 -5168 1;-9872 -5263;-9613 -5317;-9350 -5317 1;-8783 -5317;-8251 -5120;-7882 -4730 1;-7846 -4691;-7812 -4641;-7767 -4581 1;-7733 -4538;-7676 -4519;-7622 -4536 1;-7569 -4553;-7532 -4600;-7529 -4656 1;-7526 -4846;-7376 -5039;-7192 -5093 1;-6999 -5148;-6777 -5186;-6518 -5226 1;-6513 -5227;-6510 -5229;-6505 -5231 1;-6226 -5276;-5933 -5306;-5630 -5306 1;-5305 -5306;-5020 -5259;-4754 -5153 1;-4499 -5054;-4271 -4908;-4106 -4706 1;-3948 -4513;-3839 -4282;-3777 -4034 1;-3719 -3803;-3694 -3557;-3694 -3294;-3694 -1395 1;-3694 -1161;-3927 -927;-4160 -927;-5113 -927 1;-5263 -927;-5419 -1011;-5502 -1136 1;-5525 -1174;-5569 -1198;-5613 -1198 1;-5660 -1198;-5701 -1174;-5727 -1136 1;-5808 -1012;-5960 -927;-6110 -927;-7063 -927 1;-7220 -929;-7380 -1021;-7459 -1158 1;-7479 -1194;-7516 -1219;-7558 -1224 1;-7599 -1231;-7641 -1216;-7671 -1186 1;-7706 -1151;-7744 -1121;-7790 -1100 1;-7898 -1046;-7976 -1023;-8071 -995 1;-8178 -963;-8281 -942;-8392 -924 1;-8400 -923;-8409 -921;-8417 -920 1;-8419 -918;-8419 -916;-8421 -916 1;-8527 -894;-8644 -874;-8775 -861 1;-8917 -846;-9054 -841;-9183 -841 1;-9523 -841;-9837 -888;-10125 -999 1;-10132 -1003;-10138 -1004;-10148 -1010 1;-10205 -1033;-10261 -1066;-10333 -1102 1;-10380 -1126;-10438 -1119;-10478 -1085 1;-10520 -1051;-10537 -996;-10520 -944 1;-10481 -826;-10453 -709;-10453 -616 1;-10453 -441;-10525 -246;-10637 -67 1;-10667 -20;-10665 40;-10633 84 1;-10575 164;-10541 262;-10541 359;-10541 559 1;-10541 617;-10505 669;-10450 687 1;-10393 705;-10333 685;-10300 639 1;-10233 547;-10171 461;-10103 392 1;-9914 202;-9685 53;-9442 -45 1;-9439 -46;-9435 -48;-9433 -50 1;-9432 -50;-9431 -50;-9429 -50 1;-9183 -144;-8927 -200;-8662 -200 1;-8096 -200;-7562 -1;-7194 386;-7194 386 1;-7192 387;-7194 385;-7194 388 1;-7192 392;-7192 389;-7192 390;-7143 453;-7079 538 1;-7046 581;-6988 598;-6935 581 1;-6882 566;-6846 519;-6843 463 1;-6837 273;-6688 79;-6505 25 1;-6313 -30;-6091 -67;-5824 -110 1;-5540 -156;-5246 -186;-4944 -186 1;-4619 -186;-4332 -142;-4066 -35 1;-3813 64;-3584 211;-3419 414 1;-3404 432;-3387 477;-3347 534 1;-3315 585;-3254 609;-3195 590 1;-3139 574;-3101 519;-3103 459 1;-3103 459;-3103 -482;-3103 -482 1;-3103 -693;-2915 -908;-2706 -941;-1753 -1091 1;-1501 -1129;-1215 -886;-1215 -633;-1215 -241 1;-1215 -166;-1155 -108;-1082 -108;-537 -108 1;-323 -106;-109 82;-79 292 1;-70 342;-35 384;11 399 1;60 415;111 402;147 367 1;333 190;549 49;786 -47 1;1037 -144;1294 -200;1559 -200 1;2126 -200;2658 -1;3027 388 1;3144 513;3239 654;3317 805 1;3338 848;3383 875;3432 875 1;3481 876;3526 852;3550 808 1;3642 654;3737 510;3853 392 1;4045 202;4273 53;4521 -47;4521 -47 1;4521 -48;4520 -46;4522 -47 1;4529 -50;4527 -50;4527 -50 1;4773 -144;5031 -200;5295 -200 1;5861 -200;6394 -1;6763 388 1;6802 432;6839 480;6882 538 1;6914 581;6969 600;7022 585 1;7074 572;7112 529;7119 476 1;7140 310;7271 152;7427 97 1;7615 32;7831 -23;8080 -81 1;8085 -82;8083 -82;8083 -82 1;8360 -146;8654 -178;8965 -178 1;9028 -178;9099 -173;9168 -166 1;9182 -165;9190 -163;9205 -161 1;9278 -156;9347 -148;9415 -136 1;9437 -133;9445 -133;9447 -133 1;9507 -125;9570 -116;9635 -103 1;9681 -95;9728 -112;9758 -146 1;9788 -181;9800 -230;9785 -274 1;9745 -394;9718 -512;9718 -616 1;9718 -878;9838 -1186;10043 -1369 1;10048 -1372;10051 -1374;10058 -1380 1;10243 -1538;10508 -1644;10751 -1644 1;10994 -1644;11257 -1542;11452 -1373 1;11458 -1367;11460 -1365;11466 -1364 1;11671 -1177;11789 -869;11789 -616 1;11789 -441;11717 -246;11606 -67 1;11576 -18;11579 44;11619 94 1;11644 132;11678 151;11716 155 1;11753 159;11791 147;11817 122 1;11871 77;11935 44;12001 25 1;12194 -30;12416 -67;12682 -110 1;12967 -156;13260 -186;13564 -186 1;13889 -186;14173 -142;14439 -35 1;14695 64;14923 211;15088 414 1;15181 529;15253 669;15316 817 1;15337 863;15381 893;15431 897 1;15481 900;15529 874;15555 830 1;15641 673;15739 530;15862 404 1;16061 212;16299 65;16570 -35 1;16849 -140;17147 -186;17457 -186 1;17585 -186;17713 -174;17847 -161 1;17854 -161;17842 -163;17865 -161 1;17990 -153;18110 -138;18237 -120 1;18355 -103;18475 -82;18593 -58 1;18605 -52;18617 -50;18628 -50 1;18743 -26;18848 -1;18940 25 1;19124 80;19273 279;19273 472;19273 3299 1;19273 3915;19111 4466;18724 4844 1;18718 4850;18716 4852;18709 4858 1;18305 5243;17742 5390;17090 5390 18;-1305 3799 1;-1102 3799;-928 3780;-787 3748 1;-646 3712;-518 3673;-404 3626;-539 2884 1;-627 2918;-728 2946;-838 2966 1;-945 2984;-1050 2993;-1151 2993 1;-1365 2993;-1508 2933;-1581 2813 1;-1648 2694;-1683 2535;-1683 2333;-1683 1150;-539 1150;-539 357;-1683 357;-1683 -634;-2636 -481;-2636 2346 1;-2636 2565;-2618 2763;-2579 2941 1;-2536 3121;-2463 3274;-2361 3402 1;-2259 3526;-2123 3624;-1951 3696 1;-1781 3764;-1566 3799;-1305 3799 18;10752 -52 1;10906 -52;11038 -101;11149 -200 1;11264 -303;11323 -441;11323 -616 1;11323 -791;11264 -927;11149 -1026 1;11038 -1128;10906 -1179;10752 -1179 1;10600 -1179;10465 -1128;10350 -1026 1;10238 -927;10183 -791;10183 -616 1;10183 -441;10238 -303;10350 -200 1;10465 -101;10600 -52;10752 -52 18;10279 3722;11233 3722;11233 357;10279 357;10279 3722 18;-11963 3722;-11009 3722;-11009 357;-11963 357;-11963 3722 18;7581 3722;8534 3722;8534 1150 1;8575 1137;8637 1126;8718 1118 1;8804 1105;8877 1098;8937 1098 1;9069 1098;9191 1112;9306 1137 1;9424 1158;9523 1180;9608 1201;9768 414 1;9717 392;9655 375;9583 363 1;9510 345;9437 333;9365 325 1;9292 312;9220 303;9146 299 1;9075 290;9013 286;8962 286 1;8677 286;8415 314;8182 369 1;7950 420;7750 476;7581 536;7581 3722 18;12129 3722;13082 3722;13082 1125 1;13142 1115;13210 1110;13287 1105 1;13363 1097;13435 1092;13504 1092 1;13725 1092;13876 1156;13958 1285 1;14039 1411;14080 1630;14080 1937;14080 3722;15033 3722;15033 1821 1;15033 1592;15010 1381;14963 1195 1;14916 1007;14836 845;14726 709 1;14614 572;14463 467;14272 395 1;14083 318;13847 280;13562 280 1;13280 280;13012 301;12756 344 1;12499 382;12290 425;12129 472;12129 3722 18;15729 3616 1;15737 3607;15749 3596;15758 3588 1;15784 3562;15799 3528;15799 3491 1;15799 3491;15799 3489;15799 3487;15794 3440;15767 3400 1;15769 3404;15763 3384;15737 3354 1;15711 3321;15671 3303;15623 3306 1;15549 3318;15499 3374;15499 3441 1;15499 3441;15499 3524;15499 3524 1;15501 3579;15533 3628;15585 3648 1;15634 3667;15691 3656;15729 3616 18;-14662 3722;-13708 3722;-13708 1150 1;-13667 1137;-13605 1126;-13524 1118 1;-13439 1105;-13366 1098;-13306 1098 1;-13174 1098;-13051 1112;-12936 1137 1;-12819 1158;-12719 1180;-12634 1201;-12474 414 1;-12526 392;-12587 375;-12660 363 1;-12733 345;-12806 333;-12878 325 1;-12951 312;-13022 303;-13096 299 1;-13167 290;-13229 286;-13280 286 1;-13565 286;-13827 314;-14060 369 1;-14292 420;-14492 476;-14662 536;-14662 3722 18;-6378 3722;-5425 3722;-5425 1125 1;-5365 1115;-5297 1110;-5220 1105 1;-5143 1097;-5070 1092;-5002 1092 1;-4780 1092;-4628 1156;-4549 1285 1;-4467 1411;-4427 1630;-4427 1937;-4427 3722;-3474 3722;-3474 1821 1;-3474 1592;-3497 1381;-3544 1195 1;-3591 1007;-3669 845;-3781 709 1;-3890 572;-4042 467;-4235 395 1;-4422 318;-4658 280;-4945 280 1;-5227 280;-5495 301;-5751 344 1;-6007 382;-6217 425;-6378 472;-6378 3722 18;17092 4924 1;17677 4924;18107 4790;18385 4522 1;18665 4257;18806 3849;18806 3299;18806 472 1;18722 446;18624 422;18512 402 1;18405 375;18292 354;18173 337 1;18053 320;17931 307;17809 299 1;17689 286;17572 280;17457 280 1;17187 280;16947 320;16734 402 1;16521 478;16339 589;16189 733 1;16044 878;15934 1053;15857 1259 1;15781 1458;15743 1683;15743 1930 1;15743 2424;15866 2811;16113 3087 1;16361 3361;16717 3498;17182 3498 1;17309 3498;17427 3487;17533 3466 1;17644 3441;17750 3404;17854 3357;17854 3479 1;17854 3666;17796 3819;17680 3939 1;17570 4059;17377 4119;17105 4119 1;16896 4119;16707 4101;16543 4067 1;16379 4032;16224 3986;16074 3926;15908 4725 1;16083 4785;16272 4832;16477 4867 1;16682 4905;16887 4924;17092 4924 18;1726 3810 1;1846 3810;1965 3804;2084 3793 1;2204 3779;2314 3763;2417 3741 1;2524 3724;2619 3703;2705 3677 1;2790 3651;2859 3626;2910 3601;2782 2826 1;2675 2873;2538 2912;2365 2948 1;2199 2978;2029 2993;1854 2993 1;1581 2993;1364 2934;1201 2820 1;1039 2704;947 2550;926 2353;3088 2353 1;3094 2301;3097 2245;3102 2180 1;3105 2112;3109 2050;3109 1995 1;3109 1426;2968 998;2692 709 1;2416 414;2038 267;1559 267 1;1354 267;1154 305;958 382 1;766 459;596 572;446 722 1;298 870;177 1058;81 1285 1;-9 1507;-52 1766;-52 2065 1;-52 2311;-19 2542;49 2756 1;123 2969;231 3154;376 3312 1;524 3466;712 3588;933 3677 1;1159 3765;1422 3810;1726 3810 18;-8496 3810 1;-8376 3810;-8257 3804;-8137 3793 1;-8017 3779;-7908 3763;-7804 3741 1;-7697 3724;-7603 3703;-7516 3677 1;-7431 3651;-7363 3626;-7311 3601;-7440 2826 1;-7546 2873;-7684 2912;-7855 2948 1;-8022 2978;-8193 2993;-8368 2993 1;-8640 2993;-8857 2934;-9020 2820 1;-9182 2704;-9273 2550;-9296 2353;-7132 2353 1;-7128 2301;-7124 2245;-7119 2180 1;-7116 2112;-7113 2050;-7113 1995 1;-7113 1426;-7252 998;-7529 709 1;-7806 414;-8184 267;-8662 267 1;-8867 267;-9067 305;-9264 382 1;-9455 459;-9625 572;-9775 722 1;-9923 870;-10045 1058;-10140 1285 1;-10230 1507;-10273 1766;-10273 2065 1;-10273 2311;-10240 2542;-10171 2756 1;-10098 2969;-9990 3154;-9845 3312 1;-9696 3466;-9510 3588;-9288 3677 1;-9062 3765;-8798 3810;-8496 3810 18;-12484 -146 1;-12454 -181;-12442 -230;-12457 -274 1;-12497 -394;-12524 -512;-12524 -616 1;-12524 -619;-12502 -670;-12491 -753 1;-12484 -794;-12496 -836;-12524 -866 1;-12554 -896;-12594 -911;-12640 -905 1;-12765 -882;-12886 -866;-13002 -866 1;-13009 -866;-13043 -876;-13102 -878 1;-13139 -879;-13174 -866;-13199 -841 1;-13225 -814;-13240 -781;-13240 -745 1;-13240 -745;-13240 -300;-13240 -300 1;-13240 -230;-13188 -171;-13119 -166 1;-13079 -161;-13064 -165;-13074 -166;-13056 -164;-13037 -161 1;-12964 -156;-12895 -148;-12827 -136 1;-12806 -133;-12797 -133;-12795 -133;-12795 -132;-12793 -132;-12795 -133 1;-12735 -125;-12673 -116;-12607 -103 1;-12561 -95;-12514 -112;-12484 -146 18;-14662 -213;-13708 -213;-13708 -1479 1;-13602 -1432;-13492 -1395;-13377 -1371 1;-13261 -1346;-13137 -1333;-13006 -1333 1;-12765 -1333;-12556 -1376;-12373 -1461 1;-12189 -1546;-12034 -1666;-11911 -1819 1;-11788 -1972;-11694 -2156;-11629 -2368 1;-11566 -2582;-11535 -2819;-11535 -3078 1;-11535 -3348;-11574 -3591;-11657 -3808 1;-11732 -4027;-11846 -4210;-11995 -4358 1;-12144 -4513;-12328 -4630;-12544 -4711 1;-12763 -4796;-13009 -4838;-13287 -4838 1;-13402 -4838;-13524 -4833;-13652 -4819 1;-13776 -4811;-13898 -4798;-14022 -4781 1;-14142 -4765;-14257 -4743;-14369 -4716 1;-14478 -4697;-14576 -4673;-14662 -4646;-14662 -213 18;-17051 3810 1;-16800 3810;-16574 3769;-16373 3684 1;-16168 3598;-15993 3479;-15848 3324 1;-15703 3168;-15593 2980;-15517 2763 1;-15440 2540;-15401 2298;-15401 2033 1;-15401 1768;-15442 1526;-15523 1310 1;-15603 1092;-15716 906;-15861 754 1;-16006 600;-16182 480;-16386 395 1;-16586 310;-16808 267;-17051 267 1;-17291 267;-17512 310;-17717 395 1;-17917 480;-18092 600;-18242 754 1;-18387 906;-18502 1092;-18587 1310 1;-18669 1526;-18708 1768;-18708 2033 1;-18708 2298;-18669 2540;-18587 2763 1;-18505 2980;-18392 3168;-18247 3324 1;-18102 3479;-17931 3598;-17729 3684 1;-17525 3769;-17299 3810;-17051 3810 18;5463 3810 1;5581 3810;5701 3804;5821 3793 1;5939 3779;6051 3763;6152 3741 1;6259 3724;6356 3703;6441 3677 1;6526 3651;6594 3626;6646 3601;6518 2826 1;6411 2873;6272 2912;6103 2948 1;5936 2978;5765 2993;5590 2993 1;5318 2993;5100 2934;4938 2820 1;4775 2704;4683 2550;4663 2353;6826 2353 1;6829 2301;6834 2245;6837 2180 1;6843 2112;6844 2050;6844 1995 1;6844 1426;6706 998;6428 709 1;6151 414;5773 267;5297 267 1;5091 267;4891 305;4695 382 1;4503 459;4332 572;4183 722 1;4033 870;3911 1058;3819 1285 1;3729 1507;3684 1766;3684 2065 1;3684 2311;3719 2542;3787 2756 1;3858 2969;3967 3154;4112 3312 1;4262 3466;4446 3588;4668 3677 1;4895 3765;5160 3810;5463 3810 18;-1065 2521 1;-1018 2518;-969 2518;-911 2508 1;-910 2508;-909 2506;-907 2505 1;-830 2489;-762 2473;-704 2450 1;-702 2450;-700 2450;-698 2450 1;-670 2439;-642 2426;-612 2422 1;-545 2409;-500 2350;-503 2283 1;-509 2203;-520 2131;-520 2067 1;-520 1973;-502 1875;-490 1763 1;-487 1725;-500 1688;-525 1660 1;-550 1633;-586 1616;-623 1616;-1106 1616 1;-1170 1630;-1217 1687;-1215 1750;-1215 2333 1;-1215 2360;-1211 2386;-1206 2405 1;-1198 2474;-1136 2527;-1065 2521 18;-11490 -52 1;-11336 -52;-11204 -101;-11093 -200 1;-10978 -303;-10920 -441;-10920 -616 1;-10920 -791;-10978 -927;-11093 -1026 1;-11204 -1128;-11336 -1179;-11490 -1179 1;-11642 -1179;-11777 -1128;-11893 -1026 1;-12004 -927;-12059 -791;-12059 -616 1;-12059 -441;-12004 -303;-11893 -200 1;-11777 -101;-11642 -52;-11490 -52 18;-17051 -1307 1;-16800 -1307;-16574 -1349;-16373 -1434 1;-16168 -1521;-15993 -1639;-15848 -1794 1;-15703 -1951;-15593 -2139;-15517 -2355 1;-15440 -2579;-15401 -2820;-15401 -3085 1;-15401 -3350;-15442 -3591;-15523 -3808 1;-15603 -4027;-15716 -4211;-15861 -4365 1;-16006 -4518;-16182 -4638;-16386 -4723 1;-16586 -4808;-16808 -4851;-17051 -4851 1;-17291 -4851;-17512 -4808;-17717 -4723 1;-17917 -4638;-18092 -4518;-18242 -4365 1;-18387 -4211;-18502 -4027;-18587 -3808 1;-18669 -3591;-18708 -3350;-18708 -3085 1;-18708 -2820;-18669 -2579;-18587 -2355 1;-18505 -2139;-18392 -1951;-18247 -1794 1;-18102 -1639;-17931 -1521;-17729 -1434 1;-17525 -1349;-17299 -1307;-17051 -1307 18;-7064 -1395;-6112 -1395;-6112 -3993 1;-6052 -4004;-5983 -4008;-5907 -4013 1;-5830 -4021;-5759 -4027;-5690 -4027 1;-5468 -4027;-5317 -3961;-5235 -3833 1;-5155 -3707;-5113 -3488;-5113 -3182;-5113 -1395;-4160 -1395;-4160 -3296 1;-4160 -3527;-4183 -3737;-4230 -3923 1;-4277 -4111;-4357 -4273;-4467 -4410 1;-4579 -4547;-4730 -4652;-4921 -4723 1;-5110 -4800;-5347 -4838;-5631 -4838 1;-5913 -4838;-6181 -4818;-6438 -4774 1;-6694 -4736;-6903 -4693;-7064 -4646;-7064 -1395 18;-11099 -1644 1;-11068 -1689;-11067 -1748;-11095 -1794 1;-11120 -1834;-11128 -1842;-11120 -1827 1;-11143 -1872;-11189 -1899;-11242 -1899 1;-11246 -1897;-11251 -1895;-11258 -1894 1;-11298 -1887;-11332 -1862;-11353 -1827 1;-11358 -1822;-11364 -1814;-11375 -1799 1;-11394 -1761;-11396 -1718;-11379 -1679 1;-11362 -1639;-11328 -1611;-11287 -1602 1;-11257 -1596;-11240 -1594;-11242 -1594 1;-11188 -1581;-11133 -1601;-11099 -1644 18;-9182 -1307 1;-9063 -1307;-8944 -1314;-8824 -1326 1;-8706 -1339;-8594 -1356;-8492 -1378 1;-8385 -1395;-8289 -1416;-8204 -1440 1;-8119 -1468;-8051 -1493;-7999 -1517;-8127 -2292 1;-8234 -2246;-8372 -2205;-8542 -2171 1;-8708 -2141;-8881 -2126;-9055 -2126 1;-9327 -2126;-9545 -2184;-9707 -2299 1;-9870 -2413;-9961 -2569;-9982 -2766;-7819 -2766 1;-7816 -2817;-7810 -2873;-7808 -2939 1;-7803 -3007;-7801 -3069;-7801 -3123 1;-7801 -3692;-7939 -4120;-8217 -4410 1;-8494 -4705;-8872 -4851;-9349 -4851 1;-9553 -4851;-9754 -4813;-9950 -4736 1;-10141 -4660;-10314 -4547;-10462 -4396 1;-10611 -4248;-10733 -4060;-10827 -3833 1;-10917 -3612;-10962 -3352;-10962 -3054 1;-10962 -2807;-10926 -2575;-10858 -2362 1;-10787 -2149;-10678 -1964;-10533 -1806 1;-10383 -1652;-10198 -1530;-9976 -1440 1;-9750 -1352;-9485 -1307;-9182 -1307 18;16428 5964 1;15493 5816;15156 5633;14905 5136 1;14842 5010;14767 4893;14739 4875 1;14686 4843;13829 4769;13588 4777 1;13511 4779;12673 4786;11726 4791;10003 4801;9412 4491;8810 4801;8189 4815 1;7703 4826;7501 4814;7261 4759 1;6957 4690;6950 4690;6521 4785 1;6170 4863;5974 4881;5488 4880;5457 4880 1;4811 4880;4563 4831;4074 4600 1;3912 4524;3745 4461;3703 4461 1;3660 4461;3540 4504;3437 4556 1;2881 4838;1858 4973;1148 4859 1;1005 4835;741 4765;560 4703 1;206 4579;201 4579;-280 4737 1;-640 4854;-1173 4915;-1564 4883 1;-1876 4858;-2085 4806;-2522 4644 1;-2687 4583;-2700 4585;-2962 4691 1;-3221 4796;-3260 4801;-3912 4799 1;-4286 4797;-4700 4791;-4832 4785 1;-4964 4779;-5368 4786;-5729 4802 1;-6286 4827;-6433 4820;-6697 4760 1;-7003 4690;-7012 4690;-7420 4779 1;-8263 4964;-9117 4926;-9699 4679 1;-10091 4513;-10177 4511;-10482 4666;-10748 4801;-12238 4801;-12541 4645;-12844 4489;-13138 4645;-13432 4801;-14922 4801;-15208 4663 1;-15529 4507;-15467 4502;-16152 4744 1;-16831 4984;-17621 4945;-18238 4642 1;-19010 4261;-19482 3685;-19691 2865 1;-19908 2013;-19801 1156;-19391 468 1;-19227 194;-19067 14;-18762 -238 1;-18460 -488;-18462 -552;-18772 -793 1;-19030 -993;-19347 -1393;-19507 -1720 1;-19700 -2113;-19789 -2552;-19789 -3099 1;-19788 -3688;-19727 -3966;-19489 -4450 1;-19192 -5057;-18572 -5590;-17912 -5806 1;-17374 -5984;-16546 -5988;-15770 -5633;-15447 -5485;-15239 -5576 1;-14740 -5794;-14092 -5908;-13352 -5909 1;-12569 -5909;-12118 -5779;-11532 -5381 1;-11433 -5314;-11315 -5259;-11270 -5259 1;-11225 -5259;-11047 -5352;-10875 -5465 1;-10015 -6029;-9078 -6079;-8023 -5616;-7813 -5524;-7443 -5648 1;-6638 -5917;-5482 -5996;-4840 -5824 1;-4236 -5663;-3768 -5339;-3467 -4873 1;-3148 -4378;-3090 -4073;-3064 -2779 1;-3052 -2196;-3026 -1687;-3006 -1649 1;-2959 -1560;-2787 -1559;-2312 -1644 1;-2114 -1679;-1835 -1709;-1691 -1709 1;-1229 -1712;-888 -1486;-654 -1024 1;-541 -799;-528 -788;-245 -681 1;187 -519;243 -518;668 -662 1;965 -763;1135 -795;1448 -809 1;2091 -837;2566 -704;3131 -337 1;3264 -250;3400 -179;3432 -179 1;3464 -179;3619 -259;3777 -358 1;4137 -581;4287 -644;4688 -741 1;5298 -888;5896 -821;6519 -534;6870 -372;7177 -478 1;7625 -633;8076 -733;8550 -781 1;9020 -829;9105 -876;9182 -1133 1;9308 -1554;9694 -1943;10095 -2129 1;10456 -2299;11045 -2279;11428 -2095 1;11880 -1878;12170 -1593;12329 -1108 1;12441 -766;12455 -756;12815 -779 1;14010 -855;14639 -730;15238 -297 1;15327 -232;15435 -179;15479 -179 1;15522 -179;15686 -258;15843 -355 1;16603 -824;17454 -922;18651 -679 1;19204 -567;19417 -469;19628 -230 1;19900 79;19908 145;19908 1951;19908 2068 1;19908 3651;19886 3934;19718 4401 1;19503 5000;19140 5402;18544 5703 1;18007 5974;17155 6079;16428 5964 18;17928 5561 1;18700 5363;19232 4845;19456 4071 1;19512 3877;19525 3576;19538 2141 1;19550 942;19541 396;19509 289 1;19438 50;19214 -170;18973 -238 1;18265 -438;17233 -498;16751 -367 1;16348 -257;15993 -62;15706 208;15444 456;15306 271 1;15116 17;14791 -194;14388 -327 1;14080 -428;13997 -439;13508 -436 1;13048 -433;12442 -364;12083 -274 1;12000 -253;11998 -259;12035 -436 1;12080 -647;12030 -945;11908 -1199 1;11636 -1762;10897 -2045;10292 -1817 1;9806 -1635;9468 -1148;9468 -632;9468 -407;9216 -431 1;8866 -464;8256 -399;7783 -280 1;7323 -163;7201 -108;7046 57;6934 175;6772 37 1;5907 -704;4382 -591;3607 271;3424 474;3360 377 1;3119 21;2522 -336;2023 -420 1;1440 -518;756 -394;341 -114;129 29;28 -91 1;-139 -289;-354 -379;-661 -379;-932 -379;-932 -509 1;-933 -750;-1017 -955;-1187 -1124 1;-1437 -1374;-1550 -1391;-2246 -1283 1;-2569 -1232;-2887 -1163;-2953 -1129 1;-3248 -975;-3372 -718;-3372 -258;-3372 65;-3550 -58 1;-3801 -230;-4065 -337;-4405 -402 1;-4781 -475;-5094 -474;-5671 -398 1;-6502 -289;-6766 -202;-6943 24;-7040 149;-7232 3 1;-7499 -201;-7690 -292;-8042 -381 1;-8755 -564;-9576 -395;-10120 47 1;-10278 175;-10291 178;-10328 108 1;-10358 53;-10348 -23;-10289 -176 1;-10246 -291;-10206 -465;-10202 -563 1;-10196 -705;-10182 -736;-10133 -716 1;-9610 -509;-8452 -550;-7836 -799 1;-7626 -884;-7587 -889;-7532 -839 1;-7372 -694;-7184 -659;-6575 -659;-6548 -659 1;-5984 -659;-5837 -684;-5685 -810 1;-5627 -859;-5602 -854;-5492 -772 1;-5374 -686;-5322 -678;-4739 -666 1;-4055 -652;-3888 -680;-3692 -845 1;-3432 -1064;-3432 -1066;-3434 -2479 1;-3436 -3540;-3448 -3805;-3503 -4029 1;-3693 -4791;-4168 -5277;-4922 -5483 1;-5261 -5575;-6112 -5566;-6698 -5463 1;-7272 -5363;-7461 -5289;-7610 -5109;-7726 -4968;-7879 -5088 1;-8814 -5825;-10355 -5685;-11073 -4798;-11235 -4596;-11494 -4860 1;-11783 -5155;-12156 -5370;-12572 -5482 1;-13108 -5626;-14450 -5523;-14960 -5299 1;-15058 -5256;-15175 -5160;-15239 -5069;-15350 -4913;-15611 -5103 1;-16440 -5704;-17556 -5733;-18389 -5176 1;-18763 -4926;-18970 -4689;-19174 -4279 1;-19544 -3536;-19541 -2623;-19167 -1859 1;-18698 -903;-17669 -417;-16549 -623 1;-16267 -675;-15788 -888;-15574 -1057;-15421 -1179;-15405 -589 1;-15394 -169;-15373 26;-15332 89 1;-15235 239;-15319 237;-15504 86 1;-16274 -546;-17402 -634;-18285 -132 1;-19617 624;-19853 2734;-18732 3863 1;-17912 4689;-16505 4772;-15566 4050;-15379 3907;-15333 4019 1;-15270 4170;-15105 4323;-14923 4399 1;-14712 4487;-13672 4487;-13461 4399 1;-13264 4317;-13137 4195;-13048 4001 1;-12982 3860;-12974 3728;-12973 2871;-12973 2823 1;-12972 1843;-12978 1874;-12782 1926;-12692 1950 1;-12692 3973;-12680 4038;-12439 4250 1;-12222 4440;-12067 4471;-11387 4455 1;-10804 4441;-10780 4438;-10624 4329 1;-10492 4238;-10413 4134;-10306 3908 1;-10303 3901;-10213 3963;-10106 4044 1;-9632 4408;-9047 4566;-8312 4528 1;-7825 4503;-7365 4416;-7096 4297 1;-6933 4226;-6914 4225;-6848 4284 1;-6702 4417;-6483 4457;-5908 4459 1;-5355 4461;-5138 4426;-5010 4317 1;-4958 4272;-4918 4279;-4753 4362 1;-4571 4454;-4518 4461;-3955 4460 1;-3267 4459;-3106 4414;-2910 4168;-2797 4027;-2623 4147 1;-2311 4362;-2052 4456;-1635 4505 1;-1021 4577;-348 4454;37 4200;186 4101;437 4238 1;800 4435;1069 4503;1588 4529 1;1962 4547;2134 4535;2508 4464 1;3072 4358;3355 4230;3495 4017 1;3552 3931;3607 3861;3617 3861 1;3627 3861;3710 3925;3802 4004 1;4053 4219;4484 4414;4868 4487 1;5440 4594;6391 4505;6861 4299;7033 4223;7198 4332 1;7357 4438;7382 4441;8005 4453 1;8611 4465;8658 4460;8826 4373 1;8923 4323;9058 4212;9126 4127;9248 3974;9260 2937;9272 1901;9360 1902 1;9541 1903;9543 1914;9556 2969;9568 3974;9690 4127 1;9758 4212;9892 4322;9989 4371 1;10149 4453;10221 4461;10767 4460 1;11260 4459;11397 4446;11528 4387 1;11682 4317;11694 4317;11848 4387 1;11979 4446;12117 4459;12611 4460 1;13162 4461;13230 4453;13396 4369;13577 4278;13732 4359 1;13871 4432;13961 4441;14551 4441;15214 4441;15197 4626 1;15173 4869;15240 5059;15408 5233 1;15568 5397;15799 5483;16366 5586 1;16835 5672;17538 5661;17928 5561;17928 5561 18;-13184 -2139 1;-13282 -2139;-13381 -2152;-13479 -2177 1;-13572 -2202;-13648 -2234;-13708 -2274;-13708 -3993 1;-13662 -4004;-13602 -4008;-13530 -4013 1;-13457 -4021;-13379 -4027;-13293 -4027 1;-13032 -4027;-12837 -3936;-12704 -3756 1;-12572 -3582;-12506 -3348;-12506 -3054 1;-12506 -2443;-12733 -2139;-13184 -2139 18;-13248 -2631 1;-13034 -2631;-12976 -2876;-12976 -3090 1;-12976 -3284;-13054 -3505;-13248 -3506;-13248 -2631 18;17360 2730 1;16930 2730;16714 2463;16714 1930 1;16714 1675;16776 1464;16900 1297 1;17024 1130;17212 1047;17464 1047 1;17548 1047;17623 1052;17687 1060 1;17750 1065;17807 1072;17854 1080;17854 2603 1;17794 2636;17720 2666;17636 2691 1;17550 2718;17459 2730;17360 2730 18;17409 2232;17409 1571 1;17201 1492;17180 1713;17180 1926 1;17178 2120;17192 2338;17409 2232 18; 61856 99734 32;63500 98232 32;62139 96743 32;60495 98245 32;61856 99734 50; 54869 90874 32;63157 91162 32;63200 89924 32;66484 90038 32;66319 94805 32;58567 94536 32;58549 95067 32;54728 94934 32;54869 90874 50; 68204 75140 32;70579 75252 32;70681 73095 32;68306 72983 32;68204 75140 50; 64279 74690 32;64704 71464 32;66066 71643 32;65641 74869 32;64279 74690 50; 63586 65647 32;69726 66691 32;70222 63775 32;64082 62730 32;63586 65647 50; 70380 63085 32;72307 63386 32;72526 61980 32;70599 61679 32;70380 63085 50; 75286 57526 32;72411 57214 32;72366 57631 32;69314 57299 32;69333 57127 32;68714 57060 32;68643 57715 32;68836 57736 32;68661 59346 32;69687 59457 32;69649 59809 32;71999 60064 32;72037 59718 32;73324 59858 32;73296 60115 32;73858 60176 32;73895 59837 32;75021 59959 32;75286 57526 50; 71263 54445 32;71413 53007 32;70539 52916 32;70389 54354 32;71263 54445 50; 63012 54794 32;64362 45931 32;75005 47552 32;74599 50216 32;66676 49009 32;65732 55208 32;63012 54794 50; 76768 42623 32;77093 39347 32;74878 39127 32;74653 41397 32;72694 41203 32;72538 42776 32;75635 43088 32;75692 42516 32;76768 42623 50; 61912 37890 32;65430 38209 32;65471 37757 32;67532 37943 32;67293 40582 32;65189 40392 32;65124 41106 32;61650 40792 32;61912 37890 50; 71456 31230 32;76607 32518 32;77194 30168 32;72043 28880 32;71456 31230 50; 68419 35393 32;69069 31993 32;71512 32460 32;70850 35924 32;73371 36406 32;73072 37972 32;69565 37302 32;69877 35672 32;68419 35393 50; 64007 28914 32;67045 29751 32;67763 27146 32;64725 26308 32;64007 28914 50; 63907 25638 32;65482 26064 32;65900 24515 32;64325 24090 32;63907 25638 50; 61782 25613 32;63382 26276 32;64028 24716 32;62428 24053 32;61782 25613 50; 60019 21075 32;63119 22400 32;63771 20874 32;65074 21431 32;64025 23886 32;62216 23678;59417 22482 32;60019 21075 50; 71588 22291;68939 19601;69439 19142;69524 18831;68197 18258 32;67751 19292 32;65464 18305 32;64918 19569 32;63421 18923 32;64882 15539 32;70062 17775 32;70189 17853;73032 20935;71588 22291 18; 76923 18984 32;74908 18809 32;75055 17111;72954 16414 32;73427 14991 32;74813 15451 32;74629 16004 32;75136 16172;75156 15952 32;75409 15966 32;75573 14073 32;77092 14205 32;76824 17292 32;77068 17313 32;76923 18984 50; 71199 11651 32;74237 12664 32;75100 10076 32;72062 9063 32;71199 11651 50; 69561 4526 32;70361 4763 32;70524 4214 32;69724 3977 32;69561 4526 50; 78862 8076 32;75037 7864 32;75047 7691;73124 7126 32;73424 6103;71286 5476 32;71474 4837 32;70875 4661 32;71098 3901 32;73405 4578 32;73228 5182 32;75181 5715 32;75106 5968 32;75354 6041;78964 6241 32;78862 8076 50; 74401 4772;74464 2809;73438 2609;73401 4522;74401 4772 18; 75964 2046 32;79177 2209 32;79287 35 32;76074 -128 32;75964 2046 50; 73714 -417 32;70851 -1017 32;70669 -150 32;68902 -520 32;69084 -1387 32;66280 -1975 32;66098 -1108 32;65190 -1298 32;64948 -141 32;65859 50 32;65739 625 32;68546 1213 32;68665 646 32;70431 1016 32;70311 1587 32;73218 2196 32;73335 1637 32;74023 1781 32;74270 601 32;73533 447 32;73714 -417 50; 75308 -1430 32;75368 -4193 32;76143 -4176 32;76165 -5187 32;74287 -5228 32;74259 -3929 32;73944 -3936 32;73897 -1773 32;75308 -1430 50; 70145 -6834 32;79709 -6445 32;79833 -9490 32;70269 -9879 32;70145 -6834 50; 77916 -13426 32;77368 -21576 32;74409 -21377 32;74957 -13227 32;77916 -13426 50; 74045 -26455 32;64110 -25730 32;64327 -22757 32;74262 -23482 32;74045 -26455 50; 60239 -20728 32;59973 -24653 32;58782 -24573 32;59047 -20647 32;60239 -20728 50; 56809 -20516 32;56473 -25254 32;55010 -25150 32;55103 -23841 32;55374 -23860 32;55617 -20431 32;56809 -20516 50; 55536 -17617 32;67275 -18395 32;67468 -15485 32;55729 -14707 32;55536 -17617 50; 56934 -11039 32;61409 -9876 32;60903 -7933 32;58183 -8639 32;58084 -8257 32;57581 -8388 32;57680 -8770 32;56428 -9095 32;56934 -11039 50; 59400 -5670 32;60700 -5320 32;60859 -5911 32;59559 -6261 32;59400 -5670 50; 63501 -3481 32;65539 -2906 32;65796 -3816 32;63758 -4391 32;63501 -3481 50; 59713 -2006 32;62438 -1256 32;63047 -3471 32;60003 -4309 32;59833 -3690 32;60152 -3602 32;59713 -2006 50; 64251 1801 32;62026 1163 32;61814 1905 32;62157 2003 32;61822 3171 32;63704 3710 32;64251 1801 50; 70239 7052 32;63288 4927 32;62845 6377 32;64465 6872 32;64274 7496 32;69605 9126 32;70239 7052 50; 63401 4601 32;66063 5427 32;66320 4598 32;65010 4192 32;64931 4448 32;63578 4029 32;63401 4601 50; 59235 13231 32;60331 13700 32;59962 14562 32;58866 14094 32;59235 13231 50; 51251 -59310 32;51319 -62389 32;47991 -62463 32;47923 -59384 32;51251 -59310 50; 51909 -51306 32;52007 -56722 32;49887 -56760 32;49876 -56136 32;49622 -56141 32;49608 -55374 32;49867 -55369 32;49794 -51344 32;51909 -51306 50; 50967 -48176 32;52912 -48127 32;52946 -49463 32;51001 -49512 32;50967 -48176 50; 51417 -42485 32;51466 -46836 32;52072 -46829 32;52077 -47305 32;53001 -47295 32;52996 -46826 32;54155 -46813 32;54136 -45115 32;53597 -45121 32;53567 -42461 32;51417 -42485 50; 54777 -35256 32;54874 -39127 32;53794 -39154 32;53837 -40855 32;53518 -40863 32;53558 -42436 32;51653 -42484 32;51573 -39314 32;52067 -39302 32;51968 -35326 32;54777 -35256 50; 62406 -35931;62386 -37163;62118 -37426;62115 -37717;62405 -38013;62394 -38967;61415 -38977 32;61403 -37703 32;60350 -37713 32;60337 -36363 32;56054 -36404 32;56077 -38810 32;60363 -38768 32;60372 -39653 32;61581 -39641 32;61592 -40801 32;62949 -40788 32;62934 -38914;65247 -38941;65258 -37981;65516 -37729;65520 -37362;65268 -37104;65280 -35960;62406 -35931 18; 43003 -56988 32;42713 -60596 32;41283 -60481 32;41421 -58772 32;40482 -58696 32;40426 -59390 32;37774 -59177 32;37937 -57149 32;40339 -57342 32;40407 -56500 32;41227 -56566 32;41442 -56862 32;43003 -56988 50; 32729 -58785 32;34850 -58941 32;34783 -59853 32;35721 -59922 32;35732 -59766 32;37027 -59861 32;37158 -58080 32;35964 -57992 32;35931 -58439 32;34901 -58363 32;35010 -56888 32;33997 -56813 32;33964 -57264 32;32424 -57150 32;32326 -58475 32;31896 -58443 32;31848 -59094 32;32702 -59157 32;32729 -58785 50; 33591 -50014 32;41804 -50180 32;41811 -49847 32;43651 -49884 32;43709 -47017 32;41735 -46977 32;41729 -47263 32;37168 -47171 32;37268 -42219 32;35946 -42192 32;35915 -43722 32;35472 -43713 32;35404 -47097 32;33651 -47062 32;33591 -50014 50; 30666 -49189 32;30617 -50264 32;31935 -50324 32;31874 -51675 32;30560 -51615 32;30522 -52445 32;29058 -52378 32;29040 -52768 32;28442 -52741 32;28478 -51958 32;27848 -51929 32;27864 -51579 32;26419 -51513 32;26471 -50373 32;26143 -50358 32;25734 -49664 32;26491 -49697 32;26556 -48274 32;27320 -48309 32;27304 -48669 32;28527 -48725 32;28498 -49352 32;29881 -49415 32;29893 -49154 32;30666 -49189 50; 25504 -47722 32;26149 -48915 32;25011 -49530 32;24366 -48338 32;25504 -47722 50; 15048 -52091 32;14965 -55713 32;14730 -55708 32;14689 -57477 32;15682 -57500 32;15688 -57246 32;17533 -57288 32;17572 -55602 32;17867 -55609 32;17947 -52119 32;18445 -52130 32;18485 -50390 32;19027 -50402 32;19065 -48760 32;18449 -48746 32;18456 -48455 32;16214 -48404 32;16168 -50419 32;15631 -50407 32;15592 -52103 32;15048 -52091 50; 11157 -47883 32;4811 -47675 32;4769 -48975 32;11115 -49182 32;11157 -47883 50; 4719 -50638 32;4677 -51938 32;6647 -52002;6557 -55527 32;6837 -55534 32;6788 -57466 32;8623 -57513 32;8630 -57244 32;9905 -57277 32;9944 -55737 32;9517 -55726 32;9609 -52099;10954 -52143 32;10996 -50844 32;4719 -50638 50; 1498 -51399 32;1332 -54994 32;1801 -55016 32;1718 -56817 32;835 -56776 32;806 -57404 32;-27 -57366 32;1 -56756 32;-2187 -56655 32;-2099 -54759 32;-2679 -54732 32;-2663 -54381 32;-2401 -54393 32;-2343 -53131 32;-1476 -53171 32;-1394 -51402 32;-937 -51423 32;-850 -49531 32;-1270 -49512 32;-1192 -47826 32;2588 -48000 32;2512 -49647 32;2039 -49625 32;1956 -51420 32;1498 -51399 50; -6884 -50669 32;-7021 -54257 32;-6485 -54277 32;-6554 -56092 32;-8165 -56031 32;-8158 -55842 32;-10144 -55766 32;-10082 -54149 32;-10821 -54121 32;-10750 -52260 32;-10916 -52254 32;-10851 -50558 32;-9231 -50620 32;-9157 -48685 32;-7247 -48758 32;-7256 -48990 32;-6330 -49025 32;-6393 -50688 32;-6884 -50669 50; -12206 -53469 32;-12245 -54476 32;-12638 -54461 32;-12599 -53454 32;-12206 -53469 50; -21216 -54315 32;-16474 -54296 32;-16472 -54794 32;-14987 -54788 32;-14998 -52066 32;-16631 -52073 32;-16633 -51558 32;-21227 -51577 32;-21216 -54315 50; -18511 -50189 32;-17699 -50189 32;-17699 -49866 32;-18511 -49866 32;-18511 -50189 50; -23641 -49534 32;-23612 -51499 32;-22603 -51484 32;-22613 -50815 32;-22221 -50809 32;-22240 -49513 32;-23641 -49534 50; -29262 -51167 32;-26202 -51196 32;-26206 -51587 32;-24569 -51603 32;-24595 -54334 32;-26114 -54319 32;-26110 -53945 32;-29320 -53914 32;-29324 -54296 32;-30933 -54281 32;-30937 -54746 32;-32411 -54732 32;-32397 -53278 32;-32937 -53263 32;-32918 -51318 32;-33495 -51312 32;-33483 -50013 32;-32829 -50019 32;-32841 -51264 32;-32292 -51269 32;-32299 -51997 32;-30826 -52011 32;-30822 -51586 32;-29266 -51601 32;-29262 -51167 50; -29686 -49599 32;-28874 -49599 32;-28874 -49276 32;-29686 -49276 32;-29686 -49599 50; -26374 -48998; -27300 -45487 32;-26809 -46006 32;-27211 -46387 32;-27702 -45868 32;-27300 -45487 50; -34488 -50010 32;-34488 -51721 32;-34781 -51721 32;-34781 -51320 32;-35211 -51320 32;-35211 -50010 32;-34488 -50010 50; -43316 -54752 32;-43267 -51965 32;-41881 -51989 32;-41887 -52345 32;-40132 -52376 32;-40125 -51984 32;-38567 -52011 32;-38560 -51613 32;-37012 -51640 32;-37006 -51300 32;-35642 -51324 32;-35654 -51996 32;-35427 -52000 32;-35475 -54737 32;-37001 -54710 32;-36995 -54361 32;-38682 -54331 32;-38689 -54743 32;-40276 -54715 32;-40283 -55124 32;-41853 -55096 32;-41847 -54778 32;-43316 -54752 50; -39032 -50290 32;-38220 -50290 32;-38220 -49967 32;-39032 -49967 32;-39032 -50290 50; -42926 -43887 32;-42955 -47133 32;-43131 -47131 32;-43145 -48697 32;-43551 -48693 32;-43558 -49421 32;-41851 -49436 32;-41843 -48579 32;-40473 -48591 32;-40458 -46957 32;-40208 -46959 32;-40181 -43945 32;-40717 -43940 32;-40703 -42344 32;-40999 -42341 32;-40987 -40964 32;-47598 -40904 32;-47610 -42223 32;-43434 -42261 32;-43449 -43882 32;-42926 -43887 50; -46161 -43437 32;-46161 -44268 32;-46503 -44268 32;-46503 -43437 32;-46161 -43437 50; -47217 -47660; -47671 -37842 32;-41714 -37851 32;-41716 -39191 32;-47673 -39182 32;-47671 -37842 50; -37699 -40257 32;-31201 -40288 32;-31203 -40813 32;-28892 -40824 32;-28894 -41324 32;-28104 -41328 32;-28110 -42528 32;-28920 -42524 32;-28925 -43537 32;-31077 -43527 32;-31075 -43095 32;-35626 -43074 32;-35622 -42265 32;-37708 -42255 32;-37699 -40257 50; -24270 -37857 32;-21504 -37857 32;-21504 -40144 32;-22139 -40144 32;-22139 -42403 32;-22755 -42403 32;-22755 -44671 32;-24231 -44671 32;-24231 -45385 32;-25502 -45385 32;-25502 -42481 32;-24857 -42481 32;-24857 -40193 32;-24270 -40193 32;-24270 -37857 50; -48913 -28668;-41461 -28654;-41466 -25986;-46521 -25995;-47199 -23235;-50061 -23938;-48913 -28668 18; -34395 -31907 32;-34423 -35834 32;-35722 -35825 32;-35728 -36718 32;-37134 -36708 32;-37118 -34367 32;-36746 -34370 32;-36733 -32472 32;-35737 -32479 32;-35733 -31898 32;-34395 -31907 50; -29644 -36196 32;-23983 -36206 32;-23979 -33606 32;-26007 -33602 32;-26009 -34798 32;-27356 -34796 32;-27355 -34153 32;-29640 -34149 32;-29644 -36196 50; -23983 -37438 32;-22018 -37418 32;-22024 -36774 32;-22213 -36776 32;-22229 -35169 32;-23221 -35179 32;-23563 -35789 32;-23553 -36783 32;-23989 -36787 32;-23983 -37438 50; -33113 -29429 32;-28457 -29451 32;-28465 -31062 32;-26257 -31071 32;-26261 -32492 32;-28453 -32483 32;-28454 -31994 32;-33124 -31972 32;-33113 -29429 50; -14291 -20714 32;-19255 -20507 32;-19281 -21129 32;-23721 -20943 32;-23707 -20602 32;-24297 -20577 32;-24353 -21908 32;-24957 -21883 32;-25015 -23271 32;-19497 -23501 32;-19474 -22953 32;-14393 -23165 32;-14291 -20714 50; -14129 -15640 32;-21789 -15225 32;-21815 -15710 32;-26865 -15437 32;-26904 -16151 32;-32349 -15856 32;-32246 -13950 32;-33032 -13907 32;-32961 -12602 32;-32171 -12645 32;-32218 -13505 32;-26674 -13805 32;-26638 -13149 32;-21525 -13426 32;-21493 -12837 32;-13999 -13243 32;-14129 -15640 50; -40106 2232 32;-40090 -4398 32;-41348 -4401 32;-41364 2229 32;-40106 2232 50; -34919 1617 32;-34919 -4356 32;-37518 -4356 32;-37518 1617 32;-34919 1617 50; -42159 9067 32;-34430 9067 32;-34430 5984 32;-42200 5984 32;-42200 5652 32;-43334 5652 32;-43334 6377 32;-45505 6377;-45505 5984;-47122 5984 32;-47122 8362 32;-42159 8362 32;-42159 9067 50; -25909 13095 32;-25909 1951 32;-29376 1951 32;-29376 3209 32;-28754 3209 32;-28754 13095 32;-25909 13095 50; -17319 13127 32;-17306 2040 32;-20140 2037 32;-20154 13124 32;-17319 13127 50; -8696 13160 32;-8683 2075 32;-11517 2072 32;-11531 13157 32;-8696 13160 50; -54 13233 32;-41 2466 32;-2875 2463 32;-2889 13230 32;-54 13233 50; -46315 56035 32;-45765 51235 32;-51193 50604 32;-51760 55404 32;-46315 56035 50; -35856 65590 32;-35299 60791 32;-40738 60159 32;-41322 64957 32;-35856 65590 50; -69777 80432 32;-54506 82485 32;-53319 73658 32;-56328 73252;-56227 72562;-57631 72373;-57701 73026;-68242 71610;-68155 70959;-69164 70823 32;-69088 70256 32;-72172 69841 32;-73959 83131 32;-73070 83251;-72996 82722;-71638 82904 32;-71597 82600 32;-70096 82802 32;-69777 80432 50; -50694 74014 32;-49414 74190 32;-49501 74821 32;-50781 74645 32;-50694 74014 50; -43501 80881 32;-43105 77503 32;-43395 77469 32;-43323 76852;-43031 76884;-42932 76080;-45033 75836;-45074 76158;-46198 76036;-46167 75705;-48388 75448 32;-48937 80251 32;-43501 80881 50; -52033 84000 32;-44410 85003 32;-44238 83696 32;-51861 82693 32;-52033 84000 50; -51240 85832 32;-44645 86676 32;-44813 87992 32;-51408 87149 32;-51240 85832 50; -30841 88944 32;-31145 92982 32;-31365 92965 32;-31652 96771 32;-28648 96997 32;-28506 95118 32;-28175 95143 32;-28128 94522 32;-28474 94496 32;-28368 93095 32;-28050 93119 32;-27924 91448 32;-27605 91472 32;-27559 90856 32;-27879 90832 32;-27754 89177 32;-30841 88944 50; -23486 93024 32;-23223 89138 32;-20135 89347 32;-20398 93233 32;-23486 93024 50; -26154 97876 32;-21757 98153 32;-21570 95173 32;-23235 95068 32;-23215 94750 32;-24213 94687 32;-24232 94991 32;-25966 94882 32;-26154 97876 50; -20034 97907 32;-19799 96027 32;-18750 96158 32;-18985 98038 32;-20034 97907 50; -13987 97476 1;-14309 97449;-14564 97216;-14634 96917;-17097 96722 32;-16793 92830 32;-13959 93052 32;-13941 92816 32;-13317 92865 32;-13336 93104 32;-10523 93324 32;-10826 97211 32;-13255 97022 1;-13367 97313;-13661 97504;-13987 97476 18; -836 91538 32;-4047 91196 32;-3961 90411 32;-6778 90108 32;-7080 92956 32;-4350 93246 32;-4435 94018 32;-1137 94373 32;-836 91538 50; -1449 98595 32;-1338 97247 32;-811 97290 32;-922 98638 32;-1449 98595 50; 1286 94347 32;4649 94523 32;4785 91926 32;4463 91909 32;4537 90492 32;3162 90420 32;3087 91856 32;1421 91769 32;1286 94347 50; 1636 100765 32;10000 101470 32;9978 101731 32;14356 102100 32;14429 101238 32;17309 101481 32;17429 100063 32;17229 100046 32;17407 97938 32;14918 97728 32;14829 98784 32;12739 98608 32;12832 97506 32;11811 97420 32;11716 98542 32;10346 98426 32;10370 98136 32;8983 98020 32;9071 96973 32;8034 96886 32;7946 97929 32;5469 97720 32;5556 96683 32;4481 96592 32;4393 97635 32;1917 97426 32;1636 100765 50; 14591 94335 32;14826 90659 32;12658 90521 32;12608 91300 32;11475 91228 32;11416 92156 32;11044 92570 32;11020 92944 32;11343 93366 32;11294 94129 32;12012 94175 32;11990 94520 32;13697 94629 32;13719 94279 32;14591 94335 50; 47565 108673; 10807 81344 32;10848 80376 32;11859 80419 32;11818 81387 32;10807 81344 50; -19444 85137 32;-10098 85773 32;-9868 82393 32;-19214 81757 32;-19444 85137 50; -33088 83812 32;-22774 84807 32;-22462 81578 32;-24724 81360 32;-24698 81093 32;-25288 81036 32;-25316 81325 32;-29885 80884 32;-29859 80616 32;-30440 80560 32;-30465 80824 32;-32778 80601 32;-33088 83812 50; -35454 97091;-34265 87247;-34569 87136;-37998 94823;-36297 97022;-35454 97091 18; -23558 72896 32;-22576 65568 32;-27458 64914 32;-28440 72242 32;-23558 72896 50; -27090 62286 32;-26768 59822 32;-25346 60008 32;-25668 62472 32;-27090 62286 50; -20970 60848 32;-20657 58453 32;-19415 58615 32;-19728 61010 32;-20970 60848 50; -11855 64598 32;-10856 64739 32;-10721 63788 32;-11720 63646 32;-11855 64598 50; -10341 63847 32;-9432 63985 32;-9576 64935 32;-10485 64797 32;-10341 63847 50; -11001 76228 32;-9593 66412 32;-1507 67572 32;-2915 77388 32;-11001 76228 50; -13465 62772 32;-12345 54186 32;-13995 53970;-14224 53662;-14555 53897;-15048 53833 32;-16168 62419 32;-15638 62488;-15405 62760;-15116 62556;-13465 62772 50; -5155 63847 32;-4035 55261 32;-5685 55045;-5914 54737;-6245 54972;-6738 54908 32;-7858 63494 32;-7328 63563;-7095 63835;-6806 63631;-5155 63847 50; -3193 52256 32;4646 53293 32;5001 50607 32;-2838 49570 32;-3193 52256 50; 5504 53514 32;5891 50555 32;8264 50865 32;7877 53824 32;5504 53514 50; -3285 50099 32;-3177 49346 32;-4000 49228 32;-4108 49981 32;-3285 50099 50; 6189 54714 32;6257 54167 32;5732 54101 32;5664 54648 32;6189 54714 50; 2006 40097 32;8711 43803 32;9990 41488 32;2350 37267 32;2006 40097 50; 2309 44818 32;2466 43782 32;3530 43943 32;3374 44979 32;2309 44818 50; -94 49122 32;1468 37342 32;-1164 36993 32;-2726 48773 32;-94 49122 50; 9694 39471 32;10164 38586 32;11070 39067 32;10600 39952 32;9694 39471 50; 5698 37300 32;6168 36415 32;7074 36896 32;6604 37781 32;5698 37300 50; -14708 50601 32;-6855 51651 32;-6798 51223;-6487 50987;-6721 50646;-6487 48898 32;-8135 48677;-8366 48354;-8696 48602;-14340 47847 32;-14708 50601 50; -13812 47059 32;-12360 36344 32;-9608 36717 32;-10225 41270;-10003 41611;-10305 41858;-10761 45227;-10542 45539;-10833 45760;-11060 47432 32;-13812 47059 50; -9198 39546 32;-5297 40093 32;-5241 39695;-4962 39462;-5164 39146;-4908 37322 32;-6595 37085;-6813 36782;-7122 37011;-8809 36774 32;-9198 39546 50; -14624 34546 32;-13631 34702 32;-13784 35682 32;-14777 35526 32;-14624 34546 50; -14016 29426 32;-12917 29569 32;-13048 30580 32;-14147 30437 32;-14016 29426 50; -12368 17497 32;-11172 17568 32;-11112 16550 32;-12308 16479 32;-12368 17497 50; -32743 -2599 32;-24228 -2540 32;-24218 -4037 32;-21508 -4018 32;-21518 -2525 32;-12780 -2465 32;-12779 -2679 32;-8850 -2652 32;-8858 -1420 32;-442 -1362 32;-422 -4270 32;-3541 -4291 32;-3543 -3992 32;-5772 -4007 32;-5770 -4293 32;-8847 -4315 32;-8849 -4025 32;-12771 -4052 32;-12763 -5250 32;-31947 -5382 32;-31956 -4005 32;-32733 -4010 32;-32743 -2599 50; -6941 -23626 32;-10494 -23446 32;-10378 -21143 32;-9130 -21206 32;-9150 -21605 32;-6845 -21722 32;-6941 -23626 50; -14433 -27965 32;-14671 -35372 32;-13424 -35412 32;-13186 -28005 32;-14433 -27965 50; 20202 -13306 32;-2415 -12929 32;-2409 -12585 32;-3908 -12560 32;-3914 -12903 32;-11608 -12775 32;-11654 -15564 32;21497 -16117 32;21586 -10769 32;20245 -10747 32;20202 -13306 50; 101 1035 32;92 -486 32;1537 -495 32;1546 1027 32;101 1035 50; -9811 -27565 32;-10032 -35985 32;-7050 -36063 32;-7004 -34309 32;-6577 -34320 32;-6560 -33662 32;-6980 -33651 32;-6885 -30054 32;-6453 -30065 32;-6435 -29392 32;-6867 -29381 32;-6821 -27644 32;-9811 -27565 50; -5276 -30497 32;-5415 -35654 32;-4138 -35688 32;-4000 -30531 32;-5276 -30497 50; -12626 -40671; -13646 -44401 32;-14155 -44384 32;-14209 -45979 32;-14721 -45962 32;-14783 -47803 32;-15847 -47767 32;-16027 -47899 32;-16342 -47543;-17659 -47499 32;-17607 -45971 32;-17113 -45988 32;-17057 -44322 32;-16553 -44339 32;-16480 -42159 32;-16717 -41850 32;-16700 -41358 32;-16451 -41054 32;-15360 -41091 32;-15351 -40830 32;-14468 -40860 32;-14477 -41118 32;-13536 -41150 32;-13646 -44401 50; -9389 -40413 32;-9569 -45902 32;-11093 -45852 32;-11073 -45233 32;-10855 -45240 32;-10696 -40370 32;-9389 -40413 50; -7592 -40468 32;-7786 -45957 32;-6531 -46001 32;-6517 -45608 32;-4583 -45676 32;-4601 -46192 32;-3036 -46247 32;-2981 -44689 32;-2701 -44699 32;-2655 -43407 32;-4652 -43337 32;-4633 -42794 32;-6377 -42733 32;-6299 -40514 32;-7592 -40468 50; 2336 -35864 32;-664 -35726 32;-642 -35257 32;-2333 -35180 32;-2208 -32457 32;-593 -32531 32;-613 -32967 32;2463 -33109 32;2336 -35864 50; 4389 -35934 32;7319 -36043 32;7335 -35572 32;10400 -35684 32;10504 -32896 32;7529 -32786 32;7509 -33334 32;4491 -33223 32;4389 -35934 50; 12817 -36513 32;14919 -36583 32;14938 -36010 32;16478 -36061 32;16457 -36709 32;17211 -36734 32;17232 -36085 32;17818 -36104 32;17871 -34504 32;15117 -34413 32;15099 -34964 32;12870 -34891 32;12817 -36513 50; 11697 -28715 32;11614 -30388 32;13184 -30466 32;13267 -28793 32;11697 -28715 50; 17158 -29047 32;20352 -29199 32;20273 -30861 32;18573 -30780 32;18526 -31767 32;17762 -31731 32;17810 -30726 32;17080 -30691 32;17158 -29047 50; 21498 -28763 32;21443 -29966 32;22308 -30006 32;22363 -28803 32;21498 -28763 50; 25081 -29517 32;25012 -31190 32;26607 -31256 32;26676 -29583 32;25081 -29517 50; 19454 -36761 32;22150 -36844 32;22163 -36423 32;24621 -36499 32;24687 -34348 32;23338 -34306 32;23367 -33352 32;22396 -33322 32;22356 -34635 32;19522 -34548 32;19454 -36761 50; 26823 -36983 32;28994 -37038 32;29005 -36608 32;31297 -36666 32;31340 -34970 32;29160 -34914 32;29150 -35326 32;26867 -35268 32;26823 -36983 50; 33486 -31649 32;33555 -30007 32;31903 -29938 32;31835 -31580 32;33486 -31649 50; 38294 -30288 32;40339 -30377 32;40266 -32050 32;38221 -31961 32;38294 -30288 50; 33729 -37186 32;35851 -37270 32;35873 -36731 32;38083 -36817 32;38148 -35208 32;36015 -35124 32;35998 -35611 32;33795 -35525 32;33729 -37186 50; 43768 -35344 32;43781 -35007 32;46004 -35092 32;45904 -37697 32;40648 -37495 32;40735 -35227 32;43768 -35344 50; 24999 -40165 32;31276 -40411 32;31228 -41656 32;32183 -41694 32;32124 -43163 32;31157 -43125 32;31136 -43672 32;25182 -43440 32;25200 -42952 32;21230 -42795 32;21191 -43788 32;17894 -43659 32;17908 -43321 32;14583 -43191 32;14615 -42427 32;12682 -42350 32;12736 -41018 32;14656 -41094 32;14673 -40649 32;15432 -40679 32;15448 -40288 32;17934 -40385 32;17920 -40776 32;21308 -40907 32;21286 -41428 32;25253 -41585 32;25268 -41177 32;24959 -41165 32;24999 -40165 50; 964 -21200 32;7739 -21462 32;7700 -22480 32;8470 -22510 32;8423 -23722 32;6977 -23666 32;6961 -24091 32;1557 -23881 32;1571 -23516 32;-942 -23419 32;-959 -23870 32;-4664 -23726 32;-4646 -23272 32;-5894 -23224 32;-5867 -22529 32;-6603 -22500 32;-6526 -20505 32;-6905 -20490 32;-6856 -19215 32;-5882 -19253 32;-5987 -21972 32;-4611 -22025 32;-4560 -20704 32;-859 -20848 32;-911 -22177 32;923 -22248 32;964 -21200 50; 12782 -21492 32;15736 -21611 32;15751 -21276 32;16898 -21321 32;16826 -23101 32;15677 -23055 32;15617 -24482 32;12666 -24364 32;12782 -21492 50; 17838 -21332 32;17926 -19122 32;17120 -19090 32;17032 -21300 32;17838 -21332 50; 17911 -24589 32;21147 -24765 32;21332 -21367 32;20226 -21307 32;20189 -21979 32;18059 -21863 32;17911 -24589 50; 21543 -21616 32;24388 -21772 32;24458 -20500 32;21613 -20344 32;21543 -21616 50; 23225 -23102 32;23112 -24919 32;25240 -25051 32;25290 -24247 32;24868 -24221 32;24931 -23208 32;23225 -23102 50; 28563 -22876 32;31594 -23052 32;31457 -25406 32;28426 -25230 32;28563 -22876 50; 34299 -22755 32;38483 -22990 32;38435 -23839 32;40748 -23969 32;40653 -25655 32;39222 -25575 32;39253 -25023 32;38367 -24973 32;38322 -25779 32;34164 -25546 32;34210 -24724 32;33565 -24688 32;33630 -23537 32;34253 -23572 32;34299 -22755 50; 40963 -25854 32;44434 -26030 32;44542 -23897 32;45508 -23946 32;45489 -24320 32;45952 -24343 32;45971 -23973 32;46552 -24002 32;46640 -22276 32;44627 -22174 32;44597 -22796 32;43804 -22757 32;43823 -22379 32;42011 -22287 32;41904 -24379 32;41039 -24334 32;40963 -25854 50; 45452 -18019 32;45314 -20445 32;43119 -20320 32;43139 -19965 32;42109 -19906 32;42202 -18276 32;43233 -18335 32;43247 -18090 32;44052 -18136 32;44063 -17940 32;45452 -18019 50; 38916 -20853 32;37734 -20798 32;37763 -20172 32;38945 -20227 32;38916 -20853 50; 26237 -10777 32;26069 -17909 32;23524 -17849 32;23691 -10717 32;26237 -10777 50; 30871 -10875 32;30704 -17958 32;28159 -17898 32;28325 -10815 32;30871 -10875 50; 33094 -11769 32;32945 -17682 32;34404 -17719 32;34553 -11806 32;33094 -11769 50; 33045 -17704 32;32967 -19884 32;34559 -19941 32;34584 -19252 32;35558 -19287 32;35539 -19815 32;36905 -19864 32;36920 -19449 32;38234 -19496 32;38271 -18477 32;34559 -18344 32;34580 -17759 32;33045 -17704 50; 35512 -13688 32;40967 -13610 32;40925 -10659 32;35470 -10737 32;35512 -13688 50; 49233 -10019 32;49917 -12893 32;50880 -12664 32;51156 -13824 32;48957 -14348 32;48670 -13143 32;47935 -13318 32;47261 -10488 32;49233 -10019 50; 48451 -5521 32;51971 -4397 32;52630 -6461 32;50326 -7197 32;50544 -7879 32;49328 -8267 32;48451 -5521 50; 45968 -4172 32;46457 -5570 32;45153 -6026 32;44664 -4628 32;45968 -4172 50; 47982 -6264 32;48168 -6870 32;47530 -7065 32;47344 -6459 32;47982 -6264 50; 50917 1387 32;54016 2424 32;54867 -121 32;54187 -348 32;54119 -146 32;51700 -955 32;50917 1387 50; 46758 -2955 32;48362 -2388 32;48643 -3183 32;47039 -3750 32;46758 -2955 50; 45937 -589 32;43904 -1322 32;44007 -1607;43536 -1833;43072 -866;41978 -1391;42818 -3141;44363 -2596;44553 -3123 32;46586 -2390 32;45937 -589 50; 55077 3996 32;56378 4436 32;55967 5650 32;54666 5210 32;55077 3996 50; 51261 5377 32;58050 8142 32;58162 7866 32;58879 8158 32;58763 8444 32;59728 8837 32;58664 11449 32;53556 9368 32;52362 12298 32;57513 14396 32;56879 15953 32;56985 15996 32;56637 16850 32;58215 17493 32;57536 19160 32;56469 18725 32;56338 19048 32;54698 18380 32;55322 16847 32;54409 16475 32;55013 14991 32;51017 13363 32;51455 12288 32;48874 11237 32;51261 5377 50; 43264 5095 32;40409 3697 32;39272 6018 32;42127 7416 32;43264 5095 50; 44682 8018 32;47204 9143 32;46423 10894 32;43901 9770 32;44682 8018 50; 49657 14033 32;53653 15802 32;52669 18023 32;48674 16253 32;49657 14033 50; 39076 12694 32;40220 9859 32;41164 10240 32;41485 9444 32;41096 9287 32;41647 7922 32;39790 7173 32;38861 9476 32;37763 9033 32;36677 11726 32;39076 12694 50; 46593 12763 32;43846 11599 32;44239 10670 32;43394 10312 32;42671 12020 32;43565 12398 32;43084 13534 32;45043 14363 32;45327 13693 32;46067 14006 32;46593 12763 50; 52547 23214 32;55118 24289 32;55875 22480 32;56270 22645 32;56541 21997 32;55126 21405 32;54965 21790 32;53545 21196 32;53158 22121 32;51595 21467 32;50692 23627 32;51720 24057 32;51943 23524 32;52347 23693 32;52547 23214 50; 48362 23781 32;48734 22911 32;48248 22703 32;47876 23574 32;48362 23781 50; 46661 20486 32;49046 21493 32;49429 20585 32;48871 20349 32;49244 19464 32;48313 19071 32;48449 18748 32;47617 18397 32;47418 18869 32;46966 18678 32;46673 19372 32;47062 19536 32;46661 20486 50; 41245 18756 32;44315 20057 32;44455 19727 32;45618 20220 32;45200 21206 32;45818 21468 32;46238 20476 32;45526 20175 32;46098 18825 32;45030 18373 32;45256 17839 32;42184 16538 32;41245 18756 50; 36660 17979 32;38117 14527 32;35396 13378 32;34938 14462 32;34651 14341 32;34307 15155 32;34588 15274 32;33937 16815 32;35789 17597 32;35422 18466 32;37099 19174 32;37461 18317 32;36660 17979 50; 27501 -2558 32;33171 -2500 32;33143 181 32;27473 122 32;27501 -2558 50; 28793 3765 32;28780 1111 32;27507 1118 32;27521 3772 32;28793 3765 50; 25710 4636 32;31420 4664 32;31407 7346 32;25697 7318 32;25710 4636 50; 27051 10968 32;27051 8355 32;25682 8355 32;25682 10968 32;27051 10968 50; 22267 11950 32;27991 11978 32;27978 14622 32;22254 14594 32;22267 11950 50; 4220 17026 32;4924 17105 32;4866 17630 32;4162 17552 32;4220 17026 50; -1426 26520 32;-334 17934 32;-3095 17583 32;-4187 26169 32;-1426 26520 50; -10510 20644 32;-4358 20672 32;-4345 17867 32;-5540 17862;-5836 17583;-6106 17859;-8637 17848;-8912 17564;-9259 17845;-10497 17839 32;-10510 20644 50; -11768 28781;-1894 30081;-1781 29223;-1465 28981;-1420 28643;-1682 28300;-1558 27373;-6525 26709;-6746 26420;-7044 26648;-9529 26317;-9793 25972;-10122 26224;-11397 26055;-11768 28781 18; 7989 27813 32;9316 17983 32;8323 17849;8413 17271;7733 17173;7645 17757;6704 17630 32;5377 27460 32;7989 27813 50; 10173 20776 32;17418 20721 32;17398 18079 32;10153 18134 32;10173 20776 50; 18704 18979 32;19630 18965 32;19616 18023 32;18690 18037 32;18704 18979 50; 9869 24910 32;10809 25007 32;10902 24103 32;9962 24006 32;9869 24910 50; 24982 18010 32;25666 18010 32;25666 18587 32;24982 18587 32;24982 18010 50; 14854 37640 32;17840 32124 32;15498 30856 32;12512 36372 32;14854 37640 50; 11798 35867 32;12185 35165 32;11706 34900 32;11319 35602 32;11798 35867 50; 13983 31907;8175 28788 1;7902 28645;7444 28509;7139 28465;4899 28161;4744 29304;4387 29256;4330 29677;4690 29726;4541 30820;6374 31070 1;6850 31135;7091 31220;7512 31451;12683 34230;13983 31907 18; 19071 31935 32;15857 30161 32;20276 22033 1;20728 21192;20813 20872;20813 20363;20799 18385;23536 18385;23536 21082 1;23536 21579;23463 21851;23218 22284;19453 29037 32;20386 29552 32;19071 31935 50; 42757 27526;57250 33877;58010 30780;57153 30406;56995 30003;56578 30166;54392 29209;54205 28729;53735 28912;52246 28258;52074 27817;51651 27982;49455 27018;49306 26635;48879 26801;47393 26147;47223 25710;46806 25873;44747 24993;44592 24595;44178 24756;43012 24226;42757 27526 18; 45351 39345;45890 33344;45614 33319;45667 32738;44659 32293;44673 32003;44079 32015;42870 31506;42198 39027;45351 39345 18; 36914 36801 1;35494 35978;34698 35338;33823 33950;37643 31195;39285 32559;36914 36801 18; 62294 59503 32;66144 59952 32;66300 58616 32;62450 58167 32;62294 59503 50; 50218 106994 32;49208 106949 32;49178 107623 32;50188 107662 32;50218 106994 50; 50081 105547; 50533 105847 32;50229 105008 32;49606 105233 32;49918 106077 32;50533 105847 50; 50535 105846 32;50223 105002 32;49608 105229 32;49919 106073 32;50535 105846 50; 49396 103762; 49850 104061 32;49538 103217 32;48923 103444 32;49234 104288 32;49850 104061 50; 49848 104062 32;49544 103223 32;48921 103448 32;49233 104292 32;49848 104062 50; 49163 102300 32;48859 101461 32;48236 101686 32;48548 102530 32;49163 102300 50; 49165 102299 32;48853 101455 32;48238 101682 32;48549 102526 32;49165 102299 50; 48711 102000; 51183 107564 32;50879 106725 32;50256 106950 32;50568 107794 32;51183 107564 50; 50731 107264; 51185 107563 32;50873 106719 32;50258 106946 32;50569 107790 32;51185 107563 50; 45126 105235 32;45160 104461 32;44522 104433 32;44488 105207 32;45126 105235 50; 52567 106393;52056 106623;50928 104232;51636 102026;51535 101779;51787 101662;52432 103053;52309 103413;51608 103760;51541 104047; 51509 101807;50697 102157;50478 101685 1;50379 101447;50358 101179;50593 101080 16; 56290 106654;54435 107524;52848 107381;47927 110682;47562 111920;44832 114000 1;43496 115018;43059 115828;42197 117270 1;41634 118212;41247 119085;40150 119095 1;39570 119100;39066 119041;38713 118580 1;38540 118354;38363 118154;38222 117967; 41264 112609;41163 112294;41652 112137;41753 112474;41264 112609 18; 41028 114140;41478 115417;40000 115904; 40408 116307 32;40700 117171 32;40011 117404 32;39719 116540 32;40408 116307 50; 38990 115399;39365 115712;39338 116219;38877 116302;38507 115982;38512 115527;38990 115399 18; 37915 115333;38104 115679;38509 115696; 37014 115904;38371 117896; 39175 116227;39275 116555;39574 116718;39762 116659; 38693 113189; 38143 113941; 38536 114794; 39120 114110; 41094 115996; 40625 114131; 37782 115805; 39108 117040; 39466 115774; 38295 115720;38295 116068;38803 116513;39319 116386; 40970 115196; 46673 112120;46864 112452;44832 114000 1;43496 115018;43059 115828;42197 117270 1;41634 118212;41247 119085;40150 119095 1;39570 119100;39066 119041;38713 118580 1;38540 118354;38363 118154;38222 117967;37110 115812;37323 115673;38022 115254;36561 112818;36518 112339;41181 112264;41310 112635;41737 113866;46673 112120 18;40669 117181 32;40411 116362 32;39733 116576 32;39991 117395 32;40669 117181 50;39339 116261;39367 115722;38991 115397;38486 115537;38486 116003;38890 116328;39339 116261 18; 36880 115939;34037 111163 1;33956 110827;34074 110035;34487 109523;41143 109351;41456 110488;44543 109388;45634 109404;46618 112089;41737 113866;41310 112635;41776 112481;41649 112112;41181 112264;36518 112339;36561 112818;38022 115254;36880 115939 18; 33983 111136 1;33673 110584;33528 110144;33435 109517;34467 109494;34165 110113;34014 110732;33983 111136 18; 33983 111136 1;33673 110584;33528 110144;33435 109517;34467 109494;34165 110113;34014 110732;33983 111136 18; 31942 105976 32;35283 106207 32;35252 106659 32;31918 106429 32;31942 105976 50; 31912 106426 1;31656 106408;31481 106188;31502 105932;31867 101903;32327 101945; 30679 100089; 30763 99253; 28224 106250; 23724 105454 32;27242 105889 32;27287 105522 32;23769 105087 32;23724 105454 50; 25514 105308 32;27287 105543 32;27242 105892 32;25474 105668 32;25514 105308 50; 23781 105092 32;25543 105328 32;25522 105659 32;23738 105440 32;23781 105092 50; 29698 106547 1;29715 106467;29725 106384;29728 106299 1;29758 105469;29109 104771;28279 104741 1;27706 104720;27196 105023;26926 105486 16; 26780 105827 1;26746 105943;26727 106064;26722 106190 16; 11442 73470 1;11595 74160;11724 74521;11990 75192 1;12427 76294;13019 76819;13903 77609 1;15030 78616;15596 79338;16198 80724 1;16620 81696;16768 82279;17055 83300;19244 91090;19971 91566;22745 103820;22258 103926 16; 22430 103640; 22217 102484; 22251 103914;22717 103813;22307 101787;21898 102450;22251 103914 18; 29957 103129;23814 102618; 22997 97045;22933 97568;22156 98616;22775 101346;23267 101386;23357 100285;23545 100300;23684 98561;24183 98601 16; 24175 98547;24677 98597;24544 100113;24056 100068;24175 98547 18; 23680 98548 1;23712 98227;23722 97639;23402 97596;22950 97548;22156 98616;22775 101346;23267 101386;23357 100285;23545 100300;23655 98922;23664 98771;23680 98548 18; 22981 97064;22934 97572;23489 97675;23593 97826;23640 98564;24172 98612;24299 97152;22981 97064 18; 35205 107089;35245 106652;31935 106430;31959 105970;32316 101970;34181 102160;34189 101889;34222 101491 1;33971 101530;33731 101363;33681 101112 1;33633 100876;33766 100647;33984 100566;32725 99076;30538 98893;30223 102658;23915 102130;23744 105065;23895 105103;27287 105522 32;27242 105889 32;23724 105454 32;23697 105811;31872 106835;35205 107089 18; 33969 100612 1;33737 100680;33604 100924;33672 101156 1;33740 101389;33984 101522;34216 101454 1;34449 101385;34582 101142;34514 100909 1;34445 100677;34202 100544;33969 100612 18; 33655 101038;34561 101060; 34104 101470;34104 100612; 33485 101152 1;33425 100833;33597 100572;33900 100456; 39209 98935;39055 99583;36787 96979;36423 97301;36135 96970;36203 96347;35537 95582;35155 95919;32517 92754;31765 93372;25143 92597;25031 93551;23179 94180;22930 97063;22378 97020;22644 93818;24567 93117;24681 92093;31665 92868;32688 92021;34649 94391;35095 94022;36254 95423;36596 95140;37832 96634;37519 96893;38100 97596;38339 97396;39205 98458;38983 98662;39209 98935 18; 19064 87284; 22497 95396;21453 95306;21218 94195;23261 92544;23182 93622; 21566 94251; 22660 93821;22527 95376;21465 95295;21230 94184;23273 92533;23194 93611;22660 93821 18; 20672 92630;20414 91362;19707 90924;19583 90408;20784 90677;21137 92519;20672 92630 18; 20724 91587; 20672 92630;20414 91362;19707 90924;19583 90408;20784 90677;21137 92519;20672 92630 18; 21778 91441; 18036 82201 1;17721 82167;17304 82374;17382 82681 1;17516 83204;17599 83496;17749 84015;19209 89072;22228 89824 1;22415 89870;22556 89663;22571 89471;22695 87722 16; 20398 88882; 26761 92306 1;26776 92148;26784 92070;26799 91912 1;26843 91449;27240 91074;27703 91117 1;28133 91157;28494 91629;28432 92085;28377 92496; 27868 91791; 26761 92306 1;26776 92148;26784 92070;26799 91912 1;26843 91449;27240 91074;27703 91117 1;28133 91157;28494 91629;28432 92085;28377 92496;26761 92306 18; 28456 89739 1;28831 89838;29215 89615;29314 89240 1;29414 88865;29190 88480;28815 88381 1;28440 88282;28055 88505;27956 88880 1;27857 89255;28081 89640;28456 89739 18; 28624 89054; 32458 92187;31773 91382;32136 91106 1;32561 90817;32732 90910;32949 91113 1;33237 90886;33760 90732;34088 91060;34483 91520;33174 92571; 32862 91641; 32458 92187;31842 91405;32276 91048 1;32440 90909;32793 90808;32909 91111 1;32918 91136;33001 91122;33020 91105 1;33308 90838;33882 90901;34091 91110;34451 91559;33174 92571;32458 92187 18; 35087 94031 1;35249 93892;35330 93823;35492 93684 1;35661 93539;35903 93557;36048 93726 1;36184 93884;36166 94109;36008 94245;35571 94602; 35087 94031 1;35249 93892;35330 93823;35492 93684 1;35661 93539;35903 93557;36048 93726 1;36184 93884;36166 94109;36008 94245;35571 94602;35087 94031 18; 34669 95336;32223 97319;32124 99026;30538 98893;30223 102658;23915 102130;24090 100037;24090 100037;24536 100076;24663 98632;24211 98592;24341 97144;22933 97027;22933 97027;23179 94180;25031 93551;25143 92597;31765 93372;32517 92754;34669 95336 18; 35006 95740;32822 97585;32725 99076;32124 99026;32124 99026;32222 97313;34669 95336;35006 95740 18; 32156 99063;32708 99107; 41129 109358;41084 109066;33928 109105;33725 109162 1;33520 109229;33409 109252;33435 109518;41129 109358 18; -45730 102340 1;-45095 100590;-44335 100382;-43065 100464;-33229 101315;-31707 102152;-20987 102981;-18485 104438;-7560 105479;-4626 104316;4505 105239;5369 106325;19118 107811;23563 108295;24372 108374 1;26057 108538;27132 108642;29033 108827 1;30281 108948;30912 109585;31588 110641 1;33510 113643;34321 115236;36169 118284 1;36805 119278;37205 119719;37931 120649 1;38345 121180;38663 121390;38916 122014 16; 42487 121983 1;43779 118804;45475 116661;47615 114999 1;48642 114201;49116 113746;50202 113030 1;50789 112643;51361 112834;51948 113221; 54473 111819;53870 110446;69549 103488;69308 102945 1;71358 101931;73151 99902;74558 97088;75152 96692;75688 96335 1;76312 93907;76420 92639;76669 90144;76636 88824;77079 85473 1;77113 85256;77273 85255;77461 85275 16; 32923 90694; 45150 108372 32;44261 108340 32;44231 108989 32;45119 109022 32;45150 108372 50; 44701 108688; 45152 108375 32;44270 108338 32;44233 108992 32;45124 109025 32;45152 108375 50; 23729 105836;23744 105455;23800 105058;23903 102598;23927 102106;24126 99153;24173 98590;23697 98566;23562 100296;23348 100296;23260 101399;22760 101352;22158 98628;22952 97549;23007 97081;22380 97025;22515 95406;21459 95303;21221 94184;20673 92621;20451 91383;19713 90922;19586 90407;19007 90303;19072 90478;19244 91090;19971 91566;22745 103820;22258 103926;22300 104157;22337 104315 1;22510 105064;23030 105649;23592 105788;23596 105789;23729 105836 18; 39484 106861 32;39537 105839 32;38647 105792 32;38593 106814 32;39484 106861 50; 39657 104066;39440 102541 16; 29943 103128 1;30181 103149;30354 102953;30375 102715; 39240 102577;39211 102087;39318 102115;39599 102569;39240 102577 18; 40256 102552;40225 103131;40851 103162; 40335 102584;40866 102615;40858 103100;40303 103037;40335 102584 18; 35331 104839;36496 104909;36637 102728;37629 101857; 35324 104792;36426 104854;36621 102696;37568 101727;36864 100976;35527 102165;35324 104792 18; 35370 105013 32;35298 105981 32;35703 106011 32;35775 105043 32;35370 105013 50; 36636 103559;39615 103731; 38066 103668; 38814 102120;39050 98729;39484 99247;39484 99247;39258 102148;38814 102120 18; 38842 101730;38341 101712;38157 101505;37702 101875;36886 100948;35504 102165;35251 106160;31981 105953;32310 101985;34169 102139;34222 101491 1;34470 101429;34607 101177;34557 100926 1;34509 100690;34204 100518;33984 100566;32725 99076;32725 99076;32822 97585;35006 95740;35155 95919;35537 95582;36201 96345;36147 96983;36443 97324;36797 96990;38994 99538;38842 101730 18; 37701 101964;38176 101549;38355 101728;38876 101747;38851 102102;38344 102066;38035 102338;37701 101964 18; 38905 102099;38241 102064;38001 102281;37690 101920; 39258 102474;38540 102433; 38819 102122;39269 102148; 38555 102262;38886 102283; 35148 107060;41461 107440;41616 107392;41753 107302;41797 107158;44134 107266;44441 106967;40678 106750;40875 103246;40337 103101;40141 102584;39613 102543;39841 105272;39531 105872;39510 106864;38580 106843;38631 105799;36605 105045;35757 105024;35706 106006;35303 105996;35230 106678;35148 107060 18; 36641 105091;38607 105800;39594 105830;39872 105332;39280 102145;38235 102094;37993 102277;37723 101992;36744 102759;36641 105091 18; 28456 89739 1;28831 89838;29215 89615;29314 89240 1;29414 88865;29190 88480;28815 88381 1;28440 88282;28055 88505;27956 88880 1;27857 89255;28081 89640;28456 89739 18; 41700 98861 32;41123 98177 32;40614 98607 32;41201 99292 32;41700 98861 50; 41167 98740; 41702 98859 32;41115 98173 32;40613 98602 32;41199 99288 32;41702 98859 50; 41703 99306; 40837 100828;39349 100738;39461 99184;40837 100828 18; 39502 100736;39594 99543; 39429 99567;39780 99582;39466 99201;39429 99567 18; 34698 95370;32517 92754;31765 93372;25143 92597;25031 93551;23179 94180;22930 97063;22378 97020;22644 93818;24567 93117;24681 92093;31665 92868;32688 92021;34649 94391;35095 94022;36254 95423;36596 95140;37832 96634;37536 96879;38120 97580;38342 97389;39213 98460;39003 98638;39449 99174 16; 38759 95852 1;38893 95736;39080 95772;39196 95906;40256 97174 32;39794 97560 32;39794 97560 32;38529 96047 32;38759 95852 18; 39670 96938; 38759 95852 1;38893 95736;39080 95772;39196 95906;40256 97174 32;39794 97560 32;39794 97560 32;38529 96047 32;38759 95852 18; 38400 97459;39179 96807; 42937 100904;42792 104329;44505 104403;44462 105239 32;45094 105271 32;45051 106109 32;44420 106077 32;44377 106910 32;44377 106910 32;40682 106720 32;40894 102582 32;39592 102501;39279 102091;39279 102091;39378 100723;39378 100723;42937 100904 18; 44515 104426;42780 104351;42927 100881;44692 100971 32;44515 104426 18; 44340 104423;44442 102530; 44428 101199; 43740 104043;43814 102493; 43265 104019;43339 102469; 47760 100951;46912 98822;45506 98769;46302 100889;47760 100951 18; 46105 99204; 47760 100951;46912 98822;45506 98769;46302 100889;47760 100951 18; 50782 99188;51410 100585;50581 100994;49919 99559 32;50782 99188 18; 49962 98870; 50712 101184;50422 101329;50696 102011;51477 101675;51363 101406;50893 101628;50712 101184 18; 41023 109062;41043 109320;41147 109341;41436 110458;41672 110411;44543 109388;45634 109404;46618 112089;46843 112453;46942 112392;47562 111920;47927 110682;52848 107381;52848 107381;52601 106374;52421 106459;52056 106623;51540 105529;50554 105837;51185 107584;50585 107770;50285 107026;50182 107667;49169 107656;49210 106964;45065 106105;45147 108380;45116 109010;44227 109000;44269 108349;44082 108328;44036 109074;41023 109062 18; 44082 108359;44279 108349;45178 108369;45085 106147;44424 106136;44382 106922;44136 107239;44103 107615;44082 108359 18; 45064 106116;49199 106974;50254 106964;50884 106695;50605 105816;49923 106075;48228 101661;47762 100937;46284 100896;44692 100958;44517 104431;45163 104488;45064 106116 18; 46906 98819;47761 100960;48229 101691;48853 101455 32;49165 102299 32;49165 102299 32;48549 102526 32;48919 103460;48922 103469;48977 103424;49538 103217 32;49850 104061 32;49850 104061 32;49234 104288 32;49567 105244;49697 105196;50223 105002 32;50535 105846 32;51540 105551;51477 105396;50928 104232;51636 102026;51592 101918;51351 101875;50697 102157;50478 101685 1;50379 101447;50358 101179;50593 101080;49919 99559 32;46906 98819 18; 37631 91207;37014 90511;36615 90853 32;34899 88855 32;37546 86625;37527 84837;37336 84839;37311 82722;36938 82738;36928 80299;37908 79738;38069 84497;38288 84833;38302 86806;37798 87196;37694 87487;36359 88591;38194 90722;37631 91207 18; 36618 90797 32;35427 89372 32;35795 89065 32;36986 90490 32;36618 90797 50; 36816 90637;35612 89201; 36943 82409;35765 82461; 37315 84828 32;35868 84869 32;35856 84466 32;37303 84425 32;37315 84828 50; 36137 84663; 37315 84828 32;35868 84869 32;35856 84466 32;37303 84425 32;37315 84828 50; 36475 87511;35979 86953 1;36265 86699;36604 86912;36920 87129; 36475 87511;35979 86953 1;36265 86699;36604 86912;36920 87129;36475 87511 18; 32713 80358 1;32242 80384;31881 80786;31906 81257 1;31932 81728;32335 82089;32806 82064 1;33277 82038;33638 81635;33612 81164 1;33586 80693;33184 80332;32713 80358 18; 32752 81212; 31824 79542 1;32040 78991;32679 78768;33230 79005 1;33539 79138;33755 79397;33831 79702;34263 79635;34325 78250 1;34153 78231;33979 78143;33995 77971;34077 77010;32153 76846;32081 77822 1;32066 77999;31742 77996;31565 77981;31421 79336;31824 79542 18; 32982 77847; 31824 79542 1;32040 78991;32679 78768;33230 79005 1;33539 79138;33755 79397;33831 79702;34263 79635;34325 78250 1;34153 78231;33979 78143;33995 77971;34077 77010;32153 76846;32081 77822 1;32066 77999;31742 77996;31565 77981;31421 79336;31824 79542 18; 35870 80815; 36925 82180 32;36925 80351 32;37359 80333 32;37359 82180 32;36925 82180 50; 37132 82170;37158 80258; 45368 77872;43249 78430;42970 77944 1;42796 77640;42447 77481;42139 77657;40334 78666;40271 78545;39007 79271;38924 79118;36877 80295 16; 38194 90722;38194 90722;37631 91207;37631 91207;37014 90511;36615 90853 32;34899 88855 32;37546 86625;37527 84837;37336 84839;37311 82722;36906 82739;36902 80299 16; 37875 80260;39114 79561;39119 79558;39216 79738;40478 79008;40549 79151;41462 78628;42904 81548;43365 81298;42074 78637 1;41997 78510;41966 78313;42093 78237 1;42199 78173;42160 78196;42266 78132 1;42436 78030;42629 78170;42711 78350;43031 78926;45403 78274;45397 77869;45191 77919;43249 78430;42970 77944 1;42796 77640;42447 77481;42139 77657;40334 78666;40271 78545;39007 79271;38924 79118;37839 79742;37875 80260 18; 42175 77899 1;42383 77765;42669 77854;42818 78130; 42019 77639 1;42411 77401;42888 77505;43093 77915;43280 78249;45381 77696; 36896 76899;36562 76983;36301 77420;35732 77080;35987 76653;35905 76329; 45364 77612;45192 76892; 44656 77779;44485 77062; 44839 76989;44940 77400; 44323 77129;43461 77104;43158 76730;41680 76911 1;41546 76992;41576 77215;41640 77323 1;41747 77501;41801 77593;41906 77768 16; 42992 77091; 44201 77489; 41912 77779 1;41803 77597;41749 77505;41640 77323 1;41567 77200;41561 76951;41700 76917;42671 76794;42574 77380;41912 77779 18; 42625 76801;42529 77374;42960 77682;43275 78126;44567 77804;44361 77136;43455 77110;43159 76730;42625 76801 18; 39429 75155;39918 75232;40182 75065;40555 75116;40889 74879;39468 74647; 39951 75580; 41192 75462;41665 75953;44656 75553;45093 74633;45183 73570; 37036 77338;36908 80269;37131 80149;38924 79118;39007 79271;40271 78545;40334 78666;41895 77793;41813 77614 1;41759 77522;41711 77441;41640 77323 1;41567 77200;41561 76951;41700 76917;42800 76774;43158 76730;43461 77104;44323 77129;44832 77695;45227 77595;45057 76968;44671 75563;41659 75956;41179 75441;40838 75371;40883 74869;40733 74990;40555 75116;40182 75065;39918 75232;39452 75159;39391 75392;39322 76210;39414 76222;39593 76883;38603 77151;38667 77387;37651 77663;37536 77240;37036 77338 18; 33435 109534 1;33400 109294;33574 109106;33935 109111;44051 109082;44129 107271;41780 107154 1;41767 107340;41596 107446;41412 107437;35238 107077;31843 106829;23719 105812 1;23110 105736;22523 105120;22337 104315;21900 102429;22306 101777 16; 38916 122014 1;38663 121390;38345 121180;37931 120649 1;37205 119719;36805 119278;36169 118284 1;34321 115236;33510 113643;31588 110641 1;30912 109585;30281 108948;29033 108827 1;26845 108614;25751 108508;23563 108295;23853 105829;31843 106829;35238 107077;41470 107443;41638 107388;41729 107293;41788 107152;44120 107270;44104 107633;44114 107634;44046 109038;33995 109112 1;33687 109108;33466 109238;33435 109372;33435 109517 1;33511 110027;33621 110414;33826 110838;34012 111158;36853 115904;38222 117967 1;38363 118154;38540 118354;38713 118580 1;39066 119041;39570 119100;40150 119095 1;41247 119085;41634 118212;42197 117270 1;42962 115991;43461 115045;44832 114000;47562 111920;47927 110682;52848 107381;54435 107524;55546 107031;56377 109332;53869 110459;54504 111840;51948 113221 1;51361 112834;50789 112643;50202 113030 1;49116 113746;48642 114201;47615 114999 1;45462 116671;43759 118830;42487 121983;38916 122014 18; 39840 105352;39657 104066 16; 56272 106659 1;56084 106262;55901 105888;56199 105566;57572 106078;56272 106659 18; 56588 106071; 56283 106659 1;56095 106262;55878 105888;56176 105566;57549 106066;56283 106659 18; 55846 103910;56265 104650;56630 103493;55846 103910 18; 57134 103225;57628 104132; 57621 104009;58936 103387; 59336 103200;60516 102640;59872 101534; 56197 105564;60769 103289;62157 104000;63382 103359; 63615 102794; 63398 103358 1;63210 102961;62993 102587;63291 102265;64652 102718;63398 103358 18; 63387 103358 1;63199 102961;63016 102587;63314 102265;64675 102742;63387 103358 18; 61944 99769;61458 100163;62063 100909 1;63061 100215;63550 99767;64339 98845 1;65218 97817;65653 97093;66462 95984 16; 63312 102262;67161 100291;68759 100664;68773 100662 1;71275 98947;72292 97285;73158 94328 1;73470 93265;73588 92794;73800 91706;73329 91268 1;73893 87726;73930 86419;74309 83045;74683 82841 1;74589 80982;74866 80057;75509 74442 1;75577 73861;75503 73535;75114 73098 16; 60373 100268; 60559 98355;60135 98708;60432 99032; 61352 100023;60701 99310; 59154 99762;58454 100147; 58052 100141;59048 99562; 60027 98593;59805 98362;52691 97383 1;52035 97288;51767 97439;51154 97686 1;50849 97809;50679 96888;50599 96568 16; 57644 99681;57339 99162;52455 98600;51194 99138 16; 52585 106350 32;51524 104037;51622 103743;52296 103433;52444 103052;51784 101620 32;51549 101728 32;51391 101385 32;50903 101610 32;50600 100952;51424 100573;51135 99916;52728 99179 32;53070 99920 32;52868 100013 32;53148 100620 32;53831 100305 32;54699 102188;56137 101405;55152 99594;55805 99258;57167 99410;57462 99848;57755 99675 32;58232 100496 32;59197 99971 32;59541 100603 32;59241 100766 32;59788 101772 32;55852 103912;56206 104681 32;52585 106350 50; 49975 99665;51014 99203;51286 99814; 51830 98667 1;51164 98710;50695 98766;50197 98321 1;50049 98380;49972 98404;49826 98469 1;49442 98639;49712 99140;49900 99516; 51830 98667 1;51164 98710;50695 98766;50197 98321 1;50049 98380;49972 98404;49826 98469 1;49442 98639;49712 99140;49900 99516;51830 98667 18; 42040 94433;43922 96689;46045 96234;46720 96677;48938 96328;49579 96771;50491 96595;50290 95588;51537 95339;51554 95798;52242 95848;52304 92548;49800 92439;49801 93299;45187 92994;43916 91498;44551 90982;43730 89972;41014 92180;41292 92522;40812 92912;42045 94429;42040 94433 18; 50491 96595;49579 96771;48949 96328;46628 96666;46021 96235;43922 96689;42040 94433;41626 94770;43438 96910;43254 97398;43926 97651;44119 97138;46081 96743;46203 97329;49580 96803;49667 97236;50583 97053;50491 96595 18; 52518 106392;52798 107383;54443 107500;56274 106660;56128 106338 1;56020 106060;55983 105800;56199 105566;56394 105466;60769 103289;62157 104000;63372 103364;63298 103167 1;63152 102842;63064 102535;63314 102265;63326 102269;62188 100992;59784 101787;56612 103513;56227 104691;52518 106392 18; 51151 99259;51390 99820;51646 99679;52728 99179 32;53070 99920 32;52868 100013 32;53148 100620 32;53831 100305 32;54699 102188;56137 101405;55152 99594;55805 99258;57167 99410;57462 99848;57680 99719;57508 99449;57339 99162;52455 98600;51507 99004;51200 99127;51151 99259 18; 59804 101809;62008 101115;61489 100135;61816 99709;60621 98374;60148 98695;59752 98339;52148 97369;50958 97766;50540 97065;49647 97246;49799 98459;49988 98402 1;50052 98377;50113 98355;50197 98321 1;50695 98766;51164 98710;51830 98667;52138 98735;52455 98600;57339 99162;57644 99681;57857 99851;58232 100496 32;59197 99971 32;59541 100603 32;59241 100766 32;59553 101340;59640 101518;59804 101809 18; 56321 109344;55488 107035;57583 106086;57418 106021;56199 105566;56401 105462;60769 103289;62157 104000;62681 103726;64701 102738;64521 102674;63291 102265;63526 102153;67161 100291;68759 100664;69311 102928;69443 103248;69549 103488;58742 108284;56321 109344 18; 43225 97382 1;42907 97272;42989 96918;42767 96665 1;42537 96403;42423 96272;42193 96010 1;42045 95841;42091 95680;42256 95527; 43019 96650; 42276 95764; 43225 97382 1;42907 97272;42989 96918;42767 96665 1;42537 96403;42423 96272;42193 96010 1;42045 95841;42091 95680;42256 95527;43437 96921;43225 97382 18; 41687 94010 32;41079 93278 32;40670 93618 32;41278 94350 32;41687 94010 50; 44698 100989;46301 100909;45514 98774;46906 98840;49937 99590;49624 98927;49639 98621;49835 98475;49682 97222;49595 96806;46206 97324;46075 96734;44108 97142;43933 97644;43175 97361;43109 97320 1;42924 97171;42957 96881;42767 96665 1;42537 96403;42423 96272;42193 96010 1;42045 95841;42091 95680;42256 95527;41623 94774;42024 94439;41674 94023;41288 94344;40654 93608;41076 93265;40756 92901;38876 95780;38946 95782 1;39037 95783;39129 95828;39196 95906;40256 97174 32;39798 97557;40615 98601;41115 98173 32;41702 98859 32;41199 99288;40748 100765;44698 100989 18; 39195 96816;38430 97479;38611 97720;39213 98460;39003 98638;39449 99174;40806 100773;41228 99322;40609 98572;39815 97559;39195 96816 18; 38588 95974;37409 94568;36626 95215; 40753 92916 1;40492 92595;40276 92088;40630 91875;41960 91000;43526 89710 1;43685 89579;43888 89591;44021 89748 1;44176 89930;44184 90120;44051 90319 16; 41454 89927;41867 90009;42712 89340 1;42889 89200;42868 88981;42728 88804 1;42586 88625;42311 88612;42132 88754; 41640 87206; 45776 90741 1;45426 90145;44691 89413;43990 89040;43210 88585 1;43038 88447;42437 88012;42442 87826;42477 87058;42877 87074;42943 85799 16; 40753 92916 1;40492 92595;40276 92088;40630 91875;41960 91000;43526 89710 1;43685 89579;43888 89591;44021 89748 1;44176 89930;44184 90120;44051 90319;43731 90008;41020 92209;41291 92492;40753 92916 18; 41454 89927;41867 90009;42712 89340 1;42889 89200;42846 88910;42706 88733 1;42564 88554;42260 88634;42081 88776;42390 89126;41454 89927 18; 44319 90607 1;44527 90491;44750 90495;44910 90671;45340 91287;45047 91529;44593 90982 16; 44319 90607 1;44527 90491;44802 90495;44962 90671;45340 91287;45029 91512;44606 90984;44319 90607 18; 45068 92862;45811 92166 1;46031 91979;46186 91941;46472 91963 1;46579 91969;46660 91979;46739 91985 1;47073 92010;47124 92200;47098 92534;47047 93182 16; 53704 96232 1;54077 96212;54363 95893;54343 95520 1;54323 95148;54005 94862;53632 94882 1;53259 94901;52973 95220;52993 95593 1;53013 95965;53331 96252;53704 96232 18; 53704 94676 1;54077 94656;54363 94337;54343 93964 1;54323 93592;54005 93306;53632 93326 1;53259 93345;52973 93664;52993 94037 1;53013 94409;53331 94696;53704 94676 18; 52188 92563;52217 91892;51619 91308; 50720 90645;50852 86753 32;50921 84730; 51640 91359;50669 90469; 47909 93167;47998 91767;47613 91745 1;47628 91519;47799 91276;48025 91286 1;49028 91331;49458 91356;50571 91405;51039 91878;51028 92470; 49076 81752;47494 81556 16; 47489 80619;47313 82174; 43083 83816; 42816 82682;42367 83002;42460 84578;43037 84867;44119 84269;44727 84114;44707 83795;43728 83311;42816 82682 18; 43529 81648;42074 78637 1;41997 78510;41966 78313;42093 78237 1;42199 78173;42160 78196;42266 78132 1;42436 78030;42629 78170;42711 78350;43013 78894;45422 78270;45367 77548;48034 77421;47740 80620;45566 80921;44383 81175;43529 81648 18; 42928 81543;43361 81311; 43072 82602;43104 81942;43520 81706; 42727 85843 1;42752 85254;43181 84761;43766 84447 1;44322 84148;45020 83978;45644 84071; 43842 83133;43565 81701;43113 81922;43082 82671;43503 82938;43842 83133 18; 47170 81981 1;45774 82178;45086 82378;43744 82809 1;43466 82898;43221 82592;43221 82300 1;43221 81992;43450 81896;43650 81662 1;44341 80850;44891 80562;45943 80391 1;46564 80290;47321 80287;47482 80711 16; 43744 82809;45442 82279;47206 81967;47482 80660;44303 81175;43497 81720;43744 82809 18; 45255 83075; 43724 83218;43703 82838;45315 82330;47378 81986;47517 82269;47433 82964 1;47269 83473;46983 83875;46396 84071 1;46036 84191;45823 84115;45448 84060 1;45123 84013;44761 83875;44416 83707;44042 83506;43724 83218 18; 42773 89273;43348 89893;42835 90279;41960 91000;40630 91875 1;40276 92088;40492 92595;40753 92916;38923 95778;38728 95871;38543 96045;39097 96738;38316 97395;38127 97570;37672 97078;37519 96893;37832 96634;36596 95140;36254 95423;35583 94612;35707 94491;36008 94245 1;36166 94109;36184 93884;36048 93726 1;35903 93557;35661 93539;35492 93684 1;35330 93823;35249 93892;35087 94031;34658 94394;33192 92608;33324 92450;34483 91520;34088 91060 1;33760 90732;33237 90886;32949 91113 1;32819 90992;32706 90910;32545 90925;34901 88870;36594 90820;37015 90502;37631 91200;38185 90738;38832 90153;39746 91272;41419 89938;41871 90005;42773 89273 18; 41370 87095;41304 87947;41813 88499;41844 87700;41359 87244;41370 87095 18; 41597 88318;41286 87981;41335 87349;41641 82687;41223 82696;41217 82425;42887 81530;41903 79446;41462 78628;40549 79151;40478 79008;39216 79738;39119 79558;37925 80249;38069 84497;38288 84833;38302 86774;37794 87184;37661 87514;36359 88591;38174 90699;38806 90139;39732 91270;39732 91270;42407 89135;41597 88318 18; 50672 90703 1;50599 90611;50437 90664;50320 90659 1;49362 90617;47818 90552;46860 90511; 48055 88909; 46635 90511 1;46679 89819;46716 89131;47077 88540 1;47301 88173;47527 88076;47822 87763 16; 50528 84006 1;50463 83671;50140 83452;49807 83517 1;49472 83581;49253 83905;49317 84238 1;49382 84573;49705 84791;50039 84728 1;50374 84663;50593 84340;50528 84006 18; 46389 84064;48062 84526;49467 84605; 48360 82545; 45448 84060 1;44515 83925;43283 83046;42997 82834 1;42742 82670;42345 82813;42351 83223 1;42357 83639;42326 84043;42333 84459 1;42336 84612;42525 84719;42663 84792 1;42851 84891;43114 84896;43310 84753 16; 47664 81741 1;47523 82793;47146 83756;46139 84092 16; 45250 84044;46472 84095; 47324 84825; 48591 88119 1;49368 88236;50092 87700;50208 86924 1;50324 86147;49789 85423;49012 85307 1;48236 85191;47512 85726;47396 86503 1;47279 87279;47815 88003;48591 88119 18; 49383 84411 1;49361 84365;49327 84291;49317 84238 1;49253 83905;49472 83581;49807 83517 1;49921 83495;50034 83506;50205 83574 1;50367 83658;50490 83812;50528 84006 1;50567 84205;50510 84441;50296 84599 1;50005 85076;49894 85537;50101 86056 16; 49084 82227 1;49455 82655;49424 82658;49844 83038 1;49982 83163;50130 83183;50162 83366; 47101 89893;47674 89907;48729 89926 1;49202 89947;49724 89535;49849 88950;50006 87928; 50553 90645;46827 90489;46866 89943;47091 89899;48750 89926 1;49223 89947;49724 89535;49849 88950;50016 87887;50075 87346 1;50140 87217;50185 87075;50208 86924 1;50251 86633;50204 86350;50086 86104;50076 85988 1;49904 85497;50018 85055;50296 84599;50377 84539 1;50505 84400;50567 84205;50528 84006 1;50490 83812;50367 83658;50205 83574;50162 83366 1;50130 83183;49982 83163;49844 83038 1;49424 82658;49455 82655;49084 82227;49079 81920;51152 82178 32;50836 84724 32;50860 84727;50910 85041;50852 86753 32;50744 89962;50553 90645 18; 43696 88687;44624 89289; 46241 89123;44629 88973; 44538 89399 1;44636 89489;44696 89533;44767 89590 1;45212 89947;45423 90157;45747 90709;46418 90516;46428 90205;46454 90037;46538 89154;44321 88942;44538 89399 18; 48798 86727; 47104 88430;47176 86603 1;47202 85951;47529 85526;48120 85248 1;48392 85120;48361 84602;48069 84529; 48268 85404;48412 85346 1;48601 85291;48805 85276;49012 85307 1;49483 85377;49866 85671;50067 86065; 48134 85340;48325 85381;48412 85346 1;48601 85291;48805 85276;49012 85307 1;49465 85374;49836 85649;50043 86020;50070 85972 1;49907 85487;50021 85050;50296 84599;49418 84470;48376 84639;48134 85340 18; 50044 86035;50088 86106 1;50204 86352;50251 86635;50208 86924 1;50092 87700;49368 88236;48591 88119 1;48356 88084;48144 87993;47964 87862 1;47615 87593;47408 87217;47383 86804 16; 47252 88140;47724 87648;47651 87542 1;47498 87333;47377 87134;47356 86877;47337 86808;47252 88140 18; 46733 83990;48094 84390;49454 84462;50224 83605;50190 83500;50162 83366 1;50130 83183;49982 83163;49844 83038 1;49424 82658;49455 82655;49084 82227;49079 81920;49099 81922;47811 81711;47370 83076;46733 83990 18; 46797 89913;46881 89011;47208 88329;47890 87835;48067 87930 1;48223 88025;48399 88090;48591 88119 1;49207 88212;49805 87865;50078 87342;50061 87478;50016 87887;49849 88950 1;49724 89535;49202 90009;48729 89988;47091 89950;46797 89913 18; 45861 87091;44670 87009; 45831 86957 1;46006 86887;46171 86690;46077 86527 1;45804 86054;44724 85590;44301 86393 1;44081 86810;43757 87338;44003 87533 1;44290 87760;44706 87848;45071 87872 1;45415 87895;45629 87820;45953 87697 1;46200 87603;46034 87355;45841 87245;45831 86957 18; 46036 86803 1;46101 86719;46129 86618;46077 86527 1;45804 86054;44724 85590;44301 86393 1;44081 86810;43757 87338;44003 87533 1;44290 87760;44675 87858;45040 87882 1;45384 87905;45598 87759;45953 87697 1;46127 87667;46106 87543;46012 87408 16; 43209 88542;43572 88144 1;43634 87872;43593 87782;43470 87532 1;43320 87228;43364 87097;43026 87063;42476 87053;42435 87863 1;42430 88048;42875 88308;43039 88394;43209 88542 18; 43177 88566;44151 89100;46625 89241;47032 88449;47170 85996;47591 85488;48150 85139;48252 84689;46597 84152;44847 84065;43483 84610;43069 85263;42960 85663;42874 87076;43200 87565;43177 88566 18; 47973 92932; 49503 92984; 49667 92193; 47909 93167;47998 91767;47613 91745 1;47628 91519;47799 91276;48025 91286 1;49028 91331;49458 91356;50571 91405;51039 91878;51028 92470;49828 92476;49806 93321;47909 93167 18; 46146 92768; 45068 92862;45811 92166 1;46031 91979;46186 91941;46472 91963 1;46579 91969;46660 91979;46739 91985 1;47073 92010;47124 92200;47098 92534;47047 93182;45201 92997;45068 92862 18; 46441 90492;45751 90717; 41564 84584;42102 84605;42205 84559;42282 84441;42662 84867;42939 85005;42852 85780;42939 85903;42886 87076;42478 87056;42446 87852 1;42441 88038;42831 88302;42996 88389;43215 88581;43990 89040 1;44325 89218;44416 89293;44705 89538 1;45212 89967;45415 90174;45754 90719;46432 90497;46884 90528;50373 90653;50584 90653;52057 91988;52021 92554;51019 92496;51030 92365;51039 91878;50571 91405 1;49458 91356;49028 91331;48025 91286 1;47799 91276;47628 91519;47613 91745;47998 91767;47910 93158;47048 93113;47066 92937;47098 92534 1;47124 92200;47073 92010;46739 91985 1;46660 91979;46579 91969;46472 91963 1;46186 91941;46031 91979;45811 92166;45068 92862;43912 91502;44572 90972;44698 91098;45029 91512;45340 91287;44947 90715 1;44787 90539;44527 90491;44319 90607;44072 90326;44093 90246 1;44173 90076;44128 89922;43992 89763 1;43859 89606;43685 89579;43526 89710;43262 89927;42725 89329 1;42882 89188;42849 88948;42713 88776 1;42571 88597;42274 88634;42095 88776;41818 88500;41849 87705;41357 87277;41564 84584 18; 43386 88361;43572 88144 1;43634 87872;43593 87782;43470 87532 1;43330 87248;43359 87115;43087 87071; 41616 82684;41565 82241;41216 82430;41223 82684; 41637 82677;42007 82626;41964 84622;41586 84586;41695 82873;41637 82677 18; 41971 84666;42000 82641;41644 82699;41579 82271;42958 81523;43364 81312;43510 81712;43089 81958;43038 82728;42493 82873;42327 84710;41971 84666 18; 41568 84596;41960 84603;41993 82651;41622 82665; 45900 85854; 56325 97898;56673 95024;54728 94936;54873 90886;56746 90944;56891 88331;55555 88215;55410 89521;51911 89158;52100 87822;51592 87764;51897 84875;51011 84745;50837 90378;52231 91931;52318 92614;52274 95894;51548 95807;51519 95386;50358 95590;50488 96577;51011 97651;52201 97317;56325 97898 18; 36958 76899;36712 75944;36421 75821;36566 75389;36312 74430 16; 33723 73116; 33363 72814;33002 72773;32961 73133 1;32951 73224;33011 73305;33102 73315;35378 73565; 33363 72814;33002 72773;32961 73133 1;32951 73224;33011 73305;33102 73315;35327 73559;35205 73037;33363 72814 18; 30423 72813; 30227 75005; 30460 75804;30702 73121;30917 72827;31040 71601 1;31069 71312;31026 71077;30719 71054 1;30587 71044;30396 71019;30184 71006; 30460 75804;30702 73121;30917 72827;31040 71601 1;31069 71312;31026 71077;30719 71054 1;30587 71044;30396 71019;30184 71006;29703 75317;30460 75804 18; 30597 71478; 33567 69433;33048 68687;33172 67735;34268 67877;33992 69503;33567 69433 18; 33624 69475;33049 68693;33172 67732;34220 67863 16; 34276 67397;33229 67260;33327 66523;33952 65943;34451 66001 16; 34284 67398;33229 67260;33333 66512;33953 65953;34432 66000;34284 67398 18; 31015 66767; 29991 67364; 34698 63475;34238 63423;33890 62991;34021 61757;34411 61807;34510 60964;35215 61051 16; 47457 65505;43155 64920;41882 66234;41597 69924; 39533 65118 32;40149 64376 32;40492 64658 32;39872 65405 32;39533 65118 50; 39624 67834; 38743 65487;40334 66212;41131 65889; 41328 71723;41365 71366; 41567 71977 32;41641 71172 32;41150 71127 32;41076 71932 32;41567 71977 50; 41016 71933;41092 71096; 40675 71578; 40058 71832;40971 70509 16; 39142 70383 32;39210 69481 32;39640 69518 32;39573 70420 32;39142 70383 50; 40058 71793 32;39246 71220 32;39482 70863 32;40304 71442 32;40058 71793 50; 43555 63267 32;44499 63381 32;44447 63812 32;43495 63701 32;43555 63267 50; 41061 67896; 39598 69978;43879 70359; 41435 71190;41651 70444; 41673 69630;42065 70073; 39474 70860;39785 70388; 38571 71044;39104 71399;39782 70398;39577 70377;39566 70423;38637 70367;38571 71044 18; 39509 70862;40312 71461;40982 70423;39867 70327;39509 70862 18; 40048 71840;41120 71917;41244 71137;41413 70532;40961 70506;40048 71840 18; 38330 69448;39634 69530;39634 69602;41317 69746;41543 66625;41554 66266;41061 65917;40383 66163;38741 65547;38330 69448 18; 38889 65218;40352 65844;40573 65772;39993 65295;39875 65413;39218 64869;38889 65218 18; 42948 71033; 43969 63754;43823 64908; 40194 65063;41820 66431; 39286 65144; 38670 65966;40283 66695; 36808 57918; 36541 56353; 37967 56993; 36758 58849 1;36239 58811;35849 58358;35887 57838 1;35905 57590;36018 57371;36188 57215 1;35819 57076;35565 56696;35596 56272 1;35634 55753;36086 55363;36606 55401 1;37043 55433;37389 55758;37464 56172 1;37627 56062;37829 56005;38043 56021 1;38562 56059;38952 56511;38914 57032 1;38876 57551;38424 57941;37904 57903 1;37859 57900;37814 57893;37771 57884;37768 57978 1;37730 58497;37278 58887;36758 58849 18; 36758 58849 1;36239 58811;35849 58358;35887 57838 1;35905 57590;36018 57371;36188 57215 1;35819 57076;35565 56696;35596 56272 1;35634 55753;36086 55363;36606 55401 1;37043 55433;37389 55758;37464 56172 1;37627 56062;37829 56005;38043 56021 1;38562 56059;38952 56511;38914 57032 1;38876 57551;38424 57941;37904 57903 1;37859 57900;37814 57893;37771 57884;37768 57978 1;37730 58497;37278 58887;36758 58849 18; 35159 64468;35263 63544;35498 63357;35710 61495;35898 61282;37607 61461;41480 59445;41559 58404;41745 58237;42929 58338;43078 58522;43070 58741;49131 59240 16; 35178 64894;34630 70154;34169 70112;33944 72251;35744 72469;36552 75395;36408 75836;36706 75949;36928 76862;37065 77368;37536 77240;37651 77663;38667 77387;38603 77151;39593 76883;39414 76222;39322 76210;39456 74619;40898 74842;40817 75367;41193 75425;41367 74298;42581 74490;42982 74939;43397 74605;43692 71386;41658 71200;41584 72012;40051 71872;39215 71295;39106 71440;38550 71056;38618 70309;39093 70352;39170 69506;38348 69431;38690 65693;38563 65678;38280 65301;35178 64894 18; 33998 61998;35094 62132;35235 61065;34514 60980;34422 61807;34033 61758;33998 61998 18; 34946 63497;34217 63434;33892 62981;34012 61977;35087 62104;34946 63497 18; 40919 57388;39702 57280;39786 56337;41933 56531;41848 57470 16; 40919 57388;39702 57280;39786 56337;41933 56531;41848 57470;40919 57388 18; 43706 57872;44619 57943; 43381 58077 32;44958 58216 32;44998 57761 32;43421 57622 32;43381 58077 50; 43377 57600;43457 56700;45073 56845;45038 57240;45739 57302;45684 58078 16; 44267 57230; 47630 54288; 47210 58414;47290 57493;47863 57543;47894 57185;49814 57350;49669 58840;49245 58799 16; 49130 57848; 51391 59890;49171 59452;49135 59911;50401 60159;50415 60025;51334 60201;51391 59890 18; 36562 76978;36302 77420;35724 77079;35978 76639;35588 75205;35801 75147;35220 73016;33262 72804;33639 69445;34039 69492;34741 63053;35009 63081;35285 60584;37406 60814;40801 58973;40942 57387;43397 57601;43355 58068;44955 58209;44971 58028;46681 58176;46662 58396;49074 58592;49256 58782;49221 59240;43071 58730;43085 58525;42929 58341;41745 58239;41561 58409;41480 59441;37608 61462;35888 61287;35718 61432;35498 63352;35263 63537;35159 64451;38345 64872;38572 65142;38987 64661;39208 64851;38771 65350;38725 65697;38563 65678;38280 65301;35164 64892;34641 70153;34180 70131;33964 72232;35742 72463;36552 75395;36408 75836;36706 75949;36928 76862;36562 76978 18; 53034 60078 32;53699 60205 32;53780 59778 32;53116 59649 32;53034 60078 50; 54410 65753;55538 60701; 53900 65677;55090 60304; 62435 58058;56486 57195;62327 30198;44380 22387 16; 63014 65111;62986 62820 1;62978 62160;62936 61826;62802 61179 1;62642 60408;61304 59309;60072 59057 1;58735 58784;58063 58739;56723 58480 1;56274 58393;56093 58025;56028 57573 1;55994 57334;56007 57206;56057 56969 1;56765 53607;57957 47962;58666 44600;59094 44345 16; 56725 57412;56555 58452; 54278 58287; 54561 55714; 54872 54073; 54782 59186;53250 58594;53442 57869;54510 57994 16; 50692 54408;50641 54883;51769 55000;51717 55504 1;51705 55619;51765 55732;51880 55744;53523 55915;53631 54734;50692 54408 18; 52150 55364; 50681 54407;50641 54883;51769 55000;51717 55504 1;51705 55619;51765 55732;51880 55744;53523 55915;53631 54734;50681 54407 18; 47179 53825 32;48173 53929 32;48087 54751 32;47093 54647 32;47179 53825 50; 47179 53825 32;48173 53929 32;48087 54751 32;47093 54647 32;47179 53825 50; 44661 54956;45084 54996 1;45216 55008;45379 54768;45391 54636; 45723 53668 32;44536 53563 32;44470 54189 32;45656 54312 32;45723 53668 50; 45717 53685 32;44532 53551 32;44470 54189 32;45656 54312 32;45717 53685 50; 53422 53887;43294 52835; 40196 53916 32;40116 54679 32;40872 54758 32;40948 53995 32;40196 53916 50; 40196 53916 32;40116 54679 32;40872 54758 32;40948 53995 32;40196 53916 50; 43377 57600;43457 56700;45073 56845;45038 57240;45739 57302;45684 58078;45014 58033;45006 57745;43377 57600 18; 43381 58077 32;44958 58216 32;44998 57761 32;43421 57622 32;43381 58077 50; 47210 58414;47290 57493;47863 57543;47894 57185;49814 57350;49669 58840;49245 58799;49079 58596;47210 58414 18; 54740 59174;53250 58594;53442 57869;54510 57994;54575 58522;54740 59174 18; 42411 54134;41396 54035;41318 54786 32;42332 54892 32;42411 54134 18; 53443 54681 32;50696 54391 32;50712 54186 32;43906 53470;43824 54264 32;42411 54122;41398 54005;40192 53879 32;40277 53135;43151 53406;43264 52840;53516 53891;53443 54681 50; 35171 60566;35568 56305;35598 56435 1;35633 56791;35867 57094;36188 57215 1;36018 57371;35905 57590;35887 57838 1;35849 58358;36239 58811;36758 58849 1;37278 58887;37730 58497;37768 57978;37771 57884 1;37814 57893;37859 57900;37904 57903 1;38424 57941;38876 57551;38914 57032 1;38937 56712;38799 56418;38568 56230;39776 56324;39683 57290;40935 57399;40809 58969;37416 60808;35171 60566 18; 40116 54657;39771 56356;39869 56344;41933 56531;41848 57470;42016 57481;43367 57598;43398 57363;43457 56700;45073 56845;45038 57240;45739 57302;45684 58078;45826 58102;46681 58176;46662 58396;47228 58442;47231 58174;47290 57493;47863 57543;47894 57185;49814 57350;49669 58840;49253 58800;49247 58898;49221 59240;49200 59455;49328 59483;51391 59890;51387 59912;51458 59782;53039 60094;53066 59912;53116 59649 32;53780 59778 32;53699 60203;53863 60257;54692 60421;54725 60254;55060 60320;55244 59924 1;55054 59742;54896 59476;54774 59177;54605 59122;53250 58594;53442 57869;54457 57988;54484 57885 1;54474 57709;54480 57547;54501 57408;54769 56047;51801 55738;51726 55606 1;51716 55574;51713 55539;51717 55504;51769 55000;50641 54883;50690 54429;50704 54283;50712 54186 32;48158 53917;48161 54041;48087 54751 32;47093 54647 32;47179 53825 32;47074 53803;45723 53661;45699 53900;45656 54312 32;44470 54189 32;44532 53604;44484 53531;43906 53470;43824 54264 32;42419 54125;42332 54892 32;41318 54786 32;41396 54035;40947 53968;40872 54758 32;40116 54657 18; 56732 44030;54206 56000;54759 56058 16; 50641 55187 1;50629 55320;50800 55569;50936 55585;51306 55628 16; 40476 51377;40401 52092;40058 51666;40476 51377 18; 40827 47149;42351 47310;42415 46703;40891 46570;40827 47149 18; 41147 43850;40873 46572;47621 47111;47849 46957;48142 43402;43999 43043;43671 43163;43359 43549;43213 43998;41147 43850 18; 43278 47378 1;43413 47166;43579 46982;43818 47058 1;44010 47118;43998 47318;44178 47408 1;44410 47524;44560 47486;44818 47508 1;44929 47517;44975 47378;44958 47268;44909 46898;42982 46743;42968 47358;43278 47378 18; 43278 47378 1;43413 47166;43579 46982;43818 47058 1;44010 47118;43998 47318;44178 47408 1;44410 47524;44560 47486;44818 47508 1;44929 47517;44975 47378;44958 47268;44909 46898;42982 46743;42968 47358;43278 47378 18; 47868 47768;48048 47632;48134 47438;48120 47178;47990 47000;47818 46968;47789 47101;46313 47012;44920 46899;44915 46941;44958 47268 1;44975 47378;44929 47517;44818 47508 1;44560 47486;44410 47524;44178 47408 1;43998 47318;44010 47118;43818 47058 1;43579 46982;43413 47166;43278 47378;47868 47768 18; 48179 43249;45649 43037; 41189 43832;43209 44007 1;43250 43511;43620 43006;44116 43047;45600 43170;45753 41308;44270 41197 1;42669 41066;41321 42226;41189 43832 18; 47710 47043;47767 46268; 47802 45601;47982 43421; 50738 47202;50671 47826;51702 47941;51345 51221;50677 51147;50698 50900;46421 50447 32;46491 49775 32;42648 49374 32;42763 48273 32;40846 48073 32;40318 53139;43202 53411;43264 52840;53483 53888;53649 52266 32;53400 52240 32;53582 50497 32;53833 50523 32;53987 49043 32;54196 49065 32;54284 48226 32;53987 48195 32;54058 47511 32;53817 47486 32;53934 46364 32;53632 46332 32;53710 45585 32;53256 45538 32;53281 45298 32;52386 45205 32;52418 44896 32;51479 44798 32;51308 44563 32;51043 44535 32;50794 44736 32;49957 44649 32;49887 45323 32;50091 45344 32;49973 46473 32;49768 46452 32;49702 47085 32;50738 47202 18; 50841 47901;50640 49751;51141 49811;51341 47961;50841 47901 18; 48994 41295; 45796 41243;47476 41354;47585 39723;49312 39867 16; 51390 41859;45780 41369; 50382 40266 32;50874 40300 32; 50075 40497 32;51164 40581 32;51203 40077 32;50108 39993 32;50075 40497 50; 50064 40496 32;51164 40581 32;51203 40077 32;50108 39993 48; 50113 39928;50064 40556 48; 50199 40552;50126 41507; 47525 41516;50095 41746;50250 40013;50359 40002;50420 39062;49830 39035;49764 39583;47686 39482;47525 41516 18; 46528 49723 1;46896 49760;47081 49778;47449 49815 1;47835 49854;48346 49230;48368 48843;48571 49079;48695 49327;48804 49848;48691 50300;48005 50190;46487 50040;46528 49723 18; 42648 49382;46498 49792;46632 49733 1;46934 49764;47117 49782;47449 49815 1;47834 49854;48344 49232;48368 48845;48274 48746 1;48086 48581;47871 48476;47671 48458;44490 48178 1;44320 48160;44201 48329;44183 48499;44140 48918;42708 48768;42648 49382 18; 51131 49820;51494 49854;51702 47941;51329 47901;51131 49820 18; 66274 98308 32;66917 97682 32;66455 97205 32;65815 97849 32;66274 98308 50; 66378 97770; 66277 98312 32;66919 97671 32;66460 97209 32;65817 97849 32;66277 98312 50; 65184 99618 32;65827 98992 32;65365 98515 32;64725 99159 32;65184 99618 50; 65288 99080; 65187 99622 32;65829 98981 32;65370 98519 32;64727 99159 32;65187 99622 50; 69283 98928 32;69150 98133 32;68443 98262 32;68590 99053 32;69283 98928 50; 68879 98600; 69287 98927 32;69143 98138 32;68451 98260 32;68594 99052 32;69287 98927 50; 69031 97510 32;68885 96714 32;68187 96838 32;68331 97636 32;69031 97510 50; 69026 97511 32;68892 96709 32;68180 96839 32;68328 97637 32;69026 97511 50; 68619 97180; 68752 96103 32;68610 95324 32;67926 95445 32;68067 96226 32;68752 96103 50; 68748 96104 32;68616 95319 32;67919 95446 32;68064 96227 32;68748 96104 50; 68348 95780; 67052 96144; 66692 95394;66997 94189;67630 93534; 56093 97806;56533 94986;57033 95014;58549 95067 32;58567 94536 32;66319 94805 32;66347 93994;66980 94258;66694 95386;66877 95430;66601 96095;65024 98011;63172 100097;62097 100860;61546 100182;61899 99687;61999 99603;63500 98232 32;62139 96743 32;60495 98245 32;60174 98626;59792 98329;56093 97806 18; 67590 94118; 69742 92663 32;69600 91884 32;68986 92085 32;69057 92786 32;69742 92663 50; 69738 92664 32;69606 91879 32;68979 92076 32;69054 92787 32;69738 92664 50; 69328 92340; 72954 88624;72752 88847 32;71200 90554;70676 90530 32;69575 91615 32;67616 93545 16; 73091 88759;73197 88610;72869 88596;73091 88759 18; 69291 102956;68740 100686;68887 100583 1;71306 98896;72305 97240;73158 94328 1;73470 93265;73588 92794;73800 91706;73329 91268 1;73489 90261;73611 89403;73711 88613;76636 88824;76669 90144 1;76420 92639;76312 93907;75688 96335;75152 96692;74558 97088 1;73151 99902;71358 101931;69308 102945;69291 102956 18; 62066 101079;63286 102279;63507 102162;67161 100291;68759 100664;68773 100662 1;71275 98947;72292 97285;73158 94328 1;73470 93265;73588 92794;73800 91706;73329 91268 1;73490 90254;73612 89391;73713 88597;72898 88686;72752 88847 32;71200 90554;70676 90530 32;69575 91615;69603 91900;69742 92663 32;69057 92786 32;69012 92344;67038 94225;66878 95415;66578 96125;68063 96224;67919 95446 32;68616 95319 32;68748 96104 32;69288 98935;68588 99055;66938 97665;66278 98325;65848 98975;65188 99625;64448 98875;62978 100325;62066 101079 18; 64408 98938;64705 99164;64797 99086;65365 98515 32;65827 98992;65915 98980;66325 98351;66236 98270;65815 97849 32;66455 97205 32;66917 97682;68588 99079;68569 98940;68443 98262 32;69150 98133;69040 97509;68961 97523;68328 97637 32;68180 96839 32;68892 96709;68757 96102;68043 96208;66495 96102;65342 97616;64408 98938 18; 56828 88325;56668 90965;63148 91162;63157 91162 32;63200 89924 32;66484 90038 32;66341 94159;66963 94324;66980 94258;67616 93545;67616 93545;67823 93341;69575 91615 32;70676 90530 32;71200 90554;72752 88847 32;72954 88624;72648 88546;68674 88528 32;68698 83097;67586 82959 32;67745 81676 32;54474 80027 32;53842 85117 32;55080 85268 32;55165 84423 32;57154 84622 32;57126 84905 32;57509 84943 32;57444 85591 32;57186 85565 32;56918 88295;56828 88325 18; 73418 82744; 77574 84016 1;77376 83992;77268 83804;77292 83606;78012 77623;80255 54163 1;80294 53756;80564 53595;80912 53380 16; 73713 88669;76627 88881;76650 88717;77079 85473 1;77113 85256;77273 85255;77461 85275;77574 84016 1;77376 83992;77268 83804;77292 83606;78012 77623;75166 77303 1;74770 80466;74608 81355;74683 82841;74309 83045 1;74118 84749;74013 85926;73884 87130;73713 88669 18; 69224 80491;69169 80884;68519 80800;68404 81740;69049 81819;69661 81895 32;69544 82832;73150 82848 32;73125 88548 32;68674 88528 32;68698 83097;67586 82959 32;67745 81676 32;54474 80027 32;53842 85117 32;55080 85268 32;55165 84423 32;57154 84622 32;57126 84905 32;57509 84943 32;57444 85591 32;57186 85565 32;56910 88325 32;55556 88190 32;55425 89496 32;51925 89146 32;52057 87823 32;51593 87777 32;51886 84855;50836 84724 32;51152 82178 32;49061 81918 32;49837 75672 32;72607 78500 32;72647 78181 32;74427 78574 32;74113 81099 32;69224 80491 18; 69080 81840;68410 81758;68528 80795;69201 80870 32;69080 81840 18; 69031 80922;68975 81400; 74308 78498;74926 73093 1;74575 72633;74169 72606;73596 72527 1;72341 72355;69826 71999;68571 71826 16; 73051 88741;73674 88685;74296 83028;74385 83004;74683 82841 1;74589 80982;74866 80057;75509 74442 1;75577 73861;75503 73535;75114 73098;74911 73223;74308 78498;74401 78568;74427 78574 32;74113 81099 32;69224 80491;69169 80884;69084 81807;69080 81840;69170 81834;69661 81895 32;69544 82832;73150 82848 32;73147 83469;73156 88548;73051 88741 18; 71182 70642; 70182 70492; 71582 70502;71182 70812;69972 70642;69802 70202; 71582 70502;71182 70812;69972 70642;69802 70202;71582 70502 18; 74141 69908; 76149 65965; 76008 67196; 75363 69996 1;75779 69667;76078 69448;76135 68921;76602 64840;76015 64767;75363 69996 18; 75372 69926 1;75788 69597;76078 69448;76135 68921;76601 64848;76015 64768;75372 69926 18; 74790 70452;75671 63922; 72501 70642 1;73409 70751;73938 70868;74781 70512 1;75054 70324;75124 70174;75391 69982;76031 64642;75651 63922;75121 63872;74945 64220;74444 65157;74911 66459;74048 67548 32;74614 68821 32;72721 70332;72501 70642 18; 72511 70652 1;73670 70801;74575 70836;75361 69972; 73486 69688;72479 70462;67572 69655;66052 69405;65835 69102;66330 66111 16; 75006 47682;77066 47992;77354 48395 32;75952 63927;75145 63847 32;74782 64526 32;74444 65157;74911 66459;74692 66735 32;74048 67548;74614 68821;74009 69286 16; 76598 64844;79858 30295; 77993 77671;75143 77381;75190 77114 1;75283 76360;75390 75481;75509 74442 1;75577 73861;75503 73535;75114 73098;74529 72638;69140 71874;69367 70036;69777 70121;69835 70287;69972 70642;71182 70812;71582 70502;71672 70404;72563 70644;72709 70676 1;73774 70802;74620 70786;75361 69972;75493 69893 1;75841 69616;76084 69391;76135 68921;76599 64862;77319 57364;79923 57632;78074 76920;77993 77671 18; 66328 65877;65893 69242;68131 69747;72479 70462;73331 69807;73595 69845;74140 69435;74536 68749;74090 67568;74882 66437;74696 65859;74444 65157;74945 64220;75121 63872;75651 63922;75872 63903;77314 48459;77060 47978;74967 47695;74627 50241;66679 49025;65746 55219;66312 58642;66199 60000;65715 61191;64574 62027;64391 62952;66328 65877 18; 81158 49716 1;80878 49448;80667 49243;80709 48858;81638 38822;81492 34640;81746 32306 1;82795 22667;83060 17821;83874 8159 1;84432 1532;84924 -1770;85166 -8416 1;85333 -13021;85368 -15325;85515 -19931 1;85579 -21919;85390 -26429;89498 -28983; 84659 -24816 1;85467 -26779;85931 -28158;87782 -29353;84677 -29143;84659 -24816 18; 89377 -32625;87929 -32521 1;86487 -32418;85481 -33659;85399 -35103;85275 -40155; 84659 -24816 1;85467 -26779;85931 -28158;87782 -29353;84677 -29143;84659 -24816 18; 75454 43228;77852 43527 32;77525 46740 32;71924 45955;71750 47054 16; 78453 39506;77077 39370; 72915 21155;73270 21151 1;73740 21614;74558 22440;75352 23299 1;76087 24093;76333 24600;76860 25545 1;77597 26868;77965 27530;78702 28853;78553 30114;79190 30190;78407 38509;73115 37868 16; 73078 38015;72806 41161; 76847 39312 1;76733 40669;76081 41473;74767 41834 1;74153 42003;73774 42013;73175 41797 1;72892 41695;72781 41443;72805 41143 1;72896 40008;72810 39423;73027 38305 1;73182 37506;73187 36947;73815 36430 1;74874 35558;76509 36141;76796 37482 1;76938 38143;76904 38638;76847 39312 18; 75098 36035; 76066 41112; 75675 64072;76024 64779;76591 64831;79072 38619;73193 37956;72861 41218;73122 41245;74653 41397 32;74878 39127 32;77093 39347 32;76768 42623 32;75692 42516 32;75635 43088 32;75580 43082;75740 43382;77834 43556;77503 46784;71885 45964;71798 47046;77102 47866;77381 48390;75932 63882;75675 64072 18; 77292 57419;79907 57691;79933 57530;80255 54163 1;80294 53756;80564 53595;80912 53380;81158 49716 1;80878 49448;80667 49243;80709 48858;81302 42448;81362 41801;81638 38822;81492 34640;81746 32306 1;81836 31479;81920 30688;81999 29926;79924 29592;79751 29721;79861 30227;78317 46624;77292 57419 18; 81083 12362 1;81251 8771;81389 6759;81485 3166 1;81643 -2754;81646 -6074;81746 -11995 1;81786 -14393;81782 -15738;81833 -18136 1;81847 -18803;81692 -19212;81320 -19766 1;81041 -20182;81001 -20513;81021 -21013 1;81109 -23234;81158 -24479;81246 -26700 1;81288 -27760;80691 -28839;79631 -28883;74795 -29081;56271 -27721;53558 -27544 1;52501 -27475;51704 -26569;51761 -25511 1;51792 -24936;51772 -24612;51752 -24037 1;51723 -23194;51665 -22703;51378 -21909 1;50372 -19123;49815 -19400;50302 -18151 1;50526 -17575;51629 -17167;52264 -16864 1;52558 -16724;52721 -16459;52766 -16136 1;53190 -13105;53697 -11640;54394 -8660 1;54716 -7282;54936 -6595;55517 -5304 1;56581 -2940;57150 -1775;58230 581 1;59005 2270;59409 3109;60302 4739 1;61395 6734;62053 7680;63448 9476 1;64989 11460;65967 12298;67877 13930 1;69399 15230;70213 15822;71665 17200 1;72909 18380;73519 18990;74624 20301 1;76392 22398;77328 23443;78879 25705 1;79245 26239;80131 26133;80151 25816;81083 12362 18; 77477 26061;76601 24444; 76630 19911 32;77599 21080 32;76949 21618 32;75988 20458 32;76630 19911 50; 74322 19337;74872 18795; 74976 19398; 76921 21637;76555 21943; 76903 21593;76537 21899; 76460 21114; 76874 21583;76542 21871;74344 19350;74902 18766;74929 19114;76874 21583 18; 76527 21928 1;75827 21093;75093 20261;74280 19338 16; 76603 24460 1;76235 23853;75888 23421;75440 22932; 79083 22526; 78577 23617; 79864 22084;79645 25372 1;79635 25536;79355 25655;79263 25520 1;78616 24572;77994 23739;77358 22943 33;77085 22600;76809 22263;76527 21928 16; 79782 22184;79607 25394;79380 25586;79180 25429;77452 23091;78241 22317;78757 21846;79076 21736;79330 21807;79782 22184 18; 72925 21362;72636 21364;72939 21082;72925 21362 18; 73520 21172;73678 21022;72641 20002;72342 20300;72927 21033;73520 21172 18; 73066 16488;72819 17121 1;72786 17219;72679 17265;72558 17267 1;72444 17270;72317 17233;72248 17165 1;71412 16337;69872 15035;69003 14241; 66872 12360;69003 14241 16; 64888 15337;65733 15703; 72204 19779;70294 17703; 70379 17615 1;69585 16849;69120 16489;68109 16048;65148 14765;64970 15177; 65990 15810;66915 16194; 65807 15521;67317 16158;67169 16510; 65755 15573;67290 16184;67160 16498;65650 15861;65755 15573 18; 64976 15140;67349 16160;67183 16509;70219 17801;70411 17652;70320 17558 1;69561 16831;69095 16478;68109 16048;65148 14765;65063 14961;64976 15140 18; 64048 14710;63291 16448; 64030 17496 32;64474 16432 32;62943 15793 32;62499 16856 32;64030 17496 50; 64430 16426 32;65164 14763 32;64290 14416;64248 14669 32;63646 16095 32;64430 16426 50; 79857 30306 1;79902 29824;79256 28510;79044 28178 1;78699 27638;78273 26985;77537 25995; 79376 30024;78870 28785; 78730 29971;79385 30032;78861 28811;78730 29971 18; 78537 38647;79055 38734;79870 30221;79737 29684 1;79548 29114;79191 28409;79044 28178 1;78699 27638;78273 26985;77537 25995;77304 26097;78410 28328 1;78501 28492;78598 28666;78702 28853;78553 30114;79190 30190;78565 36830;78420 38537;78537 38647 18; 79973 29651;80146 25876;79925 26037 1;79633 26137;79132 26074;78879 25705 1;77328 23443;76392 22398;74624 20301 1;73519 18990;72909 18380;71665 17200 1;70213 15822;69399 15230;67877 13930 1;67231 13378;66692 12918;66202 12473;63863 12274;63562 12697;63116 13003;62974 14238;64265 14433;65148 14765;68109 16048 1;69120 16489;69585 16849;70379 17615;72284 19691;72108 19860;72438 20206;72454 20206;72636 20015;73336 20693;73669 21023;73540 21152;75355 23018;75532 22856;75574 22897 1;75881 23246;76037 23420;76311 23795 1;76478 24023;76552 24144;76701 24384 1;77008 24880;77117 25155;77399 25666 1;77468 25792;77500 25856;77567 25983;77750 26284 1;78358 27115;78734 27692;79044 28178 1;79195 28415;79567 29150;79751 29727;79839 29766;79973 29651 18; 79900 22185;80425 22203;80151 25816 1;80131 26133;79245 26239;78879 25705 1;77328 23443;76392 22398;74624 20301 1;73519 18990;72909 18380;71665 17200 1;71422 16969;71196 16760;70983 16567;70613 16236 1;69728 15456;68993 14883;67877 13930 1;67477 13588;67118 13281;66786 12991;67090 12543;68892 14143;69264 14477 1;70155 15275;71490 16415;72248 17165 1;72317 17233;72444 17270;72558 17267 1;72679 17265;72786 17219;72819 17121;73066 16488;73478 16588;75055 17111;74908 18809;74791 18874;74322 19337;74431 19509 1;75184 20364;75870 21145;76527 21928;76820 22280 1;77001 22499;77180 22720;77358 22943 33;77994 23739;78616 24572;79263 25520 1;79355 25655;79635 25536;79645 25372;79801 23032;79813 22779;79900 22185 18; 72946 37863;69294 34508;60066 21233;62632 16841;64704 16643;69837 18568;72946 21332;73370 21249 1;73850 21725;74612 22498;75352 23299 1;76087 24093;76333 24600;76860 25545 1;77597 26868;77965 27530;78702 28853;78553 30114;79190 30190;78407 38509;73985 37973;72946 37863 18; 63668 12097 1;63909 12442;63448 12803;63075 12997;62853 14354;64272 14614; 66872 12360;65783 11399; 65783 11399 1;63904 9741;61457 6223;60162 3633 16; 63668 12097 1;62844 10917;60642 8243;59368 5883 16; 57930 3294 1;58442 4331;58819 4865;59368 5883; 48535 -15189;51425 -14576 33;52072 -11748;52648 -7364;55832 -704 1;56353 385;56709 950;57288 2023 16; 60162 3633 1;59460 2229;59158 1399;58497 -25 1;57553 -2059;57023 -3198;56079 -5232;55389 -6719;53893 -12696;68840 -8861 32;69084 -9814;70271 -9747 16; 66789 13026;67061 12669;65728 11558;63779 9313;63147 8405 1;61991 6853;60893 5095;60162 3633;59955 3212 1;59393 2050;59089 1251;58497 -25 1;57553 -2059;57023 -3198;56079 -5232;55389 -6719;53866 -12803;53405 -12686 1;53690 -11461;54014 -10286;54394 -8660 1;54716 -7282;54936 -6595;55517 -5304 1;56581 -2940;57150 -1775;58230 581 1;59005 2270;59409 3109;60302 4739 1;61395 6734;62053 7680;63448 9476 1;64504 10836;65296 11658;66305 12565;66789 13026 18; 63867 12324;66260 12533;65915 12208 1;65082 11431;64364 10656;63448 9476 1;62053 7680;61395 6734;60302 4739 1;59409 3109;59005 2270;58230 581 1;57150 -1775;56581 -2940;55517 -5304 1;54936 -6595;54716 -7282;54394 -8660 1;54014 -10286;53690 -11461;53405 -12686;51897 -12351 1;52479 -9594;53386 -5821;55832 -704 1;56353 385;56709 950;57288 2023;57447 1925;58101 3238;59358 5854;61431 9185;63867 12324 18; 54605 -14221; 53970 -15455; 55357 -15833; 55082 -17040;55135 -16152 32;55627 -16182 32;55576 -17034 16; 55382 -16595;55348 -17062; 55627 -16182 32;55577 -17010 32;55085 -16980 32;55135 -16152 32;55627 -16182 50; 53578 -17356 32;53645 -16339 32;54505 -16396 32;54438 -17413 32;53578 -17356 50; 53770 -12844;53105 -16521 1;53057 -16789;52811 -17053;52560 -17159;51753 -17500; 51054 -17752;67373 -18852; 51185 -19306; 51746 -18315;51665 -19528;50687 -19459 1;50582 -19207;50537 -19052;50537 -18783 1;50537 -18491;50668 -18303;50826 -18058 16; 54018 -17373;53995 -17721; 54676 -17429; 55330 -16966;55277 -17776; 55276 -17922 32;67398 -18725 32;67381 -18464 32;55297 -17601 32;55276 -17922 50; 67256 -19739; 57915 -20579;58055 -18472; 57279 -20218; 55670 -20396;55811 -18411; 55600 -20344; 56852 -20484;57737 -20537 16; 58101 -20559;58999 -20613 16; 59010 -20618 32;58740 -24631 32;56548 -24483 32;56818 -20470 32;59010 -20618 50; 58155 -20084;61271 -20290 1;61451 -20302;61505 -20580;61493 -20760;58129 -20546;58155 -20084 18; 55913 -18381;57838 -18517;57702 -20540;55765 -20404;55913 -18381 18; 55151 -18426;50801 -18133; 51914 -21690; 55438 -22789;52172 -22558; 55524 -25250 1;55512 -25369;55506 -25436;55494 -25555 1;55483 -25664;55527 -25741;55612 -25808 1;55916 -26049;56032 -26128;56368 -26303;56467 -25311;55524 -25250 18; 56493 -25285;56299 -27389;53571 -27186 1;53309 -27166;53030 -27039;52855 -26898 16; 55054 -25197;54930 -26932;52833 -26788; 55499 -25506 1;55491 -25677;55534 -25747;55612 -25808 1;55843 -25991;55965 -26080;56157 -26189; 52392 -25433;52350 -26259; 52359 -26212 1;52368 -26443;52703 -26777;52864 -26789; 55177 -25175;56489 -25285;56285 -27376;53460 -27173;53108 -27050;52854 -26908;55041 -27046;55177 -25175 18; 52609 -22712;52547 -23585;52090 -23568 1;51962 -22653;51799 -22143;51500 -21251;52286 -21303;52211 -22444 16; 52322 -23113; 52609 -22712;52547 -23585;52090 -23568 1;51962 -22653;51799 -22143;51500 -21251;52286 -21303;52211 -22444;52609 -22712 18; 51744 -18315;51665 -19528;50687 -19459 1;50582 -19207;50537 -19052;50537 -18783 1;50537 -18491;50650 -18331;50808 -18086;50809 -18243;51744 -18315 18; 50677 -19476;51689 -19520;51753 -18297;55119 -18531;55133 -18255;55697 -18311;55446 -22678;52197 -22443;52282 -21308;51436 -21247;50677 -19476 18; 64363 45872;71796 47076;72005 46011;77553 46762;77850 43447;75582 43080;72738 41196;73052 37864;68655 34461;65445 37759;64363 45872 18; 29894 88243 1;29547 86998;30558 86212;31485 86443 1;31824 86528;32027 86292;32374 86245 1;32933 86169;33417 86525;33558 87071 1;33699 87617;33332 88100;32806 88305 1;32576 88395;32339 88421;32201 88626 1;31608 89507;30199 89340;29894 88243 18; 32472 87293; 31115 87848; 30215 88168 1;29941 87186;30801 86529;31532 86711 1;31800 86778;31985 86555;32258 86518 1;32699 86458;33204 86652;33315 87082 1;33427 87513;32964 87968;32549 88130 1;32368 88201;32157 88259;32048 88421 1;31580 89116;30456 89033;30215 88168 18; 30216 88095; 33312 87034; 29894 88243 1;29547 86998;30558 86212;31485 86443 1;31824 86528;32027 86292;32374 86245 1;32933 86169;33417 86525;33558 87071 1;33699 87617;33332 88100;32806 88305 1;32576 88395;32339 88421;32201 88626 1;31608 89507;30199 89340;29894 88243 18; 30428 81724;29873 81650;29244 81884;29540 82378;29441 83587;30195 83661;30428 81724 18; 30146 84297;29992 85795;29367 86239 1;28927 86526;28295 86639;28023 86190;27742 85726;29286 84791;29367 84215;30146 84297 18; 29873 82131; 29700 84623; 28479 85894; 30146 84297;29992 85795;29367 86239 1;28927 86526;28295 86639;28023 86190;27742 85726;29286 84791;29367 84215;30146 84297 18; 30428 81724;29873 81650;29244 81884;29540 82378;29441 83587;30195 83661;30428 81724 18; 29253 81896;29528 82371;29303 84772;27753 85697;27571 85391;28990 84541;29178 82496;28941 82020;29253 81896 18; 27928 81153;27566 80724;25543 80564;25059 80975;25290 81280;24860 81712;24543 81413;24075 81810;23803 85215;24097 85391;24164 85431;24454 82057;25768 80967;27928 81153 18; 24543 85672;25320 86207;25345 85475;24235 84759; 24173 85438;24223 84759;25345 85487;25321 86189;24173 85438 18; 25309 86202;25250 87249;25543 87436;25465 88631;25264 88818;25051 88810;24771 89070;24347 88842;24384 88286; 25272 87251;24507 86868; 24433 86819;25272 87250;25309 86202;24532 85708;24433 86819 18; 25038 88336; 24483 86843;25248 87263;25507 87413;25543 87436;25465 88631;25264 88818;25051 88810;24771 89070;24347 88842;24383 88298;24483 86843 18; 24347 88842;22417 87550; 22398 87546;24360 88842;24372 88250;22460 86843;22238 87164;22386 87324;22398 87546 18; 22309 89828;22228 89809;22228 89824;19209 89072;18560 86877;18793 86839;18993 86955;19251 87126;19238 87468;22262 89570;22447 87608;22694 87741;22571 89471 1;22558 89634;22455 89808;22309 89828 18; 21191 94198;23267 92541;23197 93614;23350 93561;24567 93117;24681 92093;26760 92324;26780 92108 1;26786 92049;26792 91991;26799 91912 1;26843 91449;27240 91074;27703 91117 1;28133 91157;28494 91629;28432 92085;28377 92496;28560 92523;31665 92868;32437 92229;32224 91912;31773 91382;32136 91106 1;32433 90904;32606 90889;32757 90967;34894 88903;33447 87792 1;33309 88018;33079 88199;32806 88305 1;32576 88395;32339 88421;32201 88626 1;31608 89507;30199 89340;29894 88243 1;29891 88231;29887 88218;29884 88206;29219 88670 1;29327 88832;29368 89037;29314 89240 1;29215 89615;28831 89838;28456 89739 1;28081 89640;27857 89255;27956 88880;25485 88323;25465 88631;25264 88818;25051 88810;24771 89070;24347 88842;24188 88736;22729 87759;22680 87927;22571 89471 1;22556 89663;22415 89870;22228 89824;19209 89072;19619 90416;20784 90677;21137 92519;20672 92630;20640 92759;21191 94198 18; 33427 87819;34849 88918;34892 88857;35028 88746;36460 87540;36380 87404;35979 86953 1;36265 86699;36604 86912;36920 87129;36993 87091;37546 86625;37527 84837;37336 84839;37336 84809;37168 84832;35868 84869 32;35856 84466 32;37303 84425 32;37304 84452;37330 84364;37311 82722;36938 82738;36936 82209;36934 80264;37073 77394;36934 76897;36547 77008;36301 77420;35732 77080;34077 77010;33995 77971 1;33979 78143;34153 78231;34325 78250;34263 79635;33831 79702 1;33755 79397;33539 79138;33230 79005 1;32679 78768;32040 78991;31824 79542;31421 79336;29954 80965;29052 81294;29252 81881;29873 81650;30428 81724;30195 83661;29441 83587;29364 84233;29367 84215;30146 84297;29992 85795;29367 86239;30264 86741 1;30587 86453;31046 86333;31485 86443 1;31824 86528;32027 86292;32374 86245 1;32933 86169;33417 86525;33558 87071 1;33624 87328;33578 87571;33456 87777;33427 87819 18; 29283 81872;29100 81288;28867 81362;28283 81575;27910 81131;27674 81131;25768 80967;24454 82057;24222 84755;24329 84827;25345 85487;25321 86189;25301 86351;25250 87246;25323 87306;25507 87413;25543 87436;25482 88373;27943 88939 1;27947 88919;27951 88900;27956 88880 1;28055 88505;28440 88282;28815 88381 1;29009 88432;29163 88560;29252 88724;29908 88291 1;29903 88275;29899 88259;29894 88243 1;29705 87564;29920 87021;30305 86706;29426 86197;29367 86239 1;28927 86526;28295 86639;28023 86190;27742 85726;29286 84791;29367 84215;29424 83486;29528 82371;29253 81896;29283 81872 18; 14815 90792;16045 90896 32;16431 90928;19047 101772 1;19210 102446;19162 103096;18590 103487 1;18118 103809;17720 103979;17154 103897;15241 103685 32;14272 101994; 14937 83031 1;16201 87001;16684 89316;17695 93348 1;18525 96658;19009 98510;19768 101837 1;20006 102881;19606 103868;18644 104339 1;18101 104604;17751 104826;17152 104746;14622 104408;14179 103580;13926 103536;13968 103187;13746 102772;10255 102476;10155 103656;9673 103615;9773 102439;5083 102040;5000 103020;3805 102919;3893 101875;1982 101714;1904 102646;-10566 101461;-21513 100284;-21425 99305;-21733 98925;-26115 98594;-26204 99767;-28371 99603;-28290 98530;-30258 98381;-30338 99432;-35511 99038 1;-36732 98945;-37243 98055;-38465 97989 1;-39872 97913;-40660 97870;-42067 97794 1;-42863 97751;-43557 96881;-43264 96139;-42113 93224;-40476 92684;-39217 89588;-35699 81278 1;-34938 79479;-32648 78178;-31558 78105;-31712 79529;-27639 79900;-27519 78801;-27239 78832;-27277 79178;-22806 80075;-22700 79545 1;-21289 79842;-20491 80013;-19053 80127 1;-13258 80585;-10012 80910;-4206 81191 1;-2212 81288;-405 81608;1528 81109 1;5080 80193;6464 79714;9961 78609 1;11121 78242;12141 78442;12996 79307 1;14092 80415;14523 81520;14937 83031 18; 14619 104410;13816 104303;13917 103542;14197 103579; 14165 103992; 14619 104410;13816 104303;13917 103542;14197 103579;14619 104410 18; 13051 101987 32;13688 102041 32;13656 102414 32;13019 102360 32;13051 101987 50; 10483 101768 32;11120 101822 32;11088 102195 32;10451 102141 32;10483 101768 50; 12436 101961;11781 101896 1;11762 102076;11893 102238;12074 102258 1;12254 102277;12416 102146;12436 101965 1;12436 101964;12436 101963;12436 101961 18; 2118 100835 32;2755 100889 32;2723 101262 32;2086 101208 32;2118 100835 50; 4071 101028;3416 100963 1;3397 101143;3528 101305;3709 101325 1;3889 101344;4051 101213;4071 101032 1;4071 101031;4071 101030;4071 101028 18; 4686 101054 32;5323 101108 32;5291 101481 32;4654 101427 32;4686 101054 50; 8818 101406 32;9455 101460 32;9423 101833 32;8786 101779 32;8818 101406 50; 6469 101205 32;8051 101340 32;8019 101713 32;6437 101578 32;6469 101205 50; 10197 103083;9717 103042; 9936 103353; 4780 102769; 5041 102577;4561 102536;4524 102973 16; 138 101692;443 98377;844 97941;262 97406;-447 98122;-763 101589; 138 101692;443 98377;844 97941;285 97427;-396 98168;-741 101581;33 101658; -621 100522;223 100605; -499 99283;315 99363; -212 100540; -107 99318; 1657 102368; 1918 102176;1438 102135;1401 102572 16; 66 97095 32;1087 97173 32;1166 96139 32;145 96061 32;66 97095 50; 913 97915;965 97156; 1035 96127;1086 95447; 305 101676;759 101449;1073 97924; 305 101676;759 101449;1073 97924;572 98350;305 101676 18; 881 95272;5320 95577;5364 94942; 1885 97375 1;1944 96757;2541 96368;3159 96424;3059 97525; 1885 97375 1;1944 96757;2541 96368;3159 96424;3059 97525;1885 97375 18; 5584 96651 32;6880 96759 32;6790 97833 32;5494 97725 32;5584 96651 50; 5584 96651 32;6880 96759 32;6790 97833 32;5494 97725 32;5584 96651 50; 9100 96938 32;10730 97075 32;10640 98149 32;9010 98012 32;9100 96938 50; 9100 96938 32;10730 97075 32;10640 98149 32;9010 98012 32;9100 96938 50; 11944 97401;12138 95103 32;6398 94618;6689 90147; 4398 90453;4435 89852;6891 89980; 5684 95826;5950 91742;4507 91648; 5149 91566;5194 90877;4534 90834;4489 91530;5149 91566 18; 5154 91497;5194 90877;4534 90834 16; 5707 93136 32;5677 93607 32;5277 93581 32;5307 93110 32;5707 93136 50; 5646 93605;5277 93581 32;5307 93110 32;5677 93134 16; 10456 95590; 7372 95504; 5885 95806;11118 96204 1;11648 96240;11715 96847;11825 97367; 5188 94955;4669 94567;4805 91869;5712 91936;5638 93127;5540 93125;5307 93110 32;5277 93581 32;5607 93602;5545 94984;5188 94955 18; 4530 90834;4583 89979;6633 90057;6675 90354;6398 94618;12138 95103;11946 97375;11755 97027 1;11665 96613;11534 96232;11118 96204;5885 95806;6170 91549;5141 91479;5184 90877;4530 90834 18; 10373 98121;10355 98426;11734 98548;11812 97205;11742 96967 1;11652 96577;11514 96231;11118 96204;5885 95806;5479 95757;1274 95469;995 98923;821 100720;1614 100799;1876 97388;1906 97246 1;2029 96704;2585 96372;3159 96424;3059 97525;4406 97597;4476 96568;6884 96760;6796 97798;7965 97929;8035 96873;9108 96934;9432 96966;10730 97075 32;10640 98149 32;10500 98137;10373 98121 18; -3945 99805;-4094 100952;-904 101367; -4001 100902;-930 101277;-852 100230;-3818 99838;-4001 100902 18; -10579 101443 32;-6725 101809 32;-6681 101342 32;-10535 100976 32;-10579 101443 50; -6941 101539; -8581 101400; -10326 101216; -10579 101443 32;-6725 101809 32;-6681 101342 32;-10535 100976 32;-10579 101443 50; -5316 97173;-5586 99370;-9071 98908 32;-8703 96147 32;-5252 96607 32;-5327 97172 32;-5316 97173 18; -4400 97253;-4731 99711;-5613 99600 32;-5327 97130;-4400 97253 18; -4740 99668;-4420 97292;-1842 97636 32;-2043 99148;-1166 99261 32;-1273 100119 32;-4740 99668 18; -5650 99376;-5856 100933; -6526 99281;-6730 100818; -6574 100641;-10156 100165; -10217 100346;-9819 97143;-10772 97072; -9854 95983;-9573 93874;-4719 94521;-4132 95128;-3059 95251;-3195 96438; -8746 96254;-10067 96078; -8136 94466; -7734 94309;-7799 94793;-8443 94706; -7795 96263 1;-7795 95905;-7972 95724;-8171 95426 1;-8337 95177;-8468 94998;-8424 94702;-8330 94721;-7799 94793;-7734 94309;-9462 94030;-9732 95949;-7795 96263 18; -3006 96438;-3111 97450;-4463 97250;-5283 97127;-5240 96621;-7796 96281;-7797 96201 1;-7816 95883;-7984 95706;-8171 95426 1;-8337 95177;-8468 94998;-8424 94702;-8326 94722;-7799 94793;-7734 94309;-7494 94151;-4719 94521;-4132 95128;-3059 95251;-3132 95889;-3006 96438 18; -5796 100352;-6500 100259; -6224 100283; -3998 99769;-4129 100982;-787 101427;-456 98147;259 97379;870 97841;1123 95163;-2855 95085;-3003 96463;-3016 96533;-3111 97450;-3142 97445;-2939 97490;-1842 97636 32;-2043 99148;-1166 99261 32;-1273 100119 32;-3804 99790;-3998 99769 18; -11706 101329 32;-14033 101079 32;-13980 100589 32;-11653 100839 32;-11706 101329 50; -15320 100940 32;-17941 100658 32;-17888 100168 32;-15267 100450 32;-15320 100940 50; -19196 100522 32;-19738 100465 32;-19685 99975 32;-19143 100032 32;-19196 100522 50; -19452 100247; -17655 100448; -15571 100657; -13678 100875; -11968 101058; -15320 100940 32;-17941 100658 32;-17888 100168 32;-15267 100450 32;-15320 100940 50; -19196 100522 32;-19738 100465 32;-19685 99975 32;-19143 100032 32;-19196 100522 50; -11706 101329 32;-14033 101079 32;-13980 100589 32;-11653 100839 32;-11706 101329 50; -17154 97527;-17242 98648;-15159 98811;-15075 97732; -14962 97754 1;-14455 98172;-13670 98310;-13069 97877; -11630 98042;-12951 97939;-13085 99651; -14116 98845; -15338 99604; -13601 99600 1;-13776 99594;-13935 99718;-13951 99892;-13968 100067;-11613 100310 1;-11664 100050;-11766 99775;-12029 99750;-13601 99600 18; -17353 98759;-17429 99633;-12240 100245;-11613 100310 1;-11664 100050;-11766 99775;-12029 99750;-12960 99661; -11412 98182;-11386 97894; -10921 97254;-10997 98230; -12315 99240; 16507 90767;17051 90832 1;17254 91606;17465 92431;17695 93348 1;18525 96658;19009 98510;19768 101837 1;20006 102881;19606 103868;18644 104339 1;18101 104604;17751 104826;17152 104746;14622 104408;14179 103580;13926 103536;13968 103187;13746 102772;10255 102476;10202 103105;9727 103043;9717 103042;9771 102467;9773 102439;5083 102040;5037 102586;4880 102563;4561 102536;4524 102973;4309 102962;3805 102919;3893 101875;4089 100972;4686 101022;4680 101121;4654 101427 32;5291 101481 32;5323 101108 32;5321 101108;6469 101205 32;6437 101578 32;8019 101713 32;8051 101340;8791 101368;8812 101476;8786 101779 32;9423 101833 32;9455 101460;9900 101462;10000 101470 32;9978 101731 32;10475 101773;10473 101886;10451 102141 32;11088 102195 32;11120 101822;11799 101898;11795 102030 1;11833 102150;11940 102243;12074 102258 1;12254 102277;12416 102146;12436 101965 1;12436 101964;12436 101963;12436 101961;13070 101996;13042 102094;13019 102360 32;13656 102414 32;13688 102041;14378 102120;14610 102585;15241 103685 32;17154 103897 1;17720 103979;18118 103809;18590 103487 1;19162 103096;19210 102446;19047 101772;18109 97883;16507 90767 18; 3907 101907;4129 101006;4029 101160 1;3966 101271;3843 101339;3709 101325 1;3528 101305;3397 101143;3416 100963;3437 100965;2747 100883;2748 100975;2723 101262 32;2086 101208 32;2118 100835 32;1637 100784;835 100667;766 101367;759 101449;305 101676;-861 101565;-1249 101365;-4129 100982;-3998 99769;-4797 99726;-5611 99603;-5747 99381;-6426 99294;-6697 100627;-10385 100219;-10535 100976 32;-6681 101342 32;-6725 101809;-6212 101875;1347 102593;1397 102565;1412 102443;1438 102135;1918 102176;1954 102048;1982 101714;3558 101847;3907 101907 18; -3115 97417;-3015 96457; -10379 100368;-9969 97297;-13249 97018;-13282 97084 1;-13410 97340;-13685 97502;-13987 97476 1;-14309 97449;-14564 97216;-14634 96917;-17097 96722 32;-17275 97523;-17034 97543;-17109 98535;-15270 98678;-15193 97730;-14944 97745;-14929 97781 1;-14421 98178;-13657 98301;-13069 97877;-12957 98011;-13085 99651;-12917 99665;-12029 99750 1;-11773 99774;-11670 100035;-11617 100289;-17424 99632;-17899 100162;-17689 100189;-15267 100450 32;-15320 100940 32;-14031 101094;-14018 100938;-13980 100589 32;-11653 100839 32;-11706 101329 32;-11723 101327;-10583 101464;-10515 100964;-10379 100368 18; -17353 98711;-17421 99612;-13967 100043;-13956 99946;-13951 99892 1;-13935 99718;-13776 99594;-13601 99600;-13155 99643;-13004 97847;-13161 97938 1;-13749 98295;-14481 98151;-14962 97754;-15089 97916;-15159 98811;-17353 98711 18; -18848 96611;-17817 96692;-17508 92754;-17910 92722; -17825 92651 32;-17915 93785 32;-18471 93741 32;-18381 92607 32;-17825 92651 50; -18130 92930;-18171 93439; -18337 92177;-17442 92247;-17382 89877; -16859 89920;-16907 91827;-16518 91857; -17829 89844 32;-16608 89948 32;-16495 88629 32;-17716 88525 32;-17829 89844 50; -18921 96112;-18034 88400; -20118 88302 32;-20160 88919 32;-20900 88869 32;-20858 88252 32;-20118 88302 50; -20156 88438;-17715 88604; -18292 96124; -17934 93990; -18848 96611;-17817 96692;-17508 92754;-17910 92722;-17912 93801;-18525 93737;-18848 96611 18; -17825 92651 32;-17915 93785 32;-18471 93741 32;-18381 92607 32;-17825 92651 50; -17786 91004; -19082 98024;-19225 99172; -19933 97949;-20067 99023; -20259 98947;-20383 100405;-19752 100473;-19724 100340;-19685 99975 32;-19143 100032 32;-19196 100522 32;-19104 100543;-17973 100665;-17930 100552;-17893 100217;-17397 99638;-17418 99502;-17353 98764;-17372 98758;-17276 97523;-17256 97508;-17054 96741;-16825 92825;-16650 92816;-16518 91857;-16907 91827;-16859 89927;-17357 89928;-17388 90103;-17442 92247;-18362 92174;-18381 92607;-17825 92651 32;-17831 92727;-17663 92742;-17508 92754;-17817 96692;-18777 96617;-18962 98050;-19844 97963;-20259 98947 18; -18376 92163;-17443 92250;-17373 89921;-17800 89851;-17748 88795;-18053 88761;-18376 92163 18; -10558 93579;-9657 93690;-9867 96071;-8720 96293;-8800 96878;-9071 98908 32;-6462 99254;-6524 99775;-6697 100627;-10302 100228;-9830 97194;-10780 97033;-10558 93579 18; -19813 96006 32;-19722 95278 32;-18960 95373 32;-19051 96101 32;-19813 96006 50; -19394 95701; -19813 96006 32;-19722 95278 32;-18960 95373 32;-19051 96101 32;-19813 96006 50; -20258 91601 32;-19806 91631 32;-19919 93306 32;-20371 93276 32;-20258 91601 50; -20144 93014; -20048 91871; -20258 91601 32;-19806 91631 32;-19919 93306 32;-20371 93276 32;-20258 91601 50; -20066 90930;-19987 89755; -20362 93458;-21169 93404; -21148 93398;-21695 93361;-21737 93989;-25406 93744;-25024 88018; -24829 87867;-28988 87589; -29095 87387;-29210 89104; -25850 91199; -25885 91713; -25579 93903;-26702 93828;-26396 89242;-25293 89316; -26067 92901; -26504 93546; -25692 93590; -25579 93903;-26702 93828;-26396 89242;-25293 89316;-25579 93903 18; -21383 95126;-21577 98228; -21549 95125;-21526 94765;-23530 94636; -23190 94668;-21531 94761;-21550 95162;-23215 95057;-23190 94668 18; -24600 94527;-25740 94456; -26546 96871; -26472 95785; -26385 94762; -26199 97908 32;-25971 94286 32;-26737 94238 32;-26965 97860 32;-26199 97908 50; -26199 97908 32;-25971 94286 32;-26737 94238 32;-26965 97860 32;-26199 97908 50; -27556 91123;-27268 91144;-27207 90310 1;-27170 89812;-27381 89508;-27716 89137; -27556 91123;-27268 91144;-27207 90310 1;-27170 89812;-27381 89508;-27716 89137;-27852 90802;-27550 90824;-27556 91123 18; -27642 92060; -27655 92912;-27720 93780; -27975 95749;-28039 96584; -27988 92936;-27649 92962;-27771 94547;-28085 94523; -28629 97057;-28677 97685;-27979 97738;-27851 96057;-28531 95404; -28358 96292; -28444 97303; -28629 97057;-28678 97694;-27980 97747;-27851 96057;-28531 95404;-28629 97057 18; -27988 92936;-27649 92962;-27771 94547;-28455 94500;-28360 93112;-28037 93121;-27988 92936 18; -21373 100270;-21268 99240;-21650 98771;-26147 98443; -26499 99438; -28122 99325; -30573 99106; -28226 99186;-28166 98398;-30377 98230;-30435 99009; -26155 99060 1;-26313 98855;-26606 98828;-26837 98959 1;-27077 99095;-27069 99459;-26947 99706;-26208 99762 32;-26155 99060 18; -26947 99706 1;-27069 99459;-27056 99160;-26824 99011 1;-26593 98863;-26313 98855;-26155 99060;-26099 98326 32;-27025 98256 32;-27134 99692 32;-26947 99706 18; -28796 97000;-28824 97506;-29968 97420 32;-30512 97379;-31887 98233;-32201 98207 48; -34660 98208;-34563 97037;-35422 96967 16; -32148 98211;-34643 98007 16; -21276 100315;-20342 100403;-20203 98941;-20050 97873;-19799 95991;-19712 95262;-18931 95362;-18150 88724;-20081 88582;-20111 88946;-20136 89334;-20272 91592;-19803 91641;-19920 93294;-20401 93263;-21111 93455;-21677 93422;-21738 94076;-25600 93903;-26702 93828;-26724 94239;-25971 94286 32;-26006 94841;-25871 94888;-24232 94991 32;-24213 94687 32;-23491 94733;-23494 94638;-21526 94765;-21549 95125;-21739 98184;-26994 97851;-27026 98263;-26089 98337;-21650 98771;-21268 99240;-21303 99587;-21276 100315 18; -26402 89252;-25261 89332;-25175 87962;-29024 87685;-29092 89067;-27722 89159;-27635 89229 1;-27347 89560;-27173 89854;-27207 90310;-27268 91144;-27556 91123;-27594 91329;-27605 91472 32;-27924 91448 32;-28035 92923;-27741 92953;-27708 93771;-27721 93894;-27771 94547;-28085 94523;-28139 94667;-28175 95143 32;-28506 95118 32;-28530 95433;-28473 95460;-28125 95794;-27891 96606;-27907 96788;-27979 97738;-28677 97685;-30552 97504;-31852 98350;-31913 99336;-31746 99325;-30349 99423;-30289 98706;-30377 98230;-28166 98398;-28301 99127;-28354 99379;-28371 99603;-27098 99699;-27001 98298;-26961 97802;-26737 94238;-26715 94163;-26684 93824;-26437 89488;-26402 89252 18; -27106 99677;-27002 98296;-26120 98351; -32704 98325 32;-34101 98211 32;-34145 98747 32;-32748 98861 32;-32704 98325 50; -32761 98894;-32714 98323; -34163 98778;-34119 98238; -42143 97594; -40428 97729; -40021 96348; -39984 94843; -41983 96237; -41810 94522; -40934 93128; -38639 97816; -36937 98260; -42032 98827;-42081 99506;-42723 99444 1;-42908 99426;-43025 99247;-43007 99062 1;-42990 98891;-42832 98773;-42661 98790;-42032 98827 18; -40663 98963;-40700 99592;-39960 99654 1;-39807 99667;-39689 99535;-39676 99382 1;-39659 99185;-39812 99017;-40009 99000;-40663 98963 18; -42802 94909;-41470 95008;-40470 92664; -40285 92219;-39686 92462;-39296 93385;-38146 93852;-37582 92464;-37062 92675; -31802 98336;-31871 99313;-32094 99298;-35511 99038 1;-36732 98945;-37243 98055;-38465 97989 1;-39872 97913;-40660 97870;-42067 97794 1;-42863 97751;-43557 96881;-43264 96139;-42777 94905;-42702 94916;-41470 95008;-40470 92664;-40309 92219;-40174 92264;-39686 92462;-39296 93385;-38146 93852;-37582 92464;-37062 92675;-37182 92993;-37998 94823;-36297 97022;-35454 97091;-35159 96988;-34563 97037;-34658 98179;-34104 98153;-34106 98277;-34145 98747 32;-34006 98758;-32748 98861 32;-32744 98808;-32685 98314;-31802 98336 18; -42032 98827;-42081 99506;-42723 99444 1;-42908 99426;-43025 99247;-43007 99062 1;-42990 98891;-42832 98773;-42661 98790;-42032 98827 18; -40663 98963;-40700 99592;-39960 99654 1;-39807 99667;-39689 99535;-39676 99382 1;-39659 99185;-39812 99017;-40009 99000;-40663 98963 18; -42797 94895;-41488 95008;-40476 92714;-42125 93228;-42797 94895 18; -37727 87650 32;-36682 85308 32;-34257 86390 32;-35301 88731 32;-37727 87650 50; -35732 86451; -36239 87586; -34212 86107; -34188 87303;-34107 86632; -33745 86114;-33236 86175;-32955 83838; -27219 87728;-27079 84378;-32977 83855;-33256 86158;-33736 85922;-34303 86611;-34268 87283;-35397 96941;-34977 97003;-34563 97037;-34648 98059;-31908 98250;-31484 98110;-30552 97504;-28828 97670;-28785 96976;-29257 96951;-31652 96771 32;-31365 92965 32;-31145 92982 32;-30841 88944 32;-29226 89066;-29029 87520;-27219 87728 18; -33740 80722; -33231 81999;-35113 81326 1;-34796 80579;-34463 80270;-33842 79747;-32744 79870;-32806 80561; -34928 81301; -33830 81708; -32941 82103;-35113 81326 1;-34796 80579;-34463 80270;-33842 79747;-32744 79870;-32806 80561;-32941 82103 18; -32942 80529;-33527 85984; -40294 92224;-39683 92468;-39265 93402;-38166 93864;-37572 92468;-37101 92669;-35304 88726;-35541 88624;-37727 87650 32;-36682 85308 32;-34257 86390 32;-34584 87123;-34347 87204;-34290 86619;-34281 86584;-33736 85922;-33681 85949;-33649 85943;-33518 85533;-33131 82009;-33339 81960;-35113 81326 1;-34796 80579;-34463 80270;-33842 79747;-32744 79870;-31712 79530;-31561 78096 1;-32575 78186;-34906 79405;-35699 81278;-37062 84497;-40294 92224 18; -39682 92442;-37710 87644; -36686 85324;-35082 81364; -27081 79615; -22231 84883;-21844 80883; -21108 84992;-20728 81064; -20907 81209;-19157 81350;-19196 81757; -18689 81255; -22785 84682;-20903 84864; -11227 82322;-11157 81303; -9843 82384;-9780 81451;-10791 81383; -11541 81325;-20858 80618;-20896 81098 16; -16772 80938 32;-12847 81236 32;-12885 81733 32;-16810 81435 32;-16772 80938 50; -17942 80849 32;-17086 80913 32;-17124 81410 32;-17980 81346 32;-17942 80849 50; -11578 82061 1;-11971 81926;-12240 81685;-12294 81272;-11566 81321;-11578 82061 18; -12146 81753; -19104 81765;-11615 82259;-11591 82061;-11652 82034 1;-12004 81895;-12243 81659;-12294 81272;-12861 81222;-12855 81345;-12885 81733 32;-16810 81435 32;-16772 80938 32;-17093 80914;-17093 81000;-17124 81410 32;-17980 81346 32;-17942 80849 32;-17923 80850;-20880 80630;-20893 81099;-19129 81309;-19104 81765 18; -10302 81879; -10843 82324 32;-9840 82392 32;-9777 81458 32;-10780 81390 32;-10843 82324 50; -6827 81748; -8303 81622;-6333 81756; -7563 86371 32;-6703 86430 32;-6658 85768 32;-7518 85709 32;-7563 86371 50; -7588 86260;-10257 86079;-10237 85780; -4840 83882; -4975 85942; -4876 84597; -6690 86325;-4576 86468;-4529 85765 32;-4403 83905 32;-4364 83324;-3759 83365; -5321 82426; -3764 83206;-3671 81835;-6354 81652;-6641 85745 16; -3764 83206;-3671 81835;-6354 81652;-6641 85745;-6678 86273;-4602 86401;-4390 83307;-3764 83206 18; 7779 87400;7770 86135;-3890 85295 32;-3760 83242 32;-1236 83401;-1208 83017;6065 83542;6106 82674;6727 82671;6728 82865;10420 82837;11395 83100;11388 83425;11726 83432;11719 83796;12026 83802;12228 84012;12218 84536;12010 84736;11747 84731;11692 87386;7779 87400 18; -2081 81944;-1228 82006; -2118 83289;-2069 82522;-808 82603;-367 83105; -2118 83289;-2069 82522;-808 82603;-367 83105;-1206 83033;-1258 83422;-2118 83289 18; -1254 82166;-659 82204;-859 81870;-1242 81845; -1254 82166;-659 82204;-871 81858;-1242 81845;-1254 82166 18; 7636 80945;7661 80340;8043 79958;10301 80019;10906 80500; 10819 79810; -358 81894 1;504 81947;1007 81859;1838 81623 1;1939 81940;1722 82357;1394 82412 1;885 82497;589 82411;74 82388 1;-147 82378;-216 82167;-321 81972;-358 81894 18; 1432 82015; 124 82128; -358 81894 1;504 81947;1007 81859;1838 81623 1;1939 81940;1722 82357;1394 82412 1;885 82497;589 82411;74 82388 1;-147 82378;-216 82167;-321 81972;-358 81894 18; 2485 81446;6321 80360;6050 82038;3225 81890 1;2935 81875;2683 81820;2559 81557;2485 81446 18; 2077 83247 1;2099 82889;2399 82604;2756 82630;6074 82872; 5823 83308;5836 83022; 5614 82829;6067 82873;6041 83536;5579 83510;5614 82829 18; 4463 82760;4414 83426; 4445 82760;5649 82847;5597 83519;4410 83458;4445 82760 18; 2064 83248;4428 83431;4454 82786;4308 82743;2756 82630 1;2422 82606;2137 82854;2084 83180;2064 83248 18; 3154 81574; 5614 81268; 5886 82053;6127 80734;6875 80751; 6782 81189; 7838 81617; 8532 81253;8558 80608;10024 80667;9996 81366; 8567 80791;9998 80843; 7733 80936 32;8073 80950 32;8093 80474 32;7753 80460 32;7733 80936 50; 10464 81398 32;10804 81412 32;10824 80936 32;10484 80922 32;10464 81398 50; 8065 80927;8426 80942; 8097 80502;8440 80516; 10118 81342;10473 81356; 10485 80953;10129 80939; 2485 81446;6321 80360;6050 82038;3225 81890 1;2935 81875;2683 81820;2559 81557;2485 81446 18; 5588 83485;5618 82851; 12945 81400; 14213 84549; 10831 81392 32;11337 81413 32;11267 83070 32;10765 82962 32;10831 81392 50; 11315 82040 1;11308 82182;11436 82248;11568 82301 1;12497 82676;13020 82884;13950 83257 1;14184 83351;14481 83049;14412 82807;14061 81895 1;13513 80896;13297 80185;12377 79513 1;11723 79035;11130 78831;10353 79059 1;9259 79380;8631 79511;7544 79853;7492 80263 16; 11760 83436;11766 83157;14857 84413 1;15297 85599;15398 86314;15664 87551;14936 87501;14997 86021;12408 85830;12325 86952;12026 86929;11895 88676;11348 88635 16; -9780 81441;-8279 81519;-6360 81667;-6657 85733;-7538 85689;-7555 86116;-10103 86021;-9780 81441 18; -3677 81831;-3773 83245;-2116 83349;-2113 83209;-2069 82522;-808 82603;-424 83040;-328 83081;2089 83271;2098 83117 1;2175 82823;2444 82607;2756 82630;6067 82871;6093 82686;6756 82704;6756 82887;10394 82826;10795 82948;10839 81395;10737 81400;10467 81386;10118 81366;9874 81358;9888 80962;8667 80917;8654 81255;8407 81245;8421 80973;8065 80949;7832 80938;7442 80924;7485 80257;7520 79864;6307 80344;6290 80606;6054 81068;5889 82036;3018 81871;2882 81836 1;2743 81790;2630 81708;2559 81557;2485 81446;2520 81436;1838 81623 1;1939 81940;1722 82357;1394 82412 1;885 82497;589 82411;74 82388 1;-147 82378;-216 82167;-321 81972;-358 81894;-859 81870;-659 82204;-1254 82166;-2032 82088;-2058 81774;-3677 81831 18; 11315 82040 1;11308 82182;11436 82248;11568 82301 1;12497 82676;13020 82884;13950 83257 1;14184 83351;14481 83049;14412 82807;14061 81895 1;13513 80896;13297 80185;12377 79513 1;11723 79035;11130 78831;10353 79059 1;9259 79380;8631 79511;7544 79853;7492 80263;8059 79971;10246 80020;10935 80509;11356 81354;11315 82040 18; 10116 81343;10468 81328;10489 80927;10759 80933;10779 80625;10239 80186;8115 80143;7836 80414;7849 80471;8090 80475;8070 80896;8426 80914;8443 80485;10149 80550;10116 81343 18; 11284 87427;11037 90536 32;12591 90647; 15306 87143; 16588 90820;16342 89611;16058 89290;16021 90179;12061 89895;12115 89142;11280 89082; 11687 89425; 12034 89930 32;11260 89875 32;11316 89081 32;12090 89136 32;12034 89930 50; 11188 90442;16553 90791;16561 90686;16342 89611;16058 89290;16021 90179;12059 89920;11238 89861;11188 90442 18; 12386 85823;11776 85291;11697 87376;11435 87402;11322 88632;11889 88676;11895 88676;12026 86929;12325 86952;12394 86023;12386 85823 18; 11331 88637;11305 89082;12099 89152;12064 89893;16025 90181;16042 89283;16348 89606;16601 90836;17063 90862;17041 90698 1;16408 88111;15898 86049;14937 83031 1;14897 82887;14858 82746;14817 82609;14369 82696;14412 82807 1;14481 83049;14184 83351;13950 83257 1;13020 82884;12497 82676;11568 82301 1;11484 82267;11401 82228;11354 82167;11281 83043;11398 83086;11435 83431;11768 83438;11762 83160;11852 83192;14857 84413 1;15297 85599;15398 86314;15664 87551;14936 87501;14997 86021;12408 85830;12325 86952;12026 86929;12015 87077;11910 88690;11331 88637 18; 11771 85323;12382 85835;15003 86026;14929 87488;15663 87556;15643 87452 1;15392 86275;15285 85567;14857 84413;11766 83157;11760 83436;11765 83793;12036 83806;12234 84009;12240 84534;12049 84750;11783 84768;11771 85323 18; 14385 102117;15298 103733;15751 103741;17154 103897 1;17720 103979;18118 103809;18590 103487 1;19162 103096;19210 102446;19047 101772;16431 90928;16045 90896 32;14815 90792;14797 91112;14591 94335 32;13719 94279 32;13697 94629 32;11990 94520 32;12012 94175 32;11294 94129 32;11343 93366 32;11020 92944 32;11044 92570 32;11416 92156 32;11475 91228 32;12608 91300 32;12649 90659;11052 90516;11209 87410;7772 87393;7754 86102;-3901 85282;-3796 83467;-4354 83450;-4564 86381;-6650 86311;-6827 90096 32;-3961 90411 32;-4047 91196 32;-836 91538 32;-1137 94373 32;-4435 94018 32;-4350 93246 32;-7080 92956;-7217 94188;-4719 94521;-4132 95128;-3064 95250;1054 95262;5277 95524;5207 94957;5155 94930;4669 94567;4669 94562;1307 94372;1412 91773;3104 91834;3174 90429;4291 90464;4430 89819;6716 89958;6471 94643;12229 95149;12037 97452;12315 97462;12832 97506 32;12739 98608 32;14829 98784 32;14918 97728 32;17407 97938 32;17229 100046 32;17429 100063 32;17309 101481 32;14429 101238 32;14402 101553;14385 102117 18; -27161 84391;-27296 87661;-24977 87895;-25310 93632;-21781 93891;-21769 93151;-22160 93114;-23486 93024 32;-23223 89138 32;-20135 89347 32;-20110 88956;-20251 88913;-20900 88869 32;-20858 88252 32;-20118 88302 32;-16509 88647;-16588 89982;-16850 89947;-16902 91823;-16518 91866;-16684 92826;-16064 92887;-13959 93052 32;-13941 92816 32;-13317 92865 32;-13336 93104 32;-10523 93324 32;-10554 93718;-9656 93765;-7143 94080;-7056 92945;-6751 90041;-6547 86458;-7969 86362;-10342 85969;-10403 85777;-19462 85127;-19200 81454;-20744 81350;-21111 84962;-22838 84822;-27161 84391 18; -21733 80911 32;-20887 80993 32;-21251 84740 32;-22097 84658 32;-21733 80911 50; -21890 81800;-20842 81902; -22048 83228;-20980 83331; -21379 81843; -21516 83280; -32776 79781;-32784 80601;-30518 80819;-30465 80824 32;-30440 80560 32;-29859 80616 32;-29885 80884 32;-25316 81325 32;-25288 81036 32;-24698 81093 32;-24724 81360 32;-24545 81377;-22456 81587;-22761 84579;-22316 84649;-21741 80933;-20877 80994;-20860 80610;-20582 80639;-11585 81295;-10630 81369;-9779 81477;-8279 81534;-6349 81644;-3653 81848;-2049 81786;-833 81877;-865 81371 1;-1759 81408;-2585 81300;-3612 81227;-4610 81171 1;-10166 80895;-13394 80574;-19053 80127 1;-20491 80013;-21289 79842;-22700 79545;-22806 80075;-27277 79178;-27239 78832;-27519 78801;-27639 79900;-28018 79865;-32345 79469;-32776 79781 18; -893 81869;-369 81913;-160 81895;376 81902 1;856 81876;1271 81784;1838 81623 1;1842 81636;1846 81649;1849 81663;2534 81519;2485 81446;6321 80360;6318 80378;7535 79924;7544 79853 1;8631 79511;9259 79380;10353 79059 1;11130 78831;11723 79035;12377 79513 1;12420 79544;12461 79576;12501 79607;12779 79849 1;13377 80427;13603 81060;14061 81895;14412 82807 1;14413 82810;14414 82813;14414 82816;14830 82653 1;14445 81331;13998 80320;12996 79307 1;12141 78442;11121 78242;9961 78609 1;9405 78785;8887 78944;8394 79093;7883 79245 1;5531 79938;4031 80466;1242 81185 1;567 81359;-353 81353;-911 81373;-893 81869 18; 29981 80952;31421 79408;31555 78074;31565 77981 1;31742 77996;32066 77999;32081 77822;32153 76846;34077 77010;34071 77079;35730 77114;35770 77016;35987 76653;35905 76329;35582 75177;35800 75150;35355 73563;33922 73405;33102 73315 1;33011 73305;32951 73224;32961 73133;33002 72773;33273 72804;33629 69476;33537 69356;33049 68693;33172 67732;34207 67861;34258 67403;33951 67355;33229 67260;33327 66523;33952 65943;34424 65998;34703 63492;34592 63466;34217 63434;33892 62981;34012 61977;34013 61830;34021 61757;34411 61807;34510 60964;35215 61051;35294 60543;32284 60260;31445 65824;29658 65562;29488 66888;30086 66933;30000 67036;30391 67442;30322 68432;29499 68355;29237 70660;29817 71375;30175 70993;30300 71014 1;30465 71028;30611 71046;30719 71054 1;31026 71077;31069 71312;31040 71601;30917 72827;30804 72982;30725 73090;30702 73121;30460 75804;30076 79555;29981 80952 18; 28566 66791;28480 67577;29038 68031;28777 70709;29117 71128;28899 71302;29291 71764;29852 71311;29263 70589;29594 67380;30088 66945;28566 66791 18; 30016 67036;30391 67438;30310 68440;29499 68361; 29919 68197; 30016 67036;30391 67438;30310 68440;29499 68361;29615 67387;30016 67036 18; 28553 66779 32;29489 66895 32;29652 65549 32;28721 65427 32;28553 66779 50; 28739 59676;28985 60258;29614 60145;31838 60546;31115 65284;28454 64865;28381 65366;31441 65828 32;32267 60199 32;28739 59676 18; 28386 65333;28454 64865;31115 65284;31794 60537;29570 60171;28976 60284;28712 59715;26556 60343 32;25860 64952 32;28386 65333 18; 28726 59657 32;29177 59724 32;29376 58388 32;28925 58321 32;28726 59657 50; 28836 58943 1;27385 59106;26543 59282;25098 59067 1;24344 58955;23920 58895;23173 58746 1;22009 58513;21200 58135;20518 57046 1;20514 57041;20496 57001;20493 56996; 20379 60151;21469 58721 1;21043 58497;20795 58373;20431 58058 1;20415 58831;20399 59292;20379 60151 18; 20379 60151;21469 58721 1;21043 58497;20795 58373;20431 58058 1;20415 58831;20399 59292;20379 60151 18; 20379 60151;21469 58721 1;21043 58497;20795 58373;20431 58058 1;20415 58831;20399 59292;20379 60151 18; 23270 58752 1;23792 58689;24074 58394;24590 58493 1;24957 58563;25161 58615;25527 58690 1;25976 58782;26245 58578;26699 58641 1;27128 58701;27446 58714;27736 59036;27610 59088 1;26796 59178;26089 59214;25098 59067 1;24549 58985;24175 58931;23723 58850;23270 58752 18; 26823 58888; 23479 59345 1;23192 59734;22927 60023;22443 60023 1;22091 60023;21899 59900;21592 59727 1;21828 59419;21984 59262;22295 59036;23479 59345 18; 25120 61676; 25429 61022; 26562 60392;24255 60043; 20497 56997;20580 56483;19518 56311;19421 56828;20497 56997 18; 19322 58478;18250 58309;17802 58635;18886 58806;19322 58478 18; 13099 57896;17605 58644;15796 60146;12796 59674 16; 14827 54670;14045 54547;13597 56214;19415 57121;19463 56813; 13981 55987; 14827 54670;14045 54547;13597 56214;19415 57121;19463 56813;14660 56035;14827 54670 18; 13197 57791 32;14292 57972 32;14430 57138 32;13335 56957 32;13197 57791 50; 14328 57978 32;16089 58269 32;16232 57407 32;14471 57116 32;14328 57978 50; 17512 58471;18484 57764;16238 57398;16108 58244;17512 58471 18; 14270 59874;14533 58203; 14393 59035; 12664 60405 1;13634 60674;14227 60730;15227 60841 1;15768 60901;16105 60693;16535 60360 1;17001 60000;17236 59764;17707 59410 1;17961 59220;18172 59117;18484 59175;19804 59410;19817 57966;18484 57764;17546 58441;17432 58725;15801 60157;12796 59654;12664 60405 18; 12651 60409 1;13621 60678;14227 60730;15227 60841 1;15768 60901;16105 60693;16535 60360 1;17001 60000;17236 59764;17707 59410 1;17961 59220;18172 59117;18484 59175;19804 59410;19817 57966;18484 57764;17546 58441;17432 58725;15801 60157;12796 59654;12651 60409 18; 22481 59725; 17089 60465; 14696 61477; 22073 63340; 22160 65376; 19211 60404; 20926 61317; 25922 65129;23553 64771;23541 63229;24737 62575;26144 62809; 25922 65129;23553 64771;23541 63229;24737 62575;26144 62809;25922 65129 18; 18570 63558;19873 61458; 19943 62166 1;21248 63039;21554 64400;21143 65801; 19211 61721 1;17734 60720;15873 61384;14901 62787; 21316 66303 1;22431 66128;23790 65744;24914 65844;28628 66238; 18871 69286; 15934 69349; 13973 67264; 13220 65253; 13553 63316; 14145 64057 1;14540 64166;14974 64026;15120 63644; 16637 68421 1;16796 68095;16713 67727;16388 67565; 15860 67251 1;15540 66991;15218 67109;15019 67392; 21321 66215;17834 64908; 17809 64760;14417 62872; 14294 62859 1;13389 62298;12867 61929;12147 61144; 17081 65758;12973 72901; 16111 62863; 17149 62324; 20291 64897; 20175 63947; 19641 63197; 19022 62074 1;17720 61192;16064 61799;15207 63035; 19689 62479 1;20841 63250;21111 64451;20748 65688; 19689 62479 1;20841 63250;21111 64451;20748 65688; 17225 66056 1;17724 66324;18550 66145;18777 65561 16; 16507 64349 1;16256 64788;16335 65505;16752 65767 16; 18254 63673 1;17734 63334;17055 63416;16784 63887 16; 18990 65061 1;19165 64572;18976 64098;18603 63882 16; 16877 66100 1;17539 66555;18445 66387;18900 65724 1;19355 65061;19186 64156;18524 63701 1;17861 63246;16955 63414;16501 64077 1;16046 64740;16214 65646;16877 66100 18; 17877 65443; 18338 64623; 17483 64152; 20322 68711; 21494 67588; 15556 68149 1;13818 66975;13288 64686;14396 62905 1;14938 62033;15772 61404;16690 61112 1;17726 60781;18870 60879;19825 61537 1;21391 62616;22145 64381;21417 66138 1;20508 68333;17792 69442;15717 68285; 17889 61174;19376 61797;18225 63682;18613 63909;19817 61994;20584 62809;20824 62277;20440 61754;19298 61043;17889 61174 18; 16635 60901 1;17735 60508;18535 60486;19627 60901 1;19918 60376;19824 60001;19811 59401 16; 21700 63605 1;21395 62652;21081 62041;20378 61329 1;21043 60365;21416 59798;22293 59017 1;23634 59436;24431 59528;25826 59659 1;26973 59767;27623 59547;28762 59375 16; 16582 61093;16693 60880 1;17763 60507;18554 60493;19627 60901 1;19918 60376;19824 60001;19811 59401;19819 57962;20431 58058 1;20415 58831;20399 59292;20379 60151;21469 58721;22293 59017 1;21416 59798;21043 60365;20378 61329 1;21081 62041;21373 62583;21678 63536 1;21705 63619;21509 63691;21468 63614;20386 61844;19295 61181;17463 60945;16582 61093 18; 21139 67064 1;21365 66753;21793 66529;22171 66461; 21049 66954;21234 66947;21517 66707;21751 66577;22079 66420;21326 66435;21049 66954 18; 20661 67045 1;20303 66908;20254 66532;20425 66208; 20415 66213;21043 66440;20642 67029;20552 66990 1;20305 66830;20275 66521;20408 66242;20415 66213 18; 16020 68222;16639 68453;16662 68361 1;16804 68043;16705 67723;16388 67565;16020 68222 18; 15025 67427;15352 67746;15509 67868;15871 67266;15791 67201 1;15525 67029;15264 67108;15080 67315;15025 67427 18; 14424 63246;14223 63721;14127 64057;14215 64074 1;14590 64149;14983 64003;15120 63644;14424 63246 18; 12521 61096 1;12617 60465;12683 60113;12804 59486; 19804 59410;19817 57966;16230 57423 16; 28909 58282;28830 58919;28699 58959 1;28345 59000;28026 59041;27725 59075;27680 58978 1;27405 58712;27100 58697;26699 58641 1;26245 58578;25976 58782;25527 58690 1;25161 58615;24957 58563;24590 58493 1;24074 58394;23792 58689;23270 58752;23024 58715 1;21934 58478;21163 58084;20508 57031;20534 56982;28909 58282 18; 28909 58282;28830 58919;28699 58959 1;28345 59000;28026 59041;27725 59075;27680 58978 1;27405 58712;27100 58697;26699 58641 1;26245 58578;25976 58782;25527 58690 1;25161 58615;24957 58563;24590 58493 1;24074 58394;23792 58689;23270 58752;23024 58715 1;21934 58478;21163 58084;20508 57031;20534 56982;28909 58282 18; 28761 59381;28734 59686;27982 59928;26556 60343 32;26204 62673;25841 62759;24737 62575;23541 63229;23553 64771;25740 65102;26015 64975;28386 65333;28734 65426;28692 65915;28325 65906;24565 65514;23998 65531;23440 65610;22943 65697;22419 65802;21695 65924;21783 65697;21844 64694;21111 62740;20265 61309;21477 59669;21642 59756 1;21921 59913;22111 60023;22443 60023 1;22927 60023;23192 59734;23479 59345;23619 59367 1;24307 59510;24948 59577;25826 59659 1;26861 59756;27492 59587;28442 59426;28761 59381 18; 28764 59373;28832 58948;28386 58996 1;27196 59139;26387 59259;25098 59067 1;24344 58955;23920 58895;23173 58746 1;22009 58513;21200 58135;20518 57046 1;20514 57041;20496 57001;20493 56996;19447 56846;19410 57105;13582 56215;13277 56930;16234 57410;18513 57786;19817 57978;20436 58069;20520 58133 1;20838 58394;21078 58516;21469 58721;21444 58754;22261 59046 1;22271 59036;22282 59027;22293 59017 1;22970 59228;23508 59357;24054 59448;24314 59490 1;24769 59557;25246 59605;25826 59659 1;26769 59747;27376 59615;28195 59469;28764 59373 18; 13071 58077;12775 59508;15718 60002;17309 58719;13071 58077 18; 19629 60887;19740 60714;19833 60467;19858 60159;19821 59400;19529 59361;18484 59175 1;18172 59117;17961 59220;17707 59410 1;17236 59764;17001 60000;16535 60360 1;16105 60693;15768 60901;15227 60841 1;14227 60730;13656 60678;12686 60409;12635 60391;12524 61082;12901 61497;13420 61907;14162 62430;14397 62557;14697 62163;15437 61496;16461 60954;17368 60695;18231 60627;19040 60707;19629 60887 18; 19101 65065;20613 65632;20884 64602;20452 63214;19552 62524;18700 63745;19034 64417;19101 65065 18; 16696 63782;17448 63406;18367 63548;19058 62326;18633 62042;17510 61746;16548 61987;15740 62573;15419 63073;16696 63782 18; 15133 63638;16379 64310;16607 65914;15849 67277;15779 67193 1;15487 67012;15201 67133;15019 67392;14658 66969;14152 66210;13937 64872;14066 64051;14228 64077 1;14538 64134;14857 64042;15033 63804;15133 63638 18; 16402 67553;17149 66239;18944 65641;20418 66227;20374 66324 1;20274 66605;20339 66899;20624 67029;20215 67751;18845 68583;17291 68713;16618 68479;16659 68372;16662 68361 1;16787 68080;16725 67798;16490 67627;16402 67553 18; 12453 66111;13983 66043; 24592 66129;24311 68912;22502 70401;22498 71162; 24156 67263 1;23755 67315;23346 67513;23353 67917 1;23360 68315;23538 68560;23850 68807; 24452 67280;24156 67263 1;23755 67315;23346 67513;23353 67917 1;23360 68315;23538 68560;23850 68807;24176 69023;24311 68912;24432 67716;24452 67280 18; 22996 70735;24688 69487;24906 67228;25220 67263;25072 69592;23423 70813;22996 70735 18; 24802 66434;24831 66144; 22503 71180;23066 70700;24679 69487;24915 67245;24740 67228;24793 66434;24798 66180;24814 66069;24637 66091;24615 66187;24302 68911;22492 70417;22503 71180 18; 18985 70625;21658 70945;22093 71488; 18966 70947;19752 71027; 20335 71080;21116 71166; 19042 70479 1;19850 70091;20571 69845;21407 69448 1;21630 69342;21823 69287;22036 69411;22215 69516;22135 70583; 22555 71151;21943 70388;21134 70299;21078 70811; 21219 70838;21699 70881;22187 71498;22477 71226;21947 70418;21243 70388;21219 70838 18; 21818 69795; 20188 70384; 19087 70516;20942 70702;20998 70115;22003 70227;22120 70387;22159 70258;22215 69516;22036 69411 1;21823 69287;21632 69346;21407 69448 1;20583 69821;19915 70069;19121 70460;19087 70516 18; 22589 69888; 18626 71270;17894 68708; 19022 70471;18688 70431; 18646 70453;19034 70496;18960 71353;18368 71285;18436 70681;18646 70453 18; 18940 71358;21906 71681;22007 71624;21562 71036;19001 70787;18940 71358 18; 18076 68820;18608 70478;19044 70521;19271 70387 1;20000 70037;20635 69797;21407 69448 1;21632 69346;21823 69287;22036 69411;22215 69516;22159 70258;22120 70387;22525 70896;22500 70722;22502 70401;24188 69013;24042 68934;23850 68807 1;23538 68560;23360 68315;23353 67917 1;23346 67513;23755 67315;24156 67263;24452 67280;24636 66072;22857 66160;21339 66578;20484 67651;18983 68585;18076 68820 18; 13209 71829;15329 68218;13880 66325;12467 66412;12485 66534 1;12610 67976;12828 69385;13101 71112;13209 71829 18; 12431 65794;13783 65759;14098 63055;12423 61833;12413 61974 1;12354 63228;12354 64307;12403 65326;12431 65794 18; 21191 65327 1;20829 65279;20512 65207;20179 65082 1;19555 64848;20065 63831;19211 63277; 15328 62465 1;15701 62701;15848 62948;16279 63041 1;16914 63179;17358 62303;18495 62901; 17910 62799; 19572 63766; 29708 52233 1;29487 52197;29278 52347;29241 52568 1;29204 52789;29354 52999;29576 53035 1;29797 53072;30006 52922;30043 52701 1;30080 52479;29930 52270;29708 52233 18; 29640 52634; 27802 52372; 25964 52110; 24125 51848; 22287 51586; 27867 51967 1;27646 51931;27437 52081;27400 52302 1;27363 52523;27513 52733;27735 52769 1;27956 52806;28165 52656;28202 52435 1;28239 52213;28089 52004;27867 51967 18; 26029 51705 1;25808 51669;25599 51819;25562 52040 1;25525 52261;25675 52471;25897 52507 1;26118 52544;26327 52394;26364 52173 1;26401 51951;26251 51742;26029 51705 18; 24191 51446 1;23970 51410;23761 51560;23724 51781 1;23687 52002;23837 52212;24059 52248 1;24280 52285;24489 52135;24526 51914 1;24563 51692;24413 51483;24191 51446 18; 22353 51187 1;22132 51151;21923 51301;21886 51522 1;21849 51743;21999 51953;22221 51989 1;22442 52026;22651 51876;22688 51655 1;22725 51433;22575 51224;22353 51187 18; 18636 50480 1;18415 50444;18206 50594;18169 50815 1;18132 51036;18282 51246;18504 51282 1;18725 51319;18934 51169;18971 50948 1;19008 50726;18858 50517;18636 50480 18; 18570 50879; 16648 50555; 16714 50156 1;16493 50120;16284 50270;16247 50491 1;16210 50712;16360 50922;16582 50958 1;16803 50995;17012 50845;17049 50624 1;17086 50402;16936 50193;16714 50156 18; 15642 48243; 16740 45874; 17776 43703; 21328 49367;22212 49389; 21642 48941 1;21410 49009;21277 49253;21345 49485 1;21413 49718;21657 49851;21889 49783 1;22122 49714;22255 49471;22187 49238 1;22118 49006;21875 48873;21642 48941 18; 21777 49799;21777 48941; 31053 56194;29745 56008;29647 56655;30959 56855;31202 56893 32;31305 56236 32;31053 56196 32;31053 56194 18; 15269 51817;14966 53695;15968 53853;16207 52514;29511 54595;29352 55593;29204 55603;29154 55895;29299 55928;29197 56571;29068 56557;28994 56829;29149 56870;28919 58319;30685 58597 32;30959 56855;29647 56655;29745 56008;31053 56194;31343 54350 32;15269 51817 18; 28924 58287;29151 56857;29001 56834;29040 56552;29197 56570;29301 55916;29156 55891;29202 55596;29348 55619;29511 54595;16212 52492 32;15996 53860 32;14756 53664 32;14655 54306 32;14890 54343 32;14624 56035 32;19428 56792;19518 56311;20573 56477;20504 56961;28924 58287 18; 13110 51834;13893 49891;14880 50114;18409 42613;17750 42303;19088 40077 16; 14438 50028;18014 42428; 12413 57281;13437 53630; 11809 57794 1;11580 57740;11459 57715;11243 57668 1;11052 57627;10940 57420;10995 57232 1;11375 55932;11629 55215;12080 53938 1;12351 53173;12558 52729;12907 51996 1;12957 51891;13036 51791;13141 51841 1;13229 51883;13194 51992;13178 52088;12876 54013;11809 57794 18; 12753 52887; 12235 54750; 11643 56811; 11809 57794 1;11580 57740;11459 57715;11243 57668 1;11052 57627;10940 57420;10995 57232 1;11375 55932;11629 55215;12080 53938 1;12351 53173;12558 52729;12907 51996 1;12957 51891;13036 51791;13141 51841 1;13229 51883;13194 51992;13178 52088;12876 54013;11809 57794 18; 11809 57794 1;11580 57740;11459 57715;11243 57668 1;11052 57627;10940 57420;10995 57232 1;11375 55932;11629 55215;12080 53938 1;12351 53173;12558 52729;12907 51996 1;12957 51891;13036 51791;13141 51841 1;13229 51883;13194 51992;13178 52088;12876 54013;11809 57794 18; 12469 57270;13228 57411; 12598 56999;13276 57122; 11811 57821;13085 58082;13320 56931;13617 56207;14053 54549;14847 54697;14890 54392;14655 54305;14760 53668;14986 53668;15283 51819;31398 54369;32013 51363;22204 49339 1;22214 49538;22088 49724;21889 49783 1;21657 49851;21413 49718;21345 49485 1;21317 49390;21323 49292;21356 49206;18409 42613;17980 42411;17941 42583;14438 50028;13881 49895;13123 51844;13191 51895 1;13210 51948;13189 52021;13178 52088;12876 54013;11892 57500;11811 57821 18; 11740 59432 1;11467 59390;11313 59368;11040 59326 1;10772 59285;10542 59496;10501 59764;11640 59938;11740 59432 18; 10214 62912 32;11304 62956 32;11326 62405 32;10236 62361 32;10214 62912 50; 10273 66023 32;11399 65935 32;11355 65366 32;10229 65454 32;10273 66023 50; 10371 67283 32;11497 67195 32;11453 66626 32;10327 66714 32;10371 67283 50; 7595 67542 32;8721 67454 32;8677 66885 32;7551 66973 32;7595 67542 50; 7497 66282 32;8623 66194 32;8579 65625 32;7453 65713 32;7497 66282 50; 7418 64121 32;8469 64112 32;8464 63520 32;7413 63529 32;7418 64121 50; 8015 58374;8558 58466 1;8829 58512;8942 58766;8965 59040;7947 58876;8015 58374 18; 8350 56931 1;8546 56971;8644 56991;8832 57031 1;9117 57091;9406 56926;9467 56641;9546 56273;8546 56058;8350 56931 18; 8890 54923 1;9345 55042;9939 55121;10120 54687;11193 51931;10042 51747 1;9536 52924;9219 53592;8890 54923 18; 9529 54297; 8890 54923 1;9345 55042;9939 55121;10120 54687;11193 51931;10042 51747 1;9536 52924;9219 53592;8890 54923 18; 8890 54923 1;9345 55042;9939 55121;10120 54687;11193 51931;10042 51747 1;9536 52924;9219 53592;8890 54923 18; 10801 50399 1;11121 50440;11473 50483;11793 50529;11885 50353 1;11945 50238;11841 50114;11721 50065;11054 49789;10801 50399 18; 12301 47242 32;13121 47643 32;13354 47166 32;12534 46765 32;12301 47242 50; 13630 44523 32;14450 44924 32;14683 44447 32;13863 44046 32;13630 44523 50; 15149 41439;15978 41884 1;16088 41650;16024 41369;15795 41247;15364 41018;15149 41439 18; 16117 39536;16335 39100;17113 39489 1;16997 39720;16715 39808;16484 39693;16117 39536 18; 7544 61667 32;8531 61753 32;8578 61217 32;7591 61131 32;7544 61667 50; 10799 70408 32;11909 70235 32;11817 69640 32;10706 69813 32;10799 70408 50; 8150 71012 32;9198 70876 32;9123 70291 32;8075 70427 32;8150 71012 50; 11256 72888 32;12379 72666 32;12494 73250 32;11371 73472 32;11256 72888 50; 8900 74578;9862 73925;9763 73456;8727 73715;8900 74578 18; 9973 74800 1;10090 75579;9880 76125;9369 76725;9122 75442;9973 74800 18; 8234 77021;8172 76738;7691 76540;6951 77095;7173 77317;8234 77021 18; 8234 77021;8172 76738;7691 76540;6951 77095;7173 77317;8234 77021 18; 9591 75602; 9270 73900; 11874 73110; 11269 70026; 8629 70667; 8123 67226; 7975 65943; 10788 65671; 10899 66967; 7939 63833; 10764 62661; 8037 61465; 11171 59639; 8420 58677; 8938 56641; 10214 62912 32;11304 62956 32;11326 62405 32;10236 62361 32;10214 62912 50; 8015 58374;8558 58466 1;8829 58512;8942 58766;8965 59040;7947 58876;8015 58374 18; 7595 67542 32;8721 67454 32;8677 66885 32;7551 66973 32;7595 67542 50; 8150 71012 32;9198 70876 32;9123 70291 32;8075 70427 32;8150 71012 50; 11740 59432 1;11467 59390;11313 59368;11040 59326 1;10772 59285;10542 59496;10501 59764;11640 59938;11740 59432 18; 7544 61667 32;8531 61753 32;8578 61217 32;7591 61131 32;7544 61667 50; 8900 74578;9862 73925;9763 73456;8727 73715;8900 74578 18; 7418 64121 32;8469 64112 32;8464 63520 32;7413 63529 32;7418 64121 50; 10273 66023 32;11399 65935 32;11355 65366 32;10229 65454 32;10273 66023 50; 8350 56931 1;8546 56971;8644 56991;8832 57031 1;9117 57091;9406 56926;9467 56641;9546 56273;8546 56058;8350 56931 18; 11256 72888 32;12379 72666 32;12494 73250 32;11371 73472 32;11256 72888 50; 9973 74800 1;10090 75579;9880 76125;9369 76725;9122 75442;9973 74800 18; 10799 70408 32;11909 70235 32;11817 69640 32;10706 69813 32;10799 70408 50; 10371 67283 32;11497 67195 32;11453 66626 32;10327 66714 32;10371 67283 50; 7497 66282 32;8623 66194 32;8579 65625 32;7453 65713 32;7497 66282 50; 12251 72681 1;12080 71734;11963 71206;11814 70255; 11709 69653 1;11576 68704;11505 68171;11395 67219; 11231 65374 1;11148 64425;11059 63880;11107 62932; 11156 62377 1;11215 61403;11277 60855;11464 59897; 7700 61117 1;7761 60241;7908 59762;8058 58896; 7626 61660 1;7586 62378;7494 62779;7503 63498; 7491 64115 1;7508 64727;7516 65071;7552 65682; 7676 67521 1;7809 68655;7860 69302;8145 70408; 8243 70987 1;8419 72049;8559 72637;8724 73701; 6977 51467 1;8839 52036;9988 52514;11165 54066 33;11801 54905;12147 55481;12543 56456;12595 56648;12543 56980;13346 57119; 14619 60103 1;14761 60546;15167 60696;15631 60731 1;16399 60789;16971 60276;17741 60276 1;18239 60276;18378 60760;18440 61254; 11182 54066; 18392 60856; 6008 56653;3143 56295 32;2976 57631 32;5841 57989 48; 6200 58320;6915 58407 1;6553 60578;6524 61895;6469 64095; 5404 56575;5245 57848; 4201 56506;4050 57712; 5413 57271; 4236 57114; 5961 56653 32;5794 57990 32;3005 57641 32;3172 56304 32;5961 56653 50; 6502 65933;6600 67450; 6695 54827 32;7583 54938 32;7658 54335 32;6770 54224 32;6695 54827 50; 7890 53848 32;8282 50848 32;8916 50931 32;8524 53931 32;7890 53848 50; 3806 70539; 3143 69580; 2340 72302; 6056 72511; 6999 71517; 910 70906 1;664 70737;328 70800;160 71046 1;-9 71291;54 71627;299 71796 1;545 71964;881 71902;1050 71656 1;1218 71410;1156 71075;910 70906 18; 910 70906 1;664 70737;328 70800;160 71046 1;-9 71291;54 71627;299 71796 1;545 71964;881 71902;1050 71656 1;1218 71410;1156 71075;910 70906 18; 6705 67411 1;6879 69327;7415 71897;7964 74690;6991 74534;7136 73632;5649 73393;5381 75064;4675 74950 16; 6038 76435 32;6576 76525 32;6662 76013 32;6124 75923 32;6038 76435 50; 3570 75319 32;4546 75483 32;4633 74964 32;3657 74800 32;3570 75319 50; 2793 75982 32;3331 76072 32;3417 75560 32;2879 75470 32;2793 75982 50; 3648 77317 32;4186 77407 32;4272 76895 32;3734 76805 32;3648 77317 50; 4075 76541 32;4613 76631 32;4699 76119 32;4161 76029 32;4075 76541 50; 5061 76026 32;5599 76116 32;5685 75604 32;5147 75514 32;5061 76026 50; 1615 74944 32;2153 75034 32;2239 74522 32;1701 74432 32;1615 74944 50; 2021 76714; 1898 77566; 5151 74718; 5377 75817; 6346 76218; 4392 76314; 3964 77117; 3092 75765; 1932 74735; -2415 73726;3648 74755; -165 74110; -993 77896; 3940 72946;7714 73587; 7157 73590;6996 74540;7940 74688;7841 74004;7730 73498;7157 73590 18; 2247 74505 1;2331 74119;1994 73722;1602 73668 1;1192 73612;906 73788;494 73755 1;-295 73692;-702 73600;-1487 73502 1;-1703 73475;-1882 73595;-1958 73799;2247 74505 18; 2237 74251 1;2178 73959;1906 73710;1602 73668 1;1225 73616;952 73761;590 73759 33;559 73759;527 73758;494 73755 1;227 73734;4 73709;-205 73682 33;-440 73651;-736 73609;-978 73573 33;-1155 73547;-1267 73529;-1487 73502 1;-1587 73490;-1696 73525;-1773 73569; -2696 76398 32;-2458 74740 32;-1872 74824 32;-2110 76482 32;-2696 76398 50; -6564 77359; -2381 78104;-2147 76472; -2905 77403;-3002 78073;-14342 77354;-14835 77761;-16032 77675;-16427 77182;-16253 75527;-11011 76256; -14343 77328 1;-13803 77017;-13459 76749;-12838 76797 1;-12453 76827;-12237 76843;-11852 76873 1;-11430 76906;-11039 76670;-10962 76254; -14861 77735;-14602 75724;-10950 76230;-11030 76456 1;-11178 76745;-11504 76900;-11852 76873 1;-12237 76843;-12453 76827;-12838 76797 1;-13459 76749;-13803 77017;-14343 77328;-14861 77735 18; -16266 75489;-14574 75733;-14618 75850;-14859 77721;-14941 77753;-16032 77675;-16427 77182;-16274 75731;-16266 75489 18; -16035 77669 1;-16444 77640;-16792 77299;-16741 76892;-16569 75438; -16308 77096;-15916 77549;-14887 77611;-14389 77210; -14375 77341;-3025 78069;-2902 77415;-10982 76243;-11003 76380;-11030 76456 1;-11178 76745;-11504 76900;-11852 76873 1;-12237 76843;-12453 76827;-12838 76797 1;-13438 76751;-13779 76999;-14288 77296;-14375 77341 18; -14375 77341;-3025 78069;-2902 77415;-10982 76243;-11003 76380;-11030 76456 1;-11178 76745;-11504 76900;-11852 76873 1;-12237 76843;-12453 76827;-12838 76797 1;-13438 76751;-13779 76999;-14288 77296;-14375 77341 18; -29859 60374;-31107 69630;-31354 71627 1;-31512 72856;-30780 74572;-29347 75500 1;-27197 76892;-25507 76772;-22997 77280 1;-20895 77706;-19710 78030;-17568 78145;-17028 78174;-14771 78297;-14302 77878;-3064 78621 1;-100 78818;1815 78174;4814 77529;5031 77735;5240 77689;5327 77421;6958 77113 16; 8235 77011;9364 76746; -2993 78060;-3055 78622 1;-142 78814;1775 78192;4813 77546;5045 77730;5264 77702;5327 77421;6951 77095;7691 76540;8172 76738;8234 77021;8456 76959;9364 76746;9346 76603;9122 75442;9995 74778;9869 73896;8900 74578;8732 73741;7723 73469 1;7802 73870;7883 74278;7964 74690;6991 74534;7136 73632;5649 73393;5381 75064;4675 74950;-2388 73717;-2536 74753;-2316 74760;-1872 74824 32;-2110 76482 32;-2696 76398 32;-2993 78060 18; 10060 54782;9528 56283; 13101 58090;11793 57819;11569 57739 1;11466 57716;11371 57696;11243 57668 1;11052 57627;10940 57420;10995 57232 1;11375 55932;11629 55215;12080 53938 1;12321 53257;12512 52831;12797 52228;11182 51960;10120 54687 1;10101 54733;10077 54773;10049 54808;10006 54935;9528 56283;9515 56416;9467 56641 1;9406 56926;9117 57091;8832 57031 1;8644 56991;8546 56971;8350 56931;7410 56793;6003 56522;5793 58181;6242 58210;7026 58303;8026 58373;8280 58419;8558 58466 1;8829 58512;8942 58766;8965 59040;8813 59016;8054 58893;8037 59012 1;7895 59807;7758 60281;7700 61117;7722 61142;8578 61217 32;8531 61753 32;7634 61675;7619 61781 1;7576 62425;7495 62820;7503 63498;7555 63528;8464 63520 32;8469 64112 32;7502 64120;7495 64258 1;7509 64784;7519 65120;7552 65682;7625 65700;8579 65625 32;8623 66194;8679 66914;8721 67454 32;7699 67534;7683 67583 1;7811 68679;7865 69322;8145 70408;8273 70401;9123 70291 32;9198 70876 32;8253 70999;8268 71138 1;8433 72107;8567 72689;8724 73701;8822 73691;9763 73456;9862 73925;11377 73500;11256 72888 32;12237 72694;12229 72560 1;12070 71687;11957 71165;11814 70255;11587 70285;10799 70408 32;10706 69813 32;11714 69656;11698 69574 1;11572 68674;11502 68144;11395 67219;11306 67210;10371 67283 32;10301 66705;10258 66024;10229 65454 32;11236 65375;11207 65114 1;11133 64321;11064 63793;11107 62941;10976 62943;10214 62912 32;10236 62361 32;11157 62398;11162 62283 1;11219 61367;11283 60824;11464 59897;11328 59890;10501 59764 1;10542 59496;10772 59285;11040 59326 1;11313 59368;11467 59390;11740 59432;11739 59436;12832 59508;13101 58090 18; 12802 59496;11747 59410;11719 59538;11640 59938;11450 59909;11437 60036 1;11277 60880;11216 61414;11162 62283;11157 62398;11217 62401;11326 62405 32;11304 62956 32;11099 62948;11101 63073 1;11071 63849;11137 64363;11207 65114;11236 65375;11229 65376;11253 65374;11355 65366 32;11399 65935 32;10242 66036 32;10292 66713;10396 66709;11453 66626 32;11497 67195 32;11410 67202;11429 67507 1;11521 68288;11590 68804;11709 69653;11781 69646;11817 69640 32;11909 70235 32;11809 70251;11827 70340 1;11970 71238;12085 71763;12251 72681;12320 72678;12379 72666 32;12493 73247;13379 72894;13221 71947;13187 71815;13139 71347 1;12803 69232;12562 67591;12448 65832 1;12447 65822;12432 65823;12431 65813 1;12417 65598;12405 65380;12396 65159;12375 64491 1;12358 63691;12339 62838;12386 61885;12493 61087 1;12570 60581;12656 60263;12738 59830;12802 59496 18; 13219 71853 1;12881 69722;12612 68074;12472 66385 16; 12432 65830 1;12355 64639;12343 63387;12417 61885 16; -16256 75510;-16562 75458;-16584 75565;-16741 76892 1;-16791 77295;-16451 77633;-16047 77668;-16137 77544;-16427 77182;-16263 75621;-16256 75510 18; -16056 77665;-16065 78215;-15933 78234;-14771 78297;-14302 77878;-11554 78060;-3004 78636;-2930 78056;-3113 78066;-14342 77354;-14835 77761;-16056 77665 18; 17578 104752;23132 105569 1;22763 105305;22461 104852;22337 104315;21900 102429;22306 101777;22215 101478;19971 91566;19244 91090;19172 90835;19114 90628;19072 90478;19007 90303;18914 89917;17055 83300;15158 83734 1;16261 87288;16745 89560;17695 93348 1;17716 93431;17737 93514;17757 93595;18069 94834 1;18687 97287;19130 99041;19768 101837 1;20006 102881;19606 103868;18644 104339 1;18333 104491;18085 104628;17818 104703;17578 104752 18; 29148 59749;32254 60211;32245 60333;35272 60595;35586 56469;35595 56286 1;35595 56282;35596 56277;35596 56272 1;35634 55753;36086 55363;36606 55401 1;36651 55404;36695 55411;36738 55420;37314 52002;31951 51351;31336 54369;31326 54367;31050 56180;31303 56233;31224 56861;30980 56878;30926 57064;30685 58597 32;29369 58390;29148 59749 18; 19018 90371;19607 90436;19672 90397;19236 89084;18573 86942;18102 87008;18835 89669;19018 90371 18; -18490 76733;-18558 77210;-20271 76966;-20203 76485; -18099 75226 32;-18391 77262 32;-20425 76970 32;-20133 74934 32;-18099 75226 50; -18341 76763;-18126 75261; -20356 76475;-20141 74973; -20453 77083;-21518 76942;-19777 63792;-19685 61052 16; -20053 67087;-19871 65710; -20135 74957;-20425 77091;-21548 76950;-19773 63763;-19694 61032;-19363 58599;-18042 58800;-20135 74957 18; -23707 74049;-24683 73918; -30771 73235;-29833 72809; -30071 72681;-29814 70681;-30825 70570 1;-30928 71507;-31000 72251;-30769 73000 16; -30619 73392 1;-30539 73567;-30441 73744;-30320 73926 1;-29067 75811;-26827 76167;-24805 76418 1;-24665 76435;-24587 76446;-24447 76467 1;-24099 76520;-23710 76383;-23657 76035 1;-23563 75418;-23510 75072;-23416 74455 1;-23367 74131;-23529 73859;-23855 73827;-29796 73026 16; -29955 73637; -30084 72699;-29813 70688;-30837 70583;-30912 71523 1;-30940 72047;-30916 72523;-30769 73000;-30084 72699 18; -29794 73007;-29679 73040;-23864 73819 1;-23538 73851;-23367 74131;-23416 74455 1;-23510 75072;-23563 75418;-23657 76035 1;-23710 76383;-24099 76520;-24447 76467 1;-24587 76446;-24665 76435;-24805 76418 1;-26827 76167;-29067 75811;-30320 73926 1;-30441 73744;-30539 73567;-30619 73392;-29794 73007 18; -29590 69164 32;-29443 68070 32;-30453 67935 32;-30600 69029 32;-29590 69164 50; -29270 66586 32;-29018 64702 32;-29991 64572 32;-30243 66456 32;-29270 66586 50; -29554 65019; -29813 68585; -29270 66586 32;-29018 64702 32;-29991 64572 32;-30243 66456 32;-29270 66586 50; -29590 69164 32;-29443 68070 32;-30453 67935 32;-30600 69029 32;-29590 69164 50; -26371 72507 32;-25529 72620 32;-25566 72898 32;-26408 72785 32;-26371 72507 50; -25353 64906 32;-24511 65019 32;-24548 65297 32;-25390 65184 32;-25353 64906 50; -16042 78236;-16024 77674;-16182 77645 1;-16523 77556;-16786 77249;-16741 76892;-16569 75438;-18127 75219;-18135 75324;-18341 76763;-18460 77323;-20434 77076;-20662 77061;-21548 76950;-19773 63763;-19694 61032;-20366 60927;-20970 60848 32;-22576 65568 32;-23558 72896 32;-25577 72626;-25543 72726;-25566 72898 32;-26408 72785 32;-26371 72507 32;-26620 72486;-28440 72242 32;-27462 64943;-29018 64702 32;-29270 66586 32;-30243 66456;-30453 67935 32;-29443 68070 32;-29590 69164 32;-30631 69023;-30862 70564;-29814 70681;-30012 72225;-30029 72629;-30026 72704;-29791 72995;-29721 73010;-29595 73051;-23864 73819 1;-23538 73851;-23367 74131;-23416 74455 1;-23510 75072;-23563 75418;-23657 76035 1;-23708 76373;-24077 76512;-24417 76471;-24666 76995 1;-24145 77074;-23594 77159;-22997 77280 1;-20895 77706;-19710 78030;-17568 78145;-17028 78174;-16521 78202;-16042 78236 18; -24609 77011;-24369 76468;-25449 76334 1;-27287 76075;-29199 75613;-30320 73926 1;-30388 73823;-30450 73722;-30504 73621;-30633 73445;-30677 73347;-30792 73057;-30808 72974;-30815 72836 1;-30921 72414;-30937 71987;-30912 71523;-30837 70583;-30581 69031;-30600 69029 32;-30453 67935;-30224 66458;-30232 66371;-29991 64572;-30421 64544;-31107 69630;-31354 71627 1;-31512 72856;-30780 74572;-29347 75500 1;-27853 76468;-26580 76705;-25100 76930;-24609 77011 18; -22404 57890 32;-25224 57517 32;-22878 39763 32;-20057 40136 32;-20080 40309;-19762 40351 32;-19889 41318 32;-20208 41276;-20347 42328;-20028 42370 32;-20230 43908 32;-20550 43866;-20693 44944;-20372 44986 32;-20499 45953 32;-20820 45911;-20854 46166;-20533 46208 32;-20660 47175 32;-20982 47132;-21121 48184;-20799 48227 32;-21001 49765 32;-21324 49722;-21467 50800;-21143 50843 32;-21270 51810 32;-21595 51767;-21632 52050;-21307 52093 32;-21434 53060 32;-21760 53017;-21899 54069;-21573 54112 32;-21775 55650 32;-22102 55607;-22245 56685;-21917 56728 32;-22044 57695 32;-22372 57652;-22404 57890 50; -22369 57928;-22056 57971;-19663 40159;-20032 40108; -22369 57928;-22057 57971;-19664 40159;-20032 40108;-22369 57928 18; -18024 53979; -16698 43249; -16035 38172; -18127 58615 1;-18489 58524;-18527 58095;-18476 57725 1;-18364 56907;-18157 56471;-18075 55649 1;-18050 55403;-17921 55160;-17674 55160;-18127 58615 18; -18383 58436 1;-18504 58255;-18511 57976;-18476 57725 1;-18364 56907;-18157 56471;-18075 55649 1;-18064 55541;-18033 55434;-17981 55348; -17871 53214 1;-17901 53083;-17908 52944;-17897 52801;-15146 32013;-15122 31907 1;-15080 31777;-14998 31666;-14891 31594 16; -21403 39907;-20932 36424; -20710 58407;-22087 58225;-22048 57931; -22027 57678;-22079 58210;-18188 58812;-13656 25400;-19175 24563;-21306 40001;-19678 40174;-22027 57678 18; -27131 62305 32;-27303 63618 32;-26420 63733 32;-26248 62420 32;-27131 62305 50; -26985 62329;-27158 63655; -27349 60849; -27065 62039;-29628 61705; -29496 61423;-29409 60652;-26794 59850; -27318 63569;-29736 63230;-29582 62039; -27281 63538;-26794 59899;-29379 60682;-29724 63224;-27281 63538 18; -27304 63634;-29816 63298 32;-29641 61993 16; -29565 61428;-29453 60588 32;-26799 59785 48; -24888 54877;-27359 54551;-27852 58284;-25220 57529; -25769 55802; -30419 64563;-29992 64563;-28988 64720;-27453 64964;-27399 64922;-25368 65194;-25369 65028;-25353 64906 32;-24511 65019 32;-24548 65297 32;-24296 65338;-22576 65568;-22498 65549;-20963 60882;-20614 58422;-22088 58212;-22045 57986;-25246 57549;-27863 58300;-29469 60620;-26756 59809;-25334 59992;-25648 62469;-26258 62391;-26263 62537;-26420 63733 32;-27203 63631;-30228 63185;-30419 64563 18; -30243 63224;-29828 63272;-29662 61994;-29580 61392;-29370 60585;-29322 60450;-29867 60393;-30243 63224 18; -54524 27770 1;-54957 29584;-55234 30593;-55659 32409 1;-56302 35157;-56421 36759;-56596 39576 1;-56752 42092;-56777 43536;-56399 46029 1;-56053 48311;-55710 49581;-54931 51753 1;-54115 54030;-53583 55289;-52480 57442 1;-51705 58956;-51171 59758;-50194 61150 1;-49246 62502;-48590 63177;-47411 64333 1;-45909 65805;-44928 66490;-43197 67684 1;-42685 68037;-42192 68186;-42055 68792;-41758 70109;-37274 72978 1;-36934 73188;-36758 73484;-36793 73883 1;-36863 74068;-36910 74266;-36929 74473 1;-37045 75711;-36135 76808;-34897 76924 1;-33658 77040;-32561 76130;-32445 74891 1;-32377 74152;-32673 73464;-33186 73004 1;-33205 72986;-33311 72859;-33331 72842 16; -33370 76491 1;-33602 76164;-33807 75853;-33623 75497 1;-33408 75082;-32956 75105;-32489 75113; -32480 74336 1;-32887 74404;-33331 74389;-33483 74005 1;-33622 73654;-33636 73367;-33548 73044 1;-33522 72948;-33323 72896;-33305 72747;-32241 64765;-33327 63371;-28007 24759;-26550 23363;-25869 18111 1;-25715 16920;-26491 15603;-27691 15634;-29681 15685;-30421 16871;-34757 16869 1;-36651 16868;-37714 16868;-39608 16867 1;-42037 16866;-43386 16497;-45775 16058;-46045 14671;-49421 13825 1;-50599 13530;-51909 14034;-52204 15212 1;-52799 17588;-53270 18883;-53905 21249 1;-54012 21648;-53906 21931;-53696 22287 1;-53564 22511;-53555 22525;-53423 22748 1;-53131 23242;-53431 23757;-53582 24311;-54524 27770 16; -34941 73552 1;-35461 73581;-35859 74026;-35830 74546 1;-35802 75067;-35356 75465;-34836 75436 1;-34316 75407;-33917 74962;-33946 74442 1;-33975 73921;-34420 73523;-34941 73552 18; -34217 76867 1;-34368 76435;-34637 76157;-35089 76082 1;-35558 76004;-35862 76064;-36293 76265; -36877 75192 1;-36569 75024;-36417 74757;-36441 74407 1;-36458 74155;-36561 73998;-36764 73848; -33076 73647; -33187 75683; -35173 76436; -36703 74499; -34956 74851; -35756 71942; -38124 71375; -41480 67682; -42340 69427; -33535 66983; -39813 65383;-41418 65197; -38275 65550;-35642 65855;-35404 63803; -38289 65716;-38443 67013;-37411 67135;-37722 69758;-38650 69648;-38847 71314;-36367 72843 1;-35318 73489;-33726 72632;-33555 71412;-32757 65717;-33457 65619;-33379 65059;-35298 64792 16; -40755 69887;-40587 68228; -40005 70288;-39820 68457; -41165 69250; -40415 67968; -39804 65187;-38233 65366;-38290 65763;-38310 65889;-38443 67013;-37411 67135;-37722 69758;-38650 69648;-38847 71314;-40939 70046;-40945 69982;-40770 68246;-39554 67941;-39352 65949;-39857 65898;-39825 65575;-39856 65562;-39804 65187 18; -33093 65381; -32458 74330;-32587 74352 1;-32962 74399;-33344 74356;-33483 74005 1;-33622 73654;-33636 73367;-33548 73044 1;-33526 72963;-33380 72913;-33325 72809;-33302 72872 1;-33263 72916;-33200 72991;-33186 73004 1;-32836 73318;-32586 73739;-32485 74209;-32458 74330 18; -32484 75124;-32597 75111 1;-33025 75104;-33425 75114;-33623 75497 1;-33807 75853;-33602 76164;-33370 76491;-33264 76427 1;-32913 76141;-32649 75750;-32520 75296;-32484 75124 18; -34953 75446;-34909 75437 1;-34885 75438;-34861 75437;-34836 75436 1;-34316 75407;-33917 74962;-33946 74442 1;-33975 73921;-34420 73523;-34941 73552 1;-35461 73581;-35859 74026;-35830 74546 1;-35807 74969;-35509 75311;-35118 75409;-34953 75446 18; -34220 76886;-34238 76809 1;-34393 76410;-34658 76154;-35089 76082 1;-35558 76004;-35862 76064;-36293 76265;-36216 76336 1;-35865 76660;-35409 76876;-34897 76924 1;-34700 76942;-34507 76935;-34321 76904;-34220 76886 18; -36898 75185;-36790 75139 1;-36541 74970;-36419 74722;-36441 74407 1;-36458 74155;-36561 73998;-36764 73848;-36825 73973 1;-36877 74132;-36913 74299;-36929 74473 1;-36949 74682;-36939 74887;-36903 75084;-36898 75185 18; -34231 76865;-34251 76778 1;-34407 76397;-34669 76152;-35089 76082 1;-35528 76009;-35822 76057;-36211 76228;-36298 76255 1;-36551 75996;-36740 75678;-36846 75326;-36877 75192 1;-36569 75024;-36417 74757;-36441 74407 1;-36456 74184;-36538 74036;-36697 73901;-36790 73841 1;-36775 73555;-36873 73323;-37063 73139;-36698 72639;-36367 72843 1;-35342 73474;-33799 72671;-33569 71496;-33551 71421;-33132 71482;-33325 72809 1;-33380 72913;-33526 72963;-33548 73044 1;-33636 73367;-33622 73654;-33483 74005 1;-33344 74356;-32962 74399;-32587 74352;-32461 74339 1;-32434 74518;-32428 74703;-32445 74890 1;-32448 74926;-32452 74960;-32457 74994;-32489 75113 1;-32956 75105;-33408 75082;-33623 75497 1;-33790 75820;-33636 76106;-33433 76401;-33357 76500 1;-33578 76661;-33828 76783;-34097 76856;-34231 76865 18;-34953 75446;-34909 75437 1;-34885 75438;-34861 75437;-34836 75436 1;-34316 75407;-33917 74962;-33946 74442 1;-33975 73921;-34420 73523;-34941 73552 1;-35461 73581;-35859 74026;-35830 74546 1;-35807 74969;-35509 75311;-35118 75409;-34953 75446 18; -37030 73167;-36646 72670;-36878 72528;-38847 71314;-38835 71210;-40937 69898;-40949 70018;-41418 69722;-41554 68932 1;-41649 68394;-41867 68067;-42319 67760 1;-43741 66794;-44548 66256;-45859 65144 1;-46306 64765;-46679 64432;-47020 64106;-47324 64418 1;-45870 65832;-44894 66513;-43197 67684 1;-42685 68037;-42192 68186;-42055 68792;-41758 70109;-37274 72978 1;-37252 72991;-37231 73005;-37211 73020;-37030 73167 18; -41397 85580 1;-40377 83183;-39817 81832;-38920 79386 1;-38666 78694;-38754 78071;-38864 77342;-39934 77512; -41338 77712;-42053 77782;-42803 77677; -39933 77348;-41382 77566; -42762 77507;-43052 77483; -43071 77470 32;-43001 76869 32;-43289 76835 32;-43359 77437 32;-43071 77470 50; -41208 85517;-41919 85424 32;-41827 84720 32;-41751 84145;-44228 83819; -44228 85084;-44087 84009; -41988 86947 1;-42603 88438;-42948 89275;-43563 90766 1;-44027 91892;-45115 92188;-46333 92198 1;-47315 92206;-47892 91962;-48740 91466 1;-49196 91199;-49322 90871;-49595 90419;-52166 86162;-51896 83888; -52055 89948; -50241 92757; -56853 94519; -59340 91022; -60848 90814; -68413 89168; -69904 88924; -68648 85062; -68909 86877; -64286 87540; -59732 88046; -59400 85691; -63920 85019; -64876 94038 32;-65034 93892 1;-65152 93955;-65288 93986;-65431 93976 1;-65847 93949;-66162 93590;-66134 93174 1;-66128 93088;-66109 93007;-66077 92931;-66640 92410 32;-66313 92055 32;-65759 92566 1;-65632 92498;-65486 92461;-65332 92471 1;-64917 92498;-64602 92858;-64629 93273 1;-64635 93369;-64659 93460;-64698 93543;-64549 93683 32;-64876 94038 50; -60472 84361;-57931 84768; -65080 83623;-62539 84030; -69867 84536;-67326 84943; -70435 88842;-67894 89249; -65920 89952;-63379 90359; -61405 90717;-58864 91124; -69011 89056 32;-68382 84775 32;-68825 84710 32;-69454 88991 32;-69011 89056 50; -64467 90187 32;-63539 83868 32;-63982 83803 32;-64910 90122 32;-64467 90187 50; -59913 90946 32;-58982 84609 32;-59425 84544 32;-60356 90881 32;-59913 90946 50; -65406 93173; -64876 94038 32;-65034 93892 1;-65152 93955;-65288 93986;-65431 93976 1;-65847 93949;-66162 93590;-66134 93174 1;-66128 93088;-66109 93007;-66077 92931;-66640 92410 32;-66313 92055 32;-65759 92566 1;-65632 92498;-65486 92461;-65332 92471 1;-64917 92498;-64602 92858;-64629 93273 1;-64635 93369;-64659 93460;-64698 93543;-64549 93683 32;-64876 94038 50; -62736 94494; -64550 94383; -51644 86198; -51252 85805;-51943 85717; -51386 87114;-51232 85794;-52003 85713;-52015 86170;-51386 87114 18; -44846 87871;-43077 88096;-43002 87509 32;-42931 86948;-41874 87082; -52025 85715;-44610 86649;-44802 87870;-43136 88070;-42909 86875;-41845 87067;-41252 85654;-42080 85532;-41897 84171;-44244 83935;-44436 85026;-51833 84066;-52025 85715 18; -51445 87154;-43056 88215;-42871 86956;-42057 87166;-42228 87529 1;-42703 88681;-43033 89480;-43563 90766 1;-44027 91892;-45115 92188;-46333 92198 1;-47315 92206;-47892 91962;-48740 91466 1;-49196 91199;-49322 90871;-49595 90419;-51420 87398;-51445 87154 18; -51741 82708;-50658 74656; -50572 74021;-49934 69287 1;-48724 70263;-48067 70794;-46821 71724;-46917 72440 16; -51542 66781 1;-49716 68611;-47958 70192;-45671 71828;-44781 72403;-42915 71985 1;-41690 72790;-40863 73069;-39800 74079 1;-38827 75003;-38423 75775;-38139 77086 1;-37953 77952;-37779 78530;-38104 79354;-44786 95616 1;-45261 96773;-46178 97514;-47426 97429;-49444 97291 16; -42749 92268;-43712 92225;-44445 92941;-46155 93255;-47673 93133;-49138 92557;-50307 92348;-51563 92488;-53081 93238;-53780 97739; -53780 97739;-46923 98280;-44690 97286;-42764 92267;-43712 92225;-44445 92941;-46155 93255;-47673 93133;-49138 92557;-50307 92348;-51563 92488;-53081 93238;-53735 97450;-53780 97739 18; -49444 97291;-60008 96439;-66307 95408 1;-66912 95289;-67263 94521;-67197 93908;-66919 91718;-66532 91789 16; -54925 94297 1;-55057 94915;-54927 95736;-54297 95789 1;-53950 95818;-53755 95835;-53408 95864; -57127 96661 1;-56415 96728;-56063 96149;-55351 96217 1;-54650 96284;-54256 96322;-53555 96389; -69872 93675;-70822 94132;-73499 93836; -69959 92984; -73290 92479; -92629 96005;-89125 83520; -91750 91081;-92750 90790; -92646 94157;-93646 93866; -69596 94589;-80305 93899 1;-81343 93832;-81937 93808;-82946 93553;-84661 93072; -91089 91196;-85766 92749; -91301 91599;-86350 93034 1;-85856 92967;-85478 92618;-85426 92109;-85377 91529;-85908 91159;-85735 88951;-85118 88729 1;-84789 87113;-84507 86216;-83872 84694;-83150 82923;-82346 83216 1;-82993 84795;-82987 84420;-83527 86039 1;-83872 87075;-84070 87669;-84206 88753 1;-84382 90151;-84508 90931;-84662 92331 1;-84719 92847;-84729 93300;-84303 93628 1;-82566 94135;-81470 94340;-79599 94445 1;-76055 94643;-74055 94818;-70511 95015 1;-69832 95053;-69341 94446;-69243 93774;-68922 91578 1;-68892 91372;-69099 91238;-69305 91208;-69459 92265;-73842 91626;-73636 90214;-74087 90148;-73211 83927 16; -91259 91601;-91128 91164;-91031 91213;-85766 92749;-85885 92867 1;-86020 92953;-86178 93011;-86350 93034;-91130 91648;-91259 91601 18; -84679 93076;-84650 93109 1;-84600 93304;-84497 93479;-84303 93628 1;-82566 94135;-81470 94340;-79599 94445 1;-76055 94643;-74055 94818;-70511 95015 1;-70144 95036;-69833 94868;-69608 94604;-69729 94580;-80305 93899 1;-81343 93832;-81937 93808;-82946 93553;-84557 93101;-84679 93076 18; -53213 93155 1;-53579 93222;-53791 93247;-54163 93229; -53978 92526 1;-53096 92338;-52636 92096;-51794 91773 1;-51087 91502;-50620 91423;-49870 91526 1;-49330 91600;-49006 91593;-48500 91798; -54257 87639; -55664 92833;-55125 92951;-54408 90147 1;-54043 88559;-53438 87764;-53212 86150 1;-53068 85119;-53520 83686;-54549 83524;-55664 92833 18; -55664 92833;-55125 92951;-54408 90147 1;-54043 88559;-53438 87764;-53212 86150 1;-53068 85119;-53494 83677;-54523 83515;-55664 92833 18; -66609 91774;-64191 92243;-64352 93279;-57394 94870;-57110 93772;-55499 94157 1;-55692 94932;-56029 95757;-56813 95694;-59989 95440;-59938 94803;-61501 94677;-61556 95361;-67017 94803 16; -54937 94276;-54465 94386;-54157 93205 16; -38719 77164;-38135 77102 1;-37951 77959;-37781 78535;-38104 79354;-38964 81447;-39164 81933;-43376 92184;-43711 92225;-43712 92225;-44445 92941;-46155 93255;-47673 93133;-49138 92557;-50307 92348;-51563 92488;-53077 93236;-53344 93178 1;-53635 93228;-53838 93245;-54163 93229;-53952 92520 1;-53087 92333;-52628 92093;-51794 91773 1;-51087 91502;-50620 91423;-49870 91526 1;-49350 91597;-49030 91593;-48554 91776;-47989 91864 1;-47483 92092;-47002 92203;-46333 92198 1;-45115 92188;-44027 91892;-43563 90766 1;-42972 89332;-42630 88503;-42058 87116;-41179 85066 1;-40293 82978;-39751 81652;-38920 79386 1;-38745 78908;-38732 78463;-38778 77994;-38719 77164 18; -48598 91751;-49357 90904;-52306 86167;-52350 86283;-53981 92530;-53880 92504 1;-53061 92320;-52605 92084;-51794 91773 1;-51087 91502;-50620 91423;-49870 91526 1;-49436 91585;-49141 91592;-48778 91700;-48598 91751 18; -53565 95853;-53146 93140;-53361 93181 1;-53643 93228;-53844 93244;-54163 93229;-54228 93479;-54465 94386;-54937 94276;-54948 94423 1;-55038 95021;-54885 95740;-54297 95789 1;-54093 95806;-53941 95819;-53780 95833;-53565 95853 18; -56906 96699;-56730 96703;-53718 96946;-53683 96377 1;-54307 96317;-54694 96280;-55351 96217 1;-55943 96161;-56286 96551;-56793 96646;-56906 96699 18; -55683 94106;-55685 94113;-55499 94157 1;-55692 94932;-56029 95757;-56813 95694;-59989 95440;-59938 94803;-61501 94677;-61556 95361;-67017 94803;-67139 94512 1;-67197 94312;-67218 94102;-67197 93908;-66919 91718;-66532 91789;-66250 91844;-64191 92243;-64352 93279;-57394 94870;-57110 93772;-56385 93945;-55683 94106 18; -67028 94797;-67002 94845 1;-66847 95128;-66609 95349;-66307 95408;-60008 96439;-56998 96682;-56750 96637 1;-56266 96529;-55926 96163;-55351 96217 1;-54694 96280;-54307 96317;-53683 96377;-53683 96382;-53582 95851;-53780 95833 1;-53941 95819;-54093 95806;-54297 95789 1;-54885 95740;-55038 95021;-54948 94423;-54937 94276;-55499 94157 1;-55692 94932;-56029 95757;-56813 95694;-59989 95440;-59938 94803;-61501 94677;-61556 95361;-61690 95347;-66856 94815;-67028 94797 18; -76008 91985; -75228 93291 1;-77983 93042;-79508 92722;-82259 92437 1;-82707 92391;-82900 92125;-83236 91826 1;-83621 91483;-83864 91305;-84161 90884 1;-84286 90706;-83965 90330;-83794 90465 1;-83324 90835;-83144 91143;-82660 91494 1;-82119 91885;-81664 91821;-81003 91913 1;-78781 92222;-77521 92289;-75298 92594 1;-75092 92622;-74883 92736;-74896 92943 1;-74908 93130;-75041 93308;-75228 93291 18; -83550 87168; -82084 87011; -84034 76532; -86146 83421; -89235 84099;-87498 84587; -87668 85103;-80421 61579;-76374 47910;-71316 30194;-65172 8505;-59447 -11679;-54759 -28605;-49800 -48418;-48147 -55326;-47228 -58797;-46252 -61219; -45691 -63260;-44075 -71981; -89524 91166;-89436 90850 32;-88781 91033 32;-88869 91349 48; -87309 91382; -91382 91752;-88956 92433; -91820 93629;-88652 94518; -77361 51461;-72728 52857 32;-66987 62087 32;-76551 73000 32;-83003 70224; -73679 61705 1;-72733 62257;-72446 62426;-71500 62978 1;-71121 63199;-70664 63226;-70381 62891 1;-70030 62476;-69834 62243;-69483 61828 1;-69153 61437;-69266 60969;-69509 60519 1;-70151 59329;-70512 58663;-71154 57473 1;-71522 56791;-72953 56747;-73138 57500 1;-73450 58770;-73625 59481;-73937 60751 1;-74013 61060;-73954 61545;-73679 61705 18; -73749 63973 1;-74330 63689;-75223 63675;-75371 64304 1;-75547 65051;-75645 65469;-75821 66216 1;-75973 66862;-75818 67499;-75232 67811 1;-74678 68106;-74170 68178;-73627 67864 1;-72699 67328;-72322 66809;-71585 66032 1;-71290 65721;-71596 65214;-71969 65002 1;-72667 64605;-73028 64326;-73749 63973 18; -72423 62350; -70381 58826; -73138 64217; -75214 67794; -77361 51461;-72728 52857 32;-66987 62087 32;-76551 73000 32;-83003 70224;-77361 51461 18; -72175 69963;-73717 69756; -83163 82922;-78743 72220;-76535 73231 1;-78128 75477;-79286 76599;-80458 79091;-82328 83203;-83163 82922 18; -90079 88255;-89703 86917; -90079 88255;-89703 86917; -88051 84594 1;-88146 84922;-88093 85321;-87767 85420 1;-87472 85510;-87140 85382;-87051 85087 1;-85960 81467;-85242 79471;-84130 75857;-76711 51895 1;-76127 49978;-75343 48702;-73440 48088 1;-72567 47806;-71873 48673;-71417 49469 1;-70364 51308;-69639 52258;-68542 54071 1;-67722 55426;-67248 56178;-66387 57508 1;-64778 59994;-63890 61403;-62113 63772 16; -88582 84391 1;-88626 85039;-88509 85871;-87881 86035 1;-87435 86152;-86698 85916;-86492 85504;-75934 52024 1;-75535 50760;-74867 50033;-73560 49687 1;-72236 49336;-71651 50921;-70891 52060 1;-69371 54340;-68622 55622;-67158 57939 1;-65593 60416;-64703 61832;-62936 64151 16; -89594 85560 1;-88921 85805;-88640 86606;-87954 86812 1;-87269 87017;-86197 86495;-85931 85831;-75105 52329 1;-74917 51748;-74414 51220;-73931 50938 1;-72629 50178;-71668 51968;-71085 52927 1;-69643 55302;-68950 56703;-67471 59055 1;-66102 61233;-65312 62453;-63685 64446 16; -90286 88098 1;-89148 88430;-88463 88559;-87322 88882 1;-86567 89096;-85929 88658;-85540 87976 1;-84303 85808;-84142 84329;-83316 81974;-73359 52712 16; -89951 86854 1;-89611 86949;-89413 86978;-89078 87090 1;-88542 87269;-87299 87760;-86442 87043; -90957 90399 1;-90070 90648;-89571 90781;-88687 91040 1;-87875 91278;-87409 91373;-86590 91583 1;-85333 91905;-83972 91508;-83530 90288 1;-83261 89544;-83213 89093;-83073 88314 1;-82961 87693;-82236 87484;-81618 87611 1;-80736 87792;-80256 87959;-79372 88129 1;-78850 88229;-78956 88932;-79056 89454 1;-79311 90784;-79449 91785;-79597 92840 1;-79704 93605;-79273 94424;-79437 95393 1;-79607 96396;-80316 97000;-80974 97776; -79461 95405; -80729 87797; -76733 81155;-78879 79575; -78619 78786;-78718 78763 1;-79068 78598;-79173 78168;-79070 77778 1;-78889 77094;-78851 76584;-78302 76138 1;-78009 75900;-77815 75663;-77458 75664;-77522 75782 1;-77827 76138;-78000 76485;-78268 77045 1;-78511 77552;-78538 77936;-78600 78459 1;-78605 78504;-78610 78549;-78614 78592;-78619 78786 18; -76744 81032 1;-77115 80647;-77413 80327;-77336 79798 1;-77243 79158;-77119 78552;-76337 78601;-76744 81032 18; -76914 89342;-76937 89326 1;-77062 85543;-80322 84664;-81651 84424 1;-82231 84319;-82633 84072;-82371 83322;-82262 83085 1;-81990 82816;-81640 82809;-81391 82471 1;-80918 81829;-80883 81311;-80361 80709 1;-79927 80208;-79515 80114;-79000 79697 1;-78605 79377;-78660 78964;-78600 78459 1;-78538 77936;-78511 77552;-78268 77045 1;-77908 76293;-77720 75925;-77152 75405 1;-76935 75206;-76718 75166;-76426 75202 1;-76055 75247;-75697 75534;-75772 75900;-76357 78600 1;-77121 78563;-77244 79164;-77336 79798 1;-77413 80327;-77115 80647;-76744 81032;-76749 81081;-76767 81142 1;-76805 81483;-76862 81672;-76862 82015 1;-76862 82279;-76719 82415;-76539 82608 1;-76153 83020;-76020 83323;-75641 83742 1;-75370 84042;-75423 84361;-75467 84763 1;-75654 86483;-75658 87491;-75729 89220 1;-75735 89358;-75992 89465;-76130 89465 1;-76375 89465;-76513 89465;-76758 89465 1;-76836 89465;-76911 89420;-76914 89342 18; -73788 82631;-73926 83656;-73152 83760; -73030 83284 32;-71684 83464 32;-71609 82901 32;-72954 82721 32;-73030 83284 50; -71762 82901;-72199 86143; -84331 89762 1;-84220 89970;-84106 90113;-83875 90157 1;-83692 90192;-83479 90164;-83430 89984;-83314 89584 1;-83216 89186;-83164 88819;-83073 88314 1;-83038 88120;-82943 87966;-82810 87850 1;-82775 87820;-82832 87769;-82875 87751 1;-83280 87580;-83836 87408;-84059 87787 1;-84155 88231;-84160 88384;-84206 88753 1;-84247 89078;-84285 89370;-84322 89644;-84331 89762 18; -78965 88982 1;-78773 89336;-78812 89758;-78422 89858 1;-78194 89916;-77978 89673;-77990 89438 1;-78002 89205;-78013 89071;-78077 88846;-78129 88666 1;-78320 87576;-78215 87613;-79039 86692 1;-79751 85897;-82597 84618;-83464 85784;-83497 85950 1;-83507 85979;-83517 86009;-83527 86039 1;-83758 86731;-83922 87226;-84047 87802;-83923 87641 1;-83656 87465;-83213 87608;-82875 87751 1;-82855 87759;-82832 87775;-82817 87792;-82696 87765 1;-82403 87580;-81991 87534;-81618 87611 1;-80736 87792;-80256 87959;-79372 88129 1;-79032 88194;-78958 88515;-78976 88875;-78965 88982 18; -79324 90777 1;-79259 90404;-79414 90066;-79669 90021 1;-79925 89977;-80184 90244;-80249 90617 1;-80314 90990;-80159 91328;-79903 91372 1;-79648 91416;-79388 91150;-79324 90777 18; -73619 84308 1;-74312 85219;-75022 85695;-75102 86837 1;-75245 88878;-75236 90027;-75294 92072 1;-75304 92425;-75640 92669;-75992 92647 1;-76995 92584;-77209 92532;-78208 92421 1;-78751 92361;-78993 91913;-78905 91374; -75219 89202; -75582 86052;-74958 86150 1;-75033 86354;-75084 86578;-75102 86837 1;-75245 88878;-75236 90027;-75294 92072 1;-75300 92279;-75418 92449;-75586 92549;-75704 92540 1;-77682 92280;-78920 92203;-81003 91913 1;-81186 91888;-81353 91874;-81511 91860;-81812 91826 1;-82100 91784;-82367 91705;-82660 91494 1;-83144 91143;-83324 90835;-83794 90465 1;-83917 90368;-84118 90535;-84174 90703;-84294 89829 1;-84193 90001;-84080 90118;-83875 90157 1;-83692 90192;-83479 90164;-83430 89984;-83314 89584 1;-83216 89186;-83164 88819;-83073 88314 1;-83038 88120;-82943 87966;-82810 87850 1;-82806 87846;-82803 87842;-82801 87838;-82585 87704 1;-82305 87571;-81946 87544;-81618 87611 1;-80736 87792;-80256 87959;-79372 88129 1;-78953 88209;-78939 88679;-79001 89131;-78859 89240 1;-78767 89518;-78719 89782;-78422 89858 1;-78194 89916;-77978 89673;-77990 89438 1;-78002 89205;-78013 89071;-78077 88846;-78129 88666 1;-78320 87576;-78215 87613;-79039 86692 1;-79751 85897;-82597 84618;-83464 85784;-83378 85604 1;-83020 84585;-82941 84616;-82501 83586;-82449 83661 1;-82492 84151;-82133 84337;-81651 84424 1;-80322 84664;-77062 85543;-76937 89326;-76914 89342 1;-76911 89420;-76836 89465;-76758 89465 1;-76513 89465;-76375 89465;-76130 89465 1;-75992 89465;-75735 89358;-75729 89220 1;-75685 88154;-75667 87362;-75615 86508;-75582 86052 18; -83104 70145;-79773 71601;-79893 71914;-83316 81974 1;-84142 84329;-84303 85808;-85540 87976 1;-85929 88658;-86567 89096;-87322 88882 1;-88463 88559;-89148 88430;-90286 88098;-89347 84135;-87496 84604;-83104 70145 18; -84821 85878;-84320 84432; -91150 91176;-85746 92767;-85648 92656 1;-85527 92508;-85448 92322;-85426 92109;-85377 91529;-85908 91159;-85735 88951;-85118 88729 1;-84789 87113;-84507 86216;-83872 84694;-83150 82923;-83043 82631;-78743 72220;-79761 71720;-79974 72151;-83316 81974 1;-84142 84329;-84303 85808;-85540 87976 1;-85929 88658;-86567 89096;-87322 88882 1;-88463 88559;-89148 88430;-90286 88098;-91150 91176 18; -85762 92773;-84637 93096;-84683 92920 1;-84700 92737;-84685 92539;-84662 92331 1;-84508 90931;-84382 90151;-84206 88753 1;-84070 87669;-83872 87075;-83527 86039 1;-82987 84420;-82993 84795;-82346 83216;-83150 82923;-83872 84694 1;-84507 86216;-84789 87113;-85118 88729;-85735 88951;-85908 91159;-85377 91529;-85426 92109 1;-85448 92329;-85532 92519;-85659 92669;-85762 92773 18; -73972 83139;-74052 83731;-73213 83873;-73235 84099;-74087 90148;-73636 90214;-73842 91626;-69459 92265;-69305 91208 1;-69099 91238;-68892 91372;-68922 91578;-69243 93774 1;-69289 94092;-69424 94396;-69625 94623;-69779 94577;-80305 93899 1;-81343 93832;-81937 93808;-82946 93553;-84557 93101;-84679 93076;-84687 92633 1;-84683 92535;-84673 92434;-84662 92331 1;-84550 91316;-84453 90627;-84342 89793;-84288 89874;-84174 90703;-84131 90926 1;-83847 91317;-83608 91495;-83236 91826 1;-82900 92125;-82707 92391;-82259 92437 1;-79508 92722;-77983 93042;-75228 93291 1;-75041 93308;-74908 93130;-74896 92943 1;-74883 92736;-75092 92622;-75298 92594 1;-75400 92580;-75501 92566;-75599 92553;-75482 92472 1;-75371 92371;-75299 92233;-75294 92072 1;-75236 90027;-75245 88878;-75102 86837 1;-75084 86578;-75033 86354;-74958 86150;-75021 86140;-75203 86112;-75582 86052;-75582 86057;-75573 85887 1;-75545 85540;-75511 85171;-75467 84763 1;-75423 84361;-75370 84042;-75641 83742 1;-76020 83323;-76153 83020;-76539 82608 1;-76719 82415;-76862 82279;-76862 82015 1;-76862 81672;-76805 81483;-76767 81142;-76752 81090;-73754 81570;-73972 83139 18; -73746 81653;-72228 70085;-73781 69859;-76572 73069;-76581 73296 1;-78150 75499;-79297 76623;-80458 79091;-82328 83203;-81998 82896 1;-81787 82781;-81564 82706;-81391 82471 1;-80918 81829;-80883 81311;-80361 80709 1;-79927 80208;-79515 80114;-79000 79697 1;-78686 79443;-78656 79130;-78627 78759;-78769 78736 1;-79078 78555;-79168 78148;-79070 77778 1;-78889 77094;-78851 76584;-78302 76138 1;-78009 75900;-77815 75663;-77458 75664;-77275 75521 1;-77236 75483;-77195 75444;-77152 75405 1;-76935 75206;-76718 75166;-76426 75202 1;-76055 75247;-75697 75534;-75772 75900;-76339 78518;-76360 78740;-76744 81032;-76642 81252;-73746 81653 18; -69771 80469 32;-54436 82530 32;-54571 83532 32;-69906 81471 32;-69771 80469 50; -69625 94594;-67022 94804;-67093 94649 1;-67185 94410;-67223 94147;-67197 93908;-66919 91718;-66532 91789;-66164 91860;-64191 92243;-64352 93279;-62001 93816;-57411 94866;-57078 93755;-55499 94138;-55116 92953;-55258 92922;-55664 92833;-54549 83524;-55038 83469;-69906 81471 32;-69970 81863;-70096 82802 32;-71597 82600 32;-71638 82904 32;-71714 82894;-71997 83422;-73030 83284 32;-73728 83166;-73850 83602;-73204 83689;-73224 83986;-73235 84099;-74087 90148;-73636 90214;-73842 91626;-69459 92265;-69305 91208 1;-69099 91238;-68892 91372;-68922 91578;-69243 93774 1;-69281 94034;-69378 94285;-69522 94492;-69625 94594 18; -37968 77915;-35909 78543;-34761 79913;-35085 80277 1;-35333 80584;-35547 80919;-35699 81278;-39217 89588;-40476 92684;-42113 93224;-43264 96139 1;-43483 96694;-43150 97321;-42635 97620;-46526 97336 1;-45733 97103;-45138 96474;-44786 95616;-38104 79354 1;-37935 78925;-37901 78562;-37933 78191;-37968 77915 18; -47343 102112 1;-46920 101139;-47696 99845;-48756 99791 1;-50544 99700;-51541 99468;-53275 99023 1;-54372 98742;-55023 98742;-56154 98674 1;-63131 98255;-67030 97834;-74001 97320 1;-77287 97077;-79185 96668;-82375 96036;-88705 94683; 23987 105832;23679 108312;22769 108209;19118 107811;5369 106325;4505 105239;-4626 104316;-7560 105479;-18485 104438;-20987 102981;-31707 102152;-33229 101315;-35903 101084;-35754 99008 1;-35676 99021;-35595 99032;-35511 99038;-30338 99432;-30258 98381;-28290 98530;-28371 99603;-26204 99767;-26115 98594;-21733 98925;-21425 99305;-21513 100284;-10566 101461;1904 102646;1982 101714;3893 101875;3805 102919;5000 103020;5083 102040;9773 102439;9673 103615;10155 103656;10255 102476;13746 102772;13968 103187;13926 103536;13818 104312;14879 104442;17152 104746 1;17432 104783;17657 104755;17874 104687;22927 105396 1;23161 105627;23438 105777;23719 105812;23758 105817;23987 105832 18; -35693 99020;-35865 101081;-36284 101051;-40739 100665;-40700 99592;-39960 99654 1;-39807 99667;-39689 99535;-39676 99382 1;-39659 99185;-39812 99017;-40009 99000;-40663 98963;-40632 97872 1;-39952 97908;-39324 97943;-38465 97989 1;-37430 98045;-36905 98692;-36028 98945;-35693 99020 18; -40703 100659;-40624 99598;-40700 99592;-40663 98963;-40591 98967;-40562 97876 1;-40585 97874;-40609 97873;-40632 97872;-40779 97864 1;-41168 97843;-41581 97820;-42067 97794 1;-42323 97780;-42569 97681;-42774 97527;-43241 97492;-42901 98862;-42847 98827 1;-42792 98797;-42728 98783;-42661 98790;-42032 98827;-42081 99506;-42723 99444 1;-42802 99436;-42869 99399;-42918 99345;-43241 100458;-42683 100511;-40703 100659 18; -43206 100449;-42877 99383 1;-42892 99371;-42906 99359;-42918 99345;-42954 99468;-42976 99255 1;-43002 99196;-43014 99130;-43007 99062 1;-42997 98962;-42939 98880;-42858 98833;-43197 97544;-46364 97265;-46657 97371 1;-46897 97427;-47153 97448;-47426 97429;-49444 97291;-49896 97255;-60008 96439;-66307 95408 1;-66623 95346;-66870 95107;-67024 94805;-67041 94779;-69570 94557;-69664 94665 1;-69883 94894;-70173 95034;-70511 95015 1;-74055 94818;-76055 94643;-79599 94445 1;-81470 94340;-82566 94135;-84303 93628 1;-84497 93479;-84600 93304;-84650 93109;-84679 93076;-85743 92767;-85807 92811 1;-85957 92929;-86144 93006;-86350 93034;-91301 91599;-91837 93674;-88714 94686;-88290 94772;-82375 96036 1;-79185 96668;-77287 97077;-74001 97320 1;-67030 97834;-63131 98255;-56154 98674 1;-55023 98742;-54372 98742;-53275 99023 1;-51541 99468;-50544 99700;-48756 99791 1;-47696 99845;-46920 101139;-47343 102112;-45730 102340 1;-45162 100774;-44493 100443;-43448 100449;-43206 100449 18; -53950 78577;-52735 78740; -52478 69067; -53501 73660;-53404 72940; -67425 71694;-67268 70524; -61445 72498;-61286 71315; -68212 71605 32;-57696 73018;-57612 72368;-56239 72566;-56313 73203;-53655 73561 32;-53492 72351 32;-68049 70395 32;-68212 71605 50; -57239 68824; -60641 68336; -54325 69147; -64139 67880; -68039 67202; -68929 67045; -68562 64515; -57198 68609 32;-60223 68203 32;-60018 66678 32;-57582 67005 32;-57688 67795 32;-57099 67874 32;-57198 68609 50; -57072 67841 32;-56967 67056 32;-57564 66976 32;-57669 67761 32;-57072 67841 50; -55538 68587 32;-55204 66103 32;-54291 66226 32;-54215 65665 32;-53747 65728 32;-53881 66723 32;-54340 66661 32;-54616 68711 32;-55538 68587 50; -55238 66083 32;-54295 66210 32;-54217 65628 32;-55160 65501 32;-55238 66083 50; -55435 68615;-55639 70132; -57165 68657;-57333 69899; -60644 68186;-60812 69428; -64118 67690;-64286 68932; -60261 68225;-67646 67233;-67976 69689;-68977 69555;-68710 67561;-70606 67307;-70793 68706;-72084 68533;-72258 69832; -69077 70234;-68044 70392; -69066 70237;-68048 70403;-68128 70934;-69128 70810;-69066 70237 18; -73531 69688;-72285 69848;-72237 69677;-72084 68533;-70793 68706;-70606 67307;-68710 67561;-68977 69555;-67976 69689;-67646 67233;-69522 65136;-73531 69688 18; -53707 65691;-53669 65424 1;-53744 65342;-53823 65258;-53904 65171 1;-53982 65087;-54174 65131;-54188 65245;-54244 65702 16; -54562 68746;-54759 70219;-54254 70287;-54115 69249;-52707 69438;-52931 71139 1;-52262 71218;-51414 71074;-51320 70411;-51029 68038 1;-51925 67140;-52538 66588;-53303 65803;-53438 66816;-53893 66755 16; -54345 63308 1;-53348 64789;-52754 65481;-51537 66788;-51188 66421;-51842 65654 1;-52518 64862;-51741 63837;-52427 63054 1;-52823 62602;-53415 62459;-53936 62758 1;-54132 62871;-54246 62969;-54337 63176 1;-54351 63207;-54364 63280;-54345 63308 18; -49251 62400 1;-51626 63855;-48989 66545;-47305 64476; -45315 65029; -47478 62517; -44216 58644; -44026 57688; -48352 64315; -49438 64377; -49327 63316; -53805 63341; -51634 66265; -52100 66165;-51708 65810; -52676 65310;-53234 64115;-52159 63613; -52842 65344;-52734 65185;-53234 64115;-52159 63613;-52230 63365 1;-52276 63258;-52339 63154;-52427 63054 1;-52823 62602;-53415 62459;-53936 62758 1;-54132 62871;-54246 62969;-54337 63176 1;-54351 63207;-54364 63280;-54345 63308 1;-53932 63921;-53589 64399;-53241 64841;-52842 65344 18; -52092 66173;-51734 65815;-51587 65953;-51188 66421;-51537 66788 1;-51676 66639;-51807 66497;-51931 66362;-52092 66173 18; -52499 57456 1;-54787 58435;-53164 60979;-51327 59517; -53042 58986; -52684 58123; -54459 53030;-55545 53375 1;-55319 52873;-55179 52509;-55335 51981 1;-55600 51084;-55808 50482;-56556 49921 1;-57061 49542;-57268 49036;-57148 48416 1;-57019 47753;-56892 47179;-56272 46910; -56981 67045;-56523 63621 1;-56455 63108;-56111 62735;-55688 62436 1;-57338 59635;-58390 58036;-59328 54911 1;-59640 53872;-60293 53471;-61169 52832 16; -54475 82533;-54335 82560 1;-54064 82599;-53880 82653;-53591 82591 1;-53333 82536;-53369 82212;-53382 81949 1;-53422 81133;-53331 80658;-53092 79876 1;-52857 79105;-52741 78540;-52592 77913;-52479 77452 1;-52426 77246;-52366 77029;-52296 76792 1;-51929 75560;-51867 74830;-51686 73557 1;-51565 72704;-52164 71879;-53021 71786;-53272 73640;-54475 82533 18; -52098 84775;-52209 84738;-52223 84577 1;-52263 84207;-52365 83872;-52444 83413 1;-52647 82228;-52688 81508;-52427 80334 1;-52022 78510;-51669 77514;-51337 75675 1;-51252 75201;-51176 74765;-51107 74352;-51046 73986 1;-50787 72395;-50559 70501;-50338 68666;-50002 68979;-50651 73869;-51900 82727;-52098 84775 18; -55549 94151;-54975 94286;-54907 94286;-54476 94379;-54130 93256;-53933 92534;-53957 92504;-53843 92002;-52350 86283;-52306 86167;-52240 85323;-52234 85280 1;-52137 84553;-52320 84136;-52444 83413 1;-52647 82228;-52688 81508;-52427 80334 1;-52056 78664;-51729 77689;-51422 76125;-51267 75284 1;-50867 73021;-50684 71588;-50406 69293;-51176 69238;-51320 70411 1;-51414 71074;-52262 71218;-52931 71139;-53021 71786 1;-52164 71879;-51565 72704;-51686 73557 1;-51867 74830;-51929 75560;-52296 76792 1;-52650 77984;-52729 78686;-53092 79876 1;-53099 79900;-53107 79924;-53114 79947;-53231 80366 1;-53365 80890;-53413 81317;-53382 81949 1;-53369 82212;-53333 82536;-53591 82591 1;-53912 82660;-54104 82586;-54430 82547;-54545 83496;-54372 83566 1;-53468 83855;-53077 85180;-53212 86150 1;-53438 87764;-54043 88559;-54408 90147;-54683 91221;-54792 91648;-55125 92951;-55264 92921;-55549 94151 18; -54430 82547 1;-54104 82586;-53912 82660;-53591 82591 1;-53333 82536;-53369 82212;-53382 81949 1;-53422 81133;-53331 80658;-53092 79876 1;-52983 79518;-52899 79204;-52826 78908 16; -52744 78564 1;-52614 78011;-52501 77484;-52296 76792 1;-51929 75560;-51867 74830;-51686 73557 1;-51565 72704;-52164 71879;-53021 71786;-53272 73640 16; -49251 62400 1;-51626 63855;-48989 66545;-47305 64476;-49251 62400 18; -52499 57456 1;-54787 58435;-53164 60979;-51327 59517;-52499 57456 18; -48422 75407;-50543 75164; -39496 75272 1;-40002 74714;-40302 74409;-40823 73978;-43056 72559;-44611 72954;-44835 75171; -42772 73904; -43784 73842; -43870 74681; -40695 74940; -39509 75323;-42405 75445;-42754 75403;-42735 75119;-44281 74968;-44306 75215;-44708 75166; -42466 75445 1;-42520 75330;-42545 75158;-42423 75123 1;-42078 75025;-41871 75030;-41524 74939 1;-41391 74904;-41262 74969;-41210 75096 1;-41166 75202;-41139 75263;-41123 75376;-42466 75445 18; -44822 75140;-44333 75212;-44306 75215;-44281 74968;-42735 75119;-42754 75403;-42405 75445;-39509 75323;-39483 75225 1;-39947 74708;-40216 74365;-40823 73978;-43056 72559;-44611 72954;-44809 74914;-44822 75140 18; -46124 75668 32;-45041 75801 32;-45087 76173 32;-46170 76040 32;-46124 75668 50; -50380 73830;-47086 74211;-47038 73798; -46908 73795 32;-46753 72457 32;-47961 72317 32;-48116 73655 32;-46908 73795 50; -46913 74410;-46248 74487;-46147 73620;-46848 73539; -46760 72826;-46113 72901;-46014 72050 1;-46808 71504;-47472 70982;-48409 70244 1;-49165 69648;-49622 69311;-50331 68668 1;-50650 71311;-50866 73064;-51337 75675 1;-51669 77514;-52022 78510;-52427 80334 1;-52688 81508;-52647 82228;-52444 83413 1;-52320 84136;-52137 84553;-52234 85280;-52339 86073;-52332 86213;-53981 92530 16; -46725 72433;-46630 71613; -46411 74075; -46769 72827;-46633 71618;-46537 71681 1;-46366 71805;-46193 71927;-46014 72050;-46113 72901;-46608 72844;-46769 72827 18; -46855 73543;-46929 74400;-46844 74418;-46248 74487;-46147 73620;-46735 73552;-46855 73543 18; -50459 73748;-47118 74150;-46891 71733;-49927 69352;-50459 73748 18; -50577 75156;-48344 75403;-48414 75675;-48937 80251 32;-43501 80881 32;-43105 77503 32;-41978 77649;-41349 77561;-39933 77348;-38783 77476;-38760 78239 1;-38742 78620;-38775 78992;-38920 79386 1;-39802 81791;-40358 83138;-41346 85461;-41879 85371;-41768 84113;-44236 83693;-51700 82707;-50577 75156 18; -46007 72056;-44664 72928;-44786 75161;-44306 75205;-44288 74979;-42736 75109;-42753 75388;-42754 75403;-42405 75445;-39522 75324;-38644 77204;-39927 77334;-40537 77439;-41349 77561;-41478 77579;-42169 77614;-42762 77509;-43059 77491;-42910 76096;-45030 75808;-48406 75416;-50552 75163;-50491 74692;-49470 74796;-49357 74072;-46914 74386;-46243 74482;-46167 73792;-46147 73620;-46848 73539;-46775 72825;-46653 72838;-46113 72901;-46045 72314;-46007 72056 18; -42511 75212 1;-42510 75147;-42486 75141;-42423 75123 1;-42078 75025;-41871 75030;-41524 74939 1;-41391 74904;-41262 74969;-41210 75096 1;-41201 75117;-41193 75136;-41186 75155; -76625 89465;-76477 89465 1;-76370 89465;-76267 89465;-76130 89465 1;-75992 89465;-75735 89358;-75729 89220 1;-75658 87491;-75654 86483;-75467 84763 1;-75423 84361;-75370 84042;-75641 83742 1;-76020 83323;-76153 83020;-76539 82608 1;-76719 82415;-76862 82279;-76862 82015 1;-76862 81672;-76805 81483;-76767 81142;-76749 81081;-76744 81032;-76702 80779;-76360 78740;-76339 78518;-75772 75900 1;-75697 75534;-76055 75247;-76426 75202 1;-76718 75166;-76935 75206;-77152 75405 1;-77195 75444;-77236 75483;-77275 75521;-77458 75664 1;-77460 75664;-77463 75664;-77465 75664;-77599 75876 1;-77858 76200;-78023 76534;-78268 77045 1;-78511 77552;-78538 77936;-78600 78459 1;-78660 78964;-78605 79377;-79000 79697 1;-79515 80114;-79927 80208;-80361 80709 1;-80883 81311;-80918 81829;-81391 82471 1;-81552 82690;-81756 82770;-81954 82873; -75228 93291 1;-77983 93042;-79508 92722;-82259 92437 1;-82707 92391;-82900 92125;-83236 91826 1;-83621 91483;-83864 91305;-84161 90884 1;-84286 90706;-83965 90330;-83794 90465 1;-83324 90835;-83144 91143;-82660 91494 1;-82119 91885;-81664 91821;-81003 91913 1;-78781 92222;-77521 92289;-75298 92594 1;-75092 92622;-74883 92736;-74896 92943 1;-74908 93130;-75041 93308;-75228 93291 18; -41845 69753;-42167 70267;-42883 69797 1;-43065 69671;-42974 69412;-42848 69230;-42250 68366; -42246 68366;-42115 68514;-41836 69744;-41895 69834;-42167 70267;-42883 69797 1;-43065 69671;-42974 69412;-42848 69230;-42313 68457;-42246 68366 18; -39564 75313;-38700 77232;-38114 77164;-38147 77051 1;-38430 75761;-38836 74995;-39800 74079 1;-40438 73473;-40990 73130;-41595 72779;-42005 72542 1;-42288 72378;-42587 72200;-42915 71985;-44781 72403;-45052 72228;-45240 72107;-45671 71828 1;-47695 70380;-49305 68975;-50915 67402;-51189 69300;-50413 69370;-50345 68727 1;-50343 68707;-50340 68686;-50338 68666;-50002 68979;-49701 69217 1;-49294 69557;-48919 69842;-48409 70244 1;-47472 70982;-46808 71504;-46014 72050;-46019 72090;-44699 72973;-44525 72932;-43056 72559;-40823 73978 1;-40377 74347;-40093 74624;-39702 75046;-39564 75313 18; -53752 66764;-54310 66685;-54607 68727;-54563 68805;-54584 68908;-54759 70219;-54254 70287;-54115 69249;-52707 69438;-52931 71139 1;-52262 71218;-51414 71074;-51320 70411;-51029 68038 1;-51925 67140;-52538 66588;-53303 65803;-53438 66816;-53673 66785;-53752 66764 18; -54206 65639;-53717 65717;-53691 65578;-53669 65424 1;-53744 65342;-53823 65258;-53904 65171 1;-53982 65087;-54174 65131;-54188 65245;-54223 65531;-54206 65639 18; -54079 65127;-53591 64394;-53518 64481 1;-53425 64605;-53333 64724;-53241 64841;-52842 65344;-52748 65444 1;-52399 65854;-52016 66273;-51537 66788;-51479 66844 1;-51272 67051;-51066 67255;-50860 67455;-50965 68110;-51102 67965 1;-51959 67110;-52559 66567;-53303 65803;-53438 66816;-53858 66760;-53652 65423;-53966 65135;-54079 65127 18; -52805 65387;-52102 66164;-51707 65813;-51789 65716;-51842 65654 1;-52360 65047;-52025 64304;-52148 63637;-52334 63695;-53234 64115;-52762 65126;-52805 65387 18; -37973 78034;-35993 78610;-35434 76804;-35617 76733 1;-36468 76348;-37021 75455;-36929 74473 1;-36910 74266;-36863 74068;-36793 73883 1;-36758 73484;-36934 73188;-37274 72978;-39551 71521;-39832 71341;-41758 70109;-41841 69742;-41941 69907;-42167 70267;-42883 69797 1;-43065 69671;-42974 69412;-42848 69230;-42250 68366;-42414 68201 1;-42630 68015;-42910 67882;-43197 67684 1;-44887 66518;-45862 65838;-47305 64436;-47388 64574 1;-48913 66278;-51116 64291;-49792 62837;-52221 63397;-52163 63598;-52135 63717 1;-52050 64363;-52339 65071;-51842 65654;-51789 65716;-51707 65813;-51648 65881;-51188 66421;-51537 66788;-51458 66865 1;-51258 67065;-51059 67262;-50860 67455;-50780 67533 1;-49215 69053;-47639 70420;-45671 71828;-44781 72403;-42915 71985 1;-41690 72790;-40863 73069;-39800 74079 1;-39713 74162;-39630 74243;-39552 74324;-39386 74501 1;-38712 75248;-38386 75963;-38147 77051;-38114 77164;-37973 78034 18; -61374 63787;-57765 64596 16; -65145 65119;-62250 63877 16; -62250 63877;-61813 63689 32;-61374 63787 16; -59649 64555;-59780 65141; -60389 64270;-60606 65237; -61147 64067;-61405 65221; -62408 64295;-61975 65304; -63202 64570;-62828 65441; -63973 64919;-63691 65577; -53565 72972;-53181 73015;-53170 72885;-53021 71786;-52902 71157;-52916 71024;-52707 69438;-54115 69249;-54254 70287;-54759 70219;-54562 68746;-54724 68696;-55296 68620;-55528 68592;-55159 65493;-54246 65628;-54216 65470;-54188 65245 1;-54177 65155;-54056 65109;-53966 65134;-53539 64452 1;-53790 64115;-54050 63746;-54345 63308 1;-54356 63292;-54356 63260;-54352 63230;-54458 63134 1;-54730 62767;-54961 62436;-55171 62116;-55688 62436 1;-56111 62735;-56455 63108;-56523 63621;-56981 67045;-57157 68639;-67643 67220;-67963 69695;-68984 69564;-68696 67583;-70598 67296;-70798 68691;-72098 68526;-72255 69817;-69071 70236;-53485 72350;-53565 72972 18; -58087 52559; -58645 51355; -59256 52350; -56761 48738; -56150 49314; -54964 52786; -55438 46826; -54812 44938; -54180 50848; -53020 52846; -52149 56105; -71477 54718;-72454 53148;-72425 53130;-72541 52816 1;-72336 52248;-71887 52120;-71372 51804 1;-70855 51488;-70419 51673;-69857 51537;-66749 49606;-65685 51420;-71477 54718 18; -61307 53454; -63034 49185; -61973 48889; -61652 50567; -65674 46118; -66390 44415; -65650 43601; -67007 42713; -66654 40247; -66637 39332 1;-67141 39325;-67555 39729;-67562 40233 1;-67569 40737;-67166 41151;-66662 41158 1;-66157 41165;-65743 40762;-65736 40258 1;-65730 39753;-66133 39339;-66637 39332 18; -66637 39332 1;-67141 39325;-67555 39729;-67562 40233 1;-67569 40737;-67166 41151;-66662 41158 1;-66157 41165;-65743 40762;-65736 40258 1;-65730 39753;-66133 39339;-66637 39332 18; -70099 50778; -70370 50037;-72652 44621; -69654 51431;-68581 53171; -73255 44770; -61221 38886 1;-59063 38913;-59067 36089;-61011 36042; -60365 37542; -74741 42648;-72420 43311;-72978 36419;-74741 42648 18; -74741 42648;-72420 43311;-72978 36541;-74741 42648 18; -75770 46312 1;-75218 46312;-74740 45910;-74571 45384;-73873 42802;-74706 42543;-75770 46312 18; -76416 51599;-75875 49802 1;-75720 49317;-75960 48768;-76451 48633;-77323 51390;-76416 51599 18; -74496 52210 1;-74276 51804;-74066 51527;-74165 51076 1;-74347 50246;-74447 49773;-74758 48982 1;-74825 48813;-74707 48702;-74653 48528 1;-74651 48521;-74170 47754;-73870 48515 1;-73450 49584;-73378 49818;-73118 50936 1;-72994 51470;-72797 52060;-72490 52192 1;-71990 52407;-71687 52044;-72281 52768;-72682 52873;-73101 52751;-74496 52210 18; -76494 51633;-76478 51499;-75875 49802 1;-75829 49659;-75818 49511;-75837 49369 1;-75875 49086;-75632 48863;-75349 48825 1;-75126 48795;-74862 48714;-74781 48924;-74671 49210 1;-74426 49871;-74329 50329;-74165 51076 1;-74066 51527;-74276 51804;-74496 52210;-76494 51633 18; -74419 44793 1;-73959 44868;-73846 45333;-73766 45792 1;-73604 46718;-73444 47307;-73741 48198 1;-73775 48301;-73861 48302;-73963 48339;-74088 48210 1;-74359 48056;-74651 48523;-74653 48528 1;-74700 48680;-74796 48784;-74775 48921;-74867 48819 1;-74985 48750;-75179 48802;-75349 48825 1;-75448 48838;-75542 48874;-75621 48928;-74419 44793 18; -76433 48651;-76341 48672 1;-76066 48792;-75888 49052;-75841 49344;-75828 49208 1;-75800 49098;-75729 49004;-75633 48936;-75599 48852;-74629 45514;-74696 45663 1;-74920 46046;-75321 46312;-75770 46312;-76433 48651 18; -73831 45475;-72465 45064;-70398 49970;-69672 51422;-69857 51537 1;-70419 51673;-70855 51488;-71372 51804 1;-71663 51982;-71932 52101;-72149 52272;-72239 52262 1;-72313 52251;-72398 52232;-72490 52192 1;-72797 52060;-72994 51470;-73118 50936 1;-73378 49818;-73450 49584;-73870 48515 1;-73896 48449;-73924 48394;-73952 48349;-73836 48297 1;-73793 48279;-73759 48254;-73741 48198 1;-73444 47307;-73604 46718;-73766 45792 1;-73784 45691;-73803 45589;-73827 45491;-73831 45475 18; -66749 49606 1;-67917 47531;-68574 46272;-68955 43922 1;-69483 40668;-69604 38753;-69177 35484 1;-68924 33547;-68454 32521;-67894 30649 1;-66131 24755;-65023 21484;-63404 15548;-66858 14734;-73026 36373;-72949 36783;-72505 42263;-72472 42670;-72420 43311;-73895 42891;-73950 43086;-74419 44820;-74243 44847 1;-74023 44954;-73911 45181;-73840 45442;-73649 45420;-72465 45064;-70398 49970;-69672 51422;-66749 49606 18; -65685 51420;-65142 52336 1;-65698 53536;-66230 54364;-67468 54831;-68614 53088;-65685 51420 18; -66809 62094 1;-66292 61633;-66108 61079;-66267 60404 1;-66391 59878;-66356 59411;-65946 59059;-65156 58368;-68605 53094;-71460 54680;-66809 62094 18; -60669 58982 1;-61410 58916;-61917 58748;-62396 58179 1;-63115 57326;-63425 56724;-63722 55649 1;-63885 55060;-64020 54702;-63931 54097;-60669 58982 18; -65142 52336;-63931 54097 1;-64020 54702;-63885 55060;-63722 55649 1;-63425 56724;-63115 57326;-62396 58179 1;-61917 58748;-61410 58916;-60669 58982;-60633 59058;-60423 59305 1;-59556 60569;-58930 61200;-58252 62575 1;-57933 63222;-57817 63633;-57746 64351;-58248 64488;-61374 63787;-61891 63672;-62503 63985;-65033 65071;-65222 64925 1;-65835 63819;-66183 63202;-66784 62089;-66722 62013 1;-66272 61568;-66117 61040;-66267 60404 1;-66391 59878;-66356 59411;-65946 59059;-65156 58368;-67471 54827;-67395 54795 1;-66247 54345;-65734 53586;-65212 52485;-65142 52336 18; -59598 56986; -60928 52527 1;-63596 50546;-65251 49304;-66788 46358 1;-67958 44116;-68231 42604;-68367 40079 1;-68444 38639;-68307 37826;-68158 36391; -64859 50536; -64341 41431; -63477 47206 1;-63377 47624;-63092 47956;-62663 47946 1;-62247 47936;-61877 47594;-61923 47181 1;-62180 44896;-62422 43625;-62613 41333 1;-62665 40714;-62634 39877;-63255 39877 1;-63688 39877;-63816 40383;-63847 40815 1;-64025 43309;-64059 44775;-63477 47206 18; -63477 47206 1;-63377 47624;-63092 47956;-62663 47946 1;-62247 47936;-61877 47594;-61923 47181 1;-62180 44896;-62422 43625;-62613 41333 1;-62665 40714;-62634 39877;-63255 39877 1;-63688 39877;-63816 40383;-63847 40815 1;-64025 43309;-64059 44775;-63477 47206 18; -63477 47206 1;-63377 47624;-63092 47956;-62663 47946 1;-62247 47936;-61877 47594;-61923 47181 1;-62180 44896;-62422 43625;-62613 41333 1;-62665 40714;-62634 39877;-63255 39877 1;-63688 39877;-63816 40383;-63847 40815 1;-64025 43309;-64059 44775;-63477 47206 18; -76422 51243;-75867 49602; -74683 45630;-73893 42916;-72388 43310;-72870 37290; -67266 49928;-69306 51195; -71178 54524;-71178 54524;-68359 52939 32;-66167 51707; -68410 53393;-68410 53393;-65156 58368 32;-65946 59059 1;-66356 59411;-66391 59878;-66267 60404 1;-66135 60963;-66239 61438;-66571 61847; -61258 63608 1;-62680 60985;-63900 59663;-65456 57117 1;-67179 54297;-68868 52119;-69843 49890 1;-71121 46969;-71775 45194;-72064 42019 1;-72358 38790;-72824 37306;-71793 33802;-66340 14977;-61634 -1325;-59401 -9263;-56335 -20161;-50257 -43932;-46400 -59172 1;-46186 -60019;-45749 -60376;-45092 -60952 16; -67670 67288;-69582 65240;-66855 62057;-66740 62170 1;-66166 63233;-65820 63846;-65222 64925;-64956 65595;-57868 65009;-57794 64590;-56690 64698;-56998 67054;-60070 66659;-60218 68238;-65474 67535;-67670 67288 18; -65061 65296;-65038 65388;-64956 65595;-57868 65009;-57823 64755;-61825 63883;-65061 65296 18; -57842 64628;-56673 64785;-56651 64580;-56523 63621 1;-56455 63108;-56111 62735;-55688 62436 1;-56105 61729;-56481 61098;-56826 60500;-57199 59842 1;-58022 58361;-58662 56987;-59268 54987;-62539 56182;-60669 58982;-60470 59250;-60423 59305 1;-59556 60569;-58930 61200;-58252 62575 1;-57933 63222;-57817 63633;-57746 64351;-57842 64628 18; -60858 52106 1;-60722 52204;-60562 51747;-60597 51583 1;-61076 49363;-61493 48379;-61661 46114 1;-61842 43674;-61989 42299;-61908 39853; -59159 53844;-59316 53403 1;-59711 53209;-59984 53003;-60093 52576 1;-60190 52197;-60060 51931;-59991 51546;-60389 50149 1;-60659 48926;-60843 48252;-61019 47024 1;-61413 44284;-61345 42708;-61280 39941 16; -54361 63265 1;-55398 61882;-55847 61012;-56726 59523 1;-57953 57445;-58373 56125;-59159 53844 1;-58197 53531;-57139 52999;-57333 52006 1;-57454 51388;-57771 50969;-58369 50772 1;-59039 50551;-59462 50764;-60111 51041 16; -59156 53823;-58999 53790 1;-58084 53471;-57150 52944;-57333 52006 1;-57454 51388;-57771 50969;-58369 50772 1;-59039 50551;-59462 50764;-60111 51041;-60127 51067;-59991 51546 1;-60060 51931;-60190 52197;-60093 52576 1;-59984 53003;-59711 53209;-59316 53403;-59208 53708;-59156 53823 18; -62522 56275;-59265 55090;-59351 54838 1;-59671 53853;-60314 53456;-61169 52832;-63397 50562;-66050 47836;-67444 44974;-68283 42346;-68357 40360;-68308 36375;-69275 36300 1;-69580 39093;-69438 40948;-68955 43922 1;-68909 44204;-68859 44471;-68805 44725;-68712 45135 1;-68311 46770;-67699 47918;-66749 49606;-66718 49660;-65685 51420;-65611 51545;-65142 52336;-65063 52451;-63931 54097;-63844 54228;-62536 56186;-62522 56275 18; -68028 39317;-61897 39848;-61913 39998 1;-61983 42354;-61838 43723;-61661 46114 1;-61493 48379;-61076 49363;-60597 51583 1;-60564 51737;-60703 52148;-60832 52118;-61798 51778;-64969 49384;-67054 46337;-68066 42439;-68436 39762;-68028 39317 18; -72505 48179; -73054 50750; -50138 58884;-49934 57130; -50138 58884;-49934 57130; -50866 58588;-50662 56834; -50866 58588;-50662 56834; -50066 56050;-49623 56101;-49682 56620; -50755 56869;-50726 56614;-49660 56735; -51139 57898; -51065 58937;-49390 61493;-49353 61369;-49129 59445;-47920 59585;-47614 56939;-48665 56817;-48544 55776;-50009 55622;-50035 55784;-50066 56054;-49623 56105;-49696 56738;-49951 56702;-50726 56614;-50755 56869;-50995 58239;-51065 58937 18; -35637 63787 32;-35487 62495 32;-35027 62548 32;-35177 63841 48; -35382 63815;-35332 63388; -35637 63787 32;-35487 62495 32;-35027 62548 32;-35177 63841 32;-35637 63787 50; -46074 54312 32;-45924 53020 32;-45464 53073 32;-45614 54366 48; -46074 54312 32;-45924 53020 32;-45464 53073 32;-45614 54366 32;-46074 54312 50; -45819 54340;-45769 53913; -50031 55845;-53029 55498; -42414 49532 32;-42007 46176 32;-38874 46555 32;-39281 49911 32;-42414 49532 50; -42280 50301 32;-42501 52107 32;-40154 52394 32;-39933 50588 32;-42280 50301 50; -38459 44665; -42420 44159; -44706 47230; -41373 47073; -40100 48696; -38634 52588 32;-38494 51450 32;-39387 51341 32;-39527 52479 32;-38634 52588 50; -38695 51012 32;-38623 50417 32;-39570 50302 32;-39642 50897 32;-38695 51012 50; -39149 50648; -39007 51973; -38634 52588 32;-38494 51450 32;-39387 51341 32;-39527 52479 32;-38634 52588 50; -42414 49532 32;-42007 46176 32;-38874 46555 32;-39281 49911 32;-42414 49532 50; -42280 50301 32;-42501 52107 32;-40154 52394 32;-39933 50588 32;-42280 50301 50; -38237 53080 32;-43107 52489 32;-42289 45747 32;-38429 46215 32;-38881 49941 32;-37871 50064 32;-38237 53080 50;-38634 52588 32;-38494 51450 32;-39387 51341 32;-39527 52479 32;-38634 52588 50;-40154 52394 32;-39933 50588 32;-42280 50301 32;-42501 52107 32;-40154 52394 50;-39281 49911 32;-38874 46555 32;-42007 46176 32;-42414 49532 32;-39281 49911 50; -34468 51263; -34939 52519; -42529 53321; -40901 52742 1;-41219 52935;-41304 52848;-41345 53220 1;-41374 53485;-41073 53714;-40697 53758 1;-38887 53970;-37873 54088;-36064 54300 1;-35721 54340;-35480 54252;-35440 53909 1;-35401 53577;-35646 53362;-35978 53323 1;-36867 53219;-37369 53170;-38258 53069;-40901 52742 18; -51905 49085; -52410 47550; -31833 46621; -31361 45665;-34519 46331 1;-35077 46449;-35407 46513;-35975 46454;-38428 46213;-42289 45747 32;-43107 52489 1;-44109 55092;-44604 56577;-45518 59212 1;-46160 61063;-46522 62061;-47215 63917 1;-47797 63344;-48302 62769;-48956 61973 1;-49101 61797;-49237 61627;-49364 61465;-49129 59445;-47920 59585;-47614 56939;-48665 56817;-48544 55776 16; -31377 46421;-31361 45665;-34519 46331 1;-35077 46449;-35407 46513;-35975 46454;-38427 46211;-38511 46278;-38542 46588;-38415 46613;-36164 46865 1;-35015 46994;-34346 46830;-33216 46581;-32007 46347;-31759 46376;-31377 46421 18; -33989 61628; -35231 64254;-32936 64556;-33853 63455;-31445 46409; -34473 64360;-33776 63546; -33550 71448;-33138 71522;-33118 71342;-32241 64765;-33327 63371;-33070 61506;-32895 60233;-30992 46420;-31456 46487;-33853 63455;-32936 64556;-35210 64289;-35313 64276;-35361 64755;-35260 64766;-33383 65067;-33453 65612;-32763 65726;-32817 66146;-33527 71210;-33550 71448 18; -33667 65606; -34705 65275; -38806 71339;-38763 71366;-36367 72843 1;-35318 73489;-33726 72632;-33555 71412;-32757 65717;-33457 65619;-33379 65059;-35290 64793;-35298 64792;-35276 64795;-35431 66096;-38293 65751;-38320 65977;-38443 67013;-37411 67135;-37722 69758;-38650 69648;-38802 70933;-38806 71339 18; -47307 64464;-46775 64355;-46726 64229 1;-45346 60699;-44779 58632;-43525 55013 1;-43187 54038;-42917 53520;-42594 52545;-43101 52442;-43107 52489;-43242 52840 1;-44155 55230;-44646 56698;-45518 59212 1;-46160 61063;-46522 62061;-47215 63917 1;-47263 63869;-47311 63822;-47358 63774;-47555 63574 1;-48004 63107;-48432 62611;-48956 61973 1;-49101 61797;-49237 61627;-49364 61465;-51077 58849;-51365 59425;-51266 59574 1;-50954 60059;-50615 60550;-50194 61150 1;-49246 62502;-48590 63177;-47411 64333 1;-47392 64352;-47373 64370;-47354 64389;-47307 64464 18; -39825 65575;-39857 65898;-39352 65949;-39554 67941;-40770 68246;-40949 70018;-41418 69722;-41554 68932 1;-41649 68394;-41867 68067;-42319 67760 1;-43741 66794;-44548 66256;-45859 65144 1;-46200 64854;-46499 64592;-46771 64343 1;-45358 60743;-44792 58671;-43525 55013 1;-43187 54038;-42917 53520;-42594 52545;-38237 53080 32;-37871 50064 32;-38881 49941 32;-38479 46606;-36164 46865 1;-35015 46994;-34346 46830;-33216 46581;-32007 46347;-31408 46417 16; -35245 64256;-32924 64570;-33840 63471;-33825 63254;-31456 46487;-31787 46373;-32007 46347;-33216 46581 1;-34346 46830;-35015 46994;-36164 46865;-38479 46606;-38881 49941 32;-37871 50064 32;-38237 53080;-38057 53092 1;-37288 53178;-36798 53227;-35978 53323 1;-35646 53362;-35401 53577;-35440 53909 1;-35480 54252;-35721 54340;-36064 54300 1;-37873 54088;-38887 53970;-40697 53758 1;-41073 53714;-41374 53485;-41345 53220 1;-41304 52848;-41219 52935;-40901 52742;-40870 52746;-41245 52711;-42594 52545 1;-42917 53520;-43187 54038;-43525 55013 1;-44792 58671;-45358 60743;-46771 64343 1;-46499 64592;-46200 64854;-45859 65144 1;-44548 66256;-43741 66794;-42319 67760 1;-41867 68067;-41649 68394;-41554 68932;-41418 69722;-40949 70018;-40770 68246;-39554 67941;-39352 65949;-39857 65898;-39828 65602;-41348 65409;-40729 60192;-35277 60785;-35460 62478;-35032 62547;-35245 64256 18; -33423 46296;-56291 43387; -51335 59480;-51047 58887;-51356 58383 1;-51545 58047;-51745 57690;-51966 57298 1;-53207 55101;-53659 53736;-54520 51364 1;-55031 49957;-55288 49150;-55606 47687 1;-55926 46212;-56090 45370;-56173 43863 1;-56264 42212;-56222 41053;-56137 39713;-56603 39688 1;-56753 42136;-56771 43573;-56399 46029 1;-56053 48311;-55710 49581;-54931 51753 1;-54115 54030;-53583 55289;-52480 57442 1;-52144 58099;-51853 58622;-51551 59117;-51335 59480 18; -52800 55710 1;-52562 56205;-52291 56723;-51966 57298 1;-51631 57891;-51346 58406;-51069 58885;-51029 58537 16; -54459 53030;-55545 53375 1;-55319 52873;-55179 52509;-55335 51981 1;-55600 51084;-55808 50482;-56556 49921 1;-57061 49542;-57268 49036;-57148 48416 1;-57019 47753;-56892 47179;-56272 46910;-54459 53030 18; -56182 43692 1;-56179 43748;-56176 43805;-56173 43863 1;-56090 45370;-55926 46212;-55606 47687 1;-55288 49150;-55031 49957;-54520 51364 1;-53943 52955;-53549 54093;-52978 55332 16; -49479 61538;-47086 64277;-45593 60131;-42830 52458;-42559 49472;-42188 45944;-35329 46684;-31382 45971;-31265 44864;-31614 44824;-32195 44235;-43027 41206;-54969 39997;-56326 40021;-56474 42958;-56376 44783;-55784 48312;-54772 51199;-53612 54505;-52083 57762;-50553 60130;-49479 61538 18; -55704 62465;-55163 62159;-55303 61913 1;-55779 61172;-56165 60474;-56726 59523 1;-57953 57445;-58373 56125;-59159 53844;-59272 53526;-59316 53403 1;-59711 53209;-59984 53003;-60093 52576 1;-60190 52197;-60060 51931;-59991 51546;-60127 51067;-60111 51041;-60272 50561;-60389 50149 1;-60659 48926;-60843 48252;-61019 47024 1;-61413 44284;-61345 42708;-61280 39941;-61898 39855;-61913 39998 1;-61983 42354;-61838 43723;-61661 46114 1;-61493 48379;-61076 49363;-60597 51583 1;-60573 51693;-60638 51935;-60724 52053 1;-60742 52078;-60727 52111;-60754 52125 1;-60794 52145;-60823 52146;-60867 52138;-61142 52796;-61131 52864;-60912 53018 1;-60185 53544;-59638 53954;-59351 54838;-59265 55090;-59190 55356 1;-58348 58002;-57396 59552;-55999 61909;-55704 62465 18; -52203 63420;-49839 62905;-49727 62769 1;-49598 62640;-49440 62516;-49251 62400;-49333 62295 1;-49608 61959;-49885 61590;-50194 61150 1;-50629 60531;-50976 60028;-51295 59528;-51455 59613 1;-53238 60861;-54734 58412;-52499 57456;-52546 57313 1;-53391 55657;-53897 54524;-54473 53001;-54577 53067;-55545 53375 1;-55319 52873;-55179 52509;-55335 51981 1;-55600 51084;-55808 50482;-56556 49921 1;-57061 49542;-57268 49036;-57148 48416 1;-57019 47753;-56892 47179;-56272 46910;-56267 46850 1;-56312 46590;-56355 46317;-56399 46029 1;-56756 43672;-56754 42253;-56620 39979;-61280 39942 1;-61345 42708;-61413 44284;-61019 47024 1;-60843 48252;-60659 48926;-60389 50149;-60131 51055;-59986 50987 1;-59406 50738;-58995 50565;-58369 50772 1;-57927 50918;-57638 51185;-57469 51565;-57366 51861 1;-57354 51908;-57343 51956;-57333 52006 1;-57139 52999;-58197 53531;-59159 53844 1;-58373 56125;-57953 57445;-56726 59523 1;-56291 60259;-55962 60844;-55615 61414;-55359 61826 1;-55079 62266;-54770 62718;-54377 63244;-54310 63119 1;-54223 62950;-54113 62860;-53936 62758 1;-53415 62459;-52823 62602;-52427 63054 1;-52340 63153;-52277 63255;-52231 63361;-52203 63420 18; -33371 76509;-32449 78301 1;-32303 78241;-31627 78101;-31561 78096;-31717 79529;-27639 79900;-27519 78801;-27239 78832;-27277 79178;-22806 80075;-22700 79545 1;-21289 79842;-20491 80013;-19053 80127 1;-18230 80192;-17459 80254;-16724 80314;-16258 80351;-15999 78229;-16424 78209;-16521 78202;-17028 78174;-17568 78145 1;-19710 78030;-20895 77706;-22997 77280 1;-23594 77159;-24145 77074;-24666 76995;-25022 76943;-25100 76930 1;-26580 76705;-27853 76468;-29347 75500 1;-30751 74591;-31482 72925;-31363 71702;-33138 71521;-33325 72809 1;-33329 72817;-33334 72824;-33339 72831;-33225 72961 1;-33207 72982;-33192 72998;-33186 73004 1;-32673 73464;-32377 74152;-32445 74891 1;-32508 75565;-32862 76142;-33371 76509 18; -33309 78752;-34194 76880 1;-34418 76930;-34655 76947;-34897 76924 1;-35107 76904;-35308 76856;-35496 76784;-36120 78709;-34846 80001 1;-34367 79483;-33804 79059;-33309 78752 18; -34194 76880;-33309 78752 1;-32975 78546;-32673 78393;-32449 78301;-33371 76509 1;-33615 76685;-33894 76813;-34194 76880 18; -39308 35432; -45153 33513; -51033 32641; -33566 36219; -33467 37995; -55791 39557; -55442 36155; -55250 38057; -55591 32080 1;-55918 32040;-56100 32018;-56427 31978 1;-56982 31911;-57425 31160;-57092 30711 1;-56636 30097;-56426 29812;-55970 29198 1;-55719 28860;-55264 28995;-54850 29071; -55893 31394; -56608 30925; -55733 30024; -59767 29679 1;-59363 29725;-59025 29625;-58804 29284 1;-58444 28730;-58238 28416;-57793 27927 1;-57483 27586;-57307 27336;-57250 26878 1;-57159 26141;-57976 25636;-58718 25669; -58755 27557; -57928 26804; -57964 22739;-57510 20995;-56586 20467;-55135 14784;-63153 14710;-69225 35585;-69269 36294;-69389 39172;-61419 39863;-61000 35224;-60185 30511;-59346 27081;-57964 22739 18; -61221 38886 1;-59063 38913;-59067 36089;-61011 36042;-61221 38886 18; -59767 29679 1;-59363 29725;-59025 29625;-58804 29284 1;-58444 28730;-58238 28416;-57793 27927 1;-57483 27586;-57307 27336;-57250 26878 1;-57159 26141;-57976 25636;-58718 25669;-59269 27158;-59767 29679 18; -55591 32080 1;-55918 32040;-56100 32018;-56427 31978 1;-56982 31911;-57425 31160;-57092 30711 1;-56636 30097;-56426 29812;-55970 29198 1;-55719 28860;-55264 28995;-54850 29071;-55591 32080 18; -55015 33534; -54129 35123;-55705 34905; -54016 34309;-55592 34091; -51446 37789 32;-52104 37693 32;-52150 38010 32;-51492 38106 32;-51446 37789 50; -45480 38614 32;-46138 38518 32;-46184 38835 32;-45526 38931 32;-45480 38614 50; -39774 40470 32;-40432 40374 32;-40478 40691 32;-39820 40787 32;-39774 40470 50; -33633 41318 32;-34291 41222 32;-34337 41539 32;-33679 41635 32;-33633 41318 50; -31614 44789 32;-32608 44652 32;-32580 44452 32;-36395 43925;-36422 44122 32;-38458 43842 32;-38431 43644;-42273 43114;-42300 43313 32;-43365 43167 32;-43211 42100 32;-44333 41945 32;-44305 41741 32;-48010 41231;-48034 41408 32;-50070 41128 32;-50046 40951;-53936 40416;-53960 40592 32;-55025 40446 32;-54573 37176 32;-53883 37271;-53912 37486;-52110 37734;-52149 38011 32;-51489 38102 32;-51451 37825;-49597 38081;-49568 37864 32;-48886 37958 32;-48915 38175;-48526 38228;-48496 38011 32;-47814 38105 32;-47844 38322;-46144 38556;-46184 38835 32;-45526 38931 32;-45485 38647;-43558 38913;-43528 38693 32;-42846 38787 32;-42992 39834 32;-42194 39944 32;-42224 40163;-40437 40409;-40478 40691 32;-39820 40787 32;-39778 40500;-37915 40757;-37885 40535 32;-37203 40629 32;-37233 40851;-36843 40904;-36813 40682 32;-36131 40776 32;-36161 40998;-34296 41255;-34337 41539 32;-33679 41635 32;-33637 41346;-31876 41589;-31845 41364 32;-31163 41458 32;-31614 44789 50; -55835 35844 1;-55907 36399;-55954 36948;-56000 37669 1;-56138 39820;-56273 41136;-56207 43105 16; -40430 40395;-40255 39127 32;-41963 38891 32;-42917 37632 32;-44715 37384 32;-44895 38689 48; -46134 38506;-45956 37213 32;-50645 36566 32;-50825 37871 48; -34293 41234;-34119 39973 32;-39078 39290 32;-39254 40564 48; -52100 37696;-51920 36390 32;-55835 35844 48; -39146 31003;-38971 29735 32;-40679 29499 32;-41633 28240 32;-43431 27992 32;-43611 29297 48; -44850 29114;-44672 27821 32;-49361 27174 32;-49541 28479 48; -33009 31842;-32835 30581 32;-37794 29898 32;-37970 31172 48; -50816 28304;-50636 26998 32;-53749 26564 48; -48832 37243; -54554 31014; -42377 39441; -34613 40540; -30983 41115; -30617 38045; -30440 35637; -31751 35612; -41423 37615; -30608 40457;-32946 40135 32;-33122 41409 48; -31174 41491;-30720 41553;-30568 40438; -29951 35210;-29374 31033; -50162 28403 32;-50820 28307 32;-50866 28624 32;-50208 28720 32;-50162 28403 50; -44196 29228 32;-44854 29132 32;-44900 29449 32;-44242 29545 32;-44196 29228 50; -30330 35403 32;-31324 35266 32;-31296 35066 32;-35111 34539;-35138 34736 32;-37174 34456 32;-37147 34258;-40989 33728;-41016 33927 32;-42081 33781 32;-41927 32714 32;-43049 32559 32;-43021 32355 32;-46726 31845;-46750 32022 32;-48786 31742 32;-48762 31565;-52652 31030;-52676 31206 32;-53741 31060 32;-53289 27790 32;-52599 27885;-52628 28100;-50826 28348;-50865 28625 32;-50205 28716 32;-50167 28439;-48313 28695;-48284 28478 32;-47602 28572 32;-47631 28789;-47242 28842;-47212 28625 32;-46530 28719 32;-46560 28936;-44860 29170;-44900 29449 32;-44242 29545 32;-44201 29261;-42274 29527;-42244 29307 32;-41562 29401 32;-41708 30448 32;-40910 30558 32;-40940 30777;-39153 31023;-39194 31305 32;-38536 31401 32;-38494 31114;-36631 31371;-36601 31149 32;-35919 31243 32;-35949 31465;-35559 31518;-35529 31296 32;-34847 31390 32;-34877 31612;-33012 31869;-33053 32153 32;-32395 32249 32;-32353 31960;-30592 32203;-30561 31978 32;-29879 32072 32;-30330 35403 50; -32349 31932 32;-33007 31836 32;-33053 32153 32;-32395 32249 32;-32349 31932 50; -38490 31084 32;-39148 30988 32;-39194 31305 32;-38536 31401 32;-38490 31084 50; -48923 19010 32;-49581 18914 32;-49627 19231 32;-48969 19327 32;-48923 19010 50; -37251 21691 32;-37909 21595 32;-37955 21912 32;-37297 22008 32;-37251 21691 50; -31110 22539 32;-31768 22443 32;-31814 22760 32;-31156 22856 32;-31110 22539 50; -42957 19835 32;-43615 19739 32;-43661 20056 32;-43003 20152 32;-42957 19835 50; -29091 26010 32;-30085 25873 32;-30057 25673 32;-33872 25146;-33899 25343 32;-35935 25063 32;-35908 24865;-39750 24335;-39777 24534 32;-40842 24388 32;-40688 23321 32;-41810 23166 32;-41782 22962 32;-45487 22452;-45511 22629 32;-47547 22349 32;-47523 22172;-51413 21637;-51437 21813 32;-52502 21667 32;-52050 18397 32;-51360 18492;-51389 18707;-49587 18955;-49626 19232 32;-48966 19323 32;-48928 19046;-47074 19302;-47045 19085 32;-46363 19179 32;-46392 19396;-46003 19449;-45973 19232 32;-45291 19326 32;-45321 19543;-43621 19777;-43661 20056 32;-43003 20152 32;-42962 19868;-41035 20134;-41005 19914 32;-40323 20008 32;-40469 21055 32;-39671 21165 32;-39701 21384;-37914 21630;-37955 21912 32;-37297 22008 32;-37255 21721;-35392 21978;-35362 21756 32;-34680 21850 32;-34710 22072;-34320 22125;-34290 21903 32;-33608 21997 32;-33638 22219;-31773 22476;-31814 22760 32;-31156 22856 32;-31114 22567;-29353 22810;-29322 22585 32;-28640 22679 32;-29091 26010 50; -31596 44818;-31261 44864;-31375 45692; -31425 46505;-31403 46341;-31369 45718;-31345 45657;-31252 44864;-31604 44821;-31160 41501;-30733 41571;-30637 40480;-30384 38369;-29939 35201;-30262 35159;-29905 32332;-29076 32436;-29122 32851;-31016 46601;-31425 46505 18; -30580 40475;-30361 38381;-30769 38404;-32290 38194 32;-32459 39404 32;-41855 38108 32;-42809 36849 32;-53709 35346 32;-53552 34210 32;-55492 33937;-55835 35844;-55674 35867;-51920 36390 32;-52099 37686;-50834 37885;-50806 37731;-50645 36566 32;-45956 37213 32;-46134 38506;-44895 38689 32;-44715 37384 32;-42917 37632 32;-41963 38891 32;-40255 39127 32;-40430 40395;-39225 40557;-39238 40446;-39078 39290 32;-34119 39973 32;-34293 41234;-33118 41409;-33094 41206;-32946 40135 32;-30811 40429;-30580 40475 18; -30511 39452;-32087 39234; -30398 38638;-31974 38420; -56602 39755;-56140 39763;-56130 39605 1;-56092 39016;-56046 38389;-56000 37669 1;-55954 36948;-55907 36399;-55835 35844;-55790 35834;-55450 33984;-55500 33903;-55488 33863 1;-55183 32378;-54927 31328;-54607 30009;-55067 29978 1;-55253 30721;-55435 31454;-55659 32409 1;-56301 35152;-56421 36754;-56595 39562;-56602 39755 18; -56412 40193;-34289 42496;-30555 42252;-29334 32865;-41617 31818;-53481 30318;-55121 30597;-55888 34331;-56377 38274;-56412 40193 18; -51750 27283; -53179 27436; -54575 30466;-53652 26731;-52920 26833; -50961 28271;-50787 27007; -49389 28487;-49215 27223; -45007 29101;-44833 27837; -43459 29314;-43285 28050; -39301 30979;-39127 29715; -37821 31195;-37647 29931; -33168 31836;-32994 30572; -31681 32041;-31507 30777; -29949 31275; -30899 31509; -35636 30732; -39091 28302; -35945 28771; -33860 28894; -29455 26957; -32811 26807 1;-33107 26726;-33414 26900;-33496 27196 1;-33578 27492;-33404 27799;-33107 27881 1;-32811 27963;-32504 27789;-32422 27492 1;-32341 27196;-32515 26889;-32811 26807 18; -32811 26807 1;-33107 26726;-33414 26900;-33496 27196 1;-33578 27492;-33404 27799;-33107 27881 1;-32811 27963;-32504 27789;-32422 27492 1;-32341 27196;-32515 26889;-32811 26807 18; -32021 27980; -31160 29989 32;-29238 30254 32;-29072 29051 32;-30994 28786 32;-31160 29989 50; -31649 28654 1;-31669 28447;-31632 28243;-31439 28166 1;-31146 28049;-30934 27974;-30637 28079 1;-30177 28241;-29724 28412;-29695 28899;-31649 28654 18; -29766 28631;-29766 28631 1;-29915 28340;-30274 28207;-30637 28079 1;-30934 27974;-31146 28049;-31439 28166 1;-31535 28204;-31593 28274;-31623 28360; -53206 24684;-50941 25001 32;-51098 26137 32;-41525 27457 32;-40571 28716 32;-31899 29912 32;-31724 28641 32;-29041 29011 48; -29324 31065;-31662 30743 32;-31838 32017 48; -30282 35135;-29939 35182; -29896 32315;-29553 32362; -41323 23741; -42310 23877; -40065 25518; -42853 26593; -42101 28814; -47309 28053; -47019 26218 1;-46574 26420;-46335 26576;-45859 26687 1;-45564 26756;-45158 26509;-45242 26218 1;-45398 25680;-45538 25382;-45859 24923 1;-45991 24734;-46276 24649;-46439 24812 1;-46716 25089;-46850 25265;-47130 25539 1;-47322 25727;-47264 26107;-47019 26218 18; -53742 26539 1;-53915 27173;-54093 27866;-54286 28675 1;-54778 30741;-55095 31944;-55503 33935;-53552 34210 32;-53709 35346 32;-42809 36849 32;-41855 38108 32;-32459 39404 32;-32291 38183 32;-30396 38444;-29556 32347 16; -50965 25033 32;-52951 24758 32;-53099 25828 32;-51113 26102 32;-50965 25033 50; -53300 25852;-53137 24670; -49991 26256 1;-49764 25620;-49440 25352;-48997 24842 1;-48656 24450;-48355 24329;-47880 24118 1;-47632 24008;-47505 23749;-47584 23490 1;-47708 23081;-48211 23186;-48639 23194 1;-49360 23207;-49680 22644;-50401 22644 1;-51187 22644;-51642 22368;-52268 21894 1;-52623 21625;-52962 21723;-53402 21789;-53321 21930 1;-53189 22166;-53101 22324;-52957 22582 1;-52791 22879;-52757 23120;-52852 23446 1;-52982 23891;-53112 24335;-53223 24713;-53045 24707;-50941 25001 32;-51098 26137 32;-51001 26150;-49991 26256 18; -53079 21754; -55101 30062;-54607 30099;-54563 29827 1;-54476 29469;-54385 29089;-54286 28675 1;-54093 27866;-53915 27173;-53742 26539;-53628 26581;-50636 26998 32;-50816 28304;-49540 28481;-49361 27174 32;-44672 27821 32;-44850 29114;-43609 29284;-43431 27992 32;-41633 28240 32;-40679 29499 32;-38971 29735 32;-39143 30978;-37961 31110;-37794 29898 32;-35797 30173;-35586 30202;-32835 30581 32;-33009 31842;-31834 31987;-31662 30743 32;-29324 31065;-29205 30260;-31142 29976;-31006 28767;-31216 28711;-31724 28641 32;-31899 29912 32;-40571 28716 32;-41525 27457 32;-51098 26137;-53318 25816;-53204 24674;-53139 24427 1;-53049 24119;-52950 23783;-52852 23446 1;-52757 23120;-52791 22879;-52957 22582 1;-53119 22292;-53210 22129;-53372 21839 1;-53518 21577;-53452 21358;-53378 21078;-53872 21126 1;-53883 21166;-53894 21208;-53905 21249 1;-54012 21648;-53906 21931;-53696 22287 1;-53564 22511;-53555 22525;-53423 22748 1;-53131 23242;-53431 23757;-53582 24311;-54524 27770;-55101 30062 18; -55185 30938;-29991 35195;-29363 30728;-40773 29123;-41645 27832;-53928 26262;-55185 30938 18; -29044 25776;-28589 25837; -29029 29056;-28581 25840;-30049 25655;-40817 24146;-40625 23065;-45510 22437;-46069 24757 1;-45987 24790;-45911 24849;-45859 24923 1;-45538 25382;-45398 25680;-45242 26218 1;-45158 26509;-45564 26756;-45859 26687 1;-46335 26576;-46574 26420;-47019 26218 1;-47195 26138;-47275 25919;-47234 25732;-47793 24070 1;-47820 24088;-47849 24104;-47880 24118 1;-48355 24329;-48656 24450;-48997 24842 1;-49440 25352;-49764 25620;-49991 26256;-50051 26250;-49617 26341;-41525 27457 32;-40571 28716 32;-31899 29912 32;-31724 28641 32;-29029 29056 18; -45508 22628;-45986 24801 1;-46012 24783;-46040 24769;-46069 24757;-46142 24735 1;-46248 24712;-46359 24732;-46439 24812 1;-46716 25089;-46850 25265;-47130 25539 1;-47198 25606;-47235 25697;-47243 25791;-47889 24121;-47743 24032 1;-47588 23901;-47521 23696;-47584 23490 1;-47708 23081;-48211 23186;-48639 23194 1;-49360 23207;-49680 22644;-50401 22644 1;-51187 22644;-51642 22368;-52268 21894 1;-52623 21625;-52962 21723;-53402 21789;-53444 21654 1;-53485 21467;-53434 21290;-53378 21078;-53331 20896 1;-53082 19919;-52855 19067;-52643 18296;-52047 18372;-52502 21667 32;-51437 21813 32;-51413 21637;-47523 22172;-47547 22349 32;-46078 22551;-45508 22628 18; -53130 18623 1;-53505 18525;-53814 18159;-53679 17795 1;-53410 17071;-53295 16642;-52912 15971 1;-52770 15723;-52631 15759;-52345 15753; -54528 14403 1;-54263 14379;-54250 14382;-53986 14353 1;-53497 14299;-53364 13389;-53171 12368 1;-53101 11999;-53328 11753;-53701 11566 1;-53905 11464;-54075 11739;-54133 11960 1;-54345 12767;-54505 13209;-54700 14021 1;-54738 14180;-54691 14418;-54528 14403 18; -54235 13920; -53594 12883; -53754 11995; -52390 6808 1;-51975 6954;-51519 6736;-51372 6320 1;-51226 5905;-51444 5449;-51860 5303 1;-52275 5156;-52731 5374;-52877 5790 1;-53024 6206;-52806 6661;-52390 6808 18; -52390 6808 1;-51975 6954;-51519 6736;-51372 6320 1;-51226 5905;-51444 5449;-51860 5303 1;-52275 5156;-52731 5374;-52877 5790 1;-53024 6206;-52806 6661;-52390 6808 18; -49148 7144; -41499 13180; -45354 12657; -28931 17317; -30344 18800; -34200 18608; -36486 18329; -35561 17858; -39469 17806; -43684 17526; -44320 18897; -43841 18390; -47478 16392; -50462 15363; -49816 17932; -49940 18804; -49627 16178;-49357 14222; -49698 16140;-49802 16891; -51624 15258 1;-51257 14451;-50426 14154;-49556 14322; -53170 17456; -54528 14403 1;-54263 14379;-54250 14382;-53986 14353 1;-53497 14299;-53364 13389;-53171 12368 1;-53101 11999;-53328 11753;-53701 11566 1;-53905 11464;-54075 11739;-54133 11960 1;-54345 12767;-54505 13209;-54700 14021 1;-54738 14180;-54691 14418;-54528 14403 18; -53130 18623 1;-53505 18525;-53814 18159;-53679 17795 1;-53410 17071;-53295 16642;-52912 15971 1;-52770 15723;-52631 15759;-52345 15753;-53130 18623 18; -48958 19015;-48337 14517; -48501 19077;-48258 17315;-48584 16884; -52672 18069;-49466 18511; -52632 18257 1;-52863 19091;-53086 19935;-53360 21010 1;-53440 21324;-53530 21556;-53372 21839 1;-53210 22129;-53119 22292;-52957 22582 1;-52791 22879;-52757 23120;-52852 23446 1;-52982 23891;-53112 24335;-53223 24713 16; -49579 18937;-49544 18686 16; -49493 18318;-49303 16939;-49701 16884;-49602 16167 16; -49561 18911;-48951 18998;-48322 14445;-49308 14174;-49622 16172;-49692 16879;-49308 16957;-49451 18269;-49522 18754;-49561 18911 18; -49175 14276;-49362 14224 1;-50375 13940;-51511 14350;-51805 15360 1;-52090 16341;-52318 17129;-52533 17899 16; -51336 18469;-49553 18697;-49572 18926;-51348 18691;-51336 18469 18; -52539 17877;-49473 18297;-49360 17431;-49308 16957;-49692 16879;-49622 16172;-49611 16104;-49467 14312;-49716 14296 1;-50453 14202;-51143 14448;-51521 15063;-51729 15151 1;-51758 15218;-51784 15287;-51805 15360 1;-51973 15937;-52121 16448;-52258 16928;-52539 17877 18; -48976 19008;-48510 19073;-48484 18957;-48258 17315;-48538 16944;-48662 16962;-48976 19008 18; -44032 19679;-43895 18696;-43489 18745;-43252 17024; -42587 19880;-42475 19069; -42648 19068;-42383 17149; -42570 19903;-42204 17085;-43242 16928;-43268 17139;-43489 18745;-43895 18696;-44032 19679;-42570 19903 18; -43225 17022 1;-44107 16876;-45008 16675;-46118 16432;-46375 15061;-48366 14503 16; -37914 21593;-37610 19384;-38700 19234;-38433 17291; -36709 21768;-36509 20316;-37053 20241;-36647 17296; -38525 19252;-38254 17282; -37916 21608;-36721 21783;-36690 21630;-36509 20316;-37053 20241;-36647 17296;-36633 17220;-38117 17220;-38396 19262;-38240 19297;-37610 19384;-37892 21435;-37916 21608 18; -31835 19229;-31569 17300; -31810 22459;-31367 19248;-31702 19202; -29034 29018;-28400 24579;-26907 23160;-26178 17897 1;-26033 16924;-26852 16010;-27836 16001 1;-28454 15995;-28801 16027;-29419 16035;-30187 17282;-30528 17289 16; -30688 21939;-30583 21181;-31026 21120;-30498 17283 16; -28057 24240 1;-28294 23530;-28235 22999;-28146 22256;-30712 21905 16; -29028 25768;-28590 25842;-28386 24565;-28043 24245;-28090 24135 1;-28288 23476;-28231 22962;-28146 22256;-30681 21909;-30583 21181;-31026 21120;-30498 17283;-31445 17301;-31698 19194;-31518 19227;-31367 19248;-31807 22435;-29334 22815;-29325 22614;-28627 22736;-29028 25768 18; -28052 24216;-27912 24115;-26907 23160;-26178 17897 1;-26033 16924;-26852 16010;-27836 16001 1;-28454 15995;-28801 16027;-29419 16035;-30187 17282;-30528 17289;-31961 17319;-32042 17321 1;-35005 17299;-36666 17281;-39629 17296 1;-40765 17302;-41701 17237;-42586 17118;-43514 16972 1;-44305 16832;-45129 16649;-46118 16432;-46375 15061;-48366 14503;-48373 14812;-48670 16961;-48591 16952;-48538 16944;-48258 17315;-48484 18957;-48509 19068;-43827 20292;-38767 22211;-30636 23258;-28052 24216 18; -31374 17307;-32042 17321 1;-33879 17307;-35216 17295;-36672 17292 16; -38059 17291 1;-38547 17291;-39064 17293;-39629 17296 1;-40765 17302;-41701 17237;-42586 17118 16; -53902 21169;-53390 21113;-52678 18318;-52545 17834;-52467 17633;-52258 16928 1;-52121 16448;-51973 15937;-51805 15360 1;-51791 15311;-51775 15264;-51757 15218;-51682 15054 1;-51270 14275;-50280 13975;-49382 14219;-48244 14537;-46375 15061;-46118 16432 1;-45052 16665;-44179 16860;-43330 17004;-42087 17179 1;-41346 17259;-40554 17301;-39629 17296 1;-39150 17294;-38706 17292;-38284 17291;-36576 17292 1;-35157 17296;-33839 17307;-32042 17321;-31484 17309;-30435 17287;-30187 17282;-29419 16035 1;-28801 16027;-28454 15995;-27836 16001 1;-26852 16010;-26033 16924;-26178 17897;-26702 21682;-26714 21765;-26907 23160;-28400 24579;-28580 25837;-28159 25860;-28007 24759;-26550 23363;-26252 21066;-26237 20947;-25869 18111 1;-25715 16920;-26491 15603;-27691 15634;-29681 15685;-30421 16871;-30784 16871;-31750 16870;-34757 16869 1;-36651 16868;-37714 16868;-39608 16867 1;-42037 16866;-43386 16497;-45775 16058;-46045 14671;-49421 13825 1;-50599 13530;-51909 14034;-52204 15212 1;-52707 17219;-53121 18455;-53620 20217;-53902 21169 18; -29530 32450;-29061 32499;-29045 32290;-28152 25808;-28583 25799;-28590 25842;-28593 25841;-28627 26165;-29034 29018;-29236 30294;-29375 31166;-29530 32450 18; -24683 53659 32;-27196 53327 32;-26565 48547 32;-24052 48879 32;-24683 53659 50; -23918 47614 32;-26410 47285 32;-25795 42631 32;-23303 42960 32;-23918 47614 50; -23140 41717 32;-25632 41388 32;-24540 33118 32;-23253 33288 16; -23052 32327 32;-24404 32144 32;-23762 27392 32;-22410 27575 32;-23052 32327 50; -22312 26392;-23599 26219;-23452 25129;-24502 23752;-24145 21100;-21622 21440; -25230 57467;-27761 58204;-27290 54531; -27132 53341;-26499 48553; -26338 47287;-25724 42633; -21968 24107 32;-24428 23775 32;-24080 21197 32;-21620 21529 48; -23542 21897; -22415 22663 1;-22194 22699;-22137 22913;-22084 23131 1;-21998 23484;-21937 23996;-22298 24038 1;-22494 24060;-22697 23866;-22685 23670 1;-22670 23411;-22763 22605;-22415 22663 18; -22685 23668 1;-22671 23406;-22762 22605;-22415 22663 1;-22196 22699;-22138 22909;-22085 23125; -26230 52054; -26082 49253; -25774 46786; -24737 45035; -25083 41075; -22290 38408; -22611 37643; -24096 35868; -23404 30045; -22189 17432; -21066 17012; -25571 41398;-25165 38321 16; -25013 37168;-24478 33119 16; -25056 38262 32;-23560 38459 32;-23427 37451 32;-24923 37254 32;-25056 38262 50; -25056 38262 32;-23560 38459 32;-23427 37451 32;-24923 37254 32;-25056 38262 50; -26783 53227;-25985 53332; -25982 47143;-25184 47248; -27751 58202;-25172 57449;-24827 54883;-25123 54846;-27292 54560;-27751 58202 18; -27129 53324;-24711 53657;-24094 48883;-24266 48851;-26462 48561;-27129 53324 18; -26326 47289;-23910 47603;-23291 42971;-23560 42926;-25709 42642;-26326 47289 18; -25558 41410;-23141 41724;-22871 39744;-21536 39910;-21046 36376;-21291 36337;-23590 36025 32;-23221 33290;-23460 33261;-24481 33126;-25558 41410 18; -24339 32146;-23074 32321;-22454 27558;-23711 27410;-24339 32146 18; -23600 26222;-22298 26401;-21983 24130;-24426 23810;-24401 23902;-23451 25142;-23600 26222 18; -24394 23748;-21964 24062;-21655 21558;-24055 21249;-24394 23748 18; -23888 20067;-21463 20363;-21176 18265;-20606 18346;-20533 17928;-19923 18013;-19657 16199;-21226 16160 1;-22327 16155;-23453 16672;-23601 17763;-23689 18415;-23888 20067 18; -10916 15685;-21530 15665 1;-22702 15721;-23869 16448;-24030 17610;-24898 23872;-23850 25224;-28204 57568;-29623 58604;-29859 60374; -29866 60446;-29325 60490;-27808 58300;-27275 54554;-24876 54894;-24710 53647;-27127 53350;-26448 48566;-24102 48880;-23953 47615;-26326 47309;-25697 42623;-23316 42955;-23167 41716;-25540 41402;-24462 33120;-23241 33299;-23081 32312;-24339 32145;-23696 27390;-22469 27575;-22308 26397;-23597 26218;-23468 25127;-24430 23823;-24055 21180;-21630 21468;-21499 20395;-23898 20063;-23851 19760;-23689 18415;-23601 17763 1;-23453 16672;-22327 16155;-21226 16160;-21183 16160;-21133 16169;-20134 16113;-20172 15636;-21565 15664 1;-22737 15720;-23862 16395;-24023 17557;-24659 22145;-24806 23210;-24898 23872;-23850 25224;-28204 57568;-29623 58604;-29811 60014;-29866 60446 18; -33150 71560;-31379 71752;-31267 70927;-31107 69630;-29859 60374;-29745 59518;-29623 58604;-28204 57568;-23850 25224;-24898 23872;-24030 17610 1;-23908 16726;-23204 16094;-22358 15818;-27064 15749 1;-26233 16092;-25744 17143;-25869 18111;-26550 23363;-28007 24759;-33327 63371;-32241 64765;-33056 70876;-33150 71560 18; -57964 22739;-57475 20862;-56586 20467;-55138 14795;-63156 14721;-69225 35585;-69269 36294;-69389 39172;-61419 39863;-61000 35224;-60185 30511;-59346 27081;-57964 22739 18; -61292 39998;-56641 40097;-56603 39688 1;-56604 39704;-56605 39719;-56606 39735;-56591 39504 1;-56419 36732;-56297 35133;-55659 32409 1;-55633 32298;-55607 32189;-55582 32083;-55839 32050 1;-56032 32026;-56192 32007;-56427 31978 1;-56982 31911;-57425 31160;-57092 30711 1;-56636 30097;-56426 29812;-55970 29198 1;-55719 28860;-55264 28995;-54850 29071;-54815 28925;-54524 27770;-53582 24311 1;-53431 23757;-53131 23242;-53423 22748 1;-53555 22525;-53564 22511;-53696 22287 1;-53906 21931;-54012 21648;-53905 21249 1;-53894 21208;-53883 21166;-53872 21126;-53811 20903 1;-53571 20024;-53353 19291;-53141 18577;-53251 18581 1;-53568 18444;-53799 18119;-53679 17795 1;-53410 17071;-53295 16642;-52912 15971 1;-52770 15723;-52631 15759;-52345 15753;-52297 15545;-54740 15595;-56245 20999;-57697 21763;-58885 25711;-58588 25669 1;-57884 25696;-57164 26184;-57250 26878 1;-57307 27336;-57483 27586;-57793 27927 1;-58238 28416;-58444 28730;-58804 29284 1;-59025 29625;-59363 29725;-59767 29679;-59946 29684;-60909 35013;-61007 36074;-60776 36062 1;-59070 36321;-59154 38912;-61221 38886;-61378 39232;-61353 39800;-61292 39998 18; -57678 21794;-56247 21029;-54742 15588;-54394 14400 16; -42375 10646; -46806 8710; -45044 8675; -43387 8692; -41119 9425; -38345 9442; -36094 9442; -34367 9407; -34070 6302; -44644 5639; -44644 4679; -47871 4819; -33477 3842;-48412 3825; -36513 4033;-36513 6007; -40374 4021;-40374 6193; -39798 1566;-39800 3553; 2190 -4523;2195 53;2212 12685 1;2213 13560;1238 14411;363 14412;-20139 14209;-36427 14189 1;-39657 14185;-41498 14096;-44668 13473 1;-46567 13100;-47611 12793;-49490 12332 1;-50688 12038;-50976 10566;-50624 9383 1;-49635 6061;-48887 4237;-47322 1144 1;-46482 -517;-45905 -1401;-44805 -2902 1;-43220 -5066;-42173 -6172;-40500 -8269 1;-39636 -9352;-38684 -10157;-37342 -9811 1;-34300 -9027;-32598 -8569;-29530 -7891 1;-27431 -7427;-26235 -7221;-24096 -7006 1;-20512 -6646;-18476 -6639;-14876 -6766 1;-10303 -6928;-7742 -7097;-3169 -7272 1;-2052 -7315;-1679 -7356;-561 -7383 1;1139 -7424;2190 -6333;2190 -4523 18; -48242 4219 1;-48963 6244;-49423 7361;-50084 9406 1;-50120 9518;-50141 9582;-50177 9694 1;-50489 10660;-49968 11750;-48985 12005 1;-46697 12599;-45419 12993;-43079 13331 1;-41171 13607;-40086 13729;-38159 13724;-33768 13713;-33730 6230 1;-33729 6052;-33905 5942;-34083 5941;-34423 5939; -35399 12739 32;-34709 12742 32;-34706 12049 32;-35396 12046 32;-35399 12739 50; -35399 12739 32;-34709 12742 32;-34706 12049 32;-35396 12046 32;-35399 12739 50; -35685 11756; -37711 13447 1;-37685 12918;-37597 12558;-37483 11980 1;-37408 11596;-37160 11127;-36769 11124 1;-36471 11122;-36203 11120;-35949 11118 33;-35313 11113;-34769 11108;-34069 11107; -16624 12666;-16623 13633;-23966 13561;-23870 1907; -32640 3154;-32631 2046;-29373 2072; -26007 13147;-26005 13579;-29630 13592; -25882 1919 32;-24983 1919 32;-24989 13737 32;-25888 13737 32;-25882 1919 50; -25882 1919 32;-24983 1919 32;-24989 13737 32;-25888 13737 32;-25882 1919 50; -32597 2016;-29369 2051;-29376 2434;-29376 3209 32;-28754 3209 32;-28754 13095 32;-26054 13095;-26006 13286;-26005 13579;-29630 13592;-30102 13587;-32725 13561;-32659 5475;-32597 2016 18; -29630 13592;-32725 13561;-32640 3154; -23686 2118;-20141 2146; -25809 914;-22913 914; -29058 907;-32617 907;-32617 -2565; -25786 907;-29058 907; -15369 907;-22951 907; -15381 914;-12019 914; -40197 5746;-33489 5746; -43822 6028;-43822 4104; -43387 5392;-40612 5392; -42751 3537;-42749 1575; -41368 65008;-41417 65415; -47312 2989; -47841 3430 1;-46664 1149;-46054 -174;-44591 -2282 1;-43534 -3806;-42867 -4609;-41678 -6033;-41569 -6163;-40038 -5377;-40050 -4390 16; -35210 2086; -33779 3135; -34507 3209; -38899 3110; -42057 3011; -44660 3011; -46252 1642; -42785 -2726; -41660 -3608; -45280 -2748; -44784 -3391; -45547 -1858 1;-45595 -2281;-45726 -2659;-45442 -2975 1;-45255 -3183;-45169 -3319;-44962 -3507 1;-44760 -3691;-44522 -3741;-44273 -3629; -45547 -1858 1;-45595 -2281;-45726 -2659;-45442 -2975 1;-45255 -3183;-45169 -3319;-44962 -3507 1;-44760 -3691;-44522 -3741;-44273 -3629;-45547 -1858 18; -42949 1606;-42548 1607; -37577 1793;-39471 1793; -41358 -3158;-41919 -3160 32;-41921 -2479 32;-41360 -2477 48; -41358 -3158;-41919 -3160 32;-41921 -2479 32;-41360 -2477 32;-41358 -3158 18; -43351 -1218;-42827 -494;-42807 -670 1;-42799 -733;-42794 -798;-42792 -869 1;-42781 -1325;-42534 -1799;-42077 -1794;-41397 -1787;-41379 -2335;-42199 -2318 1;-42758 -2286;-43310 -2016;-43344 -1458 1;-43346 -1422;-43347 -1386;-43349 -1351;-43351 -1218 18; -42821 -539;-43339 -1242;-43361 -1106 1;-43379 -944;-43430 -807;-43591 -730 1;-43817 -623;-43987 -487;-43998 -237 1;-44029 481;-44102 868;-44085 1577 1;-44081 1740;-44012 1906;-43850 1922 1;-43494 1958;-43271 1965;-42937 1836;-42968 1540 1;-43159 1170;-43247 877;-43150 457 1;-43071 116;-42948 -117;-42869 -386;-42821 -539 18; -41656 -1790;-41656 -1790;-42077 -1794 1;-42534 -1799;-42781 -1325;-42792 -869 1;-42794 -798;-42799 -733;-42807 -670;-42827 -494;-42874 -369 1;-42953 -107;-43073 123;-43150 457 1;-43226 785;-43189 1035;-43078 1305; -43259 1926;-43259 1926 1;-43445 1956;-43619 1945;-43850 1922 1;-44012 1906;-44081 1740;-44085 1577 1;-44102 868;-44029 481;-43998 -237 1;-43987 -487;-43817 -623;-43591 -730 1;-43430 -807;-43379 -944;-43361 -1106;-43339 -1242;-43346 -1418 1;-43345 -1431;-43345 -1445;-43344 -1458 1;-43315 -1940;-42898 -2207;-42425 -2292; -34519 5948;-34239 5940;-34083 5941 1;-33905 5942;-33729 6052;-33730 6230;-33755 11166;-33893 11107 1;-34969 11108;-35660 11116;-36769 11124 1;-37160 11127;-37408 11596;-37483 11980 1;-37614 12645;-37711 13021;-37719 13698;-38060 13724;-38159 13724 1;-40086 13729;-41171 13607;-43079 13331 1;-45419 12993;-46697 12599;-48985 12005 1;-49968 11750;-50489 10660;-50177 9694 1;-50141 9582;-50120 9518;-50084 9406;-48860 5919;-47150 5966;-47122 7623;-47122 8362 32;-42159 8362 32;-42159 9067 32;-34430 9067 32;-34430 7083;-34519 5948 18; -43560 6025;-43560 5572; -45503 5964;-43788 5964;-43788 5563; -45505 6377 32;-43350 6377 32;-43350 5993 32;-45505 5993 32;-45505 6377 50; -33736 5533;-33718 4216; -34879 1608;-33719 2280;-33736 3474; -36201 5533;-33722 5533;-33734 4226;-36201 4238;-36201 5533 18; -48922 6051;-47133 6026;-45517 5965;-43308 5977;-36449 6001;-36486 3731;-33747 3620;-33737 3275;-33723 2280;-34879 1608;-37535 1671;-37757 1782;-40237 1831;-41285 1745;-41371 -3992;-43130 -4264 1;-43597 -3672;-44041 -3075;-44591 -2282 1;-46054 -174;-46664 1149;-47841 3430;-48289 4350 1;-48497 4932;-48683 5440;-48860 5919;-48922 6051 18; -20952 14227;-21002 13635;-23950 13573;-23910 2006;-17315 2023;-17350 889;-32790 994;-32807 1918;-24975 1930;-24974 13748;-32895 13695;-32912 14219;-25567 14201;-20952 14227 18; -34918 171;-32735 183;-32846 13708;-33771 13690;-33721 6216;-33823 6002;-34005 5915;-33703 5585;-33706 5493;-33690 4259;-33687 4129;-33703 3503;-33718 3377;-33724 2281;-34897 1606;-34918 171 18; -33706 3456;-34184 3450; -32852 13681;-32861 14187;-33506 14193;-36427 14189 1;-39657 14185;-41498 14096;-44668 13473 1;-46567 13100;-47611 12793;-49490 12332 1;-50688 12038;-50976 10566;-50624 9383 1;-49635 6061;-48887 4237;-47322 1144 1;-46572 -338;-46032 -1202;-45143 -2437;-44847 -2831;-44273 -3629;-44105 -3842 1;-43963 -4030;-43824 -4211;-43688 -4386;-43159 -4227 1;-43615 -3648;-44053 -3058;-44591 -2282 1;-46029 -210;-46643 1103;-47781 3314;-47857 3397;-47900 3505;-48249 4165;-48289 4273;-48345 4507 1;-48531 5024;-48699 5484;-48860 5919;-48922 6051;-48957 6182 1;-49330 7194;-49667 8115;-50084 9406 1;-50120 9518;-50141 9582;-50177 9694 1;-50489 10660;-49968 11750;-48985 12005 1;-46697 12599;-45419 12993;-43079 13331 1;-41171 13607;-40086 13729;-38159 13724;-35168 13717;-33782 13727;-33752 13622;-32852 13681 18; -21031 14215;-21043 15646;-21142 15655;-21565 15664 1;-22042 15687;-22512 15812;-22911 16031;-26716 15947 1;-26824 15867;-26940 15800;-27064 15749;-27497 15641 1;-27560 15635;-27625 15632;-27691 15634;-29681 15685;-30421 16871;-31505 16871;-32615 16870;-34757 16869 1;-36651 16868;-37714 16868;-39608 16867 1;-42037 16866;-43386 16497;-45775 16058;-46045 14671;-49421 13825 1;-50275 13611;-51198 13817;-51754 14395;-50748 10660 1;-50666 11447;-50271 12140;-49490 12332 1;-47611 12793;-46567 13100;-44668 13473 1;-41498 14096;-39657 14185;-36427 14189;-28174 14199;-27335 14205;-25567 14201;-22054 14221;-21031 14215 18; -50382 -5664 1;-50250 -5726;-50093 -5682;-50025 -5553 1;-49730 -4996;-49617 -4654;-49445 -4048 1;-49327 -3633;-49416 -3323;-49654 -2963 1;-49861 -2650;-50155 -2550;-50530 -2568 1;-51092 -2595;-51427 -2788;-51838 -3172 1;-52123 -3438;-52144 -3770;-52048 -4147 1;-51939 -4572;-51736 -4824;-51345 -5023 1;-50984 -5207;-50527 -5039;-50370 -4665 1;-50198 -4258;-50177 -3706;-50592 -3555 1;-50850 -3461;-51052 -3541;-51283 -3691 1;-51397 -3765;-51384 -3917;-51320 -4036 1;-51265 -4138;-51131 -4106;-51024 -4061 1;-50895 -4007;-50669 -4008;-50678 -4147 1;-50692 -4371;-50726 -4640;-50950 -4653 1;-51285 -4672;-51596 -4490;-51653 -4159 1;-51717 -3791;-51612 -3470;-51295 -3271 1;-50928 -3040;-50545 -3017;-50185 -3259 1;-49759 -3546;-49801 -4117;-50049 -4566 1;-50221 -4877;-50311 -5055;-50481 -5368 1;-50539 -5475;-50492 -5612;-50382 -5664 18; -49099 -4406 1;-49031 -4271;-48916 -4059;-48803 -4159 1;-48703 -4247;-48623 -4284;-48569 -4406 1;-48509 -4542;-48609 -4705;-48754 -4739 1;-48984 -4794;-49085 -4977;-49136 -5208 1;-49187 -5438;-49247 -5721;-49025 -5800 1;-48817 -5874;-48622 -5710;-48544 -5504 1;-48512 -5418;-48493 -5371;-48461 -5285 1;-48433 -5212;-48368 -5140;-48297 -5171 1;-48191 -5218;-48252 -5361;-48297 -5467 1;-48476 -5892;-48807 -6354;-49235 -6183 1;-49531 -6065;-49746 -5741;-49605 -5455 1;-49404 -5047;-49305 -4816;-49099 -4406 18; -52278 -7997 1;-52045 -7531;-51861 -6922;-52313 -6662 1;-52661 -6462;-53123 -6597;-53298 -6959 1;-53415 -7200;-53477 -7339;-53551 -7596 1;-53615 -7819;-53436 -8088;-53228 -8189 1;-52876 -8359;-52453 -8347;-52278 -7997 18; -55165 -8974 1;-55084 -9120;-54841 -9096;-54738 -8965 1;-54609 -8801;-54464 -8618;-54589 -8451 1;-54728 -8266;-55009 -8270;-55182 -8424 1;-55264 -8497;-55283 -8587;-55261 -8695 1;-55238 -8808;-55221 -8873;-55165 -8974 18; -54319 -11382 1;-54207 -11519;-53927 -11471;-53874 -11303 1;-53766 -10962;-53581 -10635;-53839 -10387 1;-53993 -10240;-54082 -10158;-54258 -10038 1;-54407 -9935;-54707 -9988;-54703 -10169 1;-54693 -10636;-54649 -10979;-54319 -11382 18; -54502 -5345 1;-54146 -5276;-53701 -5401;-53673 -5763 1;-53643 -6149;-53513 -6459;-53769 -6749 1;-53931 -6932;-54170 -7063;-54371 -6924 1;-54710 -6689;-55084 -6471;-54991 -6069 1;-54914 -5737;-54837 -5410;-54502 -5345 18; -50382 -5664 1;-50250 -5726;-50093 -5682;-50025 -5553 1;-49730 -4996;-49617 -4654;-49445 -4048 1;-49327 -3633;-49416 -3323;-49654 -2963 1;-49861 -2650;-50155 -2550;-50530 -2568 1;-51092 -2595;-51427 -2788;-51838 -3172 1;-52123 -3438;-52144 -3770;-52048 -4147 1;-51939 -4572;-51736 -4824;-51345 -5023 1;-50984 -5207;-50527 -5039;-50370 -4665 1;-50198 -4258;-50177 -3706;-50592 -3555 1;-50850 -3461;-51052 -3541;-51283 -3691 1;-51397 -3765;-51384 -3917;-51320 -4036 1;-51265 -4138;-51131 -4106;-51024 -4061 1;-50895 -4007;-50669 -4008;-50678 -4147 1;-50692 -4371;-50726 -4640;-50950 -4653 1;-51285 -4672;-51596 -4490;-51653 -4159 1;-51717 -3791;-51612 -3470;-51295 -3271 1;-50928 -3040;-50545 -3017;-50185 -3259 1;-49759 -3546;-49801 -4117;-50049 -4566 1;-50221 -4877;-50311 -5055;-50481 -5368 1;-50539 -5475;-50492 -5612;-50382 -5664 18; -49099 -4406 1;-49031 -4271;-48916 -4059;-48803 -4159 1;-48703 -4247;-48623 -4284;-48569 -4406 1;-48509 -4542;-48609 -4705;-48754 -4739 1;-48984 -4794;-49085 -4977;-49136 -5208 1;-49187 -5438;-49247 -5721;-49025 -5800 1;-48817 -5874;-48622 -5710;-48544 -5504 1;-48512 -5418;-48493 -5371;-48461 -5285 1;-48433 -5212;-48368 -5140;-48297 -5171 1;-48191 -5218;-48252 -5361;-48297 -5467 1;-48476 -5892;-48807 -6354;-49235 -6183 1;-49531 -6065;-49746 -5741;-49605 -5455 1;-49404 -5047;-49305 -4816;-49099 -4406 18; -55165 -8974 1;-55084 -9120;-54841 -9096;-54738 -8965 1;-54609 -8801;-54464 -8618;-54589 -8451 1;-54728 -8266;-55009 -8270;-55182 -8424 1;-55264 -8497;-55283 -8587;-55261 -8695 1;-55238 -8808;-55221 -8873;-55165 -8974 18; -52278 -7997 1;-52045 -7531;-51861 -6922;-52313 -6662 1;-52661 -6462;-53123 -6597;-53298 -6959 1;-53415 -7200;-53477 -7339;-53551 -7596 1;-53615 -7819;-53436 -8088;-53228 -8189 1;-52876 -8359;-52453 -8347;-52278 -7997 18; -54502 -5345 1;-54146 -5276;-53701 -5401;-53673 -5763 1;-53643 -6149;-53513 -6459;-53769 -6749 1;-53931 -6932;-54170 -7063;-54371 -6924 1;-54710 -6689;-55084 -6471;-54991 -6069 1;-54914 -5737;-54837 -5410;-54502 -5345 18; -54319 -11382 1;-54207 -11519;-53927 -11471;-53874 -11303 1;-53766 -10962;-53581 -10635;-53839 -10387 1;-53993 -10240;-54082 -10158;-54258 -10038 1;-54407 -9935;-54707 -9988;-54703 -10169 1;-54693 -10636;-54649 -10979;-54319 -11382 18; -59015 64185 1;-59209 62728;-59831 62001;-60718 60829 1;-63344 57356;-64705 55319;-67010 51625 1;-68817 48728;-69582 46842;-70193 43483 1;-70691 40742;-70650 39128;-70415 36352 1;-70205 33866;-69810 32488;-69057 30110;-64621 15057;-63917 12681 1;-63706 11843;-63032 11477;-62427 11530 1;-61592 11603;-61133 11858;-60329 12097 1;-59081 12467;-58226 12542;-56996 12115 1;-55934 11747;-55465 11044;-55042 10003 1;-54771 9336;-54656 8937;-54554 8224 1;-54488 7759;-54485 7090;-54955 7090 1;-55355 7090;-55606 7458;-55635 7857 1;-55735 9209;-56383 11435;-58188 11120 1;-59756 10847;-60488 10855;-61986 10318 1;-62948 9973;-62935 8831;-62771 7823 16; -55112 8084; -55010 7313;-54776 5302; -54579 8005 1;-54003 7472;-53753 6975;-53715 6191; -55236 15606 1;-55143 15238;-55050 14995;-55175 14637 1;-55279 14338;-55264 14130;-55175 13826 1;-54884 12830;-54655 12292;-54364 11296 1;-54259 10934;-53828 10935;-53727 10572 1;-53415 9456;-53228 8833;-52956 7707 1;-52855 7289;-53080 7033;-53290 6658 1;-53559 6178;-53450 5738;-53191 5252 1;-53003 4899;-52700 4829;-52315 4722 1;-52006 4636;-51827 4473;-51698 4179 1;-50549 1551;-49856 86;-48379 -2372 1;-47158 -4404;-46430 -5548;-44827 -7294 1;-43556 -8679;-42878 -9494;-41765 -11009 1;-41363 -11556;-41571 -12114;-41124 -12625 1;-40690 -13120;-40447 -13398;-40014 -13893 1;-39570 -14400;-39000 -14271;-38546 -14771;-38110 -15378 16; -40845 -11492 1;-40225 -11480;-39757 -11539;-39340 -11998 1;-39051 -12315;-39043 -12630;-39056 -13059 1;-39061 -13245;-39102 -13537;-39279 -13479 1;-39749 -13325;-39959 -13079;-40278 -12701 1;-40287 -12691;-41321 -11501;-40845 -11492 18; -40623 -11838; -39624 -12282; -39464 -13170; -40874 -11498 1;-42498 -9356;-43504 -8230;-45167 -6119 1;-45895 -5196;-46326 -4689;-46944 -3689 1;-48487 -1195;-49301 243;-50572 2887;-51694 5354; -53503 11661;-52230 6845; -57477 20981;-57674 21758;-57294 21589;-56247 21029;-54742 15588;-54394 14400;-54596 14396 1;-54675 14327;-54728 14139;-54700 14021 1;-54505 13209;-54345 12767;-54133 11960 1;-54075 11739;-53905 11464;-53701 11566 1;-53629 11602;-53563 11640;-53503 11681;-53458 11492;-52230 6845;-52583 6709 1;-52869 6509;-52999 6136;-52877 5790 1;-52731 5374;-52275 5156;-51860 5303 1;-51804 5323;-51751 5348;-51702 5379;-51639 5233;-50572 2887 1;-49301 243;-48487 -1195;-46944 -3689 1;-46326 -4689;-45895 -5196;-45167 -6119 1;-43504 -8230;-42498 -9356;-40874 -11498;-40954 -11700 1;-40850 -12043;-40285 -12694;-40278 -12701 1;-39959 -13079;-39749 -13325;-39279 -13479 1;-39271 -13482;-39263 -13484;-39255 -13485;-39520 -14231 1;-39695 -14159;-39864 -14064;-40014 -13893 1;-40447 -13398;-40690 -13120;-41124 -12625 1;-41571 -12114;-41363 -11556;-41765 -11009 1;-42878 -9494;-43556 -8679;-44827 -7294 1;-46430 -5548;-47158 -4404;-48379 -2372 1;-49856 86;-50549 1551;-51698 4179 1;-51827 4473;-52006 4636;-52315 4722 1;-52700 4829;-53003 4899;-53191 5252 1;-53450 5738;-53559 6178;-53290 6658 1;-53080 7033;-52855 7289;-52956 7707 1;-53228 8833;-53415 9456;-53727 10572 1;-53828 10935;-54259 10934;-54364 11296 1;-54655 12292;-54884 12830;-55175 13826 1;-55264 14130;-55279 14338;-55175 14637 1;-55050 14995;-55143 15238;-55236 15606;-56565 20459;-57477 20981 18; -51879 1376 32;-50818 -869 32;-53004 -1902 32;-54065 343 32;-51879 1376 50; -54988 -943; -52731 377; -52200 -795; -51879 1376 32;-50818 -869 32;-53004 -1902 32;-54065 343 32;-51879 1376 50; -57771 9943; -59473 13485;-59281 12211; -57030 13310;-57344 12019; -55268 12089;-55896 11269; -58028 16520; -57138 15211; -59319 15403; -61186 15351; -55530 -3822;-56507 -2181;-58060 -1763;-59196 -1030;-61282 6867 32;-62930 9321 32;-63455 11687 32;-62868 13094 32;-63557 15497 16; -62283 14638;-61934 12894 1;-62167 12362;-62598 12087;-62493 11515; -62825 11575;-62171 11619; -62781 11995;-62165 11865; -62584 12427;-62029 12124; -62399 12704;-61813 12501; -62245 12883;-61616 12994; -62199 10122;-60941 8197; -62296 10059;-61038 8134; -62094 10191;-60836 8266; -62832 12955;-62656 12842 1;-62524 12800;-62366 12800;-62177 12846 1;-60743 13194;-59861 13450;-58374 13423 1;-57251 13403;-56529 13169;-55670 12446 1;-55514 12315;-55427 12184;-55349 12042;-55836 11403;-55968 11509 1;-56239 11765;-56569 11967;-56996 12115 1;-58226 12542;-59081 12467;-60329 12097 1;-61133 11858;-61592 11603;-62427 11530 1;-62864 11492;-63140 11579;-63405 11806;-62832 12955 18; -55853 11368;-55313 12031;-55220 11803 1;-55188 11747;-55153 11688;-55112 11626 1;-54774 11119;-54387 10945;-54187 10369 1;-53834 9353;-53814 8732;-53577 7683 1;-53481 7260;-53537 6870;-53705 6527;-53779 6704 1;-53879 7161;-54091 7519;-54452 7883;-54550 8192 1;-54551 8203;-54553 8214;-54554 8224 1;-54656 8937;-54771 9336;-55042 10003 1;-55212 10422;-55390 10787;-55611 11096;-55853 11368 18; -54318 6954; -54493 6098; -63269 14630;-55138 14770;-55211 14518 1;-55273 14279;-55252 14088;-55175 13826 1;-54884 12830;-54655 12292;-54364 11296 1;-54259 10934;-53828 10935;-53727 10572 1;-53415 9456;-53228 8833;-52956 7707 1;-52855 7289;-53080 7033;-53290 6658 1;-53525 6238;-53471 5849;-53281 5432;-60460 4110;-61245 6797;-62868 9326;-63461 11839;-62815 13147;-63269 14630 18; -65376 64636;-65374 64652 1;-65836 63819;-66154 63251;-66602 62426; -57781 64048;-57781 64048 1;-57856 63505;-57981 63125;-58252 62575 1;-58930 61200;-59556 60569;-60423 59305;-60633 59058;-60669 58982;-60910 58621;-62539 56182;-62958 55554;-63931 54097;-64291 53574;-65028 52501;-65219 52207;-65685 51420; -66760 49586;-66793 49527 1;-67934 47499;-68579 46242;-68955 43922 1;-69218 42301;-69380 41013;-69424 39724; -49773 -1087;-52163 4513; -49971 -1171;-52361 4429; -43599 -9210 1;-44418 -8251;-45610 -6918;-46321 -5876 1;-47598 -4006;-48194 -3144;-49294 -1165; -44474 -8506 1;-45293 -7547;-45787 -7031;-46498 -5989 1;-47775 -4119;-48379 -3244;-49479 -1265; -51617 3640; -50420 975; -49063 -2589; -46250 -5489; -49016 -15376 32;-48633 -16202 32;-49451 -16581 32;-49834 -15754 32;-49016 -15376 50; -50163 -15302; -48646 -14759; -47766 -12262; -48621 -11424; -47556 -13850; -47748 -15751; -52546 -11616; -54064 -14286; -47212 -18076;-48109 -17578; -49873 -18752;-49379 -20072; -52241 -19270;-52456 -21078; -51893 -23714;-53655 -23121 32;-54318 -20417 32;-54064 -18106 32;-54489 -16703;-55641 -12899;-56688 -8206 32;-58345 -7683 32;-59217 -3844 32;-58310 -1681 16; -62771 7823;-60406 -867 1;-60120 -1704;-59758 -2359;-58905 -2594 1;-58304 -2759;-57954 -2802;-57352 -2961 1;-57137 -3018;-56986 -3175;-56986 -3397 1;-56986 -3606;-57303 -3527;-57510 -3502 1;-58059 -3435;-58691 -3316;-58888 -3833 1;-59143 -4502;-58561 -5902;-58324 -6578 1;-57863 -7895;-58225 -7666;-57806 -9179;-55314 -18178 1;-55009 -19279;-54431 -19932;-53223 -20156 1;-51929 -20396;-51072 -20466;-49862 -19935 1;-48184 -19199;-47179 -18431;-46390 -16777 1;-45474 -14857;-45271 -13377;-45919 -11351 1;-46143 -10652;-46278 -10080;-46948 -9780 1;-47505 -9530;-48329 -9208;-49088 -9222 1;-49365 -9227;-49366 -8812;-49101 -8812 1;-47592 -8812;-45819 -9363;-45420 -10818 1;-45070 -12093;-44869 -12794;-44896 -14116 1;-44925 -15534;-45201 -16418;-45978 -17605 1;-47038 -19223;-48334 -20177;-50008 -21146 1;-50462 -21409;-50524 -20935;-51044 -21004 1;-51598 -21078;-51383 -21614;-51908 -21804 1;-53439 -22359;-53206 -23288;-53219 -24480;-53219 -24480 1;-53227 -25227;-52985 -26815;-52632 -28281;-50429 -37634;-49492 -41321 16; -56725 -8195;-56794 -6206; -60101 63913 1;-60162 62768;-60773 62234;-61433 61297 1;-64079 57541;-65669 55497;-68021 51551 1;-69747 48654;-70429 46776;-71031 43458 1;-71597 40338;-71743 38631;-71327 35488 1;-71154 34176;-70812 33303;-70439 32033;-65529 15082;-60936 -1028;-58686 -9106;-55777 -19551;-52969 -30231;-50724 -39435;-48577 -48244;-47763 -51581 16; -58532 -6318;-56910 -6265;-56787 -8097;-58393 -7591;-58532 -6318 18; -58794 -3229 1;-58506 -3855;-58315 -4202;-58148 -4870;-57834 -6297;-58550 -6353;-59213 -3788;-58794 -3229 18; -58615 -2673 1;-58041 -2880;-57673 -3105;-57393 -3648 1;-57211 -4000;-57012 -4301;-56616 -4301 1;-56074 -4301;-55735 -4313;-55247 -4079 1;-55001 -3961;-54949 -3644;-55074 -3401 1;-55262 -3033;-55454 -3135;-55864 -3080;-56505 -2266;-58319 -1674;-58615 -2673 18; -55528 -3143 1;-55320 -3227;-55193 -3169;-55074 -3401 1;-54949 -3644;-55001 -3961;-55247 -4079 1;-55735 -4313;-56074 -4301;-56616 -4301 1;-57012 -4301;-57211 -4000;-57393 -3648 1;-57601 -3244;-57858 -3016;-58213 -2840; -57307 -6251;-57850 -6275 32;-57897 -6013;-58148 -4870 1;-58275 -4360;-58417 -4037;-58605 -3635; -50679 -7657 1;-50559 -7177;-50497 -6745;-50853 -6401 1;-51190 -6076;-51458 -5889;-51708 -5493 1;-51887 -5209;-52074 -5158;-52406 -5109 1;-52605 -5079;-52976 -5143;-52877 -5319 1;-52682 -5665;-52442 -5778;-52092 -5964 1;-51519 -6268;-51269 -6731;-51219 -7378 1;-51201 -7615;-50737 -7887;-50679 -7657 18; -49404 -5319 1;-48591 -3512;-48246 -4882;-48393 -5301 1;-48541 -5724;-48766 -6133;-49213 -6104 1;-49532 -6083;-49535 -5611;-49404 -5319 18; -54046 -4534 1;-54093 -4092;-54779 -4255;-55180 -4446 1;-55912 -4795;-56242 -5517;-56087 -6313 1;-55978 -6876;-56042 -7313;-55529 -7570 1;-55278 -7696;-54917 -8048;-54761 -7814 33;-54554 -7502;-55878 -7296;-55599 -5982 1;-55487 -5453;-55363 -4925;-54831 -4830 1;-54508 -4772;-54011 -4860;-54046 -4534 18; -53960 -4892 1;-53827 -4841;-53704 -4710;-53697 -4516 1;-53691 -4365;-53534 -4298;-53383 -4289 1;-53012 -4266;-52671 -4242;-52511 -3911 1;-52488 -3837;-52468 -3752;-52448 -3662 1;-52410 -3490;-52381 -3328;-52335 -3168 1;-52358 -2924;-52594 -2626;-52371 -2562 1;-52258 -2530;-52179 -2632;-52113 -2759 1;-52047 -2734;-51986 -2691;-51913 -2655 1;-51297 -2347;-50958 -2150;-50308 -1922 1;-49944 -1794;-49566 -1931;-49384 -2271 1;-48819 -3324;-48399 -3867;-47953 -4976 1;-47725 -5542;-47918 -6089;-48389 -6476 1;-48747 -6771;-49145 -6682;-49576 -6511 1;-50330 -6212;-50894 -6141;-51390 -5499 1;-51542 -5303;-51623 -5190;-51774 -4993 1;-51990 -4710;-52309 -4774;-52664 -4801 1;-52907 -4819;-53046 -4944;-53188 -5142 1;-53368 -5393;-53110 -5718;-52830 -5846 1;-52226 -6122;-51703 -6383;-51621 -7042 1;-51533 -7747;-51651 -8384;-52263 -8745 1;-52549 -8914;-52901 -9025;-53114 -8770 1;-53234 -8626;-53340 -8579;-53509 -8499 1;-53735 -8392;-53974 -8646;-54015 -8893 1;-54066 -9196;-54162 -9525;-53891 -9671 1;-53734 -9756;-53507 -9797;-53435 -9634 1;-53386 -9524;-53449 -9434;-53521 -9338 1;-53586 -9252;-53644 -9144;-53570 -9066 1;-53470 -8961;-53279 -8945;-53200 -9066 1;-52977 -9404;-52954 -9802;-53226 -10102 1;-53447 -10346;-53262 -10615;-53312 -10941 1;-53382 -11395;-53443 -11737;-53818 -12002 1;-54097 -12200;-54483 -11988;-54632 -11681 1;-54994 -10934;-55389 -10498;-55323 -9670 1;-55298 -9360;-55415 -9180;-55582 -8918 1;-55771 -8623;-55951 -8406;-55866 -8066 1;-55848 -7993;-55834 -7954;-55816 -7881 1;-55783 -7748;-55589 -7750;-55471 -7820 1;-55183 -7990;-54975 -8093;-54644 -8042 1;-54474 -8016;-54432 -7728;-54558 -7610 1;-54999 -7197;-55446 -6955;-55458 -6351 1;-55470 -5727;-55274 -5051;-54656 -4970 1;-54374 -4933;-54135 -4917;-53960 -4892 18; -54308 -4377; -51813 -2527; -54500 -2719; -50308 -1922 1;-50958 -2150;-51297 -2347;-51913 -2655 1;-51979 -2688;-52036 -2726;-52084 -2769;-52084 -2747 1;-52094 -2751;-52103 -2755;-52113 -2759 1;-52179 -2632;-52258 -2530;-52371 -2562 1;-52558 -2616;-52422 -2834;-52360 -3047;-52318 -3169 1;-52378 -3313;-52409 -3482;-52448 -3662 1;-52476 -3790;-52505 -3907;-52544 -4013;-52564 -4000 1;-52739 -4247;-53049 -4268;-53383 -4289 1;-53534 -4298;-53691 -4365;-53697 -4516 1;-53703 -4692;-53805 -4815;-53922 -4875;-53925 -4877 1;-54144 -4917;-54379 -4934;-54656 -4970 1;-55274 -5051;-55470 -5727;-55458 -6351 1;-55446 -6955;-54999 -7197;-54558 -7610 1;-54432 -7728;-54474 -8016;-54644 -8042 1;-54975 -8093;-55183 -7990;-55471 -7820 1;-55589 -7750;-55783 -7748;-55816 -7881 1;-55834 -7954;-55848 -7993;-55866 -8066 1;-55951 -8406;-55771 -8623;-55582 -8918 1;-55415 -9180;-55298 -9360;-55323 -9670 1;-55389 -10498;-54994 -10934;-54632 -11681 1;-54483 -11988;-54097 -12200;-53818 -12002 1;-53443 -11737;-53382 -11395;-53312 -10942 1;-53262 -10615;-53447 -10346;-53226 -10102 1;-52954 -9802;-52977 -9404;-53200 -9066 1;-53279 -8945;-53470 -8961;-53570 -9066 1;-53644 -9144;-53586 -9252;-53521 -9338 1;-53449 -9434;-53386 -9524;-53435 -9634 1;-53507 -9797;-53734 -9756;-53891 -9671 1;-54162 -9525;-54066 -9196;-54015 -8893 1;-53974 -8646;-53735 -8392;-53509 -8499 1;-53340 -8579;-53234 -8626;-53114 -8770 1;-52901 -9025;-52549 -8914;-52263 -8745 1;-51651 -8384;-51533 -7747;-51621 -7042 1;-51703 -6383;-52226 -6122;-52830 -5846 1;-53110 -5718;-53368 -5393;-53188 -5142 1;-53046 -4944;-52907 -4819;-52664 -4801 1;-52309 -4774;-51990 -4710;-51774 -4993 1;-51623 -5190;-51542 -5303;-51390 -5499 1;-50894 -6141;-50330 -6212;-49576 -6511 1;-49145 -6682;-48747 -6771;-48389 -6476 1;-47918 -6089;-47725 -5542;-47953 -4976 1;-48399 -3867;-48819 -3324;-49384 -2271 1;-49566 -1931;-49944 -1794;-50308 -1922 18;-49654 -2963 1;-49416 -3323;-49327 -3633;-49445 -4048 1;-49617 -4654;-49730 -4996;-50025 -5553 1;-50093 -5682;-50250 -5726;-50382 -5664 1;-50492 -5612;-50539 -5475;-50481 -5368 1;-50311 -5055;-50221 -4877;-50049 -4566 1;-49801 -4117;-49759 -3546;-50185 -3259 1;-50545 -3017;-50928 -3040;-51295 -3271 1;-51612 -3470;-51717 -3791;-51653 -4159 1;-51596 -4490;-51285 -4672;-50950 -4653 1;-50726 -4640;-50692 -4371;-50678 -4147 1;-50669 -4008;-50895 -4007;-51024 -4061 1;-51131 -4106;-51265 -4138;-51320 -4036 1;-51384 -3917;-51397 -3765;-51283 -3691 1;-51052 -3541;-50850 -3461;-50592 -3555 1;-50177 -3706;-50198 -4258;-50370 -4665 1;-50527 -5039;-50984 -5207;-51345 -5023 1;-51736 -4824;-51939 -4572;-52048 -4147 1;-52144 -3770;-52123 -3438;-51838 -3172 1;-51427 -2788;-51092 -2595;-50530 -2568 1;-50155 -2550;-49861 -2650;-49654 -2963 18;-48803 -4159 1;-48703 -4247;-48623 -4284;-48569 -4406 1;-48509 -4542;-48609 -4705;-48754 -4739 1;-48984 -4794;-49085 -4977;-49136 -5208 1;-49187 -5438;-49247 -5721;-49025 -5800 1;-48817 -5874;-48622 -5710;-48544 -5504 1;-48512 -5418;-48493 -5371;-48461 -5285 1;-48433 -5212;-48368 -5140;-48297 -5171 1;-48191 -5218;-48252 -5361;-48297 -5467 1;-48476 -5892;-48807 -6354;-49235 -6183 1;-49531 -6065;-49746 -5741;-49605 -5455 1;-49404 -5047;-49305 -4816;-49099 -4406 1;-49031 -4271;-48916 -4059;-48803 -4159 18;-53673 -5763 1;-53643 -6149;-53513 -6459;-53769 -6749 1;-53931 -6932;-54170 -7063;-54371 -6924 1;-54710 -6689;-55084 -6471;-54991 -6069 1;-54914 -5737;-54837 -5410;-54502 -5345 1;-54146 -5276;-53701 -5401;-53673 -5763 18;-52313 -6662 1;-51861 -6922;-52045 -7531;-52278 -7997 1;-52453 -8347;-52876 -8359;-53228 -8189 1;-53436 -8088;-53615 -7819;-53551 -7596 1;-53477 -7339;-53415 -7200;-53298 -6959 1;-53123 -6597;-52661 -6462;-52313 -6662 18;-54589 -8451 1;-54464 -8618;-54609 -8801;-54738 -8965 1;-54841 -9096;-55083 -9120;-55165 -8975 1;-55221 -8873;-55238 -8808;-55261 -8695 1;-55283 -8587;-55264 -8497;-55182 -8424 1;-55009 -8270;-54728 -8266;-54589 -8451 18;-54258 -10038 1;-54082 -10158;-53993 -10240;-53839 -10387 1;-53581 -10635;-53766 -10962;-53874 -11303 1;-53927 -11471;-54207 -11519;-54319 -11382 1;-54649 -10979;-54693 -10636;-54703 -10169 1;-54707 -9988;-54407 -9935;-54258 -10038 18; -60601 4204;-53125 5611;-52903 5166;-51718 4624;-49917 972;-48437 -1742;-46586 -4580;-43946 -8034;-44538 -8454;-46191 -8429;-48190 -9959;-46907 -14351;-47672 -16745;-49473 -18373;-52434 -18373;-53667 -16276;-53741 -14845;-55173 -14104;-56628 -8183;-58355 -7640;-59170 -3889;-58380 -1619;-59219 -953;-60601 4204 18; -54886 -14896 1;-54531 -15193;-54172 -15301;-54066 -15751 1;-53978 -16127;-54054 -16539;-54415 -16676;-54886 -14896 18; -54537 -15151 1;-54318 -15304;-54136 -15452;-54066 -15751 1;-54021 -15945;-54019 -16148;-54077 -16318; -66899 14843;-63544 15707;-63494 15065;-63137 14198;-62815 13147;-63461 11839;-62868 9326;-61245 6797;-60460 4110;-60353 3351;-59196 -1030;-58266 -1630;-59152 -3810;-58239 -7708;-56734 -8128;-56512 -8893;-60114 -9016;-66899 14843 18; -54765 15636;-52287 15618;-52238 15348 1;-52227 15303;-52215 15258;-52204 15212 1;-52100 14795;-51868 14463;-51561 14220;-50717 10907;-50751 10672;-50748 10660;-50764 10315 1;-50761 10002;-50713 9683;-50624 9383 1;-49635 6061;-48887 4237;-47322 1144 1;-46696 -94;-46216 -901;-45556 -1853;-45573 -2051 1;-45626 -2397;-45682 -2708;-45442 -2975 1;-45255 -3183;-45169 -3319;-44962 -3507 1;-44763 -3688;-44530 -3739;-44285 -3634;-44166 -3765;-44105 -3842 1;-43988 -3996;-43874 -4146;-43761 -4292;-45814 -5300 1;-46203 -4803;-46532 -4355;-46944 -3689 1;-48487 -1195;-49301 243;-50572 2887;-51694 5354;-51587 5466 1;-51357 5675;-51263 6009;-51372 6320 1;-51501 6684;-51866 6896;-52233 6846;-52289 7068;-53458 11492;-53503 11681;-53324 11833 1;-53191 11978;-53130 12152;-53171 12368 1;-53364 13389;-53497 14299;-53986 14353 1;-54219 14379;-54256 14379;-54444 14396;-54569 15002;-54765 15636 18; 55495 60745;54495 65448;55701 65415;55753 67280;55520 67286;55648 72240;56755 72211;56748 71959;58504 71910;58483 71175;59214 71286;59256 70971;59630 71027;59147 74731;61751 75071;62468 69576;59436 69180;58440 68075;58328 62711;57441 62509;57374 61117;55495 60745 18; 55110 60288;53916 65757;54394 65825;54473 65484;55488 60713;55553 60376;55110 60288 18; 53927 65718;55105 60328;54725 60254;54692 60421;51437 59778;51356 60189;50415 60004;50388 60140;49129 59890;49186 59237;43071 58730;43085 58525;42929 58341;41745 58239;41561 58409;41480 59441;37608 61462;35888 61287;35718 61432;35498 63352;35263 63537;35159 64451;38345 64872;38598 65133;39002 64647;39515 65073;40093 64378;39836 64164;40261 63654;43287 62003;43747 62059;43606 63219;44459 63323;44605 62124;46782 62389;47406 63698;48787 63866;48671 64821;49026 64864;48895 65949;53287 66695;53849 66257;53927 65718 18; 47899 77413;48306 74326 32;52587 74844 32;54629 65459; 47377 70799; 46592 71741; 43829 70729;45372 71308;45255 72690;44292 72597 16; 45217 73580;44237 73490; 43729 71353 32;44071 71384 32;43962 72583 32;44623 72643 32;44576 73161 32;44277 73134 32;44202 73966 32;43498 73902 32;43729 71353 50; 43717 71397;44083 71418;43885 73611; 44081 73129;44036 73622; 43871 71371;43669 73587; 43477 73923;44233 73992;44310 73151; 43992 72538;44413 72576; 44277 73122;44322 72617; 41371 74306;41198 75466;41282 75556;41665 75953;44656 75553;45093 74633;45190 73578;44270 73517;44221 74004;43468 73942;43413 74627;42994 74929;42568 74485;41371 74306 18; 46493 74704; 45926 77103; 47671 76510; 47819 75182; 47928 73576; 48754 72812; 54166 66983;53375 67659;52149 67442;52002 68392;52732 68505;52223 71788;50313 71492;50451 70602;47492 70147;45971 71512;45752 74647;44883 76336;45058 76971 16; 46475 70076 1;46202 69909;45629 69114;45000 70364;45065 70433;45803 70723;46320 70256;46475 70076 18; 46223 69878 1;45951 69668;45572 69471;45159 70088; 41780 65885;43028 64603;43377 64646;43717 62108;43316 62038;40254 63678;39817 64158;40480 64646;40428 64742;41780 65885 18; 47811 63760 32;48755 63874 32;48703 64305 32;47751 64194 32;47811 63760 50; 47740 64206;47087 65131; 47034 65756;46903 66835;47510 66921;47095 69556;45803 70723;43885 69970 16; 47391 67588 1;45416 67880;45023 66101;45559 65565;47025 65774;46885 66847;47505 66925;47391 67588 18; 47039 67619 1;45728 67653;45253 66676;45354 66011; 45868 66664 1;46029 66879;46314 66938;46503 66796 1;46692 66653;46713 66364;46552 66149 1;46390 65933;46105 65874;45916 66016 1;45727 66159;45706 66449;45868 66664 18; 44074 71425;44000 72542;45246 72690;45382 71302;43827 70728;41829 70568;41650 71160;44074 71425 18; 48680 69574 32;50046 69785 32;50304 68109 32;48938 67898 32;48680 69574 50; 48680 69574;47721 69426;48130 66814;51669 67371;51526 68301;50836 68195;50687 69168;50153 69086 16; 49490 68859; 48680 69574 32;50046 69785 32;50304 68109 32;48938 67898 32;48680 69574 50; 51139 71598 32;51496 69298 32;52588 69467 32;52231 71767 32;51139 71598 50; 51139 71598 32;51496 69298 32;52588 69467 32;52231 71767 32;51139 71598 50; 48531 67350; 50511 68440; 50633 67542; 51375 67655; 52575 69454;53613 69628;54172 66994;54000 67100;53357 67650;52149 67442;52002 68392;52732 68505;52637 69116;52575 69454 18; 52575 69454;53613 69628;54172 66994;54000 67100;53383 67659;52149 67442;52002 68392;52732 68505;52637 69116;52575 69454 18; 53134 67840; 48806 66126;53301 66889; 44649 62146;44335 64763;47083 65138;47746 64187;47816 63777;47397 63707;46813 62407;44649 62146 18; 42124 69825;43903 69965;44994 70418;45032 70303 1;45648 69141;46207 69912;46475 70076;46465 70088;47070 69563;47393 67574;47348 67594 1;45410 67854;45027 66097;45559 65565;45570 65567;43275 65245;42176 66388;41923 69616;42124 69825 18; 49772 71439; 48180 74153 1;48273 73800;48632 73667;48995 73697 1;49771 73762;50206 73804;50981 73882 1;51230 73907;51547 73860;51561 73610 1;51584 73188;51465 72841;51770 72549 1;52025 72306;52165 72118;52227 71772;52572 69428;52638 69465;53582 69623;53664 69892;52587 74844 32;48306 74326 32;48180 74153 18; 52129 72093 1;52050 72260;51934 72393;51770 72549 1;51465 72841;51584 73188;51561 73610 1;51547 73860;51230 73907;50981 73882 1;50206 73804;49771 73762;48995 73697 1;48768 73678;48543 73723;48383 73846; 45372 77545;47827 77434;48259 74338;52564 74807;53181 71883;52117 71772;50313 71492;50451 70602;47492 70147;45971 71512;45752 74647;44883 76336;45058 76971;45372 77545 18; 47728 69428;48653 69563;48700 69447;48938 67898 32;50304 68109 32;50154 69081;50304 69109;50687 69168;50836 68195;51526 68301;51669 67371;48130 66814;47916 68178;47728 69428 18; 44891 76348;44958 76190;45752 74647;45971 71512;47492 70147;50451 70602;50313 71492;51125 71618;51168 71409;51496 69298 32;52536 69459;52591 69415;52732 68505;52002 68392;52149 67442;51669 67371;51526 68301;50836 68195;50687 69168;50209 69095;50136 69203;50046 69785 32;48680 69574 32;48369 69526;47721 69426;48130 66814;47480 66917;47510 66921;47095 69556;45803 70723;44007 70018;43818 70007;43828 70698;43886 70747;43975 70784;45372 71308;45255 72690;44616 72628;44616 72723;44576 73161 32;44277 73134 32;44248 73451;44343 73500;45217 73580;45090 74677;44637 75584;44802 76378;44891 76348 18; 47119 65093;47045 65877;46910 66833;47551 66931;48143 66827;51675 67387;52138 67462;53390 67662;54192 66983;54383 65830;53927 65774;53841 66255;53298 66681;48887 65947;49029 64880;48665 64825;48708 64325;47746 64214;47119 65093 18; 61625 75073;61361 77100; 64045 77433;64402 74712; 54651 65419;52644 74841;48335 74352;47480 81488;49190 81715;49940 75783;61420 77179;61542 74963;60447 74901;59147 74731;59630 71027;59256 70971;59214 71286;58483 71175;58504 71910;56748 71959;56755 72211;55648 72240;55520 67286;55753 67280;55701 65415;55274 65427;54651 65419 18; 59632 61086;59738 64676;59987 67806;61285 67878; 60213 66940;60049 64675;60048 64168; 58349 63911 32;58866 63900 32;58841 62690 32;58324 62701 32;58349 63911 50; 58843 66131 32;59262 66122 32;59280 66959 32;58861 66968 32;58843 66131 50; 58898 67858;59729 68780;59939 68591;59284 67863;58898 67858 18; 58843 66131 32;59262 66122 32;59280 66959 32;58861 66968 32;58843 66131 50; 58898 67858;59729 68780;59939 68591;59284 67863;58898 67858 18; 58859 66970;58877 67853; 59272 66964;59291 67857; 60157 69272;59437 69171;58442 68098;58407 66136;58835 66127;58878 67845;60157 69272 18; 59262 66283;59785 66272; 58511 60561;58807 61042;60001 61109;60134 60928; 58511 60561;58807 61042;60001 61109;60142 60919;59604 60728;58511 60561 18; 59889 57824;59721 58980; 66113 59946 1;66001 60957;65539 61856;64534 62019;64411 62772; 63586 65611;63052 65520;62959 62521; 62848 65013;62772 62543; 66016 71754;68979 72144; 68582 71921 1;68636 71591;68482 71196;68150 71156 1;67804 71114;67538 71402;67496 71748; 68582 71921 1;68636 71591;68482 71196;68150 71156 1;67804 71114;67538 71402;67496 71748;68582 71921 18; 68051 71687; 61771 75043;62351 75118; 61869 74289;62449 74364; 63375 76893; 64029 73328; 65657 71342; 61599 77165 1;62388 76999;63176 76876;63363 76091 1;63732 74542;63644 73621;63869 72045;64522 72464;63980 77387;61599 77165 18; 64979 71502;64868 70848;64584 70676 1;64037 70902;63824 71441;63869 72032;64585 72526;64671 71440;64979 71502 18; 64868 70848;66156 71011;66601 71717;64956 71474;64868 70848 18; 66549 71674;66165 71028;64900 70880 1;63533 69977;61514 67794;61434 65639;61266 61918 1;61244 61442;60922 61190;60474 61027 1;59996 60854;59761 60729;59257 60659 1;57835 60462;57124 60363;55702 60165 1;54842 60045;54370 58266;54501 57408 16; 63097 65614 1;63211 67309;64658 68427;65984 69488; 64591 70676;64470 70730 1;64007 70981;63827 71484;63869 72032;63846 72214 1;63650 73688;63719 74598;63363 76091 1;63181 76854;62431 76992;61664 77151; 61467 77130;61825 77154;62002 77080 1;62641 76937;63208 76741;63363 76091 1;63732 74542;63644 73621;63869 72045;63867 71763 1;63898 71282;64121 70867;64584 70676;64499 70592 1;63876 70111;63178 69420;62606 68621;62466 69567;61529 76846;61467 77130 18; 64047 77417;74323 78743;74881 73073;74131 72550;68810 71921;68757 72096;64901 71607;64047 77417 18; 63075 65553;66258 66133;65752 69291;65493 69094 1;64362 68175;63272 67170;63110 65760;63075 65553 18; 60113 69253;62593 69574;62657 68691 1;62640 68668;62623 68644;62606 68621;62467 68421 1;61890 67565;61470 66599;61434 65639;61266 61918 1;61244 61442;60922 61190;60474 61027 1;60341 60979;60226 60934;60120 60894;60079 61004;60001 61109;58807 61042;58511 60561;58284 60524 1;57426 60405;56761 60313;55702 60165 1;55495 60136;55311 60012;55152 59827;54969 60272;55536 60358;55487 60704;55819 60809;57374 61117;57441 62509;58328 62711;58522 62697;58841 62690 32;58866 63900 32;58349 63911 32;58358 64171;58400 66149;58559 66133;58835 66127;59262 66132;59279 66934;58856 66946;58877 67853;59291 67872;59931 68582;59939 68591;59729 68780;59722 68773;60113 69253 18; 56561 58428;56650 58463 1;56674 58469;56698 58475;56723 58480 1;57914 58710;58578 58771;59650 58974;59807 57818;56701 57364;56561 58428 18; 59752 58985;59941 59031 1;59984 59039;60028 59048;60072 59057 1;61304 59309;62642 60408;62802 61179 1;62936 61826;62978 62160;62986 62820;63014 65111;63070 65548;63601 65610;64082 62723;64304 62747;64471 62407;64534 62019 1;65539 61856;66001 60957;66113 59946;62281 59515;62429 58195;59912 57813;59752 58985 18; 66514 71685;67464 71845;67506 71688 1;67570 71369;67824 71116;68150 71156 1;68421 71189;68574 71459;68591 71736;69228 71808;69450 70056;65885 69353;65355 68982 1;64275 68093;63265 67113;63110 65760;63075 65553;62970 62320 1;62947 61944;62898 61642;62802 61179 1;62642 60408;61304 59309;60072 59057 1;58735 58784;58063 58739;56723 58480 1;56275 58393;56094 58027;56029 57577;54481 57808 1;54507 58724;54960 60062;55702 60165 1;57124 60363;57835 60462;59257 60659 1;59761 60729;59996 60854;60474 61027 1;60922 61190;61244 61442;61266 61918;61434 65639 1;61514 67794;63533 69977;64900 70880;66165 71028;66368 71370;66514 71685 18; 56413 57316;56010 57289 1;56014 57191;56030 57095;56057 56969 1;56568 54544;56895 52993;57304 51055;58688 44619;59183 44231;56386 57310;56413 57316 18; 56010 57289;56413 57316;56716 57390;56546 58470;56455 58378 1;56197 58220;56078 57921;56028 57573 1;56012 57461;56006 57374;56010 57289 18; 53671 52241;54362 52313;55866 44716; 53386 52229;54386 52315;55866 44716;52498 43951;52412 45197;53386 52229 18; 49638 47125 1;49244 47093;48858 46739;48890 46345;49142 43284;51113 43389;52518 43971;52419 44898;51471 44776;51340 44584;51052 44575;50808 44758;49928 44665;49861 45323;49647 45474;49569 46217;49771 46484;49638 47125 18; 55293 52306; 55625 50683; 56026 48886; 54753 53462;55276 53584;55982 50225;55440 50110;54753 53462 18; 54230 55979;54771 56032;55279 53553;54732 53426;54230 55979 18; 55429 50118;55984 50224;56394 48128;55887 48038;55429 50118 18; 55853 48064;56403 48169;56428 48025;57065 44942 1;57137 44585;56988 44267;56726 44053;55853 48064 18; 54494 57851;56038 57633;56016 57476 1;55998 57295;56014 57172;56057 56969 1;56765 53607;57957 47962;58666 44600;59172 44299;56781 44101 1;57008 44313;57132 44611;57065 44942;54487 57430;54494 57851 18; 51226 43428;51309 43470;52518 43971;52561 43965;55851 44713;55893 44518 1;55971 44156;55730 43760;55361 43733;51226 43428 18; 55852 44710;55865 44648;55893 44518 1;55969 44165;55742 43780;55388 43736; 54203 56004;53512 55942;53616 54749;53389 54714;53651 52236;54383 52324;55849 44786;55851 44785;55893 44518 1;55954 44236;55821 43933;55584 43801;55494 43734;56107 43780 1;56307 43811;56493 43886;56648 43993;56721 44054;54203 56004 18; 51235 43428;52508 43925;55867 44745; 49987 41616;51409 41736; 42321 39029;42204 40184;46065 40577;46807 32488;43804 31232;43595 31775 16; 49531 34932 1;49350 34922;49196 35061;49186 35242 1;49177 35422;49315 35577;49496 35586 1;49677 35596;49831 35457;49840 35276 1;49850 35096;49711 34942;49531 34932 18; 49900 39037;50421 39076;50578 36992;51676 37073;51741 36223;55006 36498;54516 41413;53131 41260;53049 41999;56628 42396 1;57177 42457;57756 42232;57851 41688;56610 41471;57449 37334;58655 37579;59220 34796;58144 34578;58583 32676;59683 32964;59784 32577;58129 32039;58213 31628;57895 31524 16; 50420 39076;50353 39970; 55633 40294; 57143 33826;56721 34789;43020 28779;42808 28534;42884 27579; 41752 27858;41682 28346;41080 28573;33743 25084; 56505 42378;56614 41881;55005 41680;55153 40258;54637 40203;54516 41413;53131 41260;53049 41999;56505 42378 18; 54708 39481;55223 39534;55546 36228;55040 36114;54708 39481 18; 55450 37205 1;55467 36816;56013 36857;56400 36900 1;56756 36939;56961 36911;57316 36961 1;57453 36980;57511 36646;57377 36612 1;56637 36425;56215 36352;55467 36202;55301 36569;55450 37205 18; 57962 36856; 58555 34998; 59157 32599; 49407 34538 1;49097 34584;48845 34833;48807 35159 1;48761 35549;49040 35902;49430 35948 1;49820 35994;50174 35715;50220 35324 1;50258 34992;50061 34686;49761 34575 16; 46333 39159;47179 39229;47031 40947;46133 40869;46333 39159 18; 47424 39229;47912 38749;49639 38871; 47397 39272;47912 38749;49657 38889;49587 39420;47397 39272 18; 50639 35957;49996 35909;49719 39580 32;47297 39397 32;47173 41036 32;45654 40921;45626 41291 16; 48706 38801;48916 36027 1;48924 35921;49000 35884;49064 35800; 49186 38008; 49256 36655; 48689 38793;49649 38862;49849 35914;49709 35926 1;49621 35951;49527 35959;49430 35948 1;49287 35931;49160 35873;49057 35788;48984 35889 1;48949 35927;48921 35965;48916 36027;48802 37528;48689 38793 18; 47704 38671; 46875 38609; 46399 39167;47166 39228; 46638 35973 1;47119 36004;47625 35715;47625 35233 1;47625 34755;47313 34484;47440 34024 1;47586 33495;48277 32956;47810 32667 1;47515 32484;47308 32455;46971 32371;46638 35973 18; 42739 29829;49044 32593 1;49675 32870;49671 33805;49574 34616; 49596 34535 1;49206 34489;48852 34768;48807 35159 1;48761 35549;49040 35902;49430 35948 1;49820 35994;50174 35715;50220 35324 1;50265 34934;49986 34581;49596 34535 18;49560 34939 1;49381 34915;49217 35041;49193 35220 1;49169 35398;49295 35563;49474 35587 1;49652 35610;49817 35485;49841 35306 1;49864 35127;49739 34963;49560 34939 18; 49993 34694 1;50434 34425;50965 34938;50511 35817;50024 35799;50075 35679 1;50152 35579;50204 35458;50220 35324 1;50245 35102;50165 34891;50020 34742;49993 34694 18; 50435 39076;49880 39027;50115 36029;50482 36029;50466 35769;50638 35344 1;50602 35245;50797 35283;50878 35331 1;50895 35341;50915 35348;50938 35352 1;51203 35402;51355 35430;51614 35478;51631 35776 1;51646 35947;51680 36157;51514 36201 1;51070 36319;50733 36496;50567 36905;50435 39076 18; 50552 36991;51668 37071;51736 36226;55018 36510;55009 36428;55039 36126;51610 35486;51617 35534;51631 35776 1;51646 35947;51680 36157;51514 36201 1;51072 36319;50736 36495;50569 36900;50552 36991 18; 50211 34623;50216 34623 1;50481 34611;50715 34857;50681 35270 32;50703 35282 1;50759 35283;50835 35305;50878 35331 1;50895 35341;50915 35348;50938 35352 1;51203 35402;51355 35430;51614 35478;51683 35500;53884 35910 32;55039 36126;55190 36148;55546 36228;55590 36227 1;56263 36361;56679 36436;57377 36612 1;57511 36646;57453 36980;57316 36961 1;56961 36911;56756 36939;56400 36900 1;56120 36869;55756 36839;55570 36972 33;55500 37023;55455 37098;55450 37205;55427 37448;55223 39534 32;54785 39489; 54705 40198;55153 40258 32;55005 41680 32;56614 41881 32;56524 42292; 45754 41368;45619 42935;48172 43268;49141 43323;49153 43268;50841 43386;56007 43772;56092 43779;56107 43780 1;56435 43831;56726 43999;56903 44237;59092 44324;59627 41918;57844 41687;57851 41688 1;57756 42232;57177 42457;56628 42396;53049 41999;53072 41789;53131 41260;54516 41413;55006 36498;51741 36223;51676 37073;50578 36992;50421 39076;50350 39979;51153 40058;51161 40572;50184 40546;50190 40669;50126 41507;51327 41654;51353 41856;45754 41368 18; 42814 30994; 50494 35849 1;50768 35536;51181 35626;51593 35684 1;53095 35897;53924 36108;55432 36277 1;56056 36347;56652 36289;56845 35692 1;57055 35042;57176 34575;57248 33896; 53533 36033; 46329 39163;47440 39274;47908 38731;48686 38793;48932 36017;49068 35733;50314 35573;55002 36350;54483 41421;55137 42247;56731 42404 1;57248 42428;57762 42198;57851 41688;56610 41471;57449 37334;58655 37579;59220 34796;58144 34578;58218 34257;57161 33933;56716 34771;55861 34421 1;55648 34620;55354 35151;55073 35028;49307 32494;42938 29915;42745 30201;42443 30451;42252 30488;42160 31397;42837 31453;43528 31774;43657 31613;43804 31232;46581 32393;47052 32391 1;47343 32461;47540 32499;47810 32667 1;48277 32956;47586 33495;47440 34024 1;47313 34484;47625 34755;47625 35233 1;47625 35715;47119 36004;46638 35973;46329 39163 18; 42302 39040;42203 40101;46015 40484;46755 32428;43795 31268;43597 31811;45633 32847;45596 33279;45880 33341;45349 39336;42302 39040 18; 42857 27576;57094 33857;56723 34736;42813 28597;42857 27576 18; 35450 18328;34853 18076; 44406 22411;37128 19067; 34798 18212;34415 19119;58663 29668;58811 28829;34798 18212 18; 56911 34790 1;57090 34868;57189 34913;57368 34991 1;57684 35130;58075 34918;58150 34581;58682 32208;57740 31938;56911 34790 18; 58562 32654;59685 32950;59771 32592;58649 32235;58562 32654 18; 57742 31982;58137 32068;58205 31642;57859 31507;57742 31982 18; 57162 34917;57254 34941 1;57289 34956;57326 34973;57368 34991 1;57600 35093;57873 35006;58031 34819; 58157 30625; 45673 25406;45920 24829; 50608 27519;50855 26942; 55528 29663;55789 29054; 57969 31087;58314 31198;58506 30404;56045 29331 16; 55404 29051;51135 27189 16; 50483 26904;46212 25041 16; 42974 24157;43151 23716;45563 24758 16; 41889 27971;42662 28314;42971 24162;42286 23852;41889 27971 18; 38595 23158;39125 21899; 42215 24508;42338 23358;39519 22127 16; 34980 23519;36167 20622; 38689 21764;36405 20767 16; 35897 20545;32575 19094;31571 21392;32086 21673 16; 39770 22600; 38209 21928; 33570 20563; 56603 41478;57849 41700;59607 41934;62141 30319;58810 28826;58135 32065;59776 32589;59683 32978;58567 32669;58477 33137;58144 34578;59220 34796;58655 37579;57449 37334;56771 40676;56603 41478 18; 57862 31517;58247 31628;58321 31224;57973 31110;57862 31517 18; 55874 29855 32;58049 30808 32;57944 31047 32;58303 31204 32;58515 30405 32;56094 29352 32;55874 29855 50; 50944 27685 32;55171 29541 32;55380 29064 32;51153 27208 32;50944 27685 50; 46028 25539 32;50236 27390 32;50453 26897 32;46245 25046 32;46028 25539 50; 42956 24163 32;45318 25237 32;45531 24768 32;43169 23694 32;42956 24163 50; 42287 23901;39316 22633 16; 38474 22274;36233 21318 16; 39106 23214 32;42203 24499 32;42280 23890 32;39333 22667 32;39106 23214 50; 35985 21869 32;38209 22819 32;38443 22254 32;36213 21323 32;35985 21869 50; 39346 22638;42288 23909;42344 23385;39543 22157;39346 22638 18; 38426 22268;38667 21744;36434 20794;36230 21324;38426 22268 18; 41057 23133; 33770 25093;41074 28559;41592 28411;41715 27881;34597 24710;33857 24883;33770 25093 18; 34910 22844;32215 21387 1;32449 20954;32958 20923;33395 21149;35207 22086;34910 22844 18; 32112 20312;32610 19292;33813 19815; 35897 20545;32575 19094;31571 21392;32086 21673;34804 23212;35897 20545 18; 32258 21335 1;32537 20951;32996 20942;33395 21149;34813 21882; 58499 30368;43135 23679;42957 24131;42288 23839;42332 23320;32556 19051;32829 18431;58650 29654;58499 30368 18; 22202 38142 32;21555 37788 32;19634 41298 32;31023 47530 32;31414 46815 32;20672 40937 32;22202 38142 50; 20669 42896; 20735 42497 1;20514 42461;20305 42611;20268 42832 1;20231 43053;20381 43263;20603 43299 1;20824 43336;21033 43186;21070 42965 1;21107 42743;20957 42534;20735 42497 18; 22298 43735; 22364 43336 1;22143 43300;21934 43450;21897 43671 1;21860 43892;22010 44102;22232 44138 1;22453 44175;22662 44025;22699 43804 1;22736 43582;22586 43373;22364 43336 18; 23803 44537; 23869 44138 1;23648 44102;23439 44252;23402 44473 1;23365 44694;23515 44904;23737 44940 1;23958 44977;24167 44827;24204 44606 1;24241 44384;24091 44175;23869 44138 18; 25493 45450; 25559 45051 1;25338 45015;25129 45165;25092 45386 1;25055 45607;25205 45817;25427 45853 1;25648 45890;25857 45740;25894 45519 1;25931 45297;25781 45088;25559 45051 18; 26899 46228; 26965 45829 1;26744 45793;26535 45943;26498 46164 1;26461 46385;26611 46595;26833 46631 1;27054 46668;27263 46518;27300 46297 1;27337 46075;27187 45866;26965 45829 18; 28408 46644 1;28187 46608;27978 46758;27941 46979 1;27904 47200;28054 47410;28276 47446 1;28497 47483;28706 47333;28743 47112 1;28780 46890;28630 46681;28408 46644 18; 28342 47043; 29962 47470 1;29741 47434;29532 47584;29495 47805 1;29458 48026;29608 48236;29830 48272 1;30051 48309;30260 48159;30297 47938 1;30334 47716;30184 47507;29962 47470 18; 29896 47869; 20099 42608;20547 41787; 22841 44104;23289 43283; 36761 49256; 35293 48441; 33491 47516; 35514 46374; 36578 44229; 35706 42833; 37965 45740; 38621 45241 1;38825 45495;38683 45947;38305 46250 1;37927 46553;37455 46593;37252 46339 1;37048 46085;37189 45633;37568 45330 1;37946 45027;38417 44987;38621 45241 18; 36622 43526 1;36261 43513;35958 43794;35944 44155 1;35930 44517;36212 44820;36573 44834 1;36934 44848;37238 44566;37252 44205 1;37265 43844;36984 43540;36622 43526 18; 35635 42305 1;35291 42336;35037 42639;35068 42983 1;35099 43328;35402 43582;35747 43551 1;36091 43520;36345 43216;36314 42872 1;36283 42528;35980 42274;35635 42305 18; 34044 44341 1;34125 44174;34464 44171;34801 44335 1;35138 44498;35346 44766;35265 44933 1;35185 45099;34846 45102;34508 44938 1;34171 44775;33963 44507;34044 44341 18; 33316 45895 1;33304 45719;33496 45563;33745 45546 1;33993 45529;34205 45658;34217 45834 1;34229 46009;34037 46166;33788 46183 1;33540 46200;33328 46071;33316 45895 18; 35919 45796 1;36167 45963;36189 46365;35968 46696 1;35746 47026;35365 47159;35117 46993 1;34869 46827;34848 46424;35069 46094 1;35291 45763;35671 45630;35919 45796 18; 34633 43318; 36576 40070; 36798 39009; 37650 38590; 38526 39145; 39389 39120; 40512 39873; 40611 41649; 42190 37209;41910 40345; 41795 40318 1;41297 40933;41248 41689;41696 42341;41848 42198 1;42418 41514;43297 41117;44270 41197;45507 41290;45533 40638;41795 40318 18; 40979 47178;41234 44074;41787 44119; 38131 47029;38587 47264 1;38920 46788;39291 46464;39870 46511 1;40263 46543;40539 46522;40845 46770;41141 43896 1;40478 43864;40284 44726;40327 45389 1;40343 45634;40268 45879;40031 45944 1;39347 46130;38751 45951;38131 47029 18; 38137 47017 1;37962 46959;37545 47103;37681 47369 1;37910 47816;37627 48188;37860 48633 1;37953 48810;38302 48656;38341 48460;38587 47264;38137 47017 18; 40565 46607 1;40357 46530;40141 46533;39870 46511 1;39291 46464;38920 46788;38587 47264 32;38560 47398;38341 48460 1;38302 48656;37953 48810;37860 48633 1;37627 48188;37910 47816;37681 47369 1;37552 47116;37944 46974;38131 47010 32;38162 46976 1;38771 45957;39359 46127;40031 45944 1;40268 45879;40343 45634;40327 45389 1;40295 44901;40392 44304;40710 44037; 32081 46240;41151 29887; 40988 30057;36658 28095 1;35892 27748;34963 27795;34549 28527 1;32620 31940;31593 33885;29663 37298 1;29043 38393;29610 39737;30712 40345;34177 42257; 31236 34451 1;31550 34620;31640 35062;31438 35438 1;31235 35813;30817 35981;30503 35812 1;30189 35643;30098 35201;30301 34825 1;30503 34449;30922 34282;31236 34451 18; 29043 33694;26329 32218;27576 29937 32;27729 30020 32;28284 29004 32;28118 28913 32;30336 24858 32;30490 24942 32;31041 23935 32;30892 23854 32;32065 21710 32;34754 23181 32;34681 23389;35237 23693;35998 21893;36908 22273;37318 22105;37496 22540;38169 22823;38085 23023;39062 23434;39145 23237;39904 23552;40309 23387;40477 23798;42191 24517;41859 27904;34630 24699;33858 24892;29043 33694 18; 28657 34473;25905 32921;26302 32193;29078 33702;28657 34473 18; 29136 33556;29937 33995; 28560 34607;29361 35046; 29911 33976;30352 34215;34151 27207;32948 26555; 33830 25324;33184 26503; 29152 33541;30349 34157;34038 27212;32915 26644;29152 33541 18; 29361 35046;29815 35295;28104 38414;26874 37739; 28547 34637 32;29770 35306 32;28080 38393 32;26857 37724 32;28547 34637 50; 36796 36754;36378 37507;32997 35613;33903 34029; 36774 36742;36356 37495;33014 35582;33903 34029;36774 36742 18; 31546 35027;32773 35694; 34329 38269;34612 37751; 33483 37806;33766 37289; 34648 38349;33977 39576;33386 39252;34059 38025; 34330 38224;33923 38969; 33908 39666;33338 39354;33338 39353;32101 38678 32;32806 37390 32;34614 38376 32;33908 39666 18; 33386 39252;32285 38648;32944 37447; 32835 37601;33282 37851 16; 33559 38006;33934 38216 16; 35350 38878; 34635 40099; 33989 41158; 33262 42523; 31621 40837;31067 41842; 32303 40981 32;33205 39336 32;31762 38545 32;31086 39778 32;31669 40098 32;31443 40510 48; 31087 39791;32543 40574;33209 39334;31772 38545;31087 39791 18; 32252 41013;32507 40547;31698 40102;31447 40564;32252 41013 18; 32535 40554 1;32763 40684;32892 40756;33120 40886 1;33355 41020;33692 40854;33856 40639;35217 38164;34774 37901;33849 39647;33213 39332;32535 40554 18; 32806 40708 1;32902 40762;32997 40816;33120 40886 1;33355 41020;33692 40854;33856 40639;35033 38499; 31063 35798;36072 38587;36503 37797;31544 35034;31063 35798 18; 31057 35798;33597 37226 16; 33902 37398;34437 37699 16; 34750 37875;36006 38581 16; 32383 38116 1;32159 37933;32017 37654;32167 37407 1;32305 37180;32060 36961;32186 36728;32315 36519;33568 37210;33327 37672;32790 37395;32383 38116 18; 33919 37413;33685 37876;34178 38153;34419 37678;33919 37413 18; 32173 37870 1;32093 37724;32075 37559;32167 37407 1;32294 37198;32097 36997;32162 36785; 31051 39754 1;30559 39505;30851 38806;31409 39180;31051 39754 18; 30837 39545;31089 39073; 25263 38881;33463 43373; 26919 37974;26312 39085; 26247 38878;28171 39931; 28218 39857;28472 39393;26776 38465;26538 38899;28218 39857 18; 31386 34570 1;31494 34691;31549 34857;31545 35035;31544 35058 16; 31092 35792 1;30957 35865;30807 35894;30667 35869 16; 30359 35700 1;30257 35589;30199 35437;30193 35272 16; 30532 34547 1;30699 34415;30902 34358;31089 34397 16; 42405 36930;41358 36837;41951 30192; 41827 33424;42731 33505; 42835 31438 32;42679 33176 32;42000 33115 32;42156 31377 32;42835 31438 50; 42660 33820 32;42409 36619 32;41698 36556 32;41949 33757 32;42660 33820 50; 42823 31447;42173 31389; 55738 34400;55715 34584 1;55523 34816;55294 35125;55073 35028;51744 33662;42959 29952;43003 29833 1;43023 29778;43038 29721;43048 29662 1;43097 29379;43023 29102;42864 28888;55738 34400 18; 41114 29093;40837 29760; 42221 28490 1;41670 28395;41145 28765;41049 29317 1;40954 29869;41324 30394;41876 30489 1;42428 30584;42953 30214;43048 29662 1;43143 29111;42773 28586;42221 28490 18; 41266 30233;41343 30122;41090 29286;40886 29733;40834 29842;41034 29968;41266 30233 18; 41312 30187 1;41404 30285;41516 30364;41643 30419 16; 42208 30491 1;42471 30449;42706 30305;42863 30094 16; 43029 29750 1;43036 29721;43043 29692;43048 29662 1;43118 29256;42961 28879;42639 28656;42664 28318 16; 41899 27970;41849 28494 1;41482 28569;41174 28844;41071 29220 16; 41863 28515 32;42624 28721 32;42672 28322 32;41902 27981 32;41863 28515 50; 36294 29464 1;36062 29168;35933 29001;35701 28705 1;35576 28545;35343 28453;35195 28591 1;35033 28742;34942 28827;34780 28978 1;34624 29124;34608 29383;34767 29525 1;34871 29618;34995 29584;35125 29533 1;35253 29483;35304 29670;35360 29795 1;35497 30099;35817 30330;36119 30188 1;36382 30064;36473 29693;36294 29464 18; 36669 28373 1;36808 28776;36757 29211;36390 29429 1;36524 29685;36528 29993;36294 30162 1;36064 30328;35804 30384;35570 30223; 35526 30319 1;35338 30588;35079 30727;34758 30659 1;34523 30609;34459 30144;34680 30048 1;34939 29935;35085 29874;35351 29778;35645 30122;35526 30319 18; 35491 30388;35421 30448 1;35245 30630;35023 30715;34758 30659 1;34523 30609;34459 30144;34680 30048 1;34823 29985;35063 29887;35177 29841; 35870 29666; 39614 30496; 37994 29607; 37637 30377; 38741 31250; 37540 31271;37026 30845;35472 31995;35821 32475; 37540 31271;37026 30845;35472 31995;35821 32475;37540 31271 18; 35969 31454 1;35531 31350;35144 31092;34818 31402 1;34044 32137;34185 32919;33762 33923;35734 32588;35289 31943;35969 31454 18; 32886 35779;30382 34459;34071 27254;33096 26600;33861 25206;41140 28599;41670 28426;39326 32596;37118 30795;35969 31454 1;35531 31350;35144 31092;34818 31402 1;34055 32126;34181 32896;33780 33880;33849 33990;32886 35779 18;36119 30188 1;36382 30064;36473 29693;36294 29464 1;36062 29168;35933 29001;35701 28705 1;35576 28545;35343 28453;35195 28591 1;35033 28742;34942 28827;34780 28978 1;34624 29124;34608 29383;34767 29525 1;34871 29618;34995 29584;35125 29533 1;35253 29483;35304 29670;35360 29795 1;35497 30099;35817 30330;36119 30188 18; 40299 34023 1;40615 34094;40803 33737;40867 33419 1;40969 32908;41102 32631;41126 32111 1;41133 31966;41082 31820;40941 31790 1;40774 31754;40701 31943;40632 32099 1;40398 32632;40211 32911;40040 33468 1;39970 33697;40066 33971;40299 34023 18; 39423 35393; 41549 42088 1;41316 41600;41340 41073;41603 40601; 41434 42845 1;41153 42845;40897 42942;40814 43211 1;40659 43713;40140 43738;39916 44214 1;39754 44558;39912 45079;40291 45113;40333 44957 1;40368 44589;40482 44228;40710 44037;41172 43909;41434 42845 18; 38755 49795;32389 46316;41258 29982;41751 30278;41197 36952;42208 37063;41851 40323;41795 40318 1;41327 40896;41256 41597;41620 42221;41708 42380 1;41585 42555;41481 42744;41399 42946;41434 42845 1;41153 42845;40897 42942;40814 43211 1;40659 43713;40140 43738;39916 44214 1;39754 44558;39912 45079;40291 45113;40333 44957 1;40368 44589;40482 44228;40710 44037;41172 43909;41189 43841;40840 47204;39581 47081;39038 47451;38755 49795 18;35968 46696 1;36189 46365;36167 45963;35919 45796 1;35671 45630;35291 45763;35069 46094 1;34848 46424;34869 46827;35117 46993 1;35365 47159;35746 47026;35968 46696 18;38305 46250 1;38683 45947;38825 45495;38621 45241 1;38417 44987;37946 45027;37568 45330 1;37189 45633;37048 46085;37252 46339 1;37455 46593;37927 46553;38305 46250 18;33788 46183 1;34037 46166;34229 46009;34217 45834 1;34205 45658;33993 45529;33745 45546 1;33496 45563;33304 45719;33316 45895 1;33328 46071;33540 46200;33788 46183 18;35265 44933 1;35346 44766;35138 44498;34801 44335 1;34464 44171;34125 44174;34044 44341 1;33963 44507;34171 44775;34508 44938 1;34846 45102;35185 45099;35265 44933 18;36573 44834 1;36934 44848;37238 44566;37252 44205 1;37265 43844;36984 43540;36622 43526 1;36261 43513;35958 43794;35944 44155 1;35930 44517;36212 44820;36573 44834 18;35747 43551 1;36091 43520;36345 43216;36314 42872 1;36283 42528;35980 42274;35635 42305 1;35291 42336;35037 42639;35068 42983 1;35099 43328;35402 43582;35747 43551 18; 30904 41443;28436 40091; 31495 41773;32520 42334; 30729 42846 32;31404 43215 32;31673 42723 32;30998 42354 32;30729 42846 50; 31672 42735 1;31946 42902;32225 43062;31987 43494; 32363 43710;32752 43365;33202 43593;32918 44068; 32789 43679; 31783 43155; 32799 43320;32354 43725;31970 43516;32070 43311;32057 43054;31926 42897;31870 42840;31900 42770;32799 43320 18; 33512 43054;28068 40071;28218 39857;28472 39393;26825 38492;26681 38405;27030 37995;28173 38649;30057 35229;35980 38562;33512 43054 18;32543 40574;33209 39334;31772 38545;31087 39791;32543 40574 18; 28679 34454;30210 35305;30572 34533;29080 33730;28679 34454 18; 42051 41988 1;41475 41659;41017 41539;40376 41709 1;38801 42126;37757 42292;36206 41796 1;35378 41531;35083 40808;34968 39947 1;34909 39503;34576 39206;34130 39162; 37358 42065; 16335 39100 32;17105 39483 32;17293 39105 32;16523 38722 32;16335 39100 50; 16568 39460; 15626 41424; 16335 39100 32;17105 39483 32;17293 39105 32;16523 38722 32;16335 39100 50; 22196 49448;37427 52641;37829 49221;32416 46363;31827 46036;31018 47537;23378 43378;22950 44163;22736 44043;23159 43265;20636 41879;20208 42666;19990 42550;20419 41764;19642 41320;20014 40612;19089 40101;17770 42316;18376 42613;21329 49229;21403 49115 1;21458 49034;21541 48971;21642 48941 1;21875 48873;22118 49006;22187 49238 1;22198 49277;22204 49316;22205 49354;22196 49448 18; 56390 57245;66667 58710;65882 54575;69161 33383;62104 23069;59686 22181;57218 27905;62301 30225;56390 57245 18; 20017 40585 32;19093 40079 32;19346 39618 32;20270 40124 32;20017 40585 50; 21616 37300 32;20692 36794 32;20945 36333 32;21869 36839 32;21616 37300 50; 22760 35213 32;21836 34707 32;22089 34246 32;23013 34752 32;22760 35213 50; 23850 33225 32;22926 32719 32;23179 32258 32;24103 32764 32;23850 33225 50; 25049 31142 32;24125 30636 32;24378 30175 32;25302 30681 32;25049 31142 50; 26415 28857 32;25491 28351 32;25744 27890 32;26668 28396 32;26415 28857 50; 28222 25871 32;27298 25365 32;27551 24904 32;28475 25410 32;28222 25871 50; 29702 23391 32;28778 22885 32;29031 22424 32;29955 22930 32;29702 23391 50; 30981 21107 32;30057 20601 32;30310 20140 32;31234 20646 32;30981 21107 50; 26415 28857 32;25491 28351 32;25744 27890 32;26668 28396 32;26415 28857 50; 22760 35213 32;21836 34707 32;22089 34246 32;23013 34752 32;22760 35213 50; 28222 25871 32;27298 25365 32;27551 24904 32;28475 25410 32;28222 25871 50; 21616 37300 32;20692 36794 32;20945 36333 32;21869 36839 32;21616 37300 50; 25049 31142 32;24125 30636 32;24378 30175 32;25302 30681 32;25049 31142 50; 29702 23391 32;28778 22885 32;29031 22424 32;29955 22930 32;29702 23391 50; 30981 21107 32;30057 20601 32;30310 20140 32;31234 20646 32;30981 21107 50; 23850 33225 32;22926 32719 32;23179 32258 32;24103 32764 32;23850 33225 50; 20017 40585 32;19093 40079 32;19346 39618 32;20270 40124 32;20017 40585 50; 30635 20625; 29381 22885; 27872 25395; 26070 28369; 24700 30639; 23493 32748; 22407 34721; 21260 36806; 19688 40099; 19991 39974;21474 37226; 21736 36755;22626 35141; 22862 34662;23716 33144; 23179 32258;24134 30639; 25162 30590;26236 28758; 26538 28306;28068 25777; 28327 25333;29536 23297; 29031 22424;30068 20578; 31083 20566;31977 18669;30872 18148;31452 16882 1;31526 16726;31687 16645;31844 16719;34511 11232;37376 5225;42560 -4791;42893 -6008;47681 -15309 1;48093 -16109;48720 -16420;49607 -16572;49564 -16053 1;49055 -15980;48620 -15824;48321 -15476; 28619 34432;30202 35310 16; 30563 34522;29027 33688 16; 23477 38300;24124 38649;23942 38987;24456 39264;24642 38919;25142 39188;30974 42377 32;30711 42857 32;32888 44049 32;31390 46788 32;20717 40950 32;22230 38183 32;23239 38735 32;23477 38300 18; 25449 38567;25108 39190;24655 38947;24455 39317;23904 39020;24099 38658;23438 38299;23737 37752 32;25449 38567 18; 23420 37524 32;24610 35352 32;24780 35445 32;25310 34475 32;25144 34384 32;25940 32930;28631 34448;26149 38984 32;23420 37524 50; 24799 35409 32;25296 34502 32;24826 34245 32;24329 35152 32;24799 35409 50; 27751 30018 32;28281 29050 32;27811 28793 32;27281 29761 32;27751 30018 50; 30525 24949 32;31055 23981 32;30579 23721 32;30055 24692 32;30525 24949 50; 23376 37537 32;24597 35307 32;23983 34971 32;22762 37201 32;23376 37537 50; 25113 34391 32;25929 32900 32;25335 32575 32;24519 34066 32;25113 34391 50; 26316 32196 32;27557 29925 32;27043 29644 32;25802 31915 32;26316 32196 50; 28093 28912 32;30318 24843 32;29859 24592 32;27634 28661 32;28093 28912 50; 30845 23857 32;31064 23457 32;30585 23195 32;30366 23595 32;30845 23857 50; 25113 34391 32;25929 32900 32;25335 32575 32;24519 34066 32;25113 34391 50; 23376 37537 32;24597 35307 32;23983 34971 32;22762 37201 32;23376 37537 50; 28093 28912 32;30318 24843 32;29859 24592 32;27634 28661 32;28093 28912 50; 26316 32196 32;27557 29925 32;27043 29644 32;25802 31915 32;26316 32196 50; 30845 23857 32;31064 23457 32;30585 23195 32;30366 23595 32;30845 23857 50; 29859 24592;30366 23595; 27043 29644;27634 28661; 19976 39973;20260 40127;21549 37801;23215 38702;23727 37746;22771 37191;23968 34964;24331 35167;24835 34262;24521 34088;25341 32552;25882 32866;26284 32203;26129 32094;25802 31915 32;27043 29644 32;27268 29767;27341 29652;27811 28793;27731 28714;27634 28661 32;29859 24592 32;30044 24693;30147 24522;30579 23721;30485 23660;30366 23595 32;30585 23195;29955 22930;29702 23391 32;29507 23284;28327 25333;28475 25410;28222 25871 32;28054 25779;26538 28306;26668 28396;26415 28857 32;26246 28764;25162 30590;25302 30681;25049 31142 32;24131 30639;23179 32258;23417 32388;24103 32764 32;23850 33225 32;23689 33137;22866 34656;22990 34740;23013 34752 32;22760 35213 32;22607 35129;21736 36755;21869 36839;21616 37300 32;21466 37218;19976 39973 18; 29032 22444;29954 22950;30580 23213;31076 23472;32079 21671;31591 21405;32540 19111;32873 18383;33097 17926;32158 17501;32375 17003;31844 16719 1;31687 16645;31526 16726;31452 16882;30872 18148;31977 18669;31083 20566;31234 20646;30981 21107 32;30057 20601 32;29032 22444 18; 34859 18179;32868 17338; 58816 28855;58645 29659;32857 18440;33104 17898;32151 17481;32358 17008;32906 17248 16; 34433 19119;34874 18090;32353 17004;32148 17492;33099 17898;32863 18456;34433 19119 18; 34507 18901;34678 18496; 34659 14272;34285 15157 32;33869 14981 32;34241 14101 48; 34412 14170;34213 14644; 35177 13534;35415 12988;36715 13553 32;38203 14200 32;42543 16087 32;42326 16586 16; 46742 19239;46066 18953; 45032 18375 32;46113 18832 32;46354 18263 32;45273 17806 32;45032 18375 50; 42738 16011;45467 17186 1;45826 17341;46471 17883;46340 18252; 42738 16011;45467 17186 1;45826 17341;46471 17883;46340 18252;42456 16638;42738 16011 18; 47594 18360;47868 17710; 46387 16209;42563 14562; 43198 13618;42829 14490; 42546 11957;41622 14083;35254 11315;36245 9406;36742 9622; 31828 16724;32327 17002;35492 18310;35794 17600;33919 16835;34597 15275;34295 15157;34634 14331;34894 14460;35492 13085;42679 16024;42889 16076;45467 17186 1;45826 17341;46471 17883;46340 18252;46116 18815;46770 19103;46953 18693;47398 18859;47834 17786;47276 17332;47381 16800;47023 15998;46849 16390;42644 14576;43612 12421;42652 12028;41684 14201;35185 11278;36215 9202;35630 8897;31828 16724 18; 39337 8321;37234 7473 32;39436 3058; 39436 3058;42634 -3312; 44938 -5534;44091 -5830;48454 -15428; 43851 -5722 1;43621 -5806;43536 -6099;43650 -6315 1;44292 -7532;44652 -8214;45294 -9431 1;45394 -9620;45490 -9683;45678 -9784; 43851 -5722 1;43621 -5806;43536 -6099;43650 -6315 1;44292 -7532;44652 -8214;45294 -9431 1;45394 -9620;45512 -9695;45700 -9796;43851 -5722 18; 49564 -16053 1;50498 -15976;51340 -15627;51557 -14715; 48294 -15360;51557 -14715;51526 -14829 1;51268 -15656;50458 -15979;49564 -16053;49418 -16029 1;48972 -15948;48591 -15790;48321 -15476;48294 -15360 18; 37829 5951 1;37604 5831;37584 5534;37706 5310;42443 -3351; 37829 5951 1;37604 5831;37584 5534;37706 5310;42443 -3351;37829 5951 18; 36146 9162;36978 7558;39267 8403;38903 9477;37780 9051;36645 11753;35436 11241;36146 9162 18; 36170 9174;37009 7546; 43002 -3826;44233 -3392; 44536 -3165 32;42996 -3709 32;42806 -3170 32;44346 -2626 32;44536 -3165 50; 44670 -3098;45146 -4420; 35613 8929;36163 9209;37061 7499;37812 5963;37700 5835 1;37603 5690;37614 5479;37706 5310;42443 -3351;42792 -3223;43001 -3685;44546 -3118;45025 -4496;44711 -4618;44929 -5360;43883 -5718;43772 -5763 1;43603 -5878;43550 -6126;43650 -6315 1;44292 -7532;44652 -8214;45294 -9431 1;45394 -9620;45490 -9683;45678 -9784;45761 -9811;48315 -15425;48377 -15537 1;48642 -15811;49002 -15953;49418 -16029;49564 -16053;49602 -16550;49350 -16522 1;48597 -16353;48052 -16029;47681 -15309;42893 -6008;42560 -4791;37376 5225;35732 8673;35613 8929 18; 36153 18609;57439 27961;60300 21052;63371 16411;64069 14631;62812 14422;63092 12851;63580 12607;63824 12188;61417 9222;58206 3709;58124 3206;57458 1910;56964 1618;54496 -3613;53361 -6969;52128 -10991;51535 -14445;48402 -15235;44182 -5883;45367 -5340;44577 -2478;41740 -1540;37249 7491;39519 8453;38903 12870;41617 14079;42727 11981;43640 12425;42801 14695;46848 16349;47070 15830;47391 16743;47243 17336;47860 17755;47527 18495;47206 19421;46010 19050;42062 17360;42494 16102;35425 13079;35301 13424;37325 14855;36153 18609 18; 47921 17602;47169 17285;47407 16721;47036 15808;46796 16376;46360 16192; 58096 3249;57412 1929; 55614 -14649; 57649 -14636; 59315 -14673; 59919 -14698; 61449 -14797; 63645 -15056; 59453 -11395 1;59538 -11784;59931 -12039;60316 -11937 1;60793 -11811;60960 -11475;61439 -11358;63906 -10741;63771 -10309;59453 -11395 18; 81041 -11904;69609 -12236;69012 -9905; 68054 -9244 1;68165 -9556;68228 -9732;68339 -10044 1;68493 -10475;68035 -10770;68177 -11205 1;68308 -11607;68703 -11666;68831 -12069 1;68997 -12591;69421 -13014;69966 -12969 1;72186 -12784;72785 -12496;75642 -12463 1;77555 -12441;78628 -12429;80539 -12340;81008 -11957;69584 -12216;68794 -8984;68054 -9244 18; 63706 -10260;68146 -9118;68260 -9667;63906 -10741;63706 -10260 18; 69433 -20429 32;72320 -20602 32;72479 -17947 32;69592 -17774 32;69433 -20429 50; 77662 -22762;73037 -22423;71492 -20634; 69462 -19080;67093 -18939; 70340 -16200; 74054 -17482; 74007 -19774; 73881 -21443; 74141 -22011; 63609 -18634;63435 -21210; 60265 -20702;63262 -20900; 63652 -21196;63354 -25709;64078 -25757; 67339 -17776;67392 -16888 32;67884 -16918 32;67833 -17770 16; 67639 -17331;67605 -17798; 67884 -16918 32;67834 -17746 32;67342 -17716 32;67392 -16888 32;67884 -16918 50; 67591 -17766;67518 -18875; 74962 -13221;74863 -12480 1;72652 -12552;71968 -12802;69966 -12969 1;69421 -13014;68997 -12591;68831 -12069 1;68703 -11666;68308 -11607;68177 -11205 1;68035 -10770;68493 -10475;68339 -10044 1;68290 -9905;68250 -9793;68211 -9684;68112 -9704;63906 -10741;63420 -10863;61439 -11358 1;60960 -11475;60793 -11811;60316 -11937 1;59931 -12039;59538 -11784;59453 -11395;53908 -12759;53200 -16622;53077 -16860;52934 -17053;52673 -17246;51938 -17543;55542 -17829;55752 -14697;67497 -15505;67362 -16899;67831 -16949;67719 -18898;69422 -19009;69594 -17738;72469 -17960;72284 -20748;71704 -20761;72975 -22315;74344 -22463;76480 -22460;76510 -22056;75631 -21969;75646 -21472;74416 -21379;74962 -13221 18; 69760 -20132;69884 -18102;72154 -18219;72018 -20267;69760 -20132 18; -64342 72109;-64186 70948; -63501 72222;-63345 71059; -59039 72823;-58882 71656; -65691 71927;-65536 70779; -66497 71819;-66341 70658; 58171 -18458;63562 -18803;63377 -20913;63070 -20887;61470 -20782;61493 -20668 1;61485 -20498;61422 -20300;61271 -20290;58155 -20084;58171 -18458 18; 59445 -27083; 60108 -26507; 65014 -28014;58830 -27595;58396 -27570;58458 -26570 1;58470 -26376;58637 -26228;58831 -26240;65108 -26681;65014 -28014 18; 61260 -26448 1;61227 -26762;61452 -27055;61766 -27077;65061 -27308;65110 -26707;61260 -26448 18; 58375 -27589;58401 -27492;58458 -26570 1;58470 -26376;58637 -26228;58831 -26240;61262 -26411;61257 -26503 1;61254 -26796;61471 -27056;61766 -27077;65061 -27308;65055 -27429;65014 -28014;58375 -27589 18; 69191 -28288;69276 -26962;72227 -27151;72142 -28481;69191 -28288 18; 68600 -28249;65577 -28051;65676 -26731;68684 -26924;68600 -28249 18; 68184 -27586; 66762 -27507; 70060 -27655; 71700 -27751; 68600 -28249;65577 -28051;65676 -26731;68684 -26924;68600 -28249 18; 69191 -28288;69276 -26962;72227 -27151;72142 -28481;69191 -28288 18; 73745 -27439 32;74817 -27517 32;74752 -28414 32;73680 -28336 32;73745 -27439 50; 73744 -27456 32;74816 -27534 32;74752 -28414 32;73680 -28336 32;73744 -27456 50; 73468 -28632;74930 -28738; 73469 -28568 32;72686 -28518 32;72715 -28064 32;73534 -28116 48; 73561 -27691;72742 -27639 32;72770 -27205 32;73566 -27256 48; 73221 -28367; 73258 -27454; 73469 -28568 32;72686 -28518 32;72715 -28064 32;73534 -28116 32;73469 -28568 50; 73561 -27691;72742 -27639 32;72770 -27205 32;73566 -27256 32;73561 -27691 18; 72297 -26365 32;72820 -26403 32;72797 -26724 32;72274 -26686 32;72297 -26365 50; 68781 -26111 32;69304 -26149 32;69281 -26470 32;68758 -26432 32;68781 -26111 50; 65209 -25853 32;65732 -25891 32;65709 -26212 32;65186 -26174 32;65209 -25853 50; 64972 -26276;64080 -26222 32;64108 -25730 32;64964 -25782 16; 64108 -25730 32;64941 -25780 32;64911 -26272 32;64080 -26222 32;64108 -25730 50; 64524 -25979;64992 -26008; 69971 -26148 32;70804 -26198 32;70774 -26690 32;69943 -26640 32;69971 -26148 50; 70835 -26694;69943 -26640 32;69971 -26148 32;70827 -26200 16; 70387 -26397;70855 -26426; 74910 -28412 1;76636 -28400;77603 -28357;79328 -28329 1;80022 -28318;80418 -27617;80414 -26923;80340 -23543;79674 -23558 32;79637 -21903 32;80280 -21889;80250 -20520 1;80237 -19942;80446 -19608;80784 -19139 1;80931 -18935;80975 -18774;80970 -18523;80840 -11990; 80475 -23417 32;80443 -21994 32;79751 -22009 32;79783 -23432 32;80475 -23417 50; 74136 -25717;74189 -24829 32;74681 -24859 32;74630 -25711 16; 74681 -24859 32;74631 -25687 32;74139 -25657 32;74189 -24829 32;74681 -24859 50; 74388 -25707;74315 -26816; 74436 -25272;74402 -25739; 74915 -24737; 73519 -26916;77419 -27155;78283 -13156; 76157 -27149;76108 -27944; 75775 -27909;75609 -28262;74920 -28284;74977 -27320;75819 -27359;75775 -27909 18; 75811 -27891;76411 -27928; 78729 -27392; 77801 -28066;79047 -28038; 79955 -12378 1;80248 -12725;80135 -13072;80140 -13526 1;80160 -15338;80118 -16354;80079 -18165 1;80068 -18684;79806 -18942;79499 -19361 1;79093 -19916;79191 -20216;79174 -20903 1;79144 -22141;79033 -23011;79104 -24247 1;79126 -24640;79499 -24780;79499 -25173 1;79499 -26045;79528 -26534;79486 -27405 1;79472 -27690;79563 -27859;79721 -28096;80104 -27921 1;80308 -27654;80416 -27287;80414 -26923;80340 -23543;79674 -23558 32;79637 -21903 32;80280 -21889;80250 -20520 1;80237 -19942;80446 -19608;80784 -19139 1;80931 -18935;80975 -18774;80970 -18523;80844 -12192;79955 -12378 18; 59585 -11679 1;59752 -11897;60036 -12011;60316 -11937 1;60793 -11811;60960 -11475;61439 -11358;63906 -10741 32;64154 -10680;68214 -9678 33;68291 -9908;68314 -9973;68339 -10044 1;68493 -10475;68035 -10770;68177 -11205 1;68308 -11607;68703 -11666;68831 -12069 1;68997 -12591;69421 -13014;69966 -12969 1;72186 -12784;72785 -12496;75642 -12463 1;77335 -12444;78405 -12432;79947 -12364 32;80045 -12505 1;80222 -12811;80136 -13127;80140 -13526 1;80160 -15338;80118 -16354;80079 -18165 1;80068 -18684;79806 -18942;79499 -19361 1;79093 -19916;79200 -20355;79183 -21042 1;79180 -21160;79142 -21316;79132 -21662 1;79106 -22556;79045 -23226;79104 -24247 1;79126 -24640;79499 -24780;79499 -25173 1;79499 -26045;79528 -26534;79486 -27405 1;79479 -27541;79497 -27651;79534 -27755 16; 77819 -15750 32;77856 -15227 32;78177 -15251 32;78140 -15774 32;77819 -15750 50; 77575 -19798 32;77612 -19275 32;77933 -19299 32;77896 -19822 32;77575 -19798 50; 77173 -13340;76287 -13289 32;76315 -12796 32;77166 -12848 16; 76315 -12796 32;77144 -12844 32;77113 -13339 32;76287 -13289 32;76315 -12796 50; 77158 -13089;78384 -13168;78371 -13440; 76729 -13040;77198 -13076; 65809 -22580; 56240 -27709;56271 -27388;56309 -27276;56493 -25285;56561 -24421;58750 -24581;59989 -24643;60251 -20734;63206 -20920;63310 -20920;63600 -21213;63634 -21222;63348 -25703;64101 -25753;65218 -25838;65201 -25961;65186 -26174 32;65709 -26212 32;65732 -25891;68759 -26048;68773 -26222;68758 -26432 32;69281 -26470 32;69304 -26149;72266 -26344;72286 -26512;72274 -26686 32;72797 -26724 32;72820 -26403;74238 -26449;74185 -26903;73558 -27224;73357 -27243;72770 -27205 32;72742 -27639 32;73523 -27689;73521 -28100;73409 -28108;72715 -28064 32;72686 -28518 32;73464 -28568;73459 -28964;69166 -28655;69218 -28290;72142 -28481;72227 -27151;69276 -26962;58815 -26237;58732 -26248 1;58583 -26281;58468 -26410;58458 -26570;58396 -27570;58404 -27570;65008 -28002;65004 -28362;56240 -27709 18; 64970 -28364;64995 -28019;65118 -26637;65686 -26686;65671 -26796;65577 -28051;68600 -28249;68684 -26924;68795 -26890;69270 -26945;69268 -27081;69191 -28288;69263 -28293;69197 -28670;64970 -28364 18; 63306 -25717;63778 -18878;69239 -19262;69151 -20692;71454 -20814;73007 -22646;77386 -22891;77125 -26904;74473 -26677;74665 -24880;74159 -24827;74246 -23536;64301 -22751;64092 -25735;63306 -25717 18; 75647 -21473 32;76476 -21521 32;76445 -22016 32;75619 -21966 32;75647 -21473 50; 76061 -21717;76530 -21753; 76505 -22017;75619 -21966 32;75647 -21473 32;76498 -21525 16; 76490 -21766;77550 -21834; 76482 -22246;77515 -22313; 76486 -22500;76522 -21955; 75329 -21835;75527 -22124;76483 -22186; 74917 -13260;74621 -12162;80814 -11940;80900 -18800;80197 -20218;80222 -21785;79605 -21933;79740 -23512;80296 -23562;80296 -27387;79778 -28263;75571 -28349;75793 -27794;76410 -27165;77298 -27091;77644 -26905;78397 -13482;74917 -13260 18; 73437 -28967;73441 -28575;75003 -28566;78921 -28336 1;79052 -28334;79187 -28331;79328 -28329 1;80022 -28318;80418 -27617;80414 -26923;80340 -23543;80474 -23358;80443 -21994 32;80216 -21432;80197 -20218;80900 -18800;80814 -11940;81744 -11857 1;81744 -11903;81745 -11949;81746 -11995 1;81786 -14393;81782 -15738;81833 -18136 1;81847 -18803;81692 -19212;81320 -19766 1;81041 -20182;81001 -20513;81021 -21013 1;81109 -23234;81158 -24479;81246 -26700 1;81288 -27760;80691 -28839;79631 -28883;74795 -29081;73631 -28996;73437 -28967 18; 71024 -12165 32;70974 -10929 32;70188 -10961 32;70238 -12197 32;71024 -12165 50; 69838 -11397; 69594 -10246; 69978 -9827;70667 -10472;70684 -10944; 70536 -10359;72041 -10298; 72016 -9799 32;72656 -9773 32;72670 -10124 32;72030 -10150 32;72016 -9799 50; 77004 -9600 32;77644 -9574 32;77658 -9925 32;77018 -9951 32;77004 -9600 50; 72758 -11955;71043 -11994; 74788 -11187; 78221 -11210; 79117 -11015; 76443 -10657 32;77691 -10621 32;77710 -11278 32;76462 -11314 32;76443 -10657 50; 80819 -11889;80843 -8484; 80844 -7731;80868 -5276; 80893 -3265;80905 -970; 76156 -4244;79209 -4177; 76175 -5108;79229 -5040; 76128 -4253 32;79173 -4186 32;79191 -5003 32;76146 -5070 32;76128 -4253 50; 78793 -5007;78776 -4239; 78794 -4618; 77389 -4635; 77388 -5024;77371 -4256; 79154 -5044;79536 -5036;79565 -6419; 80239 -10677;79836 -10689;79872 -11886; 79865 -11887 32;79834 -10536 32;80697 -10516 32;80728 -11867 32;79865 -11887 50; 72020 -10484;72035 -10846;80733 -10487 16; 79824 -8528;80774 -8522;80706 -10496;72032 -10834;72032 -10155;72241 -10141;72670 -10124 32;72657 -9802;72883 -9773;76982 -9606;77009 -9734;77018 -9951 32;77658 -9925 32;77644 -9574;79829 -9501;79824 -8528 18; 69061 -9954;72035 -9806;72047 -10843;79721 -10528;79771 -11922;69605 -12194;69061 -9954 18; 80547 -8499;80533 -10492; 80561 -5290;80537 -7726; 80591 -1224;80580 -3261; 80811 -1157;75341 -1541; 73904 -2029;65789 -3844; 65415 -2920;65014 -1273; 76035 -36;74913 -93;74125 723; 79267 141;80872 222 32;80556 10042 32;77625 9065 32;76252 13651 32;75127 13538 32;75006 14769 32;74878 16063; 76495 13335 32;77156 13533 32;77483 12441 32;76822 12243 32;76495 13335 50; 77700 9409;80716 10414; 80255 12680;79737 20057; 80446 12704;80023 18729; 76069 20353;75035 19139;75053 18800; 77474 23024;78696 21881 1;78890 21700;79169 21689;79376 21855;79835 22223; 77106 14180 32;77731 14234 32;77283 19433 32;75106 19245 32;75143 18816 32;76918 18969 32;77061 17312 32;76838 17293 32;77106 14180 50; 76522 21900;77464 23043;77641 22906;78241 22317;78757 21846;79076 21736;79330 21807;79782 22184;79778 22252;80395 22267;81055 12753;80567 12715;80145 18730;79918 18720;80224 12691;77476 12464;77156 13533 32;76495 13335 32;76274 13576;76252 13651 32;75127 13538 32;75006 14769 32;74886 15982;75209 15955;75409 15966 32;75573 14073 32;77092 14205 32;77234 14191;77731 14234 32;77283 19433 32;75205 19254;76167 20350;76664 19931;77572 21109;76522 21900 18; 76747 12212;77611 12515;80286 12705;80355 12704;80576 12718;81034 12762;81106 11866 1;81264 8579;81394 6590;81485 3166 1;81643 -2754;81646 -6074;81746 -11995 1;81746 -11996;81746 -11998;81746 -11999;80991 -11965;80685 -8501;80650 -7673;80689 -5289;80697 -3222;80929 -1161;80807 252;80563 10005;77597 9080;76747 12212 18; 80913 148;80987 -1061;75424 -1504;65813 -3798;65159 -1306;74042 717;74992 -110;77385 -72;80913 148 18; 75251 -1627;80852 -1170;80753 -8635;79704 -8622;79507 -4909;79174 -4921;79161 -4218;75312 -4243;75251 -1627 18; 72242 17150;72772 17200;73451 15201;74894 15670;75141 13511;76239 13659;77645 9082;80555 10033;80827 287;75053 -133;73992 731;65282 -1046;65183 -1465;65652 -3735;65948 -3883;74041 -1860;75300 -5117;79519 -5043;79556 -6707;70279 -9816;69070 -9865;68799 -8903;53915 -12684;54382 -10742;55389 -6719;56079 -5232 1;57023 -3198;57553 -2059;58497 -25 1;59089 1251;59393 2050;59955 3212;60090 3486;60484 4253 1;61197 5576;62150 7067;63147 8405;63779 9313;65702 11355;69757 14953;72242 17150 18; 49501 -26594 1;49320 -27302;48378 -27802;47468 -27745 1;47140 -27725;46957 -27713;46629 -27693 1;39039 -27222;34783 -26960;27192 -26506 1;22464 -26223;19813 -26061;15084 -25791 1;10304 -25519;7621 -25423;2837 -25251 1;-3124 -25036;-6466 -24899;-12430 -24779 1;-14501 -24737;-15676 -24944;-17699 -25390 1;-19105 -25700;-19909 -25945;-21161 -26655 1;-22377 -27344;-23966 -27804;-24885 -26720 1;-27303 -23868;-28430 -22601;-30851 -19752 1;-32889 -17353;-33937 -15925;-36033 -13576 1;-36664 -12869;-36449 -11422;-35527 -11203 1;-31236 -10181;-28847 -9334;-24465 -8831 1;-21354 -8474;-19501 -8324;-16370 -8395 1;-10701 -8524;-7649 -8663;-2047 -8813 1;2347 -8930;4796 -9014;9190 -8919 1;13770 -8820;16304 -8942;20879 -8709 1;23766 -8562;25392 -8327;28185 -7632 1;31893 -6710;33913 -6017;37573 -4879 1;38798 -4498;40215 -4692;40647 -5541 1;42952 -10071;44342 -12565;46590 -17124 1;47502 -18974;48066 -19986;48945 -21852 1;49740 -23540;49967 -24772;49501 -26594 18; 32883 -9217 1;32313 -8493;31629 -8340;30708 -8300 1;29779 -8260;29259 -8215;28330 -8197;28336 -8058 1;32110 -7114;34264 -6452;37966 -5256 1;38914 -4950;39872 -5188;40332 -6072;41406 -8175 16; 40978 -13469;44091 -13453 32;41309 -8339 32;33050 -9354 32;33047 -10107 32;33250 -10111 32;33242 -11733 16; 35322 -16274; 36272 -16237; 36568 -14855; 37345 -14843; 38666 -14312 32;44599 -14227 32;44879 -14752 32;38674 -14841 32;38666 -14312 50; 44390 -14522; 39061 -14571; 38666 -14312 32;44599 -14227 32;44879 -14752 32;38674 -14841 32;38666 -14312 50; 34408 -17285;43698 -17174; 37572 -17063;37533 -14324;35096 -14359; 37528 -14370;35052 -14406;35041 -13685; 45405 -18950;47009 -19049 32;46009 -17146 32;43698 -17174; 48762 -23304 1;48572 -22627;48328 -22248;48067 -21595;45140 -21429;45196 -20441; 48762 -23304 1;49065 -24383;49141 -25079;48888 -26170 1;48735 -26830;48129 -27185;47453 -27144;43183 -26885 16; 40517 -7295; 40900 -7158;41362 -6941;41421 -7040 1;43281 -10676;44604 -13096;46590 -17124 1;47470 -18908;48025 -19913;48852 -21656;48408 -20735 1;48577 -21079;48753 -21445;48945 -21852 1;49677 -23407;49906 -24585;49595 -26178;48929 -25982 1;49129 -25008;49049 -24336;48773 -23342;48684 -23049 1;48507 -22522;48293 -22161;48067 -21595;45140 -21429;45196 -20441;45227 -19013;46954 -19048;46047 -17233;37582 -17085;37570 -16920;37533 -14263;38666 -14233 32;38674 -14841 32;44879 -14752 32;44599 -14227;41443 -8203;40900 -7158 18; 38666 -14312 32;44599 -14227 32;44879 -14752 32;38674 -14841 32;38666 -14312 50; 35110 -14373;35070 -13687;44312 -13561;44679 -14224;38616 -14337;35110 -14373 18; 34501 -13636;35019 -13611;35036 -14419;37545 -14369;37589 -17065;34431 -17109;34501 -13636 18; 41366 -6941;42967 -6137;43233 -6668;47681 -15309 1;48093 -16109;48720 -16420;49607 -16572;49564 -16053;49751 -16034 1;50609 -15926;51355 -15566;51557 -14715;51564 -14275;51931 -12139;53427 -12591 1;53420 -12623;53412 -12654;53405 -12686;53312 -13098 1;53113 -13993;52933 -14943;52766 -16136 1;52721 -16459;52558 -16724;52264 -16864 1;51629 -17167;50526 -17575;50302 -18151 1;49815 -19400;50372 -19123;51378 -21909 1;51489 -22216;51566 -22478;51620 -22732;51693 -23170 1;51725 -23431;51741 -23707;51752 -24037 1;51772 -24612;51792 -24936;51761 -25511 1;51730 -26082;51948 -26609;52322 -26983;49601 -26145 1;49903 -24569;49672 -23396;48945 -21852 1;48753 -21445;48577 -21079;48408 -20735;48852 -21656 1;48025 -19913;47470 -18908;46590 -17124 1;45986 -15899;45447 -14821;44938 -13813;44434 -12820 1;43490 -10963;42564 -9268;41491 -7171;41366 -6941 18; 51616 -30253; 50482 -34388; 49871 -38907; 49260 -43426; 48493 -48049; 48496 -29303;48453 -30603;49351 -30655 1;49388 -30054;49192 -29365;48592 -29312;48496 -29303 18; 48663 -31019;49290 -31048;49194 -31615;48645 -31569;48663 -31019 18; 48067 -29251;47884 -29687;47012 -29190;48067 -29251 18; 48112 -36086;48540 -36287;48496 -36557;48069 -36540;48112 -36086 18; 48073 -29257;48483 -29309; 49342 -30673;49283 -31066; 22830 -48486 1;23821 -50287;24174 -51459;25532 -53002; 40966 -29419 1;43271 -29557;44272 -29533;46564 -29811 1;47306 -29901;48124 -30779;48033 -31521;47402 -35838;45983 -35788 16; 45946 -36480 32;47438 -36537 32;47391 -37766 32;45899 -37709 32;45946 -36480 50; 45083 -37664 32;46622 -37723 32;46582 -38755 32;45043 -38696 32;45083 -37664 50; 46379 -38935;47279 -38984;47637 -39490;46774 -44856; 41851 -51258;41840 -52204;44306 -51764 1;45191 -51606;45764 -50999;45910 -50111;46774 -44856; 82291 -39873;82168 -33261 1;82142 -31870;80943 -30819;79552 -30843 1;77943 -30871;77040 -30869;75432 -30941 1;74161 -30998;73447 -30895;72175 -30867 1;71250 -30847;70731 -30835;69806 -30815 1;68591 -30788;67753 -31996;67819 -33209;67985 -36263; 66507 -36177;66350 -32652 1;66297 -31452;65488 -30471;64291 -30367 1;60065 -30000;57688 -29887;53457 -29582 1;52022 -29479;50750 -30394;50526 -31815 1;49427 -38783;48861 -42698;47803 -49672 1;47596 -51034;47457 -51794;47253 -53156;45639 -54802 16; 81995 30074;79831 29690;79744 26075 1;79968 26053;80142 25955;80151 25816;80975 13914;81004 13496;81083 12362 1;81251 8771;81389 6759;81485 3166 1;81643 -2754;81646 -6074;81746 -11995 1;81774 -13672;81780 -14834;81799 -16202;81812 -17052 1;81818 -17392;81825 -17751;81833 -18136 1;81847 -18803;81692 -19212;81320 -19766 1;81041 -20182;81001 -20513;81021 -21013 1;81109 -23234;81158 -24479;81246 -26700 1;81288 -27760;80691 -28839;79631 -28883;79346 -28895;79360 -30846 1;79423 -30845;79487 -30844;79552 -30843 1;80943 -30819;82142 -31870;82168 -33261;82291 -39873;85275 -40155;85399 -35103 1;85481 -33659;86487 -32418;87929 -32521;89377 -32625;89498 -28983 1;85390 -26429;85579 -21920;85515 -19931 1;85368 -15325;85333 -13021;85166 -8416 1;84924 -1770;84432 1532;83874 8159 1;83156 16678;82865 21453;82088 29071;81995 30074 18;84659 -24816 1;85467 -26779;85931 -28158;87782 -29353;84677 -29143;84659 -24816 18; 79404 -28883;79456 -30837;79157 -30850 1;77785 -30871;76904 -30875;75432 -30941 1;74161 -30998;73447 -30895;72175 -30867 1;71250 -30847;70731 -30835;69806 -30815 1;68591 -30788;67753 -31996;67819 -33209;67985 -36263;66507 -36177;66350 -32652 1;66297 -31452;65488 -30471;64291 -30367 1;60065 -30000;57688 -29887;53457 -29582 1;52022 -29479;50750 -30394;50526 -31815 1;50525 -31823;50524 -31831;50522 -31839;49186 -31613;49271 -31233;49317 -30924;49330 -30840;49367 -30336 1;49290 -29909;49208 -29406;48517 -29306;48085 -29249;47064 -29202;47313 -27742;47677 -27741;47926 -27730;48323 -27644;48869 -27400;49291 -27065;49503 -26597 1;49550 -26401;49590 -26212;49623 -26028;52197 -26845 1;52236 -26893;52278 -26939;52322 -26983;52579 -27201 1;52858 -27396;53193 -27520;53558 -27544;56271 -27721;65560 -28403;66821 -28498;69197 -28670;71138 -28812;74795 -29081;78159 -28943;79404 -28883 18; 61254 -31343;53228 -30890 1;52281 -30837;51662 -31679;51519 -32617;50926 -36438 32;51991 -36438; 61254 -31343 1;62668 -31423;63460 -31467;64874 -31547 1;65504 -31583;65946 -32124;65946 -32755;66097 -36176 16; 48878 -30078; 48965 -31300; 48112 -36086;48540 -36287;48496 -36557;48069 -36540;48112 -36086 18; 48644 -31204;49271 -31233;49175 -31800;48626 -31754;48644 -31204 18; 48496 -29303;48453 -30603;49351 -30655 1;49388 -30054;49192 -29365;48592 -29312;48496 -29303 18; 48086 -29251;47903 -29687;47031 -29190;48086 -29251 18; 51563 -39310;50419 -39338;50761 -36535; 50755 -36538;52020 -36538;52055 -39295;50424 -39312;50755 -36538 18; 51389 -42635;50018 -42651 32;49045 -48212 32;50308 -47412 32;51072 -46928;51105 -48137; 51095 -49676;48847 -49733; 48763 -49553;47863 -54426; 49824 -54204;48011 -54237; 49586 -55488;47528 -55526 32;46048 -61344; 48736 -31578;49192 -31597;50524 -31794;50474 -32146 1;49409 -38909;48844 -42809;47803 -49672 1;47596 -51034;47457 -51794;47253 -53156;46079 -54354;45639 -54802;46164 -51670;45934 -51510;46433 -50462;48246 -39224;47777 -38656;48032 -36786;48041 -36561;48249 -36547;48496 -36557;48540 -36287;48112 -36086;48736 -31578 18; 53412 -12671;53340 -12970 1;53130 -13903;52941 -14886;52766 -16136 1;52721 -16459;52558 -16724;52264 -16864 1;51629 -17167;50526 -17575;50302 -18151 1;49815 -19400;50372 -19123;51378 -21909 1;51665 -22703;51723 -23194;51752 -24037 1;51772 -24612;51792 -24936;51761 -25511 1;51704 -26569;52501 -27475;53558 -27544;56271 -27721;56299 -27389;53571 -27186 1;53367 -27170;53152 -27090;52985 -26988;52849 -26928;52854 -26912;52839 -26906 1;52656 -26822;52540 -26764;52424 -26599 1;52342 -26482;52272 -26393;52242 -26279;52235 -26244;52282 -25450;52507 -25455;52476 -26196 1;52488 -26313;52536 -26372;52604 -26468 1;52673 -26566;52735 -26611;52843 -26660;52846 -26663;54879 -26850;54984 -25159;55107 -23839;55366 -23833;55399 -22923;52599 -22705;52544 -23593;52102 -23569;52102 -23568;52090 -23568 1;51965 -22678;51808 -22171;51524 -21322;51490 -21237;50706 -19460;50687 -19459 1;50582 -19207;50537 -19052;50537 -18784 1;50537 -18531;50621 -18377;50746 -18182;50830 -18041;51107 -18054;51794 -17556;51730 -17391;52447 -17084 1;52698 -16978;52944 -16783;52992 -16515;53683 -12737;53412 -12671 18; 66162 -36162;66490 -36163;66346 -32578 1;66263 -31412;65463 -30469;64291 -30367 1;60065 -30000;57688 -29887;53457 -29582 1;52022 -29479;50750 -30394;50526 -31815 1;50235 -33657;49982 -35286;49747 -36815;49572 -37955 1;48998 -41712;48511 -45007;47803 -49672 1;47646 -50706;47528 -51393;47391 -52259;47281 -52969 1;47272 -53030;47263 -53092;47253 -53156;45400 -61193;45934 -61312;47510 -55485;49918 -55398;49848 -54246;47877 -54246;48697 -49710;51018 -49605;51018 -47145;48994 -48297;49936 -42557;51681 -42522;51594 -39294;51332 -39316;50419 -39338;50761 -36535;51366 -33170;51612 -32212 1;51851 -31451;52420 -30845;53228 -30890;60955 -31326;61592 -31362 1;62803 -31431;63577 -31474;64874 -31547 1;65425 -31578;65833 -31997;65926 -32523;66162 -36162 18; 46888 -29857;47055 -29251;47878 -29675;48073 -29257;48495 -29311;48459 -30584;49357 -30655;49288 -31059;48663 -31025;48651 -31575;48760 -31584;47788 -38651;48257 -39243;46417 -50551 1;46235 -51451;45556 -51961;44696 -52224;44634 -51680 1;45332 -51444;45783 -50884;45910 -50111;46774 -44856;47614 -39367;47244 -38935;46553 -38812;46615 -37714;46810 -37744;47391 -37766 32;47438 -36537 32;45946 -36480 32;45973 -35764;47404 -35752;47725 -33626;48033 -31521 1;48117 -30839;47671 -30218;47022 -30053;46888 -29857 18; 37852 -55758;43644 -54677;45424 -54956;44621 -57869; 42956 -57633;44837 -57784; 47260 -53153;45394 -61208; 45593 -54841;47268 -53122;45462 -60913;45396 -61191;43927 -61002;43998 -60744;42714 -60642;42967 -57676;44738 -57702;45593 -54841 18; 42749 -60636;43998 -60737;43928 -60998; 37852 -55758;36137 -56111; 35926 -57978 32;34914 -57903 32;34881 -58350 32;35893 -58425 32;35926 -57978 50; 36066 -58009;36219 -55935; 34886 -56890;34925 -56359;31955 -56968; 31955 -56968;29069 -57528; 29069 -57528;24058 -58424 32;22445 -58713 32;24024 -58851 32;24977 -58934 32;25873 -59012 32;41285 -60361; 36023 -57997;36171 -56060;43635 -54703;45412 -54950;44647 -57713;42957 -57614;41304 -60366;36961 -59946;36023 -57997 18; 34916 -56356;34724 -58606;35056 -58659;34951 -59740;22773 -58676;30764 -57141;34916 -56356 18; 45932 -51514;46211 -51723 1;45770 -52360;45291 -52633;44527 -52761;41065 -53376;40990 -52929; 44492 -52290;44562 -52787;44390 -52819;41065 -53376;40991 -52935;44492 -52290 18; 44471 -52299;44615 -52248 1;45133 -52101;45591 -51867;45921 -51518;46050 -51597;46167 -51679;46210 -51737 1;45790 -52322;45284 -52607;44562 -52732;44471 -52299 18; 41735 -51821;41805 -50949;37408 -50966;37408 -52536;41735 -51821 18; 37425 -51685; 37447 -53637 32;38172 -53496 32;38255 -53924 32;37531 -54065 32;37447 -53637 50; 31905 -54655 32;34570 -54173 32;34653 -54601 32;31989 -55083 32;31905 -54655 50; 23476 -53626;23825 -53382 1;24780 -54735;25640 -55391;27126 -56122;26564 -56270 1;25238 -55450;24428 -54954;23476 -53626 18; 26373 -55929; 25056 -55030; 23983 -53966; 25815 -52995; 27061 -53980; 28443 -54695; 29763 -55004; 31905 -54655 32;34570 -54173 32;34653 -54601 32;31989 -55083 32;31905 -54655 50; 37836 -53786; 48740 -31587;47777 -38656;48246 -39224;46433 -50462 1;46247 -51618;45267 -52160;44113 -52362 1;39328 -53200;36671 -53818;31886 -54656 1;31341 -54751;30829 -54790;30342 -54775 16; 30342 -54775 1;28644 -54720;27060 -53903;25780 -52516;25353 -52803 1;26794 -54380;28353 -55272;30500 -55344;30300 -54785 16; 30293 -54778 1;28595 -54723;27130 -53964;25780 -52534;25379 -52803 1;26785 -54380;28353 -55272;30500 -55344;30300 -54785;30293 -54778 18; 23476 -53626;23825 -53382 1;24780 -54735;25640 -55391;27126 -56122;26564 -56270 1;25238 -55450;24428 -54954;23476 -53626 18; 19577 -57853;26004 -56509 1;24698 -55623;24017 -54997;23056 -53745 1;22405 -52897;22279 -52266;21723 -51352 1;21537 -51046;21290 -50844;20934 -50883 1;20553 -50925;20451 -51282;20305 -51636 1;20006 -52362;19843 -52800;19811 -53585;19577 -57853 18; 22446 -53065 1;22093 -52349;21942 -51915;21487 -51259 1;21364 -51082;21202 -50992;20989 -51024 1;20787 -51054;20641 -51146;20571 -51338 1;20377 -51872;20224 -52149;20082 -52699 1;19983 -53081;19943 -53307;19943 -53702; 23609 -55982; 21684 -56007; 24712 -55723;24904 -56569;19743 -57656;19844 -55777; 21229 -57312 1;21199 -56960;21300 -56568;21649 -56510 1;22509 -56367;23149 -56583;23832 -56041;24599 -55445 1;25000 -55798;25445 -56130;26004 -56509;24955 -56718;24753 -56028;23472 -56478;23549 -56787;21229 -57312 18; 23514 -56787;23453 -56473;24718 -56063;24822 -56508;23514 -56787 18; 23514 -56787;23453 -56473;24718 -56063;24822 -56508;23514 -56787 18; 22421 -52960;22552 -52994;22599 -53050 1;22723 -53272;22867 -53499;23056 -53745 1;23601 -54454;24055 -54963;24585 -55432;24458 -55554;23832 -56041 1;23149 -56583;22509 -56367;21649 -56510 1;21300 -56568;21199 -56960;21229 -57312;21241 -57309;21082 -57374;19743 -57656;19836 -55925;19665 -55786;19813 -53684;19945 -53567 1;19953 -53251;19995 -53035;20082 -52699 1;20224 -52149;20377 -51872;20571 -51338 1;20641 -51146;20787 -51054;20989 -51024 1;21202 -50992;21364 -51082;21487 -51259 1;21844 -51774;22014 -52153;22242 -52638;22421 -52960 18; 26920 -59533;22726 -59212 1;21649 -59130;21033 -59184;19962 -59323 1;16631 -59754;14773 -60172;11438 -60679 1;8330 -61151;6665 -61828;3567 -62363 1;-189 -63012;-2296 -63524;-6103 -63709 1;-6416 -63724;-6594 -63700;-6903 -63651 16; 22793 -46842 1;23157 -47274;23459 -47682;23725 -48078 33;24052 -48571;24110 -48660;24360 -49087 33;24877 -49976;25461 -51126;26271 -52159 1;27212 -53359;28905 -54428;30796 -54187 1;31563 -54089;31993 -54034;32760 -53936; 24432 -48446;24165 -48591; 28672 -46418;25526 -46343 32;25534 -47776; 28672 -46418;30754 -46502;31085 -46877;31914 -46894;31896 -47522; 32089 -46724;32150 -43209; 24185 -48531;25476 -47746;25476 -46402;30605 -46472;31024 -46926;32088 -46804;32090 -46647;32150 -43209;32367 -38254;40802 -37316;45390 -37456;46646 -38939;47292 -39061;47571 -39514;45889 -50460;45272 -51323;44532 -51669;41646 -52384;32763 -54099;30852 -54180 1;30834 -54182;30815 -54185;30796 -54187 1;28905 -54428;27212 -53359;26271 -52159 1;25568 -51263;25035 -50278;24569 -49453;24185 -48531 18; 41667 -52396;32747 -54095; 31342 -44398; 28929 -44361; 26560 -44336; 27053 -43502 32;28239 -43548 32;28223 -43969 32;27037 -43923 32;27053 -43502 50; 27001 -43912;26223 -43882;26184 -44890;29151 -45005;29205 -43599; 31125 -43668 32;31070 -45081 32;31604 -45102 32;31659 -43689 32;31125 -43668 50; 31125 -43668 32;31070 -45081 32;31604 -45102 32;31659 -43689 32;31125 -43668 50; 26223 -43882;26184 -44890;29151 -45005;29205 -43599;28246 -43577;28235 -43953;26223 -43882 18; 22233 -42825;22169 -44436; 21196 -43798;21397 -44391;20638 -45150 1;19611 -44652;18952 -44415;17811 -44408;13451 -44204 16; 17837 -43667;17811 -44408 1;18952 -44415;19611 -44652;20638 -45150;21397 -44391;21196 -43798;17837 -43667 18; 20626 -45175;22768 -46904 1;23132 -47336;23459 -47682;23725 -48078 33;23869 -48294;23960 -48433;24039 -48556;25495 -47712;25507 -46318;30726 -46490;31096 -46848;31972 -46848;32090 -46662;32150 -43209;31158 -43098;31133 -43628;31294 -43675;31659 -43689 32;31604 -45102 32;31070 -45081 32;31124 -43697;29184 -43603;29198 -43775;29151 -45005;26184 -44890;26223 -43882;27001 -43912;27037 -43554;25743 -43462;25182 -43440 32;25200 -42952 32;21230 -42795 32;21191 -43788 32;21234 -43910;21397 -44391;20842 -44946;20626 -45175 18; 22832 -48489 32;22439 -48005 32;22798 -47714 32;23191 -48198 32;22832 -48489 50; 22127 -47620 32;21734 -47136 32;22093 -46845 32;22486 -47329 32;22127 -47620 50; 16792 -46797 1;18472 -46868;19672 -47119;20802 -48364;20457 -48677;20419 -48635 1;19414 -47524;18253 -47369;16760 -47246;16792 -46797 18; 22802 -48123; 22105 -47241; 20162 -48098; 19703 -48646 1;19150 -48317;18664 -48125;18162 -48013 33;17593 -47885;17004 -47861;16273 -47857 32;12865 -47786 32;12809 -50455 32;12205 -51034 32;12085 -56721 32;10700 -56692 32;10753 -54152 32;9584 -54128 16; 19727 -48535 1;20051 -48756;20357 -49066;20196 -49424;18950 -52138;18793 -57894; 16187 -48363;16412 -47858 1;17680 -47870;18525 -47964;19631 -48604;19835 -48613 1;20114 -48826;20339 -49106;20196 -49424;18950 -52138;18798 -57702;17520 -57860;17572 -55602 32;17867 -55609 32;17947 -52119 32;18445 -52130 32;18485 -50390 32;19027 -50402 32;19065 -48760 32;18449 -48746 32;18456 -48455 32;17176 -48426;16187 -48363 18; 22439 -48005;22127 -47620; 20187 -45866;20342 -45499;20638 -45150;20673 -45115;20964 -45406;22793 -46842 1;22843 -46901;22891 -46960;22939 -47018;25416 -50932;26736 -52795;28266 -53683;29610 -54176;32534 -54078;32880 -54077;41646 -52384;44532 -51669;44788 -51549;44789 -52194 1;44758 -52205;44727 -52214;44696 -52224;42078 -52735;40991 -52935;40131 -53095 1;37466 -53607;35180 -54079;31886 -54656 1;31341 -54751;30829 -54790;30342 -54775;29641 -54708 1;28210 -54482;26882 -53710;25780 -52516;25353 -52803;25038 -52401 1;24068 -51135;23689 -50046;22830 -48486;22916 -48421;23191 -48198 32;22798 -47714 32;22440 -48004;22149 -47647;22127 -47620;22270 -47504;22486 -47329 32;22093 -46845 32;21760 -47115;21556 -46957 1;20749 -46163;20064 -45735;19008 -45414;20187 -45866 18; 20457 -48677;20457 -48677 1;21384 -49705;21776 -50414;22351 -51673 1;22716 -52470;22960 -52909;23486 -53610; 18634 -45308;18365 -46966 1;19285 -47156;20059 -47545;20802 -48364;20470 -48665;20773 -49040 1;21482 -49886;21846 -50567;22351 -51673 1;22713 -52465;22957 -52903;23475 -53596;23603 -53537;23825 -53382 1;24780 -54735;25640 -55391;27126 -56122;27485 -57684;34986 -56277;34961 -57931;35158 -57921;35926 -57978 32;36121 -56080;43671 -54600;45695 -54945;46312 -51787;46116 -51853 1;45701 -52398;45237 -52642;44527 -52761;41065 -53376;40995 -52958;40771 -52976;40131 -53095 1;39469 -53222;38829 -53347;38192 -53472;38203 -53658;38255 -53924 32;37531 -54065 32;37447 -53637 32;37280 -53650 1;36408 -53820;35521 -53992;34565 -54171;34593 -54293;34653 -54601 32;31989 -55083 32;31905 -54655 32;31722 -54683 1;31238 -54758;30780 -54788;30342 -54775;30335 -54883;30500 -55344 1;28353 -55272;26794 -54380;25353 -52803;25074 -52449 1;24078 -51162;23699 -50066;22830 -48486;22679 -48300;22439 -48005 32;22358 -47905;22127 -47620;22052 -47527;21734 -47136 32;21666 -47067 1;20795 -46187;20088 -45733;18950 -45396;18634 -45308 18; 65933 -36163;64841 -36189;62416 -39225;60759 -39295;59991 -38091;56135 -38806;53850 -38928;53379 -39400;53013 -47285;52925 -48158;51878 -49484;51966 -51316;51198 -56741;51267 -59306;47987 -61697;46068 -61313;47551 -55538;49976 -55468;49907 -54142;47935 -54194;48667 -49728;51058 -49589;51023 -46919;49051 -48280;49976 -42593;51825 -42540;52053 -36468;51041 -36381;51038 -35713;51519 -32617 1;51662 -31679;52281 -30837;53228 -30890;61225 -31341;61606 -31363 1;62809 -31431;63582 -31474;64874 -31547 1;65425 -31578;65833 -31997;65926 -32523;66138 -35788;65933 -36163 18; -44103 -67138; -41919 -66509; 10924 -58534;15740 -58185; 15727 -58191;18789 -57746; 3336 -58833;10924 -58534; 13459 -60032; 2310 -61524 1;-269 -62051;-1696 -62441;-4282 -62936 1;-6671 -63393;-8048 -63442;-10476 -63581 1;-15162 -63850;-17775 -64219;-22462 -64471 1;-25572 -64638;-27310 -64811;-30418 -65012 1;-32102 -65121;-33047 -65227;-34710 -65518 1;-36005 -65745;-36736 -65852;-38025 -66111 1;-39322 -66372;-40217 -67608;-39944 -68902 1;-39803 -69570;-39724 -69945;-39579 -70612; 27565 -59310;26920 -59533;22726 -59212 1;21649 -59130;21033 -59184;19962 -59323 1;19629 -59366;19310 -59409;19004 -59452;18718 -59493 1;16117 -59865;14335 -60239;11438 -60679 1;8330 -61151;6665 -61828;3567 -62363 1;3481 -62378;3397 -62393;3313 -62407;2984 -62465 1;-417 -63064;-2498 -63517;-6103 -63692;-6488 -63706;-6803 -63645;-6347 -63454;-554 -62339;2259 -61796;2444 -61885 1;4502 -61639;5704 -61091;7598 -60597;8625 -60337 1;8899 -60262;9136 -60198;9483 -60186 1;10255 -60159;10687 -60106;11454 -60018 1;15241 -59584;17425 -59130;21022 -58528;21439 -58459 1;21503 -58448;21567 -58438;21632 -58427 1;22562 -58274;24825 -58024;25765 -57958;23464 -58566;23325 -58828;27565 -59310 18; 26910 -59534;10163 -62480;-3585 -65062;-37816 -67644;-37258 -70366;-39295 -70533;-39740 -68608;-39740 -68275;-39634 -67754;-39331 -67235;-39142 -66993;-38850 -66756;-37882 -66345;-35505 -65942;-32578 -65478;-30410 -65299;-23378 -64819;-15603 -64250;-13243 -64023;-7134 -63644;-6624 -63692 1;-6458 -63712;-6310 -63719;-6103 -63709 1;-2296 -63524;-189 -63012;3567 -62363 1;6665 -61828;8330 -61151;11438 -60679 1;14027 -60285;15725 -59946;17914 -59612;18523 -59521 1;18588 -59512;18653 -59502;18718 -59493;19004 -59452 1;19310 -59409;19629 -59366;19962 -59323 1;21033 -59184;21649 -59130;22726 -59212;25927 -59457;26910 -59534 18; 26001 -56510;26575 -56276;26311 -56114 1;25132 -55382;24366 -54868;23476 -53626;23425 -53529 1;22941 -52874;22701 -52439;22351 -51673 1;21846 -50567;21482 -49886;20773 -49040;20470 -48665;20344 -48554 1;19356 -47516;18216 -47366;16760 -47246;16762 -47221;16735 -47863 1;17780 -47889;18549 -48008;19495 -48527;19791 -48580 1;20089 -48797;20346 -49090;20196 -49424;18950 -52138;18793 -57894;19578 -57853;19577 -57853;19811 -53585 1;19843 -52800;20006 -52362;20305 -51636 1;20451 -51282;20553 -50925;20934 -50883 1;21290 -50844;21537 -51046;21723 -51352 1;22279 -52266;22405 -52897;23056 -53745 1;23928 -54882;24570 -55502;25658 -56270;26001 -56510 18; 25965 -56491;26601 -56269;27168 -56127;27501 -57614;25385 -57984;23947 -58132 1;23067 -58228;22146 -58342;21632 -58427 1;17662 -59080;15451 -59560;11454 -60018 1;10687 -60106;10255 -60159;9483 -60186 1;8977 -60204;8706 -60330;8213 -60445 1;5899 -60985;4666 -61639;2304 -61901;2304 -61210;2378 -61210 1;3165 -61039;3596 -60901;4376 -60704 1;4512 -60670;4245 -60461;4105 -60469 1;2855 -60538;1754 -60601;720 -60652;186 -59182 1;1266 -59117;2348 -59050;3397 -58993;10873 -58634;15808 -58264;18788 -57871;19564 -57845;20202 -57722;25933 -56524;25965 -56491 18; -45557 -63379;-42362 -62638; 25765 -57958 1;24825 -58024;22562 -58274;21632 -58427 1;17662 -59080;15451 -59560;11454 -60018 1;10687 -60106;10255 -60159;9483 -60186 1;8977 -60204;8706 -60330;8213 -60445 1;5899 -60985;4666 -61639;2304 -61901;2304 -61210;2378 -61210 1;3165 -61039;3596 -60901;4376 -60704 1;4512 -60670;4245 -60461;4105 -60469 1;-451 -60719;-3023 -60904;-7578 -60636 1;-12853 -60326;-15816 -60026;-21099 -60118 1;-25865 -60201;-28579 -60130;-33313 -60685 1;-35761 -60972;-37145 -61034;-39516 -61706 1;-40601 -62014;-41512 -62264;-42386 -62501 16; -44657 -64381 32;-43004 -63997 32;-42789 -64924 32;-44442 -65308 32;-44657 -64381 50; -41335 -65919 1;-41541 -65117;-41610 -64649;-41644 -63822 1;-41658 -63485;-41658 -63295;-41644 -62958 1;-41630 -62619;-41219 -62482;-40892 -62391 1;-40044 -62156;-39624 -61773;-38782 -61516 1;-38577 -61454;-38432 -62274;-38313 -62453 1;-38095 -62780;-37935 -63041;-37548 -63106 1;-37165 -63170;-36980 -63461;-36869 -63834 1;-36716 -64349;-36758 -64667;-36758 -65204;-41335 -65919 18; -35110 -60919 1;-33784 -60734;-33040 -60636;-31708 -60500 1;-30186 -60345;-29324 -60368;-27800 -60238 1;-27732 -60232;-27803 -60345;-27800 -60413 1;-27786 -60700;-27950 -61173;-28236 -61198 1;-29917 -61347;-30864 -61418;-32545 -61564 1;-33082 -61611;-33281 -62083;-33819 -62122 1;-34291 -62157;-34722 -61915;-34901 -61477 1;-34989 -61262;-35340 -60951;-35110 -60919 18; -20781 -61837 1;-20615 -62472;-20698 -62939;-20678 -63595;-17562 -63356;-17588 -62654 1;-17562 -62004;-17471 -61711;-17039 -61225 1;-16727 -60874;-16567 -60622;-16603 -60153;-17237 -60122 1;-18416 -60097;-19474 -60093;-20900 -60118 1;-20883 -60791;-20952 -61186;-20781 -61837 18; -13872 -62993 1;-13886 -62163;-14006 -61661;-14415 -60939 1;-14566 -60672;-14826 -60302;-14533 -60212;-14232 -60210 1;-12790 -60279;-11358 -60374;-9736 -60477 1;-9498 -60492;-9424 -60769;-9434 -61007 1;-9449 -61376;-9825 -61497;-10184 -61583 1;-10749 -61719;-11343 -61603;-11563 -62141 1;-11663 -62386;-11741 -62525;-11702 -62787;-13872 -62993 18; -16626 -62671; -14653 -62017; -17799 -63370;-17865 -61709;-16668 -60105;-16494 -60120 1;-15818 -60137;-14955 -60189;-14308 -60218;-13700 -61997;-13832 -62997;-14781 -63091;-17799 -63370 18; -10047 -60496;-9680 -60507 1;-9016 -60549;-8320 -60592;-7578 -60636 1;-5000 -60788;-3057 -60794;-1030 -60726 1;-803 -60718;-636 -60950;-661 -61176 1;-687 -61406;-852 -61556;-1080 -61595 1;-3932 -62089;-5530 -62589;-8425 -62589 1;-9804 -62589;-10618 -62656;-11988 -62812;-11993 -62726 1;-12005 -62513;-11934 -62366;-11845 -62149 1;-11625 -61611;-11031 -61727;-10466 -61591 1;-10107 -61505;-9731 -61384;-9716 -61015 1;-9709 -60848;-9743 -60661;-9847 -60560;-10047 -60496 18; -1158 -60692;-618 -60711 1;810 -60657;2302 -60568;4105 -60469 1;4245 -60461;4512 -60670;4376 -60704 1;3596 -60901;3165 -61039;2378 -61210;2304 -61210;2304 -61407;-3949 -62820;-8328 -63344;-11888 -63553;-13946 -63780;-24763 -64670;-29265 -64861;-32931 -65215;-37446 -66029;-39148 -66622;-39963 -67510;-40111 -68694;-39765 -70644;-44083 -71976;-45712 -63488;-42430 -62550;-42126 -62430 1;-41331 -62214;-40494 -61984;-39516 -61706 1;-37145 -61034;-35761 -60972;-33313 -60685 1;-28579 -60130;-25865 -60201;-21099 -60118 1;-20599 -60109;-20120 -60104;-19658 -60102;-17725 -60113 1;-14309 -60166;-11624 -60398;-7578 -60636 1;-5241 -60773;-3426 -60792;-1598 -60743;-1158 -60692 18; -691 -60966 1;-665 -61031;-653 -61104;-661 -61176 1;-687 -61406;-852 -61556;-1080 -61595 1;-3932 -62089;-5530 -62589;-8425 -62589 1;-8628 -62589;-8819 -62590;-9001 -62593;-9460 -62605 1;-10295 -62633;-10996 -62699;-11988 -62812;-12366 -62845;-14427 -63021;-15096 -63089;-15203 -63100;-18213 -63396;-18887 -63458;-21285 -63641;-21769 -63701;-24566 -63939;-25204 -63971;-26288 -64048 1;-29975 -64340;-32039 -64560;-35710 -65008;-36594 -65154;-36918 -65229;-41335 -65919 33;-41541 -65117;-41610 -64649;-41644 -63822 1;-41658 -63485;-41658 -63295;-41644 -62958 1;-41630 -62619;-41219 -62482;-40892 -62391 1;-40542 -62294;-40265 -62172;-40001 -62044; -5414 -57917;-5479 -59452; -5414 -57917;-5484 -59461; -46167 -61470 1;-43576 -60955;-42125 -60652;-39518 -60224 1;-36465 -59723;-34746 -59434;-31659 -59225 1;-28729 -59027;-27082 -58958;-24145 -58905 1;-21240 -58852;-19611 -58922;-16706 -58967 1;-12987 -59025;-10910 -59284;-7193 -59411 1;-6707 -59428;-6191 -59433;-5654 -59429 16; -5289 -59425 1;-2633 -59387;509 -59149;3397 -58993 16; 2501 -49521;3182 -49556 32;3235 -47244 33;1849 -46957;1083 -46357;7 -45438 32;-3587 -48857 32;-3692 -52975 32;-4652 -53830 32;-4844 -57930 32;-709 -58245;3548 -58210 32;3538 -58855; -5010 -58037;-5034 -59406;-4924 -59419 1;-2348 -59366;640 -59142;3397 -58993;3441 -58271;-766 -58370;-5010 -58037 18; -5030 -57967;-5640 -57930 1;-6432 -58156;-7335 -57987;-7975 -57468;-13228 -56791;-17759 -56386;-21585 -56242;-27683 -56149;-28076 -56707 1;-31927 -56758;-33765 -56732;-36978 -56916 1;-39791 -57077;-41460 -57092;-44272 -57282;-44334 -55975; -16212 -56655;-14860 -56777; -14895 -56883 1;-14249 -57167;-13926 -57545;-13220 -57563 1;-12607 -57578;-12276 -57270;-11667 -57336 1;-10706 -57441;-10174 -57593;-9207 -57615 1;-8781 -57625;-8471 -57725;-8126 -57476;-8369 -57417;-13228 -56791;-14880 -56643;-14895 -56883 18; -13975 -57376 1;-14289 -57953;-14294 -58376;-14270 -59033;-14074 -59047 1;-11792 -59151;-9941 -59317;-7193 -59411 1;-6723 -59427;-6225 -59433;-5707 -59429;-5622 -57931;-5640 -57930 1;-6432 -58156;-7335 -57987;-7975 -57468;-8112 -57450;-8200 -57524 1;-8516 -57711;-8812 -57624;-9207 -57615 1;-10174 -57593;-10706 -57441;-11667 -57336 1;-12276 -57270;-12607 -57578;-13220 -57563 1;-13505 -57556;-13727 -57490;-13934 -57395;-13975 -57376 18; -19456 -58910 1;-19296 -58598;-19096 -58439;-18758 -58343 1;-18174 -58178;-17804 -58175;-17205 -58273 1;-16888 -58325;-16737 -58632;-16743 -58953;-19456 -58910 18; -22165 -56247 1;-21876 -56753;-21513 -57108;-20931 -57086 1;-20386 -57065;-19786 -56889;-19746 -56345;-22165 -56247 18; -26488 -56194 1;-26910 -56555;-27551 -56899;-27273 -57380 1;-27055 -57757;-26628 -57933;-26226 -57764 1;-25394 -57415;-24734 -57176;-23923 -57572 1;-23350 -57852;-22893 -58245;-22946 -58881;-23223 -58892 1;-23517 -58895;-23823 -58899;-24145 -58905 1;-26210 -58942;-27637 -58987;-29320 -59080 1;-29738 -59103;-29155 -58408;-29279 -58008 1;-29433 -57509;-29522 -57221;-29785 -56770;-29664 -56726 1;-29172 -56720;-28646 -56715;-28076 -56707;-27683 -56149;-26819 -56162;-26488 -56194 18; -41384 -60582;-41576 -57128; -41384 -60582;-41576 -57097; -42340 -60976;-45675 -58642 1;-46086 -58354;-46306 -58069;-46391 -57575;-48869 -42674;-49033 -41692;-48615 -41622;-48944 -39656; -48654 -42114;-47599 -42070; -40958 -58645; -32839 -56778 1;-32691 -57760;-32769 -58306;-32865 -59294;-32753 -59307 1;-32406 -59278;-32043 -59251;-31659 -59225 1;-30830 -59169;-30068 -59114;-29386 -59076;-29418 -58993 1;-29454 -58798;-29149 -58316;-29244 -58008 1;-29398 -57509;-29417 -57186;-29680 -56735;-29728 -56749;-31464 -56744;-32839 -56778 18; -40667 -57091;-40570 -57086 1;-39468 -57039;-38361 -56995;-36978 -56916 1;-35385 -56825;-34071 -56785;-32741 -56762;-32743 -56888 1;-32621 -57799;-32729 -58364;-32821 -59311;-33065 -59334 1;-35339 -59533;-36960 -59804;-39518 -60224 1;-39852 -60279;-40166 -60332;-40467 -60383;-40667 -57091 18; -44288 -57288;-44127 -59336;-42277 -60693;-41623 -60582;-41796 -57153;-44288 -57288 18; -41368 -57138;-40652 -57086;-40663 -57164;-40467 -60383;-41184 -60496;-41368 -57138 18; -46421 -61377;-44293 -60941; -40650 -57379;-40490 -60012; -27381 -56150;-27434 -58994;-27223 -58984 1;-26294 -58950;-25323 -58926;-24145 -58905 1;-23823 -58899;-23517 -58895;-23223 -58892;-22859 -58889 1;-20696 -58873;-19154 -58929;-16706 -58967 1;-15776 -58982;-14949 -59009;-14173 -59043;-13877 -57441;-13871 -57423 1;-14203 -57283;-14484 -57064;-14895 -56883;-14880 -56643;-15047 -56628;-17759 -56386;-21585 -56242;-27228 -56156;-27381 -56150 18; -8462 -57623;-8556 -57638 1;-8754 -57657;-8961 -57621;-9207 -57615 1;-10174 -57593;-10706 -57441;-11667 -57336 1;-12276 -57270;-12607 -57578;-13220 -57563 1;-13922 -57545;-14245 -57171;-14884 -56888; -42190 -60587;-45453 -58314 1;-45792 -58078;-45951 -57824;-46012 -57415;-46160 -56420; -46892 -56944;-46735 -57851 1;-46662 -58270;-46508 -58531;-46160 -58776;-43167 -60883; -46320 -56051;-43198 -55742 32;-43180 -54789; -48481 -42082;-46170 -55949; -44608 -59971;-44842 -60921;-44324 -61094;-43226 -60872;-44608 -59971 18; -47605 -52218;-48740 -52428;-48000 -55611;-47194 -58763;-46223 -61274;-44767 -60928;-44520 -60015;-46420 -58609;-46642 -57906;-47605 -52218 18; -44344 -55969;-44274 -57287;-44126 -59171;-45722 -58116;-46054 -57470;-46228 -56728;-46307 -56179;-44344 -55969 18; -5194 -57976;-4985 -57976;-5009 -59426;-5268 -59419;-5194 -57976 18; 243 -59164;805 -60645;215 -60676 1;-2298 -60791;-4479 -60818;-7578 -60636 1;-12853 -60326;-15816 -60026;-21099 -60118 1;-25865 -60201;-28579 -60130;-33313 -60685 1;-35761 -60972;-37145 -61034;-39516 -61706 1;-40601 -62014;-41512 -62264;-42386 -62501;-46066 -63380;-46596 -61555;-46288 -61493;-44166 -61086;-43770 -60993 1;-42420 -60728;-41214 -60502;-39518 -60224 1;-36465 -59723;-34746 -59434;-31659 -59225 1;-31586 -59220;-31515 -59215;-31443 -59211;-30975 -59180 1;-30420 -59143;-29909 -59112;-29421 -59085;-29072 -59067 1;-27494 -58983;-26108 -58940;-24145 -58905 1;-23823 -58899;-23517 -58895;-23223 -58892;-22946 -58881;-22387 -58886 1;-20462 -58882;-18972 -58932;-16706 -58967 1;-12987 -59025;-10910 -59284;-7193 -59411 1;-6707 -59428;-6191 -59433;-5654 -59429;-4798 -59416 1;-3319 -59382;-1709 -59295;-89 -59198;243 -59164 18; -50362 -45946;-48704 -45527;-47588 -52244;-48669 -52488;-50362 -45946 18; -51897 -39476;-49427 -38638;-49220 -39760;-48936 -41376;-49393 -41438;-48714 -45546;-50331 -45953;-51897 -39476 18; -52964 -35502 1;-51792 -35275;-50961 -35396;-49789 -35624;-49705 -36870;-49407 -38668;-51921 -39527;-52964 -35502 18; -49990 -30922;-40150 -30748 1;-41024 -32749;-41312 -34209;-40796 -36331;-49746 -36714; -40848 -31288; -49990 -30922;-40150 -30748 1;-41024 -32749;-41312 -34209;-40796 -36331;-49746 -36714;-49990 -30922 18; -40229 -30729;-39716 -29931 32;-40090 -28777 32;-39000 -28845 32;-35444 -25708 32;-36027 -25028;-34410 -23641;-33829 -24318 32;-33463 -24004 1;-33186 -23766;-32946 -23467;-32796 -23148 33;-32572 -22671;-32549 -22151;-32905 -21736;-37022 -16938 32;-50195 -23150 1;-51024 -23499;-51039 -24463;-50882 -25349 1;-50505 -27481;-50293 -28677;-49916 -30809; -35304 -25624;-33944 -24455; -35427 -25501;-35776 -25074;-34450 -23896;-34013 -24324;-35427 -25501 18; -49874 -30884;-50038 -30121 1;-50343 -28395;-50548 -27241;-50882 -25349 1;-51039 -24463;-51024 -23499;-50195 -23150;-37022 -16938 32;-32905 -21736 1;-32311 -22428;-32771 -23410;-33463 -24004;-33829 -24318 32;-34410 -23641;-36027 -25028;-35444 -25708 32;-39000 -28845 32;-40090 -28777 32;-39716 -29931 32;-40229 -30729;-49874 -30884 18; -51077 -24831 1;-51914 -25190;-52843 -25786;-52542 -26646 1;-52332 -27245;-52112 -27665;-51530 -27919 1;-51113 -28101;-50852 -28143;-50449 -28355;-51077 -24831 18; -54254 -30275;-50276 -29507;-49980 -30921;-49718 -36713;-52510 -37376;-54254 -30275 18; -47728 -37816;-47623 -41933;-48251 -41907;-48661 -39508 1;-48784 -38787;-48538 -37992;-47824 -37833;-47728 -37816 18; -35338 -50247;-35301 -48803 32;-35807 -48803;-36954 -49605 32;-38201 -49602 32;-42000 -49593; -35475 -54600;-34649 -54614 32;-34598 -51700; -48415 -42142;-42469 -42142;-41877 -49544;-37658 -49603;-36954 -49605 32;-35807 -48803;-35301 -48803 32;-35333 -50063;-35017 -50383;-34598 -51740;-34672 -54602;-36547 -54504;-36843 -53788;-43037 -54084;-43209 -55515;-46121 -56009;-48415 -42142 18; -34495 -54744 1;-34529 -55257;-35054 -55484;-35568 -55508;-43072 -55885; -43090 -54745;-38684 -54292;-35840 -54326;-35448 -54675;-34497 -54728;-34524 -54906 1;-34647 -55305;-35112 -55487;-35568 -55508;-43072 -55885;-43090 -54745 18; -40947 -40924 1;-40951 -40524;-40420 -40532;-40023 -40479;-39185 -46289;-40171 -46254; -40475 -48557;-39516 -48566;-39278 -48333; -49335 -39723;-49079 -41286;-49515 -41354;-49373 -42184; -48199 -41726;-48548 -39587; -48944 -39656;-48662 -41343; -49295 -42054;-48630 -41944; -48900 -44439;-48235 -44329; -48114 -49191;-47449 -49081; -48509 -46806;-47844 -46696; -46933 -56331;-46268 -56221; -47328 -53946;-46663 -53836; -47719 -51579;-47054 -51469; -46828 -56965;-46163 -56855; -46736 -57521;-46071 -57411; -45979 -57883;-46553 -58247; -45640 -58253;-46056 -58787; -45640 -58253;-46056 -58787; -45172 -58589;-45542 -59117; -44700 -58920;-45070 -59448; -44221 -59256;-44591 -59784; -43730 -59600;-44100 -60128; -43257 -59931;-43627 -60459; -42773 -60270;-43143 -60798; -47705 -39222;-47626 -40897; -49246 -39743;-49703 -36881; -48652 -39633;-48661 -39508 1;-48784 -38787;-48538 -37992;-47824 -37833;-47728 -37816;-47728 -37821; -47724 -37677;-45081 -37681; -42455 -37677;-40623 -37680;-40626 -39256; -38283 -35809;-38209 -37031; -38209 -37031;-37726 -43474; -37982 -42505 1;-38235 -42615;-38446 -42783;-38426 -43058 1;-38409 -43296;-38159 -43463;-37920 -43450; -38007 -42517 1;-38260 -42627;-38446 -42783;-38426 -43058 1;-38409 -43296;-38159 -43463;-37920 -43450;-38007 -42517 18; -39760 -42302;-40979 -42328;-40947 -40924 1;-40951 -40524;-40420 -40532;-40023 -40479;-39760 -42302 18; -40979 -42328;-39760 -42302;-39185 -46289;-40171 -46254;-41014 -43903;-40979 -42328 18; -40422 -41427; -40665 -42313;-39758 -42308; -40509 -39253;-40166 -39239;-40022 -40468;-40229 -40502 1;-40581 -40541;-40950 -40590;-40947 -40924;-47660 -40869;-47722 -39204;-41726 -39216;-41701 -37674;-40517 -37649;-40509 -39253 18; -40498 -37569;-40453 -37554;-40159 -39241;-40588 -39262;-40498 -37569 18; -40598 -36489;-40451 -37556;-42326 -37649;-42343 -37824;-47696 -37823;-47813 -37831;-47824 -37833 1;-48538 -37992;-48784 -38787;-48661 -39508;-48636 -39655;-49257 -39751;-49720 -36846;-40598 -36489 18; -29646 -46706 1;-29400 -46201;-29743 -45850;-30220 -45797 1;-30696 -45744;-31131 -45743;-31259 -46069 1;-31388 -46395;-31131 -46802;-30685 -46978 1;-30240 -47154;-29799 -47021;-29646 -46706 18; -31242 -46496 1;-31565 -46707;-31844 -47025;-31687 -47377 1;-31496 -47804;-31300 -48215;-30832 -48215 1;-30495 -48215;-30182 -47967;-30196 -47630;-30213 -47046; -31260 -46025;-31201 -45964 1;-31023 -45719;-30634 -45712;-30220 -45797 1;-29725 -45899;-29431 -46176;-29646 -46706 1;-29729 -46911;-29944 -47037;-30204 -47056;-30209 -47185;-30196 -47630 1;-30182 -47967;-30495 -48215;-30832 -48215 1;-31300 -48215;-31496 -47804;-31687 -47377 1;-31844 -47025;-31565 -46707;-31242 -46496;-31269 -46403 1;-31298 -46306;-31301 -46207;-31274 -46113;-31260 -46025 18; -32561 -45615 1;-32797 -45615;-33069 -45656;-33233 -45796 1;-33363 -45906;-33426 -46078;-33355 -46339; -31166 -45650;-30258 -45650;-30258 -45650 1;-29801 -45650;-29559 -45963;-29325 -46356; -23546 -49530;-23546 -48395;-28776 -48393;-29307 -48912 32;-32663 -48875 32;-32688 -50220; -14922 -54736;-14910 -55599 32;-13960 -55686 32;-13800 -49875 32;-16563 -49850;-21004 -48401 32;-22353 -48421 32;-22337 -49517; -12691 -41913;-12270 -41933;-12218 -40276 32;-13200 -40255 32;-14665 -40224 32;-18866 -40136 32;-20471 -46574 32;-20000 -47481;-16882 -48474 32;-16493 -48598 32;-14382 -48633 32;-12395 -45987 32;-12290 -43008;-12726 -42993; -12691 -41913;-12270 -41933;-12218 -40276 32;-13200 -40255 32;-14665 -40224 32;-18866 -40136 32;-20471 -46574 32;-20000 -47481;-16882 -48474 32;-16493 -48598 32;-14382 -48633 32;-12395 -45987 32;-12290 -43008;-12726 -42993;-12691 -41913 18; -18238 -39474 1;-18222 -38930;-18245 -38458;-17794 -38154;-14993 -39190;-15080 -39535;-18238 -39474 18; -11850 -39657 1;-11858 -39194;-11831 -38726;-11369 -38702 1;-11063 -38686;-10768 -38877;-10752 -39183;-9506 -39189;-9531 -39645;-11850 -39657 18; -24468 -54260;-24480 -55062;-23074 -55050;-23061 -54507; -15067 -55735 1;-18128 -55435;-19849 -55204;-22925 -55204; -24639 -55204 1;-27478 -55213;-29068 -55310;-31906 -55401 1;-32070 -55406;-32161 -55409;-32325 -55414 1;-32921 -55433;-33551 -55330;-33560 -54734; -33385 -51308;-33418 -54815; -22307 -49558;-22282 -48423;-21812 -48413;-21004 -48401;-16563 -49850;-13800 -49875 32;-13960 -55686 32;-14927 -55739;-15198 -55722 1;-18178 -55428;-19893 -55204;-22925 -55204;-24726 -55204 1;-27511 -55215;-29097 -55311;-31906 -55401 1;-32070 -55406;-32161 -55409;-32325 -55414 1;-32921 -55433;-33551 -55330;-33560 -54734;-33459 -50990;-32769 -50077;-32667 -49086;-32663 -48875;-29307 -48912;-28776 -48393;-23546 -48395;-23546 -49530;-22307 -49558 18; -35148 -48622;-32740 -48683;-32818 -50053;-33412 -51309;-33551 -54768;-33519 -54963 1;-33369 -55357;-32835 -55430;-32325 -55414 1;-32161 -55409;-32070 -55406;-31906 -55401 1;-29068 -55310;-27478 -55213;-24639 -55204;-22716 -55204 1;-19770 -55215;-18058 -55442;-15067 -55735;-13836 -55849;-13958 -56726;-14880 -56643;-15140 -56620;-17759 -56386;-21585 -56242;-27228 -56156;-27381 -56150;-27656 -56149;-27683 -56149;-28076 -56707 1;-28646 -56715;-29172 -56720;-29664 -56726;-29785 -56770;-30027 -56729 1;-32654 -56756;-34359 -56766;-36978 -56916 1;-38825 -57022;-40179 -57064;-41687 -57135;-41865 -57144 1;-42601 -57179;-43379 -57222;-44272 -57282;-44334 -55975;-42971 -55880;-35568 -55508 1;-35054 -55484;-34529 -55257;-34495 -54744;-34597 -51313;-35243 -49987;-35148 -48622 18; -29408 -46910;-28968 -46916;-28900 -47674;-29607 -48323;-29650 -48751;-32800 -48672;-33210 -47399;-32704 -47154;-32285 -48271;-30069 -48288;-29711 -47878;-29408 -46910 18; -28852 -48218 1;-28876 -47677;-28879 -47369;-28975 -46836 1;-29018 -46600;-29075 -46464;-29222 -46275; -31153 -45548;-32565 -45523; -33466 -46356;-32812 -48693; -35107 -48649;-34697 -46782 1;-34729 -46150;-35129 -45572;-35762 -45561 1;-36470 -45549;-36868 -45541;-37576 -45529 1;-37802 -45525;-37870 -45816;-37847 -46041;-37498 -49478; -36255 -47733; -34929 -47800 1;-35528 -47807;-35800 -48184;-36395 -48253 1;-36684 -48287;-36947 -48080;-36980 -47791 1;-37021 -47425;-36952 -46926;-37320 -46910 1;-37525 -46901;-37738 -47045;-37721 -47250;-37688 -47612;-37498 -49478;-37233 -49604;-36954 -49605 32;-35807 -48803;-35301 -48803 32;-35092 -48579;-34957 -47968;-34929 -47800 18; -37685 -45646;-36952 -45663; -35513 -46056; -34972 -46736; -35168 -47824;-35168 -47824 1;-35612 -47908;-35885 -48194;-36395 -48253 1;-36684 -48287;-36947 -48080;-36980 -47791 1;-37021 -47425;-36952 -46926;-37320 -46910 1;-37360 -46908;-37400 -46912;-37438 -46921; -33073 -47333;-32876 -47238;-32704 -47154 32;-32285 -48271 32;-30069 -48288 32;-29711 -47878;-29408 -46910 32;-28999 -46891 32;-28912 -47681;-29586 -48296; -28838 -48289;-28857 -48106 1;-28877 -47632;-28886 -47331;-28975 -46836 1;-29018 -46600;-29075 -46464;-29222 -46275;-29590 -46223 1;-29545 -46359;-29556 -46522;-29646 -46706 1;-29742 -46903;-29950 -47029;-30201 -47052;-30207 -47266;-30196 -47630 1;-30182 -47967;-30495 -48215;-30832 -48215 1;-31300 -48215;-31496 -47804;-31687 -47377 1;-31844 -47025;-31565 -46707;-31242 -46496;-31259 -46432 1;-31302 -46310;-31305 -46185;-31259 -46069 1;-31193 -45901;-31045 -45820;-30853 -45788;-31114 -45569;-31268 -45546;-32565 -45523;-32742 -45624 1;-32926 -45642;-33110 -45691;-33233 -45796 1;-33347 -45892;-33409 -46036;-33375 -46245;-33469 -46347;-33150 -47486;-32805 -48709;-29332 -48801;-28838 -48289 18; -30411 -46317; -32714 -46296; -34949 -47842;-34888 -47650;-34697 -46782 1;-34729 -46150;-35129 -45572;-35762 -45561 1;-36470 -45549;-36868 -45541;-37576 -45529 1;-37802 -45525;-37870 -45816;-37847 -46041;-37690 -47587;-36877 -48610;-35560 -48505;-34949 -47842 18; -34533 -43063;-34536 -43631;-33410 -43637;-33412 -44087;-28895 -44107;-28892 -43532; -34533 -43063;-34536 -43631;-33410 -43637;-33412 -44087;-28895 -44107;-28892 -43532;-31066 -43538;-31069 -43070;-34533 -43063 18; -27617 -41261;-27617 -45858; -28370 -41175;-27414 -41182; -26989 -46285;-23577 -46297 32;-23573 -45265;-24219 -45262; -26966 -46202;-26983 -46481;-27214 -46481;-27214 -46422;-26966 -46202 18; 27804 -44786;28588 -44817; -38283 -35809 1;-38378 -34235;-38647 -33191;-37928 -31788 1;-37224 -30413;-36451 -29855;-35276 -28852 1;-33901 -27679;-33131 -27021;-31757 -25848 1;-31087 -25276;-30090 -25098;-29502 -25755;-25246 -30665; -26429 -30922;-25016 -30930;-25251 -30669; -26247 -32393;-23718 -32400; -22379 -34995;-21611 -34995;-23831 -32330; -22014 -36913;-20068 -36895; -22154 -37393;-22149 -37873; -21493 -39020;-20603 -39020;-22174 -45527;-23395 -46452; -21241 -41621 1;-20873 -41710;-20681 -41873;-20770 -42241 1;-20846 -42557;-21143 -42604;-21459 -42528; -21092 -42136; -21241 -41621 1;-20873 -41710;-20681 -41873;-20770 -42241 1;-20846 -42557;-21143 -42604;-21459 -42528;-21241 -41621 18; -21493 -39020;-20603 -39020;-22174 -45527;-23395 -46452;-23566 -45327;-24247 -45189;-24320 -44583;-22227 -40055;-21493 -39020 18; -22174 -45527 1;-22401 -46340;-22653 -47240;-23497 -47237;-26454 -47226;-27239 -46409; -26201 -46845; -25189 -46854; -24143 -46871; -23087 -46714; -22174 -45527 1;-22401 -46340;-22653 -47240;-23497 -47237;-26454 -47226;-27239 -46409;-23416 -46453;-22174 -45527 18; -22969 -47062;-22389 -48283;-22389 -49505;-23437 -49517;-23546 -48456;-23546 -48395;-28776 -48393;-28855 -48157 1;-28876 -47653;-28883 -47348;-28975 -46836 1;-29018 -46600;-29075 -46464;-29222 -46275;-29453 -46153 1;-29653 -45861;-29883 -45650;-30258 -45650;-30258 -45650;-31086 -45650;-31153 -45548;-32565 -45523;-32631 -45561;-32742 -45624 1;-32926 -45642;-33110 -45691;-33233 -45796 1;-33347 -45892;-33409 -46036;-33375 -46245;-33466 -46344;-33454 -46399;-32812 -48693;-35108 -48666;-35082 -48533;-34957 -47968;-34929 -47800;-34869 -47567;-34697 -46782 1;-34729 -46150;-35129 -45572;-35762 -45561 1;-36470 -45549;-36868 -45541;-37576 -45529 1;-37802 -45525;-37870 -45816;-37847 -46041;-37498 -49478;-41832 -49455;-41832 -48579;-40475 -48604;-40450 -46988;-40191 -46951;-40203 -46248;-40055 -46258;-39185 -46289;-39761 -42295;-37933 -42226;-37946 -42497;-38025 -42525 1;-38269 -42635;-38446 -42790;-38426 -43058 1;-38409 -43296;-38159 -43463;-37920 -43450;-37724 -43052;-37687 -42226;-35664 -42226;-35590 -43089;-34516 -43052;-34534 -43274;-34536 -43631;-33410 -43637;-33412 -44087;-28895 -44107;-28892 -43532;-28928 -42521;-28151 -42521;-28126 -41325;-27682 -41300;-27707 -45865;-26436 -47234;-26078 -47227;-23497 -47237 1;-23334 -47238;-23193 -47204;-23070 -47145;-22969 -47062 18; -37909 -40689;-38377 -33608;-38327 -32912 1;-38256 -32553;-38131 -32185;-37928 -31788 1;-37224 -30413;-36451 -29855;-35276 -28852 1;-33901 -27679;-33131 -27021;-31757 -25848 1;-31087 -25276;-30090 -25098;-29502 -25755;-25246 -30665;-25004 -30943;-26336 -30967;-26361 -32398;-23869 -32398;-21599 -34891;-22290 -34989;-22290 -38024;-24955 -45180;-23622 -45254;-23647 -46216;-27101 -46191;-27718 -45550;-27644 -41207;-28162 -41182;-28286 -41651;-37909 -40689 18; -38379 -37086 1;-38862 -37173;-39235 -37599;-39146 -38081 1;-39069 -38496;-38713 -38763;-38291 -38744; -38379 -37086 1;-38862 -37173;-39235 -37599;-39146 -38081 1;-39069 -38496;-38713 -38763;-38291 -38744;-38379 -37086 18; -38686 -37483; -38607 -38460; -38836 -37266 1;-38933 -36434;-38926 -35955;-39023 -35123 1;-39250 -33180;-38992 -31791;-37701 -30321 1;-37220 -29773;-36955 -29455;-36405 -28976 1;-34731 -27516;-33755 -26699;-32079 -25241 1;-31334 -24593;-29988 -24320;-29189 -25139 1;-28349 -26104;-27896 -26661;-27072 -27640;-19706 -36392;-20156 -36771; -25602 -29386 1;-25100 -28995;-24388 -29031;-23978 -29518 1;-23566 -30007;-23629 -30737;-24118 -31149; -28601 -25821 1;-28403 -25623;-28085 -25772;-27903 -25985 1;-27735 -26182;-27698 -26480;-27895 -26648; -28107 -26100; -25121 -29468; -24233 -29813; -24183 -30702; -40629 -36418;-40507 -34377 1;-40456 -33518;-40416 -33010;-40088 -32214 1;-39538 -30878;-39298 -29984;-38221 -29021 1;-36134 -27155;-34929 -26161;-32842 -24295 1;-31991 -23534;-31533 -22228;-32294 -21377 1;-35032 -18315;-36308 -16371;-39086 -13345; -38342 -37086;-38432 -37097 1;-38583 -37133;-38722 -37201;-38836 -37294;-38858 -37074 1;-38942 -36357;-38959 -35876;-39048 -35110 1;-39275 -33167;-38992 -31791;-37701 -30321 1;-37220 -29773;-36955 -29455;-36405 -28976 1;-34731 -27516;-33755 -26699;-32079 -25241 1;-31334 -24593;-29988 -24320;-29189 -25139 1;-28349 -26104;-27896 -26661;-27072 -27640;-19706 -36392;-20156 -36771;-22316 -36851;-22316 -35050;-21946 -34995;-21611 -34995;-23831 -32330;-26412 -32336;-26313 -30930;-25104 -30954;-25638 -30213;-29502 -25755 1;-30090 -25098;-31087 -25276;-31757 -25848 1;-33131 -27021;-33901 -27679;-35276 -28852 1;-36451 -29855;-37224 -30413;-37928 -31788 1;-38647 -33191;-38378 -34235;-38283 -35809;-38342 -37086 18; -36993 -16889;-36418 -16433 1;-35134 -18001;-33997 -19473;-32294 -21377 1;-31533 -22228;-31991 -23534;-32842 -24295 1;-34220 -25527;-35214 -26379;-36330 -27353;-36768 -27735 1;-37214 -28126;-37689 -28545;-38221 -29021 1;-39298 -29984;-39538 -30878;-40088 -32214 1;-40416 -33010;-40456 -33518;-40507 -34377;-40622 -36309;-40839 -36147 1;-41295 -34124;-40999 -32691;-40150 -30748;-39943 -30285;-39716 -29931 32;-40090 -28777 32;-39000 -28845 32;-35498 -25645;-36027 -25028;-34410 -23641;-33829 -24318 32;-33463 -24004 1;-33186 -23766;-32946 -23467;-32796 -23148 33;-32572 -22671;-32549 -22151;-32905 -21736;-36674 -17344;-36993 -16889 18; -39770 -42346;-38000 -42272;-37836 -42009;-38084 -38700;-38379 -38744 1;-38762 -38725;-39074 -38467;-39146 -38081 1;-39202 -37779;-39076 -37499;-38856 -37310;-38850 -37150;-38858 -37074 1;-38942 -36357;-38959 -35901;-39048 -35135 1;-39275 -33192;-38992 -31791;-37701 -30321 1;-37220 -29773;-36955 -29455;-36405 -28976 1;-36151 -28755;-35914 -28549;-35688 -28352;-35442 -28140 1;-34297 -27150;-33408 -26397;-32079 -25241 1;-31467 -24709;-30449 -24429;-29662 -24801;-31971 -21954 1;-31739 -22744;-32167 -23691;-32842 -24295 1;-34929 -26161;-36134 -27155;-38221 -29021 1;-39298 -29984;-39538 -30878;-40088 -32214 1;-40416 -33010;-40456 -33518;-40507 -34377;-40629 -36418;-39770 -42346 18; -38453 -17473;-39249 -15785;-38190 -15286; -39555 -14221;-39340 -13475;-39242 -13486 1;-39176 -13490;-39132 -13437;-39104 -13363;-39006 -13432 1;-37972 -14562;-37147 -15543;-36372 -16490;-36924 -16987;-36993 -16889;-38427 -17478;-38539 -17290;-39249 -15785;-38190 -15286;-38322 -15083;-38546 -14771 1;-38845 -14441;-39195 -14385;-39526 -14240;-39555 -14221 18; -41954 -16681; -43176 -17256; -40244 -17815; -41378 -18129; -48580 -19065; -44878 -13185; -44555 -8818; -44433 -10161; -43316 -10353; -42461 -11033; -45260 -17574; -39394 -14941; -45911 -12993; -45126 -11963; -43403 -12575; -44694 -10692;-45471 -10951; -44694 -10692;-45471 -10951; -46246 -10292 1;-46692 -10241;-46881 -10163;-47248 -10421; -46226 -10294 1;-46672 -10243;-46881 -10163;-47248 -10421; -46726 -18633 1;-46535 -18376;-46332 -18132;-46023 -18213 1;-45873 -18252;-45827 -18586;-45916 -18713 1;-46086 -18956;-46220 -19123;-46469 -19285 1;-46616 -19381;-46685 -19233;-46825 -19126 1;-46988 -19001;-46974 -18891;-46862 -18719;-46726 -18633 18; -53148 -19342; -50163 -21183 1;-50397 -21251;-50641 -21246;-50722 -21328 1;-50848 -21454;-50740 -21701;-50805 -22072; -51363 -22045 1;-51337 -21665;-51156 -21402;-51520 -21294 1;-51673 -21249;-51793 -21282;-51952 -21270; -46873 -18878;-47079 -18993 1;-47880 -19821;-48796 -20443;-49921 -21094 1;-50006 -21143;-50081 -21169;-50149 -21177;-50280 -21212 1;-50474 -21252;-50654 -21260;-50722 -21328 1;-50848 -21454;-50740 -21701;-50805 -22072;-50608 -22129 1;-50209 -21962;-49815 -21806;-49266 -21471 1;-48225 -20835;-47278 -20148;-46489 -19385;-46873 -18878 18; -54040 -19058 1;-53790 -20025;-53730 -21226;-52731 -21256;-51894 -21273 1;-51763 -21275;-51654 -21255;-51520 -21294 1;-51452 -21314;-51403 -21340;-51368 -21371;-50735 -21342 1;-50841 -21472;-50742 -21714;-50805 -22072;-50527 -22095 1;-50155 -21940;-49778 -21783;-49266 -21471 1;-48225 -20835;-47278 -20148;-46489 -19385;-46426 -19324 1;-46305 -19205;-46187 -19084;-46074 -18961 1;-45992 -18871;-46088 -19155;-46136 -19267 1;-46624 -20394;-47082 -20951;-47881 -21884;-48661 -22426;-50195 -23150 1;-50558 -23303;-50765 -23573;-50869 -23903;-52155 -23626;-53655 -23121 32;-54318 -20417 32;-54292 -20176;-54040 -19058 18; -54964 -20123 1;-54378 -20475;-54137 -20770;-53464 -20890 1;-53108 -20954;-53012 -21513;-53237 -21797 1;-53697 -22378;-53853 -23078;-53923 -23816; -55247 -14022;-53555 -14772;-53555 -16185;-52403 -18191;-49507 -18156;-47972 -16185;-48460 -9921;-46559 -8317;-44744 -8282;-44238 -8003;-44053 -8151 1;-43231 -9078;-42629 -9832;-41765 -11009 1;-41363 -11556;-41571 -12114;-41124 -12625 1;-40690 -13120;-40447 -13398;-40014 -13893 1;-39570 -14400;-39000 -14271;-38546 -14771;-38221 -15224;-39196 -15749;-38306 -17564;-48024 -22030;-47739 -21718 1;-47026 -20881;-46594 -20326;-46136 -19267 1;-46097 -19176;-46026 -18971;-46045 -18946;-45916 -18713 1;-45916 -18713;-45916 -18713;-45916 -18713 1;-45827 -18586;-45873 -18252;-46023 -18213 1;-46332 -18132;-46535 -18376;-46726 -18633;-46862 -18719 1;-46897 -18772;-46922 -18819;-46935 -18863;-47148 -19053 1;-47964 -19874;-48904 -20507;-50008 -21146 1;-50144 -21225;-50244 -21237;-50333 -21218;-50776 -21374;-51368 -21371 1;-51403 -21340;-51452 -21314;-51520 -21294 1;-51654 -21255;-51763 -21275;-51894 -21273;-52731 -21256 1;-53730 -21226;-53790 -20025;-54040 -19058;-54079 -18070;-55247 -14022 18; -60253 -8930;-56576 -8757;-54109 -18084;-54306 -20354;-53591 -22994;-51864 -23562;-50827 -23784;-51039 -24823;-51208 -24889 1;-52007 -25253;-52827 -25831;-52542 -26646 1;-52332 -27245;-52112 -27665;-51530 -27919 1;-51113 -28101;-50852 -28143;-50449 -28355;-50454 -28326;-50298 -29511;-50507 -29552;-54229 -30270;-60253 -8930 18; -43370 -71742;-44697 -64381 1;-44757 -64047;-44701 -63679;-44379 -63573 1;-43332 -63229;-42293 -63202;-42521 -62336 1;-42683 -61721;-42722 -61474;-42881 -60877 1;-42949 -60622;-44162 -60932;-44848 -60982 16; -45779 -59088 1;-45545 -59803;-45089 -60357;-44050 -60251; -42916 -60004;-34103 -59131 1;-33267 -59048;-26637 -58715;-26607 -58923 1;-26530 -59457;-26517 -59760;-26478 -60298 1;-26444 -60765;-34511 -61533;-38174 -62448 1;-41523 -63284;-40242 -62974;-42780 -63826 1;-43084 -63928;-43258 -63969;-43565 -64061 1;-43938 -64173;-43931 -64630;-43862 -65013;-42675 -71538; -49851 -39984;-49193 -42560 16; -47155 -50537;-45385 -57466 1;-45175 -58288;-44487 -58851;-43643 -58763 1;-39091 -58286;-36514 -58299;-31941 -58114 1;-26794 -57906;-23897 -57749;-18751 -57975 1;-14455 -58164;-12083 -58755;-7794 -59056 1;-7639 -59067;-7543 -59215;-7549 -59370 1;-7567 -59861;-7579 -60136;-7598 -60627 1;-7606 -60835;-7795 -60960;-8003 -60975 1;-15290 -61499;-19399 -61488;-26671 -62196 1;-31338 -62650;-33987 -62812;-38571 -63801 1;-39975 -64104;-40741 -64380;-42158 -64619 1;-42725 -64714;-43105 -65264;-42997 -65828;-41887 -71281 16; -41050 -5068; -43170 -4218;-41347 -3939;-41349 -4166;-41348 -4401 32;-40090 -4398;-40049 -4462;-40038 -5377;-41569 -6163;-41678 -6033 1;-42154 -5463;-42546 -4993;-42913 -4536;-43170 -4218 18; -37385 -5713; -38841 -7440; -40321 -7514;-39088 -6613;-37903 -4898;-33647 -4849;-33684 -8217;-37496 -9216 1;-38170 -9393;-38743 -9240;-39211 -8723;-40321 -7514 18; -40321 -7514;-39088 -6613;-37903 -4898;-33647 -4849;-33684 -8217;-37496 -9216 1;-38170 -9393;-38743 -9240;-39211 -8723;-40321 -7514 18; -33191 -16173;-35659 -13212 1;-36094 -12690;-35777 -11716;-35116 -11559;-33494 -11174;-32982 -12546; -33598 -12015 1;-33408 -12018;-33259 -12355;-33266 -12767 1;-33273 -13180;-33433 -13511;-33623 -13508 1;-33813 -13505;-33962 -13168;-33955 -12756 1;-33948 -12344;-33789 -12012;-33598 -12015 18; -34844 -12607; -33684 -14593; -32920 -13915;-33046 -16218; -33191 -16173;-35659 -13212 1;-36094 -12690;-35777 -11716;-35116 -11559;-33494 -11174;-32982 -12546;-33191 -16173 18; -29588 -17825;-29640 -18610;-30565 -18837;-31743 -17511; -31873 -17634;-33217 -16153; -29478 -18734 1;-26536 -17861;-24849 -17465;-21823 -16953 1;-19826 -16615;-18684 -16421;-16659 -16465 1;-15068 -16500;-14176 -16507;-12585 -16516; -12740 -16519;-12652 -9512; -12469 -9488 32;-13591 -9473 32;-13584 -8934 32;-12462 -8949 32;-12469 -9488 50; -12836 -9661;-13795 -9648;-13787 -9074 32;-16336 -9038; -16317 -9038;-18864 -8962;-21388 -9048; -21344 -9048;-23947 -9308; -23895 -9317;-29091 -10171; -29054 -10171;-32052 -10924 32;-32163 -12676; -32277 -12837;-32080 -10937;-28995 -10123;-23814 -9210;-21075 -9037;-16214 -8963;-13771 -9062;-13771 -9580;-12661 -9728;-12735 -16464;-12958 -16514 1;-14325 -16506;-15197 -16497;-16659 -16465 1;-18684 -16421;-19826 -16615;-21823 -16953 1;-24849 -17465;-26536 -17861;-29478 -18734;-30574 -18858;-31842 -17663;-31967 -17530;-33207 -16164;-32854 -13875;-32277 -12837 18; -29649 -21177 1;-30181 -21623;-30299 -22410;-29853 -22942 1;-29400 -23483;-28593 -23531;-28052 -23078; -29014 -22795; -28601 -25821 1;-28403 -25623;-28085 -25772;-27903 -25985 1;-27735 -26182;-27698 -26480;-27895 -26648;-28601 -25821 18; -25602 -29386 1;-25100 -28995;-24388 -29031;-23978 -29518 1;-23566 -30007;-23629 -30737;-24118 -31149;-25602 -29386 18; -29649 -21177 1;-30181 -21623;-30299 -22410;-29853 -22942 1;-29400 -23483;-28593 -23531;-28052 -23078;-29649 -21177 18; -26755 -21108; -28642 -20664; -28630 -19850; -25780 -19924; -25669 -21996; -25743 -22971; -19161 -17649;-24677 -18628 32;-24754 -20460 32;-24795 -21427 32;-24814 -21871; -24881 -23263;-24939 -24638 32;-25419 -25001;-24411 -26195 1;-24163 -26521;-23890 -26600;-23491 -26682 33;-23241 -26734;-23030 -26741;-22826 -26707 33;-22611 -26672;-22403 -26590;-22165 -26464 1;-21253 -25983;-20688 -25792;-19733 -25402 32;-19653 -23507 16; -25633 -25013;-29060 -20846 1;-29403 -20430;-29324 -19601;-28800 -19473;-24822 -18496; -25646 -24962;-29048 -20845 1;-29391 -20429;-29324 -19601;-28800 -19473;-24822 -18496;-25095 -24571;-25646 -24962 18; -19251 -17640;-12799 -17590;-13045 -24212;-13628 -24213; -9097 -17121;-11639 -17097;-11817 -24135; -5812 -17182;184 -17305; -5812 -17182;-9097 -17121; -11698 -15599;-11706 -16123;20187 -16612;20199 -16116; 20200 -16058;20181 -16611;-11706 -16123;-11698 -15599;20200 -16058 18; -11452 -9948;-11499 -12750; -5411 -9302;-8446 -9191; -11452 -9948 1;-11444 -9476;-11053 -9089;-10581 -9108;-8446 -9191; -5411 -9302;-2273 -9414; -2265 -8794;-2329 -9412;-5411 -9302;-10927 -9149;-11468 -9620;-11659 -15273;-11700 -15763;-11706 -16123;19300 -16598;19274 -17649;-5812 -17182;-11712 -17088;-11820 -24414;-13021 -24415;-12799 -17590;-18968 -17638;-24727 -18519;-25116 -18568;-28800 -19473 1;-29251 -19583;-29354 -20203;-29155 -20639;-30624 -19025;-29449 -18725 1;-26525 -17858;-24839 -17463;-21823 -16953 1;-19826 -16615;-18684 -16421;-16659 -16465 1;-15114 -16499;-14229 -16507;-12722 -16515;-12496 -9464;-12464 -9132;-12462 -8949 32;-13584 -8934;-19737 -8889;-29071 -10093;-32055 -10896;-32264 -12658;-32927 -12588;-33039 -12392;-33494 -11174;-33590 -10725 1;-30417 -9918;-28143 -9253;-24465 -8831 1;-22262 -8578;-20690 -8429;-18849 -8389;-17713 -8378 1;-17289 -8379;-16844 -8384;-16370 -8395 1;-10867 -8520;-7830 -8655;-2532 -8800;-2265 -8794 18; -33460 -11197;-33546 -10728;-33832 -10786 1;-34367 -10921;-34928 -11060;-35527 -11203 1;-36449 -11422;-36664 -12869;-36033 -13576 1;-33982 -15875;-32934 -17291;-30981 -19599;-30939 -19649 1;-30910 -19683;-30880 -19717;-30851 -19752 1;-28581 -22424;-27223 -23976;-25078 -26519;-24436 -26060;-25361 -24975;-25734 -24898;-29069 -20862 1;-29188 -20718;-29225 -20498;-29246 -20303;-30500 -18913;-31783 -17655;-31985 -17511;-33207 -16164;-33404 -15917;-35659 -13212 1;-36094 -12690;-35777 -11716;-35116 -11559;-33858 -11260;-33460 -11197 18; -31834 -5407;-31818 -7837; -24246 -5308;-24238 -6517 1;-27156 -6838;-28788 -7124;-31648 -7787; -21482 -5333;-21475 -6345 1;-18092 -6010;-16102 -6231;-12703 -6195;-12728 -5270; -21482 -5333;-21475 -6345 1;-18092 -6010;-16102 -6231;-12703 -6195;-12728 -5270;-21482 -5333 18; -24246 -5308;-24238 -6517 1;-27156 -6838;-28788 -7124;-31648 -7787;-31772 -5393;-24246 -5308 18; -7394 -4905;-7383 -6484;-8857 -6406;-8869 -4926;-7394 -4905 18; -6687 -6523;-6698 -4909;-2560 -4881;-2547 -6767;-6687 -6523 18; -3374 -5800; -5829 -5824; -8572 -6142; -8674 -4922;-8667 -5923; -7394 -4905;-7383 -6484;-8857 -6406;-8869 -4926;-7394 -4905 18; -6687 -6523;-6698 -4909;-2560 -4881;-2547 -6767;-6687 -6523 18; -3579 -4319;-5745 -4334; -3535 -4315 32;-5763 -4330 32;-5765 -4035 32;-3537 -4020 32;-3535 -4315 50; -634 -7372;-615 -6837;-1994 -6794;-2007 -4892;-390 -4881;-443 -4270;-3541 -4291 32;-3644 -4316;-5755 -4330;-5930 -4294;-8847 -4315 32;-8849 -4025 32;-12771 -4052 32;-12763 -5250 32;-12727 -5303;-12703 -6195;-8857 -6400;-8869 -4926;-7394 -4905;-7383 -6484;-6687 -6523;-6698 -4909;-2560 -4881;-2547 -6767;-2562 -7288;-634 -7372 18; -2537 -6782;-2525 -7300;-2877 -7284 1;-2971 -7280;-3068 -7276;-3169 -7272 1;-7742 -7097;-10303 -6928;-14876 -6766 1;-17512 -6673;-19309 -6652;-21489 -6788;-21475 -6345 1;-18092 -6010;-16102 -6231;-12703 -6195;-8880 -6346;-8837 -6416;-7371 -6469;-7310 -6442;-6647 -6529;-2537 -6782 18; -24249 -5326;-21467 -5296;-21460 -6344;-21473 -6782;-21640 -6798 1;-22394 -6847;-23196 -6916;-24096 -7006 1;-26235 -7221;-27431 -7427;-29530 -7891 1;-32598 -8569;-34300 -9027;-37342 -9811 1;-38684 -10157;-39636 -9352;-40500 -8269 1;-41770 -6677;-42679 -5656;-43737 -4310;-43203 -4171;-41555 -6156;-40038 -5377;-40050 -4390;-40056 1678;-39380 1678;-37585 1674;-37542 -4367;-34924 -4340;-34890 283;-32787 301;-32717 -4009;-33665 -4858;-37905 -4884;-38886 -6321;-39088 -6613;-40321 -7514;-39211 -8723 1;-38743 -9240;-38170 -9393;-37496 -9216;-33684 -8217;-31964 -7805;-31633 -7797;-31531 -7760 1;-28743 -7116;-27116 -6834;-24238 -6517;-24241 -5998;-24249 -5326 18; -33657 -4822;-32767 -3983;-31982 -4036;-31982 -7857;-33683 -8250;-33657 -4822 18; -40845 -11492 1;-40225 -11480;-39757 -11539;-39340 -11998 1;-39051 -12315;-39043 -12630;-39056 -13059 1;-39061 -13245;-39102 -13537;-39279 -13479 1;-39749 -13325;-39959 -13079;-40278 -12701 1;-40287 -12691;-41321 -11501;-40845 -11492 18; -45831 -5264;-43764 -4252;-43402 -4733 1;-42480 -5882;-41634 -6848;-40500 -8269 1;-39636 -9352;-38684 -10157;-37342 -9811 1;-34300 -9027;-32598 -8569;-29530 -7891 1;-28792 -7728;-28166 -7597;-27572 -7486;-26357 -7281 1;-25676 -7179;-24964 -7093;-24096 -7006 1;-20512 -6646;-18476 -6639;-14876 -6766 1;-10303 -6928;-7742 -7097;-3169 -7272 1;-2767 -7287;-2428 -7305;-2107 -7322;-2429 -8798;-2532 -8800 1;-7830 -8655;-10867 -8520;-16370 -8395 1;-16844 -8384;-17289 -8379;-17713 -8378;-18849 -8389 1;-20690 -8429;-22262 -8578;-24465 -8831 1;-28143 -9253;-30417 -9918;-33590 -10725;-34529 -10960 1;-34851 -11040;-35183 -11121;-35527 -11203 1;-36449 -11422;-36664 -12869;-36033 -13576 1;-33982 -15875;-32934 -17291;-30981 -19599;-30939 -19649 1;-30910 -19683;-30880 -19717;-30851 -19752 1;-30422 -20257;-30026 -20721;-29651 -21160;-29787 -21310 1;-30198 -21768;-30257 -22460;-29853 -22942 1;-29400 -23483;-28593 -23531;-28052 -23078;-27897 -23208 1;-27013 -24240;-26142 -25257;-25078 -26519;-26858 -27894;-27072 -27640 1;-27381 -27273;-27637 -26966;-27882 -26674;-27810 -26544 1;-27716 -26374;-27766 -26146;-27903 -25985 1;-28085 -25772;-28403 -25623;-28601 -25821;-28713 -25691 1;-28859 -25519;-29016 -25338;-29189 -25139 1;-29432 -24890;-29725 -24742;-30036 -24675;-31916 -22235 1;-31948 -21926;-32067 -21631;-32294 -21377 1;-33405 -20134;-34276 -19075;-35104 -18054;-35785 -17212 1;-35994 -16953;-36204 -16694;-36418 -16433;-36643 -16159 1;-37341 -15314;-38092 -14431;-39006 -13432;-39104 -13363;-39065 -13188 1;-39060 -13143;-39057 -13099;-39056 -13059 1;-39043 -12630;-39051 -12315;-39340 -11998 1;-39757 -11539;-40225 -11480;-40845 -11492 1;-40854 -11492;-40863 -11493;-40872 -11494;-40978 -11361 1;-42542 -9307;-43540 -8185;-45167 -6119 1;-45376 -5854;-45561 -5623;-45733 -5404;-45831 -5264 18; -14697 -35400;-14739 -36702 1;-15267 -36706;-15707 -36662;-16044 -36257 1;-17935 -33985;-19009 -32723;-20895 -30447 1;-21220 -30055;-21132 -29631;-20974 -29147 1;-20791 -28586;-20427 -28340;-19892 -28091 1;-18650 -27513;-17891 -27316;-16560 -26992 1;-15739 -26792;-15255 -26722;-14410 -26748;-14448 -27934 16; -16199 -26890 1;-15597 -27868;-15424 -28580;-15434 -29728 1;-15448 -31429;-15445 -32384;-15521 -34083 1;-15558 -34908;-15588 -35490;-16138 -36106;-16248 -36012 1;-17931 -33992;-18975 -32761;-20640 -30754;-18888 -27662 1;-18087 -27355;-17828 -27305;-16918 -27080;-16199 -26890 18; -18889 -27653;-18925 -27727;-20640 -30754;-20785 -30580 1;-20821 -30536;-20858 -30492;-20895 -30447 1;-21220 -30055;-21132 -29631;-20974 -29147 1;-20791 -28586;-20427 -28340;-19892 -28091 1;-19714 -28008;-19546 -27933;-19385 -27865;-18889 -27653 18; -14442 -27943;-14416 -26774;-14600 -26744 1;-15222 -26735;-15655 -26787;-16236 -26916;-16156 -26961 1;-15589 -27903;-15424 -28608;-15434 -29728 1;-15448 -31429;-15445 -32384;-15521 -34083 1;-15558 -34902;-15588 -35482;-16126 -36093;-16106 -36183 1;-16085 -36208;-16065 -36232;-16044 -36257 1;-15707 -36662;-15267 -36706;-14739 -36702;-14697 -35400;-14442 -27943 18; -18010 -34661;-18441 -35030;-18624 -34819 1;-19715 -33559;-20327 -32854;-21411 -31592 1;-21767 -31182;-21718 -30722;-21535 -30214;-21483 -30126 16; -21147 -31376; -20062 -32647; -18779 -34164; -14948 -37158;-14985 -37676 1;-15532 -37676;-15924 -37718;-16354 -37380;-17465 -36214;-16983 -35844; -15988 -37249; -16982 -35846;-17063 -35905;-17465 -36214;-16354 -37380 1;-15924 -37718;-15532 -37676;-14985 -37676;-14948 -37158;-15062 -37167 1;-15548 -37168;-15927 -37049;-16262 -36663 1;-16469 -36425;-16667 -36197;-16856 -35978;-16856 -35978;-16982 -35846 18; -21465 -30142;-21464 -30207 1;-21405 -30467;-21295 -30715;-21127 -30934 1;-19872 -32416;-18992 -33475;-17982 -34663;-18400 -34995;-18441 -35030;-18624 -34819 1;-19715 -33559;-20327 -32854;-21411 -31592 1;-21756 -31195;-21721 -30750;-21552 -30261;-21465 -30142 18; -21465 -30142;-21464 -30207 1;-21405 -30467;-21295 -30715;-21127 -30934 1;-19872 -32416;-18992 -33475;-17982 -34663;-18400 -34995;-18441 -35030;-18624 -34819 1;-19715 -33559;-20327 -32854;-21411 -31592 1;-21756 -31195;-21721 -30750;-21552 -30261;-21465 -30142 18; -16982 -35846;-17063 -35905;-17465 -36214;-16354 -37380 1;-15924 -37718;-15532 -37676;-14985 -37676;-14948 -37158;-15062 -37167 1;-15548 -37168;-15927 -37049;-16262 -36663 1;-16469 -36425;-16667 -36197;-16856 -35978;-16856 -35978;-16982 -35846 18; -17585 -38895; -16610 -39068; -11308 -39203; -13059 -53996;-13096 -55797;-7050 -56587 1;-6571 -56650;-6022 -56415;-6001 -55933; -5998 -55952;-5970 -55423 32;-5812 -52381;-5302 -51735 32;-5744 -51712;-5692 -50710 32;-5192 -50736;-5145 -49817;-4890 -49830 32;-4829 -48618 32;-6186 -47680 32;-11528 -47421 32;-12367 -47964;-12910 -48729 32;-13059 -53996; -13132 -55797;-12811 -48728;-12693 -48423;-12367 -47964;-11528 -47421 32;-6186 -47680 32;-4829 -48618 32;-4890 -49830 32;-5145 -49817;-5192 -50736;-5692 -50710 32;-5744 -51712;-5302 -51735 32;-5812 -52381;-5970 -55423 32;-5993 -55849;-6094 -56229 1;-6274 -56508;-6683 -56635;-7050 -56587;-12159 -55919;-13132 -55797 18; -12978 -46766;-12176 -47708;-12354 -47956;-12367 -47964;-12910 -48729 32;-13059 -53996;-13098 -55057;-13132 -55797;-12159 -55919;-7050 -56587 1;-6683 -56635;-6274 -56508;-6094 -56229;-5993 -55849;-5970 -55423 32;-5812 -52381;-5302 -51735 32;-5744 -51712;-5692 -50710 32;-5192 -50736;-5145 -49817;-4890 -49830 32;-4832 -48675;-3591 -49001;-3692 -52975 32;-4652 -53830 32;-4844 -57930 32;-5019 -57971;-5093 -57963;-5640 -57930 1;-6432 -58156;-7335 -57987;-7975 -57468;-8516 -57398;-8805 -57361;-13228 -56791;-14016 -56720;-13936 -55747;-13812 -49813;-14417 -49826;-14442 -48654;-12978 -46766 18; -20099 -37046;-20596 -39018;-21486 -39027;-21469 -37849;-22009 -37858;-22001 -37046;-20099 -37046 18; -20102 -37047;-18997 -39977;-20424 -46501;-19930 -47537;-16574 -48573;-14280 -48623;-14304 -49758;-16426 -49782;-20991 -48376;-22546 -48302;-23060 -47148;-22764 -46908 1;-22467 -46574;-22315 -46033;-22174 -45527;-22149 -45425;-21443 -42501;-21359 -42548 1;-21082 -42591;-20838 -42523;-20770 -42241 1;-20681 -41873;-20873 -41710;-21241 -41621;-21213 -41546;-20603 -39020;-20102 -37047 18; -9345 -40125 32;-11441 -40101 32;-11656 -46411 32;-9058 -46501 32;-9039 -45951 32;-9547 -45933 32;-9345 -40125 50; -11185 -40673; -11235 -42014; -11285 -43355; -11335 -44697; -11385 -46038; -10374 -46186; -9345 -40125 32;-11435 -40108 32;-11656 -46411 32;-9058 -46501 32;-9039 -45951 32;-9547 -45933 32;-10857 -45821;-10671 -40367;-9356 -40437;-9345 -40125 50; -9537 -45182 32;-9205 -45194 32;-9028 -40150 32;-9360 -40138 32;-9537 -45182 50; -7735 -39680;-6225 -39733 1;-5520 -39758;-5057 -39855;-4450 -40182;-4241 -39824 1;-4888 -39464;-5328 -39344;-6077 -39306;-7717 -39244;-7735 -39680 18; -3110 -41146 32;-2684 -41559 32;-2362 -41227 32;-2788 -40814 32;-3110 -41146 50; -1810 -39805 32;-1384 -40218 32;-1062 -39886 32;-1488 -39473 32;-1810 -39805 50; 723 -42418 32;1149 -42831 32;1471 -42499 32;1045 -42086 32;723 -42418 50; -697 -43882 32;-271 -44295 32;51 -43963 32;-375 -43550 32;-697 -43882 50; 609 -45154;914 -44823 1;1716 -45432;2207 -45841;3193 -46049;3091 -46532 1;2171 -46395;1403 -45898;609 -45154 18; 4232 -46744 32;4852 -46779 32;4883 -46231 32;4263 -46196 32;4232 -46744 50; 1884 -43579;2180 -43259 1;2769 -43706;3019 -43977;3598 -44136;3505 -44540 1;2788 -44392;2467 -44125;1884 -43579 18; 4372 -44659 32;5795 -44739 32;5818 -44337 32;4395 -44257 32;4372 -44659 50; 8137 -44437 32;12271 -44574 32;12257 -45030 32;8123 -44893 32;8137 -44437 50; 11072 -47067 32;10566 -47058 32;10576 -46526 32;11082 -46535 32;11072 -47067 50; 12715 -47051;13975 -47077 1;13975 -46765;13668 -46571;13356 -46571 1;13051 -46571;12719 -46720;12715 -47051 18; 14924 -44716 1;14915 -45210;15222 -45485;15716 -45485 1;15952 -45485;16265 -45382;16289 -45147 1;18102 -45205;18948 -45268;20163 -45865;20345 -45493;20208 -45426 1;18946 -44809;18146 -44826;16716 -44771;14924 -44716 18; 15626 -45022; 16798 -45022; 18229 -45072; 19537 -45380; 13316 -46855; 10838 -46824; 11728 -44776; 10445 -44739; 9038 -44677; 4563 -46499; 4689 -44444; 5517 -44523; 2705 -46185; 1335 -45481; 2772 -43993; -337 -43918; 1095 -42439; -1459 -39823; -2755 -41168; -5950 -39515; -4618 -39873; -9051 -37503 1;-9063 -37893;-8813 -38277;-8423 -38280 1;-8092 -38282;-7756 -38207;-7673 -37887;-7551 -37878;-7315 -37878 1;-6526 -37878;-6023 -37866;-5326 -38236;-5157 -37917;-5248 -37869 1;-5978 -37484;-6498 -37455;-7324 -37468;-9051 -37503 18; -8323 -37815; -6744 -37710; -5697 -37885; -18238 -39474 1;-18222 -38930;-18245 -38458;-17794 -38154;-14993 -39190;-15080 -39535;-18238 -39474 18; -697 -43882 32;-271 -44295 32;51 -43963 32;-375 -43550 32;-697 -43882 50; 4372 -44659 32;5795 -44739 32;5818 -44337 32;4395 -44257 32;4372 -44659 50; -1810 -39805 32;-1384 -40218 32;-1062 -39886 32;-1488 -39473 32;-1810 -39805 50; 723 -42418 32;1149 -42831 32;1471 -42499 32;1045 -42086 32;723 -42418 50; -3110 -41146 32;-2684 -41559 32;-2362 -41227 32;-2788 -40814 32;-3110 -41146 50; 4232 -46744 32;4852 -46779 32;4883 -46231 32;4263 -46196 32;4232 -46744 50; 14924 -44716 1;14915 -45210;15222 -45485;15716 -45485 1;15952 -45485;16265 -45382;16289 -45147 1;18102 -45205;18948 -45268;20163 -45865;20345 -45493;20208 -45426 1;18946 -44809;18146 -44826;16716 -44771;14924 -44716 18; -7735 -39680;-6225 -39733 1;-5520 -39758;-5071 -39868;-4446 -40191;-4254 -39807 1;-4906 -39451;-5328 -39344;-6077 -39306;-7717 -39244;-7735 -39680 18; 16792 -46797 1;18472 -46868;19672 -47119;20802 -48364;20457 -48677;20419 -48635 1;19414 -47524;18253 -47369;16760 -47246;16792 -46797 18; -11850 -39657 1;-11858 -39194;-11825 -38701;-11363 -38677 1;-11057 -38661;-10768 -38877;-10752 -39183;-9506 -39189;-9531 -39645;-11850 -39657 18; -9051 -37503 1;-9063 -37893;-8813 -38277;-8423 -38280 1;-8092 -38282;-7756 -38207;-7673 -37887;-7551 -37878;-7315 -37878 1;-6526 -37878;-6023 -37866;-5326 -38236;-5157 -37917;-5248 -37869 1;-5978 -37484;-6498 -37455;-7324 -37468;-9051 -37503 18; 1884 -43579;2180 -43259 1;2769 -43706;3019 -43977;3598 -44136;3505 -44540 1;2788 -44392;2467 -44125;1884 -43579 18; 8137 -44437 32;12271 -44574 32;12257 -45030 32;8123 -44893 32;8137 -44437 50; 11072 -47067 32;10566 -47058 32;10576 -46526 32;11082 -46535 32;11072 -47067 50; 609 -45154;914 -44823 1;1716 -45432;2207 -45841;3193 -46049;3091 -46532 1;1962 -46299;1403 -45898;609 -45154 18; 12715 -47051;13975 -47077 1;13975 -46765;13668 -46571;13356 -46571 1;13051 -46571;12719 -46720;12715 -47051 18; 4759 -47500;11233 -47712; 10907 -53989;10984 -52169; 9631 -52097;10975 -52159;10901 -54034;9569 -53997;9631 -52097 18; 4774 -47399;4235 -47382;4174 -49233;4734 -49251;4743 -48969; 4669 -50594;4700 -49624;4170 -49607;4160 -49926;3626 -51377;3608 -51957;4638 -51975; 3630 -51985 32;3600 -52919 32;4389 -52945 32;4419 -52011 32;3630 -51985 50; 4467 -48724; 4669 -50594;4700 -49624;4170 -49607;4160 -49926;3626 -51377;3608 -51957;4638 -51975;4669 -50594 18; 4774 -47399;4235 -47382;4174 -49233;4734 -49251;4743 -48969;4774 -47399 18; 3726 -52926;3625 -56043 32;2420 -56004 32;2460 -54775 32;2503 -53432 32;1413 -53397; 12873 -47766;16624 -47801;17636 -57292;17636 -57816;14897 -58217;10727 -58479;3591 -58828;3556 -58147;-945 -58199;-4801 -57868;-4696 -53803;-3650 -52895;-3527 -48935;84 -45463;1550 -46632;3225 -47225;3190 -49546;1341 -49494;1166 -53367;2510 -53489;2440 -55966;3626 -56036;3801 -52878;4430 -52008;4665 -51965;7094 -52001;9589 -54060;10775 -54112;10706 -56607;12066 -56659;12206 -51059;12817 -50431;12873 -47766 18; 4406 -52151;4905 -52167; 3348 -49741;2756 -51178;2669 -53263; 2071 -49642;3335 -49698;3322 -49803;2756 -51178;2669 -53263;1417 -53238;1485 -51382;1966 -51400;2071 -49642 18; 11237 -47596;12693 -47596;12864 -47841;12809 -50455 32;12205 -51034 32;12085 -56721 32;10700 -56692 32;10753 -54152 32;10910 -53928;10984 -52169;10961 -51920;10996 -50844 32;4719 -50638 32;4667 -50560;4719 -49636;4170 -49610;4167 -49688;4160 -49926;3626 -51377;3608 -51957;3719 -53147;3625 -56043 32;2420 -56004 32;2460 -54775 32;2499 -53550;2672 -53186;2756 -51178;3348 -49741;3381 -47139;4220 -47398;4231 -47509;4174 -49233;4734 -49251;4742 -49000;4937 -48980;11115 -49182 32;11157 -47883 32;11237 -47596 18; -6377 -45627;-6412 -46622; -8349 -45973 32;-8368 -46525 32;-6563 -46588 32;-6544 -46035 32;-8349 -45973 50; -8348 -45949 32;-8368 -46525 32;-6563 -46588 32;-6543 -46011 32;-8348 -45949 50; -6218 -46599 1;-5340 -46837;-4839 -47062;-3930 -47037 1;-3070 -47013;-2581 -46694;-2079 -46221 1;-1637 -45805;-1154 -45309;-532 -44668;-2585 -42454; -2796 -43376;-2766 -42520;-3869 -41396 1;-4582 -40670;-5314 -40457;-6330 -40404; -6303 -40341;-7653 -40293; -6297 -40384;-6062 -40422 1;-5182 -40500;-4518 -40735;-3869 -41396;-2766 -42520;-2796 -43376;-3226 -43387;-4652 -43337 32;-4633 -42794 32;-6377 -42733 32;-6315 -40965;-6297 -40384 18; -11664 -46416;-12627 -46391;-12929 -46872;-12232 -47779;-11534 -47443;-6229 -47640;-4749 -48825;-3651 -49133;-3602 -48837;-24 -45259;-532 -44668 1;-1154 -45309;-1637 -45805;-2079 -46221 1;-2581 -46694;-3070 -47013;-3930 -47037 1;-4839 -47062;-5340 -46837;-6218 -46599;-6625 -46586;-8359 -46524;-9093 -46506;-11664 -46416 18; -2666 -42507;-2513 -42513;-532 -44668 1;-1154 -45309;-1637 -45805;-2079 -46221 1;-2581 -46694;-3070 -47013;-3930 -47037 1;-4839 -47062;-5340 -46837;-6218 -46599;-6243 -45603;-4479 -45616;-2888 -43420;-2666 -42507 18; -12037 -40098;-11438 -40086;-11671 -46463;-12541 -46432;-12325 -46012;-12251 -42897;-12726 -42829;-12702 -42070;-12251 -41984;-12037 -40098 18; -9028 -40140;-7609 -40171;-7814 -45964;-7910 -45988;-8349 -45973 32;-8368 -46525 32;-9087 -46522;-9057 -46470;-9039 -45951 32;-9547 -45933 32;-9521 -45193;-9427 -45186;-9205 -45194 32;-9046 -40673;-9028 -40140 18; -18441 -35030;-17465 -36214; -14916 -37163 1;-15484 -37193;-15889 -37092;-16262 -36663 1;-16520 -36367;-16766 -36082;-16996 -35815 16; -17979 -34668 1;-18982 -33487;-19879 -32407;-21127 -30934 1;-21942 -29869;-21404 -28123;-20152 -27640 1;-17613 -26661;-16056 -26212;-13336 -26295 1;-6784 -26495;-3118 -26594;3432 -26837 1;11347 -27130;15748 -27446;23657 -27862 1;30907 -28244;35002 -28391;42249 -28825 1;44137 -28939;45694 -28984;47011 -29197 16; -14985 -37676;-8935 -37934; -5157 -37917 1;-3783 -38532;-2890 -38767;-1792 -39796; -1384 -40218;748 -42424; 1458 -42492;2180 -43259; 3590 -44139 1;3852 -44213;4140 -44232;4412 -44248; 5705 -44311;8137 -44437; 12271 -44574;14924 -44716; 16762 -47222;13974 -47064; 12715 -47045;11067 -47063; 10566 -47058;4721 -46770; 4208 -46743 1;3783 -46722;3439 -46656;3032 -46532; 607 -45137;-263 -44284; -693 -43878;-2701 -41539; -3103 -41137 1;-3572 -40690;-3887 -40481;-4464 -40187; -7735 -39672;-9540 -39637; -11835 -39637;-15106 -39533; -12248 -36590;-6598 -36751; -13395 -35431;-13429 -36727;-12238 -36758; -6623 -36909;-5536 -36926 1;-5094 -37511;-4616 -37790;-3889 -37887 1;-3174 -37982;-2381 -37936;-2132 -37055 16; -4309 -37368; -2988 -37429; -5539 -36914 1;-5097 -37499;-4616 -37790;-3889 -37887 1;-3174 -37982;-2385 -37967;-2136 -37086;-5539 -36914 18; -5306 -35649;-5337 -36796 32;-2075 -36885 32;-384 -36930 32;738 -36960 32;696 -38508; 891 -38465;882 -38753;625 -38478;891 -38465 18; 7251 -42671;7194 -44024; 590 -38486;-385 -39381;818 -40692;1329 -40714;1306 -41256 32;2323 -41299 32;2281 -42277 32;4143 -42356 32;4106 -43227 32;5823 -43300 32;5852 -42618;8289 -42721 32;8262 -43365 32;12178 -43530 32;12452 -43216 32;12463 -42944 32;12213 -42661 32;12316 -40234 32;8367 -40068 32;8311 -41403 32;5910 -41302;5947 -40442 32;4105 -40364 32;4142 -39489 32;1404 -39373;590 -38486 18; 5855 -43316;5828 -43968;5030 -43940 1;3858 -43904;3096 -43562;2291 -42710;-635 -39611;-385 -39381 16; 13338 -42355;13264 -44234; 8171 -43867;13084 -44029; 8225 -43394;8212 -43699; 17906 -43303;17857 -44401;17584 -44397;13451 -44204;12702 -44016;8227 -43869;8213 -43677;8225 -43394;8282 -42899;8289 -42721 32;5852 -42618;5850 -43433;5828 -43968;5030 -43940 1;3858 -43904;3096 -43562;2291 -42710;-635 -39611;-385 -39381;740 -38461;9875 -38907;18092 -39178;17906 -43303 18; 21734 -47136 1;21176 -46564;20699 -46153;20133 -45851 16; 20652 -45168;20359 -45508;20094 -45371 1;18897 -44810;18102 -44824;16716 -44771;14924 -44716;14545 -44695;12598 -44592;12271 -44574;11843 -44560;8139 -44437;7904 -44425;5945 -44323;5818 -44342;5308 -44308;4395 -44257 32;4260 -44238 1;4115 -44229;3967 -44215;3824 -44191;3595 -44147;3468 -44097 1;2978 -43934;2723 -43671;2180 -43259;2021 -43090;1458 -42492;1413 -42443;1045 -42086 32;741 -42400;472 -42138;-1243 -40364;-1407 -40195;-1140 -39967;-1062 -39886 32;-619 -39628;2291 -42710 1;2438 -42865;2583 -43003;2729 -43126;2788 -43174 1;3428 -43690;4100 -43911;5030 -43940;5828 -43968;5855 -43316;5851 -43147;5852 -42618;8289 -42721 32;8282 -42899;8243 -43236;8216 -43605;8212 -43699;8244 -43869;13066 -44028;13488 -44206;17811 -44408 1;18043 -44409;18254 -44420;18453 -44441;18394 -44435 1;19159 -44506;19730 -44717;20496 -45082;20652 -45168 18; -1049 -39908;-586 -39655;-601 -39579;-385 -39381;-292 -39296;590 -38486;705 -38182;738 -36960;-2126 -36883;-2165 -37178 1;-2442 -37968;-3200 -37979;-3889 -37887 1;-4616 -37790;-5094 -37511;-5536 -36926;-5645 -36924;-5872 -36921;-6623 -36909;-6999 -36899;-11984 -36765;-12476 -36752;-13429 -36727;-13395 -35445;-13566 -35407;-14671 -35372;-14703 -35591;-14739 -36702 1;-14791 -36702;-14842 -36702;-14892 -36701;-14951 -37194;-14985 -37676;-14840 -37676;-14710 -37688;-8965 -37933;-9000 -37827 1;-9036 -37726;-9054 -37615;-9051 -37503;-7324 -37468 1;-6498 -37455;-5978 -37484;-5248 -37869;-5157 -37917;-4807 -38070 1;-3628 -38578;-2797 -38856;-1798 -39790;-1714 -39706;-1488 -39473 32;-1104 -39845;-1049 -39908 18; -14856 -36713;-14917 -37169;-15040 -37165;-15062 -37167 1;-15548 -37168;-15927 -37049;-16262 -36663 1;-16469 -36425;-16667 -36197;-16856 -35978;-16856 -35978;-16982 -35846;-17063 -35905;-17465 -36214;-17595 -36056;-18441 -35030;-18368 -34969;-17982 -34663 1;-18136 -34482;-18287 -34304;-18436 -34127;-18536 -34009 1;-19333 -33066;-20106 -32140;-21127 -30934 1;-21295 -30715;-21405 -30467;-21464 -30207;-21465 -30142;-21507 -29928 1;-21554 -29374;-21387 -28795;-21054 -28343;-20822 -28810 1;-20882 -28909;-20932 -29020;-20974 -29147 1;-21132 -29631;-21220 -30055;-20895 -30447 1;-19009 -32723;-17935 -33985;-16044 -36257 1;-15769 -36587;-15426 -36677;-15022 -36697;-14856 -36713 18; 40966 -29419;39815 -29350;39756 -30336; 38413 -30279;38455 -29302 32;32934 -29006 32;32883 -29949; 31530 -28877;26976 -28659; 31879 -30064;31284 -30039;31326 -29045; 26688 -29706;27142 -29725;27178 -28858; 25073 -29636;23841 -29596 32;24344 -28442 32;22643 -28347 32;20940 -28251 32;16928 -28063; 31403 -26255;40520 -26762 32;40545 -26244; 16415 -25363;28331 -26070; 28331 -26070;31428 -26256; 17689 -41740;18800 -28218;19144 -28244;22701 -28402 32;24368 -28476 32;23859 -29603 32;25083 -29654;27015 -29720;27142 -29725;27178 -28858;27212 -28670;31324 -28885;31318 -29231;31284 -30039;31879 -30064;32829 -30106;32898 -29676;32934 -29006 32;38455 -29302 32;38413 -30279;39714 -30340;39768 -30128;39815 -29350;40606 -29397;41333 -29440 1;43384 -29555;44397 -29548;46564 -29811 1;47306 -29901;48124 -30779;48033 -31521;47402 -35838;45990 -35788;40585 -37592;32612 -38552;32488 -42204;17689 -41740 18; 32590 -26296;32956 -19875;34352 -17328;34840 -17280;43698 -17174;44211 -17168;46009 -17146 32;47009 -19049 32;45405 -18950;45186 -20624;45140 -21429;48067 -21595 1;48313 -22210;48544 -22582;48729 -23190;48854 -23649 1;49078 -24542;49113 -25200;48888 -26170 1;48735 -26830;48129 -27185;47453 -27144;43597 -26910;43190 -27075;40691 -26962;32590 -26296 18; 16928 -28063;13681 -27918; 11797 -28708;11845 -27757;6135 -27523; 3659 -27432;-1897 -27222;-1994 -29579; 13272 -28911;13831 -28938;13871 -28115; 6135 -27523;3633 -27423; -1994 -29579;-2083 -32451; 9144 -35645;9084 -37284 32;834 -36972; -2367 -35210;-2387 -35642 1;-2398 -35882;-2188 -36065;-1948 -36074;8952 -36476; -3512 -36529;8902 -36955; 9038 -36475;8865 -36473;-1948 -36074 1;-2188 -36065;-2398 -35882;-2387 -35642;-2367 -35210;-2062 -31772;-1994 -29579;-1981 -29261;-1897 -27222;3659 -27432;9256 -27651;11845 -27757;11797 -28708;13476 -28921;13831 -28938;13871 -28115;14013 -27933;16928 -28063;17293 -28079;18877 -28150;18062 -39291;720 -38628;754 -36988;9007 -37215;9038 -36475 18; -5150 -30499;-5055 -26948; -5055 -26957;-1904 -27036;-2184 -32427;-2356 -35178;-2376 -35409;-2387 -35642 1;-2398 -35882;-2188 -36065;-1948 -36074;8952 -36476;8957 -37214;-5292 -36720;-5267 -35647;-4138 -35688 32;-4000 -30531 32;-5155 -30501;-5055 -26957 18; -13284 -27985;-13249 -26876;-6753 -27083; -13306 -26880;-13395 -35431;-13429 -36727;-12500 -36751;-6623 -36909;-5709 -36923;-5499 -36895;-5246 -26968;-6868 -26898;-13306 -26880 18; 40694 -26924;43177 -27046; 25813 -29527;25862 -28345;24550 -28291;24006 -29490;25813 -29527 18; 16625 -25196;16713 -23137; 8268 -23737;8208 -24929;15460 -25296;15492 -24493; 15666 -23063 32;16516 -23096 32;16497 -23584 32;15647 -23551 32;15666 -23063 50; 15716 -23555;16496 -23585; 1521 -23913 32;6983 -24126 32;6948 -25037 32;1486 -24824 32;1521 -23913 50; 1521 -23913 32;6983 -24126 32;6948 -25037 32;1486 -24824 32;1521 -23913 50; -4545 -23725;-4573 -24447;-901 -24589; -937 -24743;66 -24782;116 -23477; -12019 -24246;-6985 -24369; -5938 -23255;-5989 -24559;-7028 -24532; -13619 -24380 1;-14832 -24387;-15513 -24566;-16705 -24790 1;-17842 -25004;-18505 -25145;-19574 -25507; -24679 -18615;-23641 -18353;-19083 -17656;-18677 -17636;-12799 -17590;-13021 -23630;-13033 -23900;-13045 -24212;-13486 -24283;-13653 -24380 1;-14845 -24390;-15524 -24568;-16705 -24790 1;-17842 -25004;-18505 -25145;-19574 -25507;-19934 -25483 1;-20771 -25821;-21319 -26018;-22165 -26464 1;-22403 -26590;-22611 -26672;-22826 -26707 33;-23030 -26741;-23241 -26734;-23491 -26682 33;-23890 -26600;-24163 -26521;-24411 -26195;-25419 -25001;-24939 -24638 32;-24903 -23775;-24679 -18615 18; -4660 -24591;77 -24765;112 -23457;-4625 -23291;-4660 -24591 18; -24627 -26116;-25099 -26470;-24997 -26591;-24961 -26630 1;-24936 -26660;-24910 -26690;-24885 -26720 1;-24148 -27589;-22980 -27466;-21919 -27027;-21397 -26783 1;-21317 -26742;-21238 -26699;-21161 -26655 1;-19909 -25945;-19105 -25700;-17699 -25390 1;-15676 -24944;-14501 -24737;-12430 -24779 1;-6466 -24899;-3124 -25036;2837 -25251 1;6418 -25380;8822 -25466;11809 -25615;12364 -25643 1;13207 -25687;14101 -25735;15084 -25791 1;19813 -26061;22464 -26223;27192 -26506 1;28535 -26586;29774 -26661;30945 -26731;31366 -26757 1;36602 -27072;40544 -27315;46629 -27693 1;46957 -27713;47140 -27725;47468 -27745 1;48378 -27802;49320 -27302;49501 -26594 1;49551 -26398;49593 -26209;49627 -26026;48952 -25865 1;48934 -25964;48912 -26065;48888 -26170 1;48735 -26830;48129 -27185;47453 -27144;43597 -26910;43190 -27075;40691 -26962;39040 -26826;16487 -25516;16532 -23591;15656 -23560;15539 -25435;8103 -25046;8164 -23701;6992 -23652;6944 -25025;1473 -24813;1535 -23549;116 -23487;61 -24770;-4739 -24595;-4653 -23293;-5917 -23250;-5985 -24570;-12008 -24378;-12925 -24379;-13616 -24372;-13970 -24387 1;-14974 -24423;-15633 -24589;-16705 -24790 1;-17842 -25004;-18505 -25145;-19574 -25507;-20346 -25648 1;-20970 -25897;-21465 -26095;-22165 -26464 1;-22403 -26590;-22611 -26672;-22826 -26707 33;-23030 -26741;-23241 -26734;-23491 -26682 33;-23839 -26610;-24092 -26541;-24315 -26308;-24627 -26116 18; -21114 -28419;-21995 -27058;-21743 -26945;-21397 -26783 1;-21317 -26742;-21238 -26699;-21161 -26655 1;-19909 -25945;-19105 -25700;-17699 -25390 1;-15748 -24960;-14585 -24752;-12647 -24775;-12286 -24782 1;-8776 -24853;-6172 -24931;-3383 -25027;-1831 -25082 1;-411 -25132;1102 -25188;2837 -25251 1;7621 -25423;10304 -25519;15084 -25791 1;19813 -26061;22464 -26223;27192 -26506 1;34783 -26960;39039 -27222;46629 -27693 1;46957 -27713;47140 -27725;47468 -27745 1;47480 -27746;47492 -27746;47503 -27747;47189 -29199;47031 -29190;46440 -29116 1;45249 -28970;43871 -28923;42249 -28825 1;35002 -28391;30907 -28244;23657 -27862 1;15748 -27446;11347 -27130;3432 -26837 1;-3118 -26594;-6784 -26495;-13336 -26295 1;-16056 -26212;-17613 -26661;-20152 -27640 1;-20437 -27750;-20685 -27926;-20889 -28144;-21114 -28419 18; -20843 -28855;-21096 -28380;-20960 -28230;-20889 -28144 1;-20685 -27926;-20437 -27750;-20152 -27640 1;-17613 -26661;-16056 -26212;-13336 -26295 1;-6784 -26495;-3118 -26594;3432 -26837 1;11347 -27130;15748 -27446;23657 -27862 1;30907 -28244;35002 -28391;42249 -28825 1;43871 -28923;45249 -28970;46440 -29116;47031 -29190;47109 -29194;46974 -29828;39725 -29357;39655 -30343;38425 -30299;38415 -29362;32888 -28992;32814 -30053;31284 -29979;31358 -28869;27213 -28647;27139 -29634;25786 -29569;25815 -29474;25862 -28345;24550 -28291;13895 -27898;13821 -28860;11798 -28737;11823 -27824;-2034 -27044;-5135 -26973;-6808 -26902;-13310 -26933;-13397 -27933;-14483 -27933;-14442 -27748;-14410 -26748 1;-15255 -26722;-15739 -26792;-16560 -26992 1;-17891 -27316;-18650 -27513;-19892 -28091 1;-20211 -28239;-20469 -28387;-20664 -28600;-20843 -28855 18; -6756 -26916;-5235 -26978; 16111 -17576;184 -17305; 16111 -17576;19464 -17649; 19322 -17713;18844 -25412;16661 -25326;16685 -23093;15661 -23019;15464 -25276;8144 -24943;8218 -23561;-5893 -23171;-5944 -23419;-5989 -24559;-7028 -24532;-7588 -24354;-11791 -24252;-11810 -23721;-11712 -17088;-9749 -17119;19322 -17713 18; 18324 85141; 18594 86902;18799 86863;18821 86740;18913 85374;18728 85250;18783 84698;17871 84133;17800 84154;18594 86902 18; 17784 84152;17863 84126;18063 82202;17933 82198 1;17638 82212;17312 82407;17382 82681 1;17488 83097;17563 83366;17664 83720;17784 84152 18; 16683 73193; 18027 77177;18254 76950;18254 76662;18547 76662;18547 77446;19337 77854;19143 79264;19807 79596;19516 82389;18069 82238;17871 84133;18783 84698;18728 85250;18913 85374;18814 86838;19251 87126;19238 87468;22254 89592;22406 87338;22180 87151;22462 86817;24369 88209;24535 85693;24097 85391;23803 85215;24075 81810;24544 81412;24829 81698;25317 81307;25063 80971;25543 80564;27566 80724;27870 81084;28283 81575;29954 80965;30419 75813;29690 75333;30120 71093;29810 71346;29291 71764;28899 71302;29117 71128;28777 70709;29038 68031;28480 67577;28562 66826;24829 66449;24752 67206;25234 67256;25055 69582;23412 70816;23013 70770;21935 71688;17442 71190;16846 76567;18027 77177 18; 18560 77494;18569 76629;18252 76629;18252 76941;17998 77203;18560 77494 18; 16853 76479;16403 77197; 18563 77489;18307 77985;18134 77896 16; 17651 77646;16973 77287 16; 18134 77896;17651 77646 16; 16755 77327;16128 78056;15312 78362; 16529 77624;16738 77354; 16681 76380;16838 76130;16825 76495;16681 76380 18; 16918 78291; 17254 80581; 17557 77793;17366 78904;16941 78922;16996 80156;17625 80144;17607 81050;16916 81075 1;16481 80026;16176 79446;15491 78540; 9833 73908;11448 73460;11648 74263;9957 74783;9833 73908 18; 13396 72790;12463 74400;12827 75871 1;13882 77033;14527 77394;15238 78199; 16945 78961 1;16731 78916;16589 78873;16384 78949 1;16204 79016;16099 79067;15964 79202;16022 79294 1;16372 79839;16612 80343;16916 81075;17607 81050;17625 80144;16996 80156;16955 79228;16945 78961 18; 15958 79220;15991 79176 1;16113 79060;16216 79012;16384 78949 1;16589 78873;16731 78916;16945 78961;16942 78950;16941 78922;17366 78904;17557 77793;16945 77481;16871 77512;15477 78548;15755 78875;15958 79220 18; 19160 82357;19465 79428;19795 79606;19511 82388;19160 82357 18; 16557 77244;16893 77475;16931 77442;18222 78059;18493 77466;16933 76652;16557 77244 18; 16051 77854 1;15786 77476;15595 77211;15626 76750 1;15646 76453;15985 76317;15891 76034 1;15802 75768;15721 75619;15533 75411 1;15481 75353;15616 75298;15694 75294 1;16042 75278;16399 75135;16428 74788 1;16454 74472;16090 74489;15814 74333 1;15588 74205;15507 73894;15521 73635 1;15526 73539;15522 73484;15527 73388 1;15535 73233;15742 73227;15897 73240 1;16393 73281;16695 73324;17187 73400;16862 76349;16542 77251;16051 77854 18; 17960 71223;17953 71128 1;17932 70943;17845 70816;17791 70607 1;17679 70178;17232 70078;16788 70074 1;16716 70073;16646 70077;16580 70085;16493 70111 1;16240 70326;16102 70646;15937 70952 1;15843 71125;15990 71334;16177 71396 1;16623 71545;16901 71518;17368 71568;17417 71204;17960 71223 18; 17880 70853 1;17847 70781;17816 70703;17791 70607 1;17679 70178;17232 70078;16788 70074 1;16716 70073;16646 70077;16580 70085;16493 70111 1;16240 70326;16102 70646;15937 70952 1;15843 71125;15990 71334;16177 71396 1;16462 71491;16679 71515;16920 71532; 13777 74503; 13672 73578; 15228 78164;16048 77885;16066 77824;15974 77745 1;15750 77421;15598 77165;15626 76750 1;15646 76453;15985 76317;15891 76034 1;15802 75768;15721 75619;15533 75411 1;15481 75353;15616 75298;15694 75294 1;16042 75278;16399 75135;16428 74788 1;16454 74472;16090 74489;15814 74333 1;15588 74205;15507 73894;15521 73635 1;15526 73539;15522 73484;15527 73388 1;15535 73233;15742 73227;15897 73240 1;16393 73281;16695 73324;17187 73400;17409 71586;18334 71324;17636 68890;15804 68489;12480 74421;12485 74488;12827 75871 1;13702 76834;14294 77247;14877 77819;15228 78164 18; 15756 73237 1;15639 73243;15533 73278;15527 73388 1;15522 73484;15526 73539;15521 73635 1;15507 73894;15588 74205;15814 74333 1;16090 74489;16454 74472;16428 74788 1;16399 75135;16042 75278;15694 75294 1;15616 75298;15481 75353;15533 75411 1;15721 75619;15802 75768;15891 76034 1;15924 76133;15904 76214;15863 76290;15779 76418 1;15709 76515;15635 76616;15626 76750 1;15604 77074;15692 77301;15838 77538; 18112 87067;18605 86980;18532 86728;17749 84015 1;17599 83496;17516 83204;17382 82681 1;17304 82374;17721 82167;18036 82201;19172 82321;19459 79460;19307 79346;19143 79264;19337 77854;18727 77539;18125 78047;17549 77742;17545 77863;17366 78904;16941 78922;16996 80156;17625 80144;17607 81050;16916 81075 1;16499 80069;16201 79495;15574 78651;15467 78569;15249 78183;14467 77433;14304 77294 1;13885 76925;13421 76525;12827 75871;12485 74488;12480 74421;13251 73046;13336 72872;12497 73226;11450 73462;11467 73580 1;11609 74200;11738 74557;11990 75192 1;12427 76294;13019 76819;13903 77609 1;14527 78167;14979 78637;15361 79181;15744 79794 1;15899 80072;16047 80377;16198 80724 1;16620 81696;16768 82279;17055 83300;17802 85959;18112 87067 18; 9976 74799;9856 73929; 15169 83808;15108 83574 1;15053 83396;14996 83215;14937 83031 1;14523 81520;14092 80415;12996 79307 1;12572 78878;12107 78613;11605 78499;11278 78457;11273 78448 1;10859 78407;10421 78463;9961 78609 1;9707 78689;9461 78766;9221 78840;8833 78967 1;6117 79830;4672 80298;1528 81109 1;788 81300;67 81371;-654 81380;-865 81371 1;-1759 81408;-2585 81300;-3612 81227;-4610 81171 1;-9027 80952;-11972 80704;-15838 80387;-16289 80345;-16067 78210;-14771 78297;-14302 77878;-11554 78060;-5967 78436;-4750 78510;-3064 78621 1;-376 78799;1780 78179;4798 77539;5043 77731;5258 77690;5327 77446;6968 77101;7173 77317;7966 77096;8235 77011;9245 76774;9369 76725 1;9569 76490;9723 76263;9829 76027 1;9974 75706;10030 75367;9994 74970;9981 74776;11556 74291;11642 74264;11835 74794 1;11881 74916;11932 75047;11990 75191 1;12427 76294;13019 76819;13903 77609 1;15030 78616;15596 79338;16198 80724 1;16595 81638;16749 82208;17005 83121;17088 83406;15169 83808 18; 3350 72787;3089 71985; 6744 67628 1;5478 69525;4678 70806;3529 72493 1;3161 73033;2622 73572;2405 74532 16; 6438 66993;1705 67411;1616 66068 1;328 65890;-583 65876;-1479 64420 16; 294 67806; -520 68818; 750 67880 1;759 68265;764 68482;773 68867 1;778 69090;910 69326;1133 69311 1;1316 69299;1422 69111;1404 68929 1;1337 68261;1301 67887;1235 67219 1;1222 67085;1082 67024;948 67029 1;813 67034;747 67178;750 67313 1;755 67534;745 67659;750 67880 18; -497 65827;-2322 65565 32;-2381 65974 32;-555 66234 48; -2331 65765;-526 66026; -936 66347;1238 66585; -18672 64224;-1511 66683 32;-1637 67560; -930 66459;-1318 66533;-3067 66276;-2431 65984;-895 66215;-930 66459 18; -916 65746 1;-1091 65505;-1268 65364;-1566 65345 1;-2075 65313;-2359 65545;-2865 65479 1;-3331 65418;-3593 65384;-4059 65323 1;-4241 65299;-4348 65354;-4519 65423 1;-4841 65553;-4957 65599;-5279 65729; -9068 65289;-5262 65835; -4163 65727; -3125 65849; -5330 65981;-5299 65737 1;-5004 65618;-4826 65547;-4519 65423 1;-4348 65354;-4241 65299;-4059 65323 1;-3593 65384;-3331 65418;-2865 65479 1;-2359 65545;-2075 65313;-1566 65345 1;-1268 65364;-1091 65505;-916 65746;-1068 65745;-2322 65565 32;-2381 65974 32;-2502 66017;-3067 66276;-5330 65981 18; -5330 65981;-5299 65737 1;-5004 65618;-4826 65547;-4519 65423 1;-4348 65354;-4241 65299;-4059 65323 1;-3593 65384;-3331 65418;-2865 65479 1;-2359 65545;-2075 65313;-1566 65345 1;-1268 65364;-1091 65505;-916 65746;-1068 65745;-2322 65565 32;-2381 65974 32;-2502 66017;-3067 66276;-5330 65981 18; 1701 67411;1239 67170;1267 66445;-587 66264; 750 67880 1;759 68265;764 68482;773 68867 1;778 69090;910 69326;1133 69311 1;1316 69299;1422 69111;1404 68929 1;1337 68261;1301 67887;1235 67219 1;1222 67085;1082 67024;948 67029 33;813 67034;747 67178;750 67313 1;755 67534;745 67659;750 67880 18; 2827 57777;2495 57736;2024 64652 1;1382 64562;1022 64512;381 64420 1;218 64397;105 64242;132 64080 1;155 63946;270 63868;405 63883;1068 63961;1207 62570;430 62492 1;319 62481;240 62363;251 62252 1;263 62135;373 61988;490 62000;1247 62078;1391 60507;561 60431 1;441 60420;356 60279;367 60159 1;379 60029;493 59934;623 59946;1436 60021;1623 57997;755 57917 1;661 57908;597 57751;606 57657 1;616 57545;721 57417;833 57428;1669 57510;1828 55899;825 55757;944 54838;7559 55851;7398 56776;5979 56517 16; 2017 58896; 1860 61164; 1703 63476; 632 64179; 1138 57714; 1608 58120;2453 58200; 2501 57805;1681 57417; 1380 55379; 2651 55564; 4082 55749; 5611 55984; 7067 56206; 7178 54577; 6499 56592;6316 55658; 854 55759;1797 55896;1790 56283;1669 57510;833 57428 1;721 57417;616 57545;606 57657 1;597 57751;661 57908;755 57917;1623 57997;1436 60021;623 59946 1;493 59934;379 60029;367 60159 1;356 60279;441 60420;561 60431;1391 60507;1247 62078;490 62000 1;373 61988;263 62135;251 62252 1;240 62363;319 62481;430 62492;1207 62570;1068 63961;405 63883 1;270 63868;155 63946;132 64080 1;105 64242;218 64397;381 64420 1;1022 64512;1382 64562;2024 64652;2495 57736;2827 57777;2981 57589;3143 56295 32;5876 56637;5983 56525;6080 56537;7410 56793;7420 56794;7429 56595;7559 55851;944 54838;878 55504;854 55759 18; 3142 56306;2988 56083;1790 55944;1790 56283;1669 57510;833 57428 1;721 57417;616 57545;606 57657 1;597 57751;661 57908;755 57917;1623 57997;1436 60021;623 59946 1;493 59934;379 60029;367 60159 1;356 60279;441 60420;561 60431;1391 60507;1247 62078;490 62000 1;373 61988;263 62135;251 62252 1;240 62363;319 62481;430 62492;1207 62570;1068 63961;405 63883 1;270 63868;155 63946;132 64080 1;105 64242;218 64397;381 64420 1;1022 64512;1382 64562;2024 64652;2495 57736;2827 57777;2981 57589;3142 56306 18; 3786 58376; 4902 58184;5518 58261; 6735 58715;2491 58154; 2475 58136;6763 58733;6841 58358;6200 58197;5773 58162;2488 57743;2475 58136 18; -3615 63435;-2473 63409; -3306 61156;-2337 61134; -2978 58780;-2096 58760; -2664 56503;-1844 56484; -2129 64513; -1839 59648; -2358 59384;-2386 59838;-2256 59855;-1493 59903 1;-1364 59911;-1253 59813;-1245 59684 1;-1237 59553;-1337 59439;-1468 59431;-2230 59387;-2358 59384 18; -2511 64116 1;-2705 64300;-2774 64572;-2636 64802 1;-2541 64960;-2447 65049;-2263 65049;-2125 64975 1;-1929 64879;-1748 64733;-1544 64566 1;-1493 64524;-1495 64472;-1491 64406 1;-1483 64277;-1581 64165;-1710 64157;-2333 64117;-2511 64116 18; -3678 64959;-3547 64981;-2791 65081 1;-2617 65104;-2466 65092;-2326 65052;-2413 65023 1;-2506 64986;-2571 64910;-2636 64802 1;-2773 64573;-2706 64303;-2514 64119;-2631 64092;-2629 63979;-2511 62164;-1637 62216 1;-1508 62224;-1397 62126;-1389 61997 1;-1381 61866;-1481 61752;-1612 61744;-2482 61692;-2365 59848;-2339 59389;-2320 59129;-2317 59077;-2110 57553;-1209 57608 1;-1080 57616;-969 57518;-961 57389 1;-953 57258;-1053 57144;-1184 57136;-2054 57083;-1899 55888;-2427 55820;-2530 56608;-3589 64220;-3678 64959 18; -3508 63641;-3692 64962;-2791 65081 1;-2269 65150;-1951 64900;-1544 64566 1;-1493 64524;-1495 64472;-1491 64406 1;-1483 64277;-1581 64165;-1710 64157;-2637 64098;-2604 63597 16; -3192 61366;-3450 63225 16; -2578 63197;-2511 62164;-1637 62216 1;-1508 62224;-1397 62126;-1389 61997 1;-1381 61866;-1481 61752;-1612 61744;-2482 61692;-2460 61351 16; -2862 58991;-3133 60942 16; -2434 60930;-2365 59848;-1493 59903 1;-1364 59911;-1253 59813;-1245 59684 1;-1237 59553;-1337 59439;-1468 59431;-2336 59381;-2317 59077;-2304 58980 16; -2544 56708;-2801 58554 16; -2246 58553;-2110 57553;-1209 57608 1;-1080 57616;-969 57518;-961 57389 1;-953 57258;-1053 57144;-1184 57136;-2054 57083;-2006 56714 16; -1949 56276;-1860 55585;-1837 55406;-76 55636;46 54715;-2222 54391;-2484 56277 16; -1318 54585 1;-1550 54653;-1683 54897;-1615 55129 1;-1547 55362;-1303 55495;-1071 55427 1;-838 55358;-705 55115;-773 54882 1;-842 54650;-1085 54517;-1318 54585 18; -1183 55443;-1183 54585; -1632 55011;-748 55033; -395 55166; -1982 54952; -1949 56276;-1860 55585;-1837 55410;-76 55640;46 54715;-2222 54391;-2484 56277;-1949 56276 18; 8671 66894;8622 66197; 10327 66715;10275 66018; -2351 73726;-2177 73766;3648 74755;4795 74969;5381 75064;5649 73393;5778 73414;7729 73541;7675 73228 1;7236 71006;6848 68987;6705 67411;6429 66994;1705 67420;1223 67161;1240 66694;-963 66443;-1333 66542;-2351 73726 18; 7718 73514;8761 73780;8696 73525 1;8543 72569;8409 71989;8243 70987;8152 71012;8150 71012 32;8075 70427 32;8140 70419;8133 70362 1;7861 69293;7808 68650;7679 67549;7620 67540;7595 67542 32;7551 66973 32;8677 66885 32;8680 66920;8619 66181;8446 66208;7497 66282 32;7453 65713 32;7575 65703;7548 65610;7516 64956 1;7508 64736;7502 64521;7495 64258;7500 64152;7437 64121;7418 64121 32;7413 63529 32;7505 63528;7502 63393 1;7503 62778;7578 62391;7619 61781;7634 61675;7648 61676;7554 61668;7544 61667 32;7591 61131 32;7701 61141;7705 61053 1;7765 60256;7899 59787;8037 59012;8054 58893;8073 58896;7974 58880;7947 58876;8015 58374;7989 58356;7045 58288;6974 58486;6835 58907 1;6548 60792;6520 62069;6469 64095;6513 66106;6579 67132;6713 67502 1;6861 69047;7236 71004;7661 73154;7718 73514 18; 6536 65965;6511 64029;6530 61703;6795 59359;6745 58711;2468 58142;2436 58603;2024 64652 1;1382 64562;1022 64512;381 64420 1;218 64397;105 64242;132 64080 1;155 63946;270 63868;405 63883;1068 63961;1207 62570;430 62492 1;319 62481;240 62363;251 62252 1;263 62135;373 61988;490 62000;1247 62078;1391 60507;561 60431 1;441 60420;356 60279;367 60159 1;379 60029;493 59934;623 59946;1436 60021;1623 57997;755 57917 1;661 57908;597 57751;606 57657 1;616 57545;721 57417;833 57428;1669 57510;1828 55899;825 55757;943 54849;42 54747;-76 55640;-1837 55410;-1860 55585;-1931 56132;-1911 56234;-1978 56739;-2018 56805;-2054 57083;-1184 57136 1;-1053 57144;-953 57258;-961 57389 1;-969 57518;-1080 57616;-1209 57608;-2110 57553;-2215 58324;-2211 58518;-2281 59015;-2318 59092;-2336 59381;-1468 59431 1;-1337 59439;-1237 59553;-1245 59684 1;-1253 59813;-1364 59911;-1493 59903;-2365 59848;-2424 60771;-2404 60884;-2432 61373;-2467 61461;-2482 61692;-1612 61744 1;-1481 61752;-1381 61866;-1389 61997 1;-1397 62126;-1508 62224;-1637 62216;-2511 62164;-2566 63007;-2545 63160;-2579 63662;-2611 63701;-2637 64098;-1710 64157 1;-1581 64165;-1483 64277;-1491 64406 1;-1492 64423;-1493 64438;-1494 64453;-1430 64498 1;-550 65877;351 65893;1616 66068;1705 67420;6438 66993;6536 65965 18; -12714 57130;-12136 57205;-11975 55963;-11448 56031;-11262 56273;-11848 60755;-12141 60980;-12651 60913;-12498 59747;-13035 59677; -12767 57589 1;-12367 57659;-12069 57863;-11729 57641;-11412 57423;-11262 56273;-11448 56031;-11975 55963;-12136 57205;-12714 57130;-12767 57589 18; -12305 58060; -12418 58941; -12497 59743 1;-12509 59534;-12496 59302;-12697 59246 1;-12801 59217;-12862 59208;-12968 59185;-13008 59267;-13057 59646;-12497 59743 18; -12532 60031;-11712 59604;-11710 59701;-11848 60755;-12141 60980;-12651 60913;-12562 60237;-12532 60031 18; -10761 60759; -10650 59723; -10513 58378; -10415 57569; -10292 56776; -9588 60130 1;-9492 60145;-9394 60091;-9379 59995 1;-9198 58835;-9123 58181;-8947 57021 1;-8913 56794;-9063 56685;-9292 56676 1;-9518 56667;-9712 56834;-9737 57058 1;-9771 57362;-9812 57520;-9835 57824 1;-9842 57921;-9792 57978;-9712 58033 1;-9552 58144;-9429 58197;-9453 58391 1;-9525 58970;-9540 59298;-9650 59871 1;-9670 59973;-9691 60114;-9588 60130 18; -9553 60132 33;-9470 60146;-9392 60079;-9379 59995 1;-9198 58835;-9123 58181;-8947 57021 1;-8913 56794;-9063 56685;-9292 56676 1;-9518 56667;-9712 56834;-9737 57058 1;-9771 57362;-9812 57520;-9835 57824 1;-9842 57921;-9792 57978;-9712 58033 1;-9552 58144;-9429 58197;-9453 58391 1;-9525 58970;-9540 59298;-9650 59871 1;-9671 59980;-9690 60109;-9553 60132 50; -12714 57130;-12136 57205;-11975 55963;-11448 56031;-11262 56273;-11848 60755;-12141 60980;-12651 60913;-12498 59747;-13035 59677;-12714 57130 18; -3692 64949;-3766 65361 1;-3462 65401;-3224 65432;-2865 65479 1;-2359 65545;-2075 65313;-1566 65345 1;-1268 65364;-1091 65505;-916 65746;-1115 65738;-2322 65565 32;-2381 65974 32;-621 66225;-524 66270;1267 66445;1239 67170;1697 67419;1697 67299;1616 66068 1;328 65890;-583 65876;-1479 64420;-1508 64519 1;-1515 64536;-1526 64551;-1544 64566 1;-1748 64733;-1929 64879;-2125 64975;-2263 65049;-2403 65071 1;-2521 65095;-2648 65100;-2791 65081;-3515 64985;-3692 64949 18; -20115 74817;-10920 76090;-10850 75172;-9593 66412 32;-1593 67560;-1615 67404;-1511 66683 32;-18672 64224;-20115 74817 18; -4419 58213;-3841 58288;-3680 57046;-3309 57093;-3126 57336;-3712 61818;-3966 62047;-4356 61996;-4203 60830;-4740 60760; -4198 59509; -3997 61437; -4207 60827;-3609 60905;-3610 61036;-3712 61818;-3966 62047;-4356 61996;-4207 60827 18; -4760 60754;-3591 60928;-3447 59777;-3518 59762 1;-3676 59750;-3857 59800;-3936 59928 1;-4103 60199;-4339 60154;-4643 60094;-4760 60754 18; -3839 58311;-3264 58386;-3245 58246;-3126 57336;-3309 57093;-3680 57046;-3839 58311 18; -4393 58207;-3263 58377;-3403 59494;-3477 59475 1;-3669 59443;-3773 59347;-3832 59125 1;-3895 58886;-4215 58873;-4460 58846;-4393 58207 18; -4637 60148;-3495 60283;-3346 58935;-4485 58791;-4637 60148 18; -4978 62475;-3940 62610;-4233 64862;-5362 65310;-8991 64795;-9252 64376;-9470 64341; -5833 64943; -5371 65314;-6463 65159;-6444 65094 1;-6393 64811;-6195 64552;-5911 64586 1;-5562 64628;-5323 64706;-5013 64542 1;-4570 64308;-4350 64071;-4092 63653;-4236 64851;-5371 65314 18; -7367 63603;-7497 65010;-6454 65158;-6442 65085 1;-6389 64806;-6192 64552;-5911 64586 1;-5562 64628;-5323 64706;-5013 64542 1;-4570 64308;-4350 64071;-4092 63653;-3950 62616;-4974 62487;-5153 63850;-6812 63646;-7096 63807;-7367 63603 18; -10690 63767;-10382 63810; -9415 65019;-9365 64694; -12052 64639;-11995 64271; -11958 63919;-11887 63422;-10980 63551; -12704 65028;-12445 63218;-12184 62940;-11994 61613;-13278 61429; -12432 61995; -12458 63211 1;-12719 62844;-12981 62261;-12860 61860 1;-12806 61680;-12753 61700;-12639 61551;-12575 61530;-11994 61613;-12180 62937;-12458 63211 18; -8877 63188;-9248 64378;-9488 64335;-9436 63947;-11023 63702;-11416 63667;-11374 63319;-11341 63086;-11797 63021;-11639 61911;-11302 61959;-11201 61969 1;-10715 62069;-10554 62370;-10119 62694 1;-9716 62993;-9347 63111;-8947 63176;-8877 63188 18; -7486 65006;-7360 63611;-7852 63506;-7852 63310;-7965 63290 1;-8304 63248;-8604 63228;-8887 63186;-8896 63248;-9248 64378;-9233 64407;-8991 64795;-7704 64978;-7486 65006 18; -7486 65006;-7360 63611;-7852 63506;-7852 63310;-7965 63290 1;-8304 63248;-8604 63228;-8887 63186;-8896 63248;-9248 64378;-9233 64407;-8991 64795;-7704 64978;-7486 65006 18; -7665 62271 32;-8232 62197 32;-7983 60293 32;-7416 60367 32;-7665 62271 50; -7167 58241 32;-7702 58171 32;-7462 56339 32;-6927 56409 32;-7167 58241 50; -6775 54855 32;-7261 54792 32;-7463 56354 32;-6977 56417 32;-6775 54855 50; -7216 58228 32;-7700 58165 32;-7977 60299 32;-7493 60362 32;-7216 58228 50; -7717 62263 32;-8230 62196 32;-8367 63238 32;-7854 63305 32;-7717 62263 50; -8372 63240;-7276 54809;-6780 54871; -11284 61917;-11310 62091;-10054 62972;-8981 63295;-8902 63212;-8905 63183;-8877 63188;-8849 63192 1;-8698 63213;-8541 63228;-8377 63245;-7264 54810;-7338 54409;-9356 54082;-9781 54372;-10441 55853;-11284 61917 18; -10172 56031 1;-10114 56042;-10052 56053;-9978 56066 1;-9804 56097;-9743 55872;-9716 55698 1;-9651 55270;-9653 55025;-9576 54599 1;-9529 54336;-9292 54149;-9027 54189 1;-8458 54274;-8142 54350;-7570 54416 1;-7500 54424;-7471 54437;-7435 54482; -11049 54400 32;-11882 54291 32;-11839 53961 32;-11006 54070 32;-11049 54400 50; -10945 53337 1;-11475 53203;-12163 53168;-12149 52621 1;-12145 52465;-12191 52243;-12035 52237 1;-11765 52227;-11625 52380;-11355 52368 1;-11124 52358;-11033 52335;-10762 52359;-10945 53337 18; -14422 50686 1;-14351 50949;-14169 51109;-13899 51149 1;-13610 51192;-13355 51080;-13227 50817;-14422 50686 18; -12660 50896 1;-12533 51222;-12342 51441;-11997 51498 1;-11551 51572;-11079 51546;-10898 51131;-12660 50896 18; -9423 51315 1;-9330 51591;-9125 51727;-8839 51777 1;-8584 51822;-8344 51713;-8228 51481;-9423 51315 18; -12074 52902 1;-12614 52908;-12905 52733;-13406 52533 1;-13976 52305;-14334 52245;-14946 52206;-14765 50627 32;-9320 51321;-9385 52560;-10772 52373;-10857 52352 1;-11058 52341;-11153 52359;-11355 52368 1;-11625 52380;-11765 52227;-12035 52237 1;-12191 52243;-12145 52465;-12149 52621 1;-12150 52678;-12144 52729;-12132 52775;-12074 52902 18; -12074 52902 1;-12614 52908;-12905 52733;-13406 52533 1;-13976 52305;-14339 52240;-14951 52201;-14765 50627 32;-9320 51321;-9385 52560;-10772 52373;-10857 52352 1;-11058 52341;-11153 52359;-11355 52368 1;-11625 52380;-11765 52227;-12035 52237 1;-12191 52243;-12145 52465;-12149 52621 1;-12150 52678;-12144 52729;-12132 52775;-12074 52902 18; -12151 52503 1;-12687 52440;-12951 52231;-13468 52077 1;-14020 51912;-14325 51818;-14899 51769; -15105 53781;-14951 52197 1;-14339 52236;-13976 52305;-13406 52533 1;-12905 52733;-12614 52908;-12074 52902;-12050 52935 1;-11842 53191;-11348 53235;-10945 53337;-11003 54065;-11034 54066;-11839 53961 32;-11882 54291 32;-11049 54400 32;-11176 55514;-12465 55335;-12360 54182;-14014 53972;-14217 53670;-14587 53880;-15105 53781 18; -11172 53285 1;-11652 53183;-12161 53092;-12149 52621 1;-12145 52465;-12191 52243;-12035 52237 1;-11765 52227;-11625 52380;-11355 52368 1;-11200 52361;-11108 52349;-10982 52348; -3174 57675;-2687 57715;-2831 58528;-2898 59023;-3160 60907;-3231 61417;-3471 63182;-3546 63709;-3681 64989;-3740 65360;-4209 65316;-4224 64857;-3940 62610;-4978 62475;-4748 60774;-4203 60844;-4216 60927;-4356 61996;-3966 62047;-3712 61818;-3505 60231;-3174 57675 18; -4173 65321;-4212 64854;-4385 64912;-5371 65314;-6463 65159;-6657 65126;-8991 64795;-9252 64376;-9465 64342;-9556 64944;-10476 64791;-10350 63818;-10725 63766;-10869 64730;-11860 64594;-11768 63905;-11999 63866;-11938 63329;-11380 63412;-11336 63085;-11785 63015;-11633 61907;-11310 61942;-11271 61680;-12004 61602;-12011 61736;-12180 62937;-12458 63211;-12695 64982;-9023 65453;-9005 65217;-5324 65741;-4393 65369;-4173 65321 18; -13282 61431;-12005 61628;-11265 61714;-10140 53770;-10942 53675;-10980 53779;-10996 53983;-10997 54101;-11180 55504;-12478 55335;-12713 57138;-12589 57146;-12136 57205;-11975 55963;-11448 56031;-11262 56273;-11848 60755;-12141 60980;-12651 60913;-12498 59747;-13035 59677;-13282 61431 18; -4189 56330;-3036 56480;-2753 54305;-9604 53414 1;-9869 53380;-10118 53559;-10153 53817;-11302 61959;-11639 61911;-11797 63021 16; -4161 56339;-3020 56468;-3028 56416;-2753 54305;-6685 53794;-6681 53929;-6789 54863;-6648 54920;-6245 54972;-5914 54737;-5685 55045;-4035 55261 32;-4161 56339 18; -8131 48683;-7169 47995;-5827 48194;-5827 48194 1;-5698 48213;-5588 48301;-5606 48439;-6235 52988;-10772 52373;-11180 55504;-12478 55335 16; -9388 52543;-9342 51490;-9319 51515 1;-9209 51661;-9045 51741;-8839 51777 1;-8584 51822;-8344 51713;-8228 51481;-6853 51640;-6798 51223;-6487 50987;-6721 50646;-6487 48898 32;-8079 48685;-7903 48520;-7169 47995;-5827 48194;-5827 48194 1;-5698 48213;-5588 48301;-5606 48439;-6236 52991;-9388 52543 18; -7887 46818 1;-7906 46982;-8080 47120;-8236 47097 1;-8505 47058;-8656 47035;-8925 46995 1;-9235 46949;-9467 46608;-9412 46300 1;-9354 45977;-9334 45796;-9276 45473 1;-9221 45164;-8930 44868;-8707 44902;-8154 44977; -8433 46072; -11061 47450;-10145 46736;-10857 45803; -10770 45176;-10726 45182;-9951 44593;-9727 42954;-10345 42141; -10770 45176;-10726 45182;-9951 44593;-9727 42954;-10345 42141;-10770 45176 18; -11061 47450;-10145 46736;-10857 45803;-11061 47450 18; -3485 50117;-3904 53289;4433 54391;4459 53307;-3181 52283;-2919 50190;-3485 50117 18; -3661 51103;-3805 52202 32;-4241 52145 32;-4094 51027 48; -3872 51073;-3970 51816; -5016 52702 1;-4921 52193;-4599 51985;-4196 51660;-4193 51783;-4241 52145 32;-3805 52202;-3752 52289;-3875 53300;-5078 53146;-5016 52702 18; -2906 50024;-3234 49980; -4557 49418;-4070 49436;-4113 49973;-3477 50115;-3498 50214;-3620 51140;-4131 51077;-4236 51712;-4320 51759 1;-4614 51992;-4852 52183;-4968 52518;-4557 49418 18; -3661 51103;-3805 52202 32;-4241 52145 32;-4094 51027 32;-3661 51103 18; -2374 48856 32;-698 49078 32;-798 49834 32;-2474 49612 32;-2374 48856 50; -3701 49280;-3591 48508; -2763 48804;-3446 48714; -2864 49528;-2809 49113;-3285 49050;-3317 49292; -3214 49822;-2921 49863;-2799 49133;-3277 49058;-3312 49292;-3146 49320;-3214 49822 18; -3526 49261;-3324 49285;-3309 49229;-3285 49050;-2809 49113;-2864 49528;-2471 49582;-2379 48869;-3457 48718;-3526 49261 18; -3757 48521;-2116 35837;988 36364;864 37229;-1149 36968;-2757 48807;-3459 48715;-3757 48521 18; -4550 49433;-4069 49455;-4001 49239;-3813 49263;-3753 48525;-2267 37259;-2854 37183;-4550 49433 18; -4181 50043;-2992 50213;-3204 51711;-4824 51582;-4187 47003;-2571 47170;-2792 48768;-3419 48686;-3395 48541;-3790 48487;-3885 49178;-4060 49151;-4181 50043 18; -9129 42177;-8266 42331;-8343 42409 1;-8348 42418;-8352 42429;-8354 42441 1;-8378 42558;-8403 42621;-8424 42738 1;-8473 43011;-8271 43164;-8309 43439 1;-8332 43609;-8346 43705;-8369 43875 1;-8388 44014;-8515 44111;-8654 44092 1;-8878 44062;-9004 44046;-9228 44015 1;-9310 44004;-9366 43928;-9355 43846;-9129 42177 18; -8321 39694;-8575 41509;-5529 41935;-5272 40095; -8321 39694;-8575 41509;-5529 41935;-5272 40095;-8321 39694 18; -9061 41489;-8574 41514;-8315 39688;-9240 39559;-9274 39628;-9465 40988;-9189 41354;-9061 41489 18; -5077 39581;-4399 39766;-4928 43572;-5047 43550 1;-5316 43494;-5355 43406;-5445 43176 1;-5547 42916;-5800 42814;-6077 42781 1;-6436 42738;-6641 42744;-7002 42720 1;-7125 42712;-7193 42623;-7271 42545;-7167 41706;-5529 41935;-5272 40095;-5237 39698;-5077 39581 18; -9395 40463;-8452 40595; -9126 42206;-8311 42363;-8216 42337 1;-8168 42331;-8115 42336;-8066 42345 1;-7831 42388;-7701 42398;-7464 42433 1;-7366 42448;-7304 42511;-7243 42574;-7129 41722;-8572 41495;-9100 41460;-9030 41560;-9126 42206 18; -6560 37067;-5456 36244;-3945 36467;-4928 43570 1;-5088 43554;-5232 43535;-5299 43481 1;-5393 43405;-5374 43318;-5436 43172 1;-5545 42915;-5800 42814;-6077 42781 1;-6436 42738;-6641 42744;-7002 42720 1;-7209 42707;-7259 42464;-7464 42433 1;-7701 42398;-7831 42388;-8066 42345 1;-8183 42324;-8331 42325;-8354 42441 1;-8378 42558;-8403 42621;-8424 42738 1;-8473 43011;-8271 43164;-8309 43439 1;-8332 43609;-8346 43705;-8369 43875 1;-8388 44014;-8515 44111;-8654 44092 1;-8878 44062;-9004 44046;-9228 44015 1;-9310 44004;-9366 43928;-9355 43846;-9034 41560;-9465 40988;-9250 39593 16; -7322 47327;-7072 46506;-6417 46602 1;-6245 46627;-6076 46521;-6051 46349;-5868 45109 1;-5479 44861;-5304 44599;-5248 44141;-5015 44171;-5438 47239 1;-5467 47451;-5687 47568;-5898 47534;-7322 47327 18; -7579 45697;-7412 44494 1;-7543 44476;-7616 44466;-7747 44448 1;-7889 44428;-7989 44297;-7969 44155 1;-7944 43975;-7929 43874;-7904 43694 1;-7887 43570;-7772 43483;-7648 43500;-7330 43544;-7296 43298 1;-7279 43172;-7162 43084;-7036 43101 1;-6647 43155;-6429 43183;-6040 43235 1;-5882 43256;-5771 43401;-5792 43559 1;-5854 44026;-5889 44287;-5951 44754 1;-5975 44936;-6143 45064;-6325 45040;-6401 45615;-6436 45881 1;-6453 46007;-6569 46097;-6695 46080 1;-6961 46045;-7111 46024;-7377 45988 1;-7497 45972;-7603 45849;-7579 45697 18; -7579 45697;-7412 44494 1;-7543 44476;-7616 44466;-7747 44448 1;-7889 44428;-7989 44297;-7969 44155 1;-7944 43975;-7929 43874;-7904 43694 1;-7887 43570;-7772 43483;-7648 43500;-7330 43544;-7296 43298 1;-7279 43172;-7162 43084;-7036 43101 1;-6647 43155;-6429 43183;-6040 43235 1;-5882 43256;-5771 43401;-5792 43559 1;-5854 44026;-5889 44287;-5951 44754 1;-5975 44936;-6143 45064;-6325 45040;-6401 45615;-6436 45881 1;-6453 46007;-6569 46097;-6695 46080 1;-6961 46045;-7111 46024;-7377 45988 1;-7497 45972;-7603 45849;-7579 45697 18; -8772 48571;-9698 47380;-10889 48306; -8772 48571;-9698 47380;-10889 48306;-8772 48571 18; -11473 47377 32;-11588 48220 32;-13372 47977 32;-13257 47134 32;-11473 47377 50; -13272 47127 32;-13388 47977 32;-13986 47896 32;-13870 47046 32;-13272 47127 50; -13856 47101;-13957 47852; -5371 44870; -5618 45919; -5803 47054; -6673 46999; -7322 47327;-7072 46506;-6417 46602 1;-6245 46627;-6076 46521;-6051 46349;-5868 45109 1;-5479 44861;-5304 44599;-5248 44141;-5015 44171;-5438 47239 1;-5467 47451;-5687 47568;-5898 47534;-7322 47327 18; -10151 53826;-10962 53739;-10936 53633;-10772 52373;-6817 52909;-6646 52933;-6236 52991;-5606 48439 1;-5599 48387;-5610 48343;-5634 48306;-4419 48442;-5073 53147;-2771 53456;-2882 54288;-6685 53794;-8489 53550;-9510 53412 1;-9806 53374;-10055 53508;-10141 53769;-10151 53826 18; -4430 48483;-5621 48357;-5696 48243 1;-5733 48218;-5779 48201;-5827 48194;-5827 48194;-7169 47995;-8116 48672;-8216 48564;-8366 48354;-8696 48602;-8763 48593;-8829 48498;-9698 47380;-10421 47942;-10607 48087;-10889 48306;-11580 48206;-11469 47404;-11080 47441;-10155 46738;-10852 45831;-10556 45553;-10769 45218;-9951 44591;-9853 43882;-9353 43900 1;-9339 43959;-9291 44006;-9228 44015 1;-9004 44046;-8878 44062;-8654 44092 1;-8553 44106;-8457 44058;-8406 43977;-7955 44056 1;-7959 44087;-7964 44119;-7969 44155 1;-7989 44297;-7889 44428;-7747 44448 1;-7616 44466;-7543 44476;-7412 44494;-7579 45697 1;-7603 45849;-7497 45972;-7377 45988 1;-7111 46024;-6961 46045;-6695 46080 1;-6569 46097;-6453 46007;-6436 45881;-6401 45615;-6325 45040 1;-6143 45064;-5975 44936;-5951 44754 1;-5915 44483;-5888 44281;-5860 44067;-5249 44147 1;-5305 44601;-5481 44862;-5868 45109;-6051 46349 1;-6076 46521;-6245 46627;-6417 46602;-7072 46506;-7322 47327;-5898 47534 1;-5687 47568;-5467 47451;-5438 47239;-5019 44199;-3848 44332;-4430 48483 18; -9363 43932;-9873 43915;-9729 42968;-10344 42166;-10327 41874;-10030 41603;-10218 41289;-9930 39056;-9186 39152;-9463 40979;-9040 41568;-9354 43806;-9363 43932 18; -8438 43997;-7967 44093;-7944 43979 1;-7931 43889;-7920 43807;-7904 43694 1;-7887 43570;-7772 43483;-7648 43500;-7330 43544;-7296 43298 1;-7279 43172;-7162 43084;-7036 43101 1;-6647 43155;-6429 43183;-6040 43235 1;-5882 43256;-5771 43401;-5792 43559 1;-5820 43771;-5843 43941;-5865 44106;-5256 44201 1;-5255 44192;-5254 44182;-5252 44173;-5018 44180;-5023 44250;-3867 44376;-3222 39864;-4394 39724;-4928 43570 1;-5088 43554;-5232 43535;-5299 43481 1;-5393 43405;-5374 43318;-5436 43172 1;-5545 42915;-5800 42814;-6077 42781 1;-6436 42738;-6641 42744;-7002 42720 1;-7209 42707;-7259 42464;-7464 42433 1;-7701 42398;-7831 42388;-8066 42345 1;-8183 42324;-8331 42325;-8354 42441 1;-8378 42558;-8403 42621;-8424 42738 1;-8473 43011;-8271 43164;-8309 43439 1;-8329 43588;-8342 43681;-8361 43816;-8438 43997 18; -14409 50659;-14402 50749 1;-14320 50975;-14147 51112;-13899 51149 1;-13610 51192;-13355 51080;-13227 50817; -12638 50886;-12636 50954 1;-12509 51248;-12321 51444;-11997 51498 1;-11551 51572;-11079 51546;-10898 51131; -9414 51309;-9387 51404 1;-9282 51621;-9092 51733;-8839 51777 1;-8584 51822;-8344 51713;-8228 51481; -3872 53115;4385 54224;4507 53314; -2697 57728;-3171 57709;-3146 57487;-3126 57336;-3309 57093;-3680 57046;-3841 58288;-4419 58213;-4145 56335;-3597 56407;-3036 56480;-2753 54305;-2938 54281;-2884 53248;1147 53789;4576 54284;4391 55370;4268 55347;944 54838;930 54944;26 54867;42 54747;36 54722;-2221 54395;-2514 56256;-2582 56750;-2697 57728 18; 4704 52835 32;5571 52949 32;5789 51293 32;4922 51179 32;4704 52835 50; 4502 54392;4336 55378;4485 55380;7559 55851;7398 56776;7413 56818;8333 56958;8542 56068;9541 56273;10034 54842;9853 54955 1;9588 55069;9206 55006;8890 54923 1;8974 54582;9058 54285;9144 54011;9235 53732 1;9461 53070;9711 52517;10042 51747;8832 51574;8524 53931 32;7890 53848 32;6819 53725;6758 54205;6862 54236;7658 54335 32;7583 54938 32;6695 54827 32;5641 54685;5746 53559;5484 53516;5536 52944;4752 52841;4656 53298;4502 54392 18; 6695 54827 32;7583 54938 32;7658 54335 32;6770 54224 32;6695 54827 50; 6230 54775;6685 54841;6840 53718;5721 53595;5684 54047;6286 54147;6230 54775 18; 5628 49455; 5630 49055 1;5409 49053;5228 49230;5226 49451 1;5223 49672;5400 49853;5621 49856 1;5842 49858;6024 49681;6026 49460 1;6028 49239;5851 49058;5630 49055 18; 5075 50431;3392 50209; 2832 50145;755 49871; 1537 47576;1418 48481;3993 48820;4056 48341; 3836 48902 1;3806 49137;3858 49419;4093 49451 1;4262 49474;4489 49507;4516 49338 1;4560 49061;4585 48905;4629 48628 1;4644 48535;4596 48436;4503 48422;4172 48370;3836 48902 18; 1339 48220 1;1169 48201;957 48220;934 48390 1;913 48544;902 48631;881 48785 1;867 48889;939 48985;1043 48999 1;1552 49068;1840 49086;2347 49167 1;2458 49185;2547 49086;2561 48975;2587 48709;1344 48521;1339 48220 18; 1314 46922; 1445 46128; 3155 47934; 2544 46686; 1628 47605;1844 46032;2271 46091;2372 45358;3536 45518;3423 46343 1;3373 46707;3733 47024;4080 47145;3932 48331;3889 48681;1557 48377;1628 47605 18; 3933 48322;4080 47145 1;3733 47024;3373 46707;3423 46343;3536 45518;2372 45358;2271 46091;1844 46032;1629 47596; 1629 47565;977 47476; 103 47536;613 47603;397 49246;-105 49180;103 47536 18; 127 47550;606 47613;394 49221;-62 49161; 655 43537;1134 43600;682 47036;226 46976; 657 45262; 655 43537;1134 43600;682 47036;226 46976;655 43537 18; 952 41199;1588 42033;1468 42927;750 42831; 952 41199;1588 42033;1468 42927;750 42831;952 41199 18; 1950 40084;1813 41123;3298 43060;4304 43179; 8711 43826;8157 44827;7610 44984; 7661 44793;6329 44590;6180 45904;7316 46095;7932 45015;7661 44793 18; 7598 44802;4119 44315;4242 43359; 1994 40119;1864 40738;1813 41123;3298 43060;4231 43253;4237 43398;4119 44315;7598 44802;8031 44863;8157 44827;8622 43986;8577 43729;1994 40119 18; 4061 44822 32;5605 45038 32;5377 46669 32;3833 46453 32;4061 44822 50; 4061 44822 32;5605 45038 32;5377 46669 32;3833 46453 32;4061 44822 50; 7047 46598;6037 48447 1;5901 48695;5513 48551;5320 48344 1;5039 48041;4881 47872;4599 47570 1;4486 47449;4644 47183;4808 47202 1;5035 47228;5162 47243;5389 47269 1;5653 47300;5889 47120;5931 46857;5998 46419;7047 46598 18; 6510 46820; 6195 47394; 5838 47967; 7047 46598;6037 48447 1;5901 48695;5513 48551;5320 48344 1;5039 48041;4881 47872;4599 47570 1;4486 47449;4644 47183;4808 47202 1;5035 47228;5162 47243;5389 47269 1;5653 47300;5889 47120;5931 46857;5998 46419;7047 46598 18; 6324 44635;6180 45904;7316 46095;7879 45108; 3107 43702;3746 43799;3534 45199; 2755 43648;2308 43580;2190 44355; 1836 46023;1269 45944;1588 43654;1821 43686;1899 43116;2126 42943;2761 43543;2459 43765;2348 44844;2403 44912;2348 45356;2280 46072;1836 46023 18; 2427 44875;2367 45362 16; 1836 46023;1269 45944;1588 43654;1821 43686;1899 43116;2126 42943;2733 43517 16; 6874 48842 32;6513 48645 32;7106 47557 32;7467 47754 32;6874 48842 50; 7658 47448 32;7277 47241 32;7499 46833 32;7880 47041 32;7658 47448 50; 7559 47115; 6806 48540; 6874 48842 32;6513 48645 32;7106 47557 32;7467 47754 32;6874 48842 50; 7570 49244 32;8793 49912 32;8274 50861 32;6794 50664 32;7570 49244 50; 1131 39941 32;2004 40056 32;2187 38670 32;1314 38555 32;1131 39941 50; 4897 51180;5752 51293;5818 51112;5891 50555 32;6774 50670;6871 50523;7570 49244 32;7591 49255;7453 49143;6909 48846;6780 48791;6513 48645 32;7106 47557 32;7467 47754 32;7663 47438;7538 47383;7277 47241 32;7499 46833 32;7880 47041 32;9678 43748;8977 43365;8160 44812;7863 45108;7314 46085;6171 45902;5996 46417;6040 46426;7047 46598;6037 48447 1;5901 48695;5513 48551;5320 48344 1;5039 48041;4881 47872;4599 47570 1;4515 47480;4580 47311;4686 47237;4079 47154;3934 48317;4168 48376;4172 48370;4503 48422 1;4596 48436;4644 48535;4629 48628 1;4589 48882;4564 49034;4527 49272;5238 49359 1;5281 49183;5441 49053;5630 49055 1;5851 49058;6028 49239;6026 49460 1;6024 49681;5842 49858;5621 49856 1;5538 49855;5461 49829;5398 49785;5060 50331;4897 51180 18; 3834 48920;3834 48920 1;3809 49151;3864 49420;4093 49451 1;4262 49474;4489 49507;4516 49338 1;4560 49061;4585 48905;4629 48628 1;4644 48535;4596 48436;4503 48422;4172 48370;4146 48388; 2583 48752;2561 48975 1;2547 49086;2458 49185;2347 49167 1;1840 49086;1552 49068;1043 48999 1;939 48985;867 48889;881 48785 1;902 48631;913 48544;934 48390 1;957 48222;1164 48201;1333 48219; 4920 50578;5408 49819;5377 49769 1;5284 49695;5224 49580;5226 49451 1;5226 49410;5233 49371;5245 49334;4529 49239;4512 49358 1;4471 49504;4255 49473;4093 49451 1;3858 49419;3806 49137;3836 48902;3859 48866;2593 48743;2577 48817;2561 48975 1;2547 49086;2458 49185;2347 49167 1;1840 49086;1552 49068;1043 48999 1;939 48985;867 48889;881 48785 1;902 48631;913 48544;934 48390 1;957 48222;1164 48201;1333 48219;1618 47565;1834 46041;1279 45943;1588 43654;1821 43686;1899 43116;2126 42943;2752 43535;3316 43064;3298 43060;1813 41123;1864 40738;1994 40119;1797 40027;1156 39947;977 41261;1588 42038;1458 42932;736 42827;668 43531;1119 43611;699 47029;205 46973;137 47547;606 47615;408 49219;-696 49089;-801 49805;4920 50578 18; 4062 47204;4623 47284;4722 47217 1;4750 47204;4779 47199;4808 47202 1;5035 47228;5162 47243;5389 47269 1;5653 47300;5889 47120;5931 46857;5998 46419;6037 46426;6225 45911;6171 45902;6320 44637;4118 44304;4061 44822 32;5605 45038 32;5377 46669 32;3833 46453 32;3958 45558;3532 45545;3423 46343;3441 46562 1;3520 46825;3794 47040;4064 47139;4062 47204 18; 2427 44876;2374 45348;3534 45508;3537 45579;3953 45585;4074 44805;4135 44330;4234 43349;3148 43171;2738 43525;2688 43794;3524 43948;3370 44972;2427 44876 18; 14958 41825 32;15796 42265 32;15984 41887 32;15146 41447 32;14958 41825 50; 14145 44489; 12813 47215; 11365 50229; 13488 41153;8770 49903;9269 50175 32;14034 41451 32;13488 41153 18; 8787 49872;13471 41184;11641 40184 32;6909 48846 32;8787 49872 18; 11193 51931;11793 50528;11793 50529; 11054 49789;12301 47242; 12534 46765;13630 44523; 13863 44046;14958 41825; 10801 50399 1;11121 50440;11473 50483;11793 50529;11885 50353 1;11945 50238;11841 50114;11721 50065;11054 49789;10801 50399 18; 13630 44523 32;14450 44924 32;14683 44447 32;13863 44046 32;13630 44523 50; 15149 41439;15978 41884 1;16088 41650;16024 41369;15795 41247;15364 41018;15149 41439 18; 12301 47242 32;13121 47643 32;13354 47166 32;12534 46765 32;12301 47242 50; 14958 41825 32;15796 42265 32;15984 41887 32;15146 41447 32;14958 41825 50; 9264 50170;8814 49917;8283 50830;8913 50935;8839 51632;10017 51786;10089 51754;11193 51931;11775 50540;10800 50411;11059 49788;12304 47247;12586 46659;13630 44523;13902 43967;14958 41825;14034 41451 32;9264 50170 18; 11170 51992;12780 52258;12834 52150 1;12858 52100;12882 52049;12907 51996 1;12955 51895;13030 51799;13129 51836;13175 51711;13881 49895;14438 50028;17941 42583;17980 42411;17760 42308;15985 41446 1;16047 41578;16046 41740;15978 41884;15900 42057;15796 42265 32;14969 41831;14821 42104;13902 43967;13856 44061;13991 44109;14683 44447 32;14450 44924 32;13630 44523 32;13525 44737;12586 46659;12526 46784;12633 46813;13354 47166 32;13121 47643 32;12301 47242 32;12175 47510;11063 49780;11186 49844;11721 50065 1;11841 50114;11945 50238;11885 50353;11793 50529;11719 50675;11170 51992 18; -18383 61381;-18494 62134;-18561 64120;-12701 64947;-12677 64845;-12458 63211;-12451 63204;-12492 63162 1;-12741 62793;-12976 62243;-12860 61860 1;-12806 61680;-12753 61700;-12639 61551;-12611 61542;-13268 61443;-13355 61929;-13465 62772 32;-15116 62556;-15405 62760;-15638 62488;-16168 62419 32;-15816 59717;-15731 59072;-15048 53833;-15101 53740;-14951 52197;-14946 52162;-14765 50627 32;-14373 47847;-13978 47884;-13824 47014;-12394 36327;-12970 36239;-13152 36154;-13175 36037;-13160 35932;-13468 35886;-14830 35957;-18383 61381 18; 34 914;-12019 914; 152 -545;158 -1378; 140 -1785;148 -2889; 439 -3751; 1308 -3759; 1619 -3393 1;1628 -3761;1625 -4139;1631 -4536 1;1652 -5992;918 -6885;-581 -6838;-1994 -6794;-2007 -4892;100 -4877;92 -3689;765 -3007;1619 -3393 18; 1619 -3393 1;1628 -3761;1625 -4139;1631 -4536 1;1652 -5992;918 -6885;-581 -6838;-1994 -6794;-2007 -4892;100 -4877;92 -3689;765 -3007;1619 -3393 18; -14234 987;-32592 962;-32592 -2714;-24820 -4442;-21563 -4491;-12767 -2694;-8868 -2634;-8859 -1456;-425 -1370;99 -1370;186 937;-14235 888;-14234 987 18; 1557 1050;2196 1046;2195 53;2190 -4523 1;2190 -6333;1139 -7424;-561 -7383 1;-614 -7382;-665 -7380;-715 -7379;-731 -6833;-615 -6837;-472 -6840 1;954 -6841;1651 -5956;1631 -4536 1;1625 -4139;1628 -3761;1619 -3393;765 -3007;92 -3689;100 -4877;-559 -4882;-572 -4271;-443 -4270;-426 -3753;-442 -1362;88 -1362;127 -494;1557 1050 18; -16459 12674;-16426 2012;-17246 2009; -16739 13703;-24033 13593;-23893 2165;-20125 2235;-20173 13114;-17348 13104;-17246 2009;-16426 2012;-16459 12674;-16739 13703 18; -11834 13678;-15436 13652;-15423 11802; -15419 8897;-15410 6105; -14797 2808; -15411 6114;-15406 2558 32;-15406 2183 32;-11555 2188; -15428 11818;-15419 8897; -11834 13678;-8633 13701; -8653 13901;-7850 13912;-7813 2040;-8633 2037; -15485 2043;-8541 2037;-7813 2040;-7850 13912;-8653 13901;-15467 13820;-15485 2043 18; -6719 4039;-6716 2188;1837 2202; -6925 13755;-3714 13759; -6734 13566;-6719 4039; 1623 5535;1624 4663; 1618 2387;1615 4676; 1618 5521;1610 12270 1;1610 12420;1587 12565;1546 12702 33;1360 13315;790 13761;116 13760;-50 13760 32;-2345 13757 32;-3725 13755; -6720 2166;1638 2183;1610 11936;1610 12270 1;1610 12420;1587 12565;1546 12702 33;1360 13315;790 13761;116 13760;-50 13760 32;-2345 13757 32;-3725 13755;-6772 13786;-6720 2166 18; -17390 1024;-17427 2011;-17068 2010;-16426 2012;-16459 12674;-16632 13674;-21099 13622;-21116 14208;-20139 14209;363 14412 1;1238 14411;2213 13560;2212 12685;2198 2019;1565 2211;1612 10412;1610 12270 1;1610 12420;1587 12565;1546 12702 33;1360 13315;790 13761;116 13760;-50 13760 32;-2345 13757 32;-3725 13755;-6809 13692;-6774 2002;-7813 2040;-7850 13912;-8653 13901;-15550 13779;-15498 1025;-17390 1024 18; -15568 2037;-15603 990;2193 921;2198 2177;1652 2229;-7813 2159;-7813 2040;-8633 2037;-15568 2037 18; 23488 -17737;19464 -17649; 26077 -17754;28115 -17802; 30749 -17775;33010 -17828; 19162 -17670;33207 -17880;32632 -26307;18639 -25452;19162 -17670 18; 28299 -10788;28328 -9546;29613 -9576 1;30319 -9592;30930 -10125;30914 -10831; 26276 -10739;26330 -8450 1;25304 -8664;24735 -8776;23694 -8897;23654 -10667; 26276 -10739;26330 -8450 1;25304 -8664;24735 -8776;23694 -8897;23654 -10667;26276 -10739 18; 28299 -10788;28328 -9546;29613 -9576 1;30319 -9592;30930 -10125;30914 -10831;28299 -10788 18; 33281 -7193; 31813 -7489; 34490 -7354 1;35292 -7278;35887 -7222;36415 -6613 1;36914 -6037;37643 -5668;38290 -6070 1;38702 -6326;38886 -6983;38512 -7292 1;37277 -8313;36111 -8624;34539 -8316 1;34249 -8259;33968 -8217;33885 -7933 1;33832 -7749;33849 -7519;34033 -7465 1;34209 -7413;34307 -7371;34490 -7354 18; 37966 -5256 1;34264 -6452;32110 -7114;28336 -8058;28330 -8197 1;29259 -8215;29779 -8260;30708 -8300 1;31629 -8340;32313 -8493;32883 -9217;41421 -8165;40332 -6072 1;39872 -5188;38914 -4950;37966 -5256 18;38290 -6070 1;38702 -6326;38886 -6983;38512 -7292 1;37277 -8313;36111 -8624;34539 -8316 1;34249 -8259;33968 -8217;33885 -7933 1;33832 -7749;33849 -7519;34033 -7465 1;34209 -7413;34307 -7371;34490 -7354 1;35292 -7278;35887 -7222;36415 -6613 1;36914 -6037;37643 -5668;38290 -6070 18; 40962 -13427;43997 -13365;41234 -8369;33079 -9380;33042 -10108;33227 -10133;33252 -11811;34522 -11823;34502 -13710;35488 -13693;40962 -13427 18; -2273 -9414;-765 -9474; -769 -9312;2293 -9390; 6396 -9606;2269 -9556; 6396 -9606;8091 -9594; 8091 -9594;9541 -9599; 9541 -9599;12606 -9550; 15726 -9526;12606 -9550; 15726 -9526;17010 -9507; 17010 -9507;18811 -9477; 18811 -9477;20391 -9410;20378 -10705; -11446 -12938;-11421 -9853;-11403 -9677 1;-11284 -9337;-10958 -9093;-10581 -9108;-8446 -9191;-1251 -9336;-668 -9315;2293 -9390;7075 -9654;17247 -9497;20335 -9427;20318 -13492;-11446 -12938 18; 26331 -8060 1;24533 -8435;23069 -8598;20879 -8709 1;16304 -8942;13770 -8820;9190 -8919 1;4796 -9014;2347 -8930;-2047 -8813 1;-2060 -8813;-2074 -8812;-2087 -8812;-2323 -8802;-2367 -9259;-720 -9313;2293 -9390;8209 -9494;16462 -9406;20405 -9354;20370 -10750;21609 -10750;21505 -16120;20199 -16116;20187 -16612;19202 -16600;19185 -17516;23512 -17673;23658 -10489;23694 -8897 1;24735 -8776;25304 -8664;26330 -8450;26331 -8060 18; 40895 -7190;41351 -6974;41347 -6910 1;41123 -6473;40890 -6018;40647 -5541 1;40215 -4692;38798 -4498;37573 -4879 1;34307 -5894;32347 -6556;29328 -7341;29054 -7412 1;28774 -7484;28485 -7557;28185 -7632 1;27487 -7806;26861 -7951;26271 -8073;26264 -8460;26338 -8442;26252 -10842;26079 -17664;28176 -17714;28302 -10646;28328 -9546;29613 -9576 1;30319 -9592;30930 -10125;30914 -10831;30693 -17714;32914 -17751;33160 -10139;32889 -9251;32802 -9118 1;32248 -8479;31586 -8338;30708 -8300 1;29779 -8260;29259 -8215;28330 -8197;28336 -8058 1;32110 -7114;34264 -6452;37966 -5256 1;38914 -4950;39872 -5188;40332 -6072;40768 -6926;40895 -7190 18; 11980 39455 32;14384 40769 32;14685 40219 32;12281 38905 32;11980 39455 50; 13916 40136; 12678 39490; 11980 39455 32;14384 40769 32;14685 40219 32;12281 38905 32;11980 39455 50; 12907 37796;15121 38994;18659 32456; 17785 32218;18400 32551; 12428 36391 32;11958 37260 32;12489 37547 32;12959 36678 32;12428 36391 50; 12952 36644 32;12467 37540 32;15005 38915 32;18406 32639 32;17795 32308 32;14879 37688 32;12952 36644 50; 12931 37780;11989 37269;12457 36406; 11599 36132;11194 36880; 12562 34193;12080 35054; 10111 34855 1;9373 34421;8925 34239;8175 33826 1;7239 33311;6641 33107;5584 32953;2879 32535;3281 29569; 9981 34997;9592 35823 1;9535 35943;9393 35994;9273 35938 1;9171 35890;9126 35768;9174 35666;9589 34783; 8006 33956;7612 34793 1;7555 34913;7413 34964;7293 34908 1;7191 34860;7146 34738;7194 34636;7609 33753; 5434 33117;5286 33993 1;5268 34099;5162 34169;5056 34151 1;4949 34133;4846 34026;4864 33919;5008 33062; 9981 34997;9592 35823 1;9535 35943;9393 35994;9273 35938 1;9171 35890;9126 35768;9174 35666;9589 34783;9981 34997 18; 8008 33954;7612 34793 1;7555 34913;7413 34964;7293 34908 1;7191 34860;7146 34738;7194 34636;7609 33753;8008 33954 18; 5434 33117;5286 33993 1;5268 34099;5162 34169;5056 34151 1;4949 34133;4846 34026;4864 33919;5008 33062;5434 33117 18; 10016 34987;11246 35615; 3243 29229;3308 28750;2212 28601;2326 27764; 3343 29236;3692 29287;3778 28696;3424 28644;3343 29236 18; 3892 28006 32;4882 28150 32;4798 28728 32;3808 28584 32;3892 28006 50; 4815 28716;3790 28550;3690 29274;3158 29213;3123 29549;3246 29825;2879 32535;5584 32953 1;6641 33107;7239 33311;8175 33826;10002 34972;11311 35644;11952 34990;12445 34096;6930 31146;4537 30813;4815 28716 18; 2957 30550;1858 30401;1923 29919;3020 30067; 2673 32684;2511 33738;2008 33661 1;1689 33613;1470 33314;1519 32995;1633 32251;2695 32414; 2673 32684;2511 33738;2008 33661 1;1689 33613;1470 33314;1519 32995;1633 32251;2695 32414;2673 32684 18; 2957 30550;1858 30401;1923 29919;3020 30067;2957 30550 18; 12900 33848 32;13650 34268 32;14535 32685 32;13785 32266 32;12900 33848 50; 11412 39051;10072 38340;9433 39545;10682 40208;10921 39756; 9511 39183;6704 37694; 7101 36598; 8250 38393;8712 37522;8294 37300;7835 38166;8250 38393 18; 10044 41469 32;10305 40996 32;8776 40153 32;8515 40626 32;10044 41469 50; 8048 40368 32;8309 39895 32;5392 38286 32;5131 38759 32;8048 40368 50; 4697 38518 1;4829 38279;4847 37958;4609 37824 1;4430 37723;4332 37644;4130 37606 1;3808 37545;3627 37510;3305 37449 1;3189 37427;3105 37489;3035 37584; 10044 41469 32;10305 40996 32;8776 40153 32;8515 40626 32;10044 41469 50; 8048 40368 32;8309 39895 32;5392 38286 32;5131 38759 32;8048 40368 50; 4697 38518 1;4829 38279;4847 37958;4609 37824 1;4430 37723;4332 37644;4130 37606 1;3808 37545;3627 37510;3305 37449 1;3189 37427;3105 37489;3035 37584;4697 38518 18; 8652 38781 32;8449 39163 32;8081 38968 32;8284 38586 32;8652 38781 50; 7797 38397 32;7621 38728 32;5478 37590 32;5654 37259 32;7797 38397 50; 5704 36118;5232 37007;2369 36513 16; 1546 36352;-2204 35741;-2432 37307 16; 1500 37304;1761 35290 1;1547 35255;1201 35261;1164 35475;1069 36115;-96 35919;23 35023;-446 34960;-563 35852;-2064 35609;-1969 35033 1;-1941 34863;-2070 34757;-2240 34729;-2544 34692;-2649 35578;-2309 35634;-2558 37222;-2852 37174;-5070 53124;-3902 53279;-3485 50117 16; -2068 35639;-1969 35033 1;-1941 34863;-2074 34761;-2244 34733;-2525 34692;-2649 35578;-2309 35634;-2068 35639 18; 1472 37277 32;1609 36245 32;987 36162 32;850 37194 32;1472 37277 50; 1601 36537;2347 36659; 1639 36199;2416 36321; 1057 36216;1611 36307;1667 36014;1761 35290 1;1547 35255;1201 35261;1164 35475;1071 36099;1057 36216 18; -575 35928;-91 35993;-89 35865;23 35023;-446 34960;-523 35546;-575 35928 18; -5096 38866;-4306 38984;-4402 39796;-5070 39608;-4982 39481;-5135 39194;-5096 38866 18; -7150 36995;-7975 35873;-8692 35764;-8843 36756; -7150 36995;-7975 35873;-8692 35764;-8843 36756;-7150 36995 18; -4847 37278;-4071 37391;-4312 38983;-5082 38868;-4847 37278 18; -5017 37277;-5096 36305;-5176 36285;-5456 36244;-6560 37067;-5017 37277 18; -5048 37264;-4075 37426;-3953 36466;-5117 36305;-5048 37264 18; -3843 35855;-9468 35123;-10106 34303 1;-9907 34142;-9769 34067;-9631 33852;-9076 33926;-9202 34870;-7034 35159;-6911 34239;-6530 34290;-6652 35202;-4133 35539;-4043 34868 1;-4019 34693;-3859 34571;-3684 34594;-3843 35855 18; -3807 35845;-9432 35113;-10070 34293 1;-9871 34132;-9733 34057;-9595 33842;-9040 33916;-9166 34860;-6998 35149;-6875 34229;-6494 34280;-6616 35192;-4097 35529;-4007 34858 1;-3983 34683;-3823 34561;-3648 34584;-3807 35845 18; -9578 36699;-9551 36500;-10060 35831;-9965 35128;-10345 34629 1;-11028 35149;-11752 35198;-12533 34842;-12700 35058;-13076 35700 1;-13184 35875;-13230 36194;-13027 36224;-12404 36317; -9578 36699;-9551 36500;-10060 35831;-9965 35128;-10345 34629 1;-11028 35149;-11752 35198;-12533 34842;-12700 35058;-13076 35700 1;-13184 35875;-13230 36194;-13027 36224;-12404 36317;-9578 36699 18; -13877 34647;-13842 34424;-13520 34190;-13267 34406;-13470 35887;-14821 35936; -13877 34647;-13842 34424;-13520 34190;-13267 34406;-13470 35887;-14821 35936;-14774 35557;-14052 35594;-13877 34647 18; -10997 76099;-20134 74789;-18828 64936 32;-18724 64156 32;-15017 36197;-14861 35024 48; -13388 30562;-13417 30778;-13280 30960 1;-12740 30520;-11988 30296;-11247 30427 1;-10265 30602;-9722 31218;-9480 32186;-670 33346 1;-337 33390;-107 33079;-53 32748;21 32125;-1133 31988;-898 30072;262 30214;327 29685;-832 29543;-620 27815;561 27960;628 27412;-814 27236;-892 27871;-1328 27817;-1274 27379;-1494 27352 16; -13768 30531;-13817 30906;-13562 31237 1;-13796 31518;-13962 31849;-14022 32215 1;-14129 32861;-14176 33346;-13790 33874;-14206 34178;-14269 34584 16; -13768 30531;-13817 30906;-13562 31237 1;-13796 31518;-13962 31849;-14022 32215 1;-14129 32861;-14176 33346;-13790 33874;-14206 34178;-14269 34584;-14684 34542;-14151 30463;-13768 30531 18; -12864 29516;-12555 29556;-12690 30597;-12759 30634 1;-12947 30722;-13123 30832;-13280 30960;-13417 30778;-13389 30566;-13043 30589;-12864 29516 18; -12658 30576;-12526 29554; -1613 33191;-2495 32550;-3340 32988;-3606 32951;-4716 32001;-6517 32581;-1613 33191 18; -1851 30119;-1579 30391;-1691 31303;-2465 31213;-10060 30328;-11435 29687;-12456 29579;-13986 29400;-13489 25830;-11430 26092;-11744 28814;-1851 30119 18; -11638 32113 1;-11995 32060;-12327 32306;-12380 32663 1;-12433 33019;-12187 33351;-11830 33404 1;-11474 33457;-11142 33211;-11089 32855 1;-11036 32498;-11282 32166;-11638 32113 18; -11731 32748; -11638 32113 1;-11995 32060;-12327 32306;-12380 32663 1;-12433 33019;-12187 33351;-11830 33404 1;-11474 33457;-11142 33211;-11089 32855 1;-11036 32498;-11282 32166;-11638 32113 18; -1549 27329;-1898 30094;-1834 30136;-1579 30391;-1691 31303;-2465 31213;-10060 30328;-11435 29687;-12456 29579;-12544 29569;-12617 30572 1;-12194 30404;-11718 30344;-11247 30427 1;-10265 30602;-9722 31218;-9480 32186;-6452 32568;-6323 32518;-4716 32001;-3606 32951;-3340 32988;-2495 32550;-1613 33191;-1337 33258;-670 33346 1;-337 33390;-107 33079;-53 32748;21 32125;-1133 31988;-898 30072;262 30214;327 29685;-832 29543;-620 27815;561 27960;628 27412;-814 27236;-892 27871;-1328 27817;-1274 27379;-1416 27362;-1549 27329 18; -9879 39055 32;-9170 39151 32;-8940 37460 32;-9649 37364 32;-9879 39055 50; -4419 39767;-3222 39896;-3207 39726;-2852 37174;-2558 37222;-2309 35634;-2649 35578;-2544 34692;-2483 34699;-2275 33135;-9480 32186 1;-9722 31218;-10265 30602;-11247 30427 1;-11289 30420;-11331 30413;-11374 30408;-11591 32122 1;-11261 32196;-11038 32514;-11089 32855 1;-11104 32958;-11143 33052;-11199 33131;-10106 34303 1;-9907 34142;-9769 34067;-9631 33852;-9076 33926;-9202 34870;-7034 35159;-6911 34239;-6530 34290;-6652 35202;-4133 35539;-4043 34868 1;-4019 34693;-3859 34571;-3684 34594;-3843 35855;-8616 35234;-8692 35765;-8692 35764;-7975 35873;-7150 36995;-6806 36792;-6595 37085;-6451 36986;-5456 36244;-3945 36467;-4286 38931;-4419 39767 18; -9655 37361;-8949 37461;-8687 35777;-8600 35250;-8734 35218;-9468 35123;-10106 34303 1;-10097 34296;-10088 34289;-10080 34282;-11157 33062 1;-11169 33086;-11184 33109;-11199 33131;-11289 33235 1;-11429 33366;-11626 33434;-11830 33404 1;-12187 33351;-12433 33019;-12380 32663 1;-12327 32306;-11995 32060;-11638 32113 1;-11610 32117;-11582 32123;-11555 32131;-11343 30401;-11482 30397 1;-12145 30346;-12797 30567;-13280 30960;-13417 30778;-13388 30562;-13768 30497;-13781 30632;-13817 30906;-13562 31237 1;-13796 31518;-13962 31849;-14022 32215 1;-14129 32861;-14176 33346;-13790 33874;-14206 34178;-14269 34584;-13881 34632;-13858 34525;-13842 34424;-13520 34190;-13267 34406;-13470 35887;-13175 35940;-13139 35839 1;-13123 35789;-13101 35740;-13076 35700;-12700 35058;-12533 34842 1;-11752 35198;-11028 35149;-10345 34629;-9965 35128;-10060 35831;-9551 36500;-9578 36699;-9655 37361 18; -7735 46820 1;-7764 47056;-8003 47282;-8240 47244 1;-8552 47194;-8712 47169;-9024 47119 1;-9351 47067;-9608 46633;-9572 46327;-9419 45411 1;-9363 45078;-8989 44715;-8699 44753;-8120 44830;-8161 45140;-8733 45052 1;-8928 45022;-9070 45206;-9100 45401 1;-9158 45778;-9217 45985;-9267 46363 1;-9295 46576;-9183 46792;-8971 46826 1;-8680 46873;-8516 46899;-8225 46946 1;-8124 46962;-8020 46885;-8011 46783;-7735 46820 18; 9644 43757 32;8944 43374 32;9775 41855 32;10475 42238 32;9644 43757 50; 2403 36394;2572 35417 1;2818 35459;3125 35546;3088 35793;2983 36495;2403 36394 18; 2984 36488;5159 36856;5586 36023;5852 36167;5316 37170;2367 36668;2572 35417 1;2818 35459;3125 35546;3088 35793;2984 36488 18; 18495 36807 32;17571 36301 32;17824 35840 32;18748 36346 32;18495 36807 50; 18495 36807 32;17571 36301 32;17824 35840 32;18748 36346 32;18495 36807 50; 18139 36313; 19782 33314; 20138 33808 32;19214 33302 32;19467 32841 32;20391 33347 32;20138 33808 50; 20138 33808 32;19214 33302 32;19467 32841 32;20391 33347 32;20138 33808 50; 20348 32281; 20704 32775 32;19780 32269 32;20076 31729 32;21000 32235 32;20704 32775 50; 20704 32775 32;19780 32269 32;20092 31701 32;21016 32207 32;20704 32775 50; 21916 29419; 22272 29913 32;21348 29407 32;21601 28946 32;22525 29452 32;22272 29913 50; 22272 29913 32;21348 29407 32;21601 28946 32;22525 29452 32;22272 29913 50; 23585 27518 32;22661 27012 32;22914 26551 32;23838 27057 32;23585 27518 50; 23229 27024; 23585 27518 32;22661 27012 32;22914 26551 32;23838 27057 32;23585 27518 50; 23047 26614;23661 25494;24702 26064;25459 24683; 25113 24171; 25469 24665 32;24545 24159 32;24798 23698 32;25722 24204 32;25469 24665 50; 25469 24665 32;24545 24159 32;24798 23698 32;25722 24204 32;25469 24665 50; 26851 22054 32;25927 21548 32;26180 21087 32;27104 21593 32;26851 22054 50; 26851 22054 32;25927 21548 32;26180 21087 32;27104 21593 32;26851 22054 50; 26495 21560; 28535 19036 32;27611 18530 32;27864 18069 32;28788 18575 32;28535 19036 50; 28535 19036 32;27611 18530 32;27864 18069 32;28788 18575 32;28535 19036 50; 28179 18542; 28283 19495 32;27359 18989 32;27612 18528 32;28536 19034 32;28283 19495 50; 27359 18989;26180 21087; 26015 21594;24886 23744; 22783 27084;21754 29043; 21509 29508;20253 31798; 19346 33382;17933 35908; 17658 36363;16527 38732; 20391 33347;20704 32775; 16120 30345 32;15767 30997 32;17858 32130 32;18211 31478 32;16120 30345 50; 17721 42356;16000 41517;15895 41314;15346 40999;16080 39549;16217 39579;16484 39693 1;16715 39808;16997 39720;17113 39489;17283 39099;16530 38729;17659 36360;17744 36396;18495 36807 32;18748 36346 32;17943 35905;18017 35758;19346 33382;19473 33444;20138 33808 32;20391 33347;20462 33217;20704 32775;20827 32551;21016 32207 32;20253 31789;20371 31584;21509 29508;21651 29573;22272 29913 32;22525 29452 32;21743 29024;21891 28782;22783 27084;22885 27135;23585 27518 32;23838 27057 32;23034 26617;23107 26505;23661 25494;24702 26064;25459 24683;25532 24551;25722 24204 32;24885 23746;24993 23540;26015 21594;26158 21675;26851 22054 32;27104 21593 32;26180 21087;26342 20799;27359 18989;27490 19061;28283 19495 32;28536 19034;28787 18577;30872 18148;31977 18669;31083 20566;30310 20140 32;30057 20601;29897 20882;29031 22424;28943 22584;28778 22885 32;29544 23305;29356 23599;28334 25321;28189 25253;27551 24904 32;27298 25365 32;28072 25789;27913 26014;26538 28306;25881 27965;25744 27890 32;25491 28351 32;26221 28760;26080 29024;25162 30590;24379 30176;24378 30175 32;24125 30636 32;24047 30786;23179 32258;23085 32428;22926 32719 32;23706 33146;22856 34666;22089 34246 32;21836 34707 32;22617 35135;21727 36761;20945 36333 32;20692 36794 32;21474 37222;19986 39969;19346 39618 32;19093 40079;17721 42356 18; 16117 39536;16335 39100;17113 39489 1;16997 39720;16715 39808;16484 39693;16117 39536 18; 14008 41476;14939 41877;15377 40995;16124 39545;16556 38675;17655 36368;17585 36316;17838 35827;17951 35897;19347 33376;19269 33332;19214 33302 32;19467 32841 32;20391 33347 32;20381 33364;20700 32774;20578 32706;19780 32269 32;20076 31729 32;20234 31816;21502 29503;21449 29462;21348 29407 32;21601 28946 32;21768 29037;22770 27072;22661 27012 32;22914 26551 32;23028 26613;23648 25499;23002 25141;20438 29529;19068 31946;18196 31492;17838 32147;18645 32657;14008 41476 18; 14022 41454;14414 40791;14307 40727;11980 39455 32;12093 39249;11422 38968;11125 39055;10611 39980;9677 39483;9695 39343;8630 38837;8591 38896;8449 39163 32;8081 38968 32;8267 38617;7797 38397 32;7621 38728 32;5478 37590 32;5654 37259 32;5664 37265;6162 36342;5847 36176;5316 37170;2367 36668;2572 35417;1761 35291;1500 37304;1320 38549;2166 38662;2323 37240;3021 37633;3078 37533 1;3139 37468;3212 37431;3305 37449 1;3627 37510;3808 37545;4130 37606 1;4332 37644;4430 37723;4609 37824 1;4847 37958;4829 38279;4697 38518;5124 38810;5181 38669;5392 38286 32;8309 39895 32;8048 40368 32;8526 40634;8571 40524;8776 40153 32;10305 40996 32;10044 41469 32;9799 41855;10471 42247;11640 40154;14022 41454 18; 15206 39226;14701 40231;12270 38905;12067 39275;11363 39010;10093 38350;9544 39164;8242 38424;8280 38336;8712 37522;8294 37300;7835 38166;7804 38171;6737 37610;7094 36894;6169 36389;5861 36179;5573 36048;5159 36856;2984 36488;3088 35793 1;3125 35546;2818 35459;2572 35417;2569 35435;1764 35315;1695 35281 1;1482 35258;1197 35282;1164 35475;1069 36115;-96 35919;23 35023;-446 34960;-563 35852;-2064 35609;-1969 35033 1;-1941 34863;-2070 34757;-2240 34729;-2544 34692;-2338 33125;-2199 33145;-670 33346 1;-337 33390;-107 33079;-53 32748;18 32149;1624 32268;1606 32404;1574 32636;1519 32995 1;1470 33314;1689 33613;2008 33661;2511 33738;2673 32684;3023 32650;5028 33002;4999 33114;4864 33919 1;4846 34026;4949 34133;5056 34151 1;5162 34169;5268 34099;5286 33993;5434 33117;5614 33070;6804 33255;7649 33681;7587 33800;7194 34636 1;7146 34738;7191 34860;7293 34908 1;7413 34964;7555 34913;7612 34793;8001 33967;8081 33915;9611 34748;9559 34846;9174 35666 1;9126 35768;9171 35890;9273 35938 1;9393 35994;9535 35943;9592 35823;9981 34997;10011 34981;11295 35647;11800 35912;12905 33863;13614 34258;11992 37281;12954 37805;15206 39226 18; 11118 39071;11371 39205;11518 38927;10014 38130;9521 39060;8251 38390;8712 37522;8294 37300;7836 38163;6759 37589 16; 10806 39671;11072 39819;10735 40425;9234 39590;9400 39292;8632 38864;8449 39192;8047 38968;8215 38667;7784 38426;7608 38740;5525 37573;5668 37318; -21488 20374;-23910 20047 32;-23601 17763 1;-23453 16672;-22327 16155;-21226 16160;-19673 16172 32;-19863 17543 16; -20576 18146;-18895 18374; -19721 18127;-19459 16196; -19063 17068 32;-18104 17198 32;-18038 16710 32;-18997 16580 32;-19063 17068 50; -19283 16500;-19027 16535;-19131 17301;-19393 17265; -19354 16517 32;-19055 16558 32;-19153 17272 32;-19452 17231 32;-19354 16517 50; -20591 18325;-20534 17906; -18916 18553;-18859 18134; -19258 16162;-14595 16174 32;-14810 17869; -15272 16732; -14740 17837;-15209 21217;-16122 21550;-16159 21772; -16322 21740;-16734 24798; -16471 21732 1;-16404 21204;-16120 20956;-16088 20425 1;-16038 19598;-16330 19143;-16310 18315;-18309 18216;-18383 18648;-19198 24496;-16892 24808;-16471 21732 18; -20778 36401 32;-23598 36028 32;-21252 18274 32;-18431 18647 32;-18454 18820;-18136 18862 32;-18263 19829 32;-18582 19787;-18721 20839;-18402 20881 32;-18604 22419 32;-18924 22377;-19067 23455;-18746 23497 32;-18873 24464 32;-19194 24422;-19228 24677;-18907 24719 32;-19034 25686 32;-19356 25643;-19495 26695;-19173 26738 32;-19375 28276 32;-19698 28233;-19841 29311;-19517 29354 32;-19644 30321 32;-19969 30278;-20006 30561;-19681 30604 32;-19808 31571 32;-20134 31528;-20273 32580;-19947 32623 32;-20149 34161 32;-20476 34118;-20619 35196;-20291 35239 32;-20418 36206 32;-20746 36163;-20778 36401 50; -24341 32159;-24276 31678 32;-23700 27422; -19212 24546;-13733 25266; -16161 21819;-16492 21767;-16462 21672 1;-16382 21184;-16119 20935;-16088 20425 1;-16038 19598;-16330 19143;-16310 18315;-18309 18216;-18381 18639;-18909 18574;-18900 18432;-18859 18134;-19528 18077;-19406 17266;-19135 17318;-19092 17091;-18933 17086;-18104 17198 32;-18038 16710 32;-18997 16580 32;-19083 16515;-19284 16498;-19231 16201;-14678 16219;-14739 17868;-14758 17970;-15209 21217;-16122 21550;-16128 21586;-16161 21819 18; -18393 18616;-18366 18552;-18309 18216;-16310 18315 1;-16330 19143;-16038 19598;-16088 20425 1;-16120 20953;-16401 21201;-16470 21723; -14205 29164 1;-14636 28978;-14701 28506;-14637 28041 1;-14548 27396;-14543 27027;-14415 26388 1;-14333 25977;-14034 25818;-13995 25401;-13724 25426;-14205 29164 18; -14266 29470 1;-14608 29576;-14771 29877;-14771 30235 1;-14771 30493;-14620 30613;-14475 30827;-14266 29470 18; -17473 53801 1;-17810 53580;-17929 53202;-17897 52801;-15147 32020 1;-15106 31711;-14847 31471;-14537 31505;-17473 53801 18; -14686 30514;-14695 30519 1;-14741 30438;-14771 30350;-14771 30235 1;-14771 29995;-14698 29780;-14546 29633; -14476 28953;-14480 28955 1;-14662 28722;-14684 28380;-14637 28041 1;-14548 27396;-14543 27027;-14415 26388 1;-14358 26104;-14198 25940;-14091 25728; -33774 13716;-37719 13698 1;-37711 13021;-37614 12645;-37483 11980 1;-37408 11596;-37160 11127;-36769 11124 1;-35614 11116;-34913 11107;-33758 11107;-33774 13716 18;-34709 12742 32;-34706 12049 32;-35396 12046 32;-35399 12739 32;-34709 12742 50; -13205 21917 32;-14426 21755 32;-14863 25056 32;-13642 25218 32;-13205 21917 50; -12560 16334;-13579 16365;-13710 17307; -14393 21751;-13799 17276; -13570 25291;-14183 29910 32;-14861 35024 16; -13124 21925;-13570 25291 16; -14388 21755;-13216 21915;-12513 16450;-13673 16499;-14388 21755 18; -20236 15666;-20219 16111;-19669 16190;-19268 16277;-19207 16172;-14584 16172;-14749 17882;-14758 17968;-14758 17970;-15209 21217;-16122 21550;-16128 21586;-16161 21819;-16187 21815;-16572 24747;-14819 25000;-13659 16192;-12511 16216;-12400 15688;-20236 15666 18; -12360 16163;-13124 21925 16; -1093 23897;-427 23981;-761 26630;689 26813;756 26286;-277 26155;-16 24099;1038 24233;1815 18125;2204 18174;2387 16720 1;185 16681;-1050 16770;-3252 16818 1;-3465 16823;-3679 16945;-3679 17158 1;-3679 17386;-3679 17515;-3679 17743 1;-3679 17961;-3432 18019;-3217 18057 16; -923 22688 32;363 22852 32;972 18064 32;-314 17900 32;-923 22688 50; -2995 17564 1;-2963 17358;-2847 17306;-2638 17302 1;-1551 17280;-1003 17299;84 17310 1;494 17314;872 17655;816 18061; -3495 20628 32;-3254 18732 32;-4354 18592 32;-4364 20517 32;-3495 20628 50; -1249 26591;-1009 24704; -1954 26449 32;-2063 27309 32;-3917 27074 32;-3808 26214 32;-1954 26449 50; -6289 26728 1;-6254 26482;-6089 26235;-5844 26274 1;-5709 26296;-5617 26358;-5565 26484;-4510 26632;-4553 26963; -6289 26728 1;-6254 26482;-6089 26235;-5844 26274 1;-5709 26296;-5617 26358;-5565 26484;-4510 26632;-4553 26963;-6289 26728 18; -3594 21735;-5281 23054;-5570 25479;-5103 26076;-4235 26194 16; -5833 22814;-5690 23016;-5701 23106;-5962 25264;-7975 25046 16; -3594 21735;-5281 23054;-5570 25479;-5103 26076;-4235 26194;-3594 21735 18; -8163 25054;-7827 22534; -8264 24198 1;-8597 24088;-8820 23838;-8790 23575 1;-8760 23305;-8474 23108;-8114 23082 16; -7686 23133 1;-7346 23240;-7117 23494;-7147 23760 1;-7178 24036;-7478 24237;-7852 24254 16; -8798 23544 1;-8739 23133;-8321 22852;-7863 22917 1;-7406 22982;-7082 23368;-7140 23780 1;-7199 24191;-7617 24472;-8075 24407 1;-8533 24342;-8856 23956;-8798 23544 18; -9462 23452 1;-9377 22834;-8643 22425;-7821 22537 1;-6999 22648;-6400 23239;-6485 23858 1;-6569 24476;-7304 24885;-8126 24773 1;-8948 24660;-9547 24069;-9462 23452 18; -8359 24996;-9556 24843;-9836 24477;-10386 24898 1;-10426 25195;-10562 25391;-10831 25523;-10196 26220; -12913 24671 1;-12853 24322;-12473 24099;-12066 24169 1;-11659 24241;-11380 24581;-11441 24930 1;-11501 25279;-11881 25502;-12288 25432 1;-12695 25360;-12974 25019;-12913 24671 18; -12197 24384; -11686 24623; -12746 24960 1;-12609 25141;-12491 25255;-12266 25288 1;-12072 25317;-11899 25278;-11738 25165; -12913 24671 1;-12853 24322;-12473 24099;-12066 24169 1;-11659 24241;-11380 24581;-11441 24930 1;-11501 25279;-11881 25502;-12288 25432 1;-12695 25360;-12974 25019;-12913 24671 18; -10759 23249; -11405 23755;-11283 22560 1;-11269 22427;-11151 22330;-11018 22344 1;-10753 22371;-10605 22387;-10340 22415 1;-10205 22429;-10108 22550;-10122 22685 1;-10172 23160;-10199 23427;-10248 23902 1;-10263 24050;-10395 24157;-10543 24142 1;-10809 24115;-10959 24100;-11225 24073 1;-11337 24062;-11418 23962;-11407 23850;-11405 23755 18; -4485 20695 1;-4478 21501;-4860 22059;-5584 22413; -12912 21079;-11290 21279;-11320 21519 1;-11334 21634;-11274 21758;-11159 21768 1;-10163 21855;-9602 21647;-8603 21611 1;-7987 21589;-7653 21756;-7041 21829 1;-6547 21887;-5956 21861;-5652 22235 16; -10483 18366;-12629 18356; -4516 20660;-4547 21234 1;-4675 21731;-4998 22104;-5500 22370;-5688 22194 1;-6001 21863;-6566 21885;-7041 21829 1;-7653 21756;-7987 21589;-8603 21611 1;-9602 21647;-10163 21855;-11159 21768 1;-11274 21758;-11334 21634;-11320 21519;-11290 21279;-12912 21079;-12568 18436;-10509 18488;-10483 20634;-4516 20660 18; -13415 24086;-12712 23926;-12391 21125;-13033 21051;-13415 24086 18; -10823 25530;-11793 25369 1;-11936 25437;-12110 25463;-12288 25432 1;-12695 25360;-12974 25019;-12913 24671 1;-12888 24528;-12841 24418;-12729 24328;-12713 23926;-13285 24056;-13489 25830;-11430 26092;-11333 26064;-10210 26212;-10823 25530 18; -7129 26640;-7744 25849;-8763 25721;-9540 26325; -7129 26640;-7744 25849;-8763 25721;-9540 26325;-7129 26640 18; -3476 20640;-4365 20517;-4488 20842 1;-4525 21539;-4874 22038;-5500 22370;-5512 22528;-5833 22814;-5690 23016;-5701 23106;-5962 25264;-7825 25062;-7985 25077;-8384 25020;-9535 24846;-9556 24843;-9836 24477;-10386 24898 1;-10426 25195;-10562 25391;-10831 25523;-10196 26220;-9808 25980;-9507 26294;-8763 25721;-7744 25849;-7129 26640;-6772 26447;-6511 26726;-6301 26731;-6266 26624 1;-6205 26418;-6054 26241;-5844 26274 1;-5709 26296;-5617 26358;-5565 26484;-4510 26632;-4553 26963;-3908 27070;-3810 26231;-4344 26179;-5103 26076;-5570 25479;-5281 23054;-3649 21778;-3476 20640 18; -10828 25526;-10942 25510;-11755 25375;-11751 25347 1;-11589 25257;-11472 25111;-11441 24930 1;-11380 24581;-11659 24241;-12066 24169 1;-12319 24125;-12562 24195;-12724 24338;-12724 24193;-12713 23926;-12727 23929;-12705 23865;-12391 21125;-11290 21279;-11320 21519 1;-11334 21634;-11274 21758;-11159 21768 1;-10163 21855;-9602 21647;-8603 21611 1;-7987 21589;-7653 21756;-7041 21829 1;-6578 21883;-6030 21864;-5713 22169;-5634 22206;-5486 22545;-5803 22856;-5690 23016;-5701 23106;-5962 25264;-7975 25046;-9561 24836;-9836 24477;-10386 24898 1;-10426 25195;-10562 25391;-10831 25523;-10828 25526 18;-6485 23858 1;-6400 23239;-6999 22648;-7821 22537 1;-8643 22425;-9377 22834;-9462 23452 1;-9547 24069;-8948 24660;-8126 24773 1;-7304 24885;-6569 24476;-6485 23858 18;-10248 23902 1;-10199 23427;-10172 23160;-10122 22685 1;-10108 22550;-10205 22429;-10340 22415 1;-10605 22387;-10753 22371;-11018 22344 1;-11151 22330;-11269 22427;-11283 22560;-11405 23755;-11407 23850 1;-11418 23962;-11337 24062;-11225 24073 1;-10959 24100;-10809 24115;-10543 24142 1;-10395 24157;-10263 24050;-10248 23902 18; -4309 17838;-4305 16940;-4650 16947;-5531 17838; -6190 17881;-5548 17239 1;-5677 17110;-5779 16959;-5962 16961 1;-6166 16963;-6264 17183;-6264 17387;-7430 17387;-7430 17134 1;-7430 17038;-7507 16961;-7603 16961 1;-7721 16961;-7769 17047;-7855 17128;-8634 17861; -9324 17880;-9323 17812;-8723 17259 1;-8877 17100;-8992 16928;-9213 16936 1;-9423 16944;-9496 17183;-9496 17393;-10193 17393;-10193 16739;-10452 16739;-10570 17060 1;-10651 17279;-10892 17282;-11125 17282; -11081 16881 1;-10981 16886;-10879 16833;-10872 16733 1;-10853 16486;-10953 16220;-11199 16190 1;-11703 16128;-12054 16158;-12562 16171; -11081 16881 1;-10981 16886;-10879 16833;-10872 16733 1;-10853 16486;-10953 16220;-11199 16190 1;-11703 16128;-12054 16158;-12562 16171;-12361 16473;-11154 16592;-11081 16881 18; -10451 16310;-10926 15680;-10155 15693;-9670 16316;-10451 16310 18; -9680 16316;2231 16207; -6776 16278;-6410 15851;-5808 15855;-5485 16278; -2810 16247;-2444 15820;-1842 15824;-1519 16247; 920 16210;1286 15783;1888 15787;2211 16210; -6776 16278;-6410 15851;-5808 15855;-5485 16278;-6776 16278 18; -2810 16247;-2444 15820;-1842 15824;-1519 16247;-2810 16247 18; 920 16210;1286 15783;1888 15787;2211 16210;920 16210 18; -10451 16310;-10926 15680;-10155 15693;-9667 16322;-10451 16310 18; 1594 15989; -2157 15995; -6129 16040; -10299 15996; -12496 18271;-10510 18284;-10485 17858;-9289 17852;-9263 17754;-8726 17259 1;-8880 17100;-8992 16928;-9213 16936 1;-9423 16944;-9496 17183;-9496 17393;-10193 17393;-10193 16739;-10452 16739;-10570 17060 1;-10651 17279;-10892 17282;-11125 17282;-11165 17458;-11172 17568 32;-12368 17497 32;-12496 18271 18; -6190 17881;-5548 17239 1;-5677 17110;-5779 16959;-5962 16961 1;-6166 16963;-6264 17183;-6264 17387;-7430 17387;-7430 17134 1;-7430 17038;-7507 16961;-7603 16961 1;-7721 16961;-7769 17047;-7855 17128;-8634 17861;-6190 17881 18; -4309 17838;-4305 16940;-4650 16947;-5531 17838;-4309 17838 18; -8831 17613;-8315 17127; -8776 17671;-8260 17185; -8933 17450;-8459 17014; -5801 17491;-5328 17050; -5706 17690;-5190 17204; -4316 17833;-3674 17836; -4311 17397;-3687 17399; -4309 16939;-3583 16942; -12473 15690;-12333 15682;-10916 15685;-10795 15853;-10451 16310;-9670 16316;-9112 16311;2309 16206;2387 16720 1;185 16681;-1050 16770;-3252 16818 1;-3465 16823;-3679 16945;-3679 17158 1;-3679 17386;-3679 17515;-3679 17743 1;-3679 17961;-3432 18019;-3217 18057;-3266 18731;-4328 18595;-4309 17732;-4305 16940;-4650 16947;-5522 17829;-5843 17547;-5743 17437;-5533 17241;-5675 17106 1;-5756 17025;-5842 16960;-5962 16961 1;-6166 16963;-6264 17183;-6264 17387;-7430 17387;-7430 17134 1;-7430 17038;-7507 16961;-7603 16961 1;-7721 16961;-7769 17047;-7855 17128;-8629 17856;-8976 17522;-8831 17359;-8723 17259 1;-8733 17248;-8744 17237;-8754 17226;-8796 17184 1;-8918 17050;-9027 16929;-9213 16936 1;-9423 16944;-9496 17183;-9496 17393;-10193 17393;-10193 16739;-10452 16739;-10570 17060 1;-10651 17278;-10890 17282;-11123 17282;-11081 16880;-11081 16881 1;-10981 16886;-10879 16833;-10872 16733 1;-10853 16486;-10953 16220;-11199 16190 1;-11703 16128;-12054 16158;-12562 16171;-12473 15690 18; -437 24057;-16 24099;1038 24233;1815 18125;2204 18174;2387 16720 1;185 16681;-1050 16770;-3252 16818 1;-3465 16823;-3679 16945;-3679 17158 1;-3679 17386;-3679 17515;-3679 17743 1;-3679 17961;-3432 18019;-3217 18057;-3067 17645;870 18181;298 22745;-928 22597;-1093 23897;-427 23981;-437 24057 18; -16 24099;-437 24057;-761 26630;689 26813;756 26286;-277 26155;-16 24099 18; 3866 16732 32;3439 16673 32;3223 18249 32;3650 18308 32;3866 16732 50; 5383 27283;4975 27228; 4544 27166;3540 27030;4808 17640; 3519 27113;3263 27101;2320 26974;2388 26471;3449 26614;3721 24597;2659 24454;2723 23981;3786 24125;4109 21744;3012 21595;3086 21048;4178 21196;4514 18720 1;4337 18696;4237 18682;4060 18658 1;3812 18624;3638 18397;3672 18149;3869 16716;4439 16734;4408 17018;4778 17647;3519 27113 18; -1143 31999;17 32122;17 32178;1621 32314;1633 32264;2731 32425;2981 30545;2877 30539;1858 30401;1923 29919;3020 30067;3103 29612;3117 29485;3200 28846;2099 28680;2212 27782;4842 28153;5807 28286;5929 27553;5380 27404;4972 27323;4998 27106;4550 27045;4520 27257;4247 27234;2320 26974;2388 26471;3449 26614;3721 24597;2659 24454;2723 23981;3786 24125;4109 21744;3012 21595;3086 21048;4178 21196;4514 18720 1;4337 18696;4237 18682;4060 18658 1;3812 18624;3638 18397;3672 18149;3869 16716;2386 16726;2204 18174;1815 18125;1038 24233;-16 24099;-277 26155;756 26286;689 26813;-761 26630;-427 23981;-1093 23897;-1439 26543;-1957 26476;-2055 27277;-1446 27358;-1274 27379;-1328 27817;-892 27871;-814 27236;628 27412;561 27960;-620 27815;-832 29543;327 29685;262 30214;-898 30072;-1143 31999 18; 20293 29461;22893 25029; 23697 23687;24292 22575 1;25154 20964;25579 20031;26412 18405 1;26481 18270;26459 18100;26381 17967 1;26308 17843;26186 17751;26045 17751;25496 17749; 22638 25100;21893 24684; 23628 23429;22855 22998; 19436 29034 32;20221 29471 32;22623 25161 32;21838 24724 32;19436 29034 50; 18662 17992 32;18662 17582;19024 17581;19026 17986 48; 19384 17986 32;19386 17581;25150 17584;25151 17950 16; 20763 18521;19615 18529; 20181 18361;20176 17593;19400 17589;19387 18003;19600 18007;19618 18356;20181 18361 18; 18663 18025 32;19038 18019 32;19031 17580 32;18656 17586 32;18663 18025 50; 23562 18465;24944 18625;24969 18008;25147 17973;25151 17579;20176 17589;20176 17650;20181 18361;20170 18361;23507 18378;23562 18465 18; 22860 22956;23773 23419;26438 18231;26253 17812;25686 17830;25661 18571;24970 18626;24892 18619;23562 18465;23536 18583;23536 21082 1;23536 21579;23463 21851;23218 22284;23193 22329;22860 22956 18; 18947 24437;18171 24011;19504 21440;20333 21869; 18163 25930;17281 25442;15743 28155;16663 28655; 14320 28409 1;13999 28965;13463 29069;12822 29084;13772 29559;13609 29885;13949 30055;14727 28644;14320 28409 18; 12593 24011; 11606 26552; 14898 24038; 20786 20812;18674 19895;18666 19032 16; 19648 19018;19662 19920;20789 20369; 19654 18679;19654 19907;20826 20381;20789 18673;19654 18679 18; 20783 20813;20802 20381;20721 20339;19654 19907;19654 18969;18661 19000;18667 19129;18674 19895;20783 20813 18; 18297 20583; 15186 18101;15181 17581;12326 17568;12330 18081 16; 17446 20755;17939 20751;17937 20666 1;17934 20651;17933 20635;17933 20619;17899 17581;16014 17579;16019 18095;17381 18103;17446 20755 18; 15186 18101;15181 17581;12326 17568;12330 18081;15186 18101 18; 18947 24437;18171 24011;19504 21440;20333 21869;18947 24437 18; 16714 24599; 18163 25940 32;17257 25448 32;16344 27128 32;17250 27620 32;18163 25940 50; 17262 27618 32;16319 27105 32;15748 28155 32;16691 28668 32;17262 27618 50; 16276 29368;15463 28926;15036 29712;15592 30014; 15810 30174;15573 30045; 15856 30187 32;16267 29433 32;15562 29049 32;15151 29803 32;15856 30187 50; 12875 29104 32;12555 29699 32;11958 29378 32;12278 28783 32;12875 29104 50; 12875 29104 32;12555 29699 32;11958 29378 32;12278 28783 32;12875 29104 50; 12931 27608; 12321 28367 1;13033 28747;14002 28430;14318 27687;13856 27442;14222 26788;13243 26242;12882 26888;12205 26509 1;11869 27110;11622 27994;12321 28367 18; 14009 31894 32;14483 31011 32;13048 30241 32;12574 31124 32;14009 31894 50; 11895 30765;12060 30457;11008 29893;11203 29530;9054 28376 1;8861 28273;8620 28345;8516 28538;8334 28440;8278 28410 1;8126 28329;7951 28463;7892 28625; 11895 30765;12060 30457;11008 29893;11203 29530;9054 28376 1;8861 28273;8620 28345;8516 28538;8334 28440;8278 28410 1;8126 28329;7951 28463;7892 28625;11895 30765 18; 5921 27550 32;7268 27732 32;7173 28435 32;5826 28253 32;5921 27550 50; 8009 27844 32;8473 27907 32;8760 25778 32;8296 25715 32;8009 27844 50; 8009 27844 32;8469 27906 32;8756 25777 32;8296 25715 32;8009 27844 50; 8426 24754 32;8886 24816 32;9395 21039 32;8935 20977 32;8426 24754 50; 8426 24754 32;8890 24817 32;9400 21046 32;8936 20983 32;8426 24754 50; 10162 20625;9002 20398;9203 18950;10149 18958;10162 20625 18; 9679 25033;9806 23802;11074 23933;10942 25210;10531 25168; 10282 20755;10289 21612;11811 21600; 11400 21721;11269 22707;11211 22707;11134 22697;10085 21798;11400 21721 18; 11392 21771;12657 22705;12575 22727 1;12257 22771;11930 22800;11528 22748;11256 22713;11392 21771 18; 14842 22746;15156 21709;11381 21790;11483 21838;12657 22705;12729 22705 1;13085 22651;13443 22590;13898 22613 1;14168 22626;14415 22658;14644 22711;14842 22746 18; 17423 20723 32;17465 21700 32;10402 21792 32;10310 20777 32;17423 20723 50; 11817 21781;17456 21696;17443 20772; 17455 20740;17944 20723;17975 20783 1;18035 20895;18154 20970;18291 20969 1;18503 20967;18672 20794;18670 20582;18797 20611;18910 21060;17592 23541;17372 23950;17318 23923 1;17248 23865;17169 23816;17081 23779 1;16820 23670;16537 23695;16308 23824;16261 23759 1;15834 23237;15382 22924;14826 22759;14857 22698;15153 21719;15221 21730;17456 21696;17447 21052;17455 20740 18; 16019 18095;16014 17579;17899 17581;17933 20619 1;17935 20815;18095 20971;18291 20969 1;18503 20967;18672 20794;18670 20582;18797 20611;18910 21060;17366 23965 1;17284 23889;17190 23825;17081 23779 1;16820 23670;16537 23695;16309 23822 1;15658 23002;14943 22665;13898 22613 1;12972 22567;12448 22866;11528 22748;11134 22697;10109 21818 16; 15942 24211 1;15741 24698;15952 25214;16400 25402 1;16424 25412;16447 25421;16474 25432;15152 27818;14760 27586 1;15087 26953;14723 26493;14189 26032 1;13803 25698;13361 25729;12889 25924 1;12776 25971;12689 26056;12583 25994 1;12356 25861;12219 25754;12112 25514 1;11885 25003;11862 24568;12139 24083 1;12598 23279;13457 23008;14354 23237 1;15051 23415;15427 23689;15942 24211 18; 15942 24211 1;15741 24698;15952 25214;16400 25402 1;16424 25412;16447 25421;16474 25432;15152 27818;14760 27586 1;15087 26953;14723 26493;14189 26032 1;13803 25698;13361 25729;12889 25924 1;12776 25971;12689 26056;12583 25994 1;12356 25861;12219 25754;12112 25514 1;11885 25003;11862 24568;12139 24083 1;12598 23279;13457 23008;14354 23237 1;15051 23415;15427 23689;15942 24211 18; 11042 25327;10961 26462;10431 26159;10536 25265;11042 25327 18; 10961 26462;10937 27498;11668 28336;11397 28807;9233 27638;9431 26021;10417 26143;10961 26462 18; 10551 25002;10411 26141;9431 26021;9233 27638;11397 28807;11667 28335;10951 27514;10937 27498;10961 26462;11028 25341 16; 14320 28409 1;13999 28965;13463 29069;12822 29084;13772 29559;13609 29885;13949 30055;14727 28644;14320 28409 18; 14009 31894 32;14483 31011 32;13048 30241 32;12574 31124 32;14009 31894 50; 12321 28367 1;13033 28747;14002 28430;14318 27687;13856 27442;14222 26788;13243 26242;12882 26888;12205 26509 1;11869 27110;11622 27994;12321 28367 18; 13787 32280;14511 32673;15479 30828;15780 30963;16134 30352;15091 29776;15506 29039;16260 29401;16669 28666;16556 28597;15743 28155;17281 25442;18163 25930;18951 24441;18867 24393;18171 24011;19504 21440;20323 21864;20445 21708 1;20639 21320;20735 21058;20780 20806;20629 20744;18674 19895;18666 19032;18648 17576;17902 17588;17899 17625;17933 20619 1;17933 20635;17934 20651;17937 20666;17939 20751;18036 20865 1;18101 20930;18191 20970;18291 20969 1;18503 20967;18672 20794;18670 20582;18797 20611;18910 21060;17592 23541;17372 23950;17318 23923 1;17248 23865;17169 23816;17081 23779 1;16820 23670;16537 23695;16308 23824;16261 23759 1;15865 23275;15447 22970;14944 22797;14593 22701 1;14378 22654;14147 22625;13898 22613 1;13122 22574;12629 22778;11948 22776;12573 23562 1;13048 23172;13691 23068;14354 23237 1;15051 23415;15427 23689;15942 24211 1;15741 24698;15952 25214;16400 25402 1;16424 25412;16447 25421;16474 25432;15991 26304;15914 26442;15152 27818;14760 27586 1;15087 26953;14723 26493;14189 26032 1;13803 25698;13361 25729;12889 25924 1;12776 25971;12689 26056;12583 25994 1;12525 25960;12473 25928;12426 25895;12205 26509;12882 26888;13243 26242;14222 26788;13856 27442;14318 27687 1;14021 28385;13149 28707;12454 28429;12285 28787;12875 29104 32;12949 29080 1;13534 29053;14020 28928;14320 28409;14727 28644;13949 30055;13609 29885;13048 30241 32;14483 31011 32;14014 31885;13787 32280 18; 12577 31116;13053 30269;13616 29899;13773 29563;12870 29118;12560 29702;11962 29380;12271 28788;12302 28788;12480 28455;12382 28398 1;12362 28388;12341 28378;12321 28367 1;11622 27994;11869 27110;12205 26509;12443 25925;12348 25835 1;12250 25753;12176 25657;12112 25514 1;11885 25003;11862 24568;12139 24083 1;12267 23858;12427 23675;12610 23532;12082 22774 1;12038 22775;11993 22776;11948 22776;11707 22766 1;11649 22762;11589 22756;11528 22748;11256 22713;11129 22699;10105 21798;9711 23692;11092 23951;11018 25357;10926 27516;11672 28343;11419 28799;9243 27644;9233 27638;9431 26021;10417 26143;10543 25018;8421 24759;8285 25721;8754 25777;8470 27911;7261 27750;7175 28435;7884 28620;7938 28535 1;8018 28419;8156 28345;8278 28410;8334 28440;8516 28538 1;8620 28345;8861 28273;9054 28376;11203 29530;11008 29893;12060 30457;11895 30765;12577 31116 18; 8884 24850;9625 24955;9766 23654;10124 21822;10186 20631;9008 20390;8916 20976;9088 21004;9400 21046 32;8884 24850 18; 21973 24564;23613 25557;23667 25497;24702 26064;25459 24683;25403 24629;24545 24159 32;24798 23698 32;24871 23738;24928 23665;24993 23540;26015 21594;25955 21563;25927 21548 32;26180 21087 32;26212 21030;26342 20799;27359 18989;27490 19061;28283 19495 32;28534 19037;28510 19020;27612 18528;27864 18069;26296 17859 1;26329 17891;26358 17927;26381 17967 1;26459 18100;26481 18270;26412 18405 1;25579 20031;25154 20964;24292 22575;23770 23551;22793 23066;21973 24564 18; 6666 17568;4933 17340;4951 17081;4766 17050;4797 16723;4853 16717;5197 16718;7695 17103;7598 17710;6666 17568 18; 9374 17957;9444 17373;8457 17221;8350 17822;9374 17957 18; 10094 18082;10143 17478;10208 17491;10684 17564;11491 17570;11497 18087;10094 18082 18; 10115 18078;10172 17485;10684 17564;11491 17570;11497 18087 16; 5363 27174;3666 26939;4960 17340;6666 17562;5363 27174 18; 4400 17008;4432 16721;3869 16716;3672 18149 1;3638 18397;3812 18624;4060 18658 1;4237 18682;4337 18696;4514 18720;4178 21196;3086 21048;3012 21595;4109 21744;3786 24125;2723 23981;2659 24454;3721 24597;3449 26614;2388 26471;2320 26974;4536 27273;4565 27044;4998 27104;4967 27327;5380 27387 16; 9359 17942;9430 17372;5197 16718;4795 16719;4758 17066 16; -21096 15663;-21114 14223;-20744 14208;-20139 14209;363 14412 1;636 14412;920 14328;1180 14187;1879 15787;1286 15783;920 16210;876 16219;-1542 16241;-1562 16190;-1842 15824;-2444 15820;-2798 16233;-2875 16254;-5510 16278;-5557 16183;-5808 15855;-6410 15851;-6776 16278;-6976 16291;-9112 16311;-9670 16316;-9679 16316;-9755 16208;-10155 15693;-10884 15681;-19146 15669;-21096 15663 18; 28775 18579;37873 -467 1;38405 -1580;37788 -3004;36608 -3360 1;33415 -4324;31646 -4947;28413 -5769 1;27385 -6030;26816 -6207;25780 -6437;24571 -5425;23301 -5561 1;21105 -5796;19870 -5932;17663 -6017 1;14081 -6155;12069 -6110;8484 -6177 1;6663 -6211;5642 -6176;3821 -6165 1;3563 -5779;3568 -5531;3574 -5166 1;3616 -2764;3608 -1428;3636 974;3562 13963 1;3508 14853;2774 15794;1882 15788 16; 3821 -6165 1;4426 -7007;5113 -7451;6150 -7440;6830 -7440;8069 -6201 16; 7413 -6575; 6551 -6811; 5600 -6811; 4667 -6794; 31356 -3787 1;31428 -3631;31469 -3543;31542 -3387 1;31602 -3258;31792 -3278;31906 -3363 1;32071 -3486;32188 -3534;32313 -3697 1;32424 -3842;32461 -4105;32288 -4165 1;31990 -4267;31818 -4314;31511 -4386 1;31276 -4441;31254 -4007;31356 -3787 18; 30838 -3958; 31305 -4177 1;31286 -4050;31307 -3894;31356 -3787 1;31428 -3631;31469 -3543;31542 -3387 1;31602 -3258;31792 -3278;31906 -3363 1;32071 -3486;32188 -3534;32313 -3697 1;32331 -3721;32347 -3747;32361 -3776; 28208 -4099; 27388 -2773; 35045 -2925; 35211 -3231 1;35294 -3013;35206 -2644;34975 -2681 1;34268 -2795;33881 -2940;33222 -3222 1;32979 -3326;32906 -3761;33135 -3894;35211 -3231 18; 36188 -2864 1;35903 -2666;35626 -2287;35865 -2035 1;36059 -1831;36394 -1747;36580 -1957 1;36750 -2149;36936 -2269;36912 -2524;36188 -2864 18; 36668 -1425; 37311 -1354 1;37319 -1154;37347 -1024;37259 -844 1;37103 -523;37015 -344;36858 -23 1;36824 46;36767 98;36700 125 1;36632 153;36554 154;36482 120 1;36394 79;36335 3;36305 -86 1;36267 -194;36274 -322;36325 -429; 36464 795;35273 3243; 36180 1115 1;35919 960;35672 646;35859 406 1;35992 235;36038 31;36254 17;36655 122;36963 73;36593 875;36180 1115 18; 35294 1773; 31285 3008; 34382 3566; 33466 4875; 32707 4901; 34480 4917;33961 5981; 33584 6796;32893 8209; 33461 6099; 31795 10795 1;31483 10899;31163 11186;30962 10925 1;30787 10699;31154 10515;31338 10296 1;31625 9953;31645 9668;31863 9278 1;31982 9065;32400 9175;32473 9407;31795 10795 18; 28714 8871; 29573 10796; 30904 12302;30323 13504; 29732 13721; 29858 14781 1;29513 14660;29102 14382;28925 14702 1;28760 14999;28654 15319;28314 15322 1;26352 15337;25251 15278;23289 15278 1;23111 15278;23087 15501;23071 15679 1;23052 15890;23356 15896;23568 15897;29300 15924;29858 14781 18; 27625 16334; 25173 16343; 24327 16334; 20476 16271; 18551 16234; 10891 16382; 13395 16419; 14493 16382; 25699 8316 1;25118 7831;24504 7434;24549 6678 1;24614 5591;24896 5007;25265 3982;27053 3973;27056 4618; 26966 7357 1;27049 7617;27225 7801;27498 7811 1;28492 7849;29082 7750;30037 8029 1;30423 8142;30831 7785;30839 7383;26966 7357 18; 25692 8317;25710 4618;27056 4609;27053 3973;25265 3982 1;24896 5007;24614 5591;24549 6678 1;24506 7399;25017 7755;25568 8207;25692 8317 18; 23788 3555;22469 3141; 27486 1059 1;26763 778;26326 144;26431 -625 1;26542 -1441;26676 -1888;26893 -2683;27654 -5465 1;28421 -5273;29129 -5087;29811 -4899;29786 -2570 16; 24803 -223;24384 1199; 26437 -5744 32;26153 -4692 32;25649 -4828 32;25933 -5880 32;26437 -5744 50; 23793 3473;24037 2668;24009 2572 1;23843 2288;23622 2103;23706 1785;24159 -405;24054 -161 1;23777 362;23591 650;23426 1218 1;23286 1698;23171 1958;23069 2448 1;23003 2765;23002 2967;23095 3277;23793 3473 18; 25578 -4940;23223 3303; 23707 522 1;23917 -20;23684 -396;23820 -961;24675 -4843;24981 -5052;25469 -4939;24954 -2967;24126 60;23707 522 18; 24034 2677;24568 1228;24941 -180;26166 -4705;25641 -4843;24019 749;23644 2153;24034 2677 18; 23785 3482;24550 1230; 33176 221 1;33192 495;33172 859;32897 867 1;31292 915;30394 1012;28788 1041;28047 1115;27584 1118;27379 1014 1;26720 714;26331 105;26431 -625 1;26486 -1026;26546 -1339;26619 -1647 1;26635 -1715;26696 -1764;26764 -1750 1;27054 -1692;27453 -1732;27453 -1436;27480 90;33176 221 18; 34758 4719 1;33514 4180;32712 4039;31356 4030;28791 4004; 30692 -2508;30718 -4639 1;32502 -4140;34203 -3591;36436 -2827 1;37252 -2548;37682 -1370;37354 -697;34819 4554 16; 29017 3892;29007 1875; 28820 1903;29187 1901; 29224 4626;29226 4185; 31230 12063 1;30311 11646;29752 11281;28744 11261;27085 11242; 27059 9120;27676 9120;27676 10755;29071 10755;29071 11070; 25658 11917;25661 11444;27115 11452; 23058 11913;23082 7948;23725 3617; 24575 -5415;24810 -4952;25953 -5882 1;26121 -5842;26286 -5802;26450 -5762;24934 -153;24537 1268;23785 3482;23717 3677;23213 7229;23188 7402;23073 7887;23058 11913;25674 11964;25659 11829;25661 11444;26748 11450;27113 11421;29044 11116;29050 11008;29047 10755;27676 10755;27676 9120;27059 9120;27045 10971;25691 10950;25683 8342;25642 8268 1;25064 7786;24504 7431;24549 6678 1;24605 5738;24824 5174;25120 4376;25236 4063 1;25245 4037;25255 4009;25265 3982;27053 3973;27056 4618;29212 4647;29193 4259;29192 4165;28814 3806;27518 3802;27512 1118;27368 1009 1;26716 707;26332 101;26431 -625 1;26486 -1026;26546 -1339;26619 -1647 1;26623 -1664;26630 -1680;26639 -1694;26687 -1922 1;26747 -2153;26814 -2395;26893 -2683;27654 -5465;27886 -5407 1;28565 -5235;29198 -5068;29811 -4899;29786 -2570;30693 -2570;30718 -4639 1;32471 -4149;34143 -3610;36319 -2867;36490 -3396 1;33371 -4339;31606 -4957;28413 -5769 1;27385 -6030;26816 -6207;25780 -6437;24575 -5415 18; 36281 -2878;36438 -3415;36511 -3389 1;36543 -3379;36576 -3370;36608 -3360 1;37788 -3004;38405 -1580;37873 -467;30181 15636;29974 16069;28775 18579;27871 18096;26514 17930;26156 17720;25509 18024;25147 17973;25151 17579;22883 17584;22600 17583;19386 17581;19384 17986 32;19034 17990;19035 17829;19031 17580 32;18656 17586 32;18658 17693;17911 17681;17862 16724;28912 16793;31448 11539;31507 11416;34665 4874;34811 4520;34855 4502;34964 4254;37354 -697 1;37681 -1368;37255 -2541;36443 -2824;36281 -2878 18; 34665 4874;31280 11888 16; 28917 16821;22557 16753;22574 14598;22740 14596;27978 14622 32;27991 11978 32;25681 11967;25660 11836;25659 11829;25661 11444;26748 11450;27113 11421;28509 11201;31335 12145;28917 16821 18;29300 15924;29858 14781 1;29513 14660;29102 14382;28925 14702 1;28760 14999;28654 15319;28314 15322 1;26352 15337;25251 15278;23289 15278 1;23111 15278;23087 15501;23071 15679 1;23052 15890;23356 15896;23568 15897;29300 15924 18; 31396 12034;29071 11070;29071 10755;27676 10755;27676 9120;27263 9120;27053 9123;27016 8383;25692 8317;25696 7432;25672 7334;28966 7334;29064 4040;33271 4176;34900 4829;31396 12034 18;31795 10795;32473 9407 1;32400 9175;31982 9065;31863 9278 1;31645 9668;31625 9953;31338 10296 1;31154 10515;30787 10699;30962 10925 1;31163 11186;31483 10899;31795 10795 18;30839 7383;26966 7357 1;27049 7617;27225 7801;27498 7811 1;28492 7849;29082 7750;30037 8029 1;30423 8142;30831 7785;30839 7383 18; 31803 4718; 29743 3336; 34998 4817;32593 4089;29040 4003;28781 3139;28802 1041 1;30399 1011;31297 915;32897 867 1;33143 860;33185 567;33180 309;33074 203;27436 92;27449 -1484 1;27408 -1728;27037 -1695;26764 -1750 1;26734 -1756;26706 -1750;26682 -1736;26437 -1882;27522 -5719;37034 -2968;37799 -1389;37108 400;34998 4817 18;36180 1115;36593 875;36963 73;36655 122;36254 17 1;36038 31;35992 235;35859 406 1;35672 646;35919 960;36180 1115 18;36580 -1957 1;36750 -2149;36936 -2269;36912 -2524;36188 -2864 1;35903 -2666;35626 -2287;35865 -2035 1;36059 -1831;36394 -1747;36580 -1957 18;35211 -3231;33135 -3894 1;32906 -3761;32979 -3326;33222 -3222 1;33881 -2940;34268 -2795;34975 -2681 1;35206 -2644;35294 -3013;35211 -3231 18; 28745 18634;30882 18172;30962 17951;31452 16882 1;31526 16726;31687 16645;31844 16719;34511 11232;37376 5225;42560 -4791;42893 -6008;43005 -6226;41421 -7040;41362 -6941;41237 -6696 1;41047 -6325;40851 -5941;40647 -5541 1;40215 -4692;38798 -4498;37573 -4879 1;34307 -5894;32347 -6556;29328 -7341;29054 -7412 1;28774 -7484;28485 -7557;28185 -7632 1;27487 -7806;26861 -7951;26271 -8073;25780 -6437 1;26816 -6207;27385 -6030;28413 -5769 1;31646 -4947;33415 -4324;36608 -3360 1;37788 -3004;38405 -1580;37873 -467;32052 11719;28745 18634 18; 17093 14358 32;17340 12322 32;16235 12188 32;15988 14224 32;17093 14358 50; 17835 14424;18181 11569;18871 11179;20413 11389;20142 13967;19130 14578;17835 14424 18; 17155 10696;17691 10758;18956 10018;19073 8852;18592 7994;17408 8630;17155 10696 18; 18202 9313; 19194 12847; 16699 13257; 3821 -6165 1;4426 -7007;5113 -7451;6150 -7440;6830 -7440;8069 -6201;3821 -6165 18; 9218 -3440 32;10927 -3466 32;10916 -4171 32;9207 -4145 32;9218 -3440 50; 9218 -3440 32;10927 -3466 32;10916 -4171 32;9207 -4145 32;9218 -3440 50; 10046 -3806; 18595 7999;19601 7435;19937 4130;21167 4192;22467 3197; 18125 6237;18297 4358;17183 4259;17219 3859;19478 4062;19245 6655;16683 8161;16334 11084;16003 11232;15135 11130;15766 6011;18125 6237 18; 10291 13223;9603 13141; 8409 12286;8323 12964;8939 13047; 9625 13048;9402 13021;9278 14045;13167 14516;14217 13955;14549 11485;13466 11339; 5425 9904;5476 8995; 5250 9963;5304 8986; 5603 9919;5653 9009; 4440 9110; 4467 7984; 4755 11011; 4467 11753; 8021 13453; 9739 13697; 4008 12467;5386 12066;5464 12083;6487 12142;6462 12587 32;7661 12656 32;7686 12210;8363 12249;8377 12540;8323 12964;8879 13039;8888 13209;8792 13970;4035 13359;4008 12467 18; 9045 16387;9007 15203; 14607 15720;12302 15761; 12311 15757;4051 15688; 17824 16709 1;17589 15908;17223 15208;16393 15122 1;15928 15074;15665 15072;15198 15069 1;14834 15067;14673 14729;14570 14380; 14588 15714;16974 15709; 31123 12212;28912 16793;16570 16718;16577 16178;15850 16174;15845 16706;12737 16706;12740 16178;12013 16174;12011 16694;10716 16680;5324 15843;4025 15843;4033 13922;13230 15135;14747 14280;15087 11568;16082 11707;17111 11140;17155 10709 16; 17918 15777 1;19494 15810;20377 15709;21953 15709 1;22027 15709;22069 15613;22045 15542 1;21957 15283;21883 15147;21792 14889 1;21735 14726;21566 14726;21397 14691 1;21251 14660;21169 14538;21163 14389 1;21158 14257;21154 14182;21144 14050 1;21136 13935;20994 13927;20879 13920 1;20636 13905;20499 13892;20256 13896 1;20210 13897;20160 13906;20151 13951 1;20096 14242;20108 14411;20065 14704 1;20032 14931;19924 15155;19695 15160 1;18955 15175;18532 15127;17801 15246 1;17665 15268;17599 15429;17641 15561 1;17682 15692;17781 15774;17918 15777 18; 16826 14605; 15636 14432; 15839 12439; 17093 14358 1;17177 14541;17179 14803;16987 14864;15278 14672;15562 11927;15785 11666;16082 11707;16320 11576;16327 11588 1;16555 11754;16703 11951;16660 12230;16245 12184;15993 14241;17093 14358 18; 17835 14424;18181 11569;18871 11179;20413 11389;20142 13967;19130 14578;17835 14424 18; 17093 14358 32;17340 12322 32;16235 12188 32;15988 14224 32;17093 14358 50; 17155 10696;17691 10758;18956 10018;19073 8852;18592 7994;17408 8630;17155 10696 18; 16068 10716; 16330 8422; 16540 6878; 17377 6990; 18189 7052; 18939 6154; 19052 4828; 21092 7727; 22914 7857;23054 6863; 23046 11518 1;22739 11453;22486 11245;22497 10932 1;22510 10560;22731 10352;23046 10154;23046 11518 18; 20055 11320 1;20220 11035;20268 10844;20316 10518 1;20389 10026;20311 9500;20779 9331 1;21079 9223;21243 9151;21537 9026 1;21594 9002;21646 8998;21695 9035;21644 9141 1;21596 9214;21540 9286;21474 9359 1;21212 9646;21173 9901;21107 10284 1;20993 10948;20932 11321;20776 11976 1;20696 12312;20534 12475;20279 12709;20291 12552;20413 11389;20303 11374;20055 11320 18; 22855 6833 1;22876 6103;22827 5683;22969 4966 1;23089 4358;23094 4002;23257 3404;23728 3544;23709 3728;23256 6930;22855 6833 18; 21158 14273 1;21537 14132;21723 13824;21745 13421 1;21770 12958;21750 12623;21449 12270;21227 12043 1;21095 12696;21151 13208;21107 13974;21158 14273 18; 21107 13974;20156 13930;20279 12709 1;20534 12475;20696 12312;20776 11976 1;20932 11321;20993 10948;21107 10284 1;21173 9901;21212 9646;21474 9359 1;21931 8858;21894 8372;21984 7695 1;22086 6943;22198 6525;22220 5767 1;22226 5552;22133 5415;21967 5279;22133 4293;22150 4302 1;22545 4027;22729 3760;22865 3299;23249 3421;23236 3484 1;23093 4035;23084 4385;22969 4966 1;22827 5683;22876 6103;22855 6833;23122 7885;22686 9525 1;22545 10022;22360 10320;21936 10615 1;21570 10870;21475 11166;21343 11592 1;21066 12485;21161 13041;21107 13974 18; 20275 4808; 22888 8765;22686 9525 1;22545 10022;22360 10320;21936 10615 1;21570 10870;21475 11166;21343 11592 1;21066 12485;21161 13041;21107 13974;21135 14138;21158 14273;21207 14553 1;21246 14622;21310 14673;21397 14691 1;21566 14726;21735 14726;21792 14889 1;21883 15147;21957 15283;22045 15542 1;22069 15613;22027 15709;21953 15709 1;20377 15709;19494 15810;17918 15777 1;17781 15774;17682 15692;17641 15561 1;17599 15429;17665 15268;17801 15246 1;18532 15127;18955 15175;19695 15160 1;19924 15155;20032 14931;20065 14704 1;20079 14605;20088 14521;20094 14441; 17814 14442;17685 15324;17728 15275 1;17749 15260;17774 15250;17801 15246 1;18532 15127;18955 15175;19695 15160 1;19924 15155;20032 14931;20065 14704 1;20108 14411;20096 14242;20151 13951;20107 13988;19130 14578;17814 14442 18; 17915 17608;17933 16751;17761 16725;16570 16718;16577 16178;15850 16174;15845 16706;12737 16706;12740 16178;12013 16174;12011 16694;10716 16680;6602 16041;6151 15971;5324 15843;4025 15843;4033 13922;3562 13901;3562 13963 1;3509 14844;2789 15775;1909 15788;1947 15864;2211 16210;2401 16758;3438 16746;3425 16672;3820 16727;3931 16718;4439 16734;4408 17018;4423 17044;4764 17060;4776 16943;4797 16723;4853 16717;5197 16718;7695 17103;9427 17375;9199 18954;10105 18960;10105 18103;10173 17486;10297 17505;10684 17564;11491 17570;11497 18076;12338 18091;12329 17943;12326 17568;15181 17581;15186 18076;16039 18054;16018 17993;16014 17579;17915 17608 18; 22611 16755;16570 16718;16577 16178;15850 16174;15845 16706;12737 16706;12740 16178;12013 16174;12011 16694;10716 16680;6602 16041;6151 15971;5324 15843;4025 15843;4033 13924;4008 13921;4033 13922;4033 13924;10064 14717;13230 15135;14747 14280;15087 11568;15660 11648;15785 11666;15562 11927;15278 14672;16987 14864 1;17147 14813;17172 14623;17128 14455;17091 14358;17093 14358 32;17340 12322 32;16726 12247;16660 12230 1;16702 11954;16559 11759;16335 11594;16334 11568;17111 11140;17131 10944;17155 10696;17691 10758;18211 10472;18918 11185;18871 11179;18181 11569;17845 14343;17831 14444;17814 14442;17693 15272;17763 15625;17763 15729 1;17806 15760;17858 15776;17918 15777 1;19494 15810;20377 15709;21953 15709 1;22027 15709;22069 15613;22045 15542 1;21957 15283;21883 15147;21792 14889 1;21735 14726;21566 14726;21397 14691 1;21256 14661;21175 14547;21164 14405;21158 14273 1;21537 14132;21723 13824;21745 13421 1;21770 12958;21750 12623;21449 12270;21245 12061;21223 12064 1;21252 11916;21291 11760;21343 11592 1;21475 11166;21570 10870;21936 10615 1;22360 10320;22545 10022;22686 9525;23030 8231;23075 8146;23046 10154 1;22731 10352;22510 10560;22497 10932 1;22486 11234;22722 11439;23014 11511;23075 11514;23058 11950;22281 11949;22281 14593;22613 14601;22611 16755 18; 11945 13911;10753 13764 32;10804 13348;10320 13288 32;10610 10866;8540 10612;8452 12254 32;7686 12210;7661 12656 32;6462 12587 32;6487 12142;5422 12081 32;5480 10866;5352 10859 32;5404 9923 32;5809 9945;5928 7812;5604 7794 32;5656 6870 32;5979 6888;6000 6509;8752 6666 32;8608 9254;10781 9521;10990 7750 32;14090 8132 32;13945 9294;14066 9309 32;13965 10135 32;13558 10085;13234 12735;13585 12778 32;13480 13636 32;13129 13593;13090 13910 32;11961 13781;11945 13911 18; 13940 10118;13819 11026; 13767 10093;13652 11001; 13857 10996;14592 11086;15354 4865;14469 4757;14331 5891;11134 5502;11259 4475;10641 4410;10484 5768; 4386 4416; 9765 4946; 10875 5017; 14675 5350; 5665 7043 1;5169 7135;4330 7334;4391 6833;4733 4996;8632 5330;8472 6650;6000 6509;5979 6888;5656 6870;5665 7043 18; 8707 6629;8824 5661;9960 5798; 10431 7656;10575 6491 16; 10368 6525;11453 6659 32;12487 6786 16; 11128 7742;11191 7229;12949 7445;13093 6276;10443 5951; 14459 6723; 11787 5856; 12061 5632 32;12608 5697 32;12570 6014 32;12108 5958 32;12061 5632 50; 12610 5695;12572 6014; 11694 5575;11458 5950;10490 5815;10493 5697;10650 4401;11259 4475;11134 5502;11459 5542;11694 5575 18; 8645 6648;8722 6506;8824 5661;9875 5788;9978 5781;10135 4366;9542 4300;9420 5398;8644 5331;8626 5375;8472 6650;8645 6648 18; 10462 7609 32;9723 7518 32;9500 9336 32;10239 9427 32;10462 7609 50; 9721 7520;9752 7266;10336 7337; 9691 7665;8712 7545; 8650 9233;9496 9346;9723 7536;10456 7615;10242 9451;10770 9495;11053 7070;12820 7287;12929 6393;10460 6101;10639 4391;10142 4361;9967 5874;8890 5730;8755 6694;8650 9233 18; 15987 11228;15699 11184;15701 11132;16272 6501;18224 6688;18588 6310;18745 4417;19446 4487;19433 4565;19245 6655;16683 8161;16334 11084;15987 11228 18; 19439 4495;19478 4062;17219 3859;17183 4259;18297 4358;18125 6237;15766 6011;15135 11130;15688 11195;15712 11044;16272 6501;18224 6688;18588 6310;18745 4417;19439 4495 18; 12566 6087;12576 5966;12608 5697;12729 5696;14331 5891;14469 4757;15354 4865;14592 11086;13857 10996;13853 10772;13940 10118;13989 9740;14042 9306 32;13945 9294;14090 8132 32;11199 7776;11240 7291;12941 7474;13046 6148;12566 6087 18; 10367 13163;9581 13043;9402 13021;9278 14045;13167 14516;14217 13955;14549 11485;13466 11339;13403 11392;10367 13163 18; 4036 3791;4123 3792;4830 3853;4732 4996;4727 5029;4391 6833 1;4330 7334;5169 7135;5665 7043;5973 7795;5894 8450;5718 8451;4058 8315;4036 3791 18; 4062 8668;4244 8690;5480 8791;5458 9055;5414 10133;5397 10831;5493 10849;5397 12070;5303 12090;4008 12467;4036 12463;4062 8668 18; 8896 13065;8399 12943;8425 12271;8522 10956;8540 10612;10610 10866;10336 13155;9385 13022;9298 13883;9278 14045;13167 14516;14217 13955;14549 11485;13466 11339;13572 10081;13904 10116;13834 10971;13949 11007;14592 11086;15222 5940;15766 6011;15135 11130;16003 11232;16334 11084;16683 8161;19245 6655;19476 4088;19953 4131;19937 4130;19601 7435;18595 7999;18344 8127;17408 8630;17155 10696;17143 10825;17131 10944;17111 11140;16334 11568;16335 11594 1;16337 11596;16340 11597;16342 11599;16120 11686;16082 11707;15087 11568;14747 14280;13230 15135;7474 14376;6955 14308;4033 13924;4033 13922;4008 13921;4035 13359;8792 13970;8896 13065 18; 4035 787;4043 2112;4553 2154;4632 1155 16; 5570 1136;5480 2269; 5551 1154;11281 1679;11184 2740;11959 2811;12266 -536;10003 -741;10017 -61;9388 -48; 4295 1119 32;4285 -2381 32;9374 -2367 32;9382 500 32;5515 511;5517 1116 32;4295 1119 50; 9390 -1934;9982 -1936;9984 -1317;12308 -1102;12722 -5610;7940 -5668;4495 -5710 1;4235 -5713;4022 -5505;4019 -5245;4047 -3858 16; 5933 -4668; 5776 -2383;5780 -3726;4136 -3731;4135 -3298; 4133 -2417;4131 -2845; 4118 -2070;4111 798; 4301 -2057 32;3999 -2062 32;4007 -2439 32;4300 -2440 32;4301 -2057 50; 10330 1127; 6387 857; 7756 935; 9074 1031; 11625 2230; 4309 1625; 4032 762;4267 766;4272 1146;4616 1150;4559 2158;4032 2105;4032 762 18; 9983 -1922 1;10007 -2332;10107 -2879;10515 -2829 1;11474 -2712;12113 -2052;12286 -1102 1;12394 -507;12077 -178;11867 390 1;11755 693;11519 970;11239 808 1;10812 561;10374 276;10009 -55;9983 -1922 18; 11666 -5043; 5511 1137;5515 511;9382 500 32;9380 -40;9454 -49;10017 -61;10073 2 1;10424 309;10837 575;11239 808 1;11519 970;11755 693;11867 390 1;11998 35;12172 -227;12257 -519;12253 -394;11959 2811;11184 2740;11281 1679;5511 1137 18; 16348 834;16304 1409;16863 1452;16813 2101; 18420 1685;17846 1602;17772 2107;17385 2050; 16608 846;17033 908 16; 17597 990;18482 1118 16; 20003 2611 32;18768 2432 32;18837 1956;18406 1893 32;18756 -517;16686 -819;16562 812 32;15763 751;15725 1239 32;14568 1150 32;14606 663;13533 581 32;13625 -637;13423 -653 32;13486 -1481 32;13966 -1444;14131 -3607;13715 -3639 32;13783 -4526 32;14198 -4494;14235 -4975 32;16987 -4765 32;16790 -2177;18951 -1862;19207 -3630 32;22296 -3182 32;22127 -2020;22275 -1999 32;22146 -1107 32;21713 -1169;21357 1285;21792 1348 32;21663 2240 32;21228 2177;21171 2573 32;20031 2420;20003 2611 50; 17392 2172;17283 3313;21036 3671;22233 2682;22650 167;21589 -9; 13322 -1470;13395 -2441; 13497 -1526;13567 -2432; 13694 -1508;13753 -2421; 13581 -2408;13597 -2621;12950 -2671;12431 2880;13490 2979;13597 1836;16450 2103;16347 3205;16781 3254;16912 2098 16; 13197 2380; 16625 2685; 17594 2810; 16265 1455;16161 2053;16054 2066;13597 1836;13564 1825 1;13266 1692;13076 1454;13101 1128 1;13121 866;13280 709;13520 603;13735 596;14606 663;14568 1150 32;15725 1239 32;15763 751;16249 788;16265 1455 18; 16235 1407;16250 1541;16161 2053;16250 2084;16450 2103;16347 3205;16781 3254;16912 2098;16839 1487;16235 1407 18; 13602 1833;13535 1812 1;13254 1676;13077 1443;13101 1128 1;13121 866;13280 709;13520 603;13547 398;13625 -637;13423 -653 32;13486 -1481 32;13602 -2621;13319 -2642;12950 -2671;12431 2880;13490 2979;13567 2162;13602 1833 18; 18422 1745;17834 1681;17772 2107;17429 2056;17389 2206;17283 3313;21036 3671;22233 2682;22650 167;21589 -9;21206 2202;18422 1745 18; 21976 -1108;21857 -309; 22149 -1083;22037 -284; 22587 3131;22768 2993;23972 -4934;24671 -4841;25953 -5882 1;26121 -5842;26286 -5802;26450 -5762;24934 -153 16; 25574 -4922;25830 -5818 32;26328 -5676 32;26057 -4724 16; 24964 -5049;25509 -4918;25731 -5699;24964 -5049 18; 22207 -4224; 16979 -4912;17754 -4853;17805 -5529; 17900 -3718;16921 -3838; 17930 -3863;17961 -4117;18545 -4046; 16859 -2150;17705 -2037;17932 -3847;18665 -3768;18451 -1932;18979 -1888;19262 -4313;21029 -4096;21138 -4990;19816 -5146;19812 -5404;17794 -5531;17753 -4872;17038 -4877;16964 -4689;16859 -2150 18; 18577 -4858;19662 -4724 32;20696 -4597 16; 18640 -3727;18784 -4892 16; 19337 -3641;19400 -4154;21158 -3938;21302 -5107;19779 -5294;19820 -5648; 18671 -3774 32;17932 -3865 32;17709 -2047 32;18448 -1956 32;18671 -3774 50; 22076 -309;22705 -204;23439 -5002;21509 -5248 16; 14071 -3051;13931 -3054;12998 -3126;13224 -5601;13329 -5602;16481 -5563;17695 -5509;17761 -5363;17700 -4918;17098 -4883;16976 -4761;14228 -4970;14071 -3051 18; 22284 -3178;21979 -1137;22110 -1137;22135 -986;22043 -329;22133 -299;22705 -204;23439 -5002;21509 -5248;21360 -5202;21142 -3998;19475 -4120;19414 -3588;22284 -3178 18; 19884 4517;20085 4537;21486 4619;22139 4298;22230 4245 1;22570 3989;22738 3728;22865 3299;22977 3335;23094 3348;23103 3279;23095 3277 1;23002 2967;23003 2765;23069 2448 1;23171 1958;23286 1698;23426 1218 1;23519 897;23619 665;23738 430;23777 276 1;23853 -143;23705 -485;23820 -961;24675 -4843;23975 -4930;23807 -3846;22768 2993;22587 3131;22188 3410;21167 4192;19937 4130;19884 4517 18; 21739 5134;21980 5325;21975 5231;22133 4293;22139 4296;22064 4335;21486 4619;19898 4526;19851 5004;20048 5016;21739 5134 18; 18263 10444 1;18453 10569;18620 10698;18824 10598 1;19001 10511;19101 10463;19278 10376 1;19386 10323;19465 10262;19478 10142 1;19494 9991;19482 9905;19490 9753 1;19497 9618;19504 9503;19398 9420 1;19262 9314;19186 9254;19050 9148;18948 10012;18263 10444 18; 19197 10180; 19834 4997;19919 5008;20048 5016;21739 5134;21980 5325;22055 5359 1;22165 5471;22225 5594;22220 5767 1;22198 6525;22086 6943;21984 7695 1;21911 8245;21922 8669;21683 9078;21682 9062;21695 9035 1;21646 8998;21594 9002;21537 9026 1;21243 9151;21079 9223;20779 9331 1;20311 9500;20389 10026;20316 10518 1;20268 10844;20220 11035;20055 11320;19958 11327;18871 11179;18798 11220;18107 10537;18284 10431;18263 10444 1;18453 10569;18620 10698;18824 10598 1;19001 10511;19101 10463;19278 10376 1;19386 10323;19465 10262;19478 10142 1;19494 9991;19482 9905;19490 9753 1;19497 9618;19504 9503;19398 9420 1;19262 9314;19186 9254;19050 9148;19050 9079;19073 8852;18592 7994;18853 7854;19601 7435;19834 4997 18; 20589 12386 1;20673 12273;20735 12147;20776 11976 1;20932 11321;20993 10948;21107 10284 1;21173 9901;21212 9646;21474 9359 1;21540 9286;21596 9214;21644 9141;21695 9035;21748 8954 1;21921 8582;21918 8190;21984 7695 1;22086 6943;22198 6525;22220 5767 1;22223 5648;22196 5553;22143 5468;22068 5372;21978 5279;21975 5231;22133 4293;22139 4296;22209 4260 1;22443 4088;22598 3915;22713 3691; 23031 2986 1;23010 2814;23026 2656;23069 2448 1;23171 1958;23286 1698;23426 1218 1;23547 801;23679 535;23851 217 1;23913 101;23980 -22;24054 -161; 25821 -6433;26301 -8073;25840 -8159 1;24256 -8465;22867 -8608;20879 -8709 1;16304 -8942;13770 -8820;9190 -8919 1;4796 -9014;2347 -8930;-2047 -8813 1;-2060 -8813;-2074 -8812;-2087 -8812;-2323 -8802;-2508 -8804;-2162 -7324;-1942 -7315;-634 -7372;-228 -7376 1;1275 -7275;2190 -6212;2190 -4523;2195 53;2212 12685 1;2213 13337;1671 13976;1033 14260;1705 15796;2002 15783 1;2839 15709;3511 14813;3562 13963;3562 13901;3565 13394;3636 974 1;3626 103;3620 -628;3616 -1321;3611 -1977 1;3604 -2952;3596 -3914;3574 -5166 1;3568 -5531;3563 -5779;3821 -6165;3984 -6380 1;4551 -7082;5205 -7450;6150 -7440;6830 -7440;8069 -6201;9489 -6160 1;12476 -6117;14430 -6142;17663 -6017 1;19870 -5932;21105 -5796;23301 -5561;24571 -5425;25821 -6433 18; 3552 13926;4021 13956;4058 13377;4021 13309;4038 8678;5486 8809;5399 9917;5800 9917;5870 8469;4029 8321;4038 4543;3602 4517;3552 13926 18; 8895 13158;8792 13970;4035 13359;4038 8674;5480 8791;5458 9055 16; 9962 5927;10135 4366;9542 4300;9420 5398;4732 4996;4830 3853;4044 3790;4046 8315;5869 8463 16; 3616 4558;4044 4580;4038 4168;4036 3791;4123 3792;4830 3853;4733 4983;4832 5005;9420 5398;9542 4300;10135 4366;10135 4367;10143 4391;10648 4416;10650 4401;11259 4475;11205 4921;11180 5127;11134 5502;14331 5891;14469 4757;15354 4865;15217 5985;15750 6039;15845 6019;18125 6237;18297 4358;17183 4259;17219 3859;17283 3313;17392 2172;17452 2057;17723 2075;17865 1625;18420 1674;18760 -516;16662 -812;16551 823;16379 811;16354 1409;16878 1483;16915 2112;16900 2203;16781 3254;16347 3205;16450 2103;13597 1836;13490 2979;12431 2880;12950 -2671;13597 -2621;13581 -2408;13518 -1534;13949 -1497;14073 -3039;13912 -3055;12998 -3126;13227 -5633;12706 -5625;12710 -5448;12308 -1102;9984 -1317;9982 -1936;9390 -1934;9409 -66;9620 -53;10017 -61;10003 -741;12266 -536;11959 2811;11184 2740;11281 1679;7495 1332;7139 1299;5551 1154;4632 1134;4624 1254;4553 2154;4043 2112;4035 787;4010 -2073;4022 -2443;4247 -2427;5685 -2403;5728 -3687;4050 -3816;4044 -4020;4019 -5245 1;4021 -5455;4160 -5631;4350 -5689;4197 -6168 1;4075 -6167;3950 -6166;3821 -6165 1;3563 -5779;3568 -5531;3574 -5166 1;3599 -3717;3606 -2657;3614 -1516;3616 4558 18; 17695 -5509;16481 -5563;13223 -5602;12998 -3126;14080 -3042 16; 4323 -5681;4144 -6144;4467 -6170 1;5909 -6184;6890 -6207;8484 -6177 1;12069 -6110;14081 -6155;17663 -6017 1;19601 -5942;20790 -5828;22539 -5642;22674 -5628 1;22875 -5607;23083 -5584;23301 -5561;24571 -5425;24621 -5467;24839 -4981;24662 -4852;24600 -4852;23975 -4930;23807 -3846;23793 -3753;23687 -3055;22768 2993;22587 3131;22271 3347;21167 4192;19937 4130;19929 4208;19466 4196;19476 4088;17140 3856;17220 3313;17519 3336;21036 3671;22233 2682;22491 1123;22559 717;22650 167;21589 -9;21724 -1115;22099 -1063;22038 -330;22701 -208;23439 -4988;21452 -5253;19620 -5382;17825 -5493;17302 -5526;16481 -5563;13329 -5602;13224 -5601;13222 -5580;12503 -5612;7940 -5668;4323 -5681 18; 3517 28551;2526 28408;2610 27833; 3108 29551;3200 28846;2099 28680;2212 27782;2801 27865 16; 3550 27970;4842 28153 16; 3504 28696 32;3979 28764 32;4081 28058 32;3606 27990 32;3504 28696 50; 3586 27950 32;2450 27786 32;2381 28261 32;3517 28425 32;3586 27950 50; 3028 27872;2963 28328; 3314 27913;3249 28369; -11198 22399 33;-11150 22359;-11086 22337;-11018 22344 1;-10753 22371;-10605 22387;-10340 22415 1;-10205 22429;-10108 22550;-10122 22685 1;-10172 23160;-10199 23427;-10248 23902 1;-10255 23975;-10291 24038;-10343 24081 33;-10397 24126;-10468 24150;-10543 24142 1;-10809 24115;-10959 24100;-11225 24073 1;-11281 24067;-11329 24040;-11362 23999 33;-11395 23959;-11412 23906;-11407 23850 1;-11276 22495;-11244 22439;-11198 22399 50; -6378 64888 1;-6287 64699;-6123 64560;-5911 64586 1;-5562 64628;-5323 64706;-5013 64542 1;-4745 64400;-4558 64257;-4395 64074; -7851 63305 1;-8047 63278;-8230 63260;-8403 63242 16; -8840 63199 1;-9471 63136;-9542 63123;-10119 62694 1;-10587 62346;-10459 62286;-10929 62088 16; -14826 -54891 32;-14022 -54887 32;-14019 -55628 32;-14823 -55632 32;-14826 -54891 50; 9110 -2374 1;9078 -3197;8177 -3575;7370 -3515 1;6710 -3466;6248 -3036;6102 -2390;5823 -2391;5797 -3751;4044 -3769;4045 -3952;4019 -5245 1;4022 -5505;4235 -5713;4495 -5710;7940 -5668;12712 -5609;12692 -5255;12312 -1141;12259 -1231 1;12054 -2111;11430 -2717;10515 -2829 1;10109 -2879;10008 -2337;9983 -1928;9400 -1937;9356 -2373;9110 -2374 18; 6102 -2390 1;6248 -3036;6710 -3466;7370 -3515 1;8177 -3575;9078 -3197;9110 -2374;6102 -2390 18; -24477 -63943;-24629 -62010;-20633 -61694;-20516 -63604;-24477 -63943 18; -20853 -60145;-27771 -60273 1;-27775 -60310;-27802 -60370;-27800 -60413 1;-27786 -60700;-27950 -61173;-28236 -61198 1;-29917 -61347;-30864 -61418;-32545 -61564 1;-33082 -61611;-33281 -62083;-33819 -62122 1;-34291 -62157;-34722 -61915;-34901 -61477 1;-34949 -61360;-35075 -61215;-35142 -61101;-35215 -60897 1;-36368 -61028;-37365 -61163;-38596 -61463;-38746 -61500;-38614 -61655 1;-38493 -61886;-38397 -62327;-38313 -62453 1;-38095 -62780;-37935 -63041;-37548 -63106 1;-37165 -63170;-36980 -63461;-36869 -63834 1;-36718 -64341;-36757 -64658;-36758 -65181;-35710 -65008 1;-32039 -64560;-29975 -64340;-26289 -64048;-24823 -63944;-24479 -63915;-24629 -62010;-20705 -61700 1;-20737 -61562;-20767 -61405;-20780 -61174 1;-20808 -60674;-20840 -60646;-20853 -60145 18; -18988 -39976;-18754 -38787; -18207 -38881;-18754 -38805;-19008 -39972;-20112 -37131;-20114 -36709;-19966 -36611;-19706 -36392;-24137 -31127;-23923 -30939 1;-23621 -30518;-23629 -29932;-23978 -29518 1;-24388 -29031;-25100 -28995;-25602 -29386;-26893 -27858;-25122 -26454;-24961 -26630 1;-24936 -26660;-24910 -26690;-24885 -26720 1;-24148 -27589;-22980 -27466;-21919 -27027;-21049 -28336 1;-21436 -28858;-21602 -29550;-21470 -30177;-21582 -30354 1;-21724 -30807;-21734 -31220;-21411 -31592 1;-20327 -32854;-19715 -33559;-18624 -34819;-18441 -35030;-18379 -35106;-17465 -36214;-17265 -36424;-16354 -37380 1;-15924 -37718;-15532 -37676;-14985 -37676;-14984 -37662;-14710 -37688;-8935 -37934;-8883 -38043 1;-8774 -38184;-8615 -38279;-8423 -38280 1;-8092 -38282;-7756 -38207;-7673 -37887;-7551 -37878;-7315 -37878 1;-6526 -37878;-6023 -37866;-5326 -38236;-5176 -37952;-4680 -38125 1;-3565 -38600;-2758 -38891;-1792 -39796;-1243 -40364;748 -42424;886 -42576;1149 -42831 32;1471 -42499 32;1616 -42660;2180 -43259;2129 -43314;1884 -43579 1;2467 -44125;2788 -44392;3505 -44540;3595 -44147;3824 -44191 1;4007 -44222;4196 -44235;4379 -44246;4385 -44423;4372 -44659 32;5795 -44739 32;5818 -44342;5945 -44323;8137 -44437;8132 -44614;8123 -44893 32;12257 -45030 32;12271 -44574;12598 -44592;14914 -44715;14940 -44919 1;15009 -45285;15295 -45485;15716 -45485 1;15952 -45485;16265 -45382;16289 -45147 1;17338 -45181;18064 -45216;18724 -45355;18445 -46983 1;17944 -46872;17399 -46823;16792 -46797;16760 -47246;16146 -47187;13974 -47064;13952 -46935 1;13875 -46707;13617 -46571;13356 -46571 1;13052 -46571;12720 -46719;12715 -47049;12593 -47046;11067 -47063;11075 -46920;11082 -46535 32;10576 -46526 32;10566 -47058;10325 -47046;4872 -46777;4842 -46767;4858 -46671;4883 -46231 32;4263 -46196 32;4232 -46744 32;4268 -46746;4052 -46733 1;3702 -46705;3399 -46643;3051 -46538;3114 -46424;3193 -46049 1;2207 -45841;1716 -45432;914 -44823;609 -45154;427 -44960;-263 -44284;-228 -44250;51 -43963 32;-375 -43550 32;-686 -43870;-834 -43713;-2701 -41539;-2569 -41440;-2362 -41227 32;-2788 -40814 32;-3110 -41146 32;-3269 -40983 1;-3653 -40638;-3957 -40445;-4464 -40187;-4395 -40090;-4254 -39807 1;-4906 -39451;-5328 -39344;-6077 -39306;-7717 -39244;-7735 -39680;-8208 -39663;-9463 -39638;-9543 -39642;-9521 -39468;-9506 -39189;-10752 -39183 1;-10768 -38877;-11076 -38680;-11382 -38696 1;-11830 -38719;-11853 -39160;-11850 -39615;-12280 -39623;-15106 -39533;-15047 -39404;-14993 -39190;-17794 -38154 1;-18071 -38341;-18169 -38590;-18207 -38881 18; -18748 -38799;-18988 -40006;-12158 -40150;-11452 -40124;-10213 -40115;-9035 -40158;-7648 -40210;-6296 -40245;-5397 -40350;-4307 -40838;-2649 -42399;-625 -44597;-573 -44667;-58 -45252;1076 -46264;2376 -46979;3039 -47049;4120 -47406;4766 -47398;11152 -47639;12755 -47632;16778 -47725;16796 -47281;16762 -47222;14077 -47070;13975 -47056 1;13975 -47063;13975 -47070;13975 -47077;12843 -47054;12715 -47045;11202 -47062;11072 -47067 32;10659 -47060;10420 -47051;4872 -46777;4723 -46727;4721 -46770 32;4282 -46745;4035 -46739 1;3723 -46720;3512 -46673;3239 -46594;3091 -46532 1;2235 -46405;1511 -45966;776 -45307;607 -45137;-53 -44490;-333 -44233;-705 -43864;-2661 -41586;-2684 -41559 32;-3004 -41248;-3113 -41128 1;-3514 -40747;-3803 -40540;-4244 -40302;-4450 -40182 1;-5057 -39855;-5520 -39758;-6225 -39733;-7502 -39688;-7735 -39672;-9463 -39638;-9610 -39645;-11634 -39656;-11831 -39639;-14928 -39552;-18210 -39492;-18210 -39484;-18221 -38869;-18748 -38799 18; -34953 75446;-34909 75437 1;-34885 75438;-34861 75437;-34836 75436 1;-34316 75407;-33917 74962;-33946 74442 1;-33975 73921;-34420 73523;-34941 73552 1;-35461 73581;-35859 74026;-35830 74546 1;-35807 74969;-35509 75311;-35118 75409;-34953 75446 18; 3594 83351;3829 78922;9430 77657;9430 79860; 3704 83347;6001 83499;6036 82632;6774 82597;7477 80910;7489 80265;7934 79785;9352 79844;9423 77676;3798 78941;3704 83347 18; -8233 53585;-8329 54305 1;-8097 54344;-7868 54382;-7570 54416 1;-7384 54438;-7353 54626;-7282 54799;-6770 54866;-6647 53800;-8233 53585 18; -8329 54305;-8233 53585;-9510 53412 1;-9874 53365;-10139 53588;-10157 53974;-10475 55977 1;-10281 56012;-10172 56031;-9978 56066 1;-9804 56097;-9743 55872;-9716 55698 1;-9651 55270;-9653 55025;-9576 54599 1;-9529 54336;-9292 54149;-9027 54189 1;-8755 54230;-8541 54268;-8329 54305 18; 41229 47215;41152 48105;40846 48073 32;40502 51376;40058 51666;40401 52092;40272 53204;40116 54675;39791 56344;38603 56259;38386 56113 1;38282 56062;38166 56030;38043 56021 1;37829 56005;37627 56062;37464 56172 1;37405 55848;37180 55578;36878 55453;36666 55411;37783 49216;38773 49768;39027 47420;39593 47081;41229 47215 18; 48114 43686;49098 43757;49942 44659;49872 45327;50094 45366;49983 46490;49771 46484;49702 47085 32;50738 47202;50671 47826;51702 47941;51345 51221;50677 51147;50698 50900;46421 50447 32;46491 49775 32;42648 49374 32;42763 48273 32;41157 48105;41224 47213;41083 47202;41060 46522;47575 46897;48114 43686 18; 41230 47224;39582 47089;39024 47445;38763 49761;32422 46329 16; 54487 57430;57065 44942 1;57183 44357;56706 43874;56107 43780;49149 43259;49102 43835 16; 48105 43744;49108 43837;49153 43321;48146 43240;48105 43744 18; 48168 43148;48117 43750 16; 49841 44658;50753 44749;51043 44551;51266 44559;51457 44807;52468 44899;52567 43962;51242 43415;49129 43266;49104 43871;49841 44658 18; 13788 60561; -28774 122236 1;-29487 122231;-30069 122805;-30074 123518 1;-30079 124231;-29505 124812;-28792 124817 1;-28079 124822;-27498 124248;-27493 123536 1;-27488 122823;-28062 122241;-28774 122236 18; -26895 124286;-25138 122498;-24000 123616;-24576 124203 1;-25103 123685;-25542 123631;-26078 124178;-26527 124635;-26895 124286 18; -28733 126000;-27664 126000;-25823 124159 1;-25558 123894;-25129 123894;-24864 124159 1;-24585 124438;-24585 124889;-24864 125168 1;-25660 125964;-26106 126410;-26902 127206 1;-27058 127362;-27181 127468;-27401 127468 1;-28348 127468;-28880 127468;-29827 127468 1;-29922 127468;-29999 127391;-29999 127296;-28733 126000 18; -35225 129007;-32156 129007;-28742 125593 1;-28618 125469;-28591 125176;-28904 125176 1;-29650 125176;-30067 125176;-30813 125176 1;-31207 125176;-31481 125250;-31759 125529;-35225 129007 18; -34258 126602;-33171 126602;-31927 125358 1;-31871 125302;-31909 125164;-31989 125164 1;-33002 125164;-33570 125164;-34583 125164 1;-34783 125164;-34905 125242;-35046 125383 1;-35898 126235;-36375 126712;-37227 127564 1;-37507 127844;-37507 128297;-37227 128577 1;-36925 128879;-36455 128866;-36153 128564;-34258 126602 18; 96915 -144926;96914 142524 32;-102705 142524 32;-102704 -144926 32;96915 -144926 18; -94724 99498;29307 113498;38500 125237;45571 125803;49531 117317;74846 105013;77816 98932;91676 -31463;87716 -42777;78947 -41363;77816 -34857;72159 -34857;66302 -44060;56502 -43843;52359 -67385;26620 -63708;-47487 -75871;-48878 -61311;-52432 -52082;-86725 72900;-89598 78252;-95147 91437;-94724 99498 18;-87750 85332;-87944 85220;-88019 84989;-88000 84582;-87663 84664;-87700 85032;-87750 85332 18;-51203 55455 32;-49647 55455 32;-49647 60864 32;-51203 60864 32;-51203 55455 50;-50724 14979 32;-50024 14979 32;-50024 15742 32;-50724 15742 32;-50724 14979 50;-50688 1358 32;-50051 1358 32;-50051 -442 32;-50688 -442 32;-50688 1358 50;-13135 -24391 32;-12709 -24391 32;-12709 -8940 32;-13135 -8940 32;-13135 -24391 50;-13058 16186 32;-12722 16186 32;-12722 21066 32;-13058 21066 32;-13058 16186 50;-13064 29485 32;-12693 29485 32;-12693 30722 32;-13064 30722 32;-13064 29485 50;-13247 97034 32;-12628 97034 32;-12628 100163 32;-13247 100163 32;-13247 97034 50;-13097 58412 32;-12734 58412 32;-12734 61512 32;-13097 61512 32;-13097 58412 50;24437 65027 32;24755 65027 32;24755 69959 32;24437 69959 32;24437 65027 50; 62093 -43270;62093 -44202; 24593 -63582;24593 -64514; -12907 -69552;-12907 -70484; -50415 -56514;-50415 -57446; -87907 75943;-87907 75011; -61777 6671;-59865 -675 1;-59704 -1366;-59397 -1821;-58748 -2106 1;-58038 -2418;-57546 -2345;-56847 -2682 1;-56552 -2824;-56556 -3104;-56410 -3397 1;-56291 -3636;-56214 -4093;-56480 -4077 1;-56850 -4055;-57052 -3981;-57405 -3868 1;-57746 -3759;-58103 -4103;-58103 -4461 1;-58103 -5022;-57921 -5446;-57440 -5735 1;-56833 -6099;-57117 -6738;-57143 -7445 1;-57171 -8196;-57248 -8784;-57090 -9364;-54760 -17861 1;-54461 -18952;-53410 -19207;-52278 -19382 1;-50704 -19636;-49098 -18950;-47816 -17606 1;-46157 -15867;-46149 -13999;-46467 -11617 1;-46602 -10606;-47251 -9928;-48260 -9778 1;-49255 -9630;-49794 -9485;-50215 -10431 1;-50528 -11135;-49568 -11505;-49620 -12349 1;-49697 -13596;-52184 -13647;-52081 -11757 1;-52027 -10780;-52107 -10172;-51505 -9401 1;-50918 -8650;-50295 -8533;-49359 -8355 1;-48108 -8118;-47247 -8285;-46149 -8931 1;-44572 -9859;-44469 -11385;-44208 -13196 1;-43662 -16985;-46000 -19475;-49266 -21471 1;-50314 -22111;-50798 -22096;-51830 -22761 1;-52384 -23118;-52428 -23955;-52332 -24607 1;-51945 -27234;-51696 -28526;-51066 -31105 1;-50488 -33471;-50377 -34918;-49862 -37298;-49324 -39793 16; -48266 -43193;-44969 -55600 1;-44770 -56350;-44658 -56771;-44459 -57521 1;-44403 -57731;-44126 -57609;-43909 -57599 1;-37936 -57328;-34589 -57125;-28611 -57007 1;-28172 -56999;-27797 -57000;-27550 -56637 1;-27347 -56339;-26998 -56491;-26637 -56489 1;-22731 -56463;-20534 -56558;-16644 -56908 1;-14125 -57135;-12753 -57589;-10241 -57885 1;-8135 -58133;-6949 -58248;-4832 -58374 1;-3352 -58462;-2528 -58619;-1046 -58670 1;-695 -58682;-321 -58841;-313 -59193 1;-299 -59779;-292 -60108;-279 -60694 1;-271 -61060;-683 -61180;-1045 -61235 1;-4468 -61755;-6395 -62109;-9856 -62230 1;-13334 -62352;-15274 -62620;-18737 -62963 1;-24631 -63547;-27958 -63634;-33847 -64260 1;-36788 -64573;-38425 -64883;-41324 -65469 1;-41971 -65600;-42155 -65883;-42027 -66530;-41134 -71044 16; -90081 87295 1;-88904 87663;-88866 87631;-87777 87896 1;-86608 88180;-85808 87191;-85477 86452;-74252 52606 1;-74077 52075;-73143 51784;-72928 51898 1;-72514 52117;-72271 52360;-72088 52657 1;-69677 56571;-68581 58935;-65929 62690 1;-65359 63497;-65039 63950;-64469 64757 16; -58129 64311 1;-58237 63013;-58757 62349;-59461 61252 1;-61129 58653;-62367 57397;-63977 54762 1;-65401 52432;-66406 51244;-67653 48815 1;-68965 46259;-69382 44645;-69627 41783 1;-70167 35481;-69198 33882;-68259 30659;-63792 15201;-63391 13475 1;-63213 12954;-62713 12716;-62177 12846 1;-60743 13194;-59861 13450;-58374 13423 1;-57251 13403;-56529 13169;-55670 12446 1;-55374 12197;-55327 11948;-55112 11626 1;-54774 11119;-54387 10945;-54187 10369 1;-53834 9353;-53814 8732;-53577 7683 1;-53363 6737;-53908 5958;-54763 5502 1;-55535 5090;-56383 5488;-56892 6200 1;-57257 6711;-57329 7272;-56996 7805 1;-56763 8177;-56738 8505;-56874 8922 1;-57021 9373;-57429 9567;-57904 9567 1;-58992 9567;-59431 8764;-60468 8433 1;-61332 8157;-61963 7532;-61777 6671 16; 23449 -54909 1;24004 -55081;24327 -55630;24170 -56134 1;24013 -56638;23436 -56907;22881 -56735 1;22326 -56562;22003 -56013;22160 -55509 1;22316 -55005;22893 -54736;23449 -54909 18; 73320 -54693;N 69062 -61008;77555 -61008;73312 -68970;69062 -61008 18; 85399 110654;N 81141 104339;89634 104339;85391 96377;81141 104339 18; -85692 22052;N -89950 15737;-81457 15737;-85700 7775;-89950 15737 18; -90770 -101448;Construction area, forbidden access! Street / asphalt, with / without traffic Big signs, playground items, etc. -94509 -62288 32;-91828 -62288 32;-91828 -59664 32;-94509 -59664 32;-94509 -62288 50; -97187 -18612 32;-94506 -18612 32;-94506 -15988 32;-97187 -15988 32;-97187 -18612 50; -94509 -33067 32;-91828 -33067 32;-91828 -30443 32;-94509 -30443 32;-94509 -33067 50; -94509 -18612 32;-91828 -18612 32;-91828 -15988 32;-94509 -15988 32;-94509 -18612 50; -94509 -22221 32;-91828 -22221 32;-91828 -19597 32;-94509 -19597 32;-94509 -22221 50; -97187 -69541 32;-95398 -69541 32;-95398 -66917 32;-97187 -66917 32;-97187 -69541 50; -94217 -80160 32;-92083 -80160 32;-92083 -78042 32;-94217 -78042 32;-94217 -80160 50; -95807 -93036; -93145 -93018; -90746 -80314;Building; Canopy Street / asphalt, with / without traffic Private/flowerbed, forbidden access! Meadow; Meadow with trees; Forest Light; strong thicket Thicket, impassable Tree; Special tree Path; Track; Stairs Fence, forbidden to pass! Fence, passable Wall, forbidden to pass! Small wall, passable Hedge, forbidden to pass! Light; strong undergrowth Hedges, passable Countours; Hill Rough/semi-open area Sandy ground Cultivation boundary; Step Stone; Rock face Well; Small erosion gully -94509 -76769 32;-91828 -76769 32;-91828 -74145 32;-94509 -74145 32;-94509 -76769 50; -94509 -65897 32;-91828 -65897 32;-91828 -63273 32;-94509 -63273 32;-94509 -65897 50; -93630 -69541;-91828 -69541 32;-91828 -66917 32;-93630 -66917 16; -96905 -80131 32;-94837 -80131 32;-94837 -78071 32;-96905 -78071 32;-96905 -80131 50; -97187 -22221 32;-94506 -22221 32;-94506 -19597 32;-97187 -19597 32;-97187 -22221 50; -95397 -69541 32;-93622 -69541 32;-93622 -66917 32;-95397 -66917 32;-95397 -69541 50; -97186 -70537 32;-97186 -73161 32;-91827 -73161 32;-91827 -70537 32;-97186 -70537 50; -97187 -62288 32;-94506 -62288 32;-94506 -59664 32;-97187 -59664 32;-97187 -62288 50; -95825 -57271; -96926 -52501 1;-96327 -53305;-95862 -53873;-95644 -54852; -95060 -52368 1;-94445 -53213;-94272 -53817;-94000 -54826; -97176 -39024;-91837 -39024; -97176 -35418;-91837 -35418; -97187 -76769 32;-94506 -76769 32;-94506 -74145 32;-97187 -74145 32;-97187 -76769 50; -92735 -52616;-92735 -54560; -92735 -52616;-92735 -54560; -97015 -24865 1;-96587 -25062;-96095 -25420;-95626 -25727; -97003 -23702 1;-95974 -23917;-95416 -24580;-94390 -24352; -96651 -12597;-95063 -14610; -93963 -12572;-92375 -14585; -95703 -24177; -93450 -24790 1;-93191 -25215;-92761 -25425;-92488 -25260 1;-92215 -25094;-92204 -24615;-92462 -24190 1;-92721 -23764;-93151 -23554;-93424 -23720 1;-93697 -23885;-93708 -24364;-93450 -24790 18; -97187 -65897 32;-94506 -65897 32;-94506 -63273 32;-97187 -63273 32;-97187 -65897 50; -97187 -33067 32;-94506 -33067 32;-94506 -30443 32;-97187 -30443 32;-97187 -33067 50; -97176 -50002;-91837 -50002; -97176 -46314;-91837 -46314; -97176 -42701;-91837 -42701; -95238 -27135;-93674 -29044; -93744 -27135;-92180 -29044; -97012 -24218 1;-96005 -24698;-95019 -25545;-94318 -25362; -96856 -27135;-95292 -29044; -93188 -57246; -93964 -5333;-92452 -7384; -97183 -97983 32;-94502 -97983 32;-94502 -95359 32;-97183 -95359 32;-97183 -97983 50; -91824 -101608;-97183 -101608;-97183 -98984;-91824 -98984;-91824 -101608 18; -94070 -8966;-92558 -11017; -95847 -6305; -95844 -9873; -97183 -101608 32;-94502 -101608 32;-94502 -98984 32;-97183 -98984 32;-97183 -101608 50; -94505 -101608 32;-91824 -101608 32;-91824 -98984 32;-94505 -98984 32;-94505 -101608 50; -94505 -97983 32;-91824 -97983 32;-91824 -95359 32;-94505 -95359 32;-94505 -97983 50; -99428 -85496;Legend -99743 -107017;Special Signatures -108808 -113533 32;-108808 -149753 32;102997 -149753 32;102997 -113533 32;-108808 -113533 50; -108808 -113533 32;-108808 -149753 32;102997 -149753 32;102997 -113533 33;102997 -107318;103133 -103770;103115 -102260 1;101634 -102168;99386 -102232;97055 -102227 1;78346 -102190;36945 -113533;13658 -113533;-108808 -113533 50; 59183 -130917;May 2013 59183 -123479;Thomas Schöps -98032 -141948;Orienteering map 24405 -130921;Standing: -100736 -132215;Garching -100438 -131472;Garching 24405 -123483;Cartography: 59183 -136073;2.5 m 59183 -141376;1 : 4,000 24405 -141380;Scale: 24405 -136077;Contours: 76149 131570;Organizer, host, mapper, land owners and administration do not assume any liability! -75609 131433; -108809 147382 32;-108809 139763 32;102996 139763 32;102996 147382 32;-108809 147382 50; -29900 132587;-26214 132587;-23147 129520 1;-23039 129412;-23179 129177;-23332 129176;-26912 129153;-29983 132185 1;-30093 132294;-30091 132589;-29900 132587 18;-29256 132147;-23843 129647;-26750 129616;-29256 132147 18; 102997 139965 32;102996 147382 32;-108810 147387 32;-108806 139970 33;-108807 134438;-109023 130448;-108862 128687 1;-107176 128658;-105495 128666;-102866 128662 1;-84159 128623;-42757 139968;-19467 139967;102997 139965 50; -95213 128871;-95019 129525;-94453 130392;-93799 130869;-94258 131311;-94913 132089;-95195 133026;-95231 133821;-95036 134723;-94718 135306;-94223 135766;-93498 136084;-92402 136172;-91501 135978;-91129 135695;-90493 136031;-89132 136102;-88460 135748;-87771 136084;-86392 136066;-85773 135783;-85066 136049;-83970 136119;-82555 135978;-81760 136049;-78931 136048;-78472 135801;-77570 136119;-75997 136084;-75396 135836;-74388 136101;-73133 136119;-72090 135713;-71118 136102;-69827 136155;-68643 136031;-67246 136049;-66327 135819;-65355 136084;-60669 136119;-60369 136808;-59626 137144;-58248 137321;-57152 136967;-56356 136419;-55967 135606;-55914 134705;-55914 130338;-78896 125070;-95125 125565;-95213 128871 18; -20091 135644;-49858 135630;-49858 132699;-49443 132699;-49293 132430;-49280 131652;-47718 130071;-43302 130083;-43299 130083;-43278 125783 1;-43394 125565;-43439 125417;-43434 125170 1;-43423 124571;-43079 124035;-42484 123964 1;-42460 123961;-42438 123961;-42428 123939 1;-42365 123801;-42273 123699;-42122 123701 1;-41963 123703;-41840 123778;-41784 123926 1;-41777 123945;-41754 123936;-41734 123939 1;-41135 124023;-40796 124577;-40809 125182 1;-40814 125404;-40830 125539;-40928 125739;-40922 130033;-40917 130033;-40100 130042;-40077 125801 1;-40193 125583;-40238 125435;-40233 125188 1;-40222 124589;-39878 124053;-39283 123982 1;-39259 123979;-39237 123979;-39227 123957 1;-39164 123819;-39072 123717;-38921 123719 1;-38762 123721;-38639 123796;-38583 123944 1;-38576 123963;-38553 123954;-38533 123957 1;-37934 124041;-37595 124595;-37608 125200 1;-37613 125422;-37629 125557;-37727 125757;-37719 130095;-37624 130216;-37623 131207;-36642 131657;-36227 132082;-36218 133257;-36094 133381;-35342 133386;-35339 132979;-35029 132674;-34118 132681;-33837 132966;-32396 132976;-32377 130280;-32093 129471 1;-32225 129147;-32253 128931;-32212 128584 1;-32154 128088;-31923 127658;-31431 127571 1;-31410 127567;-31445 127540;-31456 127521 1;-31516 127416;-31395 127274;-31274 127271 1;-31157 127268;-31073 127432;-31130 127534 1;-30854 127619;-30632 127677;-30512 127940 1;-30412 128158;-30366 128293;-30205 128471 1;-30131 128553;-30079 128617;-30087 128727 1;-30094 128827;-30130 128894;-30212 128952 1;-30316 129025;-30300 129131;-30337 129252;-30411 129477;-30130 130277;-30149 132308;-28692 132322;-28355 132665;-27955 132669;-27656 132376;-27616 128434;-27430 128239;-27438 127889;-27330 127776;-27337 127471 1;-27343 127224;-27152 127024;-26905 127008;-26899 126783;-26749 126633;-26618 126783;-26618 126989 1;-26283 127001;-26110 127417;-26121 127752;-26017 127880;-26017 128283;-25811 128508;-25831 131855;-25620 132068;-25622 132447;-25401 132671;-25079 132673;-24872 132469;-24854 129999;-24625 129773;-24622 129318 1;-24616 128430;-23974 127846;-23117 127612;-23117 127272;-23227 127139 1;-23350 126990;-23299 126748;-23130 126653 1;-23117 126646;-23117 126633;-23117 126618 1;-23112 126405;-23012 126180;-22799 126176;-22803 125973;-22574 125782;-22388 125964;-22384 126167;-22362 126176 1;-22160 126193;-22079 126411;-22083 126613 1;-22083 126625;-22072 126629;-22061 126635 1;-21938 126706;-21859 126798;-21862 126940 1;-21865 127096;-21961 127206;-22110 127254;-22110 127621 1;-21193 127748;-20726 128455;-20490 129350;-20503 129813;-20259 130007;-20271 132150;-20077 132419;-20091 135644 18; -23551 129615;-26814 129502;-29539 132352;-23551 129615 18; -30664 135205;-28383 132918 1;-28305 132840;-28179 132840;-28101 132918 1;-28023 132995;-28023 133121;-28101 133199 1;-28992 134090;-29492 134590;-30383 135481 1;-30459 135557;-30582 135557;-30658 135481 1;-30732 135407;-30732 135286;-30664 135205 18; -35052 130658;-31782 130658;-33709 132585 1;-34002 132878;-34002 133352;-33709 133645 1;-33460 133894;-33056 133894;-32807 133645 1;-31544 132382;-30836 131674;-29573 130411 1;-29228 130066;-29577 129262;-30065 129262 1;-32132 129262;-33292 129262;-35359 129262 1;-35604 129262;-35761 129338;-35934 129511 1;-37787 131364;-38825 132402;-40678 134255 1;-40989 134566;-40989 135072;-40678 135383 1;-40415 135646;-39989 135646;-39726 135383;-35052 130658 18; -35070 136617;Munich SprintCup -53727 113748;www.ol-gruenwald.de -53661 117691;www.ol-landshut.de -54952 117697;Landshut and Freising: -96409 113730;Munich: -96266 109471;Orienteering in ... mapper-0.8.1.1/examples/examples.qrc000066400000000000000000000002201325266516600173140ustar00rootroot00000000000000 complete map.omap forest sample.omap mapper-0.8.1.1/examples/forest sample.omap000066400000000000000000014014131325266516600204230ustar00rootroot00000000000000 PURPLE BLACK BLUE BROWN GREEN YELLOW A line joining points of equal height. The standard vertical interval between contours is 5 metres. The smallest bend in a contour is 0.25 mm from centre to centre of the lines. Every fifth contour shall be drawn with a thicker line. This is an aid to the quick assessment of height difference and the overall shape of the terrain surface. Where an index contour coincides with an area of much detail, it may be shown with a normal contour line. An intermediate contour line. Form lines are used where more information can be given about the shape of the ground. They are used only where representation is not possible with ordinary contours. Only one form line may be used between neighbouring contours. Slope lines may be drawn on the lower side of a contour line, e.g. along the line of a re-entrant or in a depression. They are used only where it is necessary to clarify the direction of slope.0 0;0 -750; Contour values may be included to aid assessment of large height differences. They are inserted in the index contours in positions where other detail is not obscured. The figures should be orientated so that the top of the figure is on the higher side of the contour. A steep earth bank is an abrupt change in ground level which can be clearly distinguished from its surroundings, e.g. gravel or sand pits, road and railway cuttings or embankments. The tags should show the full extent of the slope, but may be omitted if two banks are close together. Impassable banks should be drawn with symbol 201 (impassable cliff). The line width of very high earth banks may be 0.25 mm.105 0;105 750;0 750;0 -135;-105 0;-105 750; A steep earth bank is an abrupt change in ground level which can be clearly distinguished from its surroundings, e.g. gravel or sand pits, road and railway cuttings or embankments. The tags should show the full extent of the slope, but may be omitted if two banks are close together. Impassable banks should be drawn with symbol 201 (impassable cliff). The line width of very high earth banks may be 0.25 mm.-555 0;555 0;450 0;450 750;-450 0;-450 750; The line width of very high earth banks may be 0.25 mm.105 0;105 750;0 750;0 150;-105 0;-105 750; The line width of very high earth banks may be 0.25 mm.-555 0;555 0;450 0;450 750;-450 0;-450 750; Use this symbol to display the full extent of wide earth banks. Distinct earth wall. Minimum height is 1 m. A small or partly ruined earth wall shall be shown with a dashed line. Minimum height is 0.5 m. An erosion gully or trench which is too small to be shown by symbol 106 is shown by a single line. The line width reflects the size of the gully. Minimum depth 1 m. The end of the line is pointed. A small erosion gully or trench. Minimum depth 0.5 m. A small obvious mound or rocky knoll which cannot be drawn to scale with a contour (diameter of mound less than ca. 5 m). The height of the knoll should be a minimum of 1 m from the surrounding ground. The symbol may not touch a contour line. A small obvious elongated knoll which cannot be drawn to scale with a contour (length less than 12 m and width less than 4 m). The height of the knoll should be a minimum of 1 m from the surrounding ground. Knolls larger than this must be shown by contours. The symbol may not be drawn in free form or such that two elongated knoll symbols overlap. The symbol may not touch a contour line.0 -600 1;165 -600;300 -331;300 0 1;300 332;165 600;0 600 1;-165 600;-300 332;-300 0 1;-300 -331;-165 -600;0 -600 3; Small shallow natural depressions and hollows (minimum diameter 2 m) which cannot be shown to scale by contours are represented by a semicircle. Minimum depth from the surrounding ground should be 1 m. Location is the centre of gravity of the symbol, which is orientated to north. Symbol 116 is used for man-made pits.600 -382 1;600 -51;332 217;0 217 1;-331 217;-600 -51;-600 -382; Pits and holes with distinct steep sides which cannot be shown to scale by symbol 106 (minimum diameter 2 m). Minimum depth from the surrounding ground should be 1 m. Location is the centre of gravity of the symbol which is orientated to north.231 -453;525 -453;0 747;-525 -453;-231 -453;0 75;231 -453 2; An area of pits or knolls which is too intricate to be shown in detail. The density of randomly placed dots may vary according to the detail on the ground. The size of the dots may vary. This symbol can be used for a special small land form feature. The definition of the symbol must be given in the map legend.-600 -600;600 600;-600 600;600 -600; An impassable cliff, quarry or earth bank (see 106) is shown with a 0.35 mm line and downward tags showing its full extent from the top line to the foot. For vertical rock faces the tags may be omitted if space is short, e.g. narrow passages between cliffs (the passage should be drawn with a width of at least 0.3 mm). The tags may extend over an area symbol representing detail immediately below the rock face. When a rock face drops straight into water making it impossible to pass under the cliff along the water's edge, the bank line is omitted or the tags should clearly extend over the bank line.90 150;90 750;0 150;0 750;-90 150;-90 750; An impassable cliff, quarry or earth bank (see 106) is shown with a 0.35 mm line and downward tags showing its full extent from the top line to the foot. For vertical rock faces the tags may be omitted if space is short, e.g. narrow passages between cliffs (the passage should be drawn with a width of at least 0.3 mm). The tags may extend over an area symbol representing detail immediately below the rock face. When a rock face drops straight into water making it impossible to pass under the cliff along the water's edge, the bank line is omitted or the tags should clearly extend over the bank line.-540 0;540 0;450 0;450 750;-450 0;-450 750; For vertical rock faces the tags may be omitted if space is short, e.g. narrow passages between cliffs (the passage should be drawn with a width of at least 0.3 mm). Use this symbol to display the full extent of a wide cliff. In the case of unusual features such as rock pillars or massive cliffs or gigantic boulders, the rocks shall be shown in plan shape without tags. A small vertical rock face (minimum height 1 m) may be shown without tags. If the direction of fall of the rock face is not apparent from the contours or to improve legibility, short tags should be drawn in the direction of the fall. For passable rock faces shown without tags the ends of the line may be rounded to improve legibility.90 75;90 750;0 75;0 750;-90 75;-90 750; A small vertical rock face (minimum height 1 m) may be shown without tags. If the direction of fall of the rock face is not apparent from the contours or to improve legibility, short tags should be drawn in the direction of the fall. For passable rock faces shown without tags the ends of the line may be rounded to improve legibility.-540 0;540 0;450 0;450 750;-450 0;-450 750; Should be used if the direction of fall of the rock face is apparent from the contours and the legibility is good. Should be used if the direction of fall of the rock face is apparent from the contours and the legibility is good.-450 0;450 0; For passable rock faces shown without tags the ends of the line may be rounded to improve legibility. For passable rock faces shown without tags the ends of the line may be rounded to improve legibility.-262 0;262 0; Rocky pits, holes or mineshafts which may constitute a danger to the runner. Location is the centre of gravity of the symbol, which is orientated to north.262 -465;525 -465;0 735;-525 -465;-262 -465;0 135;262 -465 2; A cave is represented by the same symbol as a rocky pit. In this case the symbol should be orientated to point up the slope as indicated opposite. The centre of gravity of the symbol marks the opening.262 -465;525 -465;0 735;-525 -465;-262 -465;0 135;262 -465 2; A small distinct boulder (minimum height 1 m). Every boulder marked on the map should be immediately identifiable on the ground. To be able to show the distinction between boulders with significant difference in size it is permitted to enlarge this symbol by 20% (diameter 0.5 mm). To be able to show the distinction between boulders with significant difference in size it is permitted to enlarge this symbol by 20% (diameter 0.5 mm). A particularly large and distinct boulder. For gigantic boulders symbol 202 should be used. An area which is covered with so many blocks of stone that they cannot be marked individually is shown with randomly orientated solid triangles with sides of ratio 8:6:5. A minimum of two triangles should be used. The going is indicated by the density of the triangles. To be able to show the distinction between boulder fields with a significant difference in boulder size it is permitted to enlarge the triangles by 20%.150 452;150 -509;-300 54;150 452 2; To be able to show the distinction between boulder fields with a significant difference in boulder size it is permitted to enlarge the triangles by 20%.180 542;180 -610;-360 65;180 542 2; A small distinct group of boulders so closely clustered together that they cannot be marked individually. The symbol is an equilateral triangle orientated to the north. To be able to show the distinction between boulder clusters with significant difference in size it is permitted to enlarge this symbol by 25% (1.0 mm).-600 347;600 347;0 -693;-600 347 2; To be able to show the distinction between boulder clusters with significant difference in size it is permitted to enlarge this symbol by 25% (1.0 mm).-750 434;750 434;0 -865;-750 434 2; Stony or rocky ground which affects going should be shown on the map. The dots should be randomly distributed with density according to the amount of rock. A minimum of three dots should be used. Stony or rocky ground which affects going should be shown on the map. The dots should be randomly distributed with density according to the amount of rock. A minimum of three dots should be used. An area of soft sandy ground or gravel with no vegetation and where running is slow. Where an area of sandy ground is open but running is good, it is shown as open land (401/402). A runnable area of rock without earth or vegetation is shown as bare rock. An area of rock covered with grass, moss or other low vegetation is shown as open land (401/402). Large areas of water are shown with dot screen. Small areas of water should be shown with full colour. A black bank line indicates that the feature cannot be crossed. A black bank line indicates that the feature cannot be crossed. Where the lake or pond is smaller than 1 mm² on the printed map, the bank line is omitted. A water-filled pit or an area of water which is too small to be shown to scale. Location is the centre of gravity of the symbol, which is orientated to north.231 -453;525 -453;0 747;-525 -453;-231 -453;0 75;231 -453 2; A crossable watercourse, minimum 2 m wide. The width of watercourses over 5 m wide should be shown to scale. A crossable watercourse (including a major drainage ditch) less than 2 m wide. For better legibility a ditch in a marsh should be drawn as a crossable watercourse (305). A natural or man-made minor water channel which may contain water only intermittently. A marsh or trickle of water which is too narrow to be shown with symbol 310 (less than ca. 5 m wide). A marsh which is uncrossable or dangerous for the runner. A black line surrounds the symbol. A black line surrounds the symbol. A crossable marsh, usually with a distinct edge. The symbol should be combined with vegetation symbols to show runnability and openness. Where a small marsh area should be combined with either 403/404 it is permitted to use 401/402 to improve legibility. -375 -225;375 -225;-375 225;375 225; An indistinct or seasonal marsh or area of gradual transition from marsh to firm ground, which is crossable. The edge is generally indistinct and the vegetation similar to that of the surrounding ground. The symbol should be combined with vegetation symbols to show runnability and openness.-675 0;675 0;-675 0;675 0; -675 -450;675 -450;-675 450;675 450;-1537 0;-187 0;188 0;1537 0; Wells and captive springs, which are clearly visible on the ground. The source of a stream with a distinct outflow. The symbol is orientated to open downstream.600 -382 1;600 -51;332 217;0 217 1;-331 217;-600 -51;-600 -382; A special small water feature. The definition of the symbol must always be given in the map legend.-600 -600;600 600;-600 600;600 -600; Cultivated land, fields, meadows, grassland, etc. without trees, offering easy running. If yellow coloured areas becomes dominant, a screen (75%) instead of full yellow may be used. Meadows with scattered trees or bushes, with grass or similar ground cover offering easy running. Areas smaller than 10 mm at the maps scale are shown as open land (401). Individual trees may be added (418, 419, 420). If yellow coloured areas becomes dominant, a screen (75%) instead of full yellow may be used. Heath, moorland, felled areas, newly planted areas (trees lower than ca. 1 m) or other generally open land with rough ground vegetation, heather or tall grass. Symbol 403 may be combined with symbols 407 and 409 to show reduced runnability. Where there are scattered trees in rough open land, areas of white (or green) should appear in the tone. Such an area may be generalised by using a regular pattern of large white dots in the yellow screen. Areas smaller than 16 mm in the maps scale are shown as rough open land (403). Individual trees may be added (418, 419, 420). Typically open runnable forest for the particular type of terrain. If no part of the forest is runnable then no white should appear on the map. An area with dense trees (low visibility) which reduces running to ca. 60-80% of normal speed. An area of dense undergrowth but otherwise good visibility (brambles, heather, low bushes, and including cut branches) which reduces running to ca. 60-80% of normal speed. This symbol may not be combined with 406 or 408. An area with dense trees or thicket (low visibility) which reduce running to ca. 20-60% of normal speed. An area of dense undergrowth but otherwise good visibility (brambles, heather, low bushes, and including cut branches) which reduces running to ca. 20-60% of normal speed. This symbol may not be combined with 406 or 408. An area of dense vegetation (trees or undergrowth) which is barely passable. Running reduced to ca. 0-20% of normal speed. Line of minimum width for symbol 410. When an area of forest provides good running in one direction but less good in others, white stripes are left in the screen symbol to show the direction of good running. When an area of forest provides good running in one direction but less good in others, white stripes are left in the screen symbol to show the direction of good running. When an area of forest provides good running in one direction but less good in others, white stripes are left in the screen symbol to show the direction of good running. Land planted with fruit trees or bushes. The dot lines may be orientated to show the direction of planting. If yellow coloured areas becomes dominant, a screen (75%) instead of full yellow may be used. The green lines may be orientated to show the direction of planting. If yellow coloured areas becomes dominant, a screen (75%) instead of full yellow may be used.0 -975;0 975;0 -975;0 975; The boundary of cultivated land when not shown with other symbols (fence, wall, path, etc.) is shown with a black line. A permanent boundary between different types of cultivated land is also shown with this symbol. Cultivated land which is seasonally out-of-bounds due to growing crops may be shown with a black dot screen. A distinct forest edge or very distinct vegetation boundary within the forest. Symbols 418, 419 and 420 can be used for special small vegetation features. The definition of the symbol must be given in each case in the map legend.-600 -600;600 600;600 -600;-600 600; Symbols 418, 419 and 420 can be used for special small vegetation features. The definition of the symbol must be given in each case in the map legend. Symbols 418, 419 and 420 can be used for special small vegetation features. The definition of the symbol must be given in each case in the map legend. A road with two carriageways. The width of the symbol should be drawn to scale but not smaller than the minimum width. The outer boundary lines may be replaced with symbols 519, 521, 522 or 524 if a fence or wall is so close to the motorway edge that it cannot practically be shown as a separate symbol. The space between the black lines must be filled with brown (50%). A road under construction may be shown with broken lines. A road under construction may be shown with broken lines. Road wider than 5m. The width of the symbol should be drawn to scale but not smaller than the minimum width. The outer boundary lines may be replaced with symbols 519, 521, 522 or 524 if a fence or wall is so close to the motorway edge that it cannot practically be shown as a separate symbol. The space between the black lines must be filled with brown (50%). A road under construction may be shown with broken lines. A road under construction may be shown with broken lines. Road 3-5 m wide. The space between the black lines must be filled with brown (50%). A road under construction may be shown with broken lines. A road under construction may be shown with broken lines. A maintained road suitable for motor vehicles in all weather. Width less than 3 m. A track or poorly maintained road suitable for vehicles only when travelling slowly. Width less than 3 m. A large path, or old vehicle track, which is distinct on the ground. A small path or (temporary) forest extraction track which can be followed at competition speed. A less distinct small path or forestry extraction track. A distinct ride, less than ca. 5 m wide. A ride is a linear break in the forest (usually plantation) which does not have a distinct path along it. Where there is a path along a ride, symbols 507 or 508 should be used in place of symbol 509. A footbridge with no path leading to it. Note: if the stream is wider than 0.25mm, adjust this symbol so it extends 0.5mm over both sides of the stream!0 -937;0 938; A railway or other kind of railed track (tramway, truckway, etc.).0 -788;0 788; Power line, cableway or skilift. The bars indicate the exact location of the pylons.0 -555;0 555; Major power lines should be drawn with a double line. The gap between the lines may indicate the extent of the powerline.0 -1125;0 1125; A way under roads, railways, etc. which may be used by the runner. This symbol is used whether or not the tunnel has a track leading to it.-450 -780;0 0;15 0;-15 0;0 0;450 -780; A way under roads, railways, etc. which may be used by the runner. This symbol is used whether or not the tunnel has a track leading to it.450 -780;0 0;-450 -780; A stone wall or stone-faced bank. A ruined stone wall may be shown by a dashed line. A stone wall higher than ca 1.5 m, not crossable to the average orienteer. A wooden or wire fence less than ca. 1.5 m high.0 0;375 650; A ruined fence may be shown with a dashed line.0 0;375 650; A boarded or wire fence higher than ca 1.5 m, not crossable to the average orienteer, eg. deer fence.450 0;825 650;-450 0;-75 650; All ways through or over high fences or walls must be indicated. The symbol may also be used for a gate through or stile over a stone wall (519) or a fence (522) or a pipeline (534).450 -750;450 750;-450 -750;-450 750; A building is shown with its ground plan so far as the scale permits. -375 -375;375 -375;375 375;-375 375;-375 -375 2; Houses and gardens and other built up areas. Roads, buildings and other significant features within a settlement must be shown. If all buildings cannot be shown, an alternative symbol (black line screen) may be used. Houses and gardens and other built up areas. Roads, buildings and other significant features within a settlement must be shown. If all buildings cannot be shown, an alternative symbol (black line screen) may be used. Areas which are permanently forbidden to the runner are shown as out of bounds. The screen is superimposed on the normal map detail. A bounding line may be drawn if there is no natural boundary (see 709). A bounding line may be drawn if there is no natural boundary (see 709). An area of hard standing used for parking or other purposes. The ground plan of a ruin is shown to scale, down to the minimum size shown opposite. Very small ruins may be drawn with a solid line. Very small ruins may be drawn with a solid line. Very small ruins may be drawn with a solid line.480 480;480 -480;-480 -480;-480 480;480 480 2; A firing range is shown with a special symbol to indicate the need for caution. Associated buildings are individually marked.2550 -1050;2550 1050;2550 0;-375 -889;0 0;-375 890;2550 0 2; A distinct grave marked by a stone or shrine. Location is at the centre of gravity of the symbol, which is orientated to north. A cemetery is shown by using grave symbols as space permits.0 -645;0 855;-525 -193;525 -193; A pipeline (gas, water, oil, etc.) above ground level which can be crossed over or under.0 0;-531 -531;0 0;-531 531; A pipeline which cannot be crossed.0 0;-531 -531;0 0;-531 531; A high tower or large pylon, standing above the level of the surrounding forest. Location is at the centre of gravity of the symbol.-1050 0;1050 0;0 -1050;0 1050; An obvious shooting platform or seat, or small tower. Location is at the centre of gravity of the symbol.0 -393;0 1107;-750 -393;750 -393; Cairn, memorial stone or boundary stone (or a trigonometric point in some countries) more than 0.5 m high.0 0; A fodder rack which is free standing or built on to a tree. Location is at the centre of gravity of the symbol. For land access reasons these may be omitted.0 -483;0 1017;-750 -49;0 -483;750 -49; Special man-made features are shown with these symbols. The definition of the symbols must be given in each case in the map legend. Special man-made features are shown with these symbols. The definition of the symbols must be given in each case in the map legend.-600 -600;600 600;600 -600;-600 600; Magnetic north lines are lines placed on the map pointing to magnetic north. Their spacing on the map should be 33.33 mm which represents 500 m on the ground at the scale of 1:15 000. For maps with other scales lines placing should be at intervals which represents a round number of meters (e.g. 50 m, 100 m, 250 m, 500 m) and the spacing should be between 20 mm and 40 mm on the map. North lines may be broken where they obscure small features such as boulders, knolls, cliffs, stream junctions, path ends, etc. In areas with very few water features, blue lines may be used. Magnetic north lines are lines placed on the map pointing to magnetic north. Their spacing on the map should be 33.33 mm which represents 500 m on the ground at the scale of 1:15 000. For maps with other scales lines placing should be at intervals which represents a round number of meters (e.g. 50 m, 100 m, 250 m, 500 m) and the spacing should be between 20 mm and 40 mm on the map. North lines may be broken where they obscure small features such as boulders, knolls, cliffs, stream junctions, path ends, etc. In areas with very few water features, blue lines may be used. At least three registration marks must be placed within the frame of a map in a non-symmetrical position. In addition, a colour check should also be possible.0 -2000;0 2000;-2000 0;2000 0; Spot heights are used for the rough assessment of height differences. The height is given to the nearest metre. The figures are orientated to the north. Water levels are given without the dot. Spot heights are used for the rough assessment of height differences. The height is given to the nearest metre. The figures are orientated to the north. Water levels are given without the dot. The start or map issue point (if not at the start) is shown by an equilateral triangle which points in the direction of the first control. The centre of the triangle shows the precise position of the start point.-3500 2021;0 -4041;3500 2021;-3500 2021 18; The control points are shown with circles. The centre of the circle shows the precise position of the feature. Sections of circles should be omitted to leave important detail showing. The number of the control is placed close to the control point circle in such a way that it does not obscure important detail. The numbers are orientated to north. Where controls are to be visited in order, the start, control points and finish are joined together by straight lines. Sections of lines should be omitted to leave important detail showing. A marked route is shown on the map with a dashed line. The finish is shown by two concentric circles.0 0; A boundary which it is not permitted to cross. A crossing point through or over a wall or fence, or across a road or railway or through a tunnel or an out-of-bounds area is drawn on the map with two lines curving outwards.-1500 725 1;-500 450;500 450;1500 725;-1500 -725 1;-500 -450;500 -450;1500 -725; An out-of-bounds area, see also symbol 528, is shown with vertical stripes. A bounding line may be drawn if there is no natural boundary, as follows: - a solid line indicates that the boundary is marked continuously (tapes, etc.) on the ground, - a dashed line indicates intermittent marking on the ground, - no line indicates no marking on the ground. A solid line indicates that the boundary is marked continuously (tapes, etc.) on the ground. A dashed line indicates intermittent marking on the ground. An area presenting danger to the competitor is shown with cross-hatched diagonal lines. A route which is out-of-bounds is shown with crosses.1061 -1061;-1061 1061;1061 1061;-1061 -1061; The location of a first aid post.-1500 0;1500 0;0 -1500;0 1500; The location of a refreshment point which is not at a control.-1340 -1395;-820 1380;1340 -1395;820 1380;0 -1770 1;600 -1770;1200 -1620;1340 -1395 1;1200 -1170;600 -1020;0 -1020 1;-600 -1020;-1200 -1170;-1340 -1395 1;-1200 -1620;-600 -1770;0 -1770;-820 1380 1;-680 1680;680 1680;820 1380; This symbol provides a simple and quick way to make training courses. The purple line will extend a bit into the finish symbol. This is a shortcoming of this simple approach.3041 0;3541 0;-3021 3500;3041 0;-3021 -3500;-3021 3500 18;-600 0;600 0;0 0; The OpenOrienteering Logo.-12797 -557 1;-12755 -447;-12770 -420;-12873 -420 1;-12953 -420;-12973 -440;-12973 -520 1;-12973 -635;-12838 -663;-12797 -557 18;-933 2063 1;-933 1925;-920 1900;-851 1900 1;-780 1900;-770 1921;-781 2050 1;-789 2154;-814 2203;-863 2213 1;-920 2224;-933 2197;-933 2063;-933 2063 18;-8875 -3860 1;-8922 -3920;-8984 -3968;-9061 -4006 1;-9134 -4045;-9225 -4065;-9337 -4065 1;-9444 -4065;-9535 -4045;-9612 -4006 1;-9688 -3974;-9752 -3927;-9803 -3867 1;-9855 -3807;-9895 -3737;-9925 -3655 1;-9950 -3578;-9970 -3500;-9982 -3418;-8723 -3418 1;-8725 -3500;-8740 -3578;-8768 -3655 1;-8789 -3732;-8824 -3800;-8875 -3860 18;-4230 -3923 1;-4183 -3737;-4160 -3527;-4160 -3296;-4160 -1395;-5113 -1395;-5113 -3182 1;-5113 -3488;-5155 -3707;-5235 -3833 1;-5317 -3961;-5468 -4027;-5690 -4027 1;-5759 -4027;-5830 -4021;-5907 -4013 1;-5983 -4008;-6052 -4004;-6112 -3993;-6112 -1395;-7064 -1395;-7064 -4646 1;-6903 -4693;-6694 -4736;-6438 -4774 1;-6181 -4818;-5913 -4838;-5631 -4838 1;-5347 -4838;-5110 -4800;-4921 -4723 1;-4730 -4652;-4579 -4547;-4467 -4410 1;-4357 -4273;-4277 -4111;-4230 -3923 18;-13524 1118 1;-13605 1126;-13667 1137;-13708 1150;-13708 3722;-14662 3722;-14662 536 1;-14492 476;-14292 420;-14060 369 1;-13827 314;-13565 286;-13280 286 1;-13229 286;-13167 290;-13096 299 1;-13022 303;-12951 312;-12878 325 1;-12806 333;-12733 345;-12660 363 1;-12587 375;-12526 392;-12474 414;-12634 1201 1;-12719 1180;-12819 1158;-12936 1137 1;-13051 1112;-13174 1098;-13306 1098 1;-13366 1098;-13439 1105;-13524 1118 18;-11093 -200 1;-11204 -101;-11336 -52;-11490 -52 1;-11642 -52;-11777 -101;-11893 -200 1;-12004 -303;-12059 -441;-12059 -616 1;-12059 -791;-12004 -927;-11893 -1026 1;-11777 -1128;-11642 -1179;-11490 -1179 1;-11336 -1179;-11204 -1128;-11093 -1026 1;-10978 -927;-10920 -791;-10920 -616 1;-10920 -441;-10978 -303;-11093 -200 18;-11009 3722;-11963 3722;-11963 357;-11009 357;-11009 3722 18;-8649 1053 1;-8755 1053;-8847 1073;-8924 1112 1;-9001 1145;-9065 1192;-9115 1252 1;-9167 1312;-9207 1381;-9237 1464 1;-9264 1539;-9282 1618;-9296 1700;-8034 1700 1;-8039 1618;-8054 1539;-8079 1464 1;-8101 1387;-8137 1318;-8189 1259 1;-8236 1199;-8297 1150;-8374 1112 1;-8445 1073;-8537 1053;-8649 1053 18;-5220 1105 1;-5297 1110;-5365 1115;-5425 1125;-5425 3722;-6378 3722;-6378 472 1;-6217 425;-6007 382;-5751 344 1;-5495 301;-5227 280;-4945 280 1;-4658 280;-4422 318;-4235 395 1;-4042 467;-3890 572;-3781 709 1;-3669 845;-3591 1007;-3544 1195 1;-3497 1381;-3474 1592;-3474 1821;-3474 3722;-4427 3722;-4427 1937 1;-4427 1630;-4467 1411;-4549 1285 1;-4628 1156;-4780 1092;-5002 1092 1;-5070 1092;-5143 1097;-5220 1105 18;-2636 2346;-2636 -481;-1683 -634;-1683 357;-539 357;-539 1150;-1683 1150;-1683 2333 1;-1683 2535;-1648 2694;-1581 2813 1;-1508 2933;-1365 2993;-1151 2993 1;-1050 2993;-945 2984;-838 2966 1;-728 2946;-627 2918;-539 2884;-404 3626 1;-518 3673;-646 3712;-787 3748 1;-928 3780;-1102 3799;-1305 3799 1;-1566 3799;-1781 3764;-1951 3696 1;-2123 3624;-2259 3526;-2361 3402 1;-2463 3274;-2536 3121;-2579 2941 1;-2618 2763;-2636 2565;-2636 2346 18;2142 1464 1;2121 1387;2084 1318;2032 1259 1;1986 1199;1924 1150;1847 1112 1;1776 1073;1685 1053;1572 1053 1;1466 1053;1375 1073;1298 1112 1;1221 1145;1156 1192;1106 1252 1;1054 1312;1014 1381;984 1464 1;958 1539;939 1618;926 1700;2187 1700 1;2183 1618;2168 1539;2142 1464 18;5923 1700 1;5919 1618;5904 1539;5878 1464 1;5856 1387;5821 1318;5769 1259 1;5723 1199;5661 1150;5585 1112 1;5511 1073;5419 1053;5308 1053 1;5201 1053;5110 1073;5033 1112 1;4957 1145;4893 1192;4841 1252 1;4790 1312;4750 1381;4720 1464 1;4695 1539;4675 1618;4663 1700;5923 1700 18;8718 1118 1;8637 1126;8575 1137;8534 1150;8534 3722;7581 3722;7581 536 1;7750 476;7950 420;8182 369 1;8415 314;8677 286;8962 286 1;9013 286;9075 290;9146 299 1;9220 303;9292 312;9365 325 1;9437 333;9510 345;9583 363 1;9655 375;9717 392;9768 414;9608 1201 1;9523 1180;9424 1158;9306 1137 1;9191 1112;9069 1098;8937 1098 1;8877 1098;8804 1105;8718 1118 18;10350 -200 1;10238 -303;10183 -441;10183 -616 1;10183 -791;10238 -927;10350 -1026 1;10465 -1128;10600 -1179;10752 -1179 1;10906 -1179;11038 -1128;11149 -1026 1;11264 -927;11323 -791;11323 -616 1;11323 -441;11264 -303;11149 -200 1;11038 -101;10906 -52;10752 -52 1;10600 -52;10465 -101;10350 -200 18;11233 3722;10279 3722;10279 357;11233 357;11233 3722 18;14726 709 1;14836 845;14916 1007;14963 1195 1;15010 1381;15033 1592;15033 1821;15033 3722;14080 3722;14080 1937 1;14080 1630;14039 1411;13958 1285 1;13876 1156;13725 1092;13504 1092 1;13435 1092;13363 1097;13287 1105 1;13210 1110;13142 1115;13082 1125;13082 3722;12129 3722;12129 472 1;12290 425;12499 382;12756 344 1;13012 301;13280 280;13562 280 1;13847 280;14083 318;14272 395 1;14463 467;14614 572;14726 709 18;-17051 -1307 1;-17299 -1307;-17525 -1349;-17729 -1434 1;-17931 -1521;-18102 -1639;-18247 -1794 1;-18392 -1951;-18505 -2139;-18587 -2355 1;-18669 -2579;-18708 -2820;-18708 -3085 1;-18708 -3350;-18669 -3591;-18587 -3808 1;-18502 -4027;-18387 -4211;-18242 -4365 1;-18092 -4518;-17917 -4638;-17717 -4723 1;-17512 -4808;-17291 -4851;-17051 -4851 1;-16808 -4851;-16586 -4808;-16386 -4723 1;-16182 -4638;-16006 -4518;-15861 -4365 1;-15716 -4211;-15603 -4027;-15523 -3808 1;-15442 -3591;-15401 -3350;-15401 -3085 1;-15401 -2820;-15440 -2579;-15517 -2355 1;-15593 -2139;-15703 -1951;-15848 -1794 1;-15993 -1639;-16168 -1521;-16373 -1434 1;-16574 -1349;-16800 -1307;-17051 -1307 18;-17051 -2126 1;-16834 -2126;-16668 -2210;-16552 -2383 1;-16433 -2557;-16373 -2792;-16373 -3085 1;-16373 -3380;-16433 -3610;-16552 -3777 1;-16668 -3946;-16834 -4034;-17051 -4034 1;-17269 -4034;-17437 -3946;-17557 -3777 1;-17675 -3610;-17736 -3380;-17736 -3085 1;-17736 -2792;-17675 -2557;-17557 -2383 1;-17437 -2210;-17269 -2126;-17051 -2126 18;-14662 -213;-14662 -4646 1;-14576 -4673;-14478 -4697;-14369 -4716 1;-14257 -4743;-14142 -4765;-14022 -4781 1;-13898 -4798;-13776 -4811;-13652 -4819 1;-13524 -4833;-13402 -4838;-13287 -4838 1;-13009 -4838;-12763 -4796;-12544 -4711 1;-12328 -4630;-12144 -4513;-11995 -4358 1;-11846 -4210;-11732 -4027;-11657 -3808 1;-11574 -3591;-11535 -3348;-11535 -3078 1;-11535 -2819;-11566 -2582;-11629 -2368 1;-11694 -2156;-11788 -1972;-11911 -1819 1;-12034 -1666;-12189 -1546;-12373 -1461 1;-12556 -1376;-12765 -1333;-13006 -1333 1;-13137 -1333;-13261 -1346;-13377 -1371 1;-13492 -1395;-13602 -1432;-13708 -1479;-13708 -213;-14662 -213 18;-13184 -2139 1;-12733 -2139;-12506 -2443;-12506 -3054 1;-12506 -3348;-12572 -3582;-12704 -3756 1;-12837 -3936;-13032 -4027;-13293 -4027 1;-13379 -4027;-13457 -4021;-13530 -4013 1;-13602 -4008;-13662 -4004;-13708 -3993;-13708 -2274 1;-13648 -2234;-13572 -2202;-13479 -2177 1;-13381 -2152;-13282 -2139;-13184 -2139 18;-9182 -1307 1;-9485 -1307;-9750 -1352;-9976 -1440 1;-10198 -1530;-10383 -1652;-10533 -1806 1;-10678 -1964;-10787 -2149;-10858 -2362 1;-10926 -2575;-10962 -2807;-10962 -3054 1;-10962 -3352;-10917 -3612;-10827 -3833 1;-10733 -4060;-10611 -4248;-10462 -4396 1;-10314 -4547;-10141 -4660;-9950 -4736 1;-9754 -4813;-9553 -4851;-9349 -4851 1;-8872 -4851;-8494 -4705;-8217 -4410 1;-7939 -4120;-7801 -3692;-7801 -3123 1;-7801 -3069;-7803 -3007;-7808 -2939 1;-7810 -2873;-7816 -2817;-7819 -2766;-9982 -2766 1;-9961 -2569;-9870 -2413;-9707 -2299 1;-9545 -2184;-9327 -2126;-9055 -2126 1;-8881 -2126;-8708 -2141;-8542 -2171 1;-8372 -2205;-8234 -2246;-8127 -2292;-7999 -1517 1;-8051 -1493;-8119 -1468;-8204 -1440 1;-8289 -1416;-8385 -1395;-8492 -1378 1;-8594 -1356;-8706 -1339;-8824 -1326 1;-8944 -1314;-9063 -1307;-9182 -1307 18;-9982 -3418;-8723 -3418 1;-8725 -3500;-8740 -3578;-8768 -3655 1;-8789 -3732;-8824 -3800;-8875 -3860 1;-8922 -3920;-8984 -3968;-9061 -4006 1;-9134 -4045;-9225 -4065;-9337 -4065 1;-9444 -4065;-9535 -4045;-9612 -4006 1;-9688 -3974;-9752 -3927;-9803 -3867 1;-9855 -3807;-9895 -3737;-9925 -3655 1;-9950 -3578;-9970 -3500;-9982 -3418 18;-17051 -2126 1;-17269 -2126;-17437 -2210;-17557 -2383 1;-17675 -2557;-17736 -2792;-17736 -3085 1;-17736 -3380;-17675 -3610;-17557 -3777 1;-17437 -3946;-17269 -4034;-17051 -4034 1;-16834 -4034;-16668 -3946;-16552 -3777 1;-16433 -3610;-16373 -3380;-16373 -3085 1;-16373 -2792;-16433 -2557;-16552 -2383 1;-16668 -2210;-16834 -2126;-17051 -2126 18;-17064 -2593 1;-16944 -2591;-16842 -2808;-16838 -3077 1;-16834 -3347;-16928 -3567;-17049 -3569 1;-17170 -3571;-17271 -3354;-17275 -3084 1;-17280 -2815;-17185 -2595;-17064 -2593 18;-8496 3810 1;-8798 3810;-9062 3765;-9288 3677 1;-9510 3588;-9696 3466;-9845 3312 1;-9990 3154;-10098 2969;-10171 2756 1;-10240 2542;-10273 2311;-10273 2065 1;-10273 1766;-10230 1507;-10140 1285 1;-10045 1058;-9923 870;-9775 722 1;-9625 572;-9455 459;-9264 382 1;-9067 305;-8867 267;-8662 267 1;-8184 267;-7806 414;-7529 709 1;-7252 998;-7113 1426;-7113 1995 1;-7113 2050;-7116 2112;-7119 2180 1;-7124 2245;-7128 2301;-7132 2353;-9296 2353 1;-9273 2550;-9182 2704;-9020 2820 1;-8857 2934;-8640 2993;-8368 2993 1;-8193 2993;-8022 2978;-7855 2948 1;-7684 2912;-7546 2873;-7440 2826;-7311 3601 1;-7363 3626;-7431 3651;-7516 3677 1;-7603 3703;-7697 3724;-7804 3741 1;-7908 3763;-8017 3779;-8137 3793 1;-8257 3804;-8376 3810;-8496 3810 18;-9296 1700;-8034 1700 1;-8039 1618;-8054 1539;-8079 1464 1;-8101 1387;-8137 1318;-8189 1259 1;-8236 1199;-8297 1150;-8374 1112 1;-8445 1073;-8537 1053;-8649 1053 1;-8755 1053;-8847 1073;-8924 1112 1;-9001 1145;-9065 1192;-9115 1252 1;-9167 1312;-9207 1381;-9237 1464 1;-9264 1539;-9282 1618;-9296 1700 18;1726 3810 1;1422 3810;1159 3765;933 3677 1;712 3588;524 3466;376 3312 1;231 3154;123 2969;49 2756 1;-19 2542;-52 2311;-52 2065 1;-52 1766;-9 1507;81 1285 1;177 1058;298 870;446 722 1;596 572;766 459;958 382 1;1154 305;1354 267;1559 267 1;2038 267;2416 414;2692 709 1;2968 998;3109 1426;3109 1995 1;3109 2050;3105 2112;3102 2180 1;3097 2245;3094 2301;3088 2353;926 2353 1;947 2550;1039 2704;1201 2820 1;1364 2934;1581 2993;1854 2993 1;2029 2993;2199 2978;2365 2948 1;2538 2912;2675 2873;2782 2826;2910 3601 1;2859 3626;2790 3651;2705 3677 1;2619 3703;2524 3724;2417 3741 1;2314 3763;2204 3779;2084 3793 1;1965 3804;1846 3810;1726 3810 18;926 1700;2187 1700 1;2183 1618;2168 1539;2142 1464 1;2121 1387;2084 1318;2032 1259 1;1986 1199;1924 1150;1847 1112 1;1776 1073;1685 1053;1572 1053 1;1466 1053;1375 1073;1298 1112 1;1221 1145;1156 1192;1106 1252 1;1054 1312;1014 1381;984 1464 1;958 1539;939 1618;926 1700 18;5463 3810 1;5160 3810;4895 3765;4668 3677 1;4446 3588;4262 3466;4112 3312 1;3967 3154;3858 2969;3787 2756 1;3719 2542;3684 2311;3684 2065 1;3684 1766;3729 1507;3819 1285 1;3911 1058;4033 870;4183 722 1;4332 572;4503 459;4695 382 1;4891 305;5091 267;5297 267 1;5773 267;6151 414;6428 709 1;6706 998;6844 1426;6844 1995 1;6844 2050;6843 2112;6837 2180 1;6834 2245;6829 2301;6826 2353;4663 2353 1;4683 2550;4775 2704;4938 2820 1;5100 2934;5318 2993;5590 2993 1;5765 2993;5936 2978;6103 2948 1;6272 2912;6411 2873;6518 2826;6646 3601 1;6594 3626;6526 3651;6441 3677 1;6356 3703;6259 3724;6152 3741 1;6051 3763;5939 3779;5821 3793 1;5701 3804;5581 3810;5463 3810 18;4663 1700;5923 1700 1;5919 1618;5904 1539;5878 1464 1;5856 1387;5821 1318;5769 1259 1;5723 1199;5661 1150;5585 1112 1;5511 1073;5419 1053;5308 1053 1;5201 1053;5110 1073;5033 1112 1;4957 1145;4893 1192;4841 1252 1;4790 1312;4750 1381;4720 1464 1;4695 1539;4675 1618;4663 1700 18;17092 4924 1;16887 4924;16682 4905;16477 4867 1;16272 4832;16083 4785;15908 4725;16074 3926 1;16224 3986;16379 4032;16543 4067 1;16707 4101;16896 4119;17105 4119 1;17377 4119;17570 4059;17680 3939 1;17796 3819;17854 3666;17854 3479;17854 3357 1;17750 3404;17644 3441;17533 3466 1;17427 3487;17309 3498;17182 3498 1;16717 3498;16361 3361;16113 3087 1;15866 2811;15743 2424;15743 1930 1;15743 1683;15781 1458;15857 1259 1;15934 1053;16044 878;16189 733 1;16339 589;16521 478;16734 402 1;16947 320;17187 280;17457 280 1;17572 280;17689 286;17809 299 1;17931 307;18053 320;18173 337 1;18292 354;18405 375;18512 402 1;18624 422;18722 446;18806 472;18806 3299 1;18806 3849;18665 4257;18385 4522 1;18107 4790;17677 4924;17092 4924 18;17360 2730 1;17459 2730;17550 2718;17636 2691 1;17720 2666;17794 2636;17854 2603;17854 1080 1;17807 1072;17750 1065;17687 1060 1;17623 1052;17548 1047;17464 1047 1;17212 1047;17024 1130;16900 1297 1;16776 1464;16714 1675;16714 1930 1;16714 2463;16930 2730;17360 2730 18;-17051 2993 1;-17269 2993;-17437 2908;-17557 2736 1;-17675 2561;-17736 2326;-17736 2033 1;-17736 1738;-17675 1509;-17557 1342 1;-17437 1171;-17269 1085;-17051 1085 1;-16834 1085;-16668 1171;-16552 1342 1;-16433 1509;-16373 1738;-16373 2033 1;-16373 2326;-16433 2561;-16552 2736 1;-16668 2908;-16834 2993;-17051 2993 18;-17064 2526 1;-16944 2528;-16842 2311;-16838 2042 1;-16834 1772;-16928 1552;-17049 1550 1;-17170 1548;-17271 1765;-17275 2035 1;-17280 2304;-17185 2524;-17064 2526 18;-17051 3810 1;-17299 3810;-17525 3769;-17729 3684 1;-17931 3598;-18102 3479;-18247 3324 1;-18392 3168;-18505 2980;-18587 2763 1;-18669 2540;-18708 2298;-18708 2033 1;-18708 1768;-18669 1526;-18587 1310 1;-18502 1092;-18387 906;-18242 754 1;-18092 600;-17917 480;-17717 395 1;-17512 310;-17291 267;-17051 267 1;-16808 267;-16586 310;-16386 395 1;-16182 480;-16006 600;-15861 754 1;-15716 906;-15603 1092;-15523 1310 1;-15442 1526;-15401 1768;-15401 2033 1;-15401 2298;-15440 2540;-15517 2763 1;-15593 2980;-15703 3168;-15848 3324 1;-15993 3479;-16168 3598;-16373 3684 1;-16574 3769;-16800 3810;-17051 3810 18;-17051 2993 1;-16834 2993;-16668 2908;-16552 2736 1;-16433 2561;-16373 2326;-16373 2033 1;-16373 1738;-16433 1509;-16552 1342 1;-16668 1171;-16834 1085;-17051 1085 1;-17269 1085;-17437 1171;-17557 1342 1;-17675 1509;-17736 1738;-17736 2033 1;-17736 2326;-17675 2561;-17557 2736 1;-17437 2908;-17269 2993;-17051 2993 18;17090 5390 1;16864 5390;16623 5362;16391 5321 1;16389 5320;16389 5320;16387 5320 1;16169 5284;15963 5240;15758 5170 1;15555 5102;15409 4847;15450 4637 1;15450 4635;15450 4634;15450 4632;15538 4221 1;15549 4165;15526 4109;15478 4079 1;15431 4049;15369 4052;15324 4088 1;15245 4152;15138 4191;15033 4191;14080 4191 1;13930 4193;13775 4107;13692 3983 1;13669 3944;13625 3921;13580 3921 1;13534 3921;13492 3944;13466 3983 1;13385 4107;13233 4191;13083 4191;12131 4191 1;12001 4191;11866 4131;11781 4032 1;11755 4004;11719 3987;11680 3987 1;11642 3987;11606 4004;11581 4032 1;11496 4131;11364 4191;11234 4191;10281 4191 1;10048 4193;9815 3957;9815 3724;9815 1796 1;9815 1758;9798 1721;9770 1697 1;9741 1670;9703 1658;9665 1663 1;9608 1670;9553 1668;9498 1655 1;9433 1638;9341 1618;9227 1597 1;9221 1595;9217 1593;9210 1592 1;9161 1582;9142 1582;9157 1584 1;9118 1578;9078 1590;9048 1615 1;9018 1640;9001 1678;9001 1716 1;9001 1716;9001 3724;9001 3724 1;9001 3957;8768 4193;8535 4191;7582 4191 1;7424 4191;7262 4099;7183 3962 1;7162 3926;7125 3900;7082 3894 1;7040 3889;6999 3902;6969 3932 1;6935 3966;6899 3996;6855 4016 1;6768 4059;6679 4092;6574 4124 1;6467 4157;6364 4178;6253 4195 1;6244 4197;6236 4199;6226 4202 1;6107 4227;5991 4240;5870 4254 1;5738 4267;5601 4279;5461 4279 1;5121 4279;4808 4231;4520 4120 1;4514 4118;4506 4114;4498 4110 1;4220 3998;3977 3840;3778 3636 1;3717 3566;3663 3494;3609 3412 1;3571 3361;3503 3344;3445 3371 1;3389 3396;3357 3459;3370 3521 1;3404 3711;3293 3930;3120 4016 1;3034 4060;2945 4092;2837 4124 1;2732 4157;2628 4178;2517 4195 1;2508 4197;2499 4199;2490 4202 1;2372 4227;2256 4240;2134 4254 1;2001 4267;1866 4279;1726 4279 1;1386 4279;1071 4231;785 4120 1;778 4118;771 4114;761 4110 1;562 4029;383 3917;218 3782 1;186 3757;145 3748;105 3757 1;64 3765;32 3793;13 3829 1;-35 3929;-124 4013;-229 4054 1;-362 4109;-512 4164;-678 4204 1;-871 4251;-1073 4266;-1303 4266 1;-1606 4266;-1876 4227;-2127 4126 1;-2355 4031;-2561 3892;-2717 3706 1;-2719 3701;-2721 3699;-2719 3702 1;-2716 3707;-2718 3706;-2721 3700 1;-2724 3694;-2734 3667;-2764 3624 1;-2799 3577;-2859 3558;-2915 3576 1;-2969 3594;-3007 3646;-3006 3704;-3006 3724 1;-3006 3957;-3239 4193;-3473 4191;-4427 4191 1;-4577 4193;-4730 4107;-4814 3983 1;-4838 3944;-4880 3921;-4927 3921 1;-4972 3921;-5013 3944;-5038 3983 1;-5122 4107;-5274 4191;-5421 4191;-6377 4191 1;-6533 4191;-6696 4099;-6777 3962 1;-6796 3926;-6833 3900;-6875 3894 1;-6916 3889;-6958 3902;-6988 3932 1;-7021 3966;-7059 3996;-7101 4016 1;-7101 4017;-7104 4016;-7104 4016 1;-7191 4059;-7278 4092;-7384 4124 1;-7489 4157;-7592 4178;-7704 4195 1;-7714 4197;-7722 4199;-7729 4199 1;-7731 4201;-7733 4202;-7735 4204 1;-7849 4227;-7966 4240;-8088 4254 1;-8221 4267;-8355 4279;-8496 4279 1;-8836 4279;-9151 4231;-9437 4120 1;-9444 4118;-9450 4114;-9461 4110 1;-9737 3998;-9980 3840;-10181 3635 1;-10185 3631;-10185 3628;-10186 3624 1;-10218 3591;-10250 3536;-10300 3470 1;-10333 3425;-10393 3404;-10450 3423 1;-10505 3440;-10541 3493;-10541 3549 1;-10541 3549;-10541 3724;-10541 3724 1;-10541 3957;-10774 4193;-11008 4191;-11961 4191 1;-12194 4193;-12427 3957;-12427 3724;-12427 1796 1;-12427 1758;-12444 1721;-12472 1697 1;-12501 1670;-12539 1658;-12577 1663 1;-12634 1670;-12689 1668;-12744 1655 1;-12809 1638;-12901 1618;-13015 1597 1;-13021 1595;-13026 1593;-13032 1592 1;-13081 1582;-13101 1582;-13086 1584 1;-13124 1578;-13164 1590;-13194 1615 1;-13224 1640;-13240 1678;-13240 1716;-13240 3724 1;-13240 3957;-13474 4193;-13707 4191;-14660 4191 1;-14893 4193;-15126 3957;-15126 3724;-15126 3532 1;-15126 3476;-15164 3423;-15218 3406 1;-15273 3387;-15335 3408;-15368 3455 1;-15420 3526;-15463 3591;-15511 3643 1;-15517 3651;-15517 3652;-15518 3656 1;-15703 3851;-15933 4002;-16189 4110 1;-16458 4224;-16748 4279;-17051 4279 1;-17346 4279;-17629 4225;-17889 4120 1;-17895 4118;-17902 4114;-17909 4112 1;-18160 4005;-18392 3849;-18580 3651 1;-18586 3643;-18587 3641;-18588 3637 1;-18776 3434;-18919 3196;-19017 2931 1;-19017 2931;-19017 2933;-19019 2927 1;-19122 2648;-19176 2350;-19176 2033 1;-19176 1718;-19123 1419;-19019 1142;-19019 1139 1;-18913 875;-18770 642;-18587 446 1;-18407 241;-18054 35;-17897 -33 1;-17894 -33;-17891 -35;-17889 -37 1;-17628 -143;-17344 -200;-17051 -200 1;-16755 -200;-16468 -143;-16206 -33 1;-15946 76;-15707 235;-15518 433 1;-15470 487;-15425 560;-15368 639 1;-15335 685;-15273 705;-15218 687 1;-15164 669;-15126 617;-15126 559;-15126 538 1;-15126 435;-15088 329;-15023 247 1;-14983 198;-14983 129;-15023 80 1;-15088 -1;-15126 -108;-15126 -211;-15126 -1586 1;-15126 -1644;-15164 -1696;-15218 -1714 1;-15273 -1733;-15335 -1712;-15368 -1666 1;-15420 -1594;-15463 -1529;-15506 -1483 1;-15508 -1478;-15511 -1474;-15517 -1465 1;-15701 -1271;-15928 -1115;-16189 -1005 1;-16463 -889;-16753 -841;-17051 -841 1;-17341 -841;-17624 -888;-17889 -995 1;-17895 -997;-17902 -1001;-17909 -1003 1;-18165 -1113;-18394 -1273;-18578 -1463 1;-18582 -1468;-18582 -1470;-18584 -1474 1;-18586 -1476;-18587 -1479;-18588 -1483 1;-18776 -1684;-18919 -1924;-19019 -2192 1;-19122 -2472;-19176 -2770;-19176 -3085 1;-19176 -3401;-19122 -3698;-19019 -3973 1;-18915 -4238;-18774 -4473;-18586 -4675 1;-18582 -4680;-18580 -4682;-18580 -4684 1;-18577 -4686;-18573 -4688;-18572 -4690 1;-18384 -4883;-18152 -5043;-17897 -5151 1;-17894 -5153;-17891 -5154;-17889 -5156 1;-17628 -5263;-17344 -5317;-17051 -5317 1;-16755 -5317;-16468 -5263;-16204 -5150 1;-15943 -5038;-15707 -4885;-15518 -4684 1;-15468 -4631;-15425 -4560;-15368 -4481 1;-15335 -4433;-15273 -4413;-15218 -4431 1;-15164 -4449;-15126 -4502;-15126 -4560 1;-15126 -4560;-15126 -4648;-15126 -4648 1;-15126 -4840;-14978 -5038;-14794 -5093 1;-14694 -5123;-14598 -5144;-14499 -5165 1;-14499 -5165;-14477 -5168;-14452 -5173 1;-14340 -5198;-14217 -5219;-14085 -5240 1;-13922 -5266;-13842 -5266;-13682 -5281 1;-13547 -5294;-13415 -5306;-13286 -5306 1;-12964 -5306;-12660 -5253;-12380 -5145 1;-12373 -5143;-12371 -5143;-12369 -5143 1;-12101 -5041;-11864 -4883;-11665 -4681 1;-11657 -4671;-11653 -4668;-11649 -4665 1;-11536 -4548;-11441 -4412;-11358 -4260 1;-11334 -4218;-11289 -4192;-11242 -4192 1;-11193 -4192;-11148 -4218;-11125 -4260 1;-11028 -4434;-10923 -4593;-10791 -4726 1;-10600 -4917;-10372 -5065;-10128 -5165 1;-10125 -5165;-10124 -5166;-10118 -5168 1;-9872 -5263;-9613 -5317;-9350 -5317 1;-8783 -5317;-8251 -5120;-7882 -4730 1;-7846 -4691;-7812 -4641;-7767 -4581 1;-7733 -4538;-7676 -4519;-7622 -4536 1;-7569 -4553;-7532 -4600;-7529 -4656 1;-7526 -4846;-7376 -5039;-7192 -5093 1;-6999 -5148;-6777 -5186;-6518 -5226 1;-6513 -5227;-6510 -5229;-6505 -5231 1;-6226 -5276;-5933 -5306;-5630 -5306 1;-5305 -5306;-5020 -5259;-4754 -5153 1;-4499 -5054;-4271 -4908;-4106 -4706 1;-3948 -4513;-3839 -4282;-3777 -4034 1;-3719 -3803;-3694 -3557;-3694 -3294;-3694 -1395 1;-3694 -1161;-3927 -927;-4160 -927;-5113 -927 1;-5263 -927;-5419 -1011;-5502 -1136 1;-5525 -1174;-5569 -1198;-5613 -1198 1;-5660 -1198;-5701 -1174;-5727 -1136 1;-5808 -1012;-5960 -927;-6110 -927;-7063 -927 1;-7220 -929;-7380 -1021;-7459 -1158 1;-7479 -1194;-7516 -1219;-7558 -1224 1;-7599 -1231;-7641 -1216;-7671 -1186 1;-7706 -1151;-7744 -1121;-7790 -1100 1;-7898 -1046;-7976 -1023;-8071 -995 1;-8178 -963;-8281 -942;-8392 -924 1;-8400 -923;-8409 -921;-8417 -920 1;-8419 -918;-8419 -916;-8421 -916 1;-8527 -894;-8644 -874;-8775 -861 1;-8917 -846;-9054 -841;-9183 -841 1;-9523 -841;-9837 -888;-10125 -999 1;-10132 -1003;-10138 -1004;-10148 -1010 1;-10205 -1033;-10261 -1066;-10333 -1102 1;-10380 -1126;-10438 -1119;-10478 -1085 1;-10520 -1051;-10537 -996;-10520 -944 1;-10481 -826;-10453 -709;-10453 -616 1;-10453 -441;-10525 -246;-10637 -67 1;-10667 -20;-10665 40;-10633 84 1;-10575 164;-10541 262;-10541 359;-10541 559 1;-10541 617;-10505 669;-10450 687 1;-10393 705;-10333 685;-10300 639 1;-10233 547;-10171 461;-10103 392 1;-9914 202;-9685 53;-9442 -45 1;-9439 -46;-9435 -48;-9433 -50 1;-9432 -50;-9431 -50;-9429 -50 1;-9183 -144;-8927 -200;-8662 -200 1;-8096 -200;-7562 -1;-7194 386;-7194 386 1;-7192 387;-7194 385;-7194 388 1;-7192 392;-7192 389;-7192 390;-7143 453;-7079 538 1;-7046 581;-6988 598;-6935 581 1;-6882 566;-6846 519;-6843 463 1;-6837 273;-6688 79;-6505 25 1;-6313 -30;-6091 -67;-5824 -110 1;-5540 -156;-5246 -186;-4944 -186 1;-4619 -186;-4332 -142;-4066 -35 1;-3813 64;-3584 211;-3419 414 1;-3404 432;-3387 477;-3347 534 1;-3315 585;-3254 609;-3195 590 1;-3139 574;-3101 519;-3103 459 1;-3103 459;-3103 -482;-3103 -482 1;-3103 -693;-2915 -908;-2706 -941;-1753 -1091 1;-1501 -1129;-1215 -886;-1215 -633;-1215 -241 1;-1215 -166;-1155 -108;-1082 -108;-537 -108 1;-323 -106;-109 82;-79 292 1;-70 342;-35 384;11 399 1;60 415;111 402;147 367 1;333 190;549 49;786 -47 1;1037 -144;1294 -200;1559 -200 1;2126 -200;2658 -1;3027 388 1;3144 513;3239 654;3317 805 1;3338 848;3383 875;3432 875 1;3481 876;3526 852;3550 808 1;3642 654;3737 510;3853 392 1;4045 202;4273 53;4521 -47;4521 -47 1;4521 -48;4520 -46;4522 -47 1;4529 -50;4527 -50;4527 -50 1;4773 -144;5031 -200;5295 -200 1;5861 -200;6394 -1;6763 388 1;6802 432;6839 480;6882 538 1;6914 581;6969 600;7022 585 1;7074 572;7112 529;7119 476 1;7140 310;7271 152;7427 97 1;7615 32;7831 -23;8080 -81 1;8085 -82;8083 -82;8083 -82 1;8360 -146;8654 -178;8965 -178 1;9028 -178;9099 -173;9168 -166 1;9182 -165;9190 -163;9205 -161 1;9278 -156;9347 -148;9415 -136 1;9437 -133;9445 -133;9447 -133 1;9507 -125;9570 -116;9635 -103 1;9681 -95;9728 -112;9758 -146 1;9788 -181;9800 -230;9785 -274 1;9745 -394;9718 -512;9718 -616 1;9718 -878;9838 -1186;10043 -1369 1;10048 -1372;10051 -1374;10058 -1380 1;10243 -1538;10508 -1644;10751 -1644 1;10994 -1644;11257 -1542;11452 -1373 1;11458 -1367;11460 -1365;11466 -1364 1;11671 -1177;11789 -869;11789 -616 1;11789 -441;11717 -246;11606 -67 1;11576 -18;11579 44;11619 94 1;11644 132;11678 151;11716 155 1;11753 159;11791 147;11817 122 1;11871 77;11935 44;12001 25 1;12194 -30;12416 -67;12682 -110 1;12967 -156;13260 -186;13564 -186 1;13889 -186;14173 -142;14439 -35 1;14695 64;14923 211;15088 414 1;15181 529;15253 669;15316 817 1;15337 863;15381 893;15431 897 1;15481 900;15529 874;15555 830 1;15641 673;15739 530;15862 404 1;16061 212;16299 65;16570 -35 1;16849 -140;17147 -186;17457 -186 1;17585 -186;17713 -174;17847 -161 1;17854 -161;17842 -163;17865 -161 1;17990 -153;18110 -138;18237 -120 1;18355 -103;18475 -82;18593 -58 1;18605 -52;18617 -50;18628 -50 1;18743 -26;18848 -1;18940 25 1;19124 80;19273 279;19273 472;19273 3299 1;19273 3915;19111 4466;18724 4844 1;18718 4850;18716 4852;18709 4858 1;18305 5243;17742 5390;17090 5390 18;-1305 3799 1;-1102 3799;-928 3780;-787 3748 1;-646 3712;-518 3673;-404 3626;-539 2884 1;-627 2918;-728 2946;-838 2966 1;-945 2984;-1050 2993;-1151 2993 1;-1365 2993;-1508 2933;-1581 2813 1;-1648 2694;-1683 2535;-1683 2333;-1683 1150;-539 1150;-539 357;-1683 357;-1683 -634;-2636 -481;-2636 2346 1;-2636 2565;-2618 2763;-2579 2941 1;-2536 3121;-2463 3274;-2361 3402 1;-2259 3526;-2123 3624;-1951 3696 1;-1781 3764;-1566 3799;-1305 3799 18;10752 -52 1;10906 -52;11038 -101;11149 -200 1;11264 -303;11323 -441;11323 -616 1;11323 -791;11264 -927;11149 -1026 1;11038 -1128;10906 -1179;10752 -1179 1;10600 -1179;10465 -1128;10350 -1026 1;10238 -927;10183 -791;10183 -616 1;10183 -441;10238 -303;10350 -200 1;10465 -101;10600 -52;10752 -52 18;10279 3722;11233 3722;11233 357;10279 357;10279 3722 18;-11963 3722;-11009 3722;-11009 357;-11963 357;-11963 3722 18;7581 3722;8534 3722;8534 1150 1;8575 1137;8637 1126;8718 1118 1;8804 1105;8877 1098;8937 1098 1;9069 1098;9191 1112;9306 1137 1;9424 1158;9523 1180;9608 1201;9768 414 1;9717 392;9655 375;9583 363 1;9510 345;9437 333;9365 325 1;9292 312;9220 303;9146 299 1;9075 290;9013 286;8962 286 1;8677 286;8415 314;8182 369 1;7950 420;7750 476;7581 536;7581 3722 18;12129 3722;13082 3722;13082 1125 1;13142 1115;13210 1110;13287 1105 1;13363 1097;13435 1092;13504 1092 1;13725 1092;13876 1156;13958 1285 1;14039 1411;14080 1630;14080 1937;14080 3722;15033 3722;15033 1821 1;15033 1592;15010 1381;14963 1195 1;14916 1007;14836 845;14726 709 1;14614 572;14463 467;14272 395 1;14083 318;13847 280;13562 280 1;13280 280;13012 301;12756 344 1;12499 382;12290 425;12129 472;12129 3722 18;15729 3616 1;15737 3607;15749 3596;15758 3588 1;15784 3562;15799 3528;15799 3491 1;15799 3491;15799 3489;15799 3487;15794 3440;15767 3400 1;15769 3404;15763 3384;15737 3354 1;15711 3321;15671 3303;15623 3306 1;15549 3318;15499 3374;15499 3441 1;15499 3441;15499 3524;15499 3524 1;15501 3579;15533 3628;15585 3648 1;15634 3667;15691 3656;15729 3616 18;-14662 3722;-13708 3722;-13708 1150 1;-13667 1137;-13605 1126;-13524 1118 1;-13439 1105;-13366 1098;-13306 1098 1;-13174 1098;-13051 1112;-12936 1137 1;-12819 1158;-12719 1180;-12634 1201;-12474 414 1;-12526 392;-12587 375;-12660 363 1;-12733 345;-12806 333;-12878 325 1;-12951 312;-13022 303;-13096 299 1;-13167 290;-13229 286;-13280 286 1;-13565 286;-13827 314;-14060 369 1;-14292 420;-14492 476;-14662 536;-14662 3722 18;-6378 3722;-5425 3722;-5425 1125 1;-5365 1115;-5297 1110;-5220 1105 1;-5143 1097;-5070 1092;-5002 1092 1;-4780 1092;-4628 1156;-4549 1285 1;-4467 1411;-4427 1630;-4427 1937;-4427 3722;-3474 3722;-3474 1821 1;-3474 1592;-3497 1381;-3544 1195 1;-3591 1007;-3669 845;-3781 709 1;-3890 572;-4042 467;-4235 395 1;-4422 318;-4658 280;-4945 280 1;-5227 280;-5495 301;-5751 344 1;-6007 382;-6217 425;-6378 472;-6378 3722 18;17092 4924 1;17677 4924;18107 4790;18385 4522 1;18665 4257;18806 3849;18806 3299;18806 472 1;18722 446;18624 422;18512 402 1;18405 375;18292 354;18173 337 1;18053 320;17931 307;17809 299 1;17689 286;17572 280;17457 280 1;17187 280;16947 320;16734 402 1;16521 478;16339 589;16189 733 1;16044 878;15934 1053;15857 1259 1;15781 1458;15743 1683;15743 1930 1;15743 2424;15866 2811;16113 3087 1;16361 3361;16717 3498;17182 3498 1;17309 3498;17427 3487;17533 3466 1;17644 3441;17750 3404;17854 3357;17854 3479 1;17854 3666;17796 3819;17680 3939 1;17570 4059;17377 4119;17105 4119 1;16896 4119;16707 4101;16543 4067 1;16379 4032;16224 3986;16074 3926;15908 4725 1;16083 4785;16272 4832;16477 4867 1;16682 4905;16887 4924;17092 4924 18;1726 3810 1;1846 3810;1965 3804;2084 3793 1;2204 3779;2314 3763;2417 3741 1;2524 3724;2619 3703;2705 3677 1;2790 3651;2859 3626;2910 3601;2782 2826 1;2675 2873;2538 2912;2365 2948 1;2199 2978;2029 2993;1854 2993 1;1581 2993;1364 2934;1201 2820 1;1039 2704;947 2550;926 2353;3088 2353 1;3094 2301;3097 2245;3102 2180 1;3105 2112;3109 2050;3109 1995 1;3109 1426;2968 998;2692 709 1;2416 414;2038 267;1559 267 1;1354 267;1154 305;958 382 1;766 459;596 572;446 722 1;298 870;177 1058;81 1285 1;-9 1507;-52 1766;-52 2065 1;-52 2311;-19 2542;49 2756 1;123 2969;231 3154;376 3312 1;524 3466;712 3588;933 3677 1;1159 3765;1422 3810;1726 3810 18;-8496 3810 1;-8376 3810;-8257 3804;-8137 3793 1;-8017 3779;-7908 3763;-7804 3741 1;-7697 3724;-7603 3703;-7516 3677 1;-7431 3651;-7363 3626;-7311 3601;-7440 2826 1;-7546 2873;-7684 2912;-7855 2948 1;-8022 2978;-8193 2993;-8368 2993 1;-8640 2993;-8857 2934;-9020 2820 1;-9182 2704;-9273 2550;-9296 2353;-7132 2353 1;-7128 2301;-7124 2245;-7119 2180 1;-7116 2112;-7113 2050;-7113 1995 1;-7113 1426;-7252 998;-7529 709 1;-7806 414;-8184 267;-8662 267 1;-8867 267;-9067 305;-9264 382 1;-9455 459;-9625 572;-9775 722 1;-9923 870;-10045 1058;-10140 1285 1;-10230 1507;-10273 1766;-10273 2065 1;-10273 2311;-10240 2542;-10171 2756 1;-10098 2969;-9990 3154;-9845 3312 1;-9696 3466;-9510 3588;-9288 3677 1;-9062 3765;-8798 3810;-8496 3810 18;-12484 -146 1;-12454 -181;-12442 -230;-12457 -274 1;-12497 -394;-12524 -512;-12524 -616 1;-12524 -619;-12502 -670;-12491 -753 1;-12484 -794;-12496 -836;-12524 -866 1;-12554 -896;-12594 -911;-12640 -905 1;-12765 -882;-12886 -866;-13002 -866 1;-13009 -866;-13043 -876;-13102 -878 1;-13139 -879;-13174 -866;-13199 -841 1;-13225 -814;-13240 -781;-13240 -745 1;-13240 -745;-13240 -300;-13240 -300 1;-13240 -230;-13188 -171;-13119 -166 1;-13079 -161;-13064 -165;-13074 -166;-13056 -164;-13037 -161 1;-12964 -156;-12895 -148;-12827 -136 1;-12806 -133;-12797 -133;-12795 -133;-12795 -132;-12793 -132;-12795 -133 1;-12735 -125;-12673 -116;-12607 -103 1;-12561 -95;-12514 -112;-12484 -146 18;-14662 -213;-13708 -213;-13708 -1479 1;-13602 -1432;-13492 -1395;-13377 -1371 1;-13261 -1346;-13137 -1333;-13006 -1333 1;-12765 -1333;-12556 -1376;-12373 -1461 1;-12189 -1546;-12034 -1666;-11911 -1819 1;-11788 -1972;-11694 -2156;-11629 -2368 1;-11566 -2582;-11535 -2819;-11535 -3078 1;-11535 -3348;-11574 -3591;-11657 -3808 1;-11732 -4027;-11846 -4210;-11995 -4358 1;-12144 -4513;-12328 -4630;-12544 -4711 1;-12763 -4796;-13009 -4838;-13287 -4838 1;-13402 -4838;-13524 -4833;-13652 -4819 1;-13776 -4811;-13898 -4798;-14022 -4781 1;-14142 -4765;-14257 -4743;-14369 -4716 1;-14478 -4697;-14576 -4673;-14662 -4646;-14662 -213 18;-17051 3810 1;-16800 3810;-16574 3769;-16373 3684 1;-16168 3598;-15993 3479;-15848 3324 1;-15703 3168;-15593 2980;-15517 2763 1;-15440 2540;-15401 2298;-15401 2033 1;-15401 1768;-15442 1526;-15523 1310 1;-15603 1092;-15716 906;-15861 754 1;-16006 600;-16182 480;-16386 395 1;-16586 310;-16808 267;-17051 267 1;-17291 267;-17512 310;-17717 395 1;-17917 480;-18092 600;-18242 754 1;-18387 906;-18502 1092;-18587 1310 1;-18669 1526;-18708 1768;-18708 2033 1;-18708 2298;-18669 2540;-18587 2763 1;-18505 2980;-18392 3168;-18247 3324 1;-18102 3479;-17931 3598;-17729 3684 1;-17525 3769;-17299 3810;-17051 3810 18;5463 3810 1;5581 3810;5701 3804;5821 3793 1;5939 3779;6051 3763;6152 3741 1;6259 3724;6356 3703;6441 3677 1;6526 3651;6594 3626;6646 3601;6518 2826 1;6411 2873;6272 2912;6103 2948 1;5936 2978;5765 2993;5590 2993 1;5318 2993;5100 2934;4938 2820 1;4775 2704;4683 2550;4663 2353;6826 2353 1;6829 2301;6834 2245;6837 2180 1;6843 2112;6844 2050;6844 1995 1;6844 1426;6706 998;6428 709 1;6151 414;5773 267;5297 267 1;5091 267;4891 305;4695 382 1;4503 459;4332 572;4183 722 1;4033 870;3911 1058;3819 1285 1;3729 1507;3684 1766;3684 2065 1;3684 2311;3719 2542;3787 2756 1;3858 2969;3967 3154;4112 3312 1;4262 3466;4446 3588;4668 3677 1;4895 3765;5160 3810;5463 3810 18;-1065 2521 1;-1018 2518;-969 2518;-911 2508 1;-910 2508;-909 2506;-907 2505 1;-830 2489;-762 2473;-704 2450 1;-702 2450;-700 2450;-698 2450 1;-670 2439;-642 2426;-612 2422 1;-545 2409;-500 2350;-503 2283 1;-509 2203;-520 2131;-520 2067 1;-520 1973;-502 1875;-490 1763 1;-487 1725;-500 1688;-525 1660 1;-550 1633;-586 1616;-623 1616;-1106 1616 1;-1170 1630;-1217 1687;-1215 1750;-1215 2333 1;-1215 2360;-1211 2386;-1206 2405 1;-1198 2474;-1136 2527;-1065 2521 18;-11490 -52 1;-11336 -52;-11204 -101;-11093 -200 1;-10978 -303;-10920 -441;-10920 -616 1;-10920 -791;-10978 -927;-11093 -1026 1;-11204 -1128;-11336 -1179;-11490 -1179 1;-11642 -1179;-11777 -1128;-11893 -1026 1;-12004 -927;-12059 -791;-12059 -616 1;-12059 -441;-12004 -303;-11893 -200 1;-11777 -101;-11642 -52;-11490 -52 18;-17051 -1307 1;-16800 -1307;-16574 -1349;-16373 -1434 1;-16168 -1521;-15993 -1639;-15848 -1794 1;-15703 -1951;-15593 -2139;-15517 -2355 1;-15440 -2579;-15401 -2820;-15401 -3085 1;-15401 -3350;-15442 -3591;-15523 -3808 1;-15603 -4027;-15716 -4211;-15861 -4365 1;-16006 -4518;-16182 -4638;-16386 -4723 1;-16586 -4808;-16808 -4851;-17051 -4851 1;-17291 -4851;-17512 -4808;-17717 -4723 1;-17917 -4638;-18092 -4518;-18242 -4365 1;-18387 -4211;-18502 -4027;-18587 -3808 1;-18669 -3591;-18708 -3350;-18708 -3085 1;-18708 -2820;-18669 -2579;-18587 -2355 1;-18505 -2139;-18392 -1951;-18247 -1794 1;-18102 -1639;-17931 -1521;-17729 -1434 1;-17525 -1349;-17299 -1307;-17051 -1307 18;-7064 -1395;-6112 -1395;-6112 -3993 1;-6052 -4004;-5983 -4008;-5907 -4013 1;-5830 -4021;-5759 -4027;-5690 -4027 1;-5468 -4027;-5317 -3961;-5235 -3833 1;-5155 -3707;-5113 -3488;-5113 -3182;-5113 -1395;-4160 -1395;-4160 -3296 1;-4160 -3527;-4183 -3737;-4230 -3923 1;-4277 -4111;-4357 -4273;-4467 -4410 1;-4579 -4547;-4730 -4652;-4921 -4723 1;-5110 -4800;-5347 -4838;-5631 -4838 1;-5913 -4838;-6181 -4818;-6438 -4774 1;-6694 -4736;-6903 -4693;-7064 -4646;-7064 -1395 18;-11099 -1644 1;-11068 -1689;-11067 -1748;-11095 -1794 1;-11120 -1834;-11128 -1842;-11120 -1827 1;-11143 -1872;-11189 -1899;-11242 -1899 1;-11246 -1897;-11251 -1895;-11258 -1894 1;-11298 -1887;-11332 -1862;-11353 -1827 1;-11358 -1822;-11364 -1814;-11375 -1799 1;-11394 -1761;-11396 -1718;-11379 -1679 1;-11362 -1639;-11328 -1611;-11287 -1602 1;-11257 -1596;-11240 -1594;-11242 -1594 1;-11188 -1581;-11133 -1601;-11099 -1644 18;-9182 -1307 1;-9063 -1307;-8944 -1314;-8824 -1326 1;-8706 -1339;-8594 -1356;-8492 -1378 1;-8385 -1395;-8289 -1416;-8204 -1440 1;-8119 -1468;-8051 -1493;-7999 -1517;-8127 -2292 1;-8234 -2246;-8372 -2205;-8542 -2171 1;-8708 -2141;-8881 -2126;-9055 -2126 1;-9327 -2126;-9545 -2184;-9707 -2299 1;-9870 -2413;-9961 -2569;-9982 -2766;-7819 -2766 1;-7816 -2817;-7810 -2873;-7808 -2939 1;-7803 -3007;-7801 -3069;-7801 -3123 1;-7801 -3692;-7939 -4120;-8217 -4410 1;-8494 -4705;-8872 -4851;-9349 -4851 1;-9553 -4851;-9754 -4813;-9950 -4736 1;-10141 -4660;-10314 -4547;-10462 -4396 1;-10611 -4248;-10733 -4060;-10827 -3833 1;-10917 -3612;-10962 -3352;-10962 -3054 1;-10962 -2807;-10926 -2575;-10858 -2362 1;-10787 -2149;-10678 -1964;-10533 -1806 1;-10383 -1652;-10198 -1530;-9976 -1440 1;-9750 -1352;-9485 -1307;-9182 -1307 18;16428 5964 1;15493 5816;15156 5633;14905 5136 1;14842 5010;14767 4893;14739 4875 1;14686 4843;13829 4769;13588 4777 1;13511 4779;12673 4786;11726 4791;10003 4801;9412 4491;8810 4801;8189 4815 1;7703 4826;7501 4814;7261 4759 1;6957 4690;6950 4690;6521 4785 1;6170 4863;5974 4881;5488 4880;5457 4880 1;4811 4880;4563 4831;4074 4600 1;3912 4524;3745 4461;3703 4461 1;3660 4461;3540 4504;3437 4556 1;2881 4838;1858 4973;1148 4859 1;1005 4835;741 4765;560 4703 1;206 4579;201 4579;-280 4737 1;-640 4854;-1173 4915;-1564 4883 1;-1876 4858;-2085 4806;-2522 4644 1;-2687 4583;-2700 4585;-2962 4691 1;-3221 4796;-3260 4801;-3912 4799 1;-4286 4797;-4700 4791;-4832 4785 1;-4964 4779;-5368 4786;-5729 4802 1;-6286 4827;-6433 4820;-6697 4760 1;-7003 4690;-7012 4690;-7420 4779 1;-8263 4964;-9117 4926;-9699 4679 1;-10091 4513;-10177 4511;-10482 4666;-10748 4801;-12238 4801;-12541 4645;-12844 4489;-13138 4645;-13432 4801;-14922 4801;-15208 4663 1;-15529 4507;-15467 4502;-16152 4744 1;-16831 4984;-17621 4945;-18238 4642 1;-19010 4261;-19482 3685;-19691 2865 1;-19908 2013;-19801 1156;-19391 468 1;-19227 194;-19067 14;-18762 -238 1;-18460 -488;-18462 -552;-18772 -793 1;-19030 -993;-19347 -1393;-19507 -1720 1;-19700 -2113;-19789 -2552;-19789 -3099 1;-19788 -3688;-19727 -3966;-19489 -4450 1;-19192 -5057;-18572 -5590;-17912 -5806 1;-17374 -5984;-16546 -5988;-15770 -5633;-15447 -5485;-15239 -5576 1;-14740 -5794;-14092 -5908;-13352 -5909 1;-12569 -5909;-12118 -5779;-11532 -5381 1;-11433 -5314;-11315 -5259;-11270 -5259 1;-11225 -5259;-11047 -5352;-10875 -5465 1;-10015 -6029;-9078 -6079;-8023 -5616;-7813 -5524;-7443 -5648 1;-6638 -5917;-5482 -5996;-4840 -5824 1;-4236 -5663;-3768 -5339;-3467 -4873 1;-3148 -4378;-3090 -4073;-3064 -2779 1;-3052 -2196;-3026 -1687;-3006 -1649 1;-2959 -1560;-2787 -1559;-2312 -1644 1;-2114 -1679;-1835 -1709;-1691 -1709 1;-1229 -1712;-888 -1486;-654 -1024 1;-541 -799;-528 -788;-245 -681 1;187 -519;243 -518;668 -662 1;965 -763;1135 -795;1448 -809 1;2091 -837;2566 -704;3131 -337 1;3264 -250;3400 -179;3432 -179 1;3464 -179;3619 -259;3777 -358 1;4137 -581;4287 -644;4688 -741 1;5298 -888;5896 -821;6519 -534;6870 -372;7177 -478 1;7625 -633;8076 -733;8550 -781 1;9020 -829;9105 -876;9182 -1133 1;9308 -1554;9694 -1943;10095 -2129 1;10456 -2299;11045 -2279;11428 -2095 1;11880 -1878;12170 -1593;12329 -1108 1;12441 -766;12455 -756;12815 -779 1;14010 -855;14639 -730;15238 -297 1;15327 -232;15435 -179;15479 -179 1;15522 -179;15686 -258;15843 -355 1;16603 -824;17454 -922;18651 -679 1;19204 -567;19417 -469;19628 -230 1;19900 79;19908 145;19908 1951;19908 2068 1;19908 3651;19886 3934;19718 4401 1;19503 5000;19140 5402;18544 5703 1;18007 5974;17155 6079;16428 5964 18;17928 5561 1;18700 5363;19232 4845;19456 4071 1;19512 3877;19525 3576;19538 2141 1;19550 942;19541 396;19509 289 1;19438 50;19214 -170;18973 -238 1;18265 -438;17233 -498;16751 -367 1;16348 -257;15993 -62;15706 208;15444 456;15306 271 1;15116 17;14791 -194;14388 -327 1;14080 -428;13997 -439;13508 -436 1;13048 -433;12442 -364;12083 -274 1;12000 -253;11998 -259;12035 -436 1;12080 -647;12030 -945;11908 -1199 1;11636 -1762;10897 -2045;10292 -1817 1;9806 -1635;9468 -1148;9468 -632;9468 -407;9216 -431 1;8866 -464;8256 -399;7783 -280 1;7323 -163;7201 -108;7046 57;6934 175;6772 37 1;5907 -704;4382 -591;3607 271;3424 474;3360 377 1;3119 21;2522 -336;2023 -420 1;1440 -518;756 -394;341 -114;129 29;28 -91 1;-139 -289;-354 -379;-661 -379;-932 -379;-932 -509 1;-933 -750;-1017 -955;-1187 -1124 1;-1437 -1374;-1550 -1391;-2246 -1283 1;-2569 -1232;-2887 -1163;-2953 -1129 1;-3248 -975;-3372 -718;-3372 -258;-3372 65;-3550 -58 1;-3801 -230;-4065 -337;-4405 -402 1;-4781 -475;-5094 -474;-5671 -398 1;-6502 -289;-6766 -202;-6943 24;-7040 149;-7232 3 1;-7499 -201;-7690 -292;-8042 -381 1;-8755 -564;-9576 -395;-10120 47 1;-10278 175;-10291 178;-10328 108 1;-10358 53;-10348 -23;-10289 -176 1;-10246 -291;-10206 -465;-10202 -563 1;-10196 -705;-10182 -736;-10133 -716 1;-9610 -509;-8452 -550;-7836 -799 1;-7626 -884;-7587 -889;-7532 -839 1;-7372 -694;-7184 -659;-6575 -659;-6548 -659 1;-5984 -659;-5837 -684;-5685 -810 1;-5627 -859;-5602 -854;-5492 -772 1;-5374 -686;-5322 -678;-4739 -666 1;-4055 -652;-3888 -680;-3692 -845 1;-3432 -1064;-3432 -1066;-3434 -2479 1;-3436 -3540;-3448 -3805;-3503 -4029 1;-3693 -4791;-4168 -5277;-4922 -5483 1;-5261 -5575;-6112 -5566;-6698 -5463 1;-7272 -5363;-7461 -5289;-7610 -5109;-7726 -4968;-7879 -5088 1;-8814 -5825;-10355 -5685;-11073 -4798;-11235 -4596;-11494 -4860 1;-11783 -5155;-12156 -5370;-12572 -5482 1;-13108 -5626;-14450 -5523;-14960 -5299 1;-15058 -5256;-15175 -5160;-15239 -5069;-15350 -4913;-15611 -5103 1;-16440 -5704;-17556 -5733;-18389 -5176 1;-18763 -4926;-18970 -4689;-19174 -4279 1;-19544 -3536;-19541 -2623;-19167 -1859 1;-18698 -903;-17669 -417;-16549 -623 1;-16267 -675;-15788 -888;-15574 -1057;-15421 -1179;-15405 -589 1;-15394 -169;-15373 26;-15332 89 1;-15235 239;-15319 237;-15504 86 1;-16274 -546;-17402 -634;-18285 -132 1;-19617 624;-19853 2734;-18732 3863 1;-17912 4689;-16505 4772;-15566 4050;-15379 3907;-15333 4019 1;-15270 4170;-15105 4323;-14923 4399 1;-14712 4487;-13672 4487;-13461 4399 1;-13264 4317;-13137 4195;-13048 4001 1;-12982 3860;-12974 3728;-12973 2871;-12973 2823 1;-12972 1843;-12978 1874;-12782 1926;-12692 1950 1;-12692 3973;-12680 4038;-12439 4250 1;-12222 4440;-12067 4471;-11387 4455 1;-10804 4441;-10780 4438;-10624 4329 1;-10492 4238;-10413 4134;-10306 3908 1;-10303 3901;-10213 3963;-10106 4044 1;-9632 4408;-9047 4566;-8312 4528 1;-7825 4503;-7365 4416;-7096 4297 1;-6933 4226;-6914 4225;-6848 4284 1;-6702 4417;-6483 4457;-5908 4459 1;-5355 4461;-5138 4426;-5010 4317 1;-4958 4272;-4918 4279;-4753 4362 1;-4571 4454;-4518 4461;-3955 4460 1;-3267 4459;-3106 4414;-2910 4168;-2797 4027;-2623 4147 1;-2311 4362;-2052 4456;-1635 4505 1;-1021 4577;-348 4454;37 4200;186 4101;437 4238 1;800 4435;1069 4503;1588 4529 1;1962 4547;2134 4535;2508 4464 1;3072 4358;3355 4230;3495 4017 1;3552 3931;3607 3861;3617 3861 1;3627 3861;3710 3925;3802 4004 1;4053 4219;4484 4414;4868 4487 1;5440 4594;6391 4505;6861 4299;7033 4223;7198 4332 1;7357 4438;7382 4441;8005 4453 1;8611 4465;8658 4460;8826 4373 1;8923 4323;9058 4212;9126 4127;9248 3974;9260 2937;9272 1901;9360 1902 1;9541 1903;9543 1914;9556 2969;9568 3974;9690 4127 1;9758 4212;9892 4322;9989 4371 1;10149 4453;10221 4461;10767 4460 1;11260 4459;11397 4446;11528 4387 1;11682 4317;11694 4317;11848 4387 1;11979 4446;12117 4459;12611 4460 1;13162 4461;13230 4453;13396 4369;13577 4278;13732 4359 1;13871 4432;13961 4441;14551 4441;15214 4441;15197 4626 1;15173 4869;15240 5059;15408 5233 1;15568 5397;15799 5483;16366 5586 1;16835 5672;17538 5661;17928 5561;17928 5561 18;-13184 -2139 1;-13282 -2139;-13381 -2152;-13479 -2177 1;-13572 -2202;-13648 -2234;-13708 -2274;-13708 -3993 1;-13662 -4004;-13602 -4008;-13530 -4013 1;-13457 -4021;-13379 -4027;-13293 -4027 1;-13032 -4027;-12837 -3936;-12704 -3756 1;-12572 -3582;-12506 -3348;-12506 -3054 1;-12506 -2443;-12733 -2139;-13184 -2139 18;-13248 -2631 1;-13034 -2631;-12976 -2876;-12976 -3090 1;-12976 -3284;-13054 -3505;-13248 -3506;-13248 -2631 18;17360 2730 1;16930 2730;16714 2463;16714 1930 1;16714 1675;16776 1464;16900 1297 1;17024 1130;17212 1047;17464 1047 1;17548 1047;17623 1052;17687 1060 1;17750 1065;17807 1072;17854 1080;17854 2603 1;17794 2636;17720 2666;17636 2691 1;17550 2718;17459 2730;17360 2730 18;17409 2232;17409 1571 1;17201 1492;17180 1713;17180 1926 1;17178 2120;17192 2338;17409 2232 18; 69180 53168 1;69435 53137;69705 53115;70028 53115 1;71160 53123;71678 53512;72750 53895 1;73958 54345;74580 54570;75870 54750 1;76800 54893;77460 54863;78090 55575 1;78675 56250;79028 56685;79148 57570 1;79200 58012;79260 58245;79185 58673 1;79065 59325;79095 59670;79230 60315 1;79320 60802;79320 61162;79725 61433 1;80370 61875;80768 62205;81548 62130 1;82448 62063;82815 61500;83730 61515 1;84795 61530;85328 61635;86408 61613 1;87367 61605;87908 61455;88725 60930 1;89160 60660;89250 60270;89265 59753 1;89280 59250;89115 58995;88830 58575 1;88418 58005;88110 57802;87585 57330 1;86760 56610;86265 56325;85568 55470 1;84998 54810;84548 54585;84165 53790 1;83798 53055;83708 52620;83625 51795 1;83505 50700;83175 50190;82770 49170 1;81855 46980;81068 46020;80310 43770 1;80055 43050;79905 42570;80227 41873 1;80288 41738;80348 41610;80408 41490; 69210 50662 1;70268 50715;70867 50573;71850 50992 1;72825 51435;73275 51742;74310 52073 1;75188 52373;75645 52485;76530 52815 1;77227 53085;77828 53085;78390 52575 1;78795 52200;78720 51780;78750 51210 1;78780 50302;78600 49815;78165 48990 1;77678 48090;77310 47700;76905 46755 1;76178 45053;75780 44130;75727 42270 1;75690 41213;75803 40545;75938 39488; 69210 46020 1;69480 45923;69705 45750;69908 45450 1;70110 45150;70200 44963;70208 44595 1;70215 44025;70005 43755;69870 43193 1;69675 42525;69518 42135;69218 41603; 89265 73830 1;89040 73545;88575 73223;88387 73530 1;88178 73875;88058 74242;88350 74512 1;88620 74783;89025 75135;89250 74813 1;89468 74483;89520 74123;89265 73830 18; 69158 62535 1;69255 62512;69360 62505;69488 62512 1;70035 62565;70418 62700;70710 63173 1;71048 63750;71025 64140;71430 64673 1;72488 66120;73155 66795;74610 67852 1;75338 68393;75878 68355;76770 68573 1;77617 68790;77977 69105;78788 69450 1;79515 69780;80025 69713;80648 70230 1;81015 70560;81278 70673;81525 71093 1;81698 71400;81885 71602;82245 71595 1;82710 71595;82890 71265;83145 70875 1;83318 70605;83408 70470;83588 70193 1;83790 69885;84240 69840;84510 70095 1;85410 70950;85808 71445;86768 72233 1;87105 72525;87135 72960;86910 73335 1;86745 73583;86655 73733;86685 74010 1;86730 74415;86895 74610;87210 74873 1;87825 75420;88005 75893;88770 76253 1;89190 76463;89550 76665;89948 76395 1;90367 76102;90660 75833;90668 75315 1;90668 74573;90218 74242;90270 73492 1;90315 72563;90398 71970;91050 71295 1;91598 70710;91830 70320;92550 69930 1;93330 69525;93885 69713;94770 69870 1;95378 69990;95700 70125;96330 70095 1;96960 70073;97320 69713;97590 69135 1;97958 68303;98130 67875;98430 66990 1;98595 66473;98895 66075;99450 66075 1;99990 66075;100260 66285;100688 66615 1;101085 66938;101325 67170;101580 67492; 118417 37920 1;118410 38063;118380 38213;118328 38370 1;118087 38993;118058 39495;117465 39773 1;117158 39923;116977 39983;116648 39975 1;116085 39960;115306 39791;115276 39235 1;115216 38718;115180 38397;115188 37880 1;115188 37647;115233 37370;115285 37190 16; 115709 37870 1;115822 35231;118914 37371;117405 38895 1;117106 39197;116122 39241;115920 38864 1;115769 38587;115717 38117;115709 37870 18; 69150 64305 1;69563 64448;69893 64695;70290 65070 1;71025 65813;71355 66225;72090 66975 1;72727 67643;73020 68003;73748 68573 1;74280 69000;74528 69293;75188 69495 1;76020 69765;76530 69593;77325 69953 1;78225 70373;78615 70740;79568 71055 1;80325 71310;80760 71483;81330 72053 1;81675 72413;81818 72803;82328 72810 1;82665 72825;82785 72555;82988 72270 1;83227 71933;83400 71790;83685 71475 1;83925 71190;84345 71220;84630 71475 1;85028 71850;85298 71970;85665 72390 1;85988 72773;86175 73193;85928 73613 1;85725 73950;85770 74220;85928 74573 1;86137 75060;86205 75352;86588 75735 1;86940 76110;88180 76923;88676 77100; 123225 70193 1;123585 69495;123690 69105;124005 68370 1;124193 67950;124335 67598;124455 67260; 124477 61650 1;124260 61403;124012 61170;123788 60810 1;123045 59693;122715 59085;121785 58110 1;120637 56925;119948 56430;118590 55492 1;117360 54660;116925 53963;115650 53213 1;114878 52785;114405 52755;113610 52395 1;113167 52215;112928 52162;112485 52012 1;112087 51885;112005 51600;111705 51315 1;111330 50970;111045 50805;110565 50738; 69158 66120 1;70035 66585;70410 67028;71168 67733 1;71970 68505;72390 68873;73305 69495 1;73770 69810;74040 69930;74565 70133 1;75637 70553;76268 70575;77227 71213 1;77985 71730;78495 71745;79365 72030 1;79680 72150;79800 72315;80048 72533 1;80430 72893;80588 73170;81090 73350 1;81578 73545;81795 73800;82328 73815 1;82755 73830;83010 73635;83288 73290 1;83558 72960;83580 72653;83970 72450 1;84420 72225;84930 72690;85005 73193 1;85065 73695;85080 73950;85028 74453 1;84990 74790;85178 74955;85365 75233 1;85695 75765;85808 76080;86288 76470 1;86820 76920;87038 77250;87690 77490 1;88125 77670;88448 77843;88793 77963; 90428 77963 1;90945 77745;91283 77288;91710 76733 1;92460 75750;92745 75113;92887 73875 1;92977 72975;93375 72473;94148 71970 1;94358 71843;94530 71895;94785 71895 1;95528 71903;96098 72030;96750 71655 1;97215 71385;97425 71280;97770 70830 1;98137 70335;98460 70193;98805 69653 1;99368 68835;99630 69180;99870 69533 1;100455 70170;100815 70373;101430 70890 1;101970 71370;102398 71385;103065 71655 1;103935 72023;104408 72150;105345 72315 1;106118 72450;106598 72690;107310 72352 1;107962 72120;108308 71955;109005 71963 1;110018 71985;110655 71970;111465 72578 1;111870 72893;111953 73185;112148 73658 1;112230 73867;112245 74010;112470 74190; 112470 74190 1;113730 74700;114465 74805;115830 74730 1;116505 74700;116917 74760;117525 74430; 118403 72773 1;118950 72383;119340 71955;119962 71685 1;120623 71408;121058 71445;121762 71258 1;122370 71100;122663 70973;123218 70665 1;123818 70335;124102 69915;124448 69495; 124485 55560 1;123878 55058;123225 54563;122370 53970 1;121335 53280;120968 52733;119910 52095 1;118748 51412;118170 51052;116910 50610 1;116258 50393;115905 50340;115230 50250 1;114735 50205;114698 49650;114225 49575 1;113220 49410;112740 49200;111727 49110 1;110265 48990;109628 48233;108172 48278 1;107595 48278;106748 48525;106005 48375 1;105525 48285;105225 48412;104805 48173 1;104108 47790;103837 47430;103087 47152 1;102420 46920;102278 46793;101768 46545; 96285 77977 1;96990 77573;97290 77123;98070 76650 1;98887 76163;99315 75653;100290 75690 1;101137 75742;101528 76200;102390 76155 1;102945 76133;103215 75900;103785 75930 1;104325 75975;104587 76080;105105 76230 1;105990 76492;106448 76665;107370 76650 1;107730 76650;107895 76523;108270 76455 1;108525 76365;108503 76088;108503 75810 1;108495 75203;108383 75188;108637 74625 1;108818 74220;109028 73823;109477 73808 1;109995 73800;110333 73733;110753 74048 1;111000 74242;111098 74400;111188 74708 1;111308 75180;111315 75158;111428 75623 1;111488 75893;111555 76155;111833 76178 1;112080 76208;112208 76193;112425 76230; 102600 78008 1;102758 77933;102915 77858;103087 77775 1;103920 77370;104348 77040;105285 77010 1;106365 76980;106875 77565;107970 77475 1;108360 77453;108585 77355;108870 77070 1;109328 76613;109005 76110;109050 75450 1;109087 74745;109170 74378;109890 74378 1;110520 74393;110715 74640;110835 75262 1;110970 75998;111113 76230;111368 76928 1;111503 77310;111975 77130;112328 77333 1;112920 77685;113250 77887;113903 78038; 116400 78045 1;116535 78000;116678 77940;116843 77873 1;117637 77543;118163 77310;118973 77018 1;119670 76778;119648 75585;120068 75315 1;121350 74498;122227 74490;123570 73305; 112425 76230 1;113490 76433;114360 76590;115425 76770 1;116648 76995;117413 77085;118628 76575 1;118695 76545;118485 76448;118575 76178 1;119385 73665;121815 74250;123698 72578; 112605 75833 1;114165 76102;115297 76320;116887 76433 1;117480 76485;117795 76463;118387 76335 1;118328 76185;118178 76020;118200 75848 1;118553 73005;121020 74085;122753 72668; 112065 74940 1;112163 75173;112358 75218;112605 75270 1;113805 75465;114405 75653;115628 75735 1;116505 75803;117015 75960;117893 75833 1;117953 75750;117810 75262;117848 75165 1;118680 72945;120518 73215;122558 72225 1;123270 71880;123750 71760;124440 71588; 111975 74288 1;112065 74617;112253 74805;112590 74873 1;113655 75015;114188 75180;115268 75210 1;116190 75255;116655 75352;117585 75233; 124508 52380 1;123735 52012;123098 51855;122205 51315 1;121118 50670;120525 50400;119325 50010 1;118508 49762;118095 49530;117488 48930 1;116887 48360;116475 48113;115665 47955 1;113558 47558;112320 47633;110542 47483; 119858 78060 1;120615 77745;121297 77460;122250 76950 1;122783 76673;122962 76275;123060 75675 1;123188 74887;123337 74242;124095 73965; 124508 49733 1;124073 49598;123623 49433;123060 49223 1;121620 48720;120990 48255;119580 47700 1;117150 46785;115725 46980;113137 46980; 123428 34110 1;123525 34253;123623 34410;123727 34590 1;123998 35145;124095 35565;124560 35858; 124523 43883 1;124208 43995;123870 44108;123465 44250 1;122137 44723;121448 44910;120068 45150 1;119010 45345;118477 45443;117428 45615 1;117098 45675;116850 45825;116528 45893 1;116318 45953;116408 46275;116625 46290 1;116828 46320;116940 46260;117150 46290 1;118883 46223;120038 46088;121410 46133 1;122025 46148;123188 46193;123465 46313; 124523 34110;124560 34193; 124530 44453 1;124065 44550;123765 44648;123270 44835 1;122400 45150;121928 45195;121087 45533 1;120848 45630;120660 45720;120922 45750 1;122415 45938;123428 45938;124523 45930; 123465 46313 1;123885 46530;124215 46748;124523 46957; 124433 71933 1;124260 71977;124087 72023;123922 72075; 124425 72308;124305 72352; 69240 38070 1;70613 38243;71858 38415;73665 38580 1;75585 38760;75908 38355;77400 38415 1;79485 38505;80453 38595;82485 38850 1;83205 38948;83580 38948;84293 39120 1;85125 39330;85650 39510;86137 40215 1;86843 41258;87262 41730;88043 42720 1;88538 43365;89115 43740;89925 43770 1;93262 44070;94875 44228;98235 44820 1;99458 45098;100065 45188;101235 45660 1;102810 46215;103493 47115;105225 47160 1;107340 47220;108435 47115;110505 46620 1;112575 46125;113655 46148;115785 46050 1;118568 45930;119925 45743;122715 45630 1;123428 45608;123983 45623;124530 45593; 69128 68242 1;69308 68363;69495 68490;69698 68625 1;73380 71183;74985 72203;78578 74783 1;80258 76043;81083 76725;82590 77948; 103875 57915; 116363 62985 1;114788 62738;112905 62512;111352 62183 1;110310 61965;108975 61800;107873 61568; 74175 70159; 79778 53235 1;81008 53250;82470 53730;84465 54450 1;86250 55103;87623 55125;89205 54060 1;90383 53265;91125 52838;92483 52380 1;94335 51840;95235 52020;97365 52230 1;97823 52275;98220 52320;98595 52365; 109912 65477;106598 63218 32;107783 61733; 107137 74783 1;107453 73980;107460 73492;107962 72773 1;108240 72375;108555 72262;109058 72233 1;109410 72225;109650 72323;109898 72367 1;110145 72405;110408 72390;110723 72488 1;111143 72645;111337 72870;111630 73208 1;111795 73410;111810 73560;111893 73808 1;111998 74175;112073 75045;112080 75405; 69195 48818 1;69338 48570;69488 48308;69645 48030; 83918 39030 1;84930 38970;85658 38850;86468 39465 1;87083 39945;87518 40027;88298 40005 1;89693 39975;90390 40058;91793 40110 1;93105 40162;94538 40350;95858 40320; 87727 42300 1;86985 41318;88560 40088;89798 40020; 102909 57261 1;100884 56076;98760 55335;96285 53693 1;95048 52890;94515 52545;93480 51938; 103515 59340; 69195 47918 1;69293 47955;69390 47993;69495 48030; 69120 74693;69383 74865 32;69120 72308;69120 74693 18; 69293 54375 1;69255 54443;69218 54510;69180 54578; 98715 77992;99255 77775 32;98858 76800 32;97913 77168;98235 77992;98715 77992 18; 74115 38700 1;73852 40298;73815 41198;72975 42570 1;71633 44760;70973 45870;69645 48060; 79605 53550 1;77198 55988;75975 57090;73665 59610 1;72218 61185;71602 61943;70125 63480 1;69720 63908;69360 64283;69143 64733; 69645 48060 1;70913 48683;71558 48938;72825 49500 1;74303 50168;75158 50332;76485 51270 1;77550 52035;78225 52238;79185 53130 1;79313 53258;79433 53378;79545 53490 33;80258 54203;80640 54742;81248 55710 1;81923 56820;82463 57270;83543 57990 1;84773 58823;85313 59393;86678 59970 1;88073 60570;88958 60750;90458 60450 1;92415 60068;93488 60255;95498 60255 1;96413 60240;96795 60195;97688 60225 1;98685 60262;99060 60270;100178 60495 1;101903 60840;102045 60870;103095 60668 1;103477 60608;103485 60668;103890 60758 1;104520 60908;104895 61207;105547 61215 1;106133 61230;106387 61253;107003 61343 1;107348 61403;107610 61485;107887 61568; 74025 39420 1;74700 41183;75150 42030;76125 43650 1;76898 44948;77310 45608;78405 46650 1;79515 47723;81060 48203;82485 48810; 79605 53370 1;80010 52973;80190 52755;80595 52350 1;81023 51923;81045 51555;81225 50970 1;81413 50348;81510 50040;81705 49410 1;81803 49080;81683 48878;81585 48540; 69188 54360 1;69840 54495;70433 54315;70988 54293 1;71648 54278;72435 54427;72968 54353 1;73688 54262;74025 54128;75045 53910 1;76635 53610;77205 53580;79275 53310; 69240 37838 1;69750 37568;70463 37065;70838 36750 1;71558 36158;73110 34868;74227 33953; 76845 38490 1;77003 37665;77100 37253;77265 36420 1;77445 35460;77617 34733;77670 33960; 73703 52598; 75953 54615; 112530 47799; 103395 60720 1;103598 59880;104025 59588;104505 58860 1;105053 58035;105570 57585;105735 56595 1;105945 55515;105675 55537;104805 54780 1;104303 54345;103898 54218;103635 53610 1;103440 53190;103643 52478;104115 52440; 102262 52545 1;102593 52515;102803 52537;102945 52605 33;103080 52665;103163 52762;103230 52883 33;103290 53010;103343 53160;103425 53332 33;103493 53475;103583 53633;103725 53798; 69195 48825 1;69270 48660;69338 48473;69398 48248 1;69428 48113;69330 48023;69195 47955;69203 48818;69195 48825 18; 124455 64207 1;123540 64485;122693 64688;121455 64710 1;119295 64755;117623 64643;116385 62880 1;115575 61740;115500 60945;115335 59550 1;114975 56640;118995 53700;122415 53370 1;123135 53295;123840 53325;124500 53430; 109485 57600 1;109178 57173;108878 56655;108488 56475 1;107685 56115;106740 55845;105465 55380; 113595 48600 1;113415 48825;112830 49245;112688 49433 1;112245 50010;112110 50400;112035 51248 1;111945 52110;111870 52650;112193 53370; 113535 53213 1;112935 53273;112530 53220;112125 53295 1;111773 53363;111525 53528;111180 53603 1;110790 53685;110265 53655;109725 53610 1;109380 53588;109305 53512;109065 53730 1;108885 53895;108712 54030;108593 54158 1;108240 54540;107730 54405;106350 55680; 112860 49238;115005 49260; 95835 51690; 91545 52650 1;90788 51840;90375 51375;89355 50940 1;88275 50490;87818 50070;86715 49680 1;86355 49560;86137 49650;85785 49530 1;84315 49058;84105 48900;82575 48750 1;82200 48720;81840 48983;81735 49290; 77385 55830 1;77273 57983;77273 59063;77085 61200 1;77033 61733;76988 62003;77085 62520 1;77183 63052;77100 63435;77498 63795 1;78203 64463;78525 64838;79313 65415 1;80115 66015;80550 66330;81518 66585 1;82148 66758;82365 66795;83010 66623 1;83512 66495;83798 66578;84390 66615 1;85095 66668;85350 66225;85808 65580 1;86213 64995;86475 64710;86768 64035 1;87008 63473;87008 63090;87233 62588; 74887 58373 1;75143 58927;75308 59235;75285 59835 1;75255 60503;75323 60840;75255 61492 1;75165 62318;75668 62707;75593 63525 1;75555 63893;75420 64073;75480 64425 1;75510 64650;75563 64845;75780 64898 1;76185 64898;76530 64665;76568 64290 1;76613 63832;76718 63525;77137 63330; 78533 64695 1;78945 64065;79117 63630;79793 63270 1;80753 62760;81278 62550;82102 61830 1;82583 61410;82673 61058;82988 60495 1;83227 60060;83581 59405;83792 59083; 83483 59820 1;83678 59985;83602 60262;83843 60360 1;84270 60548;84510 60660;84983 60615 1;85223 60593;85418 60555;85523 60330 1;85643 60060;85710 59918;85867 59655;85605 59430 33;85102 59137;84698 58830;84203 58463 32;84165 58665 33;83858 59010;83663 59295;83475 59603 32;83483 59820 18; 86408 64755 1;86588 65775;86723 66270;86873 67290 1;86933 67740;86940 68025;86723 68415 1;86348 69075;85973 69413;86078 70155; 86648 70290 1;86663 70050;86622 68946;86599 68758; 109455 41640 1;108285 41723;107408 41730;106253 41693 1;105323 41670;104887 41573;103995 41760 1;103193 41933;102758 42060;101955 41910 1;99480 41468;98205 41138;95843 40290; 95798 40305 1;96210 40185;96983 40110;96765 39510 1;96248 38115;95820 37373;95925 35880 1;95970 35145;96038 34575;96128 34020; 115028 34088 1;115448 34245;115890 34418;116415 34650 1;116790 34823;117023 34823;117375 35040 1;117758 35295;118140 35445;118395 35820 1;118740 36345;118943 36848;118905 37283; 109373 40613 1;107348 40545;106343 40582;104333 40582 1;102345 40582;101363 40418;99398 40185;96878 39855; 114840 40207 1;114675 39143;114255 37838;114825 36930 1;115403 36008;115643 35573;116385 34770; 80025 43350;78458 42855; 81231 42023; 79148 39570; 75518 39030 1;76680 39015;77258 39015;78428 39015 1;78900 39015;79088 39098;79492 39360 1;80137 39810;80685 40170;81105 40628 1;82073 41640;82673 42128;83992 42585 1;84788 42870;85185 43103;85815 43673 1;86198 44025;86490 44078;86985 44235 1;87413 44385;87623 44483;88073 44588 1;88508 44700;88748 44693;89205 44670 1;90120 44640;90578 44570;91500 44622 1;94053 44791;94018 44802;97910 45470 1;99009 45659;99116 45737;100278 45985 1;100496 46037;101150 46127;101578 46202; 69870 41093; 74992 49965 1;75248 49643;75420 49537;75653 49185 1;75803 48953;75495 48623;75262 48465 1;74955 48278;74625 48045;74378 48300 1;74003 48698;73845 48923;73463 49305 1;73365 49403;73680 49530;73762 49425 1;73973 49140;74018 48818;74378 48765 1;74528 48750;74760 48832;74708 48975 1;74573 49328;74625 49552;74453 49875;74992 49965 18; 74933 49920;74753 50310 33;75225 50520;75690 50745;76223 51098 32;76523 51090 1;76658 50985;76665 50873;76748 50715 1;76815 50573;76635 50423;76477 50400 1;76260 50378;76148 50430;75938 50400 1;75795 50385;75735 50213;75803 50085 1;76020 49673;76380 49508;76852 49545 1;77265 49590;77528 49628;77828 49920 1;77955 50055;78188 50213;78278 50040 1;78352 49890;78563 49980;78637 50137 1;78735 50363;78720 50528;78930 50655 1;79283 50880;79785 50768;80205 50753 1;80400 50753;80708 50558;80648 50363 1;80550 50078;80078 49957;79823 49785 1;79718 49725;79710 49485;79838 49485 1;79995 49485;80123 49552;80227 49425 1;80483 49103;80468 48848;80693 48495 1;80768 48375;80843 48218;80723 48135 1;80558 48037;80355 47910;80242 48060 1;80123 48218;80063 48285;79958 48435 1;79860 48578;79680 48630;79553 48525 1;78968 48075;78623 47925;77992 47550 1;77738 47408;77475 47280;77273 47475 1;77108 47640;77063 47753;76943 47940 1;76793 48173;76545 48105;76373 48300 1;75810 48930;75533 49253;74977 49875;74933 49920 18;78803 49290 1;78720 49358;78660 49350;78570 49380 1;78480 49418;78413 49343;78383 49253 1;78323 49103;78405 49012;78488 48863 1;78585 48668;78727 48623;78930 48533 1;79028 48495;79133 48615;79133 48720 1;79125 48998;78975 49117;78788 49313;78803 49290 18; 69158 66180 1;69248 66180;69315 66165;69428 66150 1;69570 66143;69623 66023;69683 65880 1;69773 65655;69825 65393;70073 65400 1;70253 65408;70350 65393;70538 65370 1;70710 65355;70703 65145;70733 64965 1;70762 64748;70793 64635;70838 64410 1;70852 64305;70838 64192;70733 64155 1;70583 64117;70710 63930;70718 63780 1;70725 63608;70740 63525;70748 63345 1;70748 63188;70950 63052;70823 62955;70568 63015 33;70433 63165;70283 63315;70125 63480 1;69975 63637;70320 64200;70193 64350 1;70050 64508;69413 64328;69308 64492 1;69248 64582;69195 64673;69143 64762;69150 66188;69158 66180 18; 70485 59445;70710 60015; 93188 51375; 81518 41640 1;81435 41843;81390 42113;81188 42060 1;80865 41993;80498 41850;80378 42150 1;80137 42713;79755 42855;79523 43410 1;79425 43635;79238 43793;79387 43980 1;79500 44138;79620 44273;79733 44430 1;79808 44550;79800 44678;79718 44790 1;79613 44940;79496 45231;79645 45328 1;79781 45426;79688 45503;79838 45585 1;79913 45638;80085 45390;80137 45300 1;80175 45218;80227 45128;80333 45135 1;80640 45180;80805 45240;81113 45165 1;81443 45090;81608 45015;81953 44985 1;82102 44978;82298 45135;82223 45270 1;82102 45480;82012 45593;82028 45825 1;82028 46005;82110 46103;82253 46200 1;82387 46305;82500 46320;82673 46275 1;82755 46260;82808 46245;82898 46260 1;83010 46290;83078 46388;83078 46500 1;83070 46635;83085 46733;83198 46800 1;83303 46875;83348 46980;83483 46965 1;83678 46950;84345 47213;84473 47063 1;84600 46912;84570 46703;84570 46500 1;84570 46305;83963 46073;83798 45960 1;83708 45908;83633 45908;83543 45945 1;83415 45998;83325 46050;83213 45990 1;83003 45893;82883 45773;82852 45540 1;82823 45345;82785 45218;82898 45045 1;82995 44895;83130 44850;83318 44865 1;83610 44895;83805 45015;84053 44850 1;84248 44723;84450 44655;84458 44415 1;84458 44183;84315 44078;84143 43920 1;83648 43500;83348 43373;82838 42975 1;82545 42758;82448 42585;82268 42270 1;82065 41925;81930 41707;81518 41640 18; 90218 53325 1;90188 53130;90083 53018;90038 52823 1;90008 52710;90045 52515;90023 52395 1;89977 52200;89805 52133;89573 51930 1;89063 51495;88733 50985;88313 50460; 69180 54270;69203 54233 1;69233 54150;69218 54075;69180 54000;69188 54262;69180 54270 18; 69180 52860 1;69240 52785;69293 52703;69338 52582 1;69495 52080;69518 51810;69637 51293 1;69653 51233;69563 51203;69503 51188 1;69390 51165;69285 51158;69188 51158;69188 52867;69180 52860 18; 69188 53783 1;69278 53768;69345 53723;69360 53655 1;69383 53573;69308 53483;69180 53423;69188 53783 18; 69218 48938 1;69668 48773;69893 48683;70358 48518 1;70403 48503;70283 48480;70238 48457 1;69983 48353;69795 48398;69608 48203;69218 48938 18; 74640 51518 1;74475 51690;74453 51923;74580 52043 1;74700 52155;74933 52117;75090 51938 33;75255 51773;75278 51540;75158 51427 1;75030 51308;74798 51345;74640 51518 18; 71977 51068 1;71835 51150;71775 51203;71678 51323 1;71558 51473;71423 51540;71242 51503 1;71093 51473;71018 51420;70867 51427 1;70658 51443;70477 51570;70477 51773 1;70455 52373;70425 52665;70433 53258 1;70433 53408;70485 53543;70628 53558 1;70740 53573;70808 53617;70928 53588 1;71070 53558;71145 53468;71198 53318 1;71400 52740;71400 52418;71678 51863 1;71715 51787;71805 51787;71887 51818 1;72075 51893;72165 51953;72367 51998 1;72428 52020;72495 52028;72555 52012 33;72600 51998;72645 51968;72683 51908 1;72735 51810;72765 51758;72818 51653 1;72848 51593;72855 51518;72833 51457 33;72825 51420;72795 51383;72758 51353 1;72548 51240;72443 51195;72233 51082 1;72113 51030;72008 50940;71933 51023; 72098 49343 1;71963 49425;71925 49508;71798 49582 1;71678 49658;71678 49883;71813 49927 1;72038 50018;72173 50040;72428 50018 1;72623 50003;72727 50010;72923 50048 1;73035 50078;73230 49995;73268 49883;72098 49343 18; 72098 51908 32;71992 51870 33;71963 51855;71925 51840;71887 51818 1;71805 51787;71715 51787;71678 51863 1;71400 52418;71400 52740;71198 53318 1;71160 53408;71123 53475;71070 53528 32;71183 53558 33;71370 53543;71550 53520;71558 53332 1;71558 53265;71588 53228;71588 53153 1;71588 53040;71490 53003;71498 52883 1;71505 52770;71565 52688;71678 52643 1;71850 52575;71963 52500;72008 52313 1;72030 52177;72068 52110;72098 51983;72098 51908 50; 70823 48802 32;70770 48908 33;70200 50115;69983 50858;69788 52207 1;69720 52650;69600 52927;69803 53318 1;69998 53715;70185 53887;70553 54128 32;71783 54188 32;71820 54075 33;72128 53280;72233 52830;72578 52012 32;72510 52020 33;72465 52028;72413 52020;72367 51998 1;72255 51975;72180 51953;72105 51915 32;72090 52012 33;72060 52125;72030 52193;72008 52313 1;71963 52500;71850 52575;71678 52643 1;71565 52688;71505 52770;71498 52883 1;71490 53003;71588 53040;71588 53153 1;71588 53228;71558 53265;71558 53332 1;71550 53550;71303 53543;71093 53565 32;70890 53595 33;70793 53610;70725 53573;70628 53558 1;70485 53543;70433 53408;70433 53258 1;70425 52665;70455 52373;70477 51773 32;70515 51623 33;70575 51510;70710 51443;70867 51427 1;70935 51427;70988 51435;71040 51450 32;71242 51503 33;71423 51540;71558 51473;71678 51323 1;71700 51293;71723 51262;71753 51240 32;71963 51037 32;71925 50887 33;71873 50745;71775 50640;71633 50512 1;71580 50483;71498 50528;71498 50588 1;71490 50685;71498 50738;71498 50828 1;71498 50903;71430 50963;71363 50948 1;71145 50918;71033 50903;70823 50858 1;70740 50843;70688 50768;70718 50693 1;70755 50580;70725 50512;70748 50393 1;70755 50348;70800 50318;70852 50318 1;70935 50325;70973 50340;71063 50348 1;71145 50363;71190 50250;71168 50168 1;71108 49988;71048 49898;71033 49703 1;70995 49410;71048 49253;71183 48983;70823 48802 50; 70440 52448 33;70433 52695;70425 52935;70433 53258 1;70433 53408;70485 53543;70628 53558 1;70740 53573;70808 53617;70928 53588 1;71070 53558;71160 53468;71213 53318 1;71325 52995;71385 52762;71453 52508 32;71475 52380 1;71063 52313;70838 52298;70433 52223;70440 52448 50; 69158 60825 1;69428 60720;69713 60660;70088 60675 1;70650 60713;70980 60765;71445 61095 1;71948 61463;72180 61853;72165 62475 1;72150 63105;72270 63443;72570 63990 1;72915 64680;73050 65085;73568 65655 1;74040 66180;74325 66405;74828 66915 1;75180 67283;75450 67492;75968 67492 1;76575 67500;76898 67523;77490 67695 1;78015 67860;78435 67703;78810 68115 1;78990 68325;79117 68385;79365 68490 1;79785 68693;80025 68760;80408 69053 1;80738 69323;80985 69413;81165 69795 1;81375 70283;81637 70643;82170 70650 1;82650 70665;82898 70343;83130 69915 1;83288 69623;83475 69540;83625 69233 1;83768 68925;84038 68790;84390 68813 1;84990 68865;85245 69180;85688 69615 1;86227 70155;86438 70492;87045 70950 1;87600 71385;87810 71865;88508 71933 1;88875 71970;89078 71850;89408 71655 1;89887 71363;89820 70913;90165 70455 1;90525 69983;90750 69765;91245 69413 1;91838 69000;92130 68700;92850 68550 1;93428 68445;93727 68400;94328 68430 1;95145 68483;95535 68670;96368 68693 1;96705 68715;96870 68490;97087 68213 1;97808 67290;97815 66615;98310 65535 1;98558 64973;99060 64815;99690 64815 1;100245 64815;100598 64973;100988 65393 1;101745 66240;102105 66690;102810 67590 1;103200 68115;103440 68363;103988 68715 1;104550 69083;104887 69330;105570 69315 1;105990 69315;106238 69375;106628 69173 1;107137 68925;107355 68693;107887 68453 1;108525 68160;108915 68063;109628 68093 1;110475 68145;110940 68295;111645 68775 1;112395 69300;112860 69443;113565 70035 1;113925 70350;114255 70335;114750 70290 1;115425 70245;115920 70223;116370 69690 1;116790 69195;116850 68790;116925 68130 1;117000 67500;117255 67245;117488 66630 1;117705 66045;117780 65715;117825 65070 1;117840 64883;117855 64793;117870 64590 1;117900 62715;116137 62302;114630 61170 1;113528 60360;112958 59963;111765 59295 1;110460 58575;109860 58125;108608 57293 1;107768 56753;107340 56512;106508 55950 1;105870 55545;105660 55140;104970 54855 1;103785 54375;103260 53992;102090 53490 1;101115 53093;100538 53070;99645 52515 1;98887 52043;98595 51683;97845 51195 1;96975 50640;96600 50153;95588 49950 1;95220 49890;95070 49733;94725 49575 1;93727 49133;93548 48420;92625 47835 1;91995 47445;91620 47325;91088 46815 1;90465 46245;90308 45705;89528 45412 1;89153 45285;88988 45218;88710 45098; 69173 58680;69758 58988 1;69930 58950;70020 58927;70200 58875 1;70320 58845;70418 58883;70508 58965;70867 58455 1;70852 58433;70852 58395;70852 58358 1;70838 57983;70838 57795;70808 57420 1;70793 57338;70695 57338;70628 57285 1;70515 57188;70455 57143;70320 57150 1;70193 57165;70163 57105;70065 57113 1;69998 57113;69938 57120;69900 57165 1;69780 57308;69698 57360;69578 57488 1;69383 57698;69262 57885;69165 58073;69173 58680 18; 69158 59453 1;69352 59325;69548 59220;69742 59137 1;69893 59078;69848 58568;70200 58523 1;70575 58478;70725 58860;70935 58957 1;71220 59093;71438 59100;71693 59130 1;71850 59153;72068 58860;72300 58905 1;72623 58980;72735 59348;72960 59475 1;73102 59550;73268 59685;73455 59783 1;73913 60052;74055 60285;74018 60802 1;73943 61508;73590 61613;73538 62302 1;73500 62753;73605 62985;73778 63383 1;74010 63953;73875 64380;74295 64823 1;75045 65633;75510 66300;76620 66300 1;77775 66300;78705 66180;79418 67102 1;79748 67545;80168 67463;80580 67823 1;80955 68175;81345 68175;81578 68625 1;81727 68933;81908 69120;82260 69143 1;82568 69165;82650 68865;82815 68580 1;83258 67845;83700 67320;84578 67305 1;85290 67305;85575 67673;86220 67980 1;87165 68460;87660 68895;88740 68903 1;89617 68925;90188 68880;90855 68303 1;91680 67590;92160 66983;93255 66983 1;94065 66983;94477 66945;95295 66983 1;96488 67043;97260 66300;97695 65183 1;98085 64185;98730 63555;99818 63503 1;100770 63473;101318 63735;102060 64343 1;102345 64598;102495 64815;102667 65063; 70867 58455;70508 58965 1;70538 58988;70560 59010;70590 59033 1;70733 59198;70935 59115;71130 59010 1;71258 58943;71333 58740;71318 58590 1;71295 58470;71085 58410;70965 58425 1;70920 58440;70875 58492;70867 58455 18; 69173 59348;69270 59468 1;69420 59250;69488 59048;69758 58988;69165 58680;69173 59348 18; 69150 64508;69158 64500 32;69150 64508 18; 69165 61440 1;69240 61403;69233 61290;69233 61185 1;69210 61005;69180 60855;69158 60720;69165 61448;69165 61440 18; 69158 59595 1;69195 59558;69233 59520;69270 59468;69165 59340;69165 59603;69158 59595 18; 69165 61440 1;69240 61403;69233 61290;69233 61185 1;69210 61005;69180 60855;69158 60720; 75105 54075 1;74992 54405;74887 54555;74843 54893 1;74828 54990;74858 55110;74955 55095 1;75075 55088;75300 55110;75390 55050 1;75465 55005;75450 54878;75480 54765 1;75548 54450;75525 54293;75600 53970;75105 54075 18; 74813 55433 1;74813 55582;74880 55688;75023 55740 1;75173 55800;75262 55800;75405 55883 1;75540 55980;75645 56033;75683 56190 1;75698 56287;75742 56370;75848 56370 1;75960 56370;75968 56242;76028 56137 1;76080 56033;76133 55988;76230 55898 1;76328 55808;76305 55710;76320 55575 1;76320 55508;76223 55500;76155 55492 1;75645 55455;75398 55373;74895 55410;74813 55433 18; 76283 55523 1;76403 55170;76553 55035;76770 54720 1;76770 54510;76440 54728;76253 54810 1;75930 54953;75810 55140;75683 55455;76283 55523 18; 77295 53948 1;77100 54218;76995 54345;76845 54630;76733 54765 1;76545 55043;76403 55185;76298 55478;76313 55643 1;76305 55748;76305 55823;76230 55898 1;76133 55988;76080 56033;76028 56137 1;75968 56242;75960 56370;75848 56370 1;75742 56370;75698 56287;75683 56190 1;75645 56033;75540 55980;75405 55883 1;75315 55838;75248 55815;75180 55800;74918 55688 1;74843 55635;74813 55568;74813 55470;74835 54945 1;74835 54930;74835 54915;74843 54893 1;74880 54593;74970 54443;75068 54180 32; 76898 54525;76770 54720 33;76620 54938;76508 55065;76410 55238 32;76448 55305 1;76673 55305;76793 55328;77010 55403 1;77070 55433;77130 55463;77183 55410 1;77453 55148;77640 55058;77865 54750 1;77895 54713;77963 54705;77955 54645 1;77865 54278;77588 54188;77273 53985;76898 54525 18; 69195 47310 1;69270 47280;69262 47160;69203 47123;69210 47295;69195 47310 18; 69203 46635 1;69233 46688;69262 46748;69293 46815 1;69315 46890;69323 46928;69367 46995 1;69405 47070;69450 47115;69533 47100 1;69735 47078;69840 47040;70058 47033;70433 46463;70125 46313;69458 45818;69203 45570;69210 46643;69203 46635 18; 69765 44475 1;69818 44130;69840 43943;70005 43620;70020 43530 33;70012 43463;69945 43410;69878 43425 1;69788 43455;69735 43440;69653 43463 1;69570 43493;69585 43568;69540 43628 1;69405 43815;69338 43928;69323 44152 1;69308 44303;69293 44378;69300 44520 32;69323 44595 1;69352 44610;69360 44640;69398 44633;69765 44475 18; 69810 44460 1;69908 44415;70005 44385;70140 44310 1;70185 44287;70193 44228;70178 44168 1;70110 43965;70050 43845;70028 43658 32;70020 43530 32;69990 43470 32;69788 44025 32;69735 44483;69810 44460 18; 69218 43140;69285 43103;69233 43050 32;69225 43133;69218 43140 18; 69203 45593 1;69495 45923;69870 46170;70335 46418 32;70425 46448 1;70500 46305;70553 46230;70635 46080 1;70515 45983;70455 45930;70328 45840 1;70275 45810;70283 45743;70320 45683 1;70358 45630;70373 45600;70418 45540 1;70470 45473;70425 45375;70350 45323 1;70178 45225;70110 45150;69968 45015 1;69818 44888;69788 44753;69780 44550;69765 44475 32;69398 44633 1;69360 44640;69352 44610;69323 44595;69300 44520 33;69293 44378;69308 44303;69323 44152 1;69323 44100;69330 44055;69338 44018 32;69345 43935 1;69293 43957;69248 43980;69210 44003;69210 45600;69203 45593 18; 69600 43448;69218 43073; 70935 43680 1;70958 43763;71025 43800;71108 43785 1;71183 43777;71220 43710;71227 43628 1;71242 43313;71250 43155;71295 42840 1;71303 42765;71318 42713;71280 42645 1;71198 42525;71123 42495;71018 42398 1;70965 42360;70883 42435;70883 42495 1;70875 42623;70838 42713;70733 42765 1;70620 42825;70545 42818;70455 42893 1;70395 42945;70448 43027;70515 43058 1;70643 43125;70748 43133;70815 43253 1;70890 43410;70860 43515;70943 43673;70935 43680 18; 72150 40838;72968 40718; 72473 39323;73290 39412; 70755 38490 1;70710 38655;70703 38738;70680 38902 1;70665 38985;70710 39060;70793 39068 1;70852 39082;70890 39082;70958 39082 1;71040 39082;71115 39143;71130 39225 1;71137 39360;71115 39435;71123 39563 1;71123 39683;71108 39758;71168 39855 1;71265 40035;71295 40170;71483 40253 1;71558 40290;71640 40283;71640 40193 1;71633 39990;71498 39878;71468 39668 1;71438 39495;71505 39390;71625 39248 1;71678 39180;71760 39188;71850 39203 1;71992 39240;72075 39218;72218 39277 1;72367 39353;72435 39457;72458 39623 1;72495 39945;72495 40110;72473 40425 1;72465 40523;72555 40628;72645 40590 1;72765 40537;72825 40500;72945 40433 1;73020 40395;73065 40350;73080 40260 1;73155 39660;73245 39360;73313 38753;70755 38490 18; 71715 42308 1;71708 42338;71715 42368;71745 42368 1;71873 42405;71933 42450;72075 42473 1;72150 42488;72188 42495;72248 42435 1;72398 42293;72428 42203;72623 42105 1;72727 42053;72750 41970;72795 41850 1;72840 41715;72878 41640;72780 41527 1;72713 41468;72645 41453;72563 41468 1;72345 41520;72240 41543;72038 41603 1;71910 41648;71850 41723;71828 41843 1;71783 42030;71753 42128;71715 42308 18; 71887 40973 1;71940 40890;71828 40845;71775 40770 1;71655 40628;71648 40440;71625 40253;71588 40260 33;71550 40275;71512 40275;71483 40253 1;71295 40170;71265 40035;71168 39855 1;71108 39758;71123 39683;71123 39563 1;71115 39435;71137 39360;71130 39225 1;71115 39143;71040 39082;70958 39082 1;70890 39082;70852 39082;70793 39068 1;70762 39068;70740 39060;70725 39045 32;70680 39015 1;70538 39240;70477 39360;70380 39593 1;70328 39720;70305 39803;70350 39923 1;70500 40350;70613 40553;70860 40935 1;70905 41010;70980 41018;71063 40995 1;71393 40920;71565 40965;71910 40988;71887 40973 18; 71895 40912;72473 40515 32;72473 40425 33;72495 40110;72495 39945;72458 39623 1;72435 39457;72367 39353;72218 39277 1;72075 39218;71992 39240;71850 39203 1;71760 39188;71678 39180;71625 39248 1;71505 39390;71438 39495;71468 39668 1;71498 39878;71633 39990;71640 40193 1;71640 40223;71625 40245;71617 40260 32;71625 40320 33;71648 40485;71663 40650;71775 40770 1;71798 40808;71828 40830;71858 40860;71895 40912 18; 74992 41880 1;74835 41978;74738 42037;74663 42195 1;74588 42353;74610 42480;74715 42615 1;74873 42840;75150 42660;75390 42525;74992 41880 18; 74693 38873 1;74925 38963;75008 39090;75210 39240 1;75323 39330;75413 39383;75555 39345 1;75653 39330;75660 39180;75600 39098 1;75533 39015;75510 38955;75503 38843;74693 38873 18; 75540 40043 1;76695 40020;77280 39975;78443 40088 1;78653 40110;78750 40140;78968 40155 1;79102 40170;79148 40245;79275 40298 1;79665 40478;80438 41018;80820 41213; 75578 41010 1;75503 41100;75465 41152;75398 41235 1;75338 41310;75293 41430;75383 41468 1;75450 41513;75480 41543;75563 41565 1;75645 41603;75705 41588;75795 41550 1;76012 41468;76117 41400;76335 41287 1;76425 41243;76500 41265;76590 41310 1;76800 41430;76905 41483;77130 41580 1;77198 41618;77265 41595;77333 41535 1;77490 41393;77558 41310;77730 41168 1;77835 41078;77933 41108;78075 41078 1;78308 41040;78383 40830;78420 40590 1;78443 40433;78435 40350;78450 40178 1;78450 40118;78383 40080;78323 40073 1;78030 40073;77887 40050;77602 40050 1;77355 40050;77227 40027;76988 40035 1;76905 40043;76830 40103;76852 40178 1;76883 40313;76905 40380;76898 40515 1;76883 40658;76867 40800;76740 40815 1;76380 40875;76200 40860;75848 40912 1;75727 40935;75637 40912;75570 40995;75578 41010 18; 75653 40433 1;75863 40448;75975 40448;76193 40448 1;76305 40448;76387 40402;76448 40298 1;76523 40162;76575 40095;76650 39945 1;76680 39878;76620 39810;76560 39758 1;76485 39705;76410 39675;76335 39713 1;76095 39832;75975 39893;75720 39945 1;75615 39968;75540 40005;75518 40103 1;75495 40193;75480 40238;75473 40328 1;75458 40402;75548 40440;75623 40433;75653 40433 18; 80940 48060 1;81038 47895;81150 47858;81255 47693 1;81398 47468;81383 47295;81390 47018 1;81390 46748;81367 46553;81165 46380 1;80955 46215;80775 46110;80535 46200 1;80265 46313;80168 46448;79980 46658 1;79883 46770;79883 46995;80033 47018 1;80205 47055;80295 47003;80475 46980 1;80588 46973;80745 47063;80700 47168 1;80580 47430;80573 47580;80445 47835;80940 48060 18; 78788 49313 1;78975 49117;79125 48998;79133 48720 1;79133 48615;79028 48495;78930 48533 1;78727 48623;78585 48668;78488 48863 1;78405 49012;78323 49103;78383 49253 1;78413 49343;78480 49418;78570 49380 1;78660 49350;78720 49358;78803 49290;78788 49313 18; 80835 52140 1;81015 52223;81098 52305;81300 52343 1;81390 52365;81480 52343;81525 52253 1;81585 52117;81555 51975;81435 51885 1;81308 51802;81248 51750;81120 51668;80835 52140 18; 82253 53228 1;82463 53310;82680 53265;82740 53108 1;82800 52965;82680 52770;82470 52680 33;82268 52598;82043 52658;81990 52800 1;81923 52950;82050 53145;82253 53228 18; 84458 54353 1;84608 54420;84690 54427;84855 54473 1;84975 54518;85035 54548;85163 54585 1;85598 54720;85838 54735;86295 54795 1;86318 54802;86363 54818;86363 54787 1;86288 54495;86145 54383;86055 54082 1;86033 54023;85935 54068;85890 54105 1;85748 54225;85673 54285;85515 54375 1;85440 54420;85387 54435;85313 54412 1;85110 54367;84990 54383;84810 54285 1;84645 54203;84653 54037;84488 53970 1;84293 53903;84173 54045;84023 54173;84458 54353 18; 88260 54473 1;88575 54367;88703 54248;89003 54090 1;89033 54075;89070 54015;89040 54008 1;88905 54000;88838 53970;88718 54008 1;88658 54030;88590 54090;88568 54030 1;88523 53955;88515 53910;88485 53820 1;88440 53730;88305 53783;88238 53850 1;87998 54098;87983 54308;87870 54623;88260 54473 18; 82643 48832 1;82425 49073;82178 49140;82148 49455 1;82117 49688;82117 49875;81915 49973 1;81810 50025;81727 49965;81645 50033 1;81615 50055;81608 50070;81600 50100 1;81548 50250;81518 50325;81503 50475 1;81488 50543;81398 50715;81458 50745 1;81578 50820;81698 50768;81848 50783 1;82178 50828;82328 50865;82635 51015 1;82793 51105;82958 51300;83115 51398 1;83168 51443;83280 51210;83325 51150 1;83393 51068;83460 50970;83565 51008 1;83760 51082;83865 51143;83992 51308 1;84105 51465;84195 51525;84367 51608 1;84615 51742;84773 51742;85058 51795 1;85553 51893;85793 51945;86295 52035 1;87083 52193;87473 52320;88238 52568 1;88448 52643;88620 52755;88628 52973 1;88628 53078;88613 53175;88710 53213 1;88852 53287;88943 53213;89108 53205 1;89280 53205;89385 53078;89550 53145 1;89738 53235;89850 53258;90008 53393;90510 53123;91395 52680;90578 51810;90270 51563;89798 51210;89258 51000;88860 50820;87750 50250;87435 50100;86700 49778;86363 49710;85920 49665;84825 49305;84352 49177;83617 48998;82643 48832 18;88860 52350 1;88635 52238;88583 52005;88650 51758 1;88680 51637;88823 51637;88950 51660 1;89265 51728;89393 51870;89633 52088 1;89745 52200;89738 52365;89640 52478 1;89453 52703;89160 52552;88920 52395;88860 52350 18; 88920 52395 1;89160 52552;89453 52703;89640 52478 1;89738 52365;89745 52200;89633 52088 1;89393 51870;89265 51728;88950 51660 1;88823 51637;88680 51637;88650 51758 1;88583 52005;88635 52238;88920 52395 18; 81293 54188 1;81143 53963;81045 53820;80798 53723 1;80693 53685;80580 53700;80535 53798 1;80468 53925;80445 54068;80565 54150 1;80783 54323;80910 54383;81150 54525 1;81210 54570;81338 54578;81338 54495 1;81338 54345;81367 54225;81255 54128;81293 54188 18; 81203 54713;82095 54735; 81180 56423 33;81218 56378;81270 56332;81323 56280 32;81390 56205 1;81173 55950;81083 55718;80918 55425 1;80880 55380;80805 55410;80753 55425 1;80633 55470;80565 55552;80617 55658 1;80753 55988;80918 56175;81135 56453;81180 56423 50; 81367 56242 1;81083 56505;80940 56730;80783 57075 1;80723 57203;80948 57203;81083 57188 1;81413 57165;81563 57023;81840 56835;81367 56242 18; 81608 57008 1;81795 57255;81915 57412;81938 57713 1;81945 57915;81908 58058;82073 58162 1;82268 58305;82380 58365;82620 58418 1;82943 58500;83130 58470;83430 58628 1;83512 58680;83588 58770;83535 58845 1;83453 58950;83430 59018;83325 59078 1;83288 59108;83333 59160;83378 59175 1;83453 59213;83490 59258;83580 59273 1;83925 59340;83933 58883;84150 58590 1;84165 58568;84098 58590;84083 58568 1;83955 58463;83887 58410;83753 58320 1;82935 57818;82553 57503;81863 56835;81795 56865 33;81735 56910;81683 56948;81630 56985 32;81608 57008 18; 79560 57285; 80573 57308 1;80303 57600;80115 57885;80160 58275; 107977 61328;109290 59745 32;109778 56753 32;111743 56580 32;111345 53918 32;113438 53565 32;115530 48675 32;118710 48150;118643 47520 32;119880 47445;121268 47355 32; 95288 77985;95070 77535;94470 77835 32;94538 77985;95288 77985 18; 96922 77460;96352 75968 32;95693 76223 32;95565 75900 32;94553 76298 32;94890 77175 32;95798 76830 32;96150 77760 32;96922 77460 18; 102600 78008;102600 77453 32;101512 77453;101512 78008;102600 78008 18; 103118 76590;102930 77602 32;104265 77835 32;104453 76830 32;103118 76590 18; 124433 72585;122693 69503 32;122078 69855 32;121935 69608 32;121073 70102 32;123158 73793 32;122880 73958 32;123480 75023 32;124433 74490;124440 72585;124433 72585 18; 124440 69053;124297 69143 32;124208 69000 32;123833 69240 32;124448 70208;124448 69053;124440 69053 18; 124455 65903;124005 66300 32;124455 66818;124462 65903;124455 65903 18; 119250 65843;119258 66113 32;118950 66120 32;119003 67515 32;119573 67485 32;119550 66637 32;120488 66600 32;120488 66405 32;120870 66390 32;120900 66983 32;121590 66953 32;121575 66173 32;121080 66188 32;121058 65235 32;120383 65250 32;120405 65805 32;119250 65843 18; 114968 65355;116408 65280 32;116430 65843 32;117210 65813 32;117158 64995 32;116768 65025 32;116700 63945 32;116070 63990 32;116070 64155 32;115665 64177 32;115680 64537 32;114698 64598 32;114705 64883 32;114330 64912 32;114413 66608 32;115028 66578 32;114968 65355 18; 113137 65588;113085 66248 32;113542 66278 32;113565 66030 32;113783 66038 32;113820 65640 32;113137 65588 18; 114848 69855;115837 69293 32;116325 69668 32;116250 70065 32;116933 70215 32;117075 69593 32;116933 69555 32;117120 68685 32;116483 68565 32;116430 68887 32;115462 68625 32;115283 68213 32;114518 68670 32;114683 69053 32;114518 69150;114848 69855 18; 124493 54315;123960 54375 32;123983 54495 32;123120 54600;123255 55733 32;124140 55620 32;124118 55350 32;124493 55305;124500 54315;124493 54315 18; 120195 53985;120712 58133 32;121178 58088; 120660 58148;121012 58110;120945 57510 32;120578 57540; 120885 54488;120945 54945 32;121080 54915 32;121193 55680 32;121672 55605 32;121628 55223 32;121905 55170 32;121935 55373 32;122520 55283 32;122408 54398 32;121635 54503 32;121628 54383 32;120885 54488 18; 119970 55710;118658 55875 32;118635 55710 32;118012 55785 32;118102 56528 32;118268 56512 32;118403 57585 32;119400 57457 32;119265 56408 32;120038 56310 32;119970 55710 18; 119917 59033;119955 59430 32;119265 59505 32;119363 60315 32;120090 60225 32;120060 59850 32;120998 59738 32;120915 58920 32;119917 59033 18; 116977 59768;118170 59558 32;118028 58770 32;117533 58867 32;117488 58658 32;116422 58853 32;116535 59535 32;115973 59640 32;116093 60375 32;117053 60218 32;116977 59768 18; 117195 62955;117068 63120 32;117368 63353;117727 62895;118268 62805;118073 61515 32;118425 61470 32;118335 60893 32;118028 60945 32;117968 60570 32;117233 60690 32;117292 61088 32;117180 61117 32;117233 61448 32;116977 61492 32;117195 62955 18; 122400 63923;123428 63795 32;123300 62760 32;124313 62617 32;124208 61800 32;122760 61980 32;122813 62408 32;122212 62475 32;122400 63923 18; 124470 59617;122917 59820;123075 61088 32;123938 60968 32;123893 60518 32;124477 60443;124477 59617;124470 59617 18; 121568 59512;124485 59160; 124485 56677;121178 57105; 69135 68858 1;71648 70628;73065 71580;75713 73440 1;77543 74738;78413 75458;80175 76845 1;80723 77288;81060 77535;81510 77940;82133 77948;82193 77685;79598 75593;74040 71685;69135 68370;69135 68873;69135 68858 18; 82410 77948;82260 77685 32;82140 77948;82410 77948 18; 81503 77933 1;81060 77535;80723 77288;80175 76845 1;78413 75458;77543 74738;75713 73440 1;73058 71580;71648 70628;69128 68850;69128 72615;69352 74865;73883 77925;81503 77940;81503 77933 18; 120083 39735;120938 39900 32;121118 38902 32;120270 38738 32;120083 39735 18; 119220 43928;115898 44108 32;112658 44033 32;109500 43140 32;109493 41123 32;109485 39743 32;109485 38348 32;110948 38340 32;114120 38318 32;114450 40470 32;115852 41730 32;115852 42428;116325 42428;116325 41738;118500 40335 32;118605 37103 32;122085 38655 32;124553 37553; 110438 64005;111825 64005 32;111825 64073 32;112635 64073 32;112635 63435 32;112080 63435 32;112080 63052 32;111120 63052 32;111120 63255 32;110438 63255 32;110438 64005 18; 110385 60015;110415 60323 32;110078 60360 32;110137 60795 32;109733 60840 32;109808 61358 32;110422 61275 32;110385 60908 32;110685 60870 32;110715 61103 32;111623 60990 32;111495 59880 32;110385 60015 18; 112837 61170;113550 61088 32;113550 61103 32;113535 60953 32;113790 60915 32;113828 61238 32;114458 61162 32;114375 60495 32;114098 60525 32;113993 59655 32;113430 59715 32;113453 59925 32;112703 60008 32;112837 61170 18; 111300 59115;112208 59010 32;112005 57270 32;111098 57367 32;111300 59115 18; 113648 58455;114465 58365 32;114428 58050 32;114810 57998 32;114735 57345 32;114563 57353 32;114488 56662 32;113453 56775 32;113648 58455 18; 114930 58658;113153 58867 32;113190 59213 32;114915 59018 32; 114615 56235;114398 54188 32;113010 54345 32;113070 54930 32;113618 54885 32;113768 56325 32;114615 56235 18; 115845 55103;115928 55740 32;116573 55643 32;116498 55012 32;115845 55103 18; 115792 53550;115860 54533 32;116775 54465 32;116805 54735 32;117360 54690 32;117278 53445 32;115792 53550 18; 119738 51653;118508 51735 32;118523 51975 32;117983 52012 32;118050 53025 32;118890 52965 32;118875 52710 32;119797 52643 32;119738 51653 18; 115628 50670;115785 51968 32;115387 52020 32;115433 52455 32;116520 52328 32;116475 52028 32;117128 51953 32;116940 50512 32;115628 50670 18; 116258 50453;116955 50558 32;117053 49905 32;116355 49793 32;116258 50453 18; 117265 49184;117337 50190 32;118448 50100 32;118383 49101 32;117265 49184 18; 119663 51090;120413 51398 32;120345 50783 32;119850 50588 32;119663 51090 18; 121275 52890;121538 52912 32;121590 52425 32;121313 52387 32;121275 52890 18; 119595 49245;120863 49245 32;120863 48698 32;120443 48698 32;120443 48510 32;120593 48510 32;120593 47955 32;119310 47955 32;119310 48488 32;119715 48488 32;119715 48735 32;119595 48735 32;119595 49245 18; 121748 50153;121770 51173 32;123060 51128 32;123038 50115 32;121748 50153 18; 124508 50063;124050 50010;123938 51045 32;124508 51105;124515 50063;124508 50063 18; 124515 46845;123540 46823;123518 47738 32;124523 47760;124523 46845;124515 46845 18; 124523 46245 1;123135 46343;122685 46275;121245 46313;121272 47476; 91035 50423; 81653 48435;82755 46448 1;82883 45675;82545 45270;82598 44475; 81203 60323; 82369 75957; 83640 59115 1;83333 59078;83175 59213;82883 59137 1;82545 59063;82380 59010;82050 58957 1;81495 58875;81173 58658;80910 58170 1;80790 57960;80715 57863;80633 57637 1;80565 57473;80408 57398;80227 57420 1;79852 57473;79643 57480;79313 57653 1;79155 57742;79035 57998;79193 58080 1;79620 58328;79808 58508;80273 58695 1;80655 58860;80895 58988;81090 59355 1;81240 59662;81383 59798;81690 59948 1;82163 60188;82380 60345;82860 60563;83640 59115 18; 87345 62145 1;87405 61965;87503 61912;87645 61770 1;88020 61395;88283 61260;88545 60795 1;88568 60758;88560 60698;88515 60683 1;87675 60533;87248 60443;86483 60068 1;86280 59978;86168 59948;85988 59828;85800 59760 33;85688 59963;85620 60105;85523 60330 1;85418 60555;85223 60593;84983 60615 1;84840 60630;84720 60630;84608 60623 32;84398 60525 1;84173 60660;84098 60758;83850 60825 1;83543 60915;83378 60893;83063 60870 1;82973 60870;82875 60840;82852 60930 1;82845 61012;82755 61058;82808 61103 1;83010 61328;83040 61470;83115 61762 1;83183 62093;83198 62273;83115 62588 1;83085 62700;83295 62783;83378 62700 1;83625 62453;83715 62242;84053 62115 1;84615 61912;85050 62490;85215 63068 1;85260 63240;85328 63323;85477 63420 1;85905 63713;85973 64035;86183 64508 1;86205 64567;86288 64628;86325 64575 1;86460 64373;86520 64268;86625 64035 1;86655 63960;86700 63893;86655 63818 1;86588 63728;86505 63728;86415 63668 1;86250 63578;86160 63443;86190 63248 1;86213 63090;86303 63037;86393 62895 1;86483 62753;86430 62617;86340 62468 1;85943 61875;85928 61440;85965 60720 1;85965 60570;86325 60713;86318 60863 1;86265 61650;86528 62137;87120 62655;87345 62145 18; 80123 63098 33;80018 63158;79913 63330;79800 63390 1;79170 63728;79058 64073;78705 64635 32;78510 64770 33;78735 64973;78983 65183;79313 65415 1;79538 65595;79740 65745;79935 65873 32;80250 65970 1;80768 65678;81098 65528;81383 64995 1;81420 64912;81323 64823;81233 64808 1;80783 64770;80550 64733;80123 64598 1;80055 64582;80055 64463;80123 64425 1;80408 64283;80528 64170;80820 64028 1;80933 63975;80985 63900;81015 63773 1;81038 63660;81038 63600;81060 63480 1;81068 63375;81083 63300;81030 63203 1;80985 63143;80918 63143;80843 63158 1;80708 63188;80655 63233;80535 63270 1;80408 63323;80325 63203;80250 63090;80123 63098 50; 82163 66180 1;82530 66173;82725 66165;83085 66053 1;83205 66015;83235 65910;83258 65775 1;83310 65438;83325 65220;83565 64957 1;83693 64815;83700 64695;83843 64552 1;83910 64485;83918 64410;83887 64313 1;83843 64200;83798 64148;83775 64028 1;83753 63953;83640 63968;83580 64012 1;83378 64155;83213 64185;82988 64102 1;82883 64073;82830 64155;82755 64223 1;82455 64485;82193 64628;81810 64523 1;81720 64500;81623 64530;81608 64613 1;81548 64845;81488 64980;81555 65205 1;81660 65573;81788 65730;81960 66068 1;81990 66143;82035 66188;82117 66180;82163 66180 18; 84135 65730 1;84600 65835;84915 65948;85335 65708 1;85403 65670;85403 65535;85328 65498 1;84908 65340;84780 65040;84645 64613 1;84608 64500;84465 64387;84383 64470 1;84068 64770;83933 65025;83940 65453 1;83940 65573;83940 65715;84060 65723;84135 65730 18; 74843 63832 1;74850 64035;74948 64200;75060 64192 1;75165 64192;75262 64012;75248 63810 33;75248 63615;75143 63450;75038 63457 1;74925 63457;74835 63637;74843 63832 18; 79140 65190 1;78855 64965;78773 64733;78428 64635 1;78180 64575;78068 64492;77865 64343 1;77490 64080;77543 63675;77145 63457 1;76958 63367;76830 63578;76703 63735 1;76635 63818;76658 63915;76718 63998 1;76808 64148;76898 64207;76958 64373 1;76980 64448;77003 64500;76958 64552 1;76898 64620;76890 64665;76883 64740 1;76860 64845;76980 64860;77063 64920 1;77475 65273;77633 65520;78075 65835 1;78218 65948;78330 65955;78518 65948 1;78900 65940;79080 65783;79418 65580;79140 65190 18; 79913 65992 1;79598 66165;79433 66255;79178 66495 1;79080 66585;79020 66727;79117 66818 1;79500 67193;79725 67335;80130 67680 1;80205 67755;80318 67852;80385 67762 1;80708 67313;80685 66960;81068 66548;79913 65992 18; 76050 66030 1;76485 65633;76815 65393;76905 64785 1;76913 64710;76867 64635;76793 64582 1;76725 64552;76628 64537;76560 64560 1;76403 64643;76448 64800;76395 64950 1;76313 65145;76283 65250;76148 65385 1;75998 65535;76058 65700;75945 65858 1;75923 65895;75915 65933;75915 65963 1;75930 66038;75960 66075;76012 66038;76050 66030 18; 69143 67950;72930 70605 1;74602 70965;73965 69128;74866 68433 1;74939 68377;74625 67815;74535 67793 1;74198 67733;74018 67725;73688 67770 1;73508 67800;73425 67883;73253 67883 1;73050 67883;72960 67845;72765 67838 1;72713 67838;72675 67793;72690 67740 1;72727 67500;72750 67373;72758 67125 1;72758 67005;72668 66960;72578 66878 1;72390 66735;72315 66645;72150 66473 1;72060 66390;72075 66308;72075 66188 1;72075 65865;72090 65648;71887 65385 1;71843 65332;71760 65370;71723 65423 1;71543 65693;71602 65895;71573 66210 1;71558 66330;71543 66398;71468 66473 1;71190 66735;70980 66825;70613 66848 1;70538 66855;70455 66833;70440 66900 1;70358 67155;70290 67290;70117 67485 1;70005 67613;69810 67755;69653 67710 1;69420 67665;69293 67710;69135 67635;69143 67943;69143 67950 18;70658 67200 1;70815 67073;70988 67012;71040 67073 1;71093 67125;71003 67290;70852 67425 33;70695 67560;70500 67635;70455 67583 1;70403 67515;70477 67320;70635 67178;70658 67200 18;72083 69983 1;72240 69840;72383 69773;72413 69555 1;72435 69360;72315 69210;72128 69150 1;71715 69030;71498 69015;71085 68918 1;70943 68887;70852 68798;70852 68655 1;70852 68512;71010 68468;71160 68468 1;71573 68483;71783 68588;72158 68768 1;72420 68903;72637 68933;72893 68775 1;73073 68663;73223 68633;73433 68700 1;73800 68828;73920 69036;73965 69426 1;74002 69786;73782 69624;73602 69924 1;73422 70224;73292 70023;72908 70230 1;72763 70308;72615 70193;72503 70313;72083 69983 18; 79673 75315 1;79718 75120;79748 74992;79658 74805 1;79613 74723;79635 74663;79673 74565 1;79718 74430;79680 74295;79560 74220 1;79403 74130;79283 74175;79110 74205 1;78998 74227;78923 74213;78833 74145 1;78000 73590;77550 73358;76748 72765 1;76665 72713;76605 72765;76515 72765 1;76425 72773;76380 72788;76298 72818; 79673 75315 1;79718 75120;79748 74992;79658 74805 1;79613 74723;79635 74663;79673 74565 1;79718 74430;79680 74295;79560 74220 1;79403 74130;79283 74175;79110 74205 1;78998 74227;78923 74213;78833 74145 1;78000 73590;77550 73358;76748 72765 1;76665 72713;76605 72765;76515 72765 1;76425 72773;76313 72818;76230 72848;79673 75315 18; 75428 72300 1;75248 71813;74963 71655;74550 71340 1;74453 71273;74363 71333;74265 71385; 75428 72300 1;75248 71813;74963 71655;74550 71340 1;74453 71273;74280 71378;74183 71430;75428 72300 18; 82815 77798 1;82838 77633;82785 77512;82658 77400 1;82253 77070;82073 76867;81630 76598 1;81383 76455;81405 76193;81473 75915 1;81495 75818;81525 75720;81443 75660 1;81090 75428;80903 75308;80505 75188 1;80363 75150;80265 75120;80153 75203 1;80025 75293;79988 75367;79898 75480;82815 77798 18; 79898 75480 1;79988 75367;80025 75293;80153 75203 1;80265 75120;80363 75150;80505 75188 1;80903 75308;81090 75428;81443 75660 1;81525 75720;81495 75818;81473 75915 1;81405 76193;81383 76455;81630 76598 1;82073 76867;82253 77070;82658 77400 1;82785 77512;82838 77633;82815 77798; 86280 77955;86595 76823;88125 77093;89985 76545 32;90015 73095 32;88875 72420;87465 72203 32;89168 68550;90420 66930 32;92348 65805 32;97688 64012 32;98828 66390;98977 70193 32;99893 75285; 83078 77940 1;83040 77468;83003 77137;83078 76590 1;83137 76095;83168 75840;83220 75338; 86512 76725 1;86415 76695;86325 76680;86265 76748 1;86108 76920;86108 77055;85965 77227 1;85905 77295;85973 77400;86055 77415 1;86168 77445;86227 77475;86348 77505;86512 76725 18; 87398 76463 1;87690 76448;87593 76117;87480 76095 1;87173 76058;86970 76080;86670 76043 1;86610 76043;86535 76050;86535 76110 1;86528 76245;86565 76313;86580 76440 1;86588 76553;86723 76545;86835 76538 1;87060 76530;87173 76500;87405 76463;87398 76463 18; 87788 76178 1;87788 76298;87870 76395;87990 76395 1;88313 76395;88560 76515;88800 76283 1;88920 76163;88875 75908;88710 75855 1;88508 75803;88380 75848;88208 75953 1;88073 76035;87983 76028;87855 76095;87788 76178 18; 87068 75180 1;86955 75023;86948 74895;86805 74768 1;86760 74745;86715 74753;86678 74768 1;86640 74798;86610 74835;86602 74865 1;86580 74925;86543 74985;86528 75038 1;86505 75113;86512 75173;86595 75195 1;86760 75285;86843 75352;87038 75352 1;87068 75352;87083 75330;87083 75285 1;87090 75255;87075 75218;87068 75180 18; 87293 75030 1;87488 74850;87578 75458;87803 75293 1;87893 75233;87975 74400;87900 74318 1;87713 74123;87698 73973;87548 73755 1;87503 73703;87435 73605;87398 73658 1;87248 73845;87180 73943;87045 74137 1;86955 74265;86835 74348;86887 74490 1;86977 74745;87090 74843;87240 75068;87293 75030 18; 88223 75090 1;88898 75262;89250 75323;89903 75570 1;90023 75623;90030 75315;89925 75240 1;89685 75083;89558 74992;89415 74745 1;89363 74663;89273 74655;89183 74678 1;88838 74790;88590 74685;88238 74648 1;88185 74648;88223 74715;88223 74760;88223 75090 18; 87218 75090 1;87345 75300;87443 75443;87690 75473 1;87878 75503;88043 75480;88140 75293 1;88283 75023;88313 74887;88230 74633 1;88178 74490;88058 74333;87923 74393;87218 75090 18; 88695 69360 1;88463 69278;88343 69240;88110 69173 1;88028 69158;87930 69113;87908 69188 1;87848 69352;87855 69450;87855 69615 1;87848 69953;87945 70178;88223 70358;88695 69360 18; 86227 66795 1;86423 66795;86520 66855;86715 66818 1;86880 66788;86775 66570;86790 66390 1;86790 66315;87024 66274;87069 66192 1;87174 66004;86948 65918;86820 65813 1;86685 65708;86453 65708;86393 65865 1;86258 66195;86120 66278;86083 66624;86227 66795 18; 89145 60818 1;89070 61177;88973 61328;88980 61688 1;88980 61778;89018 61778;89100 61808 1;89273 61898;89400 61867;89595 61800 1;90135 61628;90518 61523;91020 61800 1;91170 61890;91275 61875;91448 61860 1;91613 61853;91695 61838;91867 61830 1;91928 61830;91973 61800;92010 61740 1;92145 61530;92258 61433;92340 61185 1;92370 61088;92393 60998;92505 60975 1;93053 60893;93315 60742;93878 60742 1;93960 60742;94050 60735;94065 60645 1;94073 60593;94080 60563;94102 60503; 91965 60390 1;92108 60533;92198 60623;92430 60630 1;92610 60637;92753 60518;92850 60345;91965 60390 18; 92288 60113 1;92175 59948;92078 59835;91883 59820 1;91583 59805;91433 59753;91140 59783 1;90975 59805;90930 60023;90953 60180;92288 60113 18; 95493 60343 1;95415 60757;95610 61119;95880 61082 1;96120 61052;96212 60605;96242 60357;95493 60343 18; 95475 60542 1;95505 60812;95610 61119;95880 61082 1;96120 61052;96158 60804;96188 60556; 87000 60270 1;87030 61200;86760 61733;87120 62580 1;87255 62910;87593 62873;87953 62895 1;88523 62940;88965 62760;89363 63173 1;89783 63623;90098 63938;90713 63908 1;91117 63893;91305 63750;91695 63600 1;91852 63540;91867 63383;91867 63203 1;91852 62948;91867 62685;92108 62580 1;92400 62460;92595 62625;92850 62423 1;93068 62250;93165 62153;93330 61920 1;93525 61650;93645 61478;93968 61380 1;94433 61245;94583 60878;94703 60398 1;94755 60180;94260 60188;94140 60367;94102 60503 33;94080 60563;94073 60593;94065 60645 1;94050 60735;93960 60742;93878 60742 1;93315 60742;93053 60893;92505 60975 1;92393 60998;92370 61088;92340 61185 1;92258 61433;92145 61530;92010 61740 1;91973 61800;91928 61830;91867 61830 1;91695 61838;91613 61853;91448 61860 1;91275 61875;91170 61890;91020 61800 1;90518 61523;90135 61628;89595 61800 1;89400 61867;89273 61898;89100 61808 1;89018 61778;88980 61778;88980 61688 1;88973 61328;89070 61177;89145 60818 32;89078 60720 1;88313 60675;87915 60615;87210 60323;87000 60270 18; 88223 61200 33;87923 61508;87675 61965;87420 62438 1;87315 62625;87180 62693;87128 62850 32;87210 62970 1;87398 63075;87518 63090;87705 63203 1;87810 63278;87885 63353;87878 63480 1;87848 63698;87818 63795;87765 63998 1;87720 64133;87915 64177;88058 64170 1;88238 64162;88335 64035;88380 63855 1;88477 63427;88530 63218;88590 62775 1;88620 62505;88673 62340;88560 62085 1;88410 61778;88410 61448;88395 61103;88223 61200 50; 91140 64050 1;91170 63998;91140 63953;91140 63885;91140 63832 33;91012 63878;90878 63908;90713 63908 1;90098 63938;89783 63623;89363 63173 32;89265 63075 1;89213 62648;89295 62430;89333 61995;89318 61867 33;89250 61867;89175 61853;89100 61808 1;89018 61778;88980 61778;88980 61688 1;88973 61328;89070 61177;89145 60818 32;88875 60713 1;88740 60690;88620 60585;88545 60690 1;88470 60787;88485 60870;88440 60975;88395 61103 33;88410 61448;88410 61778;88560 62085 1;88673 62340;88620 62505;88590 62775 1;88575 62865;88560 62940;88553 63015 32;88538 63082 33;88492 63345;88448 63540;88380 63855 1;88335 64035;88238 64162;88058 64170 1;87968 64177;87863 64162;87803 64125 32;87742 64102 1;87668 64268;87660 64365;87683 64537 1;87698 64695;87765 64815;87923 64852 1;88163 64920;88290 64942;88545 64950 1;88770 64965;88725 65302;88958 65355 1;89130 65400;89235 65265;89415 65302 1;89790 65393;90000 65453;90383 65370 1;90510 65348;90585 65302;90660 65190 1;90908 64800;90938 64552;91117 64117;91140 64050 18; 87548 67688 1;88477 66870;88988 66405;90188 66060; 91898 62258 1;92018 62220;91950 62025;91860 61927;91762 61838 33;91650 61845;91575 61860;91448 61860 1;91275 61875;91170 61890;91020 61800 1;90518 61523;90135 61628;89595 61800 1;89498 61838;89423 61860;89348 61867 32;89310 62183 1;89303 62228;89325 62258;89370 62258 1;89753 62258;89940 62220;90330 62190 1;90983 62145;91313 62302;91980 62273;91898 62258 18; 107783 61703;106455 61305;105885 62887 32;106530 63255;107783 61703 18; 106583 63278;105885 62887 32;106455 61305; 99023 52508 1;98977 52830;98948 52988;98910 53302 1;98895 53378;98955 53445;99030 53445 1;99645 53475;99960 53408;100583 53325 1;100658 53318;100762 53355;100748 53423 1;100583 54023;100508 54315;100387 54915 1;100365 55012;100410 55140;100508 55125 1;100665 55103;100740 55058;100898 54998 1;101070 54938;101085 54780;101167 54608 1;101400 54098;101505 53828;101625 53265 1;101670 53033;101580 52898;101475 52673;99023 52508 18; 98273 54457 1;98378 54660;98445 54780;98430 54998 1;98422 55058;98468 55088;98528 55095 1;98843 55185;98977 55328;99218 55552 1;99248 55590;99308 55530;99315 55478 1;99330 55223;99360 55095;99398 54832 1;99405 54773;99390 54540;99330 54533 1;99172 54525;99113 54653;98977 54578 1;98753 54465;98625 54465;98385 54465;98273 54457 18; 99262 55545 1;99712 55635;99968 55785;100417 55658 1;100515 55635;100583 55575;100613 55470 1;100658 55298;100733 55223;100748 55035 1;100748 54885;100538 55080;100395 55035 1;100065 54953;99765 54585;99518 54443 1;99420 54390;99405 54503;99315 54555;99262 55545 18; 98565 54585 1;98738 54105;98813 53873;98895 53385 1;98813 53378;98723 53370;98625 53370 1;98393 53370;98273 53355;98063 53423 1;97755 53520;97643 53738;97545 54037 1;97508 54143;97493 54195;97462 54300 1;97440 54360;97448 54435;97508 54435;97538 54435 1;97868 54443;97958 54548;98295 54555 1;98393 54563;98468 54578;98565 54585 18; 99615 53423;99540 54465 32;99600 54503 33;99810 54660;100065 54945;100350 55028 32;100387 54915 33;100508 54315;100583 54023;100748 53423 1;100762 53355;100658 53318;100583 53325 1;100215 53378;99953 53423;99683 53445;99615 53423 18; 98977 52650;98977 52808 1;98955 52965;98933 53108;98910 53302;98835 53378 1;98775 53378;98700 53370;98625 53370 1;98393 53370;98273 53355;98063 53423 1;97755 53520;97643 53738;97545 54037 1;97508 54143;97493 54195;97462 54300 1;97440 54360;97448 54435;97508 54435;97538 54435 1;97868 54443;97958 54548;98295 54555;98415 54570 1;98475 54578;98528 54585;98602 54585 1;98880 54608;99008 54690;99233 54623 1;99360 54585;99413 54548;99540 54480 1;99600 54450;99608 54420;99615 54345 1;99630 53978;99660 53933;99667 53558 1;99667 53498;99667 53415;99608 53415 32;99743 53445 1;99990 53423;100238 53378;100583 53325 1;100613 53325;100650 53332;100680 53340 33;100725 53363;100755 53385;100748 53423 1;100718 53512;100695 53595;100672 53677;100628 53865 1;100538 54210;100470 54480;100387 54915 1;100365 55012;100410 55140;100508 55125 1;100665 55103;100740 55058;100898 54998 1;101070 54938;101085 54780;101167 54608 1;101400 54098;101505 53828;101625 53265 1;101655 53093;101610 52973;101542 52823 32; 93480 52238;93908 53168 32;94965 53228 32;95242 52927 32;94725 52155;93480 52238 18; 98415 53355 1;98258 53205;98198 53063;97988 53003 1;97913 52988;97905 52867;97965 52815 1;98085 52710;98160 52635;98183 52470;98047 52305 33;97830 52283;97523 52358;97283 52328 1;95175 52125;94463 51915;92595 52455 32;92325 52530 33;91867 52695;91523 52853;91163 53018 32;91170 53078 33;91215 53175;91238 53250;91298 53355 1;91320 53423;91365 53453;91440 53453 1;91583 53453;91658 53438;91808 53438 1;91883 53438;91935 53505;91943 53580 1;91943 53662;91950 53723;91958 53783 32;91845 53820 1;91455 54098;91433 54443;91373 54908 1;91335 55170;91313 55177;91373 55433 1;91403 55613;91815 55740;91852 55860 1;91928 56093;91823 56438;91770 56753 1;91703 57060;91695 57375;91710 57623 1;91755 58245;91958 58725;92198 59460;92273 59512 32;92992 59168 32;93090 59340 32;93210 59393 1;93600 59655;93773 59820;94193 60037 1;94335 60120;94575 60255;94613 60082;94650 60023 33;94665 59798;94650 59490;94867 59490 32;95063 59378 33;95333 59355;95475 59325;95835 59235 1;96413 59108;96750 58845;96968 58313 1;97215 57698;97268 57345;97297 56677 32;97283 56535 33;97283 56378;97238 56302;97260 56137 1;97260 56078;97275 56018;97290 55965 32;97283 55740 1;97297 55440;97313 55283;97320 54975;97320 54818 33;97335 54668;97380 54578;97425 54412 32;97462 54300 33;97493 54195;97508 54143;97545 54037 1;97643 53738;97755 53520;98063 53423 1;98160 53393;98235 53378;98318 53378 32;98415 53355 18; 95310 60120;97208 60075 32;97215 59963 33;97268 59550;97283 59302;97403 58845 1;97545 58275;97740 57938;97770 57345 1;97778 57105;97792 57098;97613 56933 1;97485 56828;97350 56775;97297 56655 32;97290 56738 33;97260 57367;97208 57720;96968 58313 1;96750 58845;96413 59070;95835 59198 1;95393 59310;95273 59355;94830 59400 32;94867 59490 32;94988 59520 33;95137 59595;95205 59805;95265 60008;95310 60120 18; 98573 52365 1;99278 52440;99938 52515;100815 52590 1;102773 52515;103672 52568;105645 52350 1;106800 52223;107385 51990;108405 51420 1;109658 50723;109965 50213;111165 49410 1;112042 48818;112868 48608;113925 48473 1;114683 48383;115185 48173;115815 47730 1;116408 47318;116760 46883;117308 46620 1;117727 46425;117983 46162;118185 45990; 89753 53745 1;89895 53805;89963 53865;90128 53873 1;90262 53887;90398 53940;90473 53820 1;90548 53693;90443 53580;90360 53445;89753 53745 18; 89963 57480; 84833 56933 1;84810 57060;84855 57188;84983 57218 1;85380 57330;85620 57353;85943 57608 1;86153 57780;86288 57818;86512 57975 1;86588 58035;86678 57938;86708 57840 1;86805 57480;86790 57278;86813 56895 1;86820 56707;86828 56610;86798 56423 1;86768 56295;86670 56198;86543 56213 1;85898 56295;85605 56483;85005 56723 1;84893 56768;84833 56783;84810 56895;84833 56933 18; 84818 58733;84930 58762 33;84960 58320;84870 58028;85140 57668 1;85238 57540;85283 57473;85380 57338 32;84810 57052 33;84593 57308;84548 57480;84323 57713 1;84262 57773;84195 57855;84135 57795 1;83933 57630;83775 57623;83580 57450 1;83528 57412;83453 57383;83415 57435 1;83370 57495;83340 57525;83310 57563 32;83280 57623 32;84053 58148;84818 58733 18; 85890 59340 1;85943 59025;85973 58823;86198 58560 1;86355 58380;86438 58260;86558 58065 32;86550 57998 33;86288 57818;86153 57780;85943 57608 1;85755 57465;85590 57398;85418 57338 32;85380 57338 33;85283 57473;85238 57540;85140 57668 1;84878 58020;84953 58298;84930 58718 32;84923 58808 32;85875 59423;85890 59340 18; 87878 54787;86820 55020 32;86378 55005 32;84975 54742 32;84533 54585 32;84053 54405 32;84023 54495 33;83745 55440;83910 56250;84780 56828 32;84900 56768 33;84930 56753;84960 56745;85005 56723 1;85605 56483;85898 56295;86543 56213 1;86670 56198;86768 56295;86798 56423 1;86813 56558;86820 56648;86813 56760 32;86805 56992 33;86790 57315;86790 57518;86708 57840 1;86685 57908;86633 57975;86580 57990 32;86565 58043 33;86445 58253;86363 58373;86198 58560 1;85958 58845;85943 59063;85875 59423 32;86558 59753 32;87240 60030 32;87270 59948 33;87315 59790;87413 59640;87570 59685 1;88035 59850;88283 59895;88755 60075 1;88905 60143;88980 60262;89003 60420 32;89438 60427 32;89445 60315 33;89453 59835;89430 59558;89385 59033 1;89355 58808;89303 58665;89415 58455 1;89543 58215;89685 58073;89835 57832 1;89880 57750;89865 57615;89775 57578 1;89295 57398;88920 57488;88492 57218 1;88313 57113;88290 56955;88260 56745 1;88133 56010;87975 55635;87923 54915;87878 54787 18; 90735 52568 1;91140 52537;91358 52455;91710 52238 1;92250 51908;92468 51653;93008 51300; 81742 56325;82470 57060 32;83235 57630 32;83310 57563 33;83340 57525;83370 57495;83415 57435 1;83453 57383;83528 57412;83580 57450 1;83775 57623;83933 57630;84135 57795 1;84195 57855;84262 57773;84323 57713 1;84548 57480;84593 57308;84810 57052 32;84810 56843 32;84900 56768 33;84930 56753;84960 56745;85005 56723 1;85605 56483;85898 56295;86543 56213 1;86580 56213;86625 56220;86655 56235 32;86843 54998 32;86348 54983 32;84983 54735 32;84555 54585 32;83295 54150 32;82875 54015 32;81548 53603 32;81495 53490 32;81495 53610 33;81495 53670;81503 53715;81518 53775 1;81578 54158;81690 54330;81720 54705 1;81735 54975;81630 55110;81630 55373 1;81623 55537;81765 55598;81765 55755 1;81758 55943;81750 56040;81750 56205;81742 56325 18; 84833 56933 1;84810 57060;84855 57188;84983 57218 1;85380 57330;85620 57353;85943 57608 1;86153 57780;86288 57818;86512 57975 1;86588 58035;86678 57938;86708 57840 1;86805 57480;86790 57278;86813 56895 1;86820 56707;86828 56610;86798 56423 1;86768 56295;86670 56198;86543 56213 1;85898 56295;85605 56483;85005 56723 1;84893 56768;84833 56783;84810 56895; 93023 59205;97297 56655 1;97283 56625;97275 56588;97283 56535 1;97283 56378;97238 56302;97260 56137 1;97275 55995;97297 55898;97425 55808 1;97538 55733;97620 55755;97755 55718 1;97883 55688;97950 55523;97890 55403 1;97800 55245;97680 55223;97515 55140 1;97455 55117;97403 55088;97365 55050 32;97320 54975 32;97320 54818 33;97335 54668;97380 54578;97425 54412 32;97462 54300 33;97493 54195;97508 54143;97545 54037 1;97643 53738;97755 53520;98063 53423 1;98160 53393;98235 53378;98318 53378 32;98415 53355 1;98258 53205;98198 53063;97988 53003 1;97913 52988;97905 52867;97965 52815 1;98070 52725;98145 52650;98167 52515 32;98175 52425 32;97305 52335 32;96683 52268 32;95385 52140 32;94778 52103 32;94725 52155 32;95242 52927 32;94965 53228 32;93908 53168 32;93525 52343 32;93503 52260 32;93413 52260 32;92955 52343 32;91680 52762 32;91133 52980 33;91208 53130;91223 53213;91298 53355 1;91320 53423;91365 53453;91440 53453 1;91583 53453;91658 53438;91808 53438 1;91883 53438;91935 53505;91943 53580 1;91958 53835;92003 53955;92085 54195 1;92175 54495;92310 54608;92483 54863 1;92520 54923;92475 54998;92415 55020 32;92880 59205;93023 59205 18; 97275 56625;92977 59175 32;93015 59220 32;93083 59332 32;93510 60045 32;94635 60105 32;94650 60023 33;94665 59798;94650 59490;94867 59490 32;95018 59385 33;95318 59355;95460 59332;95835 59235 1;96413 59108;96750 58845;96968 58313 1;97208 57713;97260 57360;97290 56723;97275 56625 18; 103433 60600;103823 59543 1;103658 59475;103515 59400;103387 59512 1;103163 59700;103133 59768;103005 60023 1;102885 60255;102503 60503;102292 60668;103433 60600 18; 78615 77933 1;78630 77843;78645 77753;78668 77655 1;78810 76950;78945 76605;78990 75870 1;79005 75293;78990 74385;79545 74535 1;80250 74730;80528 75060;81030 75593 1;81375 75975;81727 76020;82245 76012 1;82590 76012;82867 76050;83085 75773 1;83220 75600;83528 75683;83625 75870 1;83835 76343;84000 76553;84367 76913 1;84795 77363;85073 77640;85463 77955; 92175 77970 1;92438 77730;92678 77483;92948 77093 1;93480 76305;93885 76005;94568 75315 1;95205 74655;95775 74625;96645 74295 1;97260 74063;97650 73815;98265 73590 1;98505 73515;98505 73275;98190 72953 1;97988 72773;97950 72510;98085 72270 1;98198 72083;98348 72060;98505 71895 1;98715 71670;98887 71520;99210 71535 1;99458 71550;99540 71730;99750 71895 1;100320 72352;100545 72660;101167 73050 1;101775 73455;102060 73695;102727 73995 1;103725 74460;104295 74790;105405 74715 1;106140 74670;106590 74730;107205 74310; 73275 77528 1;73403 76950;73688 76523;73710 75795 1;73718 75030;73230 74730;72750 74130 1;72165 73433;71685 73043;71685 72135 1;71685 71715;71715 71430;72008 71115 1;72637 70425;73500 70575;74370 70950 1;75135 71303;75578 71415;76268 71910 1;76770 72285;76995 72540;77588 72735 1;78578 73073;79095 73245;80025 73733 1;80940 74220;81338 74670;82365 74835 1;82740 74903;83010 74775;83227 74453 1;83385 74213;83670 74010;83925 74175 1;84240 74385;84367 74602;84450 74970 1;84570 75593;84570 75990;85005 76433 1;85538 76980;85778 77303;86408 77730 1;86535 77820;86648 77895;86760 77963; 91343 77963 1;91755 77565;91898 77250;92325 76793 1;92790 76290;92955 75975;93225 75330 1;93510 74655;93660 74265;94208 73755 1;94680 73320;95108 73410;95708 73170 1;96420 72893;96690 72563;97268 72053 1;97958 71445;98318 71130;98910 70410 1;99008 70283;99188 70305;99330 70395 1;99870 70800;100118 71040;100688 71415 1;101528 71970;101940 72270;102810 72795 1;103470 73200;103905 73185;104670 73335 1;105540 73515;105998 73575;106890 73470 1;107280 73350;107528 73313;107813 73005; 92970 77970 1;93548 77288;93953 76793;94748 76095 1;95738 75225;96540 75210;97808 74790 1;98385 74602;98820 74505;99105 73950 1;99225 73710;99278 73590;99390 73335 1;99488 73095;99840 73095;100050 73253 1;100650 73733;100935 73995;101625 74333 1;102555 74813;103080 74963;104108 75173 1;104977 75360;105420 75585;106305 75510 1;106837 75473;107130 75435;107625 75210 1;107993 75038;108045 74730;108172 74333 1;108360 73733;108480 73148;109110 73058 1;109688 72983;110025 72998;110558 73245 1;110970 73448;111248 73583;111428 74003 1;111623 74475;111600 74745;111667 75248 1;111690 75480;111743 75675;111968 75742 1;112208 75818;112335 75803;112605 75833; 113160 53250;113565 52305; 113468 53550;113820 52403 1;113587 52268;113393 52320;113145 52403 1;112980 52463;112935 52620;112913 52785 1;112860 53093;112868 53250;112852 53558;113468 53550 18; 113520 53198;113828 52410 33;113550 52215;113250 52373;113145 52403 1;112980 52463;112935 52620;112913 52785 1;112890 52920;112875 53025;112868 53123 32;112852 53250 32;113137 53205;113520 53198 18; 114255 50985;112950 51758; 112583 51037;114893 49957; 114533 49335 1;114435 49500;114398 49590;114270 49725 1;114188 49815;114090 49808;113985 49778 1;113602 49680;113408 49575;113018 49582 1;112785 49590;112605 49628;112508 49823 1;112268 50280;112230 50543;112125 51037 1;112087 51195;112350 51188;112500 51128 1;113378 50798;113738 50438;114623 50093 1;114743 50048;114825 50048;114930 49950 1;115140 49748;115238 49582;115245 49283 1;115245 49170;115102 49140;114990 49140;114533 49335 18; 111225 53512;112005 53363 1;112193 53302;112020 53198;112035 53010 1;112035 52875;112012 52853;111990 52710 1;111878 52155;111938 51825;112155 51293 1;112208 51165;112403 50963;112080 50940 1;111750 50918;111563 51075;111375 51338 1;111068 51773;111210 52117;111225 52643 1;111233 53093;110955 53287;110625 53573;111225 53512 18; 112868 53168 33;112875 53055;112883 52935;112913 52785 1;112935 52620;112980 52463;113145 52403 1;113363 52335;113542 52283;113738 52365 32;113880 52380 1;114292 51465;114563 51023;114863 50055 1;114893 49957;114698 50048;114608 50085 1;113940 50408;113618 50588;112950 50887 1;112703 51000;112560 51075;112297 51098 1;112133 51120;112073 51278;112035 51427 1;111953 51698;111960 51848;111938 52117 1;111885 52575;112005 52815;112155 53242;112868 53168 50; 118710 48150 32;118643 47520 32;119760 47453 32;120030 47355 1;119962 47048;119723 46912;119415 46845 1;119100 46785;118890 46853;118643 47040 1;118065 47475;117705 47595;117060 47895 1;116797 48023;116700 48203;116663 48480;118710 48150 50; 115178 49207 1;115238 49207;115290 49177;115297 49110 1;115305 48975;115395 48923;115440 48787 1;115462 48705;115583 48585;115500 48578 1;115095 48555;114840 48795;114675 49162;115178 49207 18; 106943 55170 33;107385 54832;107708 54660;107962 54533 32;108098 54375 1;107977 54113;107917 53940;107685 53768 1;107580 53700;107535 53610;107550 53483 1;107558 53348;107587 53258;107528 53130 1;107422 52943;107205 53018;106988 53033 1;106553 53078;106365 53265;105938 53302 1;105593 53340;105383 53280;105098 53460 1;104910 53580;104962 53873;105135 54000 1;105368 54188;105510 54255;105803 54338 1;106297 54488;106462 54802;106725 55253;106943 55170 50; 108262 56348 1;108705 56430;108938 56370;109387 56385 1;109538 56393;109635 56220;109605 56063 1;109500 55628;109440 55403;109297 54975 1;109223 54780;109133 54623;108922 54593 1;108743 54570;108653 54653;108518 54758 1;108300 54930;108255 55073;108045 55238 1;107933 55328;107828 55373;107813 55508 1;107775 55748;107775 55867;107760 56100;108262 56348 18; 107685 56242 1;107145 56558;106733 56528;106328 56985 1;106185 57150;106148 57255;106058 57450 1;105938 57698;105908 57870;105690 58020 1;105547 58117;105495 58238;105518 58403 1;105547 58778;105465 59145;105113 59242 1;104775 59340;104587 59280;104265 59385 1;104033 59468;103973 59617;103845 59820 1;103672 60090;103613 60248;103508 60540 1;103470 60637;103560 60720;103658 60735 1;104100 60840;104333 60818;104783 60923 1;104977 60975;105068 61028;105278 61035 1;105540 61050;105720 61103;105945 60945 1;106538 60533;106845 60315;107295 59738 1;107595 59348;107738 59137;107925 58673 1;108045 58373;108180 58193;108495 58073 1;108720 57990;108953 58035;109035 57802 1;109102 57585;109178 57473;109178 57233 1;109163 56910;108900 56760;108593 56655 1;108413 56603;108337 56543;108165 56483;107685 56242 18; 106508 56880 1;106305 56550;106230 56363;105960 56085 1;105885 56018;105833 56198;105833 56287 1;105788 56940;105765 57323;105405 57855 1;105285 58028;105240 58133;105098 58275 1;104730 58643;104618 58898;104318 59310 1;104273 59378;104385 59445;104460 59430 1;104887 59385;105278 59453;105473 59063;106508 56880 18; 107078 56040;106680 55935 32;105915 55635 32;105960 56085 33;106178 56318;106268 56490;106410 56723 32;106440 56783 32;106508 56880 32;107738 56265;107078 56040 18; 107520 56332 33;107055 56558;106688 56580;106328 56985 1;106185 57150;106148 57255;106058 57450 1;105938 57698;105908 57870;105690 58020 1;105547 58117;105495 58238;105518 58403 1;105547 58778;105465 59145;105113 59242 1;104858 59318;104693 59302;104498 59332; 107768 55935 33;107775 55800;107783 55688;107813 55508 1;107828 55373;107933 55328;108045 55238 1;108255 55073;108315 54915;108533 54742 1;108585 54705;108495 54765;108547 54735; 109365 55230 1;109665 54870;109875 54735;110212 54390 1;110288 54315;110273 54143;110167 54120 1;109883 54068;109755 53985;109500 53865 1;109275 53768;109125 53835;108900 53903 1;108675 53978;108630 54128;108473 54293 1;108420 54345;108330 54353;108352 54412 1;108398 54578;108443 54660;108533 54802;109365 55230 18; 111698 56490 1;111585 55853;111608 55598;111518 54998 1;111443 54578;111675 54008;111255 53963 1;111105 53955;111083 54135;111038 54270 1;110843 54818;110745 55110;110378 55545 1;110190 55762;110040 55845;109928 56100 1;109868 56228;110040 56318;110175 56325 1;110602 56370;110820 56393;111225 56550;111698 56490 18; 109477 57503 1;109515 57555;109620 57585;109643 57518 1;109688 57345;109710 57255;109725 57075 1;109733 56933;109875 56760;109740 56715 1;109410 56625;109230 56648;108900 56633;109477 57503 18; 106628 61133 1;106898 60855;107040 60645;107430 60585 1;107760 60540;107955 60533;108233 60330 1;108420 60188;108570 60180;108818 60188 1;108983 60195;109028 60045;109133 59903 1;109268 59707;109275 59565;109313 59318 1;109403 58650;109605 58328;109583 57645 1;109575 57548;109462 57548;109395 57473 1;109313 57405;109275 57308;109178 57330;109155 57443 33;109125 57563;109080 57660;109035 57802 1;108953 58035;108720 57990;108495 58073 1;108180 58193;108045 58373;107925 58673 1;107738 59137;107595 59348;107295 59738 1;106845 60315;106538 60533;105945 60945 1;105870 60998;105795 61028;105727 61043 32;105870 61140;106628 61133 18; 105945 60945 33;106538 60533;106845 60315;107295 59738 1;107595 59348;107738 59137;107925 58673 1;108045 58373;108180 58193;108495 58073 1;108720 57990;108953 58035;109035 57802 1;109073 57683;109110 57600;109133 57503; 108023 61365;108818 60188 33;108570 60180;108420 60188;108233 60330 1;107955 60533;107760 60540;107430 60585 1;107055 60645;106905 60840;106658 61095 32;106635 61162 32;107970 61463;108023 61365 18; 112875 53603;112875 53250 33;112598 53258;112358 53258;112125 53295 1;111833 53355;111608 53475;111345 53558 32;111172 53670 1;111195 53783;111218 53828;111248 53933;112875 53603 18; 109395 55290 33;109470 55545;109523 55748;109605 56063 1;109635 56220;109538 56393;109387 56385 1;109065 56378;108855 56408;108608 56393; 109598 57525 1;109448 57383;109320 57188;109148 57285 1;108840 57457;108428 57780;108675 58020 1;108922 58275;109095 58350;109425 58478;109598 57525 18; 109365 61425;109410 60930 32;109762 60908; 109883 61253;109898 61508; 109470 60983;109433 61718 32;109830 61740 32;109770 60968;109470 60983 18; 111983 62662;111990 63525 32;112538 63503 32;112830 62873; 112073 62520;112050 63465 32;112485 63435 32;112830 62662;112073 62520 18; 116640 63818;116670 64567 32;117698 64537; 116730 63555;116730 64500 32;117885 64440;116730 63555 18; 122198 65310;122265 66060 32;122723 66015 32;122648 65273 32;122198 65310 18; 121718 65093;121748 65535 32;122625 65468 32;122745 65033; 121792 64860;121823 65460 32;122558 65408 32;122723 64800;121792 64860 18; 124485 56730;121238 57135; 121628 59468;123300 59235 1;123255 58973;123210 58800;123000 58635 1;122745 58455;122550 58433;122250 58455 1;121920 58485;121733 58635;121560 58912;121628 59468 18; 123068 57008 1;123225 57195;123390 57262;123637 57225 1;123870 57195;123990 57090;124133 56887;123068 57008 18; 121462 57908 1;121672 57675;121778 57495;121762 57173;121358 57218;121462 57908 18; 121560 58912 1;121733 58635;121920 58485;122250 58455 1;122550 58433;122745 58455;123000 58635 1;123210 58800;123255 58973;123300 59235; 123132 57085 1;123289 57272;123390 57262;123637 57225 1;123870 57195;123926 57205;124069 57002; 121564 57793 1;121774 57560;121778 57661;121762 57339; 124485 56745;121320 57165;121560 59505 32;124485 59145;124493 56745;124485 56745 18; 124058 54427;123990 53738; 122415 54832;122795 54795;122693 53723 16; 121538 54427;121500 53895; 124493 53610;124050 53498;124133 54383 32;124500 54360;124500 53610;124493 53610 18; 121568 53617;121643 54840 32;122738 54750 32;122580 53498;121568 53617 18; 121635 53048;121620 52845 32;121380 52823 32;121365 51787 32;119820 51030 32;119887 50693 32;119025 50430 32;117938 50468 32;117637 50108 32;116955 50130 32;116790 50595 32;116903 51218 32;117555 51555 32;118297 51570 32;118590 51810; 120922 53198;120915 52110 32;119723 51540 32;119460 51728; 121568 53400;121538 52898 32;121358 52853 32;121328 51750 32;120113 51120 32;119813 50677 32;119093 50423 32;117953 50460 32;117840 50363 32;117637 50108 32;116955 50130 32;116790 50595 32;116903 51218 32;117555 51555 32;118297 51570 32;118553 51787 32;119468 51765 32;119678 51570 32;121005 52140 32;121005 53453;121568 53400 18; 118095 54555;117090 54623 32;117120 55335; 118950 54878;119047 55957; 119602 55808;119955 55785 32;119910 55238 32;119558 55260 32;119602 55808 18; 119640 55328;119565 54563; 118283 54637;117165 54705 32;117225 55478;118283 54637 18; 119910 55283;119858 54473 32;119573 54593 32;119648 55343;119910 55283 18; 119010 54608;119123 55838 32;119610 55778 32;119468 54360;119010 54608 18; 116753 55568;115020 55740 32;115058 56153 32;116273 56033; 115485 56130;115553 57000; 117023 55628;115087 55793 32;115118 56115 32;115545 56085 32;115672 57278;117023 55628 18; 115125 58718;113212 58912 32;113235 59130 32;115140 58898;115125 58718 18; 114998 60488;114308 60585 32;114352 61088 32;115080 60990; 117135 57098;116640 57630 32;116145 57600; 116318 61973;116512 61710 32;117045 61650; 115253 60540;114375 60637 32;114398 61005 32;115320 60878;115253 60540 18; 115988 57503;116618 57555 32;117030 57113 32;116528 56670;115988 57503 18; 116925 63300;117210 62963 32;117023 61695 32;116565 61762 32;116183 62310;116925 63300 18; 124493 54427;124080 54465 32;123990 53760 32;122670 53670 32;122783 54795 32;121590 54908 32;121508 53873 32;120600 54158 32;120908 57143 32;124493 56760;124500 54427;124493 54427 18; 124477 59153;121545 59543 32;121395 58380 32;120360 58463 32;120075 55853 32;119018 55890 32;118935 54923 32;117810 55695 32;116693 56730 32;117083 57105 32;116693 57563 32;116010 57608 32;115658 58748 32;115808 60600;116227 62048;116468 61785 32;117045 61650 32;117330 62992 32;117262 63300 32;117922 63870 32;119415 64335 32;122408 64335 32;124470 63818;124485 59153;124477 59153 18; 100193 40162 1;99727 39803;99480 39630;98970 39330 1;98505 39060;98227 38895;97988 38415 1;97380 36720;96795 35790;97028 34020; 110587 34073 1;110715 34155;110850 34238;111000 34320 1;111630 34703;111998 34815;112672 35115 1;112792 35175;112845 35295;112808 35423 1;112448 36510;112245 37103;112365 38235; 91136 40984;91114 41531 32;91586 41554 32;91601 41006 32;91136 40984 18; 100184 42976;101369 43231 32;101466 42758 32;100274 42503 32;100184 42976 18; 109995 39923;111188 39878 32;111150 39045 32;110310 39082 32;110318 39420 32;109973 39443 32;109995 39923 18; 121193 58320;121365 58298 32;121260 57255 32;121080 57278;121193 58320 18; 116385 44813; 116280 46785 1;114150 46890;113070 46860;111000 47332 1;110190 47520;109792 47610;108990 47798 1;108810 47843;108758 47940;108630 48060; 99750 44610 1;100170 44700;100613 44805;101167 44933 1;101925 45120;102315 45293;102968 45728 1;103433 46035;103553 46125;104025 46193 1;104895 46328;105398 46380;106328 46388 1;108885 46433;110063 45938;112620 45720; 109185 48300 1;109628 48338;109852 48405;110295 48353 1;110393 48345;110520 48315;110505 48210 1;110453 47978;110295 47835;110055 47798 1;109740 47753;109448 47707;109283 47970 1;109208 48090;109110 48158;109155 48278;109185 48300 18; 107280 48968 1;107483 48960;107640 48848;107633 48728 1;107633 48615;107498 48473;107295 48488 33;107100 48495;106905 48653;106913 48765 1;106913 48885;107085 48983;107280 48968 18; 107738 47759;105855 47856; 92348 50498 1;92183 50475;92123 50378;91965 50378 1;91860 50385;91808 50490;91755 50573 1;91590 50828;91598 50992;91470 51255 1;91403 51383;91500 51510;91635 51563 1;91785 51630;91928 51630;92040 51503 1;92250 51255;92265 51060;92348 50738 1;92370 50648;92430 50520;92340 50490;92348 50498 18; 93578 52223 1;93540 52020;93510 51637;93308 51668 1;93038 51713;92910 52170;92850 52425;93578 52223 18; 84203 50093 1;84285 50242;84495 50145;84623 50025 1;85110 49552;85230 49207;85665 48683 1;85755 48578;85710 48383;85575 48330 1;85170 48195;84945 48195;84540 48082 1;84405 48052;84308 48052;84210 48143 1;84045 48300;84000 48405;83843 48563 1;83678 48728;83640 48923;83730 49133 1;83880 49523;84015 49695;84188 50070;84203 50093 18; 83498 48870 33;84225 49193;84660 49365;85793 49650 1;86153 49740;86325 49695;86685 49815 1;87788 50205;88230 50565;89310 51015 1;90113 51360;90585 51653;91110 52193 32;91305 52320 1;91643 52185;91778 52043;92093 51840 1;92242 51750;92235 51600;92235 51412;92190 51270 33;92153 51353;92108 51427;92040 51503 32;91980 51555 33;91883 51630;91755 51623;91635 51563 1;91500 51510;91403 51383;91470 51255 1;91568 51052;91583 50903;91673 50723 32;91710 50610 1;91418 50498;91260 50468;90960 50385 1;89820 50108;89242 49995;88080 49912 1;87968 49905;87878 49830;87885 49718 1;87893 49560;87870 49478;87863 49313 1;87840 49095;87690 48983;87488 48908 1;86940 48720;86655 48668;86108 48518 1;85762 48427;85755 48090;85433 47940 1;85223 47850;85110 48105;84893 48082 1;84690 48068;84578 47993;84398 48060 1;83918 48255;83745 48480;83355 48795;83498 48870 50; 83610 44588 1;84308 44760;84698 44768;85410 44625; 88328 44423 1;87803 43800;87360 43508;87233 42698; 95670 46875 1;95588 46770;95550 46710;95475 46598 1;95438 46568;95400 46545;95363 46553 1;95318 46553;95280 46568;95250 46598 1;95130 46710;95025 46733;94973 46875 1;94943 46943;94935 47003;94935 47048 1;94935 47115;94943 47175;95003 47220 1;95063 47303;95085 47363;95183 47400 1;95288 47460;95378 47430;95498 47370 1;95573 47332;95617 47295;95700 47235 1;95730 47220;95753 47183;95753 47138 1;95760 47108;95745 47070;95738 47018 1;95708 46973;95693 46943;95670 46890;95670 46875 18; 94950 46957 1;94733 47152;94455 47190;94313 47363 1;94253 47445;94313 47745;94403 47783 1;94523 47843;94867 47820;94995 47768 1;95175 47700;95205 47543;95325 47378;94950 46957 18; 102893 47858 1;103012 47895;103020 47978;103028 48037 1;103073 48248;103118 48465;103313 48540 1;103545 48653;103733 48555;103965 48443 1;104167 48360;104295 48420;104520 48480 1;104678 48533;104850 48488;104910 48653 1;104948 48773;104993 48930;105135 48960 1;105278 48998;105323 48945;105443 48848 1;105547 48765;105683 48653;105788 48555; 103755 47100 1;103417 46853;103215 46777;102840 46598 1;102637 46508;102525 46500;102315 46455 1;101873 46380;101633 46500;101212 46628 1;100823 46748;100620 46868;100320 47138 1;100238 47213;100387 47348;100500 47332 1;100643 47318;100718 47250;100860 47280 1;101610 47460;101970 47595;102720 47805 1;103178 47940;103380 47505;103762 47213;103755 47100 18; 105741 43279;105210 46148 32;103665 45893;102345 45045;97583 44123 32;92226 43370 32;89579 43106;88851 42674 32;88282 41889;88302 41376;89016 40811;89808 40565;92475 40692 32;97361 41391;97728 41725;101055 42329 32;105741 43279 18; 98346 43880;98429 43490 32;98031 43422 32;97949 43797 32;98346 43880 18; 97299 43761;97382 43356 32;96969 43281 32;96887 43679 32;97299 43761 18; 95595 34020;95400 35513;95438 36923 32;95460 37560;96165 39255;95850 39795 32;88463 39450 32;88497 37321 32;88508 36630;88538 35287;88628 34755;88755 33998; 92595 37245;93075 37253 32;93098 36405 32;92617 36405 32;92595 37245 18; 96122 41205;96225 40418 32;90075 40125 32;90009 40568 32;92498 40628;96122 41205 18; 93698 39735;93713 40110 32;95895 40125 32;95858 39840;93698 39735 18; 109140 44588 1;109118 44813;109118 44918;109125 45135 1;109125 45293;109245 45412;109403 45412 1;109590 45412;109688 45428;109883 45420 1;109988 45420;110063 45285;110025 45180 1;109950 45008;109860 44948;109733 44813;109140 44588 18; 109140 44753 1;109118 44978;109118 44918;109125 45135 1;109125 45293;109245 45412;109403 45412 1;109590 45412;109688 45428;109883 45420 1;109988 45420;110063 45285;110025 45180 1;109950 45008;109980 45053;109852 44918; 106343 43290 1;106320 43425;106350 43537;106470 43598 1;107220 44025;107580 44295;108390 44618 1;108547 44685;108750 44655;108795 44483 1;108803 44430;108712 44460;108667 44430 1;108345 44258;108165 44198;107828 44055 1;107663 43995;107587 43950;107438 43868;106343 43290 18; 106335 43380 1;106335 43478;106373 43553;106470 43598 1;107220 44025;107580 44295;108390 44618 1;108503 44670;108637 44670;108720 44603; 108488 44633;109148 44948 32;109125 44595 32;108727 44430;108488 44633 18; 109635 46050 1;109792 45863;109935 45803;110190 45743 1;110422 45698;110535 45593;110685 45398 1;110768 45293;110753 45203;110753 45060 1;110745 45015;110685 45037;110640 45023 1;110310 44955;110145 44925;109823 44835;109868 44955 33;109928 45015;109980 45082;110025 45180 1;110063 45285;109988 45420;109883 45420 1;109688 45428;109590 45412;109403 45412 1;109343 45412;109297 45405;109253 45375 32;109020 45435 33;108840 45405;108675 45330;108533 45330 1;108323 45338;108150 45495;107962 45495 1;107587 45503;107190 45398;106695 45398 1;106553 45398;106470 45338;106387 45225 1;106305 45135;106350 45113;106320 44993 1;106283 44873;106005 44873;105983 44993 1;105945 45173;105968 45383;105983 45563 1;105990 45728;106073 45855;106238 45870 1;106538 45915;106688 45923;106995 45945 1;107243 45968;107393 46080;107520 46298 32;107805 46268;109635 46050 18; 110378 45660 1;110903 45638;111165 45593;111698 45540 1;111825 45533;112080 45540;112005 45435 1;111900 45308;111803 45270;111645 45233 1;111262 45150;111083 45060;110700 45023;110378 45660 18; 105225 46245;106380 46313 32;107520 46298 33;107393 46080;107243 45968;106995 45945 1;106688 45923;106538 45915;106238 45870 1;106073 45855;105990 45728;105983 45563 1;105968 45383;105960 45285;105998 45105 1;106020 44985;106283 44985;106320 45105 1;106350 45225;106305 45308;106387 45398 1;106470 45510;106575 45495;106718 45495 1;107212 45495;107587 45503;107962 45495 1;108150 45495;108323 45338;108533 45330 1;108675 45330;108840 45405;109020 45435 32;109110 45412 32;109193 45330 33;109148 45277;109125 45210;109125 45135 1;109118 45068;109140 44993;109140 44940 32;108698 44693 32;108593 44655 32;108525 44655 33;108480 44655;108428 44640;108390 44618 1;107633 44325;107273 44070;106620 43688 32;106575 43643 33;106283 43755;106087 43800;105825 43920 32;105675 43950 32;105180 46193;105225 46245 18; 110475 43515 1;110348 43980;110303 44265;110205 44730 1;110160 44925;110393 45000;110587 45045 1;110775 45098;110873 45120;111075 45120 1;111218 45120;111405 45248;111435 45090 1;111503 44662;111585 44332;111683 43860;110475 43515 18; 109148 43770; 106815 41858;106417 41910 33;106515 42120;106590 42225;106733 42420 1;106883 42660;106995 42690;107273 42735 1;107498 42780;107678 42457;107917 42488 1;108172 42533;108195 42930;108458 42893 1;108660 42870;108735 42848;108833 42818 32;109058 42750 1;109223 42698;109223 42525;109238 42345 1;109245 42113;109058 42023;108870 41880;106815 41858 18; 103305 42098 33;103583 42412;104280 42615;104955 42720 1;104977 42728;105000 42735;105023 42735 32;105180 42683 1;104948 42308;104723 42203;104430 41873;103305 42098 50; 105458 41880 1;105578 42180;105533 42375;105720 42638 1;105803 42773;105900 42832;106058 42840 1;106462 42870;106635 43050;107025 43193 1;107625 43433;107873 43703;108458 43980 1;108615 44063;108840 43868;108885 43695 1;108975 43298;108990 43095;109118 42698 1;109163 42548;108878 42660;108727 42630 1;107783 42465;107348 42233;106455 41918;105458 41880 18; 109448 41498;109477 40680 32;107775 40620 32;107730 41588;109448 41498 18; 97508 40680 1;97433 40418;97395 40283;97297 40020 1;97230 39863;97020 39900;96870 39983 1;96698 40080;96428 40275;96593 40373;97508 40680 18; 99803 41287 1;99998 41310;100087 41363;100290 41385 1;101340 41535;101843 41820;102915 41835 1;103035 41843;103238 41835;103200 41715 1;103073 41332;102795 41018;102510 40793 1;102315 40658;102128 40673;101948 40620 1;101355 40440;100800 40590;100073 40418 1;99863 40373;99750 40410;99540 40388 1;99180 40358;99023 40223;98670 40223 1;98363 40223;98325 40545;98167 40800;99803 41287 18; 106852 41565 1;106845 41310;106778 41183;106665 40950 1;106613 40853;106590 40748;106485 40740 1;106290 40733;106200 40725;106012 40725 1;105765 40725;105630 40635;105413 40718 1;105308 40763;105285 40845;105270 40950 1;105240 41152;105255 41258;105248 41460;106852 41565 18; 106635 41543;107797 41550 32;107843 40650 32;106485 40703;106635 41543 18; 102278 40650;103148 41760 32;104348 41460 32;105278 41445 32;105390 40718 32;103215 40740 32;102683 40703;102278 40650 18; 103598 42698 1;103628 42615;103620 42563;103650 42465 1;103695 42308;103485 42270;103335 42203 1;103012 42068;102803 42105;102465 42158 1;102240 42195;102135 42255;101925 42308;103598 42698 18; 117540 45810 1;114900 45548;113212 45443;110587 44902 1;109238 44633;107730 43890;107063 43560 1;106095 43065;106170 42923;104955 42720 1;104220 42600;103455 42368;103245 42000; 119198 43943;124530 43718; 103035 45832 1;103538 45908;103913 45968;104550 45975 1;105608 45990;106125 45803;107167 45555 1;108075 45345;108518 45000;109470 45015 1;110520 45030;110985 44580;112005 44273 1;112890 44010;113318 43815;114210 43553 1;115095 43290;115568 43170;116505 43155 1;117450 43140;117930 43125;118868 42930 1;119850 42743;120315 42533;121245 42135 1;122310 41670;123068 41543;123690 40553 1;124073 39938;124530 39645;124545 38925; 124538 38925 1;124530 38310;124462 37845;123968 37613 1;123225 37290;122685 37365;122145 36773 1;121575 36165;121545 35670;121245 34890 1;121118 34590;121042 34328;120938 34095; 110880 45870 1;111315 45638;111690 45428;112305 45233 1;113955 44700;114788 44370;116505 44093 1;117780 43890;118440 43905;119685 43553 1;120750 43260;121260 43013;122288 42593 1;123248 42218;123818 41933;124538 41453; 124545 37350 1;124365 37268;124170 37178;123945 37050 1;123210 36660;122580 36503;122348 35693 1;122198 35183;122190 34912;121988 34410 1;121935 34298;121883 34193;121830 34095; 113865 46080 1;114053 45975;114248 45855;114495 45705 1;115215 45285;115590 45000;116415 44820 1;117465 44603;118020 44550;119100 44423 1;121328 44160;122288 43695;124448 42795;124538 42773; 124545 36488 1;123570 36015;122925 35468;122648 34350 1;122618 34260;122587 34178;122558 34103; 74145 38430 1;73950 38055;73710 37950;73350 37725 1;73073 37568;72870 37613;72563 37650 1;72315 37688;72135 37478;72128 37230 1;72105 37058;72053 37043;71813 37035 1;71655 37035;71588 37140;71512 37268 1;71445 37373;71378 37463;71265 37433 1;71078 37388;70980 37395;70808 37335 1;70665 37298;70305 37268;70283 37628 1;70260 37950;70538 38078;70703 38070;74145 38430 18; 70770 38093;70538 37148 32;69503 37890;70770 38093 18; 70335 37748 1;70313 37703;70298 37658;70305 37605 1;70313 37463;70560 37193;70808 37335 1;70965 37418;71078 37388;71265 37433 1;71378 37463;71445 37373;71512 37268 1;71588 37140;71655 37035;71813 37035 1;72053 37043;72105 37058;72128 37230 1;72135 37478;72315 37688;72563 37650 1;72870 37613;73073 37568;73350 37725 1;73665 37928;73890 38033;74078 38318; 74310 33945 1;73943 34118;73718 34238;73395 34530 1;72367 35460;71655 36075;70838 36750 1;70748 36825;70650 36900;70538 36983;70448 37125;70560 37320 32;70688 37335 33;70725 37328;70762 37328;70808 37335 1;70980 37395;71078 37388;71265 37433 1;71378 37463;71445 37373;71512 37268 1;71588 37140;71655 37035;71813 37035 1;72053 37043;72105 37058;72128 37230 1;72135 37478;72315 37688;72563 37650 1;72870 37613;73073 37568;73350 37725 1;73643 37912;73860 38018;74040 38265 32;74175 38483 32;76815 38385 32;76913 38108 33;77033 37515;77288 37140;77430 36443 1;77610 35483;77790 34740;77850 33960;74310 33960;74310 33945 18; 77558 35498 1;77738 35527;77835 35535;78023 35573 1;78158 35603;78315 35603;78420 35498 1;78563 35355;78600 35198;78570 34988 1;78503 34628;78593 34545;78450 34200 1;78413 34118;78233 34103;78143 34095 1;78023 34095;77948 34103;77835 34103;77558 35498 18; 79913 33975 1;79875 34027;79838 34080;79800 34140 1;79718 34260;79545 34230;79448 34133 1;79328 34035;79238 34020;79133 33968;77820 33968;77813 34125 1;78045 34118;78038 34103;78120 34103 1;78210 34110;78338 34155;78375 34238 1;78518 34582;78503 34628;78570 34988 1;78600 35198;78548 35340;78405 35483 1;78323 35565;78233 35580;78135 35565 32;77918 35543 32;77483 35408 1;77423 35408;77385 35415;77355 35453;77355 35828 1;77963 35888;78262 35948;78878 35978 1;79088 35993;79215 35970;79410 36060 1;79545 36128;79635 36203;79658 36353 1;79695 36705;79725 36878;79740 37223 1;79740 37380;79852 37568;80003 37508 1;80205 37440;80273 37305;80378 37110 1;80505 36863;80565 36735;80685 36473 1;80753 36308;80865 36263;81015 36158 1;81218 36015;81330 35963;81548 35828 1;81690 35745;81893 35707;81968 35858 1;82043 36015;82005 36113;82088 36263 1;82125 36360;82193 36510;82290 36465 1;82425 36412;82583 36270;82583 36113 1;82583 35902;82500 35805;82418 35603 1;82313 35370;82230 35250;82245 34988 1;82245 34920;82193 34868;82125 34860 1;81727 34838;81525 34793;81135 34777 1;80985 34777;80865 34815;80723 34793;80573 34763 33;80400 34680;80348 34673;80250 34598 1;80130 34523;80145 34245;80242 34140 1;80280 34095;80310 34043;80340 33998 32;80288 33975;79913 33975 18; 81360 34770 1;81352 34808;81383 34838;81420 34838 1;81600 34868;81698 34883;81893 34868 1;81960 34868;81983 34823;82035 34770 1;82117 34688;82125 34568;82058 34463 1;81938 34298;81727 34373;81548 34463 1;81413 34537;81390 34650;81383 34800;81360 34770 18; 83250 35798 1;83400 35933;83595 35963;83670 35873 1;83745 35790;83693 35603;83543 35468 33;83393 35340;83198 35303;83130 35393 1;83048 35483;83100 35670;83250 35798 18; 83063 33983 1;83108 34058;83145 34140;83168 34253 1;83168 34298;83175 34373;83220 34350;83258 34365 1;83363 34305;83468 34275;83573 34343 1;83633 34388;83708 34425;83760 34365 1;83828 34290;83805 34185;83738 34103 1;83693 34058;83648 34013;83602 33975;83055 33983;83063 33983 18; 81765 33983;81908 34380 1;81968 34388;82012 34418;82058 34463 1;82125 34568;82117 34688;82035 34770 32;81953 34853 33;82005 34860;82065 34860;82125 34860 32;82230 34890 1;82275 34725;82395 34688;82530 34575 1;82658 34470;82762 34455;82935 34470 1;83108 34493;83063 34703;83175 34838 1;83235 34920;83333 34912;83423 34860 1;83528 34793;83355 34673;83265 34575 1;83205 34515;83168 34448;83213 34373;83258 34365 32;83220 34350 1;83175 34373;83168 34298;83168 34253 1;83145 34140;83108 34058;83070 33975;81765 33983 18; 83625 36870 1;83588 36968;83483 37043;83573 37088 1;83753 37200;83910 37260;84090 37373 1;84180 37440;84315 37455;84398 37373 1;84540 37238;84488 37095;84593 36915 1;84713 36705;84840 36608;85080 36548 1;85275 36510;85463 36540;85538 36345 1;85590 36195;85628 36113;85650 35948 1;85658 35850;85628 35775;85553 35715 1;85477 35670;85425 35670;85343 35685 1;84990 35768;84803 35843;84450 35783 1;84308 35760;84165 35730;84098 35850 1;83873 36233;83783 36435;83625 36840;83625 36870 18; 83520 37035 1;83453 37185;83415 37268;83378 37418 1;83348 37515;83430 37643;83528 37628 1;83760 37598;83895 37620;84113 37515 1;84173 37485;84240 37463;84300 37410;83520 37035 18; 84367 37365 1;84863 37283;85268 37343;85560 36923 1;85643 36803;85650 36683;85583 36540 1;85538 36473;85463 36473;85387 36473 1;85095 36495;84915 36510;84705 36698 1;84495 36885;84465 37050;84367 37298;84367 37365 18; 81278 35955 1;81383 36480;81518 36713;81727 37200 1;81773 37320;81900 37373;82012 37313 1;82253 37193;82365 37118;82598 36960 1;82718 36878;82830 36863;82965 36923 1;83168 37020;83408 37043;83617 37133 1;83640 37148;83595 37215;83648 37035 1;83753 36503;83963 36428;84113 35895 1;84120 35850;84090 35828;84075 35783 1;83963 35580;83880 35385;83648 35385 1;83512 35385;83445 35408;83318 35408 1;83280 35408;83235 35400;83242 35363 1;83280 35168;83295 35055;83242 34860 1;83175 34658;83137 34478;82935 34418 1;82658 34350;82477 34493;82290 34695;81278 35955 18; 88395 35685 1;88170 35730;88050 35895;88035 36113 1;87998 36450;87983 36615;87960 36945 1;87945 37103;88012 37193;88125 37290 1;88215 37380;88283 37388;88395 37440;88395 35685 18; 88931 37484;89374 37484 32;89374 36689 32;88931 36689 32;88931 37484 18; 89900 36396;90252 35946 32;89712 35533 32;89367 35983 32;89900 36396 18; 84262 37410 1;84675 37658;84908 37740;85365 37912 1;85477 37957;85613 37928;85650 37808 1;85748 37463;85778 37275;85845 36915 1;85852 36840;85740 36758;85680 36803;85598 36848 33;85590 36870;85575 36900;85560 36923 1;85275 37328;84893 37290;84420 37358 32;84352 37320;84262 37410 18; 83617 33983;83738 34103 1;83805 34185;83828 34290;83760 34365 1;83708 34425;83633 34388;83573 34343 1;83468 34275;83363 34305;83258 34365;83220 34350 32;83190 34448 33;83190 34493;83220 34537;83265 34575 1;83355 34673;83528 34793;83423 34860 1;83378 34890;83340 34898;83303 34905 32;83242 34860 33;83288 35040;83280 35145;83250 35303 32;83220 35355 33;83273 35348;83340 35363;83408 35385 32;83445 35400 33;83505 35400;83565 35385;83648 35385 1;83880 35385;83963 35580;84075 35783 32;84098 35850 33;84165 35730;84308 35760;84450 35783 1;84803 35843;84990 35768;85343 35685 1;85425 35670;85477 35670;85553 35715 1;85628 35775;85658 35850;85650 35948 1;85628 36113;85590 36195;85538 36345 1;85523 36383;85508 36405;85492 36428 32;85455 36480 33;85500 36480;85545 36495;85583 36540 1;85613 36623;85628 36690;85620 36758 32;85770 36780 1;85943 36638;86048 36510;86137 36353 32;86288 36075 1;86325 36008;86280 35933;86220 35880 1;85838 35573;85673 35378;85410 34965 1;85343 34868;85485 34733;85605 34748 1;86003 34815;86175 35033;86565 35168 1;86678 35213;86790 35040;86813 34912 1;86835 34770;86730 34635;86850 34537 1;86925 34478;87000 34425;87068 34380;86678 33998;83617 33983 18; 88193 33998 1;88185 34058;88178 34118;88163 34193 1;88133 34328;88133 34448;88245 34523 1;88358 34605;88425 34628;88545 34703;88665 33998;88193 33998 18; 86303 36000 1;86400 36090;86558 36150;86625 36037 1;86790 35753;86768 35662;86880 35340 1;86918 35228;86783 35220;86625 35152;86565 35108 33;86175 34973;86003 34815;85605 34748 1;85485 34733;85343 34868;85410 34965 1;85673 35378;85838 35573;86220 35880 1;86235 35902;86250 35918;86265 35940 32;86303 36000 18; 86498 33998;86933 34455 1;87030 34402;87203 34313;87255 34207;87285 34125 33;87285 34080;87285 34035;87270 33990;86498 33998 18; 87210 33998 1;87293 34050;87428 34073;87473 33990;87210 33998 18; 87465 34005;87345 34043 32;87285 34043 33;87285 34073;87285 34103;87285 34125 32;87255 34207 1;87218 34283;87165 34313;87105 34335 32;87023 34410 33;86970 34448;86910 34493;86850 34537 1;86730 34635;86835 34770;86813 34912 1;86798 34988;86760 35063;86708 35115 32;86625 35152 33;86783 35220;86918 35228;86880 35340 1;86768 35662;86790 35753;86625 36037 1;86558 36150;86400 36090;86303 36000 32;86288 36075 32;86137 36353 33;86048 36510;85943 36638;85770 36780 33;85793 36810;85845 36870;85845 36915 1;85838 36938;85838 36960;85830 36975 32;85823 37020 33;85770 37320;85733 37500;85650 37808 1;85613 37928;85477 37957;85365 37912 1;84930 37755;84698 37673;84323 37455 32;84240 37455 33;84195 37478;84150 37500;84113 37515 1;84098 37523;84090 37530;84075 37530 32;84023 37553 33;83850 37620;83723 37605;83528 37628 1;83430 37643;83393 37508;83423 37410 1;83445 37313;83468 37253;83498 37178 32;83505 37095 33;83325 37035;83130 37005;82965 36923 1;82920 36908;82890 36900;82852 36893 32;82800 36870 32;82290 36465 33;82193 36510;82125 36360;82088 36263 1;82005 36113;82043 36015;81968 35858 1;81893 35707;81690 35745;81548 35828 1;81330 35963;81218 36015;81015 36158 1;80865 36263;80753 36308;80685 36473 1;80565 36735;80505 36863;80378 37110 1;80273 37305;80205 37440;80003 37508 1;79852 37568;79740 37380;79740 37223 1;79725 36878;79695 36705;79658 36353 1;79635 36203;79545 36128;79410 36060 1;79215 35970;79088 35993;78878 35978 1;78360 35955;78068 35910;77625 35858 32;77550 35858 32;77483 36120 32;76898 38453 32;85230 39015;85650 38378 32;86025 37695 32;86288 37200;87180 37365;88395 37440 33;88283 37388;88215 37380;88125 37290 1;88095 37275;88073 37253;88058 37230 32;88020 37193 33;87975 37125;87945 37050;87960 36945 1;87983 36615;87998 36450;88035 36113 1;88043 35918;88148 35760;88343 35700 32;88433 35662 32;88598 34695 32;88448 34650 33;88380 34613;88320 34582;88245 34523 1;88133 34448;88133 34328;88163 34193 1;88178 34118;88185 34050;88193 33990;87450 33998;87465 34005 18; 82245 36488;82800 36900 32;82875 36900 33;82905 36900;82935 36915;82965 36923 1;83115 36998;83295 37035;83460 37080 32;83535 37043 33;83535 36998;83595 36938;83625 36870;83625 36840 1;83783 36435;83873 36233;84098 35850 32;84075 35783 33;83963 35580;83880 35385;83648 35385 1;83573 35385;83520 35393;83468 35400 32;83543 35468 33;83693 35603;83745 35790;83670 35873 1;83602 35955;83453 35940;83318 35858 32;83250 35798 33;83100 35670;83048 35483;83130 35393 1;83145 35370;83175 35355;83213 35348 32;83250 35303 33;83280 35145;83288 35040;83242 34860 32;83175 34838 33;83063 34703;83108 34493;82935 34470 1;82762 34455;82658 34470;82530 34575 1;82395 34688;82275 34725;82230 34890 32;82245 34988 33;82230 35250;82313 35370;82418 35603 1;82500 35805;82583 35902;82583 36113 1;82583 36270;82425 36412;82290 36465;82245 36488 18; 69352 34590 1;69608 34628;69758 34748;69998 34635 1;70110 34582;70133 34463;70110 34328 1;70073 34178;69990 34095;69848 34035 1;69750 34005;69690 33975;69600 33998 1;69488 34035;69428 34058;69330 34088;69352 34590 18; 69233 37740 1;69285 37710;69345 37673;69413 37635 1;69525 37568;69727 37395;69600 37350;69233 37290;69240 37740;69233 37740 18; 69248 36248;69555 36713 32;69975 37373 32;70208 37230 33;70448 37058;70673 36885;70838 36750 1;71655 36075;72367 35460;73395 34530 1;73718 34238;73943 34125;74295 33953;69255 33938;69248 36248 18; 82305 36465 33;82470 36375;82583 36248;82583 36113 1;82583 35902;82500 35805;82418 35603 1;82313 35370;82230 35250;82245 34988 1;82245 34965;82230 34935;82215 34912;82253 34823 1;82313 34718;82410 34673;82530 34575 1;82658 34470;82762 34455;82935 34470 1;83108 34493;83063 34703;83175 34838 1;83183 34860;83198 34868;83265 34973 1;83288 35108;83265 35213;83213 35348 1;83175 35355;83145 35370;83130 35393 1;83048 35483;83100 35670;83250 35798;83318 35858 1;83453 35940;83602 35955;83670 35873 1;83745 35790;83693 35603;83543 35468;83468 35400 1;83520 35393;83573 35385;83648 35385 1;83880 35385;83963 35580;84075 35783;84098 35850 33;83873 36233;83783 36435;83625 36840;83625 36870 1;83595 36938;83535 36998;83535 37043; 80123 33975;80235 34118 1;80378 33953;80558 33960;80783 33990 1;80977 34027;81000 34200;81135 34343 1;81218 34448;81367 34380;81465 34283 1;81548 34207;81668 34103;81585 34013;81518 33968;80123 33975 18; 81893 34373;81608 34095 33;81600 34162;81518 34230;81465 34283 1;81367 34380;81218 34448;81135 34343 1;81000 34200;80977 34027;80783 33990 1;80558 33960;80378 33953;80235 34118 32;80213 34178 33;80137 34298;80137 34530;80250 34598 1;80348 34673;80400 34680;80573 34763 32;80723 34793 1;80865 34815;80985 34777;81135 34777 1;81210 34785;81293 34808;81390 34777 33;81398 34650;81420 34537;81548 34463 1;81630 34425;81727 34380;81810 34373;81893 34373 18; 79125 33968 1;79238 34020;79328 34035;79448 34133 1;79545 34230;79718 34260;79800 34140 1;79838 34080;79875 34027;79913 33968;79117 33975;79125 33968 18; 81540 33990;81585 34013 1;81608 34043;81615 34065;81608 34095 32;81645 34133 32;81893 34373 32;81938 34388;81855 33975;81540 33983;81540 33990 18; 95670 46875 1;95588 46770;95550 46710;95475 46598 1;95438 46568;95400 46545;95363 46553 1;95318 46553;95280 46568;95250 46598 1;95130 46710;95025 46733;94973 46875 1;94943 46943;94935 47003;94935 47048 1;94935 47115;94943 47175;95003 47220 1;95063 47303;95085 47363;95183 47400 1;95288 47460;95378 47430;95498 47370 1;95573 47332;95617 47295;95700 47235 1;95730 47220;95753 47183;95753 47138 1;95760 47108;95745 47070;95738 47018 1;95708 46973;95693 46943;95670 46890; 99667 53460;99720 52590 32;99000 52523 32;98977 52650 32;98977 52808 1;98955 52965;98933 53108;98910 53302;98835 53378 32;98933 53460;99667 53460 18; 100313 75218 1;100283 74850;100215 74670;100170 74295 1;100080 73658;101325 72360;101813 72060;107265 71753 32;108390 67440; 101520 67823; 103268 65910; 97365 37207; 96413 35798; 113805 35933; 115455 35190; 115335 36938; 116745 38303; 118477 35010; 118868 76673 1;118118 75915;117443 75473;117480 74408 1;117503 73643;117818 73238;118387 72713 1;119093 72060;119415 71588;120337 71288; 106417 70725 1;106515 70793;106628 70830;106718 70748 1;106868 70620;106710 70433;106620 70253 1;106373 69795;106605 69473;106553 68955 1;106523 68730;106477 68602;106320 68430 1;105998 68108;105840 67950;105540 67605 1;105375 67425;105075 67688;105023 67920 1;104940 68273;104985 68475;105113 68805 1;105285 69278;105383 69503;105608 69945 1;105803 70350;106050 70455;106410 70725;106417 70725 18; 108352 67560;108398 67463 32;110212 65933 32;110265 65843 1;110183 65783;110160 65730;110078 65670 1;110025 65640;109973 65602;109935 65640 1;109733 65828;109628 65918;109433 66098 1;108975 66525;108683 66653;108143 66945 1;108015 67020;107917 67170;108015 67275 1;108105 67380;108165 67418;108270 67508;108352 67560 18; 99090 70635 1;99780 70628;100148 70598;100815 70380 1;100973 70328;101063 70238;101108 70065 1;101243 69525;101438 69262;101445 68700 1;101445 68408;101363 68250;101205 67995 1;101123 67875;101070 67815;101063 67665 1;101055 67575;100792 67553;100830 67635 1;100898 67830;100883 67943;100958 68130 1;101025 68318;101137 68445;101040 68610 1;100883 68873;100800 69038;100530 69173 1;100238 69323;100058 69345;99743 69330 1;99450 69323;99300 69315;99015 69293;99090 70635 18; 98880 67477 1;99255 67553;99578 67658;99848 67373 1;100042 67163;100155 67073;100358 66863 1;100455 66758;100688 66810;100718 66953 1;100770 67215;100718 67365;100815 67605 1;100837 67688;100980 67680;101025 67605 1;101153 67403;101355 67178;101565 67313 1;101843 67508;102053 67605;102383 67515 1;102608 67455;102743 67313;102765 67073 1;102788 66803;102727 66630;102555 66413 1;102405 66240;102405 66090;102420 65858 1;102428 65625;102578 65453;102818 65415 1;103268 65363;103665 65340;103920 65723 1;104093 66000;104250 66120;104310 66435 1;104348 66660;104438 66915;104663 66878 1;104880 66848;105030 66720;105075 66495 1;105120 66210;105352 65985;105645 66030 1;106012 66098;106410 66120;106553 65768 1;106703 65393;106725 64957;106373 64762 1;105262 64133;104670 63608;103613 62970 1;103350 62813;103110 62828;103178 62528 1;103365 61695;103395 61740;103590 60900 1;103605 60832;103568 60750;103500 60750 1;103260 60750;103148 60690;102915 60690 1;102840 60690;102795 60735;102773 60795 1;102637 61148;102547 61418;102195 61515 1;100808 61912;100163 62242;98775 62603 1;98573 62655;98348 62662;98288 62468 1;97995 61515;97800 61170;97305 60720 1;97245 60668;97173 60451;97098 60451 1;96896 60458;96861 60334;96831 60521 1;96741 60994;96870 61043;97073 61470 1;97395 62175;97417 62595;97477 63367;97568 63383 33;97703 63360;97845 63375;98010 63427 1;98153 63495;98205 63600;98265 63742 32;98265 63795 1;98355 64095;98385 64245;98490 64537 1;98558 64755;98498 64973;98303 65063 1;98235 65093;98205 65160;98235 65220 1;98422 65685;98580 65887;98783 66345;98880 67477 18; 102893 60750;100808 60826 32;98828 60262 32;97425 60255 32;98212 62775 32;98633 62887 32;102390 61733 32;102855 61020;102893 60750 18; 98903 67500 1;99225 67553;99428 67620;99727 67477 1;99915 67395;100005 67260;100028 67043 1;100080 66518;99900 66255;99818 65723 1;99705 65033;99608 64635;99150 64095 1;98940 63863;98715 63795;98408 63832 1;98115 63878;97958 63855;97688 63960;97733 64117 32;98828 66390;98850 67117 32;98903 67500 18; 104865 66503;108323 67425 32;109028 64950 32;105900 62873 32;106433 61260 32;105150 61373 32;104633 61207 32;103500 60795 32;102690 63427 32;104917 64852;104865 66503 18; 105150 66270 1;105120 66413;105083 66593;105225 66615 1;106448 66878;107047 67065;108248 67418 1;108300 67440;108360 67410;108383 67350 1;108615 66435;108720 65977;109005 65070 1;109028 64995;109028 64920;108960 64867 1;107790 64088;107235 63653;106020 62955 1;105953 62925;105885 62873;105915 62798 1;106095 62280;106163 62010;106387 61500 1;106417 61418;106305 61358;106215 61350 1;105780 61335;105555 61433;105128 61350 1;104925 61320;104820 61298;104633 61223 1;104175 61065;103995 60840;103523 60773 1;103440 60765;103350 60765;103343 60840 1;103200 61658;103283 61485;103110 62287 1;103020 62677;103403 62903;103748 63105 1;104318 63457;104738 63720;105420 64155 1;105637 64290;105637 64793;105547 65033 1;105352 65520;105300 65775;105150 66270 18; 90503 50235;92340 50512; 103223 53100 1;102600 53100;102292 53123;101678 53175;101633 53175 33;101633 53205;101625 53235;101625 53265 1;101528 53707;101445 53963;101303 54293 32;101265 54323 1;102248 54240;102735 54165;103710 53992;103223 53100 18; 113940 41393;113933 41850 32;114735 41850 32;114743 41400 32;113940 41393 18; 118080 37358; 116430 41678;116512 41618 32;118500 40335 32;118462 39060 32;118433 38865 1;118028 38633;117803 38438;117667 37995 1;117375 37073;117540 36480;117922 35588;117983 35513 33;117818 35332;117630 35213;117375 35040 1;117105 34875;116903 34838;116655 34755 32;116385 34770 33;115643 35573;115403 36008;114825 36930 1;114623 37253;114547 37613;114533 37995 32;114533 38183 32;114585 38393 1;114968 38385;115148 38543;115493 38723 1;116055 39030;116430 39390;116430 40027 1;116422 40695;116363 41018;116318 41678;116430 41678 18; 114727 37230 1;114480 37200;114337 37253;114120 37358 1;113775 37530;113783 37853;113745 38220 1;113738 38280;113805 38287;113865 38280 1;114105 38273;114225 38348;114473 38370;114727 37230 18; 115943 35348 1;115268 35348;114930 35565;114352 35895 1;114210 35978;114233 36150;114308 36285 1;114473 36600;114570 36750;114788 37027;115943 35348 18; 111968 34073;112058 34148 1;112440 34388;112658 34455;113063 34665 1;113167 34725;113363 34703;113363 34575 1;113363 34395;113378 34215;113318 34073;111968 34073 18; 124545 37508;122010 38648 32;118598 37140 32;118508 40358 32;116333 41738;116265 42375;115868 42353 32;115837 41738 32;114420 40470 32;114075 38340 32;109523 38348 32;109448 43155 32;112613 44033 32;119220 43943 32;124530 43695;124553 37508;124545 37508 18; 118823 34080 1;118920 34230;119003 34298;119153 34410 1;119265 34508;119445 34485;119528 34350 1;119587 34245;119640 34170;119685 34088;118815 34095;118823 34080 18; 120653 34103 1;120750 34193;120848 34148;120953 34095;120667 34103;120653 34103 18; 123465 34110 1;123585 34170;123712 34253;123848 34350 1;123990 34485;124238 34568;124433 34500 1;124477 34485;124523 34478;124560 34470;124568 34110 32;123465 34110 18; 119288 35280;119813 34095; 114990 34073 1;115417 34238;115875 34418;116415 34650 1;116580 34733;116723 34777;116858 34815 32;116903 34658 1;117045 34433;117158 34253;117262 34080;114990 34088;114990 34073 18; 97598 39953 32;99398 40185 1;99653 40223;99893 40245;100118 40275 32;100193 40162 33;99727 39803;99480 39630;98970 39330 1;98565 39098;98303 38948;98085 38595 32;98003 38483 1;97875 39045;97725 39308;97508 39832;97598 39953 50; 96098 36323;96158 36930 32;97223 36323 33;97065 35783;96975 35250;96998 34605 32;96188 34793;96098 36323 18; 97028 34027 1;96795 35790;97380 36728;97988 38415 1;98227 38895;98505 39060;98970 39330 1;99480 39630;99727 39803;100193 40162;102675 40402;102750 40733;103163 40748;106050 40763;106500 40740;109470 40553;109530 38370;112365 38235 1;112245 37103;112448 36510;112808 35423 1;112845 35295;112792 35175;112672 35115 1;111998 34815;111630 34703;111000 34320 1;110843 34230;110700 34148;110565 34065;97020 34027;97028 34027 18;107258 40433 1;107512 40260;107640 40148;107970 40148 1;108255 40148;108473 40207;108645 40440;107258 40433 18; 96765 34020 1;96637 35100;96743 36075;97140 37268 1;97455 38243;97665 38655;98348 39270; 88755 34005;88628 34755 32;88538 35287;88508 36630 32;88463 39450 32;95850 39795 32;96165 39255;95460 37560;95438 36923 32;95400 35513;95595 34020;88755 34005 18; 117540 74992 1;117637 74873;117533 74393;117630 74265 1;119033 72435;120172 72390;122137 71633 1;123060 71288;123705 71033;124448 70613; 120113 71183 1;121343 70762;122333 71168;123225 70193; 108510 40305 33;108360 40185;108188 40148;107970 40148 1;107715 40148;107580 40215;107415 40328; 96930 44738 1;97740 44655;98258 44745;99165 44655 1;100215 44550;100755 44483;101805 44595 1;102810 44715;103320 44865;104325 44790 1;105248 44745;105727 44670;106590 44310 1;107595 43890;108180 43875;109245 43575 1;110227 43313;110775 43402;111750 43073 1;112868 42705;113445 42563;114570 42135 1;115440 41805;115890 41610;116828 41535 1;118028 41430;118620 41190;119828 41190 1;120885 41190;121417 40823;122310 40230 1;122708 39975;122940 39803;123165 39375 1;123375 38985;123337 38460;122925 38273 1;122288 37995;121980 37815;121305 37613 1;120915 37500;120727 37260;120630 36870 1;120435 36210;120450 35850;120210 35213 1;120128 35010;120038 34853;119933 34710; 90105 44213 1;90345 44145;90593 44055;90885 43935 1;91688 43605;92025 43260;92887 43095 1;93727 42930;94170 42983;95025 42953 1;96578 42915;97358 42953;98910 42810 1;100350 42690;101085 42623;102510 42293 1;103515 42060;104010 41790;105045 41730 1;106665 41655;107475 41655;109110 41550 1;109898 41513;110205 41085;110925 40733 1;111765 40335;112320 40313;113047 39713 1;113310 39503;113408 39315;113468 38970 1;113618 38055;113468 37523;113850 36652 1;114158 35940;114345 35580;114870 34995 1;114915 34935;114990 34943;115065 34890 1;115245 34770;115050 34380;114825 34395 1;114345 34425;114098 34380;113625 34350 1;113265 34335;113033 34230;112800 34073; 92753 44152 1;93060 44078;93383 44033;93810 44010 1;94598 43995;94988 43853;95790 43853 1;97515 43853;98385 44010;100125 43995 1;101400 43980;102000 43575;103260 43343 1;104505 43125;105105 42825;106380 42743 1;107648 42675;108300 42900;109575 42743 1;110160 42675;110408 42450;110977 42263 1;111675 42045;112140 42210;112778 41820 1;113288 41513;113528 41325;113977 40905 1;114435 40485;114908 40635;115538 40703 1;116348 40800;116768 40853;117578 40703 1;118230 40582;118635 40260;118860 39623 1;119145 38745;119205 38273;119378 37343 1;119475 36743;119610 36285;119205 35813 1;119137 35745;119070 35678;119010 35618; 83528 41888 1;83828 41603;84270 41430;84727 41152 1;85380 40763;85620 40425;86295 40065 1;87248 39570;87900 39773;88980 39705 1;90458 39623;91215 39713;92700 39465 1;94005 39240;94643 39218;95820 38588 1;96518 38228;95715 37493;95753 36698 1;95798 35662;95760 35513;95865 34545 1;95887 34328;95903 34155;95910 34020; 81375 40148 1;81495 40027;81630 39893;81788 39750 1;82800 38820;83438 38475;84690 37875 1;85800 37343;86445 37170;87390 36353 1;88155 35693;88875 35332;88905 34313 1;88905 34200;88898 34095;88890 33998; 76305 38633 1;76508 38363;76785 38123;77108 37815 1;77760 37185;78225 37005;78810 36293 1;79320 35662;79575 35213;79568 34395 1;79560 34245;79553 34103;79538 33968; 103898 66593 1;104055 66788;104078 66818;104295 67065 1;104685 67545;105120 67605;105735 67620 1;106560 67665;106935 67283;107655 66863 1;108285 66495;108630 66195;109365 66195 1;110400 66195;110865 66660;111765 67170 1;112350 67515;112920 68003;113408 67530 1;113925 67043;113828 66443;113648 65730 1;113325 64590;112875 64110;112208 63135 1;111375 61943;110340 62025;109065 61313 1;107520 60465;106650 60165;105330 58995 1;105053 58762;104805 58530;104542 58328; 69173 56070 1;69390 56018;69623 55973;69908 55935 1;70538 55860;70845 55680;71490 55710 1;72120 55755;72420 55920;73050 56070 1;73830 56273;74175 56490;74925 56790 1;75870 57180;76545 57953;76305 58935 1;76058 59955;75885 60443;75645 61455 1;75458 62220;75518 62685;75810 63412 1;75960 63802;75990 64125;76365 64290 1;76995 64590;77355 64673;78045 64830 1;78735 65010;79230 65040;79665 65610 1;79958 66000;80160 66233;80625 66352 1;81030 66465;81225 66585;81645 66555 1;82238 66525;82568 66330;82988 65895 1;83460 65393;83918 65363;84608 65310 1;85005 65295;85215 65190;85628 65213 1;86205 65250;86475 65423;87045 65550 1;87810 65745;88215 65805;89010 65775 1;90120 65745;90668 65543;91770 65310 1;92887 65085;93518 65175;94590 64733 1;95280 64455;95730 64530;96345 64110 1;96908 63735;97221 63426;97596 62849 1;98106 62046;98370 61662;98865 60845 1;99255 60207;98985 59393;98408 58912 1;97665 58320;97200 58162;96390 57675 1;95475 57143;95115 56730;94185 56250 1;93135 55733;92550 55590;91568 54953 1;90795 54465;90405 54203;89730 53595 1;88770 52740;88148 52485;87270 51555 1;86730 50992;86580 50625;86145 49973 1;85755 49395;85530 49110;85028 48630 1;84308 47963;83970 47565;83565 46673 1;83198 45885;83190 45390;83190 44513 1;83190 43838;83078 43470;83123 42848; 88515 77033 1;88898 77250;89198 77430;89730 77430 1;90240 77445;90555 77235;90870 76815 1;91148 76433;91440 76253;91448 75773 1;91448 75555;91538 75443;91530 75210 1;91500 74775;91245 74580;91290 74130 1;91328 73590;91358 73305;91605 72810 1;92145 71745;92708 71123;93885 70830 1;94380 70710;94658 70905;95130 71093 1;95438 71220;95610 71340;95948 71310 1;96465 71280;96795 71153;97125 70733 1;97800 69885;98018 69360;98625 68453 1;98835 68130;98940 67950;99165 67635 1;99308 67440;99608 67515;99765 67695 1;100215 68213;100575 68333;101085 68790 1;101715 69383;102000 69742;102705 70230 1;103297 70650;103635 70815;104348 70995 1;105225 71220;105705 71333;106628 71250 1;107227 71205;107490 70913;107985 70553 1;108547 70155;108908 69923;109605 69930 1;110445 69953;110805 70425;111428 70995 1;111945 71483;112208 71730;112605 72315 1;113070 72990;113490 73290;114285 73470 1;114968 73635;115320 73755;116025 73755 1;116580 73762;117113 73583;117653 73433; 92258 51225 1;92183 51188;92100 51143;92025 51090 1;91867 51015;91748 50933;91635 50850; 90975 49973 1;90623 49343;90255 49080;89625 48675 1;88635 48052;88208 47640;87308 46890 1;86565 46298;86063 45863;85845 45037 1;85778 44707;85740 44535;85740 44190; 115725 36315 1;115943 35828;116198 35543;116715 35543 1;117405 35565;117735 36120;118065 36735 1;118102 36818;118140 36893;118178 36968; 118178 35055 1;117960 34928;117735 34793;117488 34590 1;117233 34410;117045 34238;116865 34080; 86858 68370;87218 68723 32;86723 69218 32;87023 69503 1;87435 69518;87720 69540;88095 69270 1;88125 69255;88117 69203;88163 69203 1;88373 69218;88455 69330;88658 69405;89250 68325 1;88575 68325;88238 68250;87570 68250 1;87525 68250;87473 68220;87473 68258 1;87458 68340;87413 68393;87338 68393 1;87188 68408;87120 68393;86977 68385 1;86933 68385;86887 68378;86858 68370 18; 102165 68318 1;102450 68700;102720 68940;103208 69270 1;103837 69720;104220 69915;104985 70073 1;105765 70245;106230 70343;106988 70073 1;108030 69705;108450 69083;109568 69030 1;110685 68992;111285 69435;112148 70155 1;112958 70845;113160 71453;114090 71970 1;114727 72345;115125 72533;115868 72473 1;116917 72405;117727 72352;118328 71475 1;118845 70703;119258 70425;119970 69795 1;120660 69180;121058 68925;121710 68250 1;122205 67740;122235 67260;122265 66533 1;122295 65475;122070 64823;121365 64012 1;120818 63390;120578 63052;120008 62453 1;119400 61845;118928 61710;118365 61050 1;117848 60465;117525 60233;117068 59595 1;116340 58605;115808 58230;114825 57495 1;113738 56693;112995 56610;111825 55935 1;110310 55065;109417 54810;108030 53753 1;107595 53445;107438 53190;106965 52950 1;105788 52373;105398 51465;104085 51375 1;103238 51323;102825 51060;102090 50655 1;100890 50010;100358 49500;99045 49155 1;97898 48855;97350 48533;96428 47790 1;96158 47588;95948 47423;95730 47243; 98520 45975 1;98355 45945;98167 45915;97973 45885 1;97620 45848;97425 45848;97095 45960 1;96563 46148;96323 46283;95798 46463 1;95475 46575;95303 46598;94973 46582 1;93908 46537;93375 46508;92318 46388; 101768 46545 1;101655 46478;101633 46380;101618 46320; 85950 41993 1;85808 41948;85665 41902;85470 41850 1;84960 41730;84713 41603;84293 41295 1;83963 41070;83790 40973;83468 40740; 85913 42752; 86526 42070 1;87306 41425;87727 41280;88785 41332 1;90090 41415;90758 41475;92048 41250 1;94035 40920;95018 40605;97050 40455 1;99390 40290;100575 40590;102930 40433 1;104550 40335;105360 39990;106808 39233 1;108165 38520;109005 38303;110047 37155 1;110865 36248;110880 35123;110393 34065; 94943 47078 1;94410 47258;94035 47318;93750 47790 1;93645 47963;93653 48165;93810 48278 1;94005 48435;94260 48488;94433 48300 1;94762 47940;95033 47835;95295 47408;94943 47078 18; 95010 46673 1;94538 46290;94065 45930;93405 45473 1;93165 45323;92970 45188;92775 45082; 73852 57473; 70102 55927 1;70073 56040;70193 56100;70305 56130 1;70425 56175;70583 56198;70620 56070 1;70688 55793;70718 55658;70793 55373 1;70823 55253;70725 55155;70613 55103 1;70477 55050;70335 55125;70283 55253;70102 55927 18; 69150 64508;69158 64500 32;69150 64508 18; 85298 38888 1;86010 37688;86498 36900;86992 35588; 86303 36000 1;86400 36090;86558 36150;86625 36037 1;86790 35753;86768 35662;86880 35340 1;86918 35228;86783 35220;86625 35152;86535 35145 33;86145 35010;86003 34815;85605 34748 1;85485 34733;85343 34868;85410 34965 1;85673 35378;85838 35573;86220 35880 1;86235 35902;86250 35918;86265 35940 32;86303 36000 18; 86895 39652 1;87240 39953;87548 39893;88005 39938 1;88185 39960;88440 40027;88463 39840 1;88500 39435;88463 39248;88455 38820 1;88455 38738;88500 38625;88418 38603 1;87975 38505;87742 38505;87300 38468 1;86805 38430;86760 39045;86663 39525;86895 39652 18; 97794 60918; 107123 51255 1;107633 51165;107880 51023;108308 50715 1;109058 50183;109538 50048;110220 49418 1;110340 49305;110355 49200;110393 49028; 110955 50512 1;110738 50543;110648 50648;110475 50775 1;109808 51270;109470 51503;108765 51923 1;108518 52073;108488 52065;108248 52223; 108630 50258 1;108420 50205;108188 50160;107925 50130 1;106935 50040;106417 50115;105488 49815 1;104490 49515;103995 49320;103050 48915 1;101955 48465;101655 47700;100538 47310 1;100425 47273;100253 47543;100058 47453 1;99878 47370;99953 47168;99840 47085 1;99428 46823;99053 46650;98663 46440 1;98505 46365;98370 46335;98243 46448 1;98100 46582;98033 46755;97845 46718 1;97650 46688;97575 46553;97500 46365 1;97455 46283;97462 46260;97395 46193; 104393 52957 1;105540 52943;106178 53025;107250 52590; 97462 60128 1;97493 59505;97493 59168;97508 58545 1;97508 58223;97643 58088;97718 57765 1;97950 56693;98145 56100;97913 55020; 91463 56378; 94425 57052 1;94568 57173;94545 57203;94733 57225 1;95025 57270;95220 57233;95468 57052 1;96413 56355;96690 55710;97118 54608 1;97178 54457;96840 54457;96705 54540 1;96195 54855;95963 55088;95385 55215 1;94920 55320;94845 55710;94538 56055 1;94223 56408;94223 56633;94425 57052 18; 96465 53850 1;96428 53393;96555 53137;96398 52703 1;96352 52598;96233 52598;96120 52613 1;95835 52650;95700 52688;95423 52718 1;95265 52740;95137 52808;95123 52957 1;95070 53445;95183 53693;95190 54173 1;95190 54367;95108 54683;95295 54630 1;95835 54495;96480 54473;96488 53910;96465 53850 18; 101031 60459 1;101358 60278;100718 59408;100170 59302 1;99075 59108;98535 58973;97440 58875;97493 59190 33;97485 59483;97477 59760;97462 60128 32;97635 60225 33;97650 60225;97665 60225;97688 60225 1;98685 60262;99060 60270;100178 60495 1;100275 60518;100379 60350;100469 60365 32;101031 60459 18; 82598 44520 1;83033 44588;83438 44287;83505 43860 1;83573 43433;83663 43433;82838 42960 1;82200 42593;82088 43223;81945 43620 1;81675 44423;82178 44460;82598 44520 18; 82950 44490;83617 44925 33;83768 44948;83895 44955;84053 44850 1;84248 44723;84450 44655;84458 44415 1;84458 44183;84315 44078;84143 43920 1;83903 43725;83715 43590;83520 43463 32;83535 43650 32;83145 44490;82950 44490 18; 79725 47610 1;80183 47152;80415 46912;80895 46463 1;81195 46185;81383 46043;81540 45660 1;81750 45143;81915 44895;82020 44340; 73358 41760 1;73140 41332;73133 40980;72900 40553 1;72803 40388;72630 40748;72623 40935 1;72608 41138;72645 41243;72637 41438 1;72623 41662;72765 41835;72623 42000 1;72038 42675;71708 42983;71145 43665 1;71040 43793;70958 43890;70800 43883 1;70650 43883;70575 43860;70433 43838 1;70298 43830;70268 43980;70238 44100 1;70178 44303;70020 44363;69825 44408 1;69653 44453;69548 44475;69435 44603 1;69180 44888;69420 44498;69458 44865;69765 45240;70260 45585 32;70823 45930;73358 41760 18; 69233 40185 1;69360 39540;69548 39053;69758 38250; 69225 38925 1;69495 38993;69750 39060;70110 39165 1;70253 39218;70403 39120;70433 38970 1;70477 38738;70515 38625;70538 38385;69233 38228;69233 38933;69225 38925 18; 69210 45143 1;69285 45188;69367 45240;69458 45293;69413 45075 1;69443 44933;69413 44850;69367 44707 1;69323 44588;69270 44468;69210 44348;69218 45143;69210 45143 18; 69240 37388 1;69360 37402;69473 37418;69637 37425 1;69803 37448;70065 37402;70012 37245 1;69938 37050;69855 36960;69758 36773 1;69698 36683;69683 36630;69645 36525 1;69600 36443;69668 36360;69758 36315 1;69878 36263;69938 36225;70073 36173 1;70170 36143;70193 36045;70185 35933 1;70163 35723;70140 35610;70140 35393 1;70133 35295;70088 35213;69990 35190 1;69765 35160;69668 35085;69450 35040 1;69345 35025;69255 34898;69330 34823 1;69398 34755;69398 34680;69495 34650 1;69518 34643;69480 34613;69473 34582 1;69413 34433;69405 34343;69360 34178 1;69345 34148;69360 34110;69330 34088;69240 34058;69240 37395;69240 37388 18; 69248 34050;69330 34088 33;69428 34058;69488 34035;69600 33998 1;69690 33975;69750 34005;69848 34035 1;69990 34095;70073 34178;70110 34328 1;70117 34380;70117 34425;70110 34463 33;70102 34537;70065 34605;69998 34635 1;69795 34733;69660 34665;69495 34650 33;69398 34680;69398 34755;69330 34823 1;69255 34898;69345 35025;69450 35040 1;69668 35085;69765 35160;69990 35190 1;70088 35213;70133 35295;70140 35393 1;70140 35610;70163 35723;70185 35933 1;70193 36045;70170 36143;70073 36173 1;69938 36225;69878 36263;69758 36315 1;69668 36360;69600 36443;69645 36525 1;69683 36630;69698 36683;69758 36773 1;69818 36900;69878 36983;69930 37080; 72540 56887 1;72855 56985;73020 56820;73050 56558 1;73050 56363;72953 56048;72637 55950 33;72330 55860;72083 55613;72053 56295 1;72045 56820;71985 56768;72540 56887 18; 72833 60473 1;72473 60255;72008 60090;72143 59693 1;72300 59205;72308 58935;72293 58418 1;72270 57758;72255 57375;72233 56730; 73980 59153 1;73358 58155;73125 57855;72773 56858; 72360 55950 1;72360 55373;72338 55080;72360 54495; 87585 73350 1;87675 73020;87818 72600;88148 72698 1;88725 72878;89078 72900;89528 73305 1;89850 73605;89940 73890;89948 74325 1;89948 74805;89970 75135;89648 75473 1;89348 75780;88965 75645;88590 75450 1;88065 75188;87923 74865;87563 74400 1;87308 74085;87465 73808;87585 73350 18; 87262 33998;87330 34043 33;87390 34050;87443 34043;87473 33990;87255 33998;87262 33998 18; 98565 54585 1;98880 54608;99008 54690;99233 54623 1;99360 54585;99413 54548;99540 54480 1;99600 54450;99608 54420;99615 54345 1;99630 53978;99660 53933;99667 53558 1;99667 53498;99667 53415;99608 53415 1;99315 53415;99128 53393;98895 53385 1;98813 53873;98738 54105;98565 54585 18; 70523 46298 1;69863 46035;69795 45443;69210 45082; 96839 27067;Forest map sample 123255 78068;100740 78000;86288 77955;86602 76852;88110 77108 32;89977 76500 32;90030 73163 32;88913 72428 32;87518 72195 32;89100 68573 32;90375 66968 32;92318 65775 32;97658 64043 32;98828 66338 32;99015 70133 32;99420 72615 32;99830 75218;100312 75150;100140 74078 32;101100 72630 32;101790 72075 32;107190 71760 32;108248 67448 32;108922 64867 32;106598 63195 32;107655 61920 32;111960 62662 32;111960 63435 32;112493 63525 32;112815 62798 32;116250 63278 32;116625 63810 32;116685 64575 32;117608 64537 32;118470 64845 32;120735 65085 32;121703 65018 32;121748 65505 32;122558 65415 32;122753 64927 32;124455 64477;124462 64477;124440 73020;124433 75435;124111 77128;124110 77273 32;123810 77820 32;123255 78068 18; 121553 59528;121439 58455; 116653 56713;117190 57143; 76454 69284 1;76718 68677;76558 67267;77219 67267 1;77998 67267;78614 67769;79287 68161 1;79838 68483;80015 68692;79983 69329 1;79944 70111;79815 70424;79400 71088 1;79109 71553;78866 71605;78394 71326 1;77756 70949;77405 70721;76734 70407 1;76325 70215;76274 69698;76454 69284 18; 77063 67349 1;76453 67626;75900 67627;75582 68217 1;75244 68844;74745 69296;75021 69953 1;75367 70774;75898 71195;76756 71433 1;77650 71681;78202 71510;79130 71510;77063 67349 18; 82742 72759 1;82742 73189;83015 74001;83319 73697 1;83634 73382;83828 73182;83969 72759 1;84262 71880;83434 70554;84205 70040 1;84646 69746;85256 68794;84782 68557 1;84017 68174;83147 68721;82922 69546 1;82591 70758;82742 71502;82742 72759 18; 105741 43279;105210 46148 32;103665 45893 32;102345 45045 32;97583 44123 32;92226 43370 32;89579 43106;88851 42674 32;88282 41889 32;88302 41376;89016 40811;89808 40565;92475 40692 32;97361 41391 32;97728 41725 32;101055 42329 32;105741 43279 18; 115695 62535 32;109928 61552 32;109860 60953 32;109373 60953 32;109320 61425 32;108135 61223;109253 59775;109823 56783 32;111727 56595 32;111330 53880 32;113468 53595 32;115485 48720 32;118740 48135 32;118710 47550 32;121255 47315;121230 46298;124515 46223;124523 46223;124500 53093;123450 52935 32;121650 53093 32;121613 52867 32;121387 52748 32;121364 51785;120337 51300 32;120278 50835 32;119025 50430 32;117915 50453 32;117608 50100 32;116873 50168 32;116925 51180 32;117585 51600 32;118268 51563 32;118598 51802 32;119370 51802 32;119723 51518 32;120908 52148 32;120893 53332 32;119422 53820 32;118193 54563 32;117128 54653 32;117128 55260 32;116828 55545 32;115005 55755 32;115087 56145 32;115433 56115 32;115575 57052 32;114968 58635 32;113145 58860 32;113198 59213 32;114908 59025 32;115087 60465 32;114292 60608 32;114345 61095 32;115110 61005 32;115695 62535 50; 69135 68858 1;71648 70628;73065 71580;75713 73440 1;77543 74738;78413 75458;80175 76845 1;80723 77288;81060 77535;81510 77940; mapper-0.8.1.1/examples/overprinting.omap000066400000000000000000006166331325266516600204200ustar00rootroot00000000000000 PURPLE BLACK BLUE BROWN GREEN YELLOW A line joining points of equal height. The standard vertical interval between contours is 5 metres. The smallest bend in a contour is 0.25 mm from centre to centre of the lines. Every fifth contour shall be drawn with a thicker line. This is an aid to the quick assessment of height difference and the overall shape of the terrain surface. Where an index contour coincides with an area of much detail, it may be shown with a normal contour line. An intermediate contour line. Form lines are used where more information can be given about the shape of the ground. They are used only where representation is not possible with ordinary contours. Only one form line may be used between neighbouring contours. Slope lines may be drawn on the lower side of a contour line, e.g. along the line of a re-entrant or in a depression. They are used only where it is necessary to clarify the direction of slope.0 0;0 -500; Contour values may be included to aid assessment of large height differences. They are inserted in the index contours in positions where other detail is not obscured. The figures should be orientated so that the top of the figure is on the higher side of the contour. A steep earth bank is an abrupt change in ground level which can be clearly distinguished from its surroundings, e.g. gravel or sand pits, road and railway cuttings or embankments. The tags should show the full extent of the slope, but may be omitted if two banks are close together. Impassable banks should be drawn with symbol 201 (impassable cliff). The line width of very high earth banks may be 0.25 mm.70 0;70 500;0 500;0 -90;-70 0;-70 500; A steep earth bank is an abrupt change in ground level which can be clearly distinguished from its surroundings, e.g. gravel or sand pits, road and railway cuttings or embankments. The tags should show the full extent of the slope, but may be omitted if two banks are close together. Impassable banks should be drawn with symbol 201 (impassable cliff). The line width of very high earth banks may be 0.25 mm.-370 0;370 0;300 0;300 500;-300 0;-300 500; The line width of very high earth banks may be 0.25 mm.70 0;70 500;0 500;0 100;-70 0;-70 500; The line width of very high earth banks may be 0.25 mm.-370 0;370 0;300 0;300 500;-300 0;-300 500; Use this symbol to display the full extent of wide earth banks. Distinct earth wall. Minimum height is 1 m. A small or partly ruined earth wall shall be shown with a dashed line. Minimum height is 0.5 m. An erosion gully or trench which is too small to be shown by symbol 106 is shown by a single line. The line width reflects the size of the gully. Minimum depth 1 m. The end of the line is pointed. A small erosion gully or trench. Minimum depth 0.5 m. A small obvious mound or rocky knoll which cannot be drawn to scale with a contour (diameter of mound less than ca. 5 m). The height of the knoll should be a minimum of 1 m from the surrounding ground. The symbol may not touch a contour line. A small obvious elongated knoll which cannot be drawn to scale with a contour (length less than 12 m and width less than 4 m). The height of the knoll should be a minimum of 1 m from the surrounding ground. Knolls larger than this must be shown by contours. The symbol may not be drawn in free form or such that two elongated knoll symbols overlap. The symbol may not touch a contour line.0 -400 1;110 -400;200 -221;200 0 1;200 221;110 400;0 400 1;-110 400;-200 221;-200 0 1;-200 -221;-110 -400;0 -400 3; Small shallow natural depressions and hollows (minimum diameter 2 m) which cannot be shown to scale by contours are represented by a semicircle. Minimum depth from the surrounding ground should be 1 m. Location is the centre of gravity of the symbol, which is orientated to north. Symbol 116 is used for man-made pits.400 -255 1;400 -34;221 145;0 145 1;-221 145;-400 -34;-400 -255; Pits and holes with distinct steep sides which cannot be shown to scale by symbol 106 (minimum diameter 2 m). Minimum depth from the surrounding ground should be 1 m. Location is the centre of gravity of the symbol which is orientated to north.154 -302;350 -302;0 498;-350 -302;-154 -302;0 50;154 -302 2; An area of pits or knolls which is too intricate to be shown in detail. The density of randomly placed dots may vary according to the detail on the ground. The size of the dots may vary. This symbol can be used for a special small land form feature. The definition of the symbol must be given in the map legend.-400 -400;400 400;-400 400;400 -400; An impassable cliff, quarry or earth bank (see 106) is shown with a 0.35 mm line and downward tags showing its full extent from the top line to the foot. For vertical rock faces the tags may be omitted if space is short, e.g. narrow passages between cliffs (the passage should be drawn with a width of at least 0.3 mm). The tags may extend over an area symbol representing detail immediately below the rock face. When a rock face drops straight into water making it impossible to pass under the cliff along the water's edge, the bank line is omitted or the tags should clearly extend over the bank line.60 100;60 500;0 100;0 500;-60 100;-60 500; An impassable cliff, quarry or earth bank (see 106) is shown with a 0.35 mm line and downward tags showing its full extent from the top line to the foot. For vertical rock faces the tags may be omitted if space is short, e.g. narrow passages between cliffs (the passage should be drawn with a width of at least 0.3 mm). The tags may extend over an area symbol representing detail immediately below the rock face. When a rock face drops straight into water making it impossible to pass under the cliff along the water's edge, the bank line is omitted or the tags should clearly extend over the bank line.-360 0;360 0;300 0;300 500;-300 0;-300 500; For vertical rock faces the tags may be omitted if space is short, e.g. narrow passages between cliffs (the passage should be drawn with a width of at least 0.3 mm). Use this symbol to display the full extent of a wide cliff. In the case of unusual features such as rock pillars or massive cliffs or gigantic boulders, the rocks shall be shown in plan shape without tags. A small vertical rock face (minimum height 1 m) may be shown without tags. If the direction of fall of the rock face is not apparent from the contours or to improve legibility, short tags should be drawn in the direction of the fall. For passable rock faces shown without tags the ends of the line may be rounded to improve legibility.60 50;60 500;0 50;0 500;-60 50;-60 500; A small vertical rock face (minimum height 1 m) may be shown without tags. If the direction of fall of the rock face is not apparent from the contours or to improve legibility, short tags should be drawn in the direction of the fall. For passable rock faces shown without tags the ends of the line may be rounded to improve legibility.-360 0;360 0;300 0;300 500;-300 0;-300 500; Should be used if the direction of fall of the rock face is apparent from the contours and the legibility is good. Should be used if the direction of fall of the rock face is apparent from the contours and the legibility is good.-300 0;300 0; For passable rock faces shown without tags the ends of the line may be rounded to improve legibility. For passable rock faces shown without tags the ends of the line may be rounded to improve legibility.-175 0;175 0; Rocky pits, holes or mineshafts which may constitute a danger to the runner. Location is the centre of gravity of the symbol, which is orientated to north.175 -310;350 -310;0 490;-350 -310;-175 -310;0 90;175 -310 2; A cave is represented by the same symbol as a rocky pit. In this case the symbol should be orientated to point up the slope as indicated opposite. The centre of gravity of the symbol marks the opening.175 -310;350 -310;0 490;-350 -310;-175 -310;0 90;175 -310 2; A small distinct boulder (minimum height 1 m). Every boulder marked on the map should be immediately identifiable on the ground. To be able to show the distinction between boulders with significant difference in size it is permitted to enlarge this symbol by 20% (diameter 0.5 mm). To be able to show the distinction between boulders with significant difference in size it is permitted to enlarge this symbol by 20% (diameter 0.5 mm). A particularly large and distinct boulder. For gigantic boulders symbol 202 should be used. An area which is covered with so many blocks of stone that they cannot be marked individually is shown with randomly orientated solid triangles with sides of ratio 8:6:5. A minimum of two triangles should be used. The going is indicated by the density of the triangles. To be able to show the distinction between boulder fields with a significant difference in boulder size it is permitted to enlarge the triangles by 20%.100 301;100 -339;-200 36;100 301 2; To be able to show the distinction between boulder fields with a significant difference in boulder size it is permitted to enlarge the triangles by 20%.120 361;120 -407;-240 43;120 361 2; A small distinct group of boulders so closely clustered together that they cannot be marked individually. The symbol is an equilateral triangle orientated to the north. To be able to show the distinction between boulder clusters with significant difference in size it is permitted to enlarge this symbol by 25% (1.0 mm).-400 231;400 231;0 -462;-400 231 2; To be able to show the distinction between boulder clusters with significant difference in size it is permitted to enlarge this symbol by 25% (1.0 mm).-500 289;500 289;0 -577;-500 289 2; Stony or rocky ground which affects going should be shown on the map. The dots should be randomly distributed with density according to the amount of rock. A minimum of three dots should be used. Stony or rocky ground which affects going should be shown on the map. The dots should be randomly distributed with density according to the amount of rock. A minimum of three dots should be used. An area of soft sandy ground or gravel with no vegetation and where running is slow. Where an area of sandy ground is open but running is good, it is shown as open land (401/402). A runnable area of rock without earth or vegetation is shown as bare rock. An area of rock covered with grass, moss or other low vegetation is shown as open land (401/402). Large areas of water are shown with dot screen. Small areas of water should be shown with full colour. A black bank line indicates that the feature cannot be crossed. A black bank line indicates that the feature cannot be crossed. Where the lake or pond is smaller than 1 mm² on the printed map, the bank line is omitted. A water-filled pit or an area of water which is too small to be shown to scale. Location is the centre of gravity of the symbol, which is orientated to north.154 -302;350 -302;0 498;-350 -302;-154 -302;0 50;154 -302 2; A crossable watercourse, minimum 2 m wide. The width of watercourses over 5 m wide should be shown to scale. A crossable watercourse (including a major drainage ditch) less than 2 m wide. For better legibility a ditch in a marsh should be drawn as a crossable watercourse (305). A natural or man-made minor water channel which may contain water only intermittently. A marsh or trickle of water which is too narrow to be shown with symbol 310 (less than ca. 5 m wide). A marsh which is uncrossable or dangerous for the runner. A black line surrounds the symbol. A black line surrounds the symbol. A crossable marsh, usually with a distinct edge. The symbol should be combined with vegetation symbols to show runnability and openness. Where a small marsh area should be combined with either 403/404 it is permitted to use 401/402 to improve legibility. -250 -150;250 -150;-250 150;250 150; An indistinct or seasonal marsh or area of gradual transition from marsh to firm ground, which is crossable. The edge is generally indistinct and the vegetation similar to that of the surrounding ground. The symbol should be combined with vegetation symbols to show runnability and openness.-450 0;450 0;-450 0;450 0; -450 -300;450 -300;-450 300;450 300;-1025 0;-125 0;125 0;1025 0; Wells and captive springs, which are clearly visible on the ground. The source of a stream with a distinct outflow. The symbol is orientated to open downstream.400 -255 1;400 -34;221 145;0 145 1;-221 145;-400 -34;-400 -255; A special small water feature. The definition of the symbol must always be given in the map legend.-400 -400;400 400;-400 400;400 -400; Cultivated land, fields, meadows, grassland, etc. without trees, offering easy running. If yellow coloured areas becomes dominant, a screen (75%) instead of full yellow may be used. Meadows with scattered trees or bushes, with grass or similar ground cover offering easy running. Areas smaller than 10 mm at the maps scale are shown as open land (401). Individual trees may be added (418, 419, 420). If yellow coloured areas becomes dominant, a screen (75%) instead of full yellow may be used. Heath, moorland, felled areas, newly planted areas (trees lower than ca. 1 m) or other generally open land with rough ground vegetation, heather or tall grass. Symbol 403 may be combined with symbols 407 and 409 to show reduced runnability. Where there are scattered trees in rough open land, areas of white (or green) should appear in the tone. Such an area may be generalised by using a regular pattern of large white dots in the yellow screen. Areas smaller than 16 mm in the maps scale are shown as rough open land (403). Individual trees may be added (418, 419, 420). Typically open runnable forest for the particular type of terrain. If no part of the forest is runnable then no white should appear on the map. An area with dense trees (low visibility) which reduces running to ca. 60-80% of normal speed. An area of dense undergrowth but otherwise good visibility (brambles, heather, low bushes, and including cut branches) which reduces running to ca. 60-80% of normal speed. This symbol may not be combined with 406 or 408. An area with dense trees or thicket (low visibility) which reduce running to ca. 20-60% of normal speed. An area of dense undergrowth but otherwise good visibility (brambles, heather, low bushes, and including cut branches) which reduces running to ca. 20-60% of normal speed. This symbol may not be combined with 406 or 408. An area of dense vegetation (trees or undergrowth) which is barely passable. Running reduced to ca. 0-20% of normal speed. Line of minimum width for symbol 410. When an area of forest provides good running in one direction but less good in others, white stripes are left in the screen symbol to show the direction of good running. When an area of forest provides good running in one direction but less good in others, white stripes are left in the screen symbol to show the direction of good running. When an area of forest provides good running in one direction but less good in others, white stripes are left in the screen symbol to show the direction of good running. Land planted with fruit trees or bushes. The dot lines may be orientated to show the direction of planting. If yellow coloured areas becomes dominant, a screen (75%) instead of full yellow may be used. The green lines may be orientated to show the direction of planting. If yellow coloured areas becomes dominant, a screen (75%) instead of full yellow may be used.0 -650;0 650;0 -650;0 650; The boundary of cultivated land when not shown with other symbols (fence, wall, path, etc.) is shown with a black line. A permanent boundary between different types of cultivated land is also shown with this symbol. Cultivated land which is seasonally out-of-bounds due to growing crops may be shown with a black dot screen. A distinct forest edge or very distinct vegetation boundary within the forest. Symbols 418, 419 and 420 can be used for special small vegetation features. The definition of the symbol must be given in each case in the map legend.-400 -400;400 400;400 -400;-400 400; Symbols 418, 419 and 420 can be used for special small vegetation features. The definition of the symbol must be given in each case in the map legend. Symbols 418, 419 and 420 can be used for special small vegetation features. The definition of the symbol must be given in each case in the map legend. A road with two carriageways. The width of the symbol should be drawn to scale but not smaller than the minimum width. The outer boundary lines may be replaced with symbols 519, 521, 522 or 524 if a fence or wall is so close to the motorway edge that it cannot practically be shown as a separate symbol. The space between the black lines must be filled with brown (50%). A road under construction may be shown with broken lines. A road under construction may be shown with broken lines. Road wider than 5m. The width of the symbol should be drawn to scale but not smaller than the minimum width. The outer boundary lines may be replaced with symbols 519, 521, 522 or 524 if a fence or wall is so close to the motorway edge that it cannot practically be shown as a separate symbol. The space between the black lines must be filled with brown (50%). A road under construction may be shown with broken lines. A road under construction may be shown with broken lines. Road 3-5 m wide. The space between the black lines must be filled with brown (50%). A road under construction may be shown with broken lines. A road under construction may be shown with broken lines. A maintained road suitable for motor vehicles in all weather. Width less than 3 m. A track or poorly maintained road suitable for vehicles only when travelling slowly. Width less than 3 m. A large path, or old vehicle track, which is distinct on the ground. A small path or (temporary) forest extraction track which can be followed at competition speed. A less distinct small path or forestry extraction track. A distinct ride, less than ca. 5 m wide. A ride is a linear break in the forest (usually plantation) which does not have a distinct path along it. Where there is a path along a ride, symbols 507 or 508 should be used in place of symbol 509. A footbridge with no path leading to it. Note: if the stream is wider than 0.25mm, adjust this symbol so it extends 0.5mm over both sides of the stream!0 -625;0 625; A railway or other kind of railed track (tramway, truckway, etc.).0 -525;0 525; Power line, cableway or skilift. The bars indicate the exact location of the pylons.0 -370;0 370; Major power lines should be drawn with a double line. The gap between the lines may indicate the extent of the powerline.0 -750;0 750; A way under roads, railways, etc. which may be used by the runner. This symbol is used whether or not the tunnel has a track leading to it.-300 -520;0 0;10 0;-10 0;0 0;300 -520; A way under roads, railways, etc. which may be used by the runner. This symbol is used whether or not the tunnel has a track leading to it.300 -520;0 0;-300 -520; A stone wall or stone-faced bank. A ruined stone wall may be shown by a dashed line. A stone wall higher than ca 1.5 m, not crossable to the average orienteer. A wooden or wire fence less than ca. 1.5 m high.0 0;250 433; A ruined fence may be shown with a dashed line.0 0;250 433; A boarded or wire fence higher than ca 1.5 m, not crossable to the average orienteer, eg. deer fence.300 0;550 433;-300 0;-50 433; All ways through or over high fences or walls must be indicated. The symbol may also be used for a gate through or stile over a stone wall (519) or a fence (522) or a pipeline (534).300 -500;300 500;-300 -500;-300 500; A building is shown with its ground plan so far as the scale permits. -250 -250;250 -250;250 250;-250 250;-250 -250 2; Houses and gardens and other built up areas. Roads, buildings and other significant features within a settlement must be shown. If all buildings cannot be shown, an alternative symbol (black line screen) may be used. Houses and gardens and other built up areas. Roads, buildings and other significant features within a settlement must be shown. If all buildings cannot be shown, an alternative symbol (black line screen) may be used. Areas which are permanently forbidden to the runner are shown as out of bounds. The screen is superimposed on the normal map detail. A bounding line may be drawn if there is no natural boundary (see 709). A bounding line may be drawn if there is no natural boundary (see 709). An area of hard standing used for parking or other purposes. The ground plan of a ruin is shown to scale, down to the minimum size shown opposite. Very small ruins may be drawn with a solid line. Very small ruins may be drawn with a solid line. Very small ruins may be drawn with a solid line.320 320;320 -320;-320 -320;-320 320;320 320 2; A firing range is shown with a special symbol to indicate the need for caution. Associated buildings are individually marked.1700 -700;1700 700;1700 0;-250 -593;0 0;-250 593;1700 0 2; A distinct grave marked by a stone or shrine. Location is at the centre of gravity of the symbol, which is orientated to north. A cemetery is shown by using grave symbols as space permits.0 -430;0 570;-350 -129;350 -129; A pipeline (gas, water, oil, etc.) above ground level which can be crossed over or under.0 0;-354 -354;0 0;-354 354; A pipeline which cannot be crossed.0 0;-354 -354;0 0;-354 354; A high tower or large pylon, standing above the level of the surrounding forest. Location is at the centre of gravity of the symbol.-700 0;700 0;0 -700;0 700; An obvious shooting platform or seat, or small tower. Location is at the centre of gravity of the symbol.0 -262;0 738;-500 -262;500 -262; Cairn, memorial stone or boundary stone (or a trigonometric point in some countries) more than 0.5 m high.0 0; A fodder rack which is free standing or built on to a tree. Location is at the centre of gravity of the symbol. For land access reasons these may be omitted.0 -322;0 678;-500 -33;0 -322;500 -33; Special man-made features are shown with these symbols. The definition of the symbols must be given in each case in the map legend. Special man-made features are shown with these symbols. The definition of the symbols must be given in each case in the map legend.-400 -400;400 400;400 -400;-400 400; Magnetic north lines are lines placed on the map pointing to magnetic north. Their spacing on the map should be 33.33 mm which represents 500 m on the ground at the scale of 1:15 000. For maps with other scales lines placing should be at intervals which represents a round number of meters (e.g. 50 m, 100 m, 250 m, 500 m) and the spacing should be between 20 mm and 40 mm on the map. North lines may be broken where they obscure small features such as boulders, knolls, cliffs, stream junctions, path ends, etc. In areas with very few water features, blue lines may be used. Magnetic north lines are lines placed on the map pointing to magnetic north. Their spacing on the map should be 33.33 mm which represents 500 m on the ground at the scale of 1:15 000. For maps with other scales lines placing should be at intervals which represents a round number of meters (e.g. 50 m, 100 m, 250 m, 500 m) and the spacing should be between 20 mm and 40 mm on the map. North lines may be broken where they obscure small features such as boulders, knolls, cliffs, stream junctions, path ends, etc. In areas with very few water features, blue lines may be used. At least three registration marks must be placed within the frame of a map in a non-symmetrical position. In addition, a colour check should also be possible.0 -2000;0 2000;-2000 0;2000 0; Spot heights are used for the rough assessment of height differences. The height is given to the nearest metre. The figures are orientated to the north. Water levels are given without the dot. Spot heights are used for the rough assessment of height differences. The height is given to the nearest metre. The figures are orientated to the north. Water levels are given without the dot. The start or map issue point (if not at the start) is shown by an equilateral triangle which points in the direction of the first control. The centre of the triangle shows the precise position of the start point.-3500 2021;0 -4041;3500 2021;-3500 2021 18; The control points are shown with circles. The centre of the circle shows the precise position of the feature. Sections of circles should be omitted to leave important detail showing. The number of the control is placed close to the control point circle in such a way that it does not obscure important detail. The numbers are orientated to north. Where controls are to be visited in order, the start, control points and finish are joined together by straight lines. Sections of lines should be omitted to leave important detail showing. A marked route is shown on the map with a dashed line. The finish is shown by two concentric circles.0 0; A boundary which it is not permitted to cross. A crossing point through or over a wall or fence, or across a road or railway or through a tunnel or an out-of-bounds area is drawn on the map with two lines curving outwards.-1500 725 1;-500 450;500 450;1500 725;-1500 -725 1;-500 -450;500 -450;1500 -725; An out-of-bounds area, see also symbol 528, is shown with vertical stripes. A bounding line may be drawn if there is no natural boundary, as follows: - a solid line indicates that the boundary is marked continuously (tapes, etc.) on the ground, - a dashed line indicates intermittent marking on the ground, - no line indicates no marking on the ground. A solid line indicates that the boundary is marked continuously (tapes, etc.) on the ground. A dashed line indicates intermittent marking on the ground. An area presenting danger to the competitor is shown with cross-hatched diagonal lines. A route which is out-of-bounds is shown with crosses.1061 -1061;-1061 1061;1061 1061;-1061 -1061; The location of a first aid post.-1500 0;1500 0;0 -1500;0 1500; The location of a refreshment point which is not at a control.-1340 -1395;-820 1380;1340 -1395;820 1380;0 -1770 1;600 -1770;1200 -1620;1340 -1395 1;1200 -1170;600 -1020;0 -1020 1;-600 -1020;-1200 -1170;-1340 -1395 1;-1200 -1620;-600 -1770;0 -1770;-820 1380 1;-680 1680;680 1680;820 1380; This symbol provides a simple and quick way to make training courses. The purple line will extend a bit into the finish symbol. This is a shortcoming of this simple approach.3041 0;3541 0;-3021 3500;3041 0;-3021 -3500;-3021 3500 18;-600 0;600 0;0 0; The OpenOrienteering Logo.-12797 -557 1;-12755 -447;-12770 -420;-12873 -420 1;-12953 -420;-12973 -440;-12973 -520 1;-12973 -635;-12838 -663;-12797 -557 18;-933 2063 1;-933 1925;-920 1900;-851 1900 1;-780 1900;-770 1921;-781 2050 1;-789 2154;-814 2203;-863 2213 1;-920 2224;-933 2197;-933 2063;-933 2063 18;-8875 -3860 1;-8922 -3920;-8984 -3968;-9061 -4006 1;-9134 -4045;-9225 -4065;-9337 -4065 1;-9444 -4065;-9535 -4045;-9612 -4006 1;-9688 -3974;-9752 -3927;-9803 -3867 1;-9855 -3807;-9895 -3737;-9925 -3655 1;-9950 -3578;-9970 -3500;-9982 -3418;-8723 -3418 1;-8725 -3500;-8740 -3578;-8768 -3655 1;-8789 -3732;-8824 -3800;-8875 -3860 18;-4230 -3923 1;-4183 -3737;-4160 -3527;-4160 -3296;-4160 -1395;-5113 -1395;-5113 -3182 1;-5113 -3488;-5155 -3707;-5235 -3833 1;-5317 -3961;-5468 -4027;-5690 -4027 1;-5759 -4027;-5830 -4021;-5907 -4013 1;-5983 -4008;-6052 -4004;-6112 -3993;-6112 -1395;-7064 -1395;-7064 -4646 1;-6903 -4693;-6694 -4736;-6438 -4774 1;-6181 -4818;-5913 -4838;-5631 -4838 1;-5347 -4838;-5110 -4800;-4921 -4723 1;-4730 -4652;-4579 -4547;-4467 -4410 1;-4357 -4273;-4277 -4111;-4230 -3923 18;-13524 1118 1;-13605 1126;-13667 1137;-13708 1150;-13708 3722;-14662 3722;-14662 536 1;-14492 476;-14292 420;-14060 369 1;-13827 314;-13565 286;-13280 286 1;-13229 286;-13167 290;-13096 299 1;-13022 303;-12951 312;-12878 325 1;-12806 333;-12733 345;-12660 363 1;-12587 375;-12526 392;-12474 414;-12634 1201 1;-12719 1180;-12819 1158;-12936 1137 1;-13051 1112;-13174 1098;-13306 1098 1;-13366 1098;-13439 1105;-13524 1118 18;-11093 -200 1;-11204 -101;-11336 -52;-11490 -52 1;-11642 -52;-11777 -101;-11893 -200 1;-12004 -303;-12059 -441;-12059 -616 1;-12059 -791;-12004 -927;-11893 -1026 1;-11777 -1128;-11642 -1179;-11490 -1179 1;-11336 -1179;-11204 -1128;-11093 -1026 1;-10978 -927;-10920 -791;-10920 -616 1;-10920 -441;-10978 -303;-11093 -200 18;-11009 3722;-11963 3722;-11963 357;-11009 357;-11009 3722 18;-8649 1053 1;-8755 1053;-8847 1073;-8924 1112 1;-9001 1145;-9065 1192;-9115 1252 1;-9167 1312;-9207 1381;-9237 1464 1;-9264 1539;-9282 1618;-9296 1700;-8034 1700 1;-8039 1618;-8054 1539;-8079 1464 1;-8101 1387;-8137 1318;-8189 1259 1;-8236 1199;-8297 1150;-8374 1112 1;-8445 1073;-8537 1053;-8649 1053 18;-5220 1105 1;-5297 1110;-5365 1115;-5425 1125;-5425 3722;-6378 3722;-6378 472 1;-6217 425;-6007 382;-5751 344 1;-5495 301;-5227 280;-4945 280 1;-4658 280;-4422 318;-4235 395 1;-4042 467;-3890 572;-3781 709 1;-3669 845;-3591 1007;-3544 1195 1;-3497 1381;-3474 1592;-3474 1821;-3474 3722;-4427 3722;-4427 1937 1;-4427 1630;-4467 1411;-4549 1285 1;-4628 1156;-4780 1092;-5002 1092 1;-5070 1092;-5143 1097;-5220 1105 18;-2636 2346;-2636 -481;-1683 -634;-1683 357;-539 357;-539 1150;-1683 1150;-1683 2333 1;-1683 2535;-1648 2694;-1581 2813 1;-1508 2933;-1365 2993;-1151 2993 1;-1050 2993;-945 2984;-838 2966 1;-728 2946;-627 2918;-539 2884;-404 3626 1;-518 3673;-646 3712;-787 3748 1;-928 3780;-1102 3799;-1305 3799 1;-1566 3799;-1781 3764;-1951 3696 1;-2123 3624;-2259 3526;-2361 3402 1;-2463 3274;-2536 3121;-2579 2941 1;-2618 2763;-2636 2565;-2636 2346 18;2142 1464 1;2121 1387;2084 1318;2032 1259 1;1986 1199;1924 1150;1847 1112 1;1776 1073;1685 1053;1572 1053 1;1466 1053;1375 1073;1298 1112 1;1221 1145;1156 1192;1106 1252 1;1054 1312;1014 1381;984 1464 1;958 1539;939 1618;926 1700;2187 1700 1;2183 1618;2168 1539;2142 1464 18;5923 1700 1;5919 1618;5904 1539;5878 1464 1;5856 1387;5821 1318;5769 1259 1;5723 1199;5661 1150;5585 1112 1;5511 1073;5419 1053;5308 1053 1;5201 1053;5110 1073;5033 1112 1;4957 1145;4893 1192;4841 1252 1;4790 1312;4750 1381;4720 1464 1;4695 1539;4675 1618;4663 1700;5923 1700 18;8718 1118 1;8637 1126;8575 1137;8534 1150;8534 3722;7581 3722;7581 536 1;7750 476;7950 420;8182 369 1;8415 314;8677 286;8962 286 1;9013 286;9075 290;9146 299 1;9220 303;9292 312;9365 325 1;9437 333;9510 345;9583 363 1;9655 375;9717 392;9768 414;9608 1201 1;9523 1180;9424 1158;9306 1137 1;9191 1112;9069 1098;8937 1098 1;8877 1098;8804 1105;8718 1118 18;10350 -200 1;10238 -303;10183 -441;10183 -616 1;10183 -791;10238 -927;10350 -1026 1;10465 -1128;10600 -1179;10752 -1179 1;10906 -1179;11038 -1128;11149 -1026 1;11264 -927;11323 -791;11323 -616 1;11323 -441;11264 -303;11149 -200 1;11038 -101;10906 -52;10752 -52 1;10600 -52;10465 -101;10350 -200 18;11233 3722;10279 3722;10279 357;11233 357;11233 3722 18;14726 709 1;14836 845;14916 1007;14963 1195 1;15010 1381;15033 1592;15033 1821;15033 3722;14080 3722;14080 1937 1;14080 1630;14039 1411;13958 1285 1;13876 1156;13725 1092;13504 1092 1;13435 1092;13363 1097;13287 1105 1;13210 1110;13142 1115;13082 1125;13082 3722;12129 3722;12129 472 1;12290 425;12499 382;12756 344 1;13012 301;13280 280;13562 280 1;13847 280;14083 318;14272 395 1;14463 467;14614 572;14726 709 18;-17051 -1307 1;-17299 -1307;-17525 -1349;-17729 -1434 1;-17931 -1521;-18102 -1639;-18247 -1794 1;-18392 -1951;-18505 -2139;-18587 -2355 1;-18669 -2579;-18708 -2820;-18708 -3085 1;-18708 -3350;-18669 -3591;-18587 -3808 1;-18502 -4027;-18387 -4211;-18242 -4365 1;-18092 -4518;-17917 -4638;-17717 -4723 1;-17512 -4808;-17291 -4851;-17051 -4851 1;-16808 -4851;-16586 -4808;-16386 -4723 1;-16182 -4638;-16006 -4518;-15861 -4365 1;-15716 -4211;-15603 -4027;-15523 -3808 1;-15442 -3591;-15401 -3350;-15401 -3085 1;-15401 -2820;-15440 -2579;-15517 -2355 1;-15593 -2139;-15703 -1951;-15848 -1794 1;-15993 -1639;-16168 -1521;-16373 -1434 1;-16574 -1349;-16800 -1307;-17051 -1307 18;-17051 -2126 1;-16834 -2126;-16668 -2210;-16552 -2383 1;-16433 -2557;-16373 -2792;-16373 -3085 1;-16373 -3380;-16433 -3610;-16552 -3777 1;-16668 -3946;-16834 -4034;-17051 -4034 1;-17269 -4034;-17437 -3946;-17557 -3777 1;-17675 -3610;-17736 -3380;-17736 -3085 1;-17736 -2792;-17675 -2557;-17557 -2383 1;-17437 -2210;-17269 -2126;-17051 -2126 18;-14662 -213;-14662 -4646 1;-14576 -4673;-14478 -4697;-14369 -4716 1;-14257 -4743;-14142 -4765;-14022 -4781 1;-13898 -4798;-13776 -4811;-13652 -4819 1;-13524 -4833;-13402 -4838;-13287 -4838 1;-13009 -4838;-12763 -4796;-12544 -4711 1;-12328 -4630;-12144 -4513;-11995 -4358 1;-11846 -4210;-11732 -4027;-11657 -3808 1;-11574 -3591;-11535 -3348;-11535 -3078 1;-11535 -2819;-11566 -2582;-11629 -2368 1;-11694 -2156;-11788 -1972;-11911 -1819 1;-12034 -1666;-12189 -1546;-12373 -1461 1;-12556 -1376;-12765 -1333;-13006 -1333 1;-13137 -1333;-13261 -1346;-13377 -1371 1;-13492 -1395;-13602 -1432;-13708 -1479;-13708 -213;-14662 -213 18;-13184 -2139 1;-12733 -2139;-12506 -2443;-12506 -3054 1;-12506 -3348;-12572 -3582;-12704 -3756 1;-12837 -3936;-13032 -4027;-13293 -4027 1;-13379 -4027;-13457 -4021;-13530 -4013 1;-13602 -4008;-13662 -4004;-13708 -3993;-13708 -2274 1;-13648 -2234;-13572 -2202;-13479 -2177 1;-13381 -2152;-13282 -2139;-13184 -2139 18;-9182 -1307 1;-9485 -1307;-9750 -1352;-9976 -1440 1;-10198 -1530;-10383 -1652;-10533 -1806 1;-10678 -1964;-10787 -2149;-10858 -2362 1;-10926 -2575;-10962 -2807;-10962 -3054 1;-10962 -3352;-10917 -3612;-10827 -3833 1;-10733 -4060;-10611 -4248;-10462 -4396 1;-10314 -4547;-10141 -4660;-9950 -4736 1;-9754 -4813;-9553 -4851;-9349 -4851 1;-8872 -4851;-8494 -4705;-8217 -4410 1;-7939 -4120;-7801 -3692;-7801 -3123 1;-7801 -3069;-7803 -3007;-7808 -2939 1;-7810 -2873;-7816 -2817;-7819 -2766;-9982 -2766 1;-9961 -2569;-9870 -2413;-9707 -2299 1;-9545 -2184;-9327 -2126;-9055 -2126 1;-8881 -2126;-8708 -2141;-8542 -2171 1;-8372 -2205;-8234 -2246;-8127 -2292;-7999 -1517 1;-8051 -1493;-8119 -1468;-8204 -1440 1;-8289 -1416;-8385 -1395;-8492 -1378 1;-8594 -1356;-8706 -1339;-8824 -1326 1;-8944 -1314;-9063 -1307;-9182 -1307 18;-9982 -3418;-8723 -3418 1;-8725 -3500;-8740 -3578;-8768 -3655 1;-8789 -3732;-8824 -3800;-8875 -3860 1;-8922 -3920;-8984 -3968;-9061 -4006 1;-9134 -4045;-9225 -4065;-9337 -4065 1;-9444 -4065;-9535 -4045;-9612 -4006 1;-9688 -3974;-9752 -3927;-9803 -3867 1;-9855 -3807;-9895 -3737;-9925 -3655 1;-9950 -3578;-9970 -3500;-9982 -3418 18;-17051 -2126 1;-17269 -2126;-17437 -2210;-17557 -2383 1;-17675 -2557;-17736 -2792;-17736 -3085 1;-17736 -3380;-17675 -3610;-17557 -3777 1;-17437 -3946;-17269 -4034;-17051 -4034 1;-16834 -4034;-16668 -3946;-16552 -3777 1;-16433 -3610;-16373 -3380;-16373 -3085 1;-16373 -2792;-16433 -2557;-16552 -2383 1;-16668 -2210;-16834 -2126;-17051 -2126 18;-17064 -2593 1;-16944 -2591;-16842 -2808;-16838 -3077 1;-16834 -3347;-16928 -3567;-17049 -3569 1;-17170 -3571;-17271 -3354;-17275 -3084 1;-17280 -2815;-17185 -2595;-17064 -2593 18;-8496 3810 1;-8798 3810;-9062 3765;-9288 3677 1;-9510 3588;-9696 3466;-9845 3312 1;-9990 3154;-10098 2969;-10171 2756 1;-10240 2542;-10273 2311;-10273 2065 1;-10273 1766;-10230 1507;-10140 1285 1;-10045 1058;-9923 870;-9775 722 1;-9625 572;-9455 459;-9264 382 1;-9067 305;-8867 267;-8662 267 1;-8184 267;-7806 414;-7529 709 1;-7252 998;-7113 1426;-7113 1995 1;-7113 2050;-7116 2112;-7119 2180 1;-7124 2245;-7128 2301;-7132 2353;-9296 2353 1;-9273 2550;-9182 2704;-9020 2820 1;-8857 2934;-8640 2993;-8368 2993 1;-8193 2993;-8022 2978;-7855 2948 1;-7684 2912;-7546 2873;-7440 2826;-7311 3601 1;-7363 3626;-7431 3651;-7516 3677 1;-7603 3703;-7697 3724;-7804 3741 1;-7908 3763;-8017 3779;-8137 3793 1;-8257 3804;-8376 3810;-8496 3810 18;-9296 1700;-8034 1700 1;-8039 1618;-8054 1539;-8079 1464 1;-8101 1387;-8137 1318;-8189 1259 1;-8236 1199;-8297 1150;-8374 1112 1;-8445 1073;-8537 1053;-8649 1053 1;-8755 1053;-8847 1073;-8924 1112 1;-9001 1145;-9065 1192;-9115 1252 1;-9167 1312;-9207 1381;-9237 1464 1;-9264 1539;-9282 1618;-9296 1700 18;1726 3810 1;1422 3810;1159 3765;933 3677 1;712 3588;524 3466;376 3312 1;231 3154;123 2969;49 2756 1;-19 2542;-52 2311;-52 2065 1;-52 1766;-9 1507;81 1285 1;177 1058;298 870;446 722 1;596 572;766 459;958 382 1;1154 305;1354 267;1559 267 1;2038 267;2416 414;2692 709 1;2968 998;3109 1426;3109 1995 1;3109 2050;3105 2112;3102 2180 1;3097 2245;3094 2301;3088 2353;926 2353 1;947 2550;1039 2704;1201 2820 1;1364 2934;1581 2993;1854 2993 1;2029 2993;2199 2978;2365 2948 1;2538 2912;2675 2873;2782 2826;2910 3601 1;2859 3626;2790 3651;2705 3677 1;2619 3703;2524 3724;2417 3741 1;2314 3763;2204 3779;2084 3793 1;1965 3804;1846 3810;1726 3810 18;926 1700;2187 1700 1;2183 1618;2168 1539;2142 1464 1;2121 1387;2084 1318;2032 1259 1;1986 1199;1924 1150;1847 1112 1;1776 1073;1685 1053;1572 1053 1;1466 1053;1375 1073;1298 1112 1;1221 1145;1156 1192;1106 1252 1;1054 1312;1014 1381;984 1464 1;958 1539;939 1618;926 1700 18;5463 3810 1;5160 3810;4895 3765;4668 3677 1;4446 3588;4262 3466;4112 3312 1;3967 3154;3858 2969;3787 2756 1;3719 2542;3684 2311;3684 2065 1;3684 1766;3729 1507;3819 1285 1;3911 1058;4033 870;4183 722 1;4332 572;4503 459;4695 382 1;4891 305;5091 267;5297 267 1;5773 267;6151 414;6428 709 1;6706 998;6844 1426;6844 1995 1;6844 2050;6843 2112;6837 2180 1;6834 2245;6829 2301;6826 2353;4663 2353 1;4683 2550;4775 2704;4938 2820 1;5100 2934;5318 2993;5590 2993 1;5765 2993;5936 2978;6103 2948 1;6272 2912;6411 2873;6518 2826;6646 3601 1;6594 3626;6526 3651;6441 3677 1;6356 3703;6259 3724;6152 3741 1;6051 3763;5939 3779;5821 3793 1;5701 3804;5581 3810;5463 3810 18;4663 1700;5923 1700 1;5919 1618;5904 1539;5878 1464 1;5856 1387;5821 1318;5769 1259 1;5723 1199;5661 1150;5585 1112 1;5511 1073;5419 1053;5308 1053 1;5201 1053;5110 1073;5033 1112 1;4957 1145;4893 1192;4841 1252 1;4790 1312;4750 1381;4720 1464 1;4695 1539;4675 1618;4663 1700 18;17092 4924 1;16887 4924;16682 4905;16477 4867 1;16272 4832;16083 4785;15908 4725;16074 3926 1;16224 3986;16379 4032;16543 4067 1;16707 4101;16896 4119;17105 4119 1;17377 4119;17570 4059;17680 3939 1;17796 3819;17854 3666;17854 3479;17854 3357 1;17750 3404;17644 3441;17533 3466 1;17427 3487;17309 3498;17182 3498 1;16717 3498;16361 3361;16113 3087 1;15866 2811;15743 2424;15743 1930 1;15743 1683;15781 1458;15857 1259 1;15934 1053;16044 878;16189 733 1;16339 589;16521 478;16734 402 1;16947 320;17187 280;17457 280 1;17572 280;17689 286;17809 299 1;17931 307;18053 320;18173 337 1;18292 354;18405 375;18512 402 1;18624 422;18722 446;18806 472;18806 3299 1;18806 3849;18665 4257;18385 4522 1;18107 4790;17677 4924;17092 4924 18;17360 2730 1;17459 2730;17550 2718;17636 2691 1;17720 2666;17794 2636;17854 2603;17854 1080 1;17807 1072;17750 1065;17687 1060 1;17623 1052;17548 1047;17464 1047 1;17212 1047;17024 1130;16900 1297 1;16776 1464;16714 1675;16714 1930 1;16714 2463;16930 2730;17360 2730 18;-17051 2993 1;-17269 2993;-17437 2908;-17557 2736 1;-17675 2561;-17736 2326;-17736 2033 1;-17736 1738;-17675 1509;-17557 1342 1;-17437 1171;-17269 1085;-17051 1085 1;-16834 1085;-16668 1171;-16552 1342 1;-16433 1509;-16373 1738;-16373 2033 1;-16373 2326;-16433 2561;-16552 2736 1;-16668 2908;-16834 2993;-17051 2993 18;-17064 2526 1;-16944 2528;-16842 2311;-16838 2042 1;-16834 1772;-16928 1552;-17049 1550 1;-17170 1548;-17271 1765;-17275 2035 1;-17280 2304;-17185 2524;-17064 2526 18;-17051 3810 1;-17299 3810;-17525 3769;-17729 3684 1;-17931 3598;-18102 3479;-18247 3324 1;-18392 3168;-18505 2980;-18587 2763 1;-18669 2540;-18708 2298;-18708 2033 1;-18708 1768;-18669 1526;-18587 1310 1;-18502 1092;-18387 906;-18242 754 1;-18092 600;-17917 480;-17717 395 1;-17512 310;-17291 267;-17051 267 1;-16808 267;-16586 310;-16386 395 1;-16182 480;-16006 600;-15861 754 1;-15716 906;-15603 1092;-15523 1310 1;-15442 1526;-15401 1768;-15401 2033 1;-15401 2298;-15440 2540;-15517 2763 1;-15593 2980;-15703 3168;-15848 3324 1;-15993 3479;-16168 3598;-16373 3684 1;-16574 3769;-16800 3810;-17051 3810 18;-17051 2993 1;-16834 2993;-16668 2908;-16552 2736 1;-16433 2561;-16373 2326;-16373 2033 1;-16373 1738;-16433 1509;-16552 1342 1;-16668 1171;-16834 1085;-17051 1085 1;-17269 1085;-17437 1171;-17557 1342 1;-17675 1509;-17736 1738;-17736 2033 1;-17736 2326;-17675 2561;-17557 2736 1;-17437 2908;-17269 2993;-17051 2993 18;17090 5390 1;16864 5390;16623 5362;16391 5321 1;16389 5320;16389 5320;16387 5320 1;16169 5284;15963 5240;15758 5170 1;15555 5102;15409 4847;15450 4637 1;15450 4635;15450 4634;15450 4632;15538 4221 1;15549 4165;15526 4109;15478 4079 1;15431 4049;15369 4052;15324 4088 1;15245 4152;15138 4191;15033 4191;14080 4191 1;13930 4193;13775 4107;13692 3983 1;13669 3944;13625 3921;13580 3921 1;13534 3921;13492 3944;13466 3983 1;13385 4107;13233 4191;13083 4191;12131 4191 1;12001 4191;11866 4131;11781 4032 1;11755 4004;11719 3987;11680 3987 1;11642 3987;11606 4004;11581 4032 1;11496 4131;11364 4191;11234 4191;10281 4191 1;10048 4193;9815 3957;9815 3724;9815 1796 1;9815 1758;9798 1721;9770 1697 1;9741 1670;9703 1658;9665 1663 1;9608 1670;9553 1668;9498 1655 1;9433 1638;9341 1618;9227 1597 1;9221 1595;9217 1593;9210 1592 1;9161 1582;9142 1582;9157 1584 1;9118 1578;9078 1590;9048 1615 1;9018 1640;9001 1678;9001 1716 1;9001 1716;9001 3724;9001 3724 1;9001 3957;8768 4193;8535 4191;7582 4191 1;7424 4191;7262 4099;7183 3962 1;7162 3926;7125 3900;7082 3894 1;7040 3889;6999 3902;6969 3932 1;6935 3966;6899 3996;6855 4016 1;6768 4059;6679 4092;6574 4124 1;6467 4157;6364 4178;6253 4195 1;6244 4197;6236 4199;6226 4202 1;6107 4227;5991 4240;5870 4254 1;5738 4267;5601 4279;5461 4279 1;5121 4279;4808 4231;4520 4120 1;4514 4118;4506 4114;4498 4110 1;4220 3998;3977 3840;3778 3636 1;3717 3566;3663 3494;3609 3412 1;3571 3361;3503 3344;3445 3371 1;3389 3396;3357 3459;3370 3521 1;3404 3711;3293 3930;3120 4016 1;3034 4060;2945 4092;2837 4124 1;2732 4157;2628 4178;2517 4195 1;2508 4197;2499 4199;2490 4202 1;2372 4227;2256 4240;2134 4254 1;2001 4267;1866 4279;1726 4279 1;1386 4279;1071 4231;785 4120 1;778 4118;771 4114;761 4110 1;562 4029;383 3917;218 3782 1;186 3757;145 3748;105 3757 1;64 3765;32 3793;13 3829 1;-35 3929;-124 4013;-229 4054 1;-362 4109;-512 4164;-678 4204 1;-871 4251;-1073 4266;-1303 4266 1;-1606 4266;-1876 4227;-2127 4126 1;-2355 4031;-2561 3892;-2717 3706 1;-2719 3701;-2721 3699;-2719 3702 1;-2716 3707;-2718 3706;-2721 3700 1;-2724 3694;-2734 3667;-2764 3624 1;-2799 3577;-2859 3558;-2915 3576 1;-2969 3594;-3007 3646;-3006 3704;-3006 3724 1;-3006 3957;-3239 4193;-3473 4191;-4427 4191 1;-4577 4193;-4730 4107;-4814 3983 1;-4838 3944;-4880 3921;-4927 3921 1;-4972 3921;-5013 3944;-5038 3983 1;-5122 4107;-5274 4191;-5421 4191;-6377 4191 1;-6533 4191;-6696 4099;-6777 3962 1;-6796 3926;-6833 3900;-6875 3894 1;-6916 3889;-6958 3902;-6988 3932 1;-7021 3966;-7059 3996;-7101 4016 1;-7101 4017;-7104 4016;-7104 4016 1;-7191 4059;-7278 4092;-7384 4124 1;-7489 4157;-7592 4178;-7704 4195 1;-7714 4197;-7722 4199;-7729 4199 1;-7731 4201;-7733 4202;-7735 4204 1;-7849 4227;-7966 4240;-8088 4254 1;-8221 4267;-8355 4279;-8496 4279 1;-8836 4279;-9151 4231;-9437 4120 1;-9444 4118;-9450 4114;-9461 4110 1;-9737 3998;-9980 3840;-10181 3635 1;-10185 3631;-10185 3628;-10186 3624 1;-10218 3591;-10250 3536;-10300 3470 1;-10333 3425;-10393 3404;-10450 3423 1;-10505 3440;-10541 3493;-10541 3549 1;-10541 3549;-10541 3724;-10541 3724 1;-10541 3957;-10774 4193;-11008 4191;-11961 4191 1;-12194 4193;-12427 3957;-12427 3724;-12427 1796 1;-12427 1758;-12444 1721;-12472 1697 1;-12501 1670;-12539 1658;-12577 1663 1;-12634 1670;-12689 1668;-12744 1655 1;-12809 1638;-12901 1618;-13015 1597 1;-13021 1595;-13026 1593;-13032 1592 1;-13081 1582;-13101 1582;-13086 1584 1;-13124 1578;-13164 1590;-13194 1615 1;-13224 1640;-13240 1678;-13240 1716;-13240 3724 1;-13240 3957;-13474 4193;-13707 4191;-14660 4191 1;-14893 4193;-15126 3957;-15126 3724;-15126 3532 1;-15126 3476;-15164 3423;-15218 3406 1;-15273 3387;-15335 3408;-15368 3455 1;-15420 3526;-15463 3591;-15511 3643 1;-15517 3651;-15517 3652;-15518 3656 1;-15703 3851;-15933 4002;-16189 4110 1;-16458 4224;-16748 4279;-17051 4279 1;-17346 4279;-17629 4225;-17889 4120 1;-17895 4118;-17902 4114;-17909 4112 1;-18160 4005;-18392 3849;-18580 3651 1;-18586 3643;-18587 3641;-18588 3637 1;-18776 3434;-18919 3196;-19017 2931 1;-19017 2931;-19017 2933;-19019 2927 1;-19122 2648;-19176 2350;-19176 2033 1;-19176 1718;-19123 1419;-19019 1142;-19019 1139 1;-18913 875;-18770 642;-18587 446 1;-18407 241;-18054 35;-17897 -33 1;-17894 -33;-17891 -35;-17889 -37 1;-17628 -143;-17344 -200;-17051 -200 1;-16755 -200;-16468 -143;-16206 -33 1;-15946 76;-15707 235;-15518 433 1;-15470 487;-15425 560;-15368 639 1;-15335 685;-15273 705;-15218 687 1;-15164 669;-15126 617;-15126 559;-15126 538 1;-15126 435;-15088 329;-15023 247 1;-14983 198;-14983 129;-15023 80 1;-15088 -1;-15126 -108;-15126 -211;-15126 -1586 1;-15126 -1644;-15164 -1696;-15218 -1714 1;-15273 -1733;-15335 -1712;-15368 -1666 1;-15420 -1594;-15463 -1529;-15506 -1483 1;-15508 -1478;-15511 -1474;-15517 -1465 1;-15701 -1271;-15928 -1115;-16189 -1005 1;-16463 -889;-16753 -841;-17051 -841 1;-17341 -841;-17624 -888;-17889 -995 1;-17895 -997;-17902 -1001;-17909 -1003 1;-18165 -1113;-18394 -1273;-18578 -1463 1;-18582 -1468;-18582 -1470;-18584 -1474 1;-18586 -1476;-18587 -1479;-18588 -1483 1;-18776 -1684;-18919 -1924;-19019 -2192 1;-19122 -2472;-19176 -2770;-19176 -3085 1;-19176 -3401;-19122 -3698;-19019 -3973 1;-18915 -4238;-18774 -4473;-18586 -4675 1;-18582 -4680;-18580 -4682;-18580 -4684 1;-18577 -4686;-18573 -4688;-18572 -4690 1;-18384 -4883;-18152 -5043;-17897 -5151 1;-17894 -5153;-17891 -5154;-17889 -5156 1;-17628 -5263;-17344 -5317;-17051 -5317 1;-16755 -5317;-16468 -5263;-16204 -5150 1;-15943 -5038;-15707 -4885;-15518 -4684 1;-15468 -4631;-15425 -4560;-15368 -4481 1;-15335 -4433;-15273 -4413;-15218 -4431 1;-15164 -4449;-15126 -4502;-15126 -4560 1;-15126 -4560;-15126 -4648;-15126 -4648 1;-15126 -4840;-14978 -5038;-14794 -5093 1;-14694 -5123;-14598 -5144;-14499 -5165 1;-14499 -5165;-14477 -5168;-14452 -5173 1;-14340 -5198;-14217 -5219;-14085 -5240 1;-13922 -5266;-13842 -5266;-13682 -5281 1;-13547 -5294;-13415 -5306;-13286 -5306 1;-12964 -5306;-12660 -5253;-12380 -5145 1;-12373 -5143;-12371 -5143;-12369 -5143 1;-12101 -5041;-11864 -4883;-11665 -4681 1;-11657 -4671;-11653 -4668;-11649 -4665 1;-11536 -4548;-11441 -4412;-11358 -4260 1;-11334 -4218;-11289 -4192;-11242 -4192 1;-11193 -4192;-11148 -4218;-11125 -4260 1;-11028 -4434;-10923 -4593;-10791 -4726 1;-10600 -4917;-10372 -5065;-10128 -5165 1;-10125 -5165;-10124 -5166;-10118 -5168 1;-9872 -5263;-9613 -5317;-9350 -5317 1;-8783 -5317;-8251 -5120;-7882 -4730 1;-7846 -4691;-7812 -4641;-7767 -4581 1;-7733 -4538;-7676 -4519;-7622 -4536 1;-7569 -4553;-7532 -4600;-7529 -4656 1;-7526 -4846;-7376 -5039;-7192 -5093 1;-6999 -5148;-6777 -5186;-6518 -5226 1;-6513 -5227;-6510 -5229;-6505 -5231 1;-6226 -5276;-5933 -5306;-5630 -5306 1;-5305 -5306;-5020 -5259;-4754 -5153 1;-4499 -5054;-4271 -4908;-4106 -4706 1;-3948 -4513;-3839 -4282;-3777 -4034 1;-3719 -3803;-3694 -3557;-3694 -3294;-3694 -1395 1;-3694 -1161;-3927 -927;-4160 -927;-5113 -927 1;-5263 -927;-5419 -1011;-5502 -1136 1;-5525 -1174;-5569 -1198;-5613 -1198 1;-5660 -1198;-5701 -1174;-5727 -1136 1;-5808 -1012;-5960 -927;-6110 -927;-7063 -927 1;-7220 -929;-7380 -1021;-7459 -1158 1;-7479 -1194;-7516 -1219;-7558 -1224 1;-7599 -1231;-7641 -1216;-7671 -1186 1;-7706 -1151;-7744 -1121;-7790 -1100 1;-7898 -1046;-7976 -1023;-8071 -995 1;-8178 -963;-8281 -942;-8392 -924 1;-8400 -923;-8409 -921;-8417 -920 1;-8419 -918;-8419 -916;-8421 -916 1;-8527 -894;-8644 -874;-8775 -861 1;-8917 -846;-9054 -841;-9183 -841 1;-9523 -841;-9837 -888;-10125 -999 1;-10132 -1003;-10138 -1004;-10148 -1010 1;-10205 -1033;-10261 -1066;-10333 -1102 1;-10380 -1126;-10438 -1119;-10478 -1085 1;-10520 -1051;-10537 -996;-10520 -944 1;-10481 -826;-10453 -709;-10453 -616 1;-10453 -441;-10525 -246;-10637 -67 1;-10667 -20;-10665 40;-10633 84 1;-10575 164;-10541 262;-10541 359;-10541 559 1;-10541 617;-10505 669;-10450 687 1;-10393 705;-10333 685;-10300 639 1;-10233 547;-10171 461;-10103 392 1;-9914 202;-9685 53;-9442 -45 1;-9439 -46;-9435 -48;-9433 -50 1;-9432 -50;-9431 -50;-9429 -50 1;-9183 -144;-8927 -200;-8662 -200 1;-8096 -200;-7562 -1;-7194 386;-7194 386 1;-7192 387;-7194 385;-7194 388 1;-7192 392;-7192 389;-7192 390;-7143 453;-7079 538 1;-7046 581;-6988 598;-6935 581 1;-6882 566;-6846 519;-6843 463 1;-6837 273;-6688 79;-6505 25 1;-6313 -30;-6091 -67;-5824 -110 1;-5540 -156;-5246 -186;-4944 -186 1;-4619 -186;-4332 -142;-4066 -35 1;-3813 64;-3584 211;-3419 414 1;-3404 432;-3387 477;-3347 534 1;-3315 585;-3254 609;-3195 590 1;-3139 574;-3101 519;-3103 459 1;-3103 459;-3103 -482;-3103 -482 1;-3103 -693;-2915 -908;-2706 -941;-1753 -1091 1;-1501 -1129;-1215 -886;-1215 -633;-1215 -241 1;-1215 -166;-1155 -108;-1082 -108;-537 -108 1;-323 -106;-109 82;-79 292 1;-70 342;-35 384;11 399 1;60 415;111 402;147 367 1;333 190;549 49;786 -47 1;1037 -144;1294 -200;1559 -200 1;2126 -200;2658 -1;3027 388 1;3144 513;3239 654;3317 805 1;3338 848;3383 875;3432 875 1;3481 876;3526 852;3550 808 1;3642 654;3737 510;3853 392 1;4045 202;4273 53;4521 -47;4521 -47 1;4521 -48;4520 -46;4522 -47 1;4529 -50;4527 -50;4527 -50 1;4773 -144;5031 -200;5295 -200 1;5861 -200;6394 -1;6763 388 1;6802 432;6839 480;6882 538 1;6914 581;6969 600;7022 585 1;7074 572;7112 529;7119 476 1;7140 310;7271 152;7427 97 1;7615 32;7831 -23;8080 -81 1;8085 -82;8083 -82;8083 -82 1;8360 -146;8654 -178;8965 -178 1;9028 -178;9099 -173;9168 -166 1;9182 -165;9190 -163;9205 -161 1;9278 -156;9347 -148;9415 -136 1;9437 -133;9445 -133;9447 -133 1;9507 -125;9570 -116;9635 -103 1;9681 -95;9728 -112;9758 -146 1;9788 -181;9800 -230;9785 -274 1;9745 -394;9718 -512;9718 -616 1;9718 -878;9838 -1186;10043 -1369 1;10048 -1372;10051 -1374;10058 -1380 1;10243 -1538;10508 -1644;10751 -1644 1;10994 -1644;11257 -1542;11452 -1373 1;11458 -1367;11460 -1365;11466 -1364 1;11671 -1177;11789 -869;11789 -616 1;11789 -441;11717 -246;11606 -67 1;11576 -18;11579 44;11619 94 1;11644 132;11678 151;11716 155 1;11753 159;11791 147;11817 122 1;11871 77;11935 44;12001 25 1;12194 -30;12416 -67;12682 -110 1;12967 -156;13260 -186;13564 -186 1;13889 -186;14173 -142;14439 -35 1;14695 64;14923 211;15088 414 1;15181 529;15253 669;15316 817 1;15337 863;15381 893;15431 897 1;15481 900;15529 874;15555 830 1;15641 673;15739 530;15862 404 1;16061 212;16299 65;16570 -35 1;16849 -140;17147 -186;17457 -186 1;17585 -186;17713 -174;17847 -161 1;17854 -161;17842 -163;17865 -161 1;17990 -153;18110 -138;18237 -120 1;18355 -103;18475 -82;18593 -58 1;18605 -52;18617 -50;18628 -50 1;18743 -26;18848 -1;18940 25 1;19124 80;19273 279;19273 472;19273 3299 1;19273 3915;19111 4466;18724 4844 1;18718 4850;18716 4852;18709 4858 1;18305 5243;17742 5390;17090 5390 18;-1305 3799 1;-1102 3799;-928 3780;-787 3748 1;-646 3712;-518 3673;-404 3626;-539 2884 1;-627 2918;-728 2946;-838 2966 1;-945 2984;-1050 2993;-1151 2993 1;-1365 2993;-1508 2933;-1581 2813 1;-1648 2694;-1683 2535;-1683 2333;-1683 1150;-539 1150;-539 357;-1683 357;-1683 -634;-2636 -481;-2636 2346 1;-2636 2565;-2618 2763;-2579 2941 1;-2536 3121;-2463 3274;-2361 3402 1;-2259 3526;-2123 3624;-1951 3696 1;-1781 3764;-1566 3799;-1305 3799 18;10752 -52 1;10906 -52;11038 -101;11149 -200 1;11264 -303;11323 -441;11323 -616 1;11323 -791;11264 -927;11149 -1026 1;11038 -1128;10906 -1179;10752 -1179 1;10600 -1179;10465 -1128;10350 -1026 1;10238 -927;10183 -791;10183 -616 1;10183 -441;10238 -303;10350 -200 1;10465 -101;10600 -52;10752 -52 18;10279 3722;11233 3722;11233 357;10279 357;10279 3722 18;-11963 3722;-11009 3722;-11009 357;-11963 357;-11963 3722 18;7581 3722;8534 3722;8534 1150 1;8575 1137;8637 1126;8718 1118 1;8804 1105;8877 1098;8937 1098 1;9069 1098;9191 1112;9306 1137 1;9424 1158;9523 1180;9608 1201;9768 414 1;9717 392;9655 375;9583 363 1;9510 345;9437 333;9365 325 1;9292 312;9220 303;9146 299 1;9075 290;9013 286;8962 286 1;8677 286;8415 314;8182 369 1;7950 420;7750 476;7581 536;7581 3722 18;12129 3722;13082 3722;13082 1125 1;13142 1115;13210 1110;13287 1105 1;13363 1097;13435 1092;13504 1092 1;13725 1092;13876 1156;13958 1285 1;14039 1411;14080 1630;14080 1937;14080 3722;15033 3722;15033 1821 1;15033 1592;15010 1381;14963 1195 1;14916 1007;14836 845;14726 709 1;14614 572;14463 467;14272 395 1;14083 318;13847 280;13562 280 1;13280 280;13012 301;12756 344 1;12499 382;12290 425;12129 472;12129 3722 18;15729 3616 1;15737 3607;15749 3596;15758 3588 1;15784 3562;15799 3528;15799 3491 1;15799 3491;15799 3489;15799 3487;15794 3440;15767 3400 1;15769 3404;15763 3384;15737 3354 1;15711 3321;15671 3303;15623 3306 1;15549 3318;15499 3374;15499 3441 1;15499 3441;15499 3524;15499 3524 1;15501 3579;15533 3628;15585 3648 1;15634 3667;15691 3656;15729 3616 18;-14662 3722;-13708 3722;-13708 1150 1;-13667 1137;-13605 1126;-13524 1118 1;-13439 1105;-13366 1098;-13306 1098 1;-13174 1098;-13051 1112;-12936 1137 1;-12819 1158;-12719 1180;-12634 1201;-12474 414 1;-12526 392;-12587 375;-12660 363 1;-12733 345;-12806 333;-12878 325 1;-12951 312;-13022 303;-13096 299 1;-13167 290;-13229 286;-13280 286 1;-13565 286;-13827 314;-14060 369 1;-14292 420;-14492 476;-14662 536;-14662 3722 18;-6378 3722;-5425 3722;-5425 1125 1;-5365 1115;-5297 1110;-5220 1105 1;-5143 1097;-5070 1092;-5002 1092 1;-4780 1092;-4628 1156;-4549 1285 1;-4467 1411;-4427 1630;-4427 1937;-4427 3722;-3474 3722;-3474 1821 1;-3474 1592;-3497 1381;-3544 1195 1;-3591 1007;-3669 845;-3781 709 1;-3890 572;-4042 467;-4235 395 1;-4422 318;-4658 280;-4945 280 1;-5227 280;-5495 301;-5751 344 1;-6007 382;-6217 425;-6378 472;-6378 3722 18;17092 4924 1;17677 4924;18107 4790;18385 4522 1;18665 4257;18806 3849;18806 3299;18806 472 1;18722 446;18624 422;18512 402 1;18405 375;18292 354;18173 337 1;18053 320;17931 307;17809 299 1;17689 286;17572 280;17457 280 1;17187 280;16947 320;16734 402 1;16521 478;16339 589;16189 733 1;16044 878;15934 1053;15857 1259 1;15781 1458;15743 1683;15743 1930 1;15743 2424;15866 2811;16113 3087 1;16361 3361;16717 3498;17182 3498 1;17309 3498;17427 3487;17533 3466 1;17644 3441;17750 3404;17854 3357;17854 3479 1;17854 3666;17796 3819;17680 3939 1;17570 4059;17377 4119;17105 4119 1;16896 4119;16707 4101;16543 4067 1;16379 4032;16224 3986;16074 3926;15908 4725 1;16083 4785;16272 4832;16477 4867 1;16682 4905;16887 4924;17092 4924 18;1726 3810 1;1846 3810;1965 3804;2084 3793 1;2204 3779;2314 3763;2417 3741 1;2524 3724;2619 3703;2705 3677 1;2790 3651;2859 3626;2910 3601;2782 2826 1;2675 2873;2538 2912;2365 2948 1;2199 2978;2029 2993;1854 2993 1;1581 2993;1364 2934;1201 2820 1;1039 2704;947 2550;926 2353;3088 2353 1;3094 2301;3097 2245;3102 2180 1;3105 2112;3109 2050;3109 1995 1;3109 1426;2968 998;2692 709 1;2416 414;2038 267;1559 267 1;1354 267;1154 305;958 382 1;766 459;596 572;446 722 1;298 870;177 1058;81 1285 1;-9 1507;-52 1766;-52 2065 1;-52 2311;-19 2542;49 2756 1;123 2969;231 3154;376 3312 1;524 3466;712 3588;933 3677 1;1159 3765;1422 3810;1726 3810 18;-8496 3810 1;-8376 3810;-8257 3804;-8137 3793 1;-8017 3779;-7908 3763;-7804 3741 1;-7697 3724;-7603 3703;-7516 3677 1;-7431 3651;-7363 3626;-7311 3601;-7440 2826 1;-7546 2873;-7684 2912;-7855 2948 1;-8022 2978;-8193 2993;-8368 2993 1;-8640 2993;-8857 2934;-9020 2820 1;-9182 2704;-9273 2550;-9296 2353;-7132 2353 1;-7128 2301;-7124 2245;-7119 2180 1;-7116 2112;-7113 2050;-7113 1995 1;-7113 1426;-7252 998;-7529 709 1;-7806 414;-8184 267;-8662 267 1;-8867 267;-9067 305;-9264 382 1;-9455 459;-9625 572;-9775 722 1;-9923 870;-10045 1058;-10140 1285 1;-10230 1507;-10273 1766;-10273 2065 1;-10273 2311;-10240 2542;-10171 2756 1;-10098 2969;-9990 3154;-9845 3312 1;-9696 3466;-9510 3588;-9288 3677 1;-9062 3765;-8798 3810;-8496 3810 18;-12484 -146 1;-12454 -181;-12442 -230;-12457 -274 1;-12497 -394;-12524 -512;-12524 -616 1;-12524 -619;-12502 -670;-12491 -753 1;-12484 -794;-12496 -836;-12524 -866 1;-12554 -896;-12594 -911;-12640 -905 1;-12765 -882;-12886 -866;-13002 -866 1;-13009 -866;-13043 -876;-13102 -878 1;-13139 -879;-13174 -866;-13199 -841 1;-13225 -814;-13240 -781;-13240 -745 1;-13240 -745;-13240 -300;-13240 -300 1;-13240 -230;-13188 -171;-13119 -166 1;-13079 -161;-13064 -165;-13074 -166;-13056 -164;-13037 -161 1;-12964 -156;-12895 -148;-12827 -136 1;-12806 -133;-12797 -133;-12795 -133;-12795 -132;-12793 -132;-12795 -133 1;-12735 -125;-12673 -116;-12607 -103 1;-12561 -95;-12514 -112;-12484 -146 18;-14662 -213;-13708 -213;-13708 -1479 1;-13602 -1432;-13492 -1395;-13377 -1371 1;-13261 -1346;-13137 -1333;-13006 -1333 1;-12765 -1333;-12556 -1376;-12373 -1461 1;-12189 -1546;-12034 -1666;-11911 -1819 1;-11788 -1972;-11694 -2156;-11629 -2368 1;-11566 -2582;-11535 -2819;-11535 -3078 1;-11535 -3348;-11574 -3591;-11657 -3808 1;-11732 -4027;-11846 -4210;-11995 -4358 1;-12144 -4513;-12328 -4630;-12544 -4711 1;-12763 -4796;-13009 -4838;-13287 -4838 1;-13402 -4838;-13524 -4833;-13652 -4819 1;-13776 -4811;-13898 -4798;-14022 -4781 1;-14142 -4765;-14257 -4743;-14369 -4716 1;-14478 -4697;-14576 -4673;-14662 -4646;-14662 -213 18;-17051 3810 1;-16800 3810;-16574 3769;-16373 3684 1;-16168 3598;-15993 3479;-15848 3324 1;-15703 3168;-15593 2980;-15517 2763 1;-15440 2540;-15401 2298;-15401 2033 1;-15401 1768;-15442 1526;-15523 1310 1;-15603 1092;-15716 906;-15861 754 1;-16006 600;-16182 480;-16386 395 1;-16586 310;-16808 267;-17051 267 1;-17291 267;-17512 310;-17717 395 1;-17917 480;-18092 600;-18242 754 1;-18387 906;-18502 1092;-18587 1310 1;-18669 1526;-18708 1768;-18708 2033 1;-18708 2298;-18669 2540;-18587 2763 1;-18505 2980;-18392 3168;-18247 3324 1;-18102 3479;-17931 3598;-17729 3684 1;-17525 3769;-17299 3810;-17051 3810 18;5463 3810 1;5581 3810;5701 3804;5821 3793 1;5939 3779;6051 3763;6152 3741 1;6259 3724;6356 3703;6441 3677 1;6526 3651;6594 3626;6646 3601;6518 2826 1;6411 2873;6272 2912;6103 2948 1;5936 2978;5765 2993;5590 2993 1;5318 2993;5100 2934;4938 2820 1;4775 2704;4683 2550;4663 2353;6826 2353 1;6829 2301;6834 2245;6837 2180 1;6843 2112;6844 2050;6844 1995 1;6844 1426;6706 998;6428 709 1;6151 414;5773 267;5297 267 1;5091 267;4891 305;4695 382 1;4503 459;4332 572;4183 722 1;4033 870;3911 1058;3819 1285 1;3729 1507;3684 1766;3684 2065 1;3684 2311;3719 2542;3787 2756 1;3858 2969;3967 3154;4112 3312 1;4262 3466;4446 3588;4668 3677 1;4895 3765;5160 3810;5463 3810 18;-1065 2521 1;-1018 2518;-969 2518;-911 2508 1;-910 2508;-909 2506;-907 2505 1;-830 2489;-762 2473;-704 2450 1;-702 2450;-700 2450;-698 2450 1;-670 2439;-642 2426;-612 2422 1;-545 2409;-500 2350;-503 2283 1;-509 2203;-520 2131;-520 2067 1;-520 1973;-502 1875;-490 1763 1;-487 1725;-500 1688;-525 1660 1;-550 1633;-586 1616;-623 1616;-1106 1616 1;-1170 1630;-1217 1687;-1215 1750;-1215 2333 1;-1215 2360;-1211 2386;-1206 2405 1;-1198 2474;-1136 2527;-1065 2521 18;-11490 -52 1;-11336 -52;-11204 -101;-11093 -200 1;-10978 -303;-10920 -441;-10920 -616 1;-10920 -791;-10978 -927;-11093 -1026 1;-11204 -1128;-11336 -1179;-11490 -1179 1;-11642 -1179;-11777 -1128;-11893 -1026 1;-12004 -927;-12059 -791;-12059 -616 1;-12059 -441;-12004 -303;-11893 -200 1;-11777 -101;-11642 -52;-11490 -52 18;-17051 -1307 1;-16800 -1307;-16574 -1349;-16373 -1434 1;-16168 -1521;-15993 -1639;-15848 -1794 1;-15703 -1951;-15593 -2139;-15517 -2355 1;-15440 -2579;-15401 -2820;-15401 -3085 1;-15401 -3350;-15442 -3591;-15523 -3808 1;-15603 -4027;-15716 -4211;-15861 -4365 1;-16006 -4518;-16182 -4638;-16386 -4723 1;-16586 -4808;-16808 -4851;-17051 -4851 1;-17291 -4851;-17512 -4808;-17717 -4723 1;-17917 -4638;-18092 -4518;-18242 -4365 1;-18387 -4211;-18502 -4027;-18587 -3808 1;-18669 -3591;-18708 -3350;-18708 -3085 1;-18708 -2820;-18669 -2579;-18587 -2355 1;-18505 -2139;-18392 -1951;-18247 -1794 1;-18102 -1639;-17931 -1521;-17729 -1434 1;-17525 -1349;-17299 -1307;-17051 -1307 18;-7064 -1395;-6112 -1395;-6112 -3993 1;-6052 -4004;-5983 -4008;-5907 -4013 1;-5830 -4021;-5759 -4027;-5690 -4027 1;-5468 -4027;-5317 -3961;-5235 -3833 1;-5155 -3707;-5113 -3488;-5113 -3182;-5113 -1395;-4160 -1395;-4160 -3296 1;-4160 -3527;-4183 -3737;-4230 -3923 1;-4277 -4111;-4357 -4273;-4467 -4410 1;-4579 -4547;-4730 -4652;-4921 -4723 1;-5110 -4800;-5347 -4838;-5631 -4838 1;-5913 -4838;-6181 -4818;-6438 -4774 1;-6694 -4736;-6903 -4693;-7064 -4646;-7064 -1395 18;-11099 -1644 1;-11068 -1689;-11067 -1748;-11095 -1794 1;-11120 -1834;-11128 -1842;-11120 -1827 1;-11143 -1872;-11189 -1899;-11242 -1899 1;-11246 -1897;-11251 -1895;-11258 -1894 1;-11298 -1887;-11332 -1862;-11353 -1827 1;-11358 -1822;-11364 -1814;-11375 -1799 1;-11394 -1761;-11396 -1718;-11379 -1679 1;-11362 -1639;-11328 -1611;-11287 -1602 1;-11257 -1596;-11240 -1594;-11242 -1594 1;-11188 -1581;-11133 -1601;-11099 -1644 18;-9182 -1307 1;-9063 -1307;-8944 -1314;-8824 -1326 1;-8706 -1339;-8594 -1356;-8492 -1378 1;-8385 -1395;-8289 -1416;-8204 -1440 1;-8119 -1468;-8051 -1493;-7999 -1517;-8127 -2292 1;-8234 -2246;-8372 -2205;-8542 -2171 1;-8708 -2141;-8881 -2126;-9055 -2126 1;-9327 -2126;-9545 -2184;-9707 -2299 1;-9870 -2413;-9961 -2569;-9982 -2766;-7819 -2766 1;-7816 -2817;-7810 -2873;-7808 -2939 1;-7803 -3007;-7801 -3069;-7801 -3123 1;-7801 -3692;-7939 -4120;-8217 -4410 1;-8494 -4705;-8872 -4851;-9349 -4851 1;-9553 -4851;-9754 -4813;-9950 -4736 1;-10141 -4660;-10314 -4547;-10462 -4396 1;-10611 -4248;-10733 -4060;-10827 -3833 1;-10917 -3612;-10962 -3352;-10962 -3054 1;-10962 -2807;-10926 -2575;-10858 -2362 1;-10787 -2149;-10678 -1964;-10533 -1806 1;-10383 -1652;-10198 -1530;-9976 -1440 1;-9750 -1352;-9485 -1307;-9182 -1307 18;16428 5964 1;15493 5816;15156 5633;14905 5136 1;14842 5010;14767 4893;14739 4875 1;14686 4843;13829 4769;13588 4777 1;13511 4779;12673 4786;11726 4791;10003 4801;9412 4491;8810 4801;8189 4815 1;7703 4826;7501 4814;7261 4759 1;6957 4690;6950 4690;6521 4785 1;6170 4863;5974 4881;5488 4880;5457 4880 1;4811 4880;4563 4831;4074 4600 1;3912 4524;3745 4461;3703 4461 1;3660 4461;3540 4504;3437 4556 1;2881 4838;1858 4973;1148 4859 1;1005 4835;741 4765;560 4703 1;206 4579;201 4579;-280 4737 1;-640 4854;-1173 4915;-1564 4883 1;-1876 4858;-2085 4806;-2522 4644 1;-2687 4583;-2700 4585;-2962 4691 1;-3221 4796;-3260 4801;-3912 4799 1;-4286 4797;-4700 4791;-4832 4785 1;-4964 4779;-5368 4786;-5729 4802 1;-6286 4827;-6433 4820;-6697 4760 1;-7003 4690;-7012 4690;-7420 4779 1;-8263 4964;-9117 4926;-9699 4679 1;-10091 4513;-10177 4511;-10482 4666;-10748 4801;-12238 4801;-12541 4645;-12844 4489;-13138 4645;-13432 4801;-14922 4801;-15208 4663 1;-15529 4507;-15467 4502;-16152 4744 1;-16831 4984;-17621 4945;-18238 4642 1;-19010 4261;-19482 3685;-19691 2865 1;-19908 2013;-19801 1156;-19391 468 1;-19227 194;-19067 14;-18762 -238 1;-18460 -488;-18462 -552;-18772 -793 1;-19030 -993;-19347 -1393;-19507 -1720 1;-19700 -2113;-19789 -2552;-19789 -3099 1;-19788 -3688;-19727 -3966;-19489 -4450 1;-19192 -5057;-18572 -5590;-17912 -5806 1;-17374 -5984;-16546 -5988;-15770 -5633;-15447 -5485;-15239 -5576 1;-14740 -5794;-14092 -5908;-13352 -5909 1;-12569 -5909;-12118 -5779;-11532 -5381 1;-11433 -5314;-11315 -5259;-11270 -5259 1;-11225 -5259;-11047 -5352;-10875 -5465 1;-10015 -6029;-9078 -6079;-8023 -5616;-7813 -5524;-7443 -5648 1;-6638 -5917;-5482 -5996;-4840 -5824 1;-4236 -5663;-3768 -5339;-3467 -4873 1;-3148 -4378;-3090 -4073;-3064 -2779 1;-3052 -2196;-3026 -1687;-3006 -1649 1;-2959 -1560;-2787 -1559;-2312 -1644 1;-2114 -1679;-1835 -1709;-1691 -1709 1;-1229 -1712;-888 -1486;-654 -1024 1;-541 -799;-528 -788;-245 -681 1;187 -519;243 -518;668 -662 1;965 -763;1135 -795;1448 -809 1;2091 -837;2566 -704;3131 -337 1;3264 -250;3400 -179;3432 -179 1;3464 -179;3619 -259;3777 -358 1;4137 -581;4287 -644;4688 -741 1;5298 -888;5896 -821;6519 -534;6870 -372;7177 -478 1;7625 -633;8076 -733;8550 -781 1;9020 -829;9105 -876;9182 -1133 1;9308 -1554;9694 -1943;10095 -2129 1;10456 -2299;11045 -2279;11428 -2095 1;11880 -1878;12170 -1593;12329 -1108 1;12441 -766;12455 -756;12815 -779 1;14010 -855;14639 -730;15238 -297 1;15327 -232;15435 -179;15479 -179 1;15522 -179;15686 -258;15843 -355 1;16603 -824;17454 -922;18651 -679 1;19204 -567;19417 -469;19628 -230 1;19900 79;19908 145;19908 1951;19908 2068 1;19908 3651;19886 3934;19718 4401 1;19503 5000;19140 5402;18544 5703 1;18007 5974;17155 6079;16428 5964 18;17928 5561 1;18700 5363;19232 4845;19456 4071 1;19512 3877;19525 3576;19538 2141 1;19550 942;19541 396;19509 289 1;19438 50;19214 -170;18973 -238 1;18265 -438;17233 -498;16751 -367 1;16348 -257;15993 -62;15706 208;15444 456;15306 271 1;15116 17;14791 -194;14388 -327 1;14080 -428;13997 -439;13508 -436 1;13048 -433;12442 -364;12083 -274 1;12000 -253;11998 -259;12035 -436 1;12080 -647;12030 -945;11908 -1199 1;11636 -1762;10897 -2045;10292 -1817 1;9806 -1635;9468 -1148;9468 -632;9468 -407;9216 -431 1;8866 -464;8256 -399;7783 -280 1;7323 -163;7201 -108;7046 57;6934 175;6772 37 1;5907 -704;4382 -591;3607 271;3424 474;3360 377 1;3119 21;2522 -336;2023 -420 1;1440 -518;756 -394;341 -114;129 29;28 -91 1;-139 -289;-354 -379;-661 -379;-932 -379;-932 -509 1;-933 -750;-1017 -955;-1187 -1124 1;-1437 -1374;-1550 -1391;-2246 -1283 1;-2569 -1232;-2887 -1163;-2953 -1129 1;-3248 -975;-3372 -718;-3372 -258;-3372 65;-3550 -58 1;-3801 -230;-4065 -337;-4405 -402 1;-4781 -475;-5094 -474;-5671 -398 1;-6502 -289;-6766 -202;-6943 24;-7040 149;-7232 3 1;-7499 -201;-7690 -292;-8042 -381 1;-8755 -564;-9576 -395;-10120 47 1;-10278 175;-10291 178;-10328 108 1;-10358 53;-10348 -23;-10289 -176 1;-10246 -291;-10206 -465;-10202 -563 1;-10196 -705;-10182 -736;-10133 -716 1;-9610 -509;-8452 -550;-7836 -799 1;-7626 -884;-7587 -889;-7532 -839 1;-7372 -694;-7184 -659;-6575 -659;-6548 -659 1;-5984 -659;-5837 -684;-5685 -810 1;-5627 -859;-5602 -854;-5492 -772 1;-5374 -686;-5322 -678;-4739 -666 1;-4055 -652;-3888 -680;-3692 -845 1;-3432 -1064;-3432 -1066;-3434 -2479 1;-3436 -3540;-3448 -3805;-3503 -4029 1;-3693 -4791;-4168 -5277;-4922 -5483 1;-5261 -5575;-6112 -5566;-6698 -5463 1;-7272 -5363;-7461 -5289;-7610 -5109;-7726 -4968;-7879 -5088 1;-8814 -5825;-10355 -5685;-11073 -4798;-11235 -4596;-11494 -4860 1;-11783 -5155;-12156 -5370;-12572 -5482 1;-13108 -5626;-14450 -5523;-14960 -5299 1;-15058 -5256;-15175 -5160;-15239 -5069;-15350 -4913;-15611 -5103 1;-16440 -5704;-17556 -5733;-18389 -5176 1;-18763 -4926;-18970 -4689;-19174 -4279 1;-19544 -3536;-19541 -2623;-19167 -1859 1;-18698 -903;-17669 -417;-16549 -623 1;-16267 -675;-15788 -888;-15574 -1057;-15421 -1179;-15405 -589 1;-15394 -169;-15373 26;-15332 89 1;-15235 239;-15319 237;-15504 86 1;-16274 -546;-17402 -634;-18285 -132 1;-19617 624;-19853 2734;-18732 3863 1;-17912 4689;-16505 4772;-15566 4050;-15379 3907;-15333 4019 1;-15270 4170;-15105 4323;-14923 4399 1;-14712 4487;-13672 4487;-13461 4399 1;-13264 4317;-13137 4195;-13048 4001 1;-12982 3860;-12974 3728;-12973 2871;-12973 2823 1;-12972 1843;-12978 1874;-12782 1926;-12692 1950 1;-12692 3973;-12680 4038;-12439 4250 1;-12222 4440;-12067 4471;-11387 4455 1;-10804 4441;-10780 4438;-10624 4329 1;-10492 4238;-10413 4134;-10306 3908 1;-10303 3901;-10213 3963;-10106 4044 1;-9632 4408;-9047 4566;-8312 4528 1;-7825 4503;-7365 4416;-7096 4297 1;-6933 4226;-6914 4225;-6848 4284 1;-6702 4417;-6483 4457;-5908 4459 1;-5355 4461;-5138 4426;-5010 4317 1;-4958 4272;-4918 4279;-4753 4362 1;-4571 4454;-4518 4461;-3955 4460 1;-3267 4459;-3106 4414;-2910 4168;-2797 4027;-2623 4147 1;-2311 4362;-2052 4456;-1635 4505 1;-1021 4577;-348 4454;37 4200;186 4101;437 4238 1;800 4435;1069 4503;1588 4529 1;1962 4547;2134 4535;2508 4464 1;3072 4358;3355 4230;3495 4017 1;3552 3931;3607 3861;3617 3861 1;3627 3861;3710 3925;3802 4004 1;4053 4219;4484 4414;4868 4487 1;5440 4594;6391 4505;6861 4299;7033 4223;7198 4332 1;7357 4438;7382 4441;8005 4453 1;8611 4465;8658 4460;8826 4373 1;8923 4323;9058 4212;9126 4127;9248 3974;9260 2937;9272 1901;9360 1902 1;9541 1903;9543 1914;9556 2969;9568 3974;9690 4127 1;9758 4212;9892 4322;9989 4371 1;10149 4453;10221 4461;10767 4460 1;11260 4459;11397 4446;11528 4387 1;11682 4317;11694 4317;11848 4387 1;11979 4446;12117 4459;12611 4460 1;13162 4461;13230 4453;13396 4369;13577 4278;13732 4359 1;13871 4432;13961 4441;14551 4441;15214 4441;15197 4626 1;15173 4869;15240 5059;15408 5233 1;15568 5397;15799 5483;16366 5586 1;16835 5672;17538 5661;17928 5561;17928 5561 18;-13184 -2139 1;-13282 -2139;-13381 -2152;-13479 -2177 1;-13572 -2202;-13648 -2234;-13708 -2274;-13708 -3993 1;-13662 -4004;-13602 -4008;-13530 -4013 1;-13457 -4021;-13379 -4027;-13293 -4027 1;-13032 -4027;-12837 -3936;-12704 -3756 1;-12572 -3582;-12506 -3348;-12506 -3054 1;-12506 -2443;-12733 -2139;-13184 -2139 18;-13248 -2631 1;-13034 -2631;-12976 -2876;-12976 -3090 1;-12976 -3284;-13054 -3505;-13248 -3506;-13248 -2631 18;17360 2730 1;16930 2730;16714 2463;16714 1930 1;16714 1675;16776 1464;16900 1297 1;17024 1130;17212 1047;17464 1047 1;17548 1047;17623 1052;17687 1060 1;17750 1065;17807 1072;17854 1080;17854 2603 1;17794 2636;17720 2666;17636 2691 1;17550 2718;17459 2730;17360 2730 18;17409 2232;17409 1571 1;17201 1492;17180 1713;17180 1926 1;17178 2120;17192 2338;17409 2232 18; -15193 -49784 1;-14217 -49499;-13415 -49441;-12989 -48519 1;-12566 -47603;-11592 -45828;-12581 -46029 1;-14699 -46461;-17951 -46822;-17192 -48845 1;-16847 -49781;-16020 -50025;-15193 -49784 18; -18644 -49113 1;-16846 -47471;-15545 -46883;-13137 -46518 1;-12026 -46350;-11285 -46033;-10331 -46624 1;-9588 -47084;-8719 -47865;-8212 -47154 1;-7772 -46538;-7013 -45793;-7630 -45353 1;-9702 -43873;-11427 -43443;-13878 -44135 1;-16573 -44897;-18495 -45269;-19915 -47683 1;-20238 -48232;-20561 -48935;-20021 -49272 1;-19643 -49507;-19340 -49380;-18909 -49272 1;-18789 -49242;-18735 -49197;-18644 -49113 18; -14392 -50181 1;-14381 -49674;-14435 -47077;-14246 -46608 1;-14027 -46065;-13484 -45210;-13406 -44631;-12944 -42978; -12152 -46461 1;-12371 -47163;-12539 -47623;-12875 -48268 1;-13136 -48770;-13281 -49147;-13796 -49381; -8047 -47393 1;-8861 -46910;-9236 -46594;-10115 -46243 1;-11056 -45866;-11873 -45554;-12649 -46207 1;-13681 -47074;-14003 -47947;-15310 -48274 1;-15927 -48429;-16204 -47931;-16820 -47771 1;-17810 -47514;-18814 -47421;-19319 -48310;-19769 -49119; -7400 -46782 1;-8361 -45993;-8885 -45517;-10097 -45235 1;-11152 -44989;-12287 -44656;-13216 -45654 1;-13781 -46261;-13659 -46705;-14781 -47249 1;-15525 -47611;-16019 -46869;-16910 -46476 1;-17867 -46054;-18629 -46273;-19499 -46853;-20470 -47789; -7310 -45847 1;-8637 -44962;-9328 -44361;-10905 -44121 1;-11609 -44013;-12029 -44131;-12649 -44481 1;-12883 -44612;-12993 -44687;-13225 -44822 1;-13381 -44913;-13594 -44817;-13638 -44642 1;-13729 -44278;-14245 -44136;-14537 -44373 1;-14929 -44690;-15109 -44875;-15544 -45128 1;-15969 -45376;-16271 -45501;-16749 -45379 1;-17381 -45219;-17745 -44985;-18367 -45181;-19049 -45397; -16623 -46279; -17072 -46764; -14397 -45742 1;-14115 -45417;-13985 -45095;-14103 -44970 1;-14223 -44844;-14555 -44959;-14897 -45229 16; -15275 -45591 1;-15547 -45911;-15670 -46224;-15554 -46346 1;-15439 -46467;-15127 -46365;-14799 -46118 16; -11871 -51383 1;-12194 -50957;-12110 -50349;-11683 -50027 1;-11256 -49703;-10649 -49787;-10326 -50214 1;-10003 -50641;-10087 -51248;-10514 -51571 1;-10941 -51894;-11548 -51810;-11871 -51383 18; -8669 -51265 1;-8967 -51745;-9599 -51893;-10079 -51595 1;-10559 -51297;-10707 -50665;-10409 -50184 1;-10111 -49703;-9479 -49556;-8998 -49854 1;-8518 -50153;-8370 -50784;-8669 -51265 18; -9687 -48591 1;-9115 -48519;-8593 -48925;-8521 -49497 1;-8449 -50069;-8856 -50592;-9428 -50663 1;-10000 -50735;-10523 -50329;-10594 -49757 1;-10665 -49184;-10259 -48662;-9687 -48591 18; -10193 -48866 1;-9783 -49241;-9754 -49877;-10129 -50288 1;-10504 -50698;-11141 -50727;-11551 -50351 1;-11961 -49977;-11990 -49340;-11615 -48929 1;-11240 -48519;-10603 -48491;-10193 -48866 18; -9063 -47648 1;-8585 -47931;-8427 -48547;-8709 -49025 1;-8992 -49502;-9608 -49661;-10086 -49378 1;-10563 -49095;-10721 -48479;-10439 -48001 1;-10157 -47524;-9541 -47365;-9063 -47648 18; -11816 -48004 1;-11539 -47531;-10931 -47373;-10458 -47651 1;-9986 -47928;-9827 -48536;-10105 -49008 1;-10382 -49481;-10990 -49639;-11463 -49361 1;-11935 -49084;-12093 -48477;-11816 -48004 18; -17713 -51067;-15891 -51625;-15183 -49263;-17007 -49375;-17713 -51067 18; -13406 -45295 1;-14143 -44522;-16113 -43986;-15247 -43361 1;-14461 -42794;-13298 -41094;-13201 -42059;-12979 -44291;-13406 -45295 18; -18763 -51061;-18781 -41352; -20529 -42915;-7231 -42840; -11472 -46906 1;-11715 -47233;-12177 -47303;-12505 -47061 1;-12833 -46819;-12902 -46357;-12660 -46029 1;-12418 -45701;-11955 -45631;-11627 -45873 1;-11299 -46115;-11230 -46578;-11472 -46906 18; -13281 -51435 1;-13605 -51008;-13521 -50401;-13093 -50078 1;-12667 -49755;-12059 -49839;-11737 -50265 1;-11414 -50692;-11498 -51299;-11925 -51623 1;-12351 -51945;-12959 -51861;-13281 -51435 18; -25283 -38824; -14902 -45736; -44934 -51808;Click menu View > Overprinting simulation or press F4 to toggle overprinting preview. mapper-0.8.1.1/examples/src/000077500000000000000000000000001325266516600155645ustar00rootroot00000000000000mapper-0.8.1.1/examples/src/complete map.xmap000066400000000000000000174032651325266516600210430ustar00rootroot00000000000000 +proj=utm +datum=WGS84 +zone=32 32 N +proj=latlong +datum=WGS84 PURPLE BLACK RED BLUE BROWN GREEN YELLOW A line joining points of equal height. The standard vertical interval between contours is 2 or 2.5 m. To emphasize the 3-dimensional effect of the contour line image, contour lines shall be represented as continuous lines through all symbols, also building (526.1) and canopy (526.2). However, contour lines shall be cut out for better legibility, if they touch the following symbols: small earth wall (108.1), small knoll (112), small elongated knoll (113), small depression (115), pit or hole (116), prominent landform feature (118), step or edge of paved area (529.1). The relative height difference between neighbouring features must be represented on the map as accurately as possible. Absolute height accuracy is of less importance. It is permissible to alter the height of a contour slightly if this will improve the representation of a feature. This deviation should not exceed 25% of the contour interval and attention must be paid to neighbouring features. The smallest bend in a contour is 0.4 mm from centre to centre of the lines. Every fifth contour shall be drawn with a thicker line. This is an aid to the quick assessment of height difference and the overall shape of the terrain surface. Where an index contour coincides with an area of much detail, it may be shown with symbol contour (101). An intermediate contour line. Form lines are used where more information can be given about the shape of the ground. They are used only where representation is not possible with ordinary contours. Only one form line may be used between neighbouring contours. Slope lines should be drawn on the lower side of a contour line where it is necessary to clarify the direction of slope, e.g. along the line of a re-entrant or in a depression. Contour values may be included to aid assessment of large height differences. They are inserted in the index contours in positions where other detail is not obscured. The figures should be orientated so that the top of the figure is on the higher side of the contour. A steep earth bank is an abrupt change in ground level which can be clearly distinguished from its surroundings, e.g. gravel or sand pits, roads and railway cuttings or embankments. The tags should show the full extent of the slope, but may be omitted if two banks are close together. Impassable banks shall be drawn with the symbol impassable cliff (201). The line width of very high earth banks may be 0.37 mm. A steep earth bank is an abrupt change in ground level which can be clearly distinguished from its surroundings, e.g. gravel or sand pits, roads and railway cuttings or embankments. The tags should show the full extent of the slope, but may be omitted if two banks are close together. Impassable banks shall be drawn with the symbol impassable cliff (201). The line width of very high earth banks may be 0.37 mm. Use this symbol to display the full extent of wide earth banks. A small distinct earth wall, usually man made. The minimum height is 0.5 m. Larger earth walls should be represented with the symbols contour (101), form line (103) or earth bank (106). An erosion gully or trench which is too small to be represented with the symbol earth bank (106), contour (101), index contour (102) or form line (103) is represented by a single line. The line width reflects the size of the gully. The end of the line is pointed. Minimum depth is 1 m. Minimum length is 3 mm on the map. A small erosion gully or trench. Minimum depth is 0.5 m. A small obvious mound or rocky knoll which cannot be drawn to scale with a contour (101), index contour (102) or form line (103). The height of the knoll should be a minimum of 1 m from the surrounding ground. A small obvious elongated knoll which cannot be drawn to scale with a contour (101), index contour (102) or form line (103). The maximum length should be 6 m and the maximum width 2 m. The height of the knoll should be a minimum of 1 m from the surrounding ground. Knolls larger than this shall be shown by contours. The symbol may not be drawn in free form or such that two elongated knoll symbols touch or overlap. A small shallow natural depression or hollow which cannot be represented by the symbol contour (101) or form line (103) is represented by a semicircle. The minimum diameter should be 2 m. The minimum depth from the surrounding ground should be 1 m. The symbol is orientated to north. A pit or hole with distinct steep sides which cannot be represented to scale with the symbol earth bank (106). The minimum diameter shall be 2 m. The minimum depth from the surrounding ground shall be 1 m. The symbol is orientated to north. An area of pits or knolls, which is too complex to be represented in detail. The density of randomly placed dots may vary according to the detail on the ground. An area of pits or knolls, which is too complex to be represented in detail. The density of randomly placed dots may vary according to the detail on the ground. A small landform feature which is significant or prominent. The definition of the symbol shall always be given in the map legend. The symbol is orientated to north. An impassable cliff, quarry or earth bank [see symbol earth bank (106)]. Tags are drawn downwards, showing its full extent from the top line to the foot. For vertical rock faces the tags may be omitted if space is short, e.g. narrow passages between cliffs (the passage should be drawn with a width of at least 0.3 mm). The tags may extend over an area symbol representing detail immediately below the rock face. When a rock face drops straight into water making it impossible to pass under the cliff along the water's edge, the bank line is omitted or the tags shall clearly extend over the bank line. Minimum height is 2 meters. <span style="color:magenta">It is forbidden to cross an impassable cliff! Competitors violating this rule will be disqualified.</span> An impassable cliff, quarry or earth bank [see symbol earth bank (106)]. Tags are drawn downwards, showing its full extent from the top line to the foot. For vertical rock faces the tags may be omitted if space is short, e.g. narrow passages between cliffs (the passage should be drawn with a width of at least 0.3 mm). The tags may extend over an area symbol representing detail immediately below the rock face. When a rock face drops straight into water making it impossible to pass under the cliff along the water's edge, the bank line is omitted or the tags shall clearly extend over the bank line. Minimum height is 2 meters. <span style="color:magenta">It is forbidden to cross an impassable cliff! Competitors violating this rule will be disqualified.</span> For vertical rock faces the tags may be omitted if space is short, e.g. narrow passages between cliffs (the passage should be drawn with a width of at least 0.3 mm). For vertical rock faces the tags may be omitted if space is short, e.g. narrow passages between cliffs (the passage should be drawn with a width of at least 0.3 mm). Use this symbol to display the full extent of a wide cliff. A gigantic boulder, rock pillar or massive cliff shall be represented in plan shape without tags. A small vertical rock face may be shown without tags. If the direction of fall of the rock face is not apparent from the contours or to improve legibility, short tags should be drawn in the direction of the fall. Minimum height is 1 m. For passable rock faces shown without tags the end of the line may be rounded to improve legibility. A small vertical rock face may be shown without tags. If the direction of fall of the rock face is not apparent from the contours or to improve legibility, short tags should be drawn in the direction of the fall. Minimum height is 1 m. For passable rock faces shown without tags the end of the line may be rounded to improve legibility. Should be used if the direction of fall of the rock face is apparent from the contours and the legibility is good. Should be used if the direction of fall of the rock face is apparent from the contours and the legibility is good. Should be used if the direction of fall of the rock face is apparent from the contours and the legibility is good. For passable rock faces shown without tags the end of the line may be rounded to improve legibility. Should be used if the direction of fall of the rock face is apparent from the contours and the legibility is good. For passable rock faces shown without tags the end of the line may be rounded to improve legibility. A rocky pit, hole or mineshaft which may constitute a danger to the competitor. The symbol is orientated to north. A cave is represented by the same symbol as a rocky pit. In this case the symbol shall be orientated to point up the slope as indicated opposite. This symbol should generally not be used in urban areas. The centre of gravity of the symbol marks the opening. <span style="color:magenta">Controls may not be placed inside caves!</span> A small distinct boulder. The minimum height is 1 m. Every boulder marked on the map shall be immediately identifiable on the ground. A particularly large and distinct boulder. Gigantic boulders shall be represented in plan shape with the symbol gigantic boulder or rock pillar (202). An area which is covered with so many blocks of stone that they cannot be marked individually is represented with randomly orientated solid triangles. The runnability is reduced and is indicated by the density of the triangles. A minimum of two triangles shall be used. The triangles can be enlarged by up to 20 %. An area which is covered with so many blocks of stone that they cannot be marked individually is represented with randomly orientated solid triangles. The runnability is reduced and is indicated by the density of the triangles. A minimum of two triangles shall be used. The triangles can be enlarged by up to 20 %. An area of stony or rocky ground which reduces runnability. The dots shall be randomly distributed with density according to the amount of rock. A minimum of three dots shall be used. An area of stony or rocky ground which reduces runnability. The dots shall be randomly distributed with density according to the amount of rock. A minimum of three dots shall be used. An area of soft sandy ground or gravel with no vegetation which reduces runnability. Where an area of sandy ground is open and has good runnability, it is represented with symbol open land (401), open land with scattered trees (402) or paved area (529). An area of runnable rock without earth or vegetation. An area of rock covered with grass, moss or other low vegetation shall be represented according to its openness and runnability (401/402/403/404). A water-filled pit or an area of water which is too small to be shown to scale. The symbol is orientated to north. An area of deep water such as a lake, pond, river or fountain which may constitute a danger to the competitor or has forbidden access. The dark blue colour and the bordering black line indicates that the feature cannot or shall not be crossed. The minimum dimension is 1 mm². <span style="color:magenta">It is forbidden to cross an impassable body of water! Competitors violating this rule will be disqualified.</span> An area of deep water such as a lake, pond, river or fountain which may constitute a danger to the competitor or has forbidden access. The dark blue colour and the bordering black line indicates that the feature cannot or shall not be crossed. The minimum dimension is 1 mm². <span style="color:magenta">It is forbidden to cross an impassable body of water! Competitors violating this rule will be disqualified.</span> The bordering black line indicates that the feature cannot or shall not be crossed. An area of deep water such as a lake, pond, river or fountain which may constitute a danger to the competitor or has forbidden access. The dark blue colour and the bordering black line indicates that the feature cannot or shall not be crossed. The minimum dimension is 1 mm². <span style="color:magenta">It is forbidden to cross an impassable body of water! Competitors violating this rule will be disqualified.</span> An area of deep water such as a lake, pond, river or fountain which may constitute a danger to the competitor or has forbidden access. The dark blue colour and the bordering black line indicates that the feature cannot or shall not be crossed. The minimum dimension is 1 mm². <span style="color:magenta">It is forbidden to cross an impassable body of water! Competitors violating this rule will be disqualified.</span> An area of shallow water such as a pond, river or fountain that can be crossed. The body of water shall be less than 0.5 m deep and runnable. If the body of water is not runnable it shall be represented with the symbol impassable body of water (304.1). If no other line symbol touches the border of the passable body of water, the border shall be represented with a blue line. An area of shallow water such as a pond, river or fountain that can be crossed. The body of water shall be less than 0.5 m deep and runnable. If the body of water is not runnable it shall be represented with the symbol (304.1). If no other line symbol touches the border of the passable body of water, the border shall be represented with a blue line. An area of shallow water such as a pond, river or fountain that can be crossed. The body of water shall be less than 0.5 m deep and runnable. If the body of water is not runnable it shall be represented with the symbol impassable body of water (304.1). If no other line symbol touches the border of the passable body of water, the border shall be represented with a blue line. A crossable watercourse less than 2 m wide. A natural or man-made minor water channel which may contain water only intermittently. A marsh or trickle of water which is too narrow to be shown with symbol marsh (310). A marsh which is impassable or which may constitute a danger to the competitor. The feature cannot or shall not be crossed. <span style="color:magenta">It is forbidden to cross an impassable marsh! Competitors violating this rule will be disqualified.</span> This symbol should not be used on its own. This symbol should not be used on its own. A crossable marsh, usually with a distinct edge. The symbol shall be combined with vegetation symbols to show runnability and openness. Minimum size: not less than 2 lines, 5 mm long. An indistinct or seasonal marsh or area of gradual transition from marsh to firm ground, which is crossable. The edge is generally indistinct and the vegetation similar to that of the surrounding ground. The symbol should be combined with vegetation symbols to show runnability and openness. Minimum size: 4 dashes. Small well or fountain, which is at least 1 m high or at least 1 m in diameter. The source of a stream with a distinct outflow. This symbol should generally not be used in urban areas. The symbol is orientated to open downstream. A small water feature which is significant or prominent. The definition of the symbol shall always be given in the map legend. The symbol is orientated to north. An area of cultivated land, lawn, field, meadow, grassland, etc. without trees, offering very good runnability. An area of meadows with scattered trees or bushes, with grass or similar ground cover offering very good runnability. Areas smaller than 10 mm² at the maps scale are shown as open land (401). Symbols prominent large tree (418) and prominent bush or small tree (419) may be added. An area of heath or moorland, a felled area, a newly planted area (trees lower than ca. 1 m) or other generally open land with rough ground vegetation, i.e. heather or tall grass. This symbol may be combined with symbols undergrowth: slow running (407) and undergrowth: difficult to run (409) to show reduced runnability. An area of rough open land with scattered trees or bushes. Areas smaller than 16 mm² in the map scale are either mapped as rough open land (403) or forest: easy running (405). Symbols prominent large tree (418) and prominent bush or small tree (419) may be added. An area of typical open runnable forest for the particular type of terrain. If no part of the forest is runnable then no white should appear on the map. An area with dense trees (low visibility) which reduces running to ca. 60-80% of normal speed. Minimum width 0.25 mm. An area of dense undergrowth but otherwise good visibility (brambles, heather, low bushes, cut branches, etc.) which reduces running to ca. 60-80% of normal speed. This symbol shall not be combined with the symbol forest: slow running (406) or forest: difficult to run (408). An area with dense trees or thicket (low visibility) which reduces running to ca. 20-60% of normal speed. An area of dense undergrowth but otherwise good visibility (brambles, heather, low bushes, and including cut branches) which reduces running to ca. 20-60% of normal speed. This symbol may not be combined with 406 or 408. An area of dense vegetation (trees or undergrowth) which is barely passable. Running reduced 1-20% of normal speed. Minimum width: 0.25 mm. When an area of forest provides good running in one direction but less good in others, white stripes are left in the screen symbol (406) to show the direction with good runnability. When an area of forest provides good running in one direction but less good in others, white stripes are left in the screen symbol (408) to show the direction with good runnability. When an area of forest provides good running in one direction but less good in others, white stripes are left in the screen symbol (410) to show the direction with good runnability. Land planted with fruit trees or bushes. The dot lines may be orientated to represent the direction of planting. Land planted with fruit trees or bushes, with a distinct direction of planting which reduces the runnability. The green lines shall be orientated to show the direction of planting. The boundary of symbol cultivated land (seasonally out of bounds) (415) when not shown with other symbols (fence, wall, path, etc.) is represented with a black line. A permanent boundary between different types of cultivated land is also represented with this symbol. Cultivated land which is seasonally out-of-bounds due to growing crops may be shown with a black dot screen. A distinct forest edge or very distinct vegetation boundary within the forest. For indistinct boundaries, the area edges are shown only by the change in colour and/or dot screen. A prominent single tree. A bush or a tree with a trunk less than 0.5 m diameter. A vegetation feature which is significant or prominent. The definition of the symbol shall always be given in the map legend. The symbol is orientated to north. An area of dense vegetation (trees or undergrowth) which is impassable or which shall not be crossed, due to forbidden access or because it may constitute a danger to the competitor. Minimum width: 0.4 mm. <span style="color:magenta">It is forbidden to cross impassable vegetation! Competitors violating this rule will be disqualified.</span> An unpaved footpath or rough vehicle track is a way for passing mainly by foot, without a smooth, hard surface. The density of the brown fill-in shall be the same as the density chosen for the symbol (529). To improve the legibility of this symbol in non-urban parts of the map, the line width shall, in the non-urban parts of the map, be increased from 0.07 mm to 0.14 mm, and the brown fill-in shall, in the non-urban parts of the map, be drawn darker, so that if (x)% brown is used in urban parts of the map, (x+20)% brown shall be used in the non-urban parts of the map. Colour: black, brown 0%(white), 10%, 20% or 30% (urban) / 20%, 30%, 40%, 50% (min.60lines/cm) (non-urban); the colour and the line width shall be the same as for the symbols paved area (529) and step or edge of paved areas (529.1). An unpaved footpath or rough vehicle track is a way for passing mainly by foot, without a smooth, hard surface. The density of the brown fill-in shall be the same as the density chosen for the symbol (529). To improve the legibility of this symbol in non-urban parts of the map, the line width shall, in the non-urban parts of the map, be increased from 0.07 mm to 0.14 mm, and the brown fill-in shall, in the non-urban parts of the map, be drawn darker, so that if (x)% brown is used in urban parts of the map, (x+20)% brown shall be used in the non-urban parts of the map. Colour: black, brown 0%(white), 10%, 20% or 30% (urban) / 20%, 30%, 40%, 50% (min.60lines/cm) (non-urban); the colour and the line width shall be the same as for the symbols paved area (529) and step or edge of paved areas (529.1). An unpaved footpath or rough vehicle track is a way for passing mainly by foot, without a smooth, hard surface. The density of the brown fill-in shall be the same as the density chosen for the symbol (529). To improve the legibility of this symbol in non-urban parts of the map, the line width shall, in the non-urban parts of the map, be increased from 0.07 mm to 0.14 mm, and the brown fill-in shall, in the non-urban parts of the map, be drawn darker, so that if (x)% brown is used in urban parts of the map, (x+20)% brown shall be used in the non-urban parts of the map. Colour: black, brown 0%(white), 10%, 20% or 30% (urban) / 20%, 30%, 40%, 50% (min.60lines/cm) (non-urban); the colour and the line width shall be the same as for the symbols paved area (529) and step or edge of paved areas (529.1). An unpaved footpath or rough vehicle track is a way for passing mainly by foot, without a smooth, hard surface. The density of the brown fill-in shall be the same as the density chosen for the symbol (529). To improve the legibility of this symbol in non-urban parts of the map, the line width shall, in the non-urban parts of the map, be increased from 0.07 mm to 0.14 mm, and the brown fill-in shall, in the non-urban parts of the map, be drawn darker, so that if (x)% brown is used in urban parts of the map, (x+20)% brown shall be used in the non-urban parts of the map. Colour: black, brown 0%(white), 10%, 20% or 30% (urban) / 20%, 30%, 40%, 50% (min.60lines/cm) (non-urban); the colour and the line width shall be the same as for the symbols paved area (529) and step or edge of paved areas (529.1). A small unpaved footpath or track. Not to be used in urban areas. A less distinct path or forestry extraction track. Not to be used in urban areas. A distinct ride is a linear break in the forest (usually in a plantation), which does not have a distinct path along it. Where there is a path along a ride, the symbol small unpaved footpath or track (506.1) shall be used. Not to be used in urban areas. A bridge is a structure spanning and permitting passage over a river, chasm, road or the like. A bridge is a structure spanning and permitting passage over a river, chasm, road or the like. A railway is a permanent track laid with rails on which locomotives, carriages or wagons can travel. If it is forbidden to cross or run along the railroad, the forbidden area around the railway shall be represented with symbol area with forbidden access (528.1). A tramway is a public vehicle running regularly along certain streets, usually on rails. The track can be easily crossed by the competitor. Tramways are generally not represented. However, if they serve navigation or orientation, they can be represented. Power line, cableway or skilift. The bars indicate the exact location of the pylons. <b>Note: When drawing this symbol, press space to toggle placing the pylon symbols at new nodes.</b> Major power lines should be drawn with a double line. The gap between the lines may indicate the extent of the powerline. Very large carrying masts shall be represented in plan shape or with the symbol high tower (535). In this case, the cable lines can be left out (the map shows only the pylons). <b>Note: When drawing this symbol, press space to toggle placing the pylon symbols at new nodes.</b> An underpass or a tunnel is a passage running underneath the ground, especially a passage for pedestrians or vehicles, crossing under for instance a railroad or a road. <span style="color: magenta">If underpasses or tunnels etc. are to be used in a competition, they shall be emphasized with the symbol crossing point (708) or crossing section (708.1)!</span> A stone wall or stone faced bank. This symbol shall be used only in non-urban areas. If such a wall is higher than 2 m, it shall be represented with the symbol impassable wall (521.1). A passable wall or retaining wall is a construction made of stone, brick, concrete etc., which can be passed. This symbol is suitable for urban areas. If such a wall is higher than 2 m, it shall be represented with the symbol impassable wall (521.1). Wide walls shall be drawn in plan shape. A passable wall or retaining wall is a construction made of stone, brick, concrete etc., which can be passed. This symbol is suitable for urban areas. If such a wall is higher than 2 m, it shall be represented with the symbol (521.1). Wide walls shall be drawn in plan shape. An impassable wall or retaining wall is a wall, which fulfil the function of an enclosure or solid barrier. It shall not be crossed, due to forbidden access or because it may constitute a danger to the competitor due to its height. Very wide impassable walls shall be drawn in plan shape and represented with the symbol building (526.1). <span style="color:magenta">It is forbidden to cross an impassable wall! Competitors violating this rule will be disqualified.</span> An impassable wall or retaining wall is a wall, which fulfil the function of an enclosure or solid barrier. It shall not be crossed, due to forbidden access or because it may constitute a danger to the competitor due to its height. Very wide impassable walls shall be drawn in plan shape and represented with the symbol building (526.1). <span style="color:magenta">It is forbidden to cross an impassable wall! Competitors violating this rule will be disqualified.</span> A passable fence is a barrier enclosing or bordering a field, yard, etc., usually made of posts and wire or wood. It is used to prevent entrance or to confine or mark a boundary. A railing is a fencelike barrier composed of one or more horizontal rails supported by widely spaced upright poles, usually it can be slipped through. If a fence or railing is higher than 2 m or very difficult to cross, it shall be represented with the symbol impassable fence or railing (524). A passable fence is a barrier enclosing or bordering a field, yard, etc., usually made of posts and wire or wood. It is used to prevent entrance or to confine or mark a boundary. A railing is a fencelike barrier composed of one or more horizontal rails supported by widely spaced upright poles, usually it can be slipped through. If a fence or railing is higher than 2 m or very difficult to cross, it shall be represented with the symbol impassable fence or railing (524). An impassable fence or railing, which shall not be crossed, due to forbidden access or because it may constitute a danger to the competitor because of its height. <span style="color:magenta">It is forbidden to cross an impassable fence or railing! Competitors violating this rule will be disqualified.</span> A crossing point is a gap or an opening in a fence, railing or wall, which can easily be crossed by a competitor. Small gaps or openings which can not easily be crossed by competitors, shall not be represented on the map and shall be closed during the competition. A building is a relatively permanent construction having a roof. Buildings within symbol area with forbidden access (527.1) may just be represented in a simplified manner.Areas totally contained within a building shall be mapped as being a part of the building. The minimum gap between buildings and between buildings and other impassable features shall be 0.40 mm. The black screen percentage should be chosen according to the terrain. A dark screen gives a better contrast to passable areas, such as streets, stairways and canopies, while a light screen makes contours and course overprint more clearly visible (which can be important in very densely built up urban terrain and in steep urban terrain). The black screen shall be the same for the whole map. <span style="color:magenta">It is forbidden to pass through or over a building! Competitors violating this rule will be disqualified.</span> Do not use this symbol on its own! Do not use this symbol on its own! A building is a relatively permanent construction having a roof. Buildings within symbol area with forbidden access (527.1) may just be represented in a simplified manner.Areas totally contained within a building shall be mapped as being a part of the building. The minimum gap between buildings and between buildings and other impassable features shall be 0.40 mm. The black screen percentage should be chosen according to the terrain. A dark screen gives a better contrast to passable areas, such as streets, stairways and canopies, while a light screen makes contours and course overprint more clearly visible (which can be important in very densely built up urban terrain and in steep urban terrain). The black screen shall be the same for the whole map. <span style="color:magenta">It is forbidden to pass through or over a building! Competitors violating this rule will be disqualified.</span> A canopy is a building construction (with a roof), normally supported by pillars, poles or walls, such as passages, gangways, courts, bus stops, gas stations or garages. Small passable parts of buildings which can not easily be crossed by competitors, shall not be represented on the map and shall be closed during the competition. Do not use this symbol on its own! Do not use this symbol on its own! A pillar is an upright shaft or structure of stone, brick or other material, relatively slender in proportion to its height and any shape in section, used as a building support. Pillars smaller than 2 m × 2 m are generally not represented. Columns of pillars and pillars along buildings are not represented. However, if they are important for navigation and orientation, they can be represented. An area with forbidden access such as a private area, a flower bed, a railway area etc. No feature shall be represented in this area, except very prominent features such as railways, large buildings, or very large trees. Road entrances shall be represented clearly. Areas with forbidden access totally contained within buildings shall be mapped as being a part of the building. <span style="color:magenta">It is forbidden to cross an area with forbidden access! Competitors violating this rule will be disqualified.</span> A paved area is an area with a firm level surfaces such as asphalt, hard gravel, tiles, concrete or the like. It shall be bordered (or framed) by the symbol step or edge of paved area (529.1). Distinct differences within the paved area can be represented with the symbol step or edge of paved area (529.1), if they serve navigation. Where a paved road, footpath or track goes through a non-urban area, the brown fill-in shall be drawn darker, so that if (x)% brown is used in urban areas, (x+20)% brown shall be used in the non-urban areas, and the line width of the black outline shall be increased from 0.07 to 0.14 mm. The black border line can be omitted where it is logical (e.g. indistinct/gradual gravel-to-grass transitions). Colour: black, brown 0%(white), 10%, 20% or 30% (urban) / 20%, 30%, 40%, 50% (min.60lines/cm) (non-urban); the colour and the line width shall be the same as for the symbol unpaved footpath or track (506.1). A paved area is an area with a firm level surfaces such as asphalt, hard gravel, tiles, concrete or the like. It shall be bordered (or framed) by the symbol step or edge of paved area (529.1). Distinct differences within the paved area can be represented with the symbol step or edge of paved area (529.1), if they serve navigation. Where a paved road, footpath or track goes through a non-urban area, the brown fill-in shall be drawn darker, so that if (x)% brown is used in urban areas, (x+20)% brown shall be used in the non-urban areas, and the line width of the black outline shall be increased from 0.07 to 0.14 mm. The black border line can be omitted where it is logical (e.g. indistinct/gradual gravel-to-grass transitions). Colour: brown 0 (white), 10%, 20% or 30% (urban) / 20%, 30%, 40%, 50% (non-urban) (min. 60 lines/cm), black; the colour and the line width shall be the same as for symbol unpaved footpath or track (506.1). A paved area is an area with a firm level surfaces such as asphalt, hard gravel, tiles, concrete or the like. It shall be bordered (or framed) by the symbol step or edge of paved area (529.1). Distinct differences within the paved area can be represented with the symbol step or edge of paved area (529.1), if they serve navigation. Where a paved road, footpath or track goes through a non-urban area, the brown fill-in shall be drawn darker, so that if (x)% brown is used in urban areas, (x+20)% brown shall be used in the non-urban areas, and the line width of the black outline shall be increased from 0.07 to 0.14 mm. The black border line can be omitted where it is logical (e.g. indistinct/gradual gravel-to-grass transitions). Colour: brown 0 (white), 10%, 20% or 30% (urban) / 20%, 30%, 40%, 50% (non-urban) (min. 60 lines/cm), black; the colour and the line width shall be the same as for symbol unpaved footpath or track (506.1). A paved area is an area with a firm level surfaces such as asphalt, hard gravel, tiles, concrete or the like. It shall be bordered (or framed) by the symbol step or edge of paved area (529.1). Distinct differences within the paved area can be represented with the symbol step or edge of paved area (529.1), if they serve navigation. Where a paved road, footpath or track goes through a non-urban area, the brown fill-in shall be drawn darker, so that if (x)% brown is used in urban areas, (x+20)% brown shall be used in the non-urban areas, and the line width of the black outline shall be increased from 0.07 to 0.14 mm. The black border line can be omitted where it is logical (e.g. indistinct/gradual gravel-to-grass transitions). Colour: brown 0 (white), 10%, 20% or 30% (urban) / 20%, 30%, 40%, 50% (non-urban) (min. 60 lines/cm), black; the colour and the line width shall be the same as for symbol unpaved footpath or track (506.1). A paved area is an area with a firm level surfaces such as asphalt, hard gravel, tiles, concrete or the like. It shall be bordered (or framed) by the symbol step or edge of paved area (529.1). Distinct differences within the paved area can be represented with the symbol step or edge of paved area (529.1), if they serve navigation. Where a paved road, footpath or track goes through a non-urban area, the brown fill-in shall be drawn darker, so that if (x)% brown is used in urban areas, (x+20)% brown shall be used in the non-urban areas, and the line width of the black outline shall be increased from 0.07 to 0.14 mm. The black border line can be omitted where it is logical (e.g. indistinct/gradual gravel-to-grass transitions). Colour: brown 0 (white), 10%, 20% or 30% (urban) / 20%, 30%, 40%, 50% (non-urban) (min. 60 lines/cm), black; the colour and the line width shall be the same as for symbol unpaved footpath or track (506.1). A paved area is an area with a firm level surfaces such as asphalt, hard gravel, tiles, concrete or the like. It shall be bordered (or framed) by the symbol step or edge of paved area (529.1). Distinct differences within the paved area can be represented with the symbol step or edge of paved area (529.1), if they serve navigation. Where a paved road, footpath or track goes through a non-urban area, the brown fill-in shall be drawn darker, so that if (x)% brown is used in urban areas, (x+20)% brown shall be used in the non-urban areas, and the line width of the black outline shall be increased from 0.07 to 0.14 mm. The black border line can be omitted where it is logical (e.g. indistinct/gradual gravel-to-grass transitions). Colour: brown 0 (white), 10%, 20% or 30% (urban) / 20%, 30%, 40%, 50% (non-urban) (min. 60 lines/cm), black; the colour and the line width shall be the same as for symbol unpaved footpath or track (506.1). A paved area is an area with a firm level surfaces such as asphalt, hard gravel, tiles, concrete or the like. It shall be bordered (or framed) by the symbol step or edge of paved area (529.1). Distinct differences within the paved area can be represented with the symbol step or edge of paved area (529.1), if they serve navigation. Where a paved road, footpath or track goes through a non-urban area, the brown fill-in shall be drawn darker, so that if (x)% brown is used in urban areas, (x+20)% brown shall be used in the non-urban areas, and the line width of the black outline shall be increased from 0.07 to 0.14 mm. The black border line can be omitted where it is logical (e.g. indistinct/gradual gravel-to-grass transitions). Colour: brown 0 (white), 10%, 20% or 30% (urban) / 20%, 30%, 40%, 50% (non-urban) (min. 60 lines/cm), black; the colour and the line width shall be the same as for symbol unpaved footpath or track (506.1). A paved area is an area with a firm level surfaces such as asphalt, hard gravel, tiles, concrete or the like. It shall be bordered (or framed) by the symbol step or edge of paved area (529.1). Distinct differences within the paved area can be represented with the symbol step or edge of paved area (529.1), if they serve navigation. Where a paved road, footpath or track goes through a non-urban area, the brown fill-in shall be drawn darker, so that if (x)% brown is used in urban areas, (x+20)% brown shall be used in the non-urban areas, and the line width of the black outline shall be increased from 0.07 to 0.14 mm. The black border line can be omitted where it is logical (e.g. indistinct/gradual gravel-to-grass transitions). Colour: brown 0 (white), 10%, 20% or 30% (urban) / 20%, 30%, 40%, 50% (non-urban) (min. 60 lines/cm), black; the colour and the line width shall be the same as for symbol unpaved footpath or track (506.1). A paved area is an area with a firm level surfaces such as asphalt, hard gravel, tiles, concrete or the like. It shall be bordered (or framed) by the symbol step or edge of paved area (529.1). Distinct differences within the paved area can be represented with the symbol step or edge of paved area (529.1), if they serve navigation. Where a paved road, footpath or track goes through a non-urban area, the brown fill-in shall be drawn darker, so that if (x)% brown is used in urban areas, (x+20)% brown shall be used in the non-urban areas, and the line width of the black outline shall be increased from 0.07 to 0.14 mm. The black border line can be omitted where it is logical (e.g. indistinct/gradual gravel-to-grass transitions). Colour: brown 0 (white), 10%, 20% or 30% (urban) / 20%, 30%, 40%, 50% (non-urban) (min. 60 lines/cm), black; the colour and the line width shall be the same as for symbol unpaved footpath or track (506.1). A paved area is an area with a firm level surfaces such as asphalt, hard gravel, tiles, concrete or the like. It shall be bordered (or framed) by the symbol step or edge of paved area (529.1). Distinct differences within the paved area can be represented with the symbol step or edge of paved area (529.1), if they serve navigation. Where a paved road, footpath or track goes through a non-urban area, the brown fill-in shall be drawn darker, so that if (x)% brown is used in urban areas, (x+20)% brown shall be used in the non-urban areas, and the line width of the black outline shall be increased from 0.07 to 0.14 mm. The black border line can be omitted where it is logical (e.g. indistinct/gradual gravel-to-grass transitions). Colour: brown 0 (white), 10%, 20% or 30% (urban) / 20%, 30%, 40%, 50% (non-urban) (min. 60 lines/cm), black; the colour and the line width shall be the same as for symbol unpaved footpath or track (506.1). A paved area is an area with a firm level surfaces such as asphalt, hard gravel, tiles, concrete or the like. It shall be bordered (or framed) by the symbol step or edge of paved area (529.1). Distinct differences within the paved area can be represented with the symbol step or edge of paved area (529.1), if they serve navigation. Where a paved road, footpath or track goes through a non-urban area, the brown fill-in shall be drawn darker, so that if (x)% brown is used in urban areas, (x+20)% brown shall be used in the non-urban areas, and the line width of the black outline shall be increased from 0.07 to 0.14 mm. The black border line can be omitted where it is logical (e.g. indistinct/gradual gravel-to-grass transitions). Colour: brown 0 (white), 10%, 20% or 30% (urban) / 20%, 30%, 40%, 50% (non-urban) (min. 60 lines/cm), black; the colour and the line width shall be the same as for symbol unpaved footpath or track (506.1). A paved area is an area with a firm level surfaces such as asphalt, hard gravel, tiles, concrete or the like. It shall be bordered (or framed) by the symbol step or edge of paved area (529.1). Distinct differences within the paved area can be represented with the symbol step or edge of paved area (529.1), if they serve navigation. Where a paved road, footpath or track goes through a non-urban area, the brown fill-in shall be drawn darker, so that if (x)% brown is used in urban areas, (x+20)% brown shall be used in the non-urban areas, and the line width of the black outline shall be increased from 0.07 to 0.14 mm. The black border line can be omitted where it is logical (e.g. indistinct/gradual gravel-to-grass transitions). Colour: brown 0 (white), 10%, 20% or 30% (urban) / 20%, 30%, 40%, 50% (non-urban) (min. 60 lines/cm), black; the colour and the line width shall be the same as for symbol unpaved footpath or track (506.1). A step or an edge of a paved area. Steps of a stairway shall be represented in a generalized manner. Edges within paved areas are generally not represented, unless they serve navigation. The thickness of edge of paved areas shall be enlarged to 0.14 mm in non-urban areas to improve legibility. The thickness of step lines shall always be 0.07 mm. A step or an edge of a paved area. Steps of a stairway shall be represented in a generalized manner. Edges within paved areas are generally not represented, unless they serve navigation. The thickness of edge of paved areas shall be enlarged to 0.14 mm in non-urban areas to improve legibility. The thickness of step lines shall always be 0.07 mm. A step or an edge of a paved area. Steps of a stairway shall be represented in a generalized manner. Edges within paved areas are generally not represented, unless they serve navigation. The thickness of edge of paved areas shall be enlarged to 0.14 mm in non-urban areas to improve legibility. The thickness of step lines shall always be 0.07 mm. A step or an edge of a paved area. Steps of a stairway shall be represented in a generalized manner. Edges within paved areas are generally not represented, unless they serve navigation. The thickness of edge of paved areas shall be enlarged to 0.14 mm in non-urban areas to improve legibility. The thickness of step lines shall always be 0.07 mm. A pipeline (gas, water, oil, etc.) above ground level which can be crossed over or under. An impassable pipeline (gas, water, oil, etc.) above ground level which shall not be crossed, due to forbidden access or because it may constitute a danger to the competitor because of its height. <span style="color:magenta">It is forbidden to cross an impassable pipeline! Competitors violating this rule will be disqualified.</span> A high tower or large pylon. Very large towers shall be represented in plan shape with the symbol building (526.1). The symbol is orientated to north. An obvious small tower, platform or seat. The symbol is orientated to north. Cairn, memorial, small monument or boundary stone more than 0.5 m high. Large massive monuments shall be represented in plan shape with the symbol building (526.1). A fodder rack, which is free standing or attached to a tree. The symbol is orientated to north. A man-made feature which is significant or prominent. The definition of the symbol shall always be given in the map legend. A man-made feature which is significant or prominent. The definition of the symbol shall always be given in the map legend. The symbol is orientated to north. Magnetic north lines are lines placed on the map pointing to magnetic north. Their spacing shall be 30 mm on the 1:5 000 map so they represent 150 m on the ground. North lines may be broken where they obscure small features such as boulders, knolls, cliffs, stream junctions, path ends, etc. Magnetic north lines are lines placed on the map pointing to magnetic north. Their spacing shall be 30 mm on the 1:5 000 map so they represent 150 m on the ground. North lines may be broken where they obscure small features such as boulders, knolls, cliffs, stream junctions, path ends, etc. <b>Note: this is a non-standard addition to the symbol set.</b> Magnetic north lines are lines placed on the map pointing to magnetic north. Their spacing shall be 30 mm on the 1:5000 map so they represent 150 m on the ground. North lines may be broken where they obscure small features such as boulders, knolls, cliffs, stream junctions, path ends, etc. Cut holes in the pattern to create these breaks. Magnetic north lines are lines placed on the map pointing to magnetic north. Their spacing shall be 30 mm on the 1:5 000 map so they represent 150 m on the ground. North lines may be broken where they obscure small features such as boulders, knolls, cliffs, stream junctions, path ends, etc. Magnetic north lines are lines placed on the map pointing to magnetic north. Their spacing shall be 30 mm on the 1:5 000 map so they represent 150 m on the ground. North lines may be broken where they obscure small features such as boulders, knolls, cliffs, stream junctions, path ends, etc. <b>Note: this is a non-standard addition to the symbol set.</b> Magnetic north lines are lines placed on the map pointing to magnetic north. Their spacing shall be 30 mm on the 1:5 000 map so they represent 150 m on the ground. North lines may be broken where they obscure small features such as boulders, knolls, cliffs, stream junctions, path ends, etc. Cut holes in the pattern to create these breaks. At least three registration marks shall be placed within the frame of a map in a non-symmetrical arrangement. In addition, a colour check should be possible. Spot heights are used for the rough assessment of height differences. The height is given to the nearest metre. The figures are orientated to the north. Water levels are given without the dot. Spot heights are used for the rough assessment of height differences. The height is given to the nearest metre. The figures are orientated to the north. Water levels are given without the dot. The start or map issue point (if not at the start) is shown by an equilateral triangle which points in the direction of the first control. The centre of the triangle shows the precise position of the start point. The control points are shown with circles. The centre of the circle shows the precise position of the feature. Sections of circles should be omitted to leave important detail showing. The number of the control is placed close to the control point circle in such a way that it does not obscure important detail. The numbers are orientated to north. Where controls are to be visited in order, the start, control points and finish are joined together by straight lines. Sections of lines should be omitted to leave important detail showing. A marked route is shown on the map with a dashed line. The finish is shown by two concentric circles. A boundary which it is not permitted to cross. Uncrossable boundaries shall be mapped by using the symbols: impassable cliff (201), impassable body of water (304.1), impassable marsh (309), impassable wall (521.1), impassable fence or railing (524) or impassable pipeline (534) and shall not be overprinted with symbol uncrossable boundary (707). This symbol is to be used only for last minute updates to the competition area, as excessive use of purple for indicating barriers is unfortunate. <span style="color:magenta">It is forbidden to cross an uncrossable boundary! Competitors violating this rule will be disqualified.</span> A crossing point through or over a wall or fence, or across a road or railway or through a tunnel or an out-of-bounds area is drawn on the map with two lines curving outwards. If underpasses or tunnels etc. are to be used in a competition, they shall be emphasized with symbol crossing point (708) or crossing section (708.1). Acrossing section through or over a building, wall or fence, or across a road or railway or through a tunnel or an out-of-bounds area is drawn on the map as a linear object, according to the plan shape. If underpasses or tunnels etc. are to be used in a competition, they shall be emphasized with symbol (708) or (708.1). Out of bounds areas are mapped with the symbol area with forbidden access (528.1). This symbol shall only be used for last minute updates to the competition map (e.g. for areas that may be dangerous for the competitors during the competition, or very late changes to the competition terrain). An out-of-bounds area is shown with vertical stripes. A bounding line may be drawn if there is no natural boundary, as follows: - a solid line indicates that the boundary is marked continuously (tapes, etc.) on the ground, - a dashed line indicates intermittent marking on the ground, - no line indicates no marking on the ground. <span style="color:magenta">It is forbidden to cross an out-of-bounds area! Competitors violating this rule will be disqualified.</span> A solid line indicates that the boundary is marked continuously (tapes, etc.) on the ground. A dashed line indicates intermittent marking on the ground. The location of a first aid post. The location of a refreshment point which is not at a control or along the marked route. Obvious temporary constructions like platforms for spectators and speaker, closed area for spectators, outside restaurant areas, etc. shall be represented in plan shape. <span style="color:magenta">It is forbidden to enter a temporary construction or closed area! Competitors violating this rule will be disqualified.</span> This symbol provides a simple and quick way to make training courses. The purple line will extend a bit into the finish symbol. This is a shortcoming of this simple approach. The OpenOrienteering Logo. N N N Construction area, forbidden access! Street / asphalt, with / without traffic Big signs, playground items, etc. Building; Canopy Street / asphalt, with / without traffic Private/flowerbed, forbidden access! Meadow; Meadow with trees; Forest Light; strong thicket Thicket, impassable Tree; Special tree Path; Track; Stairs Fence, forbidden to pass! Fence, passable Wall, forbidden to pass! Small wall, passable Hedge, forbidden to pass! Light; strong undergrowth Hedges, passable Countours; Hill Rough/semi-open area Sandy ground Cultivation boundary; Step Stone; Rock face Well; Small erosion gully Legend Special Signatures May 2013 Thomas Schöps Orienteering map Standing: Garching Garching Cartography: 2.5 m 1 : 4,000 Scale: Contours: Organizer, host, mapper, land owners and administration do not assume any liability! Munich SprintCup www.ol-gruenwald.de www.ol-landshut.de Landshut and Freising: Munich: Orienteering in ... mapper-0.8.1.1/examples/src/forest sample.xmap000066400000000000000000045606611325266516600212410ustar00rootroot00000000000000 PURPLE BLACK BLUE BROWN GREEN YELLOW A line joining points of equal height. The standard vertical interval between contours is 5 metres. The smallest bend in a contour is 0.25 mm from centre to centre of the lines. Every fifth contour shall be drawn with a thicker line. This is an aid to the quick assessment of height difference and the overall shape of the terrain surface. Where an index contour coincides with an area of much detail, it may be shown with a normal contour line. An intermediate contour line. Form lines are used where more information can be given about the shape of the ground. They are used only where representation is not possible with ordinary contours. Only one form line may be used between neighbouring contours. Slope lines may be drawn on the lower side of a contour line, e.g. along the line of a re-entrant or in a depression. They are used only where it is necessary to clarify the direction of slope. Contour values may be included to aid assessment of large height differences. They are inserted in the index contours in positions where other detail is not obscured. The figures should be orientated so that the top of the figure is on the higher side of the contour. A steep earth bank is an abrupt change in ground level which can be clearly distinguished from its surroundings, e.g. gravel or sand pits, road and railway cuttings or embankments. The tags should show the full extent of the slope, but may be omitted if two banks are close together. Impassable banks should be drawn with symbol 201 (impassable cliff). The line width of very high earth banks may be 0.25 mm. A steep earth bank is an abrupt change in ground level which can be clearly distinguished from its surroundings, e.g. gravel or sand pits, road and railway cuttings or embankments. The tags should show the full extent of the slope, but may be omitted if two banks are close together. Impassable banks should be drawn with symbol 201 (impassable cliff). The line width of very high earth banks may be 0.25 mm. The line width of very high earth banks may be 0.25 mm. The line width of very high earth banks may be 0.25 mm. Use this symbol to display the full extent of wide earth banks. Distinct earth wall. Minimum height is 1 m. A small or partly ruined earth wall shall be shown with a dashed line. Minimum height is 0.5 m. An erosion gully or trench which is too small to be shown by symbol 106 is shown by a single line. The line width reflects the size of the gully. Minimum depth 1 m. The end of the line is pointed. A small erosion gully or trench. Minimum depth 0.5 m. A small obvious mound or rocky knoll which cannot be drawn to scale with a contour (diameter of mound less than ca. 5 m). The height of the knoll should be a minimum of 1 m from the surrounding ground. The symbol may not touch a contour line. A small obvious elongated knoll which cannot be drawn to scale with a contour (length less than 12 m and width less than 4 m). The height of the knoll should be a minimum of 1 m from the surrounding ground. Knolls larger than this must be shown by contours. The symbol may not be drawn in free form or such that two elongated knoll symbols overlap. The symbol may not touch a contour line. Small shallow natural depressions and hollows (minimum diameter 2 m) which cannot be shown to scale by contours are represented by a semicircle. Minimum depth from the surrounding ground should be 1 m. Location is the centre of gravity of the symbol, which is orientated to north. Symbol 116 is used for man-made pits. Pits and holes with distinct steep sides which cannot be shown to scale by symbol 106 (minimum diameter 2 m). Minimum depth from the surrounding ground should be 1 m. Location is the centre of gravity of the symbol which is orientated to north. An area of pits or knolls which is too intricate to be shown in detail. The density of randomly placed dots may vary according to the detail on the ground. The size of the dots may vary. This symbol can be used for a special small land form feature. The definition of the symbol must be given in the map legend. An impassable cliff, quarry or earth bank (see 106) is shown with a 0.35 mm line and downward tags showing its full extent from the top line to the foot. For vertical rock faces the tags may be omitted if space is short, e.g. narrow passages between cliffs (the passage should be drawn with a width of at least 0.3 mm). The tags may extend over an area symbol representing detail immediately below the rock face. When a rock face drops straight into water making it impossible to pass under the cliff along the water's edge, the bank line is omitted or the tags should clearly extend over the bank line. An impassable cliff, quarry or earth bank (see 106) is shown with a 0.35 mm line and downward tags showing its full extent from the top line to the foot. For vertical rock faces the tags may be omitted if space is short, e.g. narrow passages between cliffs (the passage should be drawn with a width of at least 0.3 mm). The tags may extend over an area symbol representing detail immediately below the rock face. When a rock face drops straight into water making it impossible to pass under the cliff along the water's edge, the bank line is omitted or the tags should clearly extend over the bank line. For vertical rock faces the tags may be omitted if space is short, e.g. narrow passages between cliffs (the passage should be drawn with a width of at least 0.3 mm). Use this symbol to display the full extent of a wide cliff. In the case of unusual features such as rock pillars or massive cliffs or gigantic boulders, the rocks shall be shown in plan shape without tags. A small vertical rock face (minimum height 1 m) may be shown without tags. If the direction of fall of the rock face is not apparent from the contours or to improve legibility, short tags should be drawn in the direction of the fall. For passable rock faces shown without tags the ends of the line may be rounded to improve legibility. A small vertical rock face (minimum height 1 m) may be shown without tags. If the direction of fall of the rock face is not apparent from the contours or to improve legibility, short tags should be drawn in the direction of the fall. For passable rock faces shown without tags the ends of the line may be rounded to improve legibility. Should be used if the direction of fall of the rock face is apparent from the contours and the legibility is good. Should be used if the direction of fall of the rock face is apparent from the contours and the legibility is good. For passable rock faces shown without tags the ends of the line may be rounded to improve legibility. For passable rock faces shown without tags the ends of the line may be rounded to improve legibility. Rocky pits, holes or mineshafts which may constitute a danger to the runner. Location is the centre of gravity of the symbol, which is orientated to north. A cave is represented by the same symbol as a rocky pit. In this case the symbol should be orientated to point up the slope as indicated opposite. The centre of gravity of the symbol marks the opening. A small distinct boulder (minimum height 1 m). Every boulder marked on the map should be immediately identifiable on the ground. To be able to show the distinction between boulders with significant difference in size it is permitted to enlarge this symbol by 20% (diameter 0.5 mm). To be able to show the distinction between boulders with significant difference in size it is permitted to enlarge this symbol by 20% (diameter 0.5 mm). A particularly large and distinct boulder. For gigantic boulders symbol 202 should be used. An area which is covered with so many blocks of stone that they cannot be marked individually is shown with randomly orientated solid triangles with sides of ratio 8:6:5. A minimum of two triangles should be used. The going is indicated by the density of the triangles. To be able to show the distinction between boulder fields with a significant difference in boulder size it is permitted to enlarge the triangles by 20%. To be able to show the distinction between boulder fields with a significant difference in boulder size it is permitted to enlarge the triangles by 20%. A small distinct group of boulders so closely clustered together that they cannot be marked individually. The symbol is an equilateral triangle orientated to the north. To be able to show the distinction between boulder clusters with significant difference in size it is permitted to enlarge this symbol by 25% (1.0 mm). To be able to show the distinction between boulder clusters with significant difference in size it is permitted to enlarge this symbol by 25% (1.0 mm). Stony or rocky ground which affects going should be shown on the map. The dots should be randomly distributed with density according to the amount of rock. A minimum of three dots should be used. Stony or rocky ground which affects going should be shown on the map. The dots should be randomly distributed with density according to the amount of rock. A minimum of three dots should be used. An area of soft sandy ground or gravel with no vegetation and where running is slow. Where an area of sandy ground is open but running is good, it is shown as open land (401/402). A runnable area of rock without earth or vegetation is shown as bare rock. An area of rock covered with grass, moss or other low vegetation is shown as open land (401/402). Large areas of water are shown with dot screen. Small areas of water should be shown with full colour. A black bank line indicates that the feature cannot be crossed. A black bank line indicates that the feature cannot be crossed. Where the lake or pond is smaller than 1 mm² on the printed map, the bank line is omitted. A water-filled pit or an area of water which is too small to be shown to scale. Location is the centre of gravity of the symbol, which is orientated to north. A crossable watercourse, minimum 2 m wide. The width of watercourses over 5 m wide should be shown to scale. A crossable watercourse (including a major drainage ditch) less than 2 m wide. For better legibility a ditch in a marsh should be drawn as a crossable watercourse (305). A natural or man-made minor water channel which may contain water only intermittently. A marsh or trickle of water which is too narrow to be shown with symbol 310 (less than ca. 5 m wide). A marsh which is uncrossable or dangerous for the runner. A black line surrounds the symbol. A black line surrounds the symbol. A crossable marsh, usually with a distinct edge. The symbol should be combined with vegetation symbols to show runnability and openness. Where a small marsh area should be combined with either 403/404 it is permitted to use 401/402 to improve legibility. An indistinct or seasonal marsh or area of gradual transition from marsh to firm ground, which is crossable. The edge is generally indistinct and the vegetation similar to that of the surrounding ground. The symbol should be combined with vegetation symbols to show runnability and openness. Wells and captive springs, which are clearly visible on the ground. The source of a stream with a distinct outflow. The symbol is orientated to open downstream. A special small water feature. The definition of the symbol must always be given in the map legend. Cultivated land, fields, meadows, grassland, etc. without trees, offering easy running. If yellow coloured areas becomes dominant, a screen (75%) instead of full yellow may be used. Meadows with scattered trees or bushes, with grass or similar ground cover offering easy running. Areas smaller than 10 mm at the maps scale are shown as open land (401). Individual trees may be added (418, 419, 420). If yellow coloured areas becomes dominant, a screen (75%) instead of full yellow may be used. Heath, moorland, felled areas, newly planted areas (trees lower than ca. 1 m) or other generally open land with rough ground vegetation, heather or tall grass. Symbol 403 may be combined with symbols 407 and 409 to show reduced runnability. Where there are scattered trees in rough open land, areas of white (or green) should appear in the tone. Such an area may be generalised by using a regular pattern of large white dots in the yellow screen. Areas smaller than 16 mm in the maps scale are shown as rough open land (403). Individual trees may be added (418, 419, 420). Typically open runnable forest for the particular type of terrain. If no part of the forest is runnable then no white should appear on the map. An area with dense trees (low visibility) which reduces running to ca. 60-80% of normal speed. An area of dense undergrowth but otherwise good visibility (brambles, heather, low bushes, and including cut branches) which reduces running to ca. 60-80% of normal speed. This symbol may not be combined with 406 or 408. An area with dense trees or thicket (low visibility) which reduce running to ca. 20-60% of normal speed. An area of dense undergrowth but otherwise good visibility (brambles, heather, low bushes, and including cut branches) which reduces running to ca. 20-60% of normal speed. This symbol may not be combined with 406 or 408. An area of dense vegetation (trees or undergrowth) which is barely passable. Running reduced to ca. 0-20% of normal speed. Line of minimum width for symbol 410. When an area of forest provides good running in one direction but less good in others, white stripes are left in the screen symbol to show the direction of good running. When an area of forest provides good running in one direction but less good in others, white stripes are left in the screen symbol to show the direction of good running. When an area of forest provides good running in one direction but less good in others, white stripes are left in the screen symbol to show the direction of good running. Land planted with fruit trees or bushes. The dot lines may be orientated to show the direction of planting. If yellow coloured areas becomes dominant, a screen (75%) instead of full yellow may be used. The green lines may be orientated to show the direction of planting. If yellow coloured areas becomes dominant, a screen (75%) instead of full yellow may be used. The boundary of cultivated land when not shown with other symbols (fence, wall, path, etc.) is shown with a black line. A permanent boundary between different types of cultivated land is also shown with this symbol. Cultivated land which is seasonally out-of-bounds due to growing crops may be shown with a black dot screen. A distinct forest edge or very distinct vegetation boundary within the forest. Symbols 418, 419 and 420 can be used for special small vegetation features. The definition of the symbol must be given in each case in the map legend. Symbols 418, 419 and 420 can be used for special small vegetation features. The definition of the symbol must be given in each case in the map legend. Symbols 418, 419 and 420 can be used for special small vegetation features. The definition of the symbol must be given in each case in the map legend. A road with two carriageways. The width of the symbol should be drawn to scale but not smaller than the minimum width. The outer boundary lines may be replaced with symbols 519, 521, 522 or 524 if a fence or wall is so close to the motorway edge that it cannot practically be shown as a separate symbol. The space between the black lines must be filled with brown (50%). A road under construction may be shown with broken lines. A road under construction may be shown with broken lines. Road wider than 5m. The width of the symbol should be drawn to scale but not smaller than the minimum width. The outer boundary lines may be replaced with symbols 519, 521, 522 or 524 if a fence or wall is so close to the motorway edge that it cannot practically be shown as a separate symbol. The space between the black lines must be filled with brown (50%). A road under construction may be shown with broken lines. A road under construction may be shown with broken lines. Road 3-5 m wide. The space between the black lines must be filled with brown (50%). A road under construction may be shown with broken lines. A road under construction may be shown with broken lines. A maintained road suitable for motor vehicles in all weather. Width less than 3 m. A track or poorly maintained road suitable for vehicles only when travelling slowly. Width less than 3 m. A large path, or old vehicle track, which is distinct on the ground. A small path or (temporary) forest extraction track which can be followed at competition speed. A less distinct small path or forestry extraction track. A distinct ride, less than ca. 5 m wide. A ride is a linear break in the forest (usually plantation) which does not have a distinct path along it. Where there is a path along a ride, symbols 507 or 508 should be used in place of symbol 509. A footbridge with no path leading to it. Note: if the stream is wider than 0.25mm, adjust this symbol so it extends 0.5mm over both sides of the stream! A railway or other kind of railed track (tramway, truckway, etc.). Power line, cableway or skilift. The bars indicate the exact location of the pylons. Major power lines should be drawn with a double line. The gap between the lines may indicate the extent of the powerline. A way under roads, railways, etc. which may be used by the runner. This symbol is used whether or not the tunnel has a track leading to it. A way under roads, railways, etc. which may be used by the runner. This symbol is used whether or not the tunnel has a track leading to it. A stone wall or stone-faced bank. A ruined stone wall may be shown by a dashed line. A stone wall higher than ca 1.5 m, not crossable to the average orienteer. A wooden or wire fence less than ca. 1.5 m high. A ruined fence may be shown with a dashed line. A boarded or wire fence higher than ca 1.5 m, not crossable to the average orienteer, eg. deer fence. All ways through or over high fences or walls must be indicated. The symbol may also be used for a gate through or stile over a stone wall (519) or a fence (522) or a pipeline (534). A building is shown with its ground plan so far as the scale permits. Houses and gardens and other built up areas. Roads, buildings and other significant features within a settlement must be shown. If all buildings cannot be shown, an alternative symbol (black line screen) may be used. Houses and gardens and other built up areas. Roads, buildings and other significant features within a settlement must be shown. If all buildings cannot be shown, an alternative symbol (black line screen) may be used. Areas which are permanently forbidden to the runner are shown as out of bounds. The screen is superimposed on the normal map detail. A bounding line may be drawn if there is no natural boundary (see 709). A bounding line may be drawn if there is no natural boundary (see 709). An area of hard standing used for parking or other purposes. The ground plan of a ruin is shown to scale, down to the minimum size shown opposite. Very small ruins may be drawn with a solid line. Very small ruins may be drawn with a solid line. Very small ruins may be drawn with a solid line. A firing range is shown with a special symbol to indicate the need for caution. Associated buildings are individually marked. A distinct grave marked by a stone or shrine. Location is at the centre of gravity of the symbol, which is orientated to north. A cemetery is shown by using grave symbols as space permits. A pipeline (gas, water, oil, etc.) above ground level which can be crossed over or under. A pipeline which cannot be crossed. A high tower or large pylon, standing above the level of the surrounding forest. Location is at the centre of gravity of the symbol. An obvious shooting platform or seat, or small tower. Location is at the centre of gravity of the symbol. Cairn, memorial stone or boundary stone (or a trigonometric point in some countries) more than 0.5 m high. A fodder rack which is free standing or built on to a tree. Location is at the centre of gravity of the symbol. For land access reasons these may be omitted. Special man-made features are shown with these symbols. The definition of the symbols must be given in each case in the map legend. Special man-made features are shown with these symbols. The definition of the symbols must be given in each case in the map legend. Magnetic north lines are lines placed on the map pointing to magnetic north. Their spacing on the map should be 33.33 mm which represents 500 m on the ground at the scale of 1:15 000. For maps with other scales lines placing should be at intervals which represents a round number of meters (e.g. 50 m, 100 m, 250 m, 500 m) and the spacing should be between 20 mm and 40 mm on the map. North lines may be broken where they obscure small features such as boulders, knolls, cliffs, stream junctions, path ends, etc. In areas with very few water features, blue lines may be used. Magnetic north lines are lines placed on the map pointing to magnetic north. Their spacing on the map should be 33.33 mm which represents 500 m on the ground at the scale of 1:15 000. For maps with other scales lines placing should be at intervals which represents a round number of meters (e.g. 50 m, 100 m, 250 m, 500 m) and the spacing should be between 20 mm and 40 mm on the map. North lines may be broken where they obscure small features such as boulders, knolls, cliffs, stream junctions, path ends, etc. In areas with very few water features, blue lines may be used. At least three registration marks must be placed within the frame of a map in a non-symmetrical position. In addition, a colour check should also be possible. Spot heights are used for the rough assessment of height differences. The height is given to the nearest metre. The figures are orientated to the north. Water levels are given without the dot. Spot heights are used for the rough assessment of height differences. The height is given to the nearest metre. The figures are orientated to the north. Water levels are given without the dot. The start or map issue point (if not at the start) is shown by an equilateral triangle which points in the direction of the first control. The centre of the triangle shows the precise position of the start point. The control points are shown with circles. The centre of the circle shows the precise position of the feature. Sections of circles should be omitted to leave important detail showing. The number of the control is placed close to the control point circle in such a way that it does not obscure important detail. The numbers are orientated to north. Where controls are to be visited in order, the start, control points and finish are joined together by straight lines. Sections of lines should be omitted to leave important detail showing. A marked route is shown on the map with a dashed line. The finish is shown by two concentric circles. A boundary which it is not permitted to cross. A crossing point through or over a wall or fence, or across a road or railway or through a tunnel or an out-of-bounds area is drawn on the map with two lines curving outwards. An out-of-bounds area, see also symbol 528, is shown with vertical stripes. A bounding line may be drawn if there is no natural boundary, as follows: - a solid line indicates that the boundary is marked continuously (tapes, etc.) on the ground, - a dashed line indicates intermittent marking on the ground, - no line indicates no marking on the ground. A solid line indicates that the boundary is marked continuously (tapes, etc.) on the ground. A dashed line indicates intermittent marking on the ground. An area presenting danger to the competitor is shown with cross-hatched diagonal lines. A route which is out-of-bounds is shown with crosses. The location of a first aid post. The location of a refreshment point which is not at a control. This symbol provides a simple and quick way to make training courses. The purple line will extend a bit into the finish symbol. This is a shortcoming of this simple approach. The OpenOrienteering Logo. Forest map sample mapper-0.8.1.1/examples/src/overprinting.xmap000066400000000000000000016721021325266516600212120ustar00rootroot00000000000000 PURPLE BLACK BLUE BROWN GREEN YELLOW A line joining points of equal height. The standard vertical interval between contours is 5 metres. The smallest bend in a contour is 0.25 mm from centre to centre of the lines. Every fifth contour shall be drawn with a thicker line. This is an aid to the quick assessment of height difference and the overall shape of the terrain surface. Where an index contour coincides with an area of much detail, it may be shown with a normal contour line. An intermediate contour line. Form lines are used where more information can be given about the shape of the ground. They are used only where representation is not possible with ordinary contours. Only one form line may be used between neighbouring contours. Slope lines may be drawn on the lower side of a contour line, e.g. along the line of a re-entrant or in a depression. They are used only where it is necessary to clarify the direction of slope. Contour values may be included to aid assessment of large height differences. They are inserted in the index contours in positions where other detail is not obscured. The figures should be orientated so that the top of the figure is on the higher side of the contour. A steep earth bank is an abrupt change in ground level which can be clearly distinguished from its surroundings, e.g. gravel or sand pits, road and railway cuttings or embankments. The tags should show the full extent of the slope, but may be omitted if two banks are close together. Impassable banks should be drawn with symbol 201 (impassable cliff). The line width of very high earth banks may be 0.25 mm. A steep earth bank is an abrupt change in ground level which can be clearly distinguished from its surroundings, e.g. gravel or sand pits, road and railway cuttings or embankments. The tags should show the full extent of the slope, but may be omitted if two banks are close together. Impassable banks should be drawn with symbol 201 (impassable cliff). The line width of very high earth banks may be 0.25 mm. The line width of very high earth banks may be 0.25 mm. The line width of very high earth banks may be 0.25 mm. Use this symbol to display the full extent of wide earth banks. Distinct earth wall. Minimum height is 1 m. A small or partly ruined earth wall shall be shown with a dashed line. Minimum height is 0.5 m. An erosion gully or trench which is too small to be shown by symbol 106 is shown by a single line. The line width reflects the size of the gully. Minimum depth 1 m. The end of the line is pointed. A small erosion gully or trench. Minimum depth 0.5 m. A small obvious mound or rocky knoll which cannot be drawn to scale with a contour (diameter of mound less than ca. 5 m). The height of the knoll should be a minimum of 1 m from the surrounding ground. The symbol may not touch a contour line. A small obvious elongated knoll which cannot be drawn to scale with a contour (length less than 12 m and width less than 4 m). The height of the knoll should be a minimum of 1 m from the surrounding ground. Knolls larger than this must be shown by contours. The symbol may not be drawn in free form or such that two elongated knoll symbols overlap. The symbol may not touch a contour line. Small shallow natural depressions and hollows (minimum diameter 2 m) which cannot be shown to scale by contours are represented by a semicircle. Minimum depth from the surrounding ground should be 1 m. Location is the centre of gravity of the symbol, which is orientated to north. Symbol 116 is used for man-made pits. Pits and holes with distinct steep sides which cannot be shown to scale by symbol 106 (minimum diameter 2 m). Minimum depth from the surrounding ground should be 1 m. Location is the centre of gravity of the symbol which is orientated to north. An area of pits or knolls which is too intricate to be shown in detail. The density of randomly placed dots may vary according to the detail on the ground. The size of the dots may vary. This symbol can be used for a special small land form feature. The definition of the symbol must be given in the map legend. An impassable cliff, quarry or earth bank (see 106) is shown with a 0.35 mm line and downward tags showing its full extent from the top line to the foot. For vertical rock faces the tags may be omitted if space is short, e.g. narrow passages between cliffs (the passage should be drawn with a width of at least 0.3 mm). The tags may extend over an area symbol representing detail immediately below the rock face. When a rock face drops straight into water making it impossible to pass under the cliff along the water's edge, the bank line is omitted or the tags should clearly extend over the bank line. An impassable cliff, quarry or earth bank (see 106) is shown with a 0.35 mm line and downward tags showing its full extent from the top line to the foot. For vertical rock faces the tags may be omitted if space is short, e.g. narrow passages between cliffs (the passage should be drawn with a width of at least 0.3 mm). The tags may extend over an area symbol representing detail immediately below the rock face. When a rock face drops straight into water making it impossible to pass under the cliff along the water's edge, the bank line is omitted or the tags should clearly extend over the bank line. For vertical rock faces the tags may be omitted if space is short, e.g. narrow passages between cliffs (the passage should be drawn with a width of at least 0.3 mm). Use this symbol to display the full extent of a wide cliff. In the case of unusual features such as rock pillars or massive cliffs or gigantic boulders, the rocks shall be shown in plan shape without tags. A small vertical rock face (minimum height 1 m) may be shown without tags. If the direction of fall of the rock face is not apparent from the contours or to improve legibility, short tags should be drawn in the direction of the fall. For passable rock faces shown without tags the ends of the line may be rounded to improve legibility. A small vertical rock face (minimum height 1 m) may be shown without tags. If the direction of fall of the rock face is not apparent from the contours or to improve legibility, short tags should be drawn in the direction of the fall. For passable rock faces shown without tags the ends of the line may be rounded to improve legibility. Should be used if the direction of fall of the rock face is apparent from the contours and the legibility is good. Should be used if the direction of fall of the rock face is apparent from the contours and the legibility is good. For passable rock faces shown without tags the ends of the line may be rounded to improve legibility. For passable rock faces shown without tags the ends of the line may be rounded to improve legibility. Rocky pits, holes or mineshafts which may constitute a danger to the runner. Location is the centre of gravity of the symbol, which is orientated to north. A cave is represented by the same symbol as a rocky pit. In this case the symbol should be orientated to point up the slope as indicated opposite. The centre of gravity of the symbol marks the opening. A small distinct boulder (minimum height 1 m). Every boulder marked on the map should be immediately identifiable on the ground. To be able to show the distinction between boulders with significant difference in size it is permitted to enlarge this symbol by 20% (diameter 0.5 mm). To be able to show the distinction between boulders with significant difference in size it is permitted to enlarge this symbol by 20% (diameter 0.5 mm). A particularly large and distinct boulder. For gigantic boulders symbol 202 should be used. An area which is covered with so many blocks of stone that they cannot be marked individually is shown with randomly orientated solid triangles with sides of ratio 8:6:5. A minimum of two triangles should be used. The going is indicated by the density of the triangles. To be able to show the distinction between boulder fields with a significant difference in boulder size it is permitted to enlarge the triangles by 20%. To be able to show the distinction between boulder fields with a significant difference in boulder size it is permitted to enlarge the triangles by 20%. A small distinct group of boulders so closely clustered together that they cannot be marked individually. The symbol is an equilateral triangle orientated to the north. To be able to show the distinction between boulder clusters with significant difference in size it is permitted to enlarge this symbol by 25% (1.0 mm). To be able to show the distinction between boulder clusters with significant difference in size it is permitted to enlarge this symbol by 25% (1.0 mm). Stony or rocky ground which affects going should be shown on the map. The dots should be randomly distributed with density according to the amount of rock. A minimum of three dots should be used. Stony or rocky ground which affects going should be shown on the map. The dots should be randomly distributed with density according to the amount of rock. A minimum of three dots should be used. An area of soft sandy ground or gravel with no vegetation and where running is slow. Where an area of sandy ground is open but running is good, it is shown as open land (401/402). A runnable area of rock without earth or vegetation is shown as bare rock. An area of rock covered with grass, moss or other low vegetation is shown as open land (401/402). Large areas of water are shown with dot screen. Small areas of water should be shown with full colour. A black bank line indicates that the feature cannot be crossed. A black bank line indicates that the feature cannot be crossed. Where the lake or pond is smaller than 1 mm² on the printed map, the bank line is omitted. A water-filled pit or an area of water which is too small to be shown to scale. Location is the centre of gravity of the symbol, which is orientated to north. A crossable watercourse, minimum 2 m wide. The width of watercourses over 5 m wide should be shown to scale. A crossable watercourse (including a major drainage ditch) less than 2 m wide. For better legibility a ditch in a marsh should be drawn as a crossable watercourse (305). A natural or man-made minor water channel which may contain water only intermittently. A marsh or trickle of water which is too narrow to be shown with symbol 310 (less than ca. 5 m wide). A marsh which is uncrossable or dangerous for the runner. A black line surrounds the symbol. A black line surrounds the symbol. A crossable marsh, usually with a distinct edge. The symbol should be combined with vegetation symbols to show runnability and openness. Where a small marsh area should be combined with either 403/404 it is permitted to use 401/402 to improve legibility. An indistinct or seasonal marsh or area of gradual transition from marsh to firm ground, which is crossable. The edge is generally indistinct and the vegetation similar to that of the surrounding ground. The symbol should be combined with vegetation symbols to show runnability and openness. Wells and captive springs, which are clearly visible on the ground. The source of a stream with a distinct outflow. The symbol is orientated to open downstream. A special small water feature. The definition of the symbol must always be given in the map legend. Cultivated land, fields, meadows, grassland, etc. without trees, offering easy running. If yellow coloured areas becomes dominant, a screen (75%) instead of full yellow may be used. Meadows with scattered trees or bushes, with grass or similar ground cover offering easy running. Areas smaller than 10 mm at the maps scale are shown as open land (401). Individual trees may be added (418, 419, 420). If yellow coloured areas becomes dominant, a screen (75%) instead of full yellow may be used. Heath, moorland, felled areas, newly planted areas (trees lower than ca. 1 m) or other generally open land with rough ground vegetation, heather or tall grass. Symbol 403 may be combined with symbols 407 and 409 to show reduced runnability. Where there are scattered trees in rough open land, areas of white (or green) should appear in the tone. Such an area may be generalised by using a regular pattern of large white dots in the yellow screen. Areas smaller than 16 mm in the maps scale are shown as rough open land (403). Individual trees may be added (418, 419, 420). Typically open runnable forest for the particular type of terrain. If no part of the forest is runnable then no white should appear on the map. An area with dense trees (low visibility) which reduces running to ca. 60-80% of normal speed. An area of dense undergrowth but otherwise good visibility (brambles, heather, low bushes, and including cut branches) which reduces running to ca. 60-80% of normal speed. This symbol may not be combined with 406 or 408. An area with dense trees or thicket (low visibility) which reduce running to ca. 20-60% of normal speed. An area of dense undergrowth but otherwise good visibility (brambles, heather, low bushes, and including cut branches) which reduces running to ca. 20-60% of normal speed. This symbol may not be combined with 406 or 408. An area of dense vegetation (trees or undergrowth) which is barely passable. Running reduced to ca. 0-20% of normal speed. Line of minimum width for symbol 410. When an area of forest provides good running in one direction but less good in others, white stripes are left in the screen symbol to show the direction of good running. When an area of forest provides good running in one direction but less good in others, white stripes are left in the screen symbol to show the direction of good running. When an area of forest provides good running in one direction but less good in others, white stripes are left in the screen symbol to show the direction of good running. Land planted with fruit trees or bushes. The dot lines may be orientated to show the direction of planting. If yellow coloured areas becomes dominant, a screen (75%) instead of full yellow may be used. The green lines may be orientated to show the direction of planting. If yellow coloured areas becomes dominant, a screen (75%) instead of full yellow may be used. The boundary of cultivated land when not shown with other symbols (fence, wall, path, etc.) is shown with a black line. A permanent boundary between different types of cultivated land is also shown with this symbol. Cultivated land which is seasonally out-of-bounds due to growing crops may be shown with a black dot screen. A distinct forest edge or very distinct vegetation boundary within the forest. Symbols 418, 419 and 420 can be used for special small vegetation features. The definition of the symbol must be given in each case in the map legend. Symbols 418, 419 and 420 can be used for special small vegetation features. The definition of the symbol must be given in each case in the map legend. Symbols 418, 419 and 420 can be used for special small vegetation features. The definition of the symbol must be given in each case in the map legend. A road with two carriageways. The width of the symbol should be drawn to scale but not smaller than the minimum width. The outer boundary lines may be replaced with symbols 519, 521, 522 or 524 if a fence or wall is so close to the motorway edge that it cannot practically be shown as a separate symbol. The space between the black lines must be filled with brown (50%). A road under construction may be shown with broken lines. A road under construction may be shown with broken lines. Road wider than 5m. The width of the symbol should be drawn to scale but not smaller than the minimum width. The outer boundary lines may be replaced with symbols 519, 521, 522 or 524 if a fence or wall is so close to the motorway edge that it cannot practically be shown as a separate symbol. The space between the black lines must be filled with brown (50%). A road under construction may be shown with broken lines. A road under construction may be shown with broken lines. Road 3-5 m wide. The space between the black lines must be filled with brown (50%). A road under construction may be shown with broken lines. A road under construction may be shown with broken lines. A maintained road suitable for motor vehicles in all weather. Width less than 3 m. A track or poorly maintained road suitable for vehicles only when travelling slowly. Width less than 3 m. A large path, or old vehicle track, which is distinct on the ground. A small path or (temporary) forest extraction track which can be followed at competition speed. A less distinct small path or forestry extraction track. A distinct ride, less than ca. 5 m wide. A ride is a linear break in the forest (usually plantation) which does not have a distinct path along it. Where there is a path along a ride, symbols 507 or 508 should be used in place of symbol 509. A footbridge with no path leading to it. Note: if the stream is wider than 0.25mm, adjust this symbol so it extends 0.5mm over both sides of the stream! A railway or other kind of railed track (tramway, truckway, etc.). Power line, cableway or skilift. The bars indicate the exact location of the pylons. Major power lines should be drawn with a double line. The gap between the lines may indicate the extent of the powerline. A way under roads, railways, etc. which may be used by the runner. This symbol is used whether or not the tunnel has a track leading to it. A way under roads, railways, etc. which may be used by the runner. This symbol is used whether or not the tunnel has a track leading to it. A stone wall or stone-faced bank. A ruined stone wall may be shown by a dashed line. A stone wall higher than ca 1.5 m, not crossable to the average orienteer. A wooden or wire fence less than ca. 1.5 m high. A ruined fence may be shown with a dashed line. A boarded or wire fence higher than ca 1.5 m, not crossable to the average orienteer, eg. deer fence. All ways through or over high fences or walls must be indicated. The symbol may also be used for a gate through or stile over a stone wall (519) or a fence (522) or a pipeline (534). A building is shown with its ground plan so far as the scale permits. Houses and gardens and other built up areas. Roads, buildings and other significant features within a settlement must be shown. If all buildings cannot be shown, an alternative symbol (black line screen) may be used. Houses and gardens and other built up areas. Roads, buildings and other significant features within a settlement must be shown. If all buildings cannot be shown, an alternative symbol (black line screen) may be used. Areas which are permanently forbidden to the runner are shown as out of bounds. The screen is superimposed on the normal map detail. A bounding line may be drawn if there is no natural boundary (see 709). A bounding line may be drawn if there is no natural boundary (see 709). An area of hard standing used for parking or other purposes. The ground plan of a ruin is shown to scale, down to the minimum size shown opposite. Very small ruins may be drawn with a solid line. Very small ruins may be drawn with a solid line. Very small ruins may be drawn with a solid line. A firing range is shown with a special symbol to indicate the need for caution. Associated buildings are individually marked. A distinct grave marked by a stone or shrine. Location is at the centre of gravity of the symbol, which is orientated to north. A cemetery is shown by using grave symbols as space permits. A pipeline (gas, water, oil, etc.) above ground level which can be crossed over or under. A pipeline which cannot be crossed. A high tower or large pylon, standing above the level of the surrounding forest. Location is at the centre of gravity of the symbol. An obvious shooting platform or seat, or small tower. Location is at the centre of gravity of the symbol. Cairn, memorial stone or boundary stone (or a trigonometric point in some countries) more than 0.5 m high. A fodder rack which is free standing or built on to a tree. Location is at the centre of gravity of the symbol. For land access reasons these may be omitted. Special man-made features are shown with these symbols. The definition of the symbols must be given in each case in the map legend. Special man-made features are shown with these symbols. The definition of the symbols must be given in each case in the map legend. Magnetic north lines are lines placed on the map pointing to magnetic north. Their spacing on the map should be 33.33 mm which represents 500 m on the ground at the scale of 1:15 000. For maps with other scales lines placing should be at intervals which represents a round number of meters (e.g. 50 m, 100 m, 250 m, 500 m) and the spacing should be between 20 mm and 40 mm on the map. North lines may be broken where they obscure small features such as boulders, knolls, cliffs, stream junctions, path ends, etc. In areas with very few water features, blue lines may be used. Magnetic north lines are lines placed on the map pointing to magnetic north. Their spacing on the map should be 33.33 mm which represents 500 m on the ground at the scale of 1:15 000. For maps with other scales lines placing should be at intervals which represents a round number of meters (e.g. 50 m, 100 m, 250 m, 500 m) and the spacing should be between 20 mm and 40 mm on the map. North lines may be broken where they obscure small features such as boulders, knolls, cliffs, stream junctions, path ends, etc. In areas with very few water features, blue lines may be used. At least three registration marks must be placed within the frame of a map in a non-symmetrical position. In addition, a colour check should also be possible. Spot heights are used for the rough assessment of height differences. The height is given to the nearest metre. The figures are orientated to the north. Water levels are given without the dot. Spot heights are used for the rough assessment of height differences. The height is given to the nearest metre. The figures are orientated to the north. Water levels are given without the dot. The start or map issue point (if not at the start) is shown by an equilateral triangle which points in the direction of the first control. The centre of the triangle shows the precise position of the start point. The control points are shown with circles. The centre of the circle shows the precise position of the feature. Sections of circles should be omitted to leave important detail showing. The number of the control is placed close to the control point circle in such a way that it does not obscure important detail. The numbers are orientated to north. Where controls are to be visited in order, the start, control points and finish are joined together by straight lines. Sections of lines should be omitted to leave important detail showing. A marked route is shown on the map with a dashed line. The finish is shown by two concentric circles. A boundary which it is not permitted to cross. A crossing point through or over a wall or fence, or across a road or railway or through a tunnel or an out-of-bounds area is drawn on the map with two lines curving outwards. An out-of-bounds area, see also symbol 528, is shown with vertical stripes. A bounding line may be drawn if there is no natural boundary, as follows: - a solid line indicates that the boundary is marked continuously (tapes, etc.) on the ground, - a dashed line indicates intermittent marking on the ground, - no line indicates no marking on the ground. A solid line indicates that the boundary is marked continuously (tapes, etc.) on the ground. A dashed line indicates intermittent marking on the ground. An area presenting danger to the competitor is shown with cross-hatched diagonal lines. A route which is out-of-bounds is shown with crosses. The location of a first aid post. The location of a refreshment point which is not at a control. This symbol provides a simple and quick way to make training courses. The purple line will extend a bit into the finish symbol. This is a shortcoming of this simple approach. The OpenOrienteering Logo. Click menu View > Overprinting simulation or press F4 to toggle overprinting preview. mapper-0.8.1.1/images/000077500000000000000000000000001325266516600144245ustar00rootroot00000000000000mapper-0.8.1.1/images/about.png000066400000000000000000000035551325266516600162540ustar00rootroot00000000000000PNG  IHDR szzsBIT|d$IDATXIGU߾y CH6qBQ E" .` !7 ,8@6G$ N,c{6Rqx$$pOZꮯ몯uS0^JY V~V)O̟"|\U! M 4F,Nfh ꎟG^Ƚzj̘mĐ$06Ⲿ4k><8k|qRb>G>u]!uHg׋sH]u7&ʄ ʥ"iNbZ>ݑϊ)iuڊ?l<b> q4Tl6CEhPJ#&f07Z:gߡ^\SWm\ԯ_SCXk%c^H$Iž\hܧш!IPW>WS!|e7 }=qGz06t:>fzfM>OĠR8^H?63s=x;c7f͏ť7 &v0>}LNeqrc)R*NvAa}e8߿!{j;5bՍ7Z4mz\ylJ^'? R9t d#ȉtv́v&J?NNk5:AHul|D {"tF>&HÆuD&RR|O'OfjW?KBKqA** `\lÄmaSH `&6 qB0IR 7(06dTXͱ]>6-\@3,&sp+0)Q4[l'VN./6$D$qc@ٷ#[dy'Gby+:{qU 7t4FU0{d˧xW9 Q*o.,D{PH $ v:9I1+!*Seێqg覧]nQd(szKSɹ3FvfVg2I\\RoathF&!bL`LSg7QJ(0 bVnݦ i\]!.tm}:2RN{8N3LҤ](0֠lʀRqg@L{kU9FNJC.e:/$)(G AsLqkbP B3 ,6b "Bk9\C0s@᏶k͏V2ZĠB  _B;^TȣK @ .%×7> - o/ LJ3T2\ٍ_p&?=웙Hss]sM72 aLٓ(ho<2PQ;tӧ&oB*S9~EU A$H6.< \̍|(ϕ,btW5H~<z* o1IENDB`mapper-0.8.1.1/images/arrow-down.png000066400000000000000000000022431325266516600172320ustar00rootroot00000000000000PNG  IHDR szzsBIT|dZIDATXKlTU{-P4jJ&1 ##MQnи%4Acbb@<ڂ0B<ǽ̴2-#a×sg9ǽ=Ӣu^`LYzҷt8gb`:+-!ׅ)S# ۉLelȦlЦlH$Y6]$:nG-n|=UUdo83@A|"6/"]Z9D@KGwlEu~6 E @ !i@P҂a0( %%^&^TgnB>XXWCf*"X VBh {H$JX: "|^X}rNh#!+!)ύtq.@xjOn=Ff1k g?ѸDOo:mA[z6R0*h@'^{ZvwS_'ڷ p+ȤkY=E*&W"r=s~Ӊ t8s5^μbeCęWƻ) 650xsw{̘<_B_xxp~"[l%4=3ko}*2捦:.v]:os :8e35jOpV~cS `(QJTuVG:k䔦#go] U`^0i 6~OOX}}~9vԵPf4x:XXPQf6gZbRз  uO^Ŀ5/vmIENDB`mapper-0.8.1.1/images/arrow-left.png000066400000000000000000000022601325266516600172140ustar00rootroot00000000000000PNG  IHDR szzsBIT|dgIDATXmhU?̊"ôYݩeVكJ SM{^ ^AA7EM0EJEe|u=޻{y۽׭@?tLa FU_้-; NȄ<&V oG޿sK_*S̵%>C- R xRւ0a MP͌Iڊ9KoKlޤoܣu n2 "Ey@|֔@zRTST #bc}+W'網Y$(_|C9A)xAGjC S-m[o$^ZJoD9YY4auXŎF GvUmOO_vSk#Cr~𗀄{4|il5N|he7vnbaͪ?P&uСd$mBR S$Ч>ןv7-q I`H9ybµF{M] /άGp;^>GpMpt2Ab̶.1CŘYttLT6rõu5)cFIENDB`mapper-0.8.1.1/images/arrow-right.png000066400000000000000000000023031325266516600173750ustar00rootroot00000000000000PNG  IHDR szzsBIT|dzIDATXhUe?{qd3S ~.(*- C2#( B7#  D#,ei6r*͙S7ڶ{~yw_{=y}mhEQl=wu$O{b{% Zh-r凯}kǓ0nzloZR0p1'p˄JS.Ggjofe,(g0Z$8G*9b|rF [^Z=OAmti}wh,S.hdOdLl$=A-ݺ5 ,^e9H$BfJa &;k4SF?HyMi=I̯ؽS3> ).B,m (Z:NӞ:*glU_.=L3W]|Zx( d#;Nr7YqwsFmYh@"VatxhKJIsh;%m%S||VKkÒ6h Fq< gR?c FC=NGI.Ffw;2 l#"UDftӔ>qXrԮ0?rsIBB-0-g! vkK>ru"OW|1D^ 򡬢=pdA4.|,cҾdA}!/wtlɡx-H@ERF677-߳Z}ETA)ɊXIENDB`mapper-0.8.1.1/images/arrow-thin-downright.png000066400000000000000000000007021325266516600212260ustar00rootroot00000000000000PNG  IHDR szzbKGD pHYs  tIME 1ruOIDATX׽JA%~ X]k!HZ;CH[V]!86{$^f׃쁌OX0~nx6c` \wci)k3{I3 @י Bh`. H:A)Z70B@h Q{"4ݵEHf"D2w8 WE {i%nX+ hHXBKY w@M9OCx9pVto`9!UXLcBp u#,N$IENDB`mapper-0.8.1.1/images/arrow-thin-upleft.png000066400000000000000000000006711325266516600205250ustar00rootroot00000000000000PNG  IHDR szzbKGD pHYs  tIME 1@RFIDATX1kAg'9 TV66 FTR$N۝ټ3>lWL1g!>`s^cKϘ9~(ǿve I_T)WT)Q*V^Ey<|q5F9RJ"bclDl..x/ ,7eo_"@ġj#&"]-D yk bw<5v!*!j>CM E4@LRJ3m؀xWxS==k ܴ봅;)bn4w=)_*6̼}W~[ -+3-rJ= .?_)vMf&Sl_rg>#0 .lU6LLtb&uk8--NCZ`ĚX~[{SnC:Ѹ=1g֝s "8 !-Xs״es쮉+PQk*]ʲ5'0idOGo#K{?t;JM?P^k/:c3U㿶ӝh5<?CقoNV\:<^1bkX,U-=k`^<&_=meunc ]_s_j:~n %+ }GJw4ڵ_VW/*:]N@U#QQcςg1J@E?{3 `(|~`%5ߐ N5!sHnax-]g\Xm[ۛgݶX玍5liY7hލ5;!1ؙ} $=m8 Q@*5PՁ ?\W~x0[f_p*tg TbAJ^7``PP{;#$䋑P+K_Ə$q\?GB;9wNah~ @ $FZaRKIh$Q:Eya&.] c)MalTfπ+PPoԆ1πg$sH>O m UIQ#:c  022i 4AzGj(VW_?T0R qP554Kgde2&l˂CD3CnPh'Ӵfg<9ioご|~IENDB`mapper-0.8.1.1/images/close.png000066400000000000000000000034751325266516600162500ustar00rootroot00000000000000PNG  IHDR szzbKGD pHYs  tIME "/3IDATXõW]lY=sxfvM&MP[QE(*]D:Z!"$$Boॼ# ,/˲Z T-DWNb؞83=3]7iW49|ߜ p\traƑ4ks1\pAcܓ$V%=lSJZQRAN<|!KL>_5BH0 z4M9L$~-I70T#o!:0D8RXT;U՚&0 oB ȌBh|H``C5bRyܸqar0v"4MSq,٬$B$I"r$I/˲y#pyyyv8{[[[ >~Ey < $I1Ldo.,,O}$y5 ÛB ^O뭯Ѷw8N~CcJ&9~1IQhZM*40fP Oː|ommK 5 h4^k^BJ T*2bqUy 4 !;Ɇϒ$!??EQV*rUTU(zܐ mqkaVHjwc'@A"mm==}p]wWBðP,a6?`x"?,+ iHwvmض}/}(J׫^/kN`IENDB`mapper-0.8.1.1/images/colors.png000066400000000000000000000004131325266516600164310ustar00rootroot00000000000000PNG  IHDR szzbKGD pHYs  tIMEQWiTXtCommentCreated with GIMPd.eoIDATXֱ 0 !qq2Ux $$ ŧx7 G'őMIpFIˎb~ @%YZ̷30  7658 MIENDB`mapper-0.8.1.1/images/compass.png000066400000000000000000000037611325266516600166060ustar00rootroot00000000000000PNG  IHDR szzbKGDC pHYs  tIME _~IDATXÕ]lS}r;?0#. HH[[M:z1M)I7nvYba0MLTMcmB |8NBp>|]c' G:rxik7|57 hYa(d6B%>꺵+Vx W=VGFH_7` V?SSxRՁê0£;Y?@46zTඝ; 4bH^`+ gfjPhMze)Et"odwsIHКom^ΞED#Z#ůácHAjdQ46z7& \RPdGkWp+_{RLw7zhj[] qDk@"läzz T433aGoa, .DNf*CCh^ze[5wk{;R 7M+<~fy5"B8c"+WuֶZ? TZf kQ&& $Z#R%> ?<5>G4LL "*k7 uul>ᒏHrg_Ǘᛘdnpe !sy=Adt&b1%S!GFkmY!য়0 ?o,JJvuU:=3`-x ˒8jΝc49x[60ǥ]h,yQJœЕl-,oj كORq`am^ RrY/Rh037cTW7M>"0fVo,%eJ{a$J"|14r9wrϨGklr~D<# Z)ūy.>sK6N:ږ377|,Xrw*txD4 gEӓNYV!oS E:.\dc5s]KT}v?NC @gβ ݩԝ\ؘ$Qs]!;)5I|{iA9DVIC՝JY3LRu8kM$bfr=mdN:-M$t ^!hyuWnм*0k¥. ȝtZQ7Umax0pXߺE;0l4\1Vd㺈]'NpQ ~Y:$ DD" N8g2lزjOuM#lZY>)WnG*Rk2[S] J|t6~4C!Vb|>i2S?dlD @Tbqɣz$7;kNLT==^*N7zdzT6+*QU?GyU*Y"IENDB`mapper-0.8.1.1/images/control.png000066400000000000000000000004761325266516600166210ustar00rootroot00000000000000PNG  IHDR szzsRGBbKGD pHYs B(xtIME  5!#.>SIDATX׻ B1 +$.@YQTA#8t/wN "sNѩԨ*mg {-&ᴀ%l5[@-Ђ=Y@/n0Cxw`7XMxu^ox |106|kā}2g"IENDB`mapper-0.8.1.1/images/copy-coords.png000066400000000000000000000024321325266516600173740ustar00rootroot00000000000000PNG  IHDR szzbKGDIDATXW[oEfoYqmI@&&"iBEV*R(֊xABB$ z< hm\ noL6dSEFsf| ׮ŏR\tUHR->U-T;rBegaaF0 .ɚ9ɤ@)%^oN,*bRV,뢮J$q qT]Ѩ .59spCa@` 5-/0 nhhHE8EH]ݣ=X,&7/*J?|wS]h뺅ӈD%4* H޸ ? ,!',́d2iߟ:Pd=dF`˚A̤?Fw` Zw V!Yi2 tc&~OUUU _`StC333!_ +愕@ÐiYc4&;⣣anmm͎յ(L>7ҳxb7A.ȅI|ubnw5>XvÒ0r ֭St{77VAb.gYeIX9ԔkkkX>n6K7I72،#Y#@$ Sxzג3PgB{a8@(<O?@W-/<>$ 5(qgI8ttthQl( rR JvmsV`p8o1Y*`,/MfHa[4F.!ue IO=t%ʩDF5ոaPV%{˫ p`iiIy "%" a0Y"*Y!"OXqBT*AeIe tС( Dsev`px(@&,T),+ L]LjH BSU(HR璂~9BxHZ_H"D0n~$?T), DM c'a)iƘia IbJ{"~{b`{ fPi&"<|iLp4D>.k*q`n9We7;{7  I*c@O{a=?\I ڵJXup1 `6}9Y.W& TD"i҇1!}3?NP]zo.'[F񩤰nߵ1v0擕յiט{VT MIENDB`mapper-0.8.1.1/images/cursor-crosshair.png000066400000000000000000000002241325266516600204400ustar00rootroot00000000000000PNG  IHDR szzsRGBNIDATX1 0vC 6ѠRW1Kͤpwpw`fW|rU$kf!(^ vIENDB`mapper-0.8.1.1/images/cursor-cut.png000066400000000000000000000020561325266516600172430ustar00rootroot00000000000000PNG  IHDR szzsRGBbKGD pHYs  tIME /w.JIDATXŕOH#gƟudhGF%%: BORZ B)-Ea\ԃ[ .Q[cĎ$u2%YD}N3}dEalxm2 D6uF 㛬h/-h/@:+nAMY1WXWX9[΢jU( ޫuhrO)mK=MplsH$/D"Q́}`'լ[W͡uX V瘜lޔA 0??_n6b fYXX؝y4@#ɲ Dq(XӴ/;Υ,7M\p/5==L&AaQȲ7q@ D"*XDŏ ¿Rb-FQibyy&Id("_yciiiI777 r0 L&#|l6^}Al6[Z\\|@SX6z{{h4 |`0y*vvv[aC\.,`0Rr9eLMM=8*؋h4*epnBdYiy4M=e?U05}U.Ɲ`ZAQ$~?v;666p8v!mBH,IMx(rOZBBn7Edby(zeY3OTU N?іx0y T ~}PºP*}3&)r NOOA(T*$ !t:uݧ(oT9G~ y4Mvz!dpR]F5ah`&h,`8IENDB`mapper-0.8.1.1/images/cursor-delete.png000066400000000000000000000014561325266516600177150ustar00rootroot00000000000000PNG  IHDR szzsRGBIDATXKTaw\3Ę4(e AТ&D.ZAQV- rm]Rd3SGϜ9Ed^g6v}~pd) e sι$I<[444d(φlXH}dZ*lXik2b]L S*6ȔM`'B733$im$)Uۻ^nmom-~Z[@*+wZ:i+;:[GG \:UЖϼӮy"Q? Bnrs  LF>Ujڳ 6C^"(6 DȳۋG|̀ T M4drx,2֒*X5:Ӗ1݊.<Ѵٴ*w]67 eD$ |$ӹa]EYB/#dW" Ep{Ӝh(CJ$[cEL$q*v0>׶6͈: ,sL>U?j曛-QFD]9Ƈ=( y@l,!g-CWU .,\6Q(olw ^3M(@0ҺO£b8pt3^3ǁy<\Dnw)@~yP|%?ԣ19;IENDB`mapper-0.8.1.1/images/cursor-draw-circle.png000066400000000000000000000012451325266516600206430ustar00rootroot00000000000000PNG  IHDR szzsRGBbKGD pHYs  tIMEEc%IDATX햱@$Vr8]cn#ۺeb^+aR!MQ\seY<0ctpȘwp`R 9;8iँ^u'0nU9NAt|ABBtgM> 0t|!f{:ΙrVEQz $A`|:~ n?O=@U }Z=[,lƶW@ 芢z^<` !zߟ-K>}Ji4n45rgex'IL)m%5SJ[$<[u 4ME(b:]ELE423+ |8>v8>K$0t$IA}i%AtI C'|q `URJQ1lP׶,90lhƵ,9۶nB e983cl6R'kAkVZVT*|?Gя(~/S|n(n@IENDB`mapper-0.8.1.1/images/cursor-draw-path.png000066400000000000000000000011561325266516600203370ustar00rootroot00000000000000PNG  IHDR szzsRGB(IDATXU=Qo\GAStca@TIX4O(6UH!X n!4i-Q/bƯ8& !H6}p  n;@Ds GuKY/ F޿kb?w ;+*M| _x^aݚvr|JZBZ,p8((IRr8p<& 'xuoD0 ɲ `^{[B 0:\a0 @D2N?w{ۂu0 & D^rh4Z@4 b6͞4\J^~[ŋ%*wAQ|F_A$6~\nPJC(:$AǑLSv81ɼ916M8N'PJYo$IMN@!ǚ˲va6no-A 94afǃZMyZ&ج J30@Qt:hT xH$~^lZIENDB`mapper-0.8.1.1/images/cursor-draw-point.png000066400000000000000000000004051325266516600205300ustar00rootroot00000000000000PNG  IHDR szzsRGBIDATX 0 E;7ɡSd#5ЂQu Ty&c s 9V1W0`?K)à D5i>R'DMq":>CJRqYkAJS[Dpw93sx(XW5oO)Z QJIWeY4u9AD}w1IENDB`mapper-0.8.1.1/images/cursor-draw-text.png000066400000000000000000000012441325266516600203650ustar00rootroot00000000000000PNG  IHDR szzsRGB^IDATX?KAƟnŤPDA XHR"g&|;;_!,bc !)&T))V~}%//cpӲ0P^q3h# b2=Z' Yz{o%F#%{.B$0?CEyeE{{ۈ D"+&C~ǯѺ*Xb-ޅ=nx^d %e˶1d!@)LFc(6ۍ 6[zol6cll EE39O?_91;A)(HKMGr:NTBMd|gg]@Q464P JK $I8N4Xwff\Q"z, BQLfHV ^ >SP5 $m.LAju)l>pIHhZplxSd I.reYw\jt:I&!3(S-S߿k景MQ"_e!j'SqQ8ǼA#T?K8F8lNߧq7s8+X؜mIENDB`mapper-0.8.1.1/images/cursor-georeferencing-add.png000066400000000000000000000003231325266516600221530ustar00rootroot00000000000000PNG  IHDR szzsRGBIDATX 0 A*dwt4!":(2*$92`}=`$fS(eٜslӁS%bmޗd@#$@<Dr "pKDj,=Tw[MU.B3ڸYIENDB`mapper-0.8.1.1/images/cursor-georeferencing-move.png000066400000000000000000000004141325266516600223720ustar00rootroot00000000000000PNG  IHDR szzsRGBIDATX1 0 E ^rW)AL W '5n68כ/"sگuIENDB`mapper-0.8.1.1/images/cursor-hollow.png000066400000000000000000000003221325266516600177460ustar00rootroot00000000000000PNG  IHDR szzsRGBIDATX핹 WZV u`d%7эnBIB)%LBDRQФ'c EXQ)V`Znaak85,˭Aex8!//;~*IBȻb:֪vBHG A JA׳+ʷ\.w0ciSf?f' Yujy&m>?(ˬi7i~6 C%3^xS0ݓPXgkXp9k}_ ndBL'*e +S\N'] B5?!Mѧ[7n#TQ,WФC(CDzRS^ @ڻHtFavCKw{_R !h z@nS!@ @EiHN~dNAsi $H{9@ngmmlGҎ`3@t[lRr} cÙ99ߜSͺŹv7`Yo% YV̟5K&{zA]Wcb…yӆ$`lE07Pz:1P@H.lC`$WSؖef:|>@OokkͲh`}Z12P7{Nw2SNɌ=3 gL۰r .LIJ1W~>>2bL]?K?{4HWIENDB`mapper-0.8.1.1/images/delete.png000066400000000000000000000035411325266516600163770ustar00rootroot00000000000000PNG  IHDR szzsRGBIDATXõWkL[~s/`p@FShZEi"UۤJLڏ*u괮]dVVU5vI֐nd  @>~߷6m۴W:9{6>,Ï=hvc.W`۲=(u`a6/ 3@pӊSQ+ii{<:lPb8|z' o;6 b8T畑wk_*/9tM0?zLg ;r%>MN$_7M[˸QοN*H^l> Lp uT$I6N''{S -Y\z#Ry0]Ls>x;@`]\r(Tz\(audgn'!2 p]GYCc YTEx}vVWPo ozdIݟ'x6{}gvdltMgMM`[`Yd0xm05&;of#0;oh\oۿsf$&@)8 n7i[طk1+NZ:&>?5mzzg @bcu>jPV]wL0 0qafHg%-M1 (JR+;$rt9%GUGlQ X@)rB_Դ8Z O8DY҉ZHʗr!}&L&3Qzk`ܺ(5`IGu HҦ1g Dzh #P^ʘS@08f dOe33T!i,zӗ.md_riW(р*sq?lo'_U%BNek zey\1e=T^.xD ;Ċ{3vm1A{IJwklqWU0U4mmg [HD;Rjݭ҆Ƴُի/(Hn:*Iڱq]X!^- 05 |j;: dЦB0ݽh5J/?? tpWm-aR(Ì:X&C<{J'@߀xI溺h2U]k` DߚȼlY;R9m&aX,pE_Xv}ZsL?fspΡ%|$O5o/\aM;MX sK$ VT'o~W PL:pY5ZEgBi!@[XxivrE.fDuNRds+9G"q`l @#CG4-OK,t4rPh@yS'ylĊ 7(Hɞ =\ O:_)kl S 8NLT*w\Ry|6@k+#V4OsյY>uOB|:dW˕zA'!yI={--|,w#Zvb^zv P52N\wJqg>Wod˵K,( x-w䯒tA#lD'8(> '_2^? IENDB`mapper-0.8.1.1/images/draw-circle.png000066400000000000000000000023521325266516600173300ustar00rootroot00000000000000PNG  IHDR szzsRGBbKGD pHYs  tIME7BUiTXtCommentCreated with GIMPd.eAIDATX͗OH#gLvA'4etS!+aN{XX7IC{hO{K{[N VȂGPm !$S8a3Chk;277}0 I`;pn 瀬iiJ8C\T*y!2n@&,&qzz @$A4mmag0Ƙ}^KuUU) /X dáYFGG9::"4M#(ʿ^:??j<YI$ gC]`I4PF0`6bN,vMVCu6KKKbK\Hr9dY}8m%Yրwa #)0f, qPU DFJ};;; VxT* 3anK,> 0pxx8'''DQ${{{X0_ƘZb|j urCQVIRSx|)NxwqvvÇLLLA5)p3 |s7dlYFMӰm[xR4'Mh43 4.hb(N_@X)DыFq4i6:_wZ}Vn&$S"zCK<#J$% /K=dTLҘgDg%[Hq\ ezCK4M\.T$`T*],NUU KTUU:HX,F"NNd't:M"]T!#m]w]qoW!h[(:7}zH/2{jd_*wX'mm㤂I*S+a&8wμ?T jьҝHfTF>X !IWQnȷr7=6~9/͞Ȝt2~LHMq.r5ӃX4P]jq~]l"gE^GŸ9x%<֝fɄիWs|)dȏ"dd-! wBୌj8x~~>'Oa @4UUU̘1FFFl޾Fi7Y-B*2@lvxTw4o 8vI<nn | 8u!z=:@ @qq1@Q݈6"ο;nX"|mtݱc(>+ ޽{/KۀkhZĸ 9m~rQ֟w9n߾шAUU, .Ç"P7p-$a0MD0fj7sN lg>O)EQ*@ivvXSS`Y1Dss3/m;#I8m8&ϱ=OZB0l۶-ɦ2N*:N8` ''˗/Wi{BtbӮJ3=S*bQ8\,[Vl6 TI&zv;=zϿ U-ۭw\(㌌jLEeuXB+"DEQPQt:YYYPPPoZTd]zunsy.\𚐾%%%_@ #Lj2zn޼ ϴ?d v|(p3Gc&{ZLuu5Q466>f0Mzƾ?@ޱJHOz]b'.ՍUIENDB`mapper-0.8.1.1/images/draw-path.png000066400000000000000000000022711325266516600170230ustar00rootroot00000000000000PNG  IHDR szzsRGBsIDATXõAh#e̤0CZ!]v-[ţxhuQx{=9 >o@ ;0,1.a  QU!qݻ=>`y;R$ajj q( ضM\.~?+++bp:b*-piff>4\b-=o/LOO:!z(Tjh6@դzBR)a 26 k@ \FIe}}7n4tz9Uns}$I¶%z= QsoYVGuW?d2TUmu]mrDjAgFUUm``4T*~jT2 zyyyUU^rLMM>8MwzzzI\Fe&&&H$ض p4/MNN"Ib۶xq&gϒH$Y ~#Jm&m]V_[YYAu 8aeYII)=׭ x<BfeQn޼51'577GTjP4<ѶoZpd(7??62& z>'N랞wa ]__Öm0{) (E877S$30@ZR.Q E˒$fLMMQ(dbbS_||nsǽ"a. aXs>xx9J\.*m>N.CQ àl^|`V=En8Z-<8d:wUf2xQ6!3==M2\/oJ$I 0MFeYxir9$ `4qMEt: rt^HhjWJ:FE沋;!0 ָ=}z}G̶t `Y(E]x,vE06|,ˢgŸ&X}]4rRȲZ3Hdh-.J4 ]}h mTUjxl6 ;v_1z[l2hfj5*m{;pU*QQzr]u}PmEQR͂e&8.KKKhFۥUT&s9*Ud?}MXZZZ_`'i<8`x5U>0 >ZYDU%I|Pir0< ^6ԁ0 m/r\EyqmDG}!?3CkpwU~v+8|Ucz Nj-'M3^ke:r{"J( ]8Y2cDղ߃Uÿ#HHm[2!dY},ˢh:+++,..rva0g\xn;Iv=4)ࣛE$IAx(]n,k7Z߰??_bt#IENDB`mapper-0.8.1.1/images/draw-point.png000066400000000000000000000005521325266516600172200ustar00rootroot00000000000000PNG  IHDR szzsRGB$IDATX1j0?C< 4!k{*G@&x!2!.iCM)$~D"c,@{>/1R=/JDfZkLkM4Ribaٔ뽆}]DUUq:.=(n#} 1}`s0@4h9`eιYL0}쬵M` `e!MS.R`5D mGEQu{R$Ix<C]%oџH$|&8WbIENDB`mapper-0.8.1.1/images/draw-rectangle.png000066400000000000000000000004341325266516600200320ustar00rootroot00000000000000PNG  IHDR szzsRGBbKGD pHYs  tIMEŪiTXtCommentCreated with GIMPd.esIDATXױ DO}X+2QRH@Jc+i(ܽd1FatޗZ>EZG@@5 ]lXYlՀJ5IENDB`mapper-0.8.1.1/images/draw-text.png000066400000000000000000000024041325266516600170510ustar00rootroot00000000000000PNG  IHDR szzsRGBIDATXWoUvڎ!!4I QE *qAEUQQ E\ *pBzBp?b #+Vgvv߾ˁhk͍Fy曙3s8~>77wCUU{qP~,kc\1R+V >|߇y}j],.v!o\tt^G?rՄ,,,www/scWv PZ\\\l`AyZl&3{S<1|SNJ@jZv%#BȻ'`yuuujgg'cy +O N\p`&c2~ƍ ]?}R˶m'Ww{F<,C @Qk/^4ݮv[q2zr|cHӷ+zxx8`0 1}x1i883;;t#J=pPB @dX .L6#!677.LQJ>TɤKtz\VfV3`b+(pdh$xBfW'8 HvWk)ص.KKIENDB`mapper-0.8.1.1/images/gps-distance-rings.png000066400000000000000000000017121325266516600206340ustar00rootroot00000000000000PNG  IHDR szzbKGD pHYs  tIME  8 6$qiTXtCommentCreated with GIMPd.e.IDATXýW=hA}wOğF LU@D<1XE +g.)klҹb E0Gng-r sٽ3ޛyA 16nIJ׮'i1hb `HBAˮjC`Fu H=c'Ej$4>h58xD) 0yKJBKYG$eH-iRRZ- pB5ۄ?ˋuy3Ϲ;eF*16"zɶLqy3{N)\QﺜH"CX3f1(n!|[$]Q2ܹtzj& ! ?ȌKG75e rD0C`YV/t.e {C#}3ۦQ~ 1X^8ǩt46G[,kJad}U:LKB>qDž xj&GH`@ifwv^%oY~wVV>?Z]ƒWٓR %t"̿V ,.0r}hZ1|RZ$pvvk7679 fj5cYl}RVoG)mɐȮIao6U†Da8ΰdPGKA@!A v\&ke[lE]?km5)HmW9-*Oh*AGUӳV/L>IENDB`mapper-0.8.1.1/images/gps-temporary-clear.png000066400000000000000000000015021325266516600210250ustar00rootroot00000000000000PNG  IHDR szzbKGD pHYs  tIME;siTXtCommentCreated with GIMPd.eIDATXMU罚3H 2$+AHQr5A?``6hT .ARZdYQAiP7BWz?96[z׻NUcEK/$X+q{Uury m@c:ƅ $dH4VcǪ׾x{jF:co} vb&ڎWN$LOfJ41=x [bI-L[c8{ºֳm`䓰\.Sq~н4/gfhu{w/H>p3 wo)kv \`;Q.R:H ^U4܌UyG9,]2-Yl9ބOã+O?Bvi5MM;] 2o//I*Ic$lxV.PV6ن=M3~o{pI; O`l6{@;08pk|Gl3^'eWڹ,FUu(llZ šL)dνsBd 9~bn$ϸqcnVUZvםqԹsmst>\99w@:C78q`TYCB%JTBC~jըT*}1wJiPi*]P՚MVVi gʲr0vGDJCZzulִuex<(8D"vhlYT ːR)z=$I1˙O5?%I8#s۶_f8~E$I$ ò.x2>e֚e^&za\,cQT4T׀EZ2p D|L#Bpp 6ͩޘ7ͳr;p {!1<Nk;iYcs0ޏD.D-b'Wz yٔRQ';hYe*hܶC.iL<;] $!?I2R r|xq"ٶoPp]/~9&u@5P){\!p ]j-2J# pXP4DƂZx(6 "STK X"wafB&vD&bJSZr*#@[`{@7p ]Qk@F7v~8*/6o_IENDB`mapper-0.8.1.1/images/gps-temporary-point.png000066400000000000000000000023521325266516600210740ustar00rootroot00000000000000PNG  IHDR szzbKGD pHYs  tIME+ /iTXtCommentCreated with GIMPd.eNIDATXke?;l7;ݵK̚Jd3M HC[A0B? &C) "$e,M-,nfC&e;&m40;<߁-5a*l`e (vm&pX恟+'}ku]GeBߺE8qZfs| $|o(;;>>ŋսj0NNNMjvVM8< oC[?PBxDbvdd$ۙ0(_aMe722M$B[!pYuKf>9L(B&E]ˡC$rLfFQ {&3iW*z>}Fo.eW(ܻ~HWSIR4i5zN.;ߋ@,OhTDeݮە˝u}}{.!drd8`J(v$B]~`HaKݪ*i4Nά~$IQ}ҳ5r$ ;@u]F*,7Qs2{;F#.@ïѓ@ 8z,[U?>艎ӊrW׋\k$`mST1-.I{Z;=9]ǐW:}JeʶmhoD`o6 rh q'd_F(r|A#nV:aROu&.6'oܸ}ݾ}ӌj:^_4]Аw8eT{nY4ݼ9<|3bVѿqv`st><YY8!i{_i0-.Yͬ "3Դ;g /(IH|-PCeYs||:lƒm,//zRLv- SV`_DiWoEuqntW*[oۏc{_0?IENDB`mapper-0.8.1.1/images/grid.png000066400000000000000000000003401325266516600160540ustar00rootroot00000000000000PNG  IHDR szzbKGD pHYs  tIME [iTXtCommentCreated with GIMPd.eDIDATXԡ V鿆Cfe^$khK1F:`:F-`Tcϙ~IENDB`mapper-0.8.1.1/images/group.png000066400000000000000000000033231325266516600162670ustar00rootroot00000000000000PNG  IHDR szzsBIT|dIDATXmlSϽ~ q IUea*պikjRM]D'[UAVS7it/jJ$0881$>Ͼ)J!ҾHG>6M59SH2Bu tv{QT, q0S*d҄ YJJtW]+{r?f^VScFd)"QN*wA|ZXXzUצEVs_w~a ogxRqȱ-[v!IP7i%LMO^K{AgJvg{ɗ|fP'TGk͵e {ڵ߫5ؘL(^&HRAQiۨѪF9pt#LZ\+4ւcqT18w[Z)iB )\zh^GzгOp*5p DJܾ /@fͯ?VYY#1([KxP(. BSNuˋ`0Cm0WJtÅm۵.b[H}]Ci2wbB܅zwkR!7`,BL+/ 6qÄw^"&u?JG f|ya{k6\v]B"8et؏u/BPT:vVV{ Ȍ ]c055w|^l!NxJ&֭⺌t:}7Т||o᰿U/Y7}Fq|!/Ʂu ,ͬBXStV7&|9BmXary.q :N$7{ bZru CBB:^:P߈+× p߄.HYn?kEHd f?HŤnBH#G_j,Cd2@?OED#ju9?)_zJܸ9Yw*MU|~9vIij0#(Ou3`eؼy5ph4f_SiTc/'"%gG`$āWT:F%} *+;4|+ }6;w=,'w=KR'ޣ,땬W랩ojx\<<~OިkgV3Je*ﮩE4<3s iu`0P $?vZq)=y20sKeJ RX\Z9?9~ (gTطo_USsO4r~<er+\ʒ\2 :s>dY b%Ca1˹&H29[k98CIENDB`mapper-0.8.1.1/images/help.png000066400000000000000000000042671325266516600160730ustar00rootroot00000000000000PNG  IHDR szzsBIT|dnIDATXŗ{pU{snnn޹ G^"D (Luv3:cgӇm+}MhV)vE ZÃ$JBBrܳw8$i359wo}kK^Jn]ّaFCq#6z_qxHCCmh[mO1Y+披Y>AXke<z h·!\(OՌ_"8ap$MǟY^:9|O>[O8ΆXm}UOIX~7i<ۍ4$!R )/Zk,(d-WS:'X_},>V|hSep'{K!Lba`)1 iHCr)dҨ,+Jpɖk['>@.7ܒn=ʞaibYnpmƤIe0${E\[15ˊ3}sCC-L V˛vT2di PQ GnV:}aZ?A(V V$lݛr .^eûGw47߼zÐyL,Ϫٜj'.IIa8]0rM x]meʻm<8Ri;@LO(  3? ,ebY&4ؽ#4pk 4Z6[^],9K'A<s4 [q|?5; s{Mi \|?$!m\ttssʢk-n^[C1N 7I0xy,,ٮA)1KI, ƕ>!ŎhU45d*,$4ncY N"`͒TZs?ek֠¥!*K Z/ gN_n=Wrf*2 NsRJc8 4?2@m<\tkO nhg0iN`%1lO$ Ȝ̴Td7H!RLc8Ջ(PJa6u^1;NoLR 3S-HM( @ χ8I).+I/+82DWedR/G X8/Թ@ "UfRӍJ)"):{yGHy"CݱWt.MhLB<c4⟲ce=)HH5<thMɲX;'5^>֏Ra$va|FC~Ebu47Ԇ /uMuBיS{4VXXұDMV([9SwI۶d>c|?XWN՝pS">1ĄM"amlN=ZSFia.}wl8KMMhN3\ܪ, M=(ZH"kJs&a;>h&a+D%l]]JV`W}%훪7҄j`sH)q[fZ 7.%h` /5շ63 8 fW[FUK .+ 8o(@v'<#a,([+*0>a_k:v=>>gYHx|ƧW~^<ʩ~Z O0# 'K/\8s0?hSw{p_t-D ~Aov;7,~k+W^QˣSxځ ZaSrZ\x)?yf4{<D* $z+ƠSUDWWfgg2vD֎}s{x]!oWUIDID)*:\L S4MH$~fٶ6mڄrrX{ևGۼr_S4Q "(" ajPV\clTף~V9 Ji f:n䤌sƹeQfpR "yAƠoFs,cŊ?dEg˞;py-J)ḛ,^()0 jQf=R0M˔R~\N !`b0vYm&aSƹiS;R \)W-4M\.X*\%\OOâ, W9Rj9GZr, ͸r¥¨@{hkHj^/sU \qe^L&۶ 9?z…}xKj|p8W4gp S̢L,wٱi&%0yk c1A A@.A..J*Y9#⹑r7xiIEQdh4j2~:00ɷ\$4|:]܌@! NPDh5>?I r1H(K.ϞI&:mJ<9L^]7pXC욭f抷O%3]wnni~;ۤ|>jZS, q}qoooP֯_-[16{hΕjIIol|.Ҡ\N8}4292 rء;::M 2DQmR@ui8q<jR~?88|pZ> +oj$R0\rdddb޽8Dٳǧichoo(/|EXpxֺn]^!M6k0 _0tpp:u_}݋lp$A$  7,6\. B.!g2:99ib1{mk;o#;~ >W WMm<uJ H$f4e&A(,Z@|Yu`d59 oQIENDB`mapper-0.8.1.1/images/map-parts.png000066400000000000000000000020441325266516600170360ustar00rootroot00000000000000PNG  IHDR szzbKGDIDATXýWkU=ޝfڤ!ݸ]6K >QDmY$D)?*}bೢ Bh)B%M5ڦD1;;3sM@vν{KH9CS!! کw""ND_˯(=HgG$kkZ:8r @|P"k%?3:Gg@] \p!P0,%A棍P QZKD"<&1H$p.m!a9o"빢F&QO?&OU A d eoقN" %s 5|CR2TAosTh*,lDD@ْfXp2BYbu\tGD'x8gv]?H21}zmv1bj: Ea/e4͉]b6ݧN>׿5.\V{T0yMRсg>dQ*x$[󋙙'G6^iʯ7j׋7 #}Mfz Zv IJ&]-cpg(CbdK.FP6Jh)?> k^߸/>ֱ?@fWg1_|I g{]˦Fy4vIENDB`mapper-0.8.1.1/images/mapper-help.png000066400000000000000000000056101325266516600173460ustar00rootroot00000000000000PNG  IHDR szzsRGBbKGD pHYs  tIMEt IDATXŖw_6LBB7BĀRB뮢]m{(=nO=6j= tw-#/+˵$\&[fwg{Nxޯy?W>9geۘ%5MV6'˖*RJ|)T!l["s9]Ӱy*5d6jRQ|Є4ᰑ;~֭[/}Rs\vGGG[kkfSڌ=Ȯ{ۦStI3@ءI)6o"p*C-[DkaLLhk纩~A^N288xnppPh94Miӻ5BOL+-M-^gE3?DknDR [c$ԗvkeO>QUFH{>)uue(MS۱`%q{ժo߾S۶={~%Oмs3=?LzȎ L۱hk"n?RQa =xoo y݅¶ylڴiGOOb @:4H݆u|s92oQ>wء:Ol!TʧTmYTMŠGkMy #'\| 3r"\U$3 jUSE{P[""c!d4I7D©⫫E&Sȱ4޺ZlL PmzD]8lk rl.f1c^39E;3A{jm5VCFbݿ߇(@}pc &'m2Mz~pT֮!_q|vs:p}} -k5ٯ{]Fp nxGe ?zO@B.:+ %h~9+gSo'Xt,qCC!KUx3}=AU1fq,jد@: O2wo{`E)-?!H(2=X\~ƶ034g_64`ժU( 4q]D/y=Ǫ*!G{hLbE,ՕqZ&b<8/0<6BM؍K\8 _{/ #U;g'h$/ DJx,Af~2=tb2us ('ބ!2NvW4ɅQQ;de~ua~Xt*c?OcI'Gѕ)s[y^+=(ޡ4-_.\0BQ,P>E?5)ZhrSX)?Z=>az7xp|>,bH4(%B* TkZyp)/LvzSo%u$\>J$) ;.lgl[*R {{n:;/^ (uRvzͷH߾ão>Iә(p\:I*!6w!8~8/:;;)++SR %<Ì%Gi\ZɆ~@O1bwjrQN|V]W6o|2t)yH+xv&**p8R FCL+I".N6gәz?AS9c.MsgL4d۶W: b -iq5 u5SO*#8٬ ˉ~G5qAv 4|yUg#TJ]ױm9 *Ey<+#WMjgbӘY;61q !atEXtSoftwareAdobe ImageReadyqe<fiTXtXML:com.adobe.xmp Õ\\IDATx\e?=SwfvnʦwB J"M JSH&@z&{ysݙͦw|d{ާ}b`˖-PU5yYo[?Rŋ)'F2m4_p]QZjvvC޸uV^zs=I=FW7]gqƍ/t f `ЮaW5H}]f_4n[R,"kKUZUmѼXX-bo1+MVgUkB5U)2#fXX,6S2Vb4fHD(_(}+6.;v;}TUb#k50.X4Zw|Ybqy^sh16Ϡ{Ӧ~o$;~{geMZ]g/H7¡0lbȀ偝›qzo A91_/qWN.KH?o|*[j'ɀHP@ G v]XqкLGLp8 mtH@#@}>a7/ z.$6 ؆x;BLv“[Ef/Bgg7 ^4vGR3(L0"Ghs{SZ0mF};5;k@\X+.FD7=G{g'2x= VƅLX[{Ohg'* D&: ›|6 BӃ"G6$¼{s=|ejX% S2mool=`+SP^w]w?Ghg=Iyfm퐛*x8 .ͭp{$بT1$%!Y=n Zhx-ƒWE,Gq5TGaT>]ANrab n`fꪲFگ3wi1y+$H-'D { dѾ=vp}($V n!1qIlĦ1)2\N2rCDVZ[osܸqSN;'L0nƍɼ6rx.ŷ&_1f ob}X3]bxóLG[&,I*{vtCnbmQ6ZAw{mio2KJBqd2%~<e#y VnD{x-6b^䓏0SPUKJ苘|4wX}g&{%ACX.XkO>9s'x$lC4sgu^ u>)K9OZn=N*̇}!(ĥvt :3[kPvq$$ ao|,K|1.!g.BGE:VP~QH4wo.Ԟ"DCYGjkvHՉN]w3@kCg/ q,vlۆn*4|>D:z{I\Ig>}hAx[QZ\b _B!ϛ#z"T\{.@_| }D,DVn{Ȝ5\ ; 󕗖z***ByYv` ȹby0rg$g$]8cz)=k~':c6l^<7@`SAC{UK)! L|f@Z6EH"<ăף8j7y#1IC]M7vm" a!3@݀gk@@4@B@Y,cذq3>_%Zڅ,U^x"e (ˡnNGȜ:BYc4I3Q͙/7m>gz #!A )HJpxvSD"ad/8ʼn8p$c-=Ρ3I:z{}<-&!H &REhDQ.4"N *z9kF xRd"v=C3sT!nF]OL%)#'Q,JڔATp*yA1W"0hkkހg+ePL\E%g՛QM" d荄`uZuQ5 DqQ v#7HDs.M迡awm=>T#,!,$(PX̡:Ę U X=DTˠtܟ>ʼn=Ei(C "Y-z |L51B FD0 \Ċ# f h8Xry }p?X0{mָ h0@8* Pv6%͛/ܬGc=z~;6,˛j]TbBJS+$D"Ih/ @ %Mh7ّ[.uH^rDč|&%22FmF|o+ڜLLtۮ o kVJ#%)tc'!{Pkt hmmCWgO۞ҫՐe&ĉ0}}  umG0&wHvk`r2A+.qRݟ7SZ$d㨗+7J"z؞w+pA Dp_zz2I5s F!#Ã͟ C(*1|1y=#;7mA 1#`3~?Gr9~\*:K*,n+"{hB{pfS(5[v4bۖbmh"z.Is'le[9bTk "@L)DRqٜzVs6<#7(~ۉ1RÕO-~6?R`Jc=?̱@/I{^P\T=U i$NlYu!k!V3Jz.7OD "ŨݱKmYz vE1jn9h` /DPW&Ğt'XW}1=W>̟;i`T_`?9g~%kZߞ.vY,*rl^m˹֎s@Ѵi;}FeX64K$wLjHV[!YRgt90=붣?a!"U4\LԄ1ׄQxo3s /=2pY(\D$qL1z8zY)FOVIMW$؄bw' |I+*h"i"Roқ^ 䢾z7F]v& gMggK-"FE&=Mi n"I1٨8fk5ۉZdkN;j޴H~Pq_ʥ.;*O^ŖzmzGOnjSaBkW'}6ћZZI)+ŤblNt}nF$ip/=(pD>m,N#'z=tkg<}:"D(557iWk z+4%6;D%C\8^ IpIf_/_sI;$a&FȟRQW~{HK%t@K3I#F$d,էb[}L{ﭏXCꙓ9.3ʮ<r|9{Hjq21A?]v$l{ Yw=({)/< r]Z!܉Ö?, KXhgAwM qAd2H[H^g?9 jK5v"~UkbT5jOC?FdqeE 4Y`E1&zGa" `'ݎ WBʋau9_$%ʹ$ ŘTN,0RU/Qxb:p;zP|mo| we)\%R9wmV!i&՟r= PpXZfHxh75;0}6w웏wd}d\GCˎ+R70@,@&qA%&_T'Ԟͤۓ5ڶ8s [3SPKPg75_Ou` /z>Ŕndz8?\h!6M8H=4~Uz'8Yéj?4N2fYHB1deau^m$m_`xi'cDRtEg\tuSjܪ0ܟA\CڄmV-߁گcwm 2 {Ç߆/fۊp'S }=!>SsD"-]8G"`3?űcA=!iL ο;Ww&Ln\z 0@#En15ii#dͦJR&p%Ȩ,Ѐps>=\bA0e%~jP1 wΪ8//]5T>3wUҿXG8j7:B 5 e_S{w5ܙ+)~+> عN}=ON1R؈q}h1tnI s)};v%8%74U8A6#Ra+ AksLu fϋJ5UI26GrTTo_uSuqAͿ$U49A,Lϥ9W/E%"..zs7wȜ2iG9D`WTur| u"C8BEF$ 1e>*}(l+z~}hO50Z*zNy3\29} Yio܂dOQTC+K8L Kr'8bt iPi 6PX~LuwjDX~QWT#o PԨje:1weDL`tdwP#j BoAxGbjqC%;vȘ9g > (uaQM~n#J(Iԓ 7~U:sŘKeS#6!Ym(Զ-;ѥȘ3 æ#<TA6Ad="F?AfW4{wg> N ZmQMhQ8-spBJ-1t U^Fx)֪5mDBRT1k}=S9a. Ry(AuRrqЗ"@.PN<32;%oa݅Yg}W|dYg?EɘxiȢͷ-U"'~w,`*1 d)!AK~‘VNgA-zF@UV*vD0>7!ԁ5 ںA?DSu"IP,f14'y<&Ik Fp"M +'@Ǒ%އ G~p0J́Xnd!R_nu#Z9hQlcB{=f'gm\9@[Գf57:F7d{&:`V9*OOF> 2]IׁOfY1 -D Fhdj,V-n'vX70nb2Hu1c-65}=JL ~79q 0M+ZLÖU 7 x~32q C- pE<jܾudHl#!Y1E^d2Z$!|Ys(3p0uc7c'W&b$2s#(\>d;ְa.'rr3X`4ffOQ\/#K aDEӴpiK3;{\C(k@& ^?jTfwlV§UCXcc fH= qo%>$7u@rGO1?|U{%,d rLAި*mmǰ<{_oU+K)!k6Yz:[“_ wA`8i5!s ;с =SrOk}E6XzO '%ΜjQ0ڬ!MR_X/w3A٬8ꆇ-D$Ǚldεb M4p`pf !Ȏ37.Tqr0#ۉbV ! b C}e3$@-OĨRbԮP1~ۡ7~q>8U%`Չ/J0iD kݲ ukޕ-{&/WT!~iIUon!@jf3 R *}=7ga b&Gf%q' ĐA?G<3ɻIs:U<{;yrٻ۱ͧQryȣ5 )6^'  Dȭ,EWOb, B)7oñw<)W^l Y.r##v%iVGS'?&w1:ڻ'o}Y*KlSw4E*9c罙)l^45lG;?zBjiHH֦Elϵ(V]x fMЫ!Kuif~oԐ"X~"r+>G w,>dvmDwQyo!(ty{P^,*OK-d+b7Ƃv^bϞ\uw%%$(L)jp;֋gMҟMo7cϕ?~3Ό$C| y,}O ˝[rPo':kp'F(4k{6o30Й,j42 Q/=xuFj~ǯڜnQ,"L>JL?Rx6phPVL5| w ٽ(0 Jg<82i\1>z&TP?P2u>Q-KJ# lg"bO`'('#H:^ CJ~u-s ,SJ8wn_@+aVLxܺ/S=q|BA'; L K>p7t;+Dn$<R/b迱ŧa3Vy<ako }ҋ 6X"%cIԐhB.U_^\'g_!}6  ~hWip-<򬄒]q$!E|AջnZ.Q"6⪜p!~G!#eZe>T/W4w$M1ID|˸%?w̘x?JI, t}ݓhߵ^BLt4vŲs~Q D\%=FW$+zk6vFZ{kQv{5L2Of!g{l', qWp/g\ID1Byr7aʧR`T>%+w$P98`#JYd ոj:B顶IM@$ԸaD ;кyVCR9cwH` =ٹN7fJzyǠtт!bcMmWvq:_}}Յ3g/Ӯ`HK 3-Cd^ ?Gb/lkWJ}ޤ/BVߩ 1|'{ &Y-;znz_~;<}}|Wc>lLmEF$SC>CГ+`v< i~LˮoEv;sQ^Vi3gݺʲ4GWnX8ě];%s|/>[=38\N-;=E30zRBf>CSl~:oM*M#F3Y4P2+@GPju|܏/-E/!_LY|-alny4q9WCE]IQ gZ[GW_˗cϞ=I{رc*//߫WwXŊ5E~.))0w$M1~xX<ﰹGa„SBXֆ7!UQz1=EOGePLX!C 5ylbw 2DWg`89ȉI,I9hll h+?[ i/^rwpQG"?/.DNxih (hEfy;\ 0Ib:}IbdC`+z)Sߘ<X\E*0u)lCMpe/`@>\B 4G4[Dbx=7Eڦ.D*7U%P\/G&s4nri|v7}Di\B1$qZm)1utwX]$y»=u<$-aJU>Hvd8#뛺-gL8#4ېY\1|%ӿ4gD'Hېp338> &U5<'>$?⿰ߊsdܪI{h2{)C|#&%^D/3q_o"" 10#I&N}{! i- .UKϐJ[E#@'įrɓ'6ou T4 4⻰/w˿ݧU-WNQ7?,cek9NT<̑3׷G+ﶸ0f<@(< 6v-]ˊk.7GZ|*"̬-|pYMWVw vs >b Z/3QU6/!>c$}}] 9Kɩ$5a1K|bObBl:tB I/: W_} w>`,Y^=D.+G`H_b?s%y_CF_qIy{p}} )L?LRqIBK؞96T)qyZ149KAKr`@WFM؆+į68b~qI'/hQJ3J,E Nob3Sq RJ MF2Ů_bn:Lve4GjP {(Ac"x tݤ#yz)2I,b} Y$i(?_J$_X5c V#o?f= S<|䌚G*<(BrO:rv}tmPEz/2Yeܲ31ċ%SG^ La Z[6#8(1ã %r ]O }}}@P/mħS0 6p}&p=}$_M.q}}6o%*_,Y=YjԬ|V'%cI7+;fJ'#95R7]jR8bi>)x)cq (?~ؔŸ^W|}}\:83KGaoygR"m Q‚bw㗝%ek湼!?Rֹ"׭ŏ/OlRjVhTp*83} Zkץ/FR@xbp(yO~}{#yG)Re!Kh\jW#3 B"BW:!nȉa!9R$ ?M?E!Cwvwu{¯7zy?Hl0b>ɎΚmޫѺuVICVɘBTw}q;GG.4UM?D×e2/H"z| 1F4~ѳʢ2-6 tIژdd:d aXB\7n18rqJZV=ȽfFѺ]M|e]8zQq18-ܧRk/cxN|@?=Dv)[hڰ~풦:EEI5hL?LhpD3e!W դ]IGΫ7?%<ρv5rJѝzA,]_}~mzeĢ¢z'޴T~*Ccn ={ GUN4BKRv)>eȁB2e2;Af*7xzdբ Y#6U0&N8 ASN9wu'fϞ#t#*$h'mz|Ǩ[Tj:1̿?q a-};@ԟ@|d;abV;${M Eę(Ȍ" n;{MYA2LweP ܃VVVn{9eCb<˿>M<JSPEWލqOO58:?PImIӦ}pTNTL[))0 RL儉SR+tȡQE!oQL50!鈟.Ĥɓť;^jHٵq.u;d(ӎwjAVS;X~ϐ[9a|{#iyMC8Z 멘tO/TvxRJ$p(arJZDڷC"xIÜ> 01}ŶkzDXzi㧨-lZ)MҺ}J01{lN̙idPGhܺjNL=z Ԟi*rJ^&\Pjc Ö˾i0uehsT@]/~LK[ʙ9$0aO Q<&w2o:eJO%7ʼ!~h/稞33Of}{$%`;1{Wd,$mM5]v} r_}}<ӖU@X\f%(3Q9khIe2W)%d'%3ٴn>{[>'m%֞zO mn[/?\6K=YæM8>#ଢJ^x<&{ ϒ T F4vpE%1 3Rb71A-س-l9H(( ڒryy$Duj}}\n JXNFӽ :#$pNYZBبs:tބ@{a- 8*qdBYͳڪGĦ $'JE^DBg gnp31p1!?ޖ:!6Nn'TIbZÃc/8ťD)6}} k6' 9 켨ɺb'Hcp9~];hC,C@F26k;Puĩ5g)\2㫞ӻ2e?{>}MeRT 8VrT e D 6IOٲ7 %5ʱx^yH;g޸7[>E1{F>6l_GDS΁*C8ԧcz%1kBr9;!-!z|5u6|ДYǙPQ=bԬE8dNm@,R؇!7 YJq3Hݸ*QqvTHF#Q{72668pYKQyȝ0 rZpo!m_xY&3xIy@k Wz_aZ R1dOQPk144𙙇QQ2LʹHm~:m #eU_~\Oj6w-8cS%Cvrg)șMO.Y7Fb&6'bcB>yBdGODf~4Z89=b>(YA `nԇHXi7cƙW qf$#äc(^K rτ^_kijrM;)MWiLpI'E͕Q9!{pu [pm0/b)#6Un73_{q]:M4! Fb+i6QyW2ҪO6%ŧbɗ"gT"b!_7NmG\b;Io9  DA`r+zjvHڳIFB!l,rǑ\wvNqv9)' m#[?N?8GQQRIA:7苧)Uvwa&~3 U8P&>ȏ>3d >N߼3cr OP$l;/yƎ8|vnFOgojhZwк2r-CVq"R!dA?6TvPV8N!`!g:pâ8l]%B0r#Wõ\Z:kPn7ä%h?LfIԋI *"2y#O^_@ωkW"6\GU F;yErLh2h91Sb"rأ;89't;뀹\2L.3 xG4XqrQ@jf}Ey|[Hд8`loq~? !5hۗ@Ly^ojcc(eƋGkGdh5~i+yQrVGp4 7W.C*&K{%֤riDm_b[ ޟjvƗܡMdrލbB]L0IlGE4/]__S\M :!F슱$]DywGrIN_&Gג/Uj+R䏟!r1izz1Yr5Bx&M#4үBnm`K2 o&TAv\ŪцJ=E9 &qEWxv8I΋]J箑jި>qKw8 ??;U>E3+Q0TV+PoO2d3'EʷT$xzկ4HbFl)!['l1K},>enN֙ŕrhCݿi#QY9oMviG0*#4!>vdk1(! ק$tLB0Fo}~2!LF%=PU =pIk9$`$]oK4l!أ/ [KN@9iz@v^9c{o'F3c:CTصF_^k0> A}ݝ2Gp\D~vqHA^ 2 Cq  ˱5k)b`ɛ82ni#<|+v!  Ƭ >?`Oc8YF7OL!Y}},a2fO-KNn2h&E Yӟ7f"rCݪۮ^c &S`\t-3g.'sX؏į >qXmfJ1FERpqg"\r$`JEXrRʥrk-po;Kjx!{ڷ {ǡg\A&(W]^ #G(3<zޔ}};>&le: r9H|Ԑ~Ǹ!5}I_SJ `ӉnhwvUZ hG: VS㺏{>n>p\4 x'ߖss~%&ϓ n.D8̺7hNCš?k$ j>UmIiZ3^(RG1mLl:B|$Ll&dYuo3"<?Ah%B]8| /Z~Z|29u!\Yi})17w$ ˆ)P>}`I/@WCD{H=z blGy#-zbƐt.L B|]u (^ÞzoxnP| 5AU &i0WSw^1t}~\+IqNT?NN˯!%YKF~K_cZuMfarTz:ANo@u- D܉zK=$=#@мBH&0eԋk^##ifd>K;랺mRg۰KhG&Nڇ\}%31 /nEjsli&p?6ys0v)\pEybtA6q7\)LKt ⑈ 7g5EWvEobsc` xɥSbgh~-eVzwm]kzVL|>RLJ>t!XzD]mۿWn[zS;[CٴP|HԞ6y]xτ7;3HDH|I#!yvg>gܱ׾@]O0icZ?nͻBPq@Z֘?fLZz:0o[)W>dpuM-OǮU˕e[ޥsLlx'73 UoyHo>%g a,?f } eLxLō@+ lM.݇ K!+q OUzB$?لұIW-ϼR61L!ɔ>I%K-zE fߟlK4Ick{}?@+E sDh{#GS 챤ݯg0%gv9ո] L?Bqhqu70;@r/*5yBh`Hюr&wڂW#J2M*6_~LO2E2/#E![%D|OD:{|h¦6u(Xu*[ M"L9pȠ(Cjdޟ>Bafyֈϙ "upn7'8fNT޸nt anomn-I$vx%rM5܀3`HD`"2;ۣV=5@`3;w[ s-4]%pr;|]u3 4͊0]_rd4=pON|D%;ǗP BCpe #RA M(M "%CIDATxKlTu;o.}BGh҇}M Rj $H4х&D&$,tQ7tX@[!R)S:v3sssswDXQ29!ք1i9֒ ۿܝ2(g#gHwO heE s7EG_mno$9_'C'_]@5s ;|M`)8 UZ@b.J"߅XA|-:{{Y>4vi$"(C>k89hp3l-2%)Wo*/6IdU69tji%" I4);uJӬvo_Ml&&4z6T8W.B&,@?0j\,83]v4t [ZI@&,㶖)ţ~jktXzJÎ23(5u\}GK+i+ʈQB=jE`eRfwk\b H3/w@l*J0 [ݤi )LLc}B!3Rw$K['W겱 ex4s墤rcqc׬J˄Gn Rr`T'B7r˘ᴙ04ŘUlW9Xoц%YtLy&[^ΰ]IENDB`mapper-0.8.1.1/images/mapper-icon/Mapper-24.png000066400000000000000000000055111325266516600210150ustar00rootroot00000000000000PNG  IHDRw=tEXtSoftwareAdobe ImageReadyqe<fiTXtXML:com.adobe.xmp myIDATxd}p?{rI%@E2,/Z-/*j[t:v:CQp:V- [)#XPȫIPJ7..v1O}eX,fTUUTf2IbH*$IK4T,i\S̲C"2sF$"576t:Y>22ǝ;=K]"ӁcJ&KtE$LܔR(IEXr<(ĮHj2|2X_?[]7l1sX+ۻ+{&.+qQDD~5RQsV. 7DNגnUye?ç(<𑡞*I[ %z_y u_$0aZBkiTQbUdu8ق#ꮿwlRߕz*Nü`:ޅ3w?<#ir>ƘǸfJۃ34z{ KnV6H)͕A(Lj=F1QQMWg'"/b3l0sP)HĤ|<+M_C`K.Ǐ9rBeiYeԧйs?u2}xâ/`aSy ٟ5G#,\0r^3Qpԁx"$zե/?^RT_&?wczo=Y0W9ٌRTH \wu/+Ou,=(ͧ!VϜ?&[D g>$QL?fL߾3p㸣"%nR1K`ђcC*'dFIƆi:2~09O.s2!]/ pv>̞cBd!lC8|T|y\DElzR)e.LQ;2a&&`Y9S!kEA3")eL;l2/EP- RWN%d󲣓9Ee$_S/ĝeDSX]dfg cX؜N1c!OlzH."~ɼ9Y<8fA 2͢4֬< ^il"KAW;Xj@_C?7 hTlnW&gV=ܲ9Z&-%ֹtE3 =˜b9]4-k D|yld~%ӓUq!6넺 }La.:.SlFI}Av eͪhWHD4L{I'tWm\CɃނK" mn:./Q`|A!E~?yE)1")-4~cq)J 5:%QZ/HD&#D&q)8 IDATx\u?wllMdS7EZ 6(ćϧ4'bÇ 6|"JjBғM۽s{ܙ2pݙ{=s:>|I躞pN}7Hk y_sNAPo7?K0~N BQ5\35k GzD3_Sܧsw7KI'mƣך>DSRcb3~S_C#WݶC.`+YlU"v.3ȿg2CM ן&JTztqM궋N_`*}-{|4;׹+c;ɋYm f.C[?Z J֡P6y͵(MFR43M^,?;a'dPs3C5U?W,&2nkyޠ.Y\ziu;\I3|kWsHM-mbɓzy]{gVfv(b>Eeo)Qq^Z5r^6i,Ykĝp8TUZ2%v%'tDi⚙.6P&t}r1> 2ݬAϺ.mbˁeT ^J0.'')5KY[m I]D:,3eɨ~4@LOM™ɤJ MωdDR b|+I\'NR г>_[>MF3CBG1jG.:XA&Iv tQ@8RpBr9mIӋv:2P3VIN>/A_8y uRrmn_fRͰ|9k1f OaG*|n|n~-[{@ҋsv|':w;%@qvmcn .cA@' }<Eq=x eEH #! &.It`v~MSRv(f1d#^ˠQZ0sh "L EɌtb4xQRR//!zu0+0C2]1M(x]56hPFc4d-j0;}>k~#)x k{NEplڴl4t#z T^&58gܝ9;\D[?0JE2-ry4f5AɄLT&3rmu5jDA/1nr4m =N#.Bi281%@wQ,bKKtf's|d7΀>mnM$>x<*65 (ᒡaL#A!h}DxRZ\j*(>LϢш,.,DBim\t>; 7AM dZ~]sY.+tѹ@0{0~O'RF")quYWV@=8+k>Y_3*_gizy}cI bΣ!pՀz Cd",P3>0Ed! ooEh.D "1r QB *,F%Z>uKi:Aw{FEE3ە2S.A!|J?a@1Q:HFeY $t#ܶW!;'ɴ ExBr?)B8K]<kZPb΃2a-Nvwc4 "xd%bLd')ru$OH0s6Om=g(Y /`0wI\ھ[LVhy@6ۭ;/aB3jjQCGd^h#!>a+61BjR#aGG%q򻥤Ky-KB&So@>@fr̜ۄJ!OnB׃O!Nt@ ǭt3OSuznR4T߈Lg?k ZQ}XlY?  fg}>LE@  ,TlCKHYӃ!>[{Vf͙Y@"1 ?!5΂}VH DHiK٭!R dFQ}h:i5iBD`dDRTM+MItq @ZȹI!ۉg}p5X-h݄{fՃpƽ2fOϦڲ;PTe!I`= +psY.dlay<^(#T?.@>|fn]ŏ `I +ő<@4A]-ME@x{cɁ)W0Ft|v /{+_~=]BnҾ">! M?%[~̜=M '^$%>s\?jŕ݇v}w= y3PQs }x=^!,4)A3ۭ*_7+j ]G1 綡Q\pFR_#h"!ctwI@%%V %±{&VQZIJ-( +қ3 +]W)8CMq*eBY֠FP!NEhG`k~j~bjxNc!qsC1 4܀SVO̦8g k- '|3p8Bв[Z#;|v*Arvs GDw?s0w֡ 1?^p:%2Zvƺv`JUN milLtJ >}ux<>̙=|'6$c^L#KL+VjYB~b ?GsYdVYU uwk!ղ_}ֆ? UT\&4}r,[]=8DLDd/1m 4'BLe~)l `qR464 pFy^:PG6| T:ν`U UZwd V<26.$Vs2o0>zoOtLr͈ PGpi RAxԕ_lM8mLy)v,%أ'x̂M gK6v~cim7E@b,Z=IlŬ\+ޅ2ڶ]XR6 \ȑSΗȈPC&l„>XR]?߻Q8_^St$;q` hΧb*'H[ۘ =k(6c+>S";,d9~G +L:q< )[XXWN֢jڿ Dw$$VR`ihD Y YP`VZ}JCT*4f3g ʖǰ`Wfɨ9?V Dq[KuPQQ@ (ԴQ—lc0DU {}ٗps?ܨ1\?=erY0bzzte\.Iz=ufK?gj6sܰ|ثsg ߟ4. F[?"NKro!疩d2c@v/CBZZDY6Q QJY/U/:} 8p Ev+{.fX vv7-WeZ~) X?oDdk, XjZs<ԁ9\&ҧvl+(^ Ų̜ѻ`{)ϑYfzw:҅M^Aڔ Ku9=C?10B?t[!Vߦ+;Bȥ5dEs!m-K544 y~ vsn?(ݡd]8+0]oR, נ)M߽z E$QTȚ,Ig8 yb =xHRfH#=eDeNYA@5O(͕Q&*b" ˼~Է{ǃ2cO@Ss@2~?e6J,1#bof+5T3b0()G6=}ɮ!(~Qi7ݷ?{3*H ,[B/e]Gr@.N^`&6>ͨAm]Ky~IQgx6?[Fj/A۷n(S:_mAwC2>9*+PPI T5C6dJ ;$í Q^V/wU@˟-=p=9=:­dra 8`g2Q6hͪ7S}|~?Z.C`n7@o&R?}nH9mn Vp,%`!#NC/2?u۟=Ў--". G`.to)%i'G /EP^ Vf _эx|q':"^߀2~A~̖%7(qtnFS Ʀ̞wi}Ȓ6s~_ab |3_}>_ʈ(ySUey;>?<1э8]4Jj*Ej4,ÈDj((U*֦`ǝwoKvwvXs9~t1c-~F`dǤ{v<>pUC\GpPs~ps_P31wbQNJϤNQ$I꓉^M?ߢѲl^~9a5-|L?zgw`SNM3[z!zvE!=snii9*;Ljtνn?3y7oK=MWP,RNN\ ]}R_I6MgYڷo>F `੍{IYG?Me15.9e6%Wy~4Uyjȳ*Q}{z^Wcm_O "}>rAGD_!cm[DV=̜5&{=O0=A xDF~\`NlMtp@ ߶ܴGX%;oy3\~Կo:1 FGD!Mv30ccm@Mw#@]Qdtdk:u-fδ\U3'A0.{P"#bUyׇm[!n WyɊUΘӎŢ0>d;J{+x0ut0و1Ó`{hspuw`xGc^抿8??EdO g J(7l2RZuuVeٳ4)Y*9xI'?r #+poZnrzr:|$tN""z⯩^{(5[o |]v(]am8ڊёa$1˷dpndhIМݒ`%oAJ0ܫFOq3 ";Pq*T\#ưMZ4bGP7FlQ|9  7BG\ bs 󞇦e2:sHcwa{χ-͏N9wee뱥2ԓRy([VSnL%F@=wHlćQ'|{֮]===)\2-{M]=W[ihr ]OcWT*:Jq{:p X[Nu~l)tAxT'pd6/!REA4fd{J%tE~I|KKK(1B`7bFnH)?7!օ98/k­aQȋ5- i8WT%Bar¾=w^4‡$\+,3=JxnOYQnt4!$ߍ%?,`· xp{uV^}@eEGřǑ/-r<{dDCQON ?YtASUE [O_&7>Ɣcs<<5VR %Ɂ/Oc 9f cd. w H3c1EAAXXpݘ<;7f1]#tCj1VI*0f sYXBVo/]~3"tͥ4`.]0_y#6k6DȅY+E Jt"r"}klƑ6Rle]|B)z4m\&&>4!YёQ1SP3 ErԨO 4ϗw6aCmocL}"\ۏ~/#Z]7lmAT2,{R6 9|>%`ɳ(.sez8὇:d"UB >)Fq1,GMƖ{n-7,锖f»!)"4 uU6_Z65g~oV]I0V*ݝ]L˩VHJB4rb+õ1@ >[X`5]{@'ʫ+sZz|IE"?2s^ف?˟ixW#Ǻx gF/Y~-,!SN5GHnd49:^Ԅ ܌*{_N/ӎ'3(j tFDPD>nD g(bkr4 }.ؗduZ.[ /:qMHօƤp['1Wo"Ԭvidܱn?E7|4i9^ײmd<8ĨC^GǾ}}┮0v݇AAkqOپ}=#rVa,xuf9sDvxO[qQ:ɲc 862mxP)Bog'\55$(p)KB1e *3xFm9.+ &u4`dpPbk'#<&d-vxG_0')hԘQrb?4R¯^ WEMMY럳f ;5siUuȰ @7yR,ABCCCt =~pyf |w˖-#1Bp/< Ŏ 4fXg1{:X"¹+U"B7,q=iCMn^JT j^-^iEa*YAl9\KQzkW@3j7Ӥ3?T,[QSbQd-o۹h.AhLu׋wCd$xfe"ϰgBpUܽ6K'bl3D={\9P@CƜ8V$HFЊXzbqK6SE` g*Hvk􄠉M -`zJm1B>Wgަ" }+aUZיʘڄVX4IUhV7k9YzngqOBpad[8_`ϐz2F#e_U2FonK!,XϨ4@JC!,wN=<2"Mٲϙۄh@ R0IFo_?JJJEgHB4s?9C'|8Y!XaQ"ZLD5{SpIvs$%L1RP`!f %bΫ,)HDۤ2mmm"@ۦ}ʋE(n4bg> }#k3h,>|Xtdb= s *BXArQ{!>(UH>ilh䝤-`=qp`@Ђ 7m-g57GKs448Ydp9rc2V5=p^HD a(~XEzZ7Pt畈jE.tL lLid\\JD8Ȩ8 :[gU!DAQ E;Ǝ;F7˙#O0'|d=OZ֜9H_ũWfCH0aeXV~nr}^YyK<~D(HYHqKšk(I S+t3Uz<6|}{AnK!+& |PRBKKE|hDݲqp59PǾBΛSA2+JE^)K5-W])#0ոJڻjuBEH(^*;$#Y1 ok )ޖ6JgGÏQ̴(]:E|ϣvR,K Eoc㗢u+Z[[E>WL*2eHM =ɮPrzX^_)(p.Y7@?.CG zuHDҾ)eE#P@"I'#f[wAIht/@i)tSDl"|5pϨH mX ޲\j(VM&EvcSM:#?]]:򙍈ry -H$}02y׮k'\{9V/Qbx 7\()ލ/(ra鏉a_E?8r8㺷Y^.$Ifdކ5OΆ!VB(("?8$dC PW@ |}ʽdx 2U- 8{a`^l#"RlJ9Z--f[\Xa&uTTU ؆s pSblyoF3/8E #OŰRLGnsm(·R&MmQ?s&^ف`gaFmAYF"?B`ܑI-'llDY)'腤ŗ'W_,pjNS3aJGv,[ x*9k Y{҇"@%Hj.TG)ɯaI-{FDl~$ְr`o_wȹ0%aRn:ut#4.],!5{ Eq2/c\=s r[|-iMC;Ο1@.ܙ֭Y)x8'9kt%|5زi8o]4dn'8Ov:StAcA/g}x!v=$RxյڏuO'7ybF2m:pF_ֶ_Ig*$(. ޒDwaO+f765(W6pA2/|Bgͼ#8SpЉtL1|OF;R"fx\y?]Xr@6ҘPR=&$]e ݤo|F̨~H{/wJG { sfeVёz6E-[?"ڳ'@ /< />0 chRmg|آ#cMXCIiId&m.tc=)祧9՚pn$2K6u0I93O7\5[S?h> +4M SJ&G-y 4o{U w|H0㘥pW@9Po?f]ҹ3 ,'gy _@,_WĸƸܢAL$5=`Vh~K5yM. s0K%c`P)E&C*r448N,ԝdסBTḯa?TîNY4a*u{SbW)>)J K蟢灗 @"wn`@.Bu2C+_ /m<s~ȹzᾜ̎߆uu"h $A^V/"Z8n,x?*;b;sԜ9A(}R ;&s߶c\ {{੪@ +&q|y{.9Mx'5ic.B@_{4dWB]{b{~:v=|gnZիK޲t bi .1"#v]t7+7ug*\zQo+pV" HXh<}\6ʉxvvsw*v%Mp,z:1ֵq%D=Մ 5GG^5-bko1ZEjpVPANZ8"9,^|o=+^r*\(Z֟~:ҳY8=:G2H1Vnt=+I5aZ ,h`҆<'l&ӒI2w (/Ë|EYcB;z:AV'Ό%JA(Cc )tލڵ+c ^z$Wb'.EgwzIs~Ŷ8]Jhq UM%Xw[2J>ȗqm}:R&qsn:ثwB7,@v9dCZ.9]^s)6)-AǯPMČ5Yy "#,BH0tPXG[tg7}PNsc]]v۰Rc@ᇲ%|ebyj1aEu| ]O+Vb¿1nG8AAO+~i_w?  ;:0s0 tlLOPjcT[%)\&<<2~+xO㥿[O@Ѻgw^v/TÍ1%*37xzG^%1&ΎЩt:Ɣ!G  > q&(1ܷYFmQVj$uص 87_ӍeE"X8k,7cy?^xg?eXwC0eRm:1ӧONλ +qSxaǜspʙgb"08dІ222,}Ġ \[9UkओN%עf˯gy{Qdlt>lì2IY5"zRQ).9^BnrV~J4Uǘ}B4w$K jwc媞Y$!8ϘEɪCa EȽy<=)Rrz"%}{؍M=3ժIufZ,Jy:3wWR = Ow%{1aNAZb GRRZZ̨WcMDϊr_ Uh +KX8C\b%f._CߏxPQK0S1Rۯ_|Xq*,!D0߇^YJ*Ǘ _1`߭AW4(SQ%^UB93 L^zVhTc;%TMjvR2 e&02sw/F#?-*`@E1дy͹j\nBϓ_4]WYlOU9Z d"Nq Ӥ?̱mc`®/ 4;>ٌKl]W%gc>~litk ],NPd/B%B9?OjaTʸz"Bqӣ9]G:8k6V|0fxXBw/;7,\<ϜBKU5͈,NQ2ñ5'`7r ^Gəq rlKd9~-b얬#I,Kt}{>w}5M">᪫4H"?" :+JJ'6Wyk  ¼дf՜`E8ڪ̌9/B V,iY]w{[wIh[dSLِOu"[ qENn9$ a:ooa[ˡTtO;'j+5SCO1a߲?N G衒`]l{2zw(N냵? b|~1 FfDN7g<5a>2SiF9̔7i%F5r`.ϝmn235jdnQC*`~6FI!=&$4VxȐ,!.qыYV-Vr)>]%!=/}x+h73u_(cw{B$ J@oC =Gd:j@eT^yqqb߹Y({pN* uy _OXOJٲ?EUAhg_"i2o Sk+)+Go=?x`,5^uzοb3 ,$(RNǥVɧ&W E-wu̻ݨXm5yߘ1ʶ sD8oo;hIʊ:Il }=GRDtgᏓ,}zMфi*n"2Q &Ė "ZwAX+aȟkY#hRԽ$$KE4-Hqe҄G3Eг7m?p*ִüJ$ӢUVk~GE|kTU0=8X1KD[}yJu &E""ƚk"4A"'<5=ҤPH.<]eGL7B 2ӁF={1waG/O*(GF¨{Xُbhxfdm:9Rh݌]9v[?m/wQB ,'00:;IK-tlAN8Mmi d3fQfhsu՚ep#U">Um3\5_}z8kj2g6? ;BQh$+/6ҒQ: ~ߏ:i?E^'e!10YB mVRkC__#9B˱oXm"zus徒<ec^cݓ.f)i1ro-`W$sy7}t_B)ŢӮ!1g<*얳Ϭ =d^H`? 8:4˿Vӂ1s kf3+B_+2eDL d4 _u=\,9U BAђpL*),E D'[ 4SK >#8s_PQu)bW6Y.D/!Nۨ,Se.w;E0 P#zl?!Hݻ(cۏxKX'4>@ѐ=IֲهèoM P]5SS L) =my[M_OԾ-0ೢW_9s}~ϙh%Z`7mjP䏏๟~m/M-F)\f[ L2aVEWgmCaɫGwv@0'cq-M"Կ褜Kxn}Y1XyFB%)eU>ɨL@30?Ü֣ ?{O0nIE5ޅzi?90 CQ#Gϰpk0縏XwFN&0= `"%`*[i̓ivX2o1?<}}w?GCPs{YtK7RpLe:{j/{+ bDHX#hE-) :[AM2ZYY}oe _*RIp0#ŸJ2*A+x /3o/u |ʈ@K?'әĀS^g롖~Ǯbk!,ԉ>z3CKxoʽYgnORD^clJ?1{T6hqaObub.T=Qhs4d,*Wȧ*|؄x` b٬f̹u0+~2"hƼ'Y-V2nqt÷b*z O_ G qۻ), WHhg+/8f$vOb׎Y*7JzbI!MUhFǠS[Tt]:C +F㲬Nޮ"^xʁcf`n?%ʭǓiLv}NӲncpg?KiUV"z*Fψ3:!|/j lV/gcPBfTb8~Q>t;[ hF]M3{ÌR}d8TٳQ|ǙІGyټCHBHD Cyi>\P-+ꈙdbx7_ ?ЏW"J b9 ?Lc1bUsSxkꓑ,v˹ď:DЖ]mC.A$G>AQ~c2c \*'-\D qb}@DBd_iDo (cEG Pr!gb|z>au;>wc1wCri_JOv$䔟h/)?GMOa%K8{pyPR*T"iOȥErRVC,)n"+ʚd8@>w7z݇䣍\Ҍ} 6H ڈ-Hwbo~S/X+ʷ Mo?ŃשE!8K- gjLcVC$FFDVd+*^~ekzg#l^FFQf+Iu/jBSE^А1+k ILVF}NdkVB%5ψ/[ͽ~\7Qۙ><ܶa;Pt"]]BdZGH=Ӣof#W8p%]'&qyזjmo,KĤWW]{b@k-.0fŧ<߻,ke{tcpҳ"l%eEg^d,"uJR&?v0"~u+_ ǿ#/^XRrB45e AǦwQ1c:+,IYieAwZoGoven.pCsgݴfsЮfx}廿%t ONS%ca܊9Ǟ8[) >HIZN7b$^xيxO+b uQPs:8B9ϑᖩFל0?[4@cW|N'V [S9wEKjr(y6_+χQIa̬ڈR7^?%௽OCǀDͧ~ފ91kCT9b|[ \(@*7՚E.ۂOYZie)s"c&Բ"Agon–?SfG -OV U&M AgxV8uEcRpWa2ќv9FQ4k 6&"jf(C{`/gӉ3A]ųblEn #Z+`vY7N/jR''a"0.saXd'\(|fQM4Ssjﲿ6jzpUQmd0X'7;BͺN5,=g~6T\ "ӥ$N+8m5,[3(5sA;Ut;GھV!~VX5O=}{;Ycj)|9@Jq̸2uե{t$qoA=GbW  B\8Y}];ky1.԰#M3 Z\݊ kcu?FɌ$JF m/-4\(Zp%ʪj u9Xӥv% 0+(9-g6c{o眐7.s·JY|4L3PѢ[k8?/3quAp9ܭL}*"P*L[FlL %c|1P1rٙ+Ejtl}H)\#l.W Oimڰ;2rd"]P+F٫N/=C\E\~PO dlW(_xnG6+|ߊb^'w!~Z~7yh> < %1(R !1ɴ/&r )rUv{\$۞3^7NZ>gW!6ġs.Tlj `i,#rd f>gXBN >NP59r{ ٭)LPèP%2k_Iԡ~K&┏[HIǧa%H1DL9^Ck<ŻxpD 14 |VJ S~^a&Y(z2&J%P1͔͂T+ STd,tq*!Lʁ.)4xzeHDa{ʽ~׏gа \~!D*pS1HLDF`:PAR! #::6v=v`fVolގ g !Ma2K%.(_i˿tRe-eF2IBx[t9]e[NGl˲t{syC.Ͻ7yg?} Q^Gd S?g&ݲ0DžT<-$7#%S4Tlgz H̅?ŃT v+#QO{")ӣ뷬s]m~ scHCUL3Oc iR"{).^ >}엨Z~WԖYWcNw#H>'vn-3NKry]ޯlƦ!I8ȏ)9>vF=I)|v~6|"*-ES.by2(ryJC rqpk-~7z,$Z>[Q$|wࢦv"B,{zA s0g0x«iځ(l]xk5*)ED.ScG>A[[7A{K:iӠe_5Vg00K6׻?L/[dn-xeiei$̞Jy5;-Ē5or*>͗?|^g,n졁m_Ց(^X OyO>n\'3Y>u-Ћ>#4ջj/+!B`鉌%G-F`X$'cǒavxNE ({R=\5~ +0?DF~²矬ա/"nzȹ׊/oK03Lw:OD{p1M"VzV, %>8`#=iYXHk"sh,a\jRZ&~ra"ğRT멮_]l<:7&[OlT7oߓB%]J !TNg/x_QޖzI@4F pTL(8[DN{i]|L+r2m'-Z;\_pDLfaTKNƅ/c%?ð#"'uh ۚāt+%ʽmPDS6d?>| oO8Lvu$YWJ*'Qz"1tE_O~ 9+'X܀?k֭T/4?z_(Lz|uDԧ2| NДc^i7"2b޻s-Î1E1: =5&20D#?]'nL:zQؕ© KK`tJ&q -{k0 țb'HW10ס"@om'jϐ S-wPb6,Cu1ʝ~J0?u|Y>b%(k]y`8t[h3gh{F.79pq(;Z8/r'J)s2e )؜<2|A[ :*kzq!#\RF@M,"j(0?$LׯQ0=G~I,oE̮9U.@\Ho)ߦ_ºFKٰ et]G#-ˠ dfB2rѭ PB)Ge`z~*,oj9?ZBa9>`QFzy4~ߔ v8+JKSʪ%Cm 0Y_uxcDʲ}v\ Lݴ1}1J=)mm Ct9:vT;ŽD88ݧkF)0fs0Zl (tB4 ʋPì\%p/v,}_:؈ $;v"5МQ9bdo4֐֥2~;*YۡhiFk^ۊZ3ef]?p@8 3 :e?lڴK" HiG\y'^ޤtJKO}ܼ+P6dl]MA!^k#W7+BxKB)#oha2&-8fJI {Nמ A5߻k J#hj;?^Vʶ |Kx06a4 tU&~'e3{@ peB< Amk^??л%c]?"0AHc}R[n|AFgQG]q*޶ $udamIagp MNXz{p?C'zJ}k a9Q0nܸl=XwF9R!m<4K>:" {:}~:n99Q'\qCFӻ\]" 3 aERQ@c6`a C0љ)wjv5Gw) 'qԤĺ&#'bh>ZQZ+2~ap37p(SF"OxTׯ- 2" M*iIQyŊn|snwuf͚Ra$?Ap ɍ;y+ݺV'! G-1.8;q|f%@" %YE"R鴈?5@ʌu. >, E.b 70]D{\׏T'u8yG;vUW]ʘG? \r1eJ_=E}`/`D}sphMc)c?qnz4RҳdK/)%t4ԛx eQ[?}SkAd #ԅM|g_,O7rYEI{XLEzGi?,^qzЌuu5*|n]u)'9aua&h;E)'gcapG)wA YBDᐷ|:*/Lщ#s{1rJzkZ'A?f,$FJ դTqvL߿_,Y'OIB EkᾚV0}c[g Iwj~9SdT_Oy[ ף0U~X'#MyAK8QLLq7di F^WLqM=fԟ8 W^u5>dq0pӠϖ-[pu˥Kc>oVVxq;l{yR?Pc:<4*OWS=ɩF|RG3NOAG`@r{Eƶlq兠("{aՋj Qe P7I}#9ۺAczHS ף?}~~✎}5R2KYn4'P__K/֭_:?#Vt]6|׼|%?cp1 ]SL֘}o3fdF$QWtO v\%h)Mb8e_5^ɸ//0^$O7u’޻p,?{Lsrrpʩ`ݟr}^~!̛7'`R+.Yi%1&XXl çy}R o?˟[=u g诈>,q;hxS&Rd;w {"OCaf,H_R1r|(^ nړv[n)D܏ .833Qڂ/vDtfhLviޜnxAٻD9nomĐ9ch!ڙ^7"3't񶵙KmHw5tEZX]fqI9k?wؾm;>ZHO}xqi;-"k1ѵo>Oįk=U-P8KQ8yF/?O?a `ebwkZo7K{qrIm]xI|zHv<<0i)MOl~_bu2P6JH6T!6_zqɧO=8N9䨴i Tuv6`ޛ$[,d.א/jq0OpeȞ+8{< }k/m7J87aIm S}l P:=_O߈9 9AD)'egwjV:Ϧx]0yi>[:HգQ,?04AmEtb.͜WʲD3q>̤h2"״Ux~LM-fGDEx?nn~<̙'?Mk3+O~?crU d7v(׽o!'䣾fYXDʕ~[L<_#]dn~17]?b-\yo_@zZzL]?;ɍYl@}vuuO;?'OL<{>sS魶ŷ^~ KJK s FaocE{QDG 9(sܽ#5 v#CI-ɝKû x5*/LB a^=×OX׏>n%}>ȣص{4QNsuXMg Jz(77V?G~diaKۏ.yL$ ߳3 a}:BN 8h޹X1k)l(<&3l#K;MF Iap ?3|HLJza"T|ebŦZG~t^M6qw=´^1'Ư80gJO3A@okŨ|\3dqX/Kդ5[^WhS>_zQ[]1!zSr7L=IHiۮ@-d&L$>?*(+ٻPv FzƔy]?/IJ͑O? :L#:GNn3A z)Fi]%Y;kտ/H@K÷q 2&2/>ȴbġI񹂷T41`q>8 qxs?(Sb v?]^8{HYN b43YaZPR_?.E#s?>.~1859*~TSV`ejT1mڡ fd:c]3w7nN/P:(zu.X&b#q~P{6%3Ga>"4(]σ?N*Dϋ`8IB$;\^3. G$q`F*O%) S~p=C;e]~`Gu, tT޹x?7"x4C/jpjQ^q oM,(_?n-ڧSۖ؄xG" (.Q}OO33wkQU u0M'gc \$=Na}찈1Gtm? ?e IiYyS0u'W>oE^{k:pǡ"3&7J,=Vt*o-d}oXD2~hBԣ0熻Q8C㷜@ca%XekBK`BvqU 3%))ҋcǗa΍hÀf:0Ґ ypn KO+z$ObK#qK.gL֟-{JlYDo*qvf 7ND=_}MWa޼~yu@~)!>LvUaѦ(C4TDCU9jv6k$[Rl ھmVRG 3 /T vL5״ P:a8bl:*)\DJrHa?Gq/gN'"q+^{wCA"T,B<}Y!ue+%.ޓlAAh]oְSpEWca[[{K^zLx/(P7"%$of\p;JHUQ]uh({p\Q\"ݮew{}vH=fPHCph2ͫGyL6kb~=H'^w3 F` %]6uuuXKwr:K]ׯg~JRHU_1EQjs3b'՞h<Bf}vѬM M(ZI%Դt֊σ7_*JEk>)h0&3xNR1hlޙHdwDEI)=hmk7AwC;4rJR7o/@RPC҂a /x?: ru#GDzjNy]߾}seg7?x2J[7+59jXU,ұINQ"DTeS4y`4e:Oày-$1*zmxmX%"բ7IJ˰gDx`Ӆ氖#='@3Fh97ۆdyFFbJxװTԤذa B z !3K^sQBC=|':eS3ƍCِ2#dDv ژ>ף|w9 Gv3;^=~p Gѿ=A~1tUGxX$Gj&i$Os99+ǎ=AkHq^hPUU=ǑIj6mMOO;  wZ[[[!$ӻWK30E maׂ:_?4L˱~.e/p5&n,I/ߔAaرc;F &`0 cРA<r.$A$h 7fF)Y >&o9kzH>zTu?ةmMLh&b1lmRJKb&U^7| R@ZvndQ8pQGbi/Ğ+_|aqhl<])^2\t-~hlǦhGXRlܰA zwa$ ?YsK+B8*X3a~=RdP86ӢVxR޳kAa(ښV~ڭkMIމ*A{|Ѓ]qk<+R~6lsɓq c3f422MT7z's9"Nざ$L8'M^̳ 6myO>_,Y͛`Ϟ= }t;uX;v Z\VؔK:9*S뗖[}k j4>}GA;q{4c "vS|ͨz 3&w.L;Ǿnj_/Z?$^yUQvh8uީ#R"m[{q̖:4J)bպ/X}5}w?YmIi* C;n<9X} lfߎ7f^;f_.]m" Г)>?jᡇ|L4 g}N:D;N?+?E'J׾Q4䏘PDb*))K9GTpݨ?̪R׏- D׏d1|ֵwH>ņGfLuSFeW뱷šcRRX˿ \<4=.q:̸w(1Qʶ'%;b4m?[˵>0 G{>֬^;=^py9s3Q3OV0~WrzG CD|WFc!vH{s,L,?4 緀?_e]?1*;\"?/F{/ E8Ʌ(IqdDڲ捧S~L%UEIs/[4`u8.S+A)կS?}?zlu`]O;MCa47vˀQޟ?k|҃s,ƌ+~~cUߎ}4gc2~$T 55YkWg)Cl~`)uo|ȌXqkVDРG "};7`?FEHJN>A@?8`!Gil>'ع:8-h~eexw<֕ |;ڑG,źc HG{$p)^zF{~#"@Gє_N/,p- I79u1~DG=t"!s <4`%ZbXfr? ^^W/?Jsx_Y߁XТ";P}&'rRO~r/..9眃/ܨh'pٳf;7'!)Ξ ڜֹs)1#uwC׏!(v3'TE1W~Zblm<#hk,gAn߻s3ValmvDlѿFh̸ Ǡ9~-^s@h2ǟ}| 2_= {pܶ7~H$|SQaιS/eٴp[oO݀ ! [e]@ (1f_# Z6I#`;fT_F <'U :/,w@ J'i݆43!x{S ^{ۯ@wjc_vSO>_}KAW^q#EBѾ7`?bErnOE?[P:ۿӔ߈QQj)/H۫1t:`qo/?("X}z_;*M>#7z-QOÉY8&Z *j1ּ$*})5[|v3Vt~oapw"4?Hv(YNaQZTşvz(8S+昆Jg'_s 4~0 Ɵq ]sN'~ ?b Q;r|RWY~z*/ >];4]>lQx~yo&{ilw !a6i%"Ŏܲו3y@@zNywD_]3gO6-45w 7/YDgqZcO/kXt''_aGL?7׏ )0[61 gkȐ_x៝>C7ßw*32xR;ߛL>Bqg `Uw{?L? Ssk #ٟ"C(('.B8e=k͓|[!KdQub6ԞE}\U3wûZ|f-7ߌ7x1hnLQSoL8XJ|sX½h_{~ڏj>E&gXUi #gQ—3'䒏izزch}+av6-=}OJ<890̒sE8Xvlk2⫱"O0?~M(}cD`1e\r%"~F17x Qo?dbO%z顸Sdx gl~v#ŗv]?bϬE=#Wy |B O,W5;} *G|m񺄍Z[c#ϒcڄ#(%uh%FFjuk ^#mqjϢk߻ Eo%bDg "oƚ7b JE(N]X}"Gv]/x1d5¹f lR}kT^KwQMi75aӢWYlL3 J Ulo:xyy?g5䤦bfK5`ÑYsIxl^u4K]/'e?Q^^e76?:<ѷX(Ռ (e~3?9X_f֊-@O+;9gԿ9B`UZsTߋzuo%X-" Rfa~aDNfJ&E ',g"0,=gen ? q􂩟{nf9sf̈~F1i~k}-{-~Iw8D%ƞrςs?Z#w>bV#f%^ꩮ_ BMENxSla/Ococh?T_Mo󐒙,FD|MG"0vNe47 p,jAq|OfziW_m?v܉O>#GR&enQX\dFݓƵ8{<(?vI@Թqp# 3{?~T6}s5q0rF{t4q*V~m"^^o䩶D[3M>pPܯy8C眊aySxbc`5ByR?Fʮu\ ET+Wo{<~k$[OG)ssOټ z85sn*CXw ani(N.G2ZBtZ[[3`*F6Nɳ/}CvZv,yvl'(* Qߧ鵶& ؑS l|8Q wTLc G<6/1~uAfhF2c$Y3H@:Iv(ݯH^O\xwH3xc]y ;˷X3pr*,C.ϽI)Ïbfrme|>i2gziJ2v%o @ֆZj1v}*.%Y 9R)|XOѹ:ܘUjN!J'CCV{&5R̾ZlBJSI?8̱a(0 \vLW_/~+!sMinLi}^cOQ{i,~677`kl0_ciWɕQ0[7÷\-ہY,=LDf$UȼvXݫqήTM(Tl>2gҩG?iy9ZaCuV^sV]nk3ӱbس虌=h= q!әj=wzHz7ng} !%?gv&iQD|5. ?;L_31"o 蹮ξ .8WKmkUG%O G0d/u(OʴkwwFA$jцǺjܸ,>2OIE+q?r6OVz\̝7f$g34Дgî~`v\2XoE|_txG1(RyvSd:nZ>,ǚmzϢL yiz avCacTSN #ۃ@v\ ?} _kE5UBTWպeXOP)Kݞ;(Gf!8{oL.I4H2ȺNa8}ռΆS]?M/IRTm?%sa2-RlYolϴ֒^ZFk0)6 bt'@«*sz`C5[JbݰNy{|sŭނ&nW/^HgU{lm>G{K G#~,ޚ>wlƇcrap M_ Oޭhڻ@ω֍S2PeNk?)z4en[~a݀?# (_^IC]x$4*<XQhiR}EcS)0RDY(s"S@X[s ظI+{+s0VBw.=~;6,cgD0tuӯiݝj.|xκ?Xi@'/iiI #01Ԯ :Dp(^çpO龗y %@= J2Ք'3& ި2-'k:"bIa/X-ssnSI'_8C8ohh7Ckj}X<#'CONKo{{8gNj9AdmNzdzT QoorW6Ef!o8&r%W%oEQnmٚu3Šl*aF9`[GLYY$ `/ow/˃Dt4ԓa/j6>!H%Ο?t\nC]D0%"o/dtwMW~%F _'~Կу:Մ 4-rj ~g wDX.XΌ@:0na{cN["2O<ԏꑆY pbu:U:0 |q-?ŠC\5~QYz!n ޲9ZOFрibGjC޿GȞ Pkc IGX8$ӑ-Q0r 21 !lUrm/"5rXշ@D;zsmA- 5s:ohvtxxt뮻=} 뎣5g޵C\%'xȅ7cy(%R]ҹDv-Ei_>qGW%cP8 WƋ< VB[m`+*0g[ 뫀C'vOtP<hnbٮb,r2裣>dzө ƞw~ Fxx5VebӇ/b,@$8) bP4v,D΀P{:tZS;[47s}8>;| 7OQSoަ~@န zM=w~D=KQAA6Dw<sc@'I岐OTJة榤ba6Ur2l?ؽ|!;kD&mF}a31됒ŗ( Pؕu_;},z/M GE}6~2(D)j6#PE}e$D/xl=xᬽ:5z;;_-!Wxu90v}!u}JOہ,'FSV'q@^ռmHfȦש"FU$ڄo<'\GvS`wQ|= 7z*^ XZӄ׍݂g3#Ly<ΌM/|0~^HkblY&sQvGڍك"olG&FI`HzkQP&`{tI|s< 4=A.@1KRy5|2v|+c1;2x{ si%}.J9ʋAcW[Q={d/G' =d0Ɲ~ sQ۲X5cGFR-j̗15V՟bgorr(Ǹ&`ádߔK͠ 7ju2d HlՅ4S Q7lV]EpE:Fo@ սkGs%sڈ?öo@%qZg a6}VEp}m)G5(_1*~ {=$=ŠD^Î>Oyϐ6e{TO)>s5գjRl?B}~!8EOc.~D]zH".F "T8%!٭V64L5~ gf6z>c@Um<ʨ.zT߳ {;\}ۿV$ M[`CPE&P~)3Ԉ( ..XC8˘t7YC130쨳U"Y^{{?QoO X1!Wۗ,OBHOk)sO،9ѓ5Fu[!h25]G:)N24뽒cUA ̹~^#kX1#C^YsWt%`MR}A; iIn8 - g'Jewj޼Rf"{j8#>숴7?0P }򻖾Z%BPszsB#%- cÐaLj؏F;I5=EO:>4v}"B'B K䷋3{P__\EKg/9ȌC'Õ|a*`XwbWea{ɌAꡰ&KfRtpk?Ͷq(AHsm[ Q6nZ\hO޾fZk]7]ƽV/-秾uPP?oxN9bLJ/ֿS~|Pq7V`ῄu8KTƵ2sFQǞ6 vOog_p>كBZ ` Fmׇz^#2u tRC25}A35""#~$Ӯg (8_FC-%zŦRffD&T5:aLumUIuRg-"RsM.s: =p2\lIIXNIێy+{WkL[$x&%,jMOvۄO9f́]k0ZoJ '|A!)ōZlIa4E}J?25A=C׷r w]FjDlv[>VD햯Pu lJ']{HQqF1s3p$wz+4N7~ֽ7TkpFG5f ~C(8Ñ4zi~O 97qo}NE;˱$?]#I46 EP2ǟrk\^LJ }I!h{EPX(T̳yğG5IWqVgyl߮ػk4Wah{ qJT>=HO>s >;($aƯuv;!'pflY:zaT\0}5_Fp哄q ssEjY43^\ zmGJszjK9]+.T/gIYWk$|?4jKK9CGԿM_U8H뵻 N>t^ol =cuz "J*o.)4IX;{2 ~g%˷iPXd*wc h`+m5 aфY5<!}Uhؽ Eٹ-{{eK/°?A!F})_戌t6;ށF9JLMSB1 d]"{B3$8^PQ~!Yq:Z9vbu,CM[OwzONZ_}\D8l >z͌&SBfwS k(k1H.9x- xl4] C3F1lu-ذg0h_5Bax.*jc- _k<6-%-egZ[>iDJw#Lqod= bSn|/83sHvd(q"vfz f7nʯrauU~?ۦ*q۸i KX׾S1p (7)YH[ېT,$wuĽJJCsNNGsGvԈꀉyD)SS]TST'"|sNzQ={#Tbo,V2}V‘S:) @(!$TbbP0&s`Q j2|tM'+hOޔs<CCQvց5@>Ua٥HGTfop{'䨂rV|yl $H~(Fa[`2-I 1;cǻ?>|ݍŪkPjQo~y"z]A ij׽6іETš7N9 i8")#٧ {(m-57QVt(KÉkY`m`y +C3nCimC(#@Cv.Zbԟ)=x Nx]W+[LaHA)@!St-@ \p MbFmʈ0%9Q\8(HmgSjڿEݢ.}ΫY>yHڄtqaǝr%}6|hMHn;hwX ?V3֢Ni3eqO+|":1I%t3Խ(! ObtC2G8b와ӣ$ȵw('T+1WADP/T[>Ɖm9?gx0Z/^rr0b2A ﴜB` qv!G&94Qa5b =GmH"g 4E2dk'?s1"0>s]'+zw9xQ\M+&ŧO:SƔ^4C? D(̘+dz\4 ҝJttKOnH Ԕ4a7VF=!8`a*0߱f(D45Px1j|^Q8A~eH_E02؃ReX F$嗝b U :S@sZO^uTc5זh^UdSWEI&?T)Y}}r<ԓugT`wo[NpUJh-0t^vG@&}RhN)=vEҳ/L8HajP5; d&X"ViF9 kE~ib nN`9yqϞUWjSڔ=`p7JX3?sV HcGq)x5_oi<6/2r_v>FtF{H]-%W2QY(Ѐ9=22P׏Y~B2j2rmط{7jo@ݎ hjn()<ɦEƸAg']Ga. <".3M(=AYJHA1iTt0ŌתbƀǧjH)$_G'kǚמ_FKrNQ?}*BG"fݜCwZ_g>+HOlx|̉!kH>ĭ66A6SO3}t0g׋嗀\u_J8x fȩaQ w|!RýU'`D"Q?y"Nxp?{wngݎm-GwuOEIQt7j|7^[R_!6֬(ѕ}GsBؓ}{ofd}h!g#EWlۚ\d qˤd74W/f21H;&=zU5ln*V%㥘u 0-TJD4O#`]&Ir 鼦GVcdI%'ިx`F^Gg X2zӈ3 4k8 {krQn+ah5^?=1 Jћ~OIE9,7/QmnXLG(=(ۅ57D[rD@24``H n'.aNzw*Om;SO8Rd}/3Y$҄!5i. >9=S@:'S5ky !)-K}3yy*Pw DUU9(FM2`[@QT*/΢W3VŽ/z EI٤so2 i~FQfj|tQRxinڡ']\|f@%!ɲס_Y"4}X^i2Os )TIf!\wtN9i.A#%#)9E.)/5QHOOYۣ+D:c& k?t`/€u@Uw-55ɧ{d74LP\@dtLGbu_$. +X"pUː7xLj$ҡ"Q>d螛аg0~z{Jy^r 8Mt:66{74G-C}(MցIo`Vȶ I >>o |E~f뙎kkA#1_!P^ԱބC~iO ,_, 멮5Wx.񹅧 sz| xxOCD6UG@[q5'u j/rdg}vgk`pUu{ᛜ /\fo:ES-P(~]J:ob 0E{r>9G^o9?{(̨wEdYay]7~"V|ö6kak/K|сo1 i:s]?nmQcNM${x )gpd7e6Z=drL}GrWFm愞1gF~ dlЎQ{tCyubťIBU5~1-h1>Vb|>%HoWK1:MN4 ߉1y/ 7 Œ=ko8i"'1_W|MGqO[.F ߓ\򂢵K2̐;lҫvo m}0dUUXg$ȿEC݌T}:{^j@辟0AGWq^#bs5`x:AI%чK7t lP>Ϸ"=lQY1S3xԔt& 3o08ƣR^Tǹ1o GbG{-1Gg @V2j}WWT: ] 2J((vezv @@B8" ((afv2Dv}y:lBXED)%&'ʶ|\Kt՝U+uA>|S$)(ʔ(y] vfgׯ{z.vꝙÊ7u_;l!ap;|ވ4_W,3A@3 9;s |9XPkx(*>/\TCXwn>"ù)U fw 殺 S/Z|?$;LGgEQH@i'~% o7RqZ}QtO[)e˨pF# jmw7EE@aեQ hѯs7c?ܷ˃9&b?,`N@9VCu} Y'w H͝.dwdmϹ+0X?GaKd/uEμf5ܮ4c2|~26$,j`~Z2Șs$9׏Z}SסێEa"H`JPf+~GGPy1k&DU͒{gb%`Uq`As|'' 8LAl LN%*JY*pL3V ]smv]Tf{ FyNxoI N+cC/i8Dfg+aIW'Ɍ~jԚZ~/EqCs,,X`+8P*iyO ȅD,,׿hPgjќ=·֏x:  p!]m!h!5Ճ5H4 [MHuI|? rɩU f9FN 1~l0[̸%z~jIVeꔼ|&X@.bH_p؞@DGn@m2 EYcEG0T7y6e(4HF! ]Xq{.m =Τ{Of]*qxqWkKݢsko}f|FmS{( )?x&gOj_Pk4i5d'M%_, Pn#&}4\/&e}o"CEkN?C1SXs}& nJ )DϹrT /k=5 RDwFzjqaxo?2:\a+.ld<=/B–) ྦྷц5NZQ"ן [GN z2j:Q~ ,l( ))u)nO䚬1%X@!&If)ꀟi5P&Bz6om ok}v iO yi3߫pi~1Vzi~}J[;`v`g'/ƥ}#A0*۱៽A^Fw.X >Y ~HF?QI p;:31ׯ6ˏ;{#N+GsX,m38}c\gǐbPOsp R>J=j|azXrgA6MX |h5zBp9Iy#H؃[TTe[o#ekI_q., F^3|+U8CD·4ݯH2 <Dϲ>jnaPd +fne{K~հkCp%hGŘe/s2鳠f?-ӧ/m ,(ky{|GA}l2tQOj?`u3o]>'>^e@k0sr}ňx/w`_?_C)Y̪Û߂ۗ`_2L[r]*QUT-B%~J]~G,"S> l}ܹ~F1Ogbeuiܑ;T\{FpSw6Xi#k y1" ADaς"]GSFRI~8P (rdё-Dy9Urd6 |+a3ѹ0>Csv+ /Fˑ86_ŸhF9n{FX9%)0Z[ 7$Z>Ǥ8F;`nWs\ s_pr-t~"fSBDۭc[pMqo#:v4]N7ZTLtyz#ZN45Q/1#+=s= z.t5}l8uh/KR&$] (ࢿ˗]V;:}.DUG.ǰ`ݟ嫈ltv : {>К>35#aIjpXRN9 R\\¨dS28^\ ʖ[O[j'd]ٲ،&@9k9JSxlg  [|+L y糟?UqT\C\K @ Dӄן9@-o,Z}osOp}"Q`*]/ja|T"F*)8FsotL ,ZϠk*G)*>:ˌ^w}$ ̼f%V!gfd g/" o/_/ NO0շ~W?9`& M> /coL O<~B!*q;<4}vŹ ':q/hiȻ: I/W`Gn|Wm߆cڦ'fTX0ye8L^t/,P&WQe<\z?Ƀizhn@WǺ+:J "c(-yM6 /J} JA z.38OC0̵'fUq ťn~=NJZ{w^'  -q:w4[!R;hNJӣuzba MK=c5Zhu$zfE+nd>`B>?v  ktJY'vnOr-V¢[97 3qX5ji,$}<4G- '! ('* % (ayKc֛@7Q"=׏F{E\g PZ8`@Ce?7J5'L]ksD wN7 C?_t.l% <Ǐ|5 I* |>6DV=kLc}k{;"DNZ|VJ&^eyY& $:3'߀} iz\YoAIJS,=L1lIw=daP@A#Z"뻵MISi?Wp{l@%jE9i8~~ D5DI_4E `.Û2ab8\,QSi}OpAK_v1ahSliU9v? -]Wiu]aՠ@`\ D& `m/~ Q<~B_|,17ګL%[evF.sĔHVŠ7 -(q jw߳Ы2 8giչh~[fǬ_p32wdvy=!~}SK{ SVjo(dTNEcƵZS ^pЈU_=J-{p1z<"Ppu_ KTE9*R:}i>۶6-nĿN|r&tBkkK Hڿ,Q HAۄEPRCq i7A>[kw+BH#ՁG=iSC;5 QNp= K lyg ąn<]+THs5{(2fԦPV̫~A))A?u+H[pлot^c [)d9T8i'Xξͷ ]DÀFk( l6黄 eϸogIU6 W#g}p<*we s5|{,vw[Te)U31F|s)1`9NIEO8m kuX}ַv8\ߧ\.7B$P[%D5e]*^;yDkӟ2=ӽA<O] -X샩o|CaWx fV46m:xcy3#c~$B֬؀#d -n=ϡSn.}nTjѭ(tI1`ZǶe&/+ju{>so:gP 9Ϸ$@sCXwZrPrV/pS898WaOÍ_O$u^6cOX-Cb0ԉgOֲ>ޒ G<咮0SAh,7[p2sC)w5]$89C&}KKd`<`su͍7Scŗ۞ki K;sZ k n~f]szN+kq3/ YOdbwvO|лw SFPc}Ix/"!Qm{V}ZUeaC=޲rɘѡjPGV&(aB{ hMjSxf?xjNDm}mTkw).-<`,ƋsbM 쵶2YGh-3DŽa,sݾ" t ) .cATPك;?$wQv\9[h#;?\+'Ymc K&8RJnz\~xpd[tgiF Fsqpǚo[+?I229fAYb"Mء|xd(RJ6>H ݸStmSM&j`(m?tV<_n.~tm1j=(7`ozº?}̛NQPJdwNK-op,&rCqh4]uhס-,.@Qfa=auoÁ^\|@D-@`!wQwFŦ qfzM,\ri 5Ο4#^"ʗNZ ?|"i@CǞ3tMbbjAi@-aGAl y1 ;x=aG{eq z-Htx5~m/>Lbi[_#;ۦ̀+m~eF kX;: p3tqQUP m+R1 b)Yo` VօneUN@4v'f\u5B2ӽ ) N:5#6 ;tJ/ Z p[ޓS†&t\ЛI;$9kc@ϧyCNruğAXLh0rܾ3s)>-(T6ң Щ$öv{ v%w| <)d[eR||fE8;K" ?>>r+|1x&0]{C.7r| dWS`Wd~`4`[K+iVz'%ݐu O,9)+%i&y}t[ZHHPLXr@.5W>;)=vۗ.ѷG׽]i:U=cˊ# ) tUt)3-'04way? +a5e@`6 [:[ƛ{k qߔ:*<ݗs-pmŠb9I.Gp9wLm}{0D}Gb֋sXOX!^Y4̴A<*†aڪOaЭ^BTDžE:<& *h8t(?M - : xj{ByNغ-}u>f:f#>6Ǎ0Bߏ\n40xA8mm2zg-_e@Hn'u+DQkXE@]ЋT࿚Δjq J]BS/M>o^/5M&Ku51 ek߇um@}?|m:tZȚs 14]n!(znzlF@`c|=y6 8pk#M[ ooC$g7fu믒d^%wjЦj/ MpO0Gc GU6A @$3sdq|mXv& g_ P8|Itd;F >}4|}gQslx=ORqQ}p-, 場m-iҬ=xͽx䙞gDN/eVJGÔ-nxbVpX+1u xϥ}xk\(~?ҏ|pMWΩݦ)k ֙#Go_. }ybc$TΧ`8B2]S6<6}Y遏M ֊(+|>zkk't~ċ{{̭ |2[`cc@!Qtnϩ8~xx@G3yWrh"ҟS=@:P%}\zV/3O&λ֏ZbwNJБ?0((5LYH,hb?4 =R|q}KctћjP3={<ӡﵼiغ[zw~cRed/G~^``%~dQ@u eJ[7# !pF87XC6We@[m۶r9hܾs׶Q `cʳ0(9GTR~4J ^fӜ<y .F("C8B!}\֭PY;ws hpISJ y}ECK&7 wMwQ@^O:kl$tcF/忱,_G׏-9a⤮7Ƕ@w~=T"(0Mr >iD (Z0 1J9sL۹kݻwo9tqlQ{W7TP.j‡ekFbaQ^,.0@c% W^w׾:iӖN>AA_`٣ 7Ҩ͚##92G'.͵ /yMo^[iȓNஔa7  2 ʝ~%wĥ!_=7śљٗA~Y\׻c1_,~h\^j A~ :1IENDB`mapper-0.8.1.1/images/mapper-icon/Mapper-32.png000066400000000000000000000077231325266516600210230ustar00rootroot00000000000000PNG  IHDR szztEXtSoftwareAdobe ImageReadyqe<fiTXtXML:com.adobe.xmp e IDATxt tT?;If  MťeUYփ]ѳ㺢l'RX@y,JHR$3I&ww9{;ss~Eu"X[iUUMCQ[ϘL&}ܺo"vMcޜS +grPE~Lr_ \| g}c_SZZ1]y#!ҾzvF KW?$)lz=F揿K[G:Dcb^ZWy7جN7(~}n $aqZL~]k׮7MX-vfZo=OƓ'-$/)o"EbtQsX*Ф JpyzvdSR #\sDJ<0J2̈́0Pdnj3qB&CKRQY$fEwp3Q0rzdD ăMaZ:8ŝ!g&a/Fbr0;)M2Qn <3=ͅKM]6 RQ;bnAk򥧱P|1}'xN#O?`I$Vxe:8Ԯ&cpG"6lx}ɒsnL8lc GHCæFW5 C$OUu?&mI4˽~/}$XX@7cL A":&b2Op$oPZƑr,:r$Vv|);Bs (P5}1mMx+_Hʫd$$7*P8, yf&Yesh+"ͼHG0~%iw_!3"ݴ*0^n3ߌfˇ *%# 6IJ(&fZ%#MwVS8dI:{6 ,N:{hٵ@G %ŠTmOxDL?q_w2ҜQ*40TU?dDē_?5 {gx"4hQ g;pd']Ċx\0q &*"ez+k^D )E/l.ԫ]P.k)f`wFBS);$Q3^wZ^^LVgW Mq;oQEņf#O컩}Ee*ү&FZ[9{-@ I+M^Y)u2IBڄ14`x>HE3vTϜ)5$ %GϋD=% \eLĖɤɓəGǃ#!x`6JPZQL) -tTE]*{~o$*?}Sʨвa~ÈinJ4Jv;QUKS .1=ilV[܄$IUGÛǕc;RHqj~Mu)ǣ(5!7؃w83*vK/,,9N!߅8o#[G#:WZBɂeҐDF_ej $Kbesg:,BP|z9%%d t*!~n`|g$z:V2&JȈ<MH'b1sToz;lΞ]9VQQѬ6lzf+9]tv -[z6IW7 rƇlBeW(a<`ԥ5U.?q濅:ӯ䌩& OLB7"|Gy$9sc;w}纛:a[%J܋1}b kdA.#iGE)+%OIENDB`mapper-0.8.1.1/images/mapper-icon/Mapper-48.png000066400000000000000000000133071325266516600210250ustar00rootroot00000000000000PNG  IHDR00WtEXtSoftwareAdobe ImageReadyqe<fiTXtXML:com.adobe.xmp IDATxZytTE^{w,@a /a9z7AQ FQc(p !$@B&$$t& )[U}믾.$I|)?/|>n5Q[&Irwh@Ej` KMp82N񠭭MVKϿJCD֨ϧ21gΜb6tU*={r8SB vF4lQ*!cXnwh]Ӆm߾haB`#4K TZƞ8pk C9HC4^ 1EQIui`4?I3}"&O Iyll\<~I)k_aИa;v UVANn NeV{U{_7~}< d-v;2Sq4UϭEc ͙&\Y b^)H#*'h5] jAcLP3Lw}Kw!$337|w ;onf5I*\\ 5jր֢mĭ4 MA5.>&mWɂ>' I*%ŁZ+l`cDN3o޼OyRrb۶m!ȿxUȃcYT ]1R_ :]꼃$ U?BnVhT5ci(] 䌇7@]H?y[~:IȢ 3n*"xc.(Ѹ;>d`5$) xPsBF7P_566Hc6 > ҰKt"'ڑ#tok[ՊV Ԓ<]L$@BonnmeQ<ܢ W~>H~ V+=+;1F &h~!hr10Mƕf, zaL~<k_A4UHIN‘p R(ӧ xaiQcM :g4QS頋`w:&`)?^(c(Z6yK'T23zým"K񆏟̱$I^{oF -`ZNO%&M=[PF]6 k=Ѹ<*& Ы##w\れr$c$ち ;bcga%~FH$u l7ښ9 Cz|cp5bL`*RV\z9tzV4r~\!tsy񠩱7 J)AÙ(MKU<G *0r;aJ򆀀K #tZt,NE7H:# Zہ*zְA}YިClHu?GaT ~L2Ay>檫S^5ɕL 7 x|rm det Ҟ[1rFLsNQS|_/ Art,hp`o? ' "a%87y`ȬYP-4ҋ)wP1,:z>COirgCF#2-}GB7S[VcL*lR?+hllİVbM63/,:y/^fkمNl^wBgAcM"Pe 5T?0CBWFTrO X& b`<̍"^3M )PrJ `)>{#g;,+ $R0Gw֘ RҀEp(:92 L=x}q!{:KyX&^J< ,@se似 {aB3U9.<.J BBlH%`i_=m^k BOHB Q~~>Ԣ+%[_R[A{vhص8K(0ӽ/\{b,U_w~جA1S0*? >h<:8#G`bؿ?6>&%1V .56FqnjEEE% c3HK]B55%XK{0bhՎ@67wo7 ϝCff/Lʙċ AO6ncYΝ%4ɞSw)agOz0~0M=VG"s&)PAvqaշl˅T|clڴ6~a|n-fgRI ZsG͈N}toYf8Mh#+!جj!KM-p4ΏZS]]%U>ׯ5/<'ΥZW6u&w-Jhp5g>bspki ߹%q~"v|/ʏmd4F/|?,f?~/~ ܅3:͆3;>@Ɂ$pD3է 6 VUBSIjO4 Kh} 3 ZnhӦ?4|7:}-G婟8I٣wzV~iQ}H2c$ xt bBo<z%.'.1o{llNJX?RuA(۬"԰Z1g !@\ۻ_߮\9-~ c|"&/G/~HnsVܸj@ 7ByPD6;o%!$Qj tW 2òS!ߴ\ȇ!scܼaQx{,\whr?bސ2NS K>AL2%6<͓Nl> 5qbYSV˿##ւ䝤Qi}GT ҟ& b{fs`ɴv9&) LUQGys#&M e&IXe@X |t1 }xZC.@Ն]{=x?IN4lliB= Mļ_cIs.]Ͼ}c`]*z&FVJ1Nn} wFŽ1>5X qsŸnNYCjX%zX~\QQDBB& **k]E&o(9+s䗗:M:ÌCiڃyk3!K8[ Tߪt[b#h#}T, s_/2!>ݒxzA32ou 0P:U (2민u]<o\~rO;ZL)))RcV`9!cvyD!rss?a ,3ɿ `ऌ)4V6wSi k-..LUZCyy[F'G)g1 B`ROAFS۽\;lz۝; g m|sZV;sg_`{WBRlTg܆q!<_V/Yѣ^UGk9jRs1>x;¨$Iz柲ྡྷU;;ʴo?,D#]IENDB`mapper-0.8.1.1/images/mapper-icon/Mapper-512.png000066400000000000000000004305721325266516600211100ustar00rootroot00000000000000PNG  IHDRxtEXtSoftwareAdobe ImageReadyqe<fiTXtXML:com.adobe.xmp >=-IDATxG?zf6]Uβ,9`[`0?`q8&p1-ٲ]6isޝɳҖ>z^X ca,0u=ׇpa,08F$,LX ca,3t~T 0X lJ@8/H ?~HX ca,0u߼2"i(s%K0X cazi+}A,08K d3|p(v%`HX ca,1_{*J^93rm(iJ`L,0ƙ,+R=z+ H7)+X ca_*s% 嬃3ƯՈ$_26GqTGqGqPZ]IQ,A YX1㘵tjq똵6zӛ9 sVkRuPn䄲lzA/(Yqa~nr8sR{A,`g;7ʊLFp|R`YGHЛ A{ݞ KٗY:, %+pʞ'yP{@\;, 9ɼ⚤y̧NaKkήf= p( gPc7K8A7@@*ka,`LN&Y.V/C,UXY0ߚ4QZ0"yC>@O A<4y`X p?` ,`lN9 T t)CS,%XL o^O'(XT,NM?L@Xbo)c`;g vQ6A^+^t"kE~X>fkS Oy=|׫ :Q?o 2SAw2O2 @*2b%vͳ(vſN: іHȔ|{, &.o1s*icb>L@(Dp2%ཀE~/D 2Cv'vdk.]l#YXs*w\)~1 I!L?!,;& <{.z-"yA>]OL"o@yq<н0}bϞGģI;"+t#2ncǎ;\MZeھ^s'|+ (eDzP#ioE(ίW Vy}1d6~Ϻ"P_>_TĊMb*F/scߕZG!S^ݙ /;ĐLׁ|kU4:|OȮ~r,a,CWUciT2{|']fYdL73dXt1jT1J筿f:m@Λm)ŤSQ9O'/w F9ˀ [z.`P,Aʕ B勴_27vx q-G`k+TkʱE20|x^OդKAx]4쏰>y'XA:0޸fҟk/=9 &yQ@C]#}C_R(} R)}yu&9AT9eD8չ&2MEJ6u"5<}omCzu RK H/yLf{zf W5<T Bna&R I5(0w]rҢ^^vhUԼs`GBDb{D:NL$^;!$ '! #~Yq%0vL{1{KWKjk ^qa=\b mz.d9 i{e)SX^s-gOǐeW93eq7rpkWDĄE21@ FšjakGA#3ܞq#^s_Wٟd8%-#7a̿"\x/cxKSɟݓs2l'ƾᐗrVXXhp-?)`*= xvڶF>XM$Tz8U.,3lGo?[]<:TFz.=%['~6Z7k[ x]LFF-ax[8l[Cg _3 s# RL_~o{f%ȁX@c eZ}~EE%TWWCMM-TVUAqd2PIq)S+R!ѹ">zD@a ʊx%z䕋x^zLf'R[YYijjk !v3Zh0 706:fg Hh\DBq(0L _Ȱ,I0A/TVU` kt'y~4CXb8ci+V?$ҘWH=s& iAX dʰ'*^j*3B̨XX : $UvMoTSB3~fXH#Ylƅ3x6M;ts;})xXH(/.z2AyY*=7 Vzhjl";oVYFF(  1PPʦe `ߦ;<B &txjrYL(U_uxO=Rs+ 444BSs3}=m( >m`g`g $,a0@\Xs%2F~LCRKRTzMk>GMuꄹYbacn?s ([;Τ>zN8a r٬+Wg(BN ؜Poƙ tS 䬖@OW-qa^}O3)@J  %C!{⧇!1<PAmzIVbEiH} 6Ab3[P8@Gmm9pi4^޿5".ea.dffF+D-, 풢N/|TГ(ny< ~NjPmY''n^PV\<w?T;c *yqԜ ۖ:;;a`p"Un4lۺ9a<#F@*رxVnԘ %bxڗR6fNLc.$a-B$B24,@:C$zr21%Y%&&ntyR*>Ksɒ%f3;]h0JS֯kcG\#ʘ IoI p 5PJcs}555P]SMvx kk7iHr2/ZFXSXz {~#E-s.Mrux~,{'8_|@*ڊ$mYY< E)hQ YΕ\[錩{!Jh~`WBYK3y Z\ :@ ˛X,FzPSJQBkK7d;֭'`t+s!J5󏊊T,!;/6 Jm h9Yٺ3퇁{ErzΧWfS6ֵTדܨi^*\?/eyTOt4R=aoXI*y*) ElܼA?x!os*wud1UTT8o`6KeQXx?}] M)8z(X[l+\f{[u "tw.ɔ]]&G7LqP(C;̷l9&'W±G6TgM%&Yq=ގPYX,u른\y޿(Fì[ !RSEUsS3  BB(u/Uv;^Քk}%+ X<AG~Vt>'SLdE{.RY>Jk\ *|J՛ 3{nFnaC"]f ݭ[%ۿ>Lv gt!,.0b'\ GYX4֎(cx;;v4ݍLsm'5>3b\>eC^+1LYd}!\&#qjkX:M slgH $[g29lBv`kf 1s`q*o!H#[m zI |~v x 5U〖 ~jTD+'[iwpO|t:z+gH~{7 Iv wƜ5eL ΧM H) ^,D˰c<;d]C,n(f^;T%s2~"vz4 瓔 p[; 3N: m8)z;^gB'f0quk C:=T'GX{f#$mTFe~Fd/23_ so[_'V"ya^ő$qSYkBXErl+y{j+{IaӦŏ ?CzzanK{;4rP(L] 0hlQD"O'ͤijUE%)puşME =M6NAls?v)+Q %j5'x luG#&&'QTGXZ?b⟣įL,+tO0x>LYg_aW%)b4J?՟jsn)azp غuCOtE)1|Ǘ;>3zlpkȜ"1 .4KI^0>Gۊ^1wy;|vPQaX4.LkayRx6ӭ0_GvZ 9?j׎/&%l,¢)bãHcPlyUzK~gr$)5/FE{B3Q.x)BS6 & {ń=w;,nkcJ2N*v 6tB^ޞ'oa-[F Gy;B`n~@QMN GXpcB`&::>s3f htnͰF,&&b)k?'LFwm4Z6nbq_`Z*[ M@5iGx(P:*-{R|Qx9+ӹCp]AGo=V?5 xfy X9#Bwb$3laSs~1(+/톲+;<ǖ'vo 9:CvNৼxJ&'Sχ;}?vƍas':\NF9 gdvohзWdjI I&&`/~uJ|]Y(An6֙gUIÊttNHt=/UD8Xx͓WK (Ծ@,/&$;OUU #62Ͽc0'l J$ G}.&5@}b҅goaTgMJ*m;gc Ŏa@'vZ^[\f.Al/}0},v~>U|B!4͙cD:ag˖ ` rn(}d| `1GbNL6|7;e%cg`WΛ9tA>90 L)TI@IW(?F1QJ`iY9lܴ=Sg-1>879܅XM}b̳xmMп<_--\b'TQw65D[T tp^K͊q줒N}{(gG;obB 3 Va5a0IcSz׳INF{=|$K*%E d+>FD26xnON<7gvך5k Axg2 }&~4f13,łX.C osddq͡vz ;z<(vwl׭gE8*$v$"O*-*(T> [_@֠BJ&1`*((42 =6n_b{bNSQ2@, ğ؎[^e4)0@2/W]r(+W"&c!ЎkstGg?'3K볘qoyA匵éUz(9RE/|q׮]/z'/ KP qm#@gVj2GyA3@ɀ*L<5#e_@w7U>j7w;*I-;gBBv6mzuf j!@bn@-ڊŏoڼ3mEw)OEg%m=C߻1&6w1n6'Eġ:?زe˝[n>;22?ROR˃>sGLc`a7rs3[Ҥ@0^uK b_d*̌Kot`YbedS SCiN:G~36vDz^j*?Ê94s 744:w|"¸(җxL>}׮]{K$"&9q?A< 7xY\ylZUmV#3yí!s{g IC0il;o?!oG*1 ibGr}sw@FˀF7Pf&jyb Yk8N0YyNsuytzZ֥9 E},42O>X>C(ЁqB ޅ&%E^&,W %Ϸv  R){ˆ.v6n$fm{_]Ż Og{7sc:V8Q;^C#lPYYe'䧿[t8 J'crۭ!h4z;wv_}}}X"KJ;഼7j*K<֬YkdjS/ket~0{܎DJVL8<9ݱ'Y%q3ρ򬠁3{CuNNYql=gZ37O۸k Z^p1bxUZ< ?H/#o^>|^9M^Fq9?ܽ{[(eRY d\+ yooiiaVfbj}/EzxD f&[W_gQnvP.ۦ l VrbG\ۖ%Nq$Xխ6~2óϜcj9V|eUu& b +q&L0uS5rd;@|Ŭ$pW,]2'xk'>OCY BL_w?͢JKHkfeKMWY^J!ckm16 ůq.WmaHoذɁ9;T:O|.Yel'TdŜq3'eq`y%KZު IZX&Mkh'tYD0)zR/k@b1p!Y3*܁l@??5,;R [EO@}C#  ZiK d)2f׌%d|=pw9}WE@kjjڐB?A#?`nnn;c~|ajjIY02. o1oOL7//<\p7Bvy=0>6Fb1i \^ lWf~D cuܪUh FdVd"vUQv f ͝;<hsו(ƒ6ZJ`~DŽ@[;f5 #ǭ X @~?}c0eݱV|PS0m왙FU^.zUPexshcMoP!l\5jOa A>ҷ\ %PRRXpX(]xubN?]K!cԴaԘJ*hhl4rFbgB!ā()GrL.4^z!D*+Ȍ yMdT2t20cJ5dtmگw|G(O dRs"0X1FFe˖35P 'ݻwwA3hHjYJRkFw&pQ*yܗm*X( K^JP"ac###padI*P(S[''`rbO"=[ Ű7}W!LNװ!w/_=(9f(K4PP%mjRQux"v= AbMyƿ27x2D#smcgӦ-;]?}&vV ;NB]Fژ(X2ٵӋFae,4ޯ`{߾};2f8btD [=זi샡~YySShX~i7$܀ncOlz{O}c;vq8C %12 34Gq]Oܞ΢h-ǠrRGɓ]0;3 Hx,(6_"cܔP8yz "I"28.<j~M8[`?ԛM6oVMP@Zb=Q@\(DWb`͕Ɏ-J`H;xG;ԃ8rtiˏsP)1qCciCv@֙ʒqz},0$~ɗpOe^{Xkv 쌌#bb:m۠W@E"m?XL2kqSdmhXME3u^շM'/` yb ;ӻvя~t HlEcުe=S? W=A8|1嗖JxtKcS)y8}PVZU%l[Q] }iU+W@bq RU)2jBSw[&5C>$qWKzX(RN7vKsK E†: g;+k+`g& OhgUǿ}F455=4 hnܶm;{} 3O)Y/3.tO\,[=JKKKk[2=D6ÿ/7HQ9; `mi:m5oefp<.%^ ^`D¤%'zq!ř3C0oepf!ĉc1瞻*++.P'cy!4zrkXWA0CyyIF"[j»\Kkжd;Sv ag׮vzL:6wFC& U9ϸiaV+u1m۷CEE%yeVD=53UH 5ˉzt,{_w^g1kA7=M;';|B!n~tXz-ߒI6AσC2gX!cjS3 {)ɚ5k.:zh<& RbEk-_hP' k>p-#d6mxef bySG(ae/X#;!Cxv|!T͛ǒa1{kgkb 2q]I g2KJw<׹=J8*a IbXiGLc駤 ;hY&bu1 2ɨ\a 0g&!0$Ji(f5F 8 E]\_javR*1\&9;,C?Qܥ%No]op40KPR:vbN8=9Xh7S=.zń0岹Kv0vr 7g?φ0tX1g ^LB@yNbRMr;`?niSڸUm6Ϊd __K`G8zM{쁙NF8e *1+0G<&ȝ^S <; csY9{\jYx"g|:H0\F6O|Kbv 7 33ӖQXB0SΥ.X0a ۠,uabyL\ݷooα;sH%I6(ҫ,EQH\v}070L~sa@_;"h؍ߝs9nc:.6,mvAǖP(ԖzŀۋZl):+tbxVq",g1S)iNa[J@ăxua^%DAQbp$x^"h].1vaeSnWu `?5vU(%} %>Td*~QX#AvcaovNĎ,ԼQV|3+b\|1ّ/ٖ{)6$=_=IBecODt]PfP)bQ Va `+6c? %&s=PfS&-[b7·v̼A++0?N:V-YaBy 5@*UO\IF+եgʿr\އmH>+p+!a 4F#f!ÎU&k7 s>l\JfԜ -ѽv.#rh?l}E]:-{df1-/O:rbrR,,vQ_:gjŐxa8:HTBر[ƹ{K;;DƘ܄&L bӏ{d)67$~H畽_9QRZVBNfΒ p)YtWcɘ ƶLzT{*WE:?VR$x5w S5cacs3D[>UTܶ- Yh0ASvn5Œ'|pTn'h>v4k vDq+a6w9Em bnjx g nse(A.vJ$)A PKKUaTs4v ŏ<-~Gz+hs|iByV>Q \RZD-|<CcS#tt$<7 97b3 _vD/\W+|`nDX5@JeQ]}}`C";'tG;<ŢE ;(H3(b)3$zpť)c'*`אݮ) P!/:A3'KBŴo(*3qޱP[W˨\Z/[1;Xz EG,,5WY^oNGx uOszD@RU095 3ӳ.n1O<@,s9snfkff.A6@u 5&j=E{-N{ةntlyPby/^<*i_`t1}3lo7w\B7V|՛]V Ztl2/NfӺv>r&i|^21Pg`>@8 v,?N H=T ׏I8o޿4(h Ge5GNzZƄ9@vf ?5=mS(/3aMp_}d# hpjj*mr>o&8:@%fz^¾VH tKH5DN&8w9*qCaEaѿ]}ک㻞缸:9o,F|V*vc7dq/;䳬\;R$1 ;uib'ySkfkzxɁ9ď>E?/fDzsy); PhE3<:I Ͱ l=m(<Ĥa ֭O?Cr5dy{ӟdjx%ڌ_7⧊Q P.d_ىMQҐ Io6A F*I>@Ȧ#܁ˢHU s#80d~'~]Mg/v n-M1"p+}' PKf GnEzMN0 i`kݧӾ7&OV=U0ك,@iY CōD.uo~*x^%s.RoA9޿Y3 GaŊU_v~KvA~~vXw*^|-w,B( (К-##MKAa H`,Fco&~w3Apfzڎ/^%Ër8j8ŎxG.,㇝ a [ÏqcŽ&v?wX@v^y𙌥PvepãO?'/KeMal*f_Ųf N a:Z>0:r pᅗ]p b _ܬyX08:;~'}a_9Ɍzct&e)(M d*m! &7}!fPyᆢ(]ZV[ii0P;8#Q3v8By@kr$ҺZXs{ oƦY+Qi%`-[YMmj%mmD>@%Խj(|,V^^Ft*x 8pZ '%7X xau004Lk<쌭g-sWgW[XM@(3j#19U4{`WC^J-~P䔽O?|9eDȔ? c9LqXu uZ06 qcȃH,)1KfɆ(qVa']ǎQ;+򓊅"ˡU݀ Cnx?D*iͽ_Ps$ i|j㯭 3uLmd/MO>n\`<߰- 'r싢q+tMHPS(~OJӵ26E-A(Ex6T$T~=5)4v>- ,+'7JPcK{Ѻ.d`'ߒdj>OߋƫTW_] swuFwNWi3FtƮ"q*~lMĽ9k?{,[yv?VhIhiYd Ci*a1Nz&]PW_ Wж }[onj&'{QcW_u' x1ۼǬ`Z=AvB:S#"gVt"h !ldojQɇ&Kvy1-"+4$r{хBN}wf.uuP$9C#e;h`B*iK"d?V![ @QxPӆS/M63vӧHիVLMMBMM5AssTW}ݧ/ lް ^zGǎB}m7^HfW,!6dPHVuĈ6Rv2x%/"%Dذdu=gR )N,@O+m(%K[_ ~zGT|mٹ1ņPO -Npv*Rc]kvc=tc&R˅kH}2nX/1( `Ӱ)R9CXMULۋu#>4#Pb,5  l2)b'W;*kpUGVb$CY]Lv)큁n5j(,3kzYD%N[675Bc]-(.-_=8gv=rNu}nАֲH{˵o}Ą ~e62\[Ep@;B֡dpdi*QU$*}/;]c2 =g@u姧=VkٹoBQ_@Y%xkV2H$%{- (b1ƁMakD DAI`I;!O<Yvm[qÇ!l BljB Wt9Ejl!o(Ր5qvNX6Utcs׽;Y )3ݸovή?;m-y`Y[&e7d!T\}dt fUl0&Y' ?K[Fxؔ=~pʢmի@ww7)뿲¥iiy5հwspd_ږnCÀ I~@i , aoٸұeN;ľWaif'm&5Qi—_wx0ʼE춵o 2'̳U|;iC H#]bbaLR׼GvG`e`!?rd1,X&Gj$0$ OeS#^P .xoy'ahh0 f/}F6d+!7?5b rt& o ؿ#BCᐛJӷIys)ĿKsG9SrK>pCkZK[6m^IΤ1m2LՕ0>: ::^_ڦueH{ kхJE#L~'RՍ|:()-#a&-a2񹪪6oW.*]UU Ѩ`zhy(tY#6(;c'UXO ;5,oaukvdU  NuM'mŽ ͙Jbs[ِ&#b(ϞnO'x|ЧJ1hydb(X>ݸ1?-+nzM| _ը/P?7k=$̒IeS?EolKʢp a #@kkv 奥zo_+_}H}2{3kS'q,K}cV-bC? ^ۙ'Ǽ6ٞ.zNj$<W]5LB:L,k_O|΃\</b 厞>fԆ0i4چ8dH0 -%9&jؑ `4XT fQ;Ǟ +b؇oX:_ OFۤ3dx>lݠ/qkĿde҉DZy~Μ(D-{_`,[1,Y”I՘>ɉUp xQ(;ye#48uҌ'L?&L2*h矮WX {7¢ڿz߳i PMVCxR2ॏhXY`N M 7qw\7mX)4L0LAYz-7qJUf5J|>[ر 3;!4 34(+Կ,#Vtz ׈Qh[wA@}jdsP/B7G bT:7<:š>sYƫ@^vky\˽z=MHԞ#+D aӯ_2;w>&ѡӹP 3lx)W(X.mm77H-Q>4:{";`#;sUC wodIvDD',0 Ѳ#;^3ld@dfpԟt>mD-rvi *a_I[FWQ_o<޺eu ?fh}.HlQzAc"u_OFyoX+a-@z~Yrx ^}}<FvXd@J4qБkfo [LE>w0bSSҚ 1`03=D!^GOQQ-SZ6Bsߘ ӈB@Hvz{#Leg12u4)6;:ey(*O#s'$ەRL\ǗHI ;Ǻ#jki}/>Fn ﷶL&e@?v2 ?@&Wث*|_LY(8a4wIO>kY6|N) w7<08md{~ʃ6vLGCxCŒq!I *I55f`5zj&ǤIXK(mr7]sfP톑G51hkkKvR5L8-7a;ӏV {kwH[8HNa&;3U٩EH^`ȎfCt0AG@# ލ:MAĘP6Ud|BH%:C]>V;d 9n|lTzR{q-F*9)] pyLɒ̺:wkxB_@7P&O ( 17N[m[7<Ǯ+.CY k9"#n.T>wbeTnfǏYV|Gl4Ʀ躗1ܨZZY3$jH=F5݄C#UJHv$J.XpGURr"ʠ"$3]knf|DAy~G=t'rskhQlv3I'fc_.: KKT#j&׌>-sQ8:bd-ps5Hnxگq8 ?Qo Q!;Cas0Qأ4%á9!đ%ق=wU]Fˉf+b,CiP Q6--M쌠d`hKj\f5HЎ$1 Ӳ4?70F;7ٙS)ٯ茏EcEgxd 4G<-0un?DYEzW]{69hkkkRɤ?zJLovlKqLdFa Ws?=Zn_uBjgUe|?(퀲 &_!v{?琢Ŭ5_grss O J-ysrQ7 Z$d3f̄2$22+nt-_?$ -ֹ.=w Cj--&gv) T+/vdr)rMHвCży 5d'40 7r(uy3JL(h8*; Y3X2 Q#([`wC^dQn ן>mN^+6$?ʂm۶k1aփzrQ0#Ypu]b$Q ٘X蜁lbaBbBGM?eP @` L7=y[ WrXoW o#īO? ʋ2m?/ ڷ\ZZ ǪN@yh-|30"zf׳ 5FjsB]VZ't ѵ{sWT!ck/1~D32+'&4=!;zx0- ;90Gz E 0 d8X@凓N:9)Q- QS]I[?.#{J!}ضu=J~KWVp@GOnjtqdrt?@1}7f oz'GF}Þ0P (w]wo6O{K`B<|\DӭzhŤ.@&x[`?c̱F/T#@gPB H8(BӔ|CG7d$p≰xɱo}o }3 l;dX{jkپ:j==d9N&D'p.;!Ƹk63p"f6dd T~HDv&2O~!dzꜼWB*`<"|cӵw~8t5U9 3+9e.\hnG"YIS+_}''L g3k4GmM?Ԟk`y9rM?|'Q\OjkI P-_2n 2Uy)i)6M7}߸ +ӈ2cbV]v2hEt%$,eN'aѧmqVVTWO&BAa!s:`O~Ͼ|9=iw^ ~P008l 0!Da6N+r$c(A(ػw7fs+;CFUvTY@"AA¨Jcc>0"Nx !WWĉ$/;CuA5E!40ۮ46)Y s>lT]]!ZLӜ0`c/'t lȚw̴Rjz.+g3TbR}) ptHh)tzrM{h튲lF@15% ',TH%Bpǥ? 1s֗߆}Sy=\r.(m(š={{zz {_s?G4o 31`ej< {Wnq=lܙU0̓ǻTm9yPO;zB@AUvcN&!) 2#cܤP7ӇɤxC\AAދ0iHv"-;R]VQ3>S:kȮ@hjrGqXyYSɺZ6McM}U˰ &w%Ld+=>Pt.}:=Z۟? .OdsW"҆е~D?:$Lg@MOI28$C-Q4)F`=W~Qil-tilw2(7ӈ ~V  BZ)/¸_x*?T*ih&IUQY[X?(܌QZIvTt6YtNr󲃦ѲCZ!q/[u7e _)\Q:ʫN;8]Ya >4`OuXN"NG ze*LL zT8KZ(a(x"aoݯm* G 굑>3wOI$JWgdzԫ.;ҪV(4?*% .X #&c!L!ڢjdg>8:hJ&@D~, EGxcɎ4TqN2LJDvT2GXC HKq7A e# _=ox:.V2r<ϟ cOXlH|f[HHXbGY@"Kf` e[~fOLJ> jR\G_s,;0 G#&[M8e5[7Am/%BW9cK %\;z!|[hQi+dXFv0ʃFp_1 CO>6viڊt$Kv!?I}UpI܊dFi,M!a8~@((F@2$ϛ; o_U s`=7CKD[%)p_#)U8s[Ѱ p?(=\>{8WPVg;'?ԿX2DRU4X4%B7 _~ zby-T,Vţ4r`'Sa*C%2[ Yػg7iѿDz9ͬj5\m$Eu - ERIکF)snXs#y|=gu $~$dl+h,DDAb8CAgOVj^7XkC;wI[]/MSU!ONA-&FS0 ;~+0st5才D?mAvOT0GSU$X9I/FdS<$d8e@]#cRI/ ZtSw30HSS6Psle}\IQWgCv`N:iFNc#5V=beՑxm8~L\wޅ$ɹ>*' P? _-bSVۧzݞH?B:; Ƀ)|QbC9wd궛=%k`·zd1H `q(wڽ܋X/=ϸ M1<>˴? Q 7v쨋);CukAHv15/BK.z*;wc*aF\m߷xy3a?m9Yۖ{N~}򌯢8E΀Izfq$;A'/(1OG'?Fu#Zjx+dUoÊgm7s2v J6:ɀWvEgyiIG(`)eCU]~~~[!ZfD%;>&`v EsdfXsPyZ+‘ 9,U6Hv>}p0 {%10rt;dd rMgLb#|Ib  ,W.h EX @C.?Ϝ9^_G#XDSV9b= g( i݉dء4(MPH\D+t-;C%[w򲃲9Y쐈L"??d5qֱ֩=0t*qڲK`_ߝ]] So0UGяs.kLd# <p)uDg>Dzv{bDO1  h?Ͽ$* hK_N#0z5ot1GEuIgaˋ s2z_ȷ"b2OǨn3gеv+%_JyK$obG(}EXԿL?yt R{8M[WAՆ]$*p7 κ.Cv:n_6Ȗtv6&["FFEtY_7̹/s>p5HAl~_;2j֌?pu~2ȇ>y1&|:Wo Q(eUTyr-kY r SϓR8??nF* ghoE@Dk~6ŏ!*nj,HQ l}znx#=1A,yO` C}0Q> w舊Cиe51 J8R of/=Meg}º I5Y樈d3S6ҍ2tƮICz-%P:dPY{p qD]?db lmmc1hh@`ɴ#zo:Jpˆ`KeM?G0B?JGi#B jh>,?rλ#d! ARx"evY- ێI^PgI};pe _ckk,~h*p>/[\Dۈ vC^x-P7DU?uWU'{(RDu"AxWCG3Q6Lm ١RL3GdZZfcHA9+Cǘ[BЭ~~L .v8^K1 c(,|n؏"!6݃Pl)J@%s~PFRz:]m u"RC 27aY[@Q7ì$oaQr=#4nq2)i8fu7w%ɜZ?(t :xr-[77J @<=ē0,m%>*uq77>hd gahv'TqqMvLm+r+Q+wuhQOFJhA\ bCXk Rߴ ~)82GbXd~hR`~D.1@yl ZeDof2ic6j|+3Er.i>dM;;[W9] @Dh16 sP或'ѿ׺2꜁ w%ww(Rmi SZ?֠d `yN8" h9NVN{dp@uU'_U@0')aऺ< #p+; t`Ds`.;~HAҌdgs @Tp9vE︑${) RN ΀,!6b7')B%ѭgN/jSymk6?Vj>CMT Sj_enХfZ`Da+ "QR) ]-UGpҝJv]U^}gMxT+Bm^cr?K`#Q__ޕZO$ߏD#TW1w"Y7dO/'dM3ewlIِYW b/B?(o_ɵ )5bDg:6Px#懲sm|6@V#T#z&ZuXAL8P= ;z` m:rV?e̤j=kk!+;`S!k数c yvh?nщhuJHAaAEJ )(S.g3\t/;mnۆ &;E(*P%t{p0 opN)ݮODQ.!*u+gE\"etK!$NVEI1HT6v50>' q7=J+/B 5oQF(L':73+`Eftzk -$Ƞ|Ϟ=8K_~ ۯDZzRdZDcP3Ob-5O!Y+u> T?A5M:AL?/>GjkʇkO+ȎWQ1c d-7aK3[:(sďQBXӆ2ΐk_cKTX%“NHv2 R$DƟăbuq_3W 0\!f?ص:=gǨIt/d9 ? !nhO \1/L=oxrmX3? w7H>['&[Y?%%PB0!7KG)ݻ;SNÏ'9~vqfD@C`DRLOUs ]'SLjf_LlCx ּ(IqX+^$=ԤtQ g,ȉ_0ߋ:$JGzu~sNZvh?=!œy޾"ZkINdl'͐ Qşth|/ XR` ԿܥG<}O{tn@lvt,O~@@^^{fXF\p[OtOJ{ x}T1:#`544ճfiQFwa-q_HN|gB`ӡlBՇÈ7 ls͜1cV\sysgƟ$0j5zhoH3I`ׄfO:lp8D|IE"V6oԄl`K 9Uhg^~TKj;7xrh??us֬ $3`xwQWYB8`Pw\ #΂w$nL9>)T~LF3m[[]KdX:wz[L/9bs̴ x0ˡzɇHDAGFNyh\ƍ`Μ%;qm촶PM KL(55+Lm)x4}s PlE%7naqvn}|Vğ 0+8j~ 7/gvqŶn=?J((NDS.D­WxMJ4(vr@Q, `ڏaڴiPTT.=y '߫g)^}3.O6 }}8j#m57$U sks @_{0e8}H¬%|;5SSSvlVm[K3vƟi#4P9oѿ "Br @}b)=pWL& tNEbBGM?m\(fꟽUu͢`މKJ̧KvN sD¬hC#D?x L\&L.<.x6i=lT6.1)nqCO[ Y'̷ *{nfϞkt(9n!}+noqFsz:HۻϜ9;d2Xv&cmx✏DˎN-tt4nt@ݡȚ"p\Ң ы,[U%99 % \ رP,O"g}QX;Čt?6P5# ##Pr(Bfr2&O SNNN[߇ .-fv6u:qHϗ”~?)8Я`pe'Vygͪq߾F"FoofsT Jvwu`ʔP\\|Dv⑝:s MOt2(/?ozI.v8"%O4`1as᳼7HK3{m'H7>0QT5GJ]ތP+ʲݟu/ B= 24!P,'tl6,kLX*Q`_R`|'!q'Fy'AW@ٹmho-lْy >K_Zx/`!GVP:A;FvH^vD-SeZ 4vP2rn3dI^vPG̴?=cDDn(D~*̓-a[W$P`j#xs gc} KtM dfs=0bxxf#Wl |4BـyK΂%UPudcMS8mn uJz l ӈƩVT@/>Uy":m)z=h\RZ_,_LU9Р%YE7V\NI}s0ը8Vvh3z"e,LK$9:ꄏ׬1[2Բ3%'BS~%IHvlҩHs1,krpIC~`}2Y}:NvMFD  kP.?# ]]Mht8HC&њ*0 ߪ2i#+ ŭh 'V$+2EF"awôiӡX+ H*;(=$^Z;{ͰU{L(0% ev׫]¨_+ȆS|%kJ~ cgΔwHf VcpHI9aCSA^ I<Ŕ'hoNccإ0t ajqnR% d>$[4]k@ҁFi2Lҧ _qu؏p'<.uc~X-S@usb˘Z3"<i'V`_xNVXFREʡ؅x ";]Q_s6*OlMhStҕ#`[קa?d/rFF2E{ǵ( D&&Sqz((Teh7=ds`y8e -CD Ƃ ׍6@kڐk$#=~ez)ޡO%5%`-^e,ѿM?3L_CJ}zta?Q3ڧ[Cy[q)RGYeΟY3 z`ɏ/;\D` pJRE,sY `F06}}}#F()5S |`͟1z7B[BC+c4">*F=ђ v]]X gg`%h͘Τd45/< WUswsSѸ 5ќlaP6f'Cvv%Mv)Д|ʟgj"HkP+(oFI/A!GJ߼-O٥QlxW4?(EtT\ p?(=yg=Qm氟g=|"TA&?e`Nۍ#= &%/)xV}M_? dgتq0nxtHRۈ=z0}7=W6=Uo9j`YByP0gPqE~' #hMb#i% `S_M{bJ;&@`pFJـQ>2yk`WCN^Ev;]ȎWVne>6ĿfIxydCv`?5X;Flq Fg7&stMӴS.wP@9}0z,.'Emyba- ~iUN k!:ddL;ūcͷJ&i啀v_Hw>-C} صk' FyD{嵗@vu0y ~\'ɹ J!wtț7g/)}=Jբ+MbF -n/3?Y7a[3b?7|8実dAIId,U0D´F )i j}3߅W]wڅpe$7%#;v<6_"fJvM١cgE=N(w$jCygEASF}{!/_=JAI'ƟLE$P2@Q \HNP#ڭҷ oq0 ##@5%)Gex["+ZdgP[>ţ_%V2khuP& .vħ Hۡ!|Gcg+|(\8 dCBmljPS+[: .UnctaFFK?e%UYٕUQ 9c+ cV6٣k&1iaj=r t5d |i’k?+ir"weEDqY9h=b-o6{NA1=BXt3@kّu%%;=m*;$%7FIEF?esDNkL Ekua`0dʆdPX0?'y%|e1Q6)eQi01$Hf/HP[@/伆Ԉ`DKo,x$!_m݊2 >2 9BRE3@@2 fdC]--j݌ר`Q0J VA>Ov%H_bP _G!HRZ?HD mz[41[|0+RI?*LR-MH& t@j$;Xu~֨ρiKNN SS*;n6Td::;"`(/Ω|?-QTg jG_=@d1dPZ~L F&rRau2RA{ RT<T?zVS (Za}}=j1]) Lf>EN?wѿ9 gXd+ WeQ?2`Nm'"R`WsDoc#p•o[PeT-F>: 5q4r-%Bg = R#9@VUڜ(5z>b EP!PAR@b)mo{G58~&p,ᤐD96** m:` "~У}c J/( 2LHv#IU30Htӭ1.RISV7{w|laj]'nF7`"U\Vȟw2Fe`\n؏"յm~[?ojC4o9Il#1He { `"WNʜ|v_A>3Jd#&TT}]M+sc(=1㓰_mϻ\!;"i]c]S$X69'E5s!;7&F^vFZ3Kn&XJG;ʎKE?;=|DSBX"StHLn$WcNikK2NNN@dOB`S=!G{Ug 0pu?|1gWؔe iHD2S7ieFBP-bnq 42yS1#.=^K p)ْӌ(տ깇?]1s0k3.4׮fƸ*##GT{\1}>ԩ~rv#; )nIGx)>%Pv(I=1 KWԷ;>"놷 ZقJ/+Nc0e/3d5AѿF3h)>/!r'srB=NG L"x~(Ҥ(Ч$;Ce59[N)A U_ndZŻ3U,B_QPZ#Fcp~@>=2D?CvhfTg%@7Fɀ(}V*yH)m:XQ'vP#j_>W\FYypWY[[V'@[包PUtA@%|2Q^d'Nϳ=Or ]* wiX狟Y@!NEX@Ŀ82'Ea?noV Xjό9VP L0%qbgd?2v"$^q%k`(#MYW |Vo- i#3b$q߯t:``ۇP^uP1XPt2-JHnv9L#w;*mrDn_^a%bu+OPP=6nf?ЦM\Q (l( :G85r4نAJUIw(0 z(8-~UK"0!'EuHӱ:\ŽϿ@L/xl,砨r-@ot3~8 C>g XFF$ N@2 Ǐw5$bs$y0H np/dWa_o~Ók ڝ!ߟQ.}@u*@ƒx_Ta"aIgdD N;!1bN啶5[_y>O0gAgo TeD𩒝-!dI*M%e1nC"|Pd@q|O_[ѱCELW u ÚѧJF ngHG=$' m_ֹ {]U-qHg|?-@"Bf:[OJ5^s qˢh8=0ԿǘQlGv2 8`(Hͫʋs`:}q)xrnfq%,>dm2TNԂH+hk޶ݵȨi;s s8ecEnQ;_~`;d pH-!aOEpՏ|X"|CQ(>HHD>@G8^Da=Bs/9.C4ԙDƤӲ}m=f:S#Tv2 KVvb9=6j%^1h$]PKv%?{@ 0/̺D;hۚz+D>.9ŖN;E@qHhXHHgA"J׋"M[W ͞]WԾvht\TaCs-?"}RRFƙP^Jv{ZAӼ)ST BٴUr O[Dk-;\1m^P T(h?DCoW'סg@֮(gdX?t@.SwcOH`8mC" {IxI2&vɃSx?6 JC:m}3 I5I$Gv~0Ѽ1y~U 'Y75+ ;+Wh>!l!hpy00ohkX@K^,]5h g|jFN'du`FI}!{sߎ~*`WQgeOn߳ OJgC#N6cf^o_-]qM}:"nR-nyD,4^}"v\/8Şntns&p̽6((&/RFh/QNߠzD]ĮUZ?IGtji^[H',PDAw$"HVY'TF°￀Նi_{G ˡݛ~Heߑl"pxm{vZvÏy~,a,VlH?`GYϽƨ[﵎v9/.QPZ& uc~@s:}_JJݪ\r&)#aڵ^=5^g%lS jK־{[y ր9:Րfw_״+`Cg I IWCX6q loVᚳ 1_zݟ5}=>gU;8'((f=_RdS3m?#f@iJTᣙk~#x۟}us┻'FஸS>uސXOv56B`5h`u]xaCMzvشcW@\>(? 'Cޓ٧@{OG_s%opoȓhpކEvucؿ-Xۇ=IZ/7&,9밊޽"X[iͼaDh@o' Á~ rv^!dA^q)!U{fhFF>7U? v8=/ fM$tFcG]>oFXvﯡj2==bNQVEt?t8o C~?g\)>1_KK \yU/jsagw7-njhXǂzSPH;S'D-~'*}Ta8~GREso>2MMM0m ^s!omz> 8!l(rw+I#5ݎ G6*{{Z}WFNSN&paJe;Lgn)1@^Kl-xFBѿCB m}Y9Y5\;z_4R1OG'-Fu#Zjx!lؘ ɥ eC|׻ nzCcya4/yF0AÆwHk\=ޱh[ }X`Ü o16B_8  k( m&΄O}oŊ(n/ߵ_޺c#l{w0쫄;Q"PU9RA{ D+_ PX9O?,e{?@e2qˇG}O^'^g5!M@,] ;U/|g?Nnޫ}'wxfsAk((؏2 TyrkǃQa(ϟz%}#\ ]]r~]ngR0g`{,|/T]sҧa_}k _N ޭuڷqݸ's*yD'jY]oɰQ^+`u߂OONሑ0`ONwnq)!(rW`78FN%++?JGi#B jh>,?rιx&~w ۶y2Hr81Ofl`!_/%~( U -s㑤=|ޅV@;sG51 `ڲߔmE7C|rۄ8-0qyxG=]LE^r?lY_̼D AeE97G jDAm(6O۔@ F_$ ` /cZP t–> ;3f'ڊ;g\G}NL3ΕԵt srڏ?ƬmC{߁ՈaܥqWIvzJd%:)͆gq ʵ1T/>L7Xk&h߻FVOug zP5Fн3TOOKW){+JHWQlm Ow"C/`Ï@T*^m@)5>C ߍŻ{Ձ_&8KQx﩯 rJzoU^$V_N*Nц=T*8Goƍ3i*8E߲B!l"'/h+?2꜁ 3_E=N3KeEk7rkПK9gɛȴlsP_RFK3b*Ȁ -;(0|"&Z/=稶̅m);ǽW0fqn$Giݹ SfT\5~h?jd@l d!{E hMpu7@{{{BƟ.dq l׳DǞG_5Uz[Šg%@ ӆd~YWf Yc R=V팶M/L=le${+|x 1"}E33o|ٔw_<}~ÆT%29q>ɛN4Fw۽uzspy洡ߢ eldc8*{NAQ*Bs GBv~\>;}o-F{m/{~Z`l!.Uhnx6rPKKni]fmɦ^UA}۩{-YP:yv4O&Qhޕ=M{>p249=fgq#ez0dP?CA<wJU&u(6yr.Ga0-~Ct'`²GѯYf |{f;R>Rz_*#P%=+_~ԧ/܋n,LćbK#X>m![ 2MUO9ǩ6r,sM01ڗ$58hm>I>ԙ$+ E \{g&'뀺- J+3I~ b_D${DvA_3? w:)8hf  ;  aA{S~/v3)$-8)Zp"T}\⭸|0u٥P xgw5ވQe鉣*s4@`,Y+T:|.y/;G O7, /K&Xܨ= hDUz؏S?]Ғ5<(uUS&b5Aj'\7M׆KoǩܡvxE*&:9_;QI]c oѧC`0\:eMи9>740C#{_X2_aBc8>yt,.Mz$#N=vQ. d1h8`@!AD"aF~ PLaIp @5؝ހh??x6a?5߸vݺE}j Jоw߻uOV>m,N(c'KE,S}2쀠ɍCANK~q[Ql 'TZߏ5ȎKQ}50bdÒǚnPZ1,@uςBWv? ŧdr; z< q議"+qD02OXÆwa/ilnىEAujFT9uvǕB=뜫Sb1_Ӎ.̸ي*'@qUMX`OGBP*>xzG'l 8 8k'p9+n`/k:jYkVe)!ԿXA7]"[# ) _Q?{ PyƒQw [GM؟~p0G2V~߽_agZoqd[a±g'?q%S^rWF8ʦ̇zok^Ղ>vq;ӹN1uXVQ la."3sYBN&CaOZBe52gDX?|x~aC:ހ 4WT6TF=WX۰m4O"a-S;IRD Ȓ0Q`FM!4QtN8oYwr8:Bc QN'WB~@3lKG QSuxwaߙ3gE͹{}( $FAfkaҙ7a%޶uLQ~ ^FQRē]:_Ձ}E^us2wEGc?+iqR+Vkcxѝ=9>Qf?tC2]8J4Ɛ[<ƿ_Ïb^}vsR""P اxIOG~CϹ7C2]UЫ xF/)IN gCn}'ſ吉#2|?]T6ӓTqg?AMO? z阶{qxGqM"^ ,ֳnxOߩsuxߤ=l)]Av7n)ڧxnm+ҧNW߫ZX #92t(t?|ψ ?؛(@bfM $$ƴi?=6s/>s oR60o?G+ۘ]2|::Z2=/yMa:l{bvǥe@>)߱*,xt/\_bH N',/Xқ&W*m~fM2~ŒHo…p7 hF_'".@%"noTz*k}6)q&zk6\=\u'cWpL%$kp5т?婺%jK֓: M K8t_UFoCZɏ,h79+/*"~{vcٲepuCuu|xo$o,~ yr6X4K@`Xl4Q^c#AFvpL]`-729Ej T;nC@4n]D]TAy'[ʎ ďj@“}]+" HE;{;[kVW^Gެ,x[^ע KAibk*𫡲(MHJB.μFՇOj_h+|Bp: S".^"?Y?vohV/YY?LœcPvOTG>}`һ!;;[xO{ W~qB5fDX[ /:}Ю}u<FBu|"=E{Pݚ=,nyZ_ػj>;cg5R BsnOާ; FBãyfVCf?@zl2GcRg*K[ yq|ڹsgOyo'QJ)%gY_^%juEHo8~EARxۖ= qAg9\`fսFݻ?}1*xf?HVW}(]Oӟw Pr?̗.ю6m Z  Ge; K>:;}\\W@jZ",hx6VU@/'RD#Nv(@$?' K]5Mѵ18PÓ Gk23~̈́ĿaÆc>Qm' ;vOEW\RJ/h2wy;m6$WWw50@a??d-;S'?һ׻!a_Ι^t? e;%'sS<*X~=/>.]8j=Gr`.%P_.@6?uN [̩Mf4H4mѝ_x_f_a$Gnaq #&[r2r8lЌWV`jd `]vqr?'?J0GB ~Goj?jF_x ~\wgCaW_~%%%m_Ώ2.O3߯ k# KF0768dx*gєEgxT>@f{k`J(_ {klU uh ]na!HFV\ɻV(h {u\v!uy~mɱU.C"1lN}Bc]p9[oMK. \|.-$/NFbEb@'=hѢEĀ,2D%M^)=g}n}iH?->Se\ϋm LÈ}l+X=egd6.X ?I?E ~)'y ɾ_ Gd2AVӛU#z0n lxܼ(?xzȟ2 _N-~E^[|PRE-EϫvMY/ u Ɓv6bO} `U?["NxF1Og5#@>H3<6X/78-(U,;xGWmةfehCa0 oQ"spR z#zk*pߣw$kv870mn#[vkJ\SOkvN7v Sgy5WI^qFPvXf>r%_4㌫ !eB*-;άvݱHd @.沾T05n[=xT^UA0φT@ePKKeq` V,q_?հ W?:Хj=tOu;j{s> q:OB!.xdNO> e/F̫k:fhIo X! <>=@2PwlAkd!ۖ'I<6h a6v,S ˽"شu'uxed]5F6D[tJ"N0(??TU!S¯_r0 f9d洴6AAMO~@e3=9AWoj^?m~' {7a>/5L oh4"M5 :aIYzo^^}3˥fp@&-VXNEՆr-ʢֽ0cܻ+|uïq `ͷoOܪs#+uhf?ӥ׉7_kQ}K7Ç\pN@Zgʑ#`Ӿ׾>nΚMƭ̹/CFVvttJq{V[31wyJoֶ󦙾XNEeojw  `BuE德]+Jᗗ?>qa8EH|׫-yFC.3~U*7xrU!(Ug-~f?xߞYQQ=V *p`G3K7 jEۗ! ܇U{sTNeչDlW^Ε xN([Aw|ZlHQx_<&'ӓP4: '> KߏVЬcwoJ^nZZuqx8u 5z'd@aɇ)\p}ݤ 7<M K8t_@no.eȂ?z eU"i|pՕW±t}%G;ҍ/'uv-;~XSd׌}P< xy_Usǯ.߭fe'^tmAo vgIExej >8|{< dh/j@m\D=@mO4viÙ}pO vG%xc9mJ1Wxk܎_ޯ_Zսt}\{]`f'`v[ `@GTqo]A#ѭ~ ak?q9P)CE2|?KÏ&Enn߾&;ɢhHo~= U*8Ne{.ͅTve-]MڎSySi=b$agaѤ`G{M?N<54}xqhH]S?`4Aan BM {QFpםBw7@*϶^OrW'QP"Vaxv({Qv܋_c{zmgyױ(w3qx i~ m$w./5OϹN_Qd? b TmT{#4d41!7.f͜w/ ŀNyH\YF§tHl>Į>˲v͂0 Ka+ 2 @Sw+ "iwcGxz6 :y]<6$(۠?i^'{b;'elh_|  #Gh\O_7 #1)=7d/g6mXH{c))fWG5 B7wb!D@_蓐`?a:&'`􆪀a-x矞~`A^aO8CWSbZ:5dyzh4wz-#'@v3uq jr?,DF@x6(Bѧwox7U;ћA"DqjNWv 0MSDl{U[a#-P$01C:Dͼ.)tl'@}jh?mf| + tpLe~^+;7s5Ug+aK}I,}Sc3h~Cnz G*a粙 ?zhѹYq러 nnos&(߼Nt;uY?͊xXq8m|z>kY X3>丄 p|dF63a cl/O}O/qʹ0o?b7QwGUuBqo> õ:G2f]LHo>C7 qsucϰw|9r<̋4@Fvchsp:b*FoVpG *jb@[MlC# ̟=[o:s,d*ǐnk<੆924S^d\еُ]@c܈ ~Poe$Ho'.K6'XT^':ܖm?wK17K}Dyo͗ =:|>uR ͫ PkCLj޾3v99ZF g*S/D?/_q%|B|qz rhvtp "ve>( ϟ!tO; INC߭Q/"tP wAmSPNUh߲hn?s|_@ ]HNWTj_77qSXŗaW;5PTN@ ~Goj?9ʵ=!p7ICk/I is?fQ !}x*$tuu$?IH{ui#R3;H5K?oZ:#~25SZ؆nRZu`Zҥc"g^wUVɆAo}'O}d6э'?;ul0uV.%`UC:VSHP%},c"K!0OYgFz"q5i067Y턘qg\v6.cg/vԌn dddD"|$2w \:S~z H9֝I  A?\n>HɺNë^|1.=;;[1:C=o>лOo-jIIaಿe;cW-_ @0_j5Sl,h^~';ψk #ZO y>̦GG{ j t}Se}Fe䳗 $` i? q/Une|'Bv> TDȆ~rGmZu˯2;7N8xhQ-[(/[m CfNn>y;ʿ]giӦ/m3^?7~]B:ɾnPHkCI?$-'пOF=>L>2LMHztkD_v_"4КVVש~Ĩ +8 4/ 'ih $ܺÑ{6GRAFlt-h<[a#蔟Y>}lu›wsn=Rq9… aƌOXjULmIf?()l'"A/*={;'nz5+@F DBCLB5zt3D(DE4HF1Q-C +M IDцqs rZ{oT)il׶mJ#2,S>P&.-5 'SK`y߁W5(wMz"ݾ} ?4fΚ _/?G6}?3߂:Ok- вP^Bjnޢ}oeV'{V1Hd=fkY9Y=>4  a՗Q$ 5:^L ]W@/"Z|e>_"3ѿqF5 ^rIu)y-Z1u}:ul C1gB/z]HL5k̞]x\G,/XO~PJY/ls¥ha{jÆzm[`qdfI>z~*Bf>Bfj#8ZRtQ)ͽpC*ׯ_3gR U~ݫ?pڵK POʣWcO#ðCMw|t%OV^LlXrUKZȂSϺjA= ]+]S6f?'vb2ַD(7ت4Чߣ5}ݯhD-I0uӞ:-K"/_u V~]O  2n72bCtGP! 4TS=3f3`ڴԐh,[L͕c\@fsrrE~>ݧ X}ֹ1$/,֒pDO:c5n 8O^e˗Ô)SUK"XhF: \ ?6mڤ>yg a`bqL[xߓO:)a}9!;PLYz'Pp`ݮ^sۦM5Y;1y~-;jHv#˘ZP/3P9[ V y3`ײYJ&K ȅ< JV5?hCN:{\#=)S{3g܈ hyX{um`A>H}QPPC VC`A0`",|='Y6۝ïo⟟ ׂ\V<s]?ж}G@%řp>?[,~A'~srY=}Uw^}\toP(!3Bg,i "^ EÇ+テFGy_̙c6E*=˕s4E}p!x0?*Tpcs'myfЈt_Bfj'}R*d 0~. ,c#9|%=$Of tg@ CV6d/ T'QqǠAnaxGc|/|S4y@tXkbd癪c%^nۦ ǁ+[oj฾}8gO޽'W,6F+S9 8Bs.إK:kx1^1G/;aA\$`7# H [ 垕 &1@0>Q㽏oᣏ?V륱z^j;p3\j=u@;vΝ;AVjdQ"h([n֬TVVBuM T=^'55r8xP_;\|axe&Jsl7Q&QcjʓƂ?*bB,DN+l?pR(Nj|]UOF% dO@ a24T5Yuq|4hː,8i,EIUdhToK9J׈|#+z+А ,i'.n< sumD+0ӽ{w$FhYXEEEТ7χM@&MyfZ2VUAo>8֭[U52ޞ={T`#crBn[z2vU=_^<9bgq?Tmv ' ؼ7d*!Ǥθus%zv ~]$Dχs#6! MxuP6e&_ 06w1l,T>47WCr&7j@Ktb x7ىH '=S=d9pm51jHo1z+ VBa˰fvQPs ~`#ksraР=p~O/@tM ?@bO Cq%o(P U_CN50iH} |WTIuE&-[>^lR- *.yh2K;2b#`Zr(H֭'p?%z&Mz/mà9= F _+u)Y"Ǣ?W!}e8Zҷv`Yi_+۟VGz.stȱo_ \Ar!"@T$D>X[_M7]rH3!` ~`tQlx{nW@]EC &NRi`6J:w?#识o?nP9?[}QU+& pkUC[]{<E%n;`C?pYӦoϪ[vXGCZxH_/뜉!7{e3,r.G,հL2_r;@Ќ $dT16TQo߂ͳv1v.PtA 5ybE1!HB"`** ̛ cWj.e}з8G}Pfk!~ V͇zscLF \n+hk:t`Qlܸ1 <W@_y ;#()T6(#ɆWk51 ʭ&O!@vv6du8Y#:1'<akQ7e F=4^[|6$1d„92 /K1w>B`+&<]K`y?^ XxqJkVT2q?lX*޹V3!6Cm0.Y̝6CC]`KۅE^O8>`}пzQ3JEސ_X3@_"Q _ț^0ĿSoxp6MU٨H#ةz*l,90<_dc}dw(î_Ka |WW=⍣pEEn-{ɔ:ĽnzxcxSo֬)9 N=5jWM!+=VFu2. 4H}R'+WBR/gy7^H~駞Fz!)u{4+OogĿ٫)H? 7PV7W( +[ 1hc:e`@/?* Sm{TKz9_:PWar {V-yMKp| BVuCć+6|㱋\JY C6w3gLzp)V;E>ED'?(ߴ2u?Zd9qi$~q{-nWZZ ~zBği)cIW@DwAmٺP_Tݰ(@9vp1N:"y01aYJv>(9 Ї^{uUY-F&Mѣς1cPr{\XEtʀ+{8M7xptS@HU'&_9i8\7?o-;A_7Q&e;l <"v#t#E,kU'bJ%H@XwЄ@Ơ 9#D4?N}ݽb.}AY ضC$x٬FԉPũ-q,[\ xx\Dv{ l-[q·s9Gͯ0kw#v//ؔid{/:"x Z S4)STv. xX1`vO|NB%FÏdsΤ̐u-/KF^RJ۷oS'tRT(RiQ{~z6WFDZm ]xG\k;F}fÆ xRXx ,u9\Re+&F;O3e k!( y E†_nE{r|j>(?xzȟ2 N= ksQLz{.k{R Hi%Uz~u@$oa񤧁p,N?'@l?Xu>""0~3Lz 0HǞǫd\'}\9鞶?=u T:^ d5n%q.5Ǻ gvRSO;dѤ5Nf`)dJ'Vi%B=~>b05zchêކ5_ɔ NO~N-\2|qQማΫn#]NF `4 v8p^{ xג ]cؗQ1qIߗ6mڨLФG Ppyyj-}^W{{r0/d!?Tzճ`5~M!;h']3dHF`s'%G #GG*`CoK 0+ Ҷ7tcNA~'yvJ<["p />sBW\^zǓ Mb'fiqznUbY[VTt F|pfx0mH_op[` wv0u.0Hu._|nxO L7rnTs^}vhZ@A{sYq{V6rć%}_ǡZ \}5;ϥF]E Ԏa?@xz i $BMf?:]^?K+Yw\dN?҈X!jRzrl*Y?#*'^6h /&ukM`OmYMOX!;KKt;`F HG3v,}V?0uyGF0@vf /iZ F҇>hH-!< sMoŽksu 'J%1%3)?-Ĵ®}aK/Qpo|?.V]?فxF[]kxtz4`e6au`E^#/3t>Ӌye}}N0}'~̲.~bd X?C*,޽8дU{yx7|/  nNʬHЄͷO=K-KzߥKp"A?SC 4o #K<ߧtpmc!Rkld@۵eCZ oxlش'x3\? dY{ز>g}irl0?sZ?y07Z~)Ox>7~AUX?aU Gj*`"kڥ@\Y& }bוo4,rf:M$^ :]\pn駟5k&|pM7…S;*((lYEM8G)J^˶imF[6.j `:Ͽ25?5OuDYkšۃ|_~kK o^K_TnJjcIqHXkk7rWr%΀av i۶gX4)4sJ@Y6a]C2DQAJ|yK'g~?S(VMH10 Ǚ a|ej`S<dcFpOANO3YG\4~(pzηB@:xAx/&O'W{,wW_u54o,`S*ږ|K{5'׾QHh33~uӍ~]q ⟟%q>TRk˶[~m#$N{Q)2;-,=iK>CWf>D1dDM4&/."(ݿn,y8ZyF?F @bjeNVnS};L\כd8ͅN^#?+#G;n :̨dyc^OK{u5é>#LТs!hZXy@Z)oJZɏ,h7r|#kҾt!"t`Ay/c_%KN$T b.OץCX[n'K 1xĄOÉW|ʴzI70Ett(CNahzXw<-KGzn`y7qܞ4?yLJigspݛnQ6$_77~Be`'meH?D*Gco1K3-h]U,u,S[0"J mHh^ R .T.5h6*lx )UyNثϹ7Pm2WNkXDN~$O~:`$noH$8?#~V=[YuRӽnM#`˜o D7@CL?g5Q,iXJ@Eq@J]eWjb5bin7zi+_O¢Et;~$D ЉÕ_+'jK#=\3yT4EEm??TK x0殭5+m9v e=~OMē=N;cr. GqDR2~jɟ*CZaV'||s/PSyPض8ROh_&0G[_z8 DЌP r٢0E \4с:eD D̙=o=Qq.x229o< K?|.t?rt1*&Lig-_UuG+a0rSOIp<8^lge0Ӛfq9gߧ6II>z>sazbEKd6p^?,)?5#ڨ<bk&?/0fui7\sD>3T@k U/tL "=W> 28̞c4X2t1ȶZ~DoZ |ʷa!+Qti.0CNr:@+pDy q=$}`Wo)`3x#sg T*TTTaKo&\vcFȤZZh!LGyp+>p_"xE4R5I7&L~\s5w=СC潌kvN++G)z,+ W,F'lD ɱwX5xQ-YRs ^#y5/C^gegWkq+e}2/**5a5}7  ˬl@\ ,׸y!~hcϺ1DR"Q(:zD?ޖ?4/mD7Pd>cǎ^4^aQzgD{9xz4TD؅qaH@V`4nىbBZxH_A~:g_"w WsT0hCuSEtB`깇 C8hDԮ|{^ylB0\i 9 ?NjD5t22tՐ$t2 \snHNx8c^?WF_Q߉ ")w7 ?AV#!x .B^ֲO^SO$'+<Ľh# cQ6&#ɆWk51 / ѭ&O!ڙ\ƛ9C6ŘV\_~֕@g=Z[_@F0Q>ڪJ4+؈d;G7Й  7~ѭ P |`jl+dh T  <~vrp mJ:Wm"Gߍ80 nY= yG/D?/F&`[YQ&^BYbM~@  J' K<̱Q&?lgsyB;ѵsyQ@r$ }/5m- MH_ו!k 2{m"`;4\m@Z͙u `%K zl_7yT/z פcpќK^߮A|Gᬳd]]x/Ձ#K}+٨qGrvb:QZKZmsنG~3߯{<9&eS?FMt#͈Y:\g^0+ {Y՗LY`=O\v޴zFfzHs4k6`T ZW; pEAaC3QNW6i꣉QmDcL$jD1%닣 0'2-S`@,`11)HW[/Do?~x/? HKz"G/gWOցn좎b_xי{~xc0\1?7mM/ZQA؃JBG>C 5_Q 6F,`u IZ%AC{2v3ާׅ~;tcR)ڒ>}Cc sA_;ܦg86 &<0|QF#pB') 'yo>~].© ][f?ϡ_F=]O~@nn@H {H\q։z=#mH"Sϗ3Ⱥ ^jQ'ͰqgkϚ?2tKaX^>C2HfzDp hd6|)do%չ}b( pu&LzW]u%$Woo?{2tkU3y E#mFpС1޿ WcPs5Hi7ח0Q-}Ld6u>u 6ⴆdDid!Ҿ>6xg,:ǜr)dcE?&| x>.toPX]?D᪢H$3 'AzoO>jjK0W;|O1Su]u/0ŻaiuFZEO= rԩӟBG3GCn{ok(((Q_o dWOj=|}I@kmTIIxtnҽjA!5*6}@so܂LRjM}f0p? D?m(Á:/id-"^~Km׮v-hM6Edfz{Ru((识^QpOʉHⵝȥhm(y$PGFKGc&y0 @ !3(AԳ%5o?4s:2>YZQ@)2Օy0+Ϡͨ ɎM2b!Geosn’ކ1)" GB% մ|dTR#JlB4颲@dLD"u|=Dus3^x睉p!u~{r34nLDh%L([~ îI0D?Fh|^7ƛzũt arAV66Ŀj _+C{{r1/d!?T) TA I6?q1gee0Y[M/w~ ;OWȐذLUOܨ-zԀD=L`+j =^ mU9!̐r< 4t$di"GA,oRyFur sN3L ?K>d&sbKpt??=qt<^n">_] G~::d*SL=E!XJ#-1;(P&" %G)IFg>ȨԼ`,Obxdef$>ܾ`sWwc1ex"K2ժ#cLFV#*Oդ yXǟxqR ~JرG#2O ju{gO*]#'1>:!d7͇n#/NOW#nu@XɲE?%EZzfT~ę ȥcHv@F[)PJ]z 'a`>:ug<} -_?n|;#Qtjk0S`%& Ŀ3ZçVӣ/AN2 K#i$z#vsF(HFuվu7M ]V)-KE sm4W }%i2 .@TDU)4f $uʁEWeYI t{ј}-P,#e  =leK@QQQʄΣmL!t[f<{ba0duNdxy^Ss"YiހNJy'/tOu;j{r6Z:OB!.}'k-yMAkqd) p/I0*_Yd9m޷p`J*n=a=-C/@^:{(`:yM  9FbǠ&̰7@u5ۈjk9]O'qQi? #1GjY$6*@YYSdA YHF^q2Ay#2Ƶo1?Y?oN<ĸx9Z\"8ü74:|2z) aGwݱ[Nمc>ُSCz*t_:yُlzT_}gĿuZUk5i9.>v{ijZ/1`ڿi%l^1Wku׀Lr*ЬJ0M= ÀѐU/J*!T(çzw}fHp@Y# jʇ2"-yT'2 ֲf$T w\^Ä'{ "I36U\R s΅{V\e;O~1)"A^k.V }:V|ǜq A^FW"!"rm%=yDǗ>?IDf~o Б CU+5mK585KGG'R7=^(V3`s7j `6l{!Z4:tz:@%R0.-*h޶ tQ<}OV;i-߮iU/gD70?Oy! SȜ̈́Bɞ;$Ho+=\nMt ֑T`޽?}Ie-^ |j7CߩNLau?~켍.>n~wwJ3A'D"dfe;Yr?ڀeAӖ]9Vϯ^攮eȂ?zA\x<+5N# >cIwrؾؽTS0&?NO Pgt1\^-]G®}4r".#U$Wu.~nU9GiJJHB(?M0zaO4A dl08a 66 Aw6Lw{ i&LTWWWWwwOyeA>T^r._Ie?q= (KUwWO!x"T~Õ0cE{k#:j1,799S[n&LF&Eه H~w($V͂KZZ*~礼r-1LA=9_W.-eӬWLʷMOb~[ӛOO!hnSf#q?ቀ_ǒm:!(",cv+EO ;)kFauQ>y,~\8Q?Qzg* XzZX|Y ;*PfC }sjb ,wBAOb 9w*~ʺZ ' /׎ST {4v+^v˰<z"A<|9kng{rnqv5/ w|p-Ex;u Y9AGALC#>>ܳSڇ-:HȔg*)K9 ݰv#OR4@)o{9^DL 歺ri Da ⬞&3P @EJ);@Bl5LʂȌ`PN>Kȥ0z/BCqjvllqp?^C}QG>Qw*`ܲG_|"t}<;]y &HЪXQ $Kf~tk¿/Td%rK!ǡ O*'C;U_L_ڻ?;v?*$x!uѶ`>I!7}/asϘ,J-j+V,AO d|ڑ ɘ?U |.d1 ͎<1C_4"a 7?{{TNj0`8R&#Oag>|],\=<ԳOq?tO|u5>3s+,+,A@@X|ۡVw&/γ&?V+/0Ͷf;y@FG) h 8fB#G?#k׮!yYm} |-=wwT&[7}:8ӏi Nc-y{UW(ȯ`z[Krڹy:XL/E-2Ŀpo%T mLAs, L>""Od8C%쬽="߃ɽ5O[v ,0mG&G׾ʚ17~0 ` cu0ĒƑ] p% DWx`Fm #B?"9aw-N Om@jlt€OGi0Tw|p ($S&nw p؝#ts{NDG Dl .~V\j4#W+ wXTC98 I+XX[ Y')2ZU:DDTX)P!_0y~ *)3f3}ϩ6^ի 4h$=bWUg?'Nwh"It/6}?߀R +\,UL,Ͳ/b=?ۘxĿI7p .}R4d|,Em <L&e<>3m^RXxKk$~A^6Z*iGo>?%Kֳ$!j0AADֿF/q2l!,ʟzTz6_Uu(@{O=QF]O;v]أfX㿇ǾYHk wnc3\~C Df}¿?3,!%3BaoEU_.U ckz6Y$ҧK(~>~sV\WSg gU&2hS`<"Wf _1 t?l w/JPP1AO28>?䀝 F50O.N*/%0OY;d>cds3 H X%}T[O'ZZe>v,?+v/Ёw+">U9|:6{7/H)Ϝ f?}ˆ.SʰW k\/ȓǝ᪷>7s̿FXN UGr"mN}gG!2:@A$Nk@UmnJII`_<(f2\?IBZ?eDt{5QSEZS^5*Pnґ.{_5puiK.?e8 npWT߿ygӨPOHI}(to!a }/W@%mzgϵ^ii_*Ǥ՟89I0wK`x:P95#aю17 aƎkG\-w?ڡ<#I[=/E9~PIxɑiS_$<1 E~%_4<_?vS~;ˢknkY˝D:Z(p,o|θ8PDv|}]ҿ'k yO[)Ӽ\2 Z7{* ^ -ZgҊHppg{y'"D`՛>r72@0SR˟o+W.w%4&!!{f?lsI*33

    @&t:͚d2ɚ)4U(R]]]c~#9RaBnjjnd2|>b1m8555!ILkkkydd/VT$|>x[_Aظ+,"4)Lj*'|1rʸ0 P}}=-_CCCȶmbdYƵ@mm-iNR*UT SR4m۶3gh>O2L4RJ0s ,! @gf$3 iD],R*%BLK+a8X Bj`%̰Rh޽bddDd2LRaYHRi̬+4&+l "#"]f2sRʔ8i]SJyY)311ᎏ{LyT*%>s\RF)U$"4f֔R$CyiZuݴbZ)w @/k '$3'( JH)'ǙBɞ={.R;KU)5g(j4M !BJF)ef+B^! `f(`'9&_8Z =[$X90>f5y2SJ5 ƔRDFR-DTT( cЈHVܸ3̜[SFh@3Q :38T&IW*UT|!EQDT*UuRf"fn'vmMM3B466fAMM l&۶aY,R1==ͩT ߯\םh2) f!~"0̙ @+ kS3> ¶ld6a0 uqd2iRhRs@Υ7 `@)# Tr!i޽d2~)e =[BDKtXHDMDJ0t]Z[[E[[-\,˚a``===R)BL!Gd,r/!>q.0GCNqP0 T__z B0 c޲D)as?D=YfNr 3pZyޤB0RrK,dFܐ6C4&.\q {{{iȹ>^a/PE;PJSB)嘦iIUeXJTi׮]Z(2x@@Jr+,ɯ 6 g%Xah֭W&)eׇq+$M'Ij ЃlooMMMtJ4;G)$%|V)5iuXKU)GDi5\#׬YC=(ͯ7==]P4=ݼ{$"sQZ[[\`l6Ǐ#Gl6ݥ "ϻrO2q)e8LLL$ڪʰJ*eD޽{eWW@+a|j*mݺuT+sabbxLMM! &lܸK.-+ؙ3gjrr ~'(A7n9166DdL>`pֱd\~&#Gرc=zRc:fnZH)UVik׮-k W_Eoo/NjG"@mm- QWWp87珉y8v9r˻$=<$da OH$Tٟ5M+e @ EvZ{lGG:`X2%DtLqF1d>G>4UT!EQtRq뭞-B&U̼ 3-[ɉ'/W^A__߼lhh͛qb͚5 3ĉ/+u AE9Kedd'OĩSpi>}x|޲ĶmYׯƍ efY馛ߙ3g3ϼqÆ p 7`ƍ9ɓ'g)l""Hpi:uxLNN[H)j*lذ7oƢEf]OxTOOOr4'Bt!~u=رC]PUTy[1"$"ڽ{`n5 cRj-E[[27lؠ_~yqY|Ł.ؾ};m6˚bسg'II}o}xsĉl2uV|#A[[۬kNrA33x U~`7BAiB&a@eppqdFL]}:>+ۿ:vXu^fbW82XSSS8SJ n;wA- R _ 2uk%8vڅgs߉UZw-njpba^_}^*YݩS/|g\tjjAԴ.ݺv F]^V8<ɣ0u项Wۇ>1 {oFz'yYQw]A xMˤ?q/ctT>(FGGOxJPfn,ËrGKfuyn?c1uSݯm8owK;wNA. O32i%Y T2"$"ڹsljj h4mb#̵W6ẅ́K/4M+"X߄o7,;@-o|>|_.3 Vס~]v \2S'ԉ#sY`/ݍSN;,Z`?o}k>|\ ˚Gbt>*>ONyuiנw.*!s }].__zzzOg`^ ! cekUTFHD^|~!DbEhrJ0hn۶M&?v@۽_B7\ބp?2#%mAww7nf!yug[]к-[G^Iy.# lXPhFtаF.ÿ(ybf:u z+`X +1d2d2͛*U!7,~ivHjh1rIo]mp1|k_+;Y,5/ ,fиFx4Kٳhnnѣ%m VSKˍj@㵿Ll`,XMy/L5z U3m)vICydmqQK%J4Ϳveg.̆lD~bXb  Q>SJt]K)=曫ʰJw03D"24MkB,EN.GnXjO%݄֭U3h+#57kđWJ.cϞ=8qDٗ_-E$%7l0̲\_Ioرc%m͆fD>[^Yeڛ0y R+v."r0žwqm4MX^JRLDJNMH)ǔR~?YTySc9rbfv 3+ZɫkH۰aCq]G,V:Yִ. KO|1 +~AOx[NzWUռ7>WCL Xx7mVr?y!$DB!}LFTwPTF5k׊1k\A̲x Ζ{WX>_rBel٭#OB4]w u]+IhKFe]'O,JFah4ZTy@4p8,u]7CDT744ȋYKQy o`!5gjn2/5 @aQtH\N~WԞ4 >{$Μ{97fn]oE`ŚinL;4{_ 710$T$56ԄAھw2M7U^>@CC HH7tZD""RH$"KJYy^3ZZ]K|e)]M4p[5t'~tiT%;19uih&{'w߂)7#PQ<>0ݗ6 ·mmsf#HWtJ9#TFsQ_u~fJP(D3>"R8²,P>yOUTFb1aY&= QPQ.pСR h nS'bY%^jm׼,Br=3z*-~,%x>ORYDh|9:#XQr{>^FW63BTTy#LFx+jjQvibrkV_T '%Oڛ*n{9qb8O_ݰ.T9~LU9SȰtuE.'~<傈[+ @2f}>@u*Uަh4J:ُ577B-'O"Ht\UyPE;gr7Lv>vK^0~othcC/`V =Y IDAT bk۷xIv1F_im/98f&2/?H#"]̻NTEرB4M)e-A 8I>|=. a/cϖ_l>яb%2#Vi~.̭>?\TNAf|@/eJzGjv̫Azx42[g&{4Y`B+y**UN:wpG2O²^98X3jmɹz |^ ;)%OFgU/0~f)}qom+;6;~'*gfVԴ^A66ZYC"2 fuK2/ D$5M˗Jw.XED$z~ E eY "xVCIgYrnƍ(l4 v~YmgʧJ+x1䱃PlrJl LODM+n+L (T(DJR)%JJw0I.u9EXl\N'hӅ gNAZEqVHz*(܅dYw޾˅\J+3hf.G ߤi9OUR坁d2I "yD^*Ms(ɹpS%/w7YrG\ X)d'+j);",Fx^ML eY|UJw0]ծR LufjTg.=ǫH (ٻҍ)Q3TVsD|L(BvOf*U3)87̿ΦWg';uQUUC;|D"LࢼWhBUWRJ)&"%^!uBPLd9oVfrF-)X xi*UTUCL&î*!retK^EX^Y^}Uſ,~`7AY40*ۖp"Mߊn$-ELJ?YqdVv (++O>g_US;"h4*Ѩloo7jY@DyRf^'y&u!6xy\׍dܑٕN[}>_Ԕ?N[3^߉f摑NӮipB4ylذ_*uX/OFR㣟OV {}%MXͱD+?hs5_-Vco?Ҝ¨k@ÖjP^_";wR$"% 8lFyu]_`R:"oBx 'kY.\zԔ?vҢhU!^e4vO)R&LT~ m7l؀f ͎U)z=6'L=nd'cj|~VhL1;"2GVJ`ZVFzd!dF tFk睋K*<^q]n@C LLˤ@BBi5ll fڗ<#?%H`!;hƨbqD{{{پ}B5檠;SR 8 d2ir[[ !=ЬNtay)Cuk7+y_~I}\ ix)|K#g~Tv!OCQ׀w5]GË0_N! @Fa$ݻwvU|R/Dh4|4`uz@'0ܡZLD-(>"2,RJDf$ňh* !mێb޽{7|w#ܓmWWt$FS)ΡCƶmPWWWxi+McߞWe7o=xճ-o!I))Nm۶NsNk.Sooo7ᰕN-4k"gF]vY޽{eխS$Igv-a5ȹ67:t-Ffm@4hɯ).PZ!ff^ODST0@OOF%ߧ+\;hTHz7.RJ !?66 p{ŷY9S1ªO[?*dqPNi[nţ>{nֵNװ䪍땬ofws RJcdd K)L$RuMJK))?/ѲU|_G?+vychߏ^fsD"1L)/T.r4o J3uv!r!F!&MӶ :"FM [y#kR555M===kW6 麮) v]wڐ|uuuwܡm۶t]3u[l{l3s#t53p].HXp=-¼{#JD4ϊ{W, g>+xDzOWMM`\ ƍO~DxfZ)71gl366/~Xl;ϛԉ.%e3x!S(13KD R*B (m"`D!@h y4&''Gp3S`ΝޮI)kQӴŞ-/C@͆ ?22_~gΜӧ ,Xttt`ӦM;gΜ1{9n!!if+l6tvvʠcΝ{n>mݹsg52 0KIU4uVX a": DDA5J)_>|Xnoo,<%7`b䅧ѸulB`E4k0q 4]hO@}9N㵿cԭلPx$̈:1~EdcsWL8wVDPf3+[fG8ݍnL;4/KPe3Qv~S0^/?"j@ʊ@A#oE|(|a)0 e@ @sR)orr'֭[fz"ZHDz`d}ӦME%xA<?23ك_|۷o{^.\H[n^|E a1fq]wu)"JG" Uӫ\62ȑ#^{{{4!,,cø[6m}}}R DԢjVJ|H$FCt -5γ('k)3]^F!˘yD9y-[h\sM ik PJ1]ׇ.\8=l!l733@WWm۶mV}!0l߾}fgɓ'SD4gCd2O{Y0hƵx}>/L"r_H"vkEpC>yx'09z#˲p-ihl6AGaP M}.y~,rA 3pH)uM6559SsM3'H$BMMM422"ɤf" l6+ àd2IaP^f|f0 e۶U>O555.rb1dDKKHqa&9# 2fiu]Y~)Tsl+Wa-D"!lۦX,FeQ<'!DaDrmakдf{ȹDE7|&|DY2؎#kw}7>O$CDJD /U%|hX,Fmߋid|>@&ZNlYTiޱc*(˨`0hTa*|7lۦs ˲fZxR)BL&X,Dr=D]P޽[b10"\^-MMM-[$8W_}*,~!4MC$͛y+СC8vXEVUwF .^Hkx89ӃC8zVLmcٲeXr%V\+V/$>bfg͚5ذa,YRMww7^{5=z>rc馛 _~etupcYYx<^z ===E7::J|>/b\D"{TUU4 F4%)-p饗b@`x뭷_dop˜Ė1] fz}c 70TUšCׇD"D"d2x<Yp8p8t:t:Q[[׏f޽{x .ҩBWWDQ c#^[~}Ak=ܸ ^xwuueD !9o'MB$IJi6(,477ӡ!Qc9wPJ1c9wQJlB]#PJ5ιS)9YXk IDATFc,iZf4M][rw TJ(4Q:,]1&e;jRs.3r44M)M4- krV똱YT4!IRvƘn,.Ƙ=_#QJET{"5c)5(nUUd}]M Z/9'c1ب7bҥ[ P=vJiiF=@))b%Ƙ(ji(*s;C)M盱gEQqeMh4a :/l6+i&jvEQR'Iqj栔JгC!NV.s$OVpeAdEQdݞC4RdYtQLeFoذAۺuk+y,3Ʋ4Fy5!{ GWWh"dɒB ',^)\x7O[M7݄Ekaǎ%jw̒f7Z"?^xi80IyxHR8~8=:µ(2֬Y38cx{nBpgc…4RuK%)uuu!9~gXƓO>Y`2%]]]"'*oU(s49)!9Oy<vs!TU1|c^S9bs9W)2ιsnې/E)ژBHᜧc qBHR'ŪfnulӦM|䡇ڄD"! l6]Q'sfyTUuQJ]A~9"T_ROs6RXmȎ>| =.$ĝV|62%zmiHJyh0pƘ!TdBH1&$9 (J4-xRn;J!zFtڵB__-L:c.Q=(z !^A%n(r1\zI9W ! Z<+bVQ iUU3 iBHtf4MȲxCrQѵn۞H$N'rU*\.=ܑD:ᜃsBuf !IX@R:94MG2LLӴt6y"DQpPJ](5M1׉ܜsk<g u\o$=SMNga*V j~<3%8r"cE[>BH*(D`<$ᜟƼd+ԘaB!d1xy'lvf3v͗fddFˊ)4^~e$I}+cpqQ-~84M `i^u8xE@Ut:X,h4:&xsAOOϔ@sYGh˞|سHh"~:ΓmB_CB'c,2@I ^X5!k^Bovι:IQsB㜳"Ag!IX&(}4M(qI[nUm6olTUug2fRRs^|SouAk+?,Э c34t~" Ow8aYSn[SU՞'V0*'x~#/TW! T 7[3 /Jtr RUG9B/ͿV81/V<= <]$ cޛo0XQQ ϗٴim޼}t<n{5M B5M#zbe 缊R{g=-*s1F›[C> @?dy:S:$A00!UU)!UU6-vXDBBqF@cɒ$e/L5P1beY6DQ,I,A҂ut-'(yP/H$J*ZNZP8ש) zhj'NJb=F[v] $^7 97Szlʟ߄r(3_nӰ<(@ O%IBed2R)R)0@)G 4N#;LcDv*|8PX7tnQEQf5t:m(K*t c5yK@BE) J1V+B3csBi BmpsNm6`_4 ,l6T*t:S$d2@7=Z4IJis^9(<Or4ݡeABlZ(8 }Ɔiv\!rYTnݺ@rxNbƒ%KJ7RrW`!x:Z8 f"AX]]455ǂ '5qPUPK1)ǃJATVVr\v=A@͛'XL9G<(c9̝;̝;Y۷OXTHB̙Sƪ*ԠR<عsglY| mRYf5Y?|#OxWF]]]g3 ۇ{HpP|O)Eg`mmmXf J%r!,Yg 7H$P(TH)~Dt. ^>^~-²eL-t:}ȑ#`A|ӟ.~3w;`5ڞ`S’ׯ \uU#^l%1<3<%ϪUHun`xx1<-W_޽{LIl2uYXn]Յ_~Ps9VXan$nݪ\ѼC/^1⦦&Σ-aرd+++ڊs碭 .,cx'd¼rk^ppd!!'\.m,㜷B^Jmݺub_}pOzp0Vb*?H$y/ϳ6 !x c=X,Á /pD](xg裏+V'> ,Xix饗ݎ 9عs'oߎǏ_M5!8q|1ٶm.2OƆ  tPcF裏g5'ApEka%i lxx"fO'H+W:{{>|xJ7k]t رcx嗹ixD~av-3bcǎ_|k֭ؼy&y>7b#ݻw/GjLӸ[[oMuApe+#ϸ)[n)qq-$&*^z%٩qތ/*"=n׿RRѨg>YmۆK/SӟONarpuסO<Ĕrlh۷n,wj녆xᇱcǎRzo}xFO(\g@˜vH6 8qB6nhv>m6)B||)! z;zBśO@$cböxiܭq,\p2Jn??{7pgtkb1FBgAE+Ŋ]`̄B2Ν;!IRAr\XxqAd2~^xᔲO'Eo;wh{(^7gu\.Wa^$ DP+v}9ren˲]v/.e䯝CN噋B4S__XK4:gi!,9o\.+ i^K55lYE~AOPeb*bXʹogܦ-Q[[(_jYH8m9428Vx8(CQ$4aUjClG:u (o~SPp뭷СC ip%Pg/(U# Hh"YH08F<ÀnǗ%[nw˿F%Ue}mAD‘$~uZz f6XvZ|_[X?я&T 픡͙*%q&x+i/ᕕ7QaSO/P *R=)VYUnJ IDAT_װ|{ԙRWiI9eDzVX;3B]'ָ'%4_XZw9 U(† u6۳:؏%k@lNj AƤ(nBwmojm@`+{Ͷ,TK[2i/v%+#sq;N|, T9AJ;; GjpMs<v1ELJ!9Y7VX`Lat:oP:88z s ?K/49+!닠5Ih&J-[oNN&q{BgKM&Hօ})/Ud~D?lC΅P)XJhTº&h7cgg'n݊uR6F@kQ,w%&MZa]9':rN9q0AOa*pmݣ?BqOB^%4mo.KP!dƍvv1VoIҚwW^] j6nUA8hy݂}j0],T r #GJp'xup3hgWESA覚%^B+O,[oSJ9\WՏ}I-z[WჁSj( {/̙S2o^Lke83 :,L&;vl6)Et!F3bg x 8RXPGVZK`s>'Ȁ2,O Tcp=^JM">!hiig9sн;8Ge79SX<YP(yƠؓc(sN.BBڵk\.qp,^+kjj[wf͕ĦCXOdpy0l@ڼpEAEr9-[1\_Տy4p-BsQ4Nиrpiubq?ǴFG1ϑhNVXJ൤9^Zk.hg}{)99(Lo֨Gаg$*T {A.Ö-[LX'x4Fdu/USFQ))؛D"T*UHL*X^MN$Gp/ )%՞E0\0YCGAoo/$J99qH2G+zedb9)UAkz Le~+hXb#W0gn߾[l1=o= -GpYФHɭF9(PJq5L2qn}$Ž^\Ҵϖ,)AƧš,~ܹwuvQy~63XL[6uk' <4X?EKZGGǔL\T4)?%atnyK2s~򓟘!J{oO_ ƣZ_Q'?1-Hp_i8eL HEEh]i !)|}GLS-0jl;] CN/wZ:=89/FjT׉i*8eW4w]/i^+Ӫ|mWYu kQe/D"{Qrjwޒts1PM$EQ< tK:m…a b۶m%'keb:q'|3ӱP8ls%QvN_]VӍ87s`с[oO=T{vᙦX} &m]%J(/| z[_n8圄 \u}dM'!D\lY7>% (:&zTug"&bboMsU`p6" *X3I:SjI''Mk`XV kF@c'aۤM,iw1NClMi\~"PgΉΜê 1ʵ"lڴ vݞosؠ5-1%U ;ɺWE8)C-sQ,tZՕu]C 1MDZP!*Ւf{;H$.BCêC x%Y1j3Uc` SXê MDJfx7۲hgԕ,K.SdlWY}(ux3-@pvɪ^4YNt%$Zb *,@p V .~ !<;56Hh"ghb lBlİ*M  NaNG +}aOWқkR-SqNFBs^A2ƜR052OFPCϑ󵝖EƬ?, 8xD1ǞA-;ft@" ז1~ߊ&~(-~\_giѼ?-P0p'yX؀ 2)a1dnp bp}ɸk`wҏ7^c]'f2$췜1VU [/ſ4|b`+&eSţõU,pe2Ÿ?8rŗNXk<1d_Wfh~54.W PgR__OBD)uj7q΅ծ鉕.clwwwKOcxP9 ދa-fp=QpM7K.?~Ǐ/5+Wliޞqi"MOPalSn̘qWpOL&-[/RdUBlX=;H2*ˈEO9'VYjqj G;(H$ÇL}}} 3ѺqR͔ }}ֵ*Dhr!tJNp8 _9t[M6׳>NX !K2X.Sի %I¢EhѢk]vOksr)E21j) n:GC$|ӟ.8'LX!*8 VS*ʘkVc`,^thkk]v1կONuL2BRw"Ahb4pb v;$BDB1ߡ[fS`O2z]0X΋D- 766>(gJOaV©9abr3l6S!h`GG . r0ɹfEw} =Ж-[60ݰsaߓrr<8b{{;Y`c1&PJƘ`BZ -J7~`܏K E;xGzb +ӉV1ZXH1Vd鹲,~a~\uUtf}tRqH39׎=XP/۱",g3h1)Yj"B 6J( Y cfL?2nzMg`r懲,M[>Mftg[-R14M$åNMIΩ`>Ϊ6kЕcǎ]`FplCf ra5r9)RU0v2L@`LѢ@fta&b6 Ӊblƺjci}0iNr:U$q5Ay6{d2B̒Fkce[ZMxHh¬,؜j$-gSI_nGcI%f2jrelazQn4{Ҷ68!QJUιJQ !^l r6䠉V0$ìDC ~kVt$gц2Vp8я~gqFX =Kxp6r0H2\(f"x^4BB(Uf91C3ɬ/V{4W_Hw.\-g91>˳ ( ?c*gq}P9kkKeƷEI&ZJ ~b&r*zB<3habEwaC2\-vs C\@&%ca+Y>00-[kʼn'cǎY,:_Fqu7 v6X WE\{to>Hxe;jhDog<3lDeZVIWT,)y[[K&fpγJ E#NОf%ʝj%ɖ-[3 [m'oM❆ >~OZXXn͛7#áq1p7禸' (vT~ Tug{c<{8=x;ܕ$ *|mh2)x<FQ8y@s. i%54F-3KAbBp#][p$;)i_# B1;ZdYB\]?.S-+pijfz?B8ė8r}GYNtƍQJƘ!-C51ízM_ :8JLMYg72s"O=r N#Y~|54t6õt@yc8 /{x" x1^2s>3?|TrwZ<_dFv(qAX@s WbŊ Z^N榣2j*ߝվf{kr&fpQ]D aԍZEUVrMZRjY#"ЁN܂8ÕUuONA@Tܕ*BX c3e>sc,!$AA V\YraVVw&{9sQIj͇q93{6lJm tj>}K]$1ϑUA{1ܽ`?>_u*=k]F_P񽖷c!e>]݋+eA؏G9/՟ Na+0~6>P15FL t羅+,) z0nǷ~ 0}9;|j(8 X!}}}Z-.]ftu Ņe Q#8u"Jp mqchUYL2XJCw')lNj)l ,(;og2jϣ[нYF-;+; 'NNKyԜ;rNH{pFpp\ _*'91JHk48(0El)תS˺ՒOVUk":sN$4F!ATCFM-f[,%=<lD+yʞ6lnvRT@Rr¼y!W_}5'x*R+e5'5CL֪-c}qɍޖ+[GMF$fȜ2,p1vرk 6Ԉj94)5puQV t<t4|ehd3o=L2q\|5tN@ "bG 7|{-< 5 ҩӾ9"DJ9cSw7OSm?q,1ߟx lBfSw\{+N,T/ osJ"rRf BBXLޏcXɗǽsAu%l5m8uC 3r=)ߴO4fOFJ3S2Bwke g^s1vG(# %8!+Rh8Q6l,k\.B(+/k֬)Ŏvϛ'#2To(uY PMw~@O2N6k7o~'۱k!gTNͺN͸_gMOi蛃XIٹ x%QaάXPZc<ϣmۆ3oQ!SJ8罜~QBH&+Ǐ//~ `ƃou...p 6WM>)7??N.uY1\tϟݬ*63oʭ cp;ZPÌ(5cPۿ>{) <ʨ/,%_|1(+L tQ<|rgT㭌9b4=oe]3nΩL$ nVTT_20?TZല42z/s1H$J(bvF#NL2Jά}RA7L{ksRBmmm%<0MӲ(F).ysRE޾};3ߒ%Kp 7^`\qG|c)s])6߂=>ZZZ?!]"-6˖-q IDAT3=_޹FxО0Sҗ _WL?ukpmY4RfˤkO{'G=9 * K"GJMl2|3)y]?#ucg)w)9~c[Ŧ{2!3Ahb|G%zw%sr9S]wT*bDQ !nB444$޽۶vZGr_ʴ|GΉ^f{gcXa3=aaHᵤhO{턾j*|_1Rk?అV&n?-}D:-|v7|3><ӦwA|TI2VXc+a jCOΎ#Y7g8u=PF2S \ {-N'?|ztggi xpOGq'ΰ0 'x#+ ?fcvÁ\.gʵ M3Xgrk2 *6tθvƍcYט}ѢE%}DMM (5<8ԀGu8Õ(̷rjL@lGGօCY7ee$5[LeKݺfhmgʹE̬{fVYr +]Gߏ[`^^~srp*Rspεl6˽^o ֬$IyG)pF3g/555M vCn֖CBTUdETQ%U 1nh\qg>JKy?T !J XFгϟ_B:D`P%]A>94773UBݚ~.Vbk} bN3F1ЧاDoO{> ]A?J珘/7p^/~SV%<*nA EQ4 ͒x͚5W |AGKT$n)\s2´grҗ۷cfff]pH$\}ᇱEصkLD"˗Q,ny/DZͽ%.!Wq{cM-߯q^jـ/r^Thn~q??5 Ss/~ Ѯa "&%)y""BOOyl۶ 7o\B҇a^{ 7@^o)۷͛ݸ}u }۷@9NMMajjݺu+^{VҥKxdZuV|G>ǭ[ZICJǏ?:~`f\r塿;N?!ĉx#I۷GrJ0U}v=zh1::w]v=00ǏCkGT*~xWZtիW177e Cd2Gȑ#(Jwe\>Il߾g'Nȑ#ؿ?Ξ=  Mz @XWZvS›o+þ}|m?d2?s??'ߚۮ[%lX_v~;2L&/C5Ν;R. M.7 ?چ0믿i-A@A "2rLrYl۶b#1<<d2uVeYxgkرc+rJ)4MJ-}駱xZb{<$Iqnܸ㎌OOZ)E={̓}DDB'ooӟAėe|;>D߿a\?яZt:g}׮]{(Oѣw۪NNN7ހaغu+kk8p73n#H8z(^z%K-^RJڵkkՠ:}sPJaff(B`޽xo{}KKKWZ3^ZZ♙~4ÒE.g>R???K/yqܹxwa&~~zwyJeÔJ%ڵk @6šCG=B?q> Ǐ~#ڵ @h;?Y{aϞ=xW{n|Gw9hWN!g؟{bT[6& B${ؗnDo#MCo XXU#ܿ?? tr^nƤR"p?Ok?2|_)>f` NӉÇ]vQ3f8Ο?o y9rT 2 /S{t||cccܐ''FFFZP ^ujធBJIۿ-֜Kp֭ KDĮ]pV-K)ᥥ1K۷߽sff.\ pBOO{nٳw^+ b^ŋqҥ ~<Ap̸u.]7oulg-!m6۷mg7޽O?4 qm\p/^ĕ+W6Y\.*+<۶]uNO]^뺘q͵K"ݻaQ.q9.JHRxghxxCOfVJ)f(@ 4r׮]b֭B^$_vM///NrrYQnif:6'o߾~XhQ*puynϪ CQ>(8}]yfm2s=zg2E(ɮ.y1_z fFTٳgyqqt+BCCC^}ջjį?$o_WZ* ^uOk]BL2Nض~5@d ̾z  vq!03QwaA1<l6qAX⢪p̼ D.9 *Ph "|^ + uQVQTT*aaaAWUx3&"bfИ\nzVܔh4P.T*i۶aMDP(MqضzӘTme"&`H)qgAbŢn6'&U`Rzw35 35o̼)CD0))e7C̼G08RAĴ²,, T,˂eY}mömmpVJ) @ !|f Q*GP'ZzPd4 ~033D1sYf0u1@D cŒRb%f0MDs "#r<#'d n"jEi.L&(<QGvxfZMGuՆ/LqwM=7fZ)o)=fQJxEx2rDẄv[aihd2dfvi3l¶mn6lvj!2ov2sUD"F-"yHT2, ;'?ɊMsƏw$^7 a}fne1m_|Qktk_>uGGG'}QR.jc 2s@"iD²lTn_7 saʦ `"PZB$3 !e )3y񼈄1 c&3Ѹ\&  2dܥDt ,5 aB+3[Ddzgt S+{-PYѰ(5$Da.!D&:wXg{nGt U2z]H PG <+(!L7'#G?R[ J)cyyY$ _D8q8^bh3U-!t c!zmAHQX3ͦavV*Z:zV)ōƭXfEED7D(bBl8UaI$$3'*JXx-VfEϞBe).iEe!4%oQGk^a!,8 qj@PlR_%7F&5dﶟò)FV ckvۻw/}%9PMD4KDRzr/*l-"N:{/3wQ&brЀ6/FnhBԣѲֺuQ"/%_նzX1!TzvlSD0@=@hTȺBFDaz@h(fl)eF.3;8!DvJZE!2Qd.u^Q`<mZ"4YfNӝ(5pg:j 쫈<e0 \%!DA,'zVM$0R 3y^`zY Qj7zH'+@zQls1s rjQWܖ_pDpXX0f9!23+fѢc"ꋞ7~Vq(].POkM>9sH?8 E3ws$#snP+[7nc,=#;'%foR-쁶=EӧOAD"a[B3J\s̜0ED-w "E ‰&"a<%Ԓb)%f"He](dV%eETİT:){9\=fnD kIl#JEzZ"M!bfEkKʉDBD |!aYq]wWk9 =bƶ(=> @D RHlhhkDTh_B4AD,^u7=7(ZV0RY1l.5yYJY)eqdT!ZKDܣB.]ȵv6":Jp+Be}?,Kh-T7u.=DRkѫUf+"+ 749LׄՈJef&ɚyL&PW^%N" ̳Af}zRb^"WRܞmDdOa&rXpYH)ZKIό9Y/HvԴKdFi@Z֥芺<<3#'pCF?QѺl!,u\#ސfܴս%#sh}RC3DBkrA?TOgc5ò6JnJp4Mi.l6oZk(M1s1Rc;i9sFA#&#NS*RvEnGOIDATGa="UJ5FD5T@#HJ);JlORX,rR\(JV.KyUJe(c2BPkBU$,'8MArFHjӖee Rب<22ao\DauqZF333r9=66F"Jj5)2D""t>& x" zEawvOJh!yMTSB4#os]WL$IAF)B$3 0玮5 p" Z$w:FАRֵum4R3 rDVB,\k\kEFiԅu"iRf3Mfz~~2avg08̨d"ś`ju:B9`kAif1 0}>s挪T*iֺ )1eF2^fN! uIŵ|P00L&F Ν;'O3gh)e tuqt:ƓZk)9f{xu4.Μ9= ry^an:~Nald|۶}˲<\.ض^}U{̬Μ9odjfL&IT%LA` ! DYEDZk%~UJyD<Ϗg_,UPPs&Zfk)ID2~{ќ#$IA = u]-`?a: !4)T*iT*;X*SP&&&\)e^DEC)eȩ1R-$DTҋyXJ& ZzL3MGhh`ttORfVRRʤZ' HAВ S]u#B"jEyRʓRRJ;(X,r_EdŋކmF"0aIfRIua2b9`+";AaxB72ޞR7 3 w' :!8y$Μ9g_[xT06os^{uLR+a6k\6JγNFCT*Yx``c ,uIEiPkM|l4i0! H _0 |7|W\.ߏ/~8qĊ(pyy?TRn+0%z-Z, s=+YO5*ɓ߉2 ݴo7T!vjVp=9kyAo߾{B"fffZֿfƙL]WX\\ŋխ[jD?Z_B2 H$ccczF:蠃>}Z-FGG ,)e1l;]RvINg a*6wu{ǀ{1V)U0APR.;c#lr6j!젃: yA咍F# WJgpDx+p_{ik(e G0AJY"%"j,..JF'5AtA QŧOȈ@5 ~ [J٥&nחu#jE\2#\7e)h 1]G#tY`Yk`IQ|> ||\{p؎NDAtz3gΈsR1%H^)gB)"JR֤ǩl"A ZQ5'5 {N73 T*A.xȎ!젃:`=w:~(jeYR*A2Hzj̜0 #a"#Gy9D,3{qpmF|;㺮@$>@JDhCH@ɊE(wޗ{:wrR(Kvvvy~<3" C^k_nܸ+~6ƼwBZÇ\@[J坛7oIZ 0?;wѣG+PJejtp-5!Gh,ĥKc֞ \̄jZ?<aTnH(sd<TZeDKȍE-n/D[ZT J>D%T/l,4> SzQJ  cѿBS^/| 6kd%5J_<^TR N R8ݝ;>3$!J WToV)iR $UziU*$AX;XhbggDbpdDC$v- xxQˡLgX?515k`mZt8)\ȍ1*Kky>:a[BH `|S@ 0 ,-27+IENDB`mapper-0.8.1.1/images/paint-on-template-settings.png000066400000000000000000000023621325266516600223310ustar00rootroot00000000000000PNG  IHDR szzbKGD pHYs  tIME 09%#hIDATXWmL[U~m R.t|V͜A:Xt#Y2tHEpq?Ꮙbf CɜQ-q Hm,@ۏt[m'ysνy}q#6{ ]RR `/9@NƇ"UW"#o7?<1_jGbp"C2~T htgoBeAu-'p>l]sk7 Xsϔ rG2ɂR]V~UkPRAHVĈ qpK dnSc{G ccݣ|>q–FꊲS%,-ClC -T$7"˂v dh!#&T"@at*Nۍǫm<yzd MkFgYL,}.Ӛ ,8dØuE湠Բt]i[A$>y/ɅOSpo7'j+rtrlDLX qҕVz7ZȵzMA~~'ZI!8 YnMXXmQ $զ67(" dBId[w3f[ Yo쭤(/O$و(Jߟc>OӴ ` #Im(,.XFVrY I ׊8ac\z}(j K,܄,sr] +>h30kq5NSueZ-m1XqAA^[[l^h3W{j Q cΠ/1{#vHГyj\<&a#uv$WNt"GJ(z@(3xj|?Hwd^r(B:.bOe|]l6"$>4G;Cu%KW'ݗ'LӴJ ɫUC#BwOYT,ROSϝWw gr=VkzmE׷Vω>j=Q2 @i A,Z7wB@]^A@ǢBh%\z?)7Q?v t~cIENDB`mapper-0.8.1.1/images/paste.png000066400000000000000000000020031325266516600162410ustar00rootroot00000000000000PNG  IHDR szzsBIT|dIDATX݋UǦhx$WJ .^BEb- "( EPVA*zK,٦$s^/3LI9Μ91eNxSӌgN :wit] TJ~Q`#Cp4P\nUK4\_8U6gYY9a Cml;] k"SԐio|XU_Z0.{h 2LHMӘCgdSzy'W??X"Yy* oLK4Y,9O[C7 orjiD=!@V0 _}۶xgmT8/<ի[(5Cuh죔K>?Nj>dsuueV"4u2naa۶Τ!"!Y9uEqۏ$\*B2STJ,>j` 8X4(X(<( 7ZMG3-s8-J ރbvЩ-f&/ً `!B;!@2  oсlۦP(D`gSoD`Xbó6 /ޡe zH*/l&^*@G.BK6hMO> R8]g6@)5fa LϽ :NI >+8g/^۫qh5[RV \W!Nv6砃;Fۙn (fgN5zxڽt7mTe~~Ev$ˀdvBOuIYX6;Xnw D?y|M@)CP `Ht_{#Q{M0u<:,G<@ oUEIENDB`mapper-0.8.1.1/images/pencil.png000066400000000000000000000021221325266516600164010ustar00rootroot00000000000000PNG  IHDR szzsRGB IDATX[Le3˲,rA,KPBB*=ZlkKbb>*>@,46 5) .\]vٹ &nINf9ߙQ*XHlvq*XgGcHJԝ6q(aG3Ry~^K8IY^Pq<0W/Ye+;34nGqjAW'IJ[3m; 62w0f1(pحUۜ0Y0 `j_ɤu\Mo2˜' 򔤔{P(vngxP<hȋlZVZ׮],@ o#Q(bj~ 4Qu]]%p8趆:WN@$0MZ:;r7 AHiIk#R#a(1ј>}_THDE{>I2# Z56`$ߗCS&>5YiƉ hyYGiȡɍ:C!c̬e%;E 0{"fa5)Z`˅}94E!큡 JAMm|Bֽ ޯIy|w1˲8 U{DEO:[멷.]ܓ%49c"!}dH䎯@maT ށa1HuD?(+d tchZ4b9Ż7fdUתA8M77!#ȫ=Q-ASuNgzRvt) bŭ1╼r)S XēyahP It:3>kl*KiKWFƧ ЬT==d7TzsԷ~zX5E8z3ٙtiZO߈qd@!Vͣ;MYSJQ4Uz m9Ǵ©U`@_vmU]~D@ m\YqO=nϻ|IENDB`mapper-0.8.1.1/images/plus.png000066400000000000000000000012061325266516600161140ustar00rootroot00000000000000PNG  IHDR szzsRGBbKGD pHYs  tIME &0uIDATXAkQo7F AP" InsnZ򊾪Uz!" NZ>G<A+#i4FuyMO=GE  oJ&<4@+7 pnL ;0pZGD<*rG97ˋ U4Q,£+鴏BrG!]^@qT">R!N7Z+;%<[+DLhER!TR%mHШBSH'zVGѹTZHk"sEipE\Nzuyk}f>4Sty.?0 ./p3,M~~V-f?l] tMͮnW;a y}878h%_qv2xS]^Y#V&,/>k< I5h%_:O=.$ 6SΌ ݯǎ[`wb6];lqӝ7ӹs3w\ '9HmqOݾKYtauEXl6ӹ~y9]`0?eйHAw :eo9-*ج5OBz8q]Xܻ03K9 !tN %$gd>V@\I6'BUtej^p-j>*&zɌxGs8(VyL-c%NeyCr8+þkGWλq8r:+P1Xp^T:֞0`t} Z:lX؇_f^Y$.Tm4`pIޠ{$~??ѦBwp:B4^r@ƕ$II$Q9֔hPIr8I$ 8I'.XozyC疈9x_:}0_/vmw]d>['B`]Ov|A`nmKuac8z w:l%%Ƽ溬Q5[3wR:%a>Ld)\7b ^)%:J`ŀ`IKíӟHŜ lNN2bizE\Lն*%l #qtg`c8-2ХQ]`ہm[<t5s.e tS19[Q* Lc ;(K9l9j 6|w吰!9M*L5b nt9&l- 8ɿ  8IB'$PNrybm䏓E^8zL $Ag.0tga pMj >ndt#]-l]ʒsAFrszC/[`spM>#+~8EqN [Jٲd 2;myb t\*t.D@*9^4MEގHF6þk\-{$laK .$*Ū=6t \gᏠMp)R#m}{NGENr9@ G6U 70u:tKSvsH}W yHp$$II<MIr8I$ 8I4 &GoX=ynد[, ~l"#67pwiض?C:9l2G1o=<ÝsP瘹ԷQNՏQ. [od)\7b ^)%:J`7):7s8t&-.[.Z:K'F=G#-ҠĹS2bגes1_trBHl1ӹs3ua\ F(Wm;Z5➺}z¯{s_ ^@ lsrV5o `rj"Zs_--?){ t(iTf<s}:I܍r5 8ɿ  p`3NIJogwy .$Ag`fZ@W-kY_'\LW;*&z{t-/1/m-tUs>tR$tmaGo ޚC@(qY(*@;YZ6ybm 9 \ECr8+þk\-{tc!zcclS6A бY^OJc6M[TpBSlY>u .b`:r@l6do`0:gt+hvp4c2Hs.R=$ 8Ip$5ş5I'I$'Iz.<~FCx_tp27l*#ـ:Bu k\: sk\+=O1`c-)1%5eՎʶQN7í0tHK[ &_Y2~lG1h `Áv%bb@fǂN٤Х`TK[]JH͜lNN*bkzE\Lն*%l^:v31 9R ܨAʵ- й},AޕH&tA`K6Ԝ}@ZrHNwNls1`kNpR7wcޅYY$ pd('~iY*c' :#,vӕ; mkPddv 1 ja&R ٻrL5l'ݷ/[`sp->#+%N t-WOШy^mϳ-!`s x Ѝ8{Q:l4MsWaXͨfW{ؗu +q͕ñـb1lӡasۅ]Ybﱡc8 @ǀm?cH%yl9zs:<Tm4`0u:tKSvsH}W yHp$$IIԨ6s1 IENDB`mapper-0.8.1.1/images/point-handles.png000066400000000000000000000015561325266516600177060ustar00rootroot00000000000000PNG  IHDR7, bKGD pHYs  tIME  3,IDAThX[0 N1!9-?0[惌%%D@Keͷs\v23%zhFc!fY&w,۲ё9QNda Ή)1f6W"fՊͤsNb\pYyNW$4_cpMEKՎ Ć%QEG-c;FR[Qݗs-⾢;y@t%ՅzC0D/8{k[>^u00=}dپJfk)=Pz3@۷uơR`㨗4ۯb&$Q(DEmK>q.9ov$aP@)Ss(U@%[#n5@-Uji N:ǚ֥. ý=34"VI_u9~bbQˌR&wZ"@Bf_J0t``gtjvxguvemv"n-1rKo9"Z{9<3:;R1F0%6~9ẘLN* ٥jۋxg#;չPoj> B*Plmi<]/M;%k)F@}N#SʌioVyOOi~Da;uI-_)_ rFFJTfc"&"1/JvcCx}}:AOӕ6E oZ*vBM+Yy0_yɢ5Y@`7}ՑFyװTPP_]'ک\vӠaˑϕ:)L0dzJPM.\sêB 0-˵socdŏ.nq {3M{[qȑ"m䈘3X2g|AS>dOX#3y~Texb-_[#p|s]߳I-^G^_r@4IMNSRl"ƙoV@>!&ma3RY5j8"dXtI~oGs[2\ߘ.('shYj gԪ |[ig*oƋ\ֺtx8?-HjeBdŧώ46Bki̸!g[(Yjذ8U(ִt= i"/bU~WFacSubu{b".t-lJq!MI@'#y-NS+TL3Pk;%uzlCf: XWluqƌQjHE茌 WW(WԩbeaE߀/# _Fv F  64ͪWU)s:wD(FvIENDB`mapper-0.8.1.1/images/print-mode-separations.png000066400000000000000000000020611325266516600215350ustar00rootroot00000000000000PNG  IHDR00`nbKGD pHYs.#.#x?vtIME: IDATX͘nW87' hHꂊ]EEY+6PU,@q`nIH!%-I3]12c;Y濟_0nS$|N[%om`|?!{ho2<9aFXa'&+,e]V Av(MB,e|dEÌHHMy/^jxReKFY'š% O`]n<@*K!tt*VR']X8$h#8̉ڪe=j`DX+P(^/׻6M&5VFN~K npQJ)m}T){VkI ;'M,9G6NzTV t~CwˬUTsE&1sļٓT+7ϳd/2#b?S$p~)#{ޕOJ%=wR 5Bb2645^*;dvC^lzÄ^PŸ[*O.G_'8 x+ZV Pqw~ݒx-; .z!q5S6ň(xMFXG@Oj@9teR禬0! >K7^. V~d,:n dɘ:7pA t YG8)\ h@Oj 4cX«!_Ht\8%N3<->p"Qk#4'm0?5eH[\Φk೏A8? @IS^QAg 2 {tǤ۰,{W+{D/Q᭳)NL֭gDKIyp-z5?V@UT~Mm 7Lc6  թ{\gҤV˹jmfLzQHGJ 0kݝ8$z V-G7 #o.g4HЁ9c^^r)aP@~է9I-+{34ЧH`㒳E,ԞѸ{޳hlw:B:y`{C ,7FfX~|=d4Vc{jm~7`yMj>MVrYw 1` =F6v/W>BZX-| @"hfVSO9'OB}JE"rTME-}ZP|W$FG*t 8*OuGQc:g9o}SG:sl['h*rcJbPշ,#!( b!zj @p 3>afcM|:/ EjP4Τ( oՈՕKT Ӧ{PxQk1؀P] gdc fT-(H9Ȼ#٬rF3dfvH↦! :#9Kւ N gn{t7nw}Ah{(Mm"(TrFIu>6G)ikw1t`F_S{Z8,+9Qn<~'8iñߎ3;aN UX8ĔY7$tBHaMgn:<;7FjG}8'˹4n_9(zAB gՒx,_YFBBBn_ ~ׯ_ϖ-['99x aaa#;N3đьr^E=ڟ67Օ~ˉ8\II 6ln3tPXjj*_=0j(v܉,CG#sS_vŊr-nQQEEE$&&bZ;~1qD@f5MZhȇpJXvwF۶mru>5LiIENDB`mapper-0.8.1.1/images/print.png000066400000000000000000000016251325266516600162720ustar00rootroot00000000000000PNG  IHDR szzsBIT|dLIDATXO$U]c,`ee+0NIFYmc" ,\s,]Cxdbu=v=q=] _ν}}[U*]Z=r(4_ZZz|UPo5u^@gD$pxxHc4cc\.Vލ `llƉD-®uLoOxG䝠-ޞ;A[vvvZ[P&\.fѴfܯT*W$8p?<<ܾOgNOO999L']ܜ~G7r‚o[*_M4~) 8CXöVVVH4M|ffM˲mDA* LOOU$P((ڲ 2ɓǍ6*y%"088xN&cb6JHXU:j~A)켌|` PA8yc0L_e;ϋTHv9>?+{'GLJB3I&fҘft:n2fMٳ߀ڭ݌Kw-~0t&N15XC(AIWa}5"KTl%^F /A}{vvvR JD5|i\*iZ؀UxR|[,~ml\M(J?D] Džl'"zqkk8? ӡ;@`'Y^8 \'@)mhn B!.ͧĿYn7hIENDB`mapper-0.8.1.1/images/redo.png000066400000000000000000000027361325266516600160730ustar00rootroot00000000000000PNG  IHDR szzsBIT|dtEXtSoftwarewww.inkscape.org<pIDATXŖ[lUߙٝݶR)޸GBT MVcFYD|GQLLKb$5ATB@*ZJwv/s;nn|9̜93Aqx|ڗvܳB~PpUB)O+yX<sY:h9]@ڪze/ *Fn,Rǧ5" <_Ϫ7r5.mmǧh WYf. لrbgO[MfWkO(<9|RMH6a ȃ{f(p%69mOsg- _vW-D B0L)08 lf1;XTrYEoe^=tVN,{lC5:&$[H։'EMҵt\{WO0ԩV/|tGWkTB0nm6OԔr$d '4-X§Uŵݠ> ?${0w$(q@ qnoѿHd ۣX Rf@!Gy0dP98rJmglij@tHMZ|G;X99'@Dw s4ɍKSFPHiQl:ysKoOm:H)ʁƑΎ`aq(|Dpr&wN AMȤ>ND$aJ(i[XtlCN @id6aO$pj{EU_c΋MW0%3(4vz9s;7N tYf<ȔK63ۓ>H2܆D$3!QPuQ݊JW'Dҥ,f@e։U?V!R%lf'$ˤ eS|LI8gMxصkBH-JlrjjL&i|{zzJ)'L&9 ,, KKK "*ձaoOӗ|J̙3!Ħ(eCCCMGGNeaB  ?f34MBdSԻ-'3L&.13|>VVVy@Q躎ZDQ@R{{;`pp(_vvvFq8 bjjFFFh)CAAU0ƿ}2_9utt Hpss31RTDJUCCCM2mixxcp_ݴh/ؗjJ4 em`hh\=O8 0<<?Z̰, ~{i}^__]׉199Icp>m*`Rv&}eY*?x|t0^ Th~חTh<(xcuuO>%HW r{``M^@#|O{zzD% !2 kGK75%S7@2\K4bXQ(dptuu!#Զ=Pcr9axUUKKKvƐN3y`&"\?ݸ|2^xwybtY;wH$D"uh@EQ P^^h4.޽ׯ_G8vU6- `mOya1X)B0 ܺu ը@YY4Mr90== !TU.y0D~1_`t]G__!cir9EQA| \Ǚ{bqfff~,82@@.Np-bC!>WWUѓ'OnL&EP *++9NӣG̟geWcڻG V Ze۶|8-f%%|D ,E"de6YP!<~xQJ?Q'6yǟ͵D> A ,KHi("{ͯ\`Iɍ4111ko7`ĉfj"L?˖T*bu0. hƖhii!)%\[<m4a~9~ kcey @&`(_{zzGۿ\Rm=TIENDB`mapper-0.8.1.1/images/save.png000066400000000000000000000015721325266516600160750ustar00rootroot00000000000000PNG  IHDR szzsBIT|d1IDATX?lE.qq.X@C?%qhP (hREAI!4n@BD4(ȊB6Etybwf}w{xٙy7;7}v&ڭ;xd_\7VV>FQe~" ;Me~?`Pw]LW^囟R#UTr"pO7iȇ?*b(Е*YɏC@tmll+@61[H<nFFHҐHe-A8n`P<' "dv"&C]` MeE{`6t _{{/ٳ#IP?дy .|a12]Č?$ mg,DZ% C(" þek-qchH3:1j5(⫯awg z}0 1jq'NK`Y`R/V*0dwg[kXki۹~D)=1(ɍ1 WG@Pmh`4\#(j-6ݖHp/ RGon3;U΃ #^scufϬ}=4@ 8] Axq|1K4xGd-$' 3Of=&n_x]j`%_ft1>GI֥Ǿ<@EKj3?ȰsϏIENDB`mapper-0.8.1.1/images/settings.png000066400000000000000000000047601325266516600170010ustar00rootroot00000000000000PNG  IHDR szzsBIT|d IDATXW{pT}w7yE$$AbH@N;2"J;LǶZ"j*NV4(L l޻;gBcX̜s~߽7aKVl @i'L.fc|& GL7v{8׏| h7?P˷:̴iix1Ax)6b%+߿d6m6l²{d|6dy]G4M{yJNMd#Y{_Q}f^mke\W[ԾϦ㑩?Vn0ӻ~E&B«Xc=U9L 7M[;a =۠&TYMncfO<|/)-l{ćsB9؂tl޲NY  ;y2[QSR񝏠\%Qή]F|(&1"1 "KMDG'(큤C":{˙n'`ČTT:Â45[hp'?$ʊː˻]M^`ժG9E.6ݻYRl2(!70TSC5ȳgە^/qʍju>;if~Ն9o.*;wF܌y hXUUgFz$uIV@/_>{{6,_aYU20P=n_վnm]'X8MS/50"Q(c`p"@@n3YɚI ڶnۺA~e^o fWT;j}8pQ B\JB;=ĸIFA`H3R U`6(EJ">}eb@JxF1 E ,Vk0U%#))QEVQVѨnUUBx"|KQ{ͣeUd4jOq.s 򺥖c[] Jwǐn[P^u^UE I:yϤzG 0xȒ1de~ ~cG_\t Ҍ[DA^v O*! a=wF'GKfܷe5AfR# 2qјgܷ|q:.WǘcXpRA \89*]5V$*bjуIuE82xwq[p޹,8qⵈ9ڨKq:xJ(c!tu0;Ǡ)JKʶRIR@Et"tR-Ὣ`/́XCGqPqA%*i9QPLwbzh0F=0{]ftzg^V>Ga6QR1ȢJ{@bDPe Ѱ4FĨxT‚ ܚn[+ R+$>oNg 8~&TAn^*6#t|b6 +ۆL#v>K۾șf,z( kVeU((g%e5Yqq:=;[~dޭ 746DqL:q;:.O4,\XQF̞}(~imy3Y|)GfzzdWݱ6-Y/+Z{ wfxa!|P -\n-#k$JFM~y Dr @_OKSVnYo88$ΞޮfU,o5"ɢs_--_oi:~DcJ"64ѥoQP5dfgp>ATMJhUAv! &]=.)v' @ruKeNFE-Q-3 PAh72-8Hb pfZž(e2FO5K|ͥdߗMe"z8 USIENDB`mapper-0.8.1.1/images/svg/000077500000000000000000000000001325266516600152235ustar00rootroot00000000000000mapper-0.8.1.1/images/svg/map-parts.svg000066400000000000000000000172351325266516600176600ustar00rootroot00000000000000 image/svg+xml 2016-10-02 Kai Pastor https://github.com/OpenOrienteering/mapper parts puzzle mapper-0.8.1.1/images/svg/move-to-gps.svg000066400000000000000000000374571325266516600201410ustar00rootroot00000000000000 image/svg+xml mapper-0.8.1.1/images/symbol_point_explanation.png000066400000000000000000000065611325266516600222620ustar00rootroot00000000000000PNG  IHDRZZ8AsRGB +IDATx]PTU~ݫ &  0N35 kN%ʐ293M(e% 5g4LimS"$dW`]`qruAsss90p/x;88xzHHHV]P(1 fcBcEl67L?644PwݘV7fddh@222l~~~Z`ʃJbRYwUUimm˓HF^T*,j4:^|qrpqbjhZ6/==;Z6J,ЂH!VZ7؝AAAx R)G3evog4Cرc-?{,v܉ `͘:u?Cj& Xl, ӦMm2$ 6`Μ9 ADD8Aqwٞk L&JjB0ӦM3-1ѓ'O^|}}%!RTTT ;;* .kG>qp8x7^x]~ NzDpժU|}}%.\@ll,bbb~(J@a J%ߏh… k___$$$F eOɾtp:#2naၓ'OyyyE h4sȑW 933HJJ<Ã:N'L)HQQ)_h4x{{SmD\\>Yoܸ~-8Μ9,֡4Zo䂂!G GUZƋ/20erf84"fxw{3fxl6:F/[,ws ws|111nKrf?ضm5OtD\ʝC!**'"##jj2y ;LR,xVO7pwxxx )) ՠqyyyntF޽{9Fĉc>Cii)5{W1%**ʗVP(  !P(8u53нdI^ "<55{m0B{PgQ裏kS__ᇩ;w.T**<<<0|*N JK^FCpee%BBBr`4Yf!** %%%-"͛J*V^0g֭*PxMk.DDDP7={qqqr KfB-ZzkrK.ŪU-[cxw;qu`61k,֭[BE}2.k8= "YNGe}¥Ki&I!ɰZx ˅tC>| OOOZVϏ8 h4Enn.~wl6Iheh4b裏pIhhh4jhXSSWyy93gߖ<믿tjظq#~Z68J\gX$iAz5O=D777W@GGGo87eJw8N E[n .… eY*a˲Ȃ"'N@բΝÚ5kXuv;t:=a8~{R{KEcƌ>s7+7nLpf69R%z(h+Uhll͛e{PqKSVVvEEKkX1RM7"GqRdqL6E0V+ jZ6 #mb9OCX@@1??1d?ގŋP!&<5Җ,YÇK2M*78ѣGi 篲n<rFQiR!jjj9jmy`S`fS||:ю<Ә={LΝYVVJ˗/deY\|˗YRVV& p[(uuuc):NReZp8 vњ*‹%m;wDaa!F6t\x:F'&&R#z}nCKKKFii)e@OƱc`4遲8FǎN2RVVX,w0)jz_C>+B>U7qVk.UIENDB`mapper-0.8.1.1/images/symbols.png000066400000000000000000000003741325266516600166260ustar00rootroot00000000000000PNG  IHDR szzIDATX햻 @DZh 8q TAtC c9!@>HBڕ.K uBkr|sp P4.f]r"aLhSGj Q-';s 'kDaw-}O8+9 O8ۀnI͟#^r;)pXf%`!\IENDB`mapper-0.8.1.1/images/tag-selector.png000066400000000000000000000012251325266516600175230ustar00rootroot00000000000000PNG  IHDR szzbKGD pHYs  tIME  */z"IDATX1OSQ;4`JBRI%]tPÏ'8a[-ե:cp&4&|Ɣvt7y;0`[w }{s?xt)0￯E >}vdOD|>_+R|ryp j58`D7Dnv93hdw޳\hWH$IJYXX0Zc-VVV(~Dm rmdd2W@q&''%l6{m0Z#a~rLfDb˲z!16*/S)w,--BAksBcqP: cx\Zm<ؠX,\M ]\c< ;B"ضX,N6c|z/ j bhSVOT*`YP)eY4OO9cB (@+ec>aJaa,((EA8*̞9 V+C{zGw(Y:ANYdxQ0 $fP 0E$0Ӥ` d8&m*Zr۷m,VPR a@`2f2i6hP Ma0E *kkx<6ok tAZp+ʥ2 B !! a`R!2i"`"'P(`-tWWR))[O+b/+I&9].0@A?R(h |Risq9\.,@U+g=N ܲ[yA]9@W$0 h, @@-p0[4PU,aSL%[[[Asҟo1EcQ,,T,"  @&ZPeҼjpPVB4ۅ)X-VD,;[ 6W94Mi fgg cddJzL0h& ` JJ gA0s7 N_$Ipt xa~kAԩv>  gMn&kNvύӷZD=ժڗf J,+,L&\.l6;IafhyA@MKqap[݁VYIh׋ C0@j5Á\.Tb(8łK/bf *z (X__3\kӤp8Qu,//A(6:AHdLrQTT&(!0Ԛٳ܌H$GBJ*|7Kq7mhzafwBE -/ a ˠX,RQEJ d oCOwf E+amRv3t.ŕ*;{rX.ƍjI@@oBye7drh @ j"،'Mǰ^RKJZߠ>u1cb^[YdJ<ǃ(J9aB_t]C4,;k|Nӵ\M[$p8 KL6E,䅿 \'Oelvp,RF8%SהrC'_n~6ynww ZCZZAxVT+*b(P*e^KKT'2l ^sZO)oksr/0 p`,`9\kK ^8b2(\\p/@FCF#h%no~&EawRTJ%=/y7˟y4gKp6?ԷLJحvHVE2g7Rǎ:.Ο/MjD5],#JQ*E$h:zP{񅗗uȽtqۗY2MV|[[[2/ G_]ϔM'4{p:wϟS?JIENDB`mapper-0.8.1.1/images/text-align-baseline.png000066400000000000000000000023531325266516600207710ustar00rootroot00000000000000PNG  IHDR szzsRGBIDATXWkU܏f7$5T*REb) EԗR[C}PII$З˂+K$dv7n;>8fgiC_\8;ws~s9>33s]U`^dQmJGO׮]m@sAMn/۶}=V7.^xh`09kB*cG+!flclO8{u]J#`TN3p?R> rlt]_Y^^ZsJijmzzzBӴP( yPԵZmV%aP(BȻ`aiiibcc#a&9GX<`dC,WJoqcbbbLO:998)5`powwꪙ̀,ː$#P f3E^gzvsT*w@6UV!i}-b,E]囏 `ٳMLEl6[~mv$AӴ>܏([[[YuFX[[yޕQqnzzz)C&r`o=t$A rCȲ|fs@R9 &<{sf!;s=L'עd2rJ0wܹF1QL" @Q|ߟ ;hzʠi[;;;NpI_UUP($aT=ϻKm QH(,+xJa*br@78@y̙3eYJWk+B:Z(_KZ*|iZjvc¦9b}$|>ocmeP2 9 F5XIJ,bB<_g$i(]!4tZQ/ ).PЮ uE¬D)6?T pW/ q*j.&HEIENDB`mapper-0.8.1.1/images/text-align-bottom.png000066400000000000000000000023571325266516600205170ustar00rootroot00000000000000PNG  IHDR szzsRGBIDATXWoUv8 IJ(R  *PEE%$*rJ#T.'p@H  %XIuُv9dZ&i^4۷3[N9~1==}SemZlڰ6S'b7n&}>EA|4{ 2VNU؃aH'333)4y}m6 BFUաK0,\pxJ)TUP:3.--F946555(ʭ3>8wqg J_T:ђ0Ɛ!佳08tkkKe<|<N? 80 (ōUU?{\˖e%Wvzz<(Bȏ @z ^TT;N3NFP*!>2t:}\.u]w)?(?Y(K.躞^7l>8w[[[hca{~(r{aaa4dܲ,XUЉloZnb$IW( QJh4q~q۳ْe8&q^\\\/ԍF cal՞3LKC2/ Pyݴmu]lnn>p08355nD A0ƐJҞ@=u(!2xʕ+z A)Fd2y7'r NihYֱ#$_ 3JDBȭ*˗'j@2glIr]w&I,ߝV [Qq'Y\.8hVvgu;G ###WMPJaFŶW?4M{lqoX8@yŋa=JkB:\(_vcKZ"$}(Jj%jnwc:"} "lV;dMӼ5 is8$l"1R$bߔga<σ9m.0tA|c).@  tI ).d'M?L qGjHE^^I\IENDB`mapper-0.8.1.1/images/text-align-hcenter.png000066400000000000000000000024701325266516600206370ustar00rootroot00000000000000PNG  IHDR szzsRGBIDATXWKoUf<<~LlljӐ`hC(E*J @JHTET .'@H,@H+bɊMŪ T Ul0bhHv2Iq5 c$mv\9;1q3ӗu]ocZuQ.mQc*O [.]*6W66cnDf ۶o' s* 9upAH^(qcLҶm0 19i0GPkZ ňQJa `+++Z&Ā`T*'+' ;744D< Dk²,!sR>|W-s.;`Rs= , }`a|||0O>118=3PTissg@UU( @l]\[[+V)t: tҾm#RԵbo4>{G)eY UU>.FWYZp<Ԣ,1L@"4*/;q:8'BpJӴ @E(=_v="yloo;qt]$Iz0///F'Zb0fm6͸8HE/4L7{sn 9E0WYխ-n>gfvwwyo@T*PHc d2mÁ^bs!IdY~@H^>{hZ RlnnŨT*= 9N{.Gj J}`8NNèK&*!m„hP%* JiS(Gr/`qb'Z L뺞f>HL,zwk mK>Q ew]w= =Ri/ɲ?[ Ik`= rxe}JWA3#QuR!!>4M$HZV߾-ݠdv&`ge2s@ض}1s@4g=σ$Ia#}0*h3$l<؟gW,r,oZ߹sGQ,}߿ |\$9Qp)?d`F\SEF}@paZdD?0/072& =%.ϩs9&(,r}x+}!IENDB`mapper-0.8.1.1/images/text-align-left.png000066400000000000000000000024041325266516600201360ustar00rootroot00000000000000PNG  IHDR szzsRGBIDATXkUǿ3w~nwlIj bUE) EԗRO'PA}|>}),dIMfw̝{g|p&m7{>37Is7TU w]Z'۶7Jw_^iZ |G4 V۶` @yg:Ѹ&`qqws~uqrܧ ۶9?뺘0t]{ (---- cu}@t]X__4͔qXܴi7O P(|xB)ZVn|K8( iM<1wwwbxSdWVV,cWorczzzRϞ@yqT~֖,ː$%(rҥKFJnkuV2}G\>KyJ ,n>Ѳ,RXeYϛh4].H)vgg<*}lMnNEwtc[l{JRtNQO@&IfRz/J7N2Z vkkkStlsΫ9jZʳa}0 獍>v$AӴ^| x u]wU{ҫccnnn'R9G&ϣq# A(/0'Ȳ|ŋSF `akk+uQ Ø~'@68ˎl'(2LF&-XpL^^& $ @Qg<[+h@U;Z-iN|ᤑ^UUP($|B)y_e鯦){8*ʯem]d9'^¿(6dž|<UUh4޲,3tC[nWUt:sQ-fgg c_"$,c\<;׎ ˥4Mfa?Rh6pm4M\[[+ҞgRiBU ɼ?22"B.˧r- c LF:qppqpp`y O L~=>>r`&(SJǍQMӾxRKmZv:]#I +ezVj**V'y( sedbu7 åb&g)j$=*aDV$_f1L]Bmۆmm{1ߐer]Q9JiW?!l)q; cQ^7ceQa=j4~9J8>J~ܼ5DQmܮ?^xtjurg} U*NNNΉ`!H$=}C Ap`]HtoeeeVAPJ۳DwTjy]ȁd2mGE:,L"A_dZJQt S ('SSSJǠaGvpIYQ%,$ ![pݾۖeqaH(0M8xJ韺||7r@⭗14;o- sRu>0_ }K 窪&fOMONNc:"}nt:1i4=r?KqIDcUHB  )ψb7eC7eY})brZ v\N v (~d%̌s)}5p.(}"G8"qBq":p}B?g EJE/甏Ԝ0?jx \u,IENDB`mapper-0.8.1.1/images/text-align-top.png000066400000000000000000000023701325266516600200100ustar00rootroot00000000000000PNG  IHDR szzsRGBIDATXWoUv !MP5JPP@TH*H rJ+#*p"GA/#V8:Y۩}oCw]+y7yG>ˎ7 𐹹7774>|߇y}akkZvŨ4wB^xJ^w4 !XXX(_)fϧ:8'yމvB!Q `biiil91]]7Kz=1؛4#ҾQ\nGS9G.#嵵DZ3i<|4N?[^^9, c q]?}\Km' Rz[Ƿvvv8,C @Q+.\(UDm6v݈y(!o=2t:}T*}Mtc߅c?X,y]k `ܹssi&"Vuq RJj(K=? `hvmeee2йm۰m{@;ra뺉b8(ʇ @( W$Ic}(?~}xxhRUUao&z䜗 Nh jϙLőq&qccݻw$I4y=2(iqFݽM)<8ήZB ApΑJҞ~u(!A s2: 򻫫j@1= }d(xk N`lБ  IR2!?`ӕJQ+6A(O;t$UUǒooo' ypttdG ǝUUUs\BbFRzuKNG #a8/2c(ApBk9eY=ؗk+B` ϻѐR$`@QO4MK7DN[NtBgBxAdu]+zL|9) `ƳRMs^p @91$׹( u]7%E}9F@t]{rZ v]N v$2c?CQ"Z 8p8p}a3yXER濡ɹ+(~jY}JIIENDB`mapper-0.8.1.1/images/text-align-vcenter.png000066400000000000000000000023101325266516600206460ustar00rootroot00000000000000PNG  IHDR szzsRGBIDATXWk#U$Ӥm6mZkiRE/+"bAp",[d}?EA''Y|GߤP|  L̽3>8S&v˾8p87w.{?ER&''oɲzo6emkS2<;;{0*ʾm߄zwww?c,lO01OABq~!Fclmm[ n|\hS>OI0GGG @2qccvsA(J_x5۶;Fy8~qrbbb^R1D"y:ԡ 8H ⭅kJ@RrgK&yW^CM>u]lgt|/d&HKXT꠱(g: Iғ @Cbˠaḓ,r: u=8Φwz CCCeq!}a$RYm~6w]_y>>[q6 :==}-zc0ME)2| $@>G)l1_] H(ZVi^?d `2⌋?/fiR)GIYu1s:60JFHI pcPJ:yg\Ms$}B @ @ځ ~JLһ#{Eh)p*F" Ď8@ g\$Hi E"2ǨfϿdA֒(>IENDB`mapper-0.8.1.1/images/three-dots.png000066400000000000000000000005331325266516600172110ustar00rootroot00000000000000PNG  IHDR szzbKGD pHYs  tIME k@iTXtCommentCreated with GIMPd.eIDATX 0 E+p4lp$>>|8R$+rl̅Bp^SO6k5$]Ӑ9svAz< X=H`/*X5p[olIK-]87eW%}2BoS˷'?"==qPIENDB`mapper-0.8.1.1/images/title.png000066400000000000000000001334711325266516600162640ustar00rootroot00000000000000PNG  IHDRd.sBIT|d pHYs%tEXtSoftwarewww.inkscape.org< IDATxwՕ6V49FE `!fck|6/ 3#2DPIH(LM=[vMUIgf{O:ܓŐEYd@P)GLgYdqj{`9&;,"N7Ydi)W"Ia*\Y#1,<ޥE&>Vrc1,"/ aY7ue.)Qa!~CR$ErgC2c"P$YdEY|5 4jC#3V3)S|)(bYd1y PM,U8?~o0$P@","kf ԁ)9?skcG2 @ @fdw;6L;S|wD ??A8! Ք1Y3]EaE?"ї @x('шCR]ztrẄ C2"HX|MP n}!'>A: Gjҥ.E),'qYdE `JcSԂE`)fT{tÙR(r 8lpR5G5)B%Oޏ7x Sy)XЎ9p nYTS*o2EPoM HMҭ;R##2Z33 H|FJbfA6)S&Ii(Aa36`Fe^0C1100<ȏK ^.E)xtH$'( l!^`N\$ Rq RC* Xl_s$0K 4@j0d/>]P9dRݐ{QS3 uN5S U>͙YE〬aE_\(`eu3 =+"+tww ===@uu   R^d=!O ?#F6 I/NYdTsy9QhFbP(466BL2E`&ޣF6Vm3-$C"یfr_0G9 ҆yٞ,:GaR*15H"D"0@-V)_IaRL!}'"@cxUչBNM!9!xs =`<\g(m>E_Lh\<H+ ===³>{z=V\믿&B >3F\`|֫6JW`h)AHARj f"`{ 8ޗ3|&@1Xp!,l߾]x1888+++7 ̟?mr4t8 `9|Y/(FfC)Fb}7xH|L8ؽn'A`'gArTPRti"2 z\8 ,?O7 Yd &A@CCè?,ȑ#hhhF7I؁N!H rة`lgXTHM@rA_oRs 8~G`|R zwyGxꩧ+%n7oߎJ>.PtCjU : *d_GT6:B#{ Y/But` ` ձ׀E0H`M #9F3vIey0,Fl*iY|@!5(386~3.zA7??3s H U)-OSBRlHl 4"YT D:Ha 9S4L~X +L>XƷ`8$O|c+i:dI N$֑pءi;M8ynnI?H/0Fя|]!#$ 6st:>V0ŵHNJ:t/:tGJ<̧!͟wM#x>fp k@`"VI]zQLȢTPD`<(SE|"Q@QeK JAa0{Qm# d#~+z͛quu0/"qtx훉F25^Qzn@( ?-ƍqƌ BB$ѣGqQ|g~ z>J]f eU`*H!~򓟤ll }}}xꩧ{%9)H5awOSS驐JO|h#~w '] ѕ'0T)PDyOhR [̵#1G֓_ޑ'5>HjSΧJ?]:+J1]d{\~{@/Z:t*M%~v"FطoB!< bQ 0O 0 Oaه_OQ|šCpBb!l}GokMX~S?d4şQ( c pk ~Mi-y5U&'/(i=pr>$>UZO̯gCM6.|`JCDhjj냸5"4Q]ք@0~ "p4t9şǯ"AHkί;DCJ \6|,?H t<NM$(B#֬Y&);W_}7t\/~~3<!HE5Y7͑O$;SSe&O+k=Fiq%YQ7~;OEz jR\߳g|S(:xsHݻwc…? } @e=#7"6ij_~ej|&NASIlNM|<t@Fr夋*LJMNL;׀Dח3hS/=y:'VZO~oKk*A.[de?}@r> Y.3dO5;VyTx65P~JM(R@CyW65Nmih6َaR`.^1Uh@0ҀA>Vx_bag( LYוfs))cVX!HW^@vHAG#>NYS~=iC5KƧr㋔^"޻ċh*'/<[J%-vpZc bX׭Vtb*R^F|l =/IY*FH{*謒G6y7h d*99Τ_ݻw+|ǦMp뭷\\q^|饗&{"AlDCiy`v? j+ .FKFsJ~Y$wF %P.(Q5Yg"T GIM:* B_>I$~Gu;_nl9H7NvYU a(7\HR8x) Vt+ESE .~:KrAr#;WxW,$by^I[',J6Kϼ ON3@gg'`41\C3+Z8w6H2lT?k90Kx<ق`0Ae }9!o /'-ΚT:(2h\Q8<$K 韼QsxgǯXh%tfV2f r}o@CRE yg@CIϠ@b@w󆪒gf1 CZW'Dɐc!3mjU |o^>V)oډtN:V'~0ib j! DJL0 b.ꆤ aK ^$i;5u<Ʉ+):}" {$1WW ו?lI4²R\ڴhG!fZ$(z2XU3y:[ Ҧ g2GʫR"+G5b+++:jWCyy9Oե'SC~O9Z`N|_O14ۑ L U rDyP3OM6_R ʍwV1uP0]i%0z!)^D8 JI$4#7es2P]Hfm^fCJƾҽqR6fFw"m|RsjrS B1]_K6O !)K2ٯvF3/ZdS+/r9(T j4+9("K4263}6 >Ht*%L&,Y]\cc IDATw|-[rJ8NQSg8qBs.ir:<O;G$E'9E~ ƃ1Hc4xEFWMt#g9HlHYNHn %>Z1L-~rPy'$!H]yZSI+i]`udl1JdvܑCtOh7]`:*ݏRaHn*! [mRnrdfJBHS`4tCb d'~*E;yuM9)섔T?R5f1 #5J1LҚ##F>oӑjM+bJtʣH0; f|u1|~;!'ϧdPhJ7'ϧ]!+~ dCVMZ3ͨ3TWzHA>U` J'0qR4sʼnk2•Rt[LX\Ek:¨♩aL4 ȝ}BvR{|7N}S[ HC)4G1ءv Q LfVAݤ=N*սMF'qN$u)vAR 0kdIͱF][[hZ:t C|T/kNo(+d_ZHyܯXуdk-+%]);_k!aܛBOOԐt>s.{x7} Hw^~e@m픴q&nJg 2ZvS~d %=mˏqSՌU5Rjד1B+ dHjѫ=,NB2`RW2]ў"=Uag+e8kYjY!;1y&$FH"(dv㦀 d.[ d‹>\., rsst: AZҊD K)&"'VB{0D _@6MZ`$^7Ȑ0 Ѧ5"+?/FQl6vf$i/+Qe:Qڴb1|8ʗљ-lUvS;ԈDHb1aӉ^3_X &I#=ѢV((`\ j0ͰX,Zܿ\S)$(7#Ð0CML6;W$AYpؿ?݋zx[fkZ̘1CXd ,YBZ<򞑠$O=4#KKK Ԅ8(F`PQQ!̟?GMM)rF"3ﴶdC񠾾v Fs,X磸XQ>]K^6CZ[[q tvv&{F¼y0|̜9St3h W@2 ~䝍^ݻ{ŧ~Vos /ƢEK}#b|C$awuu8pN8˕R?򐟟555<ߣB 7G?0 O%%$~lٲ7p[]UꪌhɱXgs*U&"t^с(eƌŒ30c yS+:vΜuYhT3(R4h4*8v?1%nܹsqyais?X$ [ SW(+O>o'N [W\qJz~]imyoI2 'O1D2hٳl2\p5~H|VHi H  AaرcZ A0}t{ zHEy!x<:t5&ҁQ qbCO),Tٌ'x1ꫯߎX,k… q"//Sio1$w N͛Q__?UѠ3gD]]M2g`W8*>F/5X`>ؙf_/" wPHnÎ?R|t(lj'p}׿v+Ը>WUUEcwƮ]pAcE]]z,YD~/$:^lhhRUU.Cii)# )N8cǎرchhhȠN iQ\\,jhC]USh0Kشim6f~t3gVZ3f#S7IS0M1==q0:sYY/^ /r]yLG^ h*Ð(ZWɚ `QXIَ~ /0CX & +V+`6Ct0+[ؼysZDMM nf}@"Q!=ecPXĺ8pׯGccӚo\r\S2'ٳg T-܂%K}T{J7eqFQ9-M4}˪ǂ\Z ]v'y>)HD8rH\`455Mj~\G<]!#N2XWX=ǭު믿~;-h&˖-W@@ lٲo&\.׸ґ zW\q/nnÐP[[ ȄvOƝGF#*\ZwȱBRń;v_Jee%n&,]HB}}}3|G i@@kЌC>v *կ~}W_=-ꫯ_+<@m_P'd99 (عsXÁ+V+dCa)ݐ6aX `)e9</s%h4#Kχ_?j$[.reLz<9ik6?nݺ#2eeeXr%."%ZtK^x!V^B"duxWo߾IU⢋.ªUV%ݎ:zdR eh!:z-lڴiB oFmm|]G v?|pZ*cVʕ+zj P` .0C)d!1-K`6$ ufǡC&lXj*~S&|N<9JuuuW9s1< AJ%Ϧ{op wqϟ͛sϝ6PWWn ӧOG^GŴO' hkkp:qM7QzbÆ H?ŋG<?ZґVK.vV>CP:R\VV=stR}P({Rl6cXb y䩁{رc3<3,t0ey睨F n6lqϦL8N.f3V\nZV/jzr'g1)l{#`N3X9 ::^4)Yw˨i|{={+--ů~\8unU7bڴQƦ7qЧϭ[ǎ~Ӧ̞=w|~ߞV}IK/ō7ވ\R~֭;t:\zXjw'BTGY`Zᅬ_|qRW{.oQ҂^xA<,,[ oHr x`bĞ"jE9`y-Y`Pt'|_vh Gŋ` NQa۶mC(̙#Q7_Ϗ޿ԩSBŎ;`X0mڴ}]:R۷oGyy vHswXL />ӅΝ;ۋ  f7iƍxg'==ť^ǣ>~;Z"Xh5r}v`M>ǏcҥX$>PO'>Qm QWWR?y08.,\Pj䰤lJ}wG}tB2088m۶eee {?OF=LB 8#dT8Ƨ~&,YDteWiw,sb߾}q jR`Z qaz‚,XMؽg"z<TUUŻO⋊)ϕW\\:8u[p!| w6&COO>#̞=*>C<.[nE]]gмxgN+k UQMqQx'%-'`޼y zYfR ÁS A3aH4,},a)X@'*X~i9{n,X Kφ NWPG-zɂPY駟N x"X`4 n7֮];cA4 !L:oh@@xGuBkss3>3,^8!c[4a0⡇ѣG'رfB^^J}^e/BWW8%Kd2Q!nc͚5i5oСCXh`2h׌bq fDGAc̙}衇&%j:tK,F*_'xIdp0Lq~e&^0ȍ`Pu|Eyf͚\:0LGJsn\veSO=_v)FMGOo/#G(5t|>l۶ EEEڵk'%e0] cp8 c2GP[[˗ef``@~]vM:^۶mCMM JJJ. ZM,'mmmkL$gzj 6T*=o4,JX#m?e͇zz:C.<. ֌XBwc̙YBowBowBgɁddFoDE3{a C@޽{q}{JJJzOX,~2Z z9SkD|^^>{ 'Pc~raՊ/{zzvڌrCa8t!8ahFtpEtpuѦҤA Ə~#JKKD~hì WIII "Ѣ?l@XZ74hkkCYY>kf 1Ԛ|( á é ! Ca\= =܃YfaͣZB(â(Da԰ިNkq'mp'࣏>( Ҩbɋ\]N]vmC":h Zאرc5k]Xf ;WC>JcN]NmFMnơæc,Ë/^qxT.Ӆ`F0k=FuG4G~}h9~8xw}|8dpr߮ î ê #ղ+@X>m>cӦM)2Mxdr ;\TÇU3Av;Κ=kL4lV2h4ml000Xu D0.NjSi;懇O]]]cXlȱX`4a0`4!|>>/|><oF]$3<'OZ-1eP% 7R} k֬xK댏'eCgg'Pߏg;qEacӗE>5 00 D#̍!wX===8qx:"'_Z`~h>D"ڵkӮ{˩CpYKYaa ڋh(txӟ>/`JK*`͇r0t \AR3'k׮E~~ڰaCFyyȩaࡽ$Q[__ 6*0RVsY,eU0W]NT,\GudBJs-ߏ|ݩ0" gPO} q`׎^{J> ~󟣡!iq9 ØeG! HL@<)`A "_]=;IۋGy$-P/pmg[0?g9H$&X 6lKHB!Zx!̚(*}Sa@վ2SE{{;z)G?Jx<GI(iØkqcNs-?4}aZ& 49`/x qK#N@xɌRuNE'7-$%=ʊ Kk߸=t--8Ңz^H̬1"M|MQ%\`X}aK@ /`ӦM) TԠ>cnתaՏ^A'琉"+w1{-nLEeI©_M˘={6AKRi5-OۈنzNbA`0=Z E\ [LXLLC?E_^1y:/OO>V肜!C)65h{˱/yaܹ&9CXh,i**5>;GFi>+lܭފ?8eD+p;2p xg$ nj97α bًJ z"Zl(_ SF;ve|f4x|l 6h#XۍkraJÐQ;2PXqW޶m[Z'WVZ-4MR s-nL3y2Ӂ''x|^UH=~>;=ya^Ko/BS{-s᭷J VubG?[ѐGTx{… )t.'`geo#]V}W\%̟?2Ek[ysJ /wk)ӿΝ^ObG_bEDeE3A ]kXo߿_NŋPUYʊr~@`]33;qFxq;kL\tID%C,çcpp0kfsR (/iuFII'?)>}K.F.`rf;`c,|r-*00,]@عsgJPgAop!yok3p7$nooƍU/KpZ0&zT!ȝb>BÇ^êUpO?#4T\w+J.=q L{p7?GT7ޑaLt}Yb.4Vr_ķmlw*X?bEB1{pgo1Iqjf,B=4RæMRg5e(֏A'pun.q]U.!sKmX`FՂS6OtT';xS|(n%E7l0^؎kr{OAc?;w.ގwIۤ%-Xf{3f~RuۇrtgB*Fl$Qך|i9`Fڍ7r`uٴa|c>ɾy`hZ۷/eڸU7ڰ>8uxI&~&T!TRe|eehϰg/QVkO3F!T;>[}q_ Ʉ.d ǹ,Ů{Ir/*X,\vD)`6pхiݾä[3gΌk)\'VpJJD 3,̞= o݆>V&3 ͝K/97H^i"yN4yma!ؠrBgCx'g**(a.̻Gy&}x b w<6nOZn]RUg'J/]9fm,a.{{D%uI[nE__SvٚmU(Lx_?<}O3͞ 3 Q NeP)R`1<(*mOصaSވ #6 vUdRϪ? 1 G yH5t6̒3~|p%^` zءdn˗7~yB`e_5i*n,;흆F#_ _靁@="k%})55lh4kFL 3 JeAqJ?Q. h1X0>8Ojۧw$ZLQu'Z(˪D"xDZuVٳGuG1s!J0bwR^=!?^ȩ̻UND"شi|ɤ}Q ش3>!GpKIp2:w6kUVəR+/'?g2B 7⺼uA˚pI܌IJ՚|x(*ŚcpL -E[? 1U2)ꫯGMj0]`ǃU55r0d-i8*3gPӧٳg6P F >}4\tcEyYҒMJJTTT?()V7K/gMY3o\|=UVx9:: hi@; ,]4,ƋY^ %@+'s A@Ŋ[Prɵ/A(c?~Ƃ|(Ak2BkhxRV+oP}k9CNIKF F÷[ grf&QXfW &~e>\8#rols&k8{>w%M 印j'v} 3*JE,nSR{^9rDg=%ec3TC3:#TML Dt񢔯#,]'fo\.y39%l6ܸ1wM/_31#vɍA2 `FaBP.j!F [ZZ{n^MK+GFAE7 ZG( AE]9^s'XY3[IL!(e>6? Q 1]4n]ӁQmE.l\CG EaSI~f!ޘ߉ɭ3_߉I2DዚF*ǥw&43<zLϸ=ۧ!F,H4]hhhL{hFLaXUYQvL⚫3әNJqUWf׭qӒ.A$Z `!E gD9N‡ \:pC?Fb,eU(<碌H5\RY518Q|8WF,`y  Cc!957Y(:\ZZT%5w6ښٺV`N\| 'қQ."r !C\Z SQ y-Gk#MOA86:S֍ f Etkè1QkJa1`Z-GwD<]H.J0H=-B\˨F0Cyﵣ/< B{9C06vbB C-c:tB lFD+ՆazMEa,`ڎ+=x4;t C>>Evm&.vu 1\q =!\a\L(t!Bga(=")M:ŹAJO!nb~)1 >rbׁa#-tB &M6mxBKl.+iL ~?݇e睛2ҲdbڽGUt89#k7+IR227 Th4>rphfv;v;*Q^>;w~X  aA Dhx ''is朅wDWևr N aX?o.>d'zR$N(zFqg8~\.‘0V+j0oܴ+y: sp. ^fwGR9+K0fFf`hL6k%dϽz׻@0L2I&hsnu}TVU)[ ӏfSgblQrZ Ez]؞U5!4*U z_;^(\u_*-6[akZq9~~%8ɡ*U n/Vp0ɀ?[5 (0̛QT^5iD"!i*a$ x8y-Z({nCcCN)fꬵ]~ǎ N` d'A"208v z¦F\|EmLϩ={dm+*q;// ܅>)9s*iF,X$jy?tEكSccn+I*"{u~s=k0寞Ȉt@`@iY)TT9s=(3 C?0dĐn` -loodx4TTpzı}K(z_9& J||.q8;:ol8F0޿aQ8Dڞo5 [?U$b1ϒ`g,NT)9{]5P'ǣ Þ`>0&1.HaPrIppKMYCBoQX!|VEG ӟB뉩qA+Ӗae6Hs(,h견eQ!OL}" h6Z;Ѭ e,7/9fόG e3xf*{eڑX#Һb͚UYwIx`5b(|]rlcL+T޴Cd>n[l=gj貢g<ӑ=zتbAEEVX C-t'?~PB.ӢXl022> QVdpK?>,Kdn[ۉల oiB=&*ƪ 8kĺ45h頲Ge{KXjt3=k/șR֕Ϣv&`5*| -M936,*0^0R 0"v|^gN"=y ZW,Y(XFiIIݛnxJeB~>1O9 U61Jo̙$P2֪J̍jct\U2 5 nfpGK?T+ 5Z!] #gmm\ʜF<81fL #c> W)6oF zټKmABed8Yj*XF[;(Js6c>c '4:3c)QFu!|qAFs\1Le8elY?sCIm_}S 2f[Hpܣ!`7 TX,3j5SkE"Co_zP-^~zzzխ ӡM X|֯[kVc%hXUU0 bLCH/33xTM}VfU ٩#kIa7 ַ+W %?bcLlW`Q"K}1{:Eo{/~"{;7a< ϧ Q}MLKu%a1 cih 83="th~K*ZRFU IDAT?uT}vɓ8uꔬ9(հE Tt(>?kۊDɜ(a;2?a%GJlŌ7k5Vb=X,L̘B@hs4vףs*`69"}~?91(`O=ZPXݡfdpEq[v h40pL555g?SNɓi v;lق[6 A!Att9Pmb-uvZ466СMVCCuٽjRu{dxMcUrZ+kW 0C0uzmE%=jjjb bhh+^dQSSK.W]u<^z%ر#gO*s6GJa J ^s5X(|?aSř#qE2sdO)i13T* 7n$z7̦8lȯM%b4:lp?}. ÐH/ǃ--hjlfÂ1gNE'NNdgj4aׯEFш6knִNXr%\9V|AX?  NgzKz=z=$JH3hB*qК,m @JI0&>UPPSl5癙L̄JӋ@ ^?3.Q9d}J %TFlRb0`PŦ0L9TGq2 g3lS)!= ۹> $b(pVBhTn1`0?Yo#G 8XaDu3%` )0Q8IaOi ĴDϼ3!R0+% Nj{(YӅ9R2US,X>ⳣ/٬T,ReaZDl,OR?qR5Ti085W_4  \5hjfHLDF Cc0fǓAMń1 ɉI>jZ\Y^!y"jS5j f4Z@&$ąe,0{za2r7S2֬8TřԬ~Ec"cϢ:g86|a6O:Y2&[P2T2E;GT*XcxyAC&"d }oʭC:@42+t  iG/T!ӚPcb>jpdg CيX '[6VF *[ G4$(k12Yx[-E@,/] (a;(P|)$tJ*cVtU|Cɺ`eߝ&ȶ#J˥F"j> 1/X'\_QP> V~F"8NAAT*Z(2fZ-c`@`4 $A6pi!0: vC`9$CR(R5$75 5LXVIדjD>,Μ9#=mA1s0$T* K\J1)~IIePpFe(|IɗqeS@+WaXv"8@I\#GߏN8qݒ}읾|` Lሲ+PJ=5/tD6 hC l?:N ɉauk'Mh|:j.SI4N-vz= c{WG&f,}#`+yR)y݈a!u>n.-'L0 2l6䨘0ϝwޑ+'ЗT0]2s0_ׅ|' h! N4p>jPөȜOzY\A/2_3W!j ~;43M aͣ#S6Ot¶p9.FЁnE x%{O(۪l&u %; ӍӌB+Mw 8d=,-\]`X`R?ًSh' Ɵ`P2ʥ$bb71= g=,cd/-[!c{c:lC}={}e?{|X1gAH`/v!R8F A,L?2.]T1LW(X,[cd53ީ|pz;Sg +60_~3"M/*B Rw dD\iyUYb:[d$6)׆ɐU\U`90Kty)) Ts *2ܧdꪔSFS;:GY%GRnVi|T݀}`N''6 )Hn*ŏjO.|`+Mn\lJ6缣rioΜ/P!=j813 (FҨ3pزhXdkJ"WOx6c۶k]6\u+E@cZ+&]3;A)2ݮnIaB*b>dUǢt%v|laEݍ1Z8~?E?K}w$ϥӟzӋaɍk ,Wq N/{َ@\䲂a^֝rGf* fˍGd +:>kNsfD6՜FvTZ;WKQuA\lͥ=VU+,5Xcˉ:bHDgrf2>rTYɞ!{pȁ鸳 l:뙏?}z"ta Q[^2'>SX@*bH3xM%餰= ܧ0_hF4Mhm:Qyuϙdz@WPU(t  ^ᏤILSU,Xxߣ{h&}X/ ][l܅U@Pqcq?n-T ,Vcj7n CKx}?;6.& 'u'wh7^?s:WU kc+^Ipx.THֲn|4V܊팪VJis1EvENs!n*EFX am߮je"3&үLV:CوNʒJ $[Wjf:vhxc`4NuY|>6naEL$֮Ym[/eﴘ͸+pp;? ֯[_ib箾 oq֏hQgq.ՄNV֍:CD4yt8t\Vue04!Tp硤}ل 4< /Z&t`P`ɵ<<G)-W )Tk4Vxb8Sß|6z7PA&rm`Ue%8Y/;4\W!t h ${T1$Z.[Fj&|68*մ;3 / r;M"w_.k^B=5[r\W^q9l6;IF~Z_N N ~%Vy)Yd`0a:lX\c-++EuU?~`v©ӧ|nшlöCp\BFh4h40 (aQRR%x>ߙkWhL; @ueqrE!/Y\F X }oӯOj5'CXMI; OIX) ɥLʫ:&lĻcE`Hs^V.15St.#V|M\K,1*ծb?qoXT ]+RGPdq!T לH9bylRl,mhu);j.0iRjmSpt?bYV^EIeCv9vq),lG;|N2F-YRNel:J–b t:zŠUyf YðS6ع{wF$Bfe>?Ƃʜ* UURjb6C1g h 7B@(qqX7ؾ};?' Ea>= }w?ÒNEo*kW}Zxl+xYL_ڤ@3g ;=,s(zaKige%&#CVL+m. i?rD*T{6JE A>CadDtppP9SI﫵 H^ßص{g7+>x'sCCxw{d;M.P\\#5hB@`@D3޺/,،h{?> ]oJH$/h?4(q1Fxfdz\_r㰂^"D[\.4tG D%R%8[-|(քl$l6\z饒';y/vZC_=_'WzwpHXI7&uه~41W̤;K#rٌhÏ%QÖ(6-j𜜟P%j8GzbLJsRյvbw-Ƌv8jňLmyF>B9ONM"B 7È h :TR {GNx^/:<^xQz6M6ʫsY1-4 n>1M @nd(-OV8Ɖ=X`R~-\f Z:R78AԟbWV]]j<32'4rk?#-j.{[nbspRcw"`Ʒ;%jr h™*biI)fEE|_G*mKӾZuHa"Z,?u5૭Ke$D'6^dt!+\uƳ`۶mP@L_5; efO=FGSg n7zY8ҽpo6s=? {U{s()9w C 5A$?CRJ5Mlރ?{,LX}o3WYó4ם,^7Z3 1& vZ-ΫQw>[a1:CF#|Qy<-40SFCq~W+U}Gq~?9shh G*F,φ3g?Y=cu/ȻS'Ex)־v@ i[N?.CM~|<ф;[VPNLЩPslYA`;36^>x ߿?=oWVV[n=>4:}~zrC"O DFf%N: @!vR-Ryfĉرchc1 F 6þx SF:v{?@` ;jŽދj!<>U(XE+KcH$i;gn8w#kymٲ---xwdn9O.XU5-qtܸ𶟆c#4iGafȘ1 BZ/| hiiɓ'%xndtBMcX`1>.db-܂^xA|vOjWHX%41Cn6q?<i/9xQXl R^h q:h©fEfv_Q3 Q>UUUg6YqmkcXh2c6)ׁ_G˱2eF7 I1:L84ߜ7x#p>`ǎUf7VXf(nl)]!=NL80g[Aց;qHGjjQe㙯z?]! ի0+̴~M1WCb_p\@\/c C_XN^7x뭷d}^yJe >Ց1Z\8/ cQ 8":D>{2`c &}@ǏOtSn&/=r\-cN r*ZC(=L P$J_@۹Sgc id2kŵ^;UUUyQf<|͇byy*+`4`4CCp{<u`11I@<-LIC L0$?8tMr$u4EK,<@rD,cX*뮻_=01;^KpWrsOSs#n>444> z)kLؓF۶m-2??ʏ^$"h4qUZZx'gNL8^>PZZ |ŏc:t(w1+Vj*YH@<[osœO>0}zi!*Ibjp C… ?`zx 0'uνBReDOV Q[[)o@LEBBFFFtɄ~9DB! BX- ~a֒GWs8=c r˃2HMڑD "%'гBMLa(@{.J[nM|[ߢٵ ۶mwhm݆믿? !i/[,F3G8q/[oMxǗ%~i)@XjU^|*lشiS^{饗ͯ^<믿^6ip6aZ7qiCYY~<+fnذ_|o۶ <+Vkz=nnuVԽO2nKP; |ݨKM`%?.zqbŊ)7n$uB[CH)RP(uo̤SqPRrn3xGj c=FBRF~k- |[o\cOKWUUa@jo!̅3k>hc: zh D+++nb13'Fw}ؾ}MΰxЗd/i16m7M,^8%pCXPfn۶m\ii):::r^N ]tٓg{ }}}9;'qxD̛7\pdr_:F"h)og{/K,~ӂ쪪*Eaƍp2bX|9~atuua(, nF:u*&˖-ߏdT*֯_ł֜)_:׃q7|nǎ9Mo,))C=FCᄒcAW!,A,:'K/UW]͛7#O9YhZlXخnt2fhdhė%qE:׭e:Gg's\M&nfvmZt$1\HAqZp袋p8iOGGgggKʢhBCI*1 Ƀ)y U8V˗cӦM̋XUU;7tdcd{ŕW^ ^O{>zD@xt$*z5W[[۷s6 BYY>஻… QQQc9yoƍC[[۔i0pw`ݺu@z xQd† +W_pyhjjߏ+.Ncܒ%KpqxSQDҪ / Unٲe8~xNX,X뤰_%b`Æ ܚ5k0::iHh4Xv-n&p 0Lظq#Μ9GիtRݻ7'۪Upwqϟ{[[[^b\y啸"1>n۶mAwwwYAYزe n6lٲ%tz4#VԄK.yr:\}ո;PYYnՕ)2. H[R8N'NLy}p wuΜ9 i +#`ժUXxqht,2zɚ^G.f76`/C`Gpu n,UCXoq)nIroKq}Ej5a]!0v;͛=zT=+XLO B(1Bpt!KLP/_ L7$Abٲeؼy3~?0PTX|9"u CqsgX-Z8NBX[}! q`0`ժUشixF2pe˖n7 Պkbxx8'ơbދEс%Rt0RI-~aP kÚ2A`"Bܮ]p1aʕXz5VX! I[o6~_ Yr ѻ'5ު*lذ6l Q+@^]0E]΅B!nhnnƑ#Grt7oj*I*|GhnnVV+,YkNH{~8qBR vMMM2`bO(Y"Qn"HGb8xJZ k֬ASSy%yY# q?<ߏE/4QUUM6+DR w tD"۷}}}RPUU;W_}5xϼ_)Gkkkq!%^^^,\ׯM3D"رc>G*T*cҥK[oرcZ\|ŸKq 괞f###o#GsJsXYYuaݺu$H,"7^7577hnnFOCR Xt)V\I@jԢRJԀ_n{\f*}r0LX`n:,8vvڅf ٌ _-IEuttÙ3g;Ƚ]~Xl ᥗ^٩A<͛%K.fo/~m?~\8K_Rp}qn'+~=G$$ ha?v8<{@`W<Ϗ*q5\ZM>xo3 S8b<CSM۽{7^u)v:ƒ jH'722N۬###LʑJBYYY R)@QҔ1 #khhh*%BfՋ&G׋#G_^W4x^x^t:0MfbܹRrKι&pB+"ϱ>3ZJ;;6wJ A^zA=X,Ƶ---k*Aף$mը̔gr2afB1 %WHm,M+Ư?88l JjHR3E$oJKK+Wo-zCUXet`?NO<Bt7у s<j1p1vs2R FahD+Ԥ!aBc 2e$.X,fMӡS2qPD$bliZoI2m962oð xf8dM\y suVĀbNky)@;TB Vd;3!(g '#1$>9fR{!a_ݐGR&R3]Dg@*;8~U:K9f]C2דô2rs=b̴)`&jLE#Pg|f3gp7յN,VH)!=0 E_r#II d^/eKfBHs=3\HYQ a!@H <+VV˱ImLzI]ǭI^W o$x6&W*8'i0jWAx : e%s藔s1h51d}>IwZaөF-ۈ ABc]IaH22\mM18roHă\t_5Ie]Fѵ|d=;!wKz ;e2C+VhJ~H /GZ%A8a^IąڐeFh+76f$J^D+hE f2|zDв,2I1B{TqBXPR/N sQ`sLgJ@#E>H&bAx#eTR!Ů+.YDquњ}SydFYtb zA#iM 11rߴrzIDATa%{DY$!}%zHEE3 BRk5y]"\Z /3WeK;,"c"$]( \bJ3JR{g)Ydƌ$9"zJMbLYf֢>r u&.Zy!*=yO@-WW+)EM=yϒZsߋ4S 5)GA 13 o8BY8'tkIqgmo2a%mbBB>8$X;"_b6Y@N ?$@^ԞnAԇ,~e>L2^1"Q6h4LR&/$:e(adNZIИdU2!eA #lA[Z%IĢˉ?"hai劌sMk:jM}H 1(hxR θ.QYK{ࢯKrUԸ~uL2qN-&N7{?="35KC̋G2!Nt7s>Hp4 ]9@61/8׮)%^,)e;uh0i3 ŊD<BVtĐ%1 L&b(R/~|@ pb!S!0a{Cs̔Eb2Gt#m"3~ԈqW:b2vVLz'M; Xw+-uA ]LbzOMdR'1$o2 q'Lj\٪䊊0::Lt~?R5@.:] *4!Th(RGU.Ipyc<#Z 4LwM7Ag Ÿ y^qqHn滃NV1̔9 ߃& 03IdΪXZ 7.YZIUvnu*SRSƍ;sC4! $J~Gг8>h_ի~{wh^Bg5z v6.T1^/ڴhT|/ ƴ&sL NcXPRſT9r܁W ~& 1i-oAXD9z|-|?T'%;|Zr豠P]MA=F9vQyߥs,)k:|kQPҾG=6յ8Zc";N6L{6<59f|Z|[۬!QBbl)T {!| DQz yEb6wb~g C__Ν;Wq2DI 6h{ا5VP7h)dIkW:!ƒAB… {GFNzYd&RC1g}޾}nZ, zL .eɮ5Bؑ ЗPiku{/OL ߷7-ZCpVi֋\X\rDivǣ)i\zsX+>$OIrF ZZ'QH/&zSfa؁*b`AГ^[mf2>}_~eprrR7&1>9&_nmǀLDZӎoAP(Y쵛qͤqK8Vçi6Us@cҳ}h7iZQ]jZ}fX=VG ZX 74}4Zz},ލ_7~1o"8k;zpW/➍]]]mNbeesA&k_|p. ݹ W ě=7Cl$_VVNAi罦ɇ{^Ei݈BKgyFӤ"t=ʠL[aZGEtݍZZ鲬h!z37Z{#_ͳ؟5.Uk(i!w6ZIX$$_=ﱴTXr9={QAYfF2FIwIM󷵠 _z,.RJNbw9Aܦm}+ݻة,7I^UZy-inƼ߇+ҏ V d>>_|¢r8}4^x i[pW7[\kN[Ψ ?]GZx뭷WMt9LMM# Vf =Hi{OO|M| uffO?t9Zuhh|5 ?{^-{odxh=쀫jxg2lC,!===8wx *~ .V2dg9owx1qk*t80'%Jveɓx饗G( ( >W_}gΜSqju?@dpm}vKfUev?hS|X]MWaI#azzo:- .m؃AxQ#.V Z__Ο?9 ,//Ν; VzIIDAi&(by 1[ 4`@@0s=?D.!_[XX^ _?UA kaf}+WO> vu8o\X+*Ъ!, dcIHo~.}!SYD([YldR~ 9P;p"P9dyrm"@P(9сNytvv r@iVYE6A!2"1YBX[YEi6SՖݔ W V6Wlpe&5^|N +Nb5^V٧y"܅x\O\ʣ(GX(\ PMzȠVy".B9WVOZBA+VFB+ f!܏^$p[d X`71 хҬE)d]\@p "? WXf!p?>"wy5jtʖ.d AC>y@ Oh҆47 EkbH6Za{_\@@@@@>Ķs2h3V5?% IENDB`mapper-0.8.1.1/images/tool-boolean-difference.png000066400000000000000000000011521325266516600216130ustar00rootroot00000000000000PNG  IHDR szzbKGDP pHYs  tIME,6IDATXWJ0QA.q# (^IwI;c6ӦwzιcۧX9o!?fhsU~iGH Oв@eN0W=&ABp!7^G:q;^wv3E`[!1ʁ`ɰe16p+$7PZ^T{w?*v*!yw u]/B4Eba PwwI`!IQ$s3{~8EF'a Νd0{qHT&^2C)HJ0G]*c^0Ћ;9]|xޖ7\<JpkD_'Ap"{;*$JN8D(H2$aQ0$Ա\PJ)%g46R`!PJɬ)];f$j"$x) l ^B`M)09y9WSDOh#kuh l;_hS`V&_ E Z&`Yg/nq@-tȍ } aB i_+^{1ӈHԑ":A yM9; "bHGpfя&yHST< v4@\e9,,}p 9_a^9X:cZ0# \-7q"`c|R\"`|F{EN!GipC2Yb503=J9m hZuպQv3a@.p /s?mwZ U)%f'R3MSV]l;E{%-6"88 Wb+ExRSM'4 ٤PIENDB`mapper-0.8.1.1/images/tool-boolean-union.png000066400000000000000000000016171325266516600206570ustar00rootroot00000000000000PNG  IHDR szzbKGDP pHYs  tIME)IDATXåW[kT1,ڭ,Z"&ЗE^Zm3>6aϞ|eD R K-*& X}Β%`8o< DyCˑv!QDnLA$5'$T! XqO`2 E.$B̡C?e; մPJ~MK-y֣#븺%In} cY3!/7=z ERC Zځ0y~m%c&3md-u @F?Ln"@ΨC޳ юFG!iH9-S ( a6<ٻaK@ B;,MTDTX>BB BQ£"d 4kt.$c"Dh %D&Hx  "'xX!|nAO@v?'<2M:" vLc0UY :jˀ @>@m%WI& 'VS{ Xl\ap_Lw)A Nڎ΅&%:;sco^=1+;NOϠm߹Rgٸ켉}EWnR(;;$aIM+=Mstai]҃Bv_tгK} Nk{}޿ڲt; `\l : ;/Y[=Exer~x'p84Ģs@@$y]n=[O|[pYh$A$JVi>G3mfw˃ W9sk{!?mM=s`HӮ0;_wC0aʞ Z(:p6vBo7(ևR[hv  299Y5lʞi{J2pZJL:^v8|-[gʞ+ٰ6G681k\rL6~n}/pBN{WElYVgSS6!D0 :a}H׋CR+/{麎%`34MS(i* v.w+ tEQBצ|\.luݺCl˅>M7OlZ,p`qx.bWmMlZ|RD HhۭJ%<&)e6Mp8It: Ø 0m{լT*EP UW.fHb1 T `๷aB]XXhSUU,B  h47oK)!,//\.vuuR8&Hh !>Fttt0<<p0 k[-p= ǙB %R5MԒ9Nb파dגmvUUNfWaH 8ZZZa dbee[n@,( .녹/JYp:pic㇉ݪ!χT/2eu> H)w0:?۶у`IENDB`mapper-0.8.1.1/images/tool-convert-to-curves.png000066400000000000000000000030251325266516600215120ustar00rootroot00000000000000PNG  IHDR szzbKGD pHYs  tIME  \IDATX]lSe9[n:W(TM۠.bR .pŠ^ &TD(. *iY!Gf"Ղleٯйnuv;gs雜;;{z(3jk2`)D4M ^[ .\lԤ!W@6OW]nV cӁ{k%Ŭ׫gӃ%&`ӱy XUi9KTն%,⥹Go R{{$%FO~nef="W8H縂KlM+mH/_X[Z  J">T9=H"Qk`}U|z`dTr]1|c)x ir8tެn?00M @OT}zghxǬ%do}\mm!L%MF?5;6ެӝ2|w&)ë5'^)>cf N-u_~PW;w8`d 1;]4Pm[Buj{+$eyZh8s;fߌDtr&7򴔖$2ZjŋR!z> |6ͩ]=RKCuV#־{4 jb z5>{KgxX~2_ 5w׫Y؂P5M@FXZjH&}R I\ Pj!#,M J+0jn}Xn$U#WQ*m@䤛=}&X\] TI v?sR2{.lZTFPLIx X&&!t-*HRުDbGdHJ QDD4;ؾ_=蚄*a*FUL$&0>" G'N7-#˴WIENDB`mapper-0.8.1.1/images/tool-cut-hole.png000066400000000000000000000045051325266516600176310ustar00rootroot00000000000000PNG  IHDR szzsRGBbKGD pHYs  tIME;x AhIDATXÕ[l3;;{׃^`wlM%EJHi%DiIK[HCBCڇℋ(OUUqc.ֲ&1oLZcbt?x>|/}g߾}<:t谮]v3ghnn.3Ms}<ޝ?~ֲZ;#uccyfާ~Z7e ,f͛(y^qeF,UV9dYl8p'PYY)Iccn7ϴ^zڠ /~aX… ś7o.>xn?@aW5Mƍ 4Mex.x]ٴiS'x< eeeؓɤE$ 4l\rE5 s`Ϟ=ϟψeY8N >˗( *PZZeYD" NӓNӿ~K8>>^kll A0 (XT*E6%CZZZhjjBudYQ[g$z3g 2eiVʕ+OF(œ9s0MAtp8VEnjjbڵ\~I,l6ˆ @4]F}}=cYsQWWn EQdYdfCCib&`/NBBϟ'ɰ`)GGG%L&1M7xCQBT p8L"`|]rD"y饗PU˲pݴb Dt*" ( !>:HD}}=mmmn,BUU֯_ύ70MI}6^;*,`?I`qYYT~<xIY$h4ʲe˦JYY' xf&/_N{{;>XnXV"Nsھl6YׁYaa!CCC9s-[PTTTTTIUUkOdү(4bNk׮y!QRR¶mۦ:$M'r=1ؿ?b1ڸpi`Bǎcppp墿s6 8vO&HL5 266w}ƍt. YQ;w211i lڴ;vf˗/n:N8d,]O<AvͲe˘ xr\իW3w\ZZZذa/_8>Ǐ,ZrL&,p8$Il6J aPTTDII (rΝibEZippaKkk+o6ݔJlSmnnf``{;twwH$D"({௞JFE{e4Mm߾EQzkX$Ir'T+((0 @ @(⭷"Ld#H _̝F@wΝ+%Igr-|vEgև.I$vAvlpp0NuԩS}Wݿn`dd[rʩ>?),,ɓ|>fnΝΛ7O֖ʇxQUPP>k޼y^eٖH$Ì+dEqQBv8zmUU㘦y%NO9siu@e2nvPqq'.]\zuN{{{ >@7MsȲ;hK,qu]_UUSIAn}MӞamm*"ܼy3mPȲLGGGfbbݻw>yw=c 0w8w[uz{{,IzjW*:;;[諀***.buuu%Ibll{M$ #; '˻ 0\r~3ht,N'+++:΃ @ၦi*JM˻[SRRp4M` Оd1Ify`GAŧx>cw^'KcIENDB`mapper-0.8.1.1/images/tool-cut.png000066400000000000000000000041141325266516600167000ustar00rootroot00000000000000PNG  IHDR szzsRGBbKGD pHYs  tIME 7| jIDATXíkl؉18LB'!XA"BVnUZRTʖ A£EEP*] R MW@\I8ulJl3sCʻw{#={o=12't]f.͠qܹa|]$ѣttO^kYBee?uFG ז. I/N=s";v:;;m%`,[9Ƕml,3ghe]$tYM555c s444P傟ϻj*XBr͛( 8rسgeYE-CWWןV7J]]]7 h4jUUx 2 "޽[ur-G!f!!Lb݈D"vfYֱd9>rW^}_1 ó~OB!chh%UUr|֛7ojjj0<<˗/#ut:x9>J@@RouuuM~n\r^xecH$MMMJ體eBdAFAQLMM2; |+,#J aH 3is}UUsҷ<. 0??ii|6Wnvt:DZebX.LW诟  ^7 9MB&AZZZ\.Ʉa5M6cz(fajj*eYqݤE)ؘ;DN/YMMM/QJX~snn.L* |iPU^iV`dd$d2 A0==* _0MW<o޼I)= >T`0覔4+م)h4zIU bSPܻw/es:0 C0?8D"ƍOB^ZGe!D"]F&(O(0)IN 着.pw'<|>r Jj%Lf-Ni@ P󌱋`]kVRJ@_<:DQ<c,t:i,[]]eD`)~H$ YxHt²!M򕕕0Mi[`(VWH4QUU|(rܾ}[3Mh4_V__ox<ϋ8YEQR e vdttTc .hllew ?^)cjllxsssxZӴ=X,(}ZWWjh@ !s_JҋFJ****^l6ۋeX333#,#2OOx]\އƿIIENDB`mapper-0.8.1.1/images/tool-cutout-physical-inner.png000066400000000000000000000036121325266516600223550ustar00rootroot00000000000000PNG  IHDR szz pHYs  tIME$4T])IDATXõ}lSvĉ'/ 4nB`%uP ['vBT:AĨFB&LP[DiREP$%dɀ6#$ ~aC`ik]ݫ{|yJ} ,[.,-8G>9`Iq;J64 iqu>kFJrp\,YΝ;zȲث0ڗ_g\O|zT0ّ R[ k.<. Á}p]~~?Qw  2`\{:$6l <^э=)ՠ{"=bI{%}Iq!.?o!?-{h_!f4Mc۶m,֓}d1(RaB@&=dLؓ% ^B Q‰\+F+!yqPޭ({r RP fYb `AɤN=HTL' cŨI;رc!ә`j?` BQ8f4qigQ\zNK%2R$!" LGFk}|SI3 7͐:gmuj/k%UUܘ]'v"w/`x_rQzn? vد!H)=Ht/s~@»Wtߛ!Y HC츄t_Nb8\~଻Hpkf!ĬIc'{Hgbg-x."1u/ku@-Ju}&>i)=Iwx߼6=$'n=4-gّ)7pcz_BL=᧘J|$BRFx+>5GA+ `zB$=&"uC9y{ jq➚us>jל;Ldf6(` ̘Yf{hN-9_ uXŽIENDB`mapper-0.8.1.1/images/tool-cutout-physical.png000066400000000000000000000036101325266516600212420ustar00rootroot00000000000000PNG  IHDR szz pHYs  tIME#.~w'IDATXímlTY;3 8,uqD 15!f t7"Y,.ª؊]mt^ܹ?R(ЪLNcx }N/C Y0{AZSoZSEK= },, e5|UnL9amVB9EK OVbw{En8/E  L517փep샽 tb P$t@[c Q>AxIELyAuyS | R7ɼݫ] )h7Cֶ)/v@^ gϬ35XɢV’̝՜2Nʋoпu{ejPBt<>CNzϢkǟZsqV T5yGA%H'yzo`!x X)6X;ɵ-' BEC7`6@ PPQEZ+ѩ;!{iB&.]B:LG5Y)8ar9lmm۶a˖-gPH+(J$I:D]]x`0q466l9|n?%":~Cl(QbD"4M k#t;̜gG1ю%('H)I/:p X9`Noq g$G$8 Kr'k%(nIl { YM$50tҘPF1rMqzdhz˗O4.?5W iJs[].u`7!0rο xb13+Lb_Fu?PK{wY.<{!] Ϧ6L6BAdž0͛uܿ?NװFb(j<"RN+lbx@ݦVFDl'Hf#VA#j2$6w Ly@L[ ~-lF S(῵Pgy*7ao0v"F` iC\G_ajy-8]PWጟiy{>qHDxtJS4. |jc5# RʆrI` BDR iD>79`8eP\5Q6j"{4x` sލָn@뮴jՠdsFLb4p ct T4Ԕ(_Sc'b@3.@zLEƦ\RWzx( #| jvN +aZIENDB`mapper-0.8.1.1/images/tool-distribute-points.png000066400000000000000000000005131325266516600215740ustar00rootroot00000000000000PNG  IHDR szzbKGD pHYs  tIME3&liTXtCommentCreated with GIMPd.eIDATX1 X<B c˹WBrىND8&IH㰌r;_Y!יYh<@ʜ'ZPDʗ`_TB.fP'7ڎjD ̈v jMKh4Fu u|V3m%LYcXM Wr|TZhIENDB`mapper-0.8.1.1/images/tool-duplicate.png000066400000000000000000000016241325266516600200620ustar00rootroot00000000000000PNG  IHDR szzbKGDB pHYs  tIME ʊE!IDATX͗MnGj!,㽭}-2 >qi x8 Ztˢfzȡ(@ rիWMF^x'nitVqY6v' i`&oY6 La@i$QJeH8HDD@ ^ '*?~.nk?ޗuK?Fi迾zz/_? D g4r֞=v0m%O1LI3r PaYrug6u9"r TaG|\J>iSaP¢"]h5^Q[H dV1CR w+ֶS}恜a˜ŜM,B#=L 4gJWcnA=]T(y>DSמ;٠B\3Tbt'v0ԍ ,cL K>RL)>aUzI5(U+,=bvX.F$H4HՊ P.g/*i\\\5Y//fgnpvό6X:8RYrn'i=⻭2RڋwHgA WR:*eb~Ln3Cs¼GQ^xy֛W:bv{5ӧd|\bW}~sWswҽ_{]] QfGrkyg}UC,< `ZF/&~vIENDB`mapper-0.8.1.1/images/tool-edit-line.png000066400000000000000000000017041325266516600177610ustar00rootroot00000000000000PNG  IHDR szzbKGD pHYs  tIME QcQIDATXýOhw?lLB@Z5xKR>=E6RR%مrIמzMbפx҃\P$͟þafըY۝f~3}~CDH1-? ,"qL[ZPX;DX\r'efffaHR   ®l6k{{{LD!6=|'"M`tvv2;;K"8 ,<;_4 )g~W"2$ 321>>`KXa@+ y2L-"Gc`fA}`nnt:pKDǀf2UGGOOϑPM4s0:Siﳳc@7p Hx(j'[OX[[(J`9WzWo#cxx[[QЇMf63ݥG/p=os @U`eeb"p9W/,--wº{ܐ6&c/fXR#7&1Pw)wIDIENDB`mapper-0.8.1.1/images/tool-edit.png000066400000000000000000000015541325266516600170370ustar00rootroot00000000000000PNG  IHDR szzsRGB&IDATXŗMhSiM -5U00zUEF(20ҀB7fʥZݷq?2(t,&縸ۨIz{-ssspHQ+/oXcR m @__\.G6MEdgMMLOOٹ ."Ɇ)LNNH$3"5@Sc{8 0o)?288 4HLJ  K1 h8#ɤDd{,4|[ZZ"NHsPB% ޾s3~.֯0P" طw/gDtmR2E8z\R]L!6Ȯ:dQ`C6֚`Af4{0Zr$Ƀ΁ FG͔L&ïNUUPÇ860pADf@hxֲ5l'NnȾ(c'TB1:]]]m 5Tj7pCDmvQ<0 + & yY[[38U2??úvν= x \ Zd#sOu8} ϜZ`zFIENDB`mapper-0.8.1.1/images/tool-fill-border.png000066400000000000000000000027741325266516600203200ustar00rootroot00000000000000PNG  IHDR szzbKGDB pHYs  tIME!/IDATX}PT?.+,Ƌ[:Qr&bv{fn44sL5M5Mًe/ it% 嚂 +CR9|{^Ev N0Ӂy@.0 :zb!`/-%PiFYWx>ia Ba'%XT!@` 3maRIL?GN덥=ADi5   1!q~< UZ=5%a  x:};`I9^؏6,Oec .r냃|VnMI|$@=AOه{ e]'.2stu_41Euw't=;R=ם@Y!PUѾCΗvw_ԏMhiٽ\w`3o$}И.,-w7:iYSc~nأը6ojc|Օٳ@ Ti <٣(> %xĎ{ W]C^^O-}jt((Hmk:2.~P : =`Ya XYrs<k5*^'Fsw%pQvϖ ,ܳ0矜z (A&F;yFjq=fw[]]]H)xv_J$"i&޾q`3Mi)sZ4i3/w5wj{ƪU ÏP]]Y}sv&!@Yj644MX7(d9e$6_]wdpcx~;&O1c Ҙ`t:8i4u]xpf{S@|+8\Ǘwo{7m:$(|\YIII B`ӱD4VMygl Ҳ%s|2%'9qܽScy|̙ٗs-睗dggSTTDKK@,P1MsiB(tt;ť+M5p#}| sހ9Ż\РY@|.ڹ"v@ڍ-'577SUUEcc#)G_Z† )..fpp F9VG` 7/_%KLZx1B(//7}&V(U;y97g?u떕 RmJc  E:csk[Vu?PJM2\Xzb8Dڮ;tmOǴ9ӞHgLK'F_guD;{:v!_/,(h-,*ܙ}X*+?/=;'='sC)@ϋo`kKҾl k֮mFnn.z{ZDm[G=e6[7.[˗@gG'|߇񦦦x2T6wa/]/++^QqᮆƟ܋99ٰmDϟ0.ŦMO-0 J0TC/֜RiJO}LeV(o@NΤzE͢‚cawxvQA3Vvl7@j:҉HaՃKqɜ}Z ! @&m۰L:+ܷm D*|$zQ"XE`x4叠AeY]]Xի ɀs)%nܸ|tm؈SNbddؼ+@yEJ %x~|uyx;/yG\ϜF,+ Z0d8(--EMM ffš*ƄiJD+Ě'DY^! 4sjkyzVheY<c1tu=2;_@+ .!bn:7/WǬ$sZ;ۋ˚@CC#=aYT05=j,)n\döm @ZBˆd%DtSp<σyHR 0T.DUUjPZR uAD\! CLNN\ZZZJnc4a2mÐBpxQRR ) \tGp|'> "s^[ =+Ǻ;rh|pϚjPVQIW`z16~5FrfB@JK$LΠ폶RjU-+ 1˂zRƍ0 GG'EiA@A*$>||iCk ⠛W 4aHQRK6q #JBtttb|"dvNnqB"@ "h /!Pʀy0M/Qp|c`|X}nQٻsgQ^^0 y\ׅcs~3"TTT "D[|0\7L!ݻq"X0JɘC"8q~BDa!χC06>_is+4NNLL7E]ʚ=(Džaۮ=:2DU-yRBX,6qA) +F`1۶119ɷs/wd-DW~ASZ)ðtw5ͦӭi8H}a4\Fc7ŗŽӧgSW;bf>{E(B^oLvt:^o_ogEEq8;j!`Y …&=1>ob,qxIK_;6wZ+eK__QUYkWCii18H&s6}s=ubz&}/hY"RZҊRk;7Pbi,+}avRJpƑSpٱ'?k1zxvs|I{7:oh[BZ&Ř%XKLʹTZt qxC}3Olz 8vl2<:kϾΙ8'/><sFhgmyy$`bd"HZCA2"|dZp̩(׆827hLpcƜ*7C566n+A#m"+'f*S<Ғ+H0.kCkJ+)"RAT1Д&}805c" !F|B9nǢ/yUU%Zk0!HV>AdDcR3mpmDݐ $0Ȏq2^/t)9IENDB`mapper-0.8.1.1/images/tool-gps-display.png000066400000000000000000000017121325266516600203420ustar00rootroot00000000000000PNG  IHDR szzbKGD pHYs  tIME  1&iTXtCommentCreated with GIMPd.e.IDATXkI?]Uq5c~lQ߸f +j0/xN\Cs%;:{ܚJ $ Lª)p)HU}8+*( ^|bC0Ѭ uT@B\]v{`εms7Zn˶'ޙ̭=Y@؀ݞ"9V3Od DL5&=M6x>p]mʔ F)]s,2y)2XKbT饊*sm_#@_nO/') TYWupb'-5˙_@G YQM֤J!" t]f,# }$;jkkǧ$ n3 6yd30G~tG{<}E ."D:nmrxviT49[AZp~[Pon.cISh5$f磭Zp(à kW=ϻl04MӅ{b:Fh?lÏPn5]6.pYt d9 NTl["r†h{ﴕ*Y.(&%W&PIW|iC l}+Zš熄'H0Bm10 dZXnްq B@_[g o{mY2ƭD #fM]en 7Ml@U!$>>C' )*/>(Oȃ-cnA-Av4t9aể$:iCP+XQw i }Bu~0Z αwzU*xiIENDB`mapper-0.8.1.1/images/tool-rotate-pattern.png000066400000000000000000000032171325266516600210610ustar00rootroot00000000000000PNG  IHDR szzbKGD pHYs  tIME0/iTXtCommentCreated with GIMPd.eIDATXOL\ƿs{o 4#aO5vaMX[{tʶ6rUYyeeiW[7&a#jn@")2Eb\"3P {λdQH YTjVs~{u/ڏ0EQd )"%,@K)dGG0x<z90|yޘR(0-bީk㤺d83uݤRclZhtt>Z4M{.xOnܸ;uyii t,B&;Tr>0 )H`ttkR+Bܾ}|[SNb(,z 3czz&&&7ߖ۾c) mOEQ{2ll5EUT=ZkA,pYRJѫW>?ɨ`؈Y^!s4P*5H)Wf_ _Ѐ5IENDB`mapper-0.8.1.1/images/tool-rotate.png000066400000000000000000000031201325266516600173770ustar00rootroot00000000000000PNG  IHDR szzbKGDP pHYs  tIME"4qiTXtCommentCreated with GIMPd.eIDATXOlƿzgh3d1xl2A"N^"@XVQN!={KQ"bKъ aD쵼38ϸaml,EJ~+h/("K)/ eBf^b23OjǙyFn޼ɻ6vnRkeYٶKRÇH$@DzgV뜝EZ}_nB@o 0::RT*uL  "i±cOf2ha/..E@J qݍv@iR.éS`tt)|>C1;;KO>FQ yJ´,+UqRHӜfnR)yooL&V J%i]f~e&߹sX,NA./--,B{#GTR.0 )H`bbSSSkR˅B9u'O3#bDQdYYOO!f J%M_fo1Dtܶ܉'82M={FDtoxSA<5뷺S7x-tGBTUa_Y:@Gh4S<㳉Zlt;3]ۣG8Ν=ۺvo`2R"7xX$)eZ4Y3/m)3u 8lhQmۃ@J ;L,nlۆaWmj0xim>fVXYYA8NJJY/8NP;(PV.3?jdZk9fw]wuIxW\!0!RJݯT*{>wtp6oz<+L aȕJJB(ǿ?CK)6-|eK)yStEMJY`l|rczzk˲>) ; AՅ @bXwx 1 8)l6yQD|RiNk۷o}WY)Ο?dfJ !/Q2yh[I_A{- sgeԧ_li?~2{ϭ^9QW !'O)󪳎\;H`1Zz DV N3w ohq/$JI4ƹpc1!-LQaVHi%C)ni+.Ɍ}8qr?اf<hu:+!HNnyB\q,Y!bVUN|x. 6qʷ89wE=( Z ]Zt6>3?zvUcR/aVN;@ÜztQ⡢0S:\}[/q-h^}UcVlz^u!:cNwN:ˏFvTsv8/F n|>~vTui|S׬rv1M/H|=61Gw[k֘}& -PQ[OIKw|[l lEx'6n$\3O6U޴x>Va5F)}.@ VsI覺Z@\ ![3߬E*5H(sV"2 2sfKyR?0)2"9\"Tfzl8̦+QLF0 T)xgҳ*@=R ՖR<3T){ij31.ÛTob7f0]Fd{.A :\t9R;U{CTG UN3ݣHpP=X0 cxnrnLYZZǬ 8;wxn_[Q I;.30WG/5t]o,>)Eg9񤍷2l;~?ЦAPχ5QIENDB`mapper-0.8.1.1/images/tool-switch-dashes.png000066400000000000000000000005341325266516600206550ustar00rootroot00000000000000PNG  IHDR szzbKGDB pHYs  tIME 3PYIDATXQ D= l]bm̟0qNJ,v9MS9SJm1wH)Qu("ܬ,Xt{DhcB0X 9k%pι b]agpѶמ]kQYc, ֛(DRQ[c=']1mzNUWnknUmp$IENDB`mapper-0.8.1.1/images/tool-switch-symbol.png000066400000000000000000000003431325266516600207110ustar00rootroot00000000000000PNG  IHDR szzbKGDP pHYs  tIME#)pIDATXK Dp8/]07鼕 JD$ݙ̚{=؎rE Ov"b t0w8}z upx UGZ rIENDB`mapper-0.8.1.1/images/tool-touch-cursor.png000066400000000000000000000022171325266516600205440ustar00rootroot00000000000000PNG  IHDR szzbKGD pHYs  tIME +/IDATXWMh\U{oclikLƅ @򺈛Mظ&2Pp.Dv&1v!nA'tts]̝ӊ>w9C eYls~Z W?;UbyK XuhxMQr^Ht1zj6~95>m/B!k*P(m۱Pps~cYW< `ʝ `iOG QeSS>f@]W>f=77wc&P帘)͕|OLD٦V!ĵQicH/Aۋe988hmoo΋we{F/T:@[[;AP/nfa L###c>[,KH rx0nhhy_'4W/TED 2N---냦iv0 beL}kJ@*$rP_Wm\rH&פۦieL8秌j<_>1a0)g`c7L];w8D)X,&KH}7\8XQ45ܽ{p~~aceǟ]^ZJ=c#Oe=(qU;H$~{j\é+ Y =(cv8ǖ~!9'д'#p++d4roRY ]7%;3X 8. Վw>vP9AD|J_}vTϯ }5If6 qs{1v.jD]{ѥO-k^ ӠYT1Tf#>Wsq<g`oD6  Q&\0jC` 죈SK}27h&}qZI֦Mo^@OM/ ULo|/\A>S~7jE3=bml)|>cG^6Ԣ&& &j,pP Ac" 6̟Ϗu{ G_^xE"*~04H f CbD?e2{[ꐕ`cD?|xASDjwA3h09qOAYv=\WlkwNزY!XwdWŞ߸܂'Ѕ{?:{/cu}VSV5Ț {HJ%7|X놭e^|]ک"vdew<.1|KgPn©X7xC>BBXU3v|6* 5pu-311Η2_]v&!T%Vuf}MǿngG*ڀqmf[d8/e@¨I@#-Ϟ9 fJۅrv~BdUjf vvy̓?[|#mܨI]≄ 36 @&[#cgSඈX0{i "e$FYi:"B@8Hz@(+g}j,A<J/*lD""P9y"D>лp~4ՖW#H<̏&[E\(I$¹|}m!71b́DŽLnԨvn_V3gڦ#uT*FG:N*fU59 Og.}ՕQ$s͝t P87Wՠ0v]* D`**85w5q-u,}b1į^Jhu?5XU]Sx+ᕷQY{3gH&q"A- tRqqo5P$}-,=P3@~YxnGXtS@P T`JaIENDB`mapper-0.8.1.1/images/view-show-all.png000066400000000000000000000023501325266516600176300ustar00rootroot00000000000000PNG  IHDR szzsBIT|dtEXtSoftwarewww.inkscape.org<zIDATXŗ[Tu?sϜUggg5*KOFd"i=P[ x)JX"P=*治[og߃;̙q$c|?9GH)?Ug4ƂjB=ڧsH߳r/?l[Y4_)>YaLa<uQ;( l {I\'e B0$7t GIYTkC%DgiO'Q`2<>li3`a p}:rJv{2\wD,y <|:D~e6EuE^Up,2\(@QNVi*V-tϚW'HLF,h:WԦ;  UV4K[HnW@4p; uϬ6"]JI._l>0)e rZu#Kf+LLϐJ[2OsLDY764nFƣ W=D,d,A<5U53M2M0z=+@y~u0)7C8('r^ u7d:T4T,$ӱSW-|l͓ReXx/ie))KǁeZtlT,9[ g?a2jSyL?Z.G)}B !|Xx]U@"@DT !T8]R?2uIENDB`mapper-0.8.1.1/images/view-zoom-in.png000066400000000000000000000041701325266516600174740ustar00rootroot00000000000000PNG  IHDR szzsRGB2IDATX͖lSǿ8NL~`J#ZuYۉ@(kmjb4MZ?ѭRNJԵk&McE Ut$L~%ĉ!sbݻ?0lcGz]ssa.sKnBӔ' !_y^۶mBcGcٿKx7i>^ߴi E4M$Ie ocgZ[[kit]eYmAE(ۍl6.!/R8p@@={ $dz>}cFoXrSR+.G-e)2Ν;ӧOX,.}#xwJ Bd޽HŇ/DzJolA'R:h&+G<7_ΆeFwwx> ù{wl߾= nŏ~s`#.|hWW ðN㓣o/W?ӱ߿⦦&b^gɲ|cccx+ [B9rESEY8^9^vҢrԣH\QkCj}ڵk[B/;::̻>buA4TĝWXpy")Cpؔ1ʘeH#0Gn9+V7ޗE555`=5g%Im۸8+|dl۶)5,,ʔ! ?[cz8IE۶QVVMI !R ]D"ڳb?/:$҆-XE3ͼ5ő20b0`&EH&(]scիWᙯ,.{$Ԥ9B44d<7D`6jٔds!~daMLL,XYW\#r<~{iƠ3ĶE)˽ر1HFF$}4aRNOU$ NjyE $Mp`=W[TXuid,\`߾}i.xrsy>@M X"XBQNO7O6dž߶n&S())EPD"[h4:nkk UnذeeeRaUO-`7v>T(@*.\MpȑAW zP(yضb\.]בH$p)lݺX B(++c g>w ?~<4 ch``)Lz,4M$4ӃK뺾iBUUFG<6$y=J r3x1V3e7B)f:v%cCCP(\oQHYqIn0a3m~lٲ;vi0MLmt100O~NY8X4=vbXիyQA!hmm͛7*D"OȌSs3Y.v:ٶm0 u||<;gϞ B~vUR444@$W_^Xs쮮u].Z˵tyTQ,3 ]J9suUU !A@&"r@YRR"&I{ppЊD";%Kos# 6ݤfP)^N׮.'~Wg{ɥH&x<"t,:1z%Ra[h7W .Ye/-Sj]UZ"zū( FGGB_jCUUȨE<&8+=%x#%lI\kj]r#ӵSMMMeہ;gQ*ǾF{ 62F3MꌡƒT8pDkFj}=YQh"0ƞ7 $,\P^GkweYJ4Bc(C @SZ: J}ɉe@B0H%:eMyJVkyQ"ղPfɄ;驛.$A!N@pӲ:AZIՕlȉ'}x!ƘMjCts2 L(6"1\.@b!Yiwz~#{d7+T̉PrFS@ ̩laqNt:j?o؉Wonns_ZyAsl@@CY)y 84M;>,ugbbbjff[z#9px  @@xF yhk6. T htL ҟ~͎z; ^>U-J ,s(z^/̠4R /D"aK "ES/&6j!kZƒtG|"}g03EMM n7*Jc$NFQmqwwO׬Y|ӦMx<p-Vzں/~rWl6 ͆X, .BUU?~| @WOOϕS|Z&A0 |8rזswvf 1N[cccf$1n]պ]l7wvv"wE&y壏>#ܣ5nt:͛7PcUB0%Bn\gbb s%sԁÐ9IENDB`mapper-0.8.1.1/images/window-new.png000066400000000000000000000012371325266516600172330ustar00rootroot00000000000000PNG  IHDR szzsBIT|dVIDATX=OQsάfY"D ab#VV„CbGabL,h҄H(Duugw{,XE~@"orrɜ﹙̅SVW Ź]nKT_oXYYy$b7r7 "@ (WwZ̾XZze钿~*5&H&ʬ|PU=~S?0x!ij!+sO.**=hc_gv2ezDIpֿi#F\ j6L\V644 ^@4%B<#RF&YB1$C xI7AZ@ll,P pU/#edb{m#LT@Lj `Rs:00H 8TFQÓs|Eä{sNa>=N/('/@Fpv8u"wc"9/9DD4][]]ߵ?$I Ool |=e*ܜ]RZHDeHmIENDB`mapper-0.8.1.1/iwyu-mapper.imp000066400000000000000000000025151325266516600161500ustar00rootroot00000000000000[ { ref: "iwyu-qt.imp" }, # Mapper uses -DQT_USE_QSTRINGBUILDER { include: [ "", "public", "", "public" ] }, { include: [ "", "public", "", "public" ] }, { symbol: [ "M_PI", "private", "", "public" ] }, { include: [ "\"util/qasconst.h\"", "private", "\"util/backports.h\"", "public" ] }, { include: [ "\"util/qoverload.h\"", "private", "\"util/backports.h\"", "public" ] }, { include: [ "\"qtsingleapplication.h\"", "private", "", "public" ] }, { include: [ "\"ogr_core.h\"", "private", "", "public" ] }, # ? { include: [ "", "private", "", "public" ] }, # libstdc++ debugging headers, for use with -D_GLIBCXX_DEBUG { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, ] mapper-0.8.1.1/iwyu-qt.imp000066400000000000000000002656011325266516600153170ustar00rootroot00000000000000[ # --- Generated by make_iwyu_qt_imp.sh --- # QtCLucene { include: [ "", "private", "", "public" ] }, # QtConcurrent { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, # QtCore { include: [ "", "private", "", "public" ] }, { symbol: [ "QJsonParseError", "private", "", "public" ] }, { symbol: [ "QSignalBlocker", "private", "", "public" ] }, { symbol: [ "QTextStreamFunction", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QSetIterator", "private", "", "public" ] }, { symbol: [ "QObjectList", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QXmlStreamNotationDeclaration", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QHashDummyValue", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QModelIndexList", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QAbstractListModel", "private", "", "public" ] }, { symbol: [ "QChildEvent", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QXmlStreamEntityDeclaration", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QJsonValueRef", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QAssociativeIterable", "private", "", "public" ] }, { symbol: [ "QRegularExpressionMatch", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QMetaTypeId", "private", "", "public" ] }, { symbol: [ "Q_PID", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QMutableListIterator", "private", "", "public" ] }, { symbol: [ "QRegularExpressionMatchIterator", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QScopedArrayPointer", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QAnimationDriver", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QFutureWatcherBase", "private", "", "public" ] }, { symbol: [ "QMutableByteArrayListIterator", "private", "", "public" ] }, { symbol: [ "QMetaClassInfo", "private", "", "public" ] }, { symbol: [ "QStringDataPtr", "private", "", "public" ] }, { symbol: [ "QCollatorSortKey", "private", "", "public" ] }, { symbol: [ "QMutableVectorIterator", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QByteArrayList", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QXmlStreamEntityDeclarations", "private", "", "public" ] }, { symbol: [ "QXmlStreamStringRef", "private", "", "public" ] }, { symbol: [ "QTimerEvent", "private", "", "public" ] }, { symbol: [ "QXmlStreamNamespaceDeclaration", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QCharRef", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QTypeInfoMerger", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QMutableHashIterator", "private", "", "public" ] }, { symbol: [ "QBBSystemLocaleData", "private", "", "public" ] }, { symbol: [ "QLinkedListIterator", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QTextEncoder", "private", "", "public" ] }, { symbol: [ "QListIterator", "private", "", "public" ] }, { symbol: [ "QMetaEnum", "private", "", "public" ] }, { symbol: [ "QLatin1Char", "private", "", "public" ] }, { symbol: [ "QVariantMap", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QUrlTwoFlags", "private", "", "public" ] }, { symbol: [ "QScopedPointerDeleteLater", "private", "", "public" ] }, { symbol: [ "QAbstractTableModel", "private", "", "public" ] }, { symbol: [ "QJsonValueRefPtr", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QPersistentModelIndex", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QModelIndex", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QMapNodeBase", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QListData", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QFlag", "private", "", "public" ] }, { symbol: [ "QFutureIterator", "private", "", "public" ] }, { symbol: [ "QDeferredDeleteEvent", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QByteRef", "private", "", "public" ] }, { symbol: [ "QMetaProperty", "private", "", "public" ] }, { symbol: [ "QScopedPointerArrayDeleter", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QStaticArrayData", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QLineF", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QInternal", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QXmlStreamNotationDeclarations", "private", "", "public" ] }, { symbol: [ "QVectorIterator", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QProcessEnvironment", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QMessageLogger", "private", "", "public" ] }, { symbol: [ "QXmlStreamAttribute", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QMapIterator", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QExplicitlySharedDataPointer", "private", "", "public" ] }, { symbol: [ "QPointF", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QThreadStorageData", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QBitRef", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QGenericReturnArgument", "private", "", "public" ] }, { symbol: [ "QEvent", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QMutableFutureIterator", "private", "", "public" ] }, { symbol: [ "QXmlStreamWriter", "private", "", "public" ] }, { symbol: [ "QJsonValuePtr", "private", "", "public" ] }, { symbol: [ "QLatin1String", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QStaticStringData", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QAtomicInteger", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QMultiMap", "private", "", "public" ] }, { symbol: [ "QSizeF", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QSharedDataPointer", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QVariantList", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QTextStreamManipulator", "private", "", "public" ] }, { symbol: [ "QReturnArgument", "private", "", "public" ] }, { symbol: [ "QStaticPlugin", "private", "", "public" ] }, { symbol: [ "QAtomicInt", "private", "", "public" ] }, { symbol: [ "QStringRef", "private", "", "public" ] }, { symbol: [ "QDate", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QMetaTypeIdQObject", "private", "", "public" ] }, { symbol: [ "QMutableSetIterator", "private", "", "public" ] }, { symbol: [ "QMutableStringListIterator", "private", "", "public" ] }, { symbol: [ "QTime", "private", "", "public" ] }, { symbol: [ "QContiguousCacheData", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QObjectData", "private", "", "public" ] }, { symbol: [ "QMetaObject", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QUnhandledException", "private", "", "public" ] }, { symbol: [ "QIntegerForSize", "private", "", "public" ] }, { symbol: [ "QMutableMapIterator", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QFunctionPointer", "private", "", "public" ] }, { symbol: [ "QVariantComparisonHelper", "private", "", "public" ] }, { symbol: [ "QMetaTypeId2", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QStaticByteArrayData", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QEnableSharedFromThis", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QMapNode", "private", "", "public" ] }, { symbol: [ "QMultiHash", "private", "", "public" ] }, { symbol: [ "QTextDecoder", "private", "", "public" ] }, { symbol: [ "QLatin1Literal", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QListSpecialMethods", "private", "", "public" ] }, { symbol: [ "QItemSelectionRange", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QContiguousCacheTypedData", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QVariantHash", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QLinkedListNode", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QDebugStateSaver", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QAtomicPointer", "private", "", "public" ] }, { symbol: [ "QEventLoopLocker", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QMutexLocker", "private", "", "public" ] }, { symbol: [ "QWeakPointer", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QHashData", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QGenericArgument", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QNoDebug", "private", "", "public" ] }, { symbol: [ "QMapDataBase", "private", "", "public" ] }, { symbol: [ "QRectF", "private", "", "public" ] }, { symbol: [ "QHashIterator", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QForeachContainer", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QArgument", "private", "", "public" ] }, { symbol: [ "QMarginsF", "private", "", "public" ] }, { symbol: [ "QXmlStreamReader", "private", "", "public" ] }, { symbol: [ "QItemSelection", "private", "", "public" ] }, { symbol: [ "QBasicMutex", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QScopedPointerPodDeleter", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QHashNode", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QXmlStreamEntityResolver", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QIncompatibleFlag", "private", "", "public" ] }, { symbol: [ "QScopedPointerObjectDeleteLater", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QMutableLinkedListIterator", "private", "", "public" ] }, { symbol: [ "QMetaMethod", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QByteArrayData", "private", "", "public" ] }, { symbol: [ "QWriteLocker", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QXmlStreamNamespaceDeclarations", "private", "", "public" ] }, { symbol: [ "QStringData", "private", "", "public" ] }, { symbol: [ "QByteArrayListIterator", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QArrayDataPointerRef", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QFileInfoList", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QObjectUserData", "private", "", "public" ] }, { symbol: [ "QScopedPointerDeleter", "private", "", "public" ] }, { symbol: [ "QLinkedListData", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QReadLocker", "private", "", "public" ] }, { symbol: [ "QMessageLogContext", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QStaticAssertFailure", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QMapData", "private", "", "public" ] }, { symbol: [ "QSequentialIterable", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QDynamicPropertyChangeEvent", "private", "", "public" ] }, { symbol: [ "QByteArrayDataPtr", "private", "", "public" ] }, { symbol: [ "QFutureInterfaceBase", "private", "", "public" ] }, { symbol: [ "QXmlStreamAttributes", "private", "", "public" ] }, { symbol: [ "QStringListIterator", "private", "", "public" ] }, # QtDBus { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QDBusSignature", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QDBusVariant", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QDBusAbstractInterfaceBase", "private", "", "public" ] }, { symbol: [ "QDBusObjectPath", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QDBusPendingReplyData", "private", "", "public" ] }, { symbol: [ "QDBusPendingCallWatcher", "private", "", "public" ] }, # QtDesigner { include: [ "", "private", "", "public" ] }, { symbol: [ "QDesignerComponents", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, # QtDesignerComponents { include: [ "", "private", "", "public" ] }, # QtGui { include: [ "", "private", "", "public" ] }, { symbol: [ "QExposeEvent", "private", "", "public" ] }, { symbol: [ "QTextBlock", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QPictureIO", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QAccessibleInterface", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QRegularExpressionValidator", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QTextBlockFormat", "private", "", "public" ] }, { symbol: [ "QDragLeaveEvent", "private", "", "public" ] }, { symbol: [ "QBrushData", "private", "", "public" ] }, { symbol: [ "QIconEngineV2", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QTextImageFormat", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QMatrix2x3", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QTextBlockUserData", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QOpenGLContextGroup", "private", "", "public" ] }, { symbol: [ "QOpenGLVersionProfile", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QPaintEvent", "private", "", "public" ] }, { symbol: [ "QHelpEvent", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QOpenGLFramebufferObjectFormat", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QEnterEvent", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QHideEvent", "private", "", "public" ] }, { symbol: [ "QGradientStop", "private", "", "public" ] }, { symbol: [ "QFocusEvent", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QWidgetSet", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QTextListFormat", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QDropEvent", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QMatrix4x3", "private", "", "public" ] }, { symbol: [ "QMoveEvent", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QTextTableFormat", "private", "", "public" ] }, { symbol: [ "QAccessibleTextRemoveEvent", "private", "", "public" ] }, { symbol: [ "QImageTextKeyLang", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QWhatsThisClickedEvent", "private", "", "public" ] }, { symbol: [ "QAccessibleTableModelChangeEvent", "private", "", "public" ] }, { symbol: [ "QPainterPathStroker", "private", "", "public" ] }, { symbol: [ "QAccessibleTextCursorEvent", "private", "", "public" ] }, { symbol: [ "QTextFrameLayoutData", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QOpenGLFunctionsPrivate", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QAccessibleTextSelectionEvent", "private", "", "public" ] }, { symbol: [ "QAccessibleImageInterface", "private", "", "public" ] }, { symbol: [ "QTextItem", "private", "", "public" ] }, { symbol: [ "QOpenGLShader", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QGradient", "private", "", "public" ] }, { symbol: [ "QCloseEvent", "private", "", "public" ] }, { symbol: [ "QActionEvent", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QAccessibleEvent", "private", "", "public" ] }, { symbol: [ "QConicalGradient", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QAccessibleTextInterface", "private", "", "public" ] }, { symbol: [ "QAccessibleTextInsertEvent", "private", "", "public" ] }, { symbol: [ "QTextCharFormat", "private", "", "public" ] }, { symbol: [ "QRegExpValidator", "private", "", "public" ] }, { symbol: [ "QAbstractUndoItem", "private", "", "public" ] }, { symbol: [ "QAccessibleTableCellInterface", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QIconDragEvent", "private", "", "public" ] }, { symbol: [ "QTextFrame", "private", "", "public" ] }, { symbol: [ "QScrollPrepareEvent", "private", "", "public" ] }, { symbol: [ "QWheelEvent", "private", "", "public" ] }, { symbol: [ "QDragEnterEvent", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QNativeGestureEvent", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QTextTableCell", "private", "", "public" ] }, { symbol: [ "QInputMethodQueryEvent", "private", "", "public" ] }, { symbol: [ "QMatrix2x4", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QMatrix3x3", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QInputEvent", "private", "", "public" ] }, { symbol: [ "QAccessibleEditableTextInterface", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QResizeEvent", "private", "", "public" ] }, { symbol: [ "QImageCleanupFunction", "private", "", "public" ] }, { symbol: [ "QLinearGradient", "private", "", "public" ] }, { symbol: [ "QPlatformSurfaceEvent", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QWidgetMapper", "private", "", "public" ] }, { symbol: [ "QAccessibleStateChangeEvent", "private", "", "public" ] }, { symbol: [ "QInputMethodEvent", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QTextLength", "private", "", "public" ] }, { symbol: [ "QHoverEvent", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QRadialGradient", "private", "", "public" ] }, { symbol: [ "QTabletEvent", "private", "", "public" ] }, { symbol: [ "QIntValidator", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QDoubleValidator", "private", "", "public" ] }, { symbol: [ "QDragMoveEvent", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QAccessibleTableInterface", "private", "", "public" ] }, { symbol: [ "QFileOpenEvent", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QStatusTipEvent", "private", "", "public" ] }, { symbol: [ "QTextInlineObject", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QMatrix3x2", "private", "", "public" ] }, { symbol: [ "QTextLine", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QOpenGLDebugMessage", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QMatrix3x4", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QToolBarChangeEvent", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QAccessibleTextUpdateEvent", "private", "", "public" ] }, { symbol: [ "QWidgetList", "private", "", "public" ] }, { symbol: [ "QTextTableCellFormat", "private", "", "public" ] }, { symbol: [ "QShowEvent", "private", "", "public" ] }, { symbol: [ "QAccessibleBridgePlugin", "private", "", "public" ] }, { symbol: [ "QScrollEvent", "private", "", "public" ] }, { symbol: [ "QShortcutEvent", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QOpenGLDebugLogger", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QTextFrameFormat", "private", "", "public" ] }, { symbol: [ "QAccessibleValueInterface", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QAccessibleActionInterface", "private", "", "public" ] }, { symbol: [ "QFontMetricsF", "private", "", "public" ] }, { symbol: [ "QTouchEvent", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QApplicationStateChangeEvent", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QAccessibleValueChangeEvent", "private", "", "public" ] }, { symbol: [ "QWindowList", "private", "", "public" ] }, { symbol: [ "QPaintEngineState", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QTextBlockGroup", "private", "", "public" ] }, { symbol: [ "QPolygonF", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QOpenGLTimeMonitor", "private", "", "public" ] }, { symbol: [ "QTextFragment", "private", "", "public" ] }, { symbol: [ "QWindowStateChangeEvent", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QAccessibleApplication", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QMatrix2x2", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QScreenOrientationChangeEvent", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QStandardItem", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QContextMenuEvent", "private", "", "public" ] }, { symbol: [ "QImageIOPlugin", "private", "", "public" ] }, { symbol: [ "QMouseEvent", "private", "", "public" ] }, { symbol: [ "QTextObjectInterface", "private", "", "public" ] }, { symbol: [ "QKeyEvent", "private", "", "public" ] }, { symbol: [ "QMatrix4x2", "private", "", "public" ] }, { symbol: [ "QGradientStops", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, # QtHelp { symbol: [ "QHelpContentItem", "private", "", "public" ] }, { symbol: [ "QHelpIndexModel", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QHelpContentModel", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QHelpSearchQuery", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QHelpGlobal", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, # QtNetwork { symbol: [ "QDnsServiceRecord", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QDnsDomainNameRecord", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QNetworkProxyQuery", "private", "", "public" ] }, { symbol: [ "QNetworkConfigurationManager", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QNetworkProxyFactory", "private", "", "public" ] }, { symbol: [ "QHttpPart", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "Q_IPV6ADDR", "private", "", "public" ] }, { symbol: [ "QNetworkAddressEntry", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QIPv6Address", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QDnsMailExchangeRecord", "private", "", "public" ] }, { symbol: [ "QNetworkCacheMetaData", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QDnsTextRecord", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QDnsHostAddressRecord", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, # QtOpenGL { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QGLFunctionsPrivate", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QGLFramebufferObjectFormat", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QGLShader", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QGLWidget", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QGLContext", "private", "", "public" ] }, { symbol: [ "QGLFormat", "private", "", "public" ] }, # QtOpenGLExtensions { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, # QtPlatformHeaders { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, # QtPlatformSupport { include: [ "", "private", "", "public" ] }, # QtPositioning { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, # QtPrintSupport { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, # QtQml { symbol: [ "QQmlProperties", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QQmlAttachedPropertiesFunc", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QQmlImageProviderBase", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QQmlTypesExtensionInterface", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QQmlListReference", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QJSValueList", "private", "", "public" ] }, { symbol: [ "QQmlListProperty", "private", "", "public" ] }, { symbol: [ "QQmlTypeInfo", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QQmlIncubationController", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QQmlDebuggingEnabler", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, # QtQmlDevTools { include: [ "", "private", "", "public" ] }, # QtQuick { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QSGGeometryNode", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QSGMaterialType", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QQuickTextureFactory", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QSGBasicGeometryNode", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QSGOpaqueTextureMaterial", "private", "", "public" ] }, { symbol: [ "QSGTransformNode", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QSGMaterialShader", "private", "", "public" ] }, { symbol: [ "QQuickTransform", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QSGNodeVisitor", "private", "", "public" ] }, { symbol: [ "QSGOpacityNode", "private", "", "public" ] }, { symbol: [ "QSGSimpleMaterialShader", "private", "", "public" ] }, { symbol: [ "QSGSimpleMaterialComparableMaterial", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QSGRootNode", "private", "", "public" ] }, { symbol: [ "QSGClipNode", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QSGDynamicTexture", "private", "", "public" ] }, # QtQuickParticles { include: [ "", "private", "", "public" ] }, # QtQuickTest { include: [ "", "private", "", "public" ] }, # QtQuickWidgets { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, # QtSql { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QSqlDriverCreatorBase", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QSqlRelation", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QSqlDriverCreator", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, # QtTest { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, # QtUiPlugin { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, # QtUiTools { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, # QtWidgets { symbol: [ "QTableWidgetItem", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QStyleOptionViewItemV4", "private", "", "public" ] }, { symbol: [ "QTapAndHoldGesture", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QMacNativeWidget", "private", "", "public" ] }, { symbol: [ "QStyleOptionTitleBar", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QGraphicsPolygonItem", "private", "", "public" ] }, { symbol: [ "QStyleOptionTabBarBase", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QStyleOptionToolBar", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QItemEditorCreator", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QDoubleSpinBox", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QStyleOptionToolBox", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QGraphicsEllipseItem", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QSplitterHandle", "private", "", "public" ] }, { symbol: [ "QGraphicsDropShadowEffect", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QGraphicsSceneHelpEvent", "private", "", "public" ] }, { symbol: [ "QGraphicsObject", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QGraphicsSceneDragDropEvent", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QHBoxLayout", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QGraphicsItemGroup", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QGraphicsAnchor", "private", "", "public" ] }, { symbol: [ "QTileRules", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QStyleOptionButton", "private", "", "public" ] }, { symbol: [ "QStyleOptionFrameV2", "private", "", "public" ] }, { symbol: [ "QGraphicsSceneMoveEvent", "private", "", "public" ] }, { symbol: [ "QStyleOptionHeader", "private", "", "public" ] }, { symbol: [ "QWidgetItemV2", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QItemEditorCreatorBase", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QStyleOptionProgressBar", "private", "", "public" ] }, { symbol: [ "QTreeWidgetItem", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QStyleHintReturnMask", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QStyleOptionComboBox", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QStyleOptionTabWidgetFrameV2", "private", "", "public" ] }, { symbol: [ "QWidgetData", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QGraphicsBlurEffect", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QDateEdit", "private", "", "public" ] }, { symbol: [ "QPanGesture", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QWidgetItem", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QStyleOptionSlider", "private", "", "public" ] }, { symbol: [ "QSwipeGesture", "private", "", "public" ] }, { symbol: [ "QStyleOptionViewItem", "private", "", "public" ] }, { symbol: [ "QGraphicsOpacityEffect", "private", "", "public" ] }, { symbol: [ "QPinchGesture", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QGraphicsTextItem", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QStyleOptionComplex", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QStyleOptionTabWidgetFrame", "private", "", "public" ] }, { symbol: [ "QStyleOptionSizeGrip", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QStyleOptionFrame", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QGraphicsRotation", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QStyleOptionTabV2", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QGraphicsRectItem", "private", "", "public" ] }, { symbol: [ "QGestureEvent", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QUndoCommand", "private", "", "public" ] }, { symbol: [ "QStandardItemEditorCreator", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QStyleOptionFocusRect", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QStyleOptionMenuItem", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QVBoxLayout", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QStyleOptionViewItemV2", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QGraphicsSceneResizeEvent", "private", "", "public" ] }, { symbol: [ "QStyleOptionDockWidgetV2", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QStyleOptionTab", "private", "", "public" ] }, { symbol: [ "QGraphicsPathItem", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QMacCocoaViewContainer", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QGraphicsPixmapItem", "private", "", "public" ] }, { symbol: [ "QGraphicsSceneHoverEvent", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QStyleOptionDockWidget", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QStyleOptionToolButton", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QStyleHintReturnVariant", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QStyleOptionGroupBox", "private", "", "public" ] }, { symbol: [ "QGraphicsScale", "private", "", "public" ] }, { symbol: [ "QWizardPage", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QGraphicsSimpleTextItem", "private", "", "public" ] }, { symbol: [ "QStyleOptionTabBarBaseV2", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QGraphicsLineItem", "private", "", "public" ] }, { symbol: [ "QStyleOptionToolBoxV2", "private", "", "public" ] }, { symbol: [ "QSpacerItem", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QGraphicsSceneWheelEvent", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QStyleOptionFrameV3", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QStyleOptionTabV3", "private", "", "public" ] }, { symbol: [ "QStyleHintReturn", "private", "", "public" ] }, { symbol: [ "QTapGesture", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QStyleOptionViewItemV3", "private", "", "public" ] }, { symbol: [ "QListWidgetItem", "private", "", "public" ] }, { symbol: [ "QPlainTextDocumentLayout", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QTimeEdit", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QStyleOptionSpinBox", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QGraphicsSceneContextMenuEvent", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QGraphicsColorizeEffect", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QStyleOptionGraphicsItem", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QTableWidgetSelectionRange", "private", "", "public" ] }, { symbol: [ "QStyleOptionProgressBarV2", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QAbstractGraphicsShapeItem", "private", "", "public" ] }, { symbol: [ "QGraphicsSceneMouseEvent", "private", "", "public" ] }, { symbol: [ "QStyleOptionRubberBand", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, # QtXml { symbol: [ "QDomNotation", "private", "", "public" ] }, { symbol: [ "QXmlDeclHandler", "private", "", "public" ] }, { symbol: [ "QXmlParseException", "private", "", "public" ] }, { symbol: [ "QXmlNamespaceSupport", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { symbol: [ "QDomNamedNodeMap", "private", "", "public" ] }, { symbol: [ "QXmlEntityResolver", "private", "", "public" ] }, { symbol: [ "QXmlAttributes", "private", "", "public" ] }, { symbol: [ "QDomElement", "private", "", "public" ] }, { symbol: [ "QDomDocumentType", "private", "", "public" ] }, { symbol: [ "QDomEntityReference", "private", "", "public" ] }, { symbol: [ "QDomDocument", "private", "", "public" ] }, { symbol: [ "QDomImplementation", "private", "", "public" ] }, { symbol: [ "QXmlSimpleReader", "private", "", "public" ] }, { symbol: [ "QDomNode", "private", "", "public" ] }, { symbol: [ "QXmlErrorHandler", "private", "", "public" ] }, { symbol: [ "QDomDocumentFragment", "private", "", "public" ] }, { symbol: [ "QXmlLexicalHandler", "private", "", "public" ] }, { symbol: [ "QDomComment", "private", "", "public" ] }, { symbol: [ "QDomCharacterData", "private", "", "public" ] }, { symbol: [ "QXmlContentHandler", "private", "", "public" ] }, { symbol: [ "QXmlDTDHandler", "private", "", "public" ] }, { symbol: [ "QDomText", "private", "", "public" ] }, { symbol: [ "QDomAttr", "private", "", "public" ] }, { symbol: [ "QDomNodeList", "private", "", "public" ] }, { symbol: [ "QDomCDATASection", "private", "", "public" ] }, { symbol: [ "QDomProcessingInstruction", "private", "", "public" ] }, { symbol: [ "QXmlDefaultHandler", "private", "", "public" ] }, { symbol: [ "QXmlLocator", "private", "", "public" ] }, { symbol: [ "QXmlReader", "private", "", "public" ] }, { symbol: [ "QXmlInputSource", "private", "", "public" ] }, { symbol: [ "QDomEntity", "private", "", "public" ] }, # --- Manual additions --- # convenience { include: [ "", "public", "", "public" ] }, { include: [ "", "public", "", "public" ] }, # http://doc.qt.io/qt-5/qt.html { include: [ "", "private", "", "public" ] }, # http://doc.qt.io/qt-5/qtglobal.html { include: [ "", "public", "", "public" ] }, { include: [ "", "public", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, # http://doc.qt.io/qt-5/qevent.html { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, # http://doc.qt.io/qt-5/qmetaobject.html { include: [ "", "private", "", "public" ] }, # http://doc.qt.io/qt-5/qobject.html { include: [ "", "private", "", "public" ] }, ] mapper-0.8.1.1/make_iwyu_qt_imp.sh000077500000000000000000000055331325266516600170670ustar00rootroot00000000000000#!/bin/sh echo "[" echo "# --- Generated by ${0##*/} ---\n" LAST_MODULE="" for I in $(find Q* -type f -name "Q*") do INCLUDE="${I##*/}" INCLUDE_LC=$(echo "$INCLUDE.h" | awk '{print tolower($0)}') INCLUDE_DIR="${I%/*}" MODULE="${I%%/*}" if [ "${MODULE}" != "${LAST_MODULE}" ] then echo "\n # ${MODULE}" LAST_MODULE="${MODULE}" fi #echo "$INCLUDE_DIR $INCLUDE $INCLUDE_LC"; if [ "${MODULE}" = "QtTest" ] then # Use only for module QtTest if [ "${INCLUDE}" = "QtTest" ] then for J in $(find "${MODULE}" -type f -name "q*.h") do INCLUDE="${J##*/}" echo " { include: [ \"<${INCLUDE}>\", \"private\", \"\", \"public\" ] }," done else echo " { include: [ \"<${INCLUDE}>\", \"private\", \"\", \"public\" ] }," fi elif [ -f "$INCLUDE_DIR/$INCLUDE_LC" -a $(grep "#include \"$INCLUDE_LC" "$I" | wc -l) -eq 1 ] then # Use public include for include grep "#include \"$INCLUDE_LC" "$I" | sed -e "s/.*\"\(.*\)\"/ { include: [ \"<\1>\", \"private\", \"<${INCLUDE}>\", \"public\" ] },/" elif [ "${INCLUDE#Qt}" = "${INCLUDE}" ] then # Use public include for QFoo symbol if [ $(grep "#include \"q" "$I" | wc -l) -eq 1 ] then echo " { symbol: [ \"${INCLUDE}\", \"private\", \"<${INCLUDE}>\", \"public\" ] }," fi elif [ -f "$INCLUDE_DIR/q${INCLUDE_LC#qt}" -a $(grep "#include \"q${INCLUDE_LC#qt}" "$I" | wc -l) -eq 1 ] then # Use public QtFoo include for private qfoo.h include grep "#include \"q${INCLUDE_LC#qt}" "$I" | sed -e "s/.*\"\(.*\)\"/ { include: [ \"<\1>\", \"private\", \"<${INCLUDE}>\", \"public\" ] },/" fi done cat << END_EXTRA # --- Manual additions --- # convenience { include: [ "", "public", "", "public" ] }, { include: [ "", "public", "", "public" ] }, # http://doc.qt.io/qt-5/qt.html { include: [ "", "private", "", "public" ] }, # http://doc.qt.io/qt-5/qtglobal.html { include: [ "", "public", "", "public" ] }, { include: [ "", "public", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, # http://doc.qt.io/qt-5/qevent.html { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, # http://doc.qt.io/qt-5/qmetaobject.html { include: [ "", "private", "", "public" ] }, # http://doc.qt.io/qt-5/qobject.html { include: [ "", "private", "", "public" ] }, END_EXTRA echo "]" mapper-0.8.1.1/packaging/000077500000000000000000000000001325266516600151035ustar00rootroot00000000000000mapper-0.8.1.1/packaging/CMakeLists.txt000066400000000000000000000431411325266516600176460ustar00rootroot00000000000000# # Copyright 2012-2017 Kai Pastor # # This file is part of OpenOrienteering. # # OpenOrienteering is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # OpenOrienteering is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with OpenOrienteering. If not, see . message(STATUS "Configuring ${PROJECT_NAME} packaging") option(Mapper_PACKAGE_QT_ALL_TRANSLATIONS "Add all Qt translations to the packages" OFF) macro(deploy_qt_translations basename) find_package(Qt5LinguistTools REQUIRED QUIET) get_target_property(LCONVERT_EXECUTABLE Qt5::lconvert IMPORTED_LOCATION) if(NOT QT_TRANSLATIONS_DIR) find_package(Qt5Core REQUIRED QUIET) get_target_property(QMAKE_EXECUTABLE Qt5::qmake IMPORTED_LOCATION) execute_process( COMMAND "${QMAKE_EXECUTABLE}" -query QT_INSTALL_TRANSLATIONS OUTPUT_VARIABLE QT_TRANSLATIONS_DIR ) string(STRIP "${QT_TRANSLATIONS_DIR}" QT_TRANSLATIONS_DIR) endif() set(install_translations_code ) if(Mapper_PACKAGE_QT_ALL_TRANSLATIONS) file(GLOB basename_files RELATIVE "${QT_TRANSLATIONS_DIR}" "${QT_TRANSLATIONS_DIR}/${basename}_??.qm") else() unset(basename_files) foreach(_mapper_trans ${Mapper_translations}) get_filename_component(basename_file ${_mapper_trans} NAME_WE) string(REPLACE OpenOrienteering ${basename} basename_file ${basename_file}) string(REGEX REPLACE "(_..)_..$" "\\1" basename_file ${basename_file}) file(GLOB translation_files RELATIVE "${QT_TRANSLATIONS_DIR}" "${QT_TRANSLATIONS_DIR}/${basename_file}*.qm") list(APPEND basename_files ${translation_files}) endforeach() endif() foreach(basename_file ${basename_files}) set(input_files "\"${QT_TRANSLATIONS_DIR}/${basename_file}\"") foreach(arg ${ARGN}) string(REPLACE ${basename} ${arg} extra_file ${basename_file}) if(EXISTS "${QT_TRANSLATIONS_DIR}/${extra_file}") set(input_files "\"${QT_TRANSLATIONS_DIR}/${extra_file}\" ${input_files}") endif() endforeach() list(APPEND install_translations_code "execute_process(COMMAND \"${LCONVERT_EXECUTABLE}\" -o \"${CMAKE_CURRENT_BINARY_DIR}/${basename_file}\" ${input_files})" "file(INSTALL DESTINATION \"\${CMAKE_INSTALL_PREFIX}/${MAPPER_DATA_DESTINATION}/translations\" TYPE FILE FILES \"${CMAKE_CURRENT_BINARY_DIR}/${basename_file}\")" ) endforeach() string(REPLACE ";" "\n " install_translations_code "${install_translations_code}") install(CODE "${install_translations_code}") endmacro(deploy_qt_translations) find_package(Qt5Core REQUIRED QUIET) if(NOT QT_LIBRARY_DIR) get_target_property(_qt5core_lib Qt5::Core IMPORTED_LOCATION_RELEASE) get_filename_component(QT_LIBRARY_DIR ${_qt5core_lib} PATH) string(REGEX REPLACE "/[^/]*\\.framework" "" QT_LIBRARY_DIR "${QT_LIBRARY_DIR}") endif() # cf. http://www.cmake.org/cmake/help/cmake-2-8-docs.html#module:CPack # cf. http://www.cmake.org/Wiki/CMake:CPackPackageGenerators set(CPACK_PACKAGE_NAME "OpenOrienteering ${CMAKE_PROJECT_NAME}") set(CPACK_PACKAGE_VENDOR "OpenOrienteering") set(CPACK_PACKAGE_VERSION_MAJOR ${Mapper_VERSION_MAJOR}) set(CPACK_PACKAGE_VERSION_MINOR ${Mapper_VERSION_MINOR}) set(CPACK_PACKAGE_VERSION_PATCH ${Mapper_VERSION_PATCH}) set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Map drawing program from OpenOrienteering") if(NOT CMAKE_SIZEOF_VOID_P AND MINGW) set(_env_lang $ENV{LANG}) set(ENV{LANG} C) execute_process( COMMAND ${CMAKE_C_COMPILER} -dumpmachine OUTPUT_VARIABLE HOST_TRIPLET ) set(ENV{LANG} ${_env_lang}) if(${HOST_TRIPLET} MATCHES ^i686) set(CMAKE_SIZEOF_VOID_P 4) elseif(${HOST_TRIPLET} MATCHES ^x86_64) set(CMAKE_SIZEOF_VOID_P 8) endif() endif() if(ANDROID) set(_system_name "Android-${CMAKE_ANDROID_ARCH_ABI}") elseif(APPLE) set(_system_name "macOS") elseif(CMAKE_SIZEOF_VOID_P EQUAL 4) set(_system_name "${CMAKE_SYSTEM_NAME}-x86") elseif(CMAKE_SIZEOF_VOID_P EQUAL 8) set(_system_name "${CMAKE_SYSTEM_NAME}-x64") else() set(_system_name "${CMAKE_SYSTEM_NAME}-unknown") endif() set(CPACK_PACKAGE_FILE_NAME "OpenOrienteering-Mapper-${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}-${_system_name}") set(CPACK_RESOURCE_FILE_LICENSE "${PROJECT_SOURCE_DIR}/COPYING") set(CPACK_STRIP_FILES "TRUE") set(CPACK_SOURCE_PACKAGE_FILE_NAME "openorienteering-mapper_${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}-src") set(CPACK_SOURCE_IGNORE_FILES "${PROJECT_BINARY_DIR}" "/[.]git/" "/3rd-party/clipper/download/" "/3rd-party/proj/download/" "/3rd-party/qt5/download/" ${CPACK_SOURCE_IGNORE_FILES} ) set(MAPPER_MACOS_SUBDIR "") if(WIN32) # Packaging as ZIP archive set(CPACK_GENERATOR_DEFAULT "ZIP") #set(CPACK_INCLUDE_TOPLEVEL_DIRECTORY 0) set(CPACK_PACKAGE_INSTALL_DIRECTORY "${CPACK_PACKAGE_NAME} ${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}") set(CPACK_PACKAGE_EXECUTABLES "Mapper" "${CPACK_PACKAGE_NAME} ${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}") find_program(MAKENSIS_EXECUTABLE "makensis") if(MAKENSIS_EXECUTABLE) list(APPEND CPACK_GENERATOR_DEFAULT "NSIS") # The title displayed at the top of the installer set(CPACK_NSIS_PACKAGE_NAME "${CPACK_PACKAGE_NAME}") # The display name string that appears in the Windows Add/Remove Program control panel set(CPACK_NSIS_DISPLAY_NAME "${CPACK_PACKAGE_NAME} ${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}") # NSIS start menu links will point to executables in this directory set(CPACK_NSIS_EXECUTABLES_DIRECTORY ".") # A path to the executable that contains the uninstaller icon. set(CPACK_NSIS_INSTALLED_ICON_NAME Mapper.exe) # URL to a web site providing more information about your application. set(CPACK_NSIS_URL_INFO_ABOUT "http://openorienteering.org/apps/mapper/") # Extra NSIS include configure_file(windows/custom.nsi.in windows/custom.nsi @ONLY) set(CPACK_NSIS_DEFINES "!include \\\"${CMAKE_CURRENT_BINARY_DIR}\\\\windows\\\\custom.nsi\\\"") # Extra NSIS commands that will be added to the install/uninstall sections. set(CPACK_NSIS_EXTRA_INSTALL_COMMANDS "Call installAssociations") set(CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS "Call un.installAssociations") # 64 bit build if(CMAKE_SIZEOF_VOID_P EQUAL 8) set(CPACK_NSIS_INSTALL_ROOT "$PROGRAMFILES64") set(CPACK_NSIS_DISPLAY_NAME "${CPACK_NSIS_DISPLAY_NAME} x64") endif() endif(MAKENSIS_EXECUTABLE) elseif(APPLE) set(MAPPER_MACOS_SUBDIR "/Mapper.app/Contents/MacOS") set(CPACK_GENERATOR_DEFAULT "DragNDrop") set(CPACK_PACKAGE_EXECUTABLES "Mapper" "OpenOrienteering Mapper ${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}") set(CPACK_PACKAGE_ICON "${PROJECT_SOURCE_DIR}/images/mapper-icon/Mapper.icns") set_target_properties(Mapper PROPERTIES MACOSX_BUNDLE_INFO_STRING "${CPACK_PACKAGE_DESCRIPTION_SUMMARY}" MACOSX_BUNDLE_ICON_FILE "Mapper.icns" MACOSX_BUNDLE_GUI_IDENTIFIER "org.openorienteering.${CMAKE_PROJECT_NAME}" MACOSX_BUNDLE_LONG_VERSION_STRING "${CMAKE_PROJECT_NAME} ${Mapper_VERSION_DISPLAY} for OS X" MACOSX_BUNDLE_BUNDLE_NAME "${CMAKE_PROJECT_NAME}" # less than 16 characters long MACOSX_BUNDLE_SHORT_VERSION_STRING "${Mapper_VERSION_MAJOR}.${Mapper_VERSION_MINOR}.${Mapper_VERSION_PATCH}" MACOSX_BUNDLE_BUNDLE_VERSION "${Mapper_VERSION_MAJOR}.${Mapper_VERSION_MINOR}.${Mapper_VERSION_PATCH}" MACOSX_BUNDLE_COPYRIGHT "${Mapper_COPYRIGHT}" ) install(FILES "${CPACK_PACKAGE_ICON}" DESTINATION "${MAPPER_DATA_DESTINATION}" ) elseif(ANDROID) # We don't use a CPack generator, # but run androiddeployqt from CPackConfig.cmake set(CPACK_GENERATOR_DEFAULT "") set(KEYSTORE_URL "KEYSTORE_URL-NOTFOUND" CACHE STRING "URL of the keystore to be used when signing APK packages." ) set(KEYSTORE_ALIAS "KEYSTORE_ALIAS-NOTFOUND" CACHE STRING "Alias in the keystore to be used when signing APK packages." ) if(KEYSTORE_URL AND KEYSTORE_ALIAS) set(SIGN_APK "$,$>") else() set(SIGN_APK 0) endif() configure_file( "${PROJECT_SOURCE_DIR}/android/CPackConfig.cmake.in" "${PROJECT_BINARY_DIR}/CPackConfig.tmp.cmake" @ONLY ) file(GENERATE OUTPUT "${PROJECT_BINARY_DIR}/CPackConfig.cmake" INPUT "${PROJECT_BINARY_DIR}/CPackConfig.tmp.cmake" ) if(NOT EXISTS "${PROJECT_BINARY_DIR}/CPackConfig.cmake") file(WRITE "${PROJECT_BINARY_DIR}/CPackConfig.cmake" [[# Placeholder to enforce 'package' target creation]] ) endif() # For Android, we create a dummy qmake project which provides # - configuration for running androiddeployqt from CPackConfig.cmake, and # - a project for debugging the Android app in Qt Creator # The directory name must match the Mapper binary. file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/Mapper") configure_file( "${PROJECT_SOURCE_DIR}/android/Mapper.pro.in" "${CMAKE_CURRENT_BINARY_DIR}/Mapper/Mapper.tmp.pro" @ONLY ) file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/Mapper/Mapper.pro" INPUT "${CMAKE_CURRENT_BINARY_DIR}/Mapper/Mapper.tmp.pro" ) elseif(UNIX AND EXISTS /usr/bin/dpkg AND EXISTS /usr/bin/lsb_release) # Packaging on Debian or similar set(CPACK_GENERATOR_DEFAULT "DEB") set(CPACK_DEBIAN_PACKAGE_NAME "openorienteering-mapper") execute_process( COMMAND /usr/bin/lsb_release -sc OUTPUT_VARIABLE CPACK_LSB_RELEASE OUTPUT_STRIP_TRAILING_WHITESPACE ) string(REPLACE "Linux-x86" "${CPACK_LSB_RELEASE}_i386" CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_FILE_NAME}" ) string(REPLACE "Linux-x64" "${CPACK_LSB_RELEASE}_amd64" CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_FILE_NAME}" ) set(CPACK_DEBIAN_PACKAGE_MAINTAINER "Kai Pastor ") set(CPACK_DEBIAN_SECTION "graphics") set(CPACK_DEBIAN_PACKAGE_HOMEPAGE "http://openorienteering.org/apps/mapper/") set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS "ON") endif() if(CPACK_GENERATOR_DEFAULT) set(CPACK_GENERATOR "${CPACK_GENERATOR_DEFAULT}" CACHE STRING "The binary package generators (ZIP;DEB;DragNDrop;NSIS)") set(CPACK_SOURCE_GENERATOR "OFF" CACHE STRING "The source package generators (TGZ;ZIP)") mark_as_advanced(CPACK_GENERATOR CPACK_SOURCE_GENERATOR) include(CPack) endif() # Cleanup obsolete cache items unset(MAPPER_LIBS CACHE) unset(MAPPER_QT_PLUGINS CACHE) unset(MAPPER_LIB_HINTS) unset(MAPPER_LIBS) if(Mapper_PACKAGE_PROJ) if(PROJ4_DIR AND NOT PROJ4_ROOT) # Cf. find_package documentation string(REGEX REPLACE "/CMake$|/cmake$" "" PROJ4_ROOT "${PROJ4_DIR}") # U string(REGEX REPLACE "/PROJ4[^/]*$" "" PROJ4_ROOT "${PROJ4_ROOT}") # U, W string(REGEX REPLACE "/cmake$" "" PROJ4_ROOT "${PROJ4_ROOT}") # U string(REGEX REPLACE "/lib/[^/]*$|/lib$|/share$" "" PROJ4_ROOT "${PROJ4_ROOT}") # U, W elseif(NOT PROJ4_ROOT) message(FATAL_ERROR "PROJ4_ROOT must be set when Mapper_PACKAGE_PROJ is enabled.") endif() install( DIRECTORY "${PROJ4_ROOT}/share/proj" DESTINATION "${MAPPER_DATA_DESTINATION}") list(APPEND MAPPER_LIB_HINTS ${PROJ4_ROOT}/bin) endif() if(Mapper_PACKAGE_GDAL) if(NOT GDAL_DATA_DIR) unset(GDAL_CONFIG CACHE) find_program(GDAL_CONFIG gdal-config ONLY_CMAKE_FIND_ROOT_PATH) if(GDAL_CONFIG) exec_program(${GDAL_CONFIG} ARGS --datadir OUTPUT_VARIABLE gdal_data_dir) endif() if(gdal_data_dir) # Search in CMAKE_FIND_ROOT_PATH find_path(GDAL_DATA_DIR NAMES ellipsoid.csv HINTS ${gdal_data_dir} ) endif() endif() if(NOT GDAL_DATA_DIR) message(FATAL_ERROR "The gdal-config script must be available, or GDAL_DATA_DIR must be set, " "when Mapper_PACKAGE_GDAL is enabled.") endif() install( DIRECTORY "${GDAL_DATA_DIR}/" DESTINATION "${MAPPER_DATA_DESTINATION}/gdal") get_filename_component(GDAL_LIBRARY_DIR "{GDAL_LIBRARY}" PATH) list(APPEND MAPPER_LIB_HINTS "${GDAL_LIBRARY_DIR}") endif() unset(MAPPER_QT_PLUGINS) if(Mapper_PACKAGE_QT) set(QT_LIB_SUFFIX "") list(APPEND MAPPER_LIB_HINTS ${QT_LIBRARY_DIR}) set(MAPPER_QT_PLUGINS generic/qevdevkeyboardplugin generic/qevdevmouseplugin generic/qevdevtabletplugin generic/qevdevtouchplugin imageformats/qgif imageformats/qicns imageformats/qico imageformats/qjp2 imageformats/qjpeg imageformats/qtiff imageformats/qwebp platforminputcontexts/composeplatforminputcontextplugin platforminputcontexts/ibusplatforminputcontextplugin platforms/qcocoa platforms/qwindows platforms/qxcb position/qtposition_cl position/qtposition_geoclue position/qtposition_serialnmea printsupport/cocoaprintersupport printsupport/cupsprintersupport printsupport/windowsprintersupport ) if(ANDROID) set(MAPPER_QT_PLUGINS ) # Ignore for now, handled by androiddeployqt endif() set(QT_PLUGIN_TARGETS ) foreach(module Gui Positioning PrintSupport Sensors Sql) find_package(Qt5${module} QUIET) if(module STREQUAL "Positioning" AND Qt5Positioning_DIR) # Workaround for QTBUG-58812 CMake: Plugin config not loaded # unless plugin class name ends in Plugin file(GLOB pluginTargets "${Qt5Positioning_DIR}/Qt5Positioning_*.cmake") foreach(file ${pluginTargets}) if(NOT file MATCHES "Plugin.cmake") include(${file}) endif() endforeach() endif() list(APPEND QT_PLUGIN_TARGETS ${Qt5${module}_PLUGINS}) endforeach() # Cf. Qt5's qt_de.ts for dependencies - qt_de.ts would not load without them. deploy_qt_translations(qt qtbase) endif() if(Mapper_PACKAGE_ASSISTANT) set(assistant_find_options ) if(CMAKE_FIND_ROOT_PATH) set(assistant_find_options ONLY_CMAKE_FIND_ROOT_PATH) endif() find_program(Qt5Help_ASSISTANT_EXECUTABLE NAMES assistant Assistant assistant.exe # HINTS bin ${assistant_find_options} ) if(NOT Qt5Help_ASSISTANT_EXECUTABLE) message(FATAL_ERROR "Qt5Help_ASSISTANT_EXECUTABLE: not found, " "but required by option Mapper_PACKAGE_ASSISTANT=" ${Mapper_PACKAGE_ASSISTANT}) endif() message(STATUS "Qt Assistant - found") if(WIN32 OR APPLE) install( PROGRAMS ${Qt5Help_ASSISTANT_EXECUTABLE} DESTINATION "${MAPPER_RUNTIME_DESTINATION}${MAPPER_MACOS_SUBDIR}") else() install( PROGRAMS ${Qt5Help_ASSISTANT_EXECUTABLE} DESTINATION "${MAPPER_LIBRARY_DESTINATION}/bin") endif() list(APPEND MAPPER_QT_PLUGINS sqldrivers/qsqlite ) deploy_qt_translations(assistant qt_help) endif(Mapper_PACKAGE_ASSISTANT) if(CMAKE_CROSSCOMPILING AND MINGW) set(_env_lang $ENV{LC_ALL}) set(ENV{LC_ALL} C) execute_process( COMMAND ${CMAKE_C_COMPILER} --print-search-dirs OUTPUT_VARIABLE MINGW_SEARCH_DIRS ) set(ENV{LC_ALL} ${_env_lang}) string(REGEX REPLACE ".*libraries: ?=?([^\n]*).*" \\1 MINGW_SEARCH_DIRS "${MINGW_SEARCH_DIRS}") string(REPLACE \; \\\; MINGW_SEARCH_DIRS "${MINGW_SEARCH_DIRS}") string(REPLACE : \; MINGW_SEARCH_DIRS "${MINGW_SEARCH_DIRS}") list(APPEND MAPPER_LIB_HINTS ${MINGW_SEARCH_DIRS}) # Grep is used (and desperately needed) to speed up objdump parsing. find_program(gp_grep_cmd NAMES grep) endif() # Install all plugins unset(MAPPER_QT_PLUGINS_FOUND) foreach(_qt_plugin ${MAPPER_QT_PLUGINS}) string(REPLACE "/" "/lib" alternative_pattern "${_qt_plugin}") foreach(plugin_target ${QT_PLUGIN_TARGETS} NOT_FOUND) if(plugin_target STREQUAL "NOT_FOUND") message(" ${_qt_plugin} plugin library - not found") break() endif() get_target_property(_qt_plugin_location ${plugin_target} IMPORTED_LOCATION_RELEASE) if(_qt_plugin_location MATCHES "${_qt_plugin}" OR _qt_plugin_location MATCHES "${alternative_pattern}") message(" ${_qt_plugin} plugin library - found") list(APPEND MAPPER_QT_PLUGINS_FOUND "${_qt_plugin}") get_filename_component(_qt_plugin_dir "${_qt_plugin}" PATH) if(APPLE) install( FILES "${_qt_plugin_location}" DESTINATION "${MAPPER_RUNTIME_DESTINATION}${MAPPER_MACOS_SUBDIR}/../PlugIns/${_qt_plugin_dir}") else() install( FILES "${_qt_plugin_location}" DESTINATION "${MAPPER_LIBRARY_DESTINATION}/plugins/${_qt_plugin_dir}") endif() break() endif() endforeach() endforeach(_qt_plugin) if(UNIX AND NOT APPLE AND NOT ANDROID) install( FILES "${PROJECT_SOURCE_DIR}/doc/man/Mapper.1" DESTINATION "share/man/man1") install( FILES "${CMAKE_CURRENT_BINARY_DIR}/linux/Mapper.desktop" DESTINATION "share/applications") install( FILES "${CMAKE_CURRENT_BINARY_DIR}/linux/openorienteering-mapper.xml" DESTINATION "share/mime/packages") # Cf. http://standards.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html#directory_layout foreach(_size 16 24 32 48 96 128 256 512) install( FILES "${PROJECT_SOURCE_DIR}/images/mapper-icon/Mapper-${_size}.png" DESTINATION "share/icons/hicolor/${_size}x${_size}/apps" RENAME Mapper.png ) install( FILES "${PROJECT_SOURCE_DIR}/images/mapper-icon/Mapper-${_size}.png" DESTINATION "share/icons/hicolor/${_size}x${_size}/mimetypes" RENAME application-x-openorienteering-xmap.png ) install( FILES "${PROJECT_SOURCE_DIR}/images/mapper-icon/Mapper-${_size}.png" DESTINATION "share/icons/hicolor/${_size}x${_size}/mimetypes" RENAME application-x-openorienteering-ocd.png ) endforeach() endif() # By exanding all @VAR, custom_install.cmake makes the install more traceable. configure_file("custom_install.cmake.in" "custom_install.cmake" @ONLY) install(CODE "include(\"${CMAKE_CURRENT_BINARY_DIR}/custom_install.cmake\")") mapper-0.8.1.1/packaging/custom_install.cmake.in000066400000000000000000000166521325266516600215640ustar00rootroot00000000000000# # Copyright 2017 Kai Pastor # # This file is part of OpenOrienteering. # # OpenOrienteering is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # OpenOrienteering is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with OpenOrienteering. If not, see . # Sets CMAKE_CROSSCOMPILING and variables which describe the target system. macro(handle_crosscompiling) set(CMAKE_CROSSCOMPILING @CMAKE_CROSSCOMPILING@) # These variables must describe the target system set(ANDROID @ANDROID@) set(APPLE @APPLE@) set(MINGW @MINGW@) set(UNIX @UNIX@) set(WIN32 @WIN32@) endmacro() # This function is an Android variant of BundleUtilities' fixup_bundle(). function(fixup_bundle_android runtime dirs) # First, collect all dependencies set(resolved ) set(remaining "libMapper.so") string(MAKE_C_IDENTIFIER "${remaining}" id) set(resolved_${id} "$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}${runtime_destination}/${remaining}") while(remaining) list(GET remaining 0 current) list(REMOVE_AT remaining 0) list(APPEND resolved "${current}") string(MAKE_C_IDENTIFIER "${current}" current_id) execute_process( COMMAND "@ANDROID_NDK_ROOT@/ndk-depends" "${resolved_${current_id}}" COMMAND grep ".so" COMMAND grep -v ":" OUTPUT_VARIABLE output ERROR_VARIABLE error RESULT_VARIABLE result OUTPUT_STRIP_TRAILING_WHITESPACE ) if(result) message(FATAL_ERROR "ndk-depends failed: ${error}") endif() string(REGEX REPLACE "[\n\r ]+" ";" output "${output}") foreach(item ${output}) if(NOT item OR item STREQUAL current) continue() endif() list(APPEND depends_${current_id} "${item}") string(MAKE_C_IDENTIFIER "${item}" id) if(item AND NOT DEFINED resolved_${id}) set(resolved_${id} "NOTFOUND") foreach(dir ${dirs}) if(NOT resolved_${id} AND dir AND EXISTS "${dir}/${item}") message(STATUS "Found '${item}' in '${dir}'") list(APPEND remaining "${item}") set(resolved_${id} "${dir}/${item}") endif() endforeach() endif() endforeach() endwhile() # Second, record QT libs, and topologically sort ANDROID_EXTRA_LIBS set(qt ) set(android_extra_libs ) set(remaining "${resolved}") list(LENGTH remaining length_before) list(APPEND remaining "MAYBE-STOP") while(NOT remaining STREQUAL "MAYBE-STOP") list(GET remaining 0 current) list(REMOVE_AT remaining 0) string(MAKE_C_IDENTIFIER "${current}" id) if(current STREQUAL "MAYBE-STOP") list(LENGTH remaining length_after) if(length_before EQUAL length_after) message(FATAL_ERROR "Cannot resolve circular dependencies of ${remaining}") endif() set(length_before ${length_after}) list(APPEND remaining "MAYBE-STOP") elseif(current MATCHES "libMapper.so|libQt5Core|libQt5Gui") continue() elseif(current MATCHES "libQt5") string(REPLACE "libQt5" "" lib "${current}") string(REPLACE ".so" "" lib "${lib}") string(TOLOWER "${lib}" lib) set(qt "${qt} ${lib}") elseif(resolved_${id}) set(depends ) foreach(item ${depends_${id}}) list(FIND remaining "${item}" index) if(NOT index EQUAL -1) list(APPEND depends "${item}") endif() endforeach() if(depends) list(APPEND remaining "${current}") # Redo after dependencies else() set(android_extra_libs "${android_extra_libs} \\\n ${resolved_${id}}") endif() endif() endwhile() # Finally, write ANDROID_EXTRA_LIBS to mapper_libs.pri file(WRITE "@CMAKE_CURRENT_BINARY_DIR@/Mapper/mapper_libs.pri" " QT += ${qt} ANDROID_EXTRA_LIBS = ${android_extra_libs} ") endfunction() # This function wraps BundleUtilities' fixup_bundle() # to make it work for cross-builds. function(fixup_bundle_portable runtime dirs) handle_crosscompiling() if(MINGW) # gp_tool and gp_cmd are needed for module GetPrerequisites. set(gp_tool "objdump") set(gp_cmd "@CMAKE_OBJDUMP@") # grep is used (and desperately needed) to speed up objdump parsing. set(gp_grep_cmd "@gp_grep_cmd@") # This function resolves all unknown items which do not match the # MinGW DLL name pattern NAME-NUMBER.dll as 'system' libraries, # thus catching the Windows system libraries in the MinGW context. function(gp_resolve_item_override context item exepath dirs resolved_item_var resolved_var) if(NOT ${resolved_var} AND NOT "${item}" MATCHES "-[0-9]*.dll$") set(${resolved_item_var} "/system/${item}" PARENT_SCOPE) set(${resolved_var} 1 PARENT_SCOPE) endif() endfunction() endif() if(WIN32) include(BundleUtilities) file(GLOB_RECURSE plugins "$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}${runtime_destination}/plugins/*.dll") list(APPEND runtime ${plugins}) fixup_bundle("$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}${runtime_destination}/Mapper.exe" "${runtime}" "${dirs}") # Strip bundled DLLs if (CMAKE_INSTALL_DO_STRIP AND NOT "@CMAKE_STRIP@" STREQUAL "") file(GLOB dlls "$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}${runtime_destination}/*.dll") foreach(item ${dlls} ${runtime}) execute_process(COMMAND "@CMAKE_STRIP@" --strip-unneeded "${item}") endforeach() endif() elseif(APPLE) include(BundleUtilities) file(GLOB_RECURSE plugins "$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}${runtime_destination}@MAPPER_MACOS_SUBDIR@/../PlugIns/*.dylib") list(APPEND runtime "${plugins}") fixup_bundle("$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}${runtime_destination}/Mapper.app" "${runtime}" "${dirs}") if (CMAKE_INSTALL_DO_STRIP AND NOT "@CMAKE_STRIP@" STREQUAL "") file(GLOB dylibs "$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}${runtime_destination}@MAPPER_MACOS_SUBDIR@/*.dylib") foreach(item ${dylibs} ${runtime}) execute_process(COMMAND "@CMAKE_STRIP@" -x "${item}") endforeach() endif() elseif(ANDROID) fixup_bundle_android("${runtime}" "${dirs}") elseif(UNIX) # Add required symlinks. execute_process(COMMAND ldconfig -n "$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}@MAPPER_LIBRARY_DESTINATION@") endif() endfunction() # Write a minimal qt.conf if needed. function(handle_qt_conf) handle_crosscompiling() set(qt_conf "$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}${runtime_destination}/qt.conf") if(EXISTS "${qt_conf}") message(STATUS "Skipping ${qt_conf}") return() elseif(WIN32) message(STATUS "Writing ${qt_conf}") file(RELATIVE_PATH rel_path "$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/@MAPPER_RUNTIME_DESTINATION@" "$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/@MAPPER_LIBRARY_DESTINATION@") if(rel_path STREQUAL "") set(rel_path ".") endif() file(WRITE "${qt_conf}" "\ [Paths] Plugins=${rel_path}/plugins Translations=${rel_path}/translations ") elseif(APPLE) set(qt_conf "$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}${runtime_destination}@MAPPER_MACOS_SUBDIR@/../Resources/qt.conf") message(STATUS "Writing ${qt_conf}") file(WRITE "${qt_conf}" "\ [Paths] Plugins=PlugIns ") endif() endfunction() # BundleUtilities stumples upon "/." set(runtime_destination "/@MAPPER_RUNTIME_DESTINATION@") if(runtime_destination STREQUAL "/.") set(runtime_destination "") endif() set(runtime "") set(dirs "@MAPPER_LIB_HINTS@") handle_qt_conf() fixup_bundle_portable("${runtime}" "${dirs}") mapper-0.8.1.1/packaging/linux/000077500000000000000000000000001325266516600162425ustar00rootroot00000000000000mapper-0.8.1.1/packaging/linux/Mapper.desktop000066400000000000000000000032411325266516600210610ustar00rootroot00000000000000[Desktop Entry] Type=Application Name=OpenOrienteering Mapper Comment=A free software for drawing orienteering maps Comment[cs]=Svobodný program na kreslení map pro orientační běh Comment[da]=Et gratis program til tegning af orienteringskort Comment[de]=Ein freies Programm zum Zeichnen von Orientierungslaufkarten Comment[eo]=Libera programaro por desegni orientiĝajn mapojn Comment[es]=Software libre para dibujar mapas de orientación Comment[fi]=Ilmainen ohjelma suunnistuskarttojen tekemiseen Comment[fr]=Un logiciel libre pour dessiner des cartes de course d'orientation Comment[hu]=Tartalmilag ez az igazság Comment[id]=Perangkat lunak gratis untuk menggambar peta orienteering Comment[it]=Un software libero per il disegno delle mappe per orienteering Comment[ja]=オリエンテーリング地図作成のための自由なソフトウェア Comment[lv]=Brīvpieejas programma orientēšanās karšu zīmēšanai Comment[nb]=Fri programvare for tegning av orienteringskart Comment[nl]=Een gratis programma voor het tekenen van oriëntatieloop kaarten Comment[pl]=Darmowy program do kreślenia map do BnO Comment[pt_BR]=Um programa livre para desenho de mapas de orientação Comment[ru]=Свободное программное обеспечение для создания спортивных карт Comment[sv]=Fri mjukvara för att rita orienteringskartor Comment[uk]=Вільна програма для креслення спортивних карт Comment[zh_CN]=用于绘制定向越野地图的免费开源软件 Exec=Mapper %F Icon=Mapper Terminal=false Categories=Graphics;Maps;Qt; MimeType=application/x-openorienteering-xmap;application/x-openorienteering-ocd; mapper-0.8.1.1/packaging/linux/openorienteering-mapper.xml000066400000000000000000000051151325266516600236240ustar00rootroot00000000000000 Orienteering map Mapa pro orientační běh Orienteringskort Orientierungslaufkarte Orientiĝa mapo Mapa de orientación Suunnistuskartta Carte d'orientation Tájfutó térkép Peta orienteering オリエンテーリング地図 Orientēšanās karte Orienteringskart Oriëntatiekaart Mapa de orientação Спортивная карта Orienteringskarta Спортивна карта 定向运动地图 Orienteering map Mapa pro orientační běh Orienteringskort Orientierungslaufkarte Orientiĝa mapo Mapa de orientación Suunnistuskartta Carte d'orientation Tájfutó térkép Peta orienteering オリエンテーリング地図 Orientēšanās karte Orienteringskart Oriëntatiekaart Mapa de orientação Спортивная карта Orienteringskarta Спортивна карта 定向运动地图 mapper-0.8.1.1/packaging/src/000077500000000000000000000000001325266516600156725ustar00rootroot00000000000000mapper-0.8.1.1/packaging/src/CMakeLists.txt000066400000000000000000000026761325266516600204450ustar00rootroot00000000000000# # Copyright 2013, 2014 Kai Pastor # # This file is part of OpenOrienteering. # # OpenOrienteering is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # OpenOrienteering is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with OpenOrienteering. If not, see . message(STATUS "Configuring ${PROJECT_NAME} source packaging") set(Mapper_Source_PREFIX "openorienteering-mapper-${Mapper_VERSION_MAJOR}.${Mapper_VERSION_MINOR}.${Mapper_VERSION_PATCH}") set(Mapper_Source_FILE_NAME "openorienteering-mapper_${Mapper_VERSION_MAJOR}.${Mapper_VERSION_MINOR}.${Mapper_VERSION_PATCH}-src") set(Mapper_Source_FORMAT tgz CACHE STRING "The archive format for source packages (see `git archive -l` for valid options)") find_program(GIT_EXECUTABLE git) if(GIT_EXECUTABLE) configure_file(Mapper_Source.cmake.in Mapper_Source.cmake @ONLY) add_custom_target(Mapper_Source COMMAND ${CMAKE_COMMAND} -P "${CMAKE_CURRENT_BINARY_DIR}/Mapper_Source.cmake" ) endif() mapper-0.8.1.1/packaging/src/Mapper_Source.cmake.in000066400000000000000000000037101325266516600220460ustar00rootroot00000000000000# # Copyright 2012, 2013, 2014 Kai Pastor # # This file is part of OpenOrienteering. # # OpenOrienteering is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # OpenOrienteering is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with OpenOrienteering. If not, see . # Synopsis: # cmake [ -DPREFIX=my/prefix ] [ -DARCHIVE_NAME=prefix-src ] # [ -DOUTPUT_DIR=/usr/src ] [ -DSKIP_CHECK=1 ] -P Mapper_Source.cmake if (NOT PREFIX) set(PREFIX "@Mapper_Source_PREFIX@") endif () if (NOT ARCHIVE_NAME) set(ARCHIVE_NAME "@Mapper_Source_FILE_NAME@") endif () if (NOT OUTPUT_DIR) set(OUTPUT_DIR "@PROJECT_BINARY_DIR@") endif () if (NOT OUTPUT_DIR) set(OUTPUT_DIR .) endif () if (NOT SKIP_CHECK) # Check that the working directory is clean. set(ENV{LANG} C) execute_process( COMMAND "@GIT_EXECUTABLE@" status WORKING_DIRECTORY "@PROJECT_SOURCE_DIR@" OUTPUT_VARIABLE GIT_STATUS_OUTPUT ) if (NOT GIT_STATUS_OUTPUT MATCHES "working directory clean") message(${GIT_STATUS_OUTPUT}) message(FATAL_ERROR "Source package must be created from a clean git working directory, " "or you must specify -DSKIP_CHECK=1." ) endif () endif () # Let git build the archive message(STATUS "Creating ${PREFIX}.@Mapper_Source_FORMAT@") execute_process( COMMAND "@GIT_EXECUTABLE@" archive --format "@Mapper_Source_FORMAT@" --prefix "${PREFIX}/" --output "${OUTPUT_DIR}/${ARCHIVE_NAME}.@Mapper_Source_FORMAT@" HEAD WORKING_DIRECTORY "@PROJECT_SOURCE_DIR@" ) mapper-0.8.1.1/packaging/translations.cpp000066400000000000000000000007701325266516600203340ustar00rootroot00000000000000/* * This file is part of OpenOrienteering. * * This file is not compiled. * These string are used for packaging and desktop integration. */ #define QT_TRANSLATE_NOOP(a,b) QT_TRANSLATE_NOOP("OpenOrienteering", "Orienteering map"); QT_TRANSLATE_NOOP("OpenOrienteering", "Software for drawing orienteering maps"); //: For the moment, we use this existing translation instead of the previous one. QT_TRANSLATE_NOOP("OpenOrienteering::AboutDialog", "A free software for drawing orienteering maps"); mapper-0.8.1.1/packaging/windows/000077500000000000000000000000001325266516600165755ustar00rootroot00000000000000mapper-0.8.1.1/packaging/windows/custom.nsi.in000066400000000000000000000061471325266516600212370ustar00rootroot00000000000000; (C) 2014 Kai Pastor ; ; This file is part of OpenOrienteering. ; ; OpenOrienteering is free software: you can redistribute it and/or modify ; it under the terms of the GNU General Public License as published by ; the Free Software Foundation, either version 3 of the License, or ; (at your option) any later version. ; ; OpenOrienteering is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with OpenOrienteering. If not, see . !define MUI_WELCOMEPAGE_TITLE_3LINES !define MUI_CUSTOMFUNCTION_GUIINIT check_x64 Function check_x64 StrCmp "@CMAKE_SIZEOF_VOID_P@" "8" 0 +5 StrCmp "$PROGRAMFILES32" "$PROGRAMFILES64" 0 +3 MessageBox MB_ICONEXCLAMATION|MB_OK "The x64 package cannot be installed on a x32 Windows system. Installation aborted." /SD IDOK Abort ClearErrors FunctionEnd !define SHCNE_ASSOCCHANGED 0x8000000 !define SHCNF_IDLIST 0 Function installAssociations ; The application WriteRegStr HKLM "Software\Classes\OpenOrienteering.Map" "" "OpenOrienteering Map" WriteRegStr HKLM "Software\Classes\OpenOrienteering.Map\DefaultIcon" "" "$INSTDIR\Mapper.exe" ReadRegStr $R0 HKLM "Software\Classes\OpenOrienteering.Map\shell\open\command" "" StrCmp $R0 "" 0 +3 WriteRegStr HKLM "Software\Classes\OpenOrienteering.Map\shell" "" "open" WriteRegStr HKLM "Software\Classes\OpenOrienteering.Map\shell\open\command" "" '"$INSTDIR\Mapper.exe" "%1"' ; The file type associations WriteRegStr HKLM "Software\Classes\.omap" "" "OpenOrienteering.Map" WriteRegStr HKLM "Software\Classes\.omap" "PerceivedType" "document" WriteRegStr HKLM "Software\Classes\.xmap" "" "OpenOrienteering.Map" WriteRegStr HKLM "Software\Classes\.xmap" "PerceivedType" "document" ReadRegStr $R0 HKLM "Software\Classes\.ocd" "" StrCmp $R0 "" 0 +3 WriteRegStr HKLM "Software\Classes\.ocd" "" "OpenOrienteering.Map" WriteRegStr HKLM "Software\Classes\.ocd" "PerceivedType" "document" WriteRegStr HKLM "Software\Classes\.ocd\OpenWithProgIDs" "OpenOrienteering.Map" "" System::Call 'Shell32::SHChangeNotify(i ${SHCNE_ASSOCCHANGED}, i ${SHCNF_IDLIST}, p0, p0)' FunctionEnd Function un.installAssociations ; The file type associations ReadRegStr $R0 HKLM "Software\Classes\.omap" "" StrCmp $R0 "OpenOrienteering.Map" 0 +2 DeleteRegKey HKLM "Software\Classes\.omap" ReadRegStr $R0 HKLM "Software\Classes\.xmap" "" StrCmp $R0 "OpenOrienteering.Map" 0 +2 DeleteRegKey HKLM "Software\Classes\.xmap" DeleteRegValue HKLM "Software\Classes\.ocd\OpenWithProgIDs" "OpenOrienteering.Map" ReadRegStr $R0 HKLM "Software\Classes\.ocd" "" StrCmp $R0 "OpenOrienteering.Map" 0 +2 DeleteRegKey HKLM "Software\Classes\.ocd" ; The application DeleteRegKey HKLM "Software\Classes\OpenOrienteering.Map" System::Call 'Shell32::SHChangeNotify(i ${SHCNE_ASSOCCHANGED}, i ${SHCNF_IDLIST}, p0, p0)' FunctionEnd mapper-0.8.1.1/resources.qrc000066400000000000000000000136141325266516600157050ustar00rootroot00000000000000 images/about.png images/arrow-down.png images/arrow-left.png images/arrow-right.png images/arrow-thin-upleft.png images/arrow-thin-downright.png images/arrow-up.png images/close.png images/colors.png images/compass.png images/control.png images/copy.png images/copy-coords.png images/cursor-crosshair.png images/cursor-cut.png images/cursor-delete.png images/cursor-draw-circle.png images/cursor-draw-path.png images/cursor-draw-point.png images/cursor-draw-rectangle.png images/cursor-draw-text.png images/cursor-fill.png images/cursor-georeferencing-add.png images/cursor-georeferencing-move.png images/cursor-hollow.png images/cursor-invisible.png images/cursor-paint-on-template.png images/cursor-rotate.png images/cursor-scale.png images/cut.png images/delete.png images/draw-circle.png images/draw-freehand.png images/draw-path.png images/draw-point.png images/draw-point-gps.png images/draw-rectangle.png images/draw-text.png images/georeferencing.png images/gps-distance-rings.png images/gps-temporary-point.png images/gps-temporary-path.png images/gps-temporary-clear.png images/grid.png images/group.png images/help.png images/magnifying-glass.png images/map-parts.png images/mapper.png images/minus.png images/move.png images/new.png images/open-orienteering.png images/open.png images/paint-on-template-settings.png images/move-to-gps.png images/paste.png images/pencil.png images/plus.png images/point-handles.png images/point-handles-2x.png images/point-handles-4x.png images/print.png images/print-mode-vector.png images/print-mode-raster.png images/print-mode-separations.png images/redo.png images/rotate-map.png images/save.png images/settings.png images/symbols.png images/symbol_point_explanation.png images/tag-selector.png images/templates.png images/text-align-left.png images/text-align-hcenter.png images/text-align-right.png images/text-align-top.png images/text-align-vcenter.png images/text-align-baseline.png images/text-align-bottom.png images/three-dots.png images/title.png images/tool-boolean-difference.png images/tool-boolean-intersection.png images/tool-boolean-union.png images/tool-boolean-xor.png images/tool-boolean-merge-holes.png images/tool-connect-paths.png images/tool-convert-to-curves.png images/tool-cut.png images/tool-cut-hole.png images/tool-cutout-physical.png images/tool-cutout-physical-inner.png images/tool-distribute-points.png images/tool-duplicate.png images/tool-edit.png images/tool-edit-line.png images/tool-fill.png images/tool-fill-border.png images/tool-gps-display.png images/tool-measure.png images/tool-rotate.png images/tool-rotate-pattern.png images/tool-scale.png images/tool-simplify-path.png images/tool-switch-dashes.png images/tool-switch-symbol.png images/tool-touch-cursor.png images/undo.png images/view-show-all.png images/view-zoom-in.png images/view-zoom-out.png images/window-new.png images/mapper-icon/Mapper-128.png doc/tip-of-the-day/tips_en.txt doc/tip-of-the-day/tips_de.txt doc/tip-of-the-day/tips_fr.txt doc/tip-of-the-day/tips_ru.txt doc/tip-of-the-day/tips_uk.txt mapper-0.8.1.1/src/000077500000000000000000000000001325266516600137465ustar00rootroot00000000000000mapper-0.8.1.1/src/CMakeLists.txt000066400000000000000000000226321325266516600165130ustar00rootroot00000000000000# # Copyright 2012-2014 Thomas Schöps # Copyright 2012-2017 Kai Pastor # # This file is part of OpenOrienteering. # # OpenOrienteering is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # OpenOrienteering is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with OpenOrienteering. If not, see . find_package(Qt5Core 5.3 REQUIRED) find_package(Qt5Widgets REQUIRED) find_package(Qt5Sensors) find_package(Qt5Positioning) if(ANDROID) find_package(Qt5AndroidExtras REQUIRED) else() find_package(Qt5Network REQUIRED) find_package(Qt5PrintSupport REQUIRED) endif() set(CMAKE_AUTOMOC ON) configure_file(mapper_config.h.in "${CMAKE_CURRENT_BINARY_DIR}/mapper_config.h.tmp") execute_process( COMMAND "${CMAKE_COMMAND}" -E copy_if_different mapper_config.h.tmp mapper_config.h WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" ) include_directories("${CMAKE_CURRENT_BINARY_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}") include_directories(AFTER "../3rd-party/qbezier/src") # Always, last set(Mapper_Common_SRCS global.cpp mapper_resource.cpp settings.cpp core/autosave.cpp core/crs_template.cpp core/crs_template_implementation.cpp core/georeferencing.cpp core/latlon.cpp core/map.cpp core/map_color.cpp core/map_coord.cpp core/map_grid.cpp core/map_part.cpp core/map_printer.cpp core/map_view.cpp core/path_coord.cpp core/storage_location.cpp core/virtual_coord_vector.cpp core/virtual_path.cpp core/objects/boolean_tool.cpp core/objects/object.cpp core/objects/object_mover.cpp core/objects/object_query.cpp core/objects/symbol_rule_set.cpp core/objects/text_object.cpp core/renderables/renderable.cpp core/renderables/renderable_implementation.cpp core/symbols/area_symbol.cpp core/symbols/combined_symbol.cpp core/symbols/line_symbol.cpp core/symbols/point_symbol.cpp core/symbols/symbol.cpp core/symbols/symbol_icon_decorator.cpp core/symbols/text_symbol.cpp fileformats/file_format.cpp fileformats/file_format_registry.cpp fileformats/file_import_export.cpp fileformats/native_file_format.cpp fileformats/ocad8_file_format.cpp fileformats/ocd_file_export.cpp fileformats/ocd_file_format.cpp fileformats/ocd_file_import.cpp fileformats/ocd_types.cpp fileformats/xml_file_format.cpp gui/about_dialog.cpp gui/autosave_dialog.cpp gui/color_dialog.cpp gui/configure_grid_dialog.cpp gui/file_dialog.cpp gui/georeferencing_dialog.cpp gui/home_screen_controller.cpp gui/main_window.cpp gui/main_window_controller.cpp gui/modifier_key.cpp gui/print_progress_dialog.cpp gui/print_tool.cpp gui/print_widget.cpp gui/select_crs_dialog.cpp gui/settings_dialog.cpp gui/task_dialog.cpp gui/text_browser_dialog.cpp gui/touch_cursor.cpp gui/util_gui.cpp gui/map/new_map_dialog.cpp gui/map/map_dialog_rotate.cpp gui/map/map_dialog_scale.cpp gui/map/map_editor.cpp gui/map/map_editor_activity.cpp gui/map/map_find_feature.cpp gui/map/map_widget.cpp gui/symbols/area_symbol_settings.cpp gui/symbols/combined_symbol_settings.cpp gui/symbols/line_symbol_settings.cpp gui/symbols/point_symbol_editor_widget.cpp gui/symbols/point_symbol_settings.cpp gui/symbols/replace_symbol_set_dialog.cpp gui/symbols/symbol_properties_widget.cpp gui/symbols/symbol_setting_dialog.cpp gui/symbols/text_symbol_settings.cpp gui/widgets/action_grid_bar.cpp gui/widgets/color_dropdown.cpp gui/widgets/color_list_widget.cpp gui/widgets/compass_display.cpp gui/widgets/crs_param_widgets.cpp gui/widgets/crs_selector.cpp gui/widgets/editor_settings_page.cpp gui/widgets/general_settings_page.cpp gui/widgets/home_screen_widget.cpp gui/widgets/key_button_bar.cpp gui/widgets/mapper_proxystyle.cpp gui/widgets/measure_widget.cpp gui/widgets/pie_menu.cpp gui/widgets/segmented_button_layout.cpp gui/widgets/settings_page.cpp gui/widgets/symbol_dropdown.cpp gui/widgets/symbol_render_widget.cpp gui/widgets/symbol_tooltip.cpp gui/widgets/symbol_widget.cpp gui/widgets/tag_select_widget.cpp gui/widgets/tags_widget.cpp gui/widgets/template_list_widget.cpp gui/widgets/text_alignment_widget.cpp gui/widgets/text_browser.cpp sensors/compass.cpp sensors/gps_display.cpp sensors/gps_temporary_markers.cpp sensors/gps_track.cpp sensors/gps_track_recorder.cpp templates/template.cpp templates/template_adjust.cpp templates/template_dialog_reopen.cpp templates/template_image.cpp templates/template_map.cpp templates/template_position_dock_widget.cpp templates/template_positioning_dialog.cpp templates/template_tool_move.cpp templates/template_tool_paint.cpp templates/template_track.cpp templates/world_file.cpp tools/cut_tool.cpp tools/cut_hole_tool.cpp tools/cutout_operation.cpp tools/cutout_tool.cpp tools/distribute_points_tool.cpp tools/draw_line_and_area_tool.cpp tools/draw_point_tool.cpp tools/draw_point_gps_tool.cpp tools/draw_path_tool.cpp tools/draw_circle_tool.cpp tools/draw_rectangle_tool.cpp tools/draw_freehand_tool.cpp tools/draw_text_tool.cpp tools/edit_tool.cpp tools/edit_point_tool.cpp tools/edit_line_tool.cpp tools/fill_tool.cpp tools/object_selector.cpp tools/pan_tool.cpp tools/point_handles.cpp tools/rotate_tool.cpp tools/rotate_pattern_tool.cpp tools/scale_tool.cpp tools/text_object_editor_helper.cpp tools/tool.cpp tools/tool_base.cpp tools/tool_helpers.cpp undo/map_part_undo.cpp undo/object_undo.cpp undo/undo.cpp undo/undo_manager.cpp util/dxfparser.cpp util/encoding.cpp util/item_delegates.cpp util/matrix.cpp util/overriding_shortcut.cpp util/recording_translator.cpp util/scoped_signals_blocker.cpp util/transformation.cpp util/translation_util.cpp util/util.cpp util/xml_stream_util.cpp ) # Extra header to be shown in the IDE or to be translated set(Mapper_Common_HEADERS core/autosave_p.h core/image_transparency_fixup.h core/objects/object_operations.h core/renderables/renderable.h core/renderables/renderable_implementation.h fileformats/file_import_export.h # translations fileformats/ocad8_file_format_p.h fileformats/ocd_file_import.h # translations fileformats/ocd_types.h fileformats/ocd_types_v8.h fileformats/ocd_types_v9.h fileformats/ocd_types_v10.h fileformats/ocd_types_v11.h fileformats/ocd_types_v12.h fileformats/xml_file_format_p.h gui/map/map_editor_p.h templates/world_file.h util/backports.h ) # Resources (from project root) list(APPEND Mapper_RESOURCES "${PROJECT_SOURCE_DIR}/resources.qrc") qt5_add_resources(Mapper_RESOURCES_RCC ${Mapper_RESOURCES} OPTIONS -no-compress) # Mapper common: static library of full runtime # (To be used by Mapper executable and by system tests.) add_library(Mapper_Common STATIC ${Mapper_Common_SRCS} ${Mapper_Common_HEADERS} # for IDE ${Mapper_RESOURCES_RCC} ) add_dependencies(Mapper_Common Mapper_prerequisites ) target_link_libraries(Mapper_Common libocad Polyclipping::Polyclipping PROJ4::proj Qt5::Widgets ) foreach(lib mapper-gdal printsupport Qt5::AndroidExtras Qt5::Network Qt5::Positioning Qt5::Sensors ) if(TARGET ${lib}) target_link_libraries(Mapper_Common ${lib}) endif() endforeach() target_compile_definitions(Mapper_Common PRIVATE QT_NO_CAST_FROM_ASCII QT_NO_CAST_TO_ASCII QT_USE_QSTRINGBUILDER # MAPPER_ENABLE_COMPATIBILITY ) mapper_translations_sources(${Mapper_Common_SRCS} ${Mapper_Common_HEADERS}) # Mapper executable set(Mapper_SRCS main.cpp ) if(WIN32) enable_language(RC) configure_file(mingw/resources.rc.in ${CMAKE_CURRENT_BINARY_DIR}/resources.rc @ONLY) configure_file(${PROJECT_SOURCE_DIR}/images/mapper-icon/Mapper.ico ${CMAKE_CURRENT_BINARY_DIR}/Mapper.ico COPYONLY) list(APPEND Mapper_SRCS ${CMAKE_CURRENT_BINARY_DIR}/resources.rc) endif() if(ANDROID) add_library(Mapper SHARED ${Mapper_SRCS}) else() add_executable(Mapper WIN32 MACOSX_BUNDLE ${Mapper_SRCS}) target_link_libraries(Mapper QtSingleApplication) endif() target_link_libraries(Mapper Mapper_Common ) target_compile_definitions(Mapper PRIVATE QT_NO_CAST_FROM_ASCII QT_NO_CAST_TO_ASCII QT_USE_QSTRINGBUILDER ) install(TARGETS Mapper RUNTIME DESTINATION "${MAPPER_RUNTIME_DESTINATION}" BUNDLE DESTINATION "${MAPPER_RUNTIME_DESTINATION}" # macOS LIBRARY DESTINATION "${MAPPER_RUNTIME_DESTINATION}" # Android ) # Workaround Qt private include dir issue # Cf. https://bugreports.qt.io/browse/QTBUG-37417 set(PRIVATE_MODULES Core Gui) if(WIN32) list(APPEND PRIVATE_MODULES PrintSupport) endif() foreach(module ${PRIVATE_MODULES}) set(qt_module Qt${module}) set(qt5_module Qt5${module}) if("${${qt5_module}_PRIVATE_INCLUDE_DIRS}" STREQUAL "") foreach(base_dir ${${qt5_module}_INCLUDE_DIRS}) if("${base_dir}" MATCHES "/${qt_module}\$") list(APPEND ${qt5_module}_PRIVATE_INCLUDE_DIRS "${base_dir}/${${qt5_module}_VERSION}/${qt_module}") endif() endforeach() endif() target_include_directories(Mapper_Common PRIVATE ${${qt5_module}_PRIVATE_INCLUDE_DIRS}) endforeach() mapper-0.8.1.1/src/core/000077500000000000000000000000001325266516600146765ustar00rootroot00000000000000mapper-0.8.1.1/src/core/autosave.cpp000066400000000000000000000064001325266516600172310ustar00rootroot00000000000000/* * Copyright 2014 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "autosave.h" #include "autosave_p.h" #include #include #include #include #include #include "settings.h" namespace OpenOrienteering { AutosavePrivate::AutosavePrivate(Autosave& document) : document(document) , autosave_needed(false) { autosave_timer.setSingleShot(true); connect(&autosave_timer, &QTimer::timeout, this, &AutosavePrivate::autosave); connect(&Settings::getInstance(), &Settings::settingsChanged, this, &AutosavePrivate::settingsChanged); settingsChanged(); } AutosavePrivate::~AutosavePrivate() { // nothing, not inlined } void AutosavePrivate::settingsChanged() { // Normally, the autosave interval can be stored as an integer. // It is loaded as a double here to allow for faster unit testing. autosave_interval = qRound(Settings::getInstance().getSetting(Settings::General_AutosaveInterval).toDouble() * 60000); if (autosave_interval < 1000) { // stop autosave autosave_interval = 0; autosave_timer.stop(); } else if (autosave_needed && !autosave_timer.isActive()) { // start autosave autosave_timer.setInterval(autosave_interval); autosave_timer.start(); } } bool AutosavePrivate::autosaveNeeded() { return autosave_needed; } void AutosavePrivate::setAutosaveNeeded(bool needed) { autosave_needed = needed; if (autosave_interval) { // autosaving enabled if (autosave_needed && !autosave_timer.isActive()) { autosave_timer.setInterval(autosave_interval); autosave_timer.start(); } else if (!autosave_needed && autosave_timer.isActive()) { autosave_timer.stop(); } } } void AutosavePrivate::autosave() { Autosave::AutosaveResult result = document.autosave(); if (autosave_interval) { switch (result) { case Autosave::TemporaryFailure: autosave_timer.setInterval(5000); autosave_timer.start(); return; case Autosave::Success: case Autosave::PermanentFailure: autosave_timer.setInterval(autosave_interval); autosave_timer.start(); return; } Q_UNREACHABLE(); } } // ### Autosave ### Autosave::Autosave() : autosave_controller(new AutosavePrivate(*this)) { // Nothing, not inlined } Autosave::~Autosave() { // Nothing, not inlined } QString Autosave::autosavePath(const QString &path) const { return path + QLatin1String(".autosave"); } void Autosave::setAutosaveNeeded(bool needed) { autosave_controller->setAutosaveNeeded(needed); } bool Autosave::autosaveNeeded() const { return autosave_controller->autosaveNeeded(); } } // namespace OpenOrienteering mapper-0.8.1.1/src/core/autosave.h000066400000000000000000000056401325266516600167030ustar00rootroot00000000000000/* * Copyright 2014, 2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_AUTOSAVE_H #define OPENORIENTEERING_AUTOSAVE_H #include class QString; namespace OpenOrienteering { class AutosavePrivate; /** * @brief Class Autosave implements autosaving behaviour. * * Autosaving means that data which has been modified is automatically saved * after some period of time if no regular saving (typically triggered by the * user) happens before. * * Classes which wish to implement autosaving may inherit from this class. * Inheriting classes must implement autosave(). Furthermore, they must call * setAutosaveNeeded() when changes need to be taken care of by Autosave, or * when normal saving has terminated the need to perform autosaving. * * Autosaving, as implemented by autosave(), may succeed, fail temporary (e.g. * during editing), or fail permanent (e.g. for lack of disk space). * On success or permanent failure, autosave() will be called again after the * regular autosaving period. * On temporary failure, autosave() will be called again after five seconds. * * The autosave period (in minutes) is taken from the setting * Settings::General_AutosaveInterval. */ class Autosave { public: /** @brief Possible results of autosave attempts. */ enum AutosaveResult { Success, ///< Autosaving succeeded. PermanentFailure, ///< Autosaving failed for some persistent reason. TemporaryFailure ///< Autosaving failed for some transient reason and shall be retried soon. }; /** @brief Returns the autosave file path for the given path. */ virtual QString autosavePath(const QString &path) const; /** @brief Performs an autosave, if possible. */ virtual AutosaveResult autosave() = 0; /** @brief Informs Autosave whether autosaving is needed or not. */ void setAutosaveNeeded(bool); /** @brief Returns true if autosave is known to be needed. */ bool autosaveNeeded() const; protected: /** @brief Initializes the autosave feature. */ Autosave(); /** @brief Destructs the autosave feature. */ virtual ~Autosave(); private: friend class AutosavePrivate; QScopedPointer autosave_controller; }; } // namespace OpenOrienteering #endif mapper-0.8.1.1/src/core/autosave_p.h000066400000000000000000000033521325266516600172200ustar00rootroot00000000000000/* * Copyright 2014 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_AUTOSAVE_P_H #define OPENORIENTEERING_AUTOSAVE_P_H #include #include #include "autosave.h" namespace OpenOrienteering { /** * @brief AutosavePrivate is a helper class of Autosave. * * AutosavePrivate implements most of Autosave's behaviour. * Autosave is meant to be used through inheritance. * Due to the implemenation of QObject and moc, only the first inherited class * may be derived from QObject. That is why Autosave itself is not derived from * QObject, but rather uses this helper class. */ class AutosavePrivate : public QObject { Q_OBJECT public: AutosavePrivate(Autosave& document); ~AutosavePrivate() override; bool autosaveNeeded(); void setAutosaveNeeded(bool); public slots: void autosave(); void settingsChanged(); private: Q_DISABLE_COPY(AutosavePrivate) Autosave& document; QTimer autosave_timer; bool autosave_needed; int autosave_interval; }; } // namespace OpenOrienteering #endif mapper-0.8.1.1/src/core/crs_template.cpp000066400000000000000000000057621325266516600200760ustar00rootroot00000000000000/* * Copyright 2013, 2014 Thomas Schöps * Copyright 2014, 2015 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "crs_template.h" #include #include #include #include namespace OpenOrienteering { // From crs_template_implementation.h/.cpp namespace CRSTemplates { CRSTemplateRegistry::TemplateList defaultList(); } // ### CRSParameterWidgetObserver ### CRSParameterWidgetObserver::~CRSParameterWidgetObserver() = default; // ### CRSTemplateParameter ### CRSTemplateParameter::CRSTemplateParameter(const QString& key, const QString& description) : param_id(key) , param_name(description) { // nothing } CRSTemplateParameter::~CRSTemplateParameter() { // nothing, not inlined } std::vector CRSTemplateParameter::specValues(const QString& edit_value) const { return { edit_value }; } // ### CRSTemplate ### CRSTemplate::CRSTemplate( const QString& id, const QString& name, const QString& coordinates_name, const QString& spec_template, ParameterList&& parameters) : template_id(id) , template_name(name) , coordinates_name(coordinates_name) , spec_template(spec_template) , params(std::move(parameters)) { // nothing } CRSTemplate::~CRSTemplate() { for (auto&& param : params) delete param; } QString CRSTemplate::coordinatesName(const std::vector& values) const { Q_ASSERT(params.size() == values.size() || values.empty()); auto name = coordinates_name; auto value = begin(values); auto last_value = end(values); for (auto key = begin(params), last = end(params); key != last && value != last_value; ++key, ++value) { name.replace(QLatin1Char('@') + (*key)->id() + QLatin1Char('@'), *value); } return name; } // ### CRSTemplateRegistry ### CRSTemplateRegistry::CRSTemplateRegistry() { static auto shared_list = CRSTemplates::defaultList(); this->templates = &shared_list; } const CRSTemplate* CRSTemplateRegistry::find(const QString& id) const { const CRSTemplate* ret = nullptr; for (auto&& temp : *templates) { if (temp->id() == id) { ret = temp.get(); break; } } return ret; } void CRSTemplateRegistry::add(std::unique_ptr temp) { templates->push_back(std::move(temp)); } } // namespace OpenOrienteering mapper-0.8.1.1/src/core/crs_template.h000066400000000000000000000165601325266516600175410ustar00rootroot00000000000000/* * Copyright 2013, 2014 Thomas Schöps * Copyright 2014, 2015 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_CRS_TEMPLATE_H #define OPENORIENTEERING_CRS_TEMPLATE_H #include #include #include class QWidget; namespace OpenOrienteering { class Georeferencing; /** * Abstract base class for users of CRS parameter widgets. */ class CRSParameterWidgetObserver { public: CRSParameterWidgetObserver() noexcept = default; CRSParameterWidgetObserver(const CRSParameterWidgetObserver&) = delete; CRSParameterWidgetObserver(CRSParameterWidgetObserver&&) = delete; virtual ~CRSParameterWidgetObserver(); CRSParameterWidgetObserver& operator=(const CRSParameterWidgetObserver&) = delete; CRSParameterWidgetObserver& operator=(CRSParameterWidgetObserver&&) = delete; /** * Informs the observer about a change in a CRS parameter widget. */ virtual void crsParameterEdited() = 0; /** * Returns the current georeferencing. * * CRS parameter widgets may use this georeferencing for calculating values. */ virtual const Georeferencing& georeferencing() const = 0; }; /** * Abstract base class for parameters in CRSTemplates. */ class CRSTemplateParameter { public: using WidgetObserver = CRSParameterWidgetObserver; /** * Constructs a new parameter with the given key and description. */ CRSTemplateParameter(const QString& id, const QString& name); CRSTemplateParameter(const CRSTemplateParameter&) = delete; CRSTemplateParameter(CRSTemplateParameter&&) = delete; /** * Destructor. */ virtual ~CRSTemplateParameter(); CRSTemplateParameter& operator=(const CRSTemplateParameter&) = delete; CRSTemplateParameter& operator=(CRSTemplateParameter&&) = delete; /** * Returns the parameter's permanent unique ID. */ QString id() const; /** * Returns the parameter's display name. */ QString name() const; /** * Creates a widget which can be used to edit the value. * * The widget should be simple in the sense that it can be used as a field * in a QFormLayout, together with the parameter's label. */ virtual QWidget* createEditor(WidgetObserver& widget_observer) const = 0; /** * Return a list of actual specification parameters values from a value in * storage format. * * The default implementation returns a vector which contains just the * single edit_value. */ virtual std::vector specValues(const QString& edit_value) const; /** * Return the widget's value(s) in form of a single string. * * This string can be stored and used for restoring the widget. * * \see CRSTemplateParameter::setValue */ virtual QString value(const QWidget* edit_widget) const = 0; /** * Sets the widget to a stored value. * * \see CRSTemplateParameter::value */ virtual void setValue(QWidget* edit_widget, const QString& value) = 0; private: const QString param_id; const QString param_name; }; /** * A template for a coordinate reference system specification (CRS) string. * * A CRSTemplate may contain one or more parameters described by the * CRSTemplateParameter class. For each parameter, spec_template must contain a * number of free parameters for QString::arg(), e.g. "%1" for the first parameter. */ class CRSTemplate { public: using Parameter = CRSTemplateParameter; using ParameterList = std::vector; /** * Creates a new CRS template. * * The id must be unique and different from "Local". * The template takes ownership of the parameters in the list. * * The coordinates_name may contain placeholders written @id@ which refer * to the parameter with the given ID. They can be replaced with actual * parameter values when calling coordinatesName(). */ CRSTemplate( const QString& template_id, const QString& template_name, const QString& coordinates_name, const QString& spec_template, ParameterList&& parameters ); // Copying of parameters is not implemented here. CRSTemplate(const CRSTemplate&) = delete; /** * Destructor. * * This deletes the parameters. */ ~CRSTemplate(); // Copying of parameters is not implemented here. CRSTemplate& operator=(const CRSTemplate&) = delete; /** * Returns the unique ID of this template. */ QString id() const; /** * Returns the display name of this template. */ QString name() const; /** * Returns the display name for the coordinates of this template, * e.g. "UTM coordinates". * * The values list must be either of the same size as the templates list * parameters, or empty. The given parameter values are substituted for the * respective @id@ placeholders in the coordinates_name which was passed to * the constructor. */ QString coordinatesName(const std::vector& values = {}) const; /** * Returns the specification string template in Proj.4 format. */ QString specificationTemplate() const; /** * Returns the parameters. */ const ParameterList& parameters() const; private: const QString template_id; const QString template_name; const QString coordinates_name; const QString spec_template; const ParameterList params; }; /** * A directory of known CRS templates. * * All instances of this class provide access to the same list. */ class CRSTemplateRegistry { public: using TemplateList = std::vector< std::unique_ptr >; /** * Creates an object for accessing the CRS template directory. */ CRSTemplateRegistry(); /** * Returns the list of registered CRS templates. */ const TemplateList& list() const; /** * Finds the registered CRS template with the given id, * or returns nullptr if the given id does not exist. */ const CRSTemplate* find(const QString& id) const; /** * Registers a CRS template. * * Note that the directory and thus the template outlives the * CRSTemplateRegistry object. */ void add(std::unique_ptr temp); private: TemplateList* templates; }; //### CRSTemplateParameter inline code ### inline QString CRSTemplateParameter::id() const { return param_id; } inline QString CRSTemplateParameter::name() const { return param_name; } //### CRSTemplate inline code ### inline QString CRSTemplate::id() const { return template_id; } inline QString CRSTemplate::name() const { return template_name; } inline QString CRSTemplate::specificationTemplate() const { return spec_template; } inline const CRSTemplate::ParameterList& CRSTemplate::parameters() const { return params; } //### CRSTemplateRegistry inline code ### inline const CRSTemplateRegistry::TemplateList& CRSTemplateRegistry::list() const { return *templates; } } // namespace OpenOrienteering #endif mapper-0.8.1.1/src/core/crs_template_implementation.cpp000066400000000000000000000207141325266516600231750ustar00rootroot00000000000000/* * Copyright 2013, 2014 Thomas Schöps * Copyright 2014-2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "crs_template_implementation.h" #include #include #include #include #include #include #include #include #include #include #include "core/crs_template.h" #include "core/georeferencing.h" #include "core/latlon.h" #include "gui/util_gui.h" #include "gui/widgets/crs_param_widgets.h" namespace OpenOrienteering { namespace CRSTemplates { CRSTemplateRegistry::TemplateList defaultList() { CRSTemplateRegistry::TemplateList templates; templates.reserve(5); // UTM auto temp = std::make_unique( QString::fromLatin1("UTM"), ::OpenOrienteering::Georeferencing::tr("UTM", "UTM coordinate reference system"), ::OpenOrienteering::Georeferencing::tr("UTM coordinates"), QString::fromLatin1("+proj=utm +datum=WGS84 +zone=%1"), CRSTemplate::ParameterList { new UTMZoneParameter(QString::fromLatin1("zone"), ::OpenOrienteering::Georeferencing::tr("UTM Zone (number north/south)")) } ); templates.push_back(std::move(temp)); // Gauss-Krueger temp = std::make_unique( QString::fromLatin1("Gauss-Krueger, datum: Potsdam"), ::OpenOrienteering::Georeferencing::tr("Gauss-Krueger, datum: Potsdam", "Gauss-Krueger coordinate reference system"), ::OpenOrienteering::Georeferencing::tr("Gauss-Krueger coordinates"), QString::fromLatin1("+proj=tmerc +lat_0=0 +lon_0=%1 +k=1.000000 +x_0=%2 +y_0=0 +ellps=bessel +datum=potsdam +units=m +no_defs"), CRSTemplate::ParameterList { new IntRangeParameter(QString::fromLatin1("zone"), ::OpenOrienteering::Georeferencing::tr("Zone number (1 to 119)", "Zone number for Gauss-Krueger coordinates"), 1, 119, { {3, 0}, {1000000, 500000} }) } ); templates.push_back(std::move(temp)); // EPSG temp = std::make_unique( QString::fromLatin1("EPSG"), ::OpenOrienteering::Georeferencing::tr("by EPSG code", "as in: The CRS is specified by EPSG code"), //: Don't translate @code@. It is placeholder. ::OpenOrienteering::Georeferencing::tr("EPSG @code@ coordinates"), QString::fromLatin1("+init=epsg:%1"), CRSTemplate::ParameterList { new IntRangeParameter(QString::fromLatin1("code"), ::OpenOrienteering::Georeferencing::tr("EPSG code"), 1000, 99999) } ); templates.push_back(std::move(temp)); // Custom temp = std::make_unique( QString::fromLatin1("PROJ.4"), // Don't change this ID. ::OpenOrienteering::Georeferencing::tr("Custom PROJ.4", "PROJ.4 specification"), ::OpenOrienteering::Georeferencing::tr("Local coordinates"), QString::fromLatin1("%1"), CRSTemplate::ParameterList { new FullSpecParameter(QString::fromLatin1("spec"), ::OpenOrienteering::Georeferencing::tr("Specification", "PROJ.4 specification")) } ); templates.push_back(std::move(temp)); return templates; } // ### TextParameter ### TextParameter::TextParameter(const QString& key, const QString& name) : CRSTemplateParameter(key, name) { // nothing } QWidget* TextParameter::createEditor(WidgetObserver& observer) const { auto widget = new Editor(); QObject::connect(widget, &TextParameter::Editor::textChanged, [&observer](){ observer.crsParameterEdited(); }); return widget; } QString TextParameter::value(const QWidget* edit_widget) const { QString value; auto field = qobject_cast(edit_widget); if (field) value = field->text(); return value; } void TextParameter::setValue(QWidget* edit_widget, const QString& value) { auto field = qobject_cast(edit_widget); if (field) field->setText(value); } // ### FullSpecParameter ### FullSpecParameter::FullSpecParameter(const QString& key, const QString& name) : TextParameter(key, name) { // nothing } QWidget* FullSpecParameter::createEditor(WidgetObserver& observer) const { auto widget = qobject_cast(TextParameter::createEditor(observer)); Q_ASSERT(widget); if (widget) { const QSignalBlocker block(widget); widget->setText(observer.georeferencing().getProjectedCRSSpec()); } return widget; } void FullSpecParameter::setValue(QWidget* edit_widget, const QString& value) { // Don't accidently clear this field. if (!value.isEmpty()) TextParameter::setValue(edit_widget, value); } // ### UTMZoneParameter ### UTMZoneParameter::UTMZoneParameter(const QString& key, const QString& name) : CRSTemplateParameter(key, name) { // nothing } UTMZoneParameter::~UTMZoneParameter() { // nothing } QWidget* UTMZoneParameter::createEditor(WidgetObserver& observer) const { auto widget = new UTMZoneEdit(observer, nullptr); QObject::connect(widget, &UTMZoneEdit::textEdited, [&observer](){ observer.crsParameterEdited(); }); return widget; } std::vector UTMZoneParameter::specValues(const QString& edit_value) const { auto zone = QString { edit_value }; zone.remove(QLatin1String(" N")); zone.replace(QLatin1String(" S"), QLatin1String(" +south")); return { zone }; } QString UTMZoneParameter::value(const QWidget* edit_widget) const { QString value; if (auto text_edit = qobject_cast(edit_widget)) value = text_edit->text(); return value; } void UTMZoneParameter::setValue(QWidget* edit_widget, const QString& value) { // Don't accidently clear this field. auto text_edit = qobject_cast(edit_widget); if (text_edit && !value.isEmpty()) text_edit->setText(value); } QVariant UTMZoneParameter::calculateUTMZone(const LatLon lat_lon) { QVariant ret; const double lat = lat_lon.latitude(); if (fabs(lat) < 84.0) { const double lon = lat_lon.longitude(); int zone_no = int(floor(lon) + 180) / 6 % 60 + 1; if (zone_no == 31 && lon >= 3.0 && lat >= 56.0 && lat < 64.0) zone_no = 32; // South Norway else if (lat >= 72.0 && lon >= 3.0 && lon <= 39.0) zone_no = 2 * (int(floor(lon) + 3.0) / 12) + 31; // Svalbard QString zone = QString::number(zone_no); if (zone_no < 10) zone.prepend(QLatin1String("0")); zone.append((lat >= 0.0) ? QLatin1String(" N") : QLatin1String(" S")); ret = zone; } return ret; } // ### IntRangeParameter ### IntRangeParameter::IntRangeParameter(const QString& key, const QString& name, int min_value, int max_value) : IntRangeParameter(key, name, min_value, max_value, { {1, 0} }) { // nothing } IntRangeParameter::IntRangeParameter(const QString& key, const QString& name, int min_value, int max_value, OutputList&& outputs) : CRSTemplateParameter(key, name) , min_value(min_value) , max_value(max_value) , outputs(std::move(outputs)) { // nothing } IntRangeParameter::~IntRangeParameter() { // nothing } QWidget* IntRangeParameter::createEditor(WidgetObserver& observer) const { using TakingIntArgument = void (QSpinBox::*)(int); auto widget = Util::SpinBox::create(min_value, max_value); QObject::connect(widget, (TakingIntArgument) &QSpinBox::valueChanged, [&observer](){ observer.crsParameterEdited(); }); return widget; } std::vector IntRangeParameter::specValues(const QString& edit_value) const { auto chosen_value = edit_value.toInt(); std::vector spec_values; spec_values.reserve(outputs.size()); for (auto&& factor_and_bias : outputs) { spec_values.push_back(QString::number(factor_and_bias.first * chosen_value + factor_and_bias.second)); } return spec_values; } QString IntRangeParameter::value(const QWidget* edit_widget) const { QString value; if (auto spin_box = qobject_cast(edit_widget)) value = spin_box->cleanText(); return value; } void IntRangeParameter::setValue(QWidget* edit_widget, const QString& value) { if (auto spin_box = qobject_cast(edit_widget)) spin_box->setValue(value.toInt()); } } // namespace CRSTemplates } // namespace OpenOrienteering mapper-0.8.1.1/src/core/crs_template_implementation.h000066400000000000000000000071001325266516600226340ustar00rootroot00000000000000/* * Copyright 2013, 2014 Thomas Schöps * Copyright 2014, 2015 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_CRS_TEMPLATE_IMPLEMENTATION_H #define OPENORIENTEERING_CRS_TEMPLATE_IMPLEMENTATION_H #include "crs_template.h" #include #include #include #include #include class QWidget; namespace OpenOrienteering { class LatLon; /** * This namespace collects CRS template implementations */ namespace CRSTemplates { /** * Creates and returns a list of known CRS Templates. */ CRSTemplateRegistry::TemplateList defaultList(); /** * CRSTemplate parameter specifying a text parameter. */ class TextParameter : public CRSTemplateParameter { public: TextParameter(const QString& id, const QString& name); QWidget* createEditor(WidgetObserver& observer) const override; QString value(const QWidget* edit_widget) const override; void setValue(QWidget* edit_widget, const QString& value) override; protected: /// The type of editor widget returned from createEditor. using Editor = QLineEdit; }; /** * CRSTemplate parameter giving a full specification. */ class FullSpecParameter : public TextParameter { public: FullSpecParameter(const QString& id, const QString& name); QWidget* createEditor(WidgetObserver& observer) const override; void setValue(QWidget* edit_widget, const QString& value) override; }; /** * CRSTemplate parameter specifying a UTM zone. */ class UTMZoneParameter : public CRSTemplateParameter { public: UTMZoneParameter(const QString& id, const QString& name); ~UTMZoneParameter() override; QWidget* createEditor(WidgetObserver& observer) const override; std::vector specValues(const QString& edit_value) const override; QString value(const QWidget* edit_widget) const override; void setValue(QWidget* edit_widget, const QString& value) override; /** * Determines the UTM zone from the given latitude and longitude. * * Returns a null value on error. */ static QVariant calculateUTMZone(const LatLon lat_lon); }; /** * CRSTemplate integer parameter, with values from an integer range. */ class IntRangeParameter : public CRSTemplateParameter { public: using OutputList = std::vector< std::pair >; IntRangeParameter(const QString& id, const QString& name, int min_value, int max_value); IntRangeParameter(const QString& id, const QString& name, int min_value, int max_value, OutputList&& outputs); ~IntRangeParameter() override; QWidget* createEditor(WidgetObserver& observer) const override; std::vector specValues(const QString& edit_value) const override; QString value(const QWidget* edit_widget) const override; void setValue(QWidget* edit_widget, const QString& value) override; private: const int min_value; const int max_value; const OutputList outputs; }; } // namespace CRSTemplates } // namespace OpenOrienteering #endif mapper-0.8.1.1/src/core/georeferencing.cpp000066400000000000000000000622451325266516600203750ustar00rootroot00000000000000/* * Copyright 2012-2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "georeferencing.h" #include #include #include #include #include #include #include #include // IWYU pragma: keep #include #include #include #include #include #include #include // IWYU pragma: keep #include #include #include #include "core/crs_template.h" #include "fileformats/file_format.h" #include "fileformats/xml_file_format.h" #include "util/xml_stream_util.h" // ### A namespace which collects various string constants of type QLatin1String. ### namespace literal { static const QLatin1String georeferencing("georeferencing"); static const QLatin1String scale("scale"); static const QLatin1String grid_scale_factor{"grid_scale_factor"}; static const QLatin1String declination("declination"); static const QLatin1String grivation("grivation"); static const QLatin1String ref_point("ref_point"); static const QLatin1String ref_point_deg("ref_point_deg"); static const QLatin1String projected_crs("projected_crs"); static const QLatin1String geographic_crs("geographic_crs"); static const QLatin1String spec("spec"); static const QLatin1String parameter("parameter"); static const QLatin1String x("x"); static const QLatin1String y("y"); static const QLatin1String id("id"); static const QLatin1String language("language"); static const QLatin1String lat("lat"); static const QLatin1String lon("lon"); static const QLatin1String proj_4("PROJ.4"); static const QLatin1String geographic_coordinates("Geographic coordinates"); } namespace OpenOrienteering { namespace { /** Helper for PROJ.4 initialization. * * To be used as static object in the right place. */ class ProjSetup { public: ProjSetup() { auto proj_data = QFileInfo(QLatin1String("data:/proj")); if (proj_data.exists()) { static const auto location = proj_data.absoluteFilePath().toLocal8Bit(); static auto data = location.constData(); pj_set_searchpath(1, &data); } #if defined(Q_OS_ANDROID) // Register file finder function needed by Proj.4 registerProjFileHelper(); #endif } }; /** Helper for PROJ.4 initialization. * * This helper adds "+no_defs" if it is not already part of the * specification. It also takes care of resetting the pj errno. */ projPJ pj_init_plus_no_defs(const QString& spec) { auto spec_latin1 = spec.toLatin1(); if (!spec_latin1.contains("+no_defs")) spec_latin1.append(" +no_defs"); *pj_get_errno_ref() = 0; return pj_init_plus(spec_latin1); } /** * List of substitutions for specifications which are known to be broken in Proj.4. */ const char* spec_substitutions[][2] = { // #542, S-JTSK (Greenwich) / Krovak East North { "+init=epsg:5514", "+proj=krovak" " +lat_0=49.5 +lon_0=24.83333333333333" " +alpha=30.28813972222222 +k=0.9999" " +x_0=0 +y_0=0" " +ellps=bessel" " +pm=greenwich" " +towgs84=542.5,89.2,456.9,5.517,2.275,5.516,6.96" " +units=m" " +no_defs" }, }; } // namespace //### Georeferencing ### const QString Georeferencing::geographic_crs_spec(QString::fromLatin1("+proj=latlong +datum=WGS84")); Georeferencing::Georeferencing() : state(Local), scale_denominator{1000}, grid_scale_factor{1.0}, declination(0.0), grivation(0.0), grivation_error(0.0), map_ref_point(0, 0), projected_ref_point(0, 0) { static ProjSetup run_once; updateTransformation(); projected_crs_id = QString::fromLatin1("Local"); projected_crs = nullptr; geographic_crs = pj_init_plus_no_defs(geographic_crs_spec); Q_ASSERT(geographic_crs); } Georeferencing::Georeferencing(const Georeferencing& other) : QObject(), state(other.state), scale_denominator(other.scale_denominator), grid_scale_factor{other.grid_scale_factor}, declination(other.declination), grivation(other.grivation), grivation_error(other.grivation_error), map_ref_point(other.map_ref_point), projected_ref_point(other.projected_ref_point), projected_crs_id(other.projected_crs_id), projected_crs_spec(other.projected_crs_spec), projected_crs_parameters(other.projected_crs_parameters), geographic_ref_point(other.geographic_ref_point) { updateTransformation(); geographic_crs = pj_init_plus_no_defs(geographic_crs_spec); Q_ASSERT(geographic_crs); projected_crs = pj_init_plus_no_defs(projected_crs_spec); } Georeferencing::~Georeferencing() { if (projected_crs) pj_free(projected_crs); if (geographic_crs) pj_free(geographic_crs); } Georeferencing& Georeferencing::operator=(const Georeferencing& other) { if (&other == this) return *this; state = other.state; scale_denominator = other.scale_denominator; grid_scale_factor = other.grid_scale_factor; declination = other.declination; grivation = other.grivation; grivation_error = other.grivation_error; map_ref_point = other.map_ref_point; projected_ref_point = other.projected_ref_point; from_projected = other.from_projected; to_projected = other.to_projected; projected_ref_point = other.projected_ref_point; projected_crs_id = other.projected_crs_id; projected_crs_spec = other.projected_crs_spec; projected_crs_parameters = other.projected_crs_parameters; geographic_ref_point = other.geographic_ref_point; if (projected_crs) pj_free(projected_crs); projected_crs = pj_init_plus_no_defs(projected_crs_spec); emit stateChanged(); emit transformationChanged(); emit declinationChanged(); emit projectionChanged(); return *this; } bool Georeferencing::isGeographic() const { return projected_crs && pj_is_latlong(projected_crs); } void Georeferencing::load(QXmlStreamReader& xml, bool load_scale_only) { Q_ASSERT(xml.name() == literal::georeferencing); { // Reset to default values const QSignalBlocker block(this); *this = Georeferencing(); } XmlElementReader georef_element(xml); scale_denominator = georef_element.attribute(literal::scale); if (scale_denominator <= 0) throw FileFormatException(tr("Map scale specification invalid or missing.")); if (georef_element.hasAttribute(literal::grid_scale_factor)) { grid_scale_factor = roundScaleFactor(georef_element.attribute(literal::grid_scale_factor)); if (grid_scale_factor <= 0.0) throw FileFormatException(tr("Invalid grid scale factor: %1").arg(QString::number(grid_scale_factor))); } state = Local; if (load_scale_only) { xml.skipCurrentElement(); } else { if (georef_element.hasAttribute(literal::declination)) declination = roundDeclination(georef_element.attribute(literal::declination)); if (georef_element.hasAttribute(literal::grivation)) { grivation = roundDeclination(georef_element.attribute(literal::grivation)); grivation_error = georef_element.attribute(literal::grivation) - grivation; } while (xml.readNextStartElement()) { if (xml.name() == literal::ref_point) { XmlElementReader ref_point_element(xml); map_ref_point.setX(ref_point_element.attribute(literal::x)); map_ref_point.setY(ref_point_element.attribute(literal::y)); } else if (xml.name() == literal::projected_crs) { XmlElementReader crs_element(xml); projected_crs_id = crs_element.attribute(literal::id); while (xml.readNextStartElement()) { XmlElementReader current_element(xml); if (xml.name() == literal::spec) { const QString language = current_element.attribute(literal::language); if (language != literal::proj_4) throw FileFormatException(tr("Unknown CRS specification language: %1").arg(language)); projected_crs_spec = xml.readElementText(); } else if (xml.name() == literal::parameter) { projected_crs_parameters.push_back(xml.readElementText()); } else if (xml.name() == literal::ref_point) { projected_ref_point.setX(current_element.attribute(literal::x)); projected_ref_point.setY(current_element.attribute(literal::y)); } else // unknown { ; // nothing } } } else if (xml.name() == literal::geographic_crs) { while (xml.readNextStartElement()) { XmlElementReader current_element(xml); if (xml.name() == literal::spec) { const QString language = current_element.attribute(literal::language); if (language != literal::proj_4) throw FileFormatException(tr("Unknown CRS specification language: %1").arg(language)); QString geographic_crs_spec = xml.readElementText(); if (Georeferencing::geographic_crs_spec != geographic_crs_spec) throw FileFormatException(tr("Unsupported geographic CRS specification: %1").arg(geographic_crs_spec)); } else if (xml.name() == literal::ref_point) { // Legacy, latitude/longitude in radiant double latitude = current_element.attribute(literal::lat); double longitude = current_element.attribute(literal::lon); geographic_ref_point = LatLon::fromRadiant(latitude, longitude); } else if (xml.name() == literal::ref_point_deg) { // Legacy, latitude/longitude in degrees double latitude = current_element.attribute(literal::lat); double longitude = current_element.attribute(literal::lon); geographic_ref_point = LatLon(latitude, longitude); } else // unknown { ; // nothing } } } else // unknown { ; // nothing } } } emit stateChanged(); updateTransformation(); emit declinationChanged(); if (!projected_crs_spec.isEmpty()) { if (projected_crs) pj_free(projected_crs); projected_crs = pj_init_plus_no_defs(projected_crs_spec); if (0 == *pj_get_errno_ref()) { state = Normal; emit stateChanged(); } } emit projectionChanged(); } void Georeferencing::save(QXmlStreamWriter& xml) const { XmlElementWriter georef_element(xml, literal::georeferencing); georef_element.writeAttribute(literal::scale, scale_denominator); if (grid_scale_factor != 1.0) georef_element.writeAttribute(literal::grid_scale_factor, grid_scale_factor, scaleFactorPrecision()); if (!qIsNull(declination)) georef_element.writeAttribute(literal::declination, declination, declinationPrecision()); if (!qIsNull(grivation)) georef_element.writeAttribute(literal::grivation, grivation, declinationPrecision()); if (!qIsNull(map_ref_point.lengthSquared())) { XmlElementWriter ref_point_element(xml, literal::ref_point); ref_point_element.writeAttribute(literal::x, map_ref_point.x()); ref_point_element.writeAttribute(literal::y, map_ref_point.y()); } { XmlElementWriter crs_element(xml, literal::projected_crs); if (!projected_crs_id.isEmpty()) crs_element.writeAttribute(literal::id, projected_crs_id); if (!projected_crs_spec.isEmpty()) { XmlElementWriter spec_element(xml, literal::spec); spec_element.writeAttribute(literal::language, literal::proj_4); xml.writeCharacters(projected_crs_spec); } if (!projected_crs_parameters.empty()) { for (const auto& projected_crs_parameter : projected_crs_parameters) { XmlElementWriter parameter_element(xml, literal::parameter); xml.writeCharacters(projected_crs_parameter); Q_UNUSED(parameter_element); // Suppress compiler warnings } } if (!qIsNull(projected_ref_point.manhattanLength())) { XmlElementWriter ref_point_element(xml, literal::ref_point); ref_point_element.writeAttribute(literal::x, projected_ref_point.x(), 6); ref_point_element.writeAttribute(literal::y, projected_ref_point.y(), 6); } } if (state == Normal) { XmlElementWriter crs_element(xml, literal::geographic_crs); crs_element.writeAttribute(literal::id, literal::geographic_coordinates); { XmlElementWriter spec_element(xml, literal::spec); spec_element.writeAttribute(literal::language, literal::proj_4); xml.writeCharacters(geographic_crs_spec); } if (XMLFileFormat::active_version < 6) { // Legacy compatibility XmlElementWriter ref_point_element(xml, literal::ref_point); ref_point_element.writeAttribute(literal::lat, degToRad(geographic_ref_point.latitude()), 10); ref_point_element.writeAttribute(literal::lon, degToRad(geographic_ref_point.longitude()), 10); } { XmlElementWriter ref_point_element(xml, literal::ref_point_deg); ref_point_element.writeAttribute(literal::lat, geographic_ref_point.latitude(), 8); ref_point_element.writeAttribute(literal::lon, geographic_ref_point.longitude(), 8); } } } void Georeferencing::setState(Georeferencing::State value) { if (state != value) { state = value; updateTransformation(); if (state != Normal) setProjectedCRS(QStringLiteral("Local"), {}); emit stateChanged(); } } void Georeferencing::setScaleDenominator(int value) { Q_ASSERT(value > 0); if (scale_denominator != (unsigned int)value) { scale_denominator = value; updateTransformation(); } } void Georeferencing::setGridScaleFactor(double value) { Q_ASSERT(value > 0); if (grid_scale_factor != value) { grid_scale_factor = value; updateTransformation(); } } void Georeferencing::setDeclination(double value) { double declination = roundDeclination(value); double grivation = declination - getConvergence(); setDeclinationAndGrivation(declination, grivation); } void Georeferencing::setGrivation(double value) { double grivation = roundDeclination(value); double declination = grivation + getConvergence(); setDeclinationAndGrivation(declination, grivation); } void Georeferencing::setDeclinationAndGrivation(double declination, double grivation) { bool declination_change = declination != this->declination; if (declination_change || grivation != this->grivation) { this->declination = declination; this->grivation = grivation; this->grivation_error = 0.0; updateTransformation(); if (declination_change) emit declinationChanged(); } } void Georeferencing::setMapRefPoint(MapCoord point) { if (map_ref_point != point) { map_ref_point = point; updateTransformation(); } } void Georeferencing::setProjectedRefPoint(QPointF point, bool update_grivation) { if (projected_ref_point != point || state == Normal) { projected_ref_point = point; bool ok; LatLon new_geo_ref_point; switch (state) { default: qWarning("Undefined georeferencing state"); // fall through case Local: break; case Normal: new_geo_ref_point = toGeographicCoords(point, &ok); if (ok && new_geo_ref_point != geographic_ref_point) { geographic_ref_point = new_geo_ref_point; if (update_grivation) updateGrivation(); emit projectionChanged(); } } updateTransformation(); } } QString Georeferencing::getProjectedCRSName() const { QString name = tr("Local"); if (auto temp = CRSTemplateRegistry().find(projected_crs_id)) name = temp->name(); return name; } QString Georeferencing::getProjectedCoordinatesName() const { QString name = tr("Local coordinates"); if (auto temp = CRSTemplateRegistry().find(projected_crs_id)) name = temp->coordinatesName(projected_crs_parameters); return name; } double Georeferencing::getConvergence() const { if (state != Normal || !isValid()) return 0.0; // Second point on the same meridian const double delta_phi = 360.0 / 40000.0; // roughly 1 km double other_latitude = geographic_ref_point.latitude(); other_latitude += (other_latitude < 0.0) ? delta_phi : -delta_phi; const double same_longitude = geographic_ref_point.longitude(); QPointF projected_other = toProjectedCoords(LatLon(other_latitude, same_longitude)); double denominator = projected_other.y() - projected_ref_point.y(); if (fabs(denominator) < 0.00000000001) return 0.0; return roundDeclination(RAD_TO_DEG * atan((projected_ref_point.x() - projected_other.x()) / denominator)); } void Georeferencing::setGeographicRefPoint(LatLon lat_lon, bool update_grivation) { bool geo_ref_point_changed = geographic_ref_point != lat_lon; if (geo_ref_point_changed || state == Normal) { geographic_ref_point = lat_lon; if (state != Normal) setState(Normal); bool ok; QPointF new_projected_ref = toProjectedCoords(lat_lon, &ok); if (ok && new_projected_ref != projected_ref_point) { projected_ref_point = new_projected_ref; if (update_grivation) updateGrivation(); updateTransformation(); emit projectionChanged(); } else if (geo_ref_point_changed) { emit projectionChanged(); } } } void Georeferencing::updateTransformation() { QTransform transform; transform.translate(projected_ref_point.x(), projected_ref_point.y()); transform.rotate(-grivation); double scale = grid_scale_factor * scale_denominator / 1000.0; // to meters transform.scale(scale, -scale); transform.translate(-map_ref_point.x(), -map_ref_point.y()); if (to_projected != transform) { to_projected = transform; from_projected = transform.inverted(); emit transformationChanged(); } } void Georeferencing::updateGrivation() { setDeclination(declination); } void Georeferencing::initDeclination() { setGrivation(grivation); } void Georeferencing::setTransformationDirectly(const QTransform& transform) { if (transform != to_projected) { to_projected = transform; from_projected = to_projected.inverted(); emit transformationChanged(); } } QTransform Georeferencing::mapToProjected() const { return to_projected; } QTransform Georeferencing::projectedToMap() const { return from_projected; } bool Georeferencing::setProjectedCRS(const QString& id, QString spec, std::vector params) { // Default return value if no change is neccessary bool ok = (state == Normal || projected_crs_spec.isEmpty()); for (const auto& substitution : spec_substitutions) { if (QLatin1String(substitution[0]) == spec) { spec = QString::fromLatin1(substitution[1]); break; } } // Changes in params shall already be recorded in spec if (projected_crs_id != id || projected_crs_spec != spec || projected_crs_parameters.size() != params.size() || !std::equal(begin(params), end(params), begin(projected_crs_parameters)) || (!ok && !spec.isEmpty()) ) { if (projected_crs) pj_free(projected_crs); projected_crs_id = id; projected_crs_spec = spec; if (projected_crs_spec.isEmpty()) { projected_crs_parameters.clear(); projected_crs = nullptr; ok = (state != Normal); } else { projected_crs_parameters.swap(params); // params was passed by value! projected_crs = pj_init_plus_no_defs(projected_crs_spec); ok = (0 == *pj_get_errno_ref()); if (ok && state != Normal) setState(Normal); } emit projectionChanged(); } return ok; } QPointF Georeferencing::toProjectedCoords(const MapCoord& map_coords) const { return to_projected.map(QPointF(map_coords)); } QPointF Georeferencing::toProjectedCoords(const MapCoordF& map_coords) const { return to_projected.map(map_coords); } MapCoord Georeferencing::toMapCoords(const QPointF& projected_coords) const { return MapCoord(from_projected.map(projected_coords)); } MapCoordF Georeferencing::toMapCoordF(const QPointF& projected_coords) const { return MapCoordF(from_projected.map(projected_coords)); } LatLon Georeferencing::toGeographicCoords(const MapCoordF& map_coords, bool* ok) const { return toGeographicCoords(toProjectedCoords(map_coords), ok); } LatLon Georeferencing::toGeographicCoords(const QPointF& projected_coords, bool* ok) const { if (ok) *ok = false; double easting = projected_coords.x(), northing = projected_coords.y(); if (projected_crs && geographic_crs) { int ret = pj_transform(projected_crs, geographic_crs, 1, 1, &easting, &northing, nullptr); if (ok) *ok = (ret == 0); } return LatLon::fromRadiant(northing, easting); } QPointF Georeferencing::toProjectedCoords(const LatLon& lat_lon, bool* ok) const { if (ok) *ok = false; double easting = degToRad(lat_lon.longitude()), northing = degToRad(lat_lon.latitude()); if (projected_crs && geographic_crs) { int ret = pj_transform(geographic_crs, projected_crs, 1, 1, &easting, &northing, nullptr); if (ok) *ok = (ret == 0); } return QPointF(easting, northing); } MapCoord Georeferencing::toMapCoords(const LatLon& lat_lon, bool* ok) const { return toMapCoords(toProjectedCoords(lat_lon, ok)); } MapCoordF Georeferencing::toMapCoordF(const LatLon& lat_lon, bool* ok) const { return toMapCoordF(toProjectedCoords(lat_lon, ok)); } MapCoordF Georeferencing::toMapCoordF(const Georeferencing* other, const MapCoordF& map_coords, bool* ok) const { if (!other) { if (ok) *ok = true; return map_coords; } else if (isLocal() || other->isLocal()) { if (ok) *ok = true; return toMapCoordF(other->toProjectedCoords(map_coords)); } else { if (ok) *ok = false; QPointF projected_coords = other->toProjectedCoords(map_coords); double easting = projected_coords.x(), northing = projected_coords.y(); if (projected_crs && other->projected_crs) { // Direct transformation: //int ret = pj_transform(other->projected_crs, projected_crs, 1, 1, &easting, &northing, nullptr); // Use geographic coordinates as intermediate step to enforce // that coordinates are assumed to have WGS84 datum if datum is specified in only one CRS spec: int ret = pj_transform(other->projected_crs, geographic_crs, 1, 1, &easting, &northing, nullptr); ret |= pj_transform(geographic_crs, projected_crs, 1, 1, &easting, &northing, nullptr); if (ret != 0) { if (ok) *ok = false; return MapCoordF(easting, northing); } if (ok) *ok = true; } return toMapCoordF(QPointF(easting, northing)); } } QString Georeferencing::getErrorText() const { int err_no = *pj_get_errno_ref(); return (err_no == 0) ? QString() : QString::fromLatin1(pj_strerrno(err_no)); } double Georeferencing::radToDeg(double val) { return RAD_TO_DEG * val; } double Georeferencing::degToRad(double val) { return DEG_TO_RAD * val; } QString Georeferencing::degToDMS(double val) { qint64 tmp = val * 360000; int csec = tmp % 6000; tmp = tmp / 6000; int min = tmp % 60; int deg = tmp / 60; QString ret = QString::fromUtf8("%1°%2'%3\"").arg(deg).arg(min).arg(QLocale().toString(csec/100.0,'f',2)); return ret; } QDebug operator<<(QDebug dbg, const Georeferencing &georef) { dbg.nospace() << "Georeferencing(1:" << georef.scale_denominator << " " << georef.grid_scale_factor << " " << georef.declination << " " << georef.grivation << "deg, " << georef.projected_crs_id << " (" << georef.projected_crs_spec << ") " << QString::number(georef.projected_ref_point.x(), 'f', 8) << "," << QString::number(georef.projected_ref_point.y(), 'f', 8); if (georef.isLocal()) dbg.nospace() << ", local)"; else dbg.nospace() << ", geographic)"; return dbg.space(); } } // namespace OpenOrienteering #if defined(Q_OS_ANDROID) QScopedPointer temp_dir; // removed upon destruction QByteArray c_string; // buffer for const char* extern "C" { /** * @brief Provides required files for Proj.4 library. * * This C function implements the interface required by pj_set_finder(). * * This functions checks if the requested file name is available in a * temporary directory. If not, it tries to copy the file from the proj * subfolder of the assets folder to this temporary directory. * * If the file exists in the temporary folder (or copying was successful) * this function returns the full path of this file as a C string. * This string becomes invalid the next time this function is called. * Otherwise it returns nullptr. */ const char* projFileHelperAndroid(const char *name) { if (temp_dir->isValid()) { QString path = QDir(temp_dir->path()).filePath(QString::fromUtf8(name)); QFile file(path); if (file.exists() || QFile::copy(QLatin1String("assets:/proj/") + QLatin1String(name), path)) { c_string = path.toLocal8Bit(); return c_string.constData(); } } qDebug() << "Could not projection data file" << name; return nullptr; } void registerProjFileHelper() { // QTemporaryDir must not be constructed before QApplication temp_dir.reset(new QTemporaryDir()); if (temp_dir->isValid()) { pj_set_finder(&projFileHelperAndroid); } else { qDebug() << "Could not create a temporary directory for projection data."; } } } #endif // defined(Q_OS_ANDROID) mapper-0.8.1.1/src/core/georeferencing.h000066400000000000000000000427701325266516600200430ustar00rootroot00000000000000/* * Copyright 2012-2016 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_GEOREFERENCING_H #define OPENORIENTEERING_GEOREFERENCING_H #include #include #include #include #include #include #include "core/latlon.h" #include "core/map_coord.h" class QDebug; class QXmlStreamReader; class QXmlStreamWriter; // IWYU pragma: no_forward_declare QPointF typedef void* projPJ; namespace OpenOrienteering { #if defined(Q_OS_ANDROID) /** * Registers a file finder function needed by Proj.4 on Android. */ extern "C" void registerProjFileHelper(); #endif /** * A Georeferencing defines a mapping between "map coordinates" (as measured on * paper) and coordinates in the real world. It provides functions for * converting coordinates from one coordinate system to another. * * Conversions between map coordinates and "projected coordinates" (flat metric * coordinates in a projected coordinate reference system) are made as affine * transformation based on the map scale (principal scale), grid scale factor, * the grivation and a defined reference point. * * Conversions between projected coordinates and geographic coordinates (here: * latitude/longitude for the WGS84 datum) are made based on a specification * of the coordinate reference system of the projected coordinates. The actual * geographic transformation is done by the PROJ.4 library for geographic * projections. * * If no (valid) specification is given, the projected coordinates are regarded * as local coordinates. Local coordinates cannot be converted to other * geographic coordinate systems. The georeferencing is "local". * * Conversions between "map coordinates" and "geographic coordinates" use the * projected coordinates as intermediate step. */ class Georeferencing : public QObject // clazy:exclude=copyable-polymorphic { Q_OBJECT friend class NativeFileImport; friend QDebug operator<<(QDebug dbg, const Georeferencing& georef); public: /// Georeferencing state enum State { /// Only conversions between map and local projected coordinates are possible. Local = 1, /// All coordinate conversions are possible (if there is no error in the /// crs specification). Normal = 2 }; /** * A shared PROJ.4 specification of a WGS84 geographic CRS. */ static const QString geographic_crs_spec; /** * @brief Returns the precision of the grid scale factor. * * The precision is given in number of decimal places, * i.e. digits after the decimal point. */ static constexpr unsigned int scaleFactorPrecision(); /** * @brief Rounds according to the defined precision of the grid scale factor. * * @see scaleFactorPrecision(); */ static double roundScaleFactor(double value); /** * @brief Returns the precision of declination/grivation/convergence. * * The precision is given in number of decimal places, * i.e. digits after the decimal point. * * All values set as declination or grivation will be rounded to this precisison. */ static constexpr unsigned int declinationPrecision(); /** * @brief Rounds according to the defined precision of declination/grivation/convergence. * * @see declinationPrecision(); */ static double roundDeclination(double); /** * Constructs a scale-only georeferencing. */ Georeferencing(); /** * Constructs a georeferencing which is a copy of an existing georeferencing. * * Note: Since QObjects may not be copied, this is better understood as * creating a new object with the same settings. */ Georeferencing(const Georeferencing& georeferencing); /** * Cleans up memory allocated by the georeferencing */ ~Georeferencing() override; /** * Saves the georeferencing to an XML stream. */ void save(QXmlStreamWriter& xml) const; /** * Creates a georeferencing from an XML stream. */ void load(QXmlStreamReader& xml, bool load_scale_only); /** * Assigns the properties of another Georeferencing to this one. * * Having this method in a QObject is in contradiction to Qt conventions. * But we really need to assign properties from one object to another, * where each object maintains its own identity. */ Georeferencing& operator=(const Georeferencing& other); /** * Returns if the georeferencing settings are valid. * * This means that coordinates can be converted between map and projected * coordinates. isLocal() can be checked to determine if a conversion * to geographic coordinates is also possible. */ bool isValid() const; /** * Returns true if this georeferencing is local. * * A georeferencing is local if no (valid) coordinate system specification * is given for the projected coordinates. A local georeferencing cannot * convert coordinates from and to geographic coordinate systems. */ bool isLocal() const; /** * Returns true if the "projected CRS" is actually geographic. * * \see pj_is_latlong(projPJ pj) in PROJ.4 */ bool isGeographic() const; /** * Returns the georeferencing state. */ State getState() const; /** * Sets the georeferencing state. * * This is only neccessary to decrease the state to Local, as otherwise it * will be automatically changed when setting the respective values. */ void setState(State value); /** * Returns the principal scale denominator. * * The principal scale - or representative fraction - is the ratio between * units on the printed map and units on ground. */ unsigned int getScaleDenominator() const; /** * Sets the principal scale denominator. */ void setScaleDenominator(int value); /** * Returns the grid scale factor. * * The grid scale factor is the ratio between a length in the grid and the * length on the earth model. It is applied as a factor to ground distances * to get grid plane distances. * * Mapper doesn't explicitly deal with any other factors (elevation factor, * unit of measurement). Technically, this property can be used as a * combined factor. */ double getGridScaleFactor() const; /** * Sets the grid scale factor. * * \see getGridScaleFactor() */ void setGridScaleFactor(double value); /** * Returns the magnetic declination (in degrees). * * @see setDeclination() */ double getDeclination() const; /** * Sets the magnetic declination (in degrees). * * Magnetic declination is the angle between magnetic north and true north. * In the context of OpenOrienteering Mapper, it is the angle between the * y axis of map coordinates and the latitude axis of geographic * coordinates. * * This will also affect the grivation value and the transformations. */ void setDeclination(double declination); /** * Returns the grivation. * * @see setGrivation() */ double getGrivation() const; /** * Returns the deviation of the grivation from the one given in pre-0.6 files. * * Only valid immediately after loading a georeferencing from a file. * Returns 0.0 in any other context. * * Files from Mapper versions before 0.6 may have used any number of decimal * places for grivation. Since version 0.6, grivation is rounded to the * number of decimal places defined by declinationPrecision(). When this * rounding takes place (i.e. only when opening a file which has not been * saved by 0.6 or later), the difference between the original value and the * rounded value is temporarily provided by this function. This value can be * used to for correcting dependent data. Any changes to declination or * grivation will invalidate this value. * * @see getGrivation() * @see declinationPrecision() */ double getGrivationError() const; /** * Sets the grivation (in degrees). * * Grivation is the angle between magnetic north and grid north. * In the context of OpenOrienteering Mapper, it is the angle between the y * axes of map coordinates and projected coordinates. * * This will also affect the declination value and the transformations. */ void setGrivation(double grivation); /** * Returns the map coordinates of the reference point. */ MapCoord getMapRefPoint() const; /** * Defines the map coordinates of the reference point. * * This will not update the map and geographic coordinates of the reference point. */ void setMapRefPoint(MapCoord point); /** * Returns the projected coordinates of the reference point. */ QPointF getProjectedRefPoint() const; /** * Defines the projected coordinates of the reference point. * * This may trigger changes of the geographic coordinates of the reference * point, the convergence, the grivation and the transformations. */ void setProjectedRefPoint(QPointF point, bool update_grivation = true); /** * Returns the unique id of the projected CRS. */ QString getProjectedCRSId() const; /** * Returns the name of the coordinate reference system (CRS) of the * projected coordinates. */ QString getProjectedCRSName() const; /** * Returns the name of the projected coordinates. */ QString getProjectedCoordinatesName() const; /** * Returns the array of projected crs parameter values. */ const std::vector& getProjectedCRSParameters() const; /** * Returns the specification of the coordinate reference system (CRS) of the * projected coordinates * @return a PROJ.4 specification of the CRS */ QString getProjectedCRSSpec() const { return projected_crs_spec; } /** * Sets the coordinate reference system (CRS) of the projected coordinates. * * This will not touch any of the reference points, the declination, the * grivation. It is up to the user to decide how to reestablish a valid * configuration of geographic reference point, projected reference point, * declination and grivation. * * @param id an identifier * @param spec the PROJ.4 specification of the CRS * @param params parameter values (ignore for empty spec) * @return true if the specification is valid or empty, false otherwise */ bool setProjectedCRS(const QString& id, QString spec, std::vector< QString > params = std::vector()); /** * Calculates the meridian convergence at the reference point. * * The meridian convergence is the angle between grid north and true north. * * @return zero for a local georeferencing, or a calculated approximation */ double getConvergence() const; /** * Returns the geographic coordinates of the reference point. */ LatLon getGeographicRefPoint() const; /** * Defines the geographic coordinates of the reference point. * * This may trigger changes of the projected coordinates of the reference * point, the convergence, the grivation and the transformations. */ void setGeographicRefPoint(LatLon lat_lon, bool update_grivation = true); /** * Transforms map (paper) coordinates to projected coordinates. */ QPointF toProjectedCoords(const MapCoord& map_coords) const; /** * Transforms map (paper) coordinates to projected coordinates. */ QPointF toProjectedCoords(const MapCoordF& map_coords) const; /** * Transforms projected coordinates to map (paper) coordinates. */ MapCoord toMapCoords(const QPointF& projected_coords) const; /** * Transforms projected coordinates to map (paper) coordinates. */ MapCoordF toMapCoordF(const QPointF& projected_coords) const; /** * Transforms map (paper) coordinates to geographic coordinates (lat/lon). */ LatLon toGeographicCoords(const MapCoordF& map_coords, bool* ok = 0) const; /** * Transforms CRS coordinates to geographic coordinates (lat/lon). */ LatLon toGeographicCoords(const QPointF& projected_coords, bool* ok = 0) const; /** * Transforms geographic coordinates (lat/lon) to CRS coordinates. */ QPointF toProjectedCoords(const LatLon& lat_lon, bool* ok = 0) const; /** * Transforms geographic coordinates (lat/lon) to map coordinates. */ MapCoord toMapCoords(const LatLon& lat_lon, bool* ok = nullptr) const; /** * Transforms geographic coordinates (lat/lon) to map coordinates. */ MapCoordF toMapCoordF(const LatLon& lat_lon, bool* ok = nullptr) const; /** * Transforms map coordinates from the other georeferencing to * map coordinates of this georeferencing, if possible. */ MapCoordF toMapCoordF(const Georeferencing* other, const MapCoordF& map_coords, bool* ok = nullptr) const; /** * Returns the current error text. */ QString getErrorText() const; /** * Converts a value from radians to degrees. */ static double radToDeg(double val); /** * Converts a value from degrees to radians. */ static double degToRad(double val); /** * Converts a value from degrees to a D°M'S" string. */ static QString degToDMS(double val); /** * Updates the transformation parameters between map coordinates and * projected coordinates from the current projected reference point * coordinates, the grivation and the scale. */ void updateTransformation(); /** * Updates the grivation. * * The new value is calculated from the declination and the convergence. * For a local georeferencing, the convergence is zero, and grivation * is set to the same value as declination. */ void updateGrivation(); /** * Initializes the declination. * * The new value is calculated from the grivation and the convergence. * For a local georeferencing, the convergence is zero, and declination * is set to the same value as grivation. */ void initDeclination(); /** * Sets the transformation matrix from map coordinates to projected * coordinates directly. */ void setTransformationDirectly(const QTransform& transform); QTransform mapToProjected() const; QTransform projectedToMap() const; signals: /** * Indicates a change of the state property. */ void stateChanged(); /** * Indicates a change to the transformation rules between map coordinates * and projected coordinates. */ void transformationChanged(); /** * Indicates a change to the projection rules between geographic coordinates * and projected coordinates. */ void projectionChanged(); /** * Indicates a change of the declination. * * The declination has no direct influence on projection or transformation. * That's why there is an independent signal. */ void declinationChanged(); private: void setDeclinationAndGrivation(double declination, double grivation); State state; unsigned int scale_denominator; double grid_scale_factor; double declination; double grivation; double grivation_error; MapCoord map_ref_point; QPointF projected_ref_point; QTransform from_projected; QTransform to_projected; /// Projected CRS id, used for lookup in the CRS registry. /// If the spec is directly entered as string, the id is empty. QString projected_crs_id; QString projected_crs_spec; std::vector< QString > projected_crs_parameters; projPJ projected_crs; LatLon geographic_ref_point; projPJ geographic_crs; }; /** * Dumps a Georeferencing to the debug output. * * Note that this requires a *reference*, not a pointer. */ QDebug operator<<(QDebug dbg, const Georeferencing &georef); //### Georeferencing inline code ### inline constexpr unsigned int Georeferencing::scaleFactorPrecision() { return 6u; } inline double Georeferencing::roundScaleFactor(double value) { // This must match the implementation in scaleFactorPrecision(). return floor(value*1000000.0+0.5)/1000000.0; } inline constexpr unsigned int Georeferencing::declinationPrecision() { // This must match the implementation in declinationRound(). return 2u; } inline double Georeferencing::roundDeclination(double value) { // This must match the implementation in declinationPrecision(). return floor(value*100.0+0.5)/100.0; } inline bool Georeferencing::isValid() const { return state == Local || projected_crs; } inline bool Georeferencing::isLocal() const { return state == Local; } inline Georeferencing::State Georeferencing::getState() const { return state; } inline unsigned int Georeferencing::getScaleDenominator() const { return scale_denominator; } inline double Georeferencing::getGridScaleFactor() const { return grid_scale_factor; } inline double Georeferencing::getDeclination() const { return declination; } inline double Georeferencing::getGrivation() const { return grivation; } inline double Georeferencing::getGrivationError() const { return grivation_error; } inline MapCoord Georeferencing::getMapRefPoint() const { return map_ref_point; } inline QPointF Georeferencing::getProjectedRefPoint() const { return projected_ref_point; } inline QString Georeferencing::getProjectedCRSId() const { return projected_crs_id; } inline const std::vector& Georeferencing::getProjectedCRSParameters() const { return projected_crs_parameters; } inline LatLon Georeferencing::getGeographicRefPoint() const { return geographic_ref_point; } } // namespace OpenOrienteering #endif mapper-0.8.1.1/src/core/image_transparency_fixup.h000066400000000000000000000054171325266516600221440ustar00rootroot00000000000000/* * Copyright 2012, 2013 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_IMAGE_TRANSPARENCY_FIXUP_H #define OPENORIENTEERING_IMAGE_TRANSPARENCY_FIXUP_H #include namespace OpenOrienteering { /** * ImageTransparencyFixup repairs a particular issue with composing * transparent pixels. * * QPainter::CompositionMode_Multiply and QPainter::CompositionMode_Darken * on a QImage of Format_ARGB32_Premultiplied calculate the resulting alpha * channel in a very efficient but not accurate way. A particular case is the * composition of two fully transparent pixels which should in theory give a * fully transparent pixel. Qt yields an alpha value of 1 (in 0..255) instead. * The error accumulates with further compositions. * * This class may be used as a functor on a particular image, providing a * comfortable way to fix the described case after each composition. * * Synopsis: * * QImage image(100, 100, QImage::Format_ARGB32_Premultiplied); * ImageTransparencyFixup fixup(&image); * QPainter p(&image); * p.setCompositionMode(QPainter::CompositionMode_Multiply); * p.drawImage(another_image); * p.end(); * fixup(); */ class ImageTransparencyFixup { public: /** * Create a fixup functor for the given image. * * The image must be of QImage::Format_ARGB32_Premultiplied. * It may be null. */ inline ImageTransparencyFixup(QImage* image) : dest(0), dest_end(0) { // NOTE: Here we may add a check for a setting which disables the // fixup (for better application performance) if (image) { dest = (QRgb*)image->bits(); dest_end = dest + image->byteCount() / sizeof(QRgb); } } /** * Checks all pixels of the image for the known wrong result of composing * fully transparent pixels, and replaces them with a fully transparent * pixel. */ inline void operator()() const { for (QRgb* px = dest; px < dest_end; px++) { if (*px == 0x01000000) /* qRgba(0, 0, 0, 1) */ *px = 0x00000000; /* qRgba(0, 0, 0, 0) */ } } protected: QRgb* dest; QRgb* dest_end; }; } // namespace OpenOrienteering #endif mapper-0.8.1.1/src/core/latlon.cpp000066400000000000000000000020351325266516600166730ustar00rootroot00000000000000/* * Copyright 2012, 2013, 2014 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "latlon.h" #include namespace OpenOrienteering { QDebug operator<<(QDebug dbg, const LatLon& lat_lon) { dbg.space() << "LatLon" << lat_lon.latitude() << lat_lon.longitude(); return dbg.space(); } } // namespace OpenOrienteering mapper-0.8.1.1/src/core/latlon.h000066400000000000000000000067671325266516600163600ustar00rootroot00000000000000/* * Copyright 2012, 2013, 2014 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_LATLON_H #define OPENORIENTEERING_LATLON_H #include class QDebug; namespace OpenOrienteering { /** * @brief Specifies geographic location by latitude and longitude. * * Latitude is a geographic coordinate that specifies the north-south * position (φ, phi). * * Longitude is a geographic coordinate that specifies the east-west * position (λ, lambda). * * LatLon is meant to be similar to QGeoCoordinate (part of Qt since 5.2). * This Qt class might eventually replace LatLon. QGeoCoordinate has altitude * as an additional property which is rarely used in Mapper at the moment. * * @see QGeoCoordinate (http://qt-project.org/doc/qt-5/qgeocoordinate.html) */ class LatLon { private: double latitude_value; double longitude_value; friend QDebug operator<<(QDebug dbg, const LatLon& lat_lon); public: /** * Constructs a new LatLon for the latitude and longitude given in degrees. */ constexpr LatLon(double latitude = 0.0, double longitude = 0.0) noexcept; /** * Returns a new LatLon for the latitude and longitude given in radiant. */ constexpr static LatLon fromRadiant(double latitude, double longitude) noexcept; /** * Returns the latitude value in degrees. */ constexpr double latitude() const; /** * Returns the longitude value in degrees. */ constexpr double longitude() const; /** * Returns true if this object has exactly the same latitude and longitude * like another. FP precision issues are not taken into account. */ constexpr bool operator==(const LatLon& rhs) const; /** * Returns true if this object has not exactly the same latitude and longitude * like another. FP precision issues are not taken into account. */ constexpr bool operator!=(const LatLon& rhs) const; }; /** * Dumps a LatLon to the debug output. * * Note that this requires a *reference*, not a pointer. */ QDebug operator<<(QDebug dbg, const LatLon &lat_lon); //### LatLon inline code ### inline constexpr LatLon::LatLon(double latitude, double longitude) noexcept : latitude_value(latitude) , longitude_value(longitude) { ; // nothing } inline constexpr LatLon LatLon::fromRadiant(double latitude, double longitude) noexcept { return LatLon(qRadiansToDegrees(latitude), qRadiansToDegrees(longitude)); } inline constexpr double LatLon::latitude() const { return latitude_value; } inline constexpr double LatLon::longitude() const { return longitude_value; } inline constexpr bool LatLon::operator==(const LatLon& rhs) const { return (this->latitude_value == rhs.latitude_value) && (this->longitude_value == rhs.longitude_value); } inline constexpr bool LatLon::operator!=(const LatLon& rhs) const { return !(*this == rhs); } } // namespace OpenOrienteering #endif mapper-0.8.1.1/src/core/map.cpp000066400000000000000000002066541325266516600161740ustar00rootroot00000000000000/* * Copyright 2012-2014 Thomas Schöps * Copyright 2013-2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "map.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "core/georeferencing.h" #include "core/map_color.h" #include "core/map_coord.h" #include "core/map_grid.h" #include "core/map_part.h" #include "core/map_printer.h" #include "core/map_view.h" #include "core/objects/object.h" #include "core/objects/object_operations.h" #include "core/renderables/renderable.h" #include "core/symbols/combined_symbol.h" #include "core/symbols/line_symbol.h" #include "core/symbols/point_symbol.h" #include "core/symbols/symbol.h" #include "core/symbols/text_symbol.h" #include "fileformats/file_format.h" #include "fileformats/file_format_registry.h" #include "fileformats/file_import_export.h" #include "gui/map/map_widget.h" #include "gui/text_browser_dialog.h" #include "templates/template.h" #include "undo/object_undo.h" #include "undo/undo.h" #include "undo/undo_manager.h" #include "util/util.h" #include "util/transformation.h" // IWYU pragma: no_forward_declare QRectF namespace OpenOrienteering { QPointer map_symbol_translator{}; namespace { // ### Misc ### /** A record of information about the mapping of a color in a source MapColorSet * to a color in a destination MapColorSet. */ struct MapColorSetMergeItem { MapColor* src_color; MapColor* dest_color; std::size_t dest_index; std::size_t lower_bound; std::size_t upper_bound; int lower_errors; int upper_errors; bool filter; MapColorSetMergeItem() : src_color(nullptr), dest_color(nullptr), dest_index(0), lower_bound(0), upper_bound(0), lower_errors(0), upper_errors(0), filter(false) { } }; /** The mapping of all colors in a source MapColorSet * to colors in a destination MapColorSet. */ typedef std::vector MapColorSetMergeList; /** * Shows a message box for a list of unformatted messages. */ void showMessageBox(QWidget* parent, const QString& title, const QString& headline, const std::vector& messages) { QString document; if (!headline.isEmpty()) document += QLatin1String("

    ") + headline + QLatin1String("

    "); for (const auto& message : messages) document += Qt::convertFromPlainText(message, Qt::WhiteSpaceNormal); TextBrowserDialog dialog(document, parent); dialog.setWindowTitle(title); dialog.setWindowModality(Qt::WindowModal); dialog.exec(); // Let Android update the screen. qApp->processEvents(QEventLoop::ExcludeUserInputEvents); } } // namespace // ### MapColorSet ### Map::MapColorSet::MapColorSet() { // nothing } Map::MapColorSet::~MapColorSet() { for (MapColor* color : colors) delete color; } void Map::MapColorSet::insert(int pos, MapColor* color) { colors.insert(colors.begin() + pos, color); adjustColorPriorities(pos + 1, colors.size()); } void Map::MapColorSet::erase(int pos) { colors.erase(colors.begin() + pos); adjustColorPriorities(pos, colors.size()); } void Map::MapColorSet::adjustColorPriorities(int first, int last) { // TODO: delete or update RenderStates with these colors for (int i = first; i < last; ++i) colors[i]->setPriority(i); } // This algorithm tries to maintain the relative order of colors. MapColorMap Map::MapColorSet::importSet(const Map::MapColorSet& other, std::vector< bool >* filter, Map* map) { MapColorMap out_pointermap; // Determine number of colors to import std::size_t import_count = other.colors.size(); if (filter) { Q_ASSERT(filter->size() == other.colors.size()); for (std::size_t i = 0, end = other.colors.size(); i != end; ++i) { MapColor* color = other.colors[i]; if (!(*filter)[i] || out_pointermap.contains(color)) { continue; } out_pointermap[color] = nullptr; // temporary used as a flag // Determine referenced spot colors, and add them to the filter if (color->getSpotColorMethod() == MapColor::CustomColor) { for (const SpotColorComponent& component : color->getComponents()) { if (!out_pointermap.contains(component.spot_color)) { // Add this spot color to the filter int j = 0; while (other.colors[j] != component.spot_color) ++j; (*filter)[j] = true; out_pointermap[component.spot_color] = nullptr; } } } } import_count = out_pointermap.size(); out_pointermap.clear(); } if (import_count > 0) { colors.reserve(colors.size() + import_count); // The conflict resolution algorithm below is simplified by setting // iterator `selected_item` to a real list element which is not related // to the actual color sets we are merging. This is the extra element // identified as `end_of_merge_list`. MapColorSetMergeList merge_list{other.colors.size() + 1}; const auto end_of_merge_list = end(merge_list) - 1; bool priorities_changed = false; // Initialize merge_list auto merge_list_item = merge_list.begin(); for (std::size_t i = 0; i < other.colors.size(); ++i) { merge_list_item->filter = (!filter || (*filter)[i]); MapColor* src_color = other.colors[i]; merge_list_item->src_color = src_color; for (std::size_t k = 0, colors_size = colors.size(); k < colors_size; ++k) { if (colors[k]->equals(*src_color, false)) { merge_list_item->dest_color = colors[k]; merge_list_item->dest_index = k; out_pointermap[src_color] = colors[k]; // Prefer a matching color at the same priority, // so just abort early if priority matches if (merge_list_item->dest_color->getPriority() == merge_list_item->src_color->getPriority()) break; } } ++merge_list_item; } Q_ASSERT(merge_list_item == end_of_merge_list); size_t iteration_number = 1; while (true) { // Evaluate bounds and conflicting order of colors int max_conflict_reduction = 0; auto selected_item = end_of_merge_list; // Note: non-const copy of an iterator for (merge_list_item = begin(merge_list); merge_list_item != end_of_merge_list; ++merge_list_item) { // Check all lower colors for a higher dest_index std::size_t& lower_bound(merge_list_item->lower_bound); lower_bound = merge_list_item->dest_color ? merge_list_item->dest_index : 0; auto it = merge_list.begin(); for (; it != merge_list_item; ++it) { if (it->dest_color) { if (it->dest_index > lower_bound) { lower_bound = it->dest_index; } if (merge_list_item->dest_color && merge_list_item->dest_index < it->dest_index) { ++merge_list_item->lower_errors; } } } // Check all higher colors for a lower dest_index std::size_t& upper_bound(merge_list_item->upper_bound); upper_bound = merge_list_item->dest_color ? merge_list_item->dest_index : colors.size(); for (++it; it != end_of_merge_list; ++it) { if (it->dest_color) { if (it->dest_index < upper_bound) { upper_bound = it->dest_index; } if (merge_list_item->dest_color && merge_list_item->dest_index > it->dest_index) { ++merge_list_item->upper_errors; } } } if (merge_list_item->filter) { if (merge_list_item->lower_errors == 0 && merge_list_item->upper_errors > max_conflict_reduction) { int conflict_reduction = merge_list_item->upper_errors; // Check new conflicts with insertion index: selected_item->upper_bound for (it = merge_list.begin(); it != merge_list_item; ++it) { if (it->dest_color && selected_item->upper_bound <= it->dest_index) --conflict_reduction; } // Also allow = here to make two-step resolves work if (conflict_reduction >= max_conflict_reduction) { selected_item = merge_list_item; max_conflict_reduction = conflict_reduction; } } else if (merge_list_item->upper_errors == 0 && merge_list_item->lower_errors > max_conflict_reduction) { int conflict_reduction = merge_list_item->lower_errors; // Check new conflicts with insertion index: (selected_item->lower_bound+1) it = merge_list_item; for (++it; it != end_of_merge_list; ++it) { if (it->dest_color && (selected_item->lower_bound+1) > it->dest_index) --conflict_reduction; } // Also allow = here to make two-step resolves work if (conflict_reduction >= max_conflict_reduction) { selected_item = merge_list_item; max_conflict_reduction = conflict_reduction; } } } } // Abort if no conflicts or maximum iteration count reached. // The latter condition is just to prevent endless loops in // case of bugs and should not occur theoretically. if (selected_item == end_of_merge_list || iteration_number > merge_list.size()) break; // Solve selected conflict item auto new_color = new MapColor(*selected_item->dest_color); selected_item->dest_color = new_color; out_pointermap[selected_item->src_color] = new_color; std::size_t insertion_index = (selected_item->lower_errors == 0) ? selected_item->upper_bound : (selected_item->lower_bound+1); if (map) map->addColor(new_color, insertion_index); else colors.insert(colors.begin() + insertion_index, new_color); priorities_changed = true; for (merge_list_item = merge_list.begin(); merge_list_item != merge_list.end(); ++merge_list_item) { merge_list_item->lower_errors = 0; merge_list_item->upper_errors = 0; if (merge_list_item->dest_color && merge_list_item->dest_index >= insertion_index) ++merge_list_item->dest_index; } selected_item->dest_index = insertion_index; ++iteration_number; } merge_list.erase(end_of_merge_list); // no longer needed // Some missing colors may be spot color compositions which can be // resolved to new colors only after all colors have been created. // That is why we create all missing colors first. for (auto it = merge_list.rbegin(); it != merge_list.rend(); ++it) { if (it->filter && !it->dest_color) { it->dest_color = new MapColor(*it->src_color); out_pointermap[it->src_color] = it->dest_color; } else { // Existing colors don't need to be touched again. it->dest_color = nullptr; } } // Now process all new colors for spot color resolution and insertion for (auto it = merge_list.rbegin(); it != merge_list.rend(); ++it) { MapColor* new_color = it->dest_color; if (new_color) { if (new_color->getSpotColorMethod() == MapColor::CustomColor) { SpotColorComponents components = new_color->getComponents(); for (SpotColorComponent& component : components) { Q_ASSERT(out_pointermap.contains(component.spot_color)); component.spot_color = out_pointermap[component.spot_color]; } new_color->setSpotColorComposition(components); } std::size_t insertion_index = it->upper_bound; if (map) map->addColor(new_color, insertion_index); else colors.insert(colors.begin() + insertion_index, new_color); priorities_changed = true; } } if (map && priorities_changed) { map->updateAllObjects(); } } return out_pointermap; } // ### Map ### bool Map::static_initialized = false; MapColor Map::covering_white(MapColor::CoveringWhite); MapColor Map::covering_red(MapColor::CoveringRed); MapColor Map::undefined_symbol_color(MapColor::Undefined); MapColor Map::registration_color(MapColor::Registration); LineSymbol* Map::covering_white_line; LineSymbol* Map::covering_red_line; LineSymbol* Map::undefined_line; PointSymbol* Map::undefined_point; TextSymbol* Map::undefined_text; CombinedSymbol* Map::covering_combined_line; Map::Map() : color_set() , has_spot_colors(false) , undo_manager(new UndoManager(this)) , renderables(new MapRenderables(this)) , selection_renderables(new MapRenderables(this)) , renderable_options(Symbol::RenderNormal) , printer_config(nullptr) { if (!static_initialized) initStatic(); georeferencing.reset(new Georeferencing()); init(); connect(this, &Map::colorAdded, this, &Map::checkSpotColorPresence); connect(this, &Map::colorChanged, this, &Map::checkSpotColorPresence); connect(this, &Map::colorDeleted, this, &Map::checkSpotColorPresence); connect(undo_manager.data(), &UndoManager::cleanChanged, this, &Map::undoCleanChanged); } Map::~Map() { clear(); // properly destruct all children } void Map::clear() { undo_manager->clear(); for (auto temp : templates) delete temp; templates.clear(); first_front_template = 0; for (auto temp : closed_templates) delete temp; closed_templates.clear(); object_selection.clear(); first_selected_object = nullptr; selection_renderables->clear(); renderables->clear(); for (MapPart* part : parts) delete part; parts.clear(); current_part_index = 0; for (auto symbol : symbols) delete symbol; symbols.clear(); // Don't clear() color_set: It is shared. } void Map::init() { color_set = new MapColorSet(); parts.push_back(new MapPart(tr("default part"), this)); widgets.clear(); undo_manager->setClean(); symbol_set_id = QString(); map_notes = QString(); printer_config.reset(); image_template_use_meters_per_pixel = true; image_template_meters_per_pixel = 0; image_template_dpi = 0; image_template_scale = 0; colors_dirty = false; symbols_dirty = false; templates_dirty = false; objects_dirty = false; other_dirty = false; unsaved_changes = false; } void Map::reset() { clear(); init(); } void Map::setScaleDenominator(unsigned int value) { georeferencing->setScaleDenominator(value); } unsigned int Map::getScaleDenominator() const { return georeferencing->getScaleDenominator(); } void Map::changeScale(unsigned int new_scale_denominator, const MapCoord& scaling_center, bool scale_symbols, bool scale_objects, bool scale_georeferencing, bool scale_templates) { if (new_scale_denominator == getScaleDenominator()) return; double factor = getScaleDenominator() / (double)new_scale_denominator; if (scale_symbols) scaleAllSymbols(factor); if (scale_objects) { undo_manager->clear(); scaleAllObjects(factor, scaling_center); if (hasPrinterConfig()) { auto print_area = printer_config->print_area; auto center = QPointF(scaling_center); print_area.setTopLeft(center + factor * (print_area.topLeft() - center)); print_area.setBottomRight(center + factor * (print_area.bottomRight() - center)); printer_config->print_area = print_area; } } if (scale_georeferencing) georeferencing->setMapRefPoint(scaling_center + factor * (georeferencing->getMapRefPoint() - scaling_center)); if (scale_templates) { for (int i = 0; i < getNumTemplates(); ++i) { Template* temp = getTemplate(i); if (temp->isTemplateGeoreferenced()) continue; setTemplateAreaDirty(i); temp->scale(factor, scaling_center); setTemplateAreaDirty(i); } for (int i = 0; i < getNumClosedTemplates(); ++i) { Template* temp = getClosedTemplate(i); if (temp->isTemplateGeoreferenced()) continue; temp->scale(factor, scaling_center); } } setScaleDenominator(new_scale_denominator); setOtherDirty(); updateAllMapWidgets(); } void Map::rotateMap(double rotation, const MapCoord& center, bool adjust_georeferencing, bool adjust_declination, bool adjust_templates) { if (std::fmod(rotation, 2 * M_PI) == 0) return; undo_manager->clear(); rotateAllObjects(rotation, center); if (adjust_georeferencing) { MapCoordF reference_point = MapCoordF(georeferencing->getMapRefPoint() - center); reference_point.rotate(-rotation); georeferencing->setMapRefPoint(MapCoord(MapCoordF(center) + reference_point)); } if (adjust_declination) { auto rotation_degrees = qRadiansToDegrees(rotation); georeferencing->setDeclination(georeferencing->getDeclination() + rotation_degrees); } if (adjust_templates) { for (int i = 0; i < getNumTemplates(); ++i) { Template* temp = getTemplate(i); if (temp->isTemplateGeoreferenced()) continue; setTemplateAreaDirty(i); temp->rotate(rotation, center); setTemplateAreaDirty(i); } for (int i = 0; i < getNumClosedTemplates(); ++i) { Template* temp = getClosedTemplate(i); if (temp->isTemplateGeoreferenced()) continue; temp->rotate(rotation, center); } } setOtherDirty(); updateAllMapWidgets(); } void Map::setMapNotes(const QString& text) { map_notes = text; } bool Map::saveTo(const QString& path, MapView* view) { bool success = exportTo(path, view); if (success) { colors_dirty = false; symbols_dirty = false; templates_dirty = false; objects_dirty = false; other_dirty = false; unsaved_changes = false; undoManager().setClean(); } return success; } bool Map::exportTo(const QString& path, MapView* view, const FileFormat* format) { Q_ASSERT(view && "Saving a file without view information is not supported!"); if (!format) format = FileFormats.findFormatForFilename(path); if (!format) format = FileFormats.findFormat(FileFormats.defaultFormat()); if (!format) { QMessageBox::warning(nullptr, tr("Error"), tr("Cannot export the map as\n\"%1\"\nbecause the format is unknown.").arg(path)); return false; } else if (!format->supportsExport()) { QMessageBox::warning(nullptr, tr("Error"), tr("Cannot export the map as\n\"%1\"\nbecause saving as %2 (.%3) is not supported."). arg(path, format->description(), format->fileExtensions().join(QLatin1String(", ")))); return false; } QSaveFile file(path); QScopedPointer exporter(format->createExporter(&file, this, view)); bool success = false; if (file.open(QIODevice::WriteOnly)) { try { exporter->doExport(); } catch (std::exception &e) { file.cancelWriting(); const QString error = QString::fromLocal8Bit(e.what()); QMessageBox::warning(nullptr, tr("Error"), tr("Internal error while saving:\n%1").arg(error)); return false; } success = file.commit(); } if (!success) { QMessageBox::warning( nullptr, tr("Error"), tr("Cannot save file\n%1:\n%2").arg(path, file.errorString()) ); } else if (!exporter->warnings().empty()) { showMessageBox(nullptr, tr("Warning"), tr("The map export generated warnings."), exporter->warnings() ); } return success; } bool Map::loadFrom(const QString& path, QWidget* dialog_parent, MapView* view, bool load_symbols_only, bool show_error_messages) { // Ensure the file exists and is readable. QFile file(path); if (!file.open(QIODevice::ReadOnly)) { if (show_error_messages) QMessageBox::warning( dialog_parent, tr("Error"), tr("Cannot open file:\n%1\nfor reading.").arg(path) ); return false; } // Delete previous objects reset(); // Read a block at the beginning of the file, that we can use for magic number checking. unsigned char buffer[256]; size_t total_read = file.read((char *)buffer, 256); file.seek(0); bool import_complete = false; QString error_msg = tr("Invalid file type."); for (auto format : FileFormats.formats()) { // If the format supports import, and thinks it can understand the file header, then proceed. if (format->supportsImport() && format->understands(buffer, total_read)) { Importer *importer = nullptr; // Wrap everything in a try block, so we can gracefully recover if the importer balks. try { // Create an importer instance for this file and map. importer = format->createImporter(&file, this, view); // Run the first pass. importer->doImport(load_symbols_only, QFileInfo(path).absolutePath()); // Are there any actions the user must take to complete the import? if (!importer->actions().empty()) { // TODO: prompt the user to resolve the action items. All-in-one dialog. } // Finish the import. importer->finishImport(); file.close(); // Display any warnings. if (!importer->warnings().empty() && show_error_messages) { showMessageBox(nullptr, tr("Warning"), tr("The map import generated warnings."), importer->warnings() ); } import_complete = true; } catch (FileFormatException &e) { error_msg = e.message(); } catch (std::exception &e) { qDebug() << "Exception:" << e.what(); error_msg = QString::fromLatin1(e.what()); } if (importer) delete importer; } // If the last importer finished successfully if (import_complete) break; } if (view) { view->setPanOffset(QPoint(0, 0)); } if (!import_complete) { if (show_error_messages) QMessageBox::warning( dialog_parent, tr("Error"), tr("Cannot open file:\n%1\n\n%2").arg(path, error_msg) ); return false; } // Update all objects without trying to remove their renderables first, this gives a significant speedup when loading large files updateAllObjects(); // TODO: is the comment above still applicable? setHasUnsavedChanges(false); return true; } void Map::importMap( const Map* other, ImportMode mode, QWidget* dialog_parent, std::vector* filter, int symbol_insert_pos, bool merge_duplicate_symbols, QHash* out_symbol_map ) { // Check if there is something to import if (other->getNumColors() == 0 && other->getNumSymbols() == 0 && other->getNumObjects() == 0) { QMessageBox::critical(dialog_parent, tr("Error"), tr("Nothing to import.")); return; } // Check scale if ((mode & 0x0f) != ColorImport && other->getNumSymbols() > 0 && other->getScaleDenominator() != getScaleDenominator()) { int answer = QMessageBox::question(dialog_parent, tr("Question"), tr("The scale of the imported data is 1:%1 which is different from this map's scale of 1:%2.\n\nRescale the imported data?") .arg(QLocale().toString(other->getScaleDenominator()), QLocale().toString(getScaleDenominator())), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); if (answer == QMessageBox::Yes) { /// \todo No need to clone iff the other map is discarded after import. Map clone; clone.setGeoreferencing(other->getGeoreferencing()); clone.importMap(other, mode, dialog_parent, filter, -1, false, out_symbol_map); clone.changeScale(getScaleDenominator(), MapCoord(0, 0), true, true, true, true); QHash symbol_map; // clone symbol -> this map's symbol importMap(&clone, mode, dialog_parent, nullptr, symbol_insert_pos, merge_duplicate_symbols, &symbol_map); if (out_symbol_map) // original imported symbol -> clone symbol { Q_ASSERT(symbol_map.size() == out_symbol_map->size()); for (auto& symbol : *out_symbol_map) { Q_ASSERT(symbol_map.contains(symbol)); symbol = symbol_map.value(symbol); // original imported symbol -> this map's symbol } } return; } } QTransform q_transform; if (mode.testFlag(GeorefImport)) { /// \todo Test and review import of georeferenced and non-georeferenced maps, in all combinations. /// \todo Handle rotation of patterns and text, cf. Object::transform. const auto& georef = getGeoreferencing(); const auto& other_georef = other->getGeoreferencing(); const auto src_origin = MapCoordF { other_georef.getMapRefPoint() }; bool ok0, ok1, ok2; PassPointList passpoints; passpoints.resize(3); passpoints[0].src_coords = src_origin; passpoints[0].dest_coords = georef.toMapCoordF(&other_georef, passpoints[0].src_coords, &ok0); passpoints[1].src_coords = src_origin + MapCoordF { 128.0, 0.0 }; // 128 mm off horizontally passpoints[1].dest_coords = georef.toMapCoordF(&other_georef, passpoints[1].src_coords, &ok1); passpoints[2].src_coords = src_origin + MapCoordF { 0.0, 128.0 }; // 128 mm off vertically passpoints[2].dest_coords = georef.toMapCoordF(&other_georef, passpoints[2].src_coords, &ok2); if (ok0 && ok1 && ok2 && !passpoints.estimateNonIsometricSimilarityTransform(&q_transform)) { /// \todo proper error message qDebug("Failed to calculate transformation"); q_transform.reset(); } } auto symbol_map = importMap(*other, mode, filter, symbol_insert_pos, merge_duplicate_symbols, q_transform); if (out_symbol_map) *out_symbol_map = symbol_map; } QHash Map::importMap( const Map& imported_map, ImportMode mode, std::vector* filter, int symbol_insert_pos, bool merge_duplicate_symbols, const QTransform& transform) { // Determine which symbols and colors to import std::vector color_filter(std::size_t(imported_map.getNumColors()), true); std::vector symbol_filter(std::size_t(imported_map.getNumSymbols()), true); if (mode.testFlag(MinimalImport)) { switch (mode & 0x0f) { case ObjectImport: if (imported_map.getNumObjects() > 0) imported_map.determineSymbolsInUse(symbol_filter); imported_map.determineColorsInUse(symbol_filter, color_filter); break; case SymbolImport: if (filter) { Q_ASSERT(filter->size() == symbol_filter.size()); symbol_filter = *filter; imported_map.determineSymbolUseClosure(symbol_filter); } imported_map.determineColorsInUse(symbol_filter, color_filter); break; case ColorImport: if (filter) { Q_ASSERT(filter->size() == color_filter.size()); color_filter = *filter; } break; default: Q_UNREACHABLE(); } } // Import colors auto color_map = color_set->importSet(*imported_map.color_set, &color_filter, this); QHash symbol_map; if ((mode & 0x0f) != ColorImport) { if (imported_map.getNumSymbols() > 0) { // Import symbols symbol_map = importSymbols(imported_map, color_map, symbol_insert_pos, merge_duplicate_symbols, symbol_filter); } if ((mode & 0x0f) != SymbolImport && imported_map.getNumObjects() > 0) { // Import parts like this: // - if the other map has only one part, import it into the current part // - else check if there is already a part with an equal name for every part to import and import into this part if found, else create a new part for (const auto* part_to_import : imported_map.parts) { MapPart* dest_part = nullptr; if (imported_map.parts.size() == 1) { dest_part = getCurrentPart(); } else { for (auto* check_part : parts) { if (check_part->getName().compare(part_to_import->getName(), Qt::CaseInsensitive) == 0) { dest_part = check_part; break; } } if (!dest_part) { // Import as new part dest_part = new MapPart(part_to_import->getName(), this); addPart(dest_part, 0); } } // Temporarily switch the current part for importing so the undo step gets created for the right part MapPart* temp_current_part = getCurrentPart(); current_part_index = std::size_t(findPartIndex(dest_part)); bool select_and_center_objects = dest_part == temp_current_part; dest_part->importPart(part_to_import, symbol_map, transform, select_and_center_objects); if (select_and_center_objects) ensureVisibilityOfSelectedObjects(Map::FullVisibility); current_part_index = std::size_t(findPartIndex(temp_current_part)); } } } return symbol_map; } bool Map::exportToIODevice(QIODevice* stream) { stream->open(QIODevice::WriteOnly); Exporter* exporter = nullptr; try { const FileFormat* native_format = FileFormats.findFormat("XML"); exporter = native_format->createExporter(stream, this, nullptr); exporter->doExport(); stream->close(); } catch (std::exception &e) { if (exporter) delete exporter; return false; } if (exporter) delete exporter; return true; } bool Map::importFromIODevice(QIODevice* stream) { Importer* importer = nullptr; try { const FileFormat* native_format = FileFormats.findFormat("XML"); importer = native_format->createImporter(stream, this, nullptr); importer->doImport(false); importer->finishImport(); stream->close(); } catch (std::exception &e) { if (importer) delete importer; return false; } if (importer) delete importer; return true; } void Map::draw(QPainter* painter, const RenderConfig& config) { // Update the renderables of all objects marked as dirty updateObjects(); // The actual drawing renderables->draw(painter, config); } void Map::drawOverprintingSimulation(QPainter* painter, const RenderConfig& config) { // Update the renderables of all objects marked as dirty updateObjects(); // The actual drawing renderables->drawOverprintingSimulation(painter, config); } void Map::drawColorSeparation(QPainter* painter, const RenderConfig& config, const MapColor* spot_color, bool use_color) { // Update the renderables of all objects marked as dirty updateObjects(); // The actual drawing renderables->drawColorSeparation(painter, config, spot_color, use_color); } void Map::drawGrid(QPainter* painter, const QRectF& bounding_box, bool on_screen) { grid.draw(painter, bounding_box, this, on_screen); } void Map::drawTemplates(QPainter* painter, const QRectF& bounding_box, int first_template, int last_template, const MapView* view, bool on_screen) const { for (int i = first_template; i <= last_template; ++i) { const Template* temp = getTemplate(i); bool visible = temp->getTemplateState() == Template::Loaded; double scale = std::max(temp->getTemplateScaleX(), temp->getTemplateScaleY()); float opacity = 1.0f; if (view) { auto visibility = view->getTemplateVisibility(temp); visible &= visibility.visible; opacity = visibility.opacity; scale *= view->getZoom(); } if (visible) { painter->save(); temp->drawTemplate(painter, bounding_box, scale, on_screen, opacity); painter->restore(); } } } void Map::updateObjects() { // TODO: It maybe would be better if the objects entered themselves into a separate list when they get dirty so not all objects have to be traversed here applyOnAllObjects(&Object::update); } void Map::removeRenderablesOfObject(const Object* object, bool mark_area_as_dirty) { renderables->removeRenderablesOfObject(object, mark_area_as_dirty); if (isObjectSelected(object)) removeSelectionRenderables(object); } void Map::insertRenderablesOfObject(const Object* object) { renderables->insertRenderablesOfObject(object); if (isObjectSelected(object)) addSelectionRenderables(object); } void Map::markAsIrregular(Object* object) { irregular_objects.insert(object); } const std::set Map::irregularObjects() const { return irregular_objects; } std::size_t Map::deleteIrregularObjects() { std::size_t result = 0; std::set unhandled; for (auto object : irregular_objects) { for (auto part : parts) { if (part->deleteObject(object, false)) { ++result; goto next_object; } } unhandled.insert(object); next_object: ; // nothing else } irregular_objects.swap(unhandled); return result; } void Map::getSelectionToSymbolCompatibility(const Symbol* symbol, bool& out_compatible, bool& out_different) const { out_compatible = symbol && !object_selection.empty(); out_different = false; if (symbol) { for (const Object* object : object_selection) { if (!symbol->isTypeCompatibleTo(object)) { out_compatible = false; out_different = true; return; } else if (symbol != object->getSymbol()) out_different = true; } } } void Map::deleteSelectedObjects() { auto obj = selectedObjectsBegin(); auto end = selectedObjectsEnd(); if (obj != end) { // FIXME: this is not ready for multiple map parts. auto undo_step = new AddObjectsUndoStep(this); MapPart* part = getCurrentPart(); for (; obj != end; ++obj) { int index = part->findObjectIndex(*obj); if (index >= 0) { undo_step->addObject(index, *obj); part->deleteObject(index, true); } else { qDebug() << this << "::deleteSelectedObjects(): Object" << *obj << "not found in current map part."; } } setObjectsDirty(); clearObjectSelection(true); push(undo_step); } } void Map::includeSelectionRect(QRectF& rect) const { for (const Object* object : object_selection) rectIncludeSafe(rect, object->getExtent()); } void Map::drawSelection(QPainter* painter, bool force_min_size, MapWidget* widget, MapRenderables* replacement_renderables, bool draw_normal) { MapView* view = widget->getMapView(); painter->save(); painter->translate(widget->width() / 2.0 + view->panOffset().x(), widget->height() / 2.0 + view->panOffset().y()); painter->setWorldTransform(view->worldTransform(), true); if (!replacement_renderables) replacement_renderables = selection_renderables.data(); RenderConfig::Options options = RenderConfig::Screen | RenderConfig::HelperSymbols; qreal selection_opacity = 1.0; if (force_min_size) options |= RenderConfig::ForceMinSize; if (!draw_normal) { options |= RenderConfig::Highlighted; selection_opacity = 0.4; } RenderConfig config = { *this, view->calculateViewedRect(widget->viewportToView(widget->rect())), view->calculateFinalZoomFactor(), options, selection_opacity }; replacement_renderables->draw(painter, config); painter->restore(); } void Map::addObjectToSelection(Object* object, bool emit_selection_changed) { Q_ASSERT(!isObjectSelected(object)); object_selection.insert(object); addSelectionRenderables(object); if (!first_selected_object) first_selected_object = object; if (emit_selection_changed) emit objectSelectionChanged(); } void Map::removeObjectFromSelection(Object* object, bool emit_selection_changed) { bool removed = object_selection.erase(object); Q_ASSERT(removed && "Map::removeObjectFromSelection: object was not selected!"); Q_UNUSED(removed); removeSelectionRenderables(object); if (first_selected_object == object) first_selected_object = object_selection.empty() ? nullptr : *object_selection.begin(); if (emit_selection_changed) emit objectSelectionChanged(); } bool Map::removeSymbolFromSelection(const Symbol* symbol, bool emit_selection_changed) { bool removed_at_least_one_object = false; auto it_end = object_selection.end(); for (auto it = object_selection.begin(); it != it_end; ) { if ((*it)->getSymbol() != symbol) { ++it; continue; } removed_at_least_one_object = true; removeSelectionRenderables(*it); Object* removed_object = *it; it = object_selection.erase(it); if (first_selected_object == removed_object) first_selected_object = object_selection.empty() ? nullptr : *object_selection.begin(); } if (emit_selection_changed && removed_at_least_one_object) emit objectSelectionChanged(); return removed_at_least_one_object; } bool Map::isObjectSelected(const Object* object) const { return object_selection.find(const_cast(object)) != object_selection.end(); } bool Map::toggleObjectSelection(Object* object, bool emit_selection_changed) { if (isObjectSelected(object)) { removeObjectFromSelection(object, emit_selection_changed); return false; } else { addObjectToSelection(object, emit_selection_changed); return true; } } void Map::clearObjectSelection(bool emit_selection_changed) { selection_renderables->clear(); object_selection.clear(); first_selected_object = nullptr; if (emit_selection_changed) emit objectSelectionChanged(); } void Map::emitSelectionChanged() { emit objectSelectionChanged(); } void Map::emitSelectionEdited() { emit selectedObjectEdited(); } void Map::addMapWidget(MapWidget* widget) { widgets.push_back(widget); } void Map::removeMapWidget(MapWidget* widget) { widgets.erase(std::remove(begin(widgets), end(widgets), widget), end(widgets)); } void Map::updateAllMapWidgets() { for (MapWidget* widget : widgets) widget->updateEverything(); } void Map::ensureVisibilityOfSelectedObjects(SelectionVisibility visibility) { if (!object_selection.empty()) { QRectF rect; includeSelectionRect(rect); for (MapWidget* widget : widgets) { switch (visibility) { case FullVisibility: widget->ensureVisibilityOfRect(rect, MapWidget::DiscreteZoom); break; case PartialVisibility: if (!widget->getMapView()->calculateViewedRect(widget->viewportToView(widget->geometry())).intersects(rect)) widget->ensureVisibilityOfRect(rect, MapWidget::DiscreteZoom); break; case IgnoreVisibilty: break; // Do nothing default: Q_UNREACHABLE(); } } } } void Map::setDrawingBoundingBox(const QRectF& map_coords_rect, int pixel_border, bool do_update) { for (MapWidget* widget : widgets) widget->setDrawingBoundingBox(map_coords_rect, pixel_border, do_update); } void Map::clearDrawingBoundingBox() { for (MapWidget* widget : widgets) widget->clearDrawingBoundingBox(); } void Map::setActivityBoundingBox(const QRectF& map_coords_rect, int pixel_border, bool do_update) { for (MapWidget* widget : widgets) widget->setActivityBoundingBox(map_coords_rect, pixel_border, do_update); } void Map::clearActivityBoundingBox() { for (MapWidget* widget : widgets) widget->clearActivityBoundingBox(); } void Map::updateDrawing(const QRectF& map_coords_rect, int pixel_border) { for (MapWidget* widget : widgets) widget->updateDrawing(map_coords_rect, pixel_border); } QString Map::translate(const QString& symbol_text) const { auto result = raw_translation(symbol_text); if (result.isEmpty()) result = symbol_text; return result; } QString Map::raw_translation(const QString& symbol_text) const { auto result = QString{}; if (map_symbol_translator) result = map_symbol_translator->translate(symbol_set_id.toUtf8(), symbol_text.toUtf8()); return result; } void Map::setColor(MapColor* color, int pos) { // MapColor* old_color = color_set->colors[pos]; color_set->colors[pos] = color; color->setPriority(pos); if (color->getSpotColorMethod() == MapColor::SpotColor) { // Update dependent colors for (MapColor* map_color : color_set->colors) { if (map_color->getSpotColorMethod() == MapColor::CustomColor) { for (const SpotColorComponent& component : map_color->getComponents()) { if (component.spot_color == color) { // Assuming each spot color is rarely used more than once per composition if (map_color->getCmykColorMethod() == MapColor::SpotColor) map_color->setCmykFromSpotColors(); if (map_color->getRgbColorMethod() == MapColor::SpotColor) map_color->setRgbFromSpotColors(); updateSymbolIcons(map_color); emit colorChanged(map_color->getPriority(), map_color); } } } } } else { // Remove from dependent colors for (MapColor* map_color : color_set->colors) { if (map_color->getSpotColorMethod() == MapColor::CustomColor && map_color->removeSpotColorComponent(color)) { updateSymbolIcons(map_color); emit colorChanged(map_color->getPriority(), map_color); } } } updateSymbolIcons(color); emit colorChanged(pos, color); } void Map::addColor(MapColor* color, int pos) { color_set->insert(pos, color); if (getNumColors() == 1) { // This is the first color - the help text in the map widget(s) should be updated updateAllMapWidgets(); } setColorsDirty(); emit colorAdded(pos, color); color->setPriority(pos); } void Map::deleteColor(int pos) { MapColor* color = color_set->colors[pos]; if (color->getSpotColorMethod() == MapColor::SpotColor) { // Update dependent colors for (MapColor* map_color : color_set->colors) { if (map_color->removeSpotColorComponent(color)) { updateSymbolIcons(map_color); emit colorChanged(map_color->getPriority(), map_color); } } } color_set->erase(pos); if (getNumColors() == 0) { // That was the last color - the help text in the map widget(s) should be updated updateAllMapWidgets(); } // Treat combined symbols first before their parts for (Symbol* symbol : symbols) { if (symbol->getType() == Symbol::Combined) symbol->colorDeleted(color); } for (Symbol* symbol : symbols) { if (symbol->getType() != Symbol::Combined) symbol->colorDeleted(color); } emit colorDeleted(pos, color); delete color; } int Map::findColorIndex(const MapColor* color) const { std::size_t size = color_set->colors.size(); for (std::size_t i = 0; i < size; ++i) { if (color_set->colors[i] == color) return (int)i; } if (color && color->getPriority() == MapColor::Registration) { return MapColor::Registration; } return -1; } void Map::setColorsDirty() { colors_dirty = true; setHasUnsavedChanges(true); } void Map::useColorsFrom(Map* map) { color_set = map->color_set; } bool Map::isColorUsedByASymbol(const MapColor* color) const { for (const Symbol* symbol : symbols) { if (symbol->containsColor(color)) return true; } return false; } void Map::determineColorsInUse(const std::vector< bool >& by_which_symbols, std::vector< bool >& out) const { if (getNumSymbols() == 0) { out.clear(); return; } Q_ASSERT(int(by_which_symbols.size()) == getNumSymbols()); out.assign(std::size_t(getNumColors()), false); for (std::size_t c = 0, last = std::size_t(getNumColors()); c != last; ++c) { for (std::size_t s = 0, last_s = std::size_t(getNumSymbols()); s != last_s; ++s) { if (by_which_symbols[s] && getSymbol(int(s))->containsColor(getColor(int(c)))) { out[c] = true; break; } } } // Include required spot colors, too for (std::size_t c = 0, last_c = std::size_t(getNumColors()); c != last_c; ++c) { if (out[c]) continue; const auto color = getColor(int(c)); if (color->getSpotColorMethod() != MapColor::SpotColor) continue; for (std::size_t o = 0, last_o = std::size_t(getNumColors()); o != last_o; ++o) { if (!out[o]) continue; const auto other = getColor(int(o)); if (other->getSpotColorMethod() != MapColor::CustomColor) continue; const auto& components = other->getComponents(); if (std::any_of(begin(components), end(components), [color](auto& component) { return component.spot_color == color; })) { out[c] = true; break; } } } } void Map::checkSpotColorPresence() { const bool has_spot_colors = hasSpotColors(); if (this->has_spot_colors != has_spot_colors) { this->has_spot_colors = has_spot_colors; emit spotColorPresenceChanged(has_spot_colors); } } bool Map::hasSpotColors() const { for (const MapColor* color : color_set->colors) { if (color->getSpotColorMethod() == MapColor::SpotColor) return true; } return false; } void Map::setSymbolSetId(const QString& id) { symbol_set_id = id; symbols_dirty = true; } QHash Map::importSymbols( const Map& other, const MapColorMap& color_map, int insert_pos, bool merge_duplicates, const std::vector& filter ) { QHash out_pointermap; std::vector created_symbols; created_symbols.reserve(other.symbols.size()); for (std::size_t i = 0, last = other.symbols.size(); i < last; ++i) { if (filter.empty() || filter[i]) { const Symbol* symbol = other.symbols[i]; if (merge_duplicates) { // Check if symbol is already present auto match = std::find_if(begin(symbols), end(symbols), [symbol](auto s) { return s->equals(symbol, Qt::CaseInsensitive, false); }); if (match != end(symbols)) { // Symbol is already present out_pointermap.insert(symbol, *match); continue; } } auto new_symbol = symbol->duplicate(&color_map); out_pointermap.insert(symbol, new_symbol); created_symbols.push_back(new_symbol); } } // Add the created symbols if (insert_pos < 0) insert_pos = getNumSymbols(); for (const auto symbol : created_symbols) { addSymbol(symbol, insert_pos); ++insert_pos; } // Notify the created symbols of the new context (mind combined symbols) for (const auto symbol : created_symbols) { for (auto it = out_pointermap.constBegin(); it != out_pointermap.constEnd(); ++it) { // symbol is what was created by duplicate() above. symbol->symbolChanged(it.key(), it.value()); } } return out_pointermap; } void Map::addSelectionRenderables(const Object* object) { object->update(); selection_renderables->insertRenderablesOfObject(object); } void Map::updateSelectionRenderables(const Object* object) { removeSelectionRenderables(object); addSelectionRenderables(object); } void Map::removeSelectionRenderables(const Object* object) { selection_renderables->removeRenderablesOfObject(object, false); } void Map::initStatic() { static_initialized = true; covering_white_line = new LineSymbol(); covering_white_line->setColor(&covering_white); covering_white_line->setLineWidth(3.0); covering_red_line = new LineSymbol(); covering_red_line->setColor(&covering_red); covering_red_line->setLineWidth(1.0); covering_combined_line = new CombinedSymbol(); covering_combined_line->setNumParts(2); covering_combined_line->setPart(0, covering_white_line, false); covering_combined_line->setPart(1, covering_red_line, false); // Undefined symbols undefined_line = new LineSymbol(); undefined_line->setColor(&undefined_symbol_color); undefined_line->setLineWidth(1); undefined_line->setIsHelperSymbol(true); undefined_point = new PointSymbol(); undefined_point->setInnerRadius(100); undefined_point->setInnerColor(&undefined_symbol_color); undefined_point->setIsHelperSymbol(true); undefined_text = new TextSymbol(); undefined_text->setColor(&undefined_symbol_color); undefined_text->setIsHelperSymbol(true); } void Map::addSymbol(Symbol* symbol, int pos) { symbols.insert(symbols.begin() + pos, symbol); if (symbols.size() == 1) { // This is the first symbol - the help text in the map widget(s) should be updated updateAllMapWidgets(); } emit symbolAdded(pos, symbol); setSymbolsDirty(); } void Map::moveSymbol(int from, int to) { symbols.insert(symbols.begin() + to, symbols[from]); if (from > to) ++from; symbols.erase(symbols.begin() + from); // TODO: emit symbolChanged(pos, symbol); ? setSymbolsDirty(); } const Symbol* Map::getSymbol(int i) const { if (i >= 0) return symbols[i]; else if (i == -1) return nullptr; else if (i == -2) return getUndefinedPoint(); else if (i == -3) return getUndefinedLine(); else { Q_ASSERT(!"Invalid symbol index given"); return getUndefinedLine(); } } Symbol* Map::getSymbol(int i) { return const_cast(static_cast(this)->getSymbol(i)); } void Map::setSymbol(Symbol* symbol, int pos) { Symbol* old_symbol = symbols[pos]; // Check if an object with this symbol is selected bool object_with_symbol_selected = false; for (const Object* object : object_selection) { if (object->getSymbol() == old_symbol || object->getSymbol()->containsSymbol(old_symbol)) { object_with_symbol_selected = true; break; } } changeSymbolForAllObjects(old_symbol, symbol); int size = (int)symbols.size(); for (int i = 0; i < size; ++i) { if (i == pos) continue; if (symbols[i]->symbolChanged(symbols[pos], symbol)) updateAllObjectsWithSymbol(symbols[i]); } // Change the symbol symbols[pos] = symbol; emit symbolChanged(pos, symbol, old_symbol); setSymbolsDirty(); delete old_symbol; if (object_with_symbol_selected) emit selectedObjectEdited(); } void Map::deleteSymbol(int pos) { if (deleteAllObjectsWithSymbol(symbols[pos])) undo_manager->clear(); int size = (int)symbols.size(); for (int i = 0; i < size; ++i) { if (i == pos) continue; if (symbols[i]->symbolChanged(symbols[pos], nullptr)) updateAllObjectsWithSymbol(symbols[i]); } // Delete the symbol Symbol* temp = symbols[pos]; delete symbols[pos]; symbols.erase(symbols.begin() + pos); if (symbols.empty()) { // That was the last symbol - the help text in the map widget(s) should be updated updateAllMapWidgets(); } emit symbolDeleted(pos, temp); setSymbolsDirty(); } int Map::findSymbolIndex(const Symbol* symbol) const { if (!symbol) return -1; int size = (int)symbols.size(); for (int i = 0; i < size; ++i) { if (symbols[i] == symbol) return i; } if (symbol == undefined_point) return -2; else if (symbol == undefined_line) return -3; else if (symbol == undefined_text) return -4; // maybe element of point symbol return -1; } void Map::setSymbolsDirty() { if (symbol_icon_scale > 0) { symbol_icon_scale = 0; QTimer::singleShot(0, this, SLOT(updateSymbolIconZoom())); } symbols_dirty = true; setHasUnsavedChanges(true); } void Map::updateSymbolIcons(const MapColor* color) { for (std::size_t i = 0, size = symbols.size(); i < size; ++i) { if (symbols[i]->containsColor(color)) { symbols[i]->resetIcon(); emit symbolIconChanged(i); } } } void Map::scaleAllSymbols(double factor) { int size = getNumSymbols(); for (int i = 0; i < size; ++i) { Symbol* symbol = getSymbol(i); symbol->scale(factor); emit symbolChanged(i, symbol, symbol); } updateAllObjects(); setSymbolsDirty(); } void Map::determineSymbolsInUse(std::vector< bool >& out) const { out.assign(symbols.size(), false); for (auto part : parts) { for (int o = 0; o < part->getNumObjects(); ++o) { const Symbol* symbol = part->getObject(o)->getSymbol(); int index = findSymbolIndex(symbol); if (index >= 0) out[index] = true; } } determineSymbolUseClosure(out); } void Map::determineSymbolUseClosure(std::vector< bool >& symbol_bitfield) const { bool change; do { change = false; for (size_t i = 0, end = symbol_bitfield.size(); i < end; ++i) { if (symbol_bitfield[i]) continue; // Check if this symbol is needed by any included symbol for (size_t k = 0; k < end; ++k) { if (i == k) continue; if (!symbol_bitfield[k]) continue; if (symbols[k]->containsSymbol(symbols[i])) { symbol_bitfield[i] = true; change = true; break; } } } } while (change); } qreal Map::symbolIconZoom() const { if (symbol_icon_scale <= 0) const_cast(this)->updateSymbolIconZoom(); return symbol_icon_scale; } void Map::updateSymbolIconZoom() { // A simple heuristics which determines the symbol icon scale from // the mean of the line symbol widths. auto values = std::vector(); values.reserve(symbols.size()); for (const auto symbol : symbols) { if (symbol->isHelperSymbol()) continue; auto size = symbol->dimensionForIcon(); if (size > 0) values.push_back(size); } std::sort(begin(values), end(values)); auto percentile = [](const auto& v, quint8 p) { return v[v.size() * p / 100]; }; auto new_scale = qreal(0); if (!values.empty()) { // Scale the symbol size at the 80th percentile to 90%. new_scale = std::max(qreal(1), 90 / percentile(values, 80)); // The symbol size at the 20th percentile shall not get much below 10%. auto small_scale = std::max(qreal(1), 10 / percentile(values, 20)); if (small_scale > new_scale) new_scale = (new_scale + small_scale) / 2; } // Convert from % to factor, and discretize to filter out minor changes. new_scale = qreal(0.05) * std::max(1, qRound(new_scale / 5)); if (!qFuzzyCompare(new_scale, symbol_icon_scale)) { symbol_icon_scale = new_scale; for (const auto symbol : symbols) symbol->resetIcon(); emit symbolIconZoomChanged(); } } void Map::setTemplate(Template* temp, int pos) { templates[pos] = temp; emit templateChanged(pos, templates[pos]); } void Map::addTemplate(Template* temp, int pos) { templates.insert(templates.begin() + pos, temp); if (templates.size() == 1) { // This is the first template - the help text in the map widget(s) should be updated updateAllMapWidgets(); } emit templateAdded(pos, temp); } void Map::removeTemplate(int pos) { auto it = templates.begin() + pos; Template* temp = *it; templates.erase(it); if (templates.empty()) { // That was the last template - the help text in the map widget(s) should maybe be updated (if there are no objects) updateAllMapWidgets(); } emit templateDeleted(pos, temp); } void Map::deleteTemplate(int pos) { Template* temp = getTemplate(pos); removeTemplate(pos); delete temp; } void Map::setTemplateAreaDirty(Template* temp, const QRectF& area, int pixel_border) { bool front_cache = findTemplateIndex(temp) >= getFirstFrontTemplate(); // TODO: is there a better way to find out if that is a front or back template? for (MapWidget* widget : widgets) { const MapView* map_view = widget->getMapView(); if (map_view->isTemplateVisible(temp)) widget->markTemplateCacheDirty(map_view->calculateViewBoundingBox(area), pixel_border, front_cache); } } void Map::setTemplateAreaDirty(int i) { if (i == -1) return; // no assert here as convenience, so setTemplateAreaDirty(-1) can be called without effect for the map part Q_ASSERT(i >= 0 && i < (int)templates.size()); templates[i]->setTemplateAreaDirty(); } int Map::findTemplateIndex(const Template* temp) const { int size = (int)templates.size(); for (int i = 0; i < size; ++i) { if (templates[i] == temp) return i; } Q_ASSERT(false); return -1; } void Map::setTemplatesDirty() { templates_dirty = true; setHasUnsavedChanges(true); } void Map::emitTemplateChanged(Template* temp) { emit templateChanged(findTemplateIndex(temp), temp); } void Map::clearClosedTemplates() { if (closed_templates.empty()) return; for (Template* temp : closed_templates) delete temp; closed_templates.clear(); setTemplatesDirty(); emit closedTemplateAvailabilityChanged(); } void Map::closeTemplate(int i) { Template* temp = getTemplate(i); removeTemplate(i); if (temp->getTemplateState() == Template::Loaded) temp->unloadTemplateFile(); closed_templates.push_back(temp); setTemplatesDirty(); if (closed_templates.size() == 1) emit closedTemplateAvailabilityChanged(); } bool Map::reloadClosedTemplate(int i, int target_pos, QWidget* dialog_parent, const QString& map_path) { Template* temp = closed_templates[i]; // Try to find and load the template again if (temp->getTemplateState() != Template::Loaded) { if (!temp->tryToFindAndReloadTemplateFile(map_path)) { if (!temp->execSwitchTemplateFileDialog(dialog_parent)) return false; } } // If successfully loaded, add to template list again if (temp->getTemplateState() == Template::Loaded) { closed_templates.erase(closed_templates.begin() + i); addTemplate(temp, target_pos); temp->setTemplateAreaDirty(); setTemplatesDirty(); if (closed_templates.empty()) emit closedTemplateAvailabilityChanged(); return true; } return false; } void Map::push(UndoStep *step) { undo_manager->push(std::unique_ptr(step)); } void Map::addPart(MapPart* part, std::size_t index) { Q_ASSERT(index <= parts.size()); parts.insert(parts.begin() + index, part); if (current_part_index >= index) setCurrentPartIndex(current_part_index + 1); emit mapPartAdded(index, part); setOtherDirty(); updateAllMapWidgets(); } void Map::removePart(std::size_t index) { Q_ASSERT(index < parts.size()); Q_ASSERT(parts.size() > 1); if (current_part_index == index) // First switch to another part when removing the current part setCurrentPartIndex((index == parts.size() - 1) ? (parts.size() - 2) : (index + 1)); MapPart* part = parts[index]; // FIXME: This loop should move to MapPart. while(part->getNumObjects()) part->deleteObject(0, false); parts.erase(parts.begin() + index); if (current_part_index >= index) setCurrentPartIndex((index == parts.size()) ? (parts.size() - 1) : index); emit mapPartDeleted(index, part); delete part; setOtherDirty(); updateAllMapWidgets(); } int Map::findPartIndex(const MapPart* part) const { std::size_t const size = parts.size(); for (std::size_t i = 0; i < size; ++i) { if (parts[i] == part) return i; } Q_ASSERT(false); return -1; } void Map::setCurrentPartIndex(std::size_t index) { Q_ASSERT(index < parts.size()); MapPart* const old_part = parts[current_part_index]; if (index != current_part_index) { current_part_index = index; emit currentMapPartIndexChanged(index); } MapPart* const new_part = parts[current_part_index]; if (new_part != old_part) { clearObjectSelection(true); emit currentMapPartChanged(new_part); } } std::size_t Map::reassignObjectsToMapPart(std::set::const_iterator begin, std::set::const_iterator end, std::size_t source, std::size_t destination) { Q_ASSERT(source < parts.size()); Q_ASSERT(destination < parts.size()); std::size_t count = 0; MapPart* const source_part = parts[source]; MapPart* const target_part = parts[destination]; for (auto it = begin; it != end; ++it) { Object* const object = *it; source_part->deleteObject(object, true); int index = target_part->getNumObjects(); target_part->addObject(object, index); ++count; } setOtherDirty(); std::size_t const target_end = target_part->getNumObjects(); std::size_t const target_begin = target_end - count; if (current_part_index == source) { int const selection_size = getNumSelectedObjects(); // When modifying the selection we must not use the original iterators // because they may be operating on the selection and then become invalid! for (std::size_t i = target_begin; i != target_end; ++i) { Object* const object = target_part->getObject(i); if (isObjectSelected(object)) removeObjectFromSelection(object, false); } if (selection_size != getNumSelectedObjects()) emit objectSelectionChanged(); } return target_begin; } std::size_t Map::reassignObjectsToMapPart(std::vector::const_iterator begin, std::vector::const_iterator end, std::size_t source, std::size_t destination) { Q_ASSERT(source < parts.size()); Q_ASSERT(destination < parts.size()); bool selection_changed = false; std::size_t count = 0; MapPart* const source_part = parts[source]; MapPart* const target_part = parts[destination]; for (auto it = begin; it != end; ++it) { Object* const object = source_part->getObject(*it); if (current_part_index == source && isObjectSelected(object)) { removeObjectFromSelection(object, false); selection_changed = true; } source_part->deleteObject(object, true); int index = target_part->getNumObjects(); target_part->addObject(object, index); ++count; } setOtherDirty(); if (selection_changed) emit objectSelectionChanged(); return target_part->getNumObjects() - count; } std::size_t Map::mergeParts(std::size_t source, std::size_t destination) { Q_ASSERT(source < parts.size()); Q_ASSERT(destination < parts.size()); std::size_t count = 0; MapPart* const source_part = parts[source]; MapPart* const target_part = parts[destination]; // Preserve order (but not efficient) for (std::size_t i = source_part->getNumObjects(); i > 0 ; --i) { Object* object = source_part->getObject(0); source_part->deleteObject(0, true); int index = target_part->getNumObjects(); target_part->addObject(object, index); ++count; } if (current_part_index == source) setCurrentPartIndex(destination); if (destination != source) removePart(source); return target_part->getNumObjects() - count; } int Map::getNumObjects() const { int num_objects = 0; for (const MapPart* part : parts) num_objects += part->getNumObjects(); return num_objects; } int Map::addObject(Object* object, int part_index) { MapPart* part = parts[(part_index < 0) ? current_part_index : part_index]; int object_index = part->getNumObjects(); part->addObject(object, object_index); return object_index; } void Map::deleteObject(Object* object, bool remove_only) { for (MapPart* part : parts) { if (part->deleteObject(object, remove_only)) return; } qCritical().nospace() << this << "::deleteObject(" << object << "," << remove_only << "): Object not found. This is a bug."; if (!remove_only) delete object; } void Map::setObjectsDirty() { objects_dirty = true; setHasUnsavedChanges(true); } QRectF Map::calculateExtent(bool include_helper_symbols, bool include_templates, const MapView* view) const { QRectF rect; // Objects for (const MapPart* part : parts) rectIncludeSafe(rect, part->calculateExtent(include_helper_symbols)); // Templates if (include_templates) { for (const Template* temp : templates) { if (view && !view->isTemplateVisible(temp)) continue; if (temp->getTemplateState() != Template::Loaded) continue; rectIncludeSafe(rect, temp->calculateTemplateBoundingBox()); } } return rect; } void Map::setObjectAreaDirty(const QRectF& map_coords_rect) { for (MapWidget* widget : widgets) widget->markObjectAreaDirty(map_coords_rect); } void Map::findObjectsAt( MapCoordF coord, float tolerance, bool treat_areas_as_paths, bool extended_selection, bool include_hidden_objects, bool include_protected_objects, SelectionInfoVector& out ) const { getCurrentPart()->findObjectsAt(coord, tolerance, treat_areas_as_paths, extended_selection, include_hidden_objects, include_protected_objects, out); } void Map::findAllObjectsAt( MapCoordF coord, float tolerance, bool treat_areas_as_paths, bool extended_selection, bool include_hidden_objects, bool include_protected_objects, SelectionInfoVector& out ) const { for (const MapPart* part : parts) part->findObjectsAt(coord, tolerance, treat_areas_as_paths, extended_selection, include_hidden_objects, include_protected_objects, out); } void Map::findObjectsAtBox( MapCoordF corner1, MapCoordF corner2, bool include_hidden_objects, bool include_protected_objects, std::vector< Object* >& out ) const { getCurrentPart()->findObjectsAtBox(corner1, corner2, include_hidden_objects, include_protected_objects, out); } int Map::countObjectsInRect(const QRectF& map_coord_rect, bool include_hidden_objects) { int count = 0; for (const MapPart* part : parts) count += part->countObjectsInRect(map_coord_rect, include_hidden_objects); return count; } bool Map::existsObject(const std::function& condition) const { return std::any_of(begin(parts), end(parts), [&condition](auto part) { return part->existsObject(condition); }); } void Map::applyOnMatchingObjects(const std::function& operation, const std::function& condition) { for (auto part : parts) part->applyOnMatchingObjects(operation, condition); } void Map::applyOnMatchingObjects(const std::function& operation, const std::function& condition) { for (auto part : parts) part->applyOnMatchingObjects(operation, condition); } void Map::applyOnAllObjects(const std::function& operation) { for (auto part : parts) part->applyOnAllObjects(operation); } void Map::applyOnAllObjects(const std::function& operation) { for (auto part : parts) part->applyOnAllObjects(operation); } void Map::scaleAllObjects(double factor, const MapCoord& scaling_center) { applyOnAllObjects(ObjectOp::Scale{factor, MapCoordF{scaling_center}}); } void Map::rotateAllObjects(double rotation, const MapCoord& center) { applyOnAllObjects(ObjectOp::Rotate{rotation, MapCoordF{center}}); } void Map::updateAllObjects() { applyOnAllObjects(&Object::forceUpdate); } void Map::updateAllObjectsWithSymbol(const Symbol* symbol) { applyOnMatchingObjects(&Object::forceUpdate, ObjectOp::HasSymbol{symbol}); } void Map::changeSymbolForAllObjects(const Symbol* old_symbol, const Symbol* new_symbol) { applyOnMatchingObjects(ObjectOp::ChangeSymbol{new_symbol}, ObjectOp::HasSymbol{old_symbol}); } bool Map::deleteAllObjectsWithSymbol(const Symbol* symbol) { bool exists = existsObject(ObjectOp::HasSymbol{symbol}); if (exists) { // Remove objects from selection removeSymbolFromSelection(symbol, true); // Delete objects from map applyOnMatchingObjects(ObjectOp::Delete(), ObjectOp::HasSymbol{symbol}); } return exists; } bool Map::existsObjectWithSymbol(const Symbol* symbol) const { return existsObject(ObjectOp::HasSymbol{symbol}); } void Map::setGeoreferencing(const Georeferencing& georeferencing) { *this->georeferencing = georeferencing; setOtherDirty(); } void Map::setGrid(const MapGrid &grid) { if (grid != this->grid) { this->grid = grid; for (MapWidget* widget : widgets) { MapView* view = widget->getMapView(); if (view && view->isGridVisible()) view->updateAllMapWidgets(); } setOtherDirty(); } } bool Map::isAreaHatchingEnabled() const { return renderable_options & Symbol::RenderAreasHatched; } void Map::setAreaHatchingEnabled(bool enabled) { if (enabled) renderable_options |= Symbol::RenderAreasHatched; else renderable_options &= ~Symbol::RenderAreasHatched; } bool Map::isBaselineViewEnabled() const { return renderable_options & Symbol::RenderBaselines; } void Map::setBaselineViewEnabled(bool enabled) { if (enabled) renderable_options |= Symbol::RenderBaselines; else renderable_options &= ~Symbol::RenderBaselines; } const MapPrinterConfig& Map::printerConfig() { if (printer_config.isNull()) printer_config.reset(new MapPrinterConfig(*this)); return *printer_config; } MapPrinterConfig Map::printerConfig() const { MapPrinterConfig ret = printer_config.isNull() ? MapPrinterConfig{ *this } : *printer_config; return ret; } void Map::setPrinterConfig(const MapPrinterConfig& config) { if (printer_config.isNull()) { printer_config.reset(new MapPrinterConfig(config)); setOtherDirty(); } else if (*printer_config != config) { *printer_config = config; setOtherDirty(); } } void Map::resetPrinterConfig() { if (printer_config) { printer_config.reset(); setOtherDirty(); } } void Map::setImageTemplateDefaults(bool use_meters_per_pixel, double meters_per_pixel, double dpi, double scale) { image_template_use_meters_per_pixel = use_meters_per_pixel; image_template_meters_per_pixel = meters_per_pixel; image_template_dpi = dpi; image_template_scale = scale; } void Map::getImageTemplateDefaults(bool& use_meters_per_pixel, double& meters_per_pixel, double& dpi, double& scale) { use_meters_per_pixel = image_template_use_meters_per_pixel; meters_per_pixel = image_template_meters_per_pixel; dpi = image_template_dpi; scale = image_template_scale; } void Map::setHasUnsavedChanges(bool has_unsaved_changes) { if (!has_unsaved_changes) { colors_dirty = false; symbols_dirty = false; templates_dirty = false; objects_dirty = false; other_dirty = false; if (unsaved_changes) { unsaved_changes = false; emit hasUnsavedChanged(unsaved_changes); } } else if (!unsaved_changes) { unsaved_changes = true; emit hasUnsavedChanged(unsaved_changes); } } void Map::setOtherDirty() { other_dirty = true; setHasUnsavedChanges(true); } // slot void Map::undoCleanChanged(bool is_clean) { if (is_clean && unsaved_changes && !(colors_dirty || symbols_dirty || templates_dirty || other_dirty)) { setHasUnsavedChanges(false); } else if (!is_clean && !unsaved_changes) { setHasUnsavedChanges(true); } } } // namespace OpenOrienteering mapper-0.8.1.1/src/core/map.h000066400000000000000000001543701325266516600156360ustar00rootroot00000000000000/* * Copyright 2012-2014 Thomas Schöps * Copyright 2013-2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_MAP_H #define OPENORIENTEERING_MAP_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "core/map_coord.h" #include "core/map_grid.h" #include "core/map_part.h" class QIODevice; class QPainter; class QTranslator; class QWidget; // IWYU pragma: no_forward_declare QRectF namespace OpenOrienteering { class CombinedSymbol; class FileFormat; class Georeferencing; class LineSymbol; class MapColor; class MapColorMap; class MapPrinterConfig; class MapRenderables; class MapView; class MapWidget; class Object; class PointSymbol; class RenderConfig; class Symbol; class Template; class TextSymbol; class UndoManager; class UndoStep; /** * The translator for color and symbol texts. * * This translator is used by class Map but kept outside of the * class' namespace in order to allow for forward declaration * instead of including "map.h". */ extern QPointer map_symbol_translator; /** Central class for an OpenOrienteering map */ class Map : public QObject { Q_OBJECT friend class MapTest; friend class MapRenderables; friend class OCAD8FileImport; friend class NativeFileImport; friend class NativeFileExport; friend class XMLFileImporter; friend class XMLFileExporter; public: /** A set of selected objects represented by a std::set of object pointers. */ typedef std::set ObjectSelection; /** * Different strategies for importing elements from another map. */ enum ImportModeFlag { ObjectImport = 0x00, ///< Import objects, symbols and colors. SymbolImport = 0x01, ///< Import symbols and colors. ColorImport = 0x02, ///< Import colors. GeorefImport = 0x10, ///< Use the georeferencing for object import. MinimalImport = 0x20, ///< Imports with minimal symbol and color dependencies. MinimalSymbolImport = SymbolImport | MinimalImport, MinimalObjectImport = ObjectImport | MinimalImport, CompleteImport = ObjectImport | GeorefImport }; Q_DECLARE_FLAGS(ImportMode, ImportModeFlag) /** Options for zooming to visibility of selection. */ enum SelectionVisibility { FullVisibility, PartialVisibility, IgnoreVisibilty }; /** Creates a new, empty map. */ Map(); /** Destroys the map. */ ~Map() override; /** * Deletes all map data. * * The resulting map must not be modified before another init(). */ void clear(); /** * Initializes an empty map. * * A map is empty when it is newly constructed or after clear(). */ void init(); /** * Deletes all map data, and reinitializes the empty map. * * This method combines a call to clear() followed by init(). */ void reset(); /** * Saves the map to the given file. * * If a MapView is given, is state will be saved. */ bool saveTo(const QString& path, MapView *view); /** * Exports the map to the given file and format. * * If a MapView is given, is state will be saved. * If a FileFormat is given, it will be used, otherwise the file format * is determined from the filename. * * If the map was modified, it will still be considered modified after * successful export. */ bool exportTo(const QString& path, MapView* view = nullptr, const FileFormat* format = nullptr); /** * Attempts to load the map from the specified path. Returns true on success. * * @param path The file path to load the map from. * @param dialog_parent The parent widget for all dialogs. * This should never be nullptr in a QWidgets application. * @param view If not nullptr, restores this map view. * @param load_symbols_only Loads only symbols from the chosen file. * Useful to load symbol sets. * @param show_error_messages Whether to show import errors and warnings. */ bool loadFrom(const QString& path, QWidget* dialog_parent, MapView* view = nullptr, bool load_symbols_only = false, bool show_error_messages = true); /** * Imports the other map into this map with the following strategy: * - if the other map contains objects, import all objects with the minimum * amount of colors and symbols needed to display them * - if the other map does not contain objects, import all symbols with * the minimum amount of colors needed to display them * - if the other map does neither contain objects nor symbols, import all colors * * WARNING: this method potentially changes the 'other' map if the * scales differ (by rescaling to fit this map's scale)! */ void importMap( const Map* other, ImportMode mode, QWidget* dialog_parent = nullptr, std::vector* filter = nullptr, int symbol_insert_pos = -1, bool merge_duplicate_symbols = true, QHash* out_symbol_map = nullptr ); /** * Imports another map into this map with the following strategy: * - If the other map contains objects, import all objects with the * minimum amount of colors and symbols needed to display them. * - If the other map does not contain objects, import all symbols * with the minimum amount of colors needed to display them. * - If the other map does neither contain objects nor symbols, * import all colors. * The transform is applied to all imported objects. */ QHash importMap( const Map& imported_map, ImportMode mode, std::vector* filter = nullptr, int symbol_insert_pos = -1, bool merge_duplicate_symbols = true, const QTransform& transform = {} ); /** * Serializes the map directly into the given IO device in a known format. * This can be imported again using importFromIODevice(). * Returns true if successful. */ bool exportToIODevice(QIODevice* stream); /** * Loads the map directly from the given IO device, * where the data must have been written by exportToIODevice(). * Returns true if successful. */ bool importFromIODevice(QIODevice* stream); /** * Draws the part of the map which is visible in the bounding box. * * @param painter The QPainter used for drawing. * @param config The rendering configuration */ void draw(QPainter* painter, const RenderConfig& config); /** * Draws a spot color overprinting simulation for the part of the map * which is visible in the given bounding box. * * @param painter Must be a QPainter on a QImage of Format_ARGB32_Premultiplied. * @param config The rendering configuration */ void drawOverprintingSimulation(QPainter* painter, const RenderConfig& config); /** * Draws the separation for a particular spot color for the part of the * map which is visible in the given bounding box. * * Separations are normally drawn in levels of gray where black means * full tone of the spot color. The parameter use_color can be used to * draw in the actual spot color instead. * * @param painter The QPainter used for drawing. * @param config The rendering configuration * @param spot_color The spot color to draw the separation for. * @param use_color If true, forces the separation to be drawn in its actual color. */ void drawColorSeparation(QPainter* painter, const RenderConfig& config, const MapColor* spot_color, bool use_color = false); /** * Draws the map grid. * * @param painter The QPainter used for drawing. * @param bounding_box Bounding box of area to draw, given in map coordinates. * @param on_screen If true, uses a cosmetic pen (one pixel wide), * otherwise uses a 0.1 mm wide pen. */ void drawGrid(QPainter* painter, const QRectF& bounding_box, bool on_screen); /** * Draws the templates with indices first_template until last_template which * are visible in the given bouding box. * * view determines template visibility and can be nullptr to show all templates. * The initial transform of the given QPainter must be the map-to-paintdevice transformation. * If on_screen is set to true, some optimizations will be applied, leading to a possibly lower display quality. * * @param painter The QPainter used for drawing. * @param bounding_box Bounding box of area to draw, given in map coordinates. * @param first_template Lowest index of the template range to draw. * @param last_template Highest index of the template range to draw. * @param view Optional pointer to MapView object which is used to query * template visibilities. * @param on_screen Potentially enables some drawing optimizations which * decrease drawing quality. Should be enabled when drawing on-screen. */ void drawTemplates(QPainter* painter, const QRectF& bounding_box, int first_template, int last_template, const MapView* view, bool on_screen) const; /** * Updates the renderables and extent of all objects which have changed. * This is automatically called by draw(), you normally do not need to call it directly. */ void updateObjects(); /** * Calculates the extent of all map elements. * * If templates shall be included, view may either be nullptr to include all * templates, or specify a MapView to take the template visibilities from. */ QRectF calculateExtent(bool include_helper_symbols = false, bool include_templates = false, const MapView* view = nullptr) const; /** * Must be called to notify the map of new widgets displaying it. * Useful to notify the widgets about which parts of the map have changed * and need to be redrawn. */ void addMapWidget(MapWidget* widget); /** * Removes the map widget, see addMapWidget(). */ void removeMapWidget(MapWidget* widget); /** * Redraws all map widgets completely - this can be slow! * Try to avoid this and do partial redraws instead, if possible. */ void updateAllMapWidgets(); /** * Makes sure that the selected object(s) are visible in all map widgets * by moving the views in the widgets to the selected objects. */ void ensureVisibilityOfSelectedObjects(SelectionVisibility visibility); // Current drawing /** * Sets the rect (given in map coordinates) as "dirty rect" for every * map widget showing this map, enlarged by the given pixel border. * This means that the area covered by the rect will be redrawn by * the active tool. Use this if the current tool's display has changed. * * @param map_coords_rect Area covered by the current tool's drawing in map coords. * @param pixel_border Border around the map coords rect which is also covered, * given in pixels. Allows to enlarge the area given by the map coords * by some pixels which are independent from the zoom level. * For example if a tool displays markers with a radius of 3 pixels, * it would set the bounding box of all markers as map_coords_rect and * 3 for pixel_border. * @param do_update Whether a repaint of the covered area should be triggered. */ void setDrawingBoundingBox(const QRectF& map_coords_rect, int pixel_border, bool do_update = true); /** * Removes the drawing bounding box and triggers a repaint. Use this if * the current drawing is hidden or erased. */ void clearDrawingBoundingBox(); /** * This is the analogon to setDrawingBoundingBox() for activities. * See setDrawingBoundingBox(). */ void setActivityBoundingBox(const QRectF& map_coords_rect, int pixel_border, bool do_update = true); /** * This is the analogon to clearDrawingBoundingBox() for activities. * See clearDrawingBoundingBox(). */ void clearActivityBoundingBox(); /** * Updates all dynamic drawings at the given positions, * i.e. tool & activity drawings. * * See setDrawingBoundingBox() and setActivityBoundingBox(). */ void updateDrawing(const QRectF& map_coords_rect, int pixel_border); // Element translations /** * Returns a translated symbol text (name or description), or the original text. */ QString translate(const QString& symbol_text) const; /** * Returns a translated symbol text (name or description), or an empty string. */ QString raw_translation(const QString& symbol_text) const; // Colors /** Returns the number of map colors defined in this map.*/ int getNumColors() const; /** Returns a pointer to the MapColor identified by the non-negative priority i. * * Returns nullptr if the color is not defined, or if it is a special color (i.e i<0), * i.e. only actual map colors are returned. */ const MapColor* getMapColor(int i) const; /** Returns a pointer to the MapColor identified by the non-negative priority i. * * Returns nullptr if the color is not defined, or if it is a special color (i.e i<0), * i.e. only actual map colors are returned. */ MapColor* getMapColor(int i); /** Returns a pointer to the const MapColor identified by the priority i. * * Parameter i may also be negative for specifying special reserved colors. * This is different from getMapColor(); * * Returns nullptr if the color is not defined. */ const MapColor* getColor(int i) const; /** * Replaces the color at index pos with the given color, updates dependent * colors and symbol icons. * * Emits colorChanged(). Does not delete the replaced color. */ void setColor(MapColor* color, int pos); /** * Adds the given color as a new color at the given index. * Emits colorAdded(). */ void addColor(MapColor* color, int pos); /** * Deletes the color at the given index. * Emits colorDeleted(). */ void deleteColor(int pos); /** * Loops through the color list, looking for the given color pointer. * Returns the index of the color, or -1 if it is not found. */ int findColorIndex(const MapColor* color) const; /** * Marks the colors as "dirty", i.e. as having unsaved changes. * Emits hasUnsavedChanged(true) if the map did not have unsaved changed before. */ void setColorsDirty(); /** * Makes this map use the color set from the given map. * Used to create the maps containing preview objects in the symbol editor. */ void useColorsFrom(Map* map); /** * Checks and returns if the given color is used by at least one symbol. */ bool isColorUsedByASymbol(const MapColor* color) const; /** * Checks which colors are in use by the symbols in this map. * * WARNING (FIXME): returns an empty list if the map does not contain symbols! * * @param by_which_symbols Must be of the same size as the symbol set. * If set to false for a symbol, it will be disregarded. * @param out Output parameter: a vector of the same size as the color list, * where each element is set to true if the color is used by at least one symbol. */ void determineColorsInUse(const std::vector< bool >& by_which_symbols, std::vector< bool >& out) const; /** Returns true if the map contains spot colors. */ bool hasSpotColors() const; // Symbols /** Returns the symbol set ID. */ QString symbolSetId() const; /** Sets the symbol set ID. */ void setSymbolSetId(const QString& id); /** Returns the number of symbols in this map. */ int getNumSymbols() const; /** Returns a pointer to the i-th symbol. */ const Symbol* getSymbol(int i) const; /** Returns a pointer to the i-th symbol. */ Symbol* getSymbol(int i); /** * Replaces the symbol at the given index with another symbol. * Emits symbolChanged() and possibly selectedObjectEdited(). */ void setSymbol(Symbol* symbol, int pos); /** * Adds the given symbol at the specified index. * Emits symbolAdded(). */ void addSymbol(Symbol* symbol, int pos); /** * Moves a symbol from one index to another in the symbol list. */ void moveSymbol(int from, int to); /** * Sorts the symbol list using the given comparator. */ template void sortSymbols(T compare); /** * Deletes the symbol at the given index. * Emits symbolDeleted(). */ void deleteSymbol(int pos); /** * Loops over all symbols, looking for the given symbol pointer. * Returns the index of the symbol, or -1 if the symbol is not found. * For the "undefined" symbols, returns special indices smaller than -1. */ int findSymbolIndex(const Symbol* symbol) const; /** * Marks the symbols as "dirty", i.e. as having unsaved changes. * Emits hasUnsavedChanged(true) if the map did not have unsaved changed before. */ void setSymbolsDirty(); /** * Updates the icons of all symbols with the given color. */ void updateSymbolIcons(const MapColor* color); /** * Scales all symbols by the given factor. */ void scaleAllSymbols(double factor); /** * Checks which symbols are in use in this map. * * Returns a vector of the same size as the symbol list, where each element * is set to true if there is at least one object which uses this symbol or * a derived (combined) symbol. */ void determineSymbolsInUse(std::vector& out) const; /** * Adds to the given symbol bitfield all other symbols which are needed to * display the symbols indicated by the bitfield because of symbol dependencies. */ void determineSymbolUseClosure(std::vector< bool >& symbol_bitfield) const; /** * Returns the scale factor to be used for default symbol icons. * * The full icon size (width, height) is represented by 1.0. */ qreal symbolIconZoom() const; public slots: /** * Updates the symbol icon zoom from the current set of symbols. * * The symbol icon zoom is chosen so that most symbols fit into the full * icon space, and the number of symbol below 10% size is kept low. * For a map without symbols, this returns 1.0. */ void updateSymbolIconZoom(); public: // Templates /** Returns the number of templates in this map. */ int getNumTemplates() const; /** Returns the i-th template. */ const Template* getTemplate(int i) const; /** Returns the i-th template. */ Template* getTemplate(int i); /** Sets the template index which is the first (lowest) to be drawn in front of the map. */ void setFirstFrontTemplate(int pos); /** Returns the template index which is the first (lowest) to be drawn in front of the map. */ int getFirstFrontTemplate() const; /** * Replaces the template at the given index with another. * Emits templateChanged(). */ void setTemplate(Template* temp, int pos); /** * Adds a new template at the given index. * * To place a template immediately below the map, adjust first_front_template * manually with setFirstFrontTemplate()! */ void addTemplate(Template* temp, int pos); /** * Removes the template with the given index from the template list, * but does not delete it. * NOTE: if required, adjust first_front_template manually with setFirstFrontTemplate()! */ void removeTemplate(int pos); /** * Removes the template with the given position from the template list and deletes it. * NOTE: if required, adjust first_front_template manually with setFirstFrontTemplate()! */ void deleteTemplate(int pos); /** * Marks the area defined by the given QRectF (in map coordinates) and * pixel border as "dirty", i.e. as needing a repaint, for the given template * in all map widgets. * * For an explanation of the area and pixel border, see setDrawingBoundingBox(). * * Warning: does nothing if the template is not visible in a widget! * So make sure to call this and showing/hiding a template in the correct order! */ void setTemplateAreaDirty(Template* temp, const QRectF& area, int pixel_border); /** * Marks the whole area of the i-th template as "to be repainted". * See setTemplateAreaDirty(). * Does nothing for i == -1. */ void setTemplateAreaDirty(int i); /** * Loops over all templates in the map and looks for the given template pointer. * Returns the index of the template. The template must be contained in the map, * otherwise an assert will be triggered! */ int findTemplateIndex(const Template* temp) const; /** * Marks the template settings as "dirty", i.e. as having unsaved changes. * Emits hasUnsavedChanged(true) if the map did not have unsaved changed before. */ void setTemplatesDirty(); /** Emits templateChanged() for the given template. */ void emitTemplateChanged(Template* temp); /** * Returns the number of manually closed templates * for which the settings are still stored. */ int getNumClosedTemplates() const; /** Returns the i-th closed template. */ const Template* getClosedTemplate(int i) const; /** Returns the i-th closed template. */ Template* getClosedTemplate(int i); /** Empties the list of closed templates. */ void clearClosedTemplates(); /** * Removes the template with the given index from the normal template list, * unloads the template file and adds the template to the closed template list * * NOTE: if required, adjust first_front_template manually with setFirstFrontTemplate()! */ void closeTemplate(int i); /** * Removes the template with the given index from the closed template list, * load the template file and adds the template to the normal template list again. * The template is made visible in the given view. * * NOTE: if required, adjust first_front_template manually with * setFirstFrontTemplate() before calling this method! * * @param i The index of the closed template to reload. * @param target_pos The desired index in the normal template list after loading. * @param dialog_parent Widget as parent for possible dialogs. * @param map_path Path where the map is saved currently. Used as possible * search location to locate missing templates. */ bool reloadClosedTemplate(int i, int target_pos, QWidget* dialog_parent, const QString& map_path = QString()); // Undo & Redo /** * Returns the UndoManager instance for this map. */ UndoManager& undoManager(); /** * Returns the UndoManager instance for this map. */ const UndoManager& undoManager() const; /** * Pushes a new undo step to the map's undoManager. */ void push(UndoStep* step); // Map parts /** * Returns the number of map parts in this map. */ int getNumParts() const; /** * Returns the i-th map part. */ MapPart* getPart(std::size_t i) const; /** * Adds the new part at the given index. */ void addPart(MapPart* part, std::size_t index); /** * Removes the map part at position. */ void removePart(std::size_t index); /** * Loops over all map parts, looking for the given part pointer. * Returns the part's index in the list. The part must be contained in the * map, otherwise an assert will be triggered! */ int findPartIndex(const MapPart* part) const; /** * Returns the current map part, i.e. the part where edit operations happen. */ MapPart* getCurrentPart() const; /** * Changes the current map part. * * This is a convenience method which looks up the part's index and then * calls setCurrentPartIndex. */ void setCurrentPart(MapPart* part); /** * Returns the index of the current map part. * * @see getCurrentPart(). */ std::size_t getCurrentPartIndex() const; /** * Changes the current map part. */ void setCurrentPartIndex(std::size_t index); /** * Moves all specified objects from the source to the destination map part. * * The objects will be continuously located at the end to the objects in the target part. * Source object which were selected will be removed from the object selection. * * @return The index of the first object which has been reassigned. */ std::size_t reassignObjectsToMapPart(std::set::const_iterator begin, std::set::const_iterator end, std::size_t source, std::size_t destination); /** * Moves all specified objects from the source to the target map part. * * The objects will be continuously located at the end to the objects in the target part. * Source object which were selected will be removed from the object selection. * * @return The index of the first object which has been reassigned. */ std::size_t reassignObjectsToMapPart(std::vector::const_iterator begin, std::vector::const_iterator end, std::size_t source, std::size_t destination); /** * Merges the source part with the destination part. * * Removes the source part unless it is identical with the destination part. * * The objects will be continuously located at the end to the objects in the target part. * Does not change the object selection. * * Makes the destination part the current part when the source part is the current part. * * @return The index of the first object which has been reassigned. */ std::size_t mergeParts(std::size_t source, std::size_t destination); // Objects /** Returns the total number of objects in this map (sum of all parts) */ int getNumObjects() const; /** * Adds the object as new object in the part with the given index, * or in the current part if the default -1 is passed. * Returns the index of the added object in the part. */ int addObject(Object* object, int part_index = -1); /** * Deletes the given object from the map. * remove_only will remove the object from the map, but not call "delete object"; * be sure to call removeObjectFromSelection() if necessary. * * TODO: make a separate method "removeObject()", remove_only is misleading! */ void deleteObject(Object* object, bool remove_only); /** * Marks the objects as "dirty", i.e. as having unsaved changes. * Emits hasUnsavedChanged(true) if the map did not have unsaved changed before. */ void setObjectsDirty(); /** * Marks the area given by map_coords_rect as "dirty" in all map widgets, * i.e. as needing to be redrawn because some object(s) changed there. */ void setObjectAreaDirty(const QRectF& map_coords_rect); /** * Finds and returns all objects at the given position in the current part. * * @param coord Coordinate where to query objects. * @param tolerance Allowed distance from the query coordinate to the objects. * @param treat_areas_as_paths If set to true, areas will be treated as paths, * i.e. they will be returned only if the query coordinate is close to * their border, not in all cases where the query coordinate is inside the area. * @param extended_selection If set to true, more object than defined by the * default behavior will be selected. For example, by default point objects * will only be returned if the query coord is close to the point object * coord. With extended_selection, they are also returned if the query * coord is just inside the graphical extent of the point object. * This can be used for a two-pass algorithm to find the most relevant * objects first, and then query for all objects if no objects are found * in the first pass. * @param include_hidden_objects Set to true if you want to find hidden objects. * @param include_protected_objects Set to true if you want to find protected objects. * @param out Output parameter. Will be filled with pairs of symbol types * and corresponding objects. The symbol type describes the way in which * an object has been found and is taken from Symbol::Type. This is e.g. * important for combined symbols, which can be found from a line or * an area. */ void findObjectsAt(MapCoordF coord, float tolerance, bool treat_areas_as_paths, bool extended_selection, bool include_hidden_objects, bool include_protected_objects, SelectionInfoVector& out) const; /** * Finds and returns all objects at the given position in all parts. * * @see Map::findObjectsAt */ void findAllObjectsAt(MapCoordF coord, float tolerance, bool treat_areas_as_paths, bool extended_selection, bool include_hidden_objects, bool include_protected_objects, SelectionInfoVector& out) const; /** * Finds and returns all objects intersecting the given box in the current part. * * @param corner1 First corner of the query box. * @param corner2 Second corner of the query box. * @param include_hidden_objects Set to true if you want to find hidden objects. * @param include_protected_objects Set to true if you want to find protected objects. * @param out Output parameter. Will be filled with an object list. */ void findObjectsAtBox(MapCoordF corner1, MapCoordF corner2, bool include_hidden_objects, bool include_protected_objects, std::vector& out) const; /** * Counts the objects whose bounding boxes intersect the given rect. * * This may be inaccurate because the true object shapes usually differ * from the bounding boxes. * * @param map_coord_rect The query rect. * @param include_hidden_objects Set to true if you want to find hidden objects. */ int countObjectsInRect(const QRectF& map_coord_rect, bool include_hidden_objects); /** * Applies a condition on all objects until the first match is found. * * @return True if there is an object matching the condition, false otherwise. */ bool existsObject(const std::function& condition) const; /** * Applies an operation on all objects which match a particular condition. */ void applyOnMatchingObjects(const std::function& operation, const std::function& condition); /** * Applies an operation on all objects which match a particular condition. */ void applyOnMatchingObjects(const std::function& operation, const std::function& condition); /** * Applies an operation on all objects. */ void applyOnAllObjects(const std::function& operation); /** * Applies an operation on all objects. */ void applyOnAllObjects(const std::function& operation); /** Scales all objects by the given factor. */ void scaleAllObjects(double factor, const MapCoord& scaling_center); /** Rotates all objects by the given rotation angle (in radians). */ void rotateAllObjects(double rotation, const MapCoord& center); /** Forces an update of all objects, i.e. calls update(true) on each map object. */ void updateAllObjects(); /** Forces an update of all objects with the given symbol. */ void updateAllObjectsWithSymbol(const Symbol* symbol); /** For all symbols with old_symbol, replaces the symbol by new_symbol. */ void changeSymbolForAllObjects(const Symbol* old_symbol, const Symbol* new_symbol); /** * Deletes all objects with the given symbol. * * @return True if at least one object was deleted, false otherwise */ bool deleteAllObjectsWithSymbol(const Symbol* symbol); /** * Returns if at least one object with the given symbol exists in the map. * WARNING: Even if no objects exist directly, the symbol could still be * required by another (combined) symbol used by an object! */ bool existsObjectWithSymbol(const Symbol* symbol) const; /** * Removes the renderables of the given object from display (does not * delete them!). */ void removeRenderablesOfObject(const Object* object, bool mark_area_as_dirty); /** * Inserts the renderables of the given object, so they will be displayed. */ void insertRenderablesOfObject(const Object* object); /** * Marks an object as irregular. */ void markAsIrregular(Object* object); /** * Returns the list of objects marked as irregular. */ const std::set irregularObjects() const; /** * Deletes the irregular objects. * * This function deletes the objects which were previously marked as irregular. * Only objects which are actually member of map parts are deleted. Objects in * undo steps or similar are ignored. * * \return The number of deleted objects. */ std::size_t deleteIrregularObjects(); // Object selection const ObjectSelection& selectedObjects() const; /** Returns the number of selected objects. */ int getNumSelectedObjects() const; /** Returns an iterator allowing to iterate over the selected objects. */ ObjectSelection::const_iterator selectedObjectsBegin() const; /** Returns an end iterator allowing to iterate over the selected objects. */ ObjectSelection::const_iterator selectedObjectsEnd() const; /** * Returns the object in the selection which was selected first by the user. * * If she later deselects it while other objects are still selected or if * the selection is done as box selection, this "first" selected object is * just a more or less random object from the selection. */ const Object* getFirstSelectedObject() const; /** * Returns the object in the selection which was selected first by the user. */ Object* getFirstSelectedObject(); /** * Checks the selected objects for compatibility with the given symbol. * @param symbol the symbol to check compatibility for * @param out_compatible returns if all selected objects are compatible * to the given symbol * @param out_different returns if at least one of the selected objects' * symbols is different to the given symbol */ void getSelectionToSymbolCompatibility(const Symbol* symbol, bool& out_compatible, bool& out_different) const; /** * Deletes the selected objects and creates an undo step for this action. */ void deleteSelectedObjects(); /** * Enlarges the given rect to cover all selected objects. */ void includeSelectionRect(QRectF& rect) const; /** * Draws the selected objects. * * @param painter The QPainter used for drawing. * @param force_min_size See draw(). * @param widget The widget in which the drawing happens. * Used to get view and viewport information. * @param replacement_renderables If given, draws these renderables instead * Of the selection renderables. TODO: HACK * @param draw_normal If set to true, draws the objects like normal objects, * otherwise draws transparent highlights. */ void drawSelection(QPainter* painter, bool force_min_size, MapWidget* widget, MapRenderables* replacement_renderables = nullptr, bool draw_normal = false); /** * Adds the given object to the selection. * @param object The object to add. * @param emit_selection_changed Set to true if objectSelectionChanged() * should be emitted. Do this only for the last in a * sequence of selection change oparations to prevent bad performance! */ void addObjectToSelection(Object* object, bool emit_selection_changed); /** * Removes the given object from the selection. * @param object The object to remove. * @param emit_selection_changed See addObjectToSelection(). */ void removeObjectFromSelection(Object* object, bool emit_selection_changed); /** * Removes from the selection all objects with the given symbol. * Returns true if at least one object has been removed. * @param symbol The symbol of the objects to remove. * @param emit_selection_changed See addObjectToSelection(). */ bool removeSymbolFromSelection(const Symbol* symbol, bool emit_selection_changed); /** Returns true if the given object is selected. */ bool isObjectSelected(const Object* object) const; /** * Toggles the selection of the given object. * Returns true if the object was selected, false if deselected. * @param object The object to select or deselect. * @param emit_selection_changed See addObjectToSelection(). */ bool toggleObjectSelection(Object* object, bool emit_selection_changed); /** * Empties the object selection. * @param emit_selection_changed See addObjectToSelection(). */ void clearObjectSelection(bool emit_selection_changed); /** * Emits objectSelectionChanged(). Use this if setting emit_selection_changed * in a selection change method is unfeasible. */ void emitSelectionChanged(); /** * Emits selectedObjectEdited(). Use this after making changes * to a selected object. */ void emitSelectionEdited(); // Other settings /** Sets the map's scale denominator. */ void setScaleDenominator(unsigned int value); /** Returns the map's scale denominator. */ unsigned int getScaleDenominator() const; /** * Changes the map's scale. * * @param new_scale_denominator The new scale denominator. * @param scaling_center The coordinate to use as scaling center. * @param scale_symbols Whether to scale the map symbols. * @param scale_objects Whether to scale the map object coordinates. * @param scale_georeferencing Whether to adjust the map's georeferencing reference point. * @param scale_templates Whether to scale non-georeferenced templates. */ void changeScale(unsigned int new_scale_denominator, const MapCoord& scaling_center, bool scale_symbols, bool scale_objects, bool scale_georeferencing, bool scale_templates); /** * Rotate the map around a point. * * @param rotation The rotation angle (in radians). * @param center The rotation center point. * @param adjust_georeferencing Whether to adjust the georeferencing reference point. * @param adjust_declination Whether to adjust the georeferencing declination. * @param adjust_templates Whether to adjust non-georeferenced templates. */ void rotateMap(double rotation, const MapCoord& center, bool adjust_georeferencing, bool adjust_declination, bool adjust_templates); /** Returns the map notes string. */ const QString& getMapNotes() const; /** * Sets the map notes string. * NOTE: Set the map to dirty manually! */ void setMapNotes(const QString& text); /** Returns the map's georeferencing object. */ const Georeferencing& getGeoreferencing() const; /** * Assigns georeferencing settings for the map from the given object and * sets the map to have unsaved changes. */ void setGeoreferencing(const Georeferencing& georeferencing); /** * Returns the map's grid settings. */ const MapGrid& getGrid() const; /** * Sets the map's grid settings from the given object and * may set the map to have unsaved changes. */ void setGrid(const MapGrid& grid); /** * TODO: These two options should really be view options, but are not due * to a limitation: * the current architecture makes it impossible to have different * renderables of the same objects in different views! */ /** Returns if area hatching is enabled. */ bool isAreaHatchingEnabled() const; /** Sets if area hatching is enabled. */ void setAreaHatchingEnabled(bool enabled); /** Returns if the baseline view is enabled. */ bool isBaselineViewEnabled() const; /** Sets if the baseline view is enabled. */ void setBaselineViewEnabled(bool enabled); /** Returns the rendering options as an int representing Symbol::RenderableOptions. */ int renderableOptions() const; /** Returns true if the map has a print configuration. */ bool hasPrinterConfig(); /** Returns a const reference to the current print configuration. * * If the map does not have a print configuration, a default configuration * is created first. */ const MapPrinterConfig& printerConfig(); /** Returns a copy of the current print configuration. * * If the map does not have a print configuration, the function will return * a default configuration for this map. */ MapPrinterConfig printerConfig() const; /** Sets the print configuration. */ void setPrinterConfig(const MapPrinterConfig& config); /** Clears the print configuration. * * After calling this method, hasPrinterConfig() returns false. */ void resetPrinterConfig(); /** Returns the default parameters for loading of image tempaltes. */ void getImageTemplateDefaults(bool& use_meters_per_pixel, double& meters_per_pixel, double& dpi, double& scale); /** * Sets default parameters for loading of image templates. * TODO: put these into a struct. */ void setImageTemplateDefaults(bool use_meters_per_pixel, double meters_per_pixel, double dpi, double scale); /** * Returns whether there are unsaved changes in the map. * * To toggle this state, never use setHasUnsavedChanges() directly unless * you know what you are doing, instead use setOtherDirty() or set one of * the more specific 'dirty' flags. This is because a call to * setHasUnsavedChanges() alone followed by a map change and an undo would * result in no changed flag. */ bool hasUnsavedChanges() const; /** Do not use this in usual cases, see hasUnsavedChanged(). */ void setHasUnsavedChanges(bool has_unsaved_changes); /** Returns if there are unsaved changes to the colors. */ bool areColorsDirty() const; /** Returns if there are unsaved changes to the symbols. */ bool areSymbolsDirty() const; /** Returns if there are unsaved changes to the templates. */ bool areTemplatesDirty() const; /** Returns if there are unsaved changes to the objects. */ bool areObjectsDirty() const; /** Returns if there are unsaved changes to anything else than the above. */ bool isOtherDirty() const; /** * Marks somthing unspecific in the map as "dirty", i.e. as having unsaved changes. * Emits hasUnsavedChanged(true) if the map did not have unsaved changed before. * * Use setColorsDirty(), setSymbolsDirty(), setTemplatesDirty() or * setObjectsDirty() if you know more specificly what has changed. */ void setOtherDirty(); // Static /** Returns the special covering red color. */ static const MapColor* getCoveringRed(); /** Returns the special covering white color. */ static const MapColor* getCoveringWhite(); /** Returns the special covering gray color for "undefined" objects. */ static const MapColor* getUndefinedColor(); /** Returns the special registration color. */ static const MapColor* getRegistrationColor(); /** Returns the special covering white line symbol. */ static LineSymbol* getCoveringWhiteLine(); /** Returns the special covering red line symbol. */ static LineSymbol* getCoveringRedLine(); /** Returns the special covering combined symbol (white + red). */ static CombinedSymbol* getCoveringCombinedLine(); /** Returns the special gray "undefined" line symbol. */ static LineSymbol* getUndefinedLine(); /** Returns the special gray "undefined" point symbol. */ static PointSymbol* getUndefinedPoint(); /** Returns the special gray "undefined" text symbol. */ static TextSymbol* getUndefinedText(); signals: /** * Emitted when a the map enters or leaves the state which is saved on map. */ void hasUnsavedChanged(bool is_clean); /** Emitted when a color is added to the map, gives the color's index and pointer. */ void colorAdded(int pos, const MapColor* color); /** Emitted when a map color is changed, gives the color's index and pointer. */ void colorChanged(int pos, const MapColor* color); /** Emitted when a map color is deleted, gives the color's index and pointer. */ void colorDeleted(int pos, const MapColor* old_color); /** Emitted when the presence of spot colors in the map changes. */ void spotColorPresenceChanged(bool has_spot_colors) const; /** Emitted when a symbol is added to the map, gives the symbol's index and pointer. */ void symbolAdded(int pos, const Symbol* symbol); /** Emitted when a symbol in the map is changed. */ void symbolChanged(int pos, const Symbol* new_symbol, const Symbol* old_symbol); /** Emitted when the icon of the symbol with the given index changes. */ void symbolIconChanged(int pos); /** Emitted when a symbol in the map is deleted. */ void symbolDeleted(int pos, const Symbol* old_symbol); /** Emitted when the symbol icon zoom changes. */ void symbolIconZoomChanged(); /** Emitted when a template is added to the map, gives the template's index and pointer. */ void templateAdded(int pos, Template* temp); /** Emitted when a template in the map is changed, gives the template's index and pointer. */ void templateChanged(int pos, Template* temp); /** Emitted when a template in the map is deleted, gives the template's index and pointer. */ void templateDeleted(int pos, const Template* old_temp); /** Emitted when the number of closed templates changes between zero and one. */ void closedTemplateAvailabilityChanged(); /** Emitted when the set of selected objects changes. Also emitted when the * symbol of a selected object changes (which is similar to selecting another * object). */ void objectSelectionChanged(); /** * Emitted when at least one of the selected objects is edited in any way. * For example, this includes the case where a symbol of one of the * selected objects is edited, too. */ void selectedObjectEdited(); /** * Emitted when the map part currently used for drawing changes. * * @see currentMapPartIndexChanged() */ void currentMapPartChanged(const MapPart* part); /** * Emitted when the index of map part currently used for drawing changes. * * This signal may be emitted even when the current MapPart object does not * change. This happens when the index changes due to addition or removal * of map parts. */ void currentMapPartIndexChanged(std::size_t index); /** * Emitted when a part is added to the map. */ void mapPartAdded(std::size_t index, const MapPart* part); /** * Emitted when a part's properties are changed. */ void mapPartChanged(std::size_t index, const MapPart* part); /** * Emitted when a part is removed from the map. */ void mapPartDeleted(std::size_t index, const MapPart* part); protected slots: void checkSpotColorPresence(); void undoCleanChanged(bool is_clean); private: typedef std::vector ColorVector; typedef std::vector SymbolVector; typedef std::vector TemplateVector; typedef std::vector PartVector; typedef std::vector WidgetVector; class MapColorSet : public QSharedData { public: ColorVector colors; MapColorSet(); MapColorSet(const MapColorSet& ) = delete; ~MapColorSet(); void insert(int pos, MapColor* color); void erase(int pos); /** Merges another MapColorSet into this set, trying to maintain * the relative order of colors. * If a filter is given, imports only the colors for which * filter[color_index] is true, or which are spot colors referenced * by the selected colors. * If a map is given, this color set is modified through the map's * color accessor methods so that other object become aware of the * changes. * @return a mapping from the imported color pointer in the other set * to color pointers in this set. */ MapColorMap importSet(const MapColorSet& other, std::vector* filter = nullptr, Map* map = nullptr); private: /** * Adjust the priorities of the colors in the range [first,last). */ void adjustColorPriorities(int first, int last); }; /** * Imports the other symbol set into this map's symbols. * * If a filter is given, imports only the symbols for which filter[color_index] == true. * Imported symbols are placed at insert_pos (if positive), or after the existing symbols. * Returns a mapping from original symbols (in other) to imported symbols. */ QHash importSymbols( const Map& other, const MapColorMap& color_map, int insert_pos = -1, bool merge_duplicates = true, const std::vector& filter = {} ); void addSelectionRenderables(const Object* object); void updateSelectionRenderables(const Object* object); void removeSelectionRenderables(const Object* object); static void initStatic(); QExplicitlySharedDataPointer color_set; bool has_spot_colors; QString symbol_set_id; SymbolVector symbols; mutable qreal symbol_icon_scale = 0; TemplateVector templates; TemplateVector closed_templates; int first_front_template = 0; // index of the first template in templates which should be drawn in front of the map PartVector parts; ObjectSelection object_selection; Object* first_selected_object = nullptr; QScopedPointer undo_manager; std::size_t current_part_index = 0; WidgetVector widgets; QScopedPointer renderables; QScopedPointer selection_renderables; QString map_notes; QScopedPointer georeferencing; MapGrid grid; int renderable_options; QScopedPointer printer_config; bool image_template_use_meters_per_pixel; double image_template_meters_per_pixel; double image_template_dpi; double image_template_scale; bool colors_dirty; // are there unsaved changes for the colors? bool symbols_dirty; // ... for the symbols? bool templates_dirty; // ... for the templates? bool objects_dirty; // ... for the objects? bool other_dirty; // ... for any other settings? bool unsaved_changes; // are there unsaved changes for any component? std::set irregular_objects; // Static static bool static_initialized; static MapColor covering_white; static MapColor covering_red; static MapColor undefined_symbol_color; static MapColor registration_color; static LineSymbol* covering_white_line; static LineSymbol* covering_red_line; static LineSymbol* undefined_line; static PointSymbol* undefined_point; static TextSymbol* undefined_text; static CombinedSymbol* covering_combined_line; }; // ### Map inline code ### inline int Map::getNumColors() const { return (int)color_set->colors.size(); } inline MapColor* Map::getMapColor(int i) { return const_cast(static_cast(this)->getMapColor(i)); } inline const MapColor* Map::getMapColor(int i) const { if (0 <= i && i < (int)color_set->colors.size()) { return color_set->colors[i]; } return nullptr; } inline const MapColor* Map::getColor(int i) const { if (0 <= i && i < (int)color_set->colors.size()) { return color_set->colors[i]; } else switch (i) { case -1005: return getCoveringRed(); case -1000: return getCoveringWhite(); case -900: return getRegistrationColor(); case -500: return getUndefinedColor(); default: return nullptr; } } inline QString Map::symbolSetId() const { return symbol_set_id; } inline int Map::getNumSymbols() const { return (int)symbols.size(); } template void Map::sortSymbols(T compare) { std::stable_sort(symbols.begin(), symbols.end(), compare); // TODO: emit symbolChanged(pos, symbol); ? s/b same choice as for moveSymbol() setSymbolsDirty(); } inline int Map::getNumTemplates() const { return templates.size(); } inline const Template*Map::getTemplate(int i) const { return templates[i]; } inline Template*Map::getTemplate(int i) { return templates[i]; } inline void Map::setFirstFrontTemplate(int pos) { first_front_template = pos; } inline int Map::getFirstFrontTemplate() const { return first_front_template; } inline int Map::getNumClosedTemplates() const { return (int)closed_templates.size(); } inline const Template* Map::getClosedTemplate(int i) const { return closed_templates[i]; } inline Template* Map::getClosedTemplate(int i) { return closed_templates[i]; } inline UndoManager& Map::undoManager() { return const_cast(static_cast(this)->undoManager()); } inline const UndoManager& Map::undoManager() const { return *(undo_manager.data()); } inline int Map::getNumParts() const { return parts.size(); } inline MapPart* Map::getPart(std::size_t i) const { return parts[i]; } inline MapPart* Map::getCurrentPart() const { return parts[current_part_index]; } inline void Map::setCurrentPart(MapPart* part) { setCurrentPartIndex(findPartIndex(part)); } inline std::size_t Map::getCurrentPartIndex() const { return current_part_index; } inline const Map::ObjectSelection& Map::selectedObjects() const { return object_selection; } inline int Map::getNumSelectedObjects() const { return (int)object_selection.size(); } inline Map::ObjectSelection::const_iterator Map::selectedObjectsBegin() const { return object_selection.cbegin(); } inline Map::ObjectSelection::const_iterator Map::selectedObjectsEnd() const { return object_selection.cend(); } inline const Object* Map::getFirstSelectedObject() const { return first_selected_object; } inline Object* Map::getFirstSelectedObject() { return first_selected_object; } inline const QString& Map::getMapNotes() const { return map_notes; } inline const Georeferencing& Map::getGeoreferencing() const { return *georeferencing; } inline const MapGrid& Map::getGrid() const { return grid; } inline int Map::renderableOptions() const { return renderable_options; } inline bool Map::hasPrinterConfig() { return !printer_config.isNull(); } inline bool Map::hasUnsavedChanges() const { return unsaved_changes; } inline bool Map::areColorsDirty() const { return colors_dirty; } inline bool Map::areSymbolsDirty() const { return symbols_dirty; } inline bool Map::areTemplatesDirty() const { return templates_dirty; } inline bool Map::areObjectsDirty() const { return objects_dirty; } inline bool Map::isOtherDirty() const { return other_dirty; } inline const MapColor* Map::getCoveringRed() { return &covering_red; } inline const MapColor* Map::getCoveringWhite() { return &covering_white; } inline const MapColor* Map::getUndefinedColor() { return &undefined_symbol_color; } inline const MapColor* Map::getRegistrationColor() { return ®istration_color; } inline LineSymbol* Map::getCoveringWhiteLine() { return covering_white_line; } inline LineSymbol* Map::getCoveringRedLine() { return covering_red_line; } inline CombinedSymbol* Map::getCoveringCombinedLine() { return covering_combined_line; } inline LineSymbol* Map::getUndefinedLine() { return undefined_line; } inline PointSymbol* Map::getUndefinedPoint() { return undefined_point; } inline TextSymbol* Map::getUndefinedText() { return undefined_text; } } // namespace OpenOrienteering Q_DECLARE_METATYPE(const OpenOrienteering::Map*) Q_DECLARE_OPERATORS_FOR_FLAGS(OpenOrienteering::Map::ImportMode) #endif mapper-0.8.1.1/src/core/map_color.cpp000066400000000000000000000232361325266516600173630ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2015 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "map_color.h" #include #include #include #include #include #include namespace OpenOrienteering { MapColor::MapColor() : name(QCoreApplication::translate("OpenOrienteering::Map", "New color")), priority(Undefined), opacity(1.0f), q_color(Qt::black), spot_color_method(MapColor::UndefinedMethod), cmyk_color_method(MapColor::CustomColor), rgb_color_method(MapColor::CmykColor), flags(0), spot_color_name() { Q_ASSERT(isBlack()); } MapColor::MapColor(int priority) : name(QCoreApplication::translate("OpenOrienteering::Map", "New color")), priority(priority), opacity(1.0f), q_color(Qt::black), spot_color_method(MapColor::UndefinedMethod), cmyk_color_method(MapColor::CustomColor), rgb_color_method(MapColor::CmykColor), flags(0), spot_color_name() { Q_ASSERT(isBlack()); switch (priority) { case CoveringWhite: setCmyk(QColor(Qt::white)); Q_ASSERT(isWhite()); opacity = 1000.0f; // HACK: (almost) always opaque, even if multiplied by opacity factors break; case CoveringRed: setRgb(QColor(Qt::red)); setCmykFromRgb(); opacity = 1000.0f; // HACK: (almost) always opaque, even if multiplied by opacity factors break; case Undefined: setCmyk(QColor(Qt::darkGray)); break; case Registration: Q_ASSERT(isBlack()); name = QCoreApplication::translate("OpenOrienteering::MapColor", "Registration black (all printed colors)"); break; default: ; // no change } } MapColor::MapColor(const QString& name, int priority) : name(name), priority(priority), opacity(1.0), q_color(Qt::black), spot_color_method(MapColor::UndefinedMethod), cmyk_color_method(MapColor::CustomColor), rgb_color_method(MapColor::CmykColor), flags(0), spot_color_name() { Q_ASSERT(isBlack()); } MapColor* MapColor::duplicate() const { MapColor* copy = new MapColor(name, priority); copy->cmyk = cmyk; copy->rgb = rgb; copy->opacity = opacity; copy->q_color = q_color; copy->spot_color_method = spot_color_method; copy->cmyk_color_method = cmyk_color_method; copy->rgb_color_method = rgb_color_method; copy->flags = flags; copy->spot_color_name = spot_color_name; copy->components = components; return copy; } bool MapColor::isBlack() const { return rgb.isBlack() && cmyk.isBlack(); } bool MapColor::isWhite() const { return rgb.isWhite() && cmyk.isWhite(); } bool operator==(const SpotColorComponents& lhs, const SpotColorComponents& rhs) { return lhs.size() == rhs.size() && std::is_permutation(begin(lhs), end(lhs), begin(rhs), [](const auto& left, const auto& right) { return *left.spot_color == *right.spot_color && qAbs(left.factor - right.factor) < 1e-03; }); } bool MapColor::componentsEqual(const MapColor& other, bool compare_priority) const { const SpotColorComponents& lhs(components); const SpotColorComponents& rhs(other.components); return lhs.size() == rhs.size() && std::is_permutation(begin(lhs), end(lhs), begin(rhs), [compare_priority](const auto& left, const auto& right) { return left.spot_color->equals(*right.spot_color, compare_priority) && qAbs(left.factor - right.factor) < 1e-03; }); } bool MapColor::equals(const MapColor& other, bool compare_priority) const { return (!compare_priority || (priority == other.priority)) && (name.compare(other.name, Qt::CaseInsensitive) == 0) && (spot_color_method == other.spot_color_method) && (cmyk_color_method == other.cmyk_color_method) && (rgb_color_method == other.rgb_color_method) && (flags == other.flags) && (cmyk_color_method != CustomColor || cmyk == other.cmyk) && (rgb_color_method != CustomColor || rgb == other.rgb) && ( spot_color_method == UndefinedMethod || (spot_color_method == SpotColor && spot_color_name.compare(other.spot_color_name, Qt::CaseInsensitive) == 0) || (spot_color_method == CustomColor && componentsEqual(other, compare_priority)) ) && (qAbs(opacity - other.opacity) < 1e-03); } void MapColor::setSpotColorName(const QString& spot_color_name) { spot_color_method = MapColor::SpotColor; this->spot_color_name = spot_color_name; components.clear(); updateCalculatedColors(); } void MapColor::setSpotColorComposition(const SpotColorComponents& components) { this->components = components; if (components.empty()) spot_color_method = UndefinedMethod; else spot_color_method = CustomColor; removeSpotColorComponent(this); updateCompositionName(); updateCalculatedColors(); } bool MapColor::removeSpotColorComponent(const MapColor* color) { auto size_before = components.size(); auto match = [this, color](const SpotColorComponent& scc) { return scc.spot_color == color; }; components.erase(std::remove_if(begin(components), end(components), match), end(components)); bool changed = components.size() != size_before; if (changed) { if (components.empty()) spot_color_method = UndefinedMethod; updateCompositionName(); updateCalculatedColors(); } return changed; } void MapColor::setKnockout(bool flag) { if (spot_color_method != MapColor::UndefinedMethod) { if (flag) { if (!getKnockout()) flags += MapColor::Knockout; } else if (getKnockout()) flags -= MapColor::Knockout; Q_ASSERT(getKnockout() == flag); } } bool MapColor::getKnockout() const { return (flags & MapColor::Knockout) > 0; } void MapColor::setCmyk(const MapColorCmyk& new_cmyk) { cmyk_color_method = MapColor::CustomColor; cmyk = new_cmyk; updateCalculatedColors(); } void MapColor::setCmykFromSpotColors() { if (spot_color_method == MapColor::CustomColor) { cmyk_color_method = MapColor::SpotColor; updateCalculatedColors(); } } void MapColor::setCmykFromRgb() { if (rgb_color_method == MapColor::CmykColor) rgb_color_method = MapColor::CustomColor; cmyk_color_method = MapColor::RgbColor; updateCalculatedColors(); } void MapColor::setRgb(const MapColorRgb& new_rgb) { rgb_color_method = MapColor::CustomColor; rgb = new_rgb; updateCalculatedColors(); } void MapColor::setRgbFromSpotColors() { if (spot_color_method == MapColor::CustomColor) { rgb_color_method = MapColor::SpotColor; updateCalculatedColors(); } } void MapColor::setRgbFromCmyk() { if (cmyk_color_method == MapColor::RgbColor) cmyk_color_method = MapColor::CustomColor; rgb_color_method = MapColor::CmykColor; updateCalculatedColors(); } void MapColor::updateCompositionName() { if (spot_color_method != MapColor::SpotColor) { spot_color_name.clear(); for (auto& component : components) { if (!spot_color_name.isEmpty()) spot_color_name += QLatin1String(", "); spot_color_name += component.spot_color->getSpotColorName() + QLatin1Char(' ') + QString::number(component.factor * 100) /* % */; } } } void MapColor::updateCalculatedColors() { Q_ASSERT(components.size() == 0 || spot_color_method == CustomColor); Q_ASSERT(components.size() > 0 || spot_color_method != CustomColor); Q_ASSERT(!((cmyk_color_method == RgbColor) && (rgb_color_method == CmykColor))); if (spot_color_method != CustomColor) { // No composition, thus cannot determine CMYK or RGB from spot colors. if (cmyk_color_method == MapColor::SpotColor) cmyk_color_method = MapColor::CustomColor; if (rgb_color_method == MapColor::SpotColor) rgb_color_method = MapColor::CustomColor; } else { if (cmyk_color_method == MapColor::SpotColor) cmyk = cmykFromSpotColors(); if (rgb_color_method == MapColor::SpotColor) rgb = rgbFromSpotColors(); } if (cmyk_color_method == MapColor::RgbColor) cmyk = MapColorCmyk(rgb); if (rgb_color_method == MapColor::CmykColor) rgb = MapColorRgb(cmyk); if (cmyk_color_method != RgbColor) q_color = cmyk; else q_color = rgb; Q_ASSERT(components.size() > 0 || cmyk_color_method != MapColor::SpotColor); Q_ASSERT(components.size() > 0 || rgb_color_method != MapColor::SpotColor); } MapColorCmyk MapColor::cmykFromSpotColors() const { Q_ASSERT(components.size() > 0); MapColorCmyk cmyk(Qt::white); Q_ASSERT(cmyk.isWhite()); for (auto&& component : components) { const MapColorCmyk& other = component.spot_color->cmyk; cmyk.c = cmyk.c + component.factor * other.c * (1.0f - cmyk.c); cmyk.m = cmyk.m + component.factor * other.m * (1.0f - cmyk.m); cmyk.y = cmyk.y + component.factor * other.y * (1.0f - cmyk.y); cmyk.k = cmyk.k + component.factor * other.k * (1.0f - cmyk.k); } return cmyk; } MapColorRgb MapColor::rgbFromSpotColors() const { Q_ASSERT(components.size() > 0); MapColorRgb rgb = QColor(Qt::white); Q_ASSERT(rgb.isWhite()); for (auto&& component : components) { const MapColorRgb& other = component.spot_color->rgb; rgb.r = rgb.r - component.factor * (1.0f - other.r) * rgb.r; rgb.g = rgb.g - component.factor * (1.0f - other.g) * rgb.g; rgb.b = rgb.b - component.factor * (1.0f - other.b) * rgb.b; } return rgb; } } // namespace OpenOrienteering mapper-0.8.1.1/src/core/map_color.h000066400000000000000000000476361325266516600170420ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps, Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_MAP_COLOR_H #define OPENORIENTEERING_MAP_COLOR_H #include #include #include #include #include #include #include #include namespace OpenOrienteering { class MapColor; /** * The MapColorCmyk class provides a datatype for storing and transfering * opaque CMYK colors. * * Components (c, m, y, k) are floats in the range [0.0; 1.0]. */ struct MapColorCmyk { /** The cyan component. */ float c = 0; /** The magenta component. */ float m = 0; /** The yellow component. */ float y = 0; /** The black component (aka key). */ float k = 1; /** Constructs a black color. */ MapColorCmyk() noexcept = default; /** Constructs a color with the given components. */ MapColorCmyk(float c, float m, float y, float k) noexcept; /** Constructs a copy of the given CMYK color. */ MapColorCmyk(const MapColorCmyk& other) noexcept = default; /** Constructs a CMYK color of the given QColor. Used for type conversions. */ MapColorCmyk(const QColor& other) noexcept; /** Assigns another color's value to this color. */ MapColorCmyk& operator=(const MapColorCmyk& other) = default; /** Converts this color to a QColor. */ operator QColor() const; /** Returns true if this color is black. */ bool isBlack() const; /** Returns true if this color is white. */ bool isWhite() const; }; /** Returns true iff the MapColorCmyk are equal in all components. */ bool operator==(const MapColorCmyk& lhs, const MapColorCmyk& rhs); /** Returns true iff the MapColorCmyk differ in at least one components. */ bool operator!=(const MapColorCmyk& lhs, const MapColorCmyk& rhs); /** * The MapColorRgb class provides a datatype for storing and transfering * opaque RGB colors. * * Components (r, g, b) are floats in the range [0.0; 1.0]. */ struct MapColorRgb { /** The red component. */ float r = 0; /** The green component. */ float g = 0; /** The blue component. */ float b = 0; /** Constructs a black color. */ MapColorRgb() noexcept = default; /** Constructs a color with the given components. */ MapColorRgb(float r, float g, float b) noexcept; /** Constructs a copy of the given RGB color. */ MapColorRgb(const MapColorRgb& other) noexcept = default; /** Constructs a RGB color of the given QColor. Used for type conversions. */ MapColorRgb(const QColor& other) noexcept; /** Assigns another color's value to this color. */ MapColorRgb& operator=(const MapColorRgb& other) noexcept = default; /** Converts this color to a QColor. */ operator QColor() const; /** Returns true if this color is black. */ bool isBlack() const; /** Returns true if this color is white. */ bool isWhite() const; }; /** Returns true if both MapColorRgb are equal in all components. */ bool operator==(const MapColorRgb& lhs, const MapColorRgb& rhs); /** Returns true iff the MapColorRgb differ in at least one components. */ bool operator!=(const MapColorRgb& lhs, const MapColorRgb& rhs); /** * The SpotColorComponent datatype describes the use of a spot color in a * screen or overprint to create another color. */ struct SpotColorComponent { /** A map color which is a spot color. */ const MapColor* spot_color; /** The factor describes the halftoning (screen). * It is a value in the range [0.0; 1.0]. */ float factor; /** Constructs a component with an undefined spot color and halftoning. */ SpotColorComponent(); /** Constructs a component for the given spot color and halftoning. */ SpotColorComponent(const MapColor* spot_color, float factor); /** Returns true iff the spot color is defined. */ bool isValid() const; }; /** * The SpotColorComponents type is a STL container that stores all * SpotColorComponent elements which make up a particular map color. */ typedef std::vector SpotColorComponents; /** * Returns true if for each element in lhs, there is an element of equal color * and factor in rhs. */ bool operator==(const SpotColorComponents &lhs, const SpotColorComponents& rhs); /** * The MapColor class provides colors which may be used by symbols (and * objects). Apart from the mere color, it specifies how to output the color * to different type of devices and optionally how the color was composed * from other colors. */ class MapColor { public: /** * SpecialProperties provides identifiers for (pseudo-)colors serving * particular purposes in the program. */ enum SpecialPriorities { CoveringRed = -1005, ///< Foreground color for tool helper lines CoveringWhite = -1000, ///< Background color for tool helper lines Registration = -900, ///< Registration Black: all printed colors Undefined = -500, ///< Color for objects with undefined symbol Reserved = -1 ///< Never drawn }; /** * ColorMethod provides identifiers for methods on how to determine a * particular realization of a color. */ enum ColorMethod { UndefinedMethod = 0, CustomColor = 1, SpotColor = 2, CmykColor = 4, RgbColor = 8, Knockout = 16 }; /** Constructs a black CMYK map color of undefined priority.*/ MapColor(); /** Constructs a black CMYK map color with the given priority. */ MapColor(int priority); /** Constructs a black CMYK map color with the given name and priority. */ MapColor(const QString& name, int priority); /** Returns a copy of the color. */ MapColor* duplicate() const; /** Returns a QColor representation (reference) of the map color. * * This color is based on the CMYK color unless the CMYK color method is * RGB: In that case, the returned value is based on the RGB color. */ operator const QColor&() const; /** Converts the current RGB values to a QRgb. */ operator QRgb() const; /** Returns the color's name for the mapping context. */ const QString& getName() const; /** Sets the color's name for the mapping context. */ void setName(const QString& name); /** Returns the color's priority. */ int getPriority() const; /** * Sets the color's priority. * Normally you don't want to call this directly. */ void setPriority(int priority); /** @deprecated Returns the color's opacity. */ float getOpacity() const; /** @deprecated Sets the color's opacity. */ void setOpacity(float opacity); /** * Returns how the spot color is to be created. * * Returns UndefinedMethod, SpotColor (for a full tone single color * referenced by name), or CustomColor (for a color created from named * colors using halftoning (screens) and overprint. */ ColorMethod getSpotColorMethod() const; /** * Returns the name for the single spot color or a label for the spot color * composition which realizes this map color. * Returns an empty string for an UndefinedMethod. */ const QString& getSpotColorName() const; /** * Sets the name of a single spot color which realizes this map color, * and sets the spot color method to SpotColor. */ void setSpotColorName(const QString& spot_color_id); /** * Sets the given components (i.e. screens and/or overprint) for the color, * and sets the spot color method to CustomColor. */ void setSpotColorComposition(const SpotColorComponents& components); /** * Returns the components of the spot color realization of this color. * Returns an empty list if the spot color method is not CustomColor. */ const SpotColorComponents& getComponents() const; /** * Removes a component color. * * Returns true if components were removed. * Returns false if the color was not part of the composition before. */ bool removeSpotColorComponent(const MapColor* color); /** * Sets the value of knockout flag for spot color printing. * * The color must have a spot color definition, or no change will be done. */ void setKnockout(bool flag); /** * Returns the value of the knockout flag. */ bool getKnockout() const; /** * Returns how the CMYK color value is determined. * * Returns CustomColor (for custom CMYK values, e.g. for named spot colors), * SpotColor (for values determined from evaluation the spot color composition), * or RgbColor (for values directly derived from the current RGB values). */ ColorMethod getCmykColorMethod() const; /** Returns the map color's CMYK values. */ const MapColorCmyk& getCmyk() const; /** Sets the CMYK values, and sets the CMYK color method to CustomColor. */ void setCmyk(const MapColorCmyk& cmyk); /** * Determines the CMYK values from the spot color composition, * and sets the CMYK color method to SpotColor. * * The spot color method must be CustomColor. * If the spot color composition is empty, the spot color method is * changed to CustomColor, and the CMYK color method is changed to * CustomColor. */ void setCmykFromSpotColors(); /** * Determines the CMYK from the current RGB value, * and sets the CMYK color method to RgbColor. * * If the RGB color method is CmykColor, it is changed to CustomColor. */ void setCmykFromRgb(); /** * Returns how the RGB color value is determined. * * Returns CustomColor (for custom RGB values), * SpotColor (for values determined from evaluation the spot color composition), * or CmykColor (for values directly derived from the current CMYK value). */ ColorMethod getRgbColorMethod() const; /** Returns the map color's RGB values. */ const MapColorRgb& getRgb() const; /** Sets the RGB values, and sets the RGB color method to CustomColor. */ void setRgb(const MapColorRgb& rgb); /** * Determines the RGB values from the spot color composition, * and sets the RGB color method to SpotColor. * * The spot color method must be CustomColor. * If the spot color composition is empty, the spot color method is * changed to CustomColor, and the RGB color method is changed to * CustomColor. */ void setRgbFromSpotColors(); /** * Determines the RGB from the current CMYK value, * and sets the RGB color method to CmykColor. * * If the CMYK color method is RgbColor, it is changed to CustomColor. */ void setRgbFromCmyk(); /** Returns true if this color is black. */ bool isBlack() const; /** Returns true if this color is white. */ bool isWhite() const; /** Compares this color and another. */ bool equals(const MapColor& other, bool compare_priority) const; /** Compares two colors given by pointers. * Returns true if the colors are equal or if both pointers are nullptr. */ static bool equal(const MapColor* color, const MapColor* other); /** Returns true if this color's priority is less than the other's. */ bool comparePriority(const MapColor& other) const; /** Compares this color's components and another. */ bool componentsEqual(const MapColor& other, bool compare_priority) const; protected: /** * Determines the composition name from the components. * * Does nothing it the spot color method is not CustomColor. */ void updateCompositionName(); /** * Updates all calculated color values. * * If the spot color method is different from CustomColor, resets CMYK and * RGB methods from SpotColor tp CustomColor. */ void updateCalculatedColors(); /** * Returns a CMYK color determined from the cmyk color of the spot color * components. */ MapColorCmyk cmykFromSpotColors() const; /** * Returns a RGB color determined from the cmyk color of the spot color * components. */ MapColorRgb rgbFromSpotColors() const; QString name; int priority; MapColorCmyk cmyk; MapColorRgb rgb; float opacity; QColor q_color; char spot_color_method; char cmyk_color_method; char rgb_color_method; char flags; QString spot_color_name; SpotColorComponents components; }; /** Returns true if both MapColor are equal in all components. */ bool operator==(const MapColor& lhs, const MapColor& rhs); /** Returns true iff the MapColor differ in at least one components. */ bool operator!=(const MapColor& lhs, const MapColor& rhs); /** * MapColorMap provides a mapping from one map color to another. * * In addition to explicitly user-defined key-value pairs of colors, it will * (in const contexts) map colors with reserved priorities to themselves, * assuming that there is only a single static instance of these colors. */ class MapColorMap { public: /** Constructs a new MapColorMap. */ MapColorMap(); /** Returns the size, i.e. the number of user-defined key-value pairs. */ int size() const; /** Clears the user-defined key-value pairs. */ void clear(); /** Returns true if there is a user-defined value for the key color */ bool contains(const MapColor* key) const; /** Returns the mapped value for the key color. * * Returns the user-defined value for the key color if it is defined. * Otherwise, if the key color's priority is from the RESERVED domain, * returns key. Otherwise returns nullptr. */ const MapColor* value(const MapColor* key) const; /** Returns the mapped value for the key color. Same as value(). * * Returns the user-defined value for the key color if it is defined. * Otherwise, if the key color's priority is from the RESERVED domain, * returns key. Otherwise returns nullptr. */ const MapColor* operator[](const MapColor* key) const; /** Returns a reference to pointer to the mapped value for the key color. * If a user-defined mapped value does not exist yet, a default- * constructed mapped value is created first. */ const MapColor* & operator[](const MapColor* key); private: /** The low-level mapping. */ QHash mapping; }; /** * Constructs a QColor with the alpha given by opacity from the prototype c. * * A QColor object must be constructible from c. */ template QColor colorWithOpacity(T c, float opacity); /** * Constructs a QColor with opacity from the given MapColor. */ QColor colorWithOpacity(const MapColor& c); // ### MapColorCmyk inline code ### inline MapColorCmyk::MapColorCmyk(float c, float m, float y, float k) noexcept : c(c), m(m), y(y), k(k) { // Nothing } inline MapColorCmyk::MapColorCmyk(const QColor& other) noexcept : c(other.cyanF()), m(other.magentaF()), y(other.yellowF()), k(other.blackF()) { // Nothing } inline MapColorCmyk::operator QColor() const { return QColor::fromCmykF(c, m, y, k); } inline bool MapColorCmyk::isBlack() const { return (1.0 == k) || (1.0 == c && 1.0 == m && 1.0 == y); } inline bool MapColorCmyk::isWhite() const { return (0.0 == c && 0.0 == m && 0.0 == y && 0.0 == k); } inline bool operator==(const MapColorCmyk& lhs, const MapColorCmyk& rhs) { // The maximum difference of two floating point member values // which are regarded as *equal*. static const float epsilon = 0.0005f; return ( qAbs(lhs.c - rhs.c) <= epsilon && qAbs(lhs.m - rhs.m) <= epsilon && qAbs(lhs.y - rhs.y) <= epsilon && qAbs(lhs.k - rhs.k) <= epsilon ); } inline bool operator!=(const MapColorCmyk& lhs, const MapColorCmyk& rhs) { return !(lhs == rhs); } // ### MapColorRgb inline code ### inline MapColorRgb::MapColorRgb(float r, float g, float b) noexcept : r(r), g(g), b(b) { // Nothing } inline MapColorRgb::MapColorRgb(const QColor& other) noexcept : r(other.redF()), g(other.greenF()), b(other.blueF()) { // Nothing } inline MapColorRgb::operator QColor() const { return QColor::fromRgbF(r, g, b); } inline bool MapColorRgb::isBlack() const { return (0.0 == r && 0.0 == g && 0.0 == b); } inline bool MapColorRgb::isWhite() const { return (1.0 == r && 1.0 == g && 1.0 == b); } inline bool operator==(const MapColorRgb& lhs, const MapColorRgb& rhs) { // The maximum difference of two floating point member values // which are regarded as *equal*. static const float epsilon = 0.0005f; return ( qAbs(lhs.r - rhs.r) <= epsilon && qAbs(lhs.g - rhs.g) <= epsilon && qAbs(lhs.b - rhs.b) <= epsilon ); } inline bool operator!=(const MapColorRgb& lhs, const MapColorRgb& rhs) { return !(lhs == rhs); } // ### SpotColorComponent inline code ### inline SpotColorComponent::SpotColorComponent() : spot_color(nullptr), factor(0.0f) { // Nothing } inline SpotColorComponent::SpotColorComponent(const MapColor* spot_color, float factor) : spot_color(spot_color), factor(factor) { // Nothing } inline bool SpotColorComponent::isValid() const { return spot_color; } // ### MapColor inline code ### inline MapColor::operator const QColor&() const { return q_color; } inline MapColor::operator QRgb() const { return qRgba(qFloor(255.9 * rgb.r), qFloor(255.9 * rgb.g), qFloor(255.9 * rgb.b), qFloor(255.9 * opacity)); } inline const QString& MapColor::getName() const { return name; } inline void MapColor::setName(const QString& name) { this->name = name; } inline int MapColor::getPriority() const { return priority; } inline void MapColor::setPriority(int priority) { this->priority = priority; } inline float MapColor::getOpacity() const { return opacity; } inline void MapColor::setOpacity(float opacity) { this->opacity = opacity; } inline MapColor::ColorMethod MapColor::getSpotColorMethod() const { return (ColorMethod)spot_color_method; } inline const QString& MapColor::getSpotColorName() const { return spot_color_name; } inline const SpotColorComponents& MapColor::getComponents() const { return components; } inline MapColor::ColorMethod MapColor::getCmykColorMethod() const { return (ColorMethod)cmyk_color_method; } inline const MapColorCmyk& MapColor::getCmyk() const { return cmyk; } inline MapColor::ColorMethod MapColor::getRgbColorMethod() const { return (ColorMethod)rgb_color_method; } inline const MapColorRgb& MapColor::getRgb() const { return rgb; } inline bool MapColor::comparePriority(const MapColor& other) const { return priority < other.priority; } inline bool MapColor::equal(const MapColor* color, const MapColor* other) { if (color == other) return true; else if (color && other) return color->equals(*other, false); else return false; } inline bool operator==(const MapColor& lhs, const MapColor& rhs) { return lhs.equals(rhs, true); } inline bool operator!=(const MapColor& lhs, const MapColor& rhs) { return !lhs.equals(rhs, true); } // ### MapColorMap inline code ### inline MapColorMap::MapColorMap() : mapping() { // Nothing. } inline int MapColorMap::size() const { return mapping.size(); } inline void MapColorMap::clear() { mapping.clear(); } inline bool MapColorMap::contains(const MapColor* key) const { return mapping.contains(key); } inline const MapColor* MapColorMap::value(const MapColor* key) const { if (mapping.contains(key)) { return mapping.value(key); } else if (key && key->getPriority() < 0) { return key; } else { return nullptr; } } inline const MapColor* MapColorMap::operator[](const MapColor* key) const { return value(key); } inline const MapColor* & MapColorMap::operator[](const MapColor* key) { return mapping[key]; } template QColor colorWithOpacity(T c, float opacity) { auto color = static_cast(c); color.setAlphaF(opacity); return color; } inline QColor colorWithOpacity(const MapColor& c) { return colorWithOpacity(static_cast(c), c.getOpacity()); } } // namespace OpenOrienteering // Allow explicit use of MapColor pointers in QVariant Q_DECLARE_METATYPE(const OpenOrienteering::MapColor*) #endif mapper-0.8.1.1/src/core/map_coord.cpp000066400000000000000000000317671325266516600173630ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2014-2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "map_coord.h" #include #include #include #include #include #include #include #include #include #include "util/xml_stream_util.h" namespace OpenOrienteering { static_assert(sizeof(qint32) <= sizeof(int), "MapCoord::setX/Y uses qRound() returning int, xp/yp is of type qint32"); static_assert(!MapCoord::BoundsOffset().check_for_offset, "Default-constructed BoundsOffset must have check_for_offset == false."); static_assert(MapCoord::BoundsOffset().x == 0, "Default-constructed BoundsOffset must have x == 0."); static_assert(MapCoord::BoundsOffset().y == 0, "Default-constructed BoundsOffset must have y == 0."); static_assert(MapCoord::BoundsOffset().isZero(), "Default-constructed BoundsOffset must be null."); static_assert(std::is_nothrow_move_constructible::value, "MapCoord must be nothrow move constructible."); static_assert(std::is_nothrow_move_assignable::value, "MapCoord must be nothrow move assignable."); static_assert(MapCoordF(1.0, 1.0) == QPointF(1.0, 1.0), "MapCoordF and QPointF constructors must use the same unit of measurement"); static_assert(MapCoord(1, 1) == MapCoord(MapCoordF(1.0, 1.0)), "MapCoord and MapCoordF constructors must use the same unit of measurement"); static_assert(MapCoord(1.0, 1.0) == MapCoord(QPointF(1.0, 1.0)), "MapCoord and QPointF constructors must use the same unit of measurement"); static_assert(MapCoord(1, 1) == MapCoord::fromNative(1000, 1000), "MapCoord::fromRaw must use the native unit of measurement"); static_assert(MapCoord(1, 1) + MapCoord (2, -1) == MapCoord(3, 0), "MapCoord has component-wise operator+"); static_assert(MapCoord(1, 1) - MapCoord (2, -1) == MapCoord(-1, 2), "MapCoord has component-wise operator-"); static_assert(MapCoord(1, 1) * 2.1 == MapCoord(2.1, 2.1), "MapCoord has component-wise operator*(coord, factor)"); static_assert(0.5 * MapCoord(1, 1) == MapCoord(0.5, 0.5), "MapCoord has component-wise operator*(factor, coord)"); static_assert(MapCoord(1, 1) / 4 == MapCoord(0.25, 0.25), "MapCoord has component-wise operator/(coord, divisor"); static_assert(-MapCoord(-2, 1) == MapCoord(2, -1), "MapCoord has unary operator-()"); static_assert(!MapCoord(0.0, 0.0).isCurveStart(), "MapCoord::isCurveStart() must return false by default."); static_assert(MapCoord(0.0, 0.0, MapCoord::Flags(MapCoord::CurveStart)).isCurveStart(), "MapCoord::CurveStart must result in MapCoord::isCurveStart() returning true."); static_assert(!MapCoord(0.0, 0.0).isClosePoint(), "MapCoord::isClosePoint() must return false by default."); static_assert(MapCoord(0.0, 0.0, MapCoord::Flags(MapCoord::ClosePoint)).isClosePoint(), "MapCoord::ClosePoint must result in MapCoord::isClosePoint() returning true."); static_assert(!MapCoord(0.0, 0.0).isHolePoint(), "MapCoord::isHolePoint() must return false by default."); static_assert(MapCoord(0.0, 0.0, MapCoord::Flags(MapCoord::HolePoint)).isHolePoint(), "MapCoord::HolePoint must result in MapCoord::isHolePoint() returning true."); static_assert(!MapCoord(0.0, 0.0).isDashPoint(), "MapCoord::isDashPoint() must return false by default."); static_assert(MapCoord(0.0, 0.0, MapCoord::Flags(MapCoord::DashPoint)).isDashPoint(), "MapCoord::DashPoint must result in MapCoord::isDashPoint() returning true."); static_assert(!MapCoord(0.0, 0.0).isGapPoint(), "MapCoord::isGapPoint() must return false by default."); static_assert(MapCoord(0.0, 0.0, MapCoord::Flags(MapCoord::GapPoint)).isGapPoint(), "MapCoord::GapPoint must result in MapCoord::isGapPoint() returning true."); static_assert(MapCoord(-1.0, 2.0).x() == -1, "MapCoord::x() must return original value (w/o flags)"); static_assert(MapCoord(-1.0, 2.0, MapCoord::Flags{255}).x() == -1.0, "MapCoord::x() must return original value (with flags)"); static_assert(MapCoord::fromNative(-1, 2).nativeX() == -1, "MapCoord::nativeX() must return original value (w/o flags)"); static_assert(MapCoord::fromNative(-1, 2, MapCoord::Flags{255}).nativeX() == -1, "MapCoord::nativeX() must return original value (with flags)"); #ifndef MAPPER_NO_QREAL_CHECK // Check that QPointF/MapCoorF will actually use double. static_assert(std::is_same::value, "qreal is not double. This could work but was not tested."); #endif static_assert(std::is_nothrow_move_constructible::value, "MapCoord must be nothrow move constructible."); static_assert(std::is_nothrow_move_assignable::value, "MapCoord must be nothrow move assignable."); static_assert(QLineF(QPointF(0,0), -MapCoordF(1,0).perpRight()) == QLineF(QPointF(0,0), QPointF(1,0)).normalVector(), "MapCoordF::perpRight() must return a vector in opposite direction of QLineF::normalVector()."); static_assert(QLineF(QPointF(0,0), MapCoordF(1,0).normalVector()) == QLineF(QPointF(0,0), QPointF(1,0)).normalVector(), "MapCoordF::normalVector() must behave like QLineF::normalVector()."); namespace literal { static const QLatin1String x("x"); static const QLatin1String y("y"); static const QLatin1String flags("flags"); } namespace { // Acceptable coord bounds on import, derived from printing UI bounds constexpr qint64 min_coord = -50000000; constexpr qint64 max_coord = +50000000; MapCoord::BoundsOffset bounds_offset; inline void applyBoundsOffset(qint64& x64, qint64& y64) { x64 -= bounds_offset.x; y64 -= bounds_offset.y; } inline void handleBoundsOffset(qint64& x64, qint64& y64) { if (bounds_offset.check_for_offset) { bounds_offset.check_for_offset = false; if (x64 < min_coord || x64 > max_coord) { bounds_offset.x = x64; x64 = 0; } if (y64 < min_coord || y64 > max_coord) { bounds_offset.y = y64; y64 = 0; } } else { applyBoundsOffset(x64, y64); } } inline void ensureBoundsForQint32(qint64 x64, qint64 y64) { if ( x64 < std::numeric_limits::min() || x64 > std::numeric_limits::max() || y64 < std::numeric_limits::min() || y64 > std::numeric_limits::max() ) { throw std::range_error(QT_TRANSLATE_NOOP("OpenOrienteering::MapCoord", "Coordinates are out-of-bounds.")); } } } // namespace MapCoord::BoundsOffset& MapCoord::boundsOffset() { return bounds_offset; } bool MapCoord::isRegular() const { return (xp > 2 * min_coord && xp < 2 * max_coord && yp > 2 * min_coord && yp < 2 * max_coord ); } MapCoord MapCoord::fromNative64(qint64 x64, qint64 y64) { // We only need to check the storage bounds here because // this is the technical invariant. // Printing bounds will be checked during map loading ATM. ensureBoundsForQint32(x64, y64); return MapCoord{ static_cast(x64), static_cast(y64), Flags() }; } MapCoord MapCoord::fromNative64withOffset(qint64 x64, qint64 y64) { applyBoundsOffset(x64, y64); ensureBoundsForQint32(x64, y64); return MapCoord { static_cast(x64), static_cast(y64), Flags() }; } void MapCoord::save(QXmlStreamWriter& xml) const { XmlElementWriter element(xml, XmlStreamLiteral::coord); element.writeAttribute(literal::x, xp); element.writeAttribute(literal::y, yp); if (fp) { element.writeAttribute(literal::flags, Flags::Int(fp)); } } MapCoord MapCoord::load(QXmlStreamReader& xml) { XmlElementReader element(xml); auto x64 = element.attribute(literal::x); auto y64 = element.attribute(literal::y); auto flags = Flags{element.attribute(literal::flags)}; handleBoundsOffset(x64, y64); ensureBoundsForQint32(x64, y64); return MapCoord { static_cast(x64), static_cast(y64), flags }; } MapCoord MapCoord::load(qreal x, qreal y, Flags flags) { auto x64 = qRound64(x * 1000); auto y64 = qRound64(y * 1000); handleBoundsOffset(x64, y64); ensureBoundsForQint32(x64, y64); return MapCoord { static_cast(x64), static_cast(y64), flags }; } MapCoord MapCoord::load(QPointF p, MapCoord::Flags flags) { return MapCoord::load(p.x(), p.y(), flags); } #ifndef NO_NATIVE_FILE_FORMAT MapCoord::MapCoord(const LegacyMapCoord& coord) noexcept : xp{ decltype(xp)(coord.x >> 4) } , yp{ decltype(yp)(coord.y >> 4) } , fp{ Flags::Int((coord.x & 0xf) | ((coord.y & 0xf) << 4)) } { //nothing } #endif QString MapCoord::toString() const { /* The buffer size must allow for * 1x ';': 1 * 2x '-': 2 * 2x ' ': 2 * 2x the decimal digits for values up to 0..2^31: * 20 * 1x the decimal digits for 0..2^8-1: * 3 * Total: 28 */ constexpr std::size_t buf_size = 1+2+2+20+3; static const QChar encoded[10] = { QLatin1Char{'0'}, QLatin1Char{'1'}, QLatin1Char{'2'}, QLatin1Char{'3'}, QLatin1Char{'4'}, QLatin1Char{'5'}, QLatin1Char{'6'}, QLatin1Char{'7'}, QLatin1Char{'8'}, QLatin1Char{'9'} }; QChar buffer[buf_size]; // For efficiency, we construct the string from the back. std::size_t j = buf_size - 1; buffer[j] = QLatin1Char{';'}; --j; auto flags = Flags::Int(fp); if (flags > 0) { do { buffer[j] = encoded[flags % 10]; flags = flags / 10; --j; } while (flags != 0); buffer[j] = QChar::Space; --j; } qint64 tmp = yp; static_assert(sizeof(decltype(tmp)) > sizeof(decltype(MapCoord::yp)), "decltype(tmp) must be large enough to hold" "-std::numeric_limits::min()" ); QChar sign { QChar::Null }; if (tmp < 0) { sign = QLatin1Char{'-'}; tmp = -tmp; } do { buffer[j] = encoded[tmp % 10]; tmp = tmp / 10; --j; } while (tmp != 0); if (!sign.isNull()) { buffer[j] = sign; --j; sign = QChar::Null; } buffer[j] = QChar::Space; --j; static_assert(sizeof(decltype(tmp)) > sizeof(decltype(MapCoord::xp)), "decltype(tmp) must be large enough to hold" "-std::numeric_limits::min()" ); tmp = xp; if (tmp < 0) { sign = QLatin1Char{'-'}; tmp = -tmp; } do { buffer[j] = encoded[tmp % 10]; tmp = tmp / 10; --j; } while (tmp != 0); if (!sign.isNull()) { buffer[j] = sign; --j; } ++j; Q_ASSERT(j < buf_size); j = qMin(j, buf_size); return QString(buffer+j, buf_size-j); } MapCoord::MapCoord(QStringRef& text) : MapCoord{} { const int len = text.length(); if (Q_UNLIKELY(len < 2)) throw std::invalid_argument("Premature end of data"); auto data = text.constData(); int i = 0; qint64 x64 = data[0].unicode(); if (x64 == '-') { x64 = '0' - data[1].unicode(); for (i = 2; i != len; ++i) { auto c = data[i].unicode(); if (c < '0' || c > '9') break; else x64 = 10*x64 + '0' - c; } } else if (x64 >= '0' && x64 <= '9') { x64 -= '0'; for (i = 1; i != len; ++i) { auto c = data[i].unicode(); if (c < '0' || c > '9') break; else x64 = 10*x64 + c - '0'; } } ++i; if (Q_UNLIKELY(i+1 >= len)) throw std::invalid_argument("Premature end of data"); qint64 y64 = data[i].unicode(); if (y64 == '-') { ++i; y64 = '0' - data[i].unicode(); for (++i; i != len; ++i) { auto c = data[i].unicode(); if (c < '0' || c > '9') break; else y64 = 10*y64 + '0' - c; } } else if (y64 >= '0' && y64 <= '9') { y64 -= '0'; for (++i; i != len; ++i) { auto c = data[i].unicode(); if (c < '0' || c > '9') break; else y64 = 10*y64 + c - '0'; } } handleBoundsOffset(x64, y64); ensureBoundsForQint32(x64, y64); xp = static_cast(x64); yp = static_cast(y64); if (i < len && data[i] == QChar::Space) { ++i; if (Q_UNLIKELY(i == len)) throw std::invalid_argument("Premature end of data"); // there are no negative flags fp = Flags(data[i].unicode() - '0'); for (++i; i < len; ++i) { auto c = data[i].unicode(); if (c < '0' || c > '9') break; else fp = Flags(10*int(fp) + c - '0'); } } if (Q_UNLIKELY(i >= len || data[i] != QLatin1Char{';'})) throw std::invalid_argument("Invalid data"); ++i; text = text.mid(i, len-i); } } // namespace OpenOrienteering mapper-0.8.1.1/src/core/map_coord.h000066400000000000000000000735771325266516600170350ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2013-2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_MAP_COORD_H #define OPENORIENTEERING_MAP_COORD_H #include #include #include #include #include #include #include #include class QXmlStreamReader; class QXmlStreamWriter; namespace OpenOrienteering { #ifndef NO_NATIVE_FILE_FORMAT /** * The legacy MapCoord structure, only used for legacy native file format. * * @deprecated */ struct LegacyMapCoord { qint64 x; qint64 y; }; #endif /** * Coordinates of a point in a map, with optional flags. * * This coordinate uses what we call native map coordinates. * One unit in native map coordinates is 1/1000th of a millimeter on the map paper. * * The possible flags are: * *
      *
    • isCurveStart(): If set, this point is the first one in a sequence of * four points which define a cubic bezier curve.
    • *
    • isHolePoint(): If set, this point marks the end of a distinct path. * Only area objects may have more than one path. The first path can be * understood as outline, while the remaining paths can be seen as holes in * the area, subject to the filling rule. *
    • isDashPoint(): This flag marks special points in the path. * The effect depends on the type of symbol. *
    • isClosePoint(): If this flag is set for the last point of a path, * this path is treated as closed. The point's coordinates must be equal to * the first point's coordinates in this path.
    • *
    • isGapPoint(): This flag is used to indicate the begin and the end * of gaps in a path. This only used internally at the moment (for dealing * with dashed lines.) *
    * * These coordinates are implemented as 32 bit integers, * the flags are store separately. */ class MapCoord { Q_DECLARE_TR_FUNCTIONS(OpenOrienteering::MapCoord) public: /** * These flags provide extra information on each coordinate in a path. * * Don't change the values, they are used in import/export. */ enum Flag { CurveStart = 1 << 0, ClosePoint = 1 << 1, GapPoint = 1 << 2, //unused 1 << 3, HolePoint = 1 << 4, DashPoint = 1 << 5, //... }; Q_DECLARE_FLAGS(Flags, Flag) /** * Offset and flag for importing and moving out-of-bounds MapCoords. * * Since reducing MapCoord bits from 60 (64 minus flags) to 32, we need to * deal with old files possibly having out-of-bounds coordinates. * * In addition, we need to deal with files suffering from out-of-print range * coordinates (issue #513). In the print dialog, the printable area is up * to 1000 m of paper [!] wide and high. The top left point is allowed to be * up 1000 m away from the origin in each direction. Every point to be * printed (from the UI) must be within this area. * * The idea for correcting maps during loading (where we may hit the * out-of-bounds coordinates for the first time) is to look at the first * point in the map (the georeferencing reference point or the coordinates * of the first object). If this point is more than 500 m of paper [!] from * the origin, it is moved to the origin, and the same offset is applied to * all other coordinates. After that adjustment, anything up to 1000 m of * paper away from the first point will be in the printable area. * * The current implementation uses a global variable and thus can handle * only one file at the same time. However, the global variables is * initially configured to a neutral value. Any code activating the tracking * of out-of-bounds coordinates it responsible to rollback this neutral * configuration when finished. * * Since symbol definitions are stored in MapCoords, too, extra care needs * to be taken not to adjust coordinates of the symbols' elements. * * \see XMLFileImporter::import */ struct BoundsOffset { qint64 x = 0; qint64 y = 0; bool check_for_offset = false; /** Returns true if both x and y are equal to zero. */ constexpr bool isZero() const; /** Resets x and y to zero, and sets check_for_offset. */ void reset(bool check_for_offset); }; private: /** Benchmark */ friend class CoordXmlTest; qint32 xp; qint32 yp; Flags fp; public: /** Returns the global bounds offset. * * It is returned as a non-const reference, so that it can be used in * QScopedValueRollack. */ static BoundsOffset& boundsOffset(); /** Creates a MapCoord with position at the origin and without any flags set. */ constexpr MapCoord() noexcept; /** Copy constructor. */ constexpr MapCoord(const MapCoord&) noexcept = default; #ifndef NO_NATIVE_FILE_FORMAT /** Creates a MapCoord from the legacy structure. * * @deprecated */ MapCoord(const LegacyMapCoord& coord) noexcept; #endif /** Creates a MapCoord from a position given in millimeters on the map. * * This is a convenience constructor for efficient construction of a point * at the origin i.e. MapCoord(0, 0), or for simple vectors i.e. * MapCoord(1, 0). Intentionally, there is no public version with flags - * you need to use other argument types than int if the compiler complains * about ambiguity. */ constexpr MapCoord(int x, int y) noexcept; private: /** Creates a MapCoord from native map coordinates. * * This is exposed via fromNative() - you must be explicit when you want to * use native units. */ constexpr MapCoord(qint32 x, qint32 y, Flags flags) noexcept; /** Creates a MapCoord from native map coordinates. * * This is exposed via fromNative() - you must be explicit when you want to * use native units. */ constexpr MapCoord(qint32 x, qint32 y, Flag flag) noexcept; // Prevent selection of MapCoord(qreal, qreal, int) MapCoord(qint32, qint32, int) = delete; public: /** Creates a MapCoord from a position given in millimeters on the map. */ constexpr MapCoord(qreal x, qreal y) noexcept; /** Creates a MapCoord with the given flags from a position given in millimeters on the map. */ constexpr MapCoord(qreal x, qreal y, Flags flags) noexcept; /** Creates a MapCoord with the given flags from a position given in millimeters on the map. */ constexpr MapCoord(qreal x, qreal y, Flag flag) noexcept; MapCoord(qreal x, qreal y, int flags) = delete; /** Creates a MapCoord with the position taken from a QPointF. */ explicit constexpr MapCoord(QPointF point) noexcept; /** Creates a MapCoord with the given flags and with the position taken from a QPointF. */ constexpr MapCoord(QPointF point, Flags flags) noexcept; /** Creates a MapCoord with the given flags and with the position taken from a QPointF. */ constexpr MapCoord(QPointF point, Flag flag) noexcept; MapCoord(QPointF point, int flags) = delete; /** Creates a MapCoord from native map coordinates. */ static constexpr MapCoord fromNative(qint32 x, qint32 y) noexcept; /** Creates a MapCoord from native map coordinates. */ static constexpr MapCoord fromNative(qint32 x, qint32 y, Flags flags) noexcept; /** Creates a MapCoord from native map coordinates. */ static constexpr MapCoord fromNative(qint32 x, qint32 y, Flag flag) noexcept; static MapCoord fromNative(qint32 x, qint32 y, int flags) = delete; /** It is illegal to call MapCoord::fromNative with qint64 arguments. * * Use fromNative64 instead. That method tests the values against the bounds of qint32. */ static MapCoord fromNative(qint64 x, qint64 y) = delete; /** Creates a MapCoord from native map coordinates. * * The coordinates are expected to be in the bounds of qint32. * * \todo Raise (and handle exceptions) when out of bounds. */ static MapCoord fromNative64(qint64 x, qint64 y); /** Creates a MapCoord from native map coordinates. * * This will apply the BoundsOffset() and throw a std::range_error if the * adjusted coordinates are out of bounds for qint32. * It does not modify BoundsOffset()! */ static MapCoord fromNative64withOffset(qint64 x, qint64 y); /** Assignment operator */ MapCoord& operator= (const MapCoord& other) = default; /** Returns the coord's x position in millimeters on the map. */ constexpr qreal x() const; /** Returns the coord's y position in millimeters on the map. */ constexpr qreal y() const; /** Sets the coord's x position to a value in millimeters on the map. */ inline void setX(qreal x); /** Sets the coord's y position to a value in millimeters on the map. */ inline void setY(qreal y); /** Returns the coord's x position in native map coords. */ constexpr qint32 nativeX() const; /** Returns the coord's y position in native map coords. */ constexpr qint32 nativeY() const; /** Sets the coord's x position to a value in native map coords. */ inline void setNativeX(qint32 new_x); /** Sets the coord's y position to a value in native map coords. */ inline void setNativeY(qint32 new_y); /** Returns the coord's flags separately, merged into the lowest 8 bits of an int. */ constexpr Flags::Int flags() const noexcept; /** Sets the flags as retrieved by flags(). */ void setFlags(Flags::Int flags) noexcept; /** * Returns true iff the coordinates are within "regular" bounds. */ bool isRegular() const; /** * Returns the length of this coord, seen as a vector from the origin * to the given coordinate, in millimeters on the map. */ qreal length() const; /** * Returns the squared length of this coord, seen as a vector from the origin * to the given coordinate, in millimeters on the map. Faster than length(). */ constexpr qreal lengthSquared() const; /** * Returns the distance from this coord to the other * in millimeters on the map. */ qreal distanceTo(const MapCoord& other) const; /** * Returns the squared distance from this coord to the other * in millimeters on the map. Faster than lengthTo(). */ constexpr qreal distanceSquaredTo(const MapCoord& other) const; /** * Returns if this coord's position is equal to that of the other one. */ constexpr bool isPositionEqualTo(const MapCoord& other) const; /** Is this point the first point of a cubic bezier curve segment? */ constexpr bool isCurveStart() const; /** Sets the curve start flag. */ void setCurveStart(bool value); /** * Is this the last point of a closed path, which is at the same position * as the first point? This is set in addition to isHolePoint(). */ constexpr bool isClosePoint() const; /** Sets the close point flag. */ void setClosePoint(bool value); /** Is this the start of a hole for a line? */ constexpr bool isHolePoint() const; /** Sets the hole point flag. */ void setHolePoint(bool value); /** Is this coordinate a special dash point? */ constexpr bool isDashPoint() const; /** Sets the dash point flag. */ void setDashPoint(bool value); /** Is this coordinate a gap point? */ constexpr bool isGapPoint() const; /** Sets the gap point flag. */ void setGapPoint(bool value); /** Additive inverse. */ constexpr MapCoord operator-() const; /** Component-wise addition. */ MapCoord& operator+= (const MapCoord& rhs_vector); /** Component-wise addition of this and a MapCoordF/QPointF. */ MapCoord& operator+= (const QPointF& rhs_vector); /** Component-wise subtraction. */ MapCoord& operator-= (const MapCoord& rhs_vector); /** Component-wise subtraction of this and a MapCoordF/QPointF. */ MapCoord& operator-= (const QPointF& rhs_vector); /** Multiply with scalar factor. */ MapCoord& operator*= (qreal factor); /** Divide by scalar factor. */ MapCoord& operator/= (qreal scalar); /** Converts the coord's position to a QPointF. */ constexpr explicit operator QPointF() const; /** * Writes raw coordinates and flags to a string. */ QString toString() const; /** * Constructs the MapCoord from the beginning of text, and moves the * reference to behind the this coordinates data. * * This is the counterpiece to toString(). It will throw a * std::invalid_argument if the (beginning of) text does not * contain valid data. * * This constructor will initialize the boundsOffset() if neccessary. * Otherwise it will apply the BoundsOffset() and throw a std::range_error * if the adjusted coordinates are out of bounds for qint32. */ MapCoord(QStringRef& text); /** Saves the MapCoord in xml format to the stream. */ void save(QXmlStreamWriter& xml) const; /** Loads the MapCoord in xml format from the stream. * * This will initialize the boundsOffset() if neccessary. Otherwise it will * apply the BoundsOffset() and throw a std::range_error if the adjusted * coordinates are out of bounds for qint32. */ static MapCoord load(QXmlStreamReader& xml); /** Creates a MapCoord from map coordinates in millimeters, with offset handling. * * This will initialize the boundsOffset() if neccessary. Otherwise it will * apply the BoundsOffset() and throw a std::range_error if the adjusted * coordinates are out of bounds for qint32. */ static MapCoord load(qreal x, qreal y, MapCoord::Flags flags); static MapCoord load(qreal x, qreal y, int flags) = delete; /** Creates a MapCoord from map coordinates in millimeters, with offset handling. * * This will initialize the boundsOffset() if neccessary. Otherwise it will * apply the BoundsOffset() and throw a std::range_error if the adjusted * coordinates are out of bounds for qint32. */ static MapCoord load(QPointF p, MapCoord::Flags flags); static MapCoord load(QPointF p, int flags) = delete; friend constexpr bool operator==(const MapCoord& lhs, const MapCoord& rhs); friend constexpr MapCoord operator+(const MapCoord& lhs, const MapCoord& rhs); friend constexpr MapCoord operator+(const MapCoord& lhs, const QPointF& rhs); friend constexpr MapCoord operator-(const MapCoord& lhs, const MapCoord& rhs); friend constexpr MapCoord operator-(const MapCoord& lhs, const QPointF& rhs); friend constexpr MapCoord operator*(const MapCoord& lhs, qreal factor); friend constexpr MapCoord operator*(qreal factor, const MapCoord& rhs); friend constexpr MapCoord operator/(const MapCoord& lhs, qreal divisor); }; /** Compare MapCoord for equality. */ constexpr bool operator==(const MapCoord& lhs, const MapCoord& rhs); /** Compare MapCoord for inequality. */ constexpr bool operator!=(const MapCoord& lhs, const MapCoord& rhs); /** Component-wise addition of MapCoord. */ constexpr MapCoord operator+(const MapCoord& lhs, const MapCoord& rhs); /** Component-wise addition of MapCoord and MapCoordF/QPointF. */ constexpr MapCoord operator+(const MapCoord& lhs, const QPointF& rhs); /** Component-wise subtraction of MapCoord. */ constexpr MapCoord operator-(const MapCoord& lhs, const MapCoord& rhs); /** Component-wise subtraction of MapCoord and MapCoordF/QPointF. */ constexpr MapCoord operator-(const MapCoord& lhs, const QPointF& rhs); /** Multiply MapCoord with scalar factor. */ constexpr MapCoord operator*(const MapCoord& lhs, qreal factor); /** Multiply MapCoord with scalar factor. */ constexpr MapCoord operator*(qreal factor, const MapCoord& rhs); /** Divide MapCoord by scalar factor. */ constexpr MapCoord operator/(const MapCoord& lhs, qreal divisor); /** * Map coordinates stored as floating point numbers. * * The unit is millimeters on the map paper. * * This type was initially meant as intermediate format for rendering but * is currently used in a wide range of functions related to editing. * In contrast to MapCoord, MapCoordF does not store flags. * * The type is based on QPointF and provides additional methods for using it to * represent 2D vectors. (Some of the methods do have counterparts in QLineF * rather than QPointF.) Similar to QPointF, many operators return const values, * although one might argue that it is no longer good practice in C++11. */ class MapCoordF : public QPointF { public: /** Creates a MapCoordF with both values set to zero. */ constexpr MapCoordF() noexcept; /** Creates a MapCoordF with the given position in map coordinates. */ constexpr MapCoordF(qreal x, qreal y) noexcept; /** Creates a MapCoordF from a MapCoord, dropping its flags. */ explicit constexpr MapCoordF(MapCoord coord) noexcept; /** Copy constructor. */ constexpr MapCoordF(const MapCoordF&) noexcept = default; /** Copy constructor for QPointF prototypes. */ explicit constexpr MapCoordF(const QPointF& point) noexcept; /** Returns a vector with the given length and angle. */ static const MapCoordF fromPolar(qreal length, qreal angle); /** Assignment operator. */ MapCoordF& operator= (const QPointF& point) noexcept; /** * Returns the length of the vector. * * The value returned the from this function is the MapCoords distance to * the origin of the coordinate system. */ qreal length() const; /** * Returns the square of the length of the vector. * * This is a slightly faster alternative to MapCoordF::length() which * preserves comparability. */ constexpr qreal lengthSquared() const; /** * Returns the distance of the coordinate to another coordinate. */ qreal distanceTo(const MapCoordF& to) const; /** * Returns the square of the distance of this coordinate to another coordinate. * * This is a silghtly faster alternative to MapCoordF::distanceTo() which * preserves comparability. */ constexpr qreal distanceSquaredTo(const MapCoordF& to) const; /** * Changes the length of the vector. * * The MapCoordF is interpreted as a vector and adjusted to a vector of the * same direction but having the given lenght. * It does nothing if the vector is very close to (0, 0). */ void setLength(qreal new_length); /** * Normalizes the length of the vector. * * The MapCoordF is interpreted as a vector and adjusted to a vector of the * same direction but length 1 (i.e. unit vector). */ void normalize(); /** * Returns the angle of the vector relative to the vector (1, 0). * * The returned value is in radians, in the range range [-PI; +PI]. * MapCoordF { 0, 1 }.getAngle() returns +PI/2, * MapCoordF { 0, -1 }.getAngle() returns -PI/2. */ qreal angle() const; /** * Rotates the vector. * * The argument is to be given in radians. * Positive arguments result in a counter-clockwise rotation. */ void rotate(qreal angle); /** * Returns a vector which is the result of rotating this vector. * * The argument is to be given in radians. * Positive arguments result in a counter-clockwise rotation. */ const MapCoordF rotated(qreal angle) const; /** * Returns a vector with the same length that is perpendicular to this vector. * * Note that in contrast to normalVector(), this function returns a * perpendicular vector pointing to the right. * * \todo Replace with normalVector(), similar to QLineF API. */ constexpr const MapCoordF perpRight() const; /** * Returns a vector with the same length that is perpendicular to this vector. * * \see QLineF::normalVector() */ constexpr const MapCoordF normalVector() const; /** Additive inverse */ constexpr const MapCoordF operator-() const; /** Component-wise addition */ MapCoordF& operator+= (const MapCoordF& rhs); /** Component-wise subtraction */ MapCoordF& operator-= (const MapCoordF& rhs); /** Multiply with a scalar */ MapCoordF& operator*= (qreal factor); /** Divide by a scalar */ MapCoordF& operator/= (qreal divisor); using QPointF::dotProduct; }; constexpr const MapCoordF operator+(const MapCoordF& lhs, const MapCoordF& rhs); constexpr const MapCoordF operator-(const MapCoordF& lhs, const MapCoordF& rhs); constexpr const MapCoordF operator*(const MapCoordF& lhs, qreal factor); constexpr const MapCoordF operator*(qreal factor, const MapCoordF& rhs); constexpr const MapCoordF operator/(const MapCoordF& lhs, qreal divisor); typedef std::vector MapCoordVector; typedef std::vector MapCoordVectorF; // ### MapCoord inline code ### constexpr bool MapCoord::BoundsOffset::isZero() const { return x==0 && y==0; } inline void MapCoord::BoundsOffset::reset(bool check_for_offset) { this->check_for_offset = check_for_offset; x = 0; y = 0; } constexpr MapCoord::MapCoord() noexcept : xp{ 0 } , yp{ 0 } , fp{ 0 } { // nothing else } constexpr MapCoord::MapCoord(int x, int y) noexcept : xp{ x*1000 } , yp{ y*1000 } , fp{ 0 } { // nothing else } constexpr MapCoord::MapCoord(qint32 x, qint32 y, Flags flags) noexcept : xp{ x } , yp{ y } , fp{ flags } { // nothing else } constexpr MapCoord::MapCoord(qint32 x, qint32 y, Flag flag) noexcept : xp{ x } , yp{ y } , fp{ flag } { // nothing else } constexpr MapCoord::MapCoord(qreal x, qreal y) noexcept : xp{ qRound(x*1000) } , yp{ qRound(y*1000) } , fp{ 0 } { // nothing else } constexpr MapCoord::MapCoord(qreal x, qreal y, Flags flags) noexcept : xp{ qRound(x*1000) } , yp{ qRound(y*1000) } , fp{ flags } { // nothing else } constexpr MapCoord::MapCoord(qreal x, qreal y, Flag flag) noexcept : xp{ qRound(x*1000) } , yp{ qRound(y*1000) } , fp{ flag } { // nothing else } constexpr MapCoord::MapCoord(QPointF point) noexcept : MapCoord { point.x(), point.y() } { // nothing else } constexpr MapCoord::MapCoord(QPointF point, Flags flags) noexcept : MapCoord { point.x(), point.y(), flags } { // nothing else } constexpr MapCoord::MapCoord(QPointF point, Flag flag) noexcept : MapCoord { point.x(), point.y(), flag } { // nothing else } constexpr MapCoord MapCoord::fromNative(qint32 x, qint32 y) noexcept { return MapCoord{ x, y, Flags() }; } constexpr MapCoord MapCoord::fromNative(qint32 x, qint32 y, Flags flags) noexcept { return MapCoord{ x, y, flags }; } constexpr MapCoord MapCoord::fromNative(qint32 x, qint32 y, Flag flag) noexcept { return MapCoord{ x, y, flag }; } constexpr qreal MapCoord::x() const { return nativeX() / 1000.0; } constexpr qreal MapCoord::y() const { return nativeY() / 1000.0; } inline void MapCoord::setX(qreal x) { this->xp = qRound(x * 1000); } inline void MapCoord::setY(qreal y) { this->yp = qRound(y * 1000); } constexpr qint32 MapCoord::nativeX() const { return xp; } constexpr qint32 MapCoord::nativeY() const { return yp; } inline void MapCoord::setNativeX(qint32 new_x) { xp = new_x; } inline void MapCoord::setNativeY(qint32 new_y) { yp = new_y; } constexpr MapCoord::Flags::Int MapCoord::flags() const noexcept { return fp; } inline void MapCoord::setFlags(Flags::Int flags) noexcept { fp = Flags(flags); } inline qreal MapCoord::length() const { return sqrt(lengthSquared()); } constexpr qreal MapCoord::lengthSquared() const { return x()*x() + y()*y(); } inline qreal MapCoord::distanceTo(const MapCoord& other) const { return sqrt(distanceSquaredTo(other)); } constexpr qreal MapCoord::distanceSquaredTo(const MapCoord& other) const { return (*this - other).lengthSquared(); } constexpr bool MapCoord::isPositionEqualTo(const MapCoord& other) const { return (xp == other.xp) && (yp == other.yp); } constexpr bool MapCoord::isCurveStart() const { return fp.testFlag(CurveStart); } inline void MapCoord::setCurveStart(bool value) { if (fp.testFlag(CurveStart) != value) fp ^= CurveStart; } constexpr bool MapCoord::isClosePoint() const { return fp.testFlag(ClosePoint); } inline void MapCoord::setClosePoint(bool value) { if (fp.testFlag(ClosePoint) != value) fp ^= ClosePoint; } constexpr bool MapCoord::isHolePoint() const { return fp.testFlag(HolePoint); } inline void MapCoord::setHolePoint(bool value) { if (fp.testFlag(HolePoint) != value) fp ^= HolePoint; } constexpr bool MapCoord::isDashPoint() const { return fp.testFlag(DashPoint); } inline void MapCoord::setDashPoint(bool value) { if (fp.testFlag(DashPoint) != value) fp ^= DashPoint; } constexpr bool MapCoord::isGapPoint() const { return fp.testFlag(GapPoint); } inline void MapCoord::setGapPoint(bool value) { if (fp.testFlag(GapPoint) != value) fp ^= GapPoint; } constexpr MapCoord MapCoord::operator-() const { return MapCoord { -xp, -yp, fp }; } inline MapCoord& MapCoord::operator+=(const MapCoord& rhs_vector) { xp += rhs_vector.xp; yp += rhs_vector.yp; return *this; } inline MapCoord& MapCoord::operator+=(const QPointF& rhs_vector) { *this += MapCoord{ rhs_vector }; return *this; } inline MapCoord& MapCoord::operator-=(const MapCoord& rhs_vector) { xp -= rhs_vector.xp; yp -= rhs_vector.yp; return *this; } inline MapCoord& MapCoord::operator-=(const QPointF& rhs_vector) { *this -= MapCoord{ rhs_vector }; return *this; } inline MapCoord& MapCoord::operator*=(qreal factor) { xp *= factor; yp *= factor; return *this; } inline MapCoord& MapCoord::operator/=(qreal divisor) { xp /= divisor; yp /= divisor; return *this; } constexpr MapCoord::operator QPointF() const { return QPointF(x(), y()); } constexpr bool operator==(const MapCoord& lhs, const MapCoord& rhs) { return (lhs.xp == rhs.xp) && (lhs.yp == rhs.yp) && (lhs.fp == rhs.fp); } constexpr bool operator!=(const MapCoord& lhs, const MapCoord& rhs) { return !(lhs == rhs); } constexpr MapCoord operator+(const MapCoord& lhs, const MapCoord& rhs) { return MapCoord::fromNative(lhs.xp + rhs.xp, lhs.yp + rhs.yp); } constexpr MapCoord operator+(const MapCoord& lhs, const QPointF& rhs) { return lhs + MapCoord{ rhs }; } constexpr MapCoord operator-(const MapCoord& lhs, const MapCoord& rhs) { return MapCoord::fromNative(lhs.xp - rhs.xp, lhs.yp - rhs.yp); } constexpr MapCoord operator-(const MapCoord& lhs, const QPointF& rhs) { return lhs - MapCoord{ rhs }; } constexpr MapCoord operator*(const MapCoord& lhs, double factor) { return MapCoord::fromNative(qRound(lhs.xp * factor), qRound(lhs.yp * factor)); } constexpr MapCoord operator*(double factor, const MapCoord& rhs) { return MapCoord::fromNative(qRound(factor * rhs.xp), qRound(factor * rhs.yp)); } constexpr MapCoord operator/(const MapCoord& lhs, double divisor) { return MapCoord::fromNative(qRound(lhs.xp / divisor), qRound(lhs.yp / divisor)); } // ### MapCoordF inline code ### constexpr MapCoordF::MapCoordF() noexcept : QPointF {} { // Nothing else } constexpr MapCoordF::MapCoordF(qreal x, qreal y) noexcept : QPointF { x, y } { // Nothing else } constexpr MapCoordF::MapCoordF(MapCoord coord) noexcept : QPointF { coord.x(), coord.y() } { // Nothing else } constexpr MapCoordF::MapCoordF(const QPointF& point) noexcept : QPointF { point } { // Nothing else } // static inline const MapCoordF MapCoordF::fromPolar(qreal length, qreal angle) { return MapCoordF(cos(angle) * length, sin(angle) * length); } inline MapCoordF& MapCoordF::operator=(const QPointF& point) noexcept { return static_cast(*static_cast(this) = point); } inline qreal MapCoordF::length() const { return sqrt(lengthSquared()); } constexpr qreal MapCoordF::lengthSquared() const { return x()*x() + y()*y(); } inline qreal MapCoordF::distanceTo(const MapCoordF& to) const { return sqrt(distanceSquaredTo(to)); } constexpr qreal MapCoordF::distanceSquaredTo(const MapCoordF& to) const { return (to - *this).lengthSquared(); } inline void MapCoordF::setLength(qreal new_length) { auto length_squared = lengthSquared(); if (length_squared > 1e-16) { auto factor = new_length / sqrt(length_squared); rx() *= factor; ry() *= factor; } } inline void MapCoordF::normalize() { setLength(1.0); } inline qreal MapCoordF::angle() const { return atan2(y(), x()); } inline void MapCoordF::rotate(qreal angle) { angle += this->angle(); auto len = length(); rx() = cos(angle) * len; ry() = sin(angle) * len; } inline const MapCoordF MapCoordF::rotated(qreal angle) const { return MapCoordF::fromPolar(length(), angle + this->angle()); } constexpr const MapCoordF MapCoordF::perpRight() const { return MapCoordF { -y(), x() }; } constexpr const MapCoordF MapCoordF::normalVector() const { return MapCoordF { y(), -x() }; } constexpr const MapCoordF MapCoordF::operator-() const { return static_cast(-static_cast(*this)); } inline MapCoordF& MapCoordF::operator+=(const MapCoordF& rhs) { return static_cast(static_cast(*this) += rhs); } inline MapCoordF& MapCoordF::operator-=(const MapCoordF& rhs) { return static_cast(static_cast(*this) -= rhs); } inline MapCoordF& MapCoordF::operator*=(qreal factor) { return static_cast(static_cast(*this) *= factor); } inline MapCoordF& MapCoordF::operator/=(qreal divisor) { return static_cast(static_cast(*this) /= divisor); } constexpr const MapCoordF operator+(const MapCoordF& lhs, const MapCoordF& rhs) { return static_cast(static_cast(lhs) + static_cast(rhs)); } constexpr const MapCoordF operator-(const MapCoordF& lhs, const MapCoordF& rhs) { return static_cast(static_cast(lhs) - static_cast(rhs)); } constexpr const MapCoordF operator*(const MapCoordF& lhs, qreal factor) { return static_cast(static_cast(lhs) * factor); } constexpr const MapCoordF operator*(qreal factor, const MapCoordF& rhs) { return static_cast(factor * static_cast(rhs)); } constexpr const MapCoordF operator/(const MapCoordF& lhs, qreal divisor) { return static_cast(static_cast(lhs) / divisor); } } // namespace OpenOrienteering Q_DECLARE_OPERATORS_FOR_FLAGS(OpenOrienteering::MapCoord::Flags) #endif mapper-0.8.1.1/src/core/map_grid.cpp000066400000000000000000000203421325266516600171650ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2014, 2015 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "map_grid.h" #include #include #include #include "core/georeferencing.h" #include "core/map.h" #include "core/map_coord.h" #include "util/util.h" #include "util/xml_stream_util.h" namespace OpenOrienteering { namespace { struct ProcessLine { QPainter* painter; void processLine(QPointF a, QPointF b); }; void ProcessLine::processLine(QPointF a, QPointF b) { painter->drawLine(a, b); } } // namespace // ### MapGrid ### MapGrid::MapGrid() { snapping_enabled = true; color = qRgba(100, 100, 100, 128); display = AllLines; alignment = MagneticNorth; additional_rotation = 0; unit = MetersInTerrain; horz_spacing = 500; vert_spacing = 500; horz_offset = 0; vert_offset = 0; } #ifndef NO_NATIVE_FILE_FORMAT const MapGrid& MapGrid::load(QIODevice* file, int version) { Q_UNUSED(version); file->read((char*)&snapping_enabled, sizeof(bool)); file->read((char*)&color, sizeof(QRgb)); int temp; file->read((char*)&temp, sizeof(int)); display = (DisplayMode)temp; file->read((char*)&temp, sizeof(int)); alignment = (Alignment)temp; file->read((char*)&additional_rotation, sizeof(double)); file->read((char*)&temp, sizeof(int)); unit = (Unit)temp; file->read((char*)&horz_spacing, sizeof(double)); file->read((char*)&vert_spacing, sizeof(double)); file->read((char*)&horz_offset, sizeof(double)); file->read((char*)&vert_offset, sizeof(double)); return *this; } #endif void MapGrid::save(QXmlStreamWriter& xml) const { XmlElementWriter element{xml, QLatin1String("grid")}; const auto name_format = qAlpha(color) == 0xff ? QColor::HexRgb : QColor::HexArgb; element.writeAttribute(QLatin1String("color"), QColor::fromRgba(color).name(name_format)); element.writeAttribute(QLatin1String("display"), display); element.writeAttribute(QLatin1String("alignment"), alignment); element.writeAttribute(QLatin1String("additional_rotation"), additional_rotation); element.writeAttribute(QLatin1String("unit"), unit); element.writeAttribute(QLatin1String("h_spacing"), horz_spacing); element.writeAttribute(QLatin1String("v_spacing"), vert_spacing); element.writeAttribute(QLatin1String("h_offset"), horz_offset); element.writeAttribute(QLatin1String("v_offset"), vert_offset); element.writeAttribute(QLatin1String("snapping_enabled"), snapping_enabled); } const MapGrid& MapGrid::load(QXmlStreamReader& xml) { Q_ASSERT(xml.name() == QLatin1String("grid")); XmlElementReader element(xml); QXmlStreamAttributes attributes = xml.attributes(); color = QColor(element.attribute(QLatin1String("color"))).rgba(); display = MapGrid::DisplayMode(element.attribute(QLatin1String("display"))); alignment = MapGrid::Alignment(element.attribute(QLatin1String("alignment"))); additional_rotation = element.attribute(QLatin1String("additional_rotation")); unit = MapGrid::Unit(element.attribute(QLatin1String("unit"))); horz_spacing = element.attribute(QLatin1String("h_spacing")); vert_spacing = element.attribute(QLatin1String("v_spacing")); horz_offset = element.attribute(QLatin1String("h_offset")); vert_offset = element.attribute(QLatin1String("v_offset")); snapping_enabled = element.attribute(QLatin1String("snapping_enabled")); return *this; } void MapGrid::draw(QPainter* painter, const QRectF& bounding_box, Map* map, bool on_screen) const { double final_horz_spacing, final_vert_spacing; double final_horz_offset, final_vert_offset; double final_rotation; calculateFinalParameters(final_horz_spacing, final_vert_spacing, final_horz_offset, final_vert_offset, final_rotation, map); QPen pen(color); if (on_screen) { // zero-width cosmetic pen (effectively one pixel) pen.setWidth(0); pen.setCosmetic(true); } else { // 0.1 mm wide non-cosmetic pen pen.setWidthF(0.1f); } painter->setPen(pen); painter->setBrush(Qt::NoBrush); painter->setOpacity(qAlpha(color) / 255.0); ProcessLine process_line; process_line.painter = painter; if (display == AllLines) Util::gridOperation(bounding_box, final_horz_spacing, final_vert_spacing, final_horz_offset, final_vert_offset, final_rotation, process_line); else if (display == HorizontalLines) Util::hatchingOperation(bounding_box, final_vert_spacing, final_vert_offset, final_rotation + M_PI / 2, process_line); else // if (display == VeritcalLines) Util::hatchingOperation(bounding_box, final_horz_spacing, final_horz_offset, final_rotation, process_line); } void MapGrid::calculateFinalParameters(double& final_horz_spacing, double& final_vert_spacing, double& final_horz_offset, double& final_vert_offset, double& final_rotation, Map* map) const { double factor = ((unit == MetersInTerrain) ? (1000.0 / map->getScaleDenominator()) : 1); final_horz_spacing = factor * horz_spacing; final_vert_spacing = factor * vert_spacing; final_horz_offset = factor * horz_offset; final_vert_offset = factor * vert_offset; final_rotation = additional_rotation + M_PI / 2; const Georeferencing& georeferencing = map->getGeoreferencing(); if (alignment == GridNorth) { final_rotation += georeferencing.getGrivation() * M_PI / 180; // Shift origin to projected coordinates origin double prev_horz_offset = MapCoordF::dotProduct(MapCoordF(0, -1).rotated(final_rotation), MapCoordF(georeferencing.getMapRefPoint().x(), -1 * georeferencing.getMapRefPoint().y())); double target_horz_offset = MapCoordF::dotProduct(MapCoordF(0, -1).rotated(additional_rotation + M_PI / 2), MapCoordF(georeferencing.getProjectedRefPoint().x(), georeferencing.getProjectedRefPoint().y())); final_horz_offset -= factor * target_horz_offset - prev_horz_offset; double prev_vert_offset = MapCoordF::dotProduct(MapCoordF(1, 0).rotated(final_rotation), MapCoordF(georeferencing.getMapRefPoint().x(), -1 * georeferencing.getMapRefPoint().y())); double target_vert_offset = MapCoordF::dotProduct(MapCoordF(1, 0).rotated(additional_rotation + M_PI / 2), MapCoordF(georeferencing.getProjectedRefPoint().x(), georeferencing.getProjectedRefPoint().y())); final_vert_offset += factor * target_vert_offset - prev_vert_offset; } else if (alignment == TrueNorth) final_rotation += georeferencing.getDeclination() * M_PI / 180; } MapCoordF MapGrid::getClosestPointOnGrid(MapCoordF position, Map* map) const { double final_horz_spacing, final_vert_spacing; double final_horz_offset, final_vert_offset; double final_rotation; calculateFinalParameters(final_horz_spacing, final_vert_spacing, final_horz_offset, final_vert_offset, final_rotation, map); position.rotate(final_rotation - M_PI / 2); return MapCoordF(qRound((position.x() - final_horz_offset) / final_horz_spacing) * final_horz_spacing + final_horz_offset, qRound((position.y() - final_vert_offset) / final_vert_spacing) * final_vert_spacing + final_vert_offset).rotated(-1 * (final_rotation - M_PI / 2)); } bool operator==(const MapGrid& lhs, const MapGrid& rhs) { return lhs.snapping_enabled == rhs.snapping_enabled && lhs.color == rhs.color && lhs.display == rhs.display && lhs.alignment == rhs.alignment && lhs.additional_rotation == rhs.additional_rotation && lhs.unit == rhs.unit && lhs.horz_spacing == rhs.horz_spacing && lhs.vert_spacing == rhs.vert_spacing && lhs.horz_offset == rhs.horz_offset && lhs.vert_offset == rhs.vert_offset; } bool operator!=(const MapGrid& lhs, const MapGrid& rhs) { return !(lhs == rhs); } } // namespace mapper-0.8.1.1/src/core/map_grid.h000066400000000000000000000117721325266516600166410ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2014 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_MAP_GRID_H #define OPENORIENTEERING_MAP_GRID_H #include class QIODevice; class QPainter; class QRectF; class QXmlStreamReader; class QXmlStreamWriter; namespace OpenOrienteering { class Map; class MapCoordF; /** * Class for displaying a grid on a map. * * Each map has an instance of this class which can be retrieved with map->getGrid(). * The grid's visibility is defined per MapView. */ class MapGrid { public: /** Options for aligning the grid with different north concepts. */ enum Alignment { MagneticNorth = 0, GridNorth = 1, TrueNorth = 2 }; /** Different units for specifying the grid interval. */ enum Unit { MillimetersOnMap = 0, MetersInTerrain = 1 }; /** Different display modes for map grids. */ enum DisplayMode { AllLines = 0, HorizontalLines = 1, VerticalLines = 2 }; /** Creates a new map grid with default settings. */ MapGrid(); /** Loads the grid in the old "native" format from the given file. */ const MapGrid& load(QIODevice* file, int version); /** Saves the grid in xml format to the given stream. */ void save(QXmlStreamWriter& xml) const; /** Loads the grid in xml format from the given stream. */ const MapGrid& load(QXmlStreamReader& xml); /** * Draws the map grid. * * @param painter The QPainter used for drawing. * @param bounding_box Bounding box of the area to draw the grid for, in * map coordinates. * @param map Map to draw the grid for. * @param on_screen If true, uses a cosmetic pen (one pixel wide), * otherwise uses a 0.1 mm wide pen. */ void draw(QPainter* painter, const QRectF& bounding_box, Map* map, bool on_screen) const; /** * Calculates the "final" parameters with the following properties: * - spacings and offsets are in millimeters on the map * - rotation is relative to the vector (1, 0) and counterclockwise */ void calculateFinalParameters( double& final_horz_spacing, double& final_vert_spacing, double& final_horz_offset, double& final_vert_offset, double& final_rotation, Map* map ) const; /** Returns the grid point which is closest to the given position. */ MapCoordF getClosestPointOnGrid(MapCoordF position, Map* map) const; // Getters / Setters inline bool isSnappingEnabled() const {return snapping_enabled;} inline void setSnappingEnabled(bool enable) {snapping_enabled = enable;} inline QRgb getColor() const {return color;} inline void setColor(QRgb color) {this->color = color;} inline DisplayMode getDisplayMode() const {return display;} inline void setDisplayMode(DisplayMode mode) {display = mode;} inline Alignment getAlignment() const {return alignment;} inline void setAlignment(Alignment alignment) {this->alignment = alignment;} inline double getAdditionalRotation() const {return additional_rotation;} inline void setAdditionalRotation(double rotation) {additional_rotation = rotation;} inline Unit getUnit() const {return unit;} inline void setUnit(Unit unit) {this->unit = unit;} inline double getHorizontalSpacing() const {return horz_spacing;} inline void setHorizontalSpacing(double spacing) {horz_spacing = spacing;} inline double getVerticalSpacing() const {return vert_spacing;} inline void setVerticalSpacing(double spacing) {vert_spacing = spacing;} inline double getHorizontalOffset() const {return horz_offset;} inline void setHorizontalOffset(double offset) {horz_offset = offset;} inline double getVerticalOffset() const {return vert_offset;} inline void setVerticalOffset(double offset) {vert_offset = offset;} private: bool snapping_enabled; QRgb color; DisplayMode display; Alignment alignment; double additional_rotation; Unit unit; double horz_spacing; double vert_spacing; double horz_offset; double vert_offset; friend bool operator==(const MapGrid& lhs, const MapGrid& rhs); }; /** * Compares two map grid objects. * * @return true if the objects are equal, false otherwise */ bool operator==(const MapGrid& lhs, const MapGrid& rhs); /** * Compares two map grid objects for inequality. * * @return true if the objects are not equal, false otherwise */ bool operator!=(const MapGrid& lhs, const MapGrid& rhs); } // namespace OpenOrienteering #endif mapper-0.8.1.1/src/core/map_part.cpp000066400000000000000000000220311325266516600172030ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2012-2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "map_part.h" #include #include #include #include #include #include #include #include #include #include "core/map.h" #include "core/map_coord.h" #include "core/objects/object.h" #include "core/symbols/symbol.h" #include "undo/object_undo.h" #include "util/util.h" #include "util/xml_stream_util.h" namespace literal { const QLatin1String part("part"); const QLatin1String name("name"); const QLatin1String objects("objects"); const QLatin1String object("object"); const QLatin1String count("count"); } namespace OpenOrienteering { MapPart::MapPart(const QString& name, Map* map) : name(name) , map(map) { Q_ASSERT(map); ; // nothing else } MapPart::~MapPart() { for (Object* object : objects) delete object; } void MapPart::setName(const QString& new_name) { name = new_name; if (map) emit map->mapPartChanged(map->findPartIndex(this), this); } #ifndef NO_NATIVE_FILE_FORMAT bool MapPart::load(QIODevice* file, int version, Map* map) { loadString(file, name); int size; file->read((char*)&size, sizeof(int)); objects.resize(size, nullptr); for (Object*& object : objects) { int save_type; file->read((char*)&save_type, sizeof(int)); object = Object::getObjectForType(static_cast(save_type), nullptr); if (!object) return false; object->load(file, version, map); } return true; } #endif void MapPart::save(QXmlStreamWriter& xml) const { XmlElementWriter part_element(xml, literal::part); part_element.writeAttribute(literal::name, name); { XmlElementWriter objects_element(xml, literal::objects); objects_element.writeAttribute(literal::count, objects.size()); for (const Object* object : objects) { writeLineBreak(xml); object->save(xml); } writeLineBreak(xml); } } MapPart* MapPart::load(QXmlStreamReader& xml, Map& map, SymbolDictionary& symbol_dict) { Q_ASSERT(xml.name() == literal::part); XmlElementReader part_element(xml); auto part = new MapPart(part_element.attribute(literal::name), &map); while (xml.readNextStartElement()) { if (xml.name() == literal::objects) { XmlElementReader objects_element(xml); std::size_t num_objects = objects_element.attribute(literal::count); if (num_objects > 0) part->objects.reserve(qMin(num_objects, std::size_t(20000))); // 20000 is not a limit while (xml.readNextStartElement()) { if (xml.name() == literal::object) part->objects.push_back(Object::load(xml, &map, symbol_dict)); else xml.skipCurrentElement(); // unknown } } else xml.skipCurrentElement(); // unknown } return part; } int MapPart::findObjectIndex(const Object* object) const { int size = objects.size(); for (int i = size - 1; i >= 0; --i) { if (objects[i] == object) return i; } Q_ASSERT(false); return -1; } void MapPart::setObject(Object* object, int pos, bool delete_old) { map->removeRenderablesOfObject(objects[pos], true); if (delete_old) delete objects[pos]; objects[pos] = object; object->setMap(map); object->update(); map->setObjectsDirty(); // TODO: remove from here, dirty state handling should be separate } void MapPart::addObject(Object* object) { addObject(object, objects.size()); } void MapPart::addObject(Object* object, int pos) { objects.insert(objects.begin() + pos, object); object->setMap(map); object->update(); if (objects.size() == 1 && map->getNumObjects() == 1) map->updateAllMapWidgets(); } void MapPart::deleteObject(int pos, bool remove_only) { map->removeRenderablesOfObject(objects[pos], true); if (remove_only) objects[pos]->setMap(nullptr); else delete objects[pos]; objects.erase(objects.begin() + pos); if (objects.empty() && map->getNumObjects() == 0) map->updateAllMapWidgets(); } bool MapPart::deleteObject(Object* object, bool remove_only) { int size = objects.size(); for (int i = size - 1; i >= 0; --i) { if (objects[i] == object) { deleteObject(i, remove_only); return true; } } return false; } void MapPart::importPart(const MapPart* other, const QHash& symbol_map, const QTransform& transform, bool select_new_objects) { if (other->getNumObjects() == 0) return; bool first_objects = map->getNumObjects() == 0; auto undo_step = new DeleteObjectsUndoStep(map); if (select_new_objects) map->clearObjectSelection(false); objects.reserve(objects.size() + other->objects.size()); for (const Object* object: other->objects) { Object* new_object = object->duplicate(); if (symbol_map.contains(new_object->getSymbol())) new_object->setSymbol(symbol_map.value(new_object->getSymbol()), true); new_object->transform(transform); objects.push_back(new_object); new_object->setMap(map); new_object->update(); undo_step->addObject((int)objects.size() - 1); if (select_new_objects) map->addObjectToSelection(new_object, false); } map->push(undo_step); map->setObjectsDirty(); if (select_new_objects) { map->emitSelectionChanged(); map->emitSelectionEdited(); // TODO: is this necessary here? // Not as long as observers listen to both... } if (first_objects) map->updateAllMapWidgets(); } void MapPart::findObjectsAt( MapCoordF coord, float tolerance, bool treat_areas_as_paths, bool extended_selection, bool include_hidden_objects, bool include_protected_objects, SelectionInfoVector& out ) const { for (Object* object : objects) { if (!include_hidden_objects && object->getSymbol()->isHidden()) continue; if (!include_protected_objects && object->getSymbol()->isProtected()) continue; object->update(); int selected_type = object->isPointOnObject(coord, tolerance, treat_areas_as_paths, extended_selection); if (selected_type != (int)Symbol::NoSymbol) out.emplace_back(selected_type, object); } } void MapPart::findObjectsAtBox( MapCoordF corner1, MapCoordF corner2, bool include_hidden_objects, bool include_protected_objects, std::vector< Object* >& out ) const { auto rect = QRectF(corner1, corner2).normalized(); for (Object* object : objects) { if (!include_hidden_objects && object->getSymbol()->isHidden()) continue; if (!include_protected_objects && object->getSymbol()->isProtected()) continue; object->update(); if (rect.intersects(object->getExtent()) && object->intersectsBox(rect)) out.push_back(object); } } int MapPart::countObjectsInRect(const QRectF& map_coord_rect, bool include_hidden_objects) const { int count = 0; for (const Object* object : objects) { if (object->getSymbol()->isHidden() && !include_hidden_objects) continue; object->update(); if (object->getExtent().intersects(map_coord_rect)) ++count; } return count; } QRectF MapPart::calculateExtent(bool include_helper_symbols) const { QRectF rect; for (const auto object : objects) { if (!object->getSymbol()->isHidden() && (include_helper_symbols || !object->getSymbol()->isHelperSymbol()) ) { object->update(); rectIncludeSafe(rect, object->getExtent()); } } return rect; } bool MapPart::existsObject(const std::function& condition) const { return std::any_of(begin(objects), end(objects), condition); } void MapPart::applyOnMatchingObjects(const std::function& operation, const std::function& condition) { std::for_each(objects.rbegin(), objects.rend(), [&operation, &condition](auto object) { if (condition(object)) operation(object); }); } void MapPart::applyOnMatchingObjects(const std::function& operation, const std::function& condition) { for (auto i = objects.size(); i > 0; ) { --i; Object* const object = objects[i]; if (condition(object)) operation(object, this, int(i)); } } void MapPart::applyOnAllObjects(const std::function& operation) { std::for_each(objects.rbegin(), objects.rend(), operation); } void MapPart::applyOnAllObjects(const std::function& operation) { for (auto i = objects.size(); i > 0; ) { --i; operation(objects[i], this, int(i)); } } } // namespace OpenOrienteering mapper-0.8.1.1/src/core/map_part.h000066400000000000000000000161531325266516600166600ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2012-2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_MAP_PART_H #define OPENORIENTEERING_MAP_PART_H #include #include #include #include #include #include #include class QIODevice; class QTransform; class QXmlStreamReader; class QXmlStreamWriter; // IWYU pragma: no_forward_declare QRectF namespace OpenOrienteering { class Map; class MapCoordF; class Object; class Symbol; using SymbolDictionary = QHash; // from symbol.h using SelectionInfoVector = std::vector> ; /** * Represents a part of a map by owning a list of map objects. * * Dividing maps in parts is e.g. useful to have multiple mappers work on a map: * every mapper can do the work in his/her part without getting into conflict * with other parts. For display, the objects from all parts are merged. * * Another application is in course setting, where it is useful to have * a map part for event-specific map objects and parts for course-specific * map objects. Then a course can be printed by merging the event-specific part * with the part for the course. * * Currently, only one map part can be used per map. */ class MapPart { friend class OCAD8FileImport; public: /** * Creates a new map part with the given name for a map. */ MapPart(const QString& name, Map* map); MapPart(const MapPart&) = delete; /** * Destroys the map part. */ ~MapPart(); MapPart& operator=(const MapPart&) = delete; /** * Loads the map part in the old "native" format from the given file. */ bool load(QIODevice* file, int version, Map* map); /** * Saves the map part in xml format to the given stream. */ void save(QXmlStreamWriter& xml) const; /** * Loads the map part in xml format from the given stream. * * Needs a dictionary to map symbol ids to symbol pointers. */ static MapPart* load(QXmlStreamReader& xml, Map& map, SymbolDictionary& symbol_dict); /** * Returns the part's name. */ const QString& getName() const; /** * Sets the part's name. */ void setName(const QString& new_name); /** * Returns the number of objects in the part. */ int getNumObjects() const; /** * Returns the i-th object from the part. */ const Object* getObject(int i) const; /** * Returns the i-th object from the part. */ Object* getObject(int i); /** * Returns the index of the object. * * Loops over all objects in the part and looks for the given pointer. * The object must be contained in this part, * otherwise an assert is triggered (in debug builds), * or -1 is returned (release builds). */ int findObjectIndex(const Object* object) const; /** * Replaces the object at the given index with another. * * If delete_old is set, calls "delete old_object". */ void setObject(Object* object, int pos, bool delete_old); /** * Adds the object as new object at the end. */ void addObject(Object* object); /** * Adds the object as new object at the given index. */ void addObject(Object* object, int pos); /** * Deleted the object from the given index. * * If remove_only is set, does not call "delete object". * * @todo Make a separate method "removeObject()", this is misleading! */ void deleteObject(int pos, bool remove_only); /** * Deleted the object from the given index. * * If remove_only is set, does not call "delete object". * Returns if the object was found in this part. * * @todo Make a separate method "removeObject()", this is misleading! */ bool deleteObject(Object* object, bool remove_only); /** * Imports the contents another part into this part. * * The other part can be from another map. * Uses symbol_map to replace all symbols contained there. * No replacement is done for symbols which are not in the symbol_map. */ void importPart(const MapPart* other, const QHash& symbol_map, const QTransform& transform, bool select_new_objects); /** * @see Map::findObjectsAt(). */ void findObjectsAt(MapCoordF coord, float tolerance, bool treat_areas_as_paths, bool extended_selection, bool include_hidden_objects, bool include_protected_objects, SelectionInfoVector& out) const; /** * @see Map::findObjectsAtBox(). */ void findObjectsAtBox(MapCoordF corner1, MapCoordF corner2, bool include_hidden_objects, bool include_protected_objects, std::vector& out) const; /** * @see Map::countObjectsInRect(). */ int countObjectsInRect(const QRectF& map_coord_rect, bool include_hidden_objects) const; /** * Calculates and returns the bounding box of all objects in this map part. */ QRectF calculateExtent(bool include_helper_symbols) const; /** * Applies a condition on all objects (until the first match is found). * * @return True if there is an object matching the condition, false otherwise. */ bool existsObject(const std::function& condition) const; /** * @copybrief Map::applyOnMatchingObjects() * @copydetails Map::applyOnMatchingObjects() */ void applyOnMatchingObjects(const std::function& operation, const std::function& condition); /** * @copybrief Map::applyOnMatchingObjects() * @copydetails Map::applyOnMatchingObjects() */ void applyOnMatchingObjects(const std::function& operation, const std::function& condition); /** * @copybrief Map::applyOnAllObjects() * @copydetails Map::applyOnAllObjects() */ void applyOnAllObjects(const std::function& operation); /** * @copybrief Map::applyOnAllObjects() * @copydetails Map::applyOnAllObjects() */ void applyOnAllObjects(const std::function& operation); private: typedef std::vector ObjectList; QString name; ObjectList objects; ///< @todo This could be a spatial representation optimized for quick access Map* const map; }; // ## MapPart inline and template code ### inline const QString& MapPart::getName() const { return name; } inline int MapPart::getNumObjects() const { return int(objects.size()); } inline Object* MapPart::getObject(int i) { return objects[std::size_t(i)]; } inline const Object* MapPart::getObject(int i) const { return objects[std::size_t(i)]; } } // namespace OpenOrienteering #endif mapper-0.8.1.1/src/core/map_printer.cpp000066400000000000000000001150141325266516600177240ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2012-2016 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "map_printer.h" #include #include #include #include #include #include #include #include #include #include #include // IWYU pragma: keep #include #include #include #include #include #if defined(QT_PRINTSUPPORT_LIB) # include # include # include # if defined(Q_OS_WIN) # include # endif #endif #include "core/georeferencing.h" #include "core/map.h" #include "core/map_color.h" #include "core/map_view.h" #include "core/renderables/renderable.h" #include "templates/template.h" #include "util/xml_stream_util.h" // ### A namespace which collects various string constants of type QLatin1String. ### namespace literal { static const QLatin1String scale("scale"); static const QLatin1String resolution("resolution"); static const QLatin1String templates_visible("templates_visible"); static const QLatin1String grid_visible("grid_visible"); static const QLatin1String simulate_overprinting("simulate_overprinting"); static const QLatin1String mode("mode"); static const QLatin1String vector("vector"); static const QLatin1String raster("raster"); static const QLatin1String separations("separations"); static const QLatin1String color_mode("color_mode"); static const QLatin1String default_color_mode("default"); static const QLatin1String device_cmyk("DeviceCMYK"); static const QLatin1String page_format("page_format"); static const QLatin1String paper_size("paper_size"); static const QLatin1String orientation("orientation"); static const QLatin1String portrait("portrait"); static const QLatin1String landscape("landscape"); static const QLatin1String h_overlap("h_overlap"); static const QLatin1String v_overlap("v_overlap"); static const QLatin1String dimensions("dimensions"); static const QLatin1String page_rect("page_rect"); static const QLatin1String print_area("print_area"); static const QLatin1String center_area("center_area"); static const QLatin1String single_page("single_page"); } namespace OpenOrienteering { // #### MapPrinterPageFormat ### MapPrinterPageFormat::MapPrinterPageFormat(QSizeF page_rect_size, qreal margin, qreal overlap) : #ifdef QT_PRINTSUPPORT_LIB paper_size(QPrinter::Custom) #else paper_size(-1) #endif , orientation(Portrait) , page_rect(QRectF(QPointF(margin, margin), page_rect_size)) , paper_dimensions(page_rect.size() + 2.0* QSizeF(margin, margin)) , h_overlap(overlap) , v_overlap(overlap) { // nothing } #ifdef QT_PRINTSUPPORT_LIB MapPrinterPageFormat::MapPrinterPageFormat(const QPrinter& printer, qreal overlap) : paper_size(printer.paperSize()) , orientation((printer.orientation() == QPrinter::Portrait) ? MapPrinterPageFormat::Portrait : MapPrinterPageFormat::Landscape) , page_rect(printer.pageRect(QPrinter::Millimeter)) , paper_dimensions(printer.paperSize(QPrinter::Millimeter)) , h_overlap(overlap) , v_overlap(overlap) { // nothing } #endif MapPrinterPageFormat MapPrinterPageFormat::fromDefaultPrinter() { #ifdef QT_PRINTSUPPORT_LIB QPrinter default_printer; return MapPrinterPageFormat(default_printer); #else return MapPrinterPageFormat(); #endif } bool operator==(const MapPrinterPageFormat& lhs, const MapPrinterPageFormat& rhs) { return lhs.paper_size == rhs.paper_size && lhs.orientation == rhs.orientation && fabs(lhs.h_overlap - rhs.h_overlap) < 0.05 && fabs(lhs.v_overlap - rhs.v_overlap) < 0.05 && fabs(lhs.page_rect.top() - rhs.page_rect.top()) < 0.05 && fabs(lhs.page_rect.left() - rhs.page_rect.left()) < 0.05 && fabs(lhs.page_rect.right() - rhs.page_rect.right()) < 0.05 && fabs(lhs.page_rect.bottom() - rhs.page_rect.bottom()) < 0.05 && fabs(lhs.paper_dimensions.width() - rhs.paper_dimensions.width()) < 0.05 && fabs(lhs.paper_dimensions.height() - rhs.paper_dimensions.height()) < 0.05; } // ### MapPrinterOptions ### MapPrinterOptions::MapPrinterOptions(unsigned int scale, int resolution, MapPrinterMode mode) : scale(scale), resolution(resolution), mode(mode), color_mode(DefaultColorMode), show_templates(false), show_grid(false), simulate_overprinting(false) { // nothing } // ### MapPrinterConfig ### MapPrinterConfig::MapPrinterConfig(const Map& map) : printer_name(QString::fromLatin1("DEFAULT")), print_area(map.calculateExtent()), page_format(MapPrinterPageFormat::fromDefaultPrinter()), options(map.getScaleDenominator()), center_print_area(false), single_page_print_area(false) { if (print_area.isEmpty()) print_area = page_format.page_rect; } MapPrinterConfig::MapPrinterConfig(const Map& map, QXmlStreamReader& xml) : printer_name(QString::fromLatin1("DEFAULT")), print_area(0.0, 0.0, 100.0, 100.0), // Avoid expensive calculation before loading. page_format(), options(map.getScaleDenominator()), center_print_area(false), single_page_print_area(false) { XmlElementReader printer_config_element(xml); options.scale = printer_config_element.attribute(literal::scale); options.resolution = printer_config_element.attribute(literal::resolution); options.show_templates = printer_config_element.attribute(literal::templates_visible); options.show_grid = printer_config_element.attribute(literal::grid_visible); options.simulate_overprinting = printer_config_element.attribute(literal::simulate_overprinting); QStringRef mode = printer_config_element.attribute(literal::mode); if (!mode.isEmpty()) { if (mode == literal::vector) options.mode = MapPrinterOptions::Vector; else if (mode == literal::raster) options.mode = MapPrinterOptions::Raster; else if (mode == literal::separations) options.mode = MapPrinterOptions::Separations; else qDebug() << "Unsupported map printing mode:" << mode; } QStringRef color_mode = printer_config_element.attribute(literal::color_mode); if (!color_mode.isEmpty()) { if (color_mode == literal::default_color_mode) options.color_mode = MapPrinterOptions::DefaultColorMode; else if (color_mode == literal::device_cmyk) options.color_mode = MapPrinterOptions::DeviceCmyk; else qDebug() << "Unsupported map color mode:" << color_mode; } while (xml.readNextStartElement()) { if (xml.name() == literal::page_format) { XmlElementReader page_format_element(xml); QString value; #ifdef QT_PRINTSUPPORT_LIB value = page_format_element.attribute(literal::paper_size); const QHash< int, const char* >& paper_size_names = MapPrinter::paperSizeNames(); for (int i = 0; i < paper_size_names.count(); ++i) { if (value == QLatin1String(paper_size_names[i])) page_format.paper_size = i; } #endif value = page_format_element.attribute(literal::orientation); page_format.orientation = (value == literal::portrait) ? MapPrinterPageFormat::Portrait : MapPrinterPageFormat::Landscape; if (page_format_element.hasAttribute(literal::h_overlap)) page_format.h_overlap = page_format_element.attribute(literal::h_overlap); if (page_format_element.hasAttribute(literal::v_overlap)) page_format.v_overlap = page_format_element.attribute(literal::v_overlap); while (xml.readNextStartElement()) { if (xml.name() == literal::dimensions) { XmlElementReader(xml).read(page_format.paper_dimensions); } else if (xml.name() == literal::page_rect) { XmlElementReader(xml).read(page_format.page_rect); } else xml.skipCurrentElement(); } } else if (xml.name() == literal::print_area) { XmlElementReader print_area_element(xml); print_area_element.read(print_area); center_print_area = print_area_element.attribute(literal::center_area); single_page_print_area= print_area_element.attribute(literal::single_page); } else xml.skipCurrentElement(); } // Sanity checks if (options.scale <= 0) options.scale = map.getScaleDenominator(); if (options.resolution <= 0) options.resolution = 600; } void MapPrinterConfig::save(QXmlStreamWriter& xml, const QLatin1String& element_name) const { XmlElementWriter printer_config_element(xml, element_name); printer_config_element.writeAttribute(literal::scale, options.scale); printer_config_element.writeAttribute(literal::resolution, options.resolution); printer_config_element.writeAttribute(literal::templates_visible, options.show_templates); printer_config_element.writeAttribute(literal::grid_visible, options.show_grid); printer_config_element.writeAttribute(literal::simulate_overprinting, options.simulate_overprinting); switch (options.mode) { case MapPrinterOptions::Vector: printer_config_element.writeAttribute(literal::mode, literal::vector); break; case MapPrinterOptions::Raster: printer_config_element.writeAttribute(literal::mode, literal::raster); break; case MapPrinterOptions::Separations: printer_config_element.writeAttribute(literal::mode, literal::separations); break; default: // Do not fail on saving qDebug() << "Unsupported map printing mode:" << options.mode; } switch (options.color_mode) { case MapPrinterOptions::DefaultColorMode: // No need to write an attribute for default mode. // printer_config_element.writeAttribute(literal::color_mode, literal::default_color_mode); break; case MapPrinterOptions::DeviceCmyk: printer_config_element.writeAttribute(literal::color_mode, literal::device_cmyk); break; default: // Do not fail on saving qDebug() << "Unsupported map color mode:" << options.color_mode; } { XmlElementWriter page_format_element(xml, literal::page_format); #ifdef QT_PRINTSUPPORT_LIB page_format_element.writeAttribute(literal::paper_size, MapPrinter::paperSizeNames()[page_format.paper_size]); #endif page_format_element.writeAttribute(literal::orientation, (page_format.orientation == MapPrinterPageFormat::Portrait) ? literal::portrait : literal::landscape ); page_format_element.writeAttribute(literal::h_overlap, page_format.h_overlap, 2); page_format_element.writeAttribute(literal::v_overlap, page_format.v_overlap, 2); { XmlElementWriter(xml, literal::dimensions).write(page_format.paper_dimensions, 3); } { XmlElementWriter(xml, literal::page_rect).write(page_format.page_rect, 3); } } { XmlElementWriter print_area_element(xml, literal::print_area); print_area_element.write(print_area, 3); print_area_element.writeAttribute(literal::center_area, center_print_area); print_area_element.writeAttribute(literal::single_page, single_page_print_area); } } #ifdef QT_PRINTSUPPORT_LIB // ### MapPrinter ### const QPrinterInfo* MapPrinter::pdfTarget() { static QPrinterInfo pdf_target; // TODO: set name and features? return &pdf_target; } const QPrinterInfo* MapPrinter::imageTarget() { static QPrinterInfo image_target; // TODO: set name and features? return &image_target; } const QHash< int, const char* >& MapPrinter::paperSizeNames() { static QHash< int, const char* > names; if (names.empty()) { names[QPrinter::A0] = "A0"; names[QPrinter::A1] = "A1"; names[QPrinter::A2] = "A2"; names[QPrinter::A3] = "A3"; names[QPrinter::A4] = "A4"; names[QPrinter::A5] = "A5"; names[QPrinter::A6] = "A6"; names[QPrinter::A7] = "A7"; names[QPrinter::A8] = "A8"; names[QPrinter::A9] = "A9"; names[QPrinter::B0] = "B0"; names[QPrinter::B1] = "B1"; names[QPrinter::B2] = "B2"; names[QPrinter::B3] = "B3"; names[QPrinter::B4] = "B4"; names[QPrinter::B5] = "B5"; names[QPrinter::B6] = "B6"; names[QPrinter::B7] = "B7"; names[QPrinter::B8] = "B8"; names[QPrinter::B9] = "B9"; names[QPrinter::B10] = "B10"; names[QPrinter::C5E] = "C5E"; names[QPrinter::DLE] = "DLE"; names[QPrinter::Executive] = "Executive"; names[QPrinter::Folio] = "Folio"; names[QPrinter::Ledger] = "Ledger"; names[QPrinter::Legal] = "Legal"; names[QPrinter::Letter] = "Letter"; names[QPrinter::Tabloid] = "Tabloid"; names[QPrinter::Comm10E] = "US Common #10 Envelope"; names[QPrinter::Custom] = "Custom"; } return names; } MapPrinter::MapPrinter(Map& map, const MapView* view, QObject* parent) : QObject(parent), MapPrinterConfig(map.printerConfig()), map(map), view(view), target(nullptr) { scale_adjustment = map.getScaleDenominator() / qreal(options.scale); updatePaperDimensions(); connect(&map.getGeoreferencing(), &Georeferencing::transformationChanged, this, &MapPrinter::mapScaleChanged); } MapPrinter::~MapPrinter() { // Do not remove. } void MapPrinter::saveConfig() const { map.setPrinterConfig(*this); } // slot void MapPrinter::setTarget(const QPrinterInfo* new_target) { if (new_target != target) { const QPrinterInfo* old_target = target; if (!new_target) target = new_target; else if (new_target == pdfTarget()) target = new_target; else if (new_target == imageTarget()) target = new_target; else { // We don't own this target, so we need to make a copy. target_copy = *new_target; target = &target_copy; } if (old_target == imageTarget() || new_target == imageTarget()) { // No page margins. Will emit pageFormatChanged( ). setCustomPaperSize(page_format.page_rect.size()); } else if (page_format.paper_size != QPrinter::Custom) { updatePaperDimensions(); emit pageFormatChanged(page_format); } emit targetChanged(target); } } std::unique_ptr MapPrinter::makePrinter() const { std::unique_ptr printer; if (!target) { printer.reset(new QPrinter(QPrinter::HighResolution)); } else if (isPrinter()) { printer.reset(new QPrinter(*target, QPrinter::HighResolution)); } else if (options.color_mode == MapPrinterOptions::DeviceCmyk) { printer.reset(new AdvancedPdfPrinter(*target, QPrinter::HighResolution)); } else { printer.reset(new QPrinter(*target, QPrinter::HighResolution)); printer->setOutputFormat(QPrinter::PdfFormat); } if (!printer->isValid()) { printer.reset(); return printer; } // Color can only be changed in (native) properties dialogs. This is the default. printer->setColorMode(separationsModeSelected() ? QPrinter::GrayScale : QPrinter::Color); if (printer->outputFormat() == QPrinter::NativeFormat) { PlatformPrinterProperties::restore(printer.get(), native_data); } printer->setDocName(::OpenOrienteering::MapPrinter::tr("- Map -")); printer->setFullPage(true); if (page_format.paper_size == QPrinter::Custom) { printer->setPaperSize(page_format.paper_dimensions, QPrinter::Millimeter); printer->setOrientation(QPrinter::Portrait); } else { printer->setPaperSize(QPrinter::PaperSize(page_format.paper_size)); printer->setOrientation((page_format.orientation == MapPrinterPageFormat::Portrait) ? QPrinter::Portrait : QPrinter::Landscape); } printer->setResolution(options.resolution); if (page_format.paper_size == QPrinter::Custom || !isPrinter()) { printer->setPageMargins(0.0, 0.0, 0.0, 0.0, QPrinter::Millimeter); } return printer; } bool MapPrinter::isPrinter() const { bool is_printer = target && target != imageTarget() && target != pdfTarget(); return is_printer; } // slot void MapPrinter::setPrintArea(const QRectF& area) { if (print_area != area && area.left() < area.right() && area.top() < area.bottom()) { print_area = area; if (target == imageTarget() && print_area.size() != page_format.paper_dimensions) setCustomPaperSize(print_area.size() * scale_adjustment); updatePageBreaks(); emit printAreaChanged(print_area); } } // slot void MapPrinter::setPaperSize(const int size) { if (page_format.paper_size != size) { if (size == QPrinter::Custom) { setCustomPaperSize(page_format.paper_dimensions); } else { if ( page_format.paper_size == QPrinter::Custom && page_format.paper_dimensions.width() > page_format.paper_dimensions.height() ) { // After QPrinter::Custom, determine orientation from actual dimensions. page_format.orientation = MapPrinterPageFormat::Landscape; } page_format.paper_size = size; updatePaperDimensions(); } emit pageFormatChanged(page_format); } } // slot void MapPrinter::setCustomPaperSize(const QSizeF dimensions) { if ((page_format.paper_size != QPrinter::Custom || page_format.paper_dimensions != dimensions) && ! dimensions.isEmpty()) { page_format.paper_size = QPrinter::Custom; page_format.orientation = MapPrinterPageFormat::Portrait; page_format.paper_dimensions = dimensions; updatePaperDimensions(); emit pageFormatChanged(page_format); } } // slot void MapPrinter::setPageOrientation(const MapPrinterPageFormat::Orientation orientation) { if (page_format.paper_size == QPrinter::Custom) { // do nothing emit pageFormatChanged(page_format); } else if (page_format.orientation != orientation) { page_format.orientation = orientation; page_format.paper_dimensions.transpose(); updatePaperDimensions(); updatePageBreaks(); emit pageFormatChanged(page_format); } } // slot void MapPrinter::setOverlap(qreal h_overlap, qreal v_overlap) { if (page_format.h_overlap != h_overlap || page_format.v_overlap != v_overlap) { page_format.h_overlap = qMax(qreal(0), qMin(h_overlap, page_format.page_rect.width())); page_format.v_overlap = qMax(qreal(0), qMin(v_overlap, page_format.page_rect.height())); updatePageBreaks(); emit pageFormatChanged(page_format); } } void MapPrinter::updatePaperDimensions() { if (target == imageTarget() && page_format.paper_size == QPrinter::Custom) { // No margins, no need to query QPrinter. page_format.page_rect = QRectF(QPointF(0.0, 0.0), page_format.paper_dimensions); page_format.h_overlap = qMax(qreal(0), qMin(page_format.h_overlap, page_format.page_rect.width())); page_format.v_overlap = qMax(qreal(0), qMin(page_format.v_overlap, page_format.page_rect.height())); updatePageBreaks(); return; } QPrinter* printer = target ? new QPrinter(*target, QPrinter::HighResolution) : new QPrinter(QPrinter::HighResolution); if (!printer->isValid() || target == imageTarget() || target == pdfTarget()) printer->setOutputFormat(QPrinter::PdfFormat); if (page_format.paper_size == QPrinter::Custom) { printer->setPaperSize(page_format.paper_dimensions, QPrinter::Millimeter); page_format.orientation = (printer->orientation() == QPrinter::Portrait) ? MapPrinterPageFormat::Portrait : MapPrinterPageFormat::Landscape; } else { printer->setPaperSize(QPrinter::PaperSize(page_format.paper_size)); printer->setOrientation((page_format.orientation == MapPrinterPageFormat::Portrait) ? QPrinter::Portrait : QPrinter::Landscape); } page_format.page_rect = printer->paperRect(QPrinter::Millimeter); page_format.paper_dimensions = page_format.page_rect.size(); if ( target != imageTarget() && target != pdfTarget() && page_format.paper_size != QPrinter::Custom ) { qreal left, top, right, bottom; printer->getPageMargins(&left, &top, &right, &bottom, QPrinter::Millimeter); page_format.page_rect.adjust(left, top, -right, -bottom); } page_format.h_overlap = qMax(qreal(0), qMin(page_format.h_overlap, page_format.page_rect.width())); page_format.v_overlap = qMax(qreal(0), qMin(page_format.v_overlap, page_format.page_rect.height())); delete printer; updatePageBreaks(); } // slot void MapPrinter::setResolution(int dpi) { if (dpi > 0 && options.resolution != dpi) { options.resolution = dpi; emit optionsChanged(options); } } // slot void MapPrinter::setScale(const unsigned int value) { if (options.scale != value) { Q_ASSERT(value > 0); options.scale = value; scale_adjustment = map.getScaleDenominator() / qreal(value); updatePageBreaks(); emit optionsChanged(options); } } // slot void MapPrinter::setMode(const MapPrinterOptions::MapPrinterMode mode) { if (options.mode != mode) { options.mode = mode; emit optionsChanged(options); } } // slot void MapPrinter::setPrintTemplates(const bool visible) { if (options.show_templates != visible) { options.show_templates = visible; emit optionsChanged(options); } } // slot void MapPrinter::setPrintGrid(const bool visible) { if (options.show_grid != visible) { options.show_grid = visible; emit optionsChanged(options); } } // slot void MapPrinter::setSimulateOverprinting(bool enabled) { if (enabled && !map.hasSpotColors()) { options.simulate_overprinting = false; emit optionsChanged(options); } else if (options.simulate_overprinting != enabled) { options.simulate_overprinting = enabled; emit optionsChanged(options); } } void MapPrinter::setColorMode(MapPrinterOptions::ColorMode color_mode) { if (options.color_mode != color_mode) { options.color_mode = color_mode; emit optionsChanged(options); } } bool MapPrinter::isOutputEmpty() const { return ( (map.getNumObjects() == 0 || (view && !view->effectiveMapVisibility().visible)) && (!options.show_templates || map.getNumTemplates() == 0) && !options.show_grid ); } void MapPrinter::updatePageBreaks() { Q_ASSERT(print_area.left() <= print_area.right()); Q_ASSERT(print_area.top() <= print_area.bottom()); // This whole implementation needs to deal with FP precision issues h_page_pos.clear(); qreal h_pos = print_area.left(); h_page_pos.push_back(h_pos); const qreal h_overlap = page_format.h_overlap / scale_adjustment; const qreal page_width = page_format.page_rect.width() / scale_adjustment - h_overlap; const qreal right_bound = print_area.right() - h_overlap - 0.05; if (page_width >= 0.01) { for (h_pos += page_width; h_pos < right_bound; h_pos += page_width) h_page_pos.push_back(h_pos); // Center the print area on the pages total area. // Don't pre-calculate this offset to avoid FP precision problems const qreal h_offset = 0.5 * (h_pos + h_overlap - print_area.right()); for (auto& pos : h_page_pos) pos -= h_offset; } v_page_pos.clear(); qreal v_pos = print_area.top(); v_page_pos.push_back(v_pos); const qreal v_overlap = page_format.v_overlap / scale_adjustment; const qreal page_height = page_format.page_rect.height() / scale_adjustment - v_overlap; const qreal bottom_bound = print_area.bottom() - v_overlap - 0.05; if (page_height >= 0.01) { for (v_pos += page_height; v_pos < bottom_bound; v_pos += page_height) v_page_pos.push_back(v_pos); // Don't pre-calculate offset to avoid FP precision problems const qreal v_offset = 0.5 * (v_pos + v_overlap - print_area.bottom()); for (auto& pos : v_page_pos) pos -= v_offset; } } void MapPrinter::mapScaleChanged() { auto value = qreal(map.getScaleDenominator()) / options.scale; if (!qFuzzyCompare(scale_adjustment, value)) { scale_adjustment = value; updatePageBreaks(); emit optionsChanged(options); } } void MapPrinter::takePrinterSettings(const QPrinter* printer) { if (!printer) return; MapPrinterPageFormat f(*printer); if (target == pdfTarget() || target == imageTarget()) { f.page_rect = QRectF(QPointF(0.0, 0.0), f.paper_dimensions); } if (f != page_format) { page_format.paper_size = f.paper_size; page_format.orientation = f.orientation; page_format.paper_dimensions = f.paper_dimensions; page_format.page_rect = f.page_rect; updatePageBreaks(); emit pageFormatChanged(page_format); } setResolution(printer->resolution()); if (printer->outputFormat() == QPrinter::NativeFormat) { PlatformPrinterProperties::save(printer, native_data); } } // local void drawBuffer(QPainter* device_painter, const QImage* page_buffer, qreal pixel2units) { Q_ASSERT(page_buffer); device_painter->save(); device_painter->scale(pixel2units, pixel2units); device_painter->setRenderHint(QPainter::SmoothPixmapTransform, false); device_painter->drawImage(0, 0, *page_buffer); device_painter->restore(); } void MapPrinter::drawPage(QPainter* device_painter, float units_per_inch, const QRectF& page_extent, bool white_background, QImage* page_buffer) const { device_painter->save(); device_painter->setRenderHint(QPainter::Antialiasing); device_painter->setRenderHint(QPainter::SmoothPixmapTransform); QPainter* painter = device_painter; // Logical units per mm qreal units_per_mm = units_per_inch / 25.4; // Image pixels per mm qreal pixel_per_mm = options.resolution / 25.4; // Scaling from pixels to logical units qreal pixel2units = units_per_inch / options.resolution; // The current painter's resolution qreal scale = units_per_mm; /* * Analyse need for page buffer * * Painting raster images with opacity is not supported on print devices. * This can only be solved by merging the images before sending them to * the printer. For the map itself, this may result in loss of sharpness * and increase in data volume. * * In vector mode, a page buffer (raster image) is used to collect background * templates and raster foreground templates. After this buffer is sent to * to the printer, map, grid, and * non-raster foreground templates are drawn on top of it. * * In raster mode, all map features are drawn to in regular order to * temporary images first. * * When the target is an image, use the temporary image to enforce the given * resolution. */ bool use_buffer_for_map = (options.mode == MapPrinterOptions::Raster || target == imageTarget()); bool use_buffer_for_background = use_buffer_for_map && options.show_templates; bool use_buffer_for_foreground = use_buffer_for_map && options.show_templates; if (view && options.show_templates) { if (!use_buffer_for_background) { for (int i = 0; i < map.getFirstFrontTemplate() && !use_buffer_for_background; ++i) { if (map.getTemplate(i)->isRasterGraphics()) { auto visibility = view->getTemplateVisibility(map.getTemplate(i)); use_buffer_for_background = visibility.visible && visibility.opacity < 1.0f; } } } if (!use_buffer_for_foreground) { for (int i = map.getFirstFrontTemplate(); i < map.getNumTemplates() && !use_buffer_for_foreground; ++i) { if (map.getTemplate(i)->isRasterGraphics()) { auto visibility = view->getTemplateVisibility(map.getTemplate(i)); use_buffer_for_foreground = visibility.visible && visibility.opacity < 1.0f; } } } } // Prepare buffer if required bool use_page_buffer = use_buffer_for_map || use_buffer_for_background || use_buffer_for_foreground; QImage scoped_buffer; if (use_page_buffer && !page_buffer) { scale = pixel_per_mm; int w = qCeil(page_format.paper_dimensions.width() * scale); int h = qCeil(page_format.paper_dimensions.height() * scale); #if defined (Q_OS_MACOS) if (device_painter->device()->physicalDpiX() == 0) { // Possible Qt bug, since according to QPaintDevice documentation, // "if the physicalDpiX() doesn't equal the logicalDpiX(), // the corresponding QPaintEngine must handle the resolution mapping" // which doesn't seem to happen here. qreal corr = device_painter->device()->logicalDpiX() / 72.0; w = qCeil(page_format.paper_dimensions.width() * scale * corr); h = qCeil(page_format.paper_dimensions.height() * scale * corr); } #endif scoped_buffer = QImage(w, h, QImage::Format_RGB32); if (scoped_buffer.isNull()) { // Allocation failed device_painter->restore(); device_painter->end(); // Signal error return; } page_buffer = &scoped_buffer; painter = new QPainter(page_buffer); painter->setRenderHints(device_painter->renderHints()); } /* * Prepare the common background */ if (use_page_buffer) { page_buffer->fill(QColor(Qt::white)); } else if (white_background) { painter->fillRect(QRect(0, 0, painter->device()->width(), painter->device()->height()), Qt::white); } /* * One-time setup of transformation and clipping */ // Translate for top left page margin painter->scale(scale, scale); painter->translate(page_format.page_rect.left(), page_format.page_rect.top()); // Convert native map scale to print scale if (scale_adjustment != 1.0) { scale *= scale_adjustment; painter->scale(scale_adjustment, scale_adjustment); } // Translate and clip for margins and print area painter->translate(-page_extent.left(), -page_extent.top()); QTransform transform = painter->transform(); QRectF page_region_used(page_extent.intersected(print_area)); painter->setClipRect(page_region_used, Qt::ReplaceClip); /* * Draw templates in the background */ if (options.show_templates) { map.drawTemplates(painter, page_region_used, 0, map.getFirstFrontTemplate() - 1, view, false); if (vectorModeSelected() && use_buffer_for_foreground) { for (int i = map.getFirstFrontTemplate(); i < map.getNumTemplates(); ++i) { if (map.getTemplate(i)->isRasterGraphics()) map.drawTemplates(painter, page_region_used, i, i, view, false); } } } if (use_buffer_for_background && !use_buffer_for_map) { // Flush the buffer, reset painter delete painter; drawBuffer(device_painter, page_buffer, pixel2units); painter = device_painter; painter->setTransform(transform); painter->setClipRect(page_region_used, Qt::ReplaceClip); } /* * Draw the map */ if (!view || view->effectiveMapVisibility().visible) { QImage map_buffer; QPainter* map_painter = painter; if (use_buffer_for_map) { // Draw map into a temporary buffer first which is printed with the map's opacity later. // This prevents artifacts with overlapping objects. if (painter == device_painter) { map_buffer = *page_buffer; // Use existing buffer } else { map_buffer = QImage(page_buffer->size(), QImage::Format_ARGB32_Premultiplied); } map_buffer.fill(QColor(Qt::transparent)); map_painter = new QPainter(&map_buffer); map_painter->setRenderHints(painter->renderHints()); map_painter->setTransform(painter->transform()); } RenderConfig config = { map, page_region_used, scale, RenderConfig::NoOptions, 1.0 }; if (rasterModeSelected() && options.simulate_overprinting) { map.drawOverprintingSimulation(map_painter, config); } else { if (vectorModeSelected() && view) config.opacity = view->effectiveMapVisibility().opacity; map.draw(map_painter, config); } if (map_painter != painter) { // Flush the buffer delete map_painter; map_painter = nullptr; // Print buffer with map opacity painter->save(); painter->resetTransform(); if (view) painter->setOpacity(view->effectiveMapVisibility().opacity); painter->setRenderHint(QPainter::SmoothPixmapTransform, false); painter->drawImage(0, 0, map_buffer); painter->restore(); } } /* * Draw the grid */ if (options.show_grid) map.drawGrid(painter, print_area, false); // Maybe replace by page_region_used? /* * Draw the foreground templates */ if (options.show_templates) { if (vectorModeSelected() && use_buffer_for_foreground) { for (int i = map.getFirstFrontTemplate(); i < map.getNumTemplates(); ++i) { if (!map.getTemplate(i)->isRasterGraphics()) map.drawTemplates(painter, page_region_used, i, i, view, false); } } else { if (use_buffer_for_foreground && !use_buffer_for_map) { page_buffer->fill(QColor(Qt::transparent)); painter = new QPainter(page_buffer); painter->setRenderHints(device_painter->renderHints()); painter->setTransform(transform); painter->setClipRect(page_region_used, Qt::ReplaceClip); } map.drawTemplates(painter, page_region_used, map.getFirstFrontTemplate(), map.getNumTemplates() - 1, view, false); } } /* * Cleanup: If a temporary buffer has been used, paint it on the device painter */ if (painter != device_painter) { delete painter; painter = nullptr; device_painter->resetTransform(); drawBuffer(device_painter, page_buffer, pixel2units); } device_painter->restore(); } void MapPrinter::drawSeparationPages(QPrinter* printer, QPainter* device_painter, float dpi, const QRectF& page_extent) const { Q_ASSERT(printer->colorMode() == QPrinter::GrayScale); device_painter->save(); device_painter->setRenderHint(QPainter::Antialiasing); // Translate for top left page margin qreal scale = dpi / 25.4; // Dots per mm device_painter->scale(scale, scale); device_painter->translate(page_format.page_rect.left(), page_format.page_rect.top()); // Convert native map scale to print scale if (scale_adjustment != 1.0) { scale *= scale_adjustment; device_painter->scale(scale_adjustment, scale_adjustment); } // Translate and clip for margins and print area device_painter->translate(-page_extent.left(), -page_extent.top()); device_painter->setClipRect(page_extent.intersected(print_area), Qt::ReplaceClip); bool need_new_page = false; for (int i = map.getNumColors() - 1; i >= 0; --i) { const MapColor* color = map.getColor(i); if (color->getSpotColorMethod() == MapColor::SpotColor) { if (need_new_page) { printer->newPage(); } RenderConfig config = { map, page_extent, scale, RenderConfig::NoOptions, 1.0 }; map.drawColorSeparation(device_painter, config, color); need_new_page = true; } } device_painter->restore(); } bool MapPrinter::printMap(QPrinter* printer) { // Printer settings may have been changed by preview or application. // We need to use them for printing. printer->setFullPage(true); takePrinterSettings(printer); QSizeF extent_size = page_format.page_rect.size() / scale_adjustment; QPainter painter(printer); auto resolution = float(options.resolution); #if defined(Q_OS_WIN) // Workaround for Wine if (printer->resolution() == 0) { return false; } /* Non-opaque drawing of vector data to the raw printer will trigger * internal rasterization in Qt. */ auto engine_will_rasterize = [](const Map& map, const MapView* view, const MapPrinterOptions& options)->bool { if (!view) return false; if (options.mode != MapPrinterOptions::Raster) { auto visibility = view->effectiveMapVisibility(); if (visibility.visible && visibility.opacity < 1.0) return true; } for (int i = 0; i < map.getNumTemplates(); ++i) { auto temp = map.getTemplate(i); if (temp->isRasterGraphics()) continue; auto visibility = view->getTemplateVisibility(temp); if (visibility.visible && visibility.opacity < 1.0) return true; } return false; }; if (printer->paintEngine()->type() == QPaintEngine::Windows && !engine_will_rasterize(map, view, options) ) { /* QWin32PrintEngine will (have to) do rounding when passing coordinates * to GDI, using the device's reported logical resolution. * We establish an MM_ISOTROPIC transformation from a higher resolution * to avoid the loss of precision due to this rounding. * However, output of rasterization produced by Qt fails when the * MM_ISOTROPIC transformation is active. */ QWin32PrintEngine* engine = static_cast(printer->printEngine()); HDC dc = engine->getDC(); // The high resolution in units per millimeter const int hires_ppmm = 1000; // The paper dimensions in high resolution units const int hires_width = qRound(page_format.page_rect.width() * hires_ppmm); const int hires_height = qRound(page_format.page_rect.height() * hires_ppmm); // The physical paper dimensions in device units const int phys_width = GetDeviceCaps(dc, PHYSICALWIDTH); const int phys_height = GetDeviceCaps(dc, PHYSICALHEIGHT); // The physical printing offset in device units const int phys_off_x = GetDeviceCaps(dc, PHYSICALOFFSETX); const int phys_off_y = GetDeviceCaps(dc, PHYSICALOFFSETY); // (Needed to work around an unexpected offset, maybe related to QTBUG-5363) if (phys_width > 0) { // Establish the transformation SetMapMode (dc, MM_ISOTROPIC); SetWindowExtEx(dc, hires_width, hires_height, nullptr); SetViewportExtEx(dc, phys_width, phys_height, nullptr); SetViewportOrgEx(dc, -phys_off_x, -phys_off_y, nullptr); resolution *= ((double)hires_width / phys_width); } } else if (printer->paintEngine()->type() == QPaintEngine::Picture) { // Preview: work around for offset, maybe related to QTBUG-5363 painter.translate( -page_format.page_rect.left()*resolution / 25.4, -page_format.page_rect.top()*resolution / 25.4 ); } #endif cancel_print_map = false; int step = 0; auto num_steps = v_page_pos.size() * h_page_pos.size(); const QString message_template( (options.mode == MapPrinterOptions::Separations) ? ::OpenOrienteering::MapPrinter::tr("Processing separations of page %1...") : ::OpenOrienteering::MapPrinter::tr("Processing page %1...") ); auto message = message_template.arg(1); emit printProgress(0, message); bool need_new_page = false; for (auto vpos : v_page_pos) { if (!painter.isActive()) { break; } for (auto hpos : h_page_pos) { if (!painter.isActive()) { break; } ++step; auto progress = qMin(99, qMax(1, int((100 * static_cast(step) - 50) / num_steps))); emit printProgress(progress, message_template.arg(step)); if (cancel_print_map) /* during printProgress handling */ { painter.end(); break; } if (need_new_page) { printer->newPage(); } QRectF page_extent = QRectF(QPointF(hpos, vpos), extent_size); if (separationsModeSelected()) { drawSeparationPages(printer, &painter, resolution, page_extent); } else { drawPage(&painter, resolution, page_extent, false); } need_new_page = true; } } if (cancel_print_map) { emit printProgress(100, ::OpenOrienteering::MapPrinter::tr("Canceled")); } else if (!painter.isActive()) { emit printProgress(100, ::OpenOrienteering::MapPrinter::tr("Error")); return false; } else { emit printProgress(100, ::OpenOrienteering::MapPrinter::tr("Finished")); } return true; } void MapPrinter::cancelPrintMap() { cancel_print_map = true; } #endif // QT_PRINTSUPPORT_LIB } // namespace OpenOrienteering mapper-0.8.1.1/src/core/map_printer.h000066400000000000000000000371511325266516600173760ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2012-2016 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_MAP_PRINTER_H #define OPENORIENTEERING_MAP_PRINTER_H #include #include #include #include #include #include #include #include #include #ifdef QT_PRINTSUPPORT_LIB # include #else class QPrinterInfo; #endif template class QHash; class QImage; class QPainter; class QPrinter; class QXmlStreamReader; class QXmlStreamWriter; namespace OpenOrienteering { class Map; class MapView; /** The MapPrinterPageFormat is a complete description of page properties. */ class MapPrinterPageFormat { public: /** Copy of QPrinter::Orientation because QPrinter is not available for Android */ enum Orientation { Portrait, Landscape }; /** Constructs a new page format. * * The format is initialized as a custom page format with a page rectangle * of the given size, surrounded by the given margin (in mm). */ MapPrinterPageFormat(QSizeF page_rect_size = QSizeF(100.0, 100.0), qreal margin = 5.0, qreal overlap = 5.0); #ifdef QT_PRINTSUPPORT_LIB /** Constructs a new page format, initialized from the given printer's settings. */ MapPrinterPageFormat(const QPrinter& printer, qreal overlap = 5.0); #endif /** Returns a page format initialized from the default printer's settings. * * When building without Qt Print Support, returns a default constructed * page format. */ static MapPrinterPageFormat fromDefaultPrinter(); /** The nominal paper size (of type QPrinter::PaperSize) */ int paper_size; /** The orientation of the paper. */ Orientation orientation; /** The printable page rectangle in mm. */ QRectF page_rect; /** The total dimensions of the page in mm. */ QSizeF paper_dimensions; /** The horizontal overlap in mm of pages when the output covers muliple pages. */ qreal h_overlap; /** The vertival overlap in mm of pages when the output covers muliple pages. */ qreal v_overlap; }; /** Returns true iff the MapPrinterPageFormat values are equal. */ bool operator==(const MapPrinterPageFormat& lhs, const MapPrinterPageFormat& rhs); /** Returns true iff the MapPrinterPageFormat values are not equal. */ bool operator!=(const MapPrinterPageFormat& lhs, const MapPrinterPageFormat& rhs); /** MapPrinterOptions control how the map is rendered. */ class MapPrinterOptions { public: /** Basic modes of printing. */ enum MapPrinterMode { Vector, ///< Print in vector graphics mode Raster, ///< Print in raster graphics mode Separations ///< Print spot color separations (b/w vector) }; /** Color modes. * * At the moment, only PDF supports a different mode than the default. */ enum ColorMode { DefaultColorMode, ///< Use the target engine's default color mode. DeviceCmyk ///< Use device-dependent CMYK for vector data. }; /** Constructs new printer options. * * The scale, the mode and the resolution are initialized to the given * values, all boolean properties are initialized to false. */ MapPrinterOptions(unsigned int scale, int resolution = 600, MapPrinterMode mode = Vector); /** The scale to be used for printing. */ unsigned int scale; /** The nominal resolution to be used. */ int resolution; /** The mode of printing. * * Note that other printing options may be available only in particular modes. */ MapPrinterMode mode; /** The color mode. */ ColorMode color_mode; /** Controls if templates get printed. * * Not available in MapPrinterOptions::Separations mode. */ bool show_templates; /** Controls if the configured grid is printed. * * Not available in MapPrinterOptions::Separations mode. */ bool show_grid; /** Controls if the desktop printing is to simulate spot color printing. * * Only available in MapPrinterOptions::Raster mode. */ bool simulate_overprinting; }; /** MapPrintParameters contains all options that control printing * and provides methods for saving and loading. */ class MapPrinterConfig { public: /** Constructs a default config for the given map. */ MapPrinterConfig(const Map& map); /** Constructs a config and loads the map print parameters from an XML stream. */ MapPrinterConfig(const Map& map, QXmlStreamReader& xml); /** Saves the map print parameters to an XML stream. */ void save(QXmlStreamWriter& xml, const QLatin1String& element_name) const; /** The name of the printer. */ QString printer_name; /** The print area. */ QRectF print_area; /** The page format. */ MapPrinterPageFormat page_format; /** Printing options. */ MapPrinterOptions options; /** A flag which indicates to the UI whether to maintain a centered * print area when the size of the map or the page changes. */ bool center_print_area; /** A flag which indicates to the UI whether to adjust the print area size * to the current page size. */ bool single_page_print_area; /** Platform-dependent data. */ std::shared_ptr native_data; }; #ifdef QT_PRINTSUPPORT_LIB /** MapPrinter provides an interface to print a map (to a printer or file). * It may render a page on any QPaintDevice, such as an QImage. */ class MapPrinter : public QObject, protected MapPrinterConfig { Q_OBJECT public: /** Returns a QPrinterInfo pointer which signals printing to a PDF file. */ static const QPrinterInfo* pdfTarget(); /** Returns a QPrinterInfo pointer which signals printing to a raster file. */ static const QPrinterInfo* imageTarget(); /** Returns a reference to a hash which maps paper sizes to names as C strings. * These strings are not translated. */ static const QHash< int, const char* >& paperSizeNames(); /** Constructs a new MapPrinter for the given map and (optional) view. */ MapPrinter(Map& map, const MapView* view, QObject* parent = nullptr); /** Destructor. */ ~MapPrinter() override; /** Returns the configured target printer in terms of QPrinterInfo. */ const QPrinterInfo* getTarget() const; /** Returns true if a real printer is configured. */ bool isPrinter() const; /** Returns the page format specification. */ const MapPrinterPageFormat& getPageFormat() const; /** Returns the map area to be printed. */ const QRectF& getPrintArea() const; /** Returns the rendering options. */ const MapPrinterOptions& getOptions() const; /** Returns true if the current print area and rendering options will * result in empty pages. (The grid is not considered.) */ bool isOutputEmpty() const; /** Returns the quotient of map scale denominator and print scale denominator. */ qreal getScaleAdjustment() const; /** Returns the paper size which is required by the current print area and scale. */ QSizeF getPrintAreaPaperSize() const; /** Returns the print area size which is possible with the current page rect size. */ QSizeF getPageRectPrintAreaSize() const; /** Returns a list of horizontal page positions on the map. */ const std::vector< qreal >& horizontalPagePositions() const; /** Returns a list of vertical page positions on the map. */ const std::vector< qreal >& verticalPagePositions() const; /** Creates a printer configured according to the current settings. */ std::unique_ptr makePrinter() const; /** Takes the settings from the given printer, * and generates signals for changing properties. */ void takePrinterSettings(const QPrinter* printer); /** Prints the map to the given printer. * * This will first update this object's properties from the printer's properties. * * @return true on success, false on error. */ bool printMap(QPrinter* printer); /** Draws a single page to the painter. * * In case of an error, the painter will be inactive when returning from * this function. * * When the actual paint device is a QImage, pass it as page_buffer. * This helps to determine the exact dimensions and to avoid the allocation * of another buffer. * Otherwise, drawPage() may allocate a buffer with this map printer's * resolution and size. Parameter units_per_inch has no influence on this * buffer but refers to the logical coordinates of device_painter. */ void drawPage(QPainter* device_painter, float units_per_inch, const QRectF& page_extent, bool white_background, QImage* page_buffer = nullptr) const; /** Draws the separations as distinct pages to the printer. */ void drawSeparationPages(QPrinter* printer, QPainter* device_painter, float dpi, const QRectF& page_extent) const; /** Returns the current configuration. */ const MapPrinterConfig& config() const; public slots: /** Sets the target QPrinterInfo. * Ownership is not taken over! Target may even be nullptr. */ void setTarget(const QPrinterInfo* new_target); /** Sets the map area which is to be printed. */ void setPrintArea(const QRectF& area); /** Sets the QPrinter::PaperSize to be used. */ void setPaperSize(const int size); /** Sets a custom paper size with the given dimensions. */ void setCustomPaperSize(const QSizeF dimensions); /** Sets the page orientation. */ void setPageOrientation(const MapPrinterPageFormat::Orientation orientation); /** Sets the overlapping of the pages at the margins. */ void setOverlap(qreal h_overlap, qreal v_overlap); /** Sets the desired printing resolution in dpi. * The actual resolution will be set by the printer. * * Does nothing if dpi is 0 or less. */ void setResolution(int dpi); /** Sets the denominator of the map scale for printing. */ void setScale(const unsigned int value); /** Sets the printing mode. */ void setMode(const MapPrinterOptions::MapPrinterMode mode); /** Controls whether to print templates. * If a MapView is given when enabling template printing, * it will determine the visibility of map and templates. */ void setPrintTemplates(const bool visible); /** Controls whether to print the map grid. */ void setPrintGrid(const bool visible); /** Controls whether to print in overprinting simulation mode. */ void setSimulateOverprinting(bool enabled); /** Controls the color mode. */ void setColorMode(MapPrinterOptions::ColorMode color_mode); /** Saves the print parameter (to the map). */ void saveConfig() const; /** Cancels a running printMap(), if possible. * This can only be used during handlers of the printMapProgress() signal. */ void cancelPrintMap(); signals: /** Indicates a new target printer. */ void targetChanged(const QPrinterInfo* target) const; /** Indicates a change in the map area. */ void printAreaChanged(const QRectF& area) const; /** Indicates a change in the page format. */ void pageFormatChanged(const MapPrinterPageFormat& format) const; /** Indicates a change in the rendering options. */ void optionsChanged(const MapPrinterOptions& options) const; /** Emitted during printMap() to indicate progress. You can expect this * signal to be emitted at the start of the print process (value = 1), * after each page printed, and at the end of the process (value = 100). * * @param value Reflects the progress in the range from 1 (just started) * to 100 (finished). * @param status A verbal representation of what printMap() is doing. */ void printProgress(int value, const QString& status) const; protected: /** Returns true if vector mode is set. */ bool vectorModeSelected() const; /** Returns true if raster mode is set. */ bool rasterModeSelected() const; /** Returns true if separations mode is set. */ bool separationsModeSelected() const; /** Updates the paper dimensions from paper format and orientation. */ void updatePaperDimensions(); /** Updates the page breaks from map area and page format. */ void updatePageBreaks(); /** Updates the scale adjustment and page breaks. */ void mapScaleChanged(); Map& map; const MapView* view; const QPrinterInfo* target; QPrinterInfo target_copy; qreal scale_adjustment; std::vector h_page_pos; std::vector v_page_pos; bool cancel_print_map; }; #endif //### MapPrinterPageFormat inline code ### inline bool operator!=(const MapPrinterPageFormat& lhs, const MapPrinterPageFormat& rhs) { return !(lhs == rhs); } //### MapPrinterOptions inline code ### /** Returns true iff the MapPrinterOptions values are equal. */ inline bool operator==(const MapPrinterOptions& lhs, const MapPrinterOptions& rhs) { return lhs.mode == rhs.mode && lhs.color_mode == rhs.color_mode && lhs.resolution == rhs.resolution && lhs.scale == rhs.scale && lhs.show_templates == rhs.show_templates && lhs.show_grid == rhs.show_grid && lhs.simulate_overprinting == rhs.simulate_overprinting; } /** Returns true iff the MapPrinterOptions values are not equal. */ inline bool operator!=(const MapPrinterOptions& lhs, const MapPrinterOptions& rhs) { return !(lhs == rhs); } // ### MapPrinterConfig ### /** Returns true iff the MapPrinterConfig values are equal. */ inline bool operator==(const MapPrinterConfig& lhs, const MapPrinterConfig& rhs) { return lhs.printer_name == rhs.printer_name && lhs.print_area == rhs.print_area && lhs.page_format == rhs.page_format && lhs.options == rhs.options && lhs.center_print_area == rhs.center_print_area && lhs.single_page_print_area == rhs.single_page_print_area; } /** Returns true iff the MapPrinterConfig values are not equal. */ inline bool operator!=(const MapPrinterConfig& lhs, const MapPrinterConfig& rhs) { return !(lhs == rhs); } // ### MapPrinter inline code ### #ifdef QT_PRINTSUPPORT_LIB inline const MapPrinterConfig& MapPrinter::config() const { return *this; } inline const QPrinterInfo* MapPrinter::getTarget() const { return target; } inline const MapPrinterPageFormat& MapPrinter::getPageFormat() const { return page_format; } inline const QRectF& MapPrinter::getPrintArea() const { return print_area; } inline const MapPrinterOptions& MapPrinter::getOptions() const { return options; } inline qreal MapPrinter::getScaleAdjustment() const { return scale_adjustment; } inline QSizeF MapPrinter::getPrintAreaPaperSize() const { return getPrintArea().size() * scale_adjustment; } inline QSizeF MapPrinter::getPageRectPrintAreaSize() const { return page_format.page_rect.size() / scale_adjustment; } inline const std::vector< qreal >& MapPrinter::horizontalPagePositions() const { return h_page_pos; } inline const std::vector< qreal >& MapPrinter::verticalPagePositions() const { return v_page_pos; } inline bool MapPrinter::vectorModeSelected() const { return options.mode == MapPrinterOptions::Vector; } inline bool MapPrinter::rasterModeSelected() const { return options.mode == MapPrinterOptions::Raster; } inline bool MapPrinter::separationsModeSelected() const { return options.mode == MapPrinterOptions::Separations; } #endif } // namespace OpenOrienteering #endif mapper-0.8.1.1/src/core/map_view.cpp000066400000000000000000000357261325266516600172260ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2014-2016 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "map_view.h" #include #include #include #include #include #include #include #include #include #include #include #include "core/map.h" #include "core/map_coord.h" #include "gui/util_gui.h" #include "templates/template.h" // IWYU pragma: keep #include "util/util.h" #include "util/xml_stream_util.h" namespace literal { static const QLatin1String zoom("zoom"); static const QLatin1String rotation("rotation"); static const QLatin1String position_x("position_x"); static const QLatin1String position_y("position_y"); static const QLatin1String grid("grid"); static const QLatin1String overprinting_simulation_enabled("overprinting_simulation_enabled"); static const QLatin1String map("map"); static const QLatin1String opacity("opacity"); static const QLatin1String visible("visible"); static const QLatin1String templates("templates"); static const QLatin1String hidden("hidden"); static const QLatin1String ref("ref"); static const QLatin1String template_string("template"); } namespace OpenOrienteering { const double MapView::zoom_in_limit = 512; const double MapView::zoom_out_limit = 1 / 16.0; MapView::MapView(QObject* parent, Map* map) : QObject { parent } , map { map } , zoom { 1.0 } , rotation { 0.0 } , map_visibility{ 1.0f, true } , all_templates_hidden{ false } , grid_visible{ false } , overprinting_simulation_enabled{ false } { Q_ASSERT(map); updateTransform(); connect(map, &Map::templateAdded, this, &MapView::onTemplateAdded); connect(map, &Map::templateDeleted, this, &MapView::onTemplateDeleted, Qt::QueuedConnection); } MapView::MapView(Map* map) : MapView { map, map } { // nothing else } MapView::~MapView() { // nothing, not inlined } #ifndef NO_NATIVE_FILE_FORMAT void MapView::load(QIODevice* file, int version) { qint64 center_x, center_y; int unused; file->read((char*)&zoom, sizeof(double)); file->read((char*)&rotation, sizeof(double)); file->read((char*)¢er_x, sizeof(qint64)); file->read((char*)¢er_y, sizeof(qint64)); file->read((char*)&unused /*view_x*/, sizeof(int)); file->read((char*)&unused /*view_y*/, sizeof(int)); file->read((char*)&pan_offset, sizeof(QPoint)); try { center_pos = MapCoord::fromNative64withOffset(center_x, center_y); } catch (std::range_error&) { // leave center_pos unchanged } updateTransform(); if (version >= 26) { file->read((char*)&map_visibility.visible, sizeof(bool)); file->read((char*)&map_visibility.opacity, sizeof(float)); } int num_template_visibilities; file->read((char*)&num_template_visibilities, sizeof(int)); for (int i = 0; i < num_template_visibilities; ++i) { int pos; file->read((char*)&pos, sizeof(int)); TemplateVisibility vis; file->read((char*)&vis.visible, sizeof(bool)); file->read((char*)&vis.opacity, sizeof(float)); setTemplateVisibilityHelper(map->getTemplate(pos), vis); } if (version >= 29) file->read((char*)&all_templates_hidden, sizeof(bool)); if (version >= 24) file->read((char*)&grid_visible, sizeof(bool)); emit viewChanged(CenterChange | ZoomChange | RotationChange); emit visibilityChanged(MultipleFeatures, true); } #endif void MapView::save(QXmlStreamWriter& xml, const QLatin1String& element_name, bool template_details) const { XmlElementWriter mapview_element(xml, element_name); mapview_element.writeAttribute(literal::zoom, zoom); mapview_element.writeAttribute(literal::rotation, rotation); mapview_element.writeAttribute(literal::position_x, center_pos.nativeX()); mapview_element.writeAttribute(literal::position_y, center_pos.nativeY()); mapview_element.writeAttribute(literal::grid, grid_visible); mapview_element.writeAttribute(literal::overprinting_simulation_enabled, overprinting_simulation_enabled); { XmlElementWriter map_element(xml, literal::map); map_element.writeAttribute(literal::opacity, map_visibility.opacity); map_element.writeAttribute(literal::visible, map_visibility.visible); } { XmlElementWriter templates_element(xml, literal::templates); templates_element.writeAttribute(literal::hidden, all_templates_hidden); if (template_details) { templates_element.writeAttribute(XmlStreamLiteral::count, template_visibilities.size()); for (auto entry : template_visibilities) { XmlElementWriter ref_element(xml, literal::ref); ref_element.writeAttribute(literal::template_string, map->findTemplateIndex(entry.temp)); ref_element.writeAttribute(literal::visible, entry.visible); ref_element.writeAttribute(literal::opacity, entry.opacity); } } } } void MapView::load(QXmlStreamReader& xml) { XmlElementReader mapview_element(xml); zoom = qMin(mapview_element.attribute(literal::zoom), zoom_in_limit); if (zoom < zoom_out_limit) zoom = 1.0; rotation = mapview_element.attribute(literal::rotation); auto center_x = mapview_element.attribute(literal::position_x); auto center_y = mapview_element.attribute(literal::position_y); try { center_pos = MapCoord::fromNative64withOffset(center_x, center_y); } catch (std::range_error&) { // leave center_pos unchanged } updateTransform(); grid_visible = mapview_element.attribute(literal::grid); overprinting_simulation_enabled = mapview_element.attribute(literal::overprinting_simulation_enabled); while (xml.readNextStartElement()) { if (xml.name() == literal::map) { XmlElementReader map_element(xml); map_visibility.opacity = map_element.attribute(literal::opacity); if (map_element.hasAttribute(literal::visible)) map_visibility.visible = map_element.attribute(literal::visible); else map_visibility.visible = true; } else if (xml.name() == literal::templates) { XmlElementReader templates_element(xml); auto num_template_visibilities = templates_element.attribute(XmlStreamLiteral::count); template_visibilities.reserve(qBound(20u, num_template_visibilities, 1000u)); all_templates_hidden = templates_element.attribute(literal::hidden); while (xml.readNextStartElement()) { if (xml.name() == literal::ref) { XmlElementReader ref_element(xml); int pos = ref_element.attribute(literal::template_string); if (pos >= 0 && pos < map->getNumTemplates()) { TemplateVisibility vis { qBound(0.0f, ref_element.attribute(literal::opacity), 1.0f), ref_element.attribute(literal::visible) }; setTemplateVisibilityHelper(map->getTemplate(pos), vis); } } else xml.skipCurrentElement(); } } else xml.skipCurrentElement(); // unsupported } emit viewChanged(CenterChange | ZoomChange | RotationChange); emit visibilityChanged(MultipleFeatures, true); } void MapView::updateAllMapWidgets() { emit visibilityChanged(MultipleFeatures, true); } MapCoord MapView::viewToMap(double x, double y) const { return MapCoord(view_to_map.m11() * x + view_to_map.m12() * y + view_to_map.m13(), view_to_map.m21() * x + view_to_map.m22() * y + view_to_map.m23()); } MapCoordF MapView::viewToMapF(double x, double y) const { return MapCoordF(view_to_map.m11() * x + view_to_map.m12() * y + view_to_map.m13(), view_to_map.m21() * x + view_to_map.m22() * y + view_to_map.m23()); } QPointF MapView::mapToView(MapCoord coords) const { return QPointF(map_to_view.m11() * coords.x() + map_to_view.m12() * coords.y() + map_to_view.m13(), map_to_view.m21() * coords.x() + map_to_view.m22() * coords.y() + map_to_view.m23()); } QPointF MapView::mapToView(MapCoordF coords) const { return QPointF(map_to_view.m11() * coords.x() + map_to_view.m12() * coords.y() + map_to_view.m13(), map_to_view.m21() * coords.x() + map_to_view.m22() * coords.y() + map_to_view.m23()); } qreal MapView::lengthToPixel(qreal length) const { return Util::mmToPixelPhysical(zoom * length / 1000.0); } qreal MapView::pixelToLength(qreal pixel) const { return Util::pixelToMMPhysical(pixel / zoom) * 1000.0; } QRectF MapView::calculateViewedRect(QRectF rect) const { auto top_left = viewToMapF(rect.topLeft()); auto top_right = viewToMapF(rect.topRight()); auto bottom_right = viewToMapF(rect.bottomRight()); auto bottom_left = viewToMapF(rect.bottomLeft()); rect = QRectF{ top_left, bottom_right }.normalized(); rectInclude(rect, top_right); rectInclude(rect, bottom_left); rect.adjust(-0.001, -0.001, +0.001, +0.001); return rect; } QRectF MapView::calculateViewBoundingBox(QRectF rect) const { auto top_left = mapToView(static_cast(rect.topLeft())); auto top_right = mapToView(static_cast(rect.topRight())); auto bottom_right = mapToView(static_cast(rect.bottomRight())); auto bottom_left = mapToView(static_cast(rect.bottomLeft())); rect = QRectF{ top_left, bottom_right }.normalized(); rectInclude(rect, top_right); rectInclude(rect, bottom_left); rect.adjust(-1.0, -1.0, +1.0, +1.0); return rect; } void MapView::setPanOffset(QPoint offset) { if (offset != pan_offset) { pan_offset = offset; emit panOffsetChanged(offset); } } void MapView::finishPanning(QPoint offset) { setPanOffset({0,0}); try { auto rotated_offset = MapCoord::fromNative64(qRound64(-pixelToLength(offset.x())), qRound64(-pixelToLength(offset.y())) ); auto rotated_offset_f = MapCoordF{ rotated_offset }; rotated_offset_f.rotate(-rotation); auto move = MapCoord{ rotated_offset_f }; setCenter(center() + move); } catch (std::range_error&) { // Do nothing } } void MapView::zoomSteps(double num_steps, QPointF cursor_pos_view) { auto zoom_to = getZoom() * pow(sqrt(2.0), num_steps); setZoom(zoom_to, cursor_pos_view); } void MapView::zoomSteps(double num_steps) { auto zoom_to = getZoom() * pow(sqrt(2.0), num_steps); setZoom(zoom_to); } void MapView::setZoom(double value, QPointF center) { auto pos = this->center(); auto zoom_pos = viewToMap(center); auto old_zoom = getZoom(); setZoom(value); if (!qFuzzyCompare(old_zoom, getZoom())) { auto zoom_factor = getZoom() / old_zoom ; setCenter(zoom_pos + (pos - zoom_pos) / zoom_factor); } } void MapView::setZoom(double value) { zoom = qBound(zoom_out_limit, value, zoom_in_limit); updateTransform(); emit viewChanged(ZoomChange); } void MapView::setRotation(double value) { rotation = value; updateTransform(); emit viewChanged(RotationChange); } void MapView::setCenter(MapCoord pos) { center_pos = pos; updateTransform(); emit viewChanged(CenterChange); } void MapView::updateTransform() { double final_zoom = calculateFinalZoomFactor(); double final_zoom_cosr = final_zoom * cos(rotation); double final_zoom_sinr = final_zoom * sin(rotation); auto center_x = center_pos.x(); auto center_y = center_pos.y(); // Create map_to_view map_to_view.setMatrix(final_zoom_cosr, -final_zoom_sinr, -final_zoom_cosr * center_x + final_zoom_sinr * center_y, final_zoom_sinr, final_zoom_cosr, -final_zoom_sinr * center_x - final_zoom_cosr * center_y, 0, 0, 1); view_to_map = map_to_view.inverted(); world_transform = map_to_view.transposed(); } TemplateVisibility MapView::effectiveMapVisibility() const { if (all_templates_hidden) return { 1.0f, true }; else if (map_visibility.opacity < 0.005f) return { 0.0f, false }; else return map_visibility; } void MapView::setMapVisibility(TemplateVisibility vis) { if (map_visibility != vis) { map_visibility = vis; emit visibilityChanged(VisibilityFeature::MapVisible, vis.visible && vis.opacity > 0, nullptr); } } MapView::TemplateVisibilityVector::const_iterator MapView::findVisibility(const Template* temp) const { return std::find_if(begin(template_visibilities), end(template_visibilities), [temp](const TemplateVisibilityEntry& entry) { return entry.temp == temp; } ); } MapView::TemplateVisibilityVector::iterator MapView::findVisibility(const Template* temp) { return std::find_if(begin(template_visibilities), end(template_visibilities), [temp](const TemplateVisibilityEntry& entry) { return entry.temp == temp; } ); } bool MapView::isTemplateVisible(const Template* temp) const { auto entry = findVisibility(temp); return entry != end(template_visibilities) && entry->visible && entry->opacity > 0; } TemplateVisibility MapView::getTemplateVisibility(const Template* temp) const { auto entry = findVisibility(temp); if (entry != end(template_visibilities)) return *entry; else return { 1.0f, false }; } void MapView::setTemplateVisibility(Template* temp, TemplateVisibility vis) { auto visible = vis.visible && vis.opacity > 0; if (visible && temp->getTemplateState() != Template::Loaded && !templateLoadingBlocked()) { vis.visible = visible = temp->loadTemplateFile(false); } if (setTemplateVisibilityHelper(temp, vis)) { emit visibilityChanged(VisibilityFeature::TemplateVisible, visible, temp); } } bool MapView::setTemplateVisibilityHelper(const Template *temp, TemplateVisibility vis) { auto stored = findVisibility(temp); if (stored == end(template_visibilities)) { template_visibilities.emplace_back(temp, vis); return true; } if (*stored != vis) { stored->opacity = vis.opacity; stored->visible = vis.visible; return true; } return false; } void MapView::onTemplateAdded(int, Template* temp) { setTemplateVisibility(temp, { 1.0f, true }); } void MapView::onTemplateDeleted(int, const Template* temp) { template_visibilities.erase(findVisibility(temp)); } void MapView::setAllTemplatesHidden(bool value) { if (all_templates_hidden != value) { all_templates_hidden = value; emit visibilityChanged(VisibilityFeature::AllTemplatesHidden, value); } } void MapView::setGridVisible(bool visible) { if (grid_visible != visible) { grid_visible = visible; emit visibilityChanged(VisibilityFeature::GridVisible, visible); } } void MapView::setOverprintingSimulationEnabled(bool enabled) { if (overprinting_simulation_enabled != enabled) { overprinting_simulation_enabled = enabled; emit visibilityChanged(VisibilityFeature::OverprintingEnabled, enabled); } } void MapView::setTemplateLoadingBlocked(bool blocked) { template_loading_blocked = blocked; } } // namespace OpenOrienteering mapper-0.8.1.1/src/core/map_view.h000066400000000000000000000324451325266516600166660ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2014-2016 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_MAP_VIEW_H #define OPENORIENTEERING_MAP_VIEW_H #include #include #include #include #include #include #include #include #include "map_coord.h" class QIODevice; class QLatin1String; class QRectF; class QXmlStreamReader; class QXmlStreamWriter; namespace OpenOrienteering { class Map; class Template; /** * Contains the visibility information for a template (or the map). */ class TemplateVisibility { public: /** Opacity from 0.0f (invisible) to 1.0f (opaque) */ float opacity; /** Visibility flag */ bool visible; }; bool operator==(TemplateVisibility lhs, TemplateVisibility rhs); bool operator!=(TemplateVisibility lhs, TemplateVisibility rhs); /** * Stores view position, zoom, rotation and grid / template visibilities * to define a view onto a map. * * These parameters define the view coordinates with origin at the view's center, * measured in pixels. The class provides methods to convert between view * coordinates and map coordinates (defined as millimeter on map paper). */ class MapView : public QObject { Q_OBJECT Q_FLAGS(ChangeFlags VisibilityFeature) public: enum ChangeFlag { NoChange = 0, CenterChange = 1, ZoomChange = 2, RotationChange = 4, }; Q_DECLARE_FLAGS(ChangeFlags, ChangeFlag) enum VisibilityFeature { MultipleFeatures = 0, GridVisible = 1, OverprintingEnabled = 2, AllTemplatesHidden = 4, TemplateVisible = 8, MapVisible = 16, }; /** * Creates a default view looking at the origin. * * The parameter map must not be null. */ MapView(QObject* parent, Map* map); /** Creates a default view looking at the origin. * * The map takes ownership of the map view. It must not be null. */ MapView(Map* map); /** Destroys the map view. */ ~MapView() override; /** Loads the map view in the old "native" format from the file. */ void load(QIODevice* file, int version); /** * Saves the map view state to an XML stream. * @param xml The XML output stream. * @param element_name The name of the element which will be written. * @param template_details Save template visibilities (default: true) */ void save(QXmlStreamWriter& xml, const QLatin1String& element_name, bool template_details = true) const; /** Loads the map view state from the current element of an xml stream. */ void load(QXmlStreamReader& xml); /** * Redraws all map widgets completely. * * Note that this calls QWidget::update() which does not cause an immediate * repaint; instead it schedules a paint event. * * Completely repainting widgets can be slow. * Try to do partial updates instead, if possible. */ void updateAllMapWidgets(); /** Converts x, y (with origin at the center of the view) to map coordinates */ MapCoord viewToMap(double x, double y) const; /** Converts the point (with origin at the center of the view) to map coordinates */ MapCoord viewToMap(QPointF point) const; /** Converts x, y (with origin at the center of the view) to map coordinates */ MapCoordF viewToMapF(double x, double y) const; /** Converts the point (with origin at the center of the view) to map coordinates */ MapCoordF viewToMapF(QPointF point) const; /// Converts map coordinates to view coordinates (with origin at the center of the view) QPointF mapToView(MapCoord coords) const; /// Converts map coordinates to view coordinates (with origin at the center of the view) QPointF mapToView(MapCoordF coords) const; /** * Converts a length from native map coordinates to the current length in view pixels. */ qreal lengthToPixel(qreal length) const; /** * Converts a length from current view pixels to native map coordinates. */ qreal pixelToLength(qreal pixel) const; /** * Calculates the bounding box of the map coordinates which can be viewed * using the given view coordinates rect */ QRectF calculateViewedRect(QRectF view_rect) const; /** * Calculates the bounding box in view coordinates * of the given map coordinates rect */ QRectF calculateViewBoundingBox(QRectF map_rect) const; /** * Returns a QTransform suitable for QPainter, so objects defined in * map coordinates will be drawn at their view coordinates. Append a * viewport transformation to this to get a complete map-to-viewport transformation * which makes the view center appear at the viewport center. * * Note: The transform is to be combined with the painter's existing transform. */ const QTransform& worldTransform() const; // Panning /** Returns the current pan offset (when dragging the map). */ QPoint panOffset() const; /** Sets the current pan offset while the map is being dragged. */ void setPanOffset(QPoint offset); /** * Finishes panning the map. * * @param offset The final offset, relative to the start of the operation. */ void finishPanning(QPoint offset); /** Returns the map this view is defined on. */ const Map* getMap() const; /** Returns the map this view is defined on. */ Map* getMap(); /** * Zooms the maps (in steps), preserving the given cursor position. * * @param num_steps Number of zoom steps to zoom in. Negative numbers zoom out. * @param cursor_pos_view The cursor position in view coordinates, must be * set if preserve_cursor_pos is used. */ void zoomSteps(double num_steps, QPointF cursor_pos_view); /** * Zooms the maps (in steps), preserving the center of the view. * * @param num_steps Number of zoom steps to zoom in. Negative numbers zoom out. */ void zoomSteps(double num_steps); /** * Returns the final zoom factor for use in transformations. * Depends on the pixel per millimeter of the display. */ double calculateFinalZoomFactor() const; /** Returns the raw zoom facor, see also calculateFinalZoomFactor(). */ double getZoom() const; /** Sets the zoom factor relative to the given point.*/ void setZoom(double value, QPointF center); /** Sets the zoom factor. */ void setZoom(double value); /** Returns the view rotation (in radians). */ double getRotation() const; /** Sets the view roation (in radians). */ void setRotation(double value); /** Returns the position of the view center. */ MapCoord center() const; /** Sets the position of the view center. */ void setCenter(MapCoord pos); // Map and template visibilities /** Returns the effectiv visibility settings of the map drawing. * * Other than getMapVisibility, this will always return an (100 %) opaque, * visible configuration when areAllTemplatesHidden() is true, * and it return an invisible configuration when the map's opacity is * below 0.005. */ TemplateVisibility effectiveMapVisibility() const; /** Returns the visibility settings of the map drawing. */ TemplateVisibility getMapVisibility() const; void setMapVisibility(TemplateVisibility vis); /** * Checks if the template is visible without creating * a template visibility object if none exists */ bool isTemplateVisible(const Template* temp) const; /** * Returns the template visibility. * * If the template is unknown, returns default settings. */ TemplateVisibility getTemplateVisibility(const Template* temp) const; /** * Sets the template visibility, and emits a change signal. */ void setTemplateVisibility(Template* temp, TemplateVisibility vis); /** Enables or disables hiding all templates in this view */ void setAllTemplatesHidden(bool value); /** * Returns if the "hide all templates" toggle is active. * See also setHideAllTemplates(). */ bool areAllTemplatesHidden() const; /** Returns if the map grid is visible. */ bool isGridVisible() const; /** Sets the map grid visibility. */ void setGridVisible(bool visible); /** Returns if overprinting simulation is enabled. */ bool isOverprintingSimulationEnabled() const; /** Enables or disables overprinting simulation. */ void setOverprintingSimulationEnabled(bool enabled); /** Temporarily blocks automatic template loading on visibility changes. */ void setTemplateLoadingBlocked(bool blocked); /** Returns true when template loading on visibility changes is disabled. */ bool templateLoadingBlocked() const { return template_loading_blocked; } signals: /** * Indicates a change of the viewed area of the map. * * @param change The aspects that have chaneged. */ void viewChanged(ChangeFlags change); /** * Indicates a change of the pan offset. */ void panOffsetChanged(QPoint offset); /** * Indicates a particular change of visibility. * * @param feature The map view feature that has changed. * @param active The features current state of activation. * @param temp If a the feature is a template, a pointer to this template. */ void visibilityChanged(VisibilityFeature feature, bool active, const Template* temp = nullptr); public: // Static /** The global zoom in limit for the zoom factor. */ static const double zoom_in_limit; /** The global zoom out limit for the zoom factor. */ static const double zoom_out_limit; protected: /** * Sets the template visibility without emitting signals. */ bool setTemplateVisibilityHelper(const Template *temp, TemplateVisibility vis); /** * Creates the visibility data when a template is added to the map. */ void onTemplateAdded(int pos, Template* temp); /** * Removes the visibility data when a template is deleted. */ void onTemplateDeleted(int pos, const Template* temp); private: Q_DISABLE_COPY(MapView) struct TemplateVisibilityEntry : public TemplateVisibility { const Template* temp; TemplateVisibilityEntry() = default; TemplateVisibilityEntry(const TemplateVisibilityEntry&) = default; TemplateVisibilityEntry(TemplateVisibilityEntry&&) = default; TemplateVisibilityEntry& operator=(const TemplateVisibilityEntry&) = default; TemplateVisibilityEntry& operator=(TemplateVisibilityEntry&&) = default; TemplateVisibilityEntry(const Template* temp, TemplateVisibility vis) : TemplateVisibility(vis) , temp(temp) {} }; typedef std::vector TemplateVisibilityVector; void updateTransform(); // recalculates the x_to_y matrices TemplateVisibilityVector::const_iterator findVisibility(const Template* temp) const; TemplateVisibilityVector::iterator findVisibility(const Template* temp); Map* map; double zoom; // factor double rotation; // counterclockwise 0 to 2*PI. This is the viewer rotation, so the map is rotated clockwise MapCoord center_pos;// position of the viewer, positive values move the map to the left; the position is in 1/1000 mm QPoint pan_offset; // the distance the content of the view was dragged with the mouse, in pixels QTransform view_to_map; QTransform map_to_view; QTransform world_transform; TemplateVisibility map_visibility; TemplateVisibilityVector template_visibilities; bool all_templates_hidden; bool grid_visible; bool overprinting_simulation_enabled; bool template_loading_blocked; }; // ### TemplateVisibility inline code ### inline bool operator==(TemplateVisibility lhs, TemplateVisibility rhs) { return lhs.visible == rhs.visible && qFuzzyCompare(1.0f+rhs.opacity, 1.0f+lhs.opacity); } inline bool operator!=(TemplateVisibility lhs, TemplateVisibility rhs) { return !(lhs == rhs); } // ### MapView inline code ### inline MapCoord MapView::viewToMap(QPointF point) const { return viewToMap(point.x(), point.y()); } inline MapCoordF MapView::viewToMapF(QPointF point) const { return viewToMapF(point.x(), point.y()); } inline const QTransform& MapView::worldTransform() const { return world_transform; } inline Map* MapView::getMap() { return map; } inline const Map* MapView::getMap() const { return map; } inline double MapView::calculateFinalZoomFactor() const { return lengthToPixel(1000.0); } inline double MapView::getZoom() const { return zoom; } inline double MapView::getRotation() const { return rotation; } inline MapCoord MapView::center() const { return center_pos; } inline QPoint MapView::panOffset() const { return pan_offset; } inline TemplateVisibility MapView::getMapVisibility() const { return map_visibility; } inline bool MapView::areAllTemplatesHidden() const { return all_templates_hidden; } inline bool MapView::isGridVisible() const { return grid_visible; } inline bool MapView::isOverprintingSimulationEnabled() const { return overprinting_simulation_enabled; } } // namespace OpenOrienteering Q_DECLARE_OPERATORS_FOR_FLAGS(OpenOrienteering::MapView::ChangeFlags) #endif mapper-0.8.1.1/src/core/objects/000077500000000000000000000000001325266516600163275ustar00rootroot00000000000000mapper-0.8.1.1/src/core/objects/boolean_tool.cpp000066400000000000000000001052351325266516600215150ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2014, 2015 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "boolean_tool.h" #include #include #include #include #include #include #include #include #include #include "core/map.h" #include "core/map_coord.h" #include "core/map_part.h" #include "core/path_coord.h" #include "core/virtual_path.h" #include "core/objects/object.h" #include "core/symbols/symbol.h" #include "undo/object_undo.h" #include "undo/undo.h" #include "util/util.h" //### Local helper functions /* * NB: qHash must be in the same namespace as the QHash key. * This is a C++ language issue, not a Qt issue. * Cf. https://bugreports.qt-project.org/browse/QTBUG-34912 */ namespace ClipperLib { /** * Implements qHash for ClipperLib::IntPoint. * * This is needed to use ClipperLib::IntPoint as QHash key. */ uint qHash(const IntPoint& point, uint seed) { quint64 tmp = (quint64(point.X) & 0xffffffff) | (quint64(point.Y) << 32); return ::qHash(tmp, seed); // must use :: namespace to prevent endless recursion } } // namespace ClipperLib namespace OpenOrienteering { /** * Removes flags from the coordinate to be able to use it in the reconstruction. */ static MapCoord resetCoordinate(MapCoord in) { in.setFlags(0); return in; } /** * Compares a MapCoord and ClipperLib::IntPoint. */ bool operator==(const MapCoord& lhs, const ClipperLib::IntPoint& rhs) { return lhs.nativeX() == rhs.X && lhs.nativeY() == rhs.Y; } /** * Compares a MapCoord and ClipperLib::IntPoint. */ bool operator==(const ClipperLib::IntPoint& lhs, const MapCoord& rhs) { return rhs == lhs; } //### BooleanTool ### BooleanTool::BooleanTool(Operation op, Map* map) : op(op) , map(map) { ; // nothing } bool BooleanTool::execute() { // Check basic prerequisite Object* const primary_object = map->getFirstSelectedObject(); if (primary_object->getType() != Object::Path) { qWarning("The first selected object must be a path."); return false; // in release build } // Filter selected objects into in_objects PathObjects in_objects; in_objects.reserve(map->getNumSelectedObjects()); for (Object* object : map->selectedObjects()) { if (object->getType() == Object::Path) { PathObject* const path = object->asPath(); if (op != MergeHoles || (object->getSymbol()->getContainedTypes() & Symbol::Area && path->parts().size() > 1 )) { in_objects.push_back(path); } } } // Perform the core operation QScopedPointer undo_step(new CombinedUndoStep(map)); PathObjects out_objects; if (!executeForObjects(primary_object->asPath(), in_objects, out_objects, *undo_step)) { Q_ASSERT(out_objects.size() == 0); return false; // in release build } map->push(undo_step.take()); map->setObjectsDirty(); map->emitSelectionChanged(); map->emitSelectionEdited(); return true; } bool BooleanTool::executePerSymbol() { PathObjects backlog; backlog.reserve(map->getNumSelectedObjects()); // Filter area objects into initial backlog for (Object* object : map->selectedObjects()) { if (object->getSymbol()->getContainedTypes() & Symbol::Area) backlog.push_back(object->asPath()); } QScopedPointer undo_step(new CombinedUndoStep(map)); PathObjects new_backlog; new_backlog.reserve(backlog.size()/2); PathObjects in_objects; in_objects.reserve(backlog.size()/2); PathObjects out_objects; while (!backlog.empty()) { PathObject* const primary_object = backlog.front(); const Symbol* const symbol = primary_object->getSymbol(); // Filter objects by symbol into in_objects or new_backlog, respectively new_backlog.clear(); in_objects.clear(); for (PathObject* object : backlog) { if (object->getSymbol() == symbol) { if (op != MergeHoles || (object->getSymbol()->getContainedTypes() & Symbol::Area && object->parts().size() > 1 )) { in_objects.push_back(object); } } else { new_backlog.push_back(object); } } backlog.swap(new_backlog); // Short cut for single object of given symbol if (in_objects.size() == 1) continue; // Perform the core operation out_objects.clear(); executeForObjects(primary_object, in_objects, out_objects, *undo_step); } bool const have_changes = undo_step->getNumSubSteps() > 0; if (have_changes) { map->push(undo_step.take()); map->setObjectsDirty(); map->emitSelectionChanged(); map->emitSelectionEdited(); } return have_changes; } bool BooleanTool::executeForObjects(PathObject* subject, PathObjects& in_objects, PathObjects& out_objects, CombinedUndoStep& undo_step) { if (!executeForObjects(subject, in_objects, out_objects)) { Q_ASSERT(out_objects.size() == 0); return false; // in release build } // Add original objects to undo step, and remove them from map. QScopedPointer add_step(new AddObjectsUndoStep(map)); for (PathObject* object : in_objects) { if (op != Difference || object == subject) { add_step->addObject(object, object); } } // Keep as separate loop to get the correct index in the previous loop for (PathObject* object : in_objects) { if (op != Difference || object == subject) { map->removeObjectFromSelection(object, false); map->getCurrentPart()->deleteObject(object, true); object->setMap(map); // necessary so objects are saved correctly } } // Add resulting objects to map, and create delete step for them QScopedPointer delete_step(new DeleteObjectsUndoStep(map)); MapPart* part = map->getCurrentPart(); for (PathObject* object : out_objects) { map->addObject(object); map->addObjectToSelection(object, false); } // Keep as separate loop to get the correct index in the previous loop for (PathObject* object : out_objects) { delete_step->addObject(part->findObjectIndex(object)); } undo_step.push(add_step.take()); undo_step.push(delete_step.take()); return true; } bool BooleanTool::executeForObjects(PathObject* subject, PathObjects& in_objects, PathObjects& out_objects) { // Convert the objects to Clipper polygons and // create a hash map, mapping point positions to the PathCoords. // These paths are to be regarded as closed. PolyMap polymap; ClipperLib::Paths subject_polygons; pathObjectToPolygons(subject, subject_polygons, polymap); ClipperLib::Paths clip_polygons; for (PathObject* object : in_objects) { if (object != subject) { pathObjectToPolygons(object, clip_polygons, polymap); } } // Do the operation. ClipperLib::Clipper clipper; clipper.AddPaths(subject_polygons, ClipperLib::ptSubject, true); clipper.AddPaths(clip_polygons, ClipperLib::ptClip, true); ClipperLib::ClipType clip_type; ClipperLib::PolyFillType fill_type = ClipperLib::pftNonZero; switch (op) { case Union: clip_type = ClipperLib::ctUnion; break; case Intersection: clip_type = ClipperLib::ctIntersection; break; case Difference: clip_type = ClipperLib::ctDifference; break; case XOr: clip_type = ClipperLib::ctXor; break; case MergeHoles: clip_type = ClipperLib::ctUnion; fill_type = ClipperLib::pftPositive; break; default: qWarning("Undefined operation"); return false; } ClipperLib::PolyTree solution; bool success = clipper.Execute(clip_type, solution, fill_type, fill_type); if (success) { // Try to convert the solution polygons to objects again polyTreeToPathObjects(solution, out_objects, subject, polymap); } return success; } void BooleanTool::polyTreeToPathObjects(const ClipperLib::PolyTree& tree, PathObjects& out_objects, const PathObject* proto, const PolyMap& polymap) { for (int i = 0, count = tree.ChildCount(); i < count; ++i) outerPolyNodeToPathObjects(*tree.Childs[i], out_objects, proto, polymap); } void BooleanTool::outerPolyNodeToPathObjects(const ClipperLib::PolyNode& node, PathObjects& out_objects, const PathObject* proto, const PolyMap& polymap) { auto object = std::unique_ptr{ proto->duplicate() }; object->clearCoordinates(); try { polygonToPathPart(node.Contour, polymap, object.get()); for (int i = 0, i_count = node.ChildCount(); i < i_count; ++i) { polygonToPathPart(node.Childs[i]->Contour, polymap, object.get()); // Add outer polygons contained by (nested within) holes ... for (int j = 0, j_count = node.Childs[i]->ChildCount(); j < j_count; ++j) outerPolyNodeToPathObjects(*node.Childs[i]->Childs[j], out_objects, proto, polymap); } out_objects.push_back(object.release()); } catch (std::range_error&) { // Do nothing } } void BooleanTool::executeForLine(const PathObject* area, const PathObject* line, BooleanTool::PathObjects& out_objects) { if (op != BooleanTool::Intersection && op != BooleanTool::Difference) { Q_ASSERT_X(false, "BooleanTool::executeForLine", "Only intersection and difference are supported."); return; // no-op in release build } if (line->parts().size() != 1) { Q_ASSERT_X(false, "BooleanTool::executeForLine", "Only single-part lines are supported."); return; // no-op in release build } // Calculate all intersection points of line with the area path PathObject::Intersections intersections; line->calcAllIntersectionsWith(area, intersections); intersections.normalize(); PathObject* first_segment = nullptr; PathObject* last_segment = nullptr; const auto& part = line->parts().front(); const auto& path_coords = part.path_coords; // Only one segment? if (intersections.empty()) { auto middle_length = part.length(); auto middle = SplitPathCoord::at(path_coords, middle_length); if (area->isPointInsideArea(middle.pos) == (op == BooleanTool::Intersection)) out_objects.push_back(line->duplicate()); return; } // First segment auto middle_length = intersections[0].length / 2; auto middle = SplitPathCoord::at(path_coords, middle_length); if (area->isPointInsideArea(middle.pos) == (op == BooleanTool::Intersection)) { PathObject* segment = line->duplicate(); segment->changePathBounds(0, 0.0, intersections[0].length); first_segment = segment; } // Middle segments for (std::size_t i = 0; i < intersections.size() - 1; ++i) { middle_length = (intersections[i].length + intersections[i+1].length) / 2; auto middle = SplitPathCoord::at(path_coords, middle_length); if (area->isPointInsideArea(middle.pos) == (op == BooleanTool::Intersection)) { PathObject* segment = line->duplicate(); segment->changePathBounds(0, intersections[i].length, intersections[i+1].length); out_objects.push_back(segment); } } // Last segment middle_length = (part.length() + intersections.back().length) / 2; middle = SplitPathCoord::at(path_coords, middle_length); if (area->isPointInsideArea(middle.pos) == (op == BooleanTool::Intersection)) { PathObject* segment = line->duplicate(); segment->changePathBounds(0, intersections.back().length, part.length()); last_segment = segment; } // If the line was closed at the beginning, merge the first and last segments if they were kept both. if (part.isClosed() && first_segment && last_segment) { last_segment->connectPathParts(0, first_segment, 0, false); delete first_segment; out_objects.push_back(last_segment); } else { if (first_segment) out_objects.push_back(first_segment); if (last_segment) out_objects.push_back(last_segment); } } void BooleanTool::pathObjectToPolygons( const PathObject* object, ClipperLib::Paths& polygons, PolyMap& polymap) { object->update(); auto coords = object->getRawCoordinateVector(); polygons.reserve(polygons.size() + object->parts().size()); for (const auto& part : object->parts()) { const PathCoordVector& path_coords = part.path_coords; auto path_coords_end = path_coords.size(); if (part.isClosed()) --path_coords_end; ClipperLib::Path polygon; polygon.reserve(path_coords_end); for (auto i = 0u; i < path_coords_end; ++i) { auto& path_coord = path_coords[i]; if (path_coord.param == 0.0) { auto point = coords[path_coord.index]; polygon.push_back(ClipperLib::IntPoint(point.nativeX(), point.nativeY())); } else { auto point = MapCoord { path_coord.pos }; polygon.push_back(ClipperLib::IntPoint(point.nativeX(), point.nativeY())); } polymap.insertMulti(polygon.back(), std::make_pair(&part, &path_coord)); } bool orientation = Orientation(polygon); if ( (&part == &object->parts().front()) != orientation ) { std::reverse(polygon.begin(), polygon.end()); } // Push_back shall move the polygon. static_assert(std::is_nothrow_move_constructible::value, "ClipperLib::Path must be nothrow move constructible"); polygons.push_back(polygon); } } void BooleanTool::polygonToPathPart(const ClipperLib::Path& polygon, const PolyMap& polymap, PathObject* object) { auto num_points = polygon.size(); if (num_points < 3) return; // Index of first used point in polygon auto part_start_index = 0u; auto cur_info = PathCoordInfo{ nullptr, nullptr }; // Check if we can find either an unknown intersection point // or a path coord with parameter 0. // This gives a starting point to search for curves to rebuild // (because we cannot start in the middle of a curve) for (; part_start_index < num_points; ++part_start_index) { auto current_point = polygon.at(part_start_index); if (!polymap.contains(current_point)) break; if (polymap.value(current_point).second->param == 0.0) { cur_info = polymap.value(current_point); break; } } if (part_start_index == num_points) { // Did not find a valid starting point. Return the part as a polygon. for (auto i = 0u; i < num_points; ++i) object->addCoordinate(MapCoord::fromNative64(polygon.at(i).X, polygon.at(i).Y), i == 0); object->parts().back().setClosed(true, true); return; } // Add the first point to the object rebuildCoordinate(part_start_index, polygon, polymap, object, true); // Index of first segment point in polygon auto segment_start_index = part_start_index; bool have_sequence = false; bool sequence_increasing = false; bool stop_before = false; // Advance along the boundary and rebuild the curve for every sequence // of path coord pointers with the same path and index. auto i = part_start_index; do { ++i; if (i >= num_points) i = 0; PathCoordInfo new_info{ nullptr, nullptr }; auto new_point = polygon.at(i); if (polymap.contains(new_point)) new_info = polymap.value(new_point); if (cur_info.first && cur_info.first == new_info.first) { // Same original part auto cur_coord_index = cur_info.second->index; MapCoord& cur_coord = cur_info.first->path->getCoordinate(cur_coord_index); auto new_coord_index = new_info.second->index; MapCoord& new_coord = new_info.first->path->getCoordinate(new_coord_index); auto cur_coord_index_adjusted = cur_coord_index; if (cur_coord_index_adjusted == new_info.first->first_index) cur_coord_index_adjusted = new_info.first->last_index; auto new_coord_index_adjusted = new_coord_index; if (new_coord_index_adjusted == new_info.first->first_index) new_coord_index_adjusted = new_info.first->last_index; if (cur_coord_index == new_coord_index) { // Somewhere on a curve bool param_increasing = new_info.second->param > cur_info.second->param; if (!have_sequence) { have_sequence = true; sequence_increasing = param_increasing; } else if (have_sequence && sequence_increasing != param_increasing) { stop_before = true; } } else if (new_info.second->param == 0.0 && ( (cur_coord.isCurveStart() && new_coord_index_adjusted == cur_coord_index + 3) || (!cur_coord.isCurveStart() && new_coord_index_adjusted == cur_coord_index + 1) ) ) { // Original curve is from cur_coord_index to new_coord_index_adjusted. if (!have_sequence) { have_sequence = true; sequence_increasing = true; } else { stop_before = !sequence_increasing; } } else if (cur_info.second->param == 0.0 && ( (new_coord.isCurveStart() && new_coord_index + 3 == cur_coord_index_adjusted) || (!new_coord.isCurveStart() && new_coord_index + 1 == cur_coord_index_adjusted) ) ) { // Original curve is from new_coord_index to cur_coord_index_adjusted. if (!have_sequence) { have_sequence = true; sequence_increasing = false; } else { stop_before = sequence_increasing; } } else if ((segment_start_index + 1) % num_points != i) { // Not immediately after segment_start_index stop_before = true; } } if (i == part_start_index || stop_before || (new_info.second && new_info.second->param == 0.0) || (cur_info.first && (cur_info.first != new_info.first || cur_info.second->index != new_info.second->index) && i != (segment_start_index + 1) % num_points) || !new_info.first) { if (stop_before) { if (i == 0) i = num_points - 1; else --i; } if (have_sequence) // A sequence of at least two points belonging to the same curve rebuildSegment(segment_start_index, i, sequence_increasing, polygon, polymap, object); else // A single straight edge rebuildCoordinate(i, polygon, polymap, object); if (stop_before) { ++i; if (i >= num_points) i = 0; rebuildCoordinate(i, polygon, polymap, object); stop_before = false; } segment_start_index = i; have_sequence = false; } cur_info = new_info; } while (i != part_start_index); object->parts().back().connectEnds(); } void BooleanTool::rebuildSegment( ClipperLib::Path::size_type start_index, ClipperLib::Path::size_type end_index, bool sequence_increasing, const ClipperLib::Path& polygon, const PolyMap& polymap, PathObject* object) { auto num_points = polygon.size(); object->getCoordinate(object->getCoordinateCount() - 1).setCurveStart(true); if ((start_index + 1) % num_points == end_index) { // This could happen for a straight line or a very flat curve - take coords directly from original rebuildTwoIndexSegment(start_index, end_index, sequence_increasing, polygon, polymap, object); return; } // Get polygon point coordinates const auto& start_point = polygon.at(start_index); const auto& second_point = polygon.at((start_index + 1) % num_points); const auto& second_last_point = polygon.at((end_index ? end_index : num_points) - 1); const auto& end_point = polygon.at(end_index); // Try to find the middle coordinates in the same part bool found = false; PathCoordInfo second_info{ nullptr, nullptr }; PathCoordInfo second_last_info{ nullptr, nullptr }; for (auto second_it = polymap.find(second_point); second_it != polymap.end(); ++second_it) { for (auto second_last_it = polymap.find(second_last_point); second_last_it != polymap.end() && second_last_it.key() == second_last_point; ++second_last_it) { if (second_it->first == second_last_it->first && second_it->second->index == second_last_it->second->index) { // Same part found = true; second_info = *second_it; second_last_info = *second_last_it; break; } } if (found) break; } if (!found) { // Need unambiguous path part information to find the original object with high probability qDebug() << "BooleanTool::rebuildSegment: cannot identify original object!"; rebuildSegmentFromPathOnly(start_point, second_point, second_last_point, end_point, object); return; } const PathPart* original_path = second_info.first; // Try to find the outer coordinates in the same part PathCoordInfo start_info{ nullptr, nullptr }; for (auto start_it = polymap.find(start_point); start_it != polymap.end() && start_it.key() == start_point; ++start_it) { if (start_it->first == original_path) { start_info = *start_it; break; } } Q_ASSERT(!start_info.first || start_info.first == second_info.first); PathCoordInfo end_info{ nullptr, nullptr }; for (auto end_it = polymap.find(end_point); end_it != polymap.end() && end_it.key() == end_point; ++end_it) { if (end_it->first == original_path) { end_info = *end_it; break; } } Q_ASSERT(!end_info.first || end_info.first == second_info.first); const PathObject* original = original_path->path; auto edge_start = second_info.second->index; if (edge_start == second_info.first->last_index) edge_start = second_info.first->first_index; // Find out start tangent auto start_param = 0.0; MapCoord start_coord = MapCoord::fromNative64(start_point.X, start_point.Y); MapCoord start_tangent; MapCoord end_tangent; MapCoord end_coord; double start_error_sq, end_error_sq; // Maximum difference in mm from reconstructed start and end coords to the // intersection points returned by Clipper const double error_bound = 0.4; if (sequence_increasing) { if ( second_info.second->param == 0.0 || ( start_info.first && start_info.second->param == 0.0 && ( start_info.second->index == edge_start || (start_info.second->index == start_info.first->last_index && start_info.first->first_index == edge_start) ) ) ) { // Take coordinates directly start_tangent = original->getCoordinate(edge_start + 1); end_tangent = original->getCoordinate(edge_start + 2); start_error_sq = start_coord.distanceSquaredTo(original->getCoordinate(edge_start + 0)); if (start_error_sq > error_bound) qDebug() << "BooleanTool::rebuildSegment: start error too high in increasing direct case: " << sqrt(start_error_sq); } else { // Approximate coords const PathCoord* prev_coord = second_info.second - 1; auto dx = second_point.X - start_point.X; auto dy = second_point.Y - start_point.Y; auto point_dist = 0.001 * sqrt(dx*dx + dy*dy); auto delta_start_param = (second_info.second->param - prev_coord->param) * point_dist / qMax(1e-7f, (second_info.second->clen - prev_coord->clen)); start_param = qBound(0.0, second_info.second->param - delta_start_param, 1.0); MapCoordF unused, o2, o3, o4; PathCoord::splitBezierCurve(MapCoordF(original->getCoordinate(edge_start + 0)), MapCoordF(original->getCoordinate(edge_start + 1)), MapCoordF(original->getCoordinate(edge_start + 2)), MapCoordF(original->getCoordinate(edge_start + 3)), start_param, unused, unused, o2, o3, o4); start_tangent = MapCoord(o3); end_tangent = MapCoord(o4); start_error_sq = start_coord.distanceSquaredTo(MapCoord(o2)); if (start_error_sq > error_bound) qDebug() << "BooleanTool::rebuildSegment: start error too high in increasing general case: " << sqrt(start_error_sq); } // Find better end point approximation and its tangent if ( second_last_info.second->param == 0.0 || (end_info.first && end_info.second->param == 0.0 && ( end_info.second->index == edge_start+3 || (end_info.second->index == end_info.first->first_index && end_info.first->last_index == edge_start+3) ) ) ) { // Take coordinates directly end_coord = original->getCoordinate(edge_start + 3); auto test_x = end_point.X - end_coord.nativeX(); auto test_y = end_point.Y - end_coord.nativeY(); end_error_sq = 0.001 * sqrt(test_x*test_x + test_y*test_y); if (end_error_sq > error_bound) qDebug() << "BooleanTool::rebuildSegment: end error too high in increasing direct case: " << sqrt(end_error_sq); } else { // Approximate coords const PathCoord* next_coord = second_last_info.second + 1; auto next_coord_param = next_coord->param; if (next_coord_param == 0.0) next_coord_param = 1.0; auto dx = end_point.X - second_last_point.X; auto dy = end_point.Y - second_last_point.Y; auto point_dist = 0.001 * sqrt(dx*dx + dy*dy); auto delta_end_param = (next_coord_param - second_last_info.second->param) * point_dist / qMax(1e-7f, (next_coord->clen - second_last_info.second->clen)); auto end_param = (second_last_info.second->param + delta_end_param - start_param) / (1.0 - start_param); MapCoordF o0, o1, o2, unused; PathCoord::splitBezierCurve(MapCoordF(start_coord), MapCoordF(start_tangent), MapCoordF(end_tangent), MapCoordF(original->getCoordinate(edge_start + 3)), end_param, o0, o1, o2, unused, unused); start_tangent = MapCoord(o0); end_tangent = MapCoord(o1); end_coord = MapCoord(o2); auto test_x = end_point.X - end_coord.nativeX(); auto test_y = end_point.Y - end_coord.nativeY(); end_error_sq = 0.001 * sqrt(test_x*test_x + test_y*test_y); if (end_error_sq > error_bound) qDebug() << "BooleanTool::rebuildSegment: end error too high in increasing general case: " << sqrt(end_error_sq); } } else // if (!sequence_increasing) { if ( second_info.second->param == 0.0 || ( start_info.first && start_info.second->param == 0.0 && ( start_info.second->index == edge_start+3 || (start_info.second->index == start_info.first->first_index && start_info.first->last_index == edge_start+3) ) ) ) { // Take coordinates directly start_tangent = original->getCoordinate(edge_start + 2); end_tangent = original->getCoordinate(edge_start + 1); start_error_sq = start_coord.distanceSquaredTo(original->getCoordinate(edge_start + 3)); if (start_error_sq > error_bound) qDebug() << "BooleanTool::rebuildSegment: start error too high in decreasing direct case: " << sqrt(start_error_sq); } else { // Approximate coords const PathCoord* next_coord = second_info.second + 1; auto next_coord_param = next_coord->param; if (next_coord_param == 0.0) next_coord_param = 1.0; auto dx = second_point.X - start_point.X; auto dy = second_point.Y - start_point.Y; auto point_dist = 0.001 * sqrt(dx*dx + dy*dy); auto delta_start_param = (next_coord_param - second_info.second->param) * point_dist / qMax(1e-7f, (next_coord->clen - second_info.second->clen)); start_param = qBound(0.0, 1.0 - second_info.second->param + delta_start_param, 1.0); MapCoordF unused, o2, o3, o4; PathCoord::splitBezierCurve(MapCoordF(original->getCoordinate(edge_start + 3)), MapCoordF(original->getCoordinate(edge_start + 2)), MapCoordF(original->getCoordinate(edge_start + 1)), MapCoordF(original->getCoordinate(edge_start + 0)), start_param, unused, unused, o2, o3, o4); start_tangent = MapCoord(o3); end_tangent = MapCoord(o4); start_error_sq = start_coord.distanceSquaredTo(MapCoord(o2)); if (start_error_sq > error_bound) qDebug() << "BooleanTool::rebuildSegment: start error too high in decreasing general case: " << sqrt(start_error_sq); } // Find better end point approximation and its tangent if ( second_last_info.second->param == 0.0 || ( end_info.first && end_info.second->param == 0.0 && ( end_info.second->index == edge_start || (end_info.second->index == end_info.first->last_index && end_info.first->first_index == edge_start) ) ) ) { // Take coordinates directly end_coord = original->getCoordinate(edge_start + 0); auto test_x = end_point.X - end_coord.nativeX(); auto test_y = end_point.Y - end_coord.nativeY(); end_error_sq = 0.001 * sqrt(test_x*test_x + test_y*test_y); if (end_error_sq > error_bound) qDebug() << "BooleanTool::rebuildSegment: end error too high in decreasing direct case: " << sqrt(end_error_sq); } else { // Approximate coords const PathCoord* prev_coord = second_last_info.second - 1; auto dx = end_point.X - second_last_point.X; auto dy = end_point.Y - second_last_point.Y; auto point_dist = 0.001 * sqrt(dx*dx + dy*dy); auto delta_end_param = (second_last_info.second->param - prev_coord->param) * point_dist / qMax(1e-7f, (second_last_info.second->clen - prev_coord->clen)); auto end_param = (1.0 - second_last_info.second->param + delta_end_param) / (1 - start_param); MapCoordF o0, o1, o2, unused; PathCoord::splitBezierCurve(MapCoordF(start_coord), MapCoordF(start_tangent), MapCoordF(end_tangent), MapCoordF(original->getCoordinate(edge_start + 0)), end_param, o0, o1, o2, unused, unused); start_tangent = MapCoord(o0); end_tangent = MapCoord(o1); end_coord = MapCoord(o2); auto test_x = end_point.X - end_coord.nativeX(); auto test_y = end_point.Y - end_coord.nativeY(); end_error_sq = 0.001 * sqrt(test_x*test_x + test_y*test_y); if (end_error_sq > error_bound) qDebug() << "BooleanTool::rebuildSegment: end error too high in decreasing general case: " << sqrt(end_error_sq); } } if (start_error_sq <= error_bound && end_error_sq <= error_bound) { // Rebuild bezier curve using information from original curve object->addCoordinate(start_tangent); object->addCoordinate(end_tangent); object->addCoordinate(resetCoordinate(end_coord)); } else { // Rebuild bezier curve approximately using tangents derived from result polygon rebuildSegmentFromPathOnly(start_point, second_point, second_last_point, end_point, object); } } void BooleanTool::rebuildSegmentFromPathOnly( const ClipperLib::IntPoint& start_point, const ClipperLib::IntPoint& second_point, const ClipperLib::IntPoint& second_last_point, const ClipperLib::IntPoint& end_point, PathObject* object) { MapCoord start_point_c = MapCoord::fromNative64(start_point.X, start_point.Y); MapCoord second_point_c = MapCoord::fromNative64(second_point.X, second_point.Y); MapCoord second_last_point_c = MapCoord::fromNative64(second_last_point.X, second_last_point.Y); MapCoord end_point_c = MapCoord::fromNative64(end_point.X, end_point.Y); MapCoordF polygon_start_tangent = MapCoordF(second_point_c - start_point_c); polygon_start_tangent.normalize(); MapCoordF polygon_end_tangent = MapCoordF(second_last_point_c - end_point_c); polygon_end_tangent.normalize(); double tangent_length = BEZIER_HANDLE_DISTANCE * start_point_c.distanceTo(end_point_c); object->addCoordinate(MapCoord(MapCoordF(start_point_c) + tangent_length * polygon_start_tangent)); object->addCoordinate(MapCoord((MapCoordF(end_point_c) + tangent_length * polygon_end_tangent))); object->addCoordinate(end_point_c); } void BooleanTool::rebuildTwoIndexSegment( ClipperLib::Path::size_type start_index, ClipperLib::Path::size_type end_index, bool sequence_increasing, const ClipperLib::Path& polygon, const PolyMap& polymap, PathObject* object) { Q_UNUSED(sequence_increasing); // only used in Q_ASSERT. PathCoordInfo start_info = polymap.value(polygon.at(start_index)); PathCoordInfo end_info = polymap.value(polygon.at(end_index)); PathObject* original = end_info.first->path; bool coords_increasing; bool is_curve; int coord_index; if (start_info.second->index == end_info.second->index) { coord_index = end_info.second->index; bool found = checkSegmentMatch(original, coord_index, polygon, start_index, end_index, coords_increasing, is_curve); if (!found) { object->getCoordinate(object->getCoordinateCount() - 1).setCurveStart(false); rebuildCoordinate(end_index, polygon, polymap, object); return; } Q_ASSERT(coords_increasing == sequence_increasing); } else { coord_index = end_info.second->index; bool found = checkSegmentMatch(original, coord_index, polygon, start_index, end_index, coords_increasing, is_curve); if (!found) { coord_index = start_info.second->index; found = checkSegmentMatch(original, coord_index, polygon, start_index, end_index, coords_increasing, is_curve); if (!found) { object->getCoordinate(object->getCoordinateCount() - 1).setCurveStart(false); rebuildCoordinate(end_index, polygon, polymap, object); return; } } } if (!is_curve) object->getCoordinate(object->getCoordinateCount() - 1).setCurveStart(false); if (coords_increasing) { object->addCoordinate(resetCoordinate(original->getCoordinate(coord_index + 1))); if (is_curve) { object->addCoordinate(original->getCoordinate(coord_index + 2)); object->addCoordinate(resetCoordinate(original->getCoordinate(coord_index + 3))); } } else { if (is_curve) { object->addCoordinate(resetCoordinate(original->getCoordinate(coord_index + 2))); object->addCoordinate(original->getCoordinate(coord_index + 1)); } object->addCoordinate(resetCoordinate(original->getCoordinate(coord_index + 0))); } } void BooleanTool::rebuildCoordinate( ClipperLib::Path::size_type index, const ClipperLib::Path& polygon, const PolyMap& polymap, PathObject* object, bool start_new_part) { auto coord = MapCoord::fromNative64(polygon.at(index).X, polygon.at(index).Y); if (polymap.contains(polygon.at(index))) { PathCoordInfo info = polymap.value(polygon.at(index)); MapCoord& original = info.first->path->getCoordinate(info.second->index); if (original.isDashPoint()) coord.setDashPoint(true); } object->addCoordinate(coord, start_new_part); } bool BooleanTool::checkSegmentMatch( const PathObject* original, int coord_index, const ClipperLib::Path& polygon, ClipperLib::Path::size_type start_index, ClipperLib::Path::size_type end_index, bool& out_coords_increasing, bool& out_is_curve ) { const MapCoord& first = original->getCoordinate(coord_index); out_is_curve = first.isCurveStart(); auto other_index = (coord_index + (out_is_curve ? 3 : 1)) % original->getCoordinateCount(); const MapCoord& other = original->getCoordinate(other_index); bool found = true; if (first == polygon.at(start_index) && other == polygon.at(end_index)) { out_coords_increasing = true; } else if (first == polygon.at(end_index) && other == polygon.at(start_index)) { out_coords_increasing = false; } else { found = false; } return found; } } // namespace OpenOrienteering mapper-0.8.1.1/src/core/objects/boolean_tool.h000066400000000000000000000205201325266516600211530ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2014, 2015 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_BOOLEAN_TOOL_H #define OPENORIENTEERING_BOOLEAN_TOOL_H #include #include #include #include namespace OpenOrienteering { class CombinedUndoStep; class Map; class PathCoord; class PathObject; class PathPart; /** * Wraps some helper functions for boolean operations. * * The implementation of the boolean operation tools is based on the Clipper * library (aka libpolyclipping) (http://www.angusj.com/delphi/clipper.php). * * Because Clipper does not support bezier curves, areas are clipped as * polygonal approximations, and after clipping we try to rebuild the curves. * * @todo This class should get unit tests. */ class BooleanTool { public: /** * A list of PathObject elements. */ typedef std::vector< PathObject* > PathObjects; /** * Types of boolean operation. */ enum Operation { Union, Intersection, Difference, XOr, MergeHoles }; /** * Constructs a tool for the given operation and map. * * map must not be nullptr (for some member functions). */ BooleanTool(Operation op, Map* map); /** * Executes the operation on the selected objects in the map. * * The first selected object is treated special and must be a path. * * @return True on success, false on error */ bool execute(); /** * Executes the operation per symbol on the selected objects in the map. * * Executes the operation independently for every group of path objects * which have got the same symbol. Objects which are not of type * Object::Path are ignored. * * Errors during the operation are ignored, too. The original objects the * operation failed for remain unchanged. The operation continues for other * groups of objects. * * @return True if the map was changed, false otherwise. */ bool executePerSymbol(); /** * Executes the operation on particular objects. * * This function does not (actively) change the collection of objects in the map * or the selection. * * @param subject The primary affected object. * @param in_objects All objects to operate on. Must contain subject. * @param out_objects The resulting collection of objects. */ bool executeForObjects( PathObject* subject, PathObjects& in_objects, PathObjects& out_objects ); /** * Executes the Intersection and Difference operation on the given line object. * * @param area The primary object, here defining a boundary. * @param line The line object to operate on. * @param out_objects The resulting collection of objects. */ void executeForLine( const PathObject* area, const PathObject* line, PathObjects& out_objects ); private: typedef std::pair< const PathPart*, const PathCoord* > PathCoordInfo; typedef QHash< ClipperLib::IntPoint, PathCoordInfo > PolyMap; /** * Executes the operation on particular objects, and provides undo steps. * * This function changes the collection of objects in the map and the selection. * * @param subject The primary affected object. * @param in_objects All objects to operate on. Must contain subject. * @param out_objects The resulting collection of objects. * @param undo_step A combined undo step which will be filled with sub steps. */ bool executeForObjects( PathObject* subject, PathObjects& in_objects, PathObjects& out_objects, CombinedUndoStep& undo_step ); /** * Converts a ClipperLib::PolyTree to PathObjects. * * @see BooleanTool::outerPolyNodeToPathObjects() */ void polyTreeToPathObjects( const ClipperLib::PolyTree& tree, PathObjects& out_objects, const PathObject* proto, const PolyMap& polymap ); /** * Converts a ClipperLib::PolyNode to PathObjects. * * The given ClipperLib::PolyNode must represent an outer polygon, not a hole. * * This method operates recursively on all outer children. */ void outerPolyNodeToPathObjects( const ClipperLib::PolyNode& node, PathObjects& out_objects, const PathObject* proto, const PolyMap& polymap ); /** * Constructs ClipperLib::Paths from a PathObject. */ static void pathObjectToPolygons( const PathObject* object, ClipperLib::Paths& polygons, PolyMap& polymap ); /** * Reconstructs a PathObject from a polygon given as ClipperLib::Path. * * Curves are reconstructed with the help of the polymap, mapping locations * to path coords of the original objects. */ static void polygonToPathPart( const ClipperLib::Path& polygon, const PolyMap& polymap, PathObject* object ); /** * Tries to reconstruct a straight or curved segment with given start and * end indices from the polygon. * The first coordinate of the segment is assumed to be already added. */ static void rebuildSegment( ClipperLib::Path::size_type start_index, ClipperLib::Path::size_type end_index, bool sequence_increasing, const ClipperLib::Path& polygon, const PolyMap& polymap, PathObject* object ); /** * Approximates a curved segment from the result polygon alone. */ static void rebuildSegmentFromPathOnly( const ClipperLib::IntPoint& start_point, const ClipperLib::IntPoint& second_point, const ClipperLib::IntPoint& second_last_point, const ClipperLib::IntPoint& end_point, PathObject* object ); /** * Special case of rebuildSegment() for straight or very short lines. */ static void rebuildTwoIndexSegment( ClipperLib::Path::size_type start_index, ClipperLib::Path::size_type end_index, bool sequence_increasing, const ClipperLib::Path& polygon, const PolyMap& polymap, PathObject* object ); /** * Reconstructs one polygon coordinate and adds it to the object. * * Uses the polymap to check whether the coorinate should be a dash point. */ static void rebuildCoordinate( ClipperLib::Path::size_type index, const ClipperLib::Path& polygon, const PolyMap& polymap, PathObject* object, bool start_new_part = false ); /** * Compares a PathObject segment to a ClipperLib::Path polygon segment. * * Returns true if the segments match. In this case, the out_... parameters are set. * * @param original The original PathObject. * @param coord_index The index of the segment start at the original. * @param polygon The ClipperLib::Path polygon. * @param start_index The start of the segement at the polygon. * @param end_index The end of the segment at the polygon. * @param out_coords_increasing If the segments match, will be set to * either true if a matching segment's point at coord_index corresponds to the point at start_index, * or false otherwise. * @param out_is_curve If the segments match, will be set to * either true if the original segment is a curve, * or false otherwise. */ static bool checkSegmentMatch( const PathObject* original, int coord_index, const ClipperLib::Path& polygon, ClipperLib::Path::size_type start_index, ClipperLib::Path::size_type end_index, bool& out_coords_increasing, bool& out_is_curve ); const Operation op; Map* const map; }; } // namespace OpenOrienteering #endif mapper-0.8.1.1/src/core/objects/object.cpp000066400000000000000000004363311325266516600203130ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2012-2015 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "object.h" #include #include #include #include #include #include #include #include "settings.h" #include "core/map.h" #include "core/objects/text_object.h" #include "core/renderables/renderable.h" #include "core/symbols/line_symbol.h" #include "core/symbols/point_symbol.h" #include "core/symbols/symbol.h" #include "core/symbols/text_symbol.h" #include "fileformats/file_format.h" #include "fileformats/file_import_export.h" #include "util/util.h" #include "util/xml_stream_util.h" // ### A namespace which collects various string constants of type QLatin1String. ### namespace literal { static const QLatin1String object("object"); static const QLatin1String symbol("symbol"); static const QLatin1String type("type"); static const QLatin1String text("text"); static const QLatin1String count("count"); static const QLatin1String coords("coords"); static const QLatin1String h_align("h_align"); static const QLatin1String v_align("v_align"); static const QLatin1String pattern("pattern"); static const QLatin1String rotation("rotation"); static const QLatin1String tags("tags"); } namespace OpenOrienteering { // ### Object implementation ### Object::Object(Object::Type type, const Symbol* symbol) : type(type), symbol(symbol), map(nullptr), output_dirty(true), extent(), output(*this) { // nothing } Object::Object(Object::Type type, const Symbol* symbol, const MapCoordVector& coords, Map* map) : type(type), symbol(symbol), coords(coords), map(map), output_dirty(true), extent(), output(*this) { // nothing } Object::Object(const Object& proto) : type(proto.type) , symbol(proto.symbol) , coords(proto.coords) , map(nullptr) , object_tags(proto.object_tags) , output_dirty(true) , extent(proto.extent) , output(*this) { // nothing } Object::~Object() { // nothing } void Object::copyFrom(const Object& other) { if (&other == this) return; if (type != other.type) throw std::invalid_argument(Q_FUNC_INFO); symbol = other.symbol; coords = other.coords; // map unchanged! object_tags = other.object_tags; output_dirty = true; extent = other.extent; } bool Object::equals(const Object* other, bool compare_symbol) const { if (type != other->type) return false; if (compare_symbol) { if (bool(symbol) != bool(other->symbol)) return false; if (symbol && !symbol->equals(other->symbol)) return false; } if (type == Text) { if (coords.front() != other->coords.front()) return false; } else { if (coords.size() != other->coords.size()) return false; for (size_t i = 0, end = coords.size(); i < end; ++i) { if (coords[i] != other->coords[i]) return false; } } if (object_tags != other->object_tags) return false; if (type == Point) { const PointObject* point_this = static_cast(this); const PointObject* point_other = static_cast(other); // Make sure that compared values are greater than 1, see qFuzzyCompare float rotation_a = point_this->getRotation(); float rotation_b = point_other->getRotation(); while (rotation_a < 1) { rotation_a += 100; rotation_b += 100; } if (!qFuzzyCompare(rotation_a, rotation_b)) return false; } else if (type == Path) { const PathObject* path_this = static_cast(this); const PathObject* path_other = static_cast(other); // Make sure that compared values are greater than 1, see qFuzzyCompare float rotation_a = path_this->getPatternRotation(); float rotation_b = path_other->getPatternRotation(); while (rotation_a < 1) { rotation_a += 100; rotation_b += 100; } if (!qFuzzyCompare(rotation_a, rotation_b)) return false; if (path_this->getPatternOrigin() != path_other->getPatternOrigin()) return false; } else if (type == Text) { const TextObject* text_this = static_cast(this); const TextObject* text_other = static_cast(other); if (text_this->getBoxSize() != text_other->getBoxSize()) return false; if (text_this->getText().compare(text_other->getText(), Qt::CaseSensitive) != 0) return false; if (text_this->getHorizontalAlignment() != text_other->getHorizontalAlignment()) return false; if (text_this->getVerticalAlignment() != text_other->getVerticalAlignment()) return false; // Make sure that compared values are greater than 1, see qFuzzyCompare float rotation_a = text_this->getRotation(); float rotation_b = text_other->getRotation(); while (rotation_a < 1) { rotation_a += 100; rotation_b += 100; } if (!qFuzzyCompare(rotation_a, rotation_b)) return false; } return true; } bool Object::validate() const { return true; } PointObject* Object::asPoint() { Q_ASSERT(type == Point); return static_cast(this); } const PointObject* Object::asPoint() const { Q_ASSERT(type == Point); return static_cast(this); } PathObject* Object::asPath() { Q_ASSERT(type == Path); return static_cast(this); } const PathObject* Object::asPath() const { Q_ASSERT(type == Path); return static_cast(this); } TextObject* Object::asText() { Q_ASSERT(type == Text); return static_cast(this); } const TextObject* Object::asText() const { Q_ASSERT(type == Text); return static_cast(this); } #ifndef NO_NATIVE_FILE_FORMAT void Object::load(QIODevice* file, int version, Map* map) { this->map = map; int symbol_index; file->read((char*)&symbol_index, sizeof(int)); if (map) { Symbol* read_symbol = map->getSymbol(symbol_index); if (read_symbol) symbol = read_symbol; } int num_coords; file->read((char*)&num_coords, sizeof(int)); coords.clear(); coords.reserve(num_coords); LegacyMapCoord coord; for (int i = 0; i < num_coords; ++i) { file->read((char*)&coord, sizeof(LegacyMapCoord)); coords.emplace_back(coord); } if (version <= 8) { bool path_closed; file->read((char*)&path_closed, sizeof(bool)); if (path_closed) coords[coords.size() - 1].setClosePoint(true); } if (version >= 8) { if (type == Point) { PointObject* point = reinterpret_cast(this); const PointSymbol* point_symbol = reinterpret_cast(point->getSymbol()); if (point_symbol->isRotatable()) { float rotation; file->read((char*)&rotation, sizeof(float)); point->setRotation(rotation); } } else if (type == Path) { PathObject* path = reinterpret_cast(this); if (version >= 21) { float rotation; file->read((char*)&rotation, sizeof(float)); path->setPatternRotation(rotation); LegacyMapCoord origin; file->read((char*)&origin, sizeof(LegacyMapCoord)); path->setPatternOrigin(origin); } } else if (type == Text) { TextObject* text = reinterpret_cast(this); float rotation; file->read((char*)&rotation, sizeof(float)); text->setRotation(rotation); int temp; file->read((char*)&temp, sizeof(int)); text->setHorizontalAlignment((TextObject::HorizontalAlignment)temp); file->read((char*)&temp, sizeof(int)); text->setVerticalAlignment((TextObject::VerticalAlignment)temp); QString str; loadString(file, str); text->setText(str); if (coords.size() > 1) { auto raw_size = coords[1]; auto w = MapCoord::boundsOffset().x + raw_size.nativeX(); auto h = MapCoord::boundsOffset().y + raw_size.nativeY(); text->setBoxSize(MapCoord::fromNative64(w, h)); } } } if (type == Path) { PathObject* path = reinterpret_cast(this); path->recalculateParts(); } output_dirty = true; } #endif void Object::save(QXmlStreamWriter& xml) const { XmlElementWriter object_element(xml, literal::object); object_element.writeAttribute(literal::type, type); int symbol_index = -1; if (map) symbol_index = map->findSymbolIndex(symbol); if (symbol_index != -1) object_element.writeAttribute(literal::symbol, symbol_index); if (type == Point) { const PointObject* point = reinterpret_cast(this); const PointSymbol* point_symbol = reinterpret_cast(point->getSymbol()); if (point_symbol->isRotatable()) object_element.writeAttribute(literal::rotation, point->getRotation()); } else if (type == Text) { const TextObject* text = reinterpret_cast(this); object_element.writeAttribute(literal::rotation, text->getRotation()); object_element.writeAttribute(literal::h_align, text->getHorizontalAlignment()); object_element.writeAttribute(literal::v_align, text->getVerticalAlignment()); // For compatibility, we must keep the box size in the second coord ATM. /// \todo Save box size separately auto object = const_cast(this); if (text->hasSingleAnchor()) { object->coords.resize(1); } else { object->coords.resize(2); object->coords.back() = text->getBoxSize(); } } if (!object_tags.empty()) { XmlElementWriter tags_element(xml, literal::tags); tags_element.write(object_tags); } { // Scope of coords XML element XmlElementWriter coords_element(xml, literal::coords); coords_element.write(coords); } if (type == Path) { const PathObject* path = reinterpret_cast(this); XmlElementWriter pattern_element(xml, literal::pattern); pattern_element.writeAttribute(literal::rotation, path->getPatternRotation()); path->getPatternOrigin().save(xml); } else if (type == Text) { const TextObject* text = reinterpret_cast(this); xml.writeTextElement(literal::text, text->getText()); } } Object* Object::load(QXmlStreamReader& xml, Map* map, const SymbolDictionary& symbol_dict, const Symbol* symbol) { Q_ASSERT(xml.name() == literal::object); XmlElementReader object_element(xml); Object::Type object_type = object_element.attribute(literal::type); Object* object = Object::getObjectForType(object_type); if (!object) throw FileFormatException(::OpenOrienteering::ImportExport::tr("Error while loading an object of type %1.").arg(object_type)); object->map = map; if (symbol) object->symbol = symbol; else { QString symbol_id = object_element.attribute(literal::symbol); object->symbol = symbol_dict[symbol_id]; // FIXME: cannot work for forward references // NOTE: object->symbol may be nullptr. } if (!object->symbol || !object->symbol->isTypeCompatibleTo(object)) { // Throwing an exception will cause loading to fail. // Rather than not loading the broken file at all, // use the same symbols which are used for importing GPS tracks etc. // FIXME: Implement a way to send a warning to the user. switch (object_type) { case Point: object->symbol = Map::getUndefinedPoint(); break; case Path: object->symbol = Map::getUndefinedLine(); break; case Text: object->symbol = Map::getUndefinedText(); break; default: throw FileFormatException( ::OpenOrienteering::ImportExport::tr("Unable to find symbol for object at %1:%2."). arg(xml.lineNumber()).arg(xml.columnNumber()) ); } } if (object_type == Point) { PointObject* point = reinterpret_cast(object); const PointSymbol* point_symbol = reinterpret_cast(point->getSymbol()); if (point_symbol && point_symbol->isRotatable()) point->setRotation(object_element.attribute(literal::rotation)); else if (!point_symbol) throw FileFormatException(::OpenOrienteering::ImportExport::tr("Point object with undefined or wrong symbol at %1:%2.").arg(xml.lineNumber()).arg(xml.columnNumber())); } else if (object_type == Text) { TextObject* text = reinterpret_cast(object); text->setRotation(object_element.attribute(literal::rotation)); text->setHorizontalAlignment(object_element.attribute(literal::h_align)); text->setVerticalAlignment(object_element.attribute(literal::v_align)); } while (xml.readNextStartElement()) { if (xml.name() == literal::coords) { XmlElementReader coords_element(xml); try { if (object_type == Text) { coords_element.readForText(object->coords); if (object->coords.size() > 1) static_cast(object)->setBoxSize(object->coords[1]); } else { coords_element.read(object->coords); } } catch (FileFormatException& e) { throw FileFormatException(::OpenOrienteering::ImportExport::tr("Error while loading an object of type %1 at %2:%3: %4"). arg(object_type).arg(xml.lineNumber()).arg(xml.columnNumber()).arg(e.message())); } } else if (xml.name() == literal::pattern && object_type == Path) { XmlElementReader element(xml); PathObject* path = reinterpret_cast(object); path->setPatternRotation(element.attribute(literal::rotation)); while (xml.readNextStartElement()) { if (xml.name() == XmlStreamLiteral::coord) { try { path->setPatternOrigin(MapCoord::load(xml)); } catch (const std::range_error& e) { /// \todo Add a warning, but don't throw - throwing lets loading fail. // throw FileFormatException(::OpenOrienteering::MapCoord::tr(e.what())); qDebug("%s", e.what()); } } else { xml.skipCurrentElement(); // unknown } } } else if (xml.name() == literal::text && object_type == Text) { TextObject* text = reinterpret_cast(object); text->setText(xml.readElementText()); } else if (xml.name() == literal::tags) { XmlElementReader(xml).read(object->object_tags); } else xml.skipCurrentElement(); // unknown } if (object_type == Path) { PathObject* path = reinterpret_cast(object); path->recalculateParts(); } object->output_dirty = true; if (map && ( object->coords.empty() || !object->coords.front().isRegular() || !object->coords.back().isRegular() ) ) { map->markAsIrregular(object); } return object; } void Object::forceUpdate() const { output_dirty = true; update(); } bool Object::update() const { if (!output_dirty) return false; Symbol::RenderableOptions options = Symbol::RenderNormal; if (map) { options = QFlag(map->renderableOptions()); if (extent.isValid()) map->setObjectAreaDirty(extent); } output.deleteRenderables(); extent = QRectF(); updateEvent(); createRenderables(output, options); Q_ASSERT(extent.right() < 60000000); // assert if bogus values are returned output_dirty = false; if (map) { map->insertRenderablesOfObject(this); if (extent.isValid()) map->setObjectAreaDirty(extent); } return true; } void Object::updateEvent() const { // nothing here } void Object::createRenderables(ObjectRenderables& output, Symbol::RenderableOptions options) const { symbol->createRenderables(this, VirtualCoordVector(coords), output, options); } void Object::move(qint32 dx, qint32 dy) { for (MapCoord& coord : coords) { coord.setNativeX(dx + coord.nativeX()); coord.setNativeY(dy + coord.nativeY()); } setOutputDirty(); } void Object::move(MapCoord offset) { for (MapCoord& coord : coords) { coord += offset; } setOutputDirty(); } void Object::scale(MapCoordF center, double factor) { for (MapCoord& coord : coords) { coord.setX(center.x() + (coord.x() - center.x()) * factor); coord.setY(center.y() + (coord.y() - center.y()) * factor); } setOutputDirty(); } void Object::scale(double factor_x, double factor_y) { for (MapCoord& coord : coords) { coord.setX(coord.x() * factor_x); coord.setY(coord.y() * factor_y); } setOutputDirty(); } void Object::rotateAround(MapCoordF center, double angle) { double sin_angle = sin(angle); double cos_angle = cos(angle); int coords_size = coords.size(); /// \todo range-for loop for (int c = 0; c < coords_size; ++c) { MapCoordF center_to_coord = MapCoordF(coords[c].x() - center.x(), coords[c].y() - center.y()); coords[c].setX(center.x() + cos_angle * center_to_coord.x() + sin_angle * center_to_coord.y()); coords[c].setY(center.y() - sin_angle * center_to_coord.x() + cos_angle * center_to_coord.y()); } if (type == Point) { PointObject* point = reinterpret_cast(this); const PointSymbol* point_symbol = reinterpret_cast(point->getSymbol()); if (point_symbol->isRotatable()) point->setRotation(point->getRotation() + angle); } else if (type == Text) { TextObject* text = reinterpret_cast(this); text->setRotation(text->getRotation() + angle); } setOutputDirty(); } void Object::rotate(double angle) { double sin_angle = sin(angle); double cos_angle = cos(angle); int coords_size = coords.size(); /// \todo range-for loop for (int c = 0; c < coords_size; ++c) { MapCoord coord = coords[c]; coords[c].setX(cos_angle * coord.x() + sin_angle * coord.y()); coords[c].setY(cos_angle * coord.y() - sin_angle * coord.x()); } if (type == Point) { PointObject* point = reinterpret_cast(this); const PointSymbol* point_symbol = reinterpret_cast(point->getSymbol()); if (point_symbol->isRotatable()) point->setRotation(point->getRotation() + angle); } else if (type == Text) { TextObject* text = reinterpret_cast(this); text->setRotation(text->getRotation() + angle); } setOutputDirty(); } int Object::isPointOnObject(MapCoordF coord, float tolerance, bool treat_areas_as_paths, bool extended_selection) const { Symbol::Type type = symbol->getType(); Symbol::Type contained_types = symbol->getContainedTypes(); // Points if (type == Symbol::Point) { if (!extended_selection) return (coord.distanceSquaredTo(MapCoordF(coords[0])) <= tolerance) ? Symbol::Point : Symbol::NoSymbol; else return extent.contains(coord) ? Symbol::Point : Symbol::NoSymbol; } // First check using extent float extent_extension = ((contained_types & Symbol::Line) || treat_areas_as_paths) ? tolerance : 0; if (coord.x() < extent.left() - extent_extension) return Symbol::NoSymbol; if (coord.y() < extent.top() - extent_extension) return Symbol::NoSymbol; if (coord.x() > extent.right() + extent_extension) return Symbol::NoSymbol; if (coord.y() > extent.bottom() + extent_extension) return Symbol::NoSymbol; if (type == Symbol::Text) { // Texts const TextObject* text_object = reinterpret_cast(this); return (text_object->calcTextPositionAt(coord, true) != -1) ? Symbol::Text : Symbol::NoSymbol; } else { // Path objects const PathObject* path = reinterpret_cast(this); return path->isPointOnPath(coord, tolerance, treat_areas_as_paths, true); } } void Object::takeRenderables() { output.takeRenderables(); } void Object::clearRenderables() { output.deleteRenderables(); extent = QRectF(); } bool Object::setSymbol(const Symbol* new_symbol, bool no_checks) { if (!no_checks && new_symbol) { if (!new_symbol->isTypeCompatibleTo(this)) return false; } symbol = new_symbol; setOutputDirty(); return true; } Object* Object::getObjectForType(Object::Type type, const Symbol* symbol) { if (type == Point) return new PointObject(symbol); else if (type == Path) return new PathObject(symbol); else if (type == Text) return new TextObject(symbol); else { Q_ASSERT(false); return nullptr; } } void Object::setTags(const Object::Tags& tags) { if (object_tags != tags) { object_tags = tags; if (map) { map->setObjectsDirty(); if (map->isObjectSelected(this)) map->emitSelectionEdited(); } } } void Object::setTag(const QString& key, const QString& value) { if (!object_tags.contains(key) || object_tags.value(key) != value) { object_tags.insert(key, value); if (map) { map->setObjectsDirty(); if (map->isObjectSelected(this)) map->emitSelectionEdited(); } } } void Object::removeTag(const QString& key) { if (object_tags.contains(key)) { object_tags.remove(key); if (map) map->setObjectsDirty(); } } void Object::includeControlPointsRect(QRectF& rect) const { if (type == Object::Path) { const PathObject* path = asPath(); int size = path->getCoordinateCount(); for (int i = 0; i < size; ++i) rectInclude(rect, QPointF(path->coords[i])); } else if (type == Object::Text) { const TextObject* text = asText(); std::vector text_handles(text->controlPoints()); for (auto& text_handle : text_handles) rectInclude(rect, text_handle); } } // ### PathPart ### /* * Some headers may not want to include object.h but rather rely on * PathPartVector::size_type being a std::size_t. */ static_assert(std::is_same::value, "PathCoordVector::size_type is not std::size_t"); PathPart::PathPart(PathObject& path, const VirtualPath& proto) : VirtualPath(path.getRawCoordinateVector(), proto.first_index, proto.last_index) , path(&path) { if (!proto.path_coords.empty()) { path_coords.reserve(proto.path_coords.size()); path_coords.insert(end(path_coords), begin(proto.path_coords), end(proto.path_coords)); } } void PathPart::setClosed(bool closed, bool may_use_existing_close_point) { Q_ASSERT(!empty()); if (!isClosed() && closed) { if (size() == 1 || !may_use_existing_close_point || !path->coords[first_index].isPositionEqualTo(path->coords[last_index])) { path->coords[last_index].setHolePoint(false); path->coords[last_index].setClosePoint(false); path->coords.insert(path->coords.begin() + last_index + 1, path->coords[first_index]); path->partSizeChanged(path->findPartForIndex(first_index), 1); } path->setClosingPoint(last_index, path->coords[first_index]); path->setOutputDirty(); } else if (isClosed() && !closed) { path->coords[last_index].setClosePoint(false); path->setOutputDirty(); } } void PathPart::connectEnds() { if (!isClosed()) { path->coords[first_index] += path->coords[last_index]; path->coords[first_index] /= 2; path->setClosingPoint(last_index, path->coords[first_index]); path->setOutputDirty(); } } void PathPart::reverse() { MapCoordVector& coords = path->coords; bool set_last_hole_point = false; auto half = (first_index + last_index + 1) / 2; for (auto c = first_index; c <= last_index; ++c) { MapCoord coord = coords[c]; if (c < half) { auto mirror_index = last_index - (c - first_index); coords[c] = coords[mirror_index]; coords[mirror_index] = coord; } if (!(c == first_index && isClosed()) && coords[c].isCurveStart()) { Q_ASSERT((c - first_index) >= 3); coords[c - 3].setCurveStart(true); if (!(c == last_index && isClosed())) coords[c].setCurveStart(false); } else if (coords[c].isHolePoint()) { coords[c].setHolePoint(false); if (c >= first_index + 1) coords[c - 1].setHolePoint(true); else set_last_hole_point = true; } } if (coords[first_index].isClosePoint()) { coords[first_index].setClosePoint(false); coords[last_index].setClosePoint(true); } if (set_last_hole_point) coords[last_index].setHolePoint(true); path->setOutputDirty(); } PathPartVector PathPart::calculatePathParts(const VirtualCoordVector& coords) { PathPartVector parts; PathCoordVector path_coords(coords); auto last = coords.size(); auto part_start = MapCoordVector::size_type { 0 }; while (part_start != last) { auto part_end = path_coords.update(part_start); parts.emplace_back(coords, part_start, part_end); parts.back().path_coords.swap(path_coords); part_start = part_end + 1; } return parts; } //### PathPartVector ### // Not inline because it is to be used by reference. bool PathPartVector::compareEndIndex(const PathPart& part, VirtualPath::size_type index) { return part.last_index+1 <= index; } // ### PathObject ### PathObject::PathObject(const Symbol* symbol) : Object(Object::Path, symbol) , pattern_rotation(0.0) , pattern_origin(0, 0) { Q_ASSERT(!symbol || (symbol->getType() == Symbol::Line || symbol->getType() == Symbol::Area || symbol->getType() == Symbol::Combined)); } PathObject::PathObject(const Symbol* symbol, const MapCoordVector& coords, Map* map) : Object(Object::Path, symbol, coords, map) , pattern_rotation(0.0) , pattern_origin(0, 0) { Q_ASSERT(!symbol || (symbol->getType() == Symbol::Line || symbol->getType() == Symbol::Area || symbol->getType() == Symbol::Combined)); recalculateParts(); } PathObject::PathObject(const Symbol* symbol, const PathObject& proto, MapCoordVector::size_type piece) : Object { Object::Path, symbol } , pattern_rotation { 0.0f } , pattern_origin { 0, 0 } { auto begin = proto.coords.begin() + piece; auto part = proto.findPartForIndex(piece); if (piece == part->last_index) { if (part->isClosed()) begin = proto.coords.begin() + part->first_index; else begin = proto.coords.begin() + part->prevCoordIndex(piece); } auto end = begin + (begin->isCurveStart() ? 4 : 2); std::for_each(begin, end, [this](MapCoord c) { c.setHolePoint(false); c.setClosePoint(false); addCoordinate(c); }); coords.back().setHolePoint(true); } PathObject::PathObject(const PathObject& proto) : Object(proto) , pattern_rotation(proto.pattern_rotation) , pattern_origin(proto.pattern_origin) { path_parts.reserve(proto.path_parts.size()); for (const PathPart& part : proto.path_parts) { path_parts.emplace_back(*this, part); } } PathObject::PathObject(const PathPart &proto_part) : Object(*proto_part.path) , pattern_rotation(proto_part.path->pattern_rotation) , pattern_origin(proto_part.path->pattern_origin) { auto begin = proto_part.path->coords.begin(); coords.reserve(proto_part.size()); coords.assign(begin + proto_part.first_index, begin + (proto_part.last_index+1)); path_parts.emplace_back(*this, proto_part); } PathObject* PathObject::duplicate() const { return new PathObject(*this); } void PathObject::copyFrom(const Object& other) { if (&other == this) return; Object::copyFrom(other); const PathObject& other_path = *other.asPath(); pattern_rotation = other_path.getPatternRotation(); pattern_origin = other_path.getPatternOrigin(); path_parts.clear(); path_parts.reserve(other_path.path_parts.size()); for (const PathPart& part : other_path.path_parts) { path_parts.emplace_back(*this, part); } } bool PathObject::validate() const { return std::all_of(begin(path_parts), end(path_parts), [](auto &part) { return !part.isClosed() || part.coords[part.first_index] == part.coords[part.last_index]; }); } void PathObject::normalize() { for (MapCoordVector::size_type i = 0; i < coords.size(); ++i) { if (coords[i].isCurveStart()) { if (i+3 >= getCoordinateCount()) { coords[i].setCurveStart(false); continue; } if (coords[i + 1].isClosePoint() || coords[i + 1].isHolePoint() || coords[i + 2].isClosePoint() || coords[i + 2].isHolePoint()) { coords[i].setCurveStart(false); continue; } coords[i + 1].setCurveStart(false); coords[i + 1].setDashPoint(false); coords[i + 2].setCurveStart(false); coords[i + 2].setDashPoint(false); i += 2; } if (i > 0 && coords[i].isHolePoint()) { if (coords[i-1].isHolePoint()) deleteCoordinate(i, false); } } } bool PathObject::intersectsBox(const QRectF& box) const { // Check path parts for an intersection with box if (std::any_of(begin(path_parts), end(path_parts), [&box](const PathPart& part) { return part.intersectsBox(box); })) { return true; }; // If this is an area, additionally check if the area contains the box if (getSymbol()->getContainedTypes() & Symbol::Area) { return isPointOnObject(MapCoordF(box.center()), 0, false, false); } return false; } PathPartVector::const_iterator PathObject::findPartForIndex(MapCoordVector::size_type coords_index) const { return std::lower_bound(begin(path_parts), end(path_parts), coords_index, PathPartVector::compareEndIndex); } PathPartVector::iterator PathObject::findPartForIndex(MapCoordVector::size_type coords_index) { setOutputDirty(); return std::lower_bound(begin(path_parts), end(path_parts), coords_index, PathPartVector::compareEndIndex); } PathPartVector::size_type PathObject::findPartIndexForIndex(MapCoordVector::size_type coords_index) const { for (PathPartVector::size_type i = 0; i < path_parts.size(); ++i) { if (path_parts[i].first_index <= coords_index && path_parts[i].last_index >= coords_index) return i; } Q_ASSERT(false); return 0; } PathCoord PathObject::findPathCoordForIndex(MapCoordVector::size_type index) const { auto part = findPartForIndex(index); if (part != end(path_parts)) { return *(std::lower_bound(begin(part->path_coords), end(part->path_coords), index, PathCoord::indexLessThanValue)); } return path_parts.front().path_coords.front(); } bool PathObject::isCurveHandle(MapCoordVector::size_type index) const { return ( index < coords.size() && !coords[index].isCurveStart() && ( ( index > 0 && coords[index-1].isCurveStart() ) || ( index > 1 && coords[index-2].isCurveStart() ) ) ); } void PathObject::deletePart(PathPartVector::size_type part_index) { setOutputDirty(); auto part = begin(path_parts) + part_index; auto first_index = part->first_index; auto end_index = part->last_index + 1; auto first_coord = begin(coords); coords.erase(first_coord + first_index, first_coord + end_index); partSizeChanged(part, first_index - end_index); path_parts.erase(part); } void PathObject::partSizeChanged(PathPartVector::iterator part, MapCoordVector::difference_type change) { Q_ASSERT(isOutputDirty()); part->last_index += change; auto last = end(path_parts); for (++part; part != last; ++part) { part->first_index += change; part->last_index += change; } } void PathObject::transform(const QTransform& t) { if (t.isIdentity()) return; for (auto& coord : coords) { const auto p = t.map(MapCoordF{coord}); coord.setX(p.x()); coord.setY(p.y()); } pattern_origin = MapCoord{t.map(MapCoordF{getPatternOrigin()})}; setOutputDirty(); } void PathObject::setPatternRotation(float rotation) { pattern_rotation = rotation; setOutputDirty(); } void PathObject::setPatternOrigin(const MapCoord& origin) { pattern_origin = origin; setOutputDirty(); } void PathObject::calcClosestPointOnPath( MapCoordF coord, float& out_distance_sq, PathCoord& out_path_coord, MapCoordVector::size_type start_index, MapCoordVector::size_type end_index) const { update(); auto bound = std::numeric_limits::max(); for (const auto& part : path_parts) { if (part.first_index <= end_index && part.last_index >= start_index) /// \todo Legacy compatibility, review/remove { auto path_coord = part.findClosestPointTo(coord, out_distance_sq, bound, start_index, end_index); if (out_distance_sq < bound) { bound = out_distance_sq; out_path_coord = path_coord; } } } } void PathObject::calcClosestCoordinate(MapCoordF coord, float& out_distance_sq, MapCoordVector::size_type& out_index) const { update(); auto coords_size = coords.size(); if (coords_size == 0) { out_distance_sq = -1; out_index = -1; return; } // NOTE: do not try to optimize this by starting with index 1, it will overlook curve starts this way out_distance_sq = 999999; out_index = 0; for (MapCoordVector::size_type i = 0; i < coords_size; ++i) { double length_sq = (coord - MapCoordF(coords[i])).lengthSquared(); if (length_sq < out_distance_sq) { out_distance_sq = length_sq; out_index = i; } if (coords[i].isCurveStart()) i += 2; } } MapCoordVector::size_type PathObject::subdivide(const PathCoord& path_coord) { return subdivide(path_coord.index, path_coord.param); } MapCoordVector::size_type PathObject::subdivide(MapCoordVector::size_type index, float param) { Q_ASSERT(index < coords.size()); if (coords[index].isCurveStart()) { MapCoordF o0, o1, o2, o3, o4; PathCoord::splitBezierCurve(MapCoordF(coords[index]), MapCoordF(coords[index+1]), MapCoordF(coords[index+2]), MapCoordF(coords[index+3]), param, o0, o1, o2, o3, o4); coords[index + 1] = MapCoord(o0); coords[index + 2] = MapCoord(o4); addCoordinate(index + 2, MapCoord(o3)); MapCoord middle_coord = MapCoord(o2); middle_coord.setCurveStart(true); addCoordinate(index + 2, middle_coord); addCoordinate(index + 2, MapCoord(o1)); Q_ASSERT(isOutputDirty()); return index + 3; } else { addCoordinate(index + 1, MapCoord(MapCoordF(coords[index]) + (MapCoordF(coords[index+1]) - MapCoordF(coords[index])) * param)); Q_ASSERT(isOutputDirty()); return index + 1; } } bool PathObject::canBeConnected(const PathObject* other, double connect_threshold_sq) const { for (const auto& part : path_parts) { if (part.isClosed()) continue; for (const auto& other_part : other->path_parts) { if (other_part.isClosed()) continue; if (coords[part.first_index].distanceSquaredTo(other->coords[other_part.first_index]) <= connect_threshold_sq) return true; else if (coords[part.first_index].distanceSquaredTo(other->coords[other_part.last_index]) <= connect_threshold_sq) return true; else if (coords[part.last_index].distanceSquaredTo(other->coords[other_part.first_index]) <= connect_threshold_sq) return true; else if (coords[part.last_index].distanceSquaredTo(other->coords[other_part.last_index]) <= connect_threshold_sq) return true; } } return false; } bool PathObject::connectIfClose(PathObject* other, double connect_threshold_sq) { bool did_connect_path = false; auto num_parts = parts().size(); auto num_other_parts = other->parts().size(); std::vector other_parts; // Which parts have not been connected to this part yet? other_parts.assign(num_other_parts, true); for (std::size_t i = 0; i < num_parts; ++i) { if (path_parts[i].isClosed()) continue; for (std::size_t k = 0; k < num_other_parts; ++k) { if (!other_parts[k] || other->path_parts[k].isClosed()) continue; if (coords[path_parts[i].first_index].distanceSquaredTo(other->coords[other->path_parts[k].first_index]) <= connect_threshold_sq) { other->path_parts[k].reverse(); connectPathParts(i, other, k, true); } else if (coords[path_parts[i].first_index].distanceSquaredTo(other->coords[other->path_parts[k].last_index]) <= connect_threshold_sq) connectPathParts(i, other, k, true); else if (coords[path_parts[i].last_index].distanceSquaredTo(other->coords[other->path_parts[k].first_index]) <= connect_threshold_sq) connectPathParts(i, other, k, false); else if (coords[path_parts[i].last_index].distanceSquaredTo(other->coords[other->path_parts[k].last_index]) <= connect_threshold_sq) { other->path_parts[k].reverse(); connectPathParts(i, other, k, false); } else continue; if (coords[path_parts[i].first_index].distanceSquaredTo(coords[path_parts[i].last_index]) <= connect_threshold_sq) path_parts[i].connectEnds(); did_connect_path = true; other_parts[k] = false; } } if (did_connect_path) { // Copy over all remaining parts of the other object getCoordinate(getCoordinateCount() - 1).setHolePoint(true); for (std::size_t i = 0; i < num_other_parts; ++i) { if (other_parts[i]) appendPathPart(other->parts()[i]); } Q_ASSERT(isOutputDirty()); } return did_connect_path; } void PathObject::connectPathParts(PathPartVector::size_type part_index, const PathObject* other, PathPartVector::size_type other_part_index, bool prepend, bool merge_ends) { Q_ASSERT(part_index < path_parts.size()); PathPart& part = path_parts[part_index]; PathPart& other_part = other->path_parts[other_part_index]; Q_ASSERT(!part.isClosed() && !other_part.isClosed()); int other_part_size = other_part.size(); int appended_part_size = other_part_size - (merge_ends ? 1 : 0); coords.resize(coords.size() + appended_part_size); if (prepend) { for (auto i = coords.size() - 1; i >= part.first_index + appended_part_size; --i) coords[i] = coords[i - appended_part_size]; MapCoord& join_coord = coords[part.first_index + appended_part_size]; if (merge_ends) { join_coord.setNativeX((join_coord.nativeX() + other->coords[other_part.last_index].nativeX()) / 2); join_coord.setNativeY((join_coord.nativeY() + other->coords[other_part.last_index].nativeY()) / 2); } join_coord.setHolePoint(false); join_coord.setClosePoint(false); for (auto i = part.first_index; i < part.first_index + appended_part_size; ++i) coords[i] = other->coords[i - part.first_index + other_part.first_index]; if (!merge_ends) { MapCoord& second_join_coord = coords[part.first_index + appended_part_size - 1]; second_join_coord.setHolePoint(false); second_join_coord.setClosePoint(false); } } else { auto end_index = part.last_index; if (merge_ends) { MapCoord coord = other->coords[other_part.first_index]; // take flags from first coord of path to append coord.setNativeX((coords[end_index].nativeX() + coord.nativeX()) / 2); coord.setNativeY((coords[end_index].nativeY() + coord.nativeY()) / 2); coords[end_index] = coord; } else { coords[end_index].setHolePoint(false); coords[end_index].setClosePoint(false); } for (auto i = coords.size() - 1; i > part.last_index + appended_part_size; --i) coords[i] = coords[i - appended_part_size]; for (auto i = part.last_index+1; i < part.last_index + 1 + appended_part_size; ++i) coords[i] = other->coords[i - end_index + other_part.first_index - (merge_ends ? 0 : 1)]; } setOutputDirty(); partSizeChanged(begin(path_parts)+part_index, appended_part_size); Q_ASSERT(!part.isClosed()); } std::vector PathObject::removeFromLine(PathPartVector::size_type part_index, qreal begin, qreal end_index) const { Q_ASSERT(path_parts.size() == 1); // TODO Q_ASSERT(symbol->getContainedTypes() == Symbol::Line); const PathPart& part = path_parts[part_index]; Q_ASSERT(part.path_coords.front().clen <= begin); Q_ASSERT(part.path_coords.front().clen <= end_index); Q_ASSERT(part.path_coords.back().clen >= begin); Q_ASSERT(part.path_coords.back().clen >= end_index); std::vector objects; if (end_index < begin || part.isClosed()) { PathObject* obj = duplicate()->asPath(); obj->changePathBounds(part_index, end_index, begin); objects.push_back(obj); } else { if (begin > part.path_coords.front().clen) { PathObject* obj = duplicate()->asPath(); obj->changePathBounds(part_index, part.path_coords.front().clen, begin); objects.push_back(obj); } if (end_index < part.path_coords.back().clen) { PathObject* obj = duplicate()->asPath(); obj->changePathBounds(part_index, end_index, part.path_coords.front().clen); objects.push_back(obj); } } return objects; } std::vector PathObject::splitLineAt(const PathCoord& split_pos) const { Q_ASSERT(path_parts.size() == 1); Q_ASSERT((symbol->getContainedTypes() & ~Symbol::Combined) == Symbol::Line); std::vector objects; if (path_parts.size() == 1) { const auto part_index = PathPartVector::size_type { 0 }; const auto& part = path_parts[part_index]; if (part.isClosed()) { PathObject* obj = duplicate()->asPath(); if ( split_pos.clen == part.path_coords.front().clen || split_pos.clen == part.path_coords.back().clen ) { (obj->path_parts[part_index]).setClosed(false); } else { obj->changePathBounds(part_index, split_pos.clen, split_pos.clen); } objects.push_back(obj); } else if ( split_pos.clen != part.path_coords.front().clen && split_pos.clen != part.path_coords.back().clen) { PathObject* obj1 = duplicate()->asPath(); obj1->changePathBounds(part_index, part.path_coords.front().clen, split_pos.clen); objects.push_back(obj1); PathObject* obj2 = duplicate()->asPath(); obj2->changePathBounds(part_index, split_pos.clen, part.path_coords.back().clen); objects.push_back(obj2); } } return objects; } void PathObject::changePathBounds( PathPartVector::size_type part_index, PathCoord::length_type start_len, PathCoord::length_type end_len) { update(); PathPart& part = path_parts[part_index]; auto part_size = part.size(); MapCoordVector out_coords; out_coords.reserve(part_size + 2); if (end_len == 0.0) end_len = part.path_coords.back().clen; auto part_begin = SplitPathCoord::begin(part.path_coords); auto part_end = SplitPathCoord::end(part.path_coords); auto start = SplitPathCoord::at(start_len, part_begin); if (end_len <= start_len) { // Make sure part_end has the right curve end points for start. part_end = SplitPathCoord::at(part_end.clen, start); part.copy(start, part_end, out_coords); out_coords.back().setHolePoint(false); out_coords.back().setClosePoint(false); auto end = SplitPathCoord::at(end_len, part_begin); part.copy(part_begin, end, out_coords); } else { auto end = SplitPathCoord::at(end_len, start); part.copy(start, end, out_coords); } out_coords.back().setHolePoint(true); out_coords.back().setClosePoint(false); const auto copy_size = qMin(out_coords.size(), (MapCoordVector::size_type)part_size); const auto part_start = begin(coords) + part.first_index; std::copy(begin(out_coords), begin(out_coords) + copy_size, part_start); if (copy_size < part_size) coords.erase(part_start + copy_size, part_start + part_size); else coords.insert(part_start + part_size, begin(out_coords) + copy_size, end(out_coords)); recalculateParts(); setOutputDirty(); } void PathObject::calcBezierPointDeletionRetainingShapeFactors(MapCoord p0, MapCoord p1, MapCoord p2, MapCoord q0, MapCoord q1, MapCoord q2, MapCoord q3, double& out_pfactor, double& out_qfactor) { // Heuristic for the split parameter sp (zero to one) QBezier p_curve = QBezier::fromPoints(QPointF(p0), QPointF(p1), QPointF(p2), QPointF(q0)); double p_length = p_curve.length(PathCoord::bezierError()); QBezier q_curve = QBezier::fromPoints(QPointF(q0), QPointF(q1), QPointF(q2), QPointF(q3)); double q_length = q_curve.length(PathCoord::bezierError()); double sp = p_length / qMax(1e-08, p_length + q_length); // Least squares curve fitting with the constraint that handles are on the same line as before. // To reproduce the formulas, run the following Matlab script: /* clear all; close all; clc; syms P0x P1x P2x P3x; syms Q1x Q2x Q3x; syms P0y P1y P2y P3y; syms Q1y Q2y Q3y; syms u v w; syms pfactor qfactor; Xx = (1-u)^3*P0x+3*(1-u)^2*u*P1x+3*(1-u)*u^2*P2x+u^3*P3x; Yx = (1-v)^3*P3x+3*(1-v)^2*v*Q1x+3*(1-v)*v^2*Q2x+v^3*Q3x; Zx = (1-w)^3*P0x+3*(1-w)^2*w*(P0x+pfactor*(P1x-P0x))+3*(1-w)*w^2*(Q3x+qfactor*(Q2x-Q3x))+w^3*Q3x; Xy = (1-u)^3*P0y+3*(1-u)^2*u*P1y+3*(1-u)*u^2*P2y+u^3*P3y; Yy = (1-v)^3*P3y+3*(1-v)^2*v*Q1y+3*(1-v)*v^2*Q2y+v^3*Q3y; Zy = (1-w)^3*P0y+3*(1-w)^2*w*(P0y+pfactor*(P1y-P0y))+3*(1-w)*w^2*(Q3y+qfactor*(Q2y-Q3y))+w^3*Q3y; syms sp; %split_param E = int((Zx - subs(Xx, u, w/sp))^2 + (Zy - subs(Xy, u, w/sp))^2, w, 0, sp) + int((Zx - subs(Yx, v, (w - sp) / (1 - sp)))^2 + (Zy - subs(Yy, v, (w - sp) / (1 - sp)))^2, w, sp, 1); Epfactor = diff(E, pfactor); Eqfactor = diff(E, qfactor); S = solve(Epfactor, Eqfactor, pfactor, qfactor); S = [S.pfactor, S.qfactor] */ double P0x = p0.x(), P1x = p1.x(), P2x = p2.x(), P3x = q0.x(); double Q1x = q1.x(), Q2x = q2.x(), Q3x = q3.x(); double P0y = p0.y(), P1y = p1.y(), P2y = p2.y(), P3y = q0.y(); double Q1y = q1.y(), Q2y = q2.y(), Q3y = q3.y(); out_pfactor = -(126*P0x*pow(Q2x,3.0)*pow(sp,3.0) - 49*pow(P0x,2.0)*pow(Q3x,2.0) - 88*pow(P0x,2.0)*pow(Q2y,2.0) - 88*pow(P0y,2.0)*pow(Q2x,2.0) - 88*pow(P0x,2.0)*pow(Q3y,2.0) - 88*pow(P0y,2.0)*pow(Q3x,2.0) - 49*pow(P0y,2.0)*pow(Q2y,2.0) - 49*pow(P0y,2.0)*pow(Q3y,2.0) - 49*pow(P0x,2.0)*pow(Q2x,2.0) + 21*P0x*pow(Q3x,3.0)*pow(sp,2.0) - 84*P0x*pow(Q2x,3.0)*pow(sp,4.0) + 14*P0x*pow(Q3x,3.0)*pow(sp,3.0) - 126*P1x*pow(Q2x,3.0)*pow(sp,3.0) - 21*P1x*pow(Q3x,3.0)*pow(sp,2.0) - 21*P0x*pow(Q3x,3.0)*pow(sp,4.0) + 84*P1x*pow(Q2x,3.0)*pow(sp,4.0) - 14*P1x*pow(Q3x,3.0)*pow(sp,3.0) + 21*P1x*pow(Q3x,3.0)*pow(sp,4.0) + 126*P0y*pow(Q2y,3.0)*pow(sp,3.0) + 21*P0y*pow(Q3y,3.0)*pow(sp,2.0) - 84*P0y*pow(Q2y,3.0)*pow(sp,4.0) + 14*P0y*pow(Q3y,3.0)*pow(sp,3.0) - 126*P1y*pow(Q2y,3.0)*pow(sp,3.0) - 21*P1y*pow(Q3y,3.0)*pow(sp,2.0) - 21*P0y*pow(Q3y,3.0)*pow(sp,4.0) + 84*P1y*pow(Q2y,3.0)*pow(sp,4.0) - 14*P1y*pow(Q3y,3.0)*pow(sp,3.0) + 21*P1y*pow(Q3y,3.0)*pow(sp,4.0) + 84*pow(P0x,2.0)*pow(Q2x,2.0)*pow(sp,2.0) - 77*pow(P0x,2.0)*pow(Q2x,2.0)*pow(sp,3.0) + 84*pow(P0x,2.0)*pow(Q3x,2.0)*pow(sp,2.0) - 168*pow(P1x,2.0)*pow(Q2x,2.0)*pow(sp,2.0) + 21*pow(P0x,2.0)*pow(Q2x,2.0)*pow(sp,4.0) - 77*pow(P0x,2.0)*pow(Q3x,2.0)*pow(sp,3.0) + 231*pow(P1x,2.0)*pow(Q2x,2.0)*pow(sp,3.0) - 168*pow(P1x,2.0)*pow(Q3x,2.0)*pow(sp,2.0) + 21*pow(P0x,2.0)*pow(Q3x,2.0)*pow(sp,4.0) - 84*pow(P1x,2.0)*pow(Q2x,2.0)*pow(sp,4.0) + 231*pow(P1x,2.0)*pow(Q3x,2.0)*pow(sp,3.0) - 84*pow(P1x,2.0)*pow(Q3x,2.0)*pow(sp,4.0) + 84*pow(P0x,2.0)*pow(Q2y,2.0)*pow(sp,2.0) + 84*pow(P0y,2.0)*pow(Q2x,2.0)*pow(sp,2.0) - 56*pow(P0x,2.0)*pow(Q2y,2.0)*pow(sp,3.0) + 84*pow(P0x,2.0)*pow(Q3y,2.0)*pow(sp,2.0) - 168*pow(P1x,2.0)*pow(Q2y,2.0)*pow(sp,2.0) - 56*pow(P0y,2.0)*pow(Q2x,2.0)*pow(sp,3.0) + 84*pow(P0y,2.0)*pow(Q3x,2.0)*pow(sp,2.0) - 168*pow(P1y,2.0)*pow(Q2x,2.0)*pow(sp,2.0) + 12*pow(P0x,2.0)*pow(Q2y,2.0)*pow(sp,4.0) - 56*pow(P0x,2.0)*pow(Q3y,2.0)*pow(sp,3.0) + 168*pow(P1x,2.0)*pow(Q2y,2.0)*pow(sp,3.0) - 168*pow(P1x,2.0)*pow(Q3y,2.0)*pow(sp,2.0) + 12*pow(P0y,2.0)*pow(Q2x,2.0)*pow(sp,4.0) - 56*pow(P0y,2.0)*pow(Q3x,2.0)*pow(sp,3.0) + 168*pow(P1y,2.0)*pow(Q2x,2.0)*pow(sp,3.0) - 168*pow(P1y,2.0)*pow(Q3x,2.0)*pow(sp,2.0) + 12*pow(P0x,2.0)*pow(Q3y,2.0)*pow(sp,4.0) - 48*pow(P1x,2.0)*pow(Q2y,2.0)*pow(sp,4.0) + 168*pow(P1x,2.0)*pow(Q3y,2.0)*pow(sp,3.0) + 12*pow(P0y,2.0)*pow(Q3x,2.0)*pow(sp,4.0) - 48*pow(P1y,2.0)*pow(Q2x,2.0)*pow(sp,4.0) + 168*pow(P1y,2.0)*pow(Q3x,2.0)*pow(sp,3.0) - 48*pow(P1x,2.0)*pow(Q3y,2.0)*pow(sp,4.0) - 48*pow(P1y,2.0)*pow(Q3x,2.0)*pow(sp,4.0) + 84*pow(P0y,2.0)*pow(Q2y,2.0)*pow(sp,2.0) - 77*pow(P0y,2.0)*pow(Q2y,2.0)*pow(sp,3.0) + 84*pow(P0y,2.0)*pow(Q3y,2.0)*pow(sp,2.0) - 168*pow(P1y,2.0)*pow(Q2y,2.0)*pow(sp,2.0) + 21*pow(P0y,2.0)*pow(Q2y,2.0)*pow(sp,4.0) - 77*pow(P0y,2.0)*pow(Q3y,2.0)*pow(sp,3.0) + 231*pow(P1y,2.0)*pow(Q2y,2.0)*pow(sp,3.0) - 168*pow(P1y,2.0)*pow(Q3y,2.0)*pow(sp,2.0) + 21*pow(P0y,2.0)*pow(Q3y,2.0)*pow(sp,4.0) - 84*pow(P1y,2.0)*pow(Q2y,2.0)*pow(sp,4.0) + 231*pow(P1y,2.0)*pow(Q3y,2.0)*pow(sp,3.0) - 84*pow(P1y,2.0)*pow(Q3y,2.0)*pow(sp,4.0) + 49*P0x*P1x*pow(Q2x,2.0) + 49*P0x*P1x*pow(Q3x,2.0) + 28*P0x*P3x*pow(Q2x,2.0) + 28*P0x*P3x*pow(Q3x,2.0) - 28*P1x*P3x*pow(Q2x,2.0) - 28*P1x*P3x*pow(Q3x,2.0) + 88*P0x*P1x*pow(Q2y,2.0) + 88*P0x*P1x*pow(Q3y,2.0) + 40*P0x*P3x*pow(Q2y,2.0) + 40*P0x*P3x*pow(Q3y,2.0) - 40*P1x*P3x*pow(Q2y,2.0) - 40*P1x*P3x*pow(Q3y,2.0) + 88*P0y*P1y*pow(Q2x,2.0) + 88*P0y*P1y*pow(Q3x,2.0) + 40*P0y*P3y*pow(Q2x,2.0) + 40*P0y*P3y*pow(Q3x,2.0) - 40*P1y*P3y*pow(Q2x,2.0) - 40*P1y*P3y*pow(Q3x,2.0) + 49*P0y*P1y*pow(Q2y,2.0) + 49*P0y*P1y*pow(Q3y,2.0) + 28*P0y*P3y*pow(Q2y,2.0) + 28*P0y*P3y*pow(Q3y,2.0) - 28*P1y*P3y*pow(Q2y,2.0) - 28*P1y*P3y*pow(Q3y,2.0) + 21*P0x*Q1x*pow(Q2x,2.0) + 21*P0x*Q1x*pow(Q3x,2.0) - 21*P1x*Q1x*pow(Q2x,2.0) - 21*P1x*Q1x*pow(Q3x,2.0) + 98*pow(P0x,2.0)*Q2x*Q3x + 48*P0x*Q1x*pow(Q2y,2.0) + 48*P0x*Q1x*pow(Q3y,2.0) - 48*P1x*Q1x*pow(Q2y,2.0) - 48*P1x*Q1x*pow(Q3y,2.0) + 176*pow(P0y,2.0)*Q2x*Q3x + 48*P0y*pow(Q2x,2.0)*Q1y + 48*P0y*pow(Q3x,2.0)*Q1y - 48*P1y*pow(Q2x,2.0)*Q1y - 48*P1y*pow(Q3x,2.0)*Q1y + 176*pow(P0x,2.0)*Q2y*Q3y + 21*P0y*Q1y*pow(Q2y,2.0) + 21*P0y*Q1y*pow(Q3y,2.0) - 21*P1y*Q1y*pow(Q2y,2.0) - 21*P1y*Q1y*pow(Q3y,2.0) + 98*pow(P0y,2.0)*Q2y*Q3y - 42*P0x*pow(Q2x,3.0)*sp + 42*P1x*pow(Q2x,3.0)*sp - 42*P0y*pow(Q2y,3.0)*sp + 42*P1y*pow(Q2y,3.0)*sp + 84*P0x*P3x*pow(Q2x,2.0)*sp + 84*P0x*P3x*pow(Q3x,2.0)*sp - 84*P1x*P3x*pow(Q2x,2.0)*sp - 84*P1x*P3x*pow(Q3x,2.0)*sp + 120*P0x*P3x*pow(Q2y,2.0)*sp + 120*P0x*P3x*pow(Q3y,2.0)*sp - 120*P1x*P3x*pow(Q2y,2.0)*sp - 120*P1x*P3x*pow(Q3y,2.0)*sp + 120*P0y*P3y*pow(Q2x,2.0)*sp + 120*P0y*P3y*pow(Q3x,2.0)*sp - 120*P1y*P3y*pow(Q2x,2.0)*sp - 120*P1y*P3y*pow(Q3x,2.0)*sp + 84*P0y*P3y*pow(Q2y,2.0)*sp + 84*P0y*P3y*pow(Q3y,2.0)*sp - 84*P1y*P3y*pow(Q2y,2.0)*sp - 84*P1y*P3y*pow(Q3y,2.0)*sp - 42*P0x*Q1x*pow(Q2x,2.0)*sp - 42*P0x*Q1x*pow(Q3x,2.0)*sp + 42*P1x*Q1x*pow(Q2x,2.0)*sp - 42*P0x*Q2x*pow(Q3x,2.0)*sp + 84*P0x*pow(Q2x,2.0)*Q3x*sp + 42*P1x*Q1x*pow(Q3x,2.0)*sp + 42*P1x*Q2x*pow(Q3x,2.0)*sp - 84*P1x*pow(Q2x,2.0)*Q3x*sp - 24*P0x*Q1x*pow(Q2y,2.0)*sp - 24*P0x*Q1x*pow(Q3y,2.0)*sp - 42*P0x*Q2x*pow(Q2y,2.0)*sp + 24*P1x*Q1x*pow(Q2y,2.0)*sp - 96*P0x*Q2x*pow(Q3y,2.0)*sp - 54*P0x*Q3x*pow(Q2y,2.0)*sp + 24*P1x*Q1x*pow(Q3y,2.0)*sp + 42*P1x*Q2x*pow(Q2y,2.0)*sp + 96*P1x*Q2x*pow(Q3y,2.0)*sp + 54*P1x*Q3x*pow(Q2y,2.0)*sp - 24*P0y*pow(Q2x,2.0)*Q1y*sp - 42*P0y*pow(Q2x,2.0)*Q2y*sp - 24*P0y*pow(Q3x,2.0)*Q1y*sp + 24*P1y*pow(Q2x,2.0)*Q1y*sp - 54*P0y*pow(Q2x,2.0)*Q3y*sp - 96*P0y*pow(Q3x,2.0)*Q2y*sp + 42*P1y*pow(Q2x,2.0)*Q2y*sp + 24*P1y*pow(Q3x,2.0)*Q1y*sp + 54*P1y*pow(Q2x,2.0)*Q3y*sp + 96*P1y*pow(Q3x,2.0)*Q2y*sp - 42*P0y*Q1y*pow(Q2y,2.0)*sp - 42*P0y*Q1y*pow(Q3y,2.0)*sp + 42*P1y*Q1y*pow(Q2y,2.0)*sp - 42*P0y*Q2y*pow(Q3y,2.0)*sp + 84*P0y*pow(Q2y,2.0)*Q3y*sp + 42*P1y*Q1y*pow(Q3y,2.0)*sp + 42*P1y*Q2y*pow(Q3y,2.0)*sp - 84*P1y*pow(Q2y,2.0)*Q3y*sp + 84*P0x*P1x*pow(Q2x,2.0)*pow(sp,2.0) - 154*P0x*P1x*pow(Q2x,2.0)*pow(sp,3.0) + 84*P0x*P1x*pow(Q3x,2.0)*pow(sp,2.0) + 252*P0x*P2x*pow(Q2x,2.0)*pow(sp,2.0) + 63*P0x*P1x*pow(Q2x,2.0)*pow(sp,4.0) - 154*P0x*P1x*pow(Q3x,2.0)*pow(sp,3.0) - 462*P0x*P2x*pow(Q2x,2.0)*pow(sp,3.0) + 252*P0x*P2x*pow(Q3x,2.0)*pow(sp,2.0) - 336*P0x*P3x*pow(Q2x,2.0)*pow(sp,2.0) - 252*P1x*P2x*pow(Q2x,2.0)*pow(sp,2.0) + 63*P0x*P1x*pow(Q3x,2.0)*pow(sp,4.0) + 210*P0x*P2x*pow(Q2x,2.0)*pow(sp,4.0) - 462*P0x*P2x*pow(Q3x,2.0)*pow(sp,3.0) + 210*P0x*P3x*pow(Q2x,2.0)*pow(sp,3.0) - 336*P0x*P3x*pow(Q3x,2.0)*pow(sp,2.0) + 462*P1x*P2x*pow(Q2x,2.0)*pow(sp,3.0) - 252*P1x*P2x*pow(Q3x,2.0)*pow(sp,2.0) + 336*P1x*P3x*pow(Q2x,2.0)*pow(sp,2.0) + 210*P0x*P2x*pow(Q3x,2.0)*pow(sp,4.0) + 210*P0x*P3x*pow(Q3x,2.0)*pow(sp,3.0) - 210*P1x*P2x*pow(Q2x,2.0)*pow(sp,4.0) + 462*P1x*P2x*pow(Q3x,2.0)*pow(sp,3.0) - 210*P1x*P3x*pow(Q2x,2.0)*pow(sp,3.0) + 336*P1x*P3x*pow(Q3x,2.0)*pow(sp,2.0) - 210*P1x*P2x*pow(Q3x,2.0)*pow(sp,4.0) - 210*P1x*P3x*pow(Q3x,2.0)*pow(sp,3.0) + 84*P0x*P1x*pow(Q2y,2.0)*pow(sp,2.0) - 112*P0x*P1x*pow(Q2y,2.0)*pow(sp,3.0) + 84*P0x*P1x*pow(Q3y,2.0)*pow(sp,2.0) + 252*P0x*P2x*pow(Q2y,2.0)*pow(sp,2.0) + 36*P0x*P1x*pow(Q2y,2.0)*pow(sp,4.0) - 112*P0x*P1x*pow(Q3y,2.0)*pow(sp,3.0) - 336*P0x*P2x*pow(Q2y,2.0)*pow(sp,3.0) + 252*P0x*P2x*pow(Q3y,2.0)*pow(sp,2.0) - 264*P0x*P3x*pow(Q2y,2.0)*pow(sp,2.0) - 252*P1x*P2x*pow(Q2y,2.0)*pow(sp,2.0) + 36*P0x*P1x*pow(Q3y,2.0)*pow(sp,4.0) + 120*P0x*P2x*pow(Q2y,2.0)*pow(sp,4.0) - 336*P0x*P2x*pow(Q3y,2.0)*pow(sp,3.0) + 120*P0x*P3x*pow(Q2y,2.0)*pow(sp,3.0) - 264*P0x*P3x*pow(Q3y,2.0)*pow(sp,2.0) + 336*P1x*P2x*pow(Q2y,2.0)*pow(sp,3.0) - 252*P1x*P2x*pow(Q3y,2.0)*pow(sp,2.0) + 264*P1x*P3x*pow(Q2y,2.0)*pow(sp,2.0) + 120*P0x*P2x*pow(Q3y,2.0)*pow(sp,4.0) + 120*P0x*P3x*pow(Q3y,2.0)*pow(sp,3.0) - 120*P1x*P2x*pow(Q2y,2.0)*pow(sp,4.0) + 336*P1x*P2x*pow(Q3y,2.0)*pow(sp,3.0) - 120*P1x*P3x*pow(Q2y,2.0)*pow(sp,3.0) + 264*P1x*P3x*pow(Q3y,2.0)*pow(sp,2.0) - 120*P1x*P2x*pow(Q3y,2.0)*pow(sp,4.0) - 120*P1x*P3x*pow(Q3y,2.0)*pow(sp,3.0) + 84*P0y*P1y*pow(Q2x,2.0)*pow(sp,2.0) - 112*P0y*P1y*pow(Q2x,2.0)*pow(sp,3.0) + 84*P0y*P1y*pow(Q3x,2.0)*pow(sp,2.0) + 252*P0y*P2y*pow(Q2x,2.0)*pow(sp,2.0) + 36*P0y*P1y*pow(Q2x,2.0)*pow(sp,4.0) - 112*P0y*P1y*pow(Q3x,2.0)*pow(sp,3.0) - 336*P0y*P2y*pow(Q2x,2.0)*pow(sp,3.0) + 252*P0y*P2y*pow(Q3x,2.0)*pow(sp,2.0) - 264*P0y*P3y*pow(Q2x,2.0)*pow(sp,2.0) - 252*P1y*P2y*pow(Q2x,2.0)*pow(sp,2.0) + 36*P0y*P1y*pow(Q3x,2.0)*pow(sp,4.0) + 120*P0y*P2y*pow(Q2x,2.0)*pow(sp,4.0) - 336*P0y*P2y*pow(Q3x,2.0)*pow(sp,3.0) + 120*P0y*P3y*pow(Q2x,2.0)*pow(sp,3.0) - 264*P0y*P3y*pow(Q3x,2.0)*pow(sp,2.0) + 336*P1y*P2y*pow(Q2x,2.0)*pow(sp,3.0) - 252*P1y*P2y*pow(Q3x,2.0)*pow(sp,2.0) + 264*P1y*P3y*pow(Q2x,2.0)*pow(sp,2.0) + 120*P0y*P2y*pow(Q3x,2.0)*pow(sp,4.0) + 120*P0y*P3y*pow(Q3x,2.0)*pow(sp,3.0) - 120*P1y*P2y*pow(Q2x,2.0)*pow(sp,4.0) + 336*P1y*P2y*pow(Q3x,2.0)*pow(sp,3.0) - 120*P1y*P3y*pow(Q2x,2.0)*pow(sp,3.0) + 264*P1y*P3y*pow(Q3x,2.0)*pow(sp,2.0) - 120*P1y*P2y*pow(Q3x,2.0)*pow(sp,4.0) - 120*P1y*P3y*pow(Q3x,2.0)*pow(sp,3.0) + 84*P0y*P1y*pow(Q2y,2.0)*pow(sp,2.0) - 154*P0y*P1y*pow(Q2y,2.0)*pow(sp,3.0) + 84*P0y*P1y*pow(Q3y,2.0)*pow(sp,2.0) + 252*P0y*P2y*pow(Q2y,2.0)*pow(sp,2.0) + 63*P0y*P1y*pow(Q2y,2.0)*pow(sp,4.0) - 154*P0y*P1y*pow(Q3y,2.0)*pow(sp,3.0) - 462*P0y*P2y*pow(Q2y,2.0)*pow(sp,3.0) + 252*P0y*P2y*pow(Q3y,2.0)*pow(sp,2.0) - 336*P0y*P3y*pow(Q2y,2.0)*pow(sp,2.0) - 252*P1y*P2y*pow(Q2y,2.0)*pow(sp,2.0) + 63*P0y*P1y*pow(Q3y,2.0)*pow(sp,4.0) + 210*P0y*P2y*pow(Q2y,2.0)*pow(sp,4.0) - 462*P0y*P2y*pow(Q3y,2.0)*pow(sp,3.0) + 210*P0y*P3y*pow(Q2y,2.0)*pow(sp,3.0) - 336*P0y*P3y*pow(Q3y,2.0)*pow(sp,2.0) + 462*P1y*P2y*pow(Q2y,2.0)*pow(sp,3.0) - 252*P1y*P2y*pow(Q3y,2.0)*pow(sp,2.0) + 336*P1y*P3y*pow(Q2y,2.0)*pow(sp,2.0) + 210*P0y*P2y*pow(Q3y,2.0)*pow(sp,4.0) + 210*P0y*P3y*pow(Q3y,2.0)*pow(sp,3.0) - 210*P1y*P2y*pow(Q2y,2.0)*pow(sp,4.0) + 462*P1y*P2y*pow(Q3y,2.0)*pow(sp,3.0) - 210*P1y*P3y*pow(Q2y,2.0)*pow(sp,3.0) + 336*P1y*P3y*pow(Q3y,2.0)*pow(sp,2.0) - 210*P1y*P2y*pow(Q3y,2.0)*pow(sp,4.0) - 210*P1y*P3y*pow(Q3y,2.0)*pow(sp,3.0) - 189*P0x*Q1x*pow(Q2x,2.0)*pow(sp,2.0) + 420*P0x*Q1x*pow(Q2x,2.0)*pow(sp,3.0) - 189*P0x*Q1x*pow(Q3x,2.0)*pow(sp,2.0) + 189*P1x*Q1x*pow(Q2x,2.0)*pow(sp,2.0) - 210*P0x*Q1x*pow(Q2x,2.0)*pow(sp,4.0) + 420*P0x*Q1x*pow(Q3x,2.0)*pow(sp,3.0) - 42*P0x*Q2x*pow(Q3x,2.0)*pow(sp,2.0) + 21*P0x*pow(Q2x,2.0)*Q3x*pow(sp,2.0) - 420*P1x*Q1x*pow(Q2x,2.0)*pow(sp,3.0) + 189*P1x*Q1x*pow(Q3x,2.0)*pow(sp,2.0) - 168*pow(P0x,2.0)*Q2x*Q3x*pow(sp,2.0) - 210*P0x*Q1x*pow(Q3x,2.0)*pow(sp,4.0) + 98*P0x*Q2x*pow(Q3x,2.0)*pow(sp,3.0) - 238*P0x*pow(Q2x,2.0)*Q3x*pow(sp,3.0) + 210*P1x*Q1x*pow(Q2x,2.0)*pow(sp,4.0) - 420*P1x*Q1x*pow(Q3x,2.0)*pow(sp,3.0) + 42*P1x*Q2x*pow(Q3x,2.0)*pow(sp,2.0) - 21*P1x*pow(Q2x,2.0)*Q3x*pow(sp,2.0) + 154*pow(P0x,2.0)*Q2x*Q3x*pow(sp,3.0) + 336*pow(P1x,2.0)*Q2x*Q3x*pow(sp,2.0) - 42*P0x*Q2x*pow(Q3x,2.0)*pow(sp,4.0) + 147*P0x*pow(Q2x,2.0)*Q3x*pow(sp,4.0) + 210*P1x*Q1x*pow(Q3x,2.0)*pow(sp,4.0) - 98*P1x*Q2x*pow(Q3x,2.0)*pow(sp,3.0) + 238*P1x*pow(Q2x,2.0)*Q3x*pow(sp,3.0) - 42*pow(P0x,2.0)*Q2x*Q3x*pow(sp,4.0) - 462*pow(P1x,2.0)*Q2x*Q3x*pow(sp,3.0) + 42*P1x*Q2x*pow(Q3x,2.0)*pow(sp,4.0) - 147*P1x*pow(Q2x,2.0)*Q3x*pow(sp,4.0) + 168*pow(P1x,2.0)*Q2x*Q3x*pow(sp,4.0) - 216*P0x*Q1x*pow(Q2y,2.0)*pow(sp,2.0) + 312*P0x*Q1x*pow(Q2y,2.0)*pow(sp,3.0) - 216*P0x*Q1x*pow(Q3y,2.0)*pow(sp,2.0) + 216*P1x*Q1x*pow(Q2y,2.0)*pow(sp,2.0) - 120*P0x*Q1x*pow(Q2y,2.0)*pow(sp,4.0) + 312*P0x*Q1x*pow(Q3y,2.0)*pow(sp,3.0) + 126*P0x*Q2x*pow(Q2y,2.0)*pow(sp,3.0) - 45*P0x*Q2x*pow(Q3y,2.0)*pow(sp,2.0) - 24*P0x*Q3x*pow(Q2y,2.0)*pow(sp,2.0) - 312*P1x*Q1x*pow(Q2y,2.0)*pow(sp,3.0) + 216*P1x*Q1x*pow(Q3y,2.0)*pow(sp,2.0) - 168*pow(P0y,2.0)*Q2x*Q3x*pow(sp,2.0) - 120*P0x*Q1x*pow(Q3y,2.0)*pow(sp,4.0) - 84*P0x*Q2x*pow(Q2y,2.0)*pow(sp,4.0) + 114*P0x*Q2x*pow(Q3y,2.0)*pow(sp,3.0) + 2*P0x*Q3x*pow(Q2y,2.0)*pow(sp,3.0) + 21*P0x*Q3x*pow(Q3y,2.0)*pow(sp,2.0) + 120*P1x*Q1x*pow(Q2y,2.0)*pow(sp,4.0) - 312*P1x*Q1x*pow(Q3y,2.0)*pow(sp,3.0) - 126*P1x*Q2x*pow(Q2y,2.0)*pow(sp,3.0) + 45*P1x*Q2x*pow(Q3y,2.0)*pow(sp,2.0) + 24*P1x*Q3x*pow(Q2y,2.0)*pow(sp,2.0) + 112*pow(P0y,2.0)*Q2x*Q3x*pow(sp,3.0) + 336*pow(P1y,2.0)*Q2x*Q3x*pow(sp,2.0) - 39*P0x*Q2x*pow(Q3y,2.0)*pow(sp,4.0) + 24*P0x*Q3x*pow(Q2y,2.0)*pow(sp,4.0) + 14*P0x*Q3x*pow(Q3y,2.0)*pow(sp,3.0) + 120*P1x*Q1x*pow(Q3y,2.0)*pow(sp,4.0) + 84*P1x*Q2x*pow(Q2y,2.0)*pow(sp,4.0) - 114*P1x*Q2x*pow(Q3y,2.0)*pow(sp,3.0) - 2*P1x*Q3x*pow(Q2y,2.0)*pow(sp,3.0) - 21*P1x*Q3x*pow(Q3y,2.0)*pow(sp,2.0) - 24*pow(P0y,2.0)*Q2x*Q3x*pow(sp,4.0) - 336*pow(P1y,2.0)*Q2x*Q3x*pow(sp,3.0) - 21*P0x*Q3x*pow(Q3y,2.0)*pow(sp,4.0) + 39*P1x*Q2x*pow(Q3y,2.0)*pow(sp,4.0) - 24*P1x*Q3x*pow(Q2y,2.0)*pow(sp,4.0) - 14*P1x*Q3x*pow(Q3y,2.0)*pow(sp,3.0) + 96*pow(P1y,2.0)*Q2x*Q3x*pow(sp,4.0) + 21*P1x*Q3x*pow(Q3y,2.0)*pow(sp,4.0) - 216*P0y*pow(Q2x,2.0)*Q1y*pow(sp,2.0) + 312*P0y*pow(Q2x,2.0)*Q1y*pow(sp,3.0) - 216*P0y*pow(Q3x,2.0)*Q1y*pow(sp,2.0) + 216*P1y*pow(Q2x,2.0)*Q1y*pow(sp,2.0) - 120*P0y*pow(Q2x,2.0)*Q1y*pow(sp,4.0) + 126*P0y*pow(Q2x,2.0)*Q2y*pow(sp,3.0) - 24*P0y*pow(Q2x,2.0)*Q3y*pow(sp,2.0) + 312*P0y*pow(Q3x,2.0)*Q1y*pow(sp,3.0) - 45*P0y*pow(Q3x,2.0)*Q2y*pow(sp,2.0) - 312*P1y*pow(Q2x,2.0)*Q1y*pow(sp,3.0) + 216*P1y*pow(Q3x,2.0)*Q1y*pow(sp,2.0) - 168*pow(P0x,2.0)*Q2y*Q3y*pow(sp,2.0) - 84*P0y*pow(Q2x,2.0)*Q2y*pow(sp,4.0) + 2*P0y*pow(Q2x,2.0)*Q3y*pow(sp,3.0) - 120*P0y*pow(Q3x,2.0)*Q1y*pow(sp,4.0) + 114*P0y*pow(Q3x,2.0)*Q2y*pow(sp,3.0) + 21*P0y*pow(Q3x,2.0)*Q3y*pow(sp,2.0) + 120*P1y*pow(Q2x,2.0)*Q1y*pow(sp,4.0) - 126*P1y*pow(Q2x,2.0)*Q2y*pow(sp,3.0) + 24*P1y*pow(Q2x,2.0)*Q3y*pow(sp,2.0) - 312*P1y*pow(Q3x,2.0)*Q1y*pow(sp,3.0) + 45*P1y*pow(Q3x,2.0)*Q2y*pow(sp,2.0) + 112*pow(P0x,2.0)*Q2y*Q3y*pow(sp,3.0) + 336*pow(P1x,2.0)*Q2y*Q3y*pow(sp,2.0) + 24*P0y*pow(Q2x,2.0)*Q3y*pow(sp,4.0) - 39*P0y*pow(Q3x,2.0)*Q2y*pow(sp,4.0) + 14*P0y*pow(Q3x,2.0)*Q3y*pow(sp,3.0) + 84*P1y*pow(Q2x,2.0)*Q2y*pow(sp,4.0) - 2*P1y*pow(Q2x,2.0)*Q3y*pow(sp,3.0) + 120*P1y*pow(Q3x,2.0)*Q1y*pow(sp,4.0) - 114*P1y*pow(Q3x,2.0)*Q2y*pow(sp,3.0) - 21*P1y*pow(Q3x,2.0)*Q3y*pow(sp,2.0) - 24*pow(P0x,2.0)*Q2y*Q3y*pow(sp,4.0) - 336*pow(P1x,2.0)*Q2y*Q3y*pow(sp,3.0) - 21*P0y*pow(Q3x,2.0)*Q3y*pow(sp,4.0) - 24*P1y*pow(Q2x,2.0)*Q3y*pow(sp,4.0) + 39*P1y*pow(Q3x,2.0)*Q2y*pow(sp,4.0) - 14*P1y*pow(Q3x,2.0)*Q3y*pow(sp,3.0) + 96*pow(P1x,2.0)*Q2y*Q3y*pow(sp,4.0) + 21*P1y*pow(Q3x,2.0)*Q3y*pow(sp,4.0) - 189*P0y*Q1y*pow(Q2y,2.0)*pow(sp,2.0) + 420*P0y*Q1y*pow(Q2y,2.0)*pow(sp,3.0) - 189*P0y*Q1y*pow(Q3y,2.0)*pow(sp,2.0) + 189*P1y*Q1y*pow(Q2y,2.0)*pow(sp,2.0) - 210*P0y*Q1y*pow(Q2y,2.0)*pow(sp,4.0) + 420*P0y*Q1y*pow(Q3y,2.0)*pow(sp,3.0) - 42*P0y*Q2y*pow(Q3y,2.0)*pow(sp,2.0) + 21*P0y*pow(Q2y,2.0)*Q3y*pow(sp,2.0) - 420*P1y*Q1y*pow(Q2y,2.0)*pow(sp,3.0) + 189*P1y*Q1y*pow(Q3y,2.0)*pow(sp,2.0) - 168*pow(P0y,2.0)*Q2y*Q3y*pow(sp,2.0) - 210*P0y*Q1y*pow(Q3y,2.0)*pow(sp,4.0) + 98*P0y*Q2y*pow(Q3y,2.0)*pow(sp,3.0) - 238*P0y*pow(Q2y,2.0)*Q3y*pow(sp,3.0) + 210*P1y*Q1y*pow(Q2y,2.0)*pow(sp,4.0) - 420*P1y*Q1y*pow(Q3y,2.0)*pow(sp,3.0) + 42*P1y*Q2y*pow(Q3y,2.0)*pow(sp,2.0) - 21*P1y*pow(Q2y,2.0)*Q3y*pow(sp,2.0) + 154*pow(P0y,2.0)*Q2y*Q3y*pow(sp,3.0) + 336*pow(P1y,2.0)*Q2y*Q3y*pow(sp,2.0) - 42*P0y*Q2y*pow(Q3y,2.0)*pow(sp,4.0) + 147*P0y*pow(Q2y,2.0)*Q3y*pow(sp,4.0) + 210*P1y*Q1y*pow(Q3y,2.0)*pow(sp,4.0) - 98*P1y*Q2y*pow(Q3y,2.0)*pow(sp,3.0) + 238*P1y*pow(Q2y,2.0)*Q3y*pow(sp,3.0) - 42*pow(P0y,2.0)*Q2y*Q3y*pow(sp,4.0) - 462*pow(P1y,2.0)*Q2y*Q3y*pow(sp,3.0) + 42*P1y*Q2y*pow(Q3y,2.0)*pow(sp,4.0) - 147*P1y*pow(Q2y,2.0)*Q3y*pow(sp,4.0) + 168*pow(P1y,2.0)*Q2y*Q3y*pow(sp,4.0) - 98*P0x*P1x*Q2x*Q3x - 56*P0x*P3x*Q2x*Q3x + 56*P1x*P3x*Q2x*Q3x + 78*P0x*P0y*Q2x*Q2y - 78*P0x*P0y*Q2x*Q3y - 78*P0x*P0y*Q3x*Q2y - 39*P0x*P1y*Q2x*Q2y - 39*P1x*P0y*Q2x*Q2y - 176*P0x*P1x*Q2y*Q3y + 78*P0x*P0y*Q3x*Q3y + 39*P0x*P1y*Q2x*Q3y + 39*P0x*P1y*Q3x*Q2y + 39*P1x*P0y*Q2x*Q3y + 39*P1x*P0y*Q3x*Q2y - 176*P0y*P1y*Q2x*Q3x - 39*P0x*P1y*Q3x*Q3y - 12*P0x*P3y*Q2x*Q2y - 39*P1x*P0y*Q3x*Q3y - 12*P3x*P0y*Q2x*Q2y - 80*P0x*P3x*Q2y*Q3y + 12*P0x*P3y*Q2x*Q3y + 12*P0x*P3y*Q3x*Q2y + 12*P1x*P3y*Q2x*Q2y + 12*P3x*P0y*Q2x*Q3y + 12*P3x*P0y*Q3x*Q2y + 12*P3x*P1y*Q2x*Q2y - 80*P0y*P3y*Q2x*Q3x - 12*P0x*P3y*Q3x*Q3y + 80*P1x*P3x*Q2y*Q3y - 12*P1x*P3y*Q2x*Q3y - 12*P1x*P3y*Q3x*Q2y - 12*P3x*P0y*Q3x*Q3y - 12*P3x*P1y*Q2x*Q3y - 12*P3x*P1y*Q3x*Q2y + 80*P1y*P3y*Q2x*Q3x + 12*P1x*P3y*Q3x*Q3y + 12*P3x*P1y*Q3x*Q3y - 98*P0y*P1y*Q2y*Q3y - 56*P0y*P3y*Q2y*Q3y + 56*P1y*P3y*Q2y*Q3y - 42*P0x*Q1x*Q2x*Q3x + 42*P1x*Q1x*Q2x*Q3x - 27*P0x*Q2x*Q1y*Q2y - 27*P0y*Q1x*Q2x*Q2y - 96*P0x*Q1x*Q2y*Q3y + 27*P0x*Q2x*Q1y*Q3y + 27*P0x*Q3x*Q1y*Q2y + 27*P1x*Q2x*Q1y*Q2y + 27*P0y*Q1x*Q2x*Q3y + 27*P0y*Q1x*Q3x*Q2y - 96*P0y*Q2x*Q3x*Q1y + 27*P1y*Q1x*Q2x*Q2y - 27*P0x*Q3x*Q1y*Q3y + 96*P1x*Q1x*Q2y*Q3y - 27*P1x*Q2x*Q1y*Q3y - 27*P1x*Q3x*Q1y*Q2y - 27*P0y*Q1x*Q3x*Q3y - 27*P1y*Q1x*Q2x*Q3y - 27*P1y*Q1x*Q3x*Q2y + 96*P1y*Q2x*Q3x*Q1y + 27*P1x*Q3x*Q1y*Q3y + 27*P1y*Q1x*Q3x*Q3y - 42*P0y*Q1y*Q2y*Q3y + 42*P1y*Q1y*Q2y*Q3y - 168*P0x*P3x*Q2x*Q3x*sp + 168*P1x*P3x*Q2x*Q3x*sp - 36*P0x*P3y*Q2x*Q2y*sp - 36*P3x*P0y*Q2x*Q2y*sp - 240*P0x*P3x*Q2y*Q3y*sp + 36*P0x*P3y*Q2x*Q3y*sp + 36*P0x*P3y*Q3x*Q2y*sp + 36*P1x*P3y*Q2x*Q2y*sp + 36*P3x*P0y*Q2x*Q3y*sp + 36*P3x*P0y*Q3x*Q2y*sp + 36*P3x*P1y*Q2x*Q2y*sp - 240*P0y*P3y*Q2x*Q3x*sp - 36*P0x*P3y*Q3x*Q3y*sp + 240*P1x*P3x*Q2y*Q3y*sp - 36*P1x*P3y*Q2x*Q3y*sp - 36*P1x*P3y*Q3x*Q2y*sp - 36*P3x*P0y*Q3x*Q3y*sp - 36*P3x*P1y*Q2x*Q3y*sp - 36*P3x*P1y*Q3x*Q2y*sp + 240*P1y*P3y*Q2x*Q3x*sp + 36*P1x*P3y*Q3x*Q3y*sp + 36*P3x*P1y*Q3x*Q3y*sp - 168*P0y*P3y*Q2y*Q3y*sp + 168*P1y*P3y*Q2y*Q3y*sp + 84*P0x*Q1x*Q2x*Q3x*sp - 84*P1x*Q1x*Q2x*Q3x*sp - 18*P0x*Q2x*Q1y*Q2y*sp - 18*P0y*Q1x*Q2x*Q2y*sp + 48*P0x*Q1x*Q2y*Q3y*sp + 18*P0x*Q2x*Q1y*Q3y*sp + 18*P0x*Q3x*Q1y*Q2y*sp + 18*P1x*Q2x*Q1y*Q2y*sp + 18*P0y*Q1x*Q2x*Q3y*sp + 18*P0y*Q1x*Q3x*Q2y*sp + 48*P0y*Q2x*Q3x*Q1y*sp + 18*P1y*Q1x*Q2x*Q2y*sp + 138*P0x*Q2x*Q2y*Q3y*sp - 18*P0x*Q3x*Q1y*Q3y*sp - 48*P1x*Q1x*Q2y*Q3y*sp - 18*P1x*Q2x*Q1y*Q3y*sp - 18*P1x*Q3x*Q1y*Q2y*sp - 18*P0y*Q1x*Q3x*Q3y*sp + 138*P0y*Q2x*Q3x*Q2y*sp - 18*P1y*Q1x*Q2x*Q3y*sp - 18*P1y*Q1x*Q3x*Q2y*sp - 48*P1y*Q2x*Q3x*Q1y*sp + 54*P0x*Q3x*Q2y*Q3y*sp - 138*P1x*Q2x*Q2y*Q3y*sp + 18*P1x*Q3x*Q1y*Q3y*sp + 54*P0y*Q2x*Q3x*Q3y*sp + 18*P1y*Q1x*Q3x*Q3y*sp - 138*P1y*Q2x*Q3x*Q2y*sp - 54*P1x*Q3x*Q2y*Q3y*sp - 54*P1y*Q2x*Q3x*Q3y*sp + 84*P0y*Q1y*Q2y*Q3y*sp - 84*P1y*Q1y*Q2y*Q3y*sp - 168*P0x*P1x*Q2x*Q3x*pow(sp,2.0) + 308*P0x*P1x*Q2x*Q3x*pow(sp,3.0) - 504*P0x*P2x*Q2x*Q3x*pow(sp,2.0) - 126*P0x*P1x*Q2x*Q3x*pow(sp,4.0) + 924*P0x*P2x*Q2x*Q3x*pow(sp,3.0) + 672*P0x*P3x*Q2x*Q3x*pow(sp,2.0) + 504*P1x*P2x*Q2x*Q3x*pow(sp,2.0) - 420*P0x*P2x*Q2x*Q3x*pow(sp,4.0) - 420*P0x*P3x*Q2x*Q3x*pow(sp,3.0) - 924*P1x*P2x*Q2x*Q3x*pow(sp,3.0) - 672*P1x*P3x*Q2x*Q3x*pow(sp,2.0) + 420*P1x*P2x*Q2x*Q3x*pow(sp,4.0) + 420*P1x*P3x*Q2x*Q3x*pow(sp,3.0) - 42*P0x*P0y*Q2x*Q2y*pow(sp,3.0) - 168*P0x*P1x*Q2y*Q3y*pow(sp,2.0) + 18*P0x*P0y*Q2x*Q2y*pow(sp,4.0) + 42*P0x*P0y*Q2x*Q3y*pow(sp,3.0) + 42*P0x*P0y*Q3x*Q2y*pow(sp,3.0) - 42*P0x*P1y*Q2x*Q2y*pow(sp,3.0) - 42*P1x*P0y*Q2x*Q2y*pow(sp,3.0) - 168*P0y*P1y*Q2x*Q3x*pow(sp,2.0) + 224*P0x*P1x*Q2y*Q3y*pow(sp,3.0) - 504*P0x*P2x*Q2y*Q3y*pow(sp,2.0) - 18*P0x*P0y*Q2x*Q3y*pow(sp,4.0) - 18*P0x*P0y*Q3x*Q2y*pow(sp,4.0) - 42*P0x*P0y*Q3x*Q3y*pow(sp,3.0) + 27*P0x*P1y*Q2x*Q2y*pow(sp,4.0) + 42*P0x*P1y*Q2x*Q3y*pow(sp,3.0) + 42*P0x*P1y*Q3x*Q2y*pow(sp,3.0) - 126*P0x*P2y*Q2x*Q2y*pow(sp,3.0) - 72*P0x*P3y*Q2x*Q2y*pow(sp,2.0) + 27*P1x*P0y*Q2x*Q2y*pow(sp,4.0) + 42*P1x*P0y*Q2x*Q3y*pow(sp,3.0) + 42*P1x*P0y*Q3x*Q2y*pow(sp,3.0) + 126*P1x*P1y*Q2x*Q2y*pow(sp,3.0) - 126*P2x*P0y*Q2x*Q2y*pow(sp,3.0) - 72*P3x*P0y*Q2x*Q2y*pow(sp,2.0) + 224*P0y*P1y*Q2x*Q3x*pow(sp,3.0) - 504*P0y*P2y*Q2x*Q3x*pow(sp,2.0) - 72*P0x*P1x*Q2y*Q3y*pow(sp,4.0) + 672*P0x*P2x*Q2y*Q3y*pow(sp,3.0) + 528*P0x*P3x*Q2y*Q3y*pow(sp,2.0) + 18*P0x*P0y*Q3x*Q3y*pow(sp,4.0) - 27*P0x*P1y*Q2x*Q3y*pow(sp,4.0) - 27*P0x*P1y*Q3x*Q2y*pow(sp,4.0) - 42*P0x*P1y*Q3x*Q3y*pow(sp,3.0) + 90*P0x*P2y*Q2x*Q2y*pow(sp,4.0) + 126*P0x*P2y*Q2x*Q3y*pow(sp,3.0) + 126*P0x*P2y*Q3x*Q2y*pow(sp,3.0) + 90*P0x*P3y*Q2x*Q2y*pow(sp,3.0) + 72*P0x*P3y*Q2x*Q3y*pow(sp,2.0) + 72*P0x*P3y*Q3x*Q2y*pow(sp,2.0) + 504*P1x*P2x*Q2y*Q3y*pow(sp,2.0) - 27*P1x*P0y*Q2x*Q3y*pow(sp,4.0) - 27*P1x*P0y*Q3x*Q2y*pow(sp,4.0) - 42*P1x*P0y*Q3x*Q3y*pow(sp,3.0) - 72*P1x*P1y*Q2x*Q2y*pow(sp,4.0) - 126*P1x*P1y*Q2x*Q3y*pow(sp,3.0) - 126*P1x*P1y*Q3x*Q2y*pow(sp,3.0) + 126*P1x*P2y*Q2x*Q2y*pow(sp,3.0) + 72*P1x*P3y*Q2x*Q2y*pow(sp,2.0) + 90*P2x*P0y*Q2x*Q2y*pow(sp,4.0) + 126*P2x*P0y*Q2x*Q3y*pow(sp,3.0) + 126*P2x*P0y*Q3x*Q2y*pow(sp,3.0) + 126*P2x*P1y*Q2x*Q2y*pow(sp,3.0) + 90*P3x*P0y*Q2x*Q2y*pow(sp,3.0) + 72*P3x*P0y*Q2x*Q3y*pow(sp,2.0) + 72*P3x*P0y*Q3x*Q2y*pow(sp,2.0) + 72*P3x*P1y*Q2x*Q2y*pow(sp,2.0) - 72*P0y*P1y*Q2x*Q3x*pow(sp,4.0) + 672*P0y*P2y*Q2x*Q3x*pow(sp,3.0) + 528*P0y*P3y*Q2x*Q3x*pow(sp,2.0) + 504*P1y*P2y*Q2x*Q3x*pow(sp,2.0) - 240*P0x*P2x*Q2y*Q3y*pow(sp,4.0) - 240*P0x*P3x*Q2y*Q3y*pow(sp,3.0) + 27*P0x*P1y*Q3x*Q3y*pow(sp,4.0) - 90*P0x*P2y*Q2x*Q3y*pow(sp,4.0) - 90*P0x*P2y*Q3x*Q2y*pow(sp,4.0) - 126*P0x*P2y*Q3x*Q3y*pow(sp,3.0) - 90*P0x*P3y*Q2x*Q3y*pow(sp,3.0) - 90*P0x*P3y*Q3x*Q2y*pow(sp,3.0) - 72*P0x*P3y*Q3x*Q3y*pow(sp,2.0) - 672*P1x*P2x*Q2y*Q3y*pow(sp,3.0) - 528*P1x*P3x*Q2y*Q3y*pow(sp,2.0) + 27*P1x*P0y*Q3x*Q3y*pow(sp,4.0) + 72*P1x*P1y*Q2x*Q3y*pow(sp,4.0) + 72*P1x*P1y*Q3x*Q2y*pow(sp,4.0) + 126*P1x*P1y*Q3x*Q3y*pow(sp,3.0) - 90*P1x*P2y*Q2x*Q2y*pow(sp,4.0) - 126*P1x*P2y*Q2x*Q3y*pow(sp,3.0) - 126*P1x*P2y*Q3x*Q2y*pow(sp,3.0) - 90*P1x*P3y*Q2x*Q2y*pow(sp,3.0) - 72*P1x*P3y*Q2x*Q3y*pow(sp,2.0) - 72*P1x*P3y*Q3x*Q2y*pow(sp,2.0) - 90*P2x*P0y*Q2x*Q3y*pow(sp,4.0) - 90*P2x*P0y*Q3x*Q2y*pow(sp,4.0) - 126*P2x*P0y*Q3x*Q3y*pow(sp,3.0) - 90*P2x*P1y*Q2x*Q2y*pow(sp,4.0) - 126*P2x*P1y*Q2x*Q3y*pow(sp,3.0) - 126*P2x*P1y*Q3x*Q2y*pow(sp,3.0) - 90*P3x*P0y*Q2x*Q3y*pow(sp,3.0) - 90*P3x*P0y*Q3x*Q2y*pow(sp,3.0) - 72*P3x*P0y*Q3x*Q3y*pow(sp,2.0) - 90*P3x*P1y*Q2x*Q2y*pow(sp,3.0) - 72*P3x*P1y*Q2x*Q3y*pow(sp,2.0) - 72*P3x*P1y*Q3x*Q2y*pow(sp,2.0) - 240*P0y*P2y*Q2x*Q3x*pow(sp,4.0) - 240*P0y*P3y*Q2x*Q3x*pow(sp,3.0) - 672*P1y*P2y*Q2x*Q3x*pow(sp,3.0) - 528*P1y*P3y*Q2x*Q3x*pow(sp,2.0) + 90*P0x*P2y*Q3x*Q3y*pow(sp,4.0) + 90*P0x*P3y*Q3x*Q3y*pow(sp,3.0) + 240*P1x*P2x*Q2y*Q3y*pow(sp,4.0) + 240*P1x*P3x*Q2y*Q3y*pow(sp,3.0) - 72*P1x*P1y*Q3x*Q3y*pow(sp,4.0) + 90*P1x*P2y*Q2x*Q3y*pow(sp,4.0) + 90*P1x*P2y*Q3x*Q2y*pow(sp,4.0) + 126*P1x*P2y*Q3x*Q3y*pow(sp,3.0) + 90*P1x*P3y*Q2x*Q3y*pow(sp,3.0) + 90*P1x*P3y*Q3x*Q2y*pow(sp,3.0) + 72*P1x*P3y*Q3x*Q3y*pow(sp,2.0) + 90*P2x*P0y*Q3x*Q3y*pow(sp,4.0) + 90*P2x*P1y*Q2x*Q3y*pow(sp,4.0) + 90*P2x*P1y*Q3x*Q2y*pow(sp,4.0) + 126*P2x*P1y*Q3x*Q3y*pow(sp,3.0) + 90*P3x*P0y*Q3x*Q3y*pow(sp,3.0) + 90*P3x*P1y*Q2x*Q3y*pow(sp,3.0) + 90*P3x*P1y*Q3x*Q2y*pow(sp,3.0) + 72*P3x*P1y*Q3x*Q3y*pow(sp,2.0) + 240*P1y*P2y*Q2x*Q3x*pow(sp,4.0) + 240*P1y*P3y*Q2x*Q3x*pow(sp,3.0) - 90*P1x*P2y*Q3x*Q3y*pow(sp,4.0) - 90*P1x*P3y*Q3x*Q3y*pow(sp,3.0) - 90*P2x*P1y*Q3x*Q3y*pow(sp,4.0) - 90*P3x*P1y*Q3x*Q3y*pow(sp,3.0) - 168*P0y*P1y*Q2y*Q3y*pow(sp,2.0) + 308*P0y*P1y*Q2y*Q3y*pow(sp,3.0) - 504*P0y*P2y*Q2y*Q3y*pow(sp,2.0) - 126*P0y*P1y*Q2y*Q3y*pow(sp,4.0) + 924*P0y*P2y*Q2y*Q3y*pow(sp,3.0) + 672*P0y*P3y*Q2y*Q3y*pow(sp,2.0) + 504*P1y*P2y*Q2y*Q3y*pow(sp,2.0) - 420*P0y*P2y*Q2y*Q3y*pow(sp,4.0) - 420*P0y*P3y*Q2y*Q3y*pow(sp,3.0) - 924*P1y*P2y*Q2y*Q3y*pow(sp,3.0) - 672*P1y*P3y*Q2y*Q3y*pow(sp,2.0) + 420*P1y*P2y*Q2y*Q3y*pow(sp,4.0) + 420*P1y*P3y*Q2y*Q3y*pow(sp,3.0) + 378*P0x*Q1x*Q2x*Q3x*pow(sp,2.0) - 840*P0x*Q1x*Q2x*Q3x*pow(sp,3.0) - 378*P1x*Q1x*Q2x*Q3x*pow(sp,2.0) + 420*P0x*Q1x*Q2x*Q3x*pow(sp,4.0) + 840*P1x*Q1x*Q2x*Q3x*pow(sp,3.0) - 420*P1x*Q1x*Q2x*Q3x*pow(sp,4.0) + 27*P0x*Q2x*Q1y*Q2y*pow(sp,2.0) + 27*P0y*Q1x*Q2x*Q2y*pow(sp,2.0) + 432*P0x*Q1x*Q2y*Q3y*pow(sp,2.0) + 108*P0x*Q2x*Q1y*Q2y*pow(sp,3.0) - 27*P0x*Q2x*Q1y*Q3y*pow(sp,2.0) - 27*P0x*Q3x*Q1y*Q2y*pow(sp,2.0) - 27*P1x*Q2x*Q1y*Q2y*pow(sp,2.0) + 108*P0y*Q1x*Q2x*Q2y*pow(sp,3.0) - 27*P0y*Q1x*Q2x*Q3y*pow(sp,2.0) - 27*P0y*Q1x*Q3x*Q2y*pow(sp,2.0) + 432*P0y*Q2x*Q3x*Q1y*pow(sp,2.0) - 27*P1y*Q1x*Q2x*Q2y*pow(sp,2.0) - 624*P0x*Q1x*Q2y*Q3y*pow(sp,3.0) - 90*P0x*Q2x*Q1y*Q2y*pow(sp,4.0) - 108*P0x*Q2x*Q1y*Q3y*pow(sp,3.0) + 45*P0x*Q2x*Q2y*Q3y*pow(sp,2.0) - 108*P0x*Q3x*Q1y*Q2y*pow(sp,3.0) + 27*P0x*Q3x*Q1y*Q3y*pow(sp,2.0) - 432*P1x*Q1x*Q2y*Q3y*pow(sp,2.0) - 108*P1x*Q2x*Q1y*Q2y*pow(sp,3.0) + 27*P1x*Q2x*Q1y*Q3y*pow(sp,2.0) + 27*P1x*Q3x*Q1y*Q2y*pow(sp,2.0) - 90*P0y*Q1x*Q2x*Q2y*pow(sp,4.0) - 108*P0y*Q1x*Q2x*Q3y*pow(sp,3.0) - 108*P0y*Q1x*Q3x*Q2y*pow(sp,3.0) + 27*P0y*Q1x*Q3x*Q3y*pow(sp,2.0) - 624*P0y*Q2x*Q3x*Q1y*pow(sp,3.0) + 45*P0y*Q2x*Q3x*Q2y*pow(sp,2.0) - 108*P1y*Q1x*Q2x*Q2y*pow(sp,3.0) + 27*P1y*Q1x*Q2x*Q3y*pow(sp,2.0) + 27*P1y*Q1x*Q3x*Q2y*pow(sp,2.0) - 432*P1y*Q2x*Q3x*Q1y*pow(sp,2.0) + 240*P0x*Q1x*Q2y*Q3y*pow(sp,4.0) + 90*P0x*Q2x*Q1y*Q3y*pow(sp,4.0) - 240*P0x*Q2x*Q2y*Q3y*pow(sp,3.0) + 90*P0x*Q3x*Q1y*Q2y*pow(sp,4.0) + 108*P0x*Q3x*Q1y*Q3y*pow(sp,3.0) + 3*P0x*Q3x*Q2y*Q3y*pow(sp,2.0) + 624*P1x*Q1x*Q2y*Q3y*pow(sp,3.0) + 90*P1x*Q2x*Q1y*Q2y*pow(sp,4.0) + 108*P1x*Q2x*Q1y*Q3y*pow(sp,3.0) - 45*P1x*Q2x*Q2y*Q3y*pow(sp,2.0) + 108*P1x*Q3x*Q1y*Q2y*pow(sp,3.0) - 27*P1x*Q3x*Q1y*Q3y*pow(sp,2.0) + 90*P0y*Q1x*Q2x*Q3y*pow(sp,4.0) + 90*P0y*Q1x*Q3x*Q2y*pow(sp,4.0) + 108*P0y*Q1x*Q3x*Q3y*pow(sp,3.0) + 240*P0y*Q2x*Q3x*Q1y*pow(sp,4.0) - 240*P0y*Q2x*Q3x*Q2y*pow(sp,3.0) + 3*P0y*Q2x*Q3x*Q3y*pow(sp,2.0) + 90*P1y*Q1x*Q2x*Q2y*pow(sp,4.0) + 108*P1y*Q1x*Q2x*Q3y*pow(sp,3.0) + 108*P1y*Q1x*Q3x*Q2y*pow(sp,3.0) - 27*P1y*Q1x*Q3x*Q3y*pow(sp,2.0) + 624*P1y*Q2x*Q3x*Q1y*pow(sp,3.0) - 45*P1y*Q2x*Q3x*Q2y*pow(sp,2.0) + 123*P0x*Q2x*Q2y*Q3y*pow(sp,4.0) - 90*P0x*Q3x*Q1y*Q3y*pow(sp,4.0) - 16*P0x*Q3x*Q2y*Q3y*pow(sp,3.0) - 240*P1x*Q1x*Q2y*Q3y*pow(sp,4.0) - 90*P1x*Q2x*Q1y*Q3y*pow(sp,4.0) + 240*P1x*Q2x*Q2y*Q3y*pow(sp,3.0) - 90*P1x*Q3x*Q1y*Q2y*pow(sp,4.0) - 108*P1x*Q3x*Q1y*Q3y*pow(sp,3.0) - 3*P1x*Q3x*Q2y*Q3y*pow(sp,2.0) - 90*P0y*Q1x*Q3x*Q3y*pow(sp,4.0) + 123*P0y*Q2x*Q3x*Q2y*pow(sp,4.0) - 16*P0y*Q2x*Q3x*Q3y*pow(sp,3.0) - 90*P1y*Q1x*Q2x*Q3y*pow(sp,4.0) - 90*P1y*Q1x*Q3x*Q2y*pow(sp,4.0) - 108*P1y*Q1x*Q3x*Q3y*pow(sp,3.0) - 240*P1y*Q2x*Q3x*Q1y*pow(sp,4.0) + 240*P1y*Q2x*Q3x*Q2y*pow(sp,3.0) - 3*P1y*Q2x*Q3x*Q3y*pow(sp,2.0) - 3*P0x*Q3x*Q2y*Q3y*pow(sp,4.0) - 123*P1x*Q2x*Q2y*Q3y*pow(sp,4.0) + 90*P1x*Q3x*Q1y*Q3y*pow(sp,4.0) + 16*P1x*Q3x*Q2y*Q3y*pow(sp,3.0) - 3*P0y*Q2x*Q3x*Q3y*pow(sp,4.0) + 90*P1y*Q1x*Q3x*Q3y*pow(sp,4.0) - 123*P1y*Q2x*Q3x*Q2y*pow(sp,4.0) + 16*P1y*Q2x*Q3x*Q3y*pow(sp,3.0) + 3*P1x*Q3x*Q2y*Q3y*pow(sp,4.0) + 3*P1y*Q2x*Q3x*Q3y*pow(sp,4.0) + 378*P0y*Q1y*Q2y*Q3y*pow(sp,2.0) - 840*P0y*Q1y*Q2y*Q3y*pow(sp,3.0) - 378*P1y*Q1y*Q2y*Q3y*pow(sp,2.0) + 420*P0y*Q1y*Q2y*Q3y*pow(sp,4.0) + 840*P1y*Q1y*Q2y*Q3y*pow(sp,3.0) - 420*P1y*Q1y*Q2y*Q3y*pow(sp,4.0))/(3*(7*pow(P0x,2.0)*pow(Q2x,2.0) + 7*pow(P0x,2.0)*pow(Q3x,2.0) + 7*pow(P1x,2.0)*pow(Q2x,2.0) + 7*pow(P1x,2.0)*pow(Q3x,2.0) + 16*pow(P0x,2.0)*pow(Q2y,2.0) + 16*pow(P0y,2.0)*pow(Q2x,2.0) + 16*pow(P0x,2.0)*pow(Q3y,2.0) + 16*pow(P1x,2.0)*pow(Q2y,2.0) + 16*pow(P0y,2.0)*pow(Q3x,2.0) + 16*pow(P1y,2.0)*pow(Q2x,2.0) + 16*pow(P1x,2.0)*pow(Q3y,2.0) + 16*pow(P1y,2.0)*pow(Q3x,2.0) + 7*pow(P0y,2.0)*pow(Q2y,2.0) + 7*pow(P0y,2.0)*pow(Q3y,2.0) + 7*pow(P1y,2.0)*pow(Q2y,2.0) + 7*pow(P1y,2.0)*pow(Q3y,2.0) - 14*P0x*P1x*pow(Q2x,2.0) - 14*P0x*P1x*pow(Q3x,2.0) - 32*P0x*P1x*pow(Q2y,2.0) - 32*P0x*P1x*pow(Q3y,2.0) - 32*P0y*P1y*pow(Q2x,2.0) - 32*P0y*P1y*pow(Q3x,2.0) - 14*P0y*P1y*pow(Q2y,2.0) - 14*P0y*P1y*pow(Q3y,2.0) - 14*pow(P0x,2.0)*Q2x*Q3x - 14*pow(P1x,2.0)*Q2x*Q3x - 32*pow(P0y,2.0)*Q2x*Q3x - 32*pow(P1y,2.0)*Q2x*Q3x - 32*pow(P0x,2.0)*Q2y*Q3y - 32*pow(P1x,2.0)*Q2y*Q3y - 14*pow(P0y,2.0)*Q2y*Q3y - 14*pow(P1y,2.0)*Q2y*Q3y + 28*P0x*P1x*Q2x*Q3x - 18*P0x*P0y*Q2x*Q2y + 18*P0x*P0y*Q2x*Q3y + 18*P0x*P0y*Q3x*Q2y + 18*P0x*P1y*Q2x*Q2y + 18*P1x*P0y*Q2x*Q2y + 64*P0x*P1x*Q2y*Q3y - 18*P0x*P0y*Q3x*Q3y - 18*P0x*P1y*Q2x*Q3y - 18*P0x*P1y*Q3x*Q2y - 18*P1x*P0y*Q2x*Q3y - 18*P1x*P0y*Q3x*Q2y - 18*P1x*P1y*Q2x*Q2y + 64*P0y*P1y*Q2x*Q3x + 18*P0x*P1y*Q3x*Q3y + 18*P1x*P0y*Q3x*Q3y + 18*P1x*P1y*Q2x*Q3y + 18*P1x*P1y*Q3x*Q2y - 18*P1x*P1y*Q3x*Q3y + 28*P0y*P1y*Q2y*Q3y)); out_qfactor = (14*pow(P0x,3.0)*Q2x - 14*pow(P0x,3.0)*Q3x + 14*pow(P0y,3.0)*Q2y - 14*pow(P0y,3.0)*Q3y + 21*pow(P0x,2.0)*pow(Q2x,2.0) + 21*pow(P0x,2.0)*pow(Q3x,2.0) + 21*pow(P1x,2.0)*pow(Q2x,2.0) + 21*pow(P1x,2.0)*pow(Q3x,2.0) + 48*pow(P0x,2.0)*pow(Q2y,2.0) + 48*pow(P0y,2.0)*pow(Q2x,2.0) + 48*pow(P0x,2.0)*pow(Q3y,2.0) + 48*pow(P1x,2.0)*pow(Q2y,2.0) + 48*pow(P0y,2.0)*pow(Q3x,2.0) + 48*pow(P1y,2.0)*pow(Q2x,2.0) + 48*pow(P1x,2.0)*pow(Q3y,2.0) + 48*pow(P1y,2.0)*pow(Q3x,2.0) + 21*pow(P0y,2.0)*pow(Q2y,2.0) + 21*pow(P0y,2.0)*pow(Q3y,2.0) + 21*pow(P1y,2.0)*pow(Q2y,2.0) + 21*pow(P1y,2.0)*pow(Q3y,2.0) + 21*pow(P0x,2.0)*pow(Q2x,2.0)*sp + 21*pow(P0x,2.0)*pow(Q3x,2.0)*sp - 63*pow(P0x,3.0)*Q2x*pow(sp,2.0) + 21*pow(P1x,2.0)*pow(Q2x,2.0)*sp + 70*pow(P0x,3.0)*Q2x*pow(sp,3.0) + 63*pow(P0x,3.0)*Q3x*pow(sp,2.0) + 21*pow(P1x,2.0)*pow(Q3x,2.0)*sp - 126*pow(P1x,3.0)*Q2x*pow(sp,2.0) - 21*pow(P0x,3.0)*Q2x*pow(sp,4.0) - 70*pow(P0x,3.0)*Q3x*pow(sp,3.0) + 210*pow(P1x,3.0)*Q2x*pow(sp,3.0) + 126*pow(P1x,3.0)*Q3x*pow(sp,2.0) + 21*pow(P0x,3.0)*Q3x*pow(sp,4.0) - 84*pow(P1x,3.0)*Q2x*pow(sp,4.0) - 210*pow(P1x,3.0)*Q3x*pow(sp,3.0) + 84*pow(P1x,3.0)*Q3x*pow(sp,4.0) - 24*pow(P0x,2.0)*pow(Q2y,2.0)*sp - 24*pow(P0y,2.0)*pow(Q2x,2.0)*sp + 48*pow(P0x,2.0)*pow(Q3y,2.0)*sp - 24*pow(P1x,2.0)*pow(Q2y,2.0)*sp + 48*pow(P0y,2.0)*pow(Q3x,2.0)*sp - 24*pow(P1y,2.0)*pow(Q2x,2.0)*sp + 48*pow(P1x,2.0)*pow(Q3y,2.0)*sp + 48*pow(P1y,2.0)*pow(Q3x,2.0)*sp + 21*pow(P0y,2.0)*pow(Q2y,2.0)*sp + 21*pow(P0y,2.0)*pow(Q3y,2.0)*sp - 63*pow(P0y,3.0)*Q2y*pow(sp,2.0) + 21*pow(P1y,2.0)*pow(Q2y,2.0)*sp + 70*pow(P0y,3.0)*Q2y*pow(sp,3.0) + 63*pow(P0y,3.0)*Q3y*pow(sp,2.0) + 21*pow(P1y,2.0)*pow(Q3y,2.0)*sp - 126*pow(P1y,3.0)*Q2y*pow(sp,2.0) - 21*pow(P0y,3.0)*Q2y*pow(sp,4.0) - 70*pow(P0y,3.0)*Q3y*pow(sp,3.0) + 210*pow(P1y,3.0)*Q2y*pow(sp,3.0) + 126*pow(P1y,3.0)*Q3y*pow(sp,2.0) + 21*pow(P0y,3.0)*Q3y*pow(sp,4.0) - 84*pow(P1y,3.0)*Q2y*pow(sp,4.0) - 210*pow(P1y,3.0)*Q3y*pow(sp,3.0) + 84*pow(P1y,3.0)*Q3y*pow(sp,4.0) - 21*pow(P0x,2.0)*pow(Q2x,2.0)*pow(sp,2.0) - 105*pow(P0x,2.0)*pow(Q2x,2.0)*pow(sp,3.0) + 21*pow(P0x,2.0)*pow(Q3x,2.0)*pow(sp,2.0) - 21*pow(P1x,2.0)*pow(Q2x,2.0)*pow(sp,2.0) + 84*pow(P0x,2.0)*pow(Q2x,2.0)*pow(sp,4.0) + 7*pow(P0x,2.0)*pow(Q3x,2.0)*pow(sp,3.0) - 105*pow(P1x,2.0)*pow(Q2x,2.0)*pow(sp,3.0) + 21*pow(P1x,2.0)*pow(Q3x,2.0)*pow(sp,2.0) - 21*pow(P0x,2.0)*pow(Q3x,2.0)*pow(sp,4.0) + 84*pow(P1x,2.0)*pow(Q2x,2.0)*pow(sp,4.0) + 7*pow(P1x,2.0)*pow(Q3x,2.0)*pow(sp,3.0) - 21*pow(P1x,2.0)*pow(Q3x,2.0)*pow(sp,4.0) - 48*pow(P0x,2.0)*pow(Q2y,2.0)*pow(sp,2.0) - 48*pow(P0y,2.0)*pow(Q2x,2.0)*pow(sp,2.0) - 24*pow(P0x,2.0)*pow(Q2y,2.0)*pow(sp,3.0) + 12*pow(P0x,2.0)*pow(Q3y,2.0)*pow(sp,2.0) - 48*pow(P1x,2.0)*pow(Q2y,2.0)*pow(sp,2.0) - 24*pow(P0y,2.0)*pow(Q2x,2.0)*pow(sp,3.0) + 12*pow(P0y,2.0)*pow(Q3x,2.0)*pow(sp,2.0) - 48*pow(P1y,2.0)*pow(Q2x,2.0)*pow(sp,2.0) + 48*pow(P0x,2.0)*pow(Q2y,2.0)*pow(sp,4.0) - 8*pow(P0x,2.0)*pow(Q3y,2.0)*pow(sp,3.0) - 24*pow(P1x,2.0)*pow(Q2y,2.0)*pow(sp,3.0) + 12*pow(P1x,2.0)*pow(Q3y,2.0)*pow(sp,2.0) + 48*pow(P0y,2.0)*pow(Q2x,2.0)*pow(sp,4.0) - 8*pow(P0y,2.0)*pow(Q3x,2.0)*pow(sp,3.0) - 24*pow(P1y,2.0)*pow(Q2x,2.0)*pow(sp,3.0) + 12*pow(P1y,2.0)*pow(Q3x,2.0)*pow(sp,2.0) - 12*pow(P0x,2.0)*pow(Q3y,2.0)*pow(sp,4.0) + 48*pow(P1x,2.0)*pow(Q2y,2.0)*pow(sp,4.0) - 8*pow(P1x,2.0)*pow(Q3y,2.0)*pow(sp,3.0) - 12*pow(P0y,2.0)*pow(Q3x,2.0)*pow(sp,4.0) + 48*pow(P1y,2.0)*pow(Q2x,2.0)*pow(sp,4.0) - 8*pow(P1y,2.0)*pow(Q3x,2.0)*pow(sp,3.0) - 12*pow(P1x,2.0)*pow(Q3y,2.0)*pow(sp,4.0) - 12*pow(P1y,2.0)*pow(Q3x,2.0)*pow(sp,4.0) - 21*pow(P0y,2.0)*pow(Q2y,2.0)*pow(sp,2.0) - 105*pow(P0y,2.0)*pow(Q2y,2.0)*pow(sp,3.0) + 21*pow(P0y,2.0)*pow(Q3y,2.0)*pow(sp,2.0) - 21*pow(P1y,2.0)*pow(Q2y,2.0)*pow(sp,2.0) + 84*pow(P0y,2.0)*pow(Q2y,2.0)*pow(sp,4.0) + 7*pow(P0y,2.0)*pow(Q3y,2.0)*pow(sp,3.0) - 105*pow(P1y,2.0)*pow(Q2y,2.0)*pow(sp,3.0) + 21*pow(P1y,2.0)*pow(Q3y,2.0)*pow(sp,2.0) - 21*pow(P0y,2.0)*pow(Q3y,2.0)*pow(sp,4.0) + 84*pow(P1y,2.0)*pow(Q2y,2.0)*pow(sp,4.0) + 7*pow(P1y,2.0)*pow(Q3y,2.0)*pow(sp,3.0) - 21*pow(P1y,2.0)*pow(Q3y,2.0)*pow(sp,4.0) - 42*P0x*P1x*pow(Q2x,2.0) + 14*P0x*pow(P1x,2.0)*Q2x - 28*pow(P0x,2.0)*P1x*Q2x - 42*P0x*P1x*pow(Q3x,2.0) - 14*P0x*pow(P1x,2.0)*Q3x + 28*pow(P0x,2.0)*P1x*Q3x - 14*pow(P0x,2.0)*P3x*Q2x + 14*pow(P0x,2.0)*P3x*Q3x - 14*pow(P1x,2.0)*P3x*Q2x + 14*pow(P1x,2.0)*P3x*Q3x + 14*P0x*pow(P0y,2.0)*Q2x - 96*P0x*P1x*pow(Q2y,2.0) - 14*P0x*pow(P0y,2.0)*Q3x - 52*P0x*pow(P1y,2.0)*Q2x - 66*P1x*pow(P0y,2.0)*Q2x - 96*P0x*P1x*pow(Q3y,2.0) + 52*P0x*pow(P1y,2.0)*Q3x + 66*P1x*pow(P0y,2.0)*Q3x + 16*P3x*pow(P0y,2.0)*Q2x - 16*P3x*pow(P0y,2.0)*Q3x + 16*P3x*pow(P1y,2.0)*Q2x - 16*P3x*pow(P1y,2.0)*Q3x + 14*pow(P0x,2.0)*P0y*Q2y - 96*P0y*P1y*pow(Q2x,2.0) - 14*pow(P0x,2.0)*P0y*Q3y - 66*pow(P0x,2.0)*P1y*Q2y - 52*pow(P1x,2.0)*P0y*Q2y - 96*P0y*P1y*pow(Q3x,2.0) + 66*pow(P0x,2.0)*P1y*Q3y + 52*pow(P1x,2.0)*P0y*Q3y + 16*pow(P0x,2.0)*P3y*Q2y - 16*pow(P0x,2.0)*P3y*Q3y + 16*pow(P1x,2.0)*P3y*Q2y - 16*pow(P1x,2.0)*P3y*Q3y - 42*P0y*P1y*pow(Q2y,2.0) + 14*P0y*pow(P1y,2.0)*Q2y - 28*pow(P0y,2.0)*P1y*Q2y - 42*P0y*P1y*pow(Q3y,2.0) - 14*P0y*pow(P1y,2.0)*Q3y + 28*pow(P0y,2.0)*P1y*Q3y - 14*pow(P0y,2.0)*P3y*Q2y + 14*pow(P0y,2.0)*P3y*Q3y - 14*pow(P1y,2.0)*P3y*Q2y + 14*pow(P1y,2.0)*P3y*Q3y - 42*pow(P0x,2.0)*Q2x*Q3x - 42*pow(P1x,2.0)*Q2x*Q3x + 36*pow(P0y,2.0)*Q1x*Q2x - 36*pow(P0y,2.0)*Q1x*Q3x + 36*pow(P1y,2.0)*Q1x*Q2x - 96*pow(P0y,2.0)*Q2x*Q3x - 36*pow(P1y,2.0)*Q1x*Q3x - 96*pow(P1y,2.0)*Q2x*Q3x + 36*pow(P0x,2.0)*Q1y*Q2y - 36*pow(P0x,2.0)*Q1y*Q3y + 36*pow(P1x,2.0)*Q1y*Q2y - 96*pow(P0x,2.0)*Q2y*Q3y - 36*pow(P1x,2.0)*Q1y*Q3y - 96*pow(P1x,2.0)*Q2y*Q3y - 42*pow(P0y,2.0)*Q2y*Q3y - 42*pow(P1y,2.0)*Q2y*Q3y - 42*P0x*P1x*pow(Q2x,2.0)*sp - 42*P0x*P1x*pow(Q3x,2.0)*sp - 42*pow(P0x,2.0)*P3x*Q2x*sp + 42*pow(P0x,2.0)*P3x*Q3x*sp - 42*pow(P1x,2.0)*P3x*Q2x*sp + 42*pow(P1x,2.0)*P3x*Q3x*sp + 48*P0x*P1x*pow(Q2y,2.0)*sp - 96*P0x*P1x*pow(Q3y,2.0)*sp + 48*P3x*pow(P0y,2.0)*Q2x*sp - 48*P3x*pow(P0y,2.0)*Q3x*sp + 48*P3x*pow(P1y,2.0)*Q2x*sp - 48*P3x*pow(P1y,2.0)*Q3x*sp + 48*P0y*P1y*pow(Q2x,2.0)*sp - 96*P0y*P1y*pow(Q3x,2.0)*sp + 48*pow(P0x,2.0)*P3y*Q2y*sp - 48*pow(P0x,2.0)*P3y*Q3y*sp + 48*pow(P1x,2.0)*P3y*Q2y*sp - 48*pow(P1x,2.0)*P3y*Q3y*sp - 42*P0y*P1y*pow(Q2y,2.0)*sp - 42*P0y*P1y*pow(Q3y,2.0)*sp - 42*pow(P0y,2.0)*P3y*Q2y*sp + 42*pow(P0y,2.0)*P3y*Q3y*sp - 42*pow(P1y,2.0)*P3y*Q2y*sp + 42*pow(P1y,2.0)*P3y*Q3y*sp + 42*pow(P0x,2.0)*Q1x*Q2x*sp - 42*pow(P0x,2.0)*Q1x*Q3x*sp + 42*pow(P1x,2.0)*Q1x*Q2x*sp - 42*pow(P0x,2.0)*Q2x*Q3x*sp - 42*pow(P1x,2.0)*Q1x*Q3x*sp - 42*pow(P1x,2.0)*Q2x*Q3x*sp + 24*pow(P0y,2.0)*Q1x*Q2x*sp - 24*pow(P0y,2.0)*Q1x*Q3x*sp + 24*pow(P1y,2.0)*Q1x*Q2x*sp - 24*pow(P0y,2.0)*Q2x*Q3x*sp - 24*pow(P1y,2.0)*Q1x*Q3x*sp - 24*pow(P1y,2.0)*Q2x*Q3x*sp + 24*pow(P0x,2.0)*Q1y*Q2y*sp - 24*pow(P0x,2.0)*Q1y*Q3y*sp + 24*pow(P1x,2.0)*Q1y*Q2y*sp - 24*pow(P0x,2.0)*Q2y*Q3y*sp - 24*pow(P1x,2.0)*Q1y*Q3y*sp - 24*pow(P1x,2.0)*Q2y*Q3y*sp + 42*pow(P0y,2.0)*Q1y*Q2y*sp - 42*pow(P0y,2.0)*Q1y*Q3y*sp + 42*pow(P1y,2.0)*Q1y*Q2y*sp - 42*pow(P0y,2.0)*Q2y*Q3y*sp - 42*pow(P1y,2.0)*Q1y*Q3y*sp - 42*pow(P1y,2.0)*Q2y*Q3y*sp + 42*P0x*P1x*pow(Q2x,2.0)*pow(sp,2.0) + 189*P0x*pow(P1x,2.0)*Q2x*pow(sp,2.0) + 210*P0x*P1x*pow(Q2x,2.0)*pow(sp,3.0) - 42*P0x*P1x*pow(Q3x,2.0)*pow(sp,2.0) - 350*P0x*pow(P1x,2.0)*Q2x*pow(sp,3.0) - 189*P0x*pow(P1x,2.0)*Q3x*pow(sp,2.0) + 70*pow(P0x,2.0)*P1x*Q2x*pow(sp,3.0) - 189*pow(P0x,2.0)*P2x*Q2x*pow(sp,2.0) - 168*P0x*P1x*pow(Q2x,2.0)*pow(sp,4.0) - 14*P0x*P1x*pow(Q3x,2.0)*pow(sp,3.0) + 147*P0x*pow(P1x,2.0)*Q2x*pow(sp,4.0) + 350*P0x*pow(P1x,2.0)*Q3x*pow(sp,3.0) - 42*pow(P0x,2.0)*P1x*Q2x*pow(sp,4.0) - 70*pow(P0x,2.0)*P1x*Q3x*pow(sp,3.0) + 420*pow(P0x,2.0)*P2x*Q2x*pow(sp,3.0) + 189*pow(P0x,2.0)*P2x*Q3x*pow(sp,2.0) + 294*pow(P0x,2.0)*P3x*Q2x*pow(sp,2.0) - 189*pow(P1x,2.0)*P2x*Q2x*pow(sp,2.0) + 42*P0x*P1x*pow(Q3x,2.0)*pow(sp,4.0) - 147*P0x*pow(P1x,2.0)*Q3x*pow(sp,4.0) + 42*pow(P0x,2.0)*P1x*Q3x*pow(sp,4.0) - 210*pow(P0x,2.0)*P2x*Q2x*pow(sp,4.0) - 420*pow(P0x,2.0)*P2x*Q3x*pow(sp,3.0) - 210*pow(P0x,2.0)*P3x*Q2x*pow(sp,3.0) - 294*pow(P0x,2.0)*P3x*Q3x*pow(sp,2.0) + 420*pow(P1x,2.0)*P2x*Q2x*pow(sp,3.0) + 189*pow(P1x,2.0)*P2x*Q3x*pow(sp,2.0) + 294*pow(P1x,2.0)*P3x*Q2x*pow(sp,2.0) + 210*pow(P0x,2.0)*P2x*Q3x*pow(sp,4.0) + 210*pow(P0x,2.0)*P3x*Q3x*pow(sp,3.0) - 210*pow(P1x,2.0)*P2x*Q2x*pow(sp,4.0) - 420*pow(P1x,2.0)*P2x*Q3x*pow(sp,3.0) - 210*pow(P1x,2.0)*P3x*Q2x*pow(sp,3.0) - 294*pow(P1x,2.0)*P3x*Q3x*pow(sp,2.0) + 210*pow(P1x,2.0)*P2x*Q3x*pow(sp,4.0) + 210*pow(P1x,2.0)*P3x*Q3x*pow(sp,3.0) - 63*P0x*pow(P0y,2.0)*Q2x*pow(sp,2.0) + 96*P0x*P1x*pow(Q2y,2.0)*pow(sp,2.0) + 70*P0x*pow(P0y,2.0)*Q2x*pow(sp,3.0) + 63*P0x*pow(P0y,2.0)*Q3x*pow(sp,2.0) + 126*P0x*pow(P1y,2.0)*Q2x*pow(sp,2.0) + 63*P1x*pow(P0y,2.0)*Q2x*pow(sp,2.0) + 48*P0x*P1x*pow(Q2y,2.0)*pow(sp,3.0) - 24*P0x*P1x*pow(Q3y,2.0)*pow(sp,2.0) - 21*P0x*pow(P0y,2.0)*Q2x*pow(sp,4.0) - 70*P0x*pow(P0y,2.0)*Q3x*pow(sp,3.0) - 98*P0x*pow(P1y,2.0)*Q2x*pow(sp,3.0) - 126*P0x*pow(P1y,2.0)*Q3x*pow(sp,2.0) + 42*P1x*pow(P0y,2.0)*Q2x*pow(sp,3.0) - 63*P1x*pow(P0y,2.0)*Q3x*pow(sp,2.0) - 126*P1x*pow(P1y,2.0)*Q2x*pow(sp,2.0) - 96*P0x*P1x*pow(Q2y,2.0)*pow(sp,4.0) + 16*P0x*P1x*pow(Q3y,2.0)*pow(sp,3.0) + 21*P0x*pow(P0y,2.0)*Q3x*pow(sp,4.0) + 24*P0x*pow(P1y,2.0)*Q2x*pow(sp,4.0) + 98*P0x*pow(P1y,2.0)*Q3x*pow(sp,3.0) - 39*P1x*pow(P0y,2.0)*Q2x*pow(sp,4.0) - 42*P1x*pow(P0y,2.0)*Q3x*pow(sp,3.0) + 210*P1x*pow(P1y,2.0)*Q2x*pow(sp,3.0) + 126*P1x*pow(P1y,2.0)*Q3x*pow(sp,2.0) + 168*P2x*pow(P0y,2.0)*Q2x*pow(sp,3.0) + 96*P3x*pow(P0y,2.0)*Q2x*pow(sp,2.0) + 24*P0x*P1x*pow(Q3y,2.0)*pow(sp,4.0) - 24*P0x*pow(P1y,2.0)*Q3x*pow(sp,4.0) + 39*P1x*pow(P0y,2.0)*Q3x*pow(sp,4.0) - 84*P1x*pow(P1y,2.0)*Q2x*pow(sp,4.0) - 210*P1x*pow(P1y,2.0)*Q3x*pow(sp,3.0) - 120*P2x*pow(P0y,2.0)*Q2x*pow(sp,4.0) - 168*P2x*pow(P0y,2.0)*Q3x*pow(sp,3.0) + 168*P2x*pow(P1y,2.0)*Q2x*pow(sp,3.0) - 120*P3x*pow(P0y,2.0)*Q2x*pow(sp,3.0) - 96*P3x*pow(P0y,2.0)*Q3x*pow(sp,2.0) + 96*P3x*pow(P1y,2.0)*Q2x*pow(sp,2.0) + 84*P1x*pow(P1y,2.0)*Q3x*pow(sp,4.0) + 120*P2x*pow(P0y,2.0)*Q3x*pow(sp,4.0) - 120*P2x*pow(P1y,2.0)*Q2x*pow(sp,4.0) - 168*P2x*pow(P1y,2.0)*Q3x*pow(sp,3.0) + 120*P3x*pow(P0y,2.0)*Q3x*pow(sp,3.0) - 120*P3x*pow(P1y,2.0)*Q2x*pow(sp,3.0) - 96*P3x*pow(P1y,2.0)*Q3x*pow(sp,2.0) + 120*P2x*pow(P1y,2.0)*Q3x*pow(sp,4.0) + 120*P3x*pow(P1y,2.0)*Q3x*pow(sp,3.0) - 63*pow(P0x,2.0)*P0y*Q2y*pow(sp,2.0) + 96*P0y*P1y*pow(Q2x,2.0)*pow(sp,2.0) + 70*pow(P0x,2.0)*P0y*Q2y*pow(sp,3.0) + 63*pow(P0x,2.0)*P0y*Q3y*pow(sp,2.0) + 63*pow(P0x,2.0)*P1y*Q2y*pow(sp,2.0) + 126*pow(P1x,2.0)*P0y*Q2y*pow(sp,2.0) + 48*P0y*P1y*pow(Q2x,2.0)*pow(sp,3.0) - 24*P0y*P1y*pow(Q3x,2.0)*pow(sp,2.0) - 21*pow(P0x,2.0)*P0y*Q2y*pow(sp,4.0) - 70*pow(P0x,2.0)*P0y*Q3y*pow(sp,3.0) + 42*pow(P0x,2.0)*P1y*Q2y*pow(sp,3.0) - 63*pow(P0x,2.0)*P1y*Q3y*pow(sp,2.0) - 98*pow(P1x,2.0)*P0y*Q2y*pow(sp,3.0) - 126*pow(P1x,2.0)*P0y*Q3y*pow(sp,2.0) - 126*pow(P1x,2.0)*P1y*Q2y*pow(sp,2.0) - 96*P0y*P1y*pow(Q2x,2.0)*pow(sp,4.0) + 16*P0y*P1y*pow(Q3x,2.0)*pow(sp,3.0) + 21*pow(P0x,2.0)*P0y*Q3y*pow(sp,4.0) - 39*pow(P0x,2.0)*P1y*Q2y*pow(sp,4.0) - 42*pow(P0x,2.0)*P1y*Q3y*pow(sp,3.0) + 168*pow(P0x,2.0)*P2y*Q2y*pow(sp,3.0) + 96*pow(P0x,2.0)*P3y*Q2y*pow(sp,2.0) + 24*pow(P1x,2.0)*P0y*Q2y*pow(sp,4.0) + 98*pow(P1x,2.0)*P0y*Q3y*pow(sp,3.0) + 210*pow(P1x,2.0)*P1y*Q2y*pow(sp,3.0) + 126*pow(P1x,2.0)*P1y*Q3y*pow(sp,2.0) + 24*P0y*P1y*pow(Q3x,2.0)*pow(sp,4.0) + 39*pow(P0x,2.0)*P1y*Q3y*pow(sp,4.0) - 120*pow(P0x,2.0)*P2y*Q2y*pow(sp,4.0) - 168*pow(P0x,2.0)*P2y*Q3y*pow(sp,3.0) - 120*pow(P0x,2.0)*P3y*Q2y*pow(sp,3.0) - 96*pow(P0x,2.0)*P3y*Q3y*pow(sp,2.0) - 24*pow(P1x,2.0)*P0y*Q3y*pow(sp,4.0) - 84*pow(P1x,2.0)*P1y*Q2y*pow(sp,4.0) - 210*pow(P1x,2.0)*P1y*Q3y*pow(sp,3.0) + 168*pow(P1x,2.0)*P2y*Q2y*pow(sp,3.0) + 96*pow(P1x,2.0)*P3y*Q2y*pow(sp,2.0) + 120*pow(P0x,2.0)*P2y*Q3y*pow(sp,4.0) + 120*pow(P0x,2.0)*P3y*Q3y*pow(sp,3.0) + 84*pow(P1x,2.0)*P1y*Q3y*pow(sp,4.0) - 120*pow(P1x,2.0)*P2y*Q2y*pow(sp,4.0) - 168*pow(P1x,2.0)*P2y*Q3y*pow(sp,3.0) - 120*pow(P1x,2.0)*P3y*Q2y*pow(sp,3.0) - 96*pow(P1x,2.0)*P3y*Q3y*pow(sp,2.0) + 120*pow(P1x,2.0)*P2y*Q3y*pow(sp,4.0) + 120*pow(P1x,2.0)*P3y*Q3y*pow(sp,3.0) + 42*P0y*P1y*pow(Q2y,2.0)*pow(sp,2.0) + 189*P0y*pow(P1y,2.0)*Q2y*pow(sp,2.0) + 210*P0y*P1y*pow(Q2y,2.0)*pow(sp,3.0) - 42*P0y*P1y*pow(Q3y,2.0)*pow(sp,2.0) - 350*P0y*pow(P1y,2.0)*Q2y*pow(sp,3.0) - 189*P0y*pow(P1y,2.0)*Q3y*pow(sp,2.0) + 70*pow(P0y,2.0)*P1y*Q2y*pow(sp,3.0) - 189*pow(P0y,2.0)*P2y*Q2y*pow(sp,2.0) - 168*P0y*P1y*pow(Q2y,2.0)*pow(sp,4.0) - 14*P0y*P1y*pow(Q3y,2.0)*pow(sp,3.0) + 147*P0y*pow(P1y,2.0)*Q2y*pow(sp,4.0) + 350*P0y*pow(P1y,2.0)*Q3y*pow(sp,3.0) - 42*pow(P0y,2.0)*P1y*Q2y*pow(sp,4.0) - 70*pow(P0y,2.0)*P1y*Q3y*pow(sp,3.0) + 420*pow(P0y,2.0)*P2y*Q2y*pow(sp,3.0) + 189*pow(P0y,2.0)*P2y*Q3y*pow(sp,2.0) + 294*pow(P0y,2.0)*P3y*Q2y*pow(sp,2.0) - 189*pow(P1y,2.0)*P2y*Q2y*pow(sp,2.0) + 42*P0y*P1y*pow(Q3y,2.0)*pow(sp,4.0) - 147*P0y*pow(P1y,2.0)*Q3y*pow(sp,4.0) + 42*pow(P0y,2.0)*P1y*Q3y*pow(sp,4.0) - 210*pow(P0y,2.0)*P2y*Q2y*pow(sp,4.0) - 420*pow(P0y,2.0)*P2y*Q3y*pow(sp,3.0) - 210*pow(P0y,2.0)*P3y*Q2y*pow(sp,3.0) - 294*pow(P0y,2.0)*P3y*Q3y*pow(sp,2.0) + 420*pow(P1y,2.0)*P2y*Q2y*pow(sp,3.0) + 189*pow(P1y,2.0)*P2y*Q3y*pow(sp,2.0) + 294*pow(P1y,2.0)*P3y*Q2y*pow(sp,2.0) + 210*pow(P0y,2.0)*P2y*Q3y*pow(sp,4.0) + 210*pow(P0y,2.0)*P3y*Q3y*pow(sp,3.0) - 210*pow(P1y,2.0)*P2y*Q2y*pow(sp,4.0) - 420*pow(P1y,2.0)*P2y*Q3y*pow(sp,3.0) - 210*pow(P1y,2.0)*P3y*Q2y*pow(sp,3.0) - 294*pow(P1y,2.0)*P3y*Q3y*pow(sp,2.0) + 210*pow(P1y,2.0)*P2y*Q3y*pow(sp,4.0) + 210*pow(P1y,2.0)*P3y*Q3y*pow(sp,3.0) + 126*pow(P0x,2.0)*Q1x*Q2x*pow(sp,2.0) - 378*pow(P0x,2.0)*Q1x*Q2x*pow(sp,3.0) - 126*pow(P0x,2.0)*Q1x*Q3x*pow(sp,2.0) + 126*pow(P1x,2.0)*Q1x*Q2x*pow(sp,2.0) + 210*pow(P0x,2.0)*Q1x*Q2x*pow(sp,4.0) + 378*pow(P0x,2.0)*Q1x*Q3x*pow(sp,3.0) - 378*pow(P1x,2.0)*Q1x*Q2x*pow(sp,3.0) - 126*pow(P1x,2.0)*Q1x*Q3x*pow(sp,2.0) - 210*pow(P0x,2.0)*Q1x*Q3x*pow(sp,4.0) + 98*pow(P0x,2.0)*Q2x*Q3x*pow(sp,3.0) + 210*pow(P1x,2.0)*Q1x*Q2x*pow(sp,4.0) + 378*pow(P1x,2.0)*Q1x*Q3x*pow(sp,3.0) - 63*pow(P0x,2.0)*Q2x*Q3x*pow(sp,4.0) - 210*pow(P1x,2.0)*Q1x*Q3x*pow(sp,4.0) + 98*pow(P1x,2.0)*Q2x*Q3x*pow(sp,3.0) - 63*pow(P1x,2.0)*Q2x*Q3x*pow(sp,4.0) - 36*pow(P0y,2.0)*Q1x*Q2x*pow(sp,2.0) - 144*pow(P0y,2.0)*Q1x*Q2x*pow(sp,3.0) + 36*pow(P0y,2.0)*Q1x*Q3x*pow(sp,2.0) - 36*pow(P1y,2.0)*Q1x*Q2x*pow(sp,2.0) + 120*pow(P0y,2.0)*Q1x*Q2x*pow(sp,4.0) + 144*pow(P0y,2.0)*Q1x*Q3x*pow(sp,3.0) + 36*pow(P0y,2.0)*Q2x*Q3x*pow(sp,2.0) - 144*pow(P1y,2.0)*Q1x*Q2x*pow(sp,3.0) + 36*pow(P1y,2.0)*Q1x*Q3x*pow(sp,2.0) - 120*pow(P0y,2.0)*Q1x*Q3x*pow(sp,4.0) + 32*pow(P0y,2.0)*Q2x*Q3x*pow(sp,3.0) + 120*pow(P1y,2.0)*Q1x*Q2x*pow(sp,4.0) + 144*pow(P1y,2.0)*Q1x*Q3x*pow(sp,3.0) + 36*pow(P1y,2.0)*Q2x*Q3x*pow(sp,2.0) - 36*pow(P0y,2.0)*Q2x*Q3x*pow(sp,4.0) - 120*pow(P1y,2.0)*Q1x*Q3x*pow(sp,4.0) + 32*pow(P1y,2.0)*Q2x*Q3x*pow(sp,3.0) - 36*pow(P1y,2.0)*Q2x*Q3x*pow(sp,4.0) - 36*pow(P0x,2.0)*Q1y*Q2y*pow(sp,2.0) - 144*pow(P0x,2.0)*Q1y*Q2y*pow(sp,3.0) + 36*pow(P0x,2.0)*Q1y*Q3y*pow(sp,2.0) - 36*pow(P1x,2.0)*Q1y*Q2y*pow(sp,2.0) + 120*pow(P0x,2.0)*Q1y*Q2y*pow(sp,4.0) + 144*pow(P0x,2.0)*Q1y*Q3y*pow(sp,3.0) + 36*pow(P0x,2.0)*Q2y*Q3y*pow(sp,2.0) - 144*pow(P1x,2.0)*Q1y*Q2y*pow(sp,3.0) + 36*pow(P1x,2.0)*Q1y*Q3y*pow(sp,2.0) - 120*pow(P0x,2.0)*Q1y*Q3y*pow(sp,4.0) + 32*pow(P0x,2.0)*Q2y*Q3y*pow(sp,3.0) + 120*pow(P1x,2.0)*Q1y*Q2y*pow(sp,4.0) + 144*pow(P1x,2.0)*Q1y*Q3y*pow(sp,3.0) + 36*pow(P1x,2.0)*Q2y*Q3y*pow(sp,2.0) - 36*pow(P0x,2.0)*Q2y*Q3y*pow(sp,4.0) - 120*pow(P1x,2.0)*Q1y*Q3y*pow(sp,4.0) + 32*pow(P1x,2.0)*Q2y*Q3y*pow(sp,3.0) - 36*pow(P1x,2.0)*Q2y*Q3y*pow(sp,4.0) + 126*pow(P0y,2.0)*Q1y*Q2y*pow(sp,2.0) - 378*pow(P0y,2.0)*Q1y*Q2y*pow(sp,3.0) - 126*pow(P0y,2.0)*Q1y*Q3y*pow(sp,2.0) + 126*pow(P1y,2.0)*Q1y*Q2y*pow(sp,2.0) + 210*pow(P0y,2.0)*Q1y*Q2y*pow(sp,4.0) + 378*pow(P0y,2.0)*Q1y*Q3y*pow(sp,3.0) - 378*pow(P1y,2.0)*Q1y*Q2y*pow(sp,3.0) - 126*pow(P1y,2.0)*Q1y*Q3y*pow(sp,2.0) - 210*pow(P0y,2.0)*Q1y*Q3y*pow(sp,4.0) + 98*pow(P0y,2.0)*Q2y*Q3y*pow(sp,3.0) + 210*pow(P1y,2.0)*Q1y*Q2y*pow(sp,4.0) + 378*pow(P1y,2.0)*Q1y*Q3y*pow(sp,3.0) - 63*pow(P0y,2.0)*Q2y*Q3y*pow(sp,4.0) - 210*pow(P1y,2.0)*Q1y*Q3y*pow(sp,4.0) + 98*pow(P1y,2.0)*Q2y*Q3y*pow(sp,3.0) - 63*pow(P1y,2.0)*Q2y*Q3y*pow(sp,4.0) + 28*P0x*P1x*P3x*Q2x - 28*P0x*P1x*P3x*Q3x + 38*P0x*P1x*P0y*Q2y + 38*P0x*P0y*P1y*Q2x - 38*P0x*P1x*P0y*Q3y + 66*P0x*P1x*P1y*Q2y - 38*P0x*P0y*P1y*Q3x + 66*P1x*P0y*P1y*Q2x - 66*P0x*P1x*P1y*Q3y - 30*P0x*P3x*P0y*Q2y - 30*P0x*P0y*P3y*Q2x - 66*P1x*P0y*P1y*Q3x - 32*P0x*P1x*P3y*Q2y + 30*P0x*P3x*P0y*Q3y + 30*P0x*P3x*P1y*Q2y + 30*P0x*P0y*P3y*Q3x + 30*P0x*P1y*P3y*Q2x + 30*P1x*P3x*P0y*Q2y + 30*P1x*P0y*P3y*Q2x - 32*P3x*P0y*P1y*Q2x + 32*P0x*P1x*P3y*Q3y - 30*P0x*P3x*P1y*Q3y - 30*P0x*P1y*P3y*Q3x - 30*P1x*P3x*P0y*Q3y - 30*P1x*P3x*P1y*Q2y - 30*P1x*P0y*P3y*Q3x - 30*P1x*P1y*P3y*Q2x + 32*P3x*P0y*P1y*Q3x + 30*P1x*P3x*P1y*Q3y + 30*P1x*P1y*P3y*Q3x + 28*P0y*P1y*P3y*Q2y - 28*P0y*P1y*P3y*Q3y + 84*P0x*P1x*Q2x*Q3x - 36*P0x*P0y*Q1x*Q2y - 36*P0x*P0y*Q2x*Q1y - 72*P0x*P1x*Q1y*Q2y + 36*P0x*P0y*Q1x*Q3y - 54*P0x*P0y*Q2x*Q2y + 36*P0x*P0y*Q3x*Q1y + 36*P0x*P1y*Q1x*Q2y + 36*P0x*P1y*Q2x*Q1y + 36*P1x*P0y*Q1x*Q2y + 36*P1x*P0y*Q2x*Q1y - 72*P0y*P1y*Q1x*Q2x + 72*P0x*P1x*Q1y*Q3y + 54*P0x*P0y*Q2x*Q3y + 54*P0x*P0y*Q3x*Q2y - 36*P0x*P1y*Q1x*Q3y + 54*P0x*P1y*Q2x*Q2y - 36*P0x*P1y*Q3x*Q1y - 36*P1x*P0y*Q1x*Q3y + 54*P1x*P0y*Q2x*Q2y - 36*P1x*P0y*Q3x*Q1y - 36*P1x*P1y*Q1x*Q2y - 36*P1x*P1y*Q2x*Q1y + 72*P0y*P1y*Q1x*Q3x + 192*P0x*P1x*Q2y*Q3y - 54*P0x*P0y*Q3x*Q3y - 54*P0x*P1y*Q2x*Q3y - 54*P0x*P1y*Q3x*Q2y - 54*P1x*P0y*Q2x*Q3y - 54*P1x*P0y*Q3x*Q2y + 36*P1x*P1y*Q1x*Q3y - 54*P1x*P1y*Q2x*Q2y + 36*P1x*P1y*Q3x*Q1y + 192*P0y*P1y*Q2x*Q3x + 54*P0x*P1y*Q3x*Q3y + 54*P1x*P0y*Q3x*Q3y + 54*P1x*P1y*Q2x*Q3y + 54*P1x*P1y*Q3x*Q2y - 54*P1x*P1y*Q3x*Q3y + 84*P0y*P1y*Q2y*Q3y + 84*P0x*P1x*P3x*Q2x*sp - 84*P0x*P1x*P3x*Q3x*sp - 90*P0x*P3x*P0y*Q2y*sp - 90*P0x*P0y*P3y*Q2x*sp - 96*P0x*P1x*P3y*Q2y*sp + 90*P0x*P3x*P0y*Q3y*sp + 90*P0x*P3x*P1y*Q2y*sp + 90*P0x*P0y*P3y*Q3x*sp + 90*P0x*P1y*P3y*Q2x*sp + 90*P1x*P3x*P0y*Q2y*sp + 90*P1x*P0y*P3y*Q2x*sp - 96*P3x*P0y*P1y*Q2x*sp + 96*P0x*P1x*P3y*Q3y*sp - 90*P0x*P3x*P1y*Q3y*sp - 90*P0x*P1y*P3y*Q3x*sp - 90*P1x*P3x*P0y*Q3y*sp - 90*P1x*P3x*P1y*Q2y*sp - 90*P1x*P0y*P3y*Q3x*sp - 90*P1x*P1y*P3y*Q2x*sp + 96*P3x*P0y*P1y*Q3x*sp + 90*P1x*P3x*P1y*Q3y*sp + 90*P1x*P1y*P3y*Q3x*sp + 84*P0y*P1y*P3y*Q2y*sp - 84*P0y*P1y*P3y*Q3y*sp - 84*P0x*P1x*Q1x*Q2x*sp + 84*P0x*P1x*Q1x*Q3x*sp + 84*P0x*P1x*Q2x*Q3x*sp + 18*P0x*P0y*Q1x*Q2y*sp + 18*P0x*P0y*Q2x*Q1y*sp - 48*P0x*P1x*Q1y*Q2y*sp - 18*P0x*P0y*Q1x*Q3y*sp + 90*P0x*P0y*Q2x*Q2y*sp - 18*P0x*P0y*Q3x*Q1y*sp - 18*P0x*P1y*Q1x*Q2y*sp - 18*P0x*P1y*Q2x*Q1y*sp - 18*P1x*P0y*Q1x*Q2y*sp - 18*P1x*P0y*Q2x*Q1y*sp - 48*P0y*P1y*Q1x*Q2x*sp + 48*P0x*P1x*Q1y*Q3y*sp - 18*P0x*P0y*Q2x*Q3y*sp - 18*P0x*P0y*Q3x*Q2y*sp + 18*P0x*P1y*Q1x*Q3y*sp - 90*P0x*P1y*Q2x*Q2y*sp + 18*P0x*P1y*Q3x*Q1y*sp + 18*P1x*P0y*Q1x*Q3y*sp - 90*P1x*P0y*Q2x*Q2y*sp + 18*P1x*P0y*Q3x*Q1y*sp + 18*P1x*P1y*Q1x*Q2y*sp + 18*P1x*P1y*Q2x*Q1y*sp + 48*P0y*P1y*Q1x*Q3x*sp + 48*P0x*P1x*Q2y*Q3y*sp - 54*P0x*P0y*Q3x*Q3y*sp + 18*P0x*P1y*Q2x*Q3y*sp + 18*P0x*P1y*Q3x*Q2y*sp + 18*P1x*P0y*Q2x*Q3y*sp + 18*P1x*P0y*Q3x*Q2y*sp - 18*P1x*P1y*Q1x*Q3y*sp + 90*P1x*P1y*Q2x*Q2y*sp - 18*P1x*P1y*Q3x*Q1y*sp + 48*P0y*P1y*Q2x*Q3x*sp + 54*P0x*P1y*Q3x*Q3y*sp + 54*P1x*P0y*Q3x*Q3y*sp - 18*P1x*P1y*Q2x*Q3y*sp - 18*P1x*P1y*Q3x*Q2y*sp - 54*P1x*P1y*Q3x*Q3y*sp - 84*P0y*P1y*Q1y*Q2y*sp + 84*P0y*P1y*Q1y*Q3y*sp + 84*P0y*P1y*Q2y*Q3y*sp + 378*P0x*P1x*P2x*Q2x*pow(sp,2.0) - 840*P0x*P1x*P2x*Q2x*pow(sp,3.0) - 378*P0x*P1x*P2x*Q3x*pow(sp,2.0) - 588*P0x*P1x*P3x*Q2x*pow(sp,2.0) + 420*P0x*P1x*P2x*Q2x*pow(sp,4.0) + 840*P0x*P1x*P2x*Q3x*pow(sp,3.0) + 420*P0x*P1x*P3x*Q2x*pow(sp,3.0) + 588*P0x*P1x*P3x*Q3x*pow(sp,2.0) - 420*P0x*P1x*P2x*Q3x*pow(sp,4.0) - 420*P0x*P1x*P3x*Q3x*pow(sp,3.0) - 63*P0x*P1x*P0y*Q2y*pow(sp,2.0) - 63*P0x*P0y*P1y*Q2x*pow(sp,2.0) + 28*P0x*P1x*P0y*Q2y*pow(sp,3.0) + 63*P0x*P1x*P0y*Q3y*pow(sp,2.0) + 63*P0x*P1x*P1y*Q2y*pow(sp,2.0) - 189*P0x*P2x*P0y*Q2y*pow(sp,2.0) + 28*P0x*P0y*P1y*Q2x*pow(sp,3.0) + 63*P0x*P0y*P1y*Q3x*pow(sp,2.0) - 189*P0x*P0y*P2y*Q2x*pow(sp,2.0) + 63*P1x*P0y*P1y*Q2x*pow(sp,2.0) - 3*P0x*P1x*P0y*Q2y*pow(sp,4.0) - 28*P0x*P1x*P0y*Q3y*pow(sp,3.0) - 252*P0x*P1x*P1y*Q2y*pow(sp,3.0) - 63*P0x*P1x*P1y*Q3y*pow(sp,2.0) + 252*P0x*P2x*P0y*Q2y*pow(sp,3.0) + 189*P0x*P2x*P0y*Q3y*pow(sp,2.0) + 189*P0x*P2x*P1y*Q2y*pow(sp,2.0) + 198*P0x*P3x*P0y*Q2y*pow(sp,2.0) - 3*P0x*P0y*P1y*Q2x*pow(sp,4.0) - 28*P0x*P0y*P1y*Q3x*pow(sp,3.0) + 252*P0x*P0y*P2y*Q2x*pow(sp,3.0) + 189*P0x*P0y*P2y*Q3x*pow(sp,2.0) + 198*P0x*P0y*P3y*Q2x*pow(sp,2.0) + 189*P0x*P1y*P2y*Q2x*pow(sp,2.0) + 189*P1x*P2x*P0y*Q2y*pow(sp,2.0) - 252*P1x*P0y*P1y*Q2x*pow(sp,3.0) - 63*P1x*P0y*P1y*Q3x*pow(sp,2.0) + 189*P1x*P0y*P2y*Q2x*pow(sp,2.0) + 3*P0x*P1x*P0y*Q3y*pow(sp,4.0) + 123*P0x*P1x*P1y*Q2y*pow(sp,4.0) + 252*P0x*P1x*P1y*Q3y*pow(sp,3.0) - 336*P0x*P1x*P2y*Q2y*pow(sp,3.0) - 192*P0x*P1x*P3y*Q2y*pow(sp,2.0) - 90*P0x*P2x*P0y*Q2y*pow(sp,4.0) - 252*P0x*P2x*P0y*Q3y*pow(sp,3.0) - 252*P0x*P2x*P1y*Q2y*pow(sp,3.0) - 189*P0x*P2x*P1y*Q3y*pow(sp,2.0) - 90*P0x*P3x*P0y*Q2y*pow(sp,3.0) - 198*P0x*P3x*P0y*Q3y*pow(sp,2.0) - 198*P0x*P3x*P1y*Q2y*pow(sp,2.0) + 3*P0x*P0y*P1y*Q3x*pow(sp,4.0) - 90*P0x*P0y*P2y*Q2x*pow(sp,4.0) - 252*P0x*P0y*P2y*Q3x*pow(sp,3.0) - 90*P0x*P0y*P3y*Q2x*pow(sp,3.0) - 198*P0x*P0y*P3y*Q3x*pow(sp,2.0) - 252*P0x*P1y*P2y*Q2x*pow(sp,3.0) - 189*P0x*P1y*P2y*Q3x*pow(sp,2.0) - 198*P0x*P1y*P3y*Q2x*pow(sp,2.0) - 252*P1x*P2x*P0y*Q2y*pow(sp,3.0) - 189*P1x*P2x*P0y*Q3y*pow(sp,2.0) - 189*P1x*P2x*P1y*Q2y*pow(sp,2.0) - 198*P1x*P3x*P0y*Q2y*pow(sp,2.0) + 123*P1x*P0y*P1y*Q2x*pow(sp,4.0) + 252*P1x*P0y*P1y*Q3x*pow(sp,3.0) - 252*P1x*P0y*P2y*Q2x*pow(sp,3.0) - 189*P1x*P0y*P2y*Q3x*pow(sp,2.0) - 198*P1x*P0y*P3y*Q2x*pow(sp,2.0) - 189*P1x*P1y*P2y*Q2x*pow(sp,2.0) - 336*P2x*P0y*P1y*Q2x*pow(sp,3.0) - 192*P3x*P0y*P1y*Q2x*pow(sp,2.0) - 123*P0x*P1x*P1y*Q3y*pow(sp,4.0) + 240*P0x*P1x*P2y*Q2y*pow(sp,4.0) + 336*P0x*P1x*P2y*Q3y*pow(sp,3.0) + 240*P0x*P1x*P3y*Q2y*pow(sp,3.0) + 192*P0x*P1x*P3y*Q3y*pow(sp,2.0) + 90*P0x*P2x*P0y*Q3y*pow(sp,4.0) + 90*P0x*P2x*P1y*Q2y*pow(sp,4.0) + 252*P0x*P2x*P1y*Q3y*pow(sp,3.0) + 90*P0x*P3x*P0y*Q3y*pow(sp,3.0) + 90*P0x*P3x*P1y*Q2y*pow(sp,3.0) + 198*P0x*P3x*P1y*Q3y*pow(sp,2.0) + 90*P0x*P0y*P2y*Q3x*pow(sp,4.0) + 90*P0x*P0y*P3y*Q3x*pow(sp,3.0) + 90*P0x*P1y*P2y*Q2x*pow(sp,4.0) + 252*P0x*P1y*P2y*Q3x*pow(sp,3.0) + 90*P0x*P1y*P3y*Q2x*pow(sp,3.0) + 198*P0x*P1y*P3y*Q3x*pow(sp,2.0) + 90*P1x*P2x*P0y*Q2y*pow(sp,4.0) + 252*P1x*P2x*P0y*Q3y*pow(sp,3.0) + 252*P1x*P2x*P1y*Q2y*pow(sp,3.0) + 189*P1x*P2x*P1y*Q3y*pow(sp,2.0) + 90*P1x*P3x*P0y*Q2y*pow(sp,3.0) + 198*P1x*P3x*P0y*Q3y*pow(sp,2.0) + 198*P1x*P3x*P1y*Q2y*pow(sp,2.0) - 123*P1x*P0y*P1y*Q3x*pow(sp,4.0) + 90*P1x*P0y*P2y*Q2x*pow(sp,4.0) + 252*P1x*P0y*P2y*Q3x*pow(sp,3.0) + 90*P1x*P0y*P3y*Q2x*pow(sp,3.0) + 198*P1x*P0y*P3y*Q3x*pow(sp,2.0) + 252*P1x*P1y*P2y*Q2x*pow(sp,3.0) + 189*P1x*P1y*P2y*Q3x*pow(sp,2.0) + 198*P1x*P1y*P3y*Q2x*pow(sp,2.0) + 240*P2x*P0y*P1y*Q2x*pow(sp,4.0) + 336*P2x*P0y*P1y*Q3x*pow(sp,3.0) + 240*P3x*P0y*P1y*Q2x*pow(sp,3.0) + 192*P3x*P0y*P1y*Q3x*pow(sp,2.0) - 240*P0x*P1x*P2y*Q3y*pow(sp,4.0) - 240*P0x*P1x*P3y*Q3y*pow(sp,3.0) - 90*P0x*P2x*P1y*Q3y*pow(sp,4.0) - 90*P0x*P3x*P1y*Q3y*pow(sp,3.0) - 90*P0x*P1y*P2y*Q3x*pow(sp,4.0) - 90*P0x*P1y*P3y*Q3x*pow(sp,3.0) - 90*P1x*P2x*P0y*Q3y*pow(sp,4.0) - 90*P1x*P2x*P1y*Q2y*pow(sp,4.0) - 252*P1x*P2x*P1y*Q3y*pow(sp,3.0) - 90*P1x*P3x*P0y*Q3y*pow(sp,3.0) - 90*P1x*P3x*P1y*Q2y*pow(sp,3.0) - 198*P1x*P3x*P1y*Q3y*pow(sp,2.0) - 90*P1x*P0y*P2y*Q3x*pow(sp,4.0) - 90*P1x*P0y*P3y*Q3x*pow(sp,3.0) - 90*P1x*P1y*P2y*Q2x*pow(sp,4.0) - 252*P1x*P1y*P2y*Q3x*pow(sp,3.0) - 90*P1x*P1y*P3y*Q2x*pow(sp,3.0) - 198*P1x*P1y*P3y*Q3x*pow(sp,2.0) - 240*P2x*P0y*P1y*Q3x*pow(sp,4.0) - 240*P3x*P0y*P1y*Q3x*pow(sp,3.0) + 90*P1x*P2x*P1y*Q3y*pow(sp,4.0) + 90*P1x*P3x*P1y*Q3y*pow(sp,3.0) + 90*P1x*P1y*P2y*Q3x*pow(sp,4.0) + 90*P1x*P1y*P3y*Q3x*pow(sp,3.0) + 378*P0y*P1y*P2y*Q2y*pow(sp,2.0) - 840*P0y*P1y*P2y*Q2y*pow(sp,3.0) - 378*P0y*P1y*P2y*Q3y*pow(sp,2.0) - 588*P0y*P1y*P3y*Q2y*pow(sp,2.0) + 420*P0y*P1y*P2y*Q2y*pow(sp,4.0) + 840*P0y*P1y*P2y*Q3y*pow(sp,3.0) + 420*P0y*P1y*P3y*Q2y*pow(sp,3.0) + 588*P0y*P1y*P3y*Q3y*pow(sp,2.0) - 420*P0y*P1y*P2y*Q3y*pow(sp,4.0) - 420*P0y*P1y*P3y*Q3y*pow(sp,3.0) - 252*P0x*P1x*Q1x*Q2x*pow(sp,2.0) + 756*P0x*P1x*Q1x*Q2x*pow(sp,3.0) + 252*P0x*P1x*Q1x*Q3x*pow(sp,2.0) - 420*P0x*P1x*Q1x*Q2x*pow(sp,4.0) - 756*P0x*P1x*Q1x*Q3x*pow(sp,3.0) + 420*P0x*P1x*Q1x*Q3x*pow(sp,4.0) - 196*P0x*P1x*Q2x*Q3x*pow(sp,3.0) + 126*P0x*P1x*Q2x*Q3x*pow(sp,4.0) + 162*P0x*P0y*Q1x*Q2y*pow(sp,2.0) + 162*P0x*P0y*Q2x*Q1y*pow(sp,2.0) + 72*P0x*P1x*Q1y*Q2y*pow(sp,2.0) - 234*P0x*P0y*Q1x*Q2y*pow(sp,3.0) - 162*P0x*P0y*Q1x*Q3y*pow(sp,2.0) - 234*P0x*P0y*Q2x*Q1y*pow(sp,3.0) + 54*P0x*P0y*Q2x*Q2y*pow(sp,2.0) - 162*P0x*P0y*Q3x*Q1y*pow(sp,2.0) - 162*P0x*P1y*Q1x*Q2y*pow(sp,2.0) - 162*P0x*P1y*Q2x*Q1y*pow(sp,2.0) - 162*P1x*P0y*Q1x*Q2y*pow(sp,2.0) - 162*P1x*P0y*Q2x*Q1y*pow(sp,2.0) + 72*P0y*P1y*Q1x*Q2x*pow(sp,2.0) + 288*P0x*P1x*Q1y*Q2y*pow(sp,3.0) - 72*P0x*P1x*Q1y*Q3y*pow(sp,2.0) + 90*P0x*P0y*Q1x*Q2y*pow(sp,4.0) + 234*P0x*P0y*Q1x*Q3y*pow(sp,3.0) + 90*P0x*P0y*Q2x*Q1y*pow(sp,4.0) - 162*P0x*P0y*Q2x*Q2y*pow(sp,3.0) - 36*P0x*P0y*Q2x*Q3y*pow(sp,2.0) + 234*P0x*P0y*Q3x*Q1y*pow(sp,3.0) - 36*P0x*P0y*Q3x*Q2y*pow(sp,2.0) + 234*P0x*P1y*Q1x*Q2y*pow(sp,3.0) + 162*P0x*P1y*Q1x*Q3y*pow(sp,2.0) + 234*P0x*P1y*Q2x*Q1y*pow(sp,3.0) - 54*P0x*P1y*Q2x*Q2y*pow(sp,2.0) + 162*P0x*P1y*Q3x*Q1y*pow(sp,2.0) + 234*P1x*P0y*Q1x*Q2y*pow(sp,3.0) + 162*P1x*P0y*Q1x*Q3y*pow(sp,2.0) + 234*P1x*P0y*Q2x*Q1y*pow(sp,3.0) - 54*P1x*P0y*Q2x*Q2y*pow(sp,2.0) + 162*P1x*P0y*Q3x*Q1y*pow(sp,2.0) + 162*P1x*P1y*Q1x*Q2y*pow(sp,2.0) + 162*P1x*P1y*Q2x*Q1y*pow(sp,2.0) + 288*P0y*P1y*Q1x*Q2x*pow(sp,3.0) - 72*P0y*P1y*Q1x*Q3x*pow(sp,2.0) - 240*P0x*P1x*Q1y*Q2y*pow(sp,4.0) - 288*P0x*P1x*Q1y*Q3y*pow(sp,3.0) - 72*P0x*P1x*Q2y*Q3y*pow(sp,2.0) - 90*P0x*P0y*Q1x*Q3y*pow(sp,4.0) + 72*P0x*P0y*Q2x*Q2y*pow(sp,4.0) + 66*P0x*P0y*Q2x*Q3y*pow(sp,3.0) - 90*P0x*P0y*Q3x*Q1y*pow(sp,4.0) + 66*P0x*P0y*Q3x*Q2y*pow(sp,3.0) + 18*P0x*P0y*Q3x*Q3y*pow(sp,2.0) - 90*P0x*P1y*Q1x*Q2y*pow(sp,4.0) - 234*P0x*P1y*Q1x*Q3y*pow(sp,3.0) - 90*P0x*P1y*Q2x*Q1y*pow(sp,4.0) + 162*P0x*P1y*Q2x*Q2y*pow(sp,3.0) + 36*P0x*P1y*Q2x*Q3y*pow(sp,2.0) - 234*P0x*P1y*Q3x*Q1y*pow(sp,3.0) + 36*P0x*P1y*Q3x*Q2y*pow(sp,2.0) - 90*P1x*P0y*Q1x*Q2y*pow(sp,4.0) - 234*P1x*P0y*Q1x*Q3y*pow(sp,3.0) - 90*P1x*P0y*Q2x*Q1y*pow(sp,4.0) + 162*P1x*P0y*Q2x*Q2y*pow(sp,3.0) + 36*P1x*P0y*Q2x*Q3y*pow(sp,2.0) - 234*P1x*P0y*Q3x*Q1y*pow(sp,3.0) + 36*P1x*P0y*Q3x*Q2y*pow(sp,2.0) - 234*P1x*P1y*Q1x*Q2y*pow(sp,3.0) - 162*P1x*P1y*Q1x*Q3y*pow(sp,2.0) - 234*P1x*P1y*Q2x*Q1y*pow(sp,3.0) + 54*P1x*P1y*Q2x*Q2y*pow(sp,2.0) - 162*P1x*P1y*Q3x*Q1y*pow(sp,2.0) - 240*P0y*P1y*Q1x*Q2x*pow(sp,4.0) - 288*P0y*P1y*Q1x*Q3x*pow(sp,3.0) - 72*P0y*P1y*Q2x*Q3x*pow(sp,2.0) + 240*P0x*P1x*Q1y*Q3y*pow(sp,4.0) - 64*P0x*P1x*Q2y*Q3y*pow(sp,3.0) - 27*P0x*P0y*Q2x*Q3y*pow(sp,4.0) - 27*P0x*P0y*Q3x*Q2y*pow(sp,4.0) + 30*P0x*P0y*Q3x*Q3y*pow(sp,3.0) + 90*P0x*P1y*Q1x*Q3y*pow(sp,4.0) - 72*P0x*P1y*Q2x*Q2y*pow(sp,4.0) - 66*P0x*P1y*Q2x*Q3y*pow(sp,3.0) + 90*P0x*P1y*Q3x*Q1y*pow(sp,4.0) - 66*P0x*P1y*Q3x*Q2y*pow(sp,3.0) - 18*P0x*P1y*Q3x*Q3y*pow(sp,2.0) + 90*P1x*P0y*Q1x*Q3y*pow(sp,4.0) - 72*P1x*P0y*Q2x*Q2y*pow(sp,4.0) - 66*P1x*P0y*Q2x*Q3y*pow(sp,3.0) + 90*P1x*P0y*Q3x*Q1y*pow(sp,4.0) - 66*P1x*P0y*Q3x*Q2y*pow(sp,3.0) - 18*P1x*P0y*Q3x*Q3y*pow(sp,2.0) + 90*P1x*P1y*Q1x*Q2y*pow(sp,4.0) + 234*P1x*P1y*Q1x*Q3y*pow(sp,3.0) + 90*P1x*P1y*Q2x*Q1y*pow(sp,4.0) - 162*P1x*P1y*Q2x*Q2y*pow(sp,3.0) - 36*P1x*P1y*Q2x*Q3y*pow(sp,2.0) + 234*P1x*P1y*Q3x*Q1y*pow(sp,3.0) - 36*P1x*P1y*Q3x*Q2y*pow(sp,2.0) + 240*P0y*P1y*Q1x*Q3x*pow(sp,4.0) - 64*P0y*P1y*Q2x*Q3x*pow(sp,3.0) + 72*P0x*P1x*Q2y*Q3y*pow(sp,4.0) - 18*P0x*P0y*Q3x*Q3y*pow(sp,4.0) + 27*P0x*P1y*Q2x*Q3y*pow(sp,4.0) + 27*P0x*P1y*Q3x*Q2y*pow(sp,4.0) - 30*P0x*P1y*Q3x*Q3y*pow(sp,3.0) + 27*P1x*P0y*Q2x*Q3y*pow(sp,4.0) + 27*P1x*P0y*Q3x*Q2y*pow(sp,4.0) - 30*P1x*P0y*Q3x*Q3y*pow(sp,3.0) - 90*P1x*P1y*Q1x*Q3y*pow(sp,4.0) + 72*P1x*P1y*Q2x*Q2y*pow(sp,4.0) + 66*P1x*P1y*Q2x*Q3y*pow(sp,3.0) - 90*P1x*P1y*Q3x*Q1y*pow(sp,4.0) + 66*P1x*P1y*Q3x*Q2y*pow(sp,3.0) + 18*P1x*P1y*Q3x*Q3y*pow(sp,2.0) + 72*P0y*P1y*Q2x*Q3x*pow(sp,4.0) + 18*P0x*P1y*Q3x*Q3y*pow(sp,4.0) + 18*P1x*P0y*Q3x*Q3y*pow(sp,4.0) - 27*P1x*P1y*Q2x*Q3y*pow(sp,4.0) - 27*P1x*P1y*Q3x*Q2y*pow(sp,4.0) + 30*P1x*P1y*Q3x*Q3y*pow(sp,3.0) - 18*P1x*P1y*Q3x*Q3y*pow(sp,4.0) - 252*P0y*P1y*Q1y*Q2y*pow(sp,2.0) + 756*P0y*P1y*Q1y*Q2y*pow(sp,3.0) + 252*P0y*P1y*Q1y*Q3y*pow(sp,2.0) - 420*P0y*P1y*Q1y*Q2y*pow(sp,4.0) - 756*P0y*P1y*Q1y*Q3y*pow(sp,3.0) + 420*P0y*P1y*Q1y*Q3y*pow(sp,4.0) - 196*P0y*P1y*Q2y*Q3y*pow(sp,3.0) + 126*P0y*P1y*Q2y*Q3y*pow(sp,4.0))/(3*(7*pow(P0x,2.0)*pow(Q2x,2.0) + 7*pow(P0x,2.0)*pow(Q3x,2.0) + 7*pow(P1x,2.0)*pow(Q2x,2.0) + 7*pow(P1x,2.0)*pow(Q3x,2.0) + 16*pow(P0x,2.0)*pow(Q2y,2.0) + 16*pow(P0y,2.0)*pow(Q2x,2.0) + 16*pow(P0x,2.0)*pow(Q3y,2.0) + 16*pow(P1x,2.0)*pow(Q2y,2.0) + 16*pow(P0y,2.0)*pow(Q3x,2.0) + 16*pow(P1y,2.0)*pow(Q2x,2.0) + 16*pow(P1x,2.0)*pow(Q3y,2.0) + 16*pow(P1y,2.0)*pow(Q3x,2.0) + 7*pow(P0y,2.0)*pow(Q2y,2.0) + 7*pow(P0y,2.0)*pow(Q3y,2.0) + 7*pow(P1y,2.0)*pow(Q2y,2.0) + 7*pow(P1y,2.0)*pow(Q3y,2.0) - 14*P0x*P1x*pow(Q2x,2.0) - 14*P0x*P1x*pow(Q3x,2.0) - 32*P0x*P1x*pow(Q2y,2.0) - 32*P0x*P1x*pow(Q3y,2.0) - 32*P0y*P1y*pow(Q2x,2.0) - 32*P0y*P1y*pow(Q3x,2.0) - 14*P0y*P1y*pow(Q2y,2.0) - 14*P0y*P1y*pow(Q3y,2.0) - 14*pow(P0x,2.0)*Q2x*Q3x - 14*pow(P1x,2.0)*Q2x*Q3x - 32*pow(P0y,2.0)*Q2x*Q3x - 32*pow(P1y,2.0)*Q2x*Q3x - 32*pow(P0x,2.0)*Q2y*Q3y - 32*pow(P1x,2.0)*Q2y*Q3y - 14*pow(P0y,2.0)*Q2y*Q3y - 14*pow(P1y,2.0)*Q2y*Q3y + 28*P0x*P1x*Q2x*Q3x - 18*P0x*P0y*Q2x*Q2y + 18*P0x*P0y*Q2x*Q3y + 18*P0x*P0y*Q3x*Q2y + 18*P0x*P1y*Q2x*Q2y + 18*P1x*P0y*Q2x*Q2y + 64*P0x*P1x*Q2y*Q3y - 18*P0x*P0y*Q3x*Q3y - 18*P0x*P1y*Q2x*Q3y - 18*P0x*P1y*Q3x*Q2y - 18*P1x*P0y*Q2x*Q3y - 18*P1x*P0y*Q3x*Q2y - 18*P1x*P1y*Q2x*Q2y + 64*P0y*P1y*Q2x*Q3x + 18*P0x*P1y*Q3x*Q3y + 18*P1x*P0y*Q3x*Q3y + 18*P1x*P1y*Q2x*Q3y + 18*P1x*P1y*Q3x*Q2y - 18*P1x*P1y*Q3x*Q3y + 28*P0y*P1y*Q2y*Q3y)); // Just an arbitrary correction heuristic for negative factors ... if (out_pfactor < 0) out_pfactor = -0.1 * out_pfactor; if (out_qfactor < 0) out_qfactor = -0.1 * out_qfactor; } float PathObject::calcBezierPointDeletionRetainingShapeCost(MapCoord p0, MapCoordF p1, MapCoordF p2, MapCoord p3, PathObject* reference) { const int num_test_points = 20; QBezier curve = QBezier::fromPoints(QPointF(p0), QPointF(p1), QPointF(p2), QPointF(p3)); float cost = 0; for (int i = 0; i < num_test_points; ++i) { auto point = MapCoordF { curve.pointAt((i + 1) / (float)(num_test_points + 1)) }; float distance_sq; PathCoord path_coord; reference->calcClosestPointOnPath(MapCoordF(point), distance_sq, path_coord); cost += distance_sq; } // Just some random scaling to pretend that we have 50 sample points return cost * (50 / 20.0f); } void PathObject::calcBezierPointDeletionRetainingShapeOptimization(MapCoord p0, MapCoord p1, MapCoord p2, MapCoord q0, MapCoord q1, MapCoord q2, MapCoord q3, double& out_pfactor, double& out_qfactor) { const float gradient_abort_threshold = 0.05f; // if the gradient magnitude is lower than this over num_abort_steps step, the optimization is aborted const float decrease_abort_threshold = 0.004f; // if the cost descrease if lower than this over num_abort_steps step, the optimization is aborted const int num_abort_steps = 2; const double derivative_delta = 0.05; const int num_tested_step_sizes = 5; const int max_num_line_search_iterations = 5; float step_size_stepping_base = 0.001f; static LineSymbol line_symbol; PathObject old_curve(&line_symbol); p0.setCurveStart(true); old_curve.addCoordinate(p0); old_curve.addCoordinate(p1); old_curve.addCoordinate(p2); q0.setCurveStart(true); old_curve.addCoordinate(q0); old_curve.addCoordinate(q1); old_curve.addCoordinate(q2); q3.setCurveStart(false); q3.setClosePoint(false); q3.setHolePoint(false); old_curve.addCoordinate(q3); old_curve.update(); float cur_cost = 0; int num_no_improvement_iterations = 0; float old_gradient[2]; float old_step_dir[2]; for (int i = 0; i < 30; ++i) { MapCoordF p_direction = MapCoordF(p1) - MapCoordF(p0); MapCoordF r1 = MapCoordF(p0) + out_pfactor * p_direction; MapCoordF q_direction = MapCoordF(q2) - MapCoordF(q3); MapCoordF r2 = MapCoordF(q3) + out_qfactor * q_direction; // Calculate gradient and cost (if first iteration) if (i == 0) cur_cost = calcBezierPointDeletionRetainingShapeCost(p0, r1, r2, q3, &old_curve); //if (i == 0) // qDebug() << "\nStart cost: " << cur_cost; float gradient[2]; gradient[0] = (calcBezierPointDeletionRetainingShapeCost(p0, r1 + derivative_delta * p_direction, r2, q3, &old_curve) - calcBezierPointDeletionRetainingShapeCost(p0, r1 - derivative_delta * p_direction, r2, q3, &old_curve)) / (2 * derivative_delta); gradient[1] = (calcBezierPointDeletionRetainingShapeCost(p0, r1, r2 + derivative_delta * q_direction, q3, &old_curve) - calcBezierPointDeletionRetainingShapeCost(p0, r1, r2 - derivative_delta * q_direction, q3, &old_curve)) / (2 * derivative_delta); // Calculate step direction float step_dir_p; float step_dir_q; float conjugate_gradient_factor = 0; if (i == 0) { // Steepest descent step_dir_p = -gradient[0]; step_dir_q = -gradient[1]; } else { // Conjugate gradient // Fletcher - Reeves: //conjugate_gradient_factor = (pow(gradient[0], 2.0) + pow(gradient[1], 2.0)) / (pow(old_gradient[0], 2.0) + pow(old_gradient[1], 2.0)); // Polak – Ribiere: conjugate_gradient_factor = qMax(0.0, ( gradient[0] * (gradient[0] - old_gradient[0]) + gradient[1] * (gradient[1] - old_gradient[1]) ) / ( pow(old_gradient[0], 2.0) + pow(old_gradient[1], 2.0) )); //qDebug() << "Factor: " << conjugate_gradient_factor; step_dir_p = -gradient[0] + conjugate_gradient_factor * old_step_dir[0]; step_dir_q = -gradient[1] + conjugate_gradient_factor * old_step_dir[1]; } // Line search in step direction for lowest cost float best_step_size = 0; float best_step_size_cost = cur_cost; int best_step_factor = 0; float adjusted_step_size_stepping_base = step_size_stepping_base; for (int iteration = 0; iteration < max_num_line_search_iterations; ++iteration) { const float step_size_stepping = adjusted_step_size_stepping_base * (out_pfactor + out_qfactor) / 2; // * qSqrt(step_dir_p*step_dir_p + step_dir_q*step_dir_q); // qSqrt(cur_cost); for (int step_test = 1; step_test <= num_tested_step_sizes; ++step_test) { float step_size = step_test * step_size_stepping; float test_p_step = step_size * step_dir_p; float test_q_step = step_size * step_dir_q; MapCoordF test_r1 = r1 + test_p_step * p_direction; MapCoordF test_r2 = r2 + test_q_step * q_direction; float test_cost = calcBezierPointDeletionRetainingShapeCost(p0, test_r1, test_r2, q3, &old_curve); if (test_cost < best_step_size_cost) { best_step_size_cost = test_cost; best_step_size = step_size; best_step_factor = step_test; } } //qDebug() << best_step_factor; if (best_step_factor == num_tested_step_sizes) adjusted_step_size_stepping_base *= num_tested_step_sizes; else if (best_step_factor == 0) adjusted_step_size_stepping_base *= (1 / (float)num_tested_step_sizes); else break; if (iteration < 3) step_size_stepping_base = adjusted_step_size_stepping_base; } if (best_step_factor == 0 && conjugate_gradient_factor == 0) return; // Update optimized parameters and constrain them to non-negative values out_pfactor += best_step_size * step_dir_p; if (out_pfactor < 0) out_pfactor = 0; out_qfactor += best_step_size * step_dir_q; if (out_qfactor < 0) out_qfactor = 0; // Abort if gradient is really low for a number of steps float gradient_magnitude = qSqrt(gradient[0]*gradient[0] + gradient[1]*gradient[1]); //qDebug() << "Gradient magnitude: " << gradient_magnitude; if (gradient_magnitude < gradient_abort_threshold || cur_cost - best_step_size_cost < decrease_abort_threshold) { ++num_no_improvement_iterations; if (num_no_improvement_iterations == num_abort_steps) break; } else num_no_improvement_iterations = 0; cur_cost = best_step_size_cost; old_gradient[0] = gradient[0]; old_gradient[1] = gradient[1]; old_step_dir[0] = step_dir_p; old_step_dir[1] = step_dir_q; //qDebug() << "Cost: " << cur_cost; } } void PathObject::appendPath(const PathObject* other) { coords.reserve(coords.size() + other->coords.size()); coords.insert(coords.end(), other->coords.begin(), other->coords.end()); recalculateParts(); setOutputDirty(); } void PathObject::appendPathPart(const PathPart &part) { coords.reserve(coords.size() + part.size()); for (std::size_t i = 0; i < part.size(); ++i) coords.emplace_back(part.path->coords[part.first_index + i]); recalculateParts(); setOutputDirty(); } void PathObject::reverse() { for (auto& part : path_parts) part.reverse(); Q_ASSERT(isOutputDirty()); } void PathObject::closeAllParts() { for (auto& part : path_parts) part.setClosed(true, true); } bool PathObject::convertToCurves(PathObject** undo_duplicate) { bool converted_a_range = false; for (const auto& part : path_parts) { for (auto index = part.first_index; index < part.last_index; index += 3) { if (!coords[index].isCurveStart()) { auto end_index = index + 1; while (end_index < part.last_index && !coords[end_index].isCurveStart()) ++end_index; if (!converted_a_range) { if (undo_duplicate) *undo_duplicate = duplicate()->asPath(); converted_a_range = true; } index = convertRangeToCurves(part, index, end_index); } } } Q_ASSERT(!converted_a_range || isOutputDirty()); return converted_a_range; } int PathObject::convertRangeToCurves(const PathPart& part, MapCoordVector::size_type start_index, MapCoordVector::size_type end_index) { Q_ASSERT(end_index > start_index); Q_ASSERT(!coords[start_index].isCurveStart()); coords[start_index].setCurveStart(true); // Special case: last coordinate MapCoord direction; if (end_index != part.last_index) { // Use direction of next segment direction = coords[end_index + 1] - coords[end_index]; } else if (part.isClosed()) { // Use average direction at close point direction = coords[part.first_index + 1] - coords[end_index - 1]; } else { // Use direction of last segment direction = coords[end_index] - coords[end_index - 1]; } MapCoordF tangent = MapCoordF(direction); tangent.normalize(); MapCoord end_handle = coords[end_index]; end_handle.setFlags(0); auto baseline = (coords[end_index] - coords[end_index - 1]).length() * BEZIER_HANDLE_DISTANCE; end_handle = end_handle - MapCoord(tangent * baseline); // Special case: first coordinate if (start_index != part.first_index) { // Use direction of previous segment direction = coords[start_index] - coords[start_index - 1]; } else if (part.isClosed()) { // Use average direction at close point direction = coords[start_index + 1] - coords[part.last_index - 1]; } else { // Use direction of first segment direction = coords[start_index + 1] - coords[start_index]; } tangent = MapCoordF(direction); tangent.normalize(); MapCoord handle = coords[start_index]; handle.setFlags(0); baseline = (coords[start_index + 1] - coords[start_index]).length() * BEZIER_HANDLE_DISTANCE; handle = handle + MapCoord(tangent * baseline); addCoordinate(start_index + 1, handle); ++end_index; // In-between coordinates for (MapCoordVector::size_type c = start_index + 2; c < end_index; ++c) { direction = coords[c + 1] - coords[c - 2]; tangent = MapCoordF(direction); tangent.normalize(); // Add previous handle handle = coords[c]; handle.setFlags(0); baseline = (coords[c] - coords[c - 2]).length() * BEZIER_HANDLE_DISTANCE; handle = handle - MapCoord(tangent * baseline); addCoordinate(c, handle); ++c; ++end_index; Q_ASSERT(!coords[c].isCurveStart()); // Set curve start flag on point coords[c].setCurveStart(true); // Add next handle handle = coords[c]; handle.setFlags(0); baseline = (coords[c + 1] - coords[c]).length() * BEZIER_HANDLE_DISTANCE; handle = handle + MapCoord(tangent * baseline); addCoordinate(c + 1, handle); ++c; ++end_index; } // Add last handle addCoordinate(end_index, end_handle); ++end_index; return end_index; } bool PathObject::simplify(PathObject** undo_duplicate, double threshold) { // A copy for reference and undo while this is modified. QScopedPointer original(new PathObject(*this)); // original_indices provides a mapping from this object's indices to the // equivalent original object indices in order to extract the relevant // parts of the original object later for cost calculation. // Note: curve handle indices may become incorrect, we don't need them. std::vector original_indices; original_indices.resize(coords.size()); for (std::size_t i = 0, end = coords.size(); i < end; ++i) original_indices[i] = i; // A high value indicating an cost of deletion which is unknown. auto const undetermined_cost = std::numeric_limits::max(); // This vector provides the costs of deleting individual nodes. std::vector costs; costs.resize(coords.size(), undetermined_cost); // The empty LineSymbol will not generate any renderables, // thus reducing the cost of update(). // The temp object will be reused (but not reallocated) many times. LineSymbol empty_symbol; PathObject temp { &empty_symbol }; temp.coords.reserve(10); // enough for two bezier edges. for (auto part = path_parts.rbegin(); part != path_parts.rend(); ++part) { // Don't simplify parts which would get deleted. MapCoordVector::size_type minimum_part_size = part->isClosed() ? 4 : 3; auto minimumPartSizeReached = [&part, minimum_part_size]() -> bool { return (part->size() <= 7 && part->countRegularNodes() < minimum_part_size); }; auto minimum_cost_step = threshold / qMax(std::log2(part->size() / 64.0), 4.0); auto minimum_cost = 0.0; // Delete nodes in max n runs (where n = max. part.end_index - part.start_index) for (auto run = part->last_index; run > part->first_index; --run) { if (minimumPartSizeReached()) break; // Determine the costs of node deletion { auto extract_start = part->first_index; auto index = part->nextCoordIndex(extract_start); while (index < part->last_index) { auto extract_end = part->nextCoordIndex(index); if (costs[index] == undetermined_cost) { temp.assignCoordinates(*this, extract_start, extract_end); temp.deleteCoordinate(index - extract_start, true, Settings::DeleteBezierPoint_RetainExistingShape); // Debug check: start and end coords of the extracts should be at the same position Q_ASSERT(coords[extract_start].isPositionEqualTo(temp.coords.front())); Q_ASSERT(coords[extract_end].isPositionEqualTo(temp.coords.back())); costs[index] = original->calcMaximumDistanceTo(original_indices[extract_start], original_indices[extract_end], &temp, 0, temp.coords.size()-1); } extract_start = index; index = extract_end; } if (costs[part->first_index] == undetermined_cost && extract_start < part->last_index) { if (part->isClosed()) { auto extract_end = part->nextCoordIndex(part->first_index); temp.assignCoordinates(*this, extract_start, extract_end); temp.deleteCoordinate(part->last_index - extract_start, true, Settings::DeleteBezierPoint_RetainExistingShape); // Debug check: start and end coords of the extracts should be at the same position Q_ASSERT(coords[extract_start].isPositionEqualTo(temp.coords.front())); Q_ASSERT(coords[extract_end].isPositionEqualTo(temp.coords.back())); costs[part->first_index] = original->calcMaximumDistanceTo(original_indices[extract_start], original_indices[extract_end], &temp, 0, temp.coords.size()-1); } else { costs[part->first_index] = threshold * 2; } } costs[part->last_index] = undetermined_cost; } // Find an upper bound for the acceptable cost in the current run auto cost_bound = threshold * 2; for (auto index = part->first_index; index < part->last_index; ++index) { if (costs[index] <= cost_bound) { cost_bound = costs[index]; if (cost_bound <= minimum_cost) { // short-cut on minimal costs cost_bound = minimum_cost; break; } } } if (cost_bound > threshold) break; while (minimum_cost <= cost_bound) minimum_cost += minimum_cost_step; if (minimum_cost > threshold) minimum_cost = threshold; // Start at the end, in order to shorten the vector quickly auto index = part->last_index; do { if (costs[index] <= cost_bound) { auto extract_start = part->prevCoordIndex(index); auto extract_end = part->nextCoordIndex(index); Q_ASSERT(original->coords[original_indices[extract_start]].isPositionEqualTo(coords[extract_start])); Q_ASSERT(original->coords[original_indices[extract_end]].isPositionEqualTo(coords[extract_end])); deleteCoordinate(index, true, Settings::DeleteBezierPoint_RetainExistingShape); if (index > part->first_index) { // Deleted inner point auto new_extract_end = part->nextCoordIndex(extract_start); auto original_start = begin(original_indices) + extract_start; original_indices.erase(original_start + 1, original_start + 1 + (extract_end - new_extract_end)); Q_ASSERT(original_indices.size() == coords.size()); Q_ASSERT(original->coords[original_indices[extract_start]].isPositionEqualTo(coords[extract_start])); Q_ASSERT(original->coords[original_indices[new_extract_end]].isPositionEqualTo(coords[new_extract_end])); costs.erase(begin(costs) + extract_start + 1, begin(costs) + extract_start + 1 + (extract_end - new_extract_end)); Q_ASSERT(costs.size() == coords.size()); costs[extract_start] = undetermined_cost; if (new_extract_end == part->last_index) { costs[part->first_index] = undetermined_cost; } else { costs[new_extract_end] = undetermined_cost; } Q_ASSERT(costs[part->last_index] > threshold); index = part->prevCoordIndex(extract_start); if (minimumPartSizeReached()) break; } else if (part->isClosed()) { Q_ASSERT(index == part->first_index); // Deleted start point of closed path // This must match PathObject::deleteCoordinate extract_start = part->prevCoordIndex(part->last_index); auto original_begin = begin(original_indices); original_indices.erase(original_begin + part->first_index, original_begin + extract_end); original_indices.resize(coords.size()); original_indices[part->last_index] = original_indices[part->first_index]; Q_ASSERT(original->coords[original_indices[extract_start]].isPositionEqualTo(coords[extract_start])); Q_ASSERT(original->coords[original_indices[part->last_index]].isPositionEqualTo(coords[part->last_index])); Q_ASSERT(original->coords[original_indices[part->first_index]].isPositionEqualTo(coords[part->first_index])); costs.erase(begin(costs) + part->first_index, begin(costs) + extract_end); costs.resize(coords.size(), undetermined_cost); costs[extract_start] = undetermined_cost; costs[part->first_index] = undetermined_cost; Q_ASSERT(costs[part->last_index] > threshold); } else { Q_ASSERT(index == part->first_index); } } else if (index != part->first_index) { --index; } } while (index != part->first_index); } } bool removed_a_point = (coords.size() != original->coords.size()); if (removed_a_point && undo_duplicate) { *undo_duplicate = original.take(); } return removed_a_point; } int PathObject::isPointOnPath(MapCoordF coord, float tolerance, bool treat_areas_as_paths, bool extended_selection) const { float side_tolerance = tolerance; if (extended_selection && map && (symbol->getType() == Symbol::Line || symbol->getType() == Symbol::Combined)) { // TODO: precalculate largest line extent for all symbols to move it out of this time critical method? side_tolerance = qMax(side_tolerance, float(symbol->calculateLargestLineExtent())); } Symbol::Type contained_types = symbol->getContainedTypes(); if ((contained_types & Symbol::Line || treat_areas_as_paths) && tolerance > 0) { update(); for (const auto& part : path_parts) { const auto& path_coords = part.path_coords; auto size = path_coords.size(); for (PathCoordVector::size_type i = 0; i < size - 1; ++i) { Q_ASSERT(path_coords[i].index < coords.size()); if (coords[path_coords[i].index].isHolePoint()) continue; MapCoordF to_coord = coord - path_coords[i].pos; MapCoordF to_next = path_coords[i+1].pos - path_coords[i].pos; MapCoordF tangent = to_next; tangent.normalize(); float dist_along_line = MapCoordF::dotProduct(to_coord, tangent); if (dist_along_line < -tolerance) continue; else if (dist_along_line < 0 && to_coord.lengthSquared() <= tolerance*tolerance) return Symbol::Line; float line_length = path_coords[i+1].clen - path_coords[i].clen; if (line_length < 1e-7) continue; if (dist_along_line > line_length + tolerance) continue; else if (dist_along_line > line_length && coord.distanceSquaredTo(path_coords[i+1].pos) <= tolerance*tolerance) return Symbol::Line; auto right = tangent.perpRight(); float dist_from_line = qAbs(MapCoordF::dotProduct(right, to_coord)); if (dist_from_line <= side_tolerance) return Symbol::Line; } } } // Check for area selection if ((contained_types & Symbol::Area) && !treat_areas_as_paths) { if (isPointInsideArea(coord)) return Symbol::Area; } return Symbol::NoSymbol; } bool PathObject::isPointInsideArea(MapCoordF coord) const { update(); bool inside = false; for (const auto& part : path_parts) { if (part.isPointInside(coord)) inside = !inside; } return inside; } double PathObject::calcMaximumDistanceTo( MapCoordVector::size_type start_index, MapCoordVector::size_type end_index, const PathObject* other, MapCoordVector::size_type other_start_index, MapCoordVector::size_type other_end_index) const { update(); Q_ASSERT(other_start_index == 0); Q_ASSERT(other_end_index == other->coords.size()-1); if (end_index < start_index) { const auto part = findPartForIndex(start_index); Q_ASSERT(part->isClosed()); Q_ASSERT(end_index >= part->first_index); Q_ASSERT(end_index <= part->last_index); auto d1 = calcMaximumDistanceTo(start_index, part->last_index, other, other_start_index, other_end_index); auto d2 = calcMaximumDistanceTo(part->first_index, end_index, other, other_start_index, other_end_index); return qMax(d1, d2); } const float test_points_per_mm = 2; float max_distance_sq = 0.0; for (const auto& part : path_parts) { if (part.first_index <= end_index && part.last_index >= start_index ) { const auto& path_coords = part.path_coords; auto pc_start = std::lower_bound(begin(path_coords), end(path_coords), start_index, PathCoord::indexLessThanValue); auto pc_end = std::lower_bound(pc_start, end(path_coords), end_index, PathCoord::indexLessThanValue); if (pc_end == end(path_coords)) { if (pc_end != pc_start) --pc_end; } PathCoord path_coord; float distance_sq = 0.0; other->calcClosestPointOnPath(pc_start->pos, distance_sq, path_coord, other_start_index, other_end_index); max_distance_sq = qMax(max_distance_sq, distance_sq); for (auto pc = pc_start; pc != pc_end; ++pc) { auto next_pc = pc + 1; double len = next_pc->clen - pc->clen; MapCoordF direction = next_pc->pos - pc->pos; int num_test_points = qMax(1, qRound(len * test_points_per_mm)); for (int p = 1; p <= num_test_points; ++p) { MapCoordF point = pc->pos + direction * ((float)p / num_test_points); other->calcClosestPointOnPath(point, distance_sq, path_coord, other_start_index, other_end_index); max_distance_sq = qMax(max_distance_sq, distance_sq); } } } } return sqrt(max_distance_sq); } PathObject::Intersection PathObject::Intersection::makeIntersectionAt(double a, double b, const PathCoord& a0, const PathCoord& a1, const PathCoord& b0, const PathCoord& b1, PathPartVector::size_type part_index, PathPartVector::size_type other_part_index) { PathObject::Intersection new_intersection; new_intersection.coord = MapCoordF(a0.pos.x() + a * (a1.pos.x() - a0.pos.x()), a0.pos.y() + a * (a1.pos.y() - a0.pos.y())); new_intersection.part_index = part_index; new_intersection.length = a0.clen + a * (a1.clen - a0.clen); new_intersection.other_part_index = other_part_index; new_intersection.other_length = b0.clen + b * (b1.clen - b0.clen); return new_intersection; } void PathObject::Intersections::normalize() { std::sort(begin(), end()); erase(std::unique(begin(), end()), end()); } void PathObject::calcAllIntersectionsWith(const PathObject* other, PathObject::Intersections& out) const { update(); other->update(); const double epsilon = 1e-10; const double zero_minus_epsilon = 0 - epsilon; const double one_plus_epsilon = 1 + epsilon; for (size_t part_index = 0; part_index < path_parts.size(); ++part_index) { const PathPart& part = path_parts[part_index]; auto path_coord_end_index = part.path_coords.size() - 1; for (auto i = PathCoordVector::size_type { 1 }; i <= path_coord_end_index; ++i) { // Get information about this path coord bool has_segment_before = (i > 1) || part.isClosed(); MapCoordF ingoing_direction; if (has_segment_before && i == 1) { Q_ASSERT(path_coord_end_index >= 1); ingoing_direction = part.path_coords[path_coord_end_index].pos - part.path_coords[path_coord_end_index - 1].pos; ingoing_direction.normalize(); } else if (has_segment_before) { Q_ASSERT(i >= 1 && i < part.path_coords.size()); ingoing_direction = part.path_coords[i-1].pos - part.path_coords[i-2].pos; ingoing_direction.normalize(); } bool has_segment_after = (i < path_coord_end_index) || part.isClosed(); MapCoordF outgoing_direction; if (has_segment_after && i == path_coord_end_index) { Q_ASSERT(part.path_coords.size() > 1); outgoing_direction = part.path_coords[1].pos - part.path_coords[0].pos; outgoing_direction.normalize(); } else if (has_segment_after) { Q_ASSERT(i < path_coord_end_index); outgoing_direction = part.path_coords[i+1].pos - part.path_coords[i].pos; outgoing_direction.normalize(); } // Collision state with other object at current other path coord bool colliding = false; // Last known intersecting point. // This is valid as long as colliding == true and entered as intersection // when the next segment suddenly is not colliding anymore. Intersection last_intersection; for (size_t other_part_index = 0; other_part_index < other->path_parts.size(); ++other_part_index) { const PathPart& other_part = other->path_parts[part_index]; /// \todo FIXME: part_index or other_part_index ??? auto other_path_coord_end_index = other_part.path_coords.size() - 1; for (auto k = PathCoordVector::size_type { 1 }; k <= other_path_coord_end_index; ++k) { // Test the two line segments against each other. // Naming: segment in this path is a, segment in other path is b const PathCoord& a0 = part.path_coords[i-1]; const PathCoord& a1 = part.path_coords[i]; const PathCoord& b0 = other_part.path_coords[k-1]; const PathCoord& b1 = other_part.path_coords[k]; MapCoordF b_direction = b1.pos - b0.pos; b_direction.normalize(); bool first_other_segment = (k == 1); if (first_other_segment) { colliding = isPointOnSegment(a0.pos, a1.pos, b0.pos); if (colliding && !other_part.isClosed()) { // Enter intersection at start of other segment bool ok; double a = parameterOfPointOnLine(a0.pos.x(), a0.pos.y(), a1.pos.x() - a0.pos.x(), a1.pos.y() - a0.pos.y(), b0.pos.x(), b0.pos.y(), ok); Q_ASSERT(ok); out.push_back(Intersection::makeIntersectionAt(a, 0, a0, a1, b0, b1, part_index, other_part_index)); } } bool last_other_segment = (k == other_path_coord_end_index); if (last_other_segment) { bool collision_at_end = isPointOnSegment(a0.pos, a1.pos, b1.pos); if (collision_at_end && !other_part.isClosed()) { // Enter intersection at end of other segment bool ok; double a = parameterOfPointOnLine(a0.pos.x(), a0.pos.y(), a1.pos.x() - a0.pos.x(), a1.pos.y() - a0.pos.y(), b1.pos.x(), b1.pos.y(), ok); Q_ASSERT(ok); out.push_back(Intersection::makeIntersectionAt(a, 1, a0, a1, b0, b1, part_index, other_part_index)); } } double denominator = a0.pos.x()*b0.pos.y() - a0.pos.y()*b0.pos.x() - a0.pos.x()*b1.pos.y() - a1.pos.x()*b0.pos.y() + a0.pos.y()*b1.pos.x() + a1.pos.y()*b0.pos.x() + a1.pos.x()*b1.pos.y() - a1.pos.y()*b1.pos.x(); if (denominator == 0) { // Parallel lines, calculate parameters for b's start and end points in a and b. // This also checks whether the lines are actually on the same level. bool ok; double b_start = 0; double a_start = parameterOfPointOnLine(a0.pos.x(), a0.pos.y(), a1.pos.x() - a0.pos.x(), a1.pos.y() - a0.pos.y(), b0.pos.x(), b0.pos.y(), ok); if (!ok) { if (colliding) out.push_back(last_intersection); colliding = false; continue; } double b_end = 1; double a_end = parameterOfPointOnLine(a0.pos.x(), a0.pos.y(), a1.pos.x() - a0.pos.x(), a1.pos.y() - a0.pos.y(), b1.pos.x(), b1.pos.y(), ok); if (!ok) { if (colliding) out.push_back(last_intersection); colliding = false; continue; } // Cull ranges if (a_start < zero_minus_epsilon && a_end < zero_minus_epsilon) { if (colliding) out.push_back(last_intersection); colliding = false; continue; } if (a_start > one_plus_epsilon && a_end > one_plus_epsilon) { if (colliding) out.push_back(last_intersection); colliding = false; continue; } // b overlaps somehow with a, check if we have to enter one or two collisions // (provided the incoming / outgoing tangents are not parallel!) if (!colliding) { if (a_start <= 0) { // b comes in over the start of a Q_ASSERT(a_end >= 0); // Check for parallel tangent case if (!has_segment_before || MapCoordF::dotProduct(b_direction, ingoing_direction) < 1 - epsilon) { // Enter intersection at a=0 double b = b_start + (0 - a_start) / (a_end - a_start) * (b_end - b_start); out.push_back(Intersection::makeIntersectionAt(0, b, a0, a1, b0, b1, part_index, other_part_index)); } colliding = true; } else if (a_start >= 1) { // b comes in over the end of a Q_ASSERT(a_end <= 1); // Check for parallel tangent case if (!has_segment_after || -1 * MapCoordF::dotProduct(b_direction, outgoing_direction) < 1 - epsilon) { // Enter intersection at a=1 double b = b_start + (1 - a_start) / (a_end - a_start) * (b_end - b_start); out.push_back(Intersection::makeIntersectionAt(1, b, a0, a1, b0, b1, part_index, other_part_index)); } colliding = true; } else Q_ASSERT(false); } if (colliding) { if (a_end > 1) { // b goes out over the end of a Q_ASSERT(a_start <= 1); // Check for parallel tangent case if (!has_segment_after || MapCoordF::dotProduct(b_direction, outgoing_direction) < 1 - epsilon) { // Enter intersection at a=1 double b = b_start + (1 - a_start) / (a_end - a_start) * (b_end - b_start); out.push_back(Intersection::makeIntersectionAt(1, b, a0, a1, b0, b1, part_index, other_part_index)); } colliding = false; } else if (a_end < 0) { // b goes out over the start of a Q_ASSERT(a_start >= 1); // Check for parallel tangent case if (!has_segment_before || -1 * MapCoordF::dotProduct(b_direction, ingoing_direction) < 1 - epsilon) { // Enter intersection at a=0 double b = b_start + (0 - a_start) / (a_end - a_start) * (b_end - b_start); out.push_back(Intersection::makeIntersectionAt(1, b, a0, a1, b0, b1, part_index, other_part_index)); } colliding = false; } else { // b stops in the middle of a. // Remember last known colliding point, the endpoint of b. last_intersection = Intersection::makeIntersectionAt(a_end, 1, a0, a1, b0, b1, part_index, other_part_index); } } // Check if there is a collision at the endpoint Q_ASSERT(colliding == (a_end >= 0 && a_end <= 1)); } else { // Non-parallel lines, calculate intersection parameters and check if in range double a = +(a0.pos.x()*b0.pos.y() - a0.pos.y()*b0.pos.x() - a0.pos.x()*b1.pos.y() + a0.pos.y()*b1.pos.x() + b0.pos.x()*b1.pos.y() - b1.pos.x()*b0.pos.y()) / denominator; if (a < zero_minus_epsilon || a > one_plus_epsilon) { if (colliding) out.push_back(last_intersection); colliding = false; continue; } double b = -(a0.pos.x()*a1.pos.y() - a1.pos.x()*a0.pos.y() - a0.pos.x()*b0.pos.y() + a0.pos.y()*b0.pos.x() + a1.pos.x()*b0.pos.y() - a1.pos.y()*b0.pos.x()) / denominator; if (b < zero_minus_epsilon || b > one_plus_epsilon) { if (colliding) out.push_back(last_intersection); colliding = false; continue; } // Special case for overlapping (cloned / traced) polylines: check if b is parallel to adjacent direction. // If so, set colliding to true / false without entering an intersection because the other line // simply continues along / comes from the path of both polylines instead of intersecting. double dot = -1; if (has_segment_before && a <= 0 + epsilon) { // Ingoing direction dot = MapCoordF::dotProduct(b_direction, ingoing_direction); if (b <= 0 + epsilon) dot = -1 * dot; } else if (has_segment_after && a >= 1 - epsilon) { // Outgoing direction dot = MapCoordF::dotProduct(b_direction, outgoing_direction); if (b >= 1 - epsilon) dot = -1 * dot; } if (dot >= 1 - epsilon) { colliding = (b > 0.5); continue; } // Enter the intersection last_intersection = Intersection::makeIntersectionAt(a, b, a0, a1, b0, b1, part_index, other_part_index); out.push_back(last_intersection); colliding = (b == 1); } } } } } } void PathObject::setCoordinate(MapCoordVector::size_type pos, MapCoord c) { Q_ASSERT(pos < getCoordinateCount()); const PathPart& part = *findPartForIndex(pos); if (part.isClosed() && pos == part.last_index) pos = part.first_index; coords[pos] = c; if (part.isClosed() && pos == part.first_index) setClosingPoint(part.last_index, c); setOutputDirty(); } void PathObject::addCoordinate(MapCoordVector::size_type pos, MapCoord c) { Q_ASSERT(pos <= coords.size()); if (coords.empty()) { coords.emplace_back(c); path_parts.clear(); path_parts.emplace_back(*this, 0, 0); } else { auto part = findPartForIndex(qMin(pos, coords.size() - 1)); coords.insert(coords.begin() + pos, c); partSizeChanged(part, +1); if (pos == part->first_index) { if (part->isClosed()) setClosingPoint(part->last_index, c); } else if (pos == part->last_index) { Q_ASSERT(pos > part->first_index); coords[pos-1].setClosePoint(false); coords[pos-1].setHolePoint(false); } } setOutputDirty(); } void PathObject::addCoordinate(MapCoord c, bool start_new_part) { if (coords.empty()) { addCoordinate(0, c); } else if (!start_new_part) { addCoordinate(coords.size(), c); } else { auto index = coords.size(); coords.push_back(c); path_parts.emplace_back(*this, index, index); setOutputDirty(); } } void PathObject::deleteCoordinate(MapCoordVector::size_type pos, bool adjust_other_coords, int delete_bezier_point_action) { const auto part = findPartForIndex(pos); const auto coords_begin = begin(coords); auto num_coords = part->size(); if (num_coords <= 7) { auto num_regular_points = part->countRegularNodes(); if (num_regular_points <= 2 && (pos == part->first_index || pos == part->last_index)) { // Too small, must delete deletePart(findPartIndexForIndex(pos)); return; } if (num_regular_points == 3 && num_coords == 4) { // A closed path of three straight segments, to be opened Q_ASSERT(part->isClosed()); if (pos == part->last_index) pos = part->first_index; // Move pos to end_index-1, than erase last two coords std::rotate(coords_begin + part->first_index, coords_begin + pos + 1, coords_begin + part->last_index); coords.erase(coords_begin + part->last_index - 1, coords_begin + part->last_index+1); partSizeChanged(part, -2); coords[part->last_index].setHolePoint(true); return; } } auto prev_index = part->prevCoordIndex(pos); if (prev_index < pos && coords[prev_index].isCurveStart() && prev_index + 3 > pos) { // Delete curve handles coords[prev_index].setCurveStart(false); coords.erase(coords_begin + prev_index + 1, coords_begin + prev_index + 3); partSizeChanged(part, -2); return; } if (pos == part->first_index || pos == part->last_index) { if (part->isClosed()) { // Make start/end point an inner point before removing auto middle_offset = coords[part->first_index].isCurveStart() ? 3 : 1; std::rotate(coords_begin + part->first_index, coords_begin + part->first_index + middle_offset, coords_begin + part->last_index); setClosingPoint(part->last_index, coords[part->first_index]); pos = part->last_index - middle_offset; prev_index = part->prevCoordIndex(pos); } else { auto start = std::min(pos, prev_index + 1); auto end = std::max(pos + 1, part->nextCoordIndex(pos)); coords.erase(coords_begin + start, coords_begin + end); partSizeChanged(part, start - end); coords[part->last_index].setHolePoint(true); coords[part->last_index].setCurveStart(false); return; } } // Delete inner point Q_ASSERT(pos > part->first_index); Q_ASSERT(pos < part->last_index); if (!(coords[prev_index].isCurveStart() && coords[pos].isCurveStart())) { // Bezier curve on single side if (coords[pos].isCurveStart()) coords[prev_index].setCurveStart(true); coords.erase(coords_begin + pos); partSizeChanged(part, -1); } else { // Bezier curves on both sides if (adjust_other_coords) { // Adjust handle positions to preserve the original shape somewhat // (of course, in general it is impossible to do this) prepareDeleteBezierPoint(pos, delete_bezier_point_action); } coords.erase(begin(coords) + pos - 1, begin(coords) + pos + 2); partSizeChanged(part, -3); } } void PathObject::prepareDeleteBezierPoint(MapCoordVector::size_type pos, int delete_bezier_point_action) { const MapCoord& p0 = coords[pos - 3]; MapCoord& p1 = coords[pos - 2]; const MapCoord& p2 = coords[pos - 1]; const MapCoord& q0 = coords[pos]; const MapCoord& q1 = coords[pos + 1]; MapCoord& q2 = coords[pos + 2]; const MapCoord& q3 = coords[pos + 3]; double pfactor, qfactor; if (delete_bezier_point_action == Settings::DeleteBezierPoint_ResetHandles) { double target_length = BEZIER_HANDLE_DISTANCE * p0.distanceTo(q3); pfactor = target_length / qMax(p0.distanceTo(p1), 0.01); qfactor = target_length / qMax(q3.distanceTo(q2), 0.01); } else if (delete_bezier_point_action == Settings::DeleteBezierPoint_RetainExistingShape) { calcBezierPointDeletionRetainingShapeFactors(p0, p1, p2, q0, q1, q2, q3, pfactor, qfactor); if (qIsInf(pfactor)) pfactor = 1; if (qIsInf(qfactor)) qfactor = 1; calcBezierPointDeletionRetainingShapeOptimization(p0, p1, p2, q0, q1, q2, q3, pfactor, qfactor); double minimum_length = 0.01 * p0.distanceTo(q3); pfactor = qMax(minimum_length / qMax(p0.distanceTo(p1), 0.01), pfactor); qfactor = qMax(minimum_length / qMax(q3.distanceTo(q2), 0.01), qfactor); } else { Q_ASSERT(delete_bezier_point_action == Settings::DeleteBezierPoint_KeepHandles); pfactor = 1; qfactor = 1; } MapCoordF p0p1 = MapCoordF(p1) - MapCoordF(p0); p1 = MapCoord(p0.x() + pfactor * p0p1.x(), p0.y() + pfactor * p0p1.y()); MapCoordF q3q2 = MapCoordF(q2) - MapCoordF(q3); q2 = MapCoord(q3.x() + qfactor * q3q2.x(), q3.y() + qfactor * q3q2.y()); } void PathObject::clearCoordinates() { coords.clear(); path_parts.clear(); setOutputDirty(); } void PathObject::assignCoordinates(const PathObject& proto, MapCoordVector::size_type first, MapCoordVector::size_type last) { Q_ASSERT(last < proto.coords.size()); auto part = proto.findPartForIndex(first); Q_ASSERT(part == proto.findPartForIndex(last)); coords.clear(); if (last >= first) coords.reserve(last - first + 1); else coords.reserve(last - part->first_index + part->last_index - first + 2); for (auto i = first; i != last; ) { MapCoord new_coord = proto.coords[i]; new_coord.setClosePoint(false); new_coord.setHolePoint(false); coords.push_back(new_coord); ++i; if (i > part->last_index) { i = part->first_index; if (part->isClosed() && i != last) coords.erase(coords.end()-1); } } // Add last point MapCoord new_coord = proto.coords[last]; new_coord.setCurveStart(false); new_coord.setClosePoint(false); new_coord.setHolePoint(true); coords.push_back(new_coord); path_parts.clear(); path_parts.emplace_back(*this, 0, coords.size()-1); setOutputDirty(); } void PathObject::updatePathCoords() const { auto part_start = MapCoordVector::size_type { 0 }; for (auto& part : path_parts) { part.first_index = part_start; part.last_index = part.path_coords.update(part_start); part_start = part.last_index+1; } } void PathObject::recalculateParts() { setOutputDirty(); path_parts.clear(); if (!coords.empty()) { MapCoordVector::size_type start_index = 0; auto last_index = coords.size()-1; for (MapCoordVector::size_type i = 0; i <= last_index; ++i) { if (coords[i].isHolePoint()) { path_parts.emplace_back(*this, start_index, i); start_index = i+1; } else if (coords[i].isCurveStart()) { i += 2; } } if (start_index <= last_index) path_parts.emplace_back(*this, start_index, last_index); } } void PathObject::setClosingPoint(MapCoordVector::size_type index, MapCoord coord) { coord.setCurveStart(false); coord.setHolePoint(true); coord.setClosePoint(true); coords[index] = coord; } void PathObject::updateEvent() const { updatePathCoords(); } void PathObject::createRenderables(ObjectRenderables& output, Symbol::RenderableOptions options) const { symbol->createRenderables(this, path_parts, output, options); } // ### PointObject ### PointObject::PointObject(const Symbol* symbol) : Object(Object::Point, symbol) , rotation(0.0) { Q_ASSERT(!symbol || (symbol->getType() == Symbol::Point)); rotation = 0; coords.push_back(MapCoord(0, 0)); } PointObject::PointObject(const PointObject& proto) : Object(proto) , rotation(proto.rotation) { // nothing } PointObject* PointObject::duplicate() const { return new PointObject(*this); } void PointObject::copyFrom(const Object& other) { if (&other == this) return; Object::copyFrom(other); const PointObject* point_other = other.asPoint(); const PointSymbol* point_symbol = getSymbol()->asPoint(); if (point_symbol && point_symbol->isRotatable()) setRotation(point_other->getRotation()); } void PointObject::setPosition(qint32 x, qint32 y) { coords[0].setNativeX(x); coords[0].setNativeY(y); setOutputDirty(); } void PointObject::setPosition(MapCoord coord) { coords[0] = coord; } void PointObject::setPosition(MapCoordF coord) { coords[0].setX(coord.x()); coords[0].setY(coord.y()); setOutputDirty(); } MapCoordF PointObject::getCoordF() const { return MapCoordF(coords.front()); } MapCoord PointObject::getCoord() const { return coords.front(); } void PointObject::transform(const QTransform& t) { if (t.isIdentity()) return; auto& coord = coords.front(); const auto p = t.map(MapCoordF{coord}); coord.setX(p.x()); coord.setY(p.y()); setOutputDirty(); } void PointObject::setRotation(float new_rotation) { Q_ASSERT(symbol->asPoint()->isRotatable() || qIsNull(new_rotation)); if (!qIsNaN(new_rotation) && symbol->asPoint()->isRotatable()) { rotation = new_rotation; setOutputDirty(); } } void PointObject::setRotation(MapCoordF vector) { setRotation(atan2(vector.x(), vector.y())); } bool PointObject::intersectsBox(const QRectF& box) const { return box.contains(QPointF(coords.front())); } } // namespace OpenOrienteering mapper-0.8.1.1/src/core/objects/object.h000066400000000000000000001071451325266516600177560ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2013-2016 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_OBJECT_H #define OPENORIENTEERING_OBJECT_H #include #include #include #include #include #include #include // IWYU pragma: no_include #include "core/map_coord.h" #include "core/path_coord.h" #include "core/virtual_path.h" #include "core/renderables/renderable.h" #include "core/symbols/symbol.h" class QIODevice; class QTransform; class QXmlStreamReader; class QXmlStreamWriter; // IWYU pragma: no_forward_declare QRectF namespace OpenOrienteering { class Map; class PointObject; class PathObject; class TextObject; class VirtualCoordVector; /** * Abstract base class which combines coordinates and a symbol to form an object * (in a map, or inside a point symbol as one of its elements). * * Every object must have a symbol. If the symbol is not known, one of the * "undefined" symbols from the Map class can be used. * * From the object's data, a call to update() will generate the object's "output", * that is a set of renderables and the calculation of the object's extent (bounding box). * The renderables can then be inserted into a map where they are used to display the object. */ class Object // clazy:exclude=copyable-polymorphic { friend class ObjectRenderables; friend class OCAD8FileImport; friend class XMLImportExport; public: /** Enumeration of possible object types. */ enum Type { /** * A single coordinate, no further coordinates can be added. * For point symbols only. */ Point = 0, /** * A dynamic list of coordinates. * For line, area and combined symbols. */ Path = 1, /** * Either one or two coordinates, for single-anchor or box text. * For text symbols only. */ Text = 4 }; /** Creates an empty object with the given type and (optional) symbol. */ explicit Object(Type type, const Symbol* symbol = nullptr); /** Creates an empty object with the given type, symbol, coords and (optional) map. */ explicit Object(Type type, const Symbol* symbol, const MapCoordVector& coords, Map* map = nullptr); protected: /** * Constructs a Object, initialized from the given prototype. * * Note that the object is NOT added to a map, and consequently, * the map pointer is initialized to nullptr. */ explicit Object(const Object& proto); public: /** Destructs the object. */ virtual ~Object(); Object& operator=(const Object& other) = delete; virtual void copyFrom(const Object& other); /** Creates an identical copy of the object. * * This needs to be implemented in non-abstract subclasses. * Implementation should use the copy constructor to ensure proper initialization. */ virtual Object* duplicate() const = 0; /** * Checks for equality with another object. If compare_symbol is set, * also the symbols are compared for having the same properties. */ bool equals(const Object* other, bool compare_symbol) const; virtual bool validate() const; /** Returns the object type determined by the subclass */ inline Type getType() const; /** Convenience cast to PointObject with type checking */ PointObject* asPoint(); /** Convenience cast to PointObject with type checking */ const PointObject* asPoint() const; /** Convenience cast to PathObject with type checking */ PathObject* asPath(); /** Convenience cast to PathObject with type checking */ const PathObject* asPath() const; /** Convenience cast to TextObject with type checking */ TextObject* asText(); /** Convenience cast to TextObject with type checking */ const TextObject* asText() const; /** Loads the object in the old "native" file format from the given file. */ void load(QIODevice* file, int version, Map* map); /** Saves the object in xml format to the given stream. */ void save(QXmlStreamWriter& xml) const; /** * Loads the object in xml format from the given stream. * @param xml The stream to load the object from, must be at the correct tag. * @param map The map in which the object will be inserted. * This value will be assigned to the object's map pointer * It may be nullptr. * @param symbol_dict A dictionary mapping symbol IDs to symbol pointers. * @param symbol If set, this symbol will be assigned to the object, rather * than reading the symbol from the stream. */ static Object* load(QXmlStreamReader& xml, Map* map, const SymbolDictionary& symbol_dict, const Symbol* symbol = nullptr); /** * If the output_dirty flag is set, regenerates output and extent, and updates the object's map (if set). * * Returns true if output was dirty. */ bool update() const; /** * Always regenerates output and extent, and updates the object's map (if set). */ void forceUpdate() const; /** Moves the whole object * @param dx X offset in native map coordinates. * @param dy Y offset in native map coordinates. */ void move(qint32 dx, qint32 dy); /** Moves the whole object by the given offset. */ void move(MapCoord offset); /** Scales all coordinates, with the given scaling center */ virtual void scale(MapCoordF center, double factor); /** Scales all coordinates, with the center (0, 0). * @param factor_x horizontal scaling factor * @param factor_y vertical scaling factor */ virtual void scale(double factor_x, double factor_y); /** Rotates the whole object around the center point. * The angle must be given in radians. */ void rotateAround(MapCoordF center, double angle); /** Rotates the whole object around the center (0, 0). * The angle must be given in radians. */ void rotate(double angle); /** * Apply a transformation to all coordinates. * * \todo Handle rotation of patterns or text (?) */ virtual void transform(const QTransform& t) = 0; /** * Checks if the given coord, with the given tolerance, is on this object. * * With extended_selection, the coord is on point objects always * if it is whithin their extent, otherwise it has to be close to * their midpoint. Returns a Symbol::Type which specifies on which * symbol type the coord is * (important for combined symbols which can have areas and lines). */ int isPointOnObject(MapCoordF coord, float tolerance, bool treat_areas_as_paths, bool extended_selection) const; /** * Checks if a path point (excluding curve control points) is included in the given box. */ virtual bool intersectsBox(const QRectF& box) const = 0; /** Takes ownership of the renderables */ void takeRenderables(); /** Deletes the renderables (and extent), undoing update() */ void clearRenderables(); /** Returns the renderables, read-only */ const ObjectRenderables& renderables() const; // Getters / Setters /** * Returns the raw MapCoordVector of the object. * It's layout and interpretation depends on the object type. */ const MapCoordVector& getRawCoordinateVector() const; /** Sets the object output's dirty state. */ void setOutputDirty(bool dirty = true); /** Returns if the object's output must be regenerated. */ bool isOutputDirty() const; /** * Changes the object's symbol, returns if successful. * * Some conversions are impossible, for example point to line. Normally, * this method checks if the types of the old and the new symbol are * compatible. If the old symbol pointer is no longer valid, you can * use no_checks to disable this. */ bool setSymbol(const Symbol* new_symbol, bool no_checks); /** Returns the object's symbol. */ const Symbol* getSymbol() const; /** NOTE: The extent is only valid after update() has been called! */ const QRectF& getExtent() const; /** * Sets the object's map pointer. * * May be nullptr if the object is not in a map. */ void setMap(Map* map); /** Returns the object's map pointer. */ Map* getMap() const; /** Constructs an object of the given type with the given symbol. */ static Object* getObjectForType(Type type, const Symbol* symbol = nullptr); /** Defines a type which maps keys to values, to be used for tagging objects. */ typedef QHash Tags; /** Returns a const reference to the object's tags. */ const Tags& tags() const; /** Replaces the object's tags. */ void setTags(const Tags& tags); /** Returns the value of the given tag key. */ QString getTag(const QString& key) const; /** Sets the given tag key to the value. */ void setTag(const QString& key, const QString& value); /** Removes the given tag key and its value. */ void removeTag(const QString& key); /** * @brief Extends a rectangle to enclose all of the object's control points. */ void includeControlPointsRect(QRectF& rect) const; protected: virtual void updateEvent() const; virtual void createRenderables(ObjectRenderables& output, Symbol::RenderableOptions options) const; Type type; const Symbol* symbol; MapCoordVector coords; Map* map; Tags object_tags; private: mutable bool output_dirty; // does the output have to be re-generated because of changes? mutable QRectF extent; // only valid after calling update() mutable ObjectRenderables output; // only valid after calling update() }; class PathPartVector; /** * Helper class with information about parts of paths. * A part is a path segment which is separated from other parts by * a hole point at its end. */ class PathPart : public VirtualPath { public: /** Pointer to path part containing this part */ PathObject* path; PathPart( PathObject& path, MapCoordVector::size_type start_index, MapCoordVector::size_type end_index ); PathPart( const VirtualCoordVector& coords, MapCoordVector::size_type start_index, MapCoordVector::size_type end_index ); PathPart( PathObject& object, const VirtualPath& path ); ~PathPart() = default; PathPart& operator=(const PathPart& rhs); /** * Closes or opens the sub-path. * * If closed == true and may_use_existing_close_point == false, * a new point is added as closing point even if its coordinates * are identical to the existing last point. Else, the last point * may be reused. */ void setClosed(bool closed, bool may_use_existing_close_point = false); /** * Closes the subpath, merging the start and end point at their center. * * \see PathPart::setClosed() */ void connectEnds(); /** * Reverses the part's coordinates. * * Reversing the coordinates results in switching the start/end/mid/dash * symbol direction for line symbols. * * \see PathObject::reverse() */ void reverse(); static PathPartVector calculatePathParts(const VirtualCoordVector& coords); }; class PathPartVector : public std::vector { public: /** * This is dangerous when copying objects (which own a PathPartVector). * * Objects need to deal with the PathParts explicitly, at least as long as * the PathPart contains distinct references to the object and to the * coordinates. * * Other use cases may consider using std::vector. */ PathPartVector& operator=(const PathPartVector&) = delete; /** * Returns true if the part's end_index is lower than index. * * This function can be used for doing a binary search on a sorted PathPartVector. * * @see std::lower_bound() */ static bool compareEndIndex(const PathPart& part, VirtualPath::size_type index); }; /** * Object type which can be used for line, area and combined symbols. * Has a dynamic number of coordinates. * * The coordinates are divided into one or multiple PathParts. A PathPart * is ended by a coordinate with the "hole point" flag. For all types of * flags which can be set, see the MapCoord documentation. */ class PathObject : public Object // clazy:exclude=copyable-polymorphic { friend class PathPart; public: /** Returned by calcAllIntersectionsWith(). */ struct Intersection { /** Coordinate of the intersection */ MapCoordF coord; /** Part index of intersection */ PathPartVector::size_type part_index; /** Length of path until this intersection point */ PathCoord::length_type length; /** Part index of intersection in other path */ PathPartVector::size_type other_part_index; /** Length of other path until this intersection point */ PathCoord::length_type other_length; /** * Creates an Intersection at the position specified by factors a and b * between the a0/a1 and b0/b1 PathCoords in the given parts. */ static Intersection makeIntersectionAt( double a, double b, const PathCoord& a0, const PathCoord& a1, const PathCoord& b0, const PathCoord& b1, PathPartVector::size_type part_index, PathPartVector::size_type other_part_index ); }; /** std::vector of Intersection with the ability to sort them and remove duplicates. */ class Intersections : public std::vector { public: /** Sorts the intersections and removes duplicates. */ void normalize(); }; /** Constructs a PathObject, optionally assigning a symbol. */ explicit PathObject(const Symbol* symbol = nullptr); /** Constructs a PathObject, assigning initial coords and optionally the map pointer. */ PathObject(const Symbol* symbol, const MapCoordVector& coords, Map* map = nullptr); /** Constructs a PathObject, assigning initial coords from a single piece of a line. */ PathObject(const Symbol* symbol, const PathObject& proto, MapCoordVector::size_type piece); protected: /** Constructs a PathObject, initalized from the given prototype. */ explicit PathObject(const PathObject& proto); public: /** Constructs a PathObject, initalized from the given part of another object. */ explicit PathObject(const PathPart& proto_part); /** * Creates a duplicate of the path object. * * Use asPath() on the result to obtain an object of type PathObject. */ PathObject* duplicate() const override; PathObject& operator=(const PathObject& other) = delete; /** Replaces this object's contents by those of the other. */ void copyFrom(const Object& other) override; bool validate() const override; /** Checks the path for valid flags, and makes corrections as neccessary. */ void normalize(); bool intersectsBox(const QRectF& box) const override; // Coordinate access methods /** Returns the number of coordinates, including curve handles and close points. */ MapCoordVector::size_type getCoordinateCount() const; /** Returns the i-th coordinate. */ const MapCoord& getCoordinate(MapCoordVector::size_type pos) const; /** Returns the i-th coordinate. */ MapCoord& getCoordinate(MapCoordVector::size_type pos); /** Replaces the i-th coordinate with c. */ void setCoordinate(MapCoordVector::size_type pos, MapCoord c); /** Adds the coordinate at the given index. */ void addCoordinate(MapCoordVector::size_type pos, MapCoord c); /** Adds the coordinate at the end, optionally starting a new part. * If starting a new part, make sure that the last coord of the old part * has the hole point flag! */ void addCoordinate(MapCoord c, bool start_new_part = false); /** * Deletes a coordinate from the path. * * When requesting a control point of a bezier arc to be deleted, the other * control point is deleted, too. * * If the number of regular points in the coordinate's part is not more * than two, the whole part is delete from the object. * * @param pos Index of the coordinate to delete. * @param adjust_other_coords If set and the deleted coordinate was joining * two bezier curves, adapts the adjacent curves with a strategy defined * by delete_bezier_point_action. adjust_other_coords does not work * when deleting bezier curve handles! * @param delete_bezier_point_action Must be an enum value from * Settings::DeleteBezierPointAction if adjust_other_coords is set. */ void deleteCoordinate(MapCoordVector::size_type pos, bool adjust_other_coords, int delete_bezier_point_action = -1); /** Deletes all coordinates of the object. */ void clearCoordinates(); /** * Assigns the given prototype's coordinates subset to this object's coordinates. * * The range must be within one part. Last may be smaller than first iff * the path is closed. */ void assignCoordinates(const PathObject& proto, MapCoordVector::size_type first, MapCoordVector::size_type last); /** Finds the path part containing the given coord index. */ PathPartVector::const_iterator findPartForIndex(MapCoordVector::size_type coords_index) const; /** Finds the path part containing the given coord index. */ PathPartVector::iterator findPartForIndex(MapCoordVector::size_type coords_index); /** * Finds the path part containing the given coord index. * * \todo Review where this signature can be replace by the one returning an iterator. */ PathPartVector::size_type findPartIndexForIndex(MapCoordVector::size_type coords_index) const; /** * Returns the path coordinate for the map coordinate with given index. * * @param index Index of normal MapCoord for which to create the PathCoord. */ PathCoord findPathCoordForIndex(MapCoordVector::size_type index) const; /** * Returns true if the given index is a curve handle. */ bool isCurveHandle(MapCoordVector::size_type index) const; /** * Returns the vector of path parts. */ const PathPartVector& parts() const; /** * Returns the vector of path parts. * * Marks the output as dirty. */ PathPartVector& parts(); /** * Deletes the i-th path part. */ void deletePart(PathPartVector::size_type part_index); /** * Transforms the coordinates and the pattern origin. */ void transform(const QTransform& t) override; // Pattern methods /** * Returns the rotation of the object pattern. Only has an effect in * combination with a symbol interpreting this value. */ float getPatternRotation() const; /** * Sets the rotation of the object pattern. Only has an effect in * combination with a symbol interpreting this value. */ void setPatternRotation(float rotation); /** * Returns the origin of the object pattern. Only has an effect in * combination with a symbol interpreting this value. */ MapCoord getPatternOrigin() const; /** * Sets the origin of the object pattern. Only has an effect in * combination with a symbol interpreting this value. */ void setPatternOrigin(const MapCoord& origin); // Operations /** * Calculates the closest point on the path to the given coordinate, * returns the squared distance of these points and PathCoord information * for the point on the path. * * This does not need to be an existing path coordinate. This method is * usually called to find the position on the path the user clicked on. * part_index can be set to a valid part index to constrain searching * to this specific path part. * * \todo Convert out_distance_sq to double (so avoiding conversions). * \todo Return PathCoord rather than writing to the provided reference. */ void calcClosestPointOnPath( MapCoordF coord, float& out_distance_sq, PathCoord& out_path_coord, MapCoordVector::size_type start_index = 0, MapCoordVector::size_type end_index = std::numeric_limits::max() ) const; /** * Calculates the closest control point coordinate to the given coordiante, * returns the squared distance of these points and the index of the control point. * * \todo Convert out_distance_sq to double (so avoiding conversions). * \todo Return index rather than writing to the provided reference. */ void calcClosestCoordinate( MapCoordF coord, float& out_distance_sq, MapCoordVector::size_type& out_index) const; /** * Splits the path at the position given by path_coord. * * Must not be called while isOutputDirty() returns true. * * Returns the index of the added point. */ MapCoordVector::size_type subdivide(const PathCoord& path_coord); /** * Splits the path in the curve which starts at the given index. * * The second parameter determines the split position between begin and end * of the curve (0.0 ... 1.0). * * Must not be called while isOutputDirty() returns true. * * @return The index of the added point. */ MapCoordVector::size_type subdivide(MapCoordVector::size_type index, float param); /** * Returns if connectIfClose() would change something with the given parameters */ bool canBeConnected(const PathObject* other, double connect_threshold_sq) const; /** * Returns if the objects were connected (if so, you can delete the other object). * If one of the paths has to be reversed, it is done for the "other" path. * Otherwise, the "other" path is not changed. * * \todo Review documentation, container usage, */ bool connectIfClose(PathObject* other, double connect_threshold_sq); /** * Connects the given parts, optionally merging the end coordinates at the * center position, and copying over the coordindates from other. */ void connectPathParts( PathPartVector::size_type part_index, const PathObject* other, PathPartVector::size_type other_part_index, bool prepend, bool merge_ends = true ); /** * Returns the result of removing the section between begin and end from the path. * * begin and end must belong to the path part with the given part_index. * However, any part_index value other than 1 is not supported at the moment. * * Returns an empty vector when nothing remains after removal. */ std::vector removeFromLine( PathPartVector::size_type part_index, qreal begin, qreal end ) const; /** * Returns the result of splitting the path at the given inner position. * * Returns an empty vector when the object is not changed by the split. * This happens when the path is not closed and split_pos is the begin or * end of the path, or when the object has got more than a single PathPart. */ std::vector splitLineAt(const PathCoord& split_pos) const; /** * Replaces the path with a range of it starting and ending at the given lengths. * * \todo Partially duplicated in LineSymbol::calculatePathCoordinates() */ void changePathBounds( PathPartVector::size_type part_index, PathCoord::length_type start_len, PathCoord::length_type end_len ); /** * Appends (copies) the coordinates of other to this path. */ void appendPath(const PathObject* other); /** * Appends (copies) the coordinates of a specific part to this path. * * The other object is determined from the part's path property. */ void appendPathPart(const PathPart& part); /** * Reverses the object's coordinates, resulting in switching * the start / end / mid / dash symbol direction for line symbols. */ void reverse(); /** Ensures that all parts are closed. Useful for objects with area-only symbols. */ void closeAllParts(); /** * Converts all polygonal sections in this path to splines. * If at least one section is converted, returns true and * returns an undo duplicate if the corresponding pointer is set. */ bool convertToCurves(PathObject** undo_duplicate = nullptr); /** * Converts the given range of coordinates to a spline by inserting handles. * The range must consist of only polygonal segments before. * * @return The new index of the end of the range. */ int convertRangeToCurves(const PathPart& part, MapCoordVector::size_type start_index, MapCoordVector::size_type end_index); /** * Tries to remove points while retaining the path shape as much as possible. * If at least one point is changed, returns true and * returns an undo duplicate if the corresponding pointer is set. */ bool simplify(PathObject** undo_duplicate, double threshold); /** See Object::isPointOnObject() */ int isPointOnPath( MapCoordF coord, float tolerance, bool treat_areas_as_paths, bool extended_selection ) const; /** * Returns true if the given coordinate is inside the area * defined by this object, which must be closed. */ bool isPointInsideArea(MapCoordF coord) const; /** * Calculates the maximum distance of the given coord ranges of two objects. */ double calcMaximumDistanceTo( MapCoordVector::size_type start_index, MapCoordVector::size_type end_index, const PathObject* other, MapCoordVector::size_type other_start_index, MapCoordVector::size_type other_end_index ) const; /** * Calculates and adds all intersections with the other path to out. * Note: intersections are not sorted and may contain duplicates! * To clean them up, call clean() on the Intersections object after adding * all intersections with objects you are interested in. */ void calcAllIntersectionsWith(const PathObject* other, Intersections& out) const; /** Called by Object::update() */ void updatePathCoords() const; /** Called by Object::load() */ void recalculateParts(); protected: /** * Adjusts the end index of the given part and the start/end indexes of the following parts. * * output_dirty must be set before calling this function. */ void partSizeChanged(PathPartVector::iterator part, MapCoordVector::difference_type change); void prepareDeleteBezierPoint(MapCoordVector::size_type pos, int delete_bezier_point_action); /** * Calculates the factors which should be applied to the length of the * remaining bezier curve handle vectors when deleting a point joining * two bezier curves to try to retain the original curves' shape. * * This is a simple version, the result should be optimized with * calcBezierPointDeletionRetainingShapeOptimization(). * * p0, p1, p2, q0 make up the first original curve, * q0, q1, q2, q3 make up the second original curve. * out_pfactor is set to the factor to apply to the vector (p1 - p0), * out_qfactor is set to the factor to apply to the vector (q2 - q3), */ static void calcBezierPointDeletionRetainingShapeFactors( MapCoord p0, MapCoord p1, MapCoord p2, MapCoord q0, MapCoord q1, MapCoord q2, MapCoord q3, double& out_pfactor, double& out_qfactor ); /** * Uses nonlinear optimization to improve the first result obtained by * calcBezierPointDeletionRetainingShapeFactors(). */ static void calcBezierPointDeletionRetainingShapeOptimization( MapCoord p0, MapCoord p1, MapCoord p2, MapCoord q0, MapCoord q1, MapCoord q2, MapCoord q3, double& out_pfactor, double& out_qfactor ); /** * Is used internally by calcBezierPointDeletionRetainingShapeOptimization() * to calculate the current cost. Evaluates the distance between p0 ... p3 * and the reference path. */ static float calcBezierPointDeletionRetainingShapeCost( MapCoord p0, MapCoordF p1, MapCoordF p2, MapCoord p3, PathObject* reference ); /** * Sets coord as the point which closes a part: sets the correct flags * on it and replaces the coord at the given index by it. * TODO: make separate methods? Setting coords exists already. */ void setClosingPoint(MapCoordVector::size_type index, MapCoord coord); void updateEvent() const override; void createRenderables(ObjectRenderables& output, Symbol::RenderableOptions options) const override; private: /** * Rotation angle of the object pattern. Only used if the object * has a symbol which interprets this value. */ float pattern_rotation; /** * Origin shift of the object pattern. Only used if the object * has a symbol which interprets this value. */ MapCoord pattern_origin; /** Path parts list */ mutable PathPartVector path_parts; }; /** Compares the length of the intersections. */ inline bool operator< (const PathObject::Intersection& lhs, const PathObject::Intersection& rhs) { return lhs.length < rhs.length; } /** Fuzzy equality check. */ inline bool operator== (const PathObject::Intersection& lhs, const PathObject::Intersection& rhs) { // NOTE: coord is not compared, as the intersection is defined by the other params already. const double epsilon = 1e-10; return lhs.part_index == rhs.part_index && lhs.other_part_index == rhs.other_part_index && qAbs(lhs.length - rhs.length) <= epsilon && qAbs(lhs.other_length - rhs.other_length) <= epsilon; } /** * Object type which can only be used for point symbols, * and is also the only object which can be used with them. * * Has exactly one coordinate, and additionally a rotation parameter. */ class PointObject : public Object // clazy:exclude=copyable-polymorphic { public: /** Constructs a PointObject, optionally assigning the symbol. */ explicit PointObject(const Symbol* symbol = nullptr); protected: /** Constructs a PointObject, initalized from the given prototype. */ explicit PointObject(const PointObject& proto); public: /** * Creates a duplicate of the point. * * Use asPoint() on the result to obtain an object of type PointObject. */ PointObject* duplicate() const override; PointObject& operator=(const PointObject& other) = delete; /** Replaces the content of this object by that of anothe. */ void copyFrom(const Object& other) override; /** Sets the point's position to a new position given in native map coordinates. */ void setPosition(qint32 x, qint32 y); /** Changes the point's position. */ void setPosition(MapCoord coord); /** Changes the point's position. */ void setPosition(MapCoordF coord); /** Returns the point's position as MapCoordF. */ MapCoordF getCoordF() const; /** Returns the point's coordinate. */ MapCoord getCoord() const; /** * Transforms the position. */ void transform(const QTransform& t) override; /** * Sets the point object's rotation (in radians). * * This does nothing if the object's symbol isn't rotable. However, it is an * error to call setRotation on such an object with an argument other than * binary 0. */ void setRotation(float new_rotation); /** * Sets the point object's rotation according to the given vector. */ void setRotation(MapCoordF vector); /** * Returns the point object's rotation (in radians). This is only used * if the object has a symbol which interprets this value. */ float getRotation() const; bool intersectsBox(const QRectF& box) const override; private: /** The object's rotation (in radians). */ float rotation; }; /** * A single PathCoord together with the object it belongs to. * * This is a convenient structure for passing around as parameter and return value. */ struct ObjectPathCoord : public PathCoord { PathObject* object; constexpr ObjectPathCoord() noexcept; constexpr ObjectPathCoord(PathObject* object) noexcept; constexpr ObjectPathCoord(PathObject* object, const PathCoord& coord) noexcept; constexpr ObjectPathCoord(PathObject* object, const PathCoord&& coord) noexcept; constexpr ObjectPathCoord(const ObjectPathCoord&) noexcept = default; constexpr ObjectPathCoord(ObjectPathCoord&&) noexcept = default; ObjectPathCoord& operator=(const ObjectPathCoord&) noexcept = default; ObjectPathCoord& operator=(ObjectPathCoord&&) noexcept = default; /** * This constructor sets the PathCoord members according to the given coordinate index. */ ObjectPathCoord(PathObject* object, MapCoordVector::size_type index); /** * Returns true iff the object is not null. */ constexpr operator bool() const; /** * Sets this PathCoord to the point on this path which is the closest to the * given coordinate. * * \return The squared distance of these points. * * \see PathObject::calcClosestPointOnPath */ float findClosestPointTo(MapCoordF map_coord); }; //### Object inline code ### inline Object::Type Object::getType() const { return type; } inline const ObjectRenderables& Object::renderables() const { return output; } inline const MapCoordVector& Object::getRawCoordinateVector() const { return coords; } inline void Object::setOutputDirty(bool dirty) { output_dirty = dirty; } inline bool Object::isOutputDirty() const { return output_dirty; } inline const Symbol* Object::getSymbol() const { return symbol; } inline const QRectF& Object::getExtent() const { return extent; } inline void Object::setMap(Map* map) { this->map = map; setOutputDirty(); } inline Map* Object::getMap() const { return map; } inline const Object::Tags& Object::tags() const { return object_tags; } inline QString Object::getTag(const QString& key) const { return object_tags.value(key); } //### PathPart inline code ### inline PathPart::PathPart( const VirtualCoordVector& coords, MapCoordVector::size_type start_index, MapCoordVector::size_type end_index) : VirtualPath(coords, start_index, end_index) , path(nullptr) { // nothing else } inline PathPart::PathPart( PathObject& path, MapCoordVector::size_type start_index, MapCoordVector::size_type end_index ) : VirtualPath(path.getRawCoordinateVector(), start_index, end_index) , path(&path) { // nothing else } inline PathPart& PathPart::operator=(const PathPart& rhs) { Q_ASSERT(path = rhs.path); VirtualPath::operator=(rhs); return *this; } //### PathObject inline code ### inline MapCoordVector::size_type PathObject::getCoordinateCount() const { return coords.size(); } inline const MapCoord& PathObject::getCoordinate(MapCoordVector::size_type pos) const { Q_ASSERT(pos < coords.size()); return coords[pos]; } inline MapCoord& PathObject::getCoordinate(MapCoordVector::size_type pos) { Q_ASSERT(pos < coords.size()); setOutputDirty(); return coords[pos]; } inline const PathPartVector& PathObject::parts() const { return path_parts; } inline PathPartVector& PathObject::parts() { setOutputDirty(); return path_parts; } inline float PathObject::getPatternRotation() const { return pattern_rotation; } inline MapCoord PathObject::getPatternOrigin() const { return pattern_origin; } //### PointObject inline code ### inline float PointObject::getRotation() const { return rotation; } //### ObjectPathCoord inline code ### inline constexpr ObjectPathCoord::ObjectPathCoord() noexcept : ObjectPathCoord { nullptr } { // nothing else } inline constexpr ObjectPathCoord::ObjectPathCoord(PathObject* object) noexcept : object { object } { // nothing else } inline constexpr ObjectPathCoord::ObjectPathCoord(PathObject* object, const PathCoord& coord) noexcept : PathCoord { coord } , object { object } { // nothing else } inline constexpr ObjectPathCoord::ObjectPathCoord(PathObject* object, const PathCoord&& coord) noexcept : PathCoord { std::move(coord) } , object { object } { // nothing else } inline ObjectPathCoord::ObjectPathCoord(PathObject* object, MapCoordVector::size_type index) : PathCoord { object->findPathCoordForIndex(index) } , object { object } { // nothing else } inline float ObjectPathCoord::findClosestPointTo(MapCoordF map_coord) { float distance_sq; object->calcClosestPointOnPath(map_coord, distance_sq, *this); return distance_sq; } inline constexpr ObjectPathCoord::operator bool() const { return bool { object }; } } // namespace OpenOrienteering #endif mapper-0.8.1.1/src/core/objects/object_mover.cpp000066400000000000000000000215271325266516600215200ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2013-2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "object_mover.h" #include #include #include #include "core/objects/object.h" #include "core/objects/text_object.h" #include "core/symbols/symbol.h" #include "core/symbols/text_symbol.h" namespace OpenOrienteering { ObjectMover::ObjectMover(Map* map, const MapCoordF& start_pos) : start_position(start_pos), prev_drag_x(0), prev_drag_y(0), constraints_calculated(true) { Q_UNUSED(map); } void ObjectMover::setStartPos(const MapCoordF& start_pos) { this->start_position = start_pos; } void ObjectMover::addObject(Object* object) { objects.insert(object); } void ObjectMover::addPoint(PathObject* object, MapCoordVector::size_type point_index) { Q_ASSERT(point_index < object->getCoordinateCount()); auto index_set = insertPointObject(object); index_set->insert(point_index); constraints_calculated = false; } void ObjectMover::addLine(PathObject* object, MapCoordVector::size_type start_point_index) { Q_ASSERT(start_point_index < object->getCoordinateCount()); auto index_set = insertPointObject(object); index_set->insert(start_point_index); index_set->insert(start_point_index + 1); if (object->getCoordinate(start_point_index).isCurveStart()) { index_set->insert(start_point_index + 2); index_set->insert(start_point_index + 3); } constraints_calculated = false; } void ObjectMover::addTextHandle(TextObject* text, MapCoordVector::size_type handle) { text_handles.insert({text, handle}); } void ObjectMover::move(const MapCoordF& cursor_pos, bool move_opposite_handles, qint32* out_dx, qint32* out_dy) { auto delta_x = qRound(1000 * (cursor_pos.x() - start_position.x())) - prev_drag_x; auto delta_y = qRound(1000 * (cursor_pos.y() - start_position.y())) - prev_drag_y; if (out_dx) *out_dx = delta_x; if (out_dy) *out_dy = delta_y; move(delta_x, delta_y, move_opposite_handles); prev_drag_x += delta_x; prev_drag_y += delta_y; } void ObjectMover::move(qint32 dx, qint32 dy, bool move_opposite_handles) { calculateConstraints(); // Move objects for (auto object : objects) object->move(dx, dy); // Move points for (auto& item : points) { PathObject* path = item.first; for (auto index : item.second) { auto coord = path->getCoordinate(index); coord.setNativeX(coord.nativeX() + dx); coord.setNativeY(coord.nativeY() + dy); path->setCoordinate(index, coord); } } // Apply handle constraints if (move_opposite_handles) { for (auto& constraint : handle_constraints) { MapCoord anchor_point = constraint.object->getCoordinate(constraint.curve_anchor_index); MapCoordF to_hover_point = MapCoordF(constraint.object->getCoordinate(constraint.moved_handle_index) - anchor_point); to_hover_point.normalize(); MapCoord control = constraint.object->getCoordinate(constraint.opposite_handle_index); control.setX(anchor_point.x() - constraint.opposite_handle_dist * to_hover_point.x()); control.setY(anchor_point.y() - constraint.opposite_handle_dist * to_hover_point.y()); constraint.object->setCoordinate(constraint.opposite_handle_index, control); } } // Move box text object handles for (auto& handle : text_handles) { TextObject* text_object = handle.first; const TextSymbol* text_symbol = text_object->getSymbol()->asText(); QTransform transform; transform.rotate(qRadiansToDegrees(text_object->getRotation())); QPointF delta_point = transform.map(QPointF(dx, dy)); const auto move_point = handle.second; int x_sign = (move_point <= 1) ? 1 : -1; int y_sign = (move_point >= 1 && move_point <= 2) ? 1 : -1; double new_box_width = qMax(text_symbol->getFontSize() / 2, text_object->getBoxWidth() + 0.001 * x_sign * delta_point.x()); double new_box_height = qMax(text_symbol->getFontSize() / 2, text_object->getBoxHeight() + 0.001 * y_sign * delta_point.y()); auto anchor = MapCoord { text_object->getAnchorCoordF() }; text_object->move(dx / 2, dy / 2); text_object->setBox(anchor.nativeX(), anchor.nativeY(), new_box_width, new_box_height); } } ObjectMover::CoordIndexSet* ObjectMover::insertPointObject(PathObject* object) { return &points.insert({object, CoordIndexSet()}).first->second; } void ObjectMover::calculateConstraints() { if (constraints_calculated) return; handle_constraints.clear(); // Remove all objects in the object list from the point list for (auto object : objects) { switch (object->getType()) { case Object::Path: points.erase(object->asPath()); break; case Object::Text: text_handles.erase(object->asText()); break; default: ; // nothing } } // Points for (auto& item : points) { PathObject* path = item.first; auto& point_set = item.second; // If end points of closed paths are contained in the move set, // change them to the corresponding start points // (as these trigger moving the end points automatically and are better to handle: // they are set as curve start points if a curve starts there, in contrast to the end points) for (const auto& part : path->parts()) { if (part.isClosed() && point_set.find(part.last_index) != point_set.end()) { point_set.erase(part.last_index); point_set.insert(part.first_index); } } // Expand set of moved points: // If curve points are moved, their handles must be moved, too. std::vector handles; for (auto index : point_set) { if (path->isCurveHandle(index)) { handles.push_back(index); } else { // If a curve starts here, add first handle if (path->getCoordinate(index).isCurveStart()) { Q_ASSERT(index + 1 < path->getCoordinateCount()); handles.push_back(index + 1); } // If a curve ends here, add last handle auto& part = *path->findPartForIndex(index); if (index == part.first_index && part.isClosed()) { index = part.last_index; } if (index > part.first_index && path->getCoordinate(part.prevCoordIndex(index)).isCurveStart()) { handles.push_back(index - 1); } } } // Add the handles to the list of points. // Determine opposite handle constraints. for (auto index : handles) { Q_ASSERT(path->isCurveHandle(index)); auto& part = *path->findPartForIndex(index); auto end_index = part.last_index; point_set.insert(index); if (index == part.prevCoordIndex(index) + 1) { // First handle of a curve auto curve_anchor_index = index - 1; if (part.isClosed() && curve_anchor_index == part.first_index) curve_anchor_index = end_index; if (curve_anchor_index != part.first_index && path->getCoordinate(part.prevCoordIndex(curve_anchor_index)).isCurveStart()) { OppositeHandleConstraint constraint; constraint.object = path; constraint.moved_handle_index = index; constraint.curve_anchor_index = index - 1; constraint.opposite_handle_index = curve_anchor_index - 1; constraint.opposite_handle_original_position = path->getCoordinate(constraint.opposite_handle_index); constraint.opposite_handle_dist = constraint.opposite_handle_original_position.distanceTo(path->getCoordinate(constraint.curve_anchor_index)); handle_constraints.push_back(constraint); } } else { // Second handle of a curve auto curve_anchor_index = index + 1; if (part.isClosed() && curve_anchor_index == end_index) curve_anchor_index = part.first_index; if (curve_anchor_index != end_index && path->getCoordinate(curve_anchor_index).isCurveStart()) { OppositeHandleConstraint constraint; constraint.object = path; constraint.moved_handle_index = index; constraint.curve_anchor_index = curve_anchor_index; constraint.opposite_handle_index = curve_anchor_index + 1; constraint.opposite_handle_original_position = path->getCoordinate(constraint.opposite_handle_index); constraint.opposite_handle_dist = constraint.opposite_handle_original_position.distanceTo(path->getCoordinate(constraint.curve_anchor_index)); handle_constraints.push_back(constraint); } } } } constraints_calculated = true; } } // namespace OpenOrienteering mapper-0.8.1.1/src/core/objects/object_mover.h000066400000000000000000000072441325266516600211650ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2015-2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_OBJECT_MOVER_H #define OPENORIENTEERING_OBJECT_MOVER_H #include #include #include #include #include #include "core/map_coord.h" namespace OpenOrienteering { class Map; class Object; class PathObject; class TextObject; using SelectionInfoVector = std::vector>; /** * Implements the logic to move sets of objects and / or object points for edit tools. */ class ObjectMover { public: /** Creates a mover for the map with the given cursor start position. */ ObjectMover(Map* map, const MapCoordF& start_pos); /** Sets the start position. */ void setStartPos(const MapCoordF& start_pos); /** Adds an object to the set of elements to move. */ void addObject(Object* object); /** Adds a point to the set of elements to move. */ void addPoint(PathObject* object, MapCoordVector::size_type point_index); /** Adds a line to the set of elements to move. */ void addLine(PathObject* object, MapCoordVector::size_type start_point_index); /** Adds a text handle to the set of elements to move. */ void addTextHandle(TextObject* text, MapCoordVector::size_type handle); /** * Moves the elements. * @param move_opposite_handles If false, opposite handles are reset to their original position. * @param out_dx returns the move along the x coordinate in map units * @param out_dy returns the move along the y coordinate in map units */ void move(const MapCoordF& cursor_pos, bool move_opposite_handles, qint32* out_dx = nullptr, qint32* out_dy = nullptr); /** Overload of move() taking delta values. */ void move(qint32 dx, qint32 dy, bool move_opposite_handles); private: using ObjectSet = std::unordered_set; using CoordIndexSet = std::unordered_set; CoordIndexSet* insertPointObject(PathObject* object); void calculateConstraints(); // Basic information MapCoordF start_position; qint32 prev_drag_x; qint32 prev_drag_y; ObjectSet objects; std::unordered_map points; std::unordered_map text_handles; /** Constraints calculated from the basic information */ struct OppositeHandleConstraint { /** Object to which the constraint applies */ PathObject* object; /** Index of moved handle */ MapCoordVector::size_type moved_handle_index; /** Index of opposite handle */ MapCoordVector::size_type opposite_handle_index; /** Index of center point in the middle of the handles */ MapCoordVector::size_type curve_anchor_index; /** Distance of opposite handle to center point */ qreal opposite_handle_dist; /** Original position of the opposite handle */ MapCoord opposite_handle_original_position; }; std::vector handle_constraints; bool constraints_calculated; }; } // namespace OpenOrienteering #endif mapper-0.8.1.1/src/core/objects/object_operations.h000066400000000000000000000055761325266516600222260ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schoeps * Copyright 2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_OBJECT_OPERATIONS_H #define OPENORIENTEERING_OBJECT_OPERATIONS_H #include "core/map.h" #include "core/objects/object.h" #include "core/symbols/symbol.h" namespace OpenOrienteering { /** * Object conditions and processors, * see methods Map::applyOnAllObjects() and MapPart::applyOnAllObjects() */ namespace ObjectOp { // Conditions /** Returns true for objects with the given symbol. */ struct HasSymbol { const Symbol* symbol; bool operator()(const Object* object) const noexcept { return object->getSymbol() == symbol; } }; /** Returns true for objects with the given symbol type. */ struct HasSymbolType { Symbol::Type type; bool operator()(const Object* object) const noexcept { return object->getSymbol()->getType() == type; } }; /** Returns true for objects where the symbol type contains the given type. */ struct ContainsSymbolType { Symbol::Type type; bool operator()(const Object* object) const noexcept { return object->getSymbol()->getContainedTypes() & type; } }; // Operations /** Scales objects by the given factor. */ struct Scale { double factor; MapCoordF center; void operator()(Object* object) const { object->scale(center, factor); object->update(); } }; /** Rotates objects by the given angle (in radians). */ struct Rotate { double angle; MapCoordF center; void operator()(Object* object) const { object->rotateAround(center, angle); object->update(); } }; /** * Changes the objects' symbols. * NOTE: Make sure to apply this to correctly fitting objects only! */ struct ChangeSymbol { const Symbol* new_symbol; void operator()(Object* object, MapPart* part, int object_index) const { if (!object->setSymbol(new_symbol, false)) part->deleteObject(object_index, false); else object->update(); } }; /** Delete objects. */ struct Delete { void operator()(const Object*, MapPart* part, int object_index) const { part->deleteObject(object_index, false); } }; } } // namespace OpenOrienteering #endif mapper-0.8.1.1/src/core/objects/object_query.cpp000066400000000000000000000407031325266516600215320ustar00rootroot00000000000000/* * Copyright 2016 Mitchell Krome * Copyright 2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "object_query.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "core/objects/object.h" #include "core/objects/text_object.h" #include "core/symbols/symbol.h" // ### Local utilites ### namespace { static QChar special_chars[9] = { QLatin1Char('"'), QLatin1Char(' '), QLatin1Char('\t'), QLatin1Char('('), QLatin1Char(')'), QLatin1Char('='), QLatin1Char('!'), QLatin1Char('~'), QLatin1Char('\\') }; QString toEscaped(QString string) { for (int i = 0; i < string.length(); ++i) { const auto c = string.at(i); if (c == QLatin1Char('\\') || c == QLatin1Char('"')) { string.insert(i, QLatin1Char('\\')); ++i; } } return string; } QString fromEscaped(QString string) { for (int i = 0; i < string.length(); ++i) { const auto c = string.at(i); if (c == QLatin1Char('\\')) { string.remove(i, 1); ++i; } } return string; } QString keyToString(const QString &key) { using std::begin; using std::end; if (std::find_first_of(begin(key), end(key), begin(special_chars), end(special_chars)) == end(key) && key != QLatin1String("AND") && key != QLatin1String("OR") && key != QLatin1String("SYMBOL")) return key; else return QLatin1Char('"') + toEscaped(key) + QLatin1Char('"'); } } namespace OpenOrienteering { // ### ObjectQuery::LogicalOperands ### ObjectQuery::LogicalOperands::LogicalOperands(const ObjectQuery::LogicalOperands& proto) : first(proto.first ? std::make_unique(*proto.first) : std::make_unique()) , second(proto.second ? std::make_unique(*proto.second) : std::make_unique()) { // nothing else } ObjectQuery::LogicalOperands::~LogicalOperands() = default; ObjectQuery::LogicalOperands& ObjectQuery::LogicalOperands::operator=(const ObjectQuery::LogicalOperands& proto) { if (&proto == this) return *this; if (proto.first) first = std::make_unique(*proto.first); if (proto.second) second = std::make_unique(*proto.second); return *this; } // ### ObjectQuery::StringOperands ### ObjectQuery::StringOperands::~StringOperands() = default; // ### ObjectQuery ### ObjectQuery::ObjectQuery() noexcept : op { ObjectQuery::OperatorInvalid } { // nothing else } ObjectQuery::ObjectQuery(const ObjectQuery& query) : op { query.op } { if (op == ObjectQuery::OperatorInvalid) { ; // nothing } else if (op < 16) { new (&subqueries) LogicalOperands(query.subqueries); } else if (op < 32) { new (&tags) StringOperands(query.tags); } else if (op == ObjectQuery::OperatorSymbol) { symbol = query.symbol; } } ObjectQuery::ObjectQuery(ObjectQuery&& proto) noexcept : op { ObjectQuery::OperatorInvalid } { consume(std::move(proto)); } ObjectQuery& ObjectQuery::operator=(const ObjectQuery& proto) noexcept { if (&proto == this) return *this; reset(); consume(ObjectQuery{proto}); return *this; } ObjectQuery& ObjectQuery::operator=(ObjectQuery&& proto) noexcept { if (&proto == this) return *this; reset(); consume(std::move(proto)); return *this; } ObjectQuery::~ObjectQuery() { reset(); } ObjectQuery::ObjectQuery(const QString& key, ObjectQuery::Operator op, const QString& value) : op { op } , tags { key, value } { // Can't have an empty key (but can have empty value) // Must be a key/value operator Q_ASSERT(op >= 16); Q_ASSERT(op <= 18); if (op < 16 || op > 18 || key.length() == 0) { reset(); } } ObjectQuery::ObjectQuery(ObjectQuery::Operator op, const QString& value) : op { op } , tags { {}, value } { // Can't have an empty key (but can have empty value) // Must be a key/value operator Q_ASSERT(op >= 19); Q_ASSERT(op <= 20); if (op < 19 || op > 20 || value.length() == 0) { reset(); } } ObjectQuery::ObjectQuery(const ObjectQuery& first, ObjectQuery::Operator op, const ObjectQuery& second) : op { op } , subqueries {} { // Both sub-queries must be valid. // Must be a logical operator Q_ASSERT(op >= 1); Q_ASSERT(op <= 2); if (op < 1 || op > 2 || !first || !second) { reset(); } else { subqueries.first = std::make_unique(first); subqueries.second = std::make_unique(second); } } ObjectQuery::ObjectQuery(ObjectQuery&& first, ObjectQuery::Operator op, ObjectQuery&& second) noexcept : op { op } , subqueries {} { // Both sub-queries must be valid. // Must be a logical operator Q_ASSERT(op >= 1); Q_ASSERT(op <= 2); if (op < 1 || op > 2 || !first || !second) { reset(); } else { subqueries.first = std::make_unique(std::move(first)); subqueries.second = std::make_unique(std::move(second)); } } ObjectQuery::ObjectQuery(const Symbol* symbol) noexcept : op { ObjectQuery::OperatorSymbol } , symbol { symbol } { // nothing else } // static QString ObjectQuery::labelFor(ObjectQuery::Operator op) { switch (op) { case OperatorIs: //: Very short label return tr("is"); case OperatorIsNot: //: Very short label return tr("is not"); case OperatorContains: //: Very short label return tr("contains"); case OperatorSearch: //: Very short label return tr("Search"); case OperatorObjectText: //: Very short label return tr("Text"); case OperatorAnd: //: Very short label return tr("and"); case OperatorOr: //: Very short label return tr("or"); case OperatorSymbol: //: Very short label return tr("Symbol"); case OperatorInvalid: //: Very short label return tr("invalid"); } Q_UNREACHABLE(); } bool ObjectQuery::operator()(const Object* object) const { const auto& object_tags = object->tags(); switch(op) { case OperatorIs: return object_tags.contains(tags.key) && object_tags.value(tags.key) == tags.value; case OperatorIsNot: // If the object does have the tag, not is true return !object_tags.contains(tags.key) || object_tags.value(tags.key) != tags.value; case OperatorContains: return object_tags.contains(tags.key) && object_tags.value(tags.key).contains(tags.value); case OperatorSearch: if (object->getSymbol() && object->getSymbol()->getName().contains(tags.value, Qt::CaseInsensitive)) return true; for (auto it = object_tags.begin(), last = object_tags.end(); it != last; ++it) { if (it.key().contains(tags.value, Qt::CaseInsensitive) || it.value().contains(tags.value, Qt::CaseInsensitive)) return true; } return false; case OperatorObjectText: if (object->getType() == Object::Text) return static_cast(object)->getText().contains(tags.value, Qt::CaseInsensitive); return false; case OperatorAnd: return (*subqueries.first)(object) && (*subqueries.second)(object); case OperatorOr: return (*subqueries.first)(object) || (*subqueries.second)(object); case OperatorSymbol: return object->getSymbol() == symbol; case OperatorInvalid: return false; } Q_UNREACHABLE(); } const ObjectQuery::LogicalOperands* ObjectQuery::logicalOperands() const { const LogicalOperands* result = nullptr; if (op >= 1 && op <= 2) { result = &subqueries; } else { Q_ASSERT(op >= 1); Q_ASSERT(op <= 2); } return result; } const ObjectQuery::StringOperands* ObjectQuery::tagOperands() const { const StringOperands* result = nullptr; if (op >= 16 && op <= 20) { result = &tags; } else { Q_ASSERT(op >= 16); Q_ASSERT(op <= 20); } return result; } const Symbol* ObjectQuery::symbolOperand() const { const Symbol* result = nullptr; if (op == ObjectQuery::OperatorSymbol) { result = symbol; } else { Q_ASSERT(op == ObjectQuery::OperatorSymbol); } return result; } QString ObjectQuery::toString() const { auto ret = QString{}; switch (op) { case OperatorIs: ret = keyToString(tags.key) + QLatin1String(" = \"") + toEscaped(tags.value) + QLatin1Char('"'); break; case OperatorIsNot: ret = keyToString(tags.key) + QLatin1String(" != \"") + toEscaped(tags.value) + QLatin1Char('"'); break; case OperatorContains: ret = keyToString(tags.key) + QLatin1String(" ~= \"") + toEscaped(tags.value) + QLatin1Char('"'); break; case OperatorSearch: case OperatorObjectText: ret = QLatin1Char('"') + toEscaped(tags.value) + QLatin1Char('"'); break; case OperatorAnd: if (subqueries.first->getOperator() == OperatorOr) ret = QLatin1Char('(') + subqueries.first->toString() + QLatin1Char(')'); else ret = subqueries.first->toString(); ret += QLatin1String(" AND "); if (subqueries.second->getOperator() == OperatorOr) ret += QLatin1Char('(') + subqueries.second->toString() + QLatin1Char(')'); else ret += subqueries.second->toString(); break; case OperatorOr: ret = subqueries.first->toString() + QLatin1String(" OR ") + subqueries.second->toString(); break; case OperatorSymbol: ret = QLatin1String("SYMBOL \"") + symbol->getNumberAsString() + QLatin1Char('\"'); break; case OperatorInvalid: // Default empty string is sufficient break; } return ret; } void ObjectQuery::reset() { if (op == ObjectQuery::OperatorInvalid) { ; // nothing } else if (op < 16) { subqueries.~LogicalOperands(); op = ObjectQuery::OperatorInvalid; } else if (op < 32) { tags.~StringOperands(); op = ObjectQuery::OperatorInvalid; } else if (op == ObjectQuery::OperatorSymbol) { op = ObjectQuery::OperatorInvalid; } } void ObjectQuery::consume(ObjectQuery&& other) { Q_ASSERT(op == ObjectQuery::OperatorInvalid); op = other.op; if (op == ObjectQuery::OperatorInvalid) { ; // nothing else } else if (op < 16) { new (&subqueries) ObjectQuery::LogicalOperands(std::move(other.subqueries)); other.subqueries.~LogicalOperands(); } else if (op < 32) { new (&tags) ObjectQuery::StringOperands(std::move(other.tags)); other.tags.~StringOperands(); } else if (op == ObjectQuery::OperatorSymbol) { symbol = other.symbol; } other.op = ObjectQuery::OperatorInvalid; } bool operator==(const ObjectQuery::StringOperands& lhs, const ObjectQuery::StringOperands& rhs) { return lhs.key == rhs.key && lhs.value == rhs.value; } bool operator==(const ObjectQuery& lhs, const ObjectQuery& rhs) { if (lhs.op != rhs.op) return false; switch(lhs.op) { case ObjectQuery::OperatorIs: case ObjectQuery::OperatorIsNot: case ObjectQuery::OperatorContains: case ObjectQuery::OperatorSearch: case ObjectQuery::OperatorObjectText: return lhs.tags == rhs.tags; case ObjectQuery::OperatorAnd: case ObjectQuery::OperatorOr: return *lhs.subqueries.first == *rhs.subqueries.first && *lhs.subqueries.second == *rhs.subqueries.second; case ObjectQuery::OperatorSymbol: return lhs.symbol == rhs.symbol; case ObjectQuery::OperatorInvalid: return false; } Q_UNREACHABLE(); } ObjectQuery ObjectQueryParser::parse(const QString& text) { auto result = ObjectQuery{}; input = {&text, 0, text.size()}; pos = 0; auto paren_depth = 0; QVarLengthArray nested_expressions; auto* current = &result; getToken(); while (token != TokenNothing) { if ((token == TokenWord || token == TokenString) && !*current) { auto key = tokenAsString(); getToken(); if (token == TokenTextOperator) { auto op = token_text; getToken(); if (token == TokenWord || token == TokenString) { auto value = tokenAsString(); switch (op.at(0).toLatin1()) { case '=': *current = { key, ObjectQuery::OperatorIs, value }; break; case '!': *current = { key, ObjectQuery::OperatorIsNot, value }; break; case '~': *current = { key, ObjectQuery::OperatorContains, value }; break; default: Q_UNREACHABLE(); } getToken(); } else { op = {}; break; } } else { *current = { ObjectQuery::OperatorSearch, key }; } } else if (token == TokenAnd && *current) { if (!nested_expressions.isEmpty() && nested_expressions.back()->getOperator() == ObjectQuery::OperatorAnd) { nested_expressions.pop_back(); // replaced by current query } // Cannot construct logical ObjectQuery with invalid operand, but can assign later... auto tmp = ObjectQuery{std::move(*current), ObjectQuery::OperatorAnd, {ObjectQuery::OperatorSearch, text}}; *current = std::move(tmp); nested_expressions.push_back(current); current = current->logicalOperands()->second.get(); *current = {}; getToken(); } else if (token == TokenOr && *current) { if (!nested_expressions.isEmpty()) { current = nested_expressions.back(); nested_expressions.pop_back(); } // Cannot construct logical ObjectQuery with invalid operand, but can assign later... auto query = ObjectQuery{std::move(*current), ObjectQuery::OperatorOr, {ObjectQuery::OperatorSearch, text}}; *current = std::move(query); nested_expressions.push_back(current); current = current->logicalOperands()->second.get(); *current = {}; getToken(); } else if (token == TokenLeftParen && !*current) { ++paren_depth; nested_expressions.push_back(current); getToken(); } else if (token == TokenRightParen && *current && !nested_expressions.isEmpty() && paren_depth > 0) { --paren_depth; current = nested_expressions.back(); nested_expressions.pop_back(); getToken(); } else { // Invalid input result = {}; break; } } if (result && (!*current || paren_depth > 0)) { result = {}; } return result; } int ObjectQueryParser::errorPos() const { return token_start; } void ObjectQueryParser::getToken() { token = TokenNothing; QChar current; while (true) { if (pos >= input.length()) return; current = input.at(pos); if (current != QLatin1Char(' ') && current != QLatin1Char('\t')) break; ++pos; } token = TokenUnknown; token_start = pos; if (current == QLatin1Char('"')) { for (++pos; pos < input.length(); ++pos) { current = input.at(pos); if (current == QLatin1Char('"')) { token = TokenString; token_text = input.mid(token_start + 1, pos - token_start - 1); ++pos; break; } else if (current == QLatin1Char('\\')) { if (pos < input.length()) ++pos; } } } else if (current == QLatin1Char('(')) { token = TokenLeftParen; token_text = input.mid(token_start, 1); ++pos; } else if (current == QLatin1Char(')')) { token = TokenRightParen; token_text = input.mid(token_start, 1); ++pos; } else if (current == QLatin1Char('=')) { token = TokenTextOperator; token_text = input.mid(token_start, 1); ++pos; } else if ((current == QLatin1Char('!') || current == QLatin1Char('~')) && pos < input.length() && input.at(pos+1) == QLatin1Char('=')) { token = TokenTextOperator; token_text = input.mid(token_start, 2); pos += 2; } else { for (++pos; pos < input.length(); ++pos) { current = input.at(pos); if (current == QLatin1Char('"') || current == QLatin1Char(' ') || current == QLatin1Char('\t') || current == QLatin1Char('(') || current == QLatin1Char(')') || current == QLatin1Char('=')) { break; } else if ((current == QLatin1Char('!') || current == QLatin1Char('~')) && pos < input.length() && input.at(pos+1) == QLatin1Char('=')) { break; } } token_text = input.mid(token_start, pos - token_start); if (token_text == QLatin1String("OR")) token = TokenOr; else if (token_text == QLatin1String("AND")) token = TokenAnd; else token = TokenWord; } } QString ObjectQueryParser::tokenAsString() const { QString string = token_text.toString(); if (token == TokenString) string = fromEscaped(string); return string; } } // namespace OpenOrienteering mapper-0.8.1.1/src/core/objects/object_query.h000066400000000000000000000160011325266516600211710ustar00rootroot00000000000000/* * Copyright 2016 Mitchell Krome * Copyright 2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_OBJECT_QUERY_H #define OPENORIENTEERING_OBJECT_QUERY_H #include #include #include #include #include namespace OpenOrienteering { class Object; class Symbol; /** * Utility to match objects based on tag values. * * This class can be used with value semantics. It can be move-constructed and * move-assigned in O(1). Normal copy-construction and assignment may involve * expensive copying of the expression tree. * * OperatorAnd and OperatorOr evaluate the left argument first. When constructing * complex queries, left sub-query chains should be kept short in order to * benefit from short circuiting the evaluation of these logical operators after * evaluation of the left argument. */ class ObjectQuery { Q_DECLARE_TR_FUNCTIONS(OpenOrienteering::ObjectQuery) public: enum Operator { // Operators 1 .. 15 operate on other queries OperatorAnd = 1, ///< And-chains two object queries OperatorOr = 2, ///< Or-chains two object queries // Operators 16 .. 18 operate on object tags and other strings OperatorIs = 16, ///< Tests an existing tag for equality with the given value (case-sensitive) OperatorIsNot = 17, ///< Tests an existing tag for inequality with the given value (case-sensitive) OperatorContains = 18, ///< Tests an existing tag for containing the given value (case-sensitive) OperatorSearch = 19, ///< Tests if the symbol name, a tag key or a tag value contains the given value (case-insensitive) OperatorObjectText = 20, ///< Text object content (case-insensitive) // More operators, 32 .. OperatorSymbol = 32, ///< Test the symbol for equality. OperatorInvalid = 0 ///< Marks an invalid query }; // Parameters for logical operations struct LogicalOperands { std::unique_ptr first; std::unique_ptr second; LogicalOperands() = default; explicit LogicalOperands(const LogicalOperands& proto); // maybe expensive copying LogicalOperands(LogicalOperands&&) = default; ~LogicalOperands(); LogicalOperands& operator=(const LogicalOperands& proto); LogicalOperands& operator=(LogicalOperands&&) = default; }; // Parameters for operations on tags. struct StringOperands { QString key; QString value; ~StringOperands(); }; ObjectQuery() noexcept; explicit ObjectQuery(const ObjectQuery& query); // maybe expensive copying ObjectQuery(ObjectQuery&& proto) noexcept; ObjectQuery& operator=(const ObjectQuery& proto) noexcept; ObjectQuery& operator=(ObjectQuery&& proto) noexcept; ~ObjectQuery(); /** * Returns true if the query is valid. */ operator bool() const noexcept { return op != OperatorInvalid; } /** * Constructs a query for a key and value. */ ObjectQuery(const QString& key, Operator op, const QString& value); /** * Constructs a query for a value. * * Valid for OperatorSearch. */ ObjectQuery(Operator op, const QString& value); /** * Constructs a query which connects two sub-queries. * * The sub-queries are copied. */ ObjectQuery(const ObjectQuery& first, Operator op, const ObjectQuery& second); /** * Constructs a query which connects two sub-queries. */ ObjectQuery(ObjectQuery&& first, Operator op, ObjectQuery&& second) noexcept; /** * Constructs a query for a particular symbol. */ ObjectQuery(const Symbol* symbol) noexcept; /** * Returns the underlying operator. */ Operator getOperator() const noexcept { return op; } /** * Returns a short label for the operator which can be used in the user interface. */ static QString labelFor(Operator op); /** * Evaluates this query on the given object and returns whether it matches. */ bool operator()(const Object* object) const; /** * Returns the operands of logical query operations. */ const LogicalOperands* logicalOperands() const; /** * Returns the operands of logical query operations. */ const StringOperands* tagOperands() const; /** * Returns the operand of symbol operations. */ const Symbol* symbolOperand() const; /** * Pretty print the query. * * The output is meant to be formal language, for possible parsing. */ QString toString() const; friend bool operator==(const ObjectQuery& lhs, const ObjectQuery& rhs); private: /** * Resets the query to the OperatorInvalid state. */ void reset(); /** * Moves the other query's data to this one which must be in OperatorInvalid state. * * Leaves the other object in OperatorInvalid state. * In effect, this is a move assignment with a strong precondition. */ void consume(ObjectQuery&& other); using SymbolOperand = const Symbol*; Operator op; union { LogicalOperands subqueries; StringOperands tags; SymbolOperand symbol; }; }; bool operator==(const ObjectQuery& lhs, const ObjectQuery& rhs); bool operator!=(const ObjectQuery& lhs, const ObjectQuery& rhs); bool operator==(const ObjectQuery::StringOperands& lhs, const ObjectQuery::StringOperands& rhs); bool operator!=(const ObjectQuery::StringOperands& lhs, const ObjectQuery::StringOperands& rhs); /** * Utility to contruct object queries from text. * * The text shall be in the formal language generate by ObjectQuery::toString(). */ class ObjectQueryParser { public: /** * Returns an ObjectQuery for the given text. * * The return query has ObjectQuery::OperatorInvalid in case of error. */ ObjectQuery parse(const QString& text); /** * In case of error, returns the approximate position where parsing the * text failed. */ int errorPos() const; enum TokenType { TokenUnknown = 0, TokenNothing, TokenString, TokenWord, TokenTextOperator, TokenOr, TokenAnd, TokenLeftParen, TokenRightParen, }; private: void getToken(); QString tokenAsString() const; QStringRef input; QStringRef token_text; TokenType token; int token_start; int pos; }; inline bool operator!=(const ObjectQuery& lhs, const ObjectQuery& rhs) { return !(lhs==rhs); } inline bool operator!=(const ObjectQuery::StringOperands& lhs, const ObjectQuery::StringOperands& rhs) { return !(lhs==rhs); } } // namespace OpenOrienteering Q_DECLARE_METATYPE(OpenOrienteering::ObjectQuery::Operator) #endif mapper-0.8.1.1/src/core/objects/symbol_rule_set.cpp000066400000000000000000000260471325266516600222530ustar00rootroot00000000000000/* * Copyright 2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "symbol_rule_set.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "core/map.h" #include "core/objects/object.h" #include "core/symbols/symbol.h" #include "undo/undo_manager.h" namespace OpenOrienteering { // The SymbolRule member "query" may throw on copying. Q_STATIC_ASSERT(std::is_nothrow_constructible::value); Q_STATIC_ASSERT(std::is_nothrow_default_constructible::value); Q_STATIC_ASSERT(std::is_copy_constructible::value); Q_STATIC_ASSERT(std::is_nothrow_move_constructible::value); Q_STATIC_ASSERT(std::is_nothrow_destructible::value); Q_STATIC_ASSERT(std::is_copy_assignable::value); Q_STATIC_ASSERT(std::is_nothrow_move_assignable::value); // Analogously for SymbolRuleSet Q_STATIC_ASSERT(std::is_nothrow_constructible::value); Q_STATIC_ASSERT(std::is_nothrow_default_constructible::value); Q_STATIC_ASSERT(std::is_copy_constructible::value); Q_STATIC_ASSERT(std::is_nothrow_move_constructible::value); Q_STATIC_ASSERT(std::is_nothrow_destructible::value); Q_STATIC_ASSERT(std::is_copy_assignable::value); Q_STATIC_ASSERT(std::is_nothrow_move_assignable::value); // static SymbolRuleSet SymbolRuleSet::forOriginalSymbols(const Map& map) { SymbolRuleSet list; list.reserve(std::size_t(map.getNumSymbols())); for (int i = 0; i < map.getNumSymbols(); ++i) { auto original = map.getSymbol(i); list.push_back({{original}, nullptr, SymbolRule::NoAssignment}); } list.sortByQueryKeyAndValue(); return list; } SymbolRuleSet SymbolRuleSet::squeezed() const { SymbolRuleSet list; list.reserve(this->size()); std::copy_if(begin(), end(), std::back_inserter(list), [](auto item) { return item.type != SymbolRule::NoAssignment; }); return list; } void SymbolRuleSet::sortByQuerySymbol() { auto last = std::remove_if(begin(), end(), [](auto item) { return item.query.getOperator() != ObjectQuery::OperatorSymbol || !item.query.symbolOperand(); }); std::sort(begin(), last, [](auto lhs, auto rhs) { auto lhs_op = lhs.query.symbolOperand(); auto rhs_op = rhs.query.symbolOperand(); return (lhs_op->getNumberAsString() < rhs_op->getNumberAsString() && lhs_op->getName() < rhs_op->getName()); }); } void SymbolRuleSet::sortByQueryKeyAndValue() { auto last = std::remove_if(begin(), end(), [](auto item) { const auto op = item.query.getOperator(); return (op != ObjectQuery::OperatorIs && op != ObjectQuery::OperatorIsNot && op != ObjectQuery::OperatorContains) || !item.query.tagOperands(); }); std::sort(begin(), last, [](auto lhs, auto rhs) { auto lhs_op = lhs.query.tagOperands(); auto rhs_op = rhs.query.tagOperands(); return (lhs_op->key < rhs_op->key) && (lhs_op->value < rhs_op->value); }); } void SymbolRuleSet::matchQuerySymbolName(const Map& other_map) { auto findMatchingSymbol = [&other_map](const Symbol* original)->const Symbol* { for (int k = 0; k < other_map.getNumSymbols(); ++k) { auto other_symbol = other_map.getSymbol(k); if (original->getName() == other_symbol->getName() && Symbol::areTypesCompatible(original->getType(), other_symbol->getType())) { return other_symbol; } } return nullptr; }; for (auto& item : *this) { if (item.query.getOperator() != ObjectQuery::OperatorSymbol) continue; auto original = item.query.symbolOperand(); if (!original) continue; auto candidate = findMatchingSymbol(original); if (!candidate) { candidate = findMatchingSymbol(original); } if (candidate != item.symbol) { if (candidate) { item.symbol = candidate; item.type = SymbolRule::AutomaticAssignment; } else { item.type = SymbolRule::NoAssignment; } } } } void SymbolRuleSet::matchQuerySymbolNumber(const Map& other_map) { auto findMatchingSymbol = [&other_map](const Symbol* original, bool ignore_trailing_zeros)->const Symbol* { for (int k = 0; k < other_map.getNumSymbols(); ++k) { auto other_symbol = other_map.getSymbol(k); if (original->numberEquals(other_symbol, ignore_trailing_zeros) && Symbol::areTypesCompatible(original->getType(), other_symbol->getType())) { return other_symbol; } } return nullptr; }; for (auto& item : *this) { if (item.query.getOperator() != ObjectQuery::OperatorSymbol) continue; auto original = item.query.symbolOperand(); if (!original) continue; auto candidate = findMatchingSymbol(original, false); if (!candidate) { candidate = findMatchingSymbol(original, true); } if (candidate != item.symbol) { if (candidate) { item.symbol = candidate; item.type = SymbolRule::AutomaticAssignment; } else { item.type = SymbolRule::NoAssignment; } } } } // static SymbolRuleSet SymbolRuleSet::loadCrt(QTextStream& stream, const Map& replacement_map) { auto list = SymbolRuleSet{}; stream.setIntegerBase(10); // No autodectection; 001 is 1. while (!stream.atEnd()) { QString replacement_key; stream >> replacement_key; if (stream.status() == QTextStream::ReadPastEnd) { stream.resetStatus(); break; } auto pattern = stream.readLine().trimmed(); if (stream.status() == QTextStream::Ok && !replacement_key.startsWith(QLatin1Char{'#'})) { auto parsed_query = ObjectQueryParser().parse(pattern); if (!parsed_query) parsed_query = {ObjectQuery::OperatorSearch, pattern}; list.push_back({std::move(parsed_query), nullptr, SymbolRule::NoAssignment}); for (int k = 0; k < replacement_map.getNumSymbols(); ++k) { auto symbol = replacement_map.getSymbol(k); if (symbol->getNumberAsString() == replacement_key) { list.back().symbol = symbol; list.back().type = SymbolRule::DefinedAssignment; break; } } } } return list; } void SymbolRuleSet::writeCrt(QTextStream& stream) const { for (const auto& item : *this) { if (item.type != SymbolRule::NoAssignment && item.symbol) { const auto& query = item.query; auto second_field = QString{}; switch (query.getOperator()) { case ObjectQuery::OperatorSearch: if (query.tagOperands()) second_field = query.tagOperands()->value; break; case ObjectQuery::OperatorSymbol: if (query.symbolOperand()) second_field = query.symbolOperand()->getNumberAsString(); break; case ObjectQuery::OperatorIs: if (query.tagOperands() && query.tagOperands()->key == QLatin1String("Layer")) { second_field = query.tagOperands()->value; break; } // fall through case ObjectQuery::OperatorIsNot: case ObjectQuery::OperatorContains: if (query.tagOperands()) second_field = query.tagOperands()->key + QLatin1Char(' ') + query.labelFor(query.getOperator()) + QLatin1String(" \"") + query.tagOperands()->value + QLatin1Char('"'); break; default: qDebug("Unsupported query in SymbolRuleSet::writeCrt"); } if (!second_field.isEmpty()) { auto first_field = item.symbol->getNumberAsString(); auto whitespace = QString(qMax(1, 10-first_field.length()), QLatin1Char{' '}); stream << first_field << whitespace << second_field << endl; } } } } void SymbolRuleSet::operator()(Object* object) const { for (const auto& item : *this) { if (item.symbol && item.query(object)) { object->setSymbol(item.symbol, false); break; } } } void SymbolRuleSet::apply(Map& object_map, const Map& symbol_set, Options options) { std::unordered_set old_symbols; // Import new symbols if needed if (&object_map != &symbol_set) { if (!options.testFlag(KeepUnusedSymbols)) { for (int i = 0; i < object_map.getNumSymbols(); ++i) old_symbols.insert(object_map.getSymbol(i)); } auto symbol_filter = std::vector(std::size_t(symbol_set.getNumSymbols()), true); if (!options.testFlag(ImportAllSymbols)) { // Import only symbols which are chosen as replacement symbols auto item = symbol_filter.begin(); for (int i = 0; i < symbol_set.getNumSymbols(); ++i) { auto symbol = symbol_set.getSymbol(i); *item = std::any_of(begin(), end(), [symbol](auto item) { return item.symbol == symbol; }); ++item; } } auto symbol_mapping = object_map.importMap(symbol_set, Map::MinimalSymbolImport, &symbol_filter, -1, false); auto preserve_state = options.testFlag(PreserveSymbolState); for (auto& item : *this) { if (!symbol_mapping.contains(item.symbol)) continue; // unused symbol auto replacement = symbol_mapping[item.symbol]; if (item.query.getOperator() == ObjectQuery::OperatorSymbol && preserve_state) { auto original = item.query.symbolOperand(); Q_ASSERT(original); replacement->setHidden(original->isHidden()); replacement->setProtected(original->isProtected()); } item.symbol = replacement; } } // Change symbols for all objects object_map.applyOnAllObjects(std::cref(*this)); // Delete unused old symbols if (!old_symbols.empty()) { std::vector symbols_in_use; object_map.determineSymbolsInUse(symbols_in_use); for (int i = object_map.getNumSymbols() - 1; i >= 0; --i) { auto symbol = object_map.getSymbol(i); if (!symbols_in_use[std::size_t(i)] && old_symbols.find(symbol) != old_symbols.end()) { object_map.deleteSymbol(i); } } } // Delete unused colors if (!options.testFlag(KeepUnusedColors)) { std::vector all_symbols; all_symbols.assign(std::size_t(object_map.getNumSymbols()), true); std::vector colors_in_use; object_map.determineColorsInUse(all_symbols, colors_in_use); if (colors_in_use.empty()) colors_in_use.assign(std::size_t(object_map.getNumColors()), false); for (int i = object_map.getNumColors() - 1; i >= 0; --i) { if (!colors_in_use[std::size_t(i)]) object_map.deleteColor(i); } } // Finish object_map.updateAllObjects(); object_map.setObjectsDirty(); object_map.setSymbolsDirty(); object_map.undoManager().clear(); } } // namespace OpenOrienteering mapper-0.8.1.1/src/core/objects/symbol_rule_set.h000066400000000000000000000124031325266516600217070ustar00rootroot00000000000000/* * Copyright 2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_SYMBOL_RULE_SET_H #define OPENORIENTEERING_SYMBOL_RULE_SET_H #include #include #include "core/objects/object_query.h" class QTextStream; namespace OpenOrienteering { class Map; class Object; class Symbol; /** * This struct defines a single symbol assignment rule. * * Unless the type is NoAssignment, a rule specifies a new symbol for objects * which match a particular query. */ struct SymbolRule { // Intentionally no user-defined constructors, destructor, assignement. // Default ones are fine. enum RuleType { NoAssignment = 0, ManualAssignment, AutomaticAssignment, DefinedAssignment, }; ObjectQuery query; const Symbol* symbol; RuleType type; }; /** * An utility for assigning symbols to objects based on queries. */ class SymbolRuleSet : public std::vector { public: // Intentionally no user-defined constructors, destructor, assignement. // Default ones are fine. /** * Creates a list of NoAssignment items for the map's symbols as originals. * * This function can be used to easily initialize variables of type * SymbolRuleSet despite the absence of specialized constructors. * Each item is initialized with an object query for a symbol from the map. * * The created list is sorted by formatted symbol number. */ static SymbolRuleSet forOriginalSymbols(const Map& map); /** * Returns a copy which has all NoAssignment items removed. */ SymbolRuleSet squeezed() const; /** * Sorts the items by original symbol number and name if possible. * * The symbol number and name are only available for queries with operator * ObjectQuery::OperatorSymbol. Othe rules are moved to the end. */ void sortByQuerySymbol(); /** * Sorts the items by tag query key and value if possible. * * The tag key and value are only available for queries with operator * ObjectQuery::OperatorIs, ObjectQuery::OperatorIsNot, and * ObjectQuery::OperatorIs. Othe rules are moved to the end. */ void sortByQueryKeyAndValue(); /** * Sets the assigned symbols to match the original symbol name if possible. * * The symbol name is only available for queries with operator * ObjectQuery::OperatorSymbol. * * The symbols' types must be compatible. */ void matchQuerySymbolName(const Map& other_map); /** * Sets the assigned symbols to match the original symbol number if possible. * * The symbol number is only available for queries with operator * ObjectQuery::OperatorSymbol. * * The symbols' types must be compatible. */ void matchQuerySymbolNumber(const Map& other_map); /** * Loads rules from a cross reference table (CRT) stream. * * Each line in a CRT file takes the form: * * REPLACEMENT pattern * * where REPLACEMENT is the formatted number of the replacement symbol. * * If the replacement symbol number exists, the import creates a new entry * of type DefinedReplacement with the given replacement symbol. Otherwise, * the replacement symbool is set to nullptr, and the type is set to * NoReplacement. No other validation is performed. * * The query is set to operand ObjectQuery::OperatorSearch and the tag value * is set to the given pattern. */ static SymbolRuleSet loadCrt(QTextStream& stream, const Map& replacement_map); /** * Writes rules to a cross reference table (CRT) stream. * * Entries of type NoAssignment are skipped. * * \see loadCrt */ void writeCrt(QTextStream& stream) const; /** * Applies the matching rules to the object. * * This operator can be used with Map::applyOnAllObjects etc. * * Normally, you don't want to call this unless the new symbols are already * part of the object's map. Note that for efficiency, this should be * called on a squeezed() map. * * \see apply */ void operator()(Object* object) const; /** * Options for importing of new colors and symbols in to a map. */ enum Option { ImportAllSymbols = 0x01, PreserveSymbolState = 0x02, KeepUnusedSymbols = 0x04, KeepUnusedColors = 0x08, }; Q_DECLARE_FLAGS(Options, Option) /** * Adds colors and symbols from the symbol map to the object map, * and applies the rules. * * Note that for efficiency, this should be called on a squeezed() map. */ void apply(Map& object_map, const Map& symbol_set, Options options = 0); }; } // namespace OpenOrienteering Q_DECLARE_OPERATORS_FOR_FLAGS(OpenOrienteering::SymbolRuleSet::Options) #endif // OPENORIENTEERING_SYMBOL_RULE_SET_H mapper-0.8.1.1/src/core/objects/text_object.cpp000066400000000000000000000351201325266516600213460ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2012, 2014, 2015 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "text_object.h" #include #include #include #include #include "settings.h" #include "core/objects/object.h" #include "core/symbols/text_symbol.h" #include "core/symbols/symbol.h" // IWYU pragma: no_forward_declare QPointF namespace OpenOrienteering { // ### TextObjectPartInfo ### int TextObjectPartInfo::getIndex(double pos_x) const { int left = 0; int right = part_text.length(); while (right != left) { int middle = (left + right) / 2; double x = part_x + metrics.width(part_text.left(middle)); if (pos_x >= x) { if (middle >= right) return right; double next = part_x + metrics.width(part_text.left(middle + 1)); if (pos_x < next) if (pos_x < (x + next) / 2) return middle; else return middle + 1; else left = middle + 1; } else // if (point.x() < x) { if (middle <= 0) return 0; double prev = part_x + metrics.width(part_text.left(middle - 1)); if (pos_x > prev) if (pos_x > (x + prev) / 2) return middle; else return middle - 1; else right = middle - 1; } } return right; } // ### TextObjectLineInfo ### double TextObjectLineInfo::getX(int index) const { int num_parts = part_infos.size(); int i = 0; for ( ; i < num_parts; i++) { const TextObjectPartInfo& part(part_infos.at(i)); if (index <= part.end_index) return part.getX(index); } return line_x + width; } int TextObjectLineInfo::getIndex(double pos_x) const { // TODO: evaluate std::vector::iterator it; int num_parts = part_infos.size(); for (int i=0; i < num_parts; i++) { if (part_infos.at(i).part_x > pos_x) { if (i==0) // before first part return start_index; else if (part_infos.at(i-1).part_x + part_infos.at(i-1).width < pos_x) { // between parts return (pos_x - (part_infos.at(i-1).part_x + part_infos.at(i-1).width) < part_infos.at(i).part_x - pos_x) ? part_infos.at(i-1).end_index : part_infos.at(i).start_index; } else // inside part return part_infos.at(i-1).start_index + part_infos.at(i-1).getIndex(pos_x); } } return part_infos.back().start_index + part_infos.at(num_parts-1).getIndex(pos_x); } // ### TextObject ### TextObject::TextObject(const Symbol* symbol) : Object(Object::Text, symbol) , h_align(AlignHCenter) , v_align(AlignVCenter) , rotation(0.0f) { Q_ASSERT(!symbol || (symbol->getType() == Symbol::Text)); coords.reserve(2); // Extra element used during saving coords.push_back(MapCoord(0, 0)); } TextObject::TextObject(const TextObject& proto) : Object(proto) , text(proto.text) , h_align(proto.h_align) , v_align(proto.v_align) , rotation(proto.rotation) , has_single_anchor(proto.has_single_anchor) , size(proto.size) , line_infos(proto.line_infos) { // nothing } TextObject* TextObject::duplicate() const { return new TextObject(*this); } void TextObject::copyFrom(const Object& other) { if (&other == this) return; Object::copyFrom(other); const TextObject& other_text = *other.asText(); text = other_text.text; h_align = other_text.h_align; v_align = other_text.v_align; rotation = other_text.rotation; has_single_anchor = other_text.has_single_anchor; size = other_text.size; line_infos = other_text.line_infos; } void TextObject::setAnchorPosition(qint32 x, qint32 y) { has_single_anchor = true; coords[0].setNativeX(x); coords[0].setNativeY(y); setOutputDirty(); } void TextObject::setAnchorPosition(MapCoord coord) { has_single_anchor = true; coords[0] = coord; setOutputDirty(); } void TextObject::setAnchorPosition(MapCoordF coord) { has_single_anchor = true; coords[0].setX(coord.x()); coords[0].setY(coord.y()); setOutputDirty(); } MapCoordF TextObject::getAnchorCoordF() const { return MapCoordF(coords[0]); } void TextObject::transform(const QTransform& t) { if (t.isIdentity()) return; auto& coord = coords.front(); const auto p = t.map(MapCoordF{coord}); coord.setX(p.x()); coord.setY(p.y()); setOutputDirty(); } void TextObject::setBox(qint32 mid_x, qint32 mid_y, qreal width, qreal height) { has_single_anchor = false; coords[0].setNativeX(mid_x); coords[0].setNativeY(mid_y); size = {width, height}; setOutputDirty(); } void TextObject::setBoxSize(const MapCoord& size) { has_single_anchor = false; this->size = size; setOutputDirty(); } std::vector TextObject::controlPoints() const { auto anchor = getAnchorCoordF(); std::vector handles(4, anchor); if (hasSingleAnchor()) { handles.resize(1); } else { QTransform transform; transform.rotate(-qRadiansToDegrees(qreal(getRotation()))); handles[0] += transform.map(QPointF(+getBoxWidth() / 2, -getBoxHeight() / 2)); handles[1] += transform.map(QPointF(+getBoxWidth() / 2, +getBoxHeight() / 2)); handles[2] += transform.map(QPointF(-getBoxWidth() / 2, +getBoxHeight() / 2)); handles[3] += transform.map(QPointF(-getBoxWidth() / 2, -getBoxHeight() / 2)); } return handles; } void TextObject::scale(MapCoordF center, double factor) { coords.front() = MapCoord{center + (MapCoordF{coords.front()} - center) * factor}; if (!has_single_anchor) size *= factor; setOutputDirty(); } void TextObject::scale(double factor_x, double factor_y) { auto& coord = coords.front(); coord.setX(coord.x() * factor_x); coord.setY(coord.y() * factor_y); if (!has_single_anchor) { size.setX(size.x() * factor_x); size.setY(size.y() * factor_y); } setOutputDirty(); } QTransform TextObject::calcTextToMapTransform() const { const TextSymbol* text_symbol = reinterpret_cast(symbol); QTransform transform; double scaling = 1.0f / text_symbol->calculateInternalScaling(); transform.translate(coords[0].x(), coords[0].y()); if (rotation != 0) transform.rotate(-rotation * 180 / M_PI); transform.scale(scaling, scaling); return transform; } QTransform TextObject::calcMapToTextTransform() const { const TextSymbol* text_symbol = reinterpret_cast(symbol); QTransform transform; double scaling = 1.0f / text_symbol->calculateInternalScaling(); transform.scale(1.0f / scaling, 1.0f / scaling); if (rotation != 0) transform.rotate(rotation * 180 / M_PI); transform.translate(-coords[0].x(), -coords[0].y()); return transform; } void TextObject::setText(const QString& text) { this->text = text; this->text.remove(QLatin1Char('\r')); setOutputDirty(); } void TextObject::setHorizontalAlignment(TextObject::HorizontalAlignment h_align) { this->h_align = h_align; setOutputDirty(); } void TextObject::setVerticalAlignment(TextObject::VerticalAlignment v_align) { this->v_align = v_align; setOutputDirty(); } void TextObject::setRotation(float new_rotation) { rotation = new_rotation; setOutputDirty(); } bool TextObject::intersectsBox(const QRectF& box) const { return getExtent().intersects(box); } int TextObject::calcTextPositionAt(MapCoordF coord, bool find_line_only) const { return calcTextPositionAt(calcMapToTextTransform().map(coord), find_line_only); } // FIXME actually this is two functions, selected by parameter find_line_only; make two functions or return TextObjectLineInfo reference int TextObject::calcTextPositionAt(QPointF point, bool find_line_only) const { auto click_tolerance = Settings::getInstance().getMapEditorClickTolerancePx(); for (int line = 0; line < getNumLines(); ++line) { const TextObjectLineInfo* line_info = getLineInfo(line); if (line_info->line_y - line_info->ascent > point.y()) return -1; // NOTE: Only true as long as every line has a bigger or equal y value than the line before if (point.x() < line_info->line_x - click_tolerance) continue; if (point.y() > line_info->line_y + line_info->descent) continue; if (point.x() > line_info->line_x + line_info->width + click_tolerance) continue; // Position in the line rect. if (find_line_only) return line; else return line_info->getIndex(point.x()); } return -1; } int TextObject::findLineForIndex(int index) const { int line_num = 0; for (int line = 1; line < getNumLines(); ++line) { const TextObjectLineInfo* line_info = getLineInfo(line); if (index < line_info->start_index) break; line_num = line; } return line_num; } const TextObjectLineInfo& TextObject::findLineInfoForIndex(int index) const { const TextObjectLineInfo* line_info = getLineInfo(0); for (int line = 1; line < getNumLines(); ++line) { const TextObjectLineInfo* next_line_info = getLineInfo(line); if (index < next_line_info->start_index) break; line_info = next_line_info; } return *line_info; } void TextObject::prepareLineInfos() const { const TextSymbol* text_symbol = reinterpret_cast(symbol); double scaling = text_symbol->calculateInternalScaling(); QFontMetricsF metrics = text_symbol->getFontMetrics(); double line_spacing = text_symbol->getLineSpacing() * metrics.lineSpacing(); double paragraph_spacing = scaling * text_symbol->getParagraphSpacing() + (text_symbol->hasLineBelow() ? (scaling * (text_symbol->getLineBelowDistance() + text_symbol->getLineBelowWidth())) : 0); double ascent = metrics.ascent(); bool word_wrap = ! hasSingleAnchor(); double box_width = word_wrap ? (scaling * getBoxWidth()) : 0.0; double box_height = word_wrap ? (scaling * getBoxHeight()) : 0.0; int text_end = text.length(); const QLatin1Char line_break('\n'); const QLatin1Char part_break('\t'); const QLatin1Char word_break(' '); line_infos.clear(); // Initialize offsets double line_x = 0.0; if (h_align == TextObject::AlignLeft) line_x -= 0.5 * box_width; else if (h_align == TextObject::AlignRight) line_x += 0.5 * box_width; double line_y = 0.0; if (v_align == TextObject::AlignTop || v_align == TextObject::AlignBaseline) line_y += -0.5 * box_height; if (v_align != TextObject::AlignBaseline) line_y += ascent; // Determine lines and parts //double next_line_x_offset = 0; // to keep indentation after word wrap in a line with tabs int num_paragraphs = 0; int line_num = 0; int line_start = 0; while (line_start <= text_end) { // Initialize input line double line_width = 0.0; int line_end = text.indexOf(line_break, line_start); if (line_end == -1) line_end = text_end; bool paragraph_end = true; std::vector part_infos; int part_start = line_start; double part_x = line_x; while (part_start <= line_end) { // Initialize part (sequence of letters terminated by tab or line break) int part_end = text.indexOf(part_break, part_start); if (part_end == -1) part_end = line_end; else if (part_end > line_end) part_end = line_end; if (part_start > 0 && text[part_start - 1] == part_break) part_x = line_x + text_symbol->getNextTab(part_x - line_x); QString part = text.mid(part_start, part_end - part_start); double part_width = metrics.boundingRect(part).width(); if (word_wrap) { // shrink overflowing part to maximum possible size while (part_x + part_width - line_x > box_width) { // find latest possible break int new_part_end = text.lastIndexOf(word_break, part_end - 1); if (new_part_end <= part_start) { // part won't fit if (part_start > line_start) { // don't put another part on this line part_end = part_start - 1; paragraph_end = false; } break; } paragraph_end = false; // Shrink the part and the line part_end = new_part_end; part = text.mid(part_start, part_end - part_start); part_width = metrics.width(part); line_end = part_end; } } if (part_end < part_start) break; // Add the current part part_infos.push_back( { part, part_start, part_end, part_x, metrics.width(part), metrics } ); // Advance to next part position part_start = part_end + 1; part_x += part_width; } TextObjectPartInfo& last_part_info = part_infos.back(); line_end = last_part_info.end_index; line_width = last_part_info.part_x + last_part_info.width - line_x; // Jump over whitespace after the end of the line and check if it contains a newline character to determine if it is a paragraph end int next_line_start = line_end + 1; /*while (next_line_start < text.size() && (text[next_line_start] == line_break || text[next_line_start] == part_break || text[next_line_start] == word_break)) { if (text[next_line_start - 1] == line_break) { paragraph_end = true; break; } ++next_line_start; }*/ line_infos.push_back( { line_start, line_end, paragraph_end, line_x, line_y, line_width, metrics.ascent(), metrics.descent(), part_infos } ); // Advance to next line line_y += line_spacing; if (paragraph_end) { line_y += paragraph_spacing; num_paragraphs++; } line_num++; line_start = next_line_start; } // Update the line and part offset for every other alignment than top-left or baseline-left double delta_y = 0.0; if (v_align == TextObject::AlignBottom || v_align == TextObject::AlignVCenter) { int num_lines = getNumLines(); double height = ascent + (num_lines - 1) * line_spacing + (num_paragraphs - 1) * paragraph_spacing; if (v_align == TextObject::AlignVCenter) delta_y = -0.5 * height; else if (v_align == TextObject::AlignBottom) delta_y = -height + 0.5 * box_height; } if (delta_y != 0.0 || h_align != TextObject::AlignLeft) { int num_lines = getNumLines(); for (int i = 0; i < num_lines; i++) { TextObjectLineInfo* line_info = &line_infos[i]; double delta_x = 0.0; if (h_align == TextObject::AlignHCenter) delta_x = -0.5 * line_info->width; else if (h_align == TextObject::AlignRight) delta_x -= line_info->width; line_info->line_x += delta_x; line_info->line_y += delta_y; int num_parts = line_info->part_infos.size(); for (int j = 0; j < num_parts; j++) { line_info->part_infos.at(j).part_x += delta_x; } } } } } // namespace OpenOrienteering mapper-0.8.1.1/src/core/objects/text_object.h000066400000000000000000000263101325266516600210140ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2012, 2014, 2015 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_OBJECT_TEXT_H #define OPENORIENTEERING_OBJECT_TEXT_H #include #include #include #include #include #include #include #include #include "core/map_coord.h" #include "core/objects/object.h" // IWYU pragma: no_forward_declare QPointF // IWYU pragma: no_forward_declare QRectF // IWYU pragma: no_forward_declare QTransform namespace OpenOrienteering { class Symbol; /** * TextObjectPartInfo contains layout information for a continuous sequence of printable characters * in a longer text. * * Use the implicit initializer list constructor to create a new object of this class. */ class TextObjectPartInfo { public: QString part_text; /// The sequence of printable characters which makes up this part int start_index; /// The index of the part's first character in the original string int end_index; /// The index of the part's last character in the original string double part_x; /// The left endpoint of the baseline of this part in text coordinates double width; /// The width of the rendered part in text coordinates QFontMetricsF metrics; /// The metrics of the font that is used to render the part /** Get the horizontal position of a particular character in a part. * @param index the index of the character in the original string * @return the character's horizontal position in text coordinates */ double getX(int index) const; /** Find the index of the character corresponding to a particular position. * @param pos_x the position for which the index is requested * @return the character's index in the original string */ int getIndex(double pos_x) const; }; /** TextObjectLineInfo contains layout information for a single line * in a longer text. A line is a sequence of different parts. */ struct TextObjectLineInfo { /** A sequence container of TextObjectPartInfo objects */ typedef std::vector PartInfoContainer; int start_index; /// The index of the part's first character in the original string int end_index; /// The index of the part's last character in the original string bool paragraph_end; /// Is this line the end of a paragraph? double line_x; /// The left endpoint of the baseline of this line in text coordinates double line_y; /// The vertical position of the baseline of this line in text coordinates double width; /// The total width of the text in this line double ascent; /// The height of the rendered text above the baseline double descent; /// The height of the rendered text below the baseline PartInfoContainer part_infos; /// The sequence of parts which make up this line /** Get the horizontal position of a particular character in a line. * @param pos the index of the character in the original string * @return the character's horizontal position in text coordinates */ double getX(int pos) const; /** Find the index of the character corresponding to a particular position. * @param pos_x the position for which the index is requested * @return the character's index in the original string */ int getIndex(double pos_x) const; }; /** A text object. * * A text object is an instance of a text symbol. * Its position may be specified by a single coordinate (the anchor point) * or by two coordinates (word wrap box: * first coordinate specifies the coordinate of the midpoint, * second coordinates specifies the width and height). * * TODO: the way of defining word wrap boxes is inconvenient, as the second * coordinate does not specify a real coordinate in this case, but is misused * as extent. Change this? */ class TextObject : public Object // clazy:exclude=copyable-polymorphic { public: enum HorizontalAlignment { AlignLeft = 0, AlignHCenter = 1, AlignRight = 2 }; enum VerticalAlignment { AlignBaseline = 0, AlignTop = 1, AlignVCenter = 2, AlignBottom = 3 }; /** A sequence container of TextObjectLineInfo objects */ typedef std::vector LineInfoContainer; /** Construct a new text object. * If a symbol is specified, it must be a text symbol. * @param symbol the text symbol (optional) */ explicit TextObject(const Symbol* symbol = nullptr); protected: /** Constructs a TextObject, initalized from the given prototype. */ explicit TextObject(const TextObject& proto); public: /** Creates a duplicate of the text object. * @return a new object with same text, symbol and formatting. */ TextObject* duplicate() const override; TextObject& operator=(const TextObject&) = delete; void copyFrom(const Object& other) override; /** Returns true if the text object has a single anchor, false if it has as word wrap box */ bool hasSingleAnchor() const; /** Sets the position of the anchor point to (x,y). * This will drop an existing word wrap box. */ void setAnchorPosition(qint32 x, qint32 y); /** Sets the position of the anchor point to coord. * This will drop an existing word wrap box. */ void setAnchorPosition(MapCoord coord); /** Sets the position of the anchor point to coord. * This will drop an existing word wrap box. */ void setAnchorPosition(MapCoordF coord); /** Returns the coordinates of the anchor point or midpoint */ MapCoordF getAnchorCoordF() const; void transform(const QTransform& t) override; /** Set position and size. * The midpoint is set to (mid_x, mid_y), the size is specifed by the parameters * width and heigt. */ void setBox(qint32 mid_x, qint32 mid_y, qreal width, qreal height); /** Set size. */ void setBoxSize(const MapCoord& size); /** Returns the size as a MapCoord. */ MapCoord getBoxSize() const { return size; } /** Returns the width of the word wrap box. * The text object must have a specified size. */ qreal getBoxWidth() const; /** Returns the height of the word wrap box. * The text object must have a specified size. */ qreal getBoxHeight() const; /** * @brief Returns the positions of the control points. * * The returned vector may have one or four members, depending on the type * of object. */ std::vector controlPoints() const; /** * Scales position and box, with the given scaling center. */ void scale(MapCoordF center, double factor) override; /** * Scales position and box, with the center (0, 0). */ void scale(double factor_x, double factor_y) override; /** Sets the text of the object. */ void setText(const QString& text); /** Returns the text of the object. */ const QString& getText() const; /** Sets the horizontal alignment of the text. */ void setHorizontalAlignment(HorizontalAlignment h_align); /** Returns the horizontal alignment of the text. */ HorizontalAlignment getHorizontalAlignment() const; /** Sets the vertical alignment of the text. */ void setVerticalAlignment(VerticalAlignment v_align); /** Returns the vertical alignment of the text. */ VerticalAlignment getVerticalAlignment() const; /** Sets the rotation of the text. * The rotation is measured in radians. The center of rotation is the anchor point. */ void setRotation(float new_rotation); /** Returns the rotation of the text. * The rotation is measured in radians. The center of rotation is the anchor point. */ float getRotation() const; bool intersectsBox(const QRectF& box) const override; /** Returns a QTransform from text coordinates to map coordinates. */ QTransform calcTextToMapTransform() const; /** Returns a QTransform from map coordinates to text coordinates. */ QTransform calcMapToTextTransform() const; /** Return the number of rendered lines. * For a text object with a word wrap box, the number of rendered lines * may be higher than the number of explicit line breaks in the original text. */ int getNumLines() const; /** Returns the layout information about a particular line. */ TextObjectLineInfo* getLineInfo(int i); /** Returns the layout information about a particular line. */ const TextObjectLineInfo* getLineInfo(int i) const; /** Return the index of the character or the line number corresponding to a particular map coordinate. * Returns -1 if the coordinate is not at a text position. * If find_line_only is true, the line number is returned, otherwise the index of the character. */ int calcTextPositionAt(MapCoordF coord, bool find_line_only) const; /** Return the index of the character or the line number corresponding to a particular text coordinate. * Returns -1 if the coordinate is not at a text position. * If find_line_only is true, the line number is returned, otherwise the index of the character. */ int calcTextPositionAt(QPointF coord, bool find_line_only) const; /** Returns the line number for a particular index in the text. */ int findLineForIndex(int index) const; /** Returns the line layout information for particular index. */ const TextObjectLineInfo& findLineInfoForIndex(int index) const; /** Prepare the text layout information. */ void prepareLineInfos() const; private: QString text; HorizontalAlignment h_align; VerticalAlignment v_align; float rotation; // 0 to 2*M_PI bool has_single_anchor = true; MapCoord size; /** Information about the text layout. */ mutable LineInfoContainer line_infos; }; //### TextObjectPartInfo inline code ### inline double TextObjectPartInfo::getX(int index) const { return part_x + metrics.width(part_text.left(index - start_index)); } //### TextObject inline code ### inline bool TextObject::hasSingleAnchor() const { return has_single_anchor; } inline qreal TextObject::getBoxWidth() const { Q_ASSERT(!hasSingleAnchor()); return size.x(); } inline qreal TextObject::getBoxHeight() const { Q_ASSERT(!hasSingleAnchor()); return size.y(); } inline const QString&TextObject::getText() const { return text; } inline TextObject::HorizontalAlignment TextObject::getHorizontalAlignment() const { return h_align; } inline TextObject::VerticalAlignment TextObject::getVerticalAlignment() const { return v_align; } inline float TextObject::getRotation() const { return rotation; } inline int TextObject::getNumLines() const { return (int)line_infos.size(); } inline TextObjectLineInfo*TextObject::getLineInfo(int i) { return &line_infos[i]; } inline const TextObjectLineInfo*TextObject::getLineInfo(int i) const { return &line_infos[i]; } } // namespace OpenOrienteering #endif mapper-0.8.1.1/src/core/path_coord.cpp000066400000000000000000000343751325266516600175400ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2012-2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "path_coord.h" #include #include #include #include #include #include "core/map_coord.h" #include "core/virtual_coord_vector.h" #include "core/virtual_path.h" namespace OpenOrienteering { static_assert(std::is_nothrow_default_constructible::value, "PathCoord must be nothrow default constructible."); static_assert(std::is_nothrow_copy_constructible::value, "PathCoord must be nothrow copy constructible."); static_assert(std::is_nothrow_move_constructible::value, "PathCoord must be nothrow move constructible."); static_assert(std::is_nothrow_copy_assignable::value, "PathCoord must be nothrow copy assignable."); static_assert(std::is_nothrow_move_assignable::value, "PathCoord must be nothrow move assignable."); static_assert(std::is_nothrow_destructible::value, "PathCoord must be nothrow destructible."); // ### PathCoord ### void PathCoord::splitBezierCurve(MapCoordF c0, MapCoordF c1, MapCoordF c2, MapCoordF c3, float p, MapCoordF& o0, MapCoordF& o1, MapCoordF& o2, MapCoordF& o3, MapCoordF& o4) { if (p >= 1.0) { o0 = c1; o1 = c2; o2 = c3; o3 = c3; o4 = c3; } else if (p <= 0.0) { o0 = c0; o1 = c0; o2 = c0; o3 = c1; o4 = c2; } else { // The output variables may point to the same storage (if unused), so we // must not access any output identifier after another one was assigned to. auto c12 = c1 + (c2 - c1) * p; o0 = c0 + (c1 - c0) * p; auto tmp_o1 = o0 + (c12 - o0) * p; o4 = c2 + (c3 - c2) * p; o3 = c12 + (o4 - c12) * p; o2 = tmp_o1 + (o3 - tmp_o1) * p; o1 = tmp_o1; } } // ### SplitPathCoord ### MapCoordF SplitPathCoord::tangentVector() const { auto& path_coords = *this->path_coords; auto& flags = path_coords.flags(); auto& coords = path_coords.coords(); auto path_closed = path_coords.isClosed(); std::size_t index = path_coord_index; auto last_index = path_coords.size() - 1; /// Check distances, advance to a significant length, handle closed paths. MapCoordF next = curve_start[0]; if (pos.distanceSquaredTo(next) >= PathCoord::tangentEpsilonSquared()) goto next_found; if (is_curve_start) { next = curve_start[1]; if (pos.distanceSquaredTo(next) >= PathCoord::tangentEpsilonSquared()) goto next_found; } // Search along current edge if (index < last_index && param != 0.0f) { do { ++index; next = path_coords[index].pos; if (pos.distanceSquaredTo(next) >= PathCoord::tangentEpsilonSquared()) goto next_found; } while (path_coords[index].param != 0.0f); } // Attention, switching from PathCoordVector index to MapCoordVectorF index. index = path_coords[index].index; last_index = coords.size() - 1; // Search along control points while (index < last_index) { ++index; next = coords[index]; if (pos.distanceSquaredTo(next) >= PathCoord::tangentEpsilonSquared()) goto next_found; } if (path_closed && path_coord_index > 0) { index = 0; last_index = path_coords[path_coord_index].index; while (index < last_index) { ++index; next = coords[index]; if (pos.distanceSquaredTo(next) >= PathCoord::tangentEpsilonSquared()) goto next_found; } if (flags[index].isCurveStart()) { next = coords[index+1]; if (pos.distanceSquaredTo(next) >= PathCoord::tangentEpsilonSquared()) goto next_found; } // Search along curve Q_ASSERT(index == path_coords[path_coord_index].index); // Attention, switching from MapCoordVectorF index to PathCoordVector index. auto pc = std::lower_bound(std::begin(path_coords), std::begin(path_coords)+path_coord_index, index, PathCoord::indexLessThanValue); index = std::distance(std::begin(path_coords), pc); last_index = path_coord_index; while (index < last_index) { if (pos.distanceSquaredTo(next) >= PathCoord::tangentEpsilonSquared()) goto next_found; ++index; next = path_coords[index].pos; } } // No coordinate is distant enough, so reset next. next = pos; next_found: next -= pos; next.normalize(); MapCoordF prev = curve_end[1]; if (pos.distanceSquaredTo(prev) >= PathCoord::tangentEpsilonSquared()) goto prev_found; if (is_curve_end) { prev = curve_end[0]; if (pos.distanceSquaredTo(prev) >= PathCoord::tangentEpsilonSquared()) goto prev_found; } index = path_coord_index; // Search along curve if (param != 0.0f) { while (path_coords[index].param != 0.0f) { --index; prev = path_coords[index].pos; if (pos.distanceSquaredTo(prev) >= PathCoord::tangentEpsilonSquared()) goto prev_found; } } // Attention, switching from PathCoordVector index to MapCoordVectorF index. index = path_coords[index].index; // Search along control points while (index > 0) { --index; prev = coords[index]; if (pos.distanceSquaredTo(prev) >= PathCoord::tangentEpsilonSquared()) goto prev_found; } last_index = path_coords.size() - 1; if (path_closed && path_coord_index != last_index) { index = path_coords[last_index].index; last_index = path_coords[path_coord_index].index + (flags[path_coord_index].isCurveStart() ? 3 : 1); while (index > last_index) { --index; prev = coords[index]; if (pos.distanceSquaredTo(prev) >= PathCoord::tangentEpsilonSquared()) goto prev_found; } // Search along curve Q_ASSERT(index > path_coords[path_coord_index].index); // Attention, switching from MapCoordVectorF index to PathCoordVector index. auto pc = std::upper_bound(std::begin(path_coords)+path_coord_index, std::end(path_coords)-1, index, PathCoord::valueLessThanIndex); index = std::distance(std::begin(path_coords), pc); last_index = path_coord_index + 1; while (index > last_index) { --index; prev = path_coords[index].pos; if (pos.distanceSquaredTo(prev) >= PathCoord::tangentEpsilonSquared()) goto prev_found; } } prev = pos; prev_found: prev -= pos; prev.normalize(); next -= prev; return next; } // static SplitPathCoord SplitPathCoord::begin(const PathCoordVector& path_coords) { Q_ASSERT(!path_coords.empty()); auto& flags = path_coords.flags(); auto& coords = path_coords.coords(); Q_ASSERT(coords.size() == flags.size()); auto first_index = path_coords.front().index; auto last_index = path_coords.back().index; Q_ASSERT(last_index > first_index); SplitPathCoord split = { coords[first_index], first_index, 0.0f, path_coords.front().clen, &path_coords, 0, false, flags[first_index].isCurveStart(), {}, {} }; if (flags[last_index].isClosePoint()) { split.curve_end[1] = coords[last_index-1]; if (last_index - first_index > 2 && flags[last_index - 3].isCurveStart()) { split.is_curve_end = true; split.curve_end[0] = coords[last_index-2]; } } else { split.curve_end[1] = split.pos; } split.curve_start[0] = coords[first_index+1]; if (split.is_curve_start) { Q_ASSERT(first_index+2 <= last_index); split.curve_start[1] = coords[first_index+2]; } return split; } // static SplitPathCoord SplitPathCoord::end(const PathCoordVector& path_coords) { Q_ASSERT(!path_coords.empty()); auto& flags = path_coords.flags(); auto& coords = path_coords.coords(); Q_ASSERT(coords.size() == flags.size()); auto first_index = path_coords.front().index; auto last_index = path_coords.back().index; Q_ASSERT(last_index > first_index); SplitPathCoord split = { coords[last_index], last_index, 0.0f, path_coords.back().clen, &path_coords, path_coords.size() - 1, false, false, {}, {} }; if (flags[last_index].isClosePoint()) { split.curve_start[0] = coords[first_index+1]; if (flags[first_index].isCurveStart()) { split.is_curve_start = true; split.curve_start[1] = coords[first_index+2]; } } else { split.curve_start[0] = split.pos; } split.curve_end[1] = coords[last_index-1]; if (last_index - first_index > 2 && flags[last_index-3].isCurveStart()) { split.is_curve_end = true; split.curve_end[0] = coords[last_index-2]; } return split; } // static SplitPathCoord SplitPathCoord::at( const PathCoordVector& path_coords, std::vector::size_type path_coord_index ) { Q_ASSERT(path_coord_index < path_coords.size()); auto& flags = path_coords.flags(); auto& coords = path_coords.coords(); Q_ASSERT(coords.size() > 1); Q_ASSERT(coords.size() == flags.size()); auto index = path_coords[path_coord_index].index; SplitPathCoord split = { coords[index], index, path_coords[path_coord_index].param, path_coords[path_coord_index].clen, &path_coords, path_coord_index, false, flags[index].isCurveStart(), {}, {} }; if (index+1 < coords.size()) { split.curve_start[0] = coords[index+1]; if (split.is_curve_start && index+2 < coords.size()) { split.curve_start[1] = coords[index+2]; } else { Q_ASSERT(!split.is_curve_start); } } else { Q_ASSERT(!split.is_curve_start); split.curve_start[0] = split.pos; } if (index >= 1) { split.curve_end[1] = coords[index-1]; if (index >= 3 && flags[index-3].isCurveStart()) { split.curve_end[0] = coords[index-2]; split.is_curve_end = true; } } else { split.curve_end[1] = split.pos; } return split; } // static SplitPathCoord SplitPathCoord::at( length_type length, const SplitPathCoord& first ) { auto& path_coords = *first.path_coords; auto& coords = path_coords.coords(); auto& flags = path_coords.flags(); SplitPathCoord split = first; split.path_coord_index = path_coords.upperBound(length, first.path_coord_index, first.path_coords->size()-1); if (split.path_coord_index > first.path_coord_index) { // New path coordinate, really split.clen = length; const auto& current_coord = path_coords[split.path_coord_index]; const auto& prev_coord = path_coords[split.path_coord_index-1]; const auto curve_length = current_coord.clen - prev_coord.clen; split.is_curve_end = flags[prev_coord.index].isCurveStart(); split.is_curve_start = split.is_curve_end; auto factor = 1.0f; if (qFuzzyCompare(1.0f + length, 1.0f + current_coord.clen) || qFuzzyCompare(1.0f + curve_length, 1.0f)) { // Close match at current path coordinate, // or near-zero curve length. split.pos = current_coord.pos; split.clen = current_coord.clen; split.param = current_coord.param; } else { // Split between path coordinates. factor = qBound(0.0f, (length - prev_coord.clen) / curve_length, 1.0f); auto prev_param = prev_coord.param; auto current_param = current_coord.param; if (current_param == 0.0f) current_param = 1.0f; split.param = prev_param + (current_param - prev_param) * factor; Q_ASSERT(split.param >= 0.0f && split.param <= 1.0f); if (split.param == 1.0f) split.param = 0.0f; } if (!split.is_curve_end) { // Straight split.pos = prev_coord.pos + qreal(factor) * (current_coord.pos - prev_coord.pos); if (current_coord.index > path_coords.front().index) split.curve_end[1] = coords[current_coord.index-1]; // else // leave split.curve_end[1] as copied from first. split.curve_start[0] = current_coord.pos; } else if (split.param == 0.0f) { // At a node, after a curve split.pos = current_coord.pos; if (prev_coord.index == first.index) { // Split in same curve as first split.curve_end[0] = first.curve_start[0]; split.curve_end[1] = first.curve_start[1]; } else { split.curve_end[0] = coords[prev_coord.index+1]; split.curve_end[1] = coords[prev_coord.index+2]; } // curve_start: handled later } else { // In curve Q_ASSERT(split.is_curve_start); Q_ASSERT(split.is_curve_end); auto edge_start = prev_coord.index; Q_ASSERT(edge_start+3 <= path_coords.back().index); if (prev_coord.index == first.index) { auto p = (split.param - first.param) / (1.0f - first.param); Q_ASSERT(p >= 0.0f); Q_ASSERT(p <= 1.0f); PathCoord::splitBezierCurve(first.pos, first.curve_start[0], first.curve_start[1], coords[edge_start+3], p, split.curve_end[0], split.curve_end[1], split.pos, split.curve_start[0], split.curve_start[1]); } else { PathCoord::splitBezierCurve(coords[edge_start], coords[edge_start+1], coords[edge_start+2], coords[edge_start+3], split.param, split.curve_end[0], split.curve_end[1], split.pos, split.curve_start[0], split.curve_start[1]); } } if (split.param == 0.0f) { // Handle curve_start for non-in-bezier splits. split.is_curve_start = flags[current_coord.index].isCurveStart(); if (split.is_curve_start) { split.curve_start[0] = coords[current_coord.index+1]; split.curve_start[1] = coords[current_coord.index+2]; } else if (current_coord.index < path_coords.back().index) { split.curve_start[0] = coords[current_coord.index+1]; } else { /// \todo Handle closed paths. split.curve_start[0] = current_coord.pos; } } else { --split.path_coord_index; } } split.index = path_coords[split.path_coord_index].index; return split; } // Not inline or constexpr, because it is meant to be used by function pointer. bool PathCoord::indexLessThanValue(const PathCoord& coord, size_type value) { return coord.index < value; } // Not inline or constexpr, because it is meant to be used by function pointer. bool PathCoord::valueLessThanIndex(size_type value, const PathCoord& coord) { return value < coord.index; } } // namespace OpenOrienteering mapper-0.8.1.1/src/core/path_coord.h000066400000000000000000000167721325266516600172060ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2012-2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_PATH_COORD_H #define OPENORIENTEERING_PATH_COORD_H #include #include #include "map_coord.h" namespace OpenOrienteering { class PathCoordVector; /** * A PathCoord represents a node in a polygonal approximation of a path. * * Complex paths which may consist of straight edges and curves are processed * into PathCoordVectors, approximating the path with straight edges only. * * Apart from a point on this polygonal path, a PathCoord contains additional * information about that point: * - the index of the original point where the current edge started, * - the relative position on this edge, and * - the length since the start of the path. */ class PathCoord { friend class PathCoordVector; public: /** A reaonably sized unsigned integer type for map coord vector sizes and indexes. */ using size_type = quint32; /** A reaonably precise float type for lengths and distances. */ using length_type = float; /** A reaonably precise float type for relative position in the range [0, 1). */ using param_type = float; /** Position. */ MapCoordF pos; /** MapCoordVector(F) index of the start of the edge which this position belongs to. */ size_type index; /** Relative location of this position on the MapCoordVector edge ([0.0, 1.0)). */ param_type param; /** Cumulative length of the path since the start of the current part. */ length_type clen; /** Default contructor. */ constexpr PathCoord() noexcept; /** Copy constructor. */ constexpr PathCoord(const PathCoord&) noexcept = default; /** Move constructor. */ PathCoord(PathCoord&&) noexcept = default; /** Explicit construction with all member values. */ constexpr PathCoord(MapCoordF pos, size_type index, param_type param, length_type clen) noexcept; /** Assignment operator. */ PathCoord& operator=(const PathCoord&) noexcept = default; /** Move assignment operator. */ PathCoord& operator=(PathCoord&&) noexcept = default; /** * Global position error threshold for approximating bezier curves with straight segments. * * @todo Make bezier error configurable */ static length_type bezierError(); /** * Returns true if the PathCoord's index is lower than value. * * This function can be used for doing a binary search on a sorted container * of PathCoords. * * @see std::lower_bound */ static bool indexLessThanValue(const PathCoord& coord, size_type value); /** * Returns true if the value is lower than the PathCoord's index. * * This function can be used for doing a binary search on a sorted container * of PathCoords. * * @see std::upper_bound */ static bool valueLessThanIndex(size_type value, const PathCoord& coord); /** * Splits a cubic bezier curve. * * The curve made up by the points c0 ... c3 is split up at the relative * position p (0..1). The new intermediate points (between c0 and c3) are * returned in o0 ... o4. * * If not all returned values are needed, it is possible to have a subset of * o0..o4 point to the same memory. */ static void splitBezierCurve( MapCoordF c0, MapCoordF c1, MapCoordF c2, MapCoordF c3, float p, MapCoordF& o0, MapCoordF& o1, MapCoordF& o2, MapCoordF& o3, MapCoordF& o4 ); /** * The minimum required (squared) distance of neighboring nodes which are to * be considered for determining path tangents. */ static constexpr qreal tangentEpsilonSquared(); }; /** * An arbitrary position on a path. * * A SplitPathCoord supports processing paths in connection with PathCoordVector. * It can represent an arbitrary position even between the elements of the * PathCoordVector. Other than PathCoord, it keeps a reference to the * PathCoordVector. It captures additional state such as adjusted curve parameters. * * \see PathCoord */ class SplitPathCoord { public: /** A reaonably precise float type for lengths and distances. */ using length_type = PathCoord::length_type; /** Position. */ MapCoordF pos; /** Index of the cooresponding or preceding map coordinate and flags. */ PathCoord::size_type index; /** Relative location of this position on the MapCoordVector edge ([0.0, 1.0)). */ float param; /** Cumulative length of the path since the start of the current part. */ length_type clen; /** The underlying vector path coordinates. */ const PathCoordVector* path_coords; /** Index of the corresponding or preceding path_coord in a vector. */ std::vector::size_type path_coord_index; /** If true, a bezier edge ends at this split position. */ bool is_curve_end; /** If true, a bezier edge starts at this split position. */ bool is_curve_start; /** If a bezier edge ends here, this will hold the last control points. * * Otherwise, curve_end[1] will hold the preceding coordinate, * or the current coordinate for the start of open paths. */ MapCoordF curve_end[2]; /** If a bezier edge starts here, this will hold the next control points. * * Otherwise, curve_start[0] will hold the next coordinate, * or the current coordinate for the end of open paths */ MapCoordF curve_start[2]; /** * Returns a vector which is a tangent to the path at this position. */ MapCoordF tangentVector() const; /** * Returns a SplitPathCoord at the begin of the given path. */ static SplitPathCoord begin(const PathCoordVector& path_coords); /** * Returns a SplitPathCoord at the end of the given path. */ static SplitPathCoord end(const PathCoordVector& path_coords); /** * Returns a SplitPathCoord at the given PathCoordVector index. */ static SplitPathCoord at( const PathCoordVector& path_coords, std::vector::size_type path_coord_index ); /** * Returns a SplitPathCoord at the given length. * * The search for the position will start at first. */ static SplitPathCoord at( length_type length, const SplitPathCoord& first ); /** * Returns a SplitPathCoord at the given length. */ static SplitPathCoord at( const PathCoordVector& path_coords, length_type length ); }; // ### PathCoord inline code ### constexpr PathCoord::PathCoord() noexcept : PathCoord( {}, 0, 0.0, 0.0) { // Nothing else } constexpr PathCoord::PathCoord(MapCoordF pos, size_type index, param_type param, PathCoord::length_type clen) noexcept : pos { pos } , index { index } , param { param } , clen { clen } { // Nothing else } constexpr qreal PathCoord::tangentEpsilonSquared() { return 0.000625; // App. 0.025 mm distance } // ### SplitPathCoord inline code ### // static inline SplitPathCoord SplitPathCoord::at(const PathCoordVector& path_coords, SplitPathCoord::length_type length) { return at(length, begin(path_coords)); } } // namespace OpenOrienteering #endif mapper-0.8.1.1/src/core/renderables/000077500000000000000000000000001325266516600171645ustar00rootroot00000000000000mapper-0.8.1.1/src/core/renderables/renderable.cpp000066400000000000000000000477571325266516600220170ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2012-2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "renderable.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "core/image_transparency_fixup.h" #include "core/map_color.h" #include "core/map.h" #include "core/objects/object.h" #include "core/symbols/symbol.h" #include "util/util.h" #if defined(Q_OS_ANDROID) && defined(QT_PRINTSUPPORT_LIB) static_assert(false, "This file needs to be modified for correct printing on Android"); #endif namespace OpenOrienteering { /* * The macro MAPPER_OVERPRINTING_CORRECTION allows to select different * implementations of spot color overprinting simulation correction towards * the appearance of colors in normal (non-spot color) output. * * -1: Mapper 0.5.0 correction. * Results in undesired brightening when overprinting halftones. * * 0: No correction. Plain multiply spot color composition. * Results in undesired green from 100% blue on 100% yellow. * * 1: Weak correction. Blends the normal output over the * overprinting simulation with an alpha of 0.125. * * 2: Middle correction. Blends the normal output over the * overprinting simulation with an alpha of 0.25. * * 3: Strong correction. Blends the normal output over the * overprinting simulation with an alpha of 0.5. * * Options 3 and 2 seem to give the best results. Output from option 3 is quite * similar to option -1 (Mapper 0.5.0), but without the undesired brightening. * * Options 1..3 work only as long as the color set and the symbol set are * defined in a way that the raw overprinting simulation output and the normal * output do not differ significantly. */ #ifndef MAPPER_OVERPRINTING_CORRECTION // Default: [new] middle correction #define MAPPER_OVERPRINTING_CORRECTION 2 #endif // ### Renderable ### Renderable::~Renderable() = default; // ### SharedRenderables ### SharedRenderables::~SharedRenderables() { deleteRenderables(); } void SharedRenderables::deleteRenderables() { for (auto renderables = begin(); renderables != end(); ) { for (auto renderable : renderables->second) { delete renderable; } renderables->second.clear(); if (renderables->first.clip_path) renderables = erase(renderables); else ++renderables; } } void SharedRenderables::compact() { for (auto renderables = begin(); renderables != end(); ) { if (renderables->second.empty()) renderables = erase(renderables); else ++renderables; } } // ### ObjectRenderables ### ObjectRenderables::ObjectRenderables(Object& object) : extent(object.extent) { // nothing else } ObjectRenderables::~ObjectRenderables() = default; void ObjectRenderables::draw(int map_color, const QColor& color, QPainter* painter, const RenderConfig& config) const { if (!extent.intersects(config.bounding_box)) return; auto color_renderables = std::find_if(begin(), end(), [map_color](auto item) { return item.first == map_color; }); if (color_renderables == end()) return; const QPainterPath initial_clip = clip_path ? *clip_path : painter->clipPath(); const QPainterPath* current_clip = nullptr; painter->save(); for (const auto& config_renderables : *(color_renderables->second)) { const PainterConfig& state = config_renderables.first; if (!state.activate(painter, current_clip, config, color, initial_clip)) continue; for (const auto renderable : config_renderables.second) { if (renderable->intersects(config.bounding_box)) { renderable->render(*painter, config); } } } painter->restore(); } void ObjectRenderables::setClipPath(const QPainterPath* path) { clip_path = path; } void ObjectRenderables::insertRenderable(Renderable* r, const PainterConfig& state) { SharedRenderables::Pointer& container(operator[](state.color_priority)); if (!container) container = new SharedRenderables(); container->operator[](state).push_back(r); if (!clip_path) { if (extent.isValid()) rectInclude(extent, r->getExtent()); else extent = r->getExtent(); } } void ObjectRenderables::clear() { for (auto& renderables : *this) { renderables.second->clear(); } } void ObjectRenderables::takeRenderables() { for (auto& color : *this) { auto new_container = new SharedRenderables(); // Pre-allocate as much space as in the original container for (const auto& renderables : *color.second) { (*new_container)[renderables.first].reserve(renderables.second.size()); } color.second = new_container; } } void ObjectRenderables::deleteRenderables() { for (auto& color : *this) { color.second->deleteRenderables(); } } // ### MapRenderables ### void MapRenderables::ObjectDeleter::operator()(Object* object) const { renderables.removeRenderablesOfObject(object, false); delete object; } MapRenderables::MapRenderables(Map* map) : map(map) { ; // nothing } void MapRenderables::draw(QPainter *painter, const RenderConfig &config) const { // TODO: improve performance by using some spatial acceleration structure? #ifdef Q_OS_ANDROID const qreal min_dimension = 1.0/config.scaling; #endif QPainterPath initial_clip = painter->clipPath(); const QPainterPath* current_clip = nullptr; painter->save(); auto end_of_colors = rend(); auto color = rbegin(); while (color != end_of_colors && color->first >= map->getNumColors()) { ++color; } for (; color != end_of_colors; ++color) { if ( config.testFlag(RenderConfig::RequireSpotColor) && (color->first < 0 || map->getColor(color->first)->getSpotColorMethod() == MapColor::UndefinedMethod) ) { continue; } for (const auto& object : color->second) { // Settings check const Symbol* symbol = object.first->getSymbol(); if (!config.testFlag(RenderConfig::HelperSymbols) && symbol->isHelperSymbol()) continue; if (symbol->isHidden()) continue; if (!object.first->getExtent().intersects(config.bounding_box)) continue; for (const auto& renderables : *object.second) { // Render the renderables const PainterConfig& state = renderables.first; const MapColor* map_color = map->getColor(state.color_priority); if (!map_color) { Q_ASSERT(state.color_priority == MapColor::Reserved); continue; // in release build } QColor color = *map_color; if (state.color_priority >= 0 && map_color->getOpacity() < 1) color.setAlphaF(map_color->getOpacity()); if (!state.activate(painter, current_clip, config, color, initial_clip)) continue; for (const auto renderable : renderables.second) { #ifdef Q_OS_ANDROID const QRectF& extent = renderable->getExtent(); if (extent.width() < min_dimension && extent.height() < min_dimension) continue; #endif if (renderable->intersects(config.bounding_box)) { renderable->render(*painter, config); } } } // each common render attributes } // each object } // each map color painter->restore(); } void MapRenderables::drawOverprintingSimulation(QPainter* painter, const RenderConfig& config) const { // NOTE: painter must be a QPainter on a QImage of Format_ARGB32_Premultiplied. QImage* image = static_cast(painter->device()); ImageTransparencyFixup image_fixup(image); QPainter::RenderHints hints = painter->renderHints(); QTransform t = painter->worldTransform(); painter->save(); painter->resetTransform(); painter->setCompositionMode(QPainter::CompositionMode_Multiply); // Alternative: CompositionMode_Darken QImage separation(image->size(), QImage::Format_ARGB32_Premultiplied); for (auto map_color = map->color_set->colors.rbegin(); map_color != map->color_set->colors.rend(); map_color++) { if ((*map_color)->getSpotColorMethod() == MapColor::SpotColor) { separation.fill(Qt::GlobalColor(Qt::transparent)); // Collect all halftones and knockouts of a single color QPainter p(&separation); p.setRenderHints(hints); p.setWorldTransform(t, false); drawColorSeparation(&p, config, *map_color, true); p.end(); // Add this separation to the composition with multiplication. painter->setCompositionMode(QPainter::CompositionMode_Multiply); painter->drawImage(0, 0, separation); image_fixup(); #if MAPPER_OVERPRINTING_CORRECTION == -1 // Add some opacity to the multiplication, but not for black, // since halftones (i.e. grey) might unduly lighten the composition. if (static_cast(**map_color) != 0xff000000) { // FIXME: Implement this for Format_ARGB32_Premultiplied, // if efficiently possible. QImage copy = separation.convertToFormat(QImage::Format_ARGB32); QRgb* dest = (QRgb*)copy.bits(); const QRgb* dest_end = dest + copy.byteCount() / sizeof(QRgb); for (QRgb* px = dest; px < dest_end; ++px) { const unsigned int alpha = qAlpha(*px) * ((255-qGray(*px)) << 16) & 0xff000000; *px = alpha | (*px & 0xffffff); } painter->setCompositionMode(QPainter::CompositionMode_SourceOver); painter->drawImage(0, 0, copy); } #endif } } painter->setCompositionMode(QPainter::CompositionMode_SourceOver); #if MAPPER_OVERPRINTING_CORRECTION > 0 separation.fill(Qt::GlobalColor(Qt::transparent)); QPainter p(&separation); p.setRenderHints(hints); p.setWorldTransform(t, false); RenderConfig config_copy = config; config_copy.options |= RenderConfig::RequireSpotColor; draw(&p, config_copy); p.end(); QRgb* dest = reinterpret_cast(separation.bits()); const QRgb* dest_end = dest + separation.byteCount() / sizeof(QRgb); for (QRgb* px = dest; px < dest_end; ++px) { /* Each pixel is a premultipled RGBA, so the alpha value is adjusted * by applying the same factor to all 4 channels (bytes). * Implemented by bitwise operators for efficiency. */ #if MAPPER_OVERPRINTING_CORRECTION == 1 *px = (*px >> 3) & 0x1f1f1f1f; #elif MAPPER_OVERPRINTING_CORRECTION == 2 *px = (*px >> 2) & 0x3f3f3f3f; #else /* MAPPER_OVERPRINTING_CORRECTION == 3 or stronger */ *px = (*px >> 1) & 0x7f7f7f7f; #endif } painter->drawImage(0, 0, separation); #endif painter->restore(); if (config.testFlag(RenderConfig::Screen)) { static MapColor reserved_color(MapColor::Reserved); drawColorSeparation(painter, config, &reserved_color, true); } } void MapRenderables::drawColorSeparation(QPainter* painter, const RenderConfig& config, const MapColor* separation, bool use_color) const { painter->save(); const QPainterPath initial_clip(painter->clipPath()); const QPainterPath* current_clip = nullptr; // As soon as the spot color is actually used for drawing (i.e. drawing_started = true), // we need to take care of knockouts. bool drawing_started = false; // For each pair of color priority and its renderables collection... auto end_of_colors = rend(); auto color = rbegin(); while (color != end_of_colors && color->first >= map->getNumColors()) { ++color; } for (; color != end_of_colors; ++color) { SpotColorComponent drawing_color(map->getColor(color->first), 1.0f); // Check whether the current color [priority] applies to the current separation. if (color->first > MapColor::Reserved) { if (separation->getPriority() == MapColor::Reserved) { // Don't process regular colors for the "Reserved" separation. continue; } switch (drawing_color.spot_color->getSpotColorMethod()) { case MapColor::UndefinedMethod: continue; case MapColor::SpotColor: if (drawing_color.spot_color == separation) { ; // okay } else if (drawing_started && drawing_color.spot_color->getKnockout()) { drawing_color.factor = 0.0f; } else { continue; } break; case MapColor::CustomColor: { // First, check if the renderables draw color to this separation // TODO: Use an efficient data structure to avoid reiterating each time a separation is drawn const SpotColorComponents& components = drawing_color.spot_color->getComponents(); for (const auto& component : components) { if (component.spot_color == separation) { // The renderables do draw the current spot color drawing_color = component; break; } } if (drawing_color.spot_color != separation) { // If the renderables do not explicitly draw color to this separation, // check if they need a knockout. if (drawing_started && drawing_color.spot_color->getKnockout()) { drawing_color = SpotColorComponent(separation, 0.0f); } else { continue; } } break; } default: Q_ASSERT(false); // in development builds continue; // in release build } } else if (separation->getPriority() == MapColor::Reserved) { if (color->first == MapColor::Registration) continue; // treated per spot color else if (color->first == MapColor::Reserved) continue; // never drawn else if (!drawing_color.spot_color) { Q_ASSERT(!"Invalid reserved color!"); // in development build drawing_color.spot_color = Map::getUndefinedColor(); // in release build } painter->setRenderHint(QPainter::Antialiasing, true); } else if (color->first == MapColor::Registration) { // Draw Registration Black as fulltone of regular spot color drawing_color.spot_color = separation; } else { // Don't draw reserved color in regular separation. continue; } // For each pair of object and its renderables [states] for a particular map color... for (const auto& object : color->second) { // Check whether the symbol and object is to be drawn at all. const Symbol* symbol = object.first->getSymbol(); if (!config.testFlag(RenderConfig::HelperSymbols) && symbol->isHelperSymbol()) continue; if (symbol->isHidden()) continue; if (!object.first->getExtent().intersects(config.bounding_box)) continue; // For each pair of common rendering attributes and collection of renderables... for (const auto& renderables : *object.second) { const PainterConfig& state = renderables.first; QColor color = *drawing_color.spot_color; bool drawing = (drawing_color.factor >= 0.0005f); if (!drawing) { if (!drawing_started) continue; color = Qt::white; } else if (use_color) { qreal c, m, y, k; color.getCmykF(&c, &m, &y, &k); color.setCmykF(c*drawing_color.factor, m*drawing_color.factor, y*drawing_color.factor, k*drawing_color.factor, 1.0); } else { color.setCmykF(0.0, 0.0, 0.0, drawing_color.factor, 1.0); } if (!state.activate(painter, current_clip, config, color, initial_clip)) continue; // For each renderable that uses the current painter configuration... // Render the renderable for (const auto renderable : renderables.second) { if (renderable->intersects(config.bounding_box)) { renderable->render(*painter, config); drawing_started |= drawing; } } } // each common render attributes } // each object } // each map color painter->restore(); } void MapRenderables::insertRenderablesOfObject(const Object* object) { auto end_of_colors = object->renderables().end(); auto color = object->renderables().begin(); for (; color != end_of_colors; ++color) { operator[](color->first)[object] = color->second; } } void MapRenderables::removeRenderablesOfObject(const Object* object, bool mark_area_as_dirty) { for (auto& color : *this) { auto obj = color.second.find(object); if (obj != color.second.end()) { if (mark_area_as_dirty) { // We don't want to loop over every dot in an area ... QRectF extent = object->getExtent(); if (!extent.isValid()) { // ... because here it gets expensive for (const auto& renderables : *obj->second) { for (const auto renderable : renderables.second) { extent = extent.isValid() ? extent.united(renderable->getExtent()) : renderable->getExtent(); } } } map->setObjectAreaDirty(extent); } color.second.erase(obj); } } } void MapRenderables::clear(bool mark_area_as_dirty) { if (mark_area_as_dirty) { for (const auto& color : *this) { for (const auto& object : color.second) { for (const auto& renderables : *object.second) { for (const auto renderable : renderables.second) { map->setObjectAreaDirty(renderable->getExtent()); } } } } } std::map::clear(); } // ### PainterConfig ### namespace { inline QColor highlightedColor(const QColor& original) { const int highlight_alpha = 255; if (original.value() > 127) { const qreal factor = 0.35; return QColor(factor * original.red(), factor * original.green(), factor * original.blue(), highlight_alpha); } else { const qreal factor = 0.15; return QColor(255 - factor * (255 - original.red()), 255 - factor * (255 - original.green()), 255 - factor * (255 - original.blue()), highlight_alpha); } } } bool PainterConfig::activate(QPainter* painter, const QPainterPath*& current_clip, const RenderConfig& config, const QColor& color, const QPainterPath& initial_clip) const { if (current_clip != clip_path) { if (initial_clip.isEmpty()) { if (clip_path) painter->setClipPath(*clip_path, Qt::ReplaceClip); else painter->setClipPath(initial_clip, Qt::NoClip); } else if (clip_path) { /* This used to be a workaround for a Qt::IntersectClip problem * with Windows and Mac printers (cf. [tickets:#196]), and * with Linux PDF export (cf. [tickets:#225]). * But it seems to be faster in general. */ QPainterPath merged = initial_clip.intersected(*clip_path); if (merged.isEmpty()) return false; // outside of initial clip painter->setClipPath(merged, Qt::ReplaceClip); } else { painter->setClipPath(initial_clip, Qt::ReplaceClip); } current_clip = clip_path; } qreal actual_pen_width = pen_width; if (color_priority < 0 && color_priority != MapColor::Registration) { if (color_priority == MapColor::Reserved) return false; if (!config.testFlag(RenderConfig::DisableAntialiasing)) { // this is not undone here anywhere as it should apply to // all special symbols and these are always painted last painter->setRenderHint(QPainter::Antialiasing, true); } actual_pen_width /= config.scaling; } else if (config.testFlag(RenderConfig::DisableAntialiasing)) { painter->setRenderHint(QPainter::Antialiasing, false); painter->setRenderHint(QPainter::TextAntialiasing, false); } QBrush brush(config.testFlag(RenderConfig::Highlighted) ? highlightedColor(color) : color); if (mode == PainterConfig::PenOnly) { #ifdef Q_OS_ANDROID if (pen_width * config.scaling < 0.1) return false; #endif if (config.testFlag(RenderConfig::ForceMinSize) && pen_width * config.scaling <= 1.0) actual_pen_width = 0.0; // Forces cosmetic pen painter->setPen(QPen(brush, actual_pen_width)); painter->setBrush(QBrush(Qt::NoBrush)); } else if (mode == PainterConfig::BrushOnly) { painter->setPen(QPen(Qt::NoPen)); painter->setBrush(brush); } painter->setOpacity(config.opacity); return true; } } // namespace OpenOrienteering mapper-0.8.1.1/src/core/renderables/renderable.h000066400000000000000000000327041325266516600214460ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2012-2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_RENDERABLE_H #define OPENORIENTEERING_RENDERABLE_H #include #include #include #include #include #include #include #include "core/map_color.h" class QColor; class QPainter; class QPainterPath; // IWYU pragma: no_forward_declare QRectF namespace OpenOrienteering { class Map; class Object; class PainterConfig; /** * This class contains rendering configuration values. * * A reference to an object of this class replaces what used to be part of the * parameter list of various draw()/render() methods. With that old approach, * each new rendering configuration option required changes in many signatures * and function calls. In addition, the boolean options were not verbose at all. * * Objects are meant to be initialized by initializer lists (C++11). */ class RenderConfig { public: /** * Flags indicating particular rendering configuration options. */ enum Option { Screen = 1<<0, ///< Indicates that the drawing is for the screen. /// Can turn on optimizations which result in slightly /// lower display quality (e.g. disable antialiasing /// for texts) for the benefit of speed. DisableAntialiasing = 1<<1, ///< Forces disabling of Antialiasing. ForceMinSize = 1<<2, ///< Forces a minimum size of app. 1 pixel for objects. /// Makes maps look better at small zoom levels without antialiasing. HelperSymbols = 1<<3, ///< Activates display of symbols with the "helper symbol" flag. Highlighted = 1<<4, ///< Makes the color appear highlighted. RequireSpotColor = 1<<5, ///< Skips colors which do not have a spot color definition. Tool = Screen | ForceMinSize | HelperSymbols, ///< The recommended flags for tools. NoOptions = 0 ///< No option activated. }; /** * \class RenderConfig::Options * * A combination of flags for rendering configuration options. * * \see QFlags::testFlag(), RenderConfig::Option */ Q_DECLARE_FLAGS(Options, Option) const Map& map; ///< The map. QRectF bounding_box; ///< The bounding box of the area to be drawn. /// Given in map coordinates. qreal scaling; ///< The scaling. /// Used to calculate the final object sizes when /// ForceMinSize is set. Options options; ///< The rendering options. qreal opacity; ///< The opacity. /** * A convenience method for testing flags in the options value. * * \see QFlags::testFlag() */ bool testFlag(const Option flag) const; }; /** * A Renderable is a graphical item with a simple shape and a single color. * * This is the abstract base class. Inheriting classes must implement the * abstract methods, and they must set the extent during construction. */ class Renderable // clazy:exclude=copyable-polymorphic { protected: /** The constructor for new renderables. */ explicit Renderable(const MapColor* color); public: Renderable(const Renderable&) = delete; Renderable(Renderable&&) = delete; /** * The destructor. */ virtual ~Renderable(); Renderable& operator=(const Renderable&) = delete; Renderable& operator=(Renderable&&) = delete; /** * Returns the extent (bounding box). */ const QRectF& getExtent() const; /** * Tests whether the renderable's extent intersects the given rect. */ bool intersects(const QRectF& rect) const; /** * Returns the painter configuration information. * * This configuration must be set when rendering this renderable. */ virtual PainterConfig getPainterConfig(const QPainterPath* clip_path = nullptr) const = 0; /** * Renders the renderable with the given painter and rendering configuration. */ virtual void render(QPainter& painter, const RenderConfig& config) const = 0; protected: /** The color priority is a major attribute and cannot be modified. */ const int color_priority; /** The extent must be set by inheriting classes. */ QRectF extent; }; /** * PainterConfig contains painter configuration information. * * When painting a renderable item, the QPainter shall be configured according * to this information. * * A PainterConfig is an immutable values, constructed with initializer lists. */ class PainterConfig { public: enum PainterMode { BrushOnly = 0, ///< Render using the brush only. PenOnly = 1, ///< Render using the pen only. Reserved = -1 ///< Not used. }; const int color_priority; ///< The color priority which determines rendering order const PainterMode mode; ///< The mode of painting const qreal pen_width; ///< The width of the pen const QPainterPath* clip_path; ///< A clip_path which may be shared by several Renderables /** * Activates the configuration on the given painter. * * If this method returns false, the corresponding renderables shall not be drawn. * * @param painter The painter to be configured. * @param current_clip A pointer which will be set to the address of the current clip, * in order to avoid switching the clip area unneccessarily. * @param config The rendering configurations. * @param color The QColor to be used for the pen or brush. * @param initial_clip The clip which was set initially for this painter. * @return True if the configuration was activated, false if the corresponding renderables shall not be drawn. */ bool activate(QPainter* painter, const QPainterPath*& current_clip, const RenderConfig& config, const QColor& color, const QPainterPath& initial_clip) const; friend bool operator==(const PainterConfig& lhs, const PainterConfig& rhs); friend bool operator<(const PainterConfig& lhs, const PainterConfig& rhs); }; /** * Returns true if the configurations are equal. */ bool operator==(const PainterConfig& lhs, const PainterConfig& rhs); /** * Returns true if the configurations are not equal. */ bool operator!=(const PainterConfig& lhs, const PainterConfig& rhs); /** * Defines an order over values which are not equal. */ bool operator<(const PainterConfig& lhs, const PainterConfig& rhs); /** * A low-level container for renderables. */ typedef std::vector RenderableVector; /** * A shared high-level container for renderables * grouped by common render attributes. * * This shared container can be used in different collections. When the last * reference to this container is dropped, it will delete the renderables. */ class SharedRenderables : public QSharedData, public std::map< PainterConfig, RenderableVector > { public: typedef QExplicitlySharedDataPointer Pointer; SharedRenderables() = default; SharedRenderables(const SharedRenderables&) = delete; SharedRenderables& operator=(const SharedRenderables&) = delete; ~SharedRenderables(); void deleteRenderables(); void compact(); // release memory which is occupied by unused PainterConfig, FIXME: maybe call this regularly... }; /** * A high-level container for all renderables of a single object, * grouped by color priority and common render attributes. */ class ObjectRenderables : protected std::map { friend class MapRenderables; public: ObjectRenderables(Object& object); ObjectRenderables(const ObjectRenderables&) = delete; ObjectRenderables& operator=(const ObjectRenderables&) = delete; ~ObjectRenderables(); inline void insertRenderable(Renderable* r); void insertRenderable(Renderable* r, const PainterConfig& state); void clear(); void deleteRenderables(); void takeRenderables(); /** * Draws all renderables matching the given map color with the given color. * * If map_color is -1, this functions draws all renderables of color which * are not in the list of map colors (i.e. objects with undefined symbol). * Used by FillTool to encode object IDs as colors. */ void draw(int map_color, const QColor& color, QPainter* painter, const RenderConfig& config) const; void setClipPath(const QPainterPath* path); const QPainterPath* getClipPath() const; const QRectF& getExtent() const; private: QRectF& extent; const QPainterPath* clip_path = nullptr; // no memory management here! }; /** * A low-level container for renderables of multiple objects * grouped by object and common render attributes. * * This container uses a smart pointer to the renderable collection * of each single object. */ typedef std::map ObjectRenderablesMap; /** * A high-level container for renderables of multiple objects * grouped by color priority, object and common render attributes. * * This container is able to draw the renderables. */ class MapRenderables : protected std::map { public: /** * An Object deleter which takes care of removing the renderables of the object. * * Synopsis: * std::unique_ptr object { nullptr, { renderables } }; */ class ObjectDeleter { public: MapRenderables& renderables; void operator()(Object* object) const; }; MapRenderables(Map* map); /** * Draws the renderables normally (one opaque over the other). * * @param painter The QPainter used for drawing. * @param config The rendering configuration */ void draw(QPainter* painter, const RenderConfig& config) const; /** * Draws the renderables in a spot color overprinting simulation. * * @param painter Must be a QPainter on a QImage of Format_ARGB32_Premultiplied. * @param config The rendering configuration */ void drawOverprintingSimulation(QPainter* painter, const RenderConfig& config) const; /** * Draws only the renderables which belong to a particular spot color. * * Separations are normally drawn in levels of gray where black means * full tone of the spot color. The parameter use_color can be used to * draw in the actual spot color instead. * * @param painter The QPainter used for drawing. * @param config The rendering configuration * @param separation The spot color to draw the separation for. * @param use_color If true, forces the separation to be drawn in its actual color. */ void drawColorSeparation(QPainter* painter, const RenderConfig& config, const MapColor* separation, bool use_color = false) const; void insertRenderablesOfObject(const Object* object); /* NOTE: does not delete the renderables, just removes them from display */ void removeRenderablesOfObject(const Object* object, bool mark_area_as_dirty); void clear(bool mark_area_as_dirty = false); inline bool empty() const; private: Map* const map; }; // ### RenderConfig ### inline bool RenderConfig::testFlag(const RenderConfig::Option flag) const { return options.testFlag(flag); } // ### Renderable ### inline Renderable::Renderable(const MapColor* color) : color_priority(color ? color->getPriority() : MapColor::Reserved) { ; // nothing } inline const QRectF&Renderable::getExtent() const { return extent; } inline bool Renderable::intersects(const QRectF& rect) const { return extent.intersects(rect); } // ### PainterConfig ### inline bool operator==(const PainterConfig& lhs, const PainterConfig& rhs) { return (lhs.color_priority == rhs.color_priority) && (lhs.mode == rhs.mode) && (lhs.pen_width == rhs.pen_width || lhs.mode == PainterConfig::BrushOnly) && (lhs.clip_path != rhs.clip_path); } inline bool operator!=(const PainterConfig& lhs, const PainterConfig& rhs) { return !(lhs == rhs); } inline bool operator<(const PainterConfig& lhs, const PainterConfig& rhs) { // First, decide by priority if (lhs.color_priority != rhs.color_priority) return lhs.color_priority > rhs.color_priority; // Same priority, decide by clip path else if (lhs.clip_path != rhs.clip_path) return lhs.clip_path > rhs.clip_path; // Same clip path, decide by mode else if ((int)lhs.mode != (int)rhs.mode) return (int)lhs.mode > (int)rhs.mode; // Same mode, decide by pen width else return lhs.pen_width < rhs.pen_width; } // ### ObjectRenderables ### inline void ObjectRenderables::insertRenderable(Renderable* r) { insertRenderable(r, r->getPainterConfig(clip_path)); } inline const QPainterPath* ObjectRenderables::getClipPath() const { return clip_path; } inline const QRectF &ObjectRenderables::getExtent() const { return extent; } // ### MapRenderables ### inline bool MapRenderables::empty() const { return std::map::empty(); } } // namespace OpenOrienteering Q_DECLARE_OPERATORS_FOR_FLAGS(OpenOrienteering::RenderConfig::Options) #endif mapper-0.8.1.1/src/core/renderables/renderable_implementation.cpp000066400000000000000000000533651325266516600251140ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2012-2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "renderable_implementation.h" #include #include #include #include #include #include #include #include #include #include #include #include #include // IWYU pragma: no_include #include "settings.h" #include "core/map_coord.h" #include "core/virtual_coord_vector.h" #include "core/virtual_path.h" #include "core/objects/object.h" #include "core/objects/text_object.h" #include "core/renderables/renderable.h" #include "core/symbols/area_symbol.h" #include "core/symbols/line_symbol.h" #include "core/symbols/point_symbol.h" #include "core/symbols/text_symbol.h" #include "util/util.h" #ifdef QT_PRINTSUPPORT_LIB # include "advanced_pdf_printer.h" #endif // IWYU pragma: no_forward_declare QFontMetricsF namespace { /** * When painting to a PDF engine, the miter limit must be adjusted from Qt's * concept to PDF's concept. This should be done in the PDF engine, but this * isn't the case. This may even result in PDF files which are considered * invalid by Reader & Co. * * The PDF miter limit could be precalculated for the Mapper use cases, but * this optimization would be lost when Qt gets fixed. * * Upstream issue: QTBUG-52641 */ inline void fixPenForPdf(QPen& pen, const QPainter& painter) { #ifdef QT_PRINTSUPPORT_LIB auto engine = painter.paintEngine()->type(); if (Q_UNLIKELY(engine == QPaintEngine::Pdf || engine == AdvancedPdfPrinter::paintEngineType())) { const auto miter_limit = pen.miterLimit(); pen.setMiterLimit(qSqrt(1.0 + miter_limit * miter_limit * 4)); } #else Q_UNUSED(pen) Q_UNUSED(painter) #endif } } // namespace namespace OpenOrienteering { // ### DotRenderable ### DotRenderable::DotRenderable(const PointSymbol* symbol, MapCoordF coord) : Renderable(symbol->getInnerColor()) { double x = coord.x(); double y = coord.y(); double radius = (0.001 * symbol->getInnerRadius()); extent = QRectF(x - radius, y - radius, 2 * radius, 2 * radius); } PainterConfig DotRenderable::getPainterConfig(const QPainterPath* clip_path) const { return { color_priority, PainterConfig::BrushOnly, 0, clip_path }; } void DotRenderable::render(QPainter &painter, const RenderConfig &config) const { if (config.options.testFlag(RenderConfig::ForceMinSize) && extent.width() * config.scaling < 1.5) painter.drawEllipse(extent.center(), 0.5 / config.scaling, 0.5 * config.scaling); else painter.drawEllipse(extent); } // ### CircleRenderable ### CircleRenderable::CircleRenderable(const PointSymbol* symbol, MapCoordF coord) : Renderable(symbol->getOuterColor()) , line_width(0.001 * symbol->getOuterWidth()) { double x = coord.x(); double y = coord.y(); double radius = (0.001 * symbol->getInnerRadius()) + line_width/2; rect = QRectF(x - radius, y - radius, 2 * radius, 2 * radius); extent = QRectF(rect.x() - 0.5*line_width, rect.y() - 0.5*line_width, rect.width() + line_width, rect.height() + line_width); } PainterConfig CircleRenderable::getPainterConfig(const QPainterPath* clip_path) const { return { color_priority, PainterConfig::PenOnly, line_width, clip_path }; } void CircleRenderable::render(QPainter &painter, const RenderConfig &config) const { if (config.options.testFlag(RenderConfig::ForceMinSize) && rect.width() * config.scaling < 1.5) painter.drawEllipse(rect.center(), 0.5 / config.scaling, 0.5 / config.scaling); else painter.drawEllipse(rect); } // ### LineRenderable ### LineRenderable::LineRenderable(const LineSymbol* symbol, const VirtualPath& virtual_path, bool closed) : Renderable(symbol->getColor()) , line_width(0.001 * symbol->getLineWidth()) { Q_ASSERT(virtual_path.size() >= 2); qreal half_line_width = (color_priority < 0) ? 0 : line_width/2; switch (symbol->getCapStyle()) { case LineSymbol::FlatCap: cap_style = Qt::FlatCap; break; case LineSymbol::RoundCap: cap_style = Qt::RoundCap; break; case LineSymbol::SquareCap: cap_style = Qt::SquareCap; break; case LineSymbol::PointedCap: cap_style = Qt::FlatCap; break; } switch (symbol->getJoinStyle()) { case LineSymbol::BevelJoin: join_style = Qt::BevelJoin; break; case LineSymbol::MiterJoin: join_style = Qt::MiterJoin; break; case LineSymbol::RoundJoin: join_style = Qt::RoundJoin; break; } auto& flags = virtual_path.coords.flags; auto& coords = virtual_path.coords; bool has_curve = false; bool hole = false; bool gap = false; QPainterPath first_subpath; auto i = virtual_path.first_index; path.moveTo(coords[i]); extent = QRectF(coords[i].x(), coords[i].y(), 0.0001, 0.0001); extentIncludeCap(i, half_line_width, false, symbol, virtual_path); for (++i; i <= virtual_path.last_index; ++i) { if (gap) { if (flags[i].isHolePoint()) { gap = false; hole = true; } else if (flags[i].isGapPoint()) { gap = false; if (first_subpath.isEmpty() && closed) { first_subpath = path; path = QPainterPath(); } path.moveTo(coords[i]); extentIncludeCap(i, half_line_width, false, symbol, virtual_path); } continue; } if (hole) { Q_ASSERT(!flags[i].isHolePoint() && "Two hole points in a row!"); if (first_subpath.isEmpty() && closed) { first_subpath = path; path = QPainterPath(); } path.moveTo(coords[i]); extentIncludeCap(i, half_line_width, false, symbol, virtual_path); hole = false; continue; } if (flags[i-1].isCurveStart()) { Q_ASSERT(i < virtual_path.last_index-1); has_curve = true; path.cubicTo(coords[i], coords[i+1], coords[i+2]); i += 2; } else path.lineTo(coords[i]); if (flags[i].isHolePoint()) hole = true; else if (flags[i].isGapPoint()) gap = true; if ((i < virtual_path.last_index && !hole && !gap) || (i == virtual_path.last_index && closed)) extentIncludeJoin(i, half_line_width, symbol, virtual_path); else extentIncludeCap(i, half_line_width, true, symbol, virtual_path); } if (closed) { if (first_subpath.isEmpty()) path.closeSubpath(); else path.connectPath(first_subpath); } // If we do not have the path coords, but there was a curve, calculate path coords. if (has_curve) { // This happens for point symbols with curved lines in them. const auto& path_coords = virtual_path.path_coords; Q_ASSERT(path_coords.front().param == 0.0f); Q_ASSERT(path_coords.back().param == 0.0f); for (auto i = path_coords.size()-1; i > 0; --i) { if (path_coords[i].param != 0.0f) { const auto& pos = path_coords[i].pos; auto to_coord = pos - path_coords[i-1].pos; auto to_next = path_coords[i+1].pos - pos; to_coord.normalize(); to_next.normalize(); auto right = (to_coord + to_next).perpRight(); right.setLength(half_line_width); rectInclude(extent, pos + right); rectInclude(extent, pos - right); } } } Q_ASSERT(extent.right() < 60000000); // assert if bogus values are returned } LineRenderable::LineRenderable(const LineSymbol* symbol, QPointF first, QPointF second) : Renderable(symbol->getColor()) , line_width(0.001 * symbol->getLineWidth()) , cap_style(Qt::FlatCap) , join_style(Qt::MiterJoin) { qreal half_line_width = (color_priority < 0) ? 0 : line_width/2; auto right = MapCoordF(second - first).perpRight(); right.normalize(); right *= half_line_width; extent.setTopLeft(first + right); rectInclude(extent, first - right); rectInclude(extent, second - right); rectInclude(extent, second + right); path.moveTo(first); path.lineTo(second); } void LineRenderable::extentIncludeCap(quint32 i, qreal half_line_width, bool end_cap, const LineSymbol* symbol, const VirtualPath& path) { const auto& coord = path.coords[i]; if (half_line_width < 0.0005) { rectInclude(extent, coord); return; } if (symbol->getCapStyle() == LineSymbol::RoundCap) { rectInclude(extent, QPointF(coord.x() - half_line_width, coord.y() - half_line_width)); rectInclude(extent, QPointF(coord.x() + half_line_width, coord.y() + half_line_width)); return; } auto right = path.calculateTangent(i).perpRight(); right.normalize(); rectInclude(extent, coord + half_line_width * right); rectInclude(extent, coord - half_line_width * right); if (symbol->getCapStyle() == LineSymbol::SquareCap) { auto back = right.perpRight(); if (end_cap) back = -back; rectInclude(extent, coord + half_line_width * (back - right)); rectInclude(extent, coord + half_line_width * (back + right)); } } void LineRenderable::extentIncludeJoin(quint32 i, qreal half_line_width, const LineSymbol* symbol, const VirtualPath& path) { const auto& coord = path.coords[i]; if (half_line_width < 0.0005) { rectInclude(extent, coord); return; } if (symbol->getJoinStyle() == LineSymbol::RoundJoin) { rectInclude(extent, QPointF(coord.x() - half_line_width, coord.y() - half_line_width)); rectInclude(extent, QPointF(coord.x() + half_line_width, coord.y() + half_line_width)); return; } bool ok_to_coord, ok_to_next; MapCoordF to_coord = path.calculateIncomingTangent(i, ok_to_coord); MapCoordF to_next = path.calculateOutgoingTangent(i, ok_to_next); if (!ok_to_next) { if (!ok_to_coord) return; to_next = to_coord; } else if (!ok_to_coord) { to_coord = to_next; } auto r0 = to_coord.perpRight(); r0.setLength(half_line_width); auto r1 = to_next.perpRight(); r1.setLength(half_line_width); auto to_coord_rhs = coord + r0; auto to_coord_lhs = coord - r0; auto to_next_rhs = coord + r1; auto to_next_lhs = coord - r1; if (symbol->getJoinStyle() == LineSymbol::BevelJoin) { rectInclude(extent, to_coord_rhs); rectInclude(extent, to_coord_lhs); rectInclude(extent, to_next_rhs); rectInclude(extent, to_next_lhs); return; } auto limit = line_width * LineSymbol::miterLimit(); to_coord.setLength(limit); to_next.setLength(limit); const auto scaling = to_coord.y() * to_next.x() - to_coord.x() * to_next.y(); if (qIsNull(scaling) || !qIsFinite(scaling)) return; // straight line, no impact on extent // rhs boundary auto p = to_coord_rhs - to_next_rhs; auto factor = (to_next.y() * p.x() - to_next.x() * p.y()) / scaling; if (factor > 1) { // outer boundary, intersection exceeds miter limit rectInclude(extent, to_coord_rhs + to_coord); rectInclude(extent, to_next_rhs - to_next); } else if (factor > 0) { // outer boundary, intersection within miter limit rectInclude(extent, to_coord_rhs + to_coord * factor); } else { // inner boundary rectInclude(extent, to_coord_rhs); rectInclude(extent, to_next_rhs); } // lhs boundary p = to_coord_lhs - to_next_lhs; factor = (to_next.y() * p.x() - to_next.x() * p.y()) / scaling; if (factor > 1) { // outer boundary, intersection exceeds miter limit rectInclude(extent, to_coord_lhs + to_coord); rectInclude(extent, to_next_lhs - to_next); } else if (factor > 0) { // outer boundary, intersection within miter limit rectInclude(extent, to_coord_lhs + to_coord * factor); } else { // inner boundary, catch rare cases rectInclude(extent, to_coord_lhs); rectInclude(extent, to_next_lhs); } } PainterConfig LineRenderable::getPainterConfig(const QPainterPath* clip_path) const { return { color_priority, PainterConfig::PenOnly, line_width, clip_path }; } void LineRenderable::render(QPainter &painter, const RenderConfig &config) const { QPen pen(painter.pen()); pen.setCapStyle(cap_style); pen.setJoinStyle(join_style); if (join_style == Qt::MiterJoin) { pen.setMiterLimit(LineSymbol::miterLimit()); fixPenForPdf(pen, painter); } painter.setPen(pen); // One-time adjustment for line width QRectF bounding_box = config.bounding_box.adjusted(-line_width, -line_width, line_width, line_width); const int count = path.elementCount(); if (count <= 2 || bounding_box.contains(path.controlPointRect())) { // path fully contained painter.drawPath(path); } else { // Manually clip the path with bounding_box, this seems to be faster. // The code splits up the painter path into new paths which intersect // the view rect and renders these only. // NOTE: this does not work correctly with miter joins, but this // should be a minor issue. QPainterPath::Element element = path.elementAt(0); QPainterPath::Element last_element = path.elementAt(count-1); bool path_closed = (element.x == last_element.x) && (element.y == last_element.y); QPainterPath part_path; QPainterPath first_path; bool path_started = false; bool part_finished = false; bool current_part_is_first = bounding_box.contains(element); QPainterPath::Element prev_element = element; for (int i = 1; i < count; ++i) { element = path.elementAt(i); if (element.isLineTo()) { qreal min_x, min_y, max_x, max_y; if (prev_element.x < element.x) { min_x = prev_element.x; max_x = element.x; } else { min_x = element.x; max_x = prev_element.x; } if (prev_element.y < element.y) { min_y = prev_element.y; max_y = element.y; } else { min_y = element.y; max_y = prev_element.y; } if ( min_x <= bounding_box.right() && max_x >= bounding_box.left() && min_y <= bounding_box.bottom() && max_y >= bounding_box.top() ) { if (!path_started) { part_path = QPainterPath(); part_path.moveTo(prev_element.x, prev_element.y); path_started = true; } part_path.lineTo(element.x, element.y); } else if (path_started) { part_finished = true; } else { current_part_is_first = false; } } else if (element.isCurveTo()) { Q_ASSERT(i < count - 2); QPainterPath::Element next_element = path.elementAt(i + 1); QPainterPath::Element end_element = path.elementAt(i + 2); qreal min_x = qMin(prev_element.x, qMin(element.x, qMin(next_element.x, end_element.x))); qreal min_y = qMin(prev_element.y, qMin(element.y, qMin(next_element.y, end_element.y))); qreal max_x = qMax(prev_element.x, qMax(element.x, qMax(next_element.x, end_element.x))); qreal max_y = qMax(prev_element.y, qMax(element.y, qMax(next_element.y, end_element.y))); if ( min_x <= bounding_box.right() && max_x >= bounding_box.left() && min_y <= bounding_box.bottom() && max_y >= bounding_box.top() ) { if (!path_started) { part_path = QPainterPath(); part_path.moveTo(prev_element.x, prev_element.y); path_started = true; } part_path.cubicTo(element.x, element.y, next_element.x, next_element.y, end_element.x, end_element.y); } else if (path_started) { part_finished = true; } else { current_part_is_first = false; } } else if (element.isMoveTo() && path_started) { part_path.moveTo(element.x, element.y); } if (part_finished) { if (current_part_is_first && path_closed) { current_part_is_first = false; first_path = part_path; } else { painter.drawPath(part_path); } path_started = false; part_finished = false; } prev_element = element; } if (path_started) { if (path_closed && !first_path.isEmpty()) part_path.connectPath(first_path); painter.drawPath(part_path); } } // DEBUG: show all control points /*QPen debugPen(QColor(Qt::red)); painter.setPen(debugPen); for (int i = 0; i < path.elementCount(); ++i) { const QPainterPath::Element& e = path.elementAt(i); painter.drawEllipse(QPointF(e.x, e.y), 0.2f, 0.2f); } painter.setPen(pen);*/ } // ### AreaRenderable ### AreaRenderable::AreaRenderable(const AreaSymbol* symbol, const PathPartVector& path_parts) : Renderable(symbol->getColor()) { if (!path_parts.empty()) { auto part = begin(path_parts); if (part->size() > 2) { extent = part->path_coords.calculateExtent(); addSubpath(*part); auto last = end(path_parts); for (++part; part != last; ++part) { rectInclude(extent, part->path_coords.calculateExtent()); addSubpath(*part); } } } Q_ASSERT(extent.right() < 60000000); // assert if bogus values are returned } AreaRenderable::AreaRenderable(const AreaSymbol* symbol, const VirtualPath& path) : Renderable(symbol->getColor()) { extent = path.path_coords.calculateExtent(); addSubpath(path); } void AreaRenderable::addSubpath(const VirtualPath& virtual_path) { auto& flags = virtual_path.coords.flags; auto& coords = virtual_path.coords; Q_ASSERT(!flags.data().empty()); auto i = virtual_path.first_index; path.moveTo(coords[i]); for (++i; i <= virtual_path.last_index; ++i) { if (flags[i-1].isCurveStart()) { Q_ASSERT(i+2 < coords.size()); path.cubicTo(coords[i], coords[i+1], coords[i+2]); i += 2; } else { path.lineTo(coords[i]); } } path.closeSubpath(); } PainterConfig AreaRenderable::getPainterConfig(const QPainterPath* clip_path) const { return { color_priority, PainterConfig::BrushOnly, 0, clip_path }; } void AreaRenderable::render(QPainter &painter, const RenderConfig &/*config*/) const { painter.drawPath(path); // DEBUG: show all control points /*QPen pen(painter.pen()); QBrush brush(painter.brush()); QPen debugPen(QColor(Qt::red)); painter.setPen(debugPen); painter.setBrush(Qt::NoBrush); for (int i = 0; i < path.elementCount(); ++i) { const QPainterPath::Element& e = path.elementAt(i); painter.drawEllipse(QPointF(e.x, e.y), 0.1f, 0.1f); } painter.setPen(pen); painter.setBrush(brush);*/ } // ### TextRenderable ### TextRenderable::TextRenderable(const TextSymbol* symbol, const TextObject* text_object, const MapColor* color, double anchor_x, double anchor_y) : Renderable { color } , anchor_x { anchor_x } , anchor_y { anchor_y } , rotation { 0.0 } , scale_factor { symbol->getFontSize() / TextSymbol::internal_point_size } { path.setFillRule(Qt::WindingFill); // Otherwise, when text and an underline intersect, holes appear const QFont& font(symbol->getQFont()); const QFontMetricsF& metrics(symbol->getFontMetrics()); int num_lines = text_object->getNumLines(); for (int i=0; i < num_lines; i++) { const TextObjectLineInfo* line_info = text_object->getLineInfo(i); double line_y = line_info->line_y; double underline_x0 = 0.0; double underline_y0 = line_info->line_y + metrics.underlinePos(); double underline_y1 = underline_y0 + metrics.lineWidth(); auto num_parts = line_info->part_infos.size(); for (std::size_t j=0; j < num_parts; j++) { const TextObjectPartInfo& part(line_info->part_infos.at(j)); if (font.underline()) { if (j > 0) { // draw underline for gap between parts as rectangle // TODO: watch out for inconsistency between text and gap underline path.moveTo(underline_x0, underline_y0); path.lineTo(part.part_x, underline_y0); path.lineTo(part.part_x, underline_y1); path.lineTo(underline_x0, underline_y1); path.closeSubpath(); } underline_x0 = part.part_x; } path.addText(part.part_x, line_y, font, part.part_text); } } QTransform t { 1.0, 0.0, 0.0, 1.0, anchor_x, anchor_y }; t.scale(scale_factor, scale_factor); auto rotation_rad = qreal(text_object->getRotation()); if (!qIsNull(rotation_rad)) { rotation = -qRadiansToDegrees(rotation_rad); t.rotate(rotation); } extent = t.mapRect(path.controlPointRect()); } PainterConfig TextRenderable::getPainterConfig(const QPainterPath* clip_path) const { return { color_priority, PainterConfig::BrushOnly, 0.0, clip_path }; } void TextRenderable::render(QPainter &painter, const RenderConfig &config) const { painter.save(); renderCommon(painter, config); painter.restore(); } void TextRenderable::renderCommon(QPainter& painter, const RenderConfig& config) const { bool disable_antialiasing = config.options.testFlag(RenderConfig::Screen) && !(Settings::getInstance().getSettingCached(Settings::MapDisplay_TextAntialiasing).toBool()); if (disable_antialiasing) { painter.setRenderHint(QPainter::Antialiasing, false); painter.setRenderHint(QPainter::TextAntialiasing, false); } painter.translate(anchor_x, anchor_y); if (rotation != 0.0) painter.rotate(rotation); painter.scale(scale_factor, scale_factor); painter.drawPath(path); } // ### TextRenderable ### TextFramingRenderable::TextFramingRenderable(const TextSymbol* symbol, const TextObject* text_object, const MapColor* color, double anchor_x, double anchor_y) : TextRenderable { symbol, text_object, color, anchor_x, anchor_y } , framing_line_width { 2 * 0.001 * symbol->getFramingLineHalfWidth() / scale_factor } { auto adjustment = 0.001 * symbol->getFramingLineHalfWidth() ; extent.adjust(-adjustment, -adjustment, +adjustment, +adjustment); } PainterConfig TextFramingRenderable::getPainterConfig(const QPainterPath* clip_path) const { return { color_priority, PainterConfig::PenOnly, framing_line_width, clip_path }; } void TextFramingRenderable::render(QPainter& painter, const RenderConfig& config) const { painter.save(); QPen pen(painter.pen()); pen.setJoinStyle(Qt::MiterJoin); pen.setMiterLimit(0.5); fixPenForPdf(pen, painter); painter.setPen(pen); TextRenderable::renderCommon(painter, config); painter.restore(); } } // namespace OpenOrienteering mapper-0.8.1.1/src/core/renderables/renderable_implementation.h000066400000000000000000000106251325266516600245510ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2012-2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_RENDERABLE_IMPLENTATION_H #define OPENORIENTEERING_RENDERABLE_IMPLENTATION_H #include #include #include #include #include #include "renderable.h" class QPainter; class QPointF; namespace OpenOrienteering { class AreaSymbol; class LineSymbol; class MapColor; class MapCoordF; class PathPartVector; class PointSymbol; class TextObject; class TextSymbol; class VirtualPath; /** Renderable for displaying a filled dot. */ class DotRenderable : public Renderable { public: DotRenderable(const PointSymbol* symbol, MapCoordF coord); void render(QPainter& painter, const RenderConfig& config) const override; PainterConfig getPainterConfig(const QPainterPath* clip_path = nullptr) const override; }; /** Renderable for displaying a circle. */ class CircleRenderable : public Renderable { public: CircleRenderable(const PointSymbol* symbol, MapCoordF coord); void render(QPainter& painter, const RenderConfig& config) const override; PainterConfig getPainterConfig(const QPainterPath* clip_path = nullptr) const override; protected: const qreal line_width; QRectF rect; }; /** Renderable for displaying a line. */ class LineRenderable : public Renderable { public: LineRenderable(const LineSymbol* symbol, const VirtualPath& virtual_path, bool closed); LineRenderable(const LineSymbol* symbol, QPointF first, QPointF second); void render(QPainter& painter, const RenderConfig& config) const override; PainterConfig getPainterConfig(const QPainterPath* clip_path = nullptr) const override; protected: void extentIncludeCap(quint32 i, qreal half_line_width, bool end_cap, const LineSymbol* symbol, const VirtualPath& path); void extentIncludeJoin(quint32 i, qreal half_line_width, const LineSymbol* symbol, const VirtualPath& path); const qreal line_width; QPainterPath path; Qt::PenCapStyle cap_style; Qt::PenJoinStyle join_style; }; /** Renderable for displaying an area. */ class AreaRenderable : public Renderable { public: AreaRenderable(const AreaSymbol* symbol, const PathPartVector& path_parts); AreaRenderable(const AreaSymbol* symbol, const VirtualPath& path); void render(QPainter& painter, const RenderConfig& config) const override; PainterConfig getPainterConfig(const QPainterPath* clip_path = nullptr) const override; inline const QPainterPath* painterPath() const; protected: void addSubpath(const VirtualPath& virtual_path); QPainterPath path; }; /** Renderable for displaying text. */ class TextRenderable : public Renderable { public: TextRenderable(const TextSymbol* symbol, const TextObject* text_object, const MapColor* color, double anchor_x, double anchor_y); PainterConfig getPainterConfig(const QPainterPath* clip_path = nullptr) const override; void render(QPainter& painter, const RenderConfig& config) const override; protected: void renderCommon(QPainter& painter, const RenderConfig& config) const; QPainterPath path; qreal anchor_x; qreal anchor_y; qreal rotation; qreal scale_factor; }; /** Renderable for displaying framing line for text. */ class TextFramingRenderable : public TextRenderable { public: TextFramingRenderable(const TextSymbol* symbol, const TextObject* text_object, const MapColor* color, double anchor_x, double anchor_y); PainterConfig getPainterConfig(const QPainterPath* clip_path = nullptr) const override; void render(QPainter& painter, const RenderConfig& config) const override; protected: qreal framing_line_width; }; // ### AreaRenderable inline code ### const QPainterPath* AreaRenderable::painterPath() const { return &path; } } // namespace OpenOrienteering #endif mapper-0.8.1.1/src/core/storage_location.cpp000066400000000000000000000232541325266516600207440ustar00rootroot00000000000000/* * Copyright 2016 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "storage_location.h" #include #include #include #include #include #ifdef Q_OS_ANDROID #include #include #include #include #include #include #include #include namespace OpenOrienteering { namespace Android { /** * The cache of known locations. */ static std::shared_ptr> locations_cache; /** * Tells the media scanner to register the given file or folder. * * This is required to make files quickly available for transfer via MTP. */ void mediaScannerScanFile(const QString path) { static const auto ACTION_MEDIA_SCANNER_SCAN_FILE = QAndroidJniObject::getStaticObjectField("android/content/Intent", "ACTION_MEDIA_SCANNER_SCAN_FILE"); auto intent = QAndroidJniObject { "android/content/Intent", "(Ljava/lang/String;)V", ACTION_MEDIA_SCANNER_SCAN_FILE.object() }; auto file = QAndroidJniObject { "java/io/File", "(Ljava/lang/String;)V", QAndroidJniObject::fromString(path).object() }; auto uri = QAndroidJniObject::callStaticObjectMethod( "android/net/Uri", "fromFile", "(Ljava/io/File;)Landroid/net/Uri;", file.object()); intent.callObjectMethod("setData", "(Landroid/net/Uri;)Landroid/content/Intent;", uri.object()); auto activity = QtAndroid::androidActivity(); auto context = activity.callObjectMethod( "getApplicationContext", "()Landroid/content/Context;"); context.callMethod("sendBroadcast", "(Landroid/content/Intent;)V", intent.object()); } /** * Returns writable application-specific directories on all external storage volumes. * * These directories are named "Android/data/PACKAGENAME" and use "synthesized * permissions", i.e. apps from the package may write to these folders without needing * explicit permissions. Other apps may read these files. * * Files in this location will be deleted when the app is uninstalled. * * Requires API level 19. * * \see https://developer.android.com/reference/android/content/Context.html#getExternalFilesDirs(java.lang.String) */ std::vector getExternalFilesDirs(jstring* type) { auto activity = QtAndroid::androidActivity(); auto context = activity.callObjectMethod( "getApplicationContext", "()Landroid/content/Context;"); // Get directories with synthesized permissions on all external storage volumes auto external_files_dirs = context.callObjectMethod( "getExternalFilesDirs", "(Ljava/lang/String;)[Ljava/io/File;", type); QAndroidJniEnvironment jni; const auto length = jni->GetArrayLength(external_files_dirs.object()); std::vector locations; locations.reserve(std::size_t(length)); for (auto i = 0; i < length; ++i) { auto location_jni = jni->GetObjectArrayElement(external_files_dirs.object(), i); auto location = QAndroidJniObject{ location_jni }.toString(); const auto warning_path = QString(location + QLatin1String("/README.html")); QFile warning(warning_path); if (warning.open(QIODevice::WriteOnly | QIODevice::Truncate)) { auto android_start = location.indexOf(QLatin1String("/Android/data/")); warning.write("

    \n"); warning.write(StorageLocation::fileHintTextTemplate(StorageLocation::HintApplication).arg(location.mid(android_start+1)).toUtf8()); warning.write("\n

    "); warning.close(); locations.push_back(location); mediaScannerScanFile(warning_path); } } return locations; } /** * Constructs a list of OOMapper folders on secondary storage volumes */ std::vector getLegacySecondaryStorage(const QString& primary_storage, const std::vector& external_files_dirs) { std::vector result; result.reserve(external_files_dirs.size()); for (const auto& dir : external_files_dirs) { if (dir.startsWith(primary_storage)) continue; auto index = dir.indexOf(QLatin1String("Android/data/")); if (index < 0) continue; result.push_back(dir.leftRef(index) + QLatin1String("OOMapper")); } return result; } /** * Constructs the cache of known storage locations. */ std::shared_ptr> buildLocationCache() { std::vector locations_normal; std::vector locations_application; std::vector locations_readonly; // API level 1, single primary external storage auto primary_storage = QAndroidJniObject::callStaticObjectMethod( "android/os/Environment", "getExternalStorageDirectory", "()Ljava/io/File;", nullptr).toString(); // Easy: "OOMapper" folder on primary external storage. // Write access depends on the WRITE_EXTERNAL_STORAGE permission, which // may be revoked by the user since Android 6.0. (Read access depends on the // READ_EXTERNAL_STORAGE permission only when write access is not given.) const QFileInfo primary_storage_oomapper { primary_storage + QLatin1String("/OOMapper") }; if (primary_storage_oomapper.exists()) { const auto path = primary_storage_oomapper.filePath(); if (primary_storage_oomapper.isWritable()) locations_normal.push_back(path); else locations_readonly.push_back(path); mediaScannerScanFile(path); } // Volatile: Application-specific directories on external storage. if (QtAndroid::androidSdkVersion() >= 19) { // API level 19 locations_application = Android::getExternalFilesDirs(nullptr); } // Difficult: "OOMapper" folder on secondary external storage. // Read access, but (normally) no write access. std::vector secondary_storage_paths; const auto env = QProcessEnvironment::systemEnvironment(); auto env_secondary_storage = env.value(QLatin1String("SECONDARY_STORAGE")); if (!env_secondary_storage.isEmpty()) { // Mapper legacy approach, API level < 19 const auto paths = env_secondary_storage.splitRef(QLatin1Char{';'}); secondary_storage_paths.reserve(std::size_t(paths.size())); for (const auto& path : paths) secondary_storage_paths.emplace_back(path + QLatin1String("/OOMapper")); } else if (!locations_application.empty()) { // API level 19 secondary_storage_paths = Android::getLegacySecondaryStorage(primary_storage, locations_application); } for (const auto& path : secondary_storage_paths) { const QFileInfo secondary_storage { path }; if (secondary_storage.exists()) { if (secondary_storage.isWritable()) locations_normal.push_back(path); else locations_readonly.push_back(path); mediaScannerScanFile(path); } } auto locations = std::make_shared>(); locations->reserve(locations_normal.size() + locations_application.size() + locations_readonly.size()); for (const auto& path : locations_normal) locations->emplace_back(path, StorageLocation::HintNormal); for (const auto& path : locations_application) locations->emplace_back(path, StorageLocation::HintApplication); for (const auto& path : locations_readonly) locations->emplace_back(path, StorageLocation::HintReadOnly); return locations; } } // namespace Android } // namespace OpenOrienteering #endif namespace OpenOrienteering { // static std::shared_ptr> StorageLocation::knownLocations() { #ifdef Q_OS_ANDROID auto locations = Android::locations_cache; if (!locations) { locations = Android::buildLocationCache(); Android::locations_cache = locations; } Q_ASSERT(locations); return locations; #else auto locations = std::make_shared>(); auto paths = QStandardPaths::standardLocations(QStandardPaths::DocumentsLocation); locations->reserve(std::size_t(paths.size())); for (const auto& path : paths) { locations->emplace_back(path, HintNormal); } return locations; #endif } void StorageLocation::refresh() { #ifdef Q_OS_ANDROID Android::locations_cache.reset(); #endif } // static QString StorageLocation::fileHintTextTemplate(Hint hint) { switch (hint) { case HintNormal: return tr("'%1' is stored in a regular location."); case HintApplication: return tr("'%1' is located in app storage. The files will be removed when uninstalling the app."); case HintReadOnly: return tr("'%1' is not writable. Changes cannot be saved."); case HintInvalid: return tr("'%1' is not a valid storage location."); } Q_UNREACHABLE(); } } // namespace OpenOrienteering mapper-0.8.1.1/src/core/storage_location.h000066400000000000000000000063141325266516600204070ustar00rootroot00000000000000/* * Copyright 2016 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_STORAGE_LOCATION_H #define OPENORIENTEERING_STORAGE_LOCATION_H #include #include #include #include #include namespace OpenOrienteering { // noexcept since Qt 5.5 constexpr bool qstring_is_nothrow_copy_constructible = std::is_nothrow_copy_constructible::value; constexpr bool qstring_is_nothrow_move_constructible = std::is_nothrow_move_constructible::value; /** * Provides information about document storage locations. */ class StorageLocation { Q_DECLARE_TR_FUNCTIONS(OpenOrienteering::StorageLocation) public: /** Various hints about locations. */ enum Hint { HintNormal, ///< Normal location HintApplication, ///< Location which might get cleaned unexpectedly HintReadOnly, ///< Read-only location HintInvalid ///< Not a valid location at all }; /** Constructs a new location. */ StorageLocation(const QString& path, Hint hint) noexcept; /** Default copy constructor. */ StorageLocation(const StorageLocation&) noexcept(qstring_is_nothrow_copy_constructible) = default; /** Default move constructor. */ StorageLocation(StorageLocation&&) noexcept(qstring_is_nothrow_move_constructible) = default; /** Returns the path of this location. */ QString path() const; /** Returns the hint for this location. */ Hint hint() const; /** Returns the text representing the hint for this location. */ QString hintText() const; /** Returns a text template for giving the hint for the given path. */ static QString fileHintTextTemplate(Hint hint); /** * Returns the known locations for documents. * * The returned shared-ptr will always have an object, even if it is an empty list. */ static std::shared_ptr> knownLocations(); /** Forces a new scan of locations on the next call to knownLocations(). */ static void refresh(); private: const QString m_path; const Hint m_hint; }; inline StorageLocation::StorageLocation(const QString& path, StorageLocation::Hint hint) noexcept : m_path { path } , m_hint { hint } { // nothing else } inline QString StorageLocation::path() const { return m_path; } inline StorageLocation::Hint StorageLocation::hint() const { return m_hint; } inline QString StorageLocation::hintText() const { return fileHintTextTemplate(hint()).arg(path()); } } // namespace OpenOrienteering #endif // OPENORIENTEERING_STORAGE_LOCATION_H mapper-0.8.1.1/src/core/symbols/000077500000000000000000000000001325266516600163665ustar00rootroot00000000000000mapper-0.8.1.1/src/core/symbols/area_symbol.cpp000066400000000000000000000577471325266516600214130ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2012-2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "area_symbol.h" #include #include #include #include #include #include #include #include #include // IWYU pragma: keep #include "core/map.h" #include "core/map_color.h" #include "core/map_coord.h" #include "core/objects/object.h" #include "core/renderables/renderable.h" #include "core/renderables/renderable_implementation.h" #include "core/symbols/line_symbol.h" #include "core/symbols/point_symbol.h" #include "core/symbols/symbol.h" #include "core/virtual_coord_vector.h" #include "util/xml_stream_util.h" class QXmlStreamWriter; // IWYU pragma: no_forward_declare QXmlStreamReader namespace OpenOrienteering { // ### FillPattern ### AreaSymbol::FillPattern::FillPattern() noexcept : type { LinePattern } , flags { Default } , angle { 0 } , line_spacing { 5000 } // 5 mm , line_offset { 0 } , line_color { nullptr } , line_width { 200 } // 0.2 mm , offset_along_line { 0 } , point_distance { 5000 } // 5 mm , point { nullptr } , name {} { // nothing else } #ifndef NO_NATIVE_FILE_FORMAT bool AreaSymbol::FillPattern::load(QIODevice* file, int version, Map* map) { flags = Option::Default; qint32 itype; file->read((char*)&itype, sizeof(qint32)); type = (Type)itype; file->read((char*)&angle, sizeof(float)); if (version >= 4) { bool is_rotatable; file->read((char*)&is_rotatable, sizeof(bool)); setRotatable(is_rotatable); } file->read((char*)&line_spacing, sizeof(int)); if (version >= 3) { file->read((char*)&line_offset, sizeof(int)); file->read((char*)&offset_along_line, sizeof(int)); } if (type == LinePattern) { int color_index; file->read((char*)&color_index, sizeof(int)); line_color = (color_index >= 0) ? map->getColor(color_index) : nullptr; file->read((char*)&line_width, sizeof(int)); } else { file->read((char*)&point_distance, sizeof(int)); bool have_point; file->read((char*)&have_point, sizeof(bool)); if (have_point) { point = new PointSymbol(); if (!point->load(file, version, map)) return false; if (version < 21) point->setRotatable(true); } else point = nullptr; } return true; } #endif void AreaSymbol::FillPattern::save(QXmlStreamWriter& xml, const Map& map) const { XmlElementWriter element { xml, QLatin1String("pattern") }; element.writeAttribute(QLatin1String("type"), type); element.writeAttribute(QLatin1String("angle"), angle); if (auto no_clipping = int(flags & Option::AlternativeToClipping)) element.writeAttribute(QLatin1String("no_clipping"), no_clipping); if (rotatable()) element.writeAttribute(QLatin1String("rotatable"), true); element.writeAttribute(QLatin1String("line_spacing"), line_spacing); element.writeAttribute(QLatin1String("line_offset"), line_offset); element.writeAttribute(QLatin1String("offset_along_line"), offset_along_line); switch (type) { case LinePattern: element.writeAttribute(QLatin1String("color"), map.findColorIndex(line_color)); element.writeAttribute(QLatin1String("line_width"), line_width); break; case PointPattern: element.writeAttribute(QLatin1String("point_distance"), point_distance); if (point) point->save(xml, map); break; } } void AreaSymbol::FillPattern::load(QXmlStreamReader& xml, const Map& map, SymbolDictionary& symbol_dict) { Q_ASSERT (xml.name() == QLatin1String("pattern")); XmlElementReader element { xml }; type = element.attribute(QLatin1String("type")); angle = element.attribute(QLatin1String("angle")); flags = Options{element.attribute(QLatin1String("no_clipping")) & Option::AlternativeToClipping}; if (element.attribute(QLatin1String("rotatable"))) flags |= Option::Rotatable; line_spacing = element.attribute(QLatin1String("line_spacing")); line_offset = element.attribute(QLatin1String("line_offset")); offset_along_line = element.attribute(QLatin1String("offset_along_line")); switch (type) { case LinePattern: line_color = map.getColor(element.attribute(QLatin1String("color"))); line_width = element.attribute(QLatin1String("line_width")); break; case PointPattern: point_distance = element.attribute(QLatin1String("point_distance")); while (xml.readNextStartElement()) { if (xml.name() == QLatin1String("symbol")) point = static_cast(Symbol::load(xml, map, symbol_dict)); else xml.skipCurrentElement(); } break; } } bool AreaSymbol::FillPattern::equals(const AreaSymbol::FillPattern& other, Qt::CaseSensitivity case_sensitivity) const { if (type != other.type) return false; if (qAbs(angle - other.angle) > 1e-05f) return false; if (flags != other.flags) return false; if (line_spacing != other.line_spacing) return false; if (line_offset != other.line_offset) return false; if (type == PointPattern) { if (offset_along_line != other.offset_along_line) return false; if (point_distance != other.point_distance) return false; if (bool(point) != bool(other.point)) return false; if (point && !point->equals(other.point, case_sensitivity)) return false; } else if (type == LinePattern) { if (!MapColor::equal(line_color, other.line_color)) return false; if (line_width != other.line_width) return false; } if (name.compare(other.name, case_sensitivity) != 0) return false; return true; } void AreaSymbol::FillPattern::setRotatable(bool value) { flags = value ? (flags | Option::Rotatable) : (flags & ~Option::Rotatable); } void AreaSymbol::FillPattern::setClipping(Options clipping) { flags = (flags & ~Option::AlternativeToClipping) | (clipping & Option::AlternativeToClipping); } void AreaSymbol::FillPattern::colorDeleted(const MapColor* color) { switch (type) { case FillPattern::PointPattern: point->colorDeleted(color); break; case FillPattern::LinePattern: if (line_color == color) line_color = nullptr; break; } } bool AreaSymbol::FillPattern::containsColor(const MapColor* color) const { switch (type) { case FillPattern::PointPattern: return point && point->containsColor(color); case FillPattern::LinePattern: return line_color == color; } Q_UNREACHABLE(); } const MapColor* AreaSymbol::FillPattern::guessDominantColor() const { const MapColor* color = nullptr; switch (type) { case FillPattern::PointPattern: if (point) color = point->guessDominantColor(); break; case FillPattern::LinePattern: color = line_color; break; } return color; } template <> inline void AreaSymbol::FillPattern::createLine( MapCoordF first, MapCoordF second, qreal, LineSymbol* line, float, const AreaRenderable&, ObjectRenderables& output ) const { // out of inlining output.insertRenderable(new LineRenderable(line, first, second)); } template <> inline void AreaSymbol::FillPattern::createLine( MapCoordF first, MapCoordF second, qreal delta_offset, LineSymbol*, float rotation, const AreaRenderable& outline, ObjectRenderables& output ) const { // out of inlining createPointPatternLine(first, second, delta_offset, rotation, outline, output); } // This template will be instantiated in non-template createRenderables() // once for each type of pattern, thus duplicating the complex body. // This is by intention, in order to let the compiler optimize each // instantiation independently, with regard to unused parameters in // createLine(), and to eliminate any runtime checks for pattern type // outside of non-template createRenderables(). template void AreaSymbol::FillPattern::createRenderables( const AreaRenderable& outline, float delta_rotation, const MapCoord& pattern_origin, const QRectF& point_extent, LineSymbol* line, qreal rotation, ObjectRenderables& output ) const { auto extent = outline.getExtent(); extent.adjust(-point_extent.right(), -point_extent.bottom(), -point_extent.left(), -point_extent.top()); MapCoordF first, second; // Fill qreal delta_line_offset = 0; qreal delta_along_line_offset = 0; if (rotatable()) { MapCoordF line_normal(0, -1); line_normal.rotate(rotation); line_normal.setY(-line_normal.y()); delta_line_offset = MapCoordF::dotProduct(line_normal, MapCoordF(pattern_origin)); MapCoordF line_tangent(1, 0); line_tangent.rotate(rotation); line_tangent.setY(-line_tangent.y()); delta_along_line_offset = MapCoordF::dotProduct(line_tangent, MapCoordF(pattern_origin)); } auto line_spacing_f = 0.001*line_spacing; const auto offset = 0.001 * line_offset + delta_line_offset; if (qAbs(rotation - M_PI/2) < 0.0001) { // Special case: vertical lines delta_along_line_offset = -delta_along_line_offset; double first_offset = offset + ceil((extent.left() - offset) / line_spacing_f) * line_spacing_f; for (double cur = first_offset; cur < extent.right(); cur += line_spacing_f) { first = MapCoordF(cur, extent.top()); second = MapCoordF(cur, extent.bottom()); createLine(first, second, delta_along_line_offset, line, delta_rotation, outline, output); } } else if (qAbs(rotation - 0) < 0.0001) { // Special case: horizontal lines double first_offset = offset + ceil((extent.top() - offset) / line_spacing_f) * line_spacing_f; for (double cur = first_offset; cur < extent.bottom(); cur += line_spacing_f) { first = MapCoordF(extent.left(), cur); second = MapCoordF(extent.right(), cur); createLine(first, second, delta_along_line_offset, line, delta_rotation, outline, output); } } else { // General case if (rotation < M_PI / 2) delta_along_line_offset = -delta_along_line_offset; auto xfactor = 1.0 / sin(rotation); auto yfactor = 1.0 / cos(rotation); auto dist_x = xfactor * line_spacing_f; auto dist_y = yfactor * line_spacing_f; auto offset_x = xfactor * offset; auto offset_y = yfactor * offset; if (rotation < M_PI/2) { // Start with the upper left corner offset_x += (-extent.top()) / tan(rotation); offset_y -= extent.left() * tan(rotation); auto start_x = offset_x + ceil((extent.x() - offset_x) / dist_x) * dist_x; auto start_y = extent.top(); auto end_x = extent.left(); auto end_y = offset_y + ceil((extent.y() - offset_y) / dist_y) * dist_y; do { // Correct coordinates if (start_x > extent.right()) { start_y += ((start_x - extent.right()) / dist_x) * dist_y; start_x = extent.right(); } if (end_y > extent.bottom()) { end_x += ((end_y - extent.bottom()) / dist_y) * dist_x; end_y = extent.bottom(); } if (start_y > extent.bottom()) break; // Create the renderable(s) first = MapCoordF(start_x, start_y); second = MapCoordF(end_x, end_y); createLine(first, second, delta_along_line_offset, line, delta_rotation, outline, output); // Move to next position start_x += dist_x; end_y += dist_y; } while (true); } else { // Start with left lower corner offset_x += (-extent.bottom()) / tan(rotation); offset_y -= extent.x() * tan(rotation); auto start_x = offset_x + ceil((extent.x() - offset_x) / dist_x) * dist_x; auto start_y = extent.bottom(); auto end_x = extent.x(); auto end_y = offset_y + ceil((extent.bottom() - offset_y) / dist_y) * dist_y; do { // Correct coordinates if (start_x > extent.right()) { start_y += ((start_x - extent.right()) / dist_x) * dist_y; start_x = extent.right(); } if (end_y < extent.y()) { end_x += ((end_y - extent.y()) / dist_y) * dist_x; end_y = extent.y(); } if (start_y < extent.y()) break; // Create the renderable(s) first = MapCoordF(start_x, start_y); second = MapCoordF(end_x, end_y); createLine(first, second, delta_along_line_offset, line, delta_rotation, outline, output); // Move to next position start_x += dist_x; end_y += dist_y; } while (true); } } } void AreaSymbol::FillPattern::createRenderables(const AreaRenderable& outline, float delta_rotation, const MapCoord& pattern_origin, ObjectRenderables& output) const { if (line_spacing <= 0) return; if (!rotatable()) delta_rotation = 0; // Make rotation unique auto rotation = double(angle + delta_rotation); rotation = fmod(1.0 * rotation, M_PI); if (rotation < 0) rotation = M_PI + rotation; Q_ASSERT(rotation >= 0 && rotation <= M_PI); // Handle clipping const auto old_clip_path = output.getClipPath(); if (!(flags & Option::AlternativeToClipping)) { output.setClipPath(outline.painterPath()); } switch (type) { case LinePattern: { LineSymbol line; line.setColor(line_color); auto line_width_f = 0.001*line_width; line.setLineWidth(line_width_f); auto margin = line_width_f / 2; auto point_extent = QRectF{-margin, -margin, margin, margin}; createRenderables(outline, delta_rotation, pattern_origin, point_extent, &line, rotation, output); } break; case PointPattern: if (point && point_distance > 0) { PointObject point_object(point); point_object.setRotation(delta_rotation); point_object.update(); auto point_extent = point_object.getExtent(); createRenderables(outline, delta_rotation, pattern_origin, point_extent, nullptr, rotation, output); } break; } output.setClipPath(old_clip_path); } void AreaSymbol::FillPattern::createPointPatternLine( MapCoordF first, MapCoordF second, qreal delta_offset, float rotation, const AreaRenderable& outline, ObjectRenderables& output ) const { auto direction = second - first; auto length = direction.length(); direction /= length; // normalize auto offset = MapCoordF::dotProduct(direction, first) - 0.001 * offset_along_line - delta_offset; auto step_length = 0.001 * point_distance; auto start_length = ceil((offset) / step_length) * step_length - offset; auto to_next = direction * step_length; auto coord = first + direction * start_length; // Duplicated loops for optimum locality of code switch (flags & Option::AlternativeToClipping) { case Option::NoClippingIfCenterInside: for (auto cur = start_length; cur < length; cur += step_length, coord += to_next) point->createRenderablesIfCenterInside(coord, -rotation, outline.painterPath(), output); break; case Option::NoClippingIfCompletelyInside: for (auto cur = start_length; cur < length; cur += step_length, coord += to_next) point->createRenderablesIfCompletelyInside(coord, -rotation, outline.painterPath(), output); break; case Option::Default: #if 1 // Avoids expensive check, but may create objects which won't be rendered. for (auto cur = start_length; cur < length; cur += step_length, coord += to_next) point->createRenderablesScaled(coord, -rotation, output); break; #endif case Option::NoClippingIfPartiallyInside: for (auto cur = start_length; cur < length; cur += step_length, coord += to_next) point->createRenderablesIfPartiallyInside(coord, -rotation, outline.painterPath(), output); break; default: Q_UNREACHABLE(); } } void AreaSymbol::FillPattern::scale(double factor) { line_spacing = qRound(factor * line_spacing); line_width = qRound(factor * line_width); line_offset = qRound(factor * line_offset); offset_along_line = qRound(factor * offset_along_line); point_distance = qRound(factor * point_distance); if (point) point->scale(factor); } qreal AreaSymbol::FillPattern::dimensionForIcon() const { // Ignore large spacing for icon scaling auto size = qreal(0); switch (type) { case LinePattern: size = qreal(0.002 * line_width); break; case PointPattern: size = qreal(0.002 * point->dimensionForIcon()); if (point_distance < 5000) size = std::max(size, qreal(0.002 * point_distance)); break; } if (line_spacing < 5000) size = std::max(size, qreal(0.0015 * line_spacing)); return size; } // ### AreaSymbol ### AreaSymbol::AreaSymbol() noexcept : Symbol { Symbol::Area } , color { nullptr } , minimum_area { 0 } { // nothing else } AreaSymbol::~AreaSymbol() { for (auto& pattern : patterns) { if (pattern.type == FillPattern::PointPattern) delete pattern.point; } } Symbol* AreaSymbol::duplicate(const MapColorMap* color_map) const { auto new_area = new AreaSymbol(); new_area->duplicateImplCommon(this); new_area->color = color_map ? color_map->value(color) : color; new_area->minimum_area = minimum_area; new_area->patterns = patterns; for (auto& new_pattern : new_area->patterns) { if (new_pattern.type == FillPattern::PointPattern) new_pattern.point = static_cast(new_pattern.point->duplicate(color_map)); else if (new_pattern.type == FillPattern::LinePattern && color_map) new_pattern.line_color = color_map->value(new_pattern.line_color); } return new_area; } void AreaSymbol::createRenderables( const Object *object, const VirtualCoordVector &coords, ObjectRenderables &output, Symbol::RenderableOptions options) const { if (coords.size() < 3) return; auto path = static_cast(object); PathPartVector path_parts = PathPart::calculatePathParts(coords); createRenderables(path, path_parts, output, options); } void AreaSymbol::createRenderables( const PathObject* object, const PathPartVector& path_parts, ObjectRenderables &output, Symbol::RenderableOptions options) const { if (options == Symbol::RenderNormal) { createRenderablesNormal(object, path_parts, output); } else { const MapColor* dominant_color = guessDominantColor(); createBaselineRenderables(object, path_parts, output, dominant_color); if (options.testFlag(Symbol::RenderAreasHatched)) { createHatchingRenderables(object, path_parts, output, dominant_color); } } } void AreaSymbol::createRenderablesNormal( const PathObject* object, const PathPartVector& path_parts, ObjectRenderables& output) const { // The shape output is even created if the area is not filled with a color // because the QPainterPath created by it is needed as clip path for the fill objects auto color_fill = new AreaRenderable(this, path_parts); output.insertRenderable(color_fill); auto rotation = object->getPatternRotation(); auto origin = object->getPatternOrigin(); for (const auto& pattern : patterns) { pattern.createRenderables(*color_fill, rotation, origin, output); } } void AreaSymbol::createHatchingRenderables( const PathObject* object, const PathPartVector& path_parts, ObjectRenderables& output, const MapColor* color) const { Q_ASSERT(getContainedTypes() & Symbol::Area); if (color) { // Insert hatched area renderable AreaSymbol area_symbol; area_symbol.setNumFillPatterns(1); AreaSymbol::FillPattern& pattern = area_symbol.getFillPattern(0); pattern.type = AreaSymbol::FillPattern::LinePattern; pattern.angle = qDegreesToRadians(45.0f); pattern.line_spacing = 1000; pattern.line_offset = 0; pattern.line_color = color; pattern.line_width = 70; auto symbol = object->getSymbol(); if (symbol && symbol->getType() == Symbol::Area) { const AreaSymbol* orig_symbol = symbol->asArea(); if (!orig_symbol->getColor() && orig_symbol->getNumFillPatterns() >= 1) { const AreaSymbol::FillPattern& orig_pattern = orig_symbol->getFillPattern(0); pattern.angle = orig_pattern.angle; pattern.flags = orig_pattern.flags & ~FillPattern::AlternativeToClipping; if (orig_pattern.type == AreaSymbol::FillPattern::LinePattern) { pattern.line_spacing = std::max(1000, orig_pattern.line_spacing); pattern.line_offset = orig_pattern.line_offset; } } } area_symbol.createRenderablesNormal(object, path_parts, output); } } void AreaSymbol::colorDeleted(const MapColor* color) { if (containsColor(color)) { if (color == this->color) this->color = nullptr; for (auto& pattern : patterns) pattern.colorDeleted(color); resetIcon(); } } bool AreaSymbol::containsColor(const MapColor* color) const { return color == this->color || std::any_of(begin(patterns), end(patterns), [color](const auto& pattern){ return pattern.containsColor(color); }); } const MapColor* AreaSymbol::guessDominantColor() const { auto color = this->color; auto pattern = begin(patterns); while (!color && pattern != end(patterns)) { color = pattern->guessDominantColor(); ++pattern; } return color; } void AreaSymbol::scale(double factor) { minimum_area = qRound(factor*factor * minimum_area); for (auto& pattern : patterns) pattern.scale(factor); resetIcon(); } qreal AreaSymbol::dimensionForIcon() const { qreal size = 0; for (auto& pattern : patterns) size = qMax(size, pattern.dimensionForIcon()); return size; } bool AreaSymbol::hasRotatableFillPattern() const { return std::any_of(begin(patterns), end(patterns), [](auto& pattern){ return pattern.rotatable(); }); } #ifndef NO_NATIVE_FILE_FORMAT bool AreaSymbol::loadImpl(QIODevice* file, int version, Map* map) { int temp; file->read((char*)&temp, sizeof(int)); color = (temp >= 0) ? map->getColor(temp) : nullptr; if (version >= 2) file->read((char*)&minimum_area, sizeof(int)); int size; file->read((char*)&size, sizeof(int)); patterns.resize(size); for (int i = 0; i < size; ++i) if (!patterns[i].load(file, version, map)) return false; return true; } #endif void AreaSymbol::saveImpl(QXmlStreamWriter& xml, const Map& map) const { XmlElementWriter element { xml, QLatin1String("area_symbol") }; element.writeAttribute(QLatin1String{"inner_color"}, map.findColorIndex(color)); element.writeAttribute(QLatin1String{"min_area"}, minimum_area); element.writeAttribute(QLatin1String{"patterns"}, patterns.size()); for (const auto& pattern : patterns) pattern.save(xml, map); } bool AreaSymbol::loadImpl(QXmlStreamReader& xml, const Map& map, SymbolDictionary& symbol_dict) { if (xml.name() != QLatin1String("area_symbol")) return false; XmlElementReader element { xml }; color = map.getColor(element.attribute(QLatin1String("inner_color"))); minimum_area = element.attribute(QLatin1String("min_area")); auto num_patterns = element.attribute(QLatin1String("patterns")); patterns.reserve(num_patterns % 100); // 100 is not the limit while (xml.readNextStartElement()) { if (xml.name() == QLatin1String("pattern")) { patterns.push_back(FillPattern()); patterns.back().load(xml, map, symbol_dict); } else { xml.skipCurrentElement(); } } return true; } bool AreaSymbol::equalsImpl(const Symbol* other, Qt::CaseSensitivity case_sensitivity) const { const AreaSymbol* area = static_cast(other); if (!MapColor::equal(color, area->color)) return false; if (minimum_area != area->minimum_area) return false; if (patterns.size() != area->patterns.size()) return false; // std::is_permutation would identify equal sets of patterns. // However, guessDominantColor() depends on pattern order if there is no // AreaSymbol::color (or after the AreaSymbol::color is set to nullptr). // So equalsImpl cannot be changed unless guessDominantColor is changed. return std::equal(begin(patterns), end(patterns), begin(area->patterns), [case_sensitivity](auto& lhs, auto& rhs){ return lhs.equals(rhs, case_sensitivity); }); } } // namespace OpenOrienteering mapper-0.8.1.1/src/core/symbols/area_symbol.h000066400000000000000000000213371325266516600210420ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2012-2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_AREA_SYMBOL_H #define OPENORIENTEERING_AREA_SYMBOL_H #include #include #include #include #include #include #include #include "symbol.h" class QIODevice; class QRectF; class QXmlStreamReader; class QXmlStreamWriter; namespace OpenOrienteering { class AreaRenderable; class LineSymbol; class Map; class MapColor; class MapColorMap; class MapCoord; class MapCoordF; class Object; class ObjectRenderables; class PathObject; class PathPartVector; class PointSymbol; class SymbolPropertiesWidget; class SymbolSettingDialog; class VirtualCoordVector; /** * Symbol for PathObjects where the enclosed area is filled with a solid color * and / or with one or more patterns. */ class AreaSymbol : public Symbol { friend class AreaSymbolSettings; friend class PointSymbolEditorWidget; friend class OCAD8FileImport; public: /** Describes a fill pattern. */ struct FillPattern { /** Types of fill patterns. */ enum Type { /** Parallel lines pattern */ LinePattern = 1, /** Point grid pattern */ PointPattern = 2 }; /** * Flags for pattern properties. */ enum Option { Default = 0x00, NoClippingIfCompletelyInside = 0x01, NoClippingIfCenterInside = 0x02, NoClippingIfPartiallyInside = 0x03, AlternativeToClipping = 0x03, ///< Bitmask for NoClipping* options Rotatable = 0x10, ///< Pattern is rotatable per-object }; Q_DECLARE_FLAGS(Options, Option) /** Type of the pattern */ Type type; /** Basic properties of the pattern. */ Options flags; /** Rotation angle in radians * * \todo Switch to qreal when legacy native file format is dropped. */ float angle; /** Distance between parallel lines, as usual in 0.001mm */ int line_spacing; /** Offset of the first line from the origin */ int line_offset; // For type == LinePattern only: /** Line color */ const MapColor* line_color; /** Line width */ int line_width; // For type == PointPattern only: /** Offset of first point along parallel lines */ int offset_along_line; /** Point distance along parallel lines */ int point_distance; /** Contained point symbol */ PointSymbol* point; /** Display name (transient) */ QString name; /** Creates a default fill pattern */ FillPattern() noexcept; /** Loads the pattern in the old "native" format */ bool load(QIODevice* file, int version, Map* map); /** Saves the pattern in xml format */ void save(QXmlStreamWriter& file, const Map& map) const; /** Loads the pattern in xml format */ void load(QXmlStreamReader& xml, const Map& map, SymbolDictionary& symbol_dict); /** * Checks if the pattern settings are equal to the other. * TODO: should the transient name really be compared?! */ bool equals(const FillPattern& other, Qt::CaseSensitivity case_sensitivity) const; /** * Returns true if the pattern is rotatable per object. */ bool rotatable() const; /** * Controls whether the pattern is rotatable per object. */ void setRotatable(bool value); /** * Returns the flags which control drawing at boundary. */ Options clipping() const; /** * Sets the flags which control drawing at boundary. */ void setClipping(Options clipping); /** * Removes the pattern's references to the deleted color. */ void colorDeleted(const MapColor* color); /** * Tests if the pattern contains the given color. */ bool containsColor(const MapColor* color) const; /** * Returns the patterns primary color. */ const MapColor* guessDominantColor() const; /** * Creates renderables for this pattern to fill the area surrounded by the outline. * @param outline A renderable giving the extent and outline. * @param delta_rotation Rotation offest which is added to the pattern angle. * @param pattern_origin Origin point for line / point placement. * @param output Created renderables will be inserted here. */ void createRenderables( const AreaRenderable& outline, float delta_rotation, const MapCoord& pattern_origin, ObjectRenderables& output ) const; /** Does the heavy-lifting in loops over lines. */ template void createRenderables( const AreaRenderable& outline, float delta_rotation, const MapCoord& pattern_origin, const QRectF& point_extent, LineSymbol* line, qreal rotation, ObjectRenderables& output ) const; /** Creates one line of renderables, called by createRenderables(). */ template void createLine( MapCoordF first, MapCoordF second, qreal delta_offset, LineSymbol* line, float rotation, const AreaRenderable& outline, ObjectRenderables& output ) const; /** Creates a single line of renderables for a PointPattern. */ void createPointPatternLine( MapCoordF first, MapCoordF second, qreal delta_offset, float rotation, const AreaRenderable& outline, ObjectRenderables& output ) const; /** Spatially scales the pattern settings by the given factor. */ void scale(double factor); qreal dimensionForIcon() const; }; AreaSymbol() noexcept; ~AreaSymbol() override; Symbol* duplicate(const MapColorMap* color_map = nullptr) const override; void createRenderables( const Object *object, const VirtualCoordVector &coords, ObjectRenderables &output, Symbol::RenderableOptions options) const override; void createRenderables( const PathObject* object, const PathPartVector& path_parts, ObjectRenderables &output, Symbol::RenderableOptions options) const override; void createRenderablesNormal( const PathObject* object, const PathPartVector& path_parts, ObjectRenderables& output) const; /** * Creates area hatching renderables for a path object. */ void createHatchingRenderables( const PathObject *object, const PathPartVector& path_parts, ObjectRenderables &output, const MapColor* color) const; void colorDeleted(const MapColor* color) override; bool containsColor(const MapColor* color) const override; const MapColor* guessDominantColor() const override; void scale(double factor) override; qreal dimensionForIcon() const override; // Getters / Setters inline const MapColor* getColor() const {return color;} inline void setColor(const MapColor* color) {this->color = color;} inline int getMinimumArea() const {return minimum_area; } inline int getNumFillPatterns() const {return int(patterns.size());} inline void setNumFillPatterns(int count) {patterns.resize(std::size_t(count));} inline FillPattern& getFillPattern(int i) {return patterns[std::size_t(i)];} inline const FillPattern& getFillPattern(int i) const {return patterns[std::size_t(i)];} bool hasRotatableFillPattern() const; SymbolPropertiesWidget* createPropertiesWidget(SymbolSettingDialog* dialog) override; protected: #ifndef NO_NATIVE_FILE_FORMAT bool loadImpl(QIODevice* file, int version, Map* map) override; #endif void saveImpl(QXmlStreamWriter& xml, const Map& map) const override; bool loadImpl(QXmlStreamReader& xml, const Map& map, SymbolDictionary& symbol_dict) override; /** * Compares AreaSymbol objects for equality. * * Fill patterns are only compared in order. */ bool equalsImpl(const Symbol* other, Qt::CaseSensitivity case_sensitivity) const override; const MapColor* color; int minimum_area; // in mm^2 // FIXME: unit (factor) wrong std::vector patterns; }; inline bool AreaSymbol::FillPattern::rotatable() const { return flags.testFlag(Option::Rotatable); } inline AreaSymbol::FillPattern::Options AreaSymbol::FillPattern::clipping() const { return flags & Option::AlternativeToClipping; } } // namespace OpenOrienteering Q_DECLARE_OPERATORS_FOR_FLAGS(OpenOrienteering::AreaSymbol::FillPattern::Options) #endif mapper-0.8.1.1/src/core/symbols/combined_symbol.cpp000066400000000000000000000231771325266516600222510ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2012-2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "combined_symbol.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "core/map.h" #include "core/map_color.h" #include "core/objects/object.h" #include "core/symbols/symbol.h" namespace OpenOrienteering { CombinedSymbol::CombinedSymbol() : Symbol{Symbol::Combined} , private_parts{ false, false } , parts{ private_parts.size(), nullptr } { Q_ASSERT(private_parts.size() == parts.size()); // nothing else } CombinedSymbol::~CombinedSymbol() { auto is_private = begin(private_parts); for (auto subsymbol : parts) { if (*is_private) delete subsymbol; ++is_private; }; } Symbol* CombinedSymbol::duplicate(const MapColorMap* color_map) const { auto new_symbol = new CombinedSymbol(); new_symbol->duplicateImplCommon(this); new_symbol->parts = parts; new_symbol->private_parts = private_parts; auto is_private = begin(new_symbol->private_parts); for (auto& subsymbol : new_symbol->parts) { if (*is_private) subsymbol = subsymbol->duplicate(color_map); ++is_private; } return new_symbol; } bool CombinedSymbol::validate() const { return std::all_of(begin(parts), end(parts), [](auto& symbol) { return symbol->validate(); }); } void CombinedSymbol::createRenderables( const Object *object, const VirtualCoordVector &coords, ObjectRenderables &output, Symbol::RenderableOptions options) const { auto path = static_cast(object); PathPartVector path_parts = PathPart::calculatePathParts(coords); createRenderables(path, path_parts, output, options); } void CombinedSymbol::createRenderables( const PathObject* object, const PathPartVector& path_parts, ObjectRenderables &output, Symbol::RenderableOptions options) const { for (auto subsymbol : parts) { if (subsymbol) subsymbol->createRenderables(object, path_parts, output, options); } } void CombinedSymbol::colorDeleted(const MapColor* color) { if (containsColor(color)) resetIcon(); auto is_private = begin(private_parts); for (auto subsymbol : parts) { if (*is_private) const_cast(subsymbol)->colorDeleted(color); ++is_private; }; } bool CombinedSymbol::containsColor(const MapColor* color) const { return std::any_of(begin(parts), end(parts), [color](const auto& part) { return part && part->containsColor(color); }); } const MapColor* CombinedSymbol::guessDominantColor() const { // Speculative heuristic. Prefers areas and non-white colors. const MapColor* dominant_color = nullptr; for (auto subsymbol : parts) { if (subsymbol && subsymbol->getContainedTypes() & Symbol::Area) { dominant_color = subsymbol->guessDominantColor(); if (dominant_color && !dominant_color->isWhite()) return dominant_color; } } if (dominant_color) return dominant_color; for (auto subsymbol : parts) { if (subsymbol && !(subsymbol->getContainedTypes() & Symbol::Area)) { dominant_color = subsymbol->guessDominantColor(); if (dominant_color && !dominant_color->isWhite()) return dominant_color; } } return dominant_color; } bool CombinedSymbol::symbolChanged(const Symbol* old_symbol, const Symbol* new_symbol) { bool have_symbol = false; for (auto& subsymbol : parts) { if (subsymbol == old_symbol) { have_symbol = true; subsymbol = new_symbol; } } // always invalidate the icon, since the parts might have changed. resetIcon(); return have_symbol; } bool CombinedSymbol::containsSymbol(const Symbol* symbol) const { for (auto subsymbol : parts) { if (subsymbol == symbol) return true; if (subsymbol == nullptr) continue; if (subsymbol->getType() == Symbol::Combined) // TODO: see TODO in SymbolDropDown constructor. { const CombinedSymbol* combined_symbol = reinterpret_cast(subsymbol); if (combined_symbol->containsSymbol(symbol)) return true; } } return false; } void CombinedSymbol::scale(double factor) { auto is_private = begin(private_parts); for (auto subsymbol : parts) { if (*is_private) const_cast(subsymbol)->scale(factor); ++is_private; }; resetIcon(); } Symbol::Type CombinedSymbol::getContainedTypes() const { auto type = int(getType()); for (auto subsymbol : parts) { if (subsymbol) type |= subsymbol->getContainedTypes(); } return Type(type); } #ifndef NO_NATIVE_FILE_FORMAT bool CombinedSymbol::loadImpl(QIODevice* file, int version, Map* map) { int size; file->read((char*)&size, sizeof(int)); temp_part_indices.resize(size); parts.resize(size); for (int i = 0; i < size; ++i) { bool is_private = false; if (version >= 22) file->read((char*)&is_private, sizeof(bool)); private_parts[i] = is_private; if (is_private) { // Note on const_cast: private part is owned by this symbol. if (!Symbol::loadSymbol(const_cast(parts[i]), file, version, map)) return false; temp_part_indices[i] = -1; } else { int temp; file->read((char*)&temp, sizeof(int)); temp_part_indices[i] = temp; } } return true; } #endif void CombinedSymbol::saveImpl(QXmlStreamWriter& xml, const Map& map) const { xml.writeStartElement(QString::fromLatin1("combined_symbol")); int num_parts = int(parts.size()); xml.writeAttribute(QString::fromLatin1("parts"), QString::number(num_parts)); auto is_private = begin(private_parts); for (const auto subsymbol : parts) { xml.writeStartElement(QString::fromLatin1("part")); if (*is_private) { xml.writeAttribute(QString::fromLatin1("private"), QString::fromLatin1("true")); subsymbol->save(xml, map); } else { auto index = map.findSymbolIndex(subsymbol); xml.writeAttribute(QString::fromLatin1("symbol"), QString::number(index)); } xml.writeEndElement(/*part*/); ++is_private; } xml.writeEndElement(/*combined_symbol*/); } bool CombinedSymbol::loadImpl(QXmlStreamReader& xml, const Map& map, SymbolDictionary& symbol_dict) { if (xml.name() != QLatin1String("combined_symbol")) return false; int num_parts = xml.attributes().value(QLatin1String("parts")).toInt(); temp_part_indices.reserve(num_parts % 10); // 10 is not the limit private_parts.clear(); private_parts.reserve(num_parts % 10); parts.clear(); parts.reserve(num_parts % 10); while (xml.readNextStartElement()) { if (xml.name() == QLatin1String("part")) { bool is_private = (xml.attributes().value(QLatin1String("private")) == QLatin1String("true")); private_parts.push_back(is_private); if (is_private) { xml.readNextStartElement(); parts.push_back(Symbol::load(xml, map, symbol_dict)); temp_part_indices.push_back(-1); } else { int temp = xml.attributes().value(QLatin1String("symbol")).toInt(); temp_part_indices.push_back(temp); parts.push_back(nullptr); } xml.skipCurrentElement(); } else xml.skipCurrentElement(); // unknown } return true; } bool CombinedSymbol::equalsImpl(const Symbol* other, Qt::CaseSensitivity case_sensitivity) const { const CombinedSymbol* combination = static_cast(other); return parts.size() == combination->parts.size() && std::equal(begin(private_parts), end(private_parts), begin(combination->private_parts)) && std::equal(begin(parts), end(parts), begin(combination->parts), [case_sensitivity](const auto lhs, const auto rhs) { return (!lhs && !rhs) || (lhs && rhs && lhs->equals(rhs, case_sensitivity)); }); } bool CombinedSymbol::loadFinished(Map* map) { const auto num_symbols = map->getNumSymbols(); const auto last = std::find_if(begin(temp_part_indices), end(temp_part_indices), [num_symbols](const auto index) { return index >= num_symbols; }); std::transform(begin(temp_part_indices), last, begin(parts), begin(parts), [map](const auto index, const auto& subsymbol) -> const Symbol* { return (index < 0) ? subsymbol : map->getSymbol(index); }); return last == end(temp_part_indices); } qreal CombinedSymbol::dimensionForIcon() const { return std::accumulate(begin(parts), end(parts), qreal(0), [](qreal value, auto subsymbol) { return subsymbol ? qMax(value, subsymbol->dimensionForIcon()) : value; }); } qreal CombinedSymbol::calculateLargestLineExtent() const { return std::accumulate(begin(parts), end(parts), qreal(0), [](qreal value, auto subsymbol) { return subsymbol ? qMax(value, subsymbol->calculateLargestLineExtent()) : value; }); } void CombinedSymbol::setPart(int i, const Symbol* symbol, bool is_private) { const auto index = std::size_t(i); if (private_parts[index]) delete parts[index]; parts[index] = symbol; private_parts[index] = symbol ? is_private : false; } } // namespace OpenOrienteering mapper-0.8.1.1/src/core/symbols/combined_symbol.h000066400000000000000000000077071325266516600217170ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2012-2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_COMBINED_SYMBOL_H #define OPENORIENTEERING_COMBINED_SYMBOL_H #include "symbol.h" #include #include #include class QIODevice; class QXmlStreamReader; class QXmlStreamWriter; namespace OpenOrienteering { class Map; class MapColor; class MapColorMap; class Object; class ObjectRenderables; class PathObject; class PathPartVector; class SymbolPropertiesWidget; class SymbolSettingDialog; class VirtualCoordVector; /** * Symbol which can combine other line and area symbols, * creating renderables for each of them. * * To use, set the number of parts with setNumParts() and set the indivdual part * pointers with setPart(). Parts can be private, i.e. the CombinedSymbol owns * the part symbol and it is not entered in the map as an individual symbol. */ class CombinedSymbol : public Symbol { friend class CombinedSymbolSettings; friend class PointSymbolEditorWidget; friend class OCAD8FileImport; public: CombinedSymbol(); ~CombinedSymbol() override; Symbol* duplicate(const MapColorMap* color_map = nullptr) const override; bool validate() const override; void createRenderables( const Object *object, const VirtualCoordVector &coords, ObjectRenderables &output, Symbol::RenderableOptions options) const override; void createRenderables( const PathObject* object, const PathPartVector& path_parts, ObjectRenderables &output, Symbol::RenderableOptions options) const override; void colorDeleted(const MapColor* color) override; bool containsColor(const MapColor* color) const override; const MapColor* guessDominantColor() const override; bool symbolChanged(const Symbol* old_symbol, const Symbol* new_symbol) override; bool containsSymbol(const Symbol* symbol) const override; void scale(double factor) override; Type getContainedTypes() const override; bool loadFinished(Map* map) override; qreal dimensionForIcon() const override; qreal calculateLargestLineExtent() const override; // Getters / Setter inline int getNumParts() const {return (int)parts.size();} inline void setNumParts(int num) {parts.resize(num, nullptr); private_parts.resize(num, false);} inline const Symbol* getPart(int i) const {return parts[i];} void setPart(int i, const Symbol* symbol, bool is_private); inline bool isPartPrivate(int i) const {return private_parts[i];} inline void setPartPrivate(int i, bool set_private) {private_parts[i] = set_private;} SymbolPropertiesWidget* createPropertiesWidget(SymbolSettingDialog* dialog) override; protected: #ifndef NO_NATIVE_FILE_FORMAT bool loadImpl(QIODevice* file, int version, Map* map) override; #endif void saveImpl(QXmlStreamWriter& xml, const Map& map) const override; bool loadImpl(QXmlStreamReader& xml, const Map& map, SymbolDictionary& symbol_dict) override; bool equalsImpl(const Symbol* other, Qt::CaseSensitivity case_sensitivity) const override; std::vector private_parts; std::vector parts; std::vector temp_part_indices; // temporary vector of the indices of the 'parts' symbols, used just for loading }; } // namespace OpenOrienteering #endif mapper-0.8.1.1/src/core/symbols/line_symbol.cpp000066400000000000000000002044741325266516600214210ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2012-2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "line_symbol.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "core/map.h" #include "core/map_color.h" #include "core/map_coord.h" #include "core/path_coord.h" #include "core/objects/object.h" #include "core/renderables/renderable.h" #include "core/renderables/renderable_implementation.h" #include "core/symbols/area_symbol.h" #include "core/symbols/point_symbol.h" #include "core/symbols/symbol.h" #include "core/virtual_coord_vector.h" #include "core/virtual_path.h" namespace OpenOrienteering { // ### LineSymbolBorder ### void LineSymbolBorder::reset() noexcept { color = nullptr; width = 0; shift = 0; dashed = false; dash_length = 2 * 1000; break_length = 1 * 1000; } #ifndef NO_NATIVE_FILE_FORMAT bool LineSymbolBorder::load(QIODevice* file, int version, Map* map) { Q_UNUSED(version); int temp; file->read((char*)&temp, sizeof(int)); color = (temp >= 0) ? map->getColor(temp) : nullptr; file->read((char*)&width, sizeof(int)); file->read((char*)&shift, sizeof(int)); file->read((char*)&dashed, sizeof(bool)); file->read((char*)&dash_length, sizeof(int)); file->read((char*)&break_length, sizeof(int)); return true; } #endif void LineSymbolBorder::save(QXmlStreamWriter& xml, const Map& map) const { xml.writeStartElement(QString::fromLatin1("border")); xml.writeAttribute(QString::fromLatin1("color"), QString::number(map.findColorIndex(color))); xml.writeAttribute(QString::fromLatin1("width"), QString::number(width)); xml.writeAttribute(QString::fromLatin1("shift"), QString::number(shift)); if (dashed) xml.writeAttribute(QString::fromLatin1("dashed"), QString::fromLatin1("true")); xml.writeAttribute(QString::fromLatin1("dash_length"), QString::number(dash_length)); xml.writeAttribute(QString::fromLatin1("break_length"), QString::number(break_length)); xml.writeEndElement(/*border*/); } bool LineSymbolBorder::load(QXmlStreamReader& xml, const Map& map) { Q_ASSERT(xml.name() == QLatin1String("border")); QXmlStreamAttributes attributes = xml.attributes(); int temp = attributes.value(QLatin1String("color")).toInt(); color = map.getColor(temp); width = attributes.value(QLatin1String("width")).toInt(); shift = attributes.value(QLatin1String("shift")).toInt(); dashed = (attributes.value(QLatin1String("dashed")) == QLatin1String("true")); dash_length = attributes.value(QLatin1String("dash_length")).toInt(); break_length = attributes.value(QLatin1String("break_length")).toInt(); xml.skipCurrentElement(); return !xml.error(); } bool LineSymbolBorder::equals(const LineSymbolBorder* other) const { if (!MapColor::equal(color, other->color)) return false; if (width != other->width) return false; if (shift != other->shift) return false; if (dashed != other->dashed) return false; if (dashed) { if (dash_length != other->dash_length) return false; if (break_length != other->break_length) return false; } return true; } void LineSymbolBorder::assign(const LineSymbolBorder& other, const MapColorMap* color_map) { color = color_map ? color_map->value(other.color) : other.color; width = other.width; shift = other.shift; dashed = other.dashed; dash_length = other.dash_length; break_length = other.break_length; } bool LineSymbolBorder::isVisible() const { return width > 0 && color && !(dash_length == 0 && dashed); } void LineSymbolBorder::createSymbol(LineSymbol& out) const { out.setLineWidth(0.001 * width); out.setColor(color); if (dashed) { out.setDashed(true); out.setDashLength(dash_length); out.setBreakLength(break_length); } } void LineSymbolBorder::scale(double factor) { width = qRound(factor * width); shift = qRound(factor * shift); dash_length = qRound(factor * dash_length); break_length = qRound(factor * break_length); } // ### LineSymbol ### LineSymbol::LineSymbol() noexcept : Symbol(Symbol::Line) { line_width = 0; color = nullptr; minimum_length = 0; cap_style = FlatCap; join_style = MiterJoin; pointed_cap_length = 1000; start_symbol = nullptr; mid_symbol = nullptr; end_symbol = nullptr; dash_symbol = nullptr; dashed = false; segment_length = 4000; end_length = 0; show_at_least_one_symbol = true; minimum_mid_symbol_count = 0; minimum_mid_symbol_count_when_closed = 0; dash_length = 4000; break_length = 1000; dashes_in_group = 1; in_group_break_length = 500; half_outer_dashes = false; mid_symbols_per_spot = 1; mid_symbol_distance = 0; suppress_dash_symbol_at_ends = false; scale_dash_symbol = true; // Border lines have_border_lines = false; border.reset(); right_border.reset(); } LineSymbol::~LineSymbol() { delete start_symbol; delete mid_symbol; delete end_symbol; delete dash_symbol; } Symbol* LineSymbol::duplicate(const MapColorMap* color_map) const { auto new_line = new LineSymbol(); new_line->duplicateImplCommon(this); new_line->line_width = line_width; new_line->color = color_map ? color_map->value(color) : color; new_line->minimum_length = minimum_length; new_line->cap_style = cap_style; new_line->join_style = join_style; new_line->pointed_cap_length = pointed_cap_length; using MemberSymbol = PointSymbol* LineSymbol::*; MemberSymbol members[4] = { &LineSymbol::start_symbol, &LineSymbol::mid_symbol, &LineSymbol::end_symbol, &LineSymbol::dash_symbol }; for (auto member : members) { auto sub_symbol = this->*member; if (sub_symbol && !sub_symbol->isEmpty()) new_line->*member = static_cast(sub_symbol->duplicate(color_map)); } new_line->dashed = dashed; new_line->segment_length = segment_length; new_line->end_length = end_length; new_line->show_at_least_one_symbol = show_at_least_one_symbol; new_line->minimum_mid_symbol_count = minimum_mid_symbol_count; new_line->minimum_mid_symbol_count_when_closed = minimum_mid_symbol_count_when_closed; new_line->dash_length = dash_length; new_line->break_length = break_length; new_line->dashes_in_group = dashes_in_group; new_line->in_group_break_length = in_group_break_length; new_line->half_outer_dashes = half_outer_dashes; new_line->mid_symbols_per_spot = mid_symbols_per_spot; new_line->mid_symbol_distance = mid_symbol_distance; new_line->suppress_dash_symbol_at_ends = suppress_dash_symbol_at_ends; new_line->scale_dash_symbol = scale_dash_symbol; new_line->have_border_lines = have_border_lines; new_line->border.assign(border, color_map); new_line->right_border.assign(right_border, color_map); return new_line; } bool LineSymbol::validate() const { using std::begin; using std::end; using MemberSymbol = PointSymbol* LineSymbol::*; MemberSymbol members[4] = { &LineSymbol::start_symbol, &LineSymbol::mid_symbol, &LineSymbol::end_symbol, &LineSymbol::dash_symbol }; return std::all_of(begin(members), end(members), [this](auto& member) { auto sub_symbol = this->*member; return !sub_symbol || !sub_symbol->isEmpty() || sub_symbol->validate(); }); } void LineSymbol::createRenderables( const Object* object, const VirtualCoordVector& coords, ObjectRenderables& output, RenderableOptions options ) const { Q_UNUSED(options); PathPartVector path_parts = PathPart::calculatePathParts(coords); for (const auto& part : path_parts) { createPathCoordRenderables(object, part, part.isClosed(), output); } } void LineSymbol::createRenderables( const PathObject* object, const PathPartVector& path_parts, ObjectRenderables& output, RenderableOptions options ) const { if (options.testFlag(Symbol::RenderBaselines)) { createBaselineRenderables(object, path_parts, output, guessDominantColor()); } else { for (const auto& part : path_parts) { createPathCoordRenderables(object, part, part.isClosed(), output); } } } void LineSymbol::createPathRenderables(const Object* object, bool path_closed, const MapCoordVector& flags, const MapCoordVectorF& coords, ObjectRenderables& output) const { auto path = VirtualPath { flags, coords }; auto last = path.path_coords.update(0); createPathCoordRenderables(object, path, path_closed, output); Q_ASSERT(last+1 == coords.size()); Q_UNUSED(last); } void LineSymbol::createPathCoordRenderables(const Object* object, const VirtualPath& path, bool path_closed, ObjectRenderables& output) const { if (path.size() < 2) return; auto& coords = path.coords; // Start or end symbol? if (start_symbol && !start_symbol->isEmpty()) { auto orientation = 0.0f; if (start_symbol->isRotatable()) { bool ok; MapCoordF tangent = path.calculateOutgoingTangent(0, ok); if (ok) orientation = tangent.angle(); } start_symbol->createRenderablesScaled(coords[0], orientation, output); } if (end_symbol && !end_symbol->isEmpty()) { std::size_t last = coords.size() - 1; auto orientation = 0.0f; if (end_symbol->isRotatable()) { bool ok; MapCoordF tangent = path.calculateIncomingTangent(last, ok); if (ok) orientation = tangent.angle(); } end_symbol->createRenderablesScaled(coords[last], orientation, output); } // Dash symbols? if (dash_symbol && !dash_symbol->isEmpty()) { createDashSymbolRenderables(path, path_closed, output); } // The line itself MapCoordVector processed_flags; MapCoordVectorF processed_coords; bool create_border = have_border_lines && (border.isVisible() || right_border.isVisible()); bool pointed_cap = cap_style == PointedCap && pointed_cap_length > 0; if (!dashed) { // Base line? if (line_width > 0) { if (color && !pointed_cap && !create_border) { output.insertRenderable(new LineRenderable(this, path, path_closed)); } else if (create_border || pointed_cap) { auto last = coords.size(); auto part_start = MapCoordVector::size_type { 0 }; auto next_part_start = last; //path_coords.update(part_start); bool has_start = !(part_start == 0 && path_closed); bool has_end = !(next_part_start == last && path_closed); auto start = SplitPathCoord::begin(path.path_coords); auto end = SplitPathCoord::end(path.path_coords); processContinuousLine(path, start, end, has_start, has_end, processed_flags, processed_coords, false, output); } } // Symbols? if (mid_symbol && !mid_symbol->isEmpty() && segment_length > 0) createMidSymbolRenderables(path, path_closed, output); } else if (dash_length > 0) { // Dashed lines processDashedLine(path, path_closed, processed_flags, processed_coords, output); } else { // Invalid configuration return; } if (!processed_coords.empty() && (color || create_border)) { Q_ASSERT(processed_coords.size() != 1); VirtualPath path = { processed_flags, processed_coords }; path.path_coords.update(path.first_index); if (color) { output.insertRenderable(new LineRenderable(this, path, path_closed)); } if (create_border) { createBorderLines(object, path, output); } } } void LineSymbol::createBorderLines( const Object* object, const VirtualPath& path, ObjectRenderables& output) const { double main_shift = 0.0005 * line_width; if (!areBordersDifferent()) { MapCoordVector border_flags; MapCoordVectorF border_coords; LineSymbol border_symbol; border.createSymbol(border_symbol); border_symbol.setJoinStyle(join_style == RoundJoin ? RoundJoin : MiterJoin); if (border.dashed && border.dash_length > 0 && border.break_length > 0) { MapCoordVector dashed_flags; MapCoordVectorF dashed_coords; border_symbol.processDashedLine(path, path.isClosed(), dashed_flags, dashed_coords, output); border_symbol.dashed = false; // important, otherwise more dashes might be added by createRenderables()! auto dashed_path = VirtualPath { dashed_flags, dashed_coords }; shiftCoordinates(dashed_path, main_shift, border_flags, border_coords); border_symbol.createPathRenderables(object, path.isClosed(), border_flags, border_coords, output); shiftCoordinates(dashed_path, -main_shift, border_flags, border_coords); border_symbol.createPathRenderables(object, path.isClosed(), border_flags, border_coords, output); } else { shiftCoordinates(path, main_shift, border_flags, border_coords); border_symbol.createPathRenderables(object, path.isClosed(), border_flags, border_coords, output); shiftCoordinates(path, -main_shift, border_flags, border_coords); border_symbol.createPathRenderables(object, path.isClosed(), border_flags, border_coords, output); } } else { createBorderLine(object, path, path.isClosed(), output, border, -main_shift); createBorderLine(object, path, path.isClosed(), output, right_border, main_shift); } } void LineSymbol::createBorderLine( const Object* object, const VirtualPath& path, bool path_closed, ObjectRenderables& output, const LineSymbolBorder& border, double main_shift ) const { MapCoordVector border_flags; MapCoordVectorF border_coords; LineSymbol border_symbol; border.createSymbol(border_symbol); border_symbol.setJoinStyle(join_style == RoundJoin ? RoundJoin : MiterJoin); if (border.dashed && border.dash_length > 0 && border.break_length > 0) { MapCoordVector dashed_flags; MapCoordVectorF dashed_coords; border_symbol.processDashedLine(path, path_closed, dashed_flags, dashed_coords, output); border_symbol.dashed = false; // important, otherwise more dashes might be added by createRenderables()! auto dashed_path = VirtualPath { dashed_flags, dashed_coords }; shiftCoordinates(dashed_path, main_shift, border_flags, border_coords); } else { shiftCoordinates(path, main_shift, border_flags, border_coords); } border_symbol.createPathRenderables(object, path_closed, border_flags, border_coords, output); } void LineSymbol::shiftCoordinates(const VirtualPath& path, double main_shift, MapCoordVector& out_flags, MapCoordVectorF& out_coords) const { const float curve_threshold = 0.03f; // TODO: decrease for export/print? const int MAX_OFFSET = 16; QBezier offsetCurves[MAX_OFFSET]; double miter_limit = 2.0 * miterLimit(); // needed more than once if (miter_limit <= 0.0) miter_limit = 1.0e6; // Q_ASSERT(miter_limit != 0) double miter_reference = 0.0; // reference value: if (join_style == MiterJoin) // when to bevel MiterJoins miter_reference = cos(atan(4.0 / miter_limit)); // sign of shift and main shift indicates left or right border // but u_border_shift is unsigned double u_border_shift = 0.001 * ((main_shift > 0.0 && areBordersDifferent()) ? right_border.shift : border.shift); double shift = main_shift + ((main_shift > 0.0) ? u_border_shift : -u_border_shift); auto size = path.size(); out_flags.clear(); out_coords.clear(); out_flags.reserve(size); out_coords.reserve(size); const MapCoord no_flags; bool ok_in, ok_out; MapCoordF segment_start, right_vector, end_right_vector, vector_in, vector_out, tangent_in, tangent_out, middle0, middle1; auto last_i = path.last_index; for (auto i = path.first_index; i < size; ++i) { auto coords_i = path.coords[i]; const auto& flags_i = path.coords.flags[i]; vector_in = path.calculateIncomingTangent(i, ok_in); vector_out = path.calculateOutgoingTangent(i, ok_out); if (!ok_in) { vector_in = vector_out; ok_in = ok_out; } if (ok_in) { tangent_in = vector_in; tangent_in.normalize(); } if (!ok_out) { vector_out = vector_in; ok_out = ok_in; } if (ok_out) { tangent_out = vector_out; tangent_out.normalize(); } if (!ok_in && !ok_out) { // Rare but existing case. No valid solution, but // at least we need to output a point to handle the flags correctly. //qDebug("No valid tangent"); segment_start = coords_i; } else if (i == 0 && !path.isClosed()) { // Simple start point right_vector = tangent_out.perpRight(); segment_start = coords_i + shift * right_vector; } else if (i == last_i && !path.isClosed()) { // Simple end point right_vector = tangent_in.perpRight(); segment_start = coords_i + shift * right_vector; } else { // Corner point right_vector = tangent_out.perpRight(); middle0 = tangent_in + tangent_out; middle0.normalize(); double offset; // Determine type of corner (inner vs. outer side of corner) double a = (tangent_out.x() * tangent_in.y() - tangent_in.x() * tangent_out.y()) * main_shift; if (a > 0.0) { // Outer side of corner if (join_style == BevelJoin || join_style == RoundJoin) { middle1 = tangent_in + middle0; middle1.normalize(); double phi1 = acos(MapCoordF::dotProduct(middle1, tangent_in)); offset = tan(phi1) * u_border_shift; if (i > 0 && !qIsNaN(offset)) { // First border corner point end_right_vector = tangent_in.perpRight(); out_flags.push_back(no_flags); out_coords.push_back(coords_i + shift * end_right_vector + offset * tangent_in); if (join_style == RoundJoin) { // Extra border corner point // TODO: better approximation of round corner / use bezier curve out_flags.push_back(no_flags); out_coords.push_back(coords_i + shift * middle0.perpRight()); } } } else /* join_style == MiterJoin */ { // miter_check has no concrete interpretation, // but was derived by mathematical simplifications. double miter_check = MapCoordF::dotProduct(middle0, tangent_in); if (miter_check <= miter_reference) { // Two border corner points middle1 = tangent_in + middle0; middle1.normalize(); double phi1 = acos(MapCoordF::dotProduct(middle1, tangent_in)); offset = miter_limit * fabs(main_shift) + tan(phi1) * u_border_shift; if (i > 0 && !qIsNaN(offset)) { // First border corner point end_right_vector = tangent_in.perpRight(); out_flags.push_back(no_flags); out_coords.push_back(coords_i + shift * end_right_vector + offset * tangent_in); } } else { double phi = acos(MapCoordF::dotProduct(middle0.perpRight(), tangent_in)); offset = fabs(1.0/tan(phi) * shift); } } if (qIsNaN(offset)) { offset = 0.0; } // single or second border corner point segment_start = coords_i + shift * right_vector - offset * tangent_out; } else if (i > 2 && path.coords.flags[i-3].isCurveStart() && flags_i.isCurveStart()) { // Inner side of corner (or no corner), and both sides are beziers // old behaviour right_vector = middle0.perpRight(); double phi = acos(MapCoordF::dotProduct(right_vector, tangent_in)); double sin_phi = sin(phi); double inset = (sin_phi > (1.0/miter_limit)) ? (1.0 / sin_phi) : miter_limit; segment_start = coords_i + (shift * inset) * right_vector; } else { // Inner side of corner (or no corner), and no more than on bezier involved // Default solution double phi = acos(MapCoordF::dotProduct(middle0.perpRight(), tangent_in)); double tan_phi = tan(phi); offset = -fabs(shift/tan_phi); if (tan_phi >= 1.0) { segment_start = coords_i + shift * right_vector - offset * tangent_out; } else { // Critical case double len_in = vector_in.length(); double len_out = vector_out.length(); a = qIsNaN(offset) ? 0.0 : (fabs(offset) - qMin(len_in, len_out)); if (a < -0.0) { // Offset acceptable segment_start = coords_i + shift * right_vector - offset * tangent_out; #ifdef MAPPER_FILL_MITER_LIMIT_GAP // Avoid miter limit effects by extra points // This is be nice for ISOM roads, but not for powerlines... // TODO: add another flag to the signature? out_flags.push_back(no_flags); out_coords.push_back(coords_i + shift * right_vector - offset * tangent_out); out_flags.push_back(no_flags); out_coords.push_back(out_coords.back() - (shift - main_shift) * qMax(0.0, 1.0 / sin(phi) - miter_limit) * middle0); // single or second border corner point segment_start = coords_i + shift * right_vector - offset * tangent_out; #endif } else { // Default solution is too far off from main path if (len_in < len_out) { segment_start = coords_i + shift * right_vector + len_in * tangent_out; } else { right_vector = tangent_in.perpRight(); segment_start = coords_i + shift * right_vector - len_out * tangent_in; } } } } } out_flags.emplace_back(flags_i); out_flags.back().setCurveStart(false); // Must not be set if bezier.shifted() fails! out_coords.emplace_back(segment_start); if (flags_i.isCurveStart()) { Q_ASSERT(i+2 < path.last_index); // Use QBezierCopy code to shift the curve, but set start and end point manually to get the correct end points (because of line joins) // TODO: it may be necessary to remove some of the generated curves in the case an outer point is moved inwards if (main_shift > 0.0) { QBezier bezier = QBezier::fromPoints(path.coords[i+3], path.coords[i+2], path.coords[i+1], coords_i); auto count = bezier.shifted(offsetCurves, MAX_OFFSET, qAbs(shift), curve_threshold); for (auto j = count - 1; j >= 0; --j) { out_flags.back().setCurveStart(true); out_flags.emplace_back(); out_coords.emplace_back(offsetCurves[j].pt3()); out_flags.emplace_back(); out_coords.emplace_back(offsetCurves[j].pt2()); if (j > 0) { out_flags.emplace_back(); out_coords.emplace_back(offsetCurves[j].pt1()); } } } else { QBezier bezier = QBezier::fromPoints(path.coords[i], path.coords[i+1], path.coords[i+2], path.coords[i+3]); int count = bezier.shifted(offsetCurves, MAX_OFFSET, qAbs(shift), curve_threshold); for (int j = 0; j < count; ++j) { out_flags.back().setCurveStart(true); out_flags.emplace_back(); out_coords.emplace_back(offsetCurves[j].pt2()); out_flags.emplace_back(); out_coords.emplace_back(offsetCurves[j].pt3()); if (j < count - 1) { out_flags.emplace_back(); out_coords.emplace_back(offsetCurves[j].pt4()); } } } i += 2; } } } void LineSymbol::processContinuousLine( const VirtualPath& path, const SplitPathCoord& start, const SplitPathCoord& end, bool has_start, bool has_end, MapCoordVector& processed_flags, MapCoordVectorF& processed_coords, bool set_mid_symbols, ObjectRenderables& output ) const { bool create_line = true; float effective_cap_length = 0.0f; if (cap_style == PointedCap && (has_start || has_end)) { effective_cap_length = 0.001f * pointed_cap_length; int num_caps = 2 - (has_end ? 0 : 1) - (has_start ? 0 : 1); auto max_effective_cap_length = (end.clen - start.clen) / num_caps; if (effective_cap_length >= max_effective_cap_length) { create_line = false; effective_cap_length = max_effective_cap_length; } } auto split = start; auto next_split = SplitPathCoord(); if (has_start && effective_cap_length > 0.0f) { // Create pointed line cap start next_split = SplitPathCoord::at(start.clen + effective_cap_length, start); createPointedLineCap(path, split, next_split, false, output); split = next_split; } if (create_line) { // Add line to processed_coords/flags PathCoord::length_type mid_symbol_distance_f = 0.001 * mid_symbol_distance; auto mid_symbols_length = (qMax(1, mid_symbols_per_spot) - 1) * mid_symbol_distance_f; auto effective_end_length = end.clen; if (has_end) { effective_end_length -= effective_cap_length; } set_mid_symbols = set_mid_symbols && mid_symbol && !mid_symbol->isEmpty() && mid_symbols_per_spot; if (set_mid_symbols && mid_symbols_length <= effective_end_length - split.clen) { auto mid_position = (split.clen + effective_end_length - mid_symbols_length) / 2; next_split = SplitPathCoord::at(mid_position, split); path.copy(split, next_split, processed_flags, processed_coords); split = next_split; auto orientation = 0.0f; bool mid_symbol_rotatable = bool(mid_symbol) && mid_symbol->isRotatable(); for (auto i = mid_symbols_per_spot; i > 0; --i) { if (mid_symbol_rotatable) orientation = split.tangentVector().angle(); mid_symbol->createRenderablesScaled(split.pos, orientation, output); if (i > 1) { mid_position += mid_symbol_distance_f; next_split = SplitPathCoord::at(mid_position, split); path.copy(split, next_split, processed_flags, processed_coords); split = next_split; } } } // End position next_split = SplitPathCoord::at(effective_end_length, split); path.copy(split, next_split, processed_flags, processed_coords); split = next_split; processed_flags.back().setGapPoint(true); Q_ASSERT(!processed_flags[processed_flags.size()-2].isCurveStart()); } if (has_end && effective_cap_length > 0.0f) { // Create pointed line cap end next_split = end; if (end.is_curve_end) { // Get updated curve end next_split = SplitPathCoord::at(end.clen, split); } createPointedLineCap(path, split, next_split, true, output); } } void LineSymbol::createPointedLineCap( const VirtualPath& path, const SplitPathCoord& start, const SplitPathCoord& end, bool is_end, ObjectRenderables& output ) const { AreaSymbol area_symbol; area_symbol.setColor(color); float line_half_width = 0.001f * 0.5f * line_width; float cap_length = 0.001f * pointed_cap_length; float tan_angle = line_half_width / cap_length; MapCoordVector cap_flags; MapCoordVectorF cap_middle_coords; path.copy(start, end, cap_flags, cap_middle_coords); auto cap_size = VirtualPath::size_type(cap_middle_coords.size()); auto cap_middle_path = VirtualPath { cap_flags, cap_middle_coords }; std::vector cap_lengths; cap_lengths.reserve(cap_size); path.copyLengths(start, end, cap_lengths); Q_ASSERT(cap_middle_coords.size() == cap_lengths.size()); // Calculate coordinates on the left and right side of the line MapCoordVectorF cap_coords; MapCoordVectorF cap_coords_left; float sign = is_end ? (-1) : 1; for (MapCoordVectorF::size_type i = 0; i < cap_size; ++i) { float dist_from_start = is_end ? (end.clen - cap_lengths[i]) : (cap_lengths[i] - start.clen); float factor = dist_from_start / cap_length; //Q_ASSERT(factor >= -0.01f && factor <= 1.01f); happens when using large break lengths as these are not adjusted factor = qBound(0.0f, factor, 1.0f); auto tangent_info = cap_middle_path.calculateTangentScaling(i); auto right_vector = tangent_info.first.perpRight(); right_vector.normalize(); auto radius = qBound(0.0, line_half_width*factor*tangent_info.second, line_half_width*2.0); cap_flags[i].setHolePoint(false); cap_coords.emplace_back(cap_middle_coords[i] + radius * right_vector); cap_coords_left.emplace_back(cap_middle_coords[i] - radius * right_vector); // Control points for bezier curves if (i >= 3 && cap_flags[i-3].isCurveStart()) { cap_coords.emplace_back(cap_coords.back()); cap_coords_left.emplace_back(cap_coords_left.back()); MapCoordF tangent = cap_middle_coords[i] - cap_middle_coords[i-1]; Q_ASSERT(tangent.lengthSquared() < 999*999); auto right_scale = tangent.length() * tan_angle * sign; cap_coords[i-1] = cap_coords[i] - tangent - right_vector * right_scale; cap_coords_left[i-1] = cap_coords_left[i] - tangent + right_vector * right_scale; } if (cap_flags[i].isCurveStart()) { // TODO: Tangent scaling depending on curvature? Adaptive subdivision of the curves? MapCoordF tangent = cap_middle_coords[i+1] - cap_middle_coords[i]; Q_ASSERT(tangent.lengthSquared() < 999*999); float right_scale = tangent.length() * tan_angle * sign; cap_coords.emplace_back(cap_coords[i] + tangent + right_vector * right_scale); cap_coords_left.emplace_back(cap_coords_left[i] + tangent - right_vector * right_scale); i += 2; } } // Create small overlap to avoid visual glitches with the on-screen display (should not matter for printing, could probably turn it off for this) const float overlap_length = 0.05f; if (end.clen - start.clen > 4 * overlap_length) { bool ok; int end_pos = is_end ? 0 : (cap_size - 1); int end_cap_pos = is_end ? 0 : (cap_coords.size() - 1); MapCoordF tangent = cap_middle_path.calculateTangent(end_pos, !is_end, ok); if (ok) { tangent.setLength(overlap_length * sign); auto right = MapCoordF{ is_end ? tangent : -tangent }.perpRight(); MapCoordF shifted_coord = cap_coords[end_cap_pos]; shifted_coord += tangent + right; MapCoordF shifted_coord_left = cap_coords_left[end_cap_pos]; shifted_coord_left += tangent - right; if (is_end) { cap_flags.insert(cap_flags.begin(), MapCoord()); cap_coords.insert(cap_coords.begin(), shifted_coord); cap_coords_left.insert(cap_coords_left.begin(), shifted_coord_left); } else { cap_flags.emplace_back(); cap_coords.emplace_back(shifted_coord); cap_coords_left.emplace_back(shifted_coord_left); } } } // Concatenate left and right side coordinates cap_flags.reserve(2 * cap_flags.size()); cap_coords.reserve(2 * cap_coords.size()); MapCoord curve_start; curve_start.setCurveStart(true); if (!is_end) { for (auto i = cap_coords_left.size(); i > 0; ) { --i; if (i >= 3 && cap_flags[i-3].isCurveStart()) { cap_flags.emplace_back(curve_start); cap_flags.emplace_back(); cap_flags.emplace_back(); cap_coords.emplace_back(cap_coords_left[i]); cap_coords.emplace_back(cap_coords_left[i-1]); cap_coords.emplace_back(cap_coords_left[i-2]); i -= 2; } else { cap_flags.emplace_back(); cap_coords.emplace_back(cap_coords_left[i]); } } } else { for (auto i = cap_coords_left.size() - 1; i > 0; ) { --i; if (i >= 3 && cap_flags[i - 3].isCurveStart()) { cap_flags.emplace_back(curve_start); cap_flags.emplace_back(); cap_flags.emplace_back(); cap_coords.emplace_back(cap_coords_left[i]); cap_coords.emplace_back(cap_coords_left[i-1]); cap_coords.emplace_back(cap_coords_left[i-2]); i -= 2; } else if (i >= 2 && i == cap_coords_left.size() - 2 && cap_flags[i - 2].isCurveStart()) { cap_flags[cap_flags.size() - 1].setCurveStart(true); cap_flags.emplace_back(); cap_flags.emplace_back(); cap_coords.emplace_back(cap_coords_left[i]); cap_coords.emplace_back(cap_coords_left[i-1]); i -= 1; } else { cap_flags.emplace_back(); cap_coords.emplace_back(cap_coords_left[i]); } } } // Add renderable Q_ASSERT(cap_coords.size() >= 3); Q_ASSERT(cap_coords.size() == cap_flags.size()); VirtualPath cap_path { cap_flags, cap_coords }; cap_path.path_coords.update(0); output.insertRenderable(new AreaRenderable(&area_symbol, cap_path)); } void LineSymbol::processDashedLine( const VirtualPath& path, bool path_closed, MapCoordVector& out_flags, MapCoordVectorF& out_coords, ObjectRenderables& output ) const { auto& path_coords = path.path_coords; Q_ASSERT(!path_coords.empty()); auto out_coords_size = path.size() * 4; out_flags.reserve(out_coords_size); out_coords.reserve(out_coords_size); auto last = path.last_index; auto groups_start = SplitPathCoord::begin(path_coords); auto line_start = groups_start; for (bool is_part_end = false; !is_part_end; ) { auto groups_end_path_coord_index = path_coords.findNextDashPoint(groups_start.path_coord_index); auto groups_end_index = path_coords[groups_end_path_coord_index].index; auto groups_end = SplitPathCoord::at(path_coords, groups_end_path_coord_index); // Intentionally look at groups_start (current node), // not line_start (where to continue drawing)! bool is_part_start = (groups_start.index == path.first_index); is_part_end = (groups_end_index == last); line_start = createDashGroups(path, path_closed, line_start, groups_start, groups_end, is_part_start, is_part_end, out_flags, out_coords, output); groups_start = groups_end; // Search then next split (node) after groups_end (current node). } Q_ASSERT(line_start.clen == groups_start.clen); } SplitPathCoord LineSymbol::createDashGroups( const VirtualPath& path, bool path_closed, const SplitPathCoord& line_start, const SplitPathCoord& start, const SplitPathCoord& end, bool is_part_start, bool is_part_end, MapCoordVector& out_flags, MapCoordVectorF& out_coords, ObjectRenderables& output ) const { auto& flags = path.coords.flags; auto& path_coords = path.path_coords; auto orientation = 0.0f; bool mid_symbol_rotatable = bool(mid_symbol) && mid_symbol->isRotatable(); double mid_symbol_distance_f = 0.001 * mid_symbol_distance; bool half_first_group = is_part_start ? (half_outer_dashes || path_closed) : (flags[start.index].isDashPoint() && dashes_in_group == 1); bool ends_with_dashpoint = is_part_end ? path_closed : true; double dash_length_f = 0.001 * dash_length; double break_length_f = 0.001 * break_length; double in_group_break_length_f = 0.001 * in_group_break_length; double total_in_group_dash_length = dashes_in_group * dash_length_f; double total_in_group_break_length = (dashes_in_group - 1) * in_group_break_length_f; double total_group_length = total_in_group_dash_length + total_in_group_break_length; double total_group_and_break_length = total_group_length + break_length_f; double length = end.clen - start.clen; double length_plus_break = length + break_length_f; bool half_last_group = is_part_end ? (half_outer_dashes || path_closed) : (ends_with_dashpoint && dashes_in_group == 1); int num_half_groups = (half_first_group ? 1 : 0) + (half_last_group ? 1 : 0); double num_dashgroups_f = num_half_groups + (length_plus_break - num_half_groups * 0.5 * dash_length_f) / total_group_and_break_length; int lower_dashgroup_count = qRound(floor(num_dashgroups_f)); double minimum_optimum_num_dashes = dashes_in_group * 2.0 - num_half_groups * 0.5; double minimum_optimum_length = 2.0 * total_group_and_break_length; double switch_deviation = 0.2 * total_group_and_break_length / dashes_in_group; bool set_mid_symbols = length >= dash_length_f - switch_deviation; if (mid_symbols_per_spot > 0 && mid_symbol && !mid_symbol->isEmpty()) { if (line_start.clen < start.clen || (!is_part_start && flags[start.index].isDashPoint())) { set_mid_symbols = false; // Handle mid symbols at begin for explicit dash points when we draw the whole dash here auto split = SplitPathCoord::begin(path_coords); PathCoord::length_type position = start.clen - (mid_symbols_per_spot - 1) * 0.5 * mid_symbol_distance_f; for (int s = 0; s < mid_symbols_per_spot; s+=2) { if (position >= split.clen) { auto next_split = SplitPathCoord::at(position, split); if (mid_symbol_rotatable) orientation = next_split.tangentVector().angle(); mid_symbol->createRenderablesScaled(next_split.pos, orientation, output); split = next_split; } position += mid_symbol_distance_f; } } if (half_first_group) { // Handle mid symbols at start for closing point or explicit dash points. auto split = start; PathCoord::length_type position = split.clen + (mid_symbols_per_spot%2 + 1) * 0.5 * mid_symbol_distance_f; for (int s = 1; s < mid_symbols_per_spot && position <= path_coords.back().clen; s+=2) { auto next_split = SplitPathCoord::at(position, split); if (mid_symbol_rotatable) orientation = next_split.tangentVector().angle(); mid_symbol->createRenderablesScaled(next_split.pos, orientation, output); position += mid_symbol_distance_f; split = next_split; } } } if (length <= 0.0 || (lower_dashgroup_count <= 1 && length < minimum_optimum_length - minimum_optimum_num_dashes * switch_deviation)) { // Line part too short for dashes if (is_part_end) { // Can't be handled correctly by the next round of dash groups drawing: // Just draw a continuous line. processContinuousLine(path, line_start, end, !half_first_group, !half_last_group, out_flags, out_coords, set_mid_symbols, output); } else { // Give this length to the next round of dash groups drawing. return line_start; } } else { int higher_dashgroup_count = qRound(ceil(num_dashgroups_f)); double lower_dashgroup_deviation = (length_plus_break - lower_dashgroup_count * total_group_and_break_length) / lower_dashgroup_count; double higher_dashgroup_deviation = (higher_dashgroup_count * total_group_and_break_length - length_plus_break) / higher_dashgroup_count; Q_ASSERT(half_first_group || half_last_group || (lower_dashgroup_deviation >= -0.001 && higher_dashgroup_deviation >= -0.001)); // TODO; seems to fail as long as halving first/last dashes affects the outermost dash only int num_dashgroups = (lower_dashgroup_deviation > higher_dashgroup_deviation) ? higher_dashgroup_count : lower_dashgroup_count; Q_ASSERT(num_dashgroups >= 2); int num_half_dashes = 2*num_dashgroups*dashes_in_group - num_half_groups; double adjusted_dash_length = (length - (num_dashgroups-1) * break_length_f - num_dashgroups * total_in_group_break_length) / (0.5 * num_half_dashes); adjusted_dash_length = qMax(adjusted_dash_length, 0.0); // could be negative for large break lengths double cur_length = start.clen; SplitPathCoord dash_start = line_start; for (int dashgroup = 1; dashgroup <= num_dashgroups; ++dashgroup) { bool is_first_dashgroup = dashgroup == 1; bool is_last_dashgroup = dashgroup == num_dashgroups; for (int dash = 1; dash <= dashes_in_group; ++dash) { // Draw a single dash bool is_first_dash = is_first_dashgroup && dash == 1; bool is_last_dash = is_last_dashgroup && dash == dashes_in_group; // The dash has an start if it is not the first dash in a half first group. bool has_start = !(is_first_dash && half_first_group); // The dash has an end if it is not the last dash in a half last group. bool has_end = !(is_last_dash && half_last_group); Q_ASSERT(has_start || has_end); // At least one half must be left... bool is_half_dash = has_start != has_end; double cur_dash_length = is_half_dash ? adjusted_dash_length / 2 : adjusted_dash_length; set_mid_symbols = !is_half_dash; if (!is_first_dash) { auto next_dash_start = SplitPathCoord::at(cur_length, dash_start); path.copy(dash_start, next_dash_start, out_flags, out_coords); out_flags.back().setGapPoint(true); dash_start = next_dash_start; } if (is_last_dash && !is_part_end) { // Last dash before a dash point (which is not the closing point): // Give the remaining length to the next round of dash groups drawing. return dash_start; } SplitPathCoord dash_end = SplitPathCoord::at(cur_length + cur_dash_length, dash_start); processContinuousLine(path, dash_start, dash_end, has_start, has_end, out_flags, out_coords, set_mid_symbols, output); cur_length += cur_dash_length; dash_start = dash_end; if (dash < dashes_in_group) cur_length += in_group_break_length_f; } if (dashgroup < num_dashgroups) cur_length += break_length_f; } if (half_last_group && mid_symbols_per_spot > 0 && mid_symbol && !mid_symbol->isEmpty()) { // Handle mid symbols at end for closing point or for (some) explicit dash points. auto split = start; PathCoord::length_type position = end.clen - (mid_symbols_per_spot-1) * 0.5 * mid_symbol_distance_f; for (int s = 0; s < mid_symbols_per_spot; s+=2) { auto next_split = SplitPathCoord::at(position, split); if (mid_symbol_rotatable) orientation = next_split.tangentVector().angle(); mid_symbol->createRenderablesScaled(next_split.pos, orientation, output); position += mid_symbol_distance_f; split = next_split; } } } return end; } void LineSymbol::createDashSymbolRenderables( const VirtualPath& path, bool path_closed , ObjectRenderables& output) const { Q_ASSERT(dash_symbol); auto& flags = path.coords.flags; auto& coords = path.coords; auto last = path.last_index; auto i = path.first_index; if (suppress_dash_symbol_at_ends && path.size() > 0) { // Suppress dash symbol at line ends ++i; --last; } else if (path_closed) { ++i; } for (; i <= last; ++i) { if (flags[i].isDashPoint()) { const auto params = path.calculateTangentScaling(i); //params.first.perpRight(); auto rotation = dash_symbol->isRotatable() ? params.first.angle() : 0.0; auto scale = scale_dash_symbol ? qMin(params.second, 2.0 * LineSymbol::miterLimit()) : 1.0; dash_symbol->createRenderablesScaled(coords[i], rotation, output, scale); } } } void LineSymbol::createMidSymbolRenderables( const VirtualPath& path, bool path_closed, ObjectRenderables& output) const { Q_ASSERT(mid_symbol); auto orientation = 0.0f; bool mid_symbol_rotatable = bool(mid_symbol) && mid_symbol->isRotatable(); int mid_symbol_num_gaps = mid_symbols_per_spot - 1; double segment_length_f = 0.001 * segment_length; double end_length_f = 0.001 * end_length; double end_length_twice_f = 0.002 * end_length; double mid_symbol_distance_f = 0.001 * mid_symbol_distance; double mid_symbols_length = mid_symbol_num_gaps * mid_symbol_distance_f; auto& path_coords = path.path_coords; Q_ASSERT(!path_coords.empty()); auto groups_start = SplitPathCoord::begin(path_coords); if (end_length == 0 && !path_closed) { // Insert point at start coordinate if (mid_symbol_rotatable) orientation = groups_start.tangentVector().angle(); mid_symbol->createRenderablesScaled(groups_start.pos, orientation, output); } auto part_end = path.last_index; while (groups_start.index != part_end) { auto groups_end_path_coord_index = path_coords.findNextDashPoint(groups_start.path_coord_index); auto groups_end = SplitPathCoord::at(path_coords, groups_end_path_coord_index); // The total length of the current continuous part double length = groups_end.clen - groups_start.clen; // The length which is available for placing mid symbols double segmented_length = qMax(0.0, length - end_length_twice_f) - mid_symbols_length; // The number of segments to be created by mid symbols double segment_count_raw = qMax((end_length == 0) ? 1.0 : 0.0, (segmented_length / (segment_length_f + mid_symbols_length))); int lower_segment_count = qFloor(segment_count_raw); int higher_segment_count = qCeil(segment_count_raw); if (end_length > 0) { if (length <= mid_symbols_length) { if (show_at_least_one_symbol) { // Insert point at start coordinate if (mid_symbol_rotatable) orientation = groups_start.tangentVector().angle(); mid_symbol->createRenderablesScaled(groups_start.pos, orientation, output); // Insert point at end coordinate if (mid_symbol_rotatable) orientation = groups_end.tangentVector().angle(); mid_symbol->createRenderablesScaled(groups_end.pos, orientation, output); } } else { double lower_abs_deviation = qAbs(length - lower_segment_count * segment_length_f - (lower_segment_count+1)*mid_symbols_length - end_length_twice_f); double higher_abs_deviation = qAbs(length - higher_segment_count * segment_length_f - (higher_segment_count+1)*mid_symbols_length - end_length_twice_f); int segment_count = (lower_abs_deviation >= higher_abs_deviation) ? higher_segment_count : lower_segment_count; double deviation = (lower_abs_deviation >= higher_abs_deviation) ? -higher_abs_deviation : lower_abs_deviation; double ideal_length = segment_count * segment_length_f + end_length_twice_f; double adjusted_end_length = end_length_f + deviation * (end_length_f / ideal_length); double adjusted_segment_length = segment_length_f + deviation * (segment_length_f / ideal_length); Q_ASSERT(qAbs(2*adjusted_end_length + segment_count*adjusted_segment_length + (segment_count + 1)*mid_symbols_length - length) < 0.001); if (adjusted_segment_length >= 0 && (show_at_least_one_symbol || higher_segment_count > 0 || length > end_length_twice_f - 0.5 * (segment_length_f + mid_symbols_length))) { adjusted_segment_length += mid_symbols_length; auto split = groups_start; for (int i = 0; i < segment_count + 1; ++i) { double position = groups_start.clen + adjusted_end_length + i * adjusted_segment_length - mid_symbol_distance_f; for (int s = 0; s < mid_symbols_per_spot; ++s) { position += mid_symbol_distance_f; split = SplitPathCoord::at(position, split); if (mid_symbol_rotatable) orientation = split.tangentVector().angle(); mid_symbol->createRenderablesScaled(split.pos, orientation, output); } } } } } else { // end_length == 0 if (length > mid_symbols_length) { double lower_segment_deviation = qAbs(length - lower_segment_count * segment_length_f - (lower_segment_count+1)*mid_symbols_length) / lower_segment_count; double higher_segment_deviation = qAbs(length - higher_segment_count * segment_length_f - (higher_segment_count+1)*mid_symbols_length) / higher_segment_count; int segment_count = (lower_segment_deviation > higher_segment_deviation) ? higher_segment_count : lower_segment_count; double adapted_segment_length = (length - (segment_count+1)*mid_symbols_length) / segment_count + mid_symbols_length; Q_ASSERT(qAbs(segment_count * adapted_segment_length + mid_symbols_length) - length < 0.001f); if (adapted_segment_length >= mid_symbols_length) { auto split = groups_start; for (int i = 0; i <= segment_count; ++i) { double position = groups_start.clen + i * adapted_segment_length - mid_symbol_distance_f; for (int s = 0; s < mid_symbols_per_spot; ++s) { position += mid_symbol_distance_f; // The outermost symbols are handled outside this loop if (i == 0 && s == 0) continue; if (i == segment_count && s == mid_symbol_num_gaps) break; split = SplitPathCoord::at(position, split); if (mid_symbol_rotatable) orientation = split.tangentVector().angle(); mid_symbol->createRenderablesScaled(split.pos, orientation, output); } } } } // Insert point at end coordinate if (mid_symbol_rotatable) orientation = groups_end.tangentVector().angle(); mid_symbol->createRenderablesScaled(groups_end.pos, orientation, output); } groups_start = groups_end; // Search then next split (node) after groups_end (current node). } } void LineSymbol::colorDeleted(const MapColor* color) { bool have_changes = false; if (mid_symbol && mid_symbol->containsColor(color)) { mid_symbol->colorDeleted(color); have_changes = true; } if (start_symbol && start_symbol->containsColor(color)) { start_symbol->colorDeleted(color); have_changes = true; } if (end_symbol && end_symbol->containsColor(color)) { end_symbol->colorDeleted(color); have_changes = true; } if (dash_symbol && dash_symbol->containsColor(color)) { dash_symbol->colorDeleted(color); have_changes = true; } if (color == this->color) { this->color = nullptr; have_changes = true; } if (color == border.color) { this->border.color = nullptr; have_changes = true; } if (color == right_border.color) { this->right_border.color = nullptr; have_changes = true; } if (have_changes) resetIcon(); } bool LineSymbol::containsColor(const MapColor* color) const { if (color == this->color || color == border.color || color == right_border.color) return true; if (mid_symbol && mid_symbol->containsColor(color)) return true; if (start_symbol && start_symbol->containsColor(color)) return true; if (end_symbol && end_symbol->containsColor(color)) return true; if (dash_symbol && dash_symbol->containsColor(color)) return true; return false; } const MapColor* LineSymbol::guessDominantColor() const { bool has_main_line = line_width > 0 && color; bool has_border = hasBorder() && border.width > 0 && border.color; bool has_right_border = hasBorder() && right_border.width > 0 && right_border.color; // Use the color of the thickest line if (has_main_line) { if (has_border) { if (has_right_border) { if (line_width > 2 * border.width) return (line_width > 2 * right_border.width) ? color : right_border.color; else return (border.width > right_border.width) ? border.color : right_border.color; } else return (line_width > 2 * border.width) ? color : border.color; } else { if (has_right_border) return (line_width > 2 * right_border.width) ? color : right_border.color; else return color; } } else { if (has_border) { if (has_right_border) return (border.width > right_border.width) ? border.color : right_border.color; else return border.color; } else { if (has_right_border) return right_border.color; } } const MapColor* dominant_color = mid_symbol ? mid_symbol->guessDominantColor() : nullptr; if (dominant_color) return dominant_color; dominant_color = start_symbol ? start_symbol->guessDominantColor() : nullptr; if (dominant_color) return dominant_color; dominant_color = end_symbol ? end_symbol->guessDominantColor() : nullptr; if (dominant_color) return dominant_color; dominant_color = dash_symbol ? dash_symbol->guessDominantColor() : nullptr; if (dominant_color) return dominant_color; return nullptr; } void LineSymbol::scale(double factor) { line_width = qRound(factor * line_width); minimum_length = qRound(factor * minimum_length); pointed_cap_length = qRound(factor * pointed_cap_length); if (start_symbol) start_symbol->scale(factor); if (mid_symbol) mid_symbol->scale(factor); if (end_symbol) end_symbol->scale(factor); if (dash_symbol) dash_symbol->scale(factor); segment_length = qRound(factor * segment_length); end_length = qRound(factor * end_length); minimum_mid_symbol_count = qRound(factor * minimum_mid_symbol_count); minimum_mid_symbol_count_when_closed = qRound(factor * minimum_mid_symbol_count_when_closed); dash_length = qRound(factor * dash_length); break_length = qRound(factor * break_length); in_group_break_length = qRound(factor * in_group_break_length); mid_symbol_distance = qRound(factor * mid_symbol_distance); border.scale(factor); right_border.scale(factor); resetIcon(); } void LineSymbol::ensurePointSymbols(const QString& start_name, const QString& mid_name, const QString& end_name, const QString& dash_name) { if (!start_symbol) { start_symbol = new PointSymbol(); start_symbol->setRotatable(true); } start_symbol->setName(start_name); if (!mid_symbol) { mid_symbol = new PointSymbol(); mid_symbol->setRotatable(true); } mid_symbol->setName(mid_name); if (!end_symbol) { end_symbol = new PointSymbol(); end_symbol->setRotatable(true); } end_symbol->setName(end_name); if (!dash_symbol) { dash_symbol = new PointSymbol(); dash_symbol->setRotatable(true); } dash_symbol->setName(dash_name); } void LineSymbol::cleanupPointSymbols() { if (start_symbol && start_symbol->isEmpty()) { delete start_symbol; start_symbol = nullptr; } if (mid_symbol && mid_symbol->isEmpty()) { delete mid_symbol; mid_symbol = nullptr; } if (end_symbol && end_symbol->isEmpty()) { delete end_symbol; end_symbol = nullptr; } if (dash_symbol && dash_symbol->isEmpty()) { delete dash_symbol; dash_symbol = nullptr; } } qreal LineSymbol::dimensionForIcon() const { // calculateLargestLineExtent() gives half line width, and for the icon, // we suggest a half line width of white space on each side. auto size = 4 * calculateLargestLineExtent(); if (start_symbol && !start_symbol->isEmpty()) size = qMax(size, start_symbol->dimensionForIcon()); if (mid_symbol && !mid_symbol->isEmpty()) size = qMax(size, 2 * mid_symbol->dimensionForIcon() + getMidSymbolDistance() * (getMidSymbolsPerSpot() - 1) / 1000); if (dash_symbol && !dash_symbol->isEmpty()) size = qMax(size, 2 * dash_symbol->dimensionForIcon()); if (end_symbol && !end_symbol->isEmpty()) size = qMax(size, end_symbol->dimensionForIcon()); return size; } qreal LineSymbol::calculateLargestLineExtent() const { auto line_extent_f = 0.001 * 0.5 * getLineWidth(); auto result = line_extent_f; if (hasBorder()) { result = qMax(result, line_extent_f + 0.001 * (getBorder().shift + getBorder().width)/2); result = qMax(result, line_extent_f + 0.001 * (getRightBorder().shift + getRightBorder().width)/2); } return result; } void LineSymbol::setStartSymbol(PointSymbol* symbol) { replaceSymbol(start_symbol, symbol, QCoreApplication::translate("OpenOrienteering::LineSymbolSettings", "Start symbol")); } void LineSymbol::setMidSymbol(PointSymbol* symbol) { replaceSymbol(mid_symbol, symbol, QCoreApplication::translate("OpenOrienteering::LineSymbolSettings", "Mid symbol")); } void LineSymbol::setEndSymbol(PointSymbol* symbol) { replaceSymbol(end_symbol, symbol, QCoreApplication::translate("OpenOrienteering::LineSymbolSettings", "End symbol")); } void LineSymbol::setDashSymbol(PointSymbol* symbol) { replaceSymbol(dash_symbol, symbol, QCoreApplication::translate("OpenOrienteering::LineSymbolSettings", "Dash symbol")); } void LineSymbol::replaceSymbol(PointSymbol*& old_symbol, PointSymbol* replace_with, const QString& name) { delete old_symbol; old_symbol = replace_with; replace_with->setName(name); } #ifndef NO_NATIVE_FILE_FORMAT bool LineSymbol::loadImpl(QIODevice* file, int version, Map* map) { file->read((char*)&line_width, sizeof(int)); int temp; file->read((char*)&temp, sizeof(int)); color = (temp >= 0) ? map->getColor(temp) : nullptr; if (version >= 2) file->read((char*)&minimum_length, sizeof(int)); file->read((char*)&temp, sizeof(int)); cap_style = (CapStyle)temp; file->read((char*)&temp, sizeof(int)); join_style = (JoinStyle)temp; file->read((char*)&pointed_cap_length, sizeof(int)); bool have_symbol; file->read((char*)&have_symbol, sizeof(bool)); if (have_symbol) { start_symbol = new PointSymbol(); if (!start_symbol->load(file, version, map)) return false; } file->read((char*)&have_symbol, sizeof(bool)); if (have_symbol) { mid_symbol = new PointSymbol(); if (!mid_symbol->load(file, version, map)) return false; } file->read((char*)&have_symbol, sizeof(bool)); if (have_symbol) { end_symbol = new PointSymbol(); if (!end_symbol->load(file, version, map)) return false; } file->read((char*)&have_symbol, sizeof(bool)); if (have_symbol) { dash_symbol = new PointSymbol(); if (!dash_symbol->load(file, version, map)) return false; } file->read((char*)&dashed, sizeof(bool)); file->read((char*)&segment_length, sizeof(int)); if (version >= 1) file->read((char*)&end_length, sizeof(int)); if (version >= 5) file->read((char*)&show_at_least_one_symbol, sizeof(bool)); if (version >= 2) { file->read((char*)&minimum_mid_symbol_count, sizeof(int)); file->read((char*)&minimum_mid_symbol_count_when_closed, sizeof(int)); } file->read((char*)&dash_length, sizeof(int)); file->read((char*)&break_length, sizeof(int)); file->read((char*)&dashes_in_group, sizeof(int)); file->read((char*)&in_group_break_length, sizeof(int)); file->read((char*)&half_outer_dashes, sizeof(bool)); file->read((char*)&mid_symbols_per_spot, sizeof(int)); file->read((char*)&mid_symbol_distance, sizeof(int)); file->read((char*)&have_border_lines, sizeof(bool)); if (version <= 22) { if (!border.load(file, version, map)) return false; right_border.assign(border, nullptr); } else { bool are_borders_different; file->read((char*)&are_borders_different, sizeof(bool)); if (!border.load(file, version, map)) return false; if (are_borders_different) { if (!right_border.load(file, version, map)) return false; } else right_border.assign(border, nullptr); } cleanupPointSymbols(); return true; } #endif void LineSymbol::saveImpl(QXmlStreamWriter& xml, const Map& map) const { xml.writeStartElement(QString::fromLatin1("line_symbol")); xml.writeAttribute(QString::fromLatin1("color"), QString::number(map.findColorIndex(color))); xml.writeAttribute(QString::fromLatin1("line_width"), QString::number(line_width)); xml.writeAttribute(QString::fromLatin1("minimum_length"), QString::number(minimum_length)); xml.writeAttribute(QString::fromLatin1("join_style"), QString::number(join_style)); xml.writeAttribute(QString::fromLatin1("cap_style"), QString::number(cap_style)); xml.writeAttribute(QString::fromLatin1("pointed_cap_length"), QString::number(pointed_cap_length)); if (dashed) xml.writeAttribute(QString::fromLatin1("dashed"), QString::fromLatin1("true")); xml.writeAttribute(QString::fromLatin1("segment_length"), QString::number(segment_length)); xml.writeAttribute(QString::fromLatin1("end_length"), QString::number(end_length)); if (show_at_least_one_symbol) xml.writeAttribute(QString::fromLatin1("show_at_least_one_symbol"), QString::fromLatin1("true")); xml.writeAttribute(QString::fromLatin1("minimum_mid_symbol_count"), QString::number(minimum_mid_symbol_count)); xml.writeAttribute(QString::fromLatin1("minimum_mid_symbol_count_when_closed"), QString::number(minimum_mid_symbol_count_when_closed)); xml.writeAttribute(QString::fromLatin1("dash_length"), QString::number(dash_length)); xml.writeAttribute(QString::fromLatin1("break_length"), QString::number(break_length)); xml.writeAttribute(QString::fromLatin1("dashes_in_group"), QString::number(dashes_in_group)); xml.writeAttribute(QString::fromLatin1("in_group_break_length"), QString::number(in_group_break_length)); if (half_outer_dashes) xml.writeAttribute(QString::fromLatin1("half_outer_dashes"), QString::fromLatin1("true")); xml.writeAttribute(QString::fromLatin1("mid_symbols_per_spot"), QString::number(mid_symbols_per_spot)); xml.writeAttribute(QString::fromLatin1("mid_symbol_distance"), QString::number(mid_symbol_distance)); if (suppress_dash_symbol_at_ends) xml.writeAttribute(QString::fromLatin1("suppress_dash_symbol_at_ends"), QString::fromLatin1("true")); if (!scale_dash_symbol) xml.writeAttribute(QString::fromLatin1("scale_dash_symbol"), QString::fromLatin1("false")); if (start_symbol) { xml.writeStartElement(QString::fromLatin1("start_symbol")); start_symbol->save(xml, map); xml.writeEndElement(); } if (mid_symbol) { xml.writeStartElement(QString::fromLatin1("mid_symbol")); mid_symbol->save(xml, map); xml.writeEndElement(); } if (end_symbol) { xml.writeStartElement(QString::fromLatin1("end_symbol")); end_symbol->save(xml, map); xml.writeEndElement(); } if (dash_symbol) { xml.writeStartElement(QString::fromLatin1("dash_symbol")); dash_symbol->save(xml, map); xml.writeEndElement(); } if (have_border_lines) { xml.writeStartElement(QString::fromLatin1("borders")); bool are_borders_different = areBordersDifferent(); if (are_borders_different) xml.writeAttribute(QString::fromLatin1("borders_different"), QString::fromLatin1("true")); border.save(xml, map); if (are_borders_different) right_border.save(xml, map); xml.writeEndElement(/*borders*/); } xml.writeEndElement(/*line_symbol*/); } bool LineSymbol::loadImpl(QXmlStreamReader& xml, const Map& map, SymbolDictionary& symbol_dict) { if (xml.name() != QLatin1String("line_symbol")) return false; QXmlStreamAttributes attributes = xml.attributes(); int temp = attributes.value(QLatin1String("color")).toInt(); color = map.getColor(temp); line_width = attributes.value(QLatin1String("line_width")).toInt(); minimum_length = attributes.value(QLatin1String("minimum_length")).toInt(); join_style = static_cast(attributes.value(QLatin1String("join_style")).toInt()); cap_style = static_cast(attributes.value(QLatin1String("cap_style")).toInt()); pointed_cap_length = attributes.value(QLatin1String("pointed_cap_length")).toInt(); dashed = (attributes.value(QLatin1String("dashed")) == QLatin1String("true")); segment_length = attributes.value(QLatin1String("segment_length")).toInt(); end_length = attributes.value(QLatin1String("end_length")).toInt(); show_at_least_one_symbol = (attributes.value(QLatin1String("show_at_least_one_symbol")) == QLatin1String("true")); minimum_mid_symbol_count = attributes.value(QLatin1String("minimum_mid_symbol_count")).toInt(); minimum_mid_symbol_count_when_closed = attributes.value(QLatin1String("minimum_mid_symbol_count_when_closed")).toInt(); dash_length = attributes.value(QLatin1String("dash_length")).toInt(); break_length = attributes.value(QLatin1String("break_length")).toInt(); dashes_in_group = attributes.value(QLatin1String("dashes_in_group")).toInt(); in_group_break_length = attributes.value(QLatin1String("in_group_break_length")).toInt(); half_outer_dashes = (attributes.value(QLatin1String("half_outer_dashes")) == QLatin1String("true")); mid_symbols_per_spot = attributes.value(QLatin1String("mid_symbols_per_spot")).toInt(); mid_symbol_distance = attributes.value(QLatin1String("mid_symbol_distance")).toInt(); suppress_dash_symbol_at_ends = (attributes.value(QLatin1String("suppress_dash_symbol_at_ends")) == QLatin1String("true")); scale_dash_symbol = (attributes.value(QLatin1String("scale_dash_symbol")) != QLatin1String("false")); have_border_lines = false; while (xml.readNextStartElement()) { if (xml.name() == QLatin1String("start_symbol")) start_symbol = loadPointSymbol(xml, map, symbol_dict); else if (xml.name() == QLatin1String("mid_symbol")) mid_symbol = loadPointSymbol(xml, map, symbol_dict); else if (xml.name() == QLatin1String("end_symbol")) end_symbol = loadPointSymbol(xml, map, symbol_dict); else if (xml.name() == QLatin1String("dash_symbol")) dash_symbol = loadPointSymbol(xml, map, symbol_dict); else if (xml.name() == QLatin1String("borders")) { // bool are_borders_different = (xml.attributes().value("borders_different") == "true"); bool right_border_loaded = false; while (xml.readNextStartElement()) { if (xml.name() == QLatin1String("border")) { if (!have_border_lines) { border.load(xml, map); have_border_lines = true; } else { right_border.load(xml, map); right_border_loaded = true; xml.skipCurrentElement(); break; } } else xml.skipCurrentElement(); } if (have_border_lines && !right_border_loaded) right_border.assign(border, nullptr); } else xml.skipCurrentElement(); // unknown } cleanupPointSymbols(); return true; } PointSymbol* LineSymbol::loadPointSymbol(QXmlStreamReader& xml, const Map& map, SymbolDictionary& symbol_dict) { while (xml.readNextStartElement()) { if (xml.name() == QLatin1String("symbol")) { PointSymbol* symbol = static_cast(Symbol::load(xml, map, symbol_dict)); xml.skipCurrentElement(); return symbol; } else xml.skipCurrentElement(); } return nullptr; } bool LineSymbol::equalsImpl(const Symbol* other, Qt::CaseSensitivity case_sensitivity) const { Q_UNUSED(case_sensitivity); const LineSymbol* line = static_cast(other); if (line_width != line->line_width) return false; if (minimum_length != line->minimum_length) return false; if (line_width > 0) { if (!MapColor::equal(color, line->color)) return false; if ((cap_style != line->cap_style) || (join_style != line->join_style)) return false; if (cap_style == PointedCap && (pointed_cap_length != line->pointed_cap_length)) return false; if (dashed != line->dashed) return false; if (dashed) { if (dash_length != line->dash_length || break_length != line->break_length || dashes_in_group != line->dashes_in_group || half_outer_dashes != line->half_outer_dashes || (dashes_in_group > 1 && (in_group_break_length != line->in_group_break_length))) return false; } else { if (segment_length != line->segment_length || end_length != line->end_length || (mid_symbol && (show_at_least_one_symbol != line->show_at_least_one_symbol || minimum_mid_symbol_count != line->minimum_mid_symbol_count || minimum_mid_symbol_count_when_closed != line->minimum_mid_symbol_count_when_closed))) return false; } } if (bool(start_symbol) != bool(line->start_symbol)) return false; if (start_symbol && !start_symbol->equals(line->start_symbol)) return false; if (bool(mid_symbol) != bool(line->mid_symbol)) return false; if (mid_symbol && !mid_symbol->equals(line->mid_symbol)) return false; if (bool(end_symbol) != bool(line->end_symbol)) return false; if (end_symbol && !end_symbol->equals(line->end_symbol)) return false; if (bool(dash_symbol) != bool(line->dash_symbol)) return false; if (dash_symbol && !dash_symbol->equals(line->dash_symbol)) return false; if (suppress_dash_symbol_at_ends != line->suppress_dash_symbol_at_ends) return false; if (scale_dash_symbol != line->scale_dash_symbol) return false; if (mid_symbol) { if (mid_symbols_per_spot != line->mid_symbols_per_spot) return false; if (mid_symbol_distance != line->mid_symbol_distance) return false; } if (have_border_lines != line->have_border_lines) return false; if (have_border_lines) { if (!border.equals(&line->border) || !right_border.equals(&line->right_border)) return false; } return true; } } // namespace OpenOrienteering mapper-0.8.1.1/src/core/symbols/line_symbol.h000066400000000000000000000301261325266516600210550ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2012-2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_LINE_SYMBOL_H #define OPENORIENTEERING_LINE_SYMBOL_H #include // IWYU pragma: keep #include #include #include #include "core/map_coord.h" // IWYU pragma: keep #include "core/symbols/symbol.h" class QIODevice; class QXmlStreamReader; class QXmlStreamWriter; namespace OpenOrienteering { class LineSymbol; class Map; class MapColor; class MapColorMap; class Object; class ObjectRenderables; class PathObject; class PathPartVector; class PointSymbol; class SplitPathCoord; class SymbolPropertiesWidget; class SymbolSettingDialog; class VirtualCoordVector; class VirtualPath; using MapCoordVector = std::vector; using MapCoordVectorF = std::vector; /** Settings for a line symbol's border. */ struct LineSymbolBorder { const MapColor* color; int width; int shift; bool dashed; int dash_length; int break_length; void reset() noexcept; bool load(QIODevice* file, int version, Map* map); void save(QXmlStreamWriter& xml, const Map& map) const; bool load(QXmlStreamReader& xml, const Map& map); bool equals(const LineSymbolBorder* other) const; void assign(const LineSymbolBorder& other, const MapColorMap* color_map); bool isVisible() const; void createSymbol(LineSymbol& out) const; void scale(double factor); }; /** Symbol for PathObjects which displays a line along the path. */ class LineSymbol : public Symbol { friend class LineSymbolSettings; friend class PointSymbolEditorWidget; friend class OCAD8FileImport; public: enum CapStyle { FlatCap = 0, RoundCap = 1, SquareCap = 2, PointedCap = 3 }; enum JoinStyle { BevelJoin = 0, MiterJoin = 1, RoundJoin = 2 }; /** Constructs an empty line symbol. */ LineSymbol() noexcept; ~LineSymbol() override; Symbol* duplicate(const MapColorMap* color_map = nullptr) const override; bool validate() const override; void createRenderables( const Object *object, const VirtualCoordVector &coords, ObjectRenderables &output, Symbol::RenderableOptions options ) const override; void createRenderables( const PathObject* object, const PathPartVector& path_parts, ObjectRenderables &output, Symbol::RenderableOptions options ) const override; /** * Creates the renderables for a single path. * * @deprecated * * Calls to this function need to be replaced by calls to createPathCoordRenderables() * as soon as it is no longer neccesary to update the PathCoordVector in advance. */ void createPathRenderables(const Object* object, bool path_closed, const MapCoordVector& flags, const MapCoordVectorF& coords, ObjectRenderables& output) const; /** * Creates the renderables for a single VirtualPath. */ void createPathCoordRenderables(const Object* object, const VirtualPath& path, bool path_closed, ObjectRenderables& output) const; void colorDeleted(const MapColor* color) override; bool containsColor(const MapColor* color) const override; const MapColor* guessDominantColor() const override; void scale(double factor) override; /** * Creates empty point symbols with the given names for undefined subsymbols. * * After calling this method, all subsymbols are defined, i.e. not nullptr. * Call cleanupPointSymbols() later to remove the empty symbols. */ void ensurePointSymbols( const QString& start_name, const QString& mid_name, const QString& end_name, const QString& dash_name ); /** * Deletes unused point symbols and sets them to nullptr again. * * See ensurePointSymbols(). */ void cleanupPointSymbols(); /** * Returns the dimension which shall considered when scaling the icon. */ qreal dimensionForIcon() const override; /** * Returns the largest extent (half width) of the components of this line. */ qreal calculateLargestLineExtent() const override; /** * Returns the limit for miter joins in units of the line width. * See the Qt docs for QPainter::setMiterJoin(). * TODO: Should that better be a line property? * FIXME: shall be 0 for border lines. */ static constexpr qreal miterLimit() { return 1; } // Getters / Setters inline int getLineWidth() const {return line_width;} inline void setLineWidth(double width) {line_width = qRound(1000 * width);} inline const MapColor* getColor() const {return color;} inline void setColor(const MapColor* color) {this->color = color;} inline int getMinimumLength() const {return minimum_length;} inline void setMinimumLength(int length) {this->minimum_length = length;} inline CapStyle getCapStyle() const {return cap_style;} inline void setCapStyle(CapStyle style) {cap_style = style;} inline JoinStyle getJoinStyle() const {return join_style;} inline void setJoinStyle(JoinStyle style) {join_style = style;} inline int getPointedCapLength() const {return pointed_cap_length;} inline void setPointedCapLength(int value) {pointed_cap_length = value;} inline bool isDashed() const {return dashed;} inline void setDashed(bool value) {dashed = value;} inline PointSymbol* getStartSymbol() const {return start_symbol;} void setStartSymbol(PointSymbol* symbol); inline PointSymbol* getMidSymbol() const {return mid_symbol;} void setMidSymbol(PointSymbol* symbol); inline PointSymbol* getEndSymbol() const {return end_symbol;} void setEndSymbol(PointSymbol* symbol); inline PointSymbol* getDashSymbol() const {return dash_symbol;} void setDashSymbol(PointSymbol* symbol); inline int getMidSymbolsPerSpot() const {return mid_symbols_per_spot;} inline void setMidSymbolsPerSpot(int value) {mid_symbols_per_spot = value;} inline int getMidSymbolDistance() const {return mid_symbol_distance;} inline void setMidSymbolDistance(int value) {mid_symbol_distance = value;} inline bool getSuppressDashSymbolAtLineEnds() const {return suppress_dash_symbol_at_ends;} inline void setSuppressDashSymbolAtLineEnds(bool value) {suppress_dash_symbol_at_ends = value;} bool getScaleDashSymbol() const { return scale_dash_symbol; } void setScaleDashSymbol(bool value) { scale_dash_symbol = value; } inline int getSegmentLength() const {return segment_length;} inline void setSegmentLength(int value) {segment_length = value;} inline int getEndLength() const {return end_length;} inline void setEndLength(int value) {end_length = value;} inline bool getShowAtLeastOneSymbol() const {return show_at_least_one_symbol;} inline void setShowAtLeastOneSymbol(bool value) {show_at_least_one_symbol = value;} inline int getMinimumMidSymbolCount() const {return minimum_mid_symbol_count;} inline void setMinimumMidSymbolCount(int value) {minimum_mid_symbol_count = value;} inline int getMinimumMidSymbolCountWhenClosed() const {return minimum_mid_symbol_count_when_closed;} inline void setMinimumMidSymbolCountWhenClosed(int value) {minimum_mid_symbol_count_when_closed = value;} inline int getDashLength() const {return dash_length;} inline void setDashLength(int value) {dash_length = value;} inline int getBreakLength() const {return break_length;} inline void setBreakLength(int value) {break_length = value;} inline int getDashesInGroup() const {return dashes_in_group;} inline void setDashesInGroup(int value) {dashes_in_group = value;} inline int getInGroupBreakLength() const {return in_group_break_length;} inline void setInGroupBreakLength(int value) {in_group_break_length = value;} inline bool getHalfOuterDashes() const {return half_outer_dashes;} inline void setHalfOuterDashes(bool value) {half_outer_dashes = value;} inline bool hasBorder() const {return have_border_lines;} inline void setHasBorder(bool value) {have_border_lines = value;} inline bool areBordersDifferent() const {return !border.equals(&right_border);} inline LineSymbolBorder& getBorder() {return border;} inline const LineSymbolBorder& getBorder() const {return border;} inline LineSymbolBorder& getRightBorder() {return right_border;} inline const LineSymbolBorder& getRightBorder() const {return right_border;} SymbolPropertiesWidget* createPropertiesWidget(SymbolSettingDialog* dialog) override; protected: #ifndef NO_NATIVE_FILE_FORMAT bool loadImpl(QIODevice* file, int version, Map* map) override; #endif void saveImpl(QXmlStreamWriter& xml, const Map& map) const override; bool loadImpl(QXmlStreamReader& xml, const Map& map, SymbolDictionary& symbol_dict) override; PointSymbol* loadPointSymbol(QXmlStreamReader& xml, const Map& map, SymbolDictionary& symbol_dict); bool equalsImpl(const Symbol* other, Qt::CaseSensitivity case_sensitivity) const override; void createBorderLines( const Object* object, const VirtualPath& path, ObjectRenderables& output ) const; void createBorderLine( const Object* object, const VirtualPath& path, bool path_closed, ObjectRenderables& output, const LineSymbolBorder& border, double main_shift ) const; void shiftCoordinates( const VirtualPath& path, double main_shift, MapCoordVector& out_flags, MapCoordVectorF& out_coords ) const; void processContinuousLine( const VirtualPath& path, const SplitPathCoord& start, const SplitPathCoord& end, bool has_start, bool has_end, MapCoordVector& processed_flags, MapCoordVectorF& processed_coords, bool set_mid_symbols, ObjectRenderables& output ) const; void createPointedLineCap( const VirtualPath& path, const SplitPathCoord& start, const SplitPathCoord& end, bool is_end, ObjectRenderables& output ) const; void processDashedLine( const VirtualPath& path, bool path_closed, MapCoordVector& out_flags, MapCoordVectorF& out_coords, ObjectRenderables& output ) const; SplitPathCoord createDashGroups( const VirtualPath& path, bool path_closed, const SplitPathCoord& line_start, const SplitPathCoord& start, const SplitPathCoord& end, bool is_part_start, bool is_part_end, MapCoordVector& out_flags, MapCoordVectorF& out_coords, ObjectRenderables& output ) const; void createDashSymbolRenderables( const VirtualPath& path, bool path_closed, ObjectRenderables& output ) const; void createMidSymbolRenderables( const VirtualPath& path, bool path_closed, ObjectRenderables& output ) const; void replaceSymbol(PointSymbol*& old_symbol, PointSymbol* replace_with, const QString& name); // Base line int line_width; // in 1/1000 mm const MapColor* color; int minimum_length; CapStyle cap_style; JoinStyle join_style; int pointed_cap_length; bool dashed; // Point symbols PointSymbol* start_symbol; PointSymbol* mid_symbol; PointSymbol* end_symbol; PointSymbol* dash_symbol; int mid_symbols_per_spot; int mid_symbol_distance; bool suppress_dash_symbol_at_ends; bool scale_dash_symbol; // Not dashed int segment_length; int end_length; bool show_at_least_one_symbol; int minimum_mid_symbol_count; int minimum_mid_symbol_count_when_closed; // Dashed int dash_length; int break_length; int dashes_in_group; int in_group_break_length; bool half_outer_dashes; // Border lines bool have_border_lines; LineSymbolBorder border; LineSymbolBorder right_border; }; } // namespace OpenOrienteering #endif mapper-0.8.1.1/src/core/symbols/point_symbol.cpp000066400000000000000000000532161325266516600216170ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2012-2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "point_symbol.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "core/map.h" #include "core/map_color.h" #include "core/map_coord.h" #include "core/objects/object.h" #include "core/renderables/renderable.h" #include "core/renderables/renderable_implementation.h" #include "core/symbols/symbol.h" #include "core/virtual_coord_vector.h" #include "util/util.h" // IWYU pragma: no_forward_declare QPainterPath // IWYU pragma: no_forward_declare QXmlStreamReader // IWYU pragma: no_forward_declare QXmlStreamWriter namespace OpenOrienteering { PointSymbol::PointSymbol() noexcept : Symbol{Symbol::Point} , rotatable{false} , inner_radius{1000} , inner_color{nullptr} , outer_width{0} , outer_color{nullptr} { // nothing else } PointSymbol::~PointSymbol() { for (auto object : objects) delete object; for (auto symbol : symbols) delete symbol; } Symbol* PointSymbol::duplicate(const MapColorMap* color_map) const { auto new_point = new PointSymbol(); new_point->duplicateImplCommon(this); new_point->rotatable = rotatable; new_point->inner_radius = inner_radius; new_point->inner_color = color_map ? color_map->value(inner_color) : inner_color; new_point->outer_width = outer_width; new_point->outer_color = color_map ? color_map->value(outer_color) : outer_color; new_point->symbols.resize(symbols.size()); for (int i = 0; i < (int)symbols.size(); ++i) new_point->symbols[i] = symbols[i]->duplicate(color_map); new_point->objects.resize(objects.size()); for (int i = 0; i < (int)objects.size(); ++i) { new_point->objects[i] = objects[i]->duplicate(); new_point->objects[i]->setSymbol(new_point->symbols[i], true); } return new_point; } bool PointSymbol::validate() const { return std::all_of(begin(symbols), end(symbols), [](auto& symbol) { return symbol->validate(); }) && std::all_of(begin(objects), end(objects), [](auto& object) { return object->validate(); }); } void PointSymbol::createRenderables( const Object* object, const VirtualCoordVector& coords, ObjectRenderables& output, RenderableOptions options ) const { auto point = object->asPoint(); auto rotation = isRotatable() ? -point->getRotation() : 0.0f; if (options.testFlag(Symbol::RenderBaselines)) { const MapColor* dominant_color = guessDominantColor(); if (dominant_color) { PointSymbol* point = Map::getUndefinedPoint(); const MapColor* temp_color = point->getInnerColor(); point->setInnerColor(dominant_color); point->createRenderablesScaled(coords[0], rotation, output, 1.0f); point->setInnerColor(temp_color); } } else { createRenderablesScaled(coords[0], rotation, output, 1.0f); } } void PointSymbol::createRenderablesScaled(MapCoordF coord, float rotation, ObjectRenderables& output, float coord_scale) const { if (inner_color && inner_radius > 0) output.insertRenderable(new DotRenderable(this, coord)); if (outer_color && outer_width > 0) output.insertRenderable(new CircleRenderable(this, coord)); if (!objects.empty()) { auto offset_x = coord.x(); auto offset_y = coord.y(); auto cosr = 1.0; auto sinr = 0.0; if (rotation != 0.0) { cosr = cos(rotation); sinr = sin(rotation); } // Add elements which possibly need to be moved and rotated auto size = objects.size(); for (auto i = 0u; i < size; ++i) { // Point symbol elements should not be entered into the map, // otherwise map settings like area hatching affect them Q_ASSERT(!objects[i]->getMap()); const MapCoordVector& object_coords = objects[i]->getRawCoordinateVector(); MapCoordVectorF transformed_coords; transformed_coords.reserve(object_coords.size()); for (auto& coord : object_coords) { auto ox = coord_scale * coord.x(); auto oy = coord_scale * coord.y(); transformed_coords.emplace_back(ox * cosr - oy * sinr + offset_x, oy * cosr + ox * sinr + offset_y); } // TODO: if this point is rotated, it has to pass it on to its children to make it work that rotatable point objects can be children. // But currently only basic, rotationally symmetric points can be children, so it does not matter for now. symbols[i]->createRenderables(objects[i], VirtualCoordVector(object_coords, transformed_coords), output, Symbol::RenderNormal); } } } void PointSymbol::createRenderablesIfCenterInside(MapCoordF point_coord, qreal rotation, const QPainterPath* outline, ObjectRenderables& output) const { if (outline->contains(point_coord)) { if (inner_color && inner_radius > 0) { output.insertRenderable(new DotRenderable(this, point_coord)); } if (outer_color && outer_width > 0) { output.insertRenderable(new CircleRenderable(this, point_coord)); } } if (!objects.empty()) { auto offset_x = point_coord.x(); auto offset_y = point_coord.y(); auto cosr = 1.0; auto sinr = 0.0; if (rotation != 0.0) { cosr = qCos(rotation); sinr = qSin(rotation); } // Add elements which possibly need to be moved and rotated auto size = objects.size(); for (auto i = 0u; i < size; ++i) { const auto symbol = symbols[i]; const auto object = objects[i]; // Point symbol elements should not be entered into the map, // otherwise map settings like area hatching affect them Q_ASSERT(!object->getMap()); MapCoordF center{}; const MapCoordVector& element_coords = object->getRawCoordinateVector(); MapCoordVectorF transformed_coords; transformed_coords.reserve(element_coords.size()); for (auto& coord : element_coords) { auto ex = coord.x(); auto ey = coord.y(); transformed_coords.emplace_back(ex * cosr - ey * sinr + offset_x, ey * cosr + ex * sinr + offset_y); center += transformed_coords.back(); } if (!transformed_coords.empty() && outline->contains(center/transformed_coords.size())) { // TODO: if this point is rotated, it has to pass it on to its children to make it work that rotatable point objects can be children. // But currently only basic, rotationally symmetric points can be children, so it does not matter for now. symbol->createRenderables(object, VirtualCoordVector(element_coords, transformed_coords), output, Symbol::RenderNormal); } } } } void PointSymbol::createPrimitivesIfCompletelyInside(MapCoordF point_coord, const QPainterPath* outline, ObjectRenderables& output) const { if (inner_color && inner_radius > 0) { auto r = inner_radius/1000.0; if (outline->contains({point_coord.x()-r, point_coord.y()}) && outline->contains({point_coord.x(), point_coord.y()-r}) && outline->contains({point_coord.x()+r, point_coord.y()}) && outline->contains({point_coord.x(), point_coord.y()+r}) ) { output.insertRenderable(new DotRenderable(this, point_coord)); } } if (outer_color && outer_width > 0) { auto r = inner_radius/1000.0 + outer_width/2000.0; if (outline->contains({point_coord.x()-r, point_coord.y()}) && outline->contains({point_coord.x(), point_coord.y()-r}) && outline->contains({point_coord.x()+r, point_coord.y()}) && outline->contains({point_coord.x(), point_coord.y()+r}) ) { output.insertRenderable(new CircleRenderable(this, point_coord)); } } } void PointSymbol::createRenderablesIfCompletelyInside(MapCoordF point_coord, qreal rotation, const QPainterPath* outline, ObjectRenderables& output) const { createPrimitivesIfCompletelyInside(point_coord, outline, output); if (!objects.empty()) { auto offset_x = point_coord.x(); auto offset_y = point_coord.y(); auto cosr = 1.0; auto sinr = 0.0; if (rotation != 0.0) { cosr = qCos(rotation); sinr = qSin(rotation); } // Add elements which possibly need to be moved and rotated auto size = objects.size(); for (auto i = 0u; i < size; ++i) { const auto symbol = symbols[i]; const auto object = objects[i]; // Point symbol elements should not be entered into the map, // otherwise map settings like area hatching affect them Q_ASSERT(!object->getMap()); if (symbol->getType() == Symbol::Point) { auto coord = object->getRawCoordinateVector().front(); auto transformed_coord = MapCoordF{ coord.x() * cosr - coord.y() * sinr + offset_x, coord.y() * cosr + coord.x() * sinr + offset_y}; static_cast(symbol)->createPrimitivesIfCompletelyInside(transformed_coord, outline, output); continue; } const MapCoordVector& element_coords = object->getRawCoordinateVector(); MapCoordVectorF transformed_coords; transformed_coords.reserve(element_coords.size()); for (auto& coord : element_coords) { auto ex = coord.x(); auto ey = coord.y(); transformed_coords.emplace_back(ex * cosr - ey * sinr + offset_x, ey * cosr + ex * sinr + offset_y); if (!outline->contains(transformed_coords.back())) { transformed_coords.clear(); break; } } if (!transformed_coords.empty()) { // TODO: if this point is rotated, it has to pass it on to its children to make it work that rotatable point objects can be children. // But currently only basic, rotationally symmetric points can be children, so it does not matter for now. symbol->createRenderables(object, VirtualCoordVector(element_coords, transformed_coords), output, Symbol::RenderNormal); } } } } void PointSymbol::createPrimitivesIfPartiallyInside(MapCoordF point_coord, const QPainterPath* outline, ObjectRenderables& output) const { if (inner_color && inner_radius > 0) { auto r = inner_radius/1000.0; if (outline->contains({point_coord.x()-r, point_coord.y()}) || outline->contains({point_coord.x(), point_coord.y()-r}) || outline->contains({point_coord.x()+r, point_coord.y()}) || outline->contains({point_coord.x(), point_coord.y()+r}) ) { output.insertRenderable(new DotRenderable(this, point_coord)); } } if (outer_color && outer_width > 0) { auto r = inner_radius/1000.0 + outer_width/2000.0; if (outline->contains({point_coord.x()-r, point_coord.y()}) || outline->contains({point_coord.x(), point_coord.y()-r}) || outline->contains({point_coord.x()+r, point_coord.y()}) || outline->contains({point_coord.x(), point_coord.y()+r}) ) { output.insertRenderable(new CircleRenderable(this, point_coord)); } } } void PointSymbol::createRenderablesIfPartiallyInside(MapCoordF point_coord, qreal rotation, const QPainterPath* outline, ObjectRenderables& output) const { createPrimitivesIfPartiallyInside(point_coord, outline, output); if (!objects.empty()) { auto offset_x = point_coord.x(); auto offset_y = point_coord.y(); auto cosr = 1.0; auto sinr = 0.0; if (rotation != 0.0) { cosr = qCos(rotation); sinr = qSin(rotation); } // Add elements which possibly need to be moved and rotated auto size = objects.size(); for (auto i = 0u; i < size; ++i) { const auto symbol = symbols[i]; const auto object = objects[i]; // Point symbol elements should not be entered into the map, // otherwise map settings like area hatching affect them Q_ASSERT(!object->getMap()); if (symbol->getType() == Symbol::Point) { auto coord = object->getRawCoordinateVector().front(); auto transformed_coord = MapCoordF{ coord.x() * cosr - coord.y() * sinr + offset_x, coord.y() * cosr + coord.x() * sinr + offset_y}; static_cast(symbol)->createPrimitivesIfPartiallyInside(transformed_coord, outline, output); continue; } bool is_partially_inside = false; const MapCoordVector& element_coords = object->getRawCoordinateVector(); MapCoordVectorF transformed_coords; transformed_coords.reserve(element_coords.size()); for (auto& coord : element_coords) { auto ex = coord.x(); auto ey = coord.y(); transformed_coords.emplace_back(ex * cosr - ey * sinr + offset_x, ey * cosr + ex * sinr + offset_y); if (!is_partially_inside) is_partially_inside = outline->contains(transformed_coords.back()); } if (is_partially_inside) { // TODO: if this point is rotated, it has to pass it on to its children to make it work that rotatable point objects can be children. // But currently only basic, rotationally symmetric points can be children, so it does not matter for now. symbol->createRenderables(object, VirtualCoordVector(element_coords, transformed_coords), output, Symbol::RenderNormal); } } } } int PointSymbol::getNumElements() const { return (int)objects.size(); } void PointSymbol::addElement(int pos, Object* object, Symbol* symbol) { objects.insert(objects.begin() + pos, object); symbols.insert(symbols.begin() + pos, symbol); } Object* PointSymbol::getElementObject(int pos) { return objects[pos]; } const Object* PointSymbol::getElementObject(int pos) const { return objects[pos]; } Symbol* PointSymbol::getElementSymbol(int pos) { return symbols[pos]; } const Symbol* PointSymbol::getElementSymbol(int pos) const { return symbols[pos]; } void PointSymbol::deleteElement(int pos) { delete objects[pos]; objects.erase(objects.begin() + pos); delete symbols[pos]; symbols.erase(symbols.begin() + pos); } bool PointSymbol::isEmpty() const { return getNumElements() == 0 && (!inner_color || inner_radius == 0) && (!outer_color || outer_width == 0); } bool PointSymbol::isSymmetrical() const { int num_elements = (int)objects.size(); for (int i = 0; i < num_elements; ++i) { if (symbols[i]->getType() != Symbol::Point) return false; PointObject* point = reinterpret_cast(objects[i]); if (point->getCoordF() != MapCoordF(0, 0)) return false; } return true; } void PointSymbol::colorDeleted(const MapColor* color) { bool change = false; if (color == inner_color) { inner_color = nullptr; change = true; } if (color == outer_color) { outer_color = nullptr; change = true; } int num_elements = (int)objects.size(); for (int i = 0; i < num_elements; ++i) { symbols[i]->colorDeleted(color); change = true; } if (change) resetIcon(); } bool PointSymbol::containsColor(const MapColor* color) const { if (color == inner_color) return true; if (color == outer_color) return true; int num_elements = (int)objects.size(); for (int i = 0; i < num_elements; ++i) { if (symbols[i]->containsColor(color)) return true; } return false; } const MapColor* PointSymbol::guessDominantColor() const { bool have_inner_color = inner_color && inner_radius > 0; bool have_outer_color = outer_color && outer_width > 0; if (have_inner_color != have_outer_color) return have_inner_color ? inner_color : outer_color; else if (have_inner_color && have_outer_color) { if (inner_color->isWhite()) return outer_color; else if (outer_color->isWhite()) return inner_color; else return (qPow(inner_radius, 2) * M_PI > qPow(inner_radius + outer_width, 2) * M_PI - qPow(inner_radius, 2) * M_PI) ? inner_color : outer_color; } else { // Hope that the first element's color is representative if (symbols.size() > 0) return symbols[0]->guessDominantColor(); else return nullptr; } } void PointSymbol::scale(double factor) { inner_radius = qRound(inner_radius * factor); outer_width = qRound(outer_width * factor); int size = (int)objects.size(); for (int i = 0; i < size; ++i) { symbols[i]->scale(factor); objects[i]->scale(MapCoordF(0, 0), factor); } resetIcon(); } qreal PointSymbol::dimensionForIcon() const { auto size = qreal(0); if (getOuterColor()) size = 0.002 * (getInnerRadius() + getOuterWidth()); else if (getInnerColor()) size = 0.002 * getInnerRadius(); QRectF extent; for (int i = 0; i < getNumElements(); ++i) { auto object = std::unique_ptr(getElementObject(i)->duplicate()); object->setSymbol(getElementSymbol(i), true); object->update(); rectIncludeSafe(extent, object->getExtent()); object->clearRenderables(); } if (extent.isValid()) { auto w = 2 * std::max(std::abs(extent.left()), std::abs(extent.right())); auto h = 2 * std::max(std::abs(extent.top()), std::abs(extent.bottom())); return std::max(size, std::max(w, h)); } return size; } #ifndef NO_NATIVE_FILE_FORMAT bool PointSymbol::loadImpl(QIODevice* file, int version, Map* map) { file->read((char*)&rotatable, sizeof(bool)); file->read((char*)&inner_radius, sizeof(int)); int temp; file->read((char*)&temp, sizeof(int)); inner_color = (temp >= 0) ? map->getColor(temp) : nullptr; file->read((char*)&outer_width, sizeof(int)); file->read((char*)&temp, sizeof(int)); outer_color = (temp >= 0) ? map->getColor(temp) : nullptr; int num_elements; file->read((char*)&num_elements, sizeof(int)); objects.resize(num_elements); symbols.resize(num_elements); for (int i = 0; i < num_elements; ++i) { int save_type; file->read((char*)&save_type, sizeof(int)); symbols[i] = Symbol::getSymbolForType(static_cast(save_type)); if (!symbols[i]) return false; if (!symbols[i]->load(file, version, map)) return false; file->read((char*)&save_type, sizeof(int)); objects[i] = Object::getObjectForType(static_cast(save_type), symbols[i]); if (!objects[i]) return false; objects[i]->load(file, version, nullptr); } return true; } #endif void PointSymbol::saveImpl(QXmlStreamWriter& xml, const Map& map) const { xml.writeStartElement(QString::fromLatin1("point_symbol")); if (rotatable) xml.writeAttribute(QString::fromLatin1("rotatable"), QString::fromLatin1("true")); xml.writeAttribute(QString::fromLatin1("inner_radius"), QString::number(inner_radius)); xml.writeAttribute(QString::fromLatin1("inner_color"), QString::number(map.findColorIndex(inner_color))); xml.writeAttribute(QString::fromLatin1("outer_width"), QString::number(outer_width)); xml.writeAttribute(QString::fromLatin1("outer_color"), QString::number(map.findColorIndex(outer_color))); int num_elements = (int)objects.size(); xml.writeAttribute(QString::fromLatin1("elements"), QString::number(num_elements)); for (int i = 0; i < num_elements; ++i) { xml.writeStartElement(QString::fromLatin1("element")); symbols[i]->save(xml, map); objects[i]->save(xml); xml.writeEndElement(/*element*/); } xml.writeEndElement(/*point_symbol*/); } bool PointSymbol::loadImpl(QXmlStreamReader& xml, const Map& map, SymbolDictionary& symbol_dict) { if (xml.name() != QLatin1String("point_symbol")) return false; QXmlStreamAttributes attributes(xml.attributes()); rotatable = (attributes.value(QLatin1String("rotatable")) == QLatin1String("true")); inner_radius = attributes.value(QLatin1String("inner_radius")).toInt(); int temp = attributes.value(QLatin1String("inner_color")).toInt(); inner_color = (temp >= 0) ? map.getColor(temp) : nullptr; outer_width = attributes.value(QLatin1String("outer_width")).toInt(); temp = attributes.value(QLatin1String("outer_color")).toInt(); outer_color = (temp >= 0) ? map.getColor(temp) : nullptr; int num_elements = attributes.value(QLatin1String("elements")).toInt(); symbols.reserve(qMin(num_elements, 10)); // 10 is not a limit objects.reserve(qMin(num_elements, 10)); // 10 is not a limit for (int i = 0; xml.readNextStartElement(); ++i) { if (xml.name() == QLatin1String("element")) { while (xml.readNextStartElement()) { if (xml.name() == QLatin1String("symbol")) symbols.push_back(Symbol::load(xml, map, symbol_dict)); else if (xml.name() == QLatin1String("object")) objects.push_back(Object::load(xml, nullptr, symbol_dict, symbols.back())); else xml.skipCurrentElement(); // unknown element } } else xml.skipCurrentElement(); // unknown element } return true; } bool PointSymbol::equalsImpl(const Symbol* other, Qt::CaseSensitivity case_sensitivity) const { const PointSymbol* point = static_cast(other); if (rotatable != point->rotatable) return false; if (!MapColor::equal(inner_color, point->inner_color)) return false; if (inner_color && inner_radius != point->inner_radius) return false; if (!MapColor::equal(outer_color, point->outer_color)) return false; if (outer_color && outer_width != point->outer_width) return false; if (symbols.size() != point->symbols.size()) return false; // TODO: Comparing the contained elements in fixed order does not find every case of identical points symbols // (but at least if the point symbol has not been changed, it is always seen as identical). Could be improved. for (int i = 0, size = (int)objects.size(); i < size; ++i) { if (!symbols[i]->equals(point->symbols[i], case_sensitivity)) return false; if (!objects[i]->equals(point->objects[i], false)) return false; } return true; } } // namespace OpenOrienteering mapper-0.8.1.1/src/core/symbols/point_symbol.h000066400000000000000000000133061325266516600212600ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2012-2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_POINT_SYMBOL_H #define OPENORIENTEERING_POINT_SYMBOL_H #include #include #include #include "symbol.h" class QIODevice; class QPainterPath; class QXmlStreamReader; class QXmlStreamWriter; namespace OpenOrienteering { class Map; class MapColor; class MapColorMap; class MapCoordF; class Object; class ObjectRenderables; class SymbolPropertiesWidget; class SymbolSettingDialog; class VirtualCoordVector; /** * Symbol for PointObjects. * * Apart from its own settings which are presented to the user as "[Midpoint * symbol]" in the point symbol editor, this class can contain a list of * elements together with a symbol for each element. All elements and the * PointObject's own settings make up the appearance of the point symbol. * The reason the own settings are left in is just efficiency, as for some * symbols like crop land, a really huge number of point objects may be generated. */ class PointSymbol : public Symbol { friend class PointSymbolSettings; friend class PointSymbolEditorWidget; friend class OCAD8FileImport; friend class XMLImportExport; public: /** Constructs an empty point symbol. */ PointSymbol() noexcept; ~PointSymbol() override; Symbol* duplicate(const MapColorMap* color_map = nullptr) const override; bool validate() const override; void createRenderables( const Object *object, const VirtualCoordVector &coords, ObjectRenderables &output, RenderableOptions options ) const override; void createRenderablesScaled(MapCoordF coord, float rotation, ObjectRenderables& output, float coord_scale = 1.0f) const; void createRenderablesIfCenterInside(MapCoordF point_coord, qreal rotation, const QPainterPath* outline, ObjectRenderables& output) const; void createPrimitivesIfCompletelyInside(MapCoordF point_coord, const QPainterPath* outline, ObjectRenderables& output) const; void createRenderablesIfCompletelyInside(MapCoordF point_coord, qreal rotation, const QPainterPath* outline, ObjectRenderables& output) const; void createPrimitivesIfPartiallyInside(MapCoordF point_coord, const QPainterPath* outline, ObjectRenderables& output) const; void createRenderablesIfPartiallyInside(MapCoordF point_coord, qreal rotation, const QPainterPath* outline, ObjectRenderables& output) const; void colorDeleted(const MapColor* color) override; bool containsColor(const MapColor* color) const override; const MapColor* guessDominantColor() const override; void scale(double factor) override; qreal dimensionForIcon() const override; // Contained objects and symbols (elements) /** Returns the number of contained elements. */ int getNumElements() const; /** Adds a new element consisting of object and symbol at the given index. */ void addElement(int pos, Object* object, Symbol* symbol); /** Returns the object of the i-th element. */ Object* getElementObject(int pos); /** Returns the object of the i-th element. */ const Object* getElementObject(int pos) const; /** Returns the symbol of the i-th element. */ Symbol* getElementSymbol(int pos); /** Returns the symbol of the i-th element. */ const Symbol* getElementSymbol(int pos) const; /** Deletes the i-th element. */ void deleteElement(int pos); /** * Returns true if the point contains no elements and does not create * renderables itself. Useful to check if it can be deleted. */ bool isEmpty() const; /** * Checks if the contained elements are rotationally symmetrical around * the origin (this means, only point elements at (0,0) are allowed). */ bool isSymmetrical() const; // Getters / Setters inline bool isRotatable() const {return rotatable;} inline void setRotatable(bool enable) {rotatable = enable;} inline int getInnerRadius() const {return inner_radius;} inline void setInnerRadius(int value) {inner_radius = value;} inline const MapColor* getInnerColor() const {return inner_color;} inline void setInnerColor(const MapColor* color) {inner_color = color;} inline int getOuterWidth() const {return outer_width;} inline void setOuterWidth(int value) {outer_width = value;} inline const MapColor* getOuterColor() const {return outer_color;} inline void setOuterColor(const MapColor* color) {outer_color = color;} SymbolPropertiesWidget* createPropertiesWidget(SymbolSettingDialog* dialog) override; protected: #ifndef NO_NATIVE_FILE_FORMAT bool loadImpl(QIODevice* file, int version, Map* map) override; #endif void saveImpl(QXmlStreamWriter& xml, const Map& map) const override; bool loadImpl(QXmlStreamReader& xml, const Map& map, SymbolDictionary& symbol_dict) override; bool equalsImpl(const Symbol* other, Qt::CaseSensitivity case_sensitivity) const override; std::vector objects; std::vector symbols; bool rotatable; int inner_radius; // in 1/1000 mm const MapColor* inner_color; int outer_width; // in 1/1000 mm const MapColor* outer_color; }; } // namespace OpenOrienteering #endif mapper-0.8.1.1/src/core/symbols/symbol.cpp000066400000000000000000000572171325266516600204130ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2012-2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "symbol.h" #include #include #include #include #include #include #include #include #include #include #include #include "core/map.h" #include "core/map_color.h" #include "core/map_coord.h" #include "core/map_view.h" #include "core/objects/object.h" #include "core/objects/text_object.h" #include "core/renderables/renderable.h" #include "core/renderables/renderable_implementation.h" #include "core/symbols/area_symbol.h" #include "core/symbols/combined_symbol.h" #include "core/symbols/line_symbol.h" #include "core/symbols/point_symbol.h" #include "core/symbols/text_symbol.h" #include "fileformats/file_format.h" #include "fileformats/file_import_export.h" #include "gui/util_gui.h" #include "util/util.h" #include "settings.h" // IWYU pragma: no_include namespace OpenOrienteering { Symbol::Symbol(Type type) noexcept : type { type } , number { -1, -1, -1 } , is_helper_symbol(false) , is_hidden(false) , is_protected(false) { // nothing else } Symbol::~Symbol() = default; bool Symbol::validate() const { return true; } bool Symbol::equals(const Symbol* other, Qt::CaseSensitivity case_sensitivity, bool compare_state) const { if (type != other->type) return false; for (int i = 0; i < number_components; ++i) { if (number[i] != other->number[i]) return false; if (number[i] == -1 && other->number[i] == -1) break; } if (is_helper_symbol != other->is_helper_symbol) return false; if (compare_state && (is_hidden != other->is_hidden || is_protected != other->is_protected)) return false; if (name.compare(other->name, case_sensitivity) != 0) return false; if (description.compare(other->description, case_sensitivity) != 0) return false; return equalsImpl(other, case_sensitivity); } const PointSymbol* Symbol::asPoint() const { Q_ASSERT(type == Point); return static_cast(this); } PointSymbol* Symbol::asPoint() { Q_ASSERT(type == Point); return static_cast(this); } const LineSymbol* Symbol::asLine() const { Q_ASSERT(type == Line); return static_cast(this); } LineSymbol* Symbol::asLine() { Q_ASSERT(type == Line); return static_cast(this); } const AreaSymbol* Symbol::asArea() const { Q_ASSERT(type == Area); return static_cast(this); } AreaSymbol* Symbol::asArea() { Q_ASSERT(type == Area); return static_cast(this); } const TextSymbol* Symbol::asText() const { Q_ASSERT(type == Text); return static_cast(this); } TextSymbol* Symbol::asText() { Q_ASSERT(type == Text); return static_cast(this); } const CombinedSymbol* Symbol::asCombined() const { Q_ASSERT(type == Combined); return static_cast(this); } CombinedSymbol* Symbol::asCombined() { Q_ASSERT(type == Combined); return static_cast(this); } bool Symbol::isTypeCompatibleTo(const Object* object) const { if (type == Point && object->getType() == Object::Point) return true; else if ((type == Line || type == Area || type == Combined) && object->getType() == Object::Path) return true; else if (type == Text && object->getType() == Object::Text) return true; return false; } bool Symbol::numberEquals(const Symbol* other, bool ignore_trailing_zeros) const { if (ignore_trailing_zeros) { for (int i = 0; i < number_components; ++i) { if (number[i] == -1 && other->number[i] == -1) return true; if ((number[i] == 0 || number[i] == -1) && (other->number[i] == 0 || other->number[i] == -1)) continue; if (number[i] != other->number[i]) return false; } } else { for (int i = 0; i < number_components; ++i) { if (number[i] != other->number[i]) return false; if (number[i] == -1) return true; } } return true; } #ifndef NO_NATIVE_FILE_FORMAT bool Symbol::load(QIODevice* file, int version, Map* map) { loadString(file, name); for (int i = 0; i < number_components; ++i) file->read((char*)&number[i], sizeof(int)); loadString(file, description); file->read((char*)&is_helper_symbol, sizeof(bool)); if (version >= 10) file->read((char*)&is_hidden, sizeof(bool)); if (version >= 11) file->read((char*)&is_protected, sizeof(bool)); return loadImpl(file, version, map); } #endif void Symbol::save(QXmlStreamWriter& xml, const Map& map) const { xml.writeStartElement(QString::fromLatin1("symbol")); xml.writeAttribute(QString::fromLatin1("type"), QString::number(type)); int id = map.findSymbolIndex(this); if (id >= 0) xml.writeAttribute(QString::fromLatin1("id"), QString::number(id)); // unique if given xml.writeAttribute(QString::fromLatin1("code"), getNumberAsString()); // not always unique if (!name.isEmpty()) xml.writeAttribute(QString::fromLatin1("name"), name); if (is_helper_symbol) xml.writeAttribute(QString::fromLatin1("is_helper_symbol"), QString::fromLatin1("true")); if (is_hidden) xml.writeAttribute(QString::fromLatin1("is_hidden"), QString::fromLatin1("true")); if (is_protected) xml.writeAttribute(QString::fromLatin1("is_protected"), QString::fromLatin1("true")); if (!description.isEmpty()) xml.writeTextElement(QString::fromLatin1("description"), description); saveImpl(xml, map); xml.writeEndElement(/*symbol*/); } Symbol* Symbol::load(QXmlStreamReader& xml, const Map& map, SymbolDictionary& symbol_dict) { Q_ASSERT(xml.name() == QLatin1String("symbol")); int symbol_type = xml.attributes().value(QLatin1String("type")).toInt(); Symbol* symbol = Symbol::getSymbolForType(static_cast(symbol_type)); if (!symbol) throw FileFormatException(::OpenOrienteering::ImportExport::tr("Error while loading a symbol of type %1 at line %2 column %3.").arg(symbol_type).arg(xml.lineNumber()).arg(xml.columnNumber())); QXmlStreamAttributes attributes = xml.attributes(); QString code = attributes.value(QLatin1String("code")).toString(); if (attributes.hasAttribute(QLatin1String("id"))) { QString id = attributes.value(QLatin1String("id")).toString(); if (symbol_dict.contains(id)) throw FileFormatException(::OpenOrienteering::ImportExport::tr("Symbol ID '%1' not unique at line %2 column %3.").arg(id).arg(xml.lineNumber()).arg(xml.columnNumber())); symbol_dict[id] = symbol; if (code.isEmpty()) code = id; } if (code.isEmpty()) symbol->number[0] = -1; else { for (int i = 0, index = 0; i < number_components && index >= 0; ++i) { if (index == -1) symbol->number[i] = -1; else { int dot = code.indexOf(QLatin1Char('.'), index+1); int num = code.midRef(index, (dot == -1) ? -1 : (dot - index)).toInt(); symbol->number[i] = num; index = dot; if (index != -1) index++; } } } symbol->name = attributes.value(QLatin1String("name")).toString(); symbol->is_helper_symbol = (attributes.value(QLatin1String("is_helper_symbol")) == QLatin1String("true")); symbol->is_hidden = (attributes.value(QLatin1String("is_hidden")) == QLatin1String("true")); symbol->is_protected = (attributes.value(QLatin1String("is_protected")) == QLatin1String("true")); while (xml.readNextStartElement()) { if (xml.name() == QLatin1String("description")) symbol->description = xml.readElementText(); else { if (!symbol->loadImpl(xml, map, symbol_dict)) xml.skipCurrentElement(); } } if (xml.error()) { delete symbol; throw FileFormatException( ::OpenOrienteering::ImportExport::tr("Error while loading a symbol of type %1 at line %2 column %3: %4") .arg(symbol_type) .arg(xml.lineNumber()) .arg(xml.columnNumber()) .arg(xml.errorString()) ); } return symbol; } bool Symbol::loadFinished(Map* map) { Q_UNUSED(map); return true; } void Symbol::createRenderables(const PathObject*, const PathPartVector&, ObjectRenderables&, Symbol::RenderableOptions) const { qWarning("Missing implementation of Symbol::createRenderables(const PathObject*, const PathPartVector&, ObjectRenderables&)"); } void Symbol::createBaselineRenderables( const PathObject*, const PathPartVector& path_parts, ObjectRenderables& output, const MapColor* color) const { Q_ASSERT((getContainedTypes() & ~(Symbol::Line | Symbol::Area | Symbol::Combined)) == 0); if (color) { // Insert line renderable LineSymbol line_symbol; line_symbol.setColor(color); line_symbol.setLineWidth(0); for (const auto& part : path_parts) { auto line_renderable = new LineRenderable(&line_symbol, part, false); output.insertRenderable(line_renderable); } } } bool Symbol::symbolChanged(const Symbol* old_symbol, const Symbol* new_symbol) { Q_UNUSED(old_symbol); Q_UNUSED(new_symbol); return false; } bool Symbol::containsSymbol(const Symbol* symbol) const { Q_UNUSED(symbol); return false; } QImage Symbol::getIcon(const Map* map) const { if (icon.isNull() && map) icon = createIcon(*map, Settings::getInstance().getSymbolWidgetIconSizePx()); return icon; } QImage Symbol::createIcon(const Map& map, int side_length, bool antialiasing, qreal zoom) const { // Desktop default used to be 2x zoom at 8 mm side length, plus/minus // a border of one white pixel around some objects. // If the icon is bigger than the rectangle with this zoom factor, the zoom // is reduced later to make the icon fit into the rectangle. if (zoom <= 0) zoom = map.symbolIconZoom(); auto max_icon_mm_half = 0.5 / zoom; // Create image QImage image(side_length, side_length, QImage::Format_ARGB32_Premultiplied); QPainter painter(&image); if (antialiasing) painter.setRenderHint(QPainter::Antialiasing); // Make background transparent auto mode = painter.compositionMode(); painter.setCompositionMode(QPainter::CompositionMode_Clear); painter.fillRect(image.rect(), Qt::transparent); painter.setCompositionMode(mode); painter.translate(0.5 * side_length, 0.5 * side_length); // Create geometry Object* object = nullptr; std::unique_ptr symbol_copy; auto offset = MapCoord{}; auto contained_types = getContainedTypes(); if (type == Point) { max_icon_mm_half *= 0.90; // white border object = new PointObject(static_cast(this)); } else if (type == Text) { max_icon_mm_half *= 0.95; // white border auto text = new TextObject(this); text->setText(static_cast(this)->getIconText()); object = text; } else if (type == Area || (type == Combined && contained_types & Area)) { auto path = new PathObject(this); path->addCoordinate(0, MapCoord(-max_icon_mm_half, -max_icon_mm_half)); path->addCoordinate(1, MapCoord(max_icon_mm_half, -max_icon_mm_half)); path->addCoordinate(2, MapCoord(max_icon_mm_half, max_icon_mm_half)); path->addCoordinate(3, MapCoord(-max_icon_mm_half, max_icon_mm_half)); path->parts().front().setClosed(true, true); object = path; } else if (type == Line || type == Combined) { bool show_dash_symbol = false; auto line_length_half = max_icon_mm_half; if (type == Line) { auto line = static_cast(this); if (line->getCapStyle() == LineSymbol::RoundCap) { offset.setNativeX(-line->getLineWidth()/3); } else if (line->getCapStyle() == LineSymbol::PointedCap) { line_length_half = std::max(line_length_half, 0.0012 * line->getPointedCapLength()); } if (line->getDashSymbol() && !line->getDashSymbol()->isEmpty()) { line_length_half = std::max(line_length_half, line->getDashSymbol()->dimensionForIcon()); show_dash_symbol = true; } if (line->getMidSymbol() && !line->getMidSymbol()->isEmpty()) { auto icon_size = line->getMidSymbol()->dimensionForIcon(); icon_size += line->getMidSymbolDistance() * (line->getMidSymbolsPerSpot() - 1) / 2000; line_length_half = std::max(line_length_half, icon_size); if (!line->getShowAtLeastOneSymbol()) { if (!symbol_copy) symbol_copy.reset(line->duplicate()); auto icon_line = static_cast(symbol_copy.get()); icon_line->setShowAtLeastOneSymbol(true); } } if (line->getStartSymbol() && !line->getStartSymbol()->isEmpty()) { line_length_half = std::max(line_length_half, line->getStartSymbol()->dimensionForIcon() / 2); } if (line->getEndSymbol() && !line->getEndSymbol()->isEmpty()) { line_length_half = std::max(line_length_half, line->getEndSymbol()->dimensionForIcon() / 2); } // If there are breaks in the line, scale them down so they fit into the icon exactly. auto max_ideal_length = 0; if (line->isDashed() && line->getBreakLength() > 0) { auto ideal_length = 2 * line->getDashesInGroup() * line->getDashLength() + 2 * (line->getDashesInGroup() - 1) * line->getInGroupBreakLength() + line->getBreakLength(); if (max_ideal_length < ideal_length) max_ideal_length = ideal_length; } if (line->hasBorder()) { auto& border = line->getBorder(); if (border.dashed && border.break_length > 0) { auto ideal_length = 2 * border.dash_length + border.break_length; if (max_ideal_length < ideal_length) max_ideal_length = ideal_length; } auto& right_border = line->getRightBorder(); if (line->areBordersDifferent() && right_border.dashed && right_border.break_length > 0) { auto ideal_length = 2 * right_border.dash_length + right_border.break_length; if (max_ideal_length < ideal_length) max_ideal_length = ideal_length; } } if (max_ideal_length > 0) { auto ideal_length_half = qreal(max_ideal_length) / 2000; auto factor = qMin(qreal(0.5), line_length_half / qMax(qreal(0.001), ideal_length_half)); if (!symbol_copy) symbol_copy.reset(line->duplicate()); auto icon_line = static_cast(symbol_copy.get()); icon_line->setDashLength(qRound(factor * icon_line->getDashLength())); icon_line->setBreakLength(qRound(factor * icon_line->getBreakLength())); icon_line->setInGroupBreakLength(qRound(factor * icon_line->getInGroupBreakLength())); icon_line->setShowAtLeastOneSymbol(true); icon_line->getBorder().dash_length *= factor; icon_line->getBorder().break_length *= factor; icon_line->getRightBorder().dash_length *= factor; icon_line->getRightBorder().break_length *= factor; } } else if (type == Combined) { auto max_ideal_length = 0; auto combined = static_cast(this); for (int i = 0; i < combined->getNumParts(); ++i) { auto part = combined->getPart(i); if (part && part->getType() == Line) { auto line = static_cast(part); auto dash_symbol = line->getDashSymbol(); if (dash_symbol && !dash_symbol->isEmpty()) show_dash_symbol = true; if (line->isDashed() && line->getBreakLength() > 0) { auto ideal_length = 2 * line->getDashesInGroup() * line->getDashLength() + 2 * (line->getDashesInGroup() - 1) * line->getInGroupBreakLength() + line->getBreakLength(); if (max_ideal_length < ideal_length) max_ideal_length = ideal_length; } if (line->hasBorder()) { auto& border = line->getBorder(); if (border.dashed && border.break_length > 0) { auto ideal_length = 2 * border.dash_length + border.break_length; if (max_ideal_length < ideal_length) max_ideal_length = ideal_length; } auto& right_border = line->getRightBorder(); if (line->areBordersDifferent() && right_border.dashed && right_border.break_length > 0) { auto ideal_length = 2 * right_border.dash_length + right_border.break_length; if (max_ideal_length < ideal_length) max_ideal_length = ideal_length; } } } } // If there are breaks in the line, scale them down so they fit into the icon exactly. if (max_ideal_length > 0) { auto ideal_length_half = qreal(max_ideal_length) / 2000; auto factor = qMin(qreal(0.5), line_length_half / qMax(qreal(0.001), ideal_length_half)); symbol_copy.reset(new CombinedSymbol()); static_cast(symbol_copy.get())->setNumParts(combined->getNumParts()); for (int i = 0; i < combined->getNumParts(); ++i) { auto proto = static_cast(combined)->getPart(i); if (!proto) continue; auto copy = proto->duplicate(); if (copy->getType() == Line) { auto icon_line = static_cast(copy); icon_line->setDashLength(qRound(factor * icon_line->getDashLength())); icon_line->setBreakLength(qRound(factor * icon_line->getBreakLength())); icon_line->setInGroupBreakLength(qRound(factor * icon_line->getInGroupBreakLength())); icon_line->setShowAtLeastOneSymbol(true); icon_line->getBorder().dash_length *= factor; icon_line->getBorder().break_length *= factor; icon_line->getRightBorder().dash_length *= factor; icon_line->getRightBorder().break_length *= factor; } static_cast(symbol_copy.get())->setPart(i, copy, true); } } } auto path = new PathObject(symbol_copy ? symbol_copy.get() : this); path->addCoordinate(0, MapCoord(-line_length_half, 0.0)); path->addCoordinate(1, MapCoord(line_length_half, 0.0)); if (show_dash_symbol) { MapCoord dash_coord(0, 0); dash_coord.setDashPoint(true); path->addCoordinate(1, dash_coord); } object = path; } else { qWarning("Unhandled symbol: %s", qPrintable(getDescription())); return image; } // Create icon map and view Map icon_map; // const_cast promise: We won't change the colors, thus we won't change map. icon_map.useColorsFrom(const_cast(&map)); icon_map.setScaleDenominator(map.getScaleDenominator()); icon_map.addObject(object); const auto& extent = object->getExtent(); auto w = std::max(std::abs(extent.left()), std::abs(extent.right())); auto h = std::max(std::abs(extent.top()), std::abs(extent.bottom())); auto real_icon_mm_half = std::max(w, h); auto final_zoom = side_length * zoom * std::min(qreal(1), max_icon_mm_half / real_icon_mm_half); painter.scale(final_zoom, final_zoom); if (type == Text) { // Center text painter.translate(-extent.center()); } else if (type == Point) { // Do not completely offset the symbols relative position painter.translate(-extent.center() / 2); } else if (contained_types & Line && !(contained_types & Area)) { painter.translate(MapCoordF(-offset)); } RenderConfig config = { map, QRectF(-10000, -10000, 20000, 20000), final_zoom, RenderConfig::HelperSymbols, 1.0 }; bool was_hidden = is_hidden; // Ensure that an icon is created for hidden symbols. if (symbol_copy) symbol_copy.get()->setHidden(false); else is_hidden = false; icon_map.draw(&painter, config); is_hidden = was_hidden; painter.end(); // Add shadow to dominant white on transparent auto color = guessDominantColor(); if (color && color->isWhite()) { for (int y = image.height() - 1; y >= 0; --y) { for (int x = image.width() - 1; x >= 0; --x) { if (qAlpha(image.pixel(x, y)) > 0) continue; auto is_white = [](const QRgb& rgb) { return rgb > 0 && qAlpha(rgb) == qRed(rgb) && qRed(rgb) == qGreen(rgb) && qGreen(rgb) == qBlue(rgb); }; if (x > 0) { auto preceding = image.pixel(x-1, y); if (is_white(preceding)) { image.setPixel(x, y, qPremultiply(qRgba(0, 0, 0, 127))); continue; } } if (y > 0) { auto preceding = image.pixel(x, y-1); if (is_white(preceding)) { image.setPixel(x, y, qPremultiply(qRgba(0, 0, 0, 127))); } } } } } return image; } void Symbol::resetIcon() { icon = {}; } qreal Symbol::dimensionForIcon() const { return 0; } qreal Symbol::calculateLargestLineExtent() const { return 0; } QString Symbol::getNumberAsString() const { QString str; for (auto n : number) { if (n < 0) break; if (!str.isEmpty()) str += QLatin1Char('.'); str += QString::number(n); } return str; } Symbol* Symbol::getSymbolForType(Symbol::Type type) { if (type == Symbol::Point) return new PointSymbol(); else if (type == Symbol::Line) return new LineSymbol(); else if (type == Symbol::Area) return new AreaSymbol(); else if (type == Symbol::Text) return new TextSymbol(); else if (type == Symbol::Combined) return new CombinedSymbol(); else { Q_ASSERT(false); return nullptr; } } #ifndef NO_NATIVE_FILE_FORMAT bool Symbol::loadSymbol(Symbol*& symbol, QIODevice* stream, int version, Map* map) { int save_type; stream->read((char*)&save_type, sizeof(int)); symbol = Symbol::getSymbolForType(static_cast(save_type)); if (!symbol) return false; if (!symbol->load(stream, version, map)) return false; return true; } #endif bool Symbol::areTypesCompatible(Symbol::Type a, Symbol::Type b) { return (getCompatibleTypes(a) & b) != 0; } int Symbol::getCompatibleTypes(Symbol::Type type) { if (type == Point) return Point; else if (type == Line || type == Area || type == Combined) return Line | Area | Combined; else if (type == Text) return Text; Q_ASSERT(false); return type; } void Symbol::duplicateImplCommon(const Symbol* other) { type = other->type; name = other->name; for (int i = 0; i < number_components; ++i) number[i] = other->number[i]; description = other->description; is_helper_symbol = other->is_helper_symbol; is_hidden = other->is_hidden; icon = other->icon; } bool Symbol::compareByNumber(const Symbol* s1, const Symbol* s2) { int n1 = s1->number_components, n2 = s2->number_components; for (int i = 0; i < n1 && i < n2; i++) { if (s1->getNumberComponent(i) < s2->getNumberComponent(i)) return true; // s1 < s2 if (s1->getNumberComponent(i) > s2->getNumberComponent(i)) return false; // s1 > s2 // if s1 == s2, loop to the next component } return false; // s1 == s2 } bool Symbol::compareByColorPriority(const Symbol* s1, const Symbol* s2) { const MapColor* c1 = s1->guessDominantColor(); const MapColor* c2 = s2->guessDominantColor(); if (c1 && c2) return c1->comparePriority(*c2); else if (c2) return true; else if (c1) return false; return false; // s1 == s2 } Symbol::compareByColor::compareByColor(Map* map) { int next_priority = map->getNumColors() - 1; // Iterating in reverse order so identical colors are at the position where they appear with lowest priority. for (int i = map->getNumColors() - 1; i >= 0; --i) { QRgb color_code = QRgb(*map->getColor(i)); if (!color_map.contains(color_code)) { color_map.insert(color_code, next_priority); --next_priority; } } } bool Symbol::compareByColor::operator() (const Symbol* s1, const Symbol* s2) { const MapColor* c1 = s1->guessDominantColor(); const MapColor* c2 = s2->guessDominantColor(); if (c1 && c2) return color_map.value(QRgb(*c1)) < color_map.value(QRgb(*c2)); else if (c2) return true; else if (c1) return false; return false; // s1 == s2 } } // namespace OpenOrienteering mapper-0.8.1.1/src/core/symbols/symbol.h000066400000000000000000000346031325266516600200520ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2012-2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_SYMBOL_H #define OPENORIENTEERING_SYMBOL_H #include #include #include #include #include #include #include #include class QIODevice; class QXmlStreamReader; class QXmlStreamWriter; namespace OpenOrienteering { class AreaSymbol; class CombinedSymbol; class LineSymbol; class Map; class MapColor; class MapColorMap; class Object; class ObjectRenderables; class PathObject; class PathPartVector; class PointSymbol; class Symbol; class SymbolPropertiesWidget; class SymbolSettingDialog; class TextSymbol; class VirtualCoordVector; typedef QHash SymbolDictionary; // From gui/util_gui.h, but avoiding extra dependencies namespace Util { QString plainText(QString maybe_markup); } // namespace Util /** * Abstract base class for map symbols. * * Provides among other things a symbol number consisting of multiple parts, * e.g. "2.4.12". Parts which are not set are assigned the value -1. */ class Symbol { public: /** Enumeration of all possible symbol types, * must be able to be used as bits in a bitmask. */ enum Type { Point = 1, Line = 2, Area = 4, Text = 8, Combined = 16, NoSymbol = 0, AllSymbols = Point | Line | Area | Text | Combined }; /** * RenderableOptions denominate variations in painting symbols. */ enum RenderableOption { RenderBaselines = 1 << 0, ///< Paint cosmetique contours and baselines RenderAreasHatched = 1 << 1, ///< Paint hatching instead of opaque fill RenderNormal = 0 ///< Paint normally }; Q_DECLARE_FLAGS(RenderableOptions, RenderableOption) /** Constructs an empty symbol */ Symbol(Type type) noexcept; Symbol(const Symbol&) = delete; Symbol(Symbol&&) = delete; virtual ~Symbol(); virtual Symbol* duplicate(const MapColorMap* color_map = nullptr) const = 0; Symbol& operator=(const Symbol&) = delete; Symbol& operator=(Symbol&&) = delete; virtual bool validate() const; /** * Checks for equality to the other symbol. * @param other The symbol to compare with. * @param case_sensitivity Comparison mode for strings, e.g. symbol names. * @param compare_state If true, also compares symbol state (protected / hidden). */ bool equals(const Symbol* other, Qt::CaseSensitivity case_sensitivity = Qt::CaseSensitive, bool compare_state = false) const; /** Returns the type of the symbol */ inline Type getType() const {return type;} // Convenience casts with type checking /** Case to PointSymbol with type checking */ const PointSymbol* asPoint() const; /** Case to PointSymbol with type checking */ PointSymbol* asPoint(); /** Case to LineSymbol with type checking */ const LineSymbol* asLine() const; /** Case to LineSymbol with type checking */ LineSymbol* asLine(); /** Case to AreaSymbol with type checking */ const AreaSymbol* asArea() const; /** Case to AreaSymbol with type checking */ AreaSymbol* asArea(); /** Case to TextSymbol with type checking */ const TextSymbol* asText() const; /** Case to TextSymbol with type checking */ TextSymbol* asText(); /** Case to CombinedSymbol with type checking */ const CombinedSymbol* asCombined() const; /** Case to CombinedSymbol with type checking */ CombinedSymbol* asCombined(); /** Returns the or-ed together bitmask of all symbol types * this symbol contains */ virtual Type getContainedTypes() const {return getType();} /** * Checks if the symbol can be applied to the given object. * TODO: refactor: use static areTypesCompatible() instead with the type of the object's symbol */ bool isTypeCompatibleTo(const Object* object) const; /** Returns if the symbol numbers are identical. */ bool numberEquals(const Symbol* other, bool ignore_trailing_zeros) const; // Saving and loading /** Loads the symbol in the old "native" file format. */ bool load(QIODevice* file, int version, Map* map); /** * Saves the symbol in xml format. * @param xml Stream to save to. * @param map Reference to the map containing the symbol. Needed to find * symbol indices. */ void save(QXmlStreamWriter& xml, const Map& map) const; /** * Load the symbol in xml format. * @param xml Stream to load from. * @param map Reference to the map containing the symbol. * @param symbol_dict Dictionary mapping symbol IDs to symbol pointers. */ static Symbol* load(QXmlStreamReader& xml, const Map& map, SymbolDictionary& symbol_dict); /** * Called after loading of the map is finished. * Can do tasks that need to reference other symbols or map objects. */ virtual bool loadFinished(Map* map); /** * Creates renderables for a generic object. * * This will create the renderables according to the object's properties * and the given coordinates. * * NOTE: methods which implement this should use the given coordinates * instead of the object's coordinates, as those can be an updated, * transformed version of the object's coords! */ virtual void createRenderables( const Object *object, const VirtualCoordVector &coords, ObjectRenderables &output, Symbol::RenderableOptions options) const = 0; /** * Creates renderables for a path object. * * This will create the renderables according to the object's properties * and the coordinates given by the path_parts. This allows the immediate * use of precalculated meta-information on paths. * * \see createRenderables() */ virtual void createRenderables( const PathObject* object, const PathPartVector& path_parts, ObjectRenderables &output, Symbol::RenderableOptions options) const; /** * Creates baseline renderables for a path object. * * Baseline renderables show the coordinate paths with minimum line width. * * \see createRenderables() */ virtual void createBaselineRenderables( const PathObject* object, const PathPartVector& path_parts, ObjectRenderables &output, const MapColor* color) const; /** * Called by the map in which the symbol is to notify it of a color being * deleted (pointer becomes invalid, indices change). */ virtual void colorDeleted(const MapColor* color) = 0; /** Must return if the given color is used by this symbol. */ virtual bool containsColor(const MapColor* color) const = 0; /** * Returns the dominant color of this symbol, or a guess for this color * in case it is impossible to determine it uniquely. */ virtual const MapColor* guessDominantColor() const = 0; /** * Called by the map in which the symbol is to notify it of a symbol being * changed (pointer becomes invalid). * If !new_symbol, the symbol is being deleted. * Must return true if this symbol contained the deleted symbol. */ virtual bool symbolChanged(const Symbol* old_symbol, const Symbol* new_symbol); /** * Must return if the given symbol is referenced by this symbol. * Should NOT return true if the argument is itself. */ virtual bool containsSymbol(const Symbol* symbol) const; /** Scales the whole symbol */ virtual void scale(double factor) = 0; /** * Returns the symbol's icon. * * This icon uses a default size and zoom, and it is cached, making * repeated calls cheap. */ QImage getIcon(const Map* map) const; /** * Creates a symbol icon with the given side length (pixels). * * If the zoom parameter is zero, the map's symbolIconZoom() is used. * At a zoom of 1.0 (100%), a square symbol of 1 mm side length would fill * 50% of the icon width and height. Larger symbols may be scaled down. */ QImage createIcon(const Map& map, int side_length, bool antialiasing = true, qreal zoom = 0) const; /** * Clear the symbol's cached icon. * * It will be recreated the next time getIcon() gets called. */ void resetIcon(); /** * Returns the dimension which shall considered when scaling the icon. */ virtual qreal dimensionForIcon() const; /** * Returns the largest extent of all primitive lines which are part of the symbol. * * Effectively, this is the half line width. */ virtual qreal calculateLargestLineExtent() const; // Getters / Setters /** Returns the symbol name. */ inline const QString& getName() const {return name;} /** Returns the symbol name after stripping all HTML. */ QString getPlainTextName() const { return Util::plainText(name); } /** Sets the symbol name. */ inline void setName(const QString& new_name) {name = new_name;} /** Returns the symbol number as string. */ QString getNumberAsString() const; /** Returns the i-th component of the symbol number as int. */ inline int getNumberComponent(int i) const {Q_ASSERT(i >= 0 && i < number_components); return number[i];} /** Sets the i-th component of the symbol number. */ inline void setNumberComponent(int i, int new_number) {Q_ASSERT(i >= 0 && i < number_components); number[i] = new_number;} /** Returns the symbol description. */ inline const QString& getDescription() const {return description;} /** Sets the symbol description. */ inline void setDescription(const QString& new_description) {description = new_description;} /** Returns if this is a helper symbol (which is not printed in the final map) */ inline bool isHelperSymbol() const {return is_helper_symbol;} /** Sets if this is a helper symbol, see isHelperSymbol(). */ inline void setIsHelperSymbol(bool value) {is_helper_symbol = value;} /** Returns if this symbol is hidden. */ inline bool isHidden() const {return is_hidden;} /** Sets the hidden state of this symbol. */ inline void setHidden(bool value) {is_hidden = value;} /** Returns if this symbol is protected, i.e. objects with this symbol * cannot be edited. */ inline bool isProtected() const {return is_protected;} /** Sets the protected state of this symbol. */ inline void setProtected(bool value) {is_protected = value;} /** Creates a properties widget for the symbol. */ virtual SymbolPropertiesWidget* createPropertiesWidget(SymbolSettingDialog* dialog); // Static /** Returns a newly created symbol of the given type */ static Symbol* getSymbolForType(Type type); /** Static read function; reads the type number, creates a symbol of * this type and loads it. Returns true if successful. */ static bool loadSymbol(Symbol*& symbol, QIODevice* stream, int version, Map* map); /** * Returns if the symbol types can be applied to the same object types */ static bool areTypesCompatible(Type a, Type b); /** * Returns a bitmask of all types which can be applied to * the same objects as the given type. */ static int getCompatibleTypes(Type type); /** * @brief Compares two symbols by number. * @return True if the number of s1 is less than the number of s2. */ static bool compareByNumber(const Symbol* s1, const Symbol* s2); /** * @brief Compares two symbols by the dominant colors' priorities. * @return True if s1's dominant color's priority is lower than s2's dominant color's priority. */ static bool compareByColorPriority(const Symbol* s1, const Symbol* s2); /** * @brief Functor for comparing symbols by dominant colors. * * Other than compareByColorPriority(), this comparison uses the lowest * priority which exists for a particular color in the map. All map colors * are preprocessed in the constructor. Thus the functor becomes invalid as * soon as colors are changed. */ struct compareByColor { /** * @brief Constructs the functor. * @param map The map which defines all colors. */ compareByColor(Map* map); /** * @brief Operator which compares two symbols by dominant colors. * @return True if s1's dominant color exists with lower prority then s2's dominant color. */ bool operator() (const Symbol* s1, const Symbol* s2); private: /** * @brief Maps color code to priority. */ QHash color_map; }; /** * Number of components of symbol numbers. */ static const int number_components = 3; protected: #ifndef NO_NATIVE_FILE_FORMAT /** * Must be overridden to load type-specific symbol properties. See saveImpl() */ virtual bool loadImpl(QIODevice* file, int version, Map* map) = 0; #endif /** * Must be overridden to save type-specific symbol properties. * The map pointer can be used to get persistent indices to any pointers on map data */ virtual void saveImpl(QXmlStreamWriter& xml, const Map& map) const = 0; /** * Must be overridden to load type-specific symbol properties. See saveImpl(). * Return false if the current xml tag does not belong to the symbol and * should be skipped, true if the element has been read completely. */ virtual bool loadImpl(QXmlStreamReader& xml, const Map& map, SymbolDictionary& symbol_dict) = 0; /** * Must be overridden to compare symbol-specific attributes. */ virtual bool equalsImpl(const Symbol* other, Qt::CaseSensitivity case_sensitivity) const = 0; /** * Duplicates properties which are common for all * symbols from other to this object */ void duplicateImplCommon(const Symbol* other); private: /** Symbol icon. If icon.isNull() is true, it is not generated yet. */ mutable QImage icon; /** Symbol name */ QString name; /** Symbol description */ QString description; /** The symbol type, determined by the subclass */ Type type; /** Symbol number */ int number[number_components]; /** Helper symbol flag, see isHelperSymbol() */ bool is_helper_symbol; /** Hidden flag, see isHidden() */ mutable bool is_hidden; /** Protected flag, see isProtected() */ bool is_protected; }; } // namespace OpenOrienteering Q_DECLARE_METATYPE(const OpenOrienteering::Symbol*) Q_DECLARE_OPERATORS_FOR_FLAGS(OpenOrienteering::Symbol::RenderableOptions) #endif mapper-0.8.1.1/src/core/symbols/symbol_icon_decorator.cpp000066400000000000000000000061161325266516600234550ustar00rootroot00000000000000/* * Copyright 2014, 2018 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "symbol_icon_decorator.h" #include #include namespace OpenOrienteering { //### SymbolIconDecorator ### SymbolIconDecorator::~SymbolIconDecorator() = default; //### HiddenSymbolDecorator ### HiddenSymbolDecorator::HiddenSymbolDecorator(int icon_size) : icon_size(icon_size) , pen_width(qMax(1, qCeil(0.06*icon_size))) , x_width(icon_size/3) , offset(1 + pen_width, 1 + pen_width) { ; // nothing else } HiddenSymbolDecorator::~HiddenSymbolDecorator() = default; void HiddenSymbolDecorator::draw(QPainter& painter) const { // Draw a lock symbol painter.save(); painter.setRenderHint(QPainter::Antialiasing, true); painter.setOpacity(0.6); painter.fillRect(0, 0, icon_size, icon_size, QBrush(Qt::white)); painter.translate(offset); painter.setOpacity(1.0); QPen pen(Qt::red); pen.setWidth(pen_width); painter.setPen(pen); painter.drawLine(QPoint(0, 0), QPoint(x_width, x_width)); painter.drawLine(QPoint(x_width, 0), QPoint(0, x_width)); painter.restore(); } //### ProtectedSymbolDecorator ### ProtectedSymbolDecorator::ProtectedSymbolDecorator(int icon_size) : arc_size(qFloor(qMax(3.0, 0.15*icon_size))) , pen_width(qMax(1, qCeil(0.4*arc_size))) , box_width(arc_size + pen_width + qMax(1, qFloor(0.1*icon_size))) , box_height(qMax(arc_size, qCeil(0.6*box_width))) , offset(icon_size - 3 - box_width, 1 + pen_width) { ; // nothing else } ProtectedSymbolDecorator::~ProtectedSymbolDecorator() = default; void ProtectedSymbolDecorator::draw(QPainter& painter) const { // Draw a lock symbol painter.save(); painter.setRenderHint(QPainter::Antialiasing, true); painter.translate(offset); painter.setOpacity(0.5); QPen arc_background_pen(Qt::white); arc_background_pen.setWidth(pen_width+2); painter.setPen(arc_background_pen); painter.drawRoundedRect((box_width-arc_size)/2, 0, arc_size, arc_size+pen_width, qreal(pen_width), qreal(pen_width)); painter.fillRect(-1, arc_size-1, box_width+2, box_height+2, QBrush(Qt::white)); painter.setOpacity(1.0); QPen arc_pen(Qt::darkGray); arc_pen.setWidth(pen_width); painter.setPen(arc_pen); painter.drawRoundedRect((box_width-arc_size)/2, 0, arc_size, arc_size+pen_width, qreal(pen_width), qreal(pen_width)); painter.fillRect(0, arc_size, box_width, box_height, QBrush(Qt::darkGray)); painter.restore(); } } // namespace OpenOrienteering mapper-0.8.1.1/src/core/symbols/symbol_icon_decorator.h000066400000000000000000000045501325266516600231220ustar00rootroot00000000000000/* * Copyright 2014, 2018 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_SYMBOL_ICON_DECORATOR_H #define OPENORIENTEERING_SYMBOL_ICON_DECORATOR_H #include class QPainter; namespace OpenOrienteering { /** * An abstract interface for classes which draws icon decorations. * * The icon is expected to be at (0, 0) in the painter's coordinates. */ class SymbolIconDecorator { public: SymbolIconDecorator() noexcept = default; SymbolIconDecorator(const SymbolIconDecorator&) = delete; SymbolIconDecorator(SymbolIconDecorator&&) = delete; SymbolIconDecorator& operator=(const SymbolIconDecorator&) = delete; SymbolIconDecorator& operator=(SymbolIconDecorator&&) = delete; virtual ~SymbolIconDecorator(); virtual void draw(QPainter& p) const = 0; }; /** * Draws the decoration for a hidden symbol. * * A small red x is drawn in the top-left corner of the icon. */ class HiddenSymbolDecorator : public SymbolIconDecorator { public: explicit HiddenSymbolDecorator(int icon_size); ~HiddenSymbolDecorator() override; void draw(QPainter& p) const override; private: int icon_size; int pen_width; int x_width; QPoint offset; }; /** * Draws the decoration for a protected symbol. * * A small gray lock is drawn in the top-right corner of the icon. */ class ProtectedSymbolDecorator : public SymbolIconDecorator { public: explicit ProtectedSymbolDecorator(int icon_size); ~ProtectedSymbolDecorator() override; void draw(QPainter& p) const override; private: int arc_size; int pen_width; int box_width; int box_height; QPoint offset; }; } // namespace OpenOrienteering #endif // OPENORIENTEERING_SYMBOL_ICON_DECORATOR_H mapper-0.8.1.1/src/core/symbols/text_symbol.cpp000066400000000000000000000455761325266516600214640ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2012-2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "text_symbol.h" #include #include #include // IWYU pragma: no_include #include #include #include #include #include #include #include #include #include #include #include #include #include "core/map.h" #include "core/map_color.h" #include "core/map_coord.h" #include "core/objects/object.h" #include "core/objects/text_object.h" #include "core/renderables/renderable.h" #include "core/renderables/renderable_implementation.h" #include "core/symbols/area_symbol.h" #include "core/symbols/line_symbol.h" #include "core/symbols/symbol.h" #include "core/virtual_coord_vector.h" #include "core/virtual_path.h" #include "util/util.h" namespace OpenOrienteering { TextSymbol::TextSymbol() : Symbol(Symbol::Text) , metrics(QFont()) { color = nullptr; font_family = QString::fromLatin1("Arial"); font_size = 4 * 1000; bold = false; italic = false; underline = false; line_spacing = 1; paragraph_spacing = 0; character_spacing = 0; kerning = true; icon_text = QString{}; framing = false; framing_color = nullptr; framing_mode = LineFraming; framing_line_half_width = 200; framing_shadow_x_offset = 200; framing_shadow_y_offset = 200; line_below = false; line_below_color = nullptr; line_below_width = 0; line_below_distance = 0; updateQFont(); } TextSymbol::~TextSymbol() = default; Symbol* TextSymbol::duplicate(const MapColorMap* color_map) const { auto new_text = new TextSymbol(); new_text->duplicateImplCommon(this); new_text->color = color_map ? color_map->value(color) : color; new_text->font_family = font_family; new_text->font_size = font_size; new_text->bold = bold; new_text->italic = italic; new_text->underline = underline; new_text->line_spacing = line_spacing; new_text->paragraph_spacing = paragraph_spacing; new_text->character_spacing = character_spacing; new_text->kerning = kerning; new_text->icon_text = icon_text; new_text->framing = framing; new_text->framing_color = color_map ? color_map->value(framing_color) : framing_color; new_text->framing_mode = framing_mode; new_text->framing_line_half_width = framing_line_half_width; new_text->framing_shadow_x_offset = framing_shadow_x_offset; new_text->framing_shadow_y_offset = framing_shadow_y_offset; new_text->line_below = line_below; new_text->line_below_color = color_map ? color_map->value(line_below_color) : line_below_color; new_text->line_below_width = line_below_width; new_text->line_below_distance = line_below_distance; new_text->custom_tabs = custom_tabs; new_text->updateQFont(); return new_text; } void TextSymbol::createRenderables( const Object *object, const VirtualCoordVector &coords, ObjectRenderables &output, Symbol::RenderableOptions options) const { Q_ASSERT(object); const TextObject* text_object = static_cast(object); text_object->prepareLineInfos(); if (options.testFlag(Symbol::RenderBaselines)) { createBaselineRenderables(text_object, coords, output); } else { auto anchor = coords[0]; double anchor_x = anchor.x(); double anchor_y = anchor.y(); if (color) output.insertRenderable(new TextRenderable(this, text_object, color, anchor_x, anchor_y)); if (line_below && line_below_color && line_below_width > 0) createLineBelowRenderables(object, output); if (framing && framing_color) { if (framing_mode == LineFraming && framing_line_half_width > 0) { output.insertRenderable(new TextFramingRenderable(this, text_object, framing_color, anchor_x, anchor_y)); } else if (framing_mode == ShadowFraming) { output.insertRenderable(new TextRenderable(this, text_object, framing_color, anchor_x + 0.001 * framing_shadow_x_offset, anchor_y + 0.001 * framing_shadow_y_offset)); } } } } void TextSymbol::createBaselineRenderables( const TextObject* text_object, const VirtualCoordVector& coords, ObjectRenderables& output) const { const MapColor* dominant_color = guessDominantColor(); if (dominant_color && text_object->getNumLines() > 0) { // Insert text boundary LineSymbol line_symbol; line_symbol.setColor(dominant_color); line_symbol.setLineWidth(0); const TextObjectLineInfo* line = text_object->getLineInfo(0); QRectF text_bbox(line->line_x, line->line_y - line->ascent, line->width, line->ascent + line->descent); for (int i = 1; i < text_object->getNumLines(); ++i) { const TextObjectLineInfo* line = text_object->getLineInfo(i); rectInclude(text_bbox, QRectF(line->line_x, line->line_y - line->ascent, line->width, line->ascent + line->descent)); } Q_UNUSED(coords); // coords should be used for calcTextToMapTransform() QTransform text_to_map = text_object->calcTextToMapTransform(); PathObject path; path.addCoordinate(MapCoord(text_to_map.map(text_bbox.topLeft()))); path.addCoordinate(MapCoord(text_to_map.map(text_bbox.topRight()))); path.addCoordinate(MapCoord(text_to_map.map(text_bbox.bottomRight()))); path.addCoordinate(MapCoord(text_to_map.map(text_bbox.bottomLeft()))); path.parts().front().setClosed(true, true); path.updatePathCoords(); auto line_renderable = new LineRenderable(&line_symbol, path.parts().front(), false); output.insertRenderable(line_renderable); } } void TextSymbol::createLineBelowRenderables(const Object* object, ObjectRenderables& output) const { auto text_object = reinterpret_cast(object); if (text_object->getNumLines()) { double scale_factor = calculateInternalScaling(); AreaSymbol area_symbol; area_symbol.setColor(line_below_color); MapCoordVector line_flags(4); MapCoordVectorF line_coords(4); VirtualPath line_path = { line_flags, line_coords }; line_flags.back().setHolePoint(true); QTransform transform = text_object->calcTextToMapTransform(); for (int i = 0; i < text_object->getNumLines(); ++i) { const TextObjectLineInfo* line_info = text_object->getLineInfo(i); if (!line_info->paragraph_end) continue; double line_below_x0; double line_below_x1; if (text_object->hasSingleAnchor()) { line_below_x0 = line_info->line_x; line_below_x1 = line_below_x0 + line_info->width; } else { double box_width = text_object->getBoxWidth() * scale_factor; line_below_x0 = -0.5 * box_width; line_below_x1 = line_below_x0 + box_width; } double line_below_y0 = line_info->line_y + getLineBelowDistance() * scale_factor; double line_below_y1 = line_below_y0 + getLineBelowWidth() * scale_factor; line_coords[0] = MapCoordF(transform.map(QPointF(line_below_x0, line_below_y0))); line_coords[1] = MapCoordF(transform.map(QPointF(line_below_x1, line_below_y0))); line_coords[2] = MapCoordF(transform.map(QPointF(line_below_x1, line_below_y1))); line_coords[3] = MapCoordF(transform.map(QPointF(line_below_x0, line_below_y1))); line_path.path_coords.update(0); output.insertRenderable(new AreaRenderable(&area_symbol, line_path)); } } } void TextSymbol::colorDeleted(const MapColor* c) { auto changes = 0; if (c == color) { color = nullptr; ++changes; } if (c == framing_color) { framing_color = nullptr; ++changes; } if (c == line_below_color) { line_below_color = nullptr; ++changes; } if (changes) { resetIcon(); } } bool TextSymbol::containsColor(const MapColor* c) const { return c == color || c == framing_color || c == line_below_color; } const MapColor* TextSymbol::guessDominantColor() const { auto c = color; if (!c) c = framing_color; if (!c) c = line_below_color; return c; } void TextSymbol::scale(double factor) { font_size = qMax(10, qRound(factor * font_size)); // minimum 0.01 mm framing_line_half_width = qRound(factor * framing_line_half_width); framing_shadow_x_offset = qRound(factor * framing_shadow_x_offset); framing_shadow_y_offset = qRound(factor * framing_shadow_y_offset); updateQFont(); resetIcon(); } void TextSymbol::updateQFont() { qfont = QFont(); qfont.setBold(bold); qfont.setItalic(italic); qfont.setUnderline(underline); qfont.setPixelSize(internal_point_size); qfont.setFamily(font_family); qfont.setHintingPreference(QFont::PreferNoHinting); qfont.setKerning(kerning); metrics = QFontMetricsF(qfont); qfont.setLetterSpacing(QFont::AbsoluteSpacing, metrics.width(QString(QLatin1String{" "})) * character_spacing); qfont.setStyleStrategy(QFont::ForceOutline); metrics = QFontMetricsF(qfont); tab_interval = 8.0 * metrics.averageCharWidth(); } #ifndef NO_NATIVE_FILE_FORMAT bool TextSymbol::loadImpl(QIODevice* file, int version, Map* map) { int temp; file->read((char*)&temp, sizeof(int)); color = (temp >= 0) ? map->getColor(temp) : nullptr; loadString(file, font_family); file->read((char*)&font_size, sizeof(int)); file->read((char*)&bold, sizeof(bool)); file->read((char*)&italic, sizeof(bool)); file->read((char*)&underline, sizeof(bool)); file->read((char*)&line_spacing, sizeof(float)); if (version >= 13) file->read((char*)¶graph_spacing, sizeof(double)); if (version >= 14) file->read((char*)&character_spacing, sizeof(double)); if (version >= 12) file->read((char*)&kerning, sizeof(bool)); if (version >= 19) loadString(file, icon_text); if (version >= 20) { file->read((char*)&framing, sizeof(bool)); file->read((char*)&temp, sizeof(int)); framing_color = map->getColor(temp); file->read((char*)&framing_mode, sizeof(int)); file->read((char*)&framing_line_half_width, sizeof(int)); file->read((char*)&framing_shadow_x_offset, sizeof(int)); file->read((char*)&framing_shadow_y_offset, sizeof(int)); } if (version >= 13) { file->read((char*)&line_below, sizeof(bool)); file->read((char*)&temp, sizeof(int)); line_below_color = (temp >= 0) ? map->getColor(temp) : nullptr; file->read((char*)&line_below_width, sizeof(int)); file->read((char*)&line_below_distance, sizeof(int)); int num_custom_tabs; file->read((char*)&num_custom_tabs, sizeof(int)); custom_tabs.resize(num_custom_tabs); for (int i = 0; i < num_custom_tabs; ++i) file->read((char*)&custom_tabs[i], sizeof(int)); } updateQFont(); return true; } #endif void TextSymbol::saveImpl(QXmlStreamWriter& xml, const Map& map) const { xml.writeStartElement(QStringLiteral("text_symbol")); xml.writeAttribute(QStringLiteral("icon_text"), icon_text); xml.writeStartElement(QStringLiteral("font")); xml.writeAttribute(QStringLiteral("family"), font_family); xml.writeAttribute(QStringLiteral("size"), QString::number(font_size)); if (bold) xml.writeAttribute(QStringLiteral("bold"), QStringLiteral("true")); if (italic) xml.writeAttribute(QStringLiteral("italic"), QStringLiteral("true")); if (underline) xml.writeAttribute(QStringLiteral("underline"), QStringLiteral("true")); xml.writeEndElement(/*font*/); xml.writeStartElement(QStringLiteral("text")); xml.writeAttribute(QStringLiteral("color"), QString::number(map.findColorIndex(color))); xml.writeAttribute(QStringLiteral("line_spacing"), QString::number(line_spacing)); xml.writeAttribute(QStringLiteral("paragraph_spacing"), QString::number(paragraph_spacing)); xml.writeAttribute(QStringLiteral("character_spacing"), QString::number(character_spacing)); if (kerning) xml.writeAttribute(QStringLiteral("kerning"), QStringLiteral("true")); xml.writeEndElement(/*text*/); if (framing) { xml.writeStartElement(QStringLiteral("framing")); xml.writeAttribute(QStringLiteral("color"), QString::number(map.findColorIndex(framing_color))); xml.writeAttribute(QStringLiteral("mode"), QString::number(framing_mode)); xml.writeAttribute(QStringLiteral("line_half_width"), QString::number(framing_line_half_width)); xml.writeAttribute(QStringLiteral("shadow_x_offset"), QString::number(framing_shadow_x_offset)); xml.writeAttribute(QStringLiteral("shadow_y_offset"), QString::number(framing_shadow_y_offset)); xml.writeEndElement(/*framing*/); } if (line_below) { xml.writeStartElement(QStringLiteral("line_below")); xml.writeAttribute(QStringLiteral("color"), QString::number(map.findColorIndex(line_below_color))); xml.writeAttribute(QStringLiteral("width"), QString::number(line_below_width)); xml.writeAttribute(QStringLiteral("distance"), QString::number(line_below_distance)); xml.writeEndElement(/*line_below*/); } int num_custom_tabs = getNumCustomTabs(); if (num_custom_tabs > 0) { xml.writeStartElement(QStringLiteral("tabs")); xml.writeAttribute(QStringLiteral("count"), QString::number(num_custom_tabs)); for (int i = 0; i < num_custom_tabs; ++i) xml.writeTextElement(QStringLiteral("tab"), QString::number(custom_tabs[i])); xml.writeEndElement(/*tabs*/); } xml.writeEndElement(/*text_symbol*/); } bool TextSymbol::loadImpl(QXmlStreamReader& xml, const Map& map, SymbolDictionary& symbol_dict) { Q_UNUSED(symbol_dict); if (xml.name() != QLatin1String("text_symbol")) return false; icon_text = xml.attributes().value(QLatin1String("icon_text")).toString(); framing = false; line_below = false; custom_tabs.clear(); while (xml.readNextStartElement()) { QXmlStreamAttributes attributes(xml.attributes()); if (xml.name() == QLatin1String("font")) { font_family = xml.attributes().value(QLatin1String("family")).toString(); font_size = attributes.value(QLatin1String("size")).toInt(); bold = (attributes.value(QLatin1String("bold")) == QLatin1String("true")); italic = (attributes.value(QLatin1String("italic")) == QLatin1String("true")); underline = (attributes.value(QLatin1String("underline")) == QLatin1String("true")); xml.skipCurrentElement(); } else if (xml.name() == QLatin1String("text")) { int temp = attributes.value(QLatin1String("color")).toInt(); color = map.getColor(temp); line_spacing = attributes.value(QLatin1String("line_spacing")).toFloat(); paragraph_spacing = attributes.value(QLatin1String("paragraph_spacing")).toInt(); character_spacing = attributes.value(QLatin1String("character_spacing")).toFloat(); kerning = (attributes.value(QLatin1String("kerning")) == QLatin1String("true")); xml.skipCurrentElement(); } else if (xml.name() == QLatin1String("framing")) { framing = true; int temp = attributes.value(QLatin1String("color")).toInt(); framing_color = map.getColor(temp); framing_mode = attributes.value(QLatin1String("mode")).toInt(); framing_line_half_width = attributes.value(QLatin1String("line_half_width")).toInt(); framing_shadow_x_offset = attributes.value(QLatin1String("shadow_x_offset")).toInt(); framing_shadow_y_offset = attributes.value(QLatin1String("shadow_y_offset")).toInt(); xml.skipCurrentElement(); } else if (xml.name() == QLatin1String("line_below")) { line_below = true; int temp = attributes.value(QLatin1String("color")).toInt(); line_below_color = map.getColor(temp); line_below_width = attributes.value(QLatin1String("width")).toInt(); line_below_distance = attributes.value(QLatin1String("distance")).toInt(); xml.skipCurrentElement(); } else if (xml.name() == QLatin1String("tabs")) { int num_custom_tabs = attributes.value(QLatin1String("count")).toInt(); custom_tabs.reserve(num_custom_tabs % 20); // 20 is not the limit while (xml.readNextStartElement()) { if (xml.name() == QLatin1String("tab")) custom_tabs.push_back(xml.readElementText().toInt()); else xml.skipCurrentElement(); } } else xml.skipCurrentElement(); // unknown } updateQFont(); return true; } bool TextSymbol::equalsImpl(const Symbol* other, Qt::CaseSensitivity case_sensitivity) const { Q_UNUSED(case_sensitivity); const TextSymbol* text = static_cast(other); if (!MapColor::equal(color, text->color)) return false; if (font_family.compare(text->font_family, Qt::CaseInsensitive) != 0) return false; if (font_size != text->font_size || bold != text->bold || italic != text->italic || underline != text->underline || line_spacing != text->line_spacing || paragraph_spacing != text->paragraph_spacing || character_spacing != text->character_spacing || kerning != text->kerning || line_below != text->line_below || framing != text->framing) return false; if (framing) { if (!MapColor::equal(framing_color, text->framing_color)) return false; if (framing_mode != text->framing_mode || (framing_mode == LineFraming && framing_line_half_width != text->framing_line_half_width) || (framing_mode == ShadowFraming && (framing_shadow_x_offset != text->framing_shadow_x_offset || framing_shadow_y_offset != text->framing_shadow_y_offset))) return false; } if (line_below) { if (!MapColor::equal(line_below_color, text->line_below_color)) return false; if (line_below_width != text->line_below_width || line_below_distance != text->line_below_distance) return false; } if (tab_interval != text->tab_interval) return false; if (custom_tabs.size() != text->custom_tabs.size()) return false; for (size_t i = 0, end = custom_tabs.size(); i < end; ++i) { if (custom_tabs[i] != text->custom_tabs[i]) return false; } return true; } QString TextSymbol::getIconText() const { if (icon_text.isEmpty()) return QCoreApplication::translate("OpenOrienteering::TextSymbolSettings", "A", "First capital letter of the local alphabet"); return icon_text; } double TextSymbol::getNextTab(double pos) const { if (!custom_tabs.empty()) { double scaling = calculateInternalScaling(); double map_pos = pos / scaling; for (auto tab : custom_tabs) { if (0.001 * tab > map_pos) return scaling * 0.001 * tab; } // After the given positions, OCAD repeats the distance between the last two tab positions double custom_tab_interval = (custom_tabs.size() > 1) ? (custom_tabs[custom_tabs.size() - 1] - custom_tabs[custom_tabs.size() - 2]) : custom_tabs[0]; custom_tab_interval *= 0.001; return scaling * (0.001 * custom_tabs[custom_tabs.size() - 1] + (floor((map_pos - 0.001 * custom_tabs[custom_tabs.size() - 1]) / custom_tab_interval) + 1.0) * custom_tab_interval); } double next_tab = (floor(pos / tab_interval) + 1.0) * tab_interval; Q_ASSERT(next_tab > pos); return next_tab; } } // namespace OpenOrienteering mapper-0.8.1.1/src/core/symbols/text_symbol.h000066400000000000000000000151461325266516600211170ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2012-2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_TEXT_SYMBOL_H #define OPENORIENTEERING_TEXT_SYMBOL_H #include "symbol.h" #include #include #include #include #include class QIODevice; class QXmlStreamReader; class QXmlStreamWriter; // IWYU pragma: no_forward_declare QFontMetricsF namespace OpenOrienteering { class Map; class MapColor; class MapColorMap; class Object; class ObjectRenderables; class SymbolPropertiesWidget; class SymbolSettingDialog; class TextObject; class VirtualCoordVector; /** Symbol for text, can be applied to TextObjects. * * NOTE: Uses a hack to disable hinting for fonts: * Uses a big internal font size, internal_point_size, where hinting is * neglegible and scales it down for output. * TODO: A maybe better solution would be to use FreeType directly. */ class TextSymbol : public Symbol { friend class TextSymbolSettings; friend class PointSymbolEditorWidget; friend class OCAD8FileImport; public: /** Modes for text framing */ enum FramingMode { /** Only the text itself is displayed. */ NoFraming = 0, /** * The text path is drawn with a QPainter with active QPen in addition * to the normal fill, resulting in a line around the letters. */ LineFraming = 1, /** * The text is drawn a second time with a slight offset to the main * text, creating a shadow effect. */ ShadowFraming = 2 }; /** Creates an empty text symbol. */ TextSymbol(); ~TextSymbol() override; Symbol* duplicate(const MapColorMap* color_map = nullptr) const override; void createRenderables( const Object *object, const VirtualCoordVector &coords, ObjectRenderables &output, Symbol::RenderableOptions options) const override; using Symbol::createBaselineRenderables; void createBaselineRenderables( const TextObject* text_object, const VirtualCoordVector &coords, ObjectRenderables &output) const; void createLineBelowRenderables(const Object* object, ObjectRenderables& output) const; void colorDeleted(const MapColor* color) override; bool containsColor(const MapColor* color) const override; const MapColor* guessDominantColor() const override; void scale(double factor) override; /** Updates the internal QFont from the font settings. */ void updateQFont(); /** Calculates the factor to convert from the real font size to the internal font size */ inline double calculateInternalScaling() const {return internal_point_size / (0.001 * font_size);} // Getters inline const MapColor* getColor() const {return color;} inline void setColor(const MapColor* color) {this->color = color;} inline const QString& getFontFamily() const {return font_family;} inline double getFontSize() const {return 0.001 * font_size;} inline bool isBold() const {return bold;} inline bool isItalic() const {return italic;} inline bool isUnderlined() const {return underline;} inline float getLineSpacing() const {return line_spacing;} inline double getParagraphSpacing() const {return 0.001 * paragraph_spacing;} inline double getCharacterSpacing() const {return 0.001 * character_spacing;} inline bool usesKerning() const {return kerning;} QString getIconText() const; // returns a default text if no custom text is set. inline bool usesFraming() const {return framing;} inline const MapColor* getFramingColor() const {return framing_color;} inline int getFramingMode() const {return framing_mode;} inline int getFramingLineHalfWidth() const {return framing_line_half_width;} inline int getFramingShadowXOffset() const {return framing_shadow_x_offset;} inline int getFramingShadowYOffset() const {return framing_shadow_y_offset;} inline bool hasLineBelow() const {return line_below;} inline const MapColor* getLineBelowColor() const {return line_below_color;} inline double getLineBelowWidth() const {return 0.001 * line_below_width;} inline double getLineBelowDistance() const {return 0.001 * line_below_distance;} inline int getNumCustomTabs() const {return (int)custom_tabs.size();} inline int getCustomTab(int index) const {return custom_tabs[index];} inline const QFont& getQFont() const {return qfont;} inline const QFontMetricsF& getFontMetrics() const { return metrics; } double getNextTab(double pos) const; constexpr static qreal internal_point_size = 256; SymbolPropertiesWidget* createPropertiesWidget(SymbolSettingDialog* dialog) override; protected: #ifndef NO_NATIVE_FILE_FORMAT bool loadImpl(QIODevice* file, int version, Map* map) override; #endif void saveImpl(QXmlStreamWriter& xml, const Map& map) const override; bool loadImpl(QXmlStreamReader& xml, const Map& map, SymbolDictionary& symbol_dict) override; bool equalsImpl(const Symbol* other, Qt::CaseSensitivity case_sensitivity) const override; QFont qfont; QFontMetricsF metrics; const MapColor* color; QString font_family; int font_size; // this defines the font size in 1000 mm. How big the letters really are depends on the design of the font though bool bold; bool italic; bool underline; float line_spacing; // as factor of original line spacing // (BEGIN) DON'T CHANGE THE ORDER OF THESE FIELDS BEFORE SOLVING [tickets:#428] int paragraph_spacing; // in mm float character_spacing; // as a factor of the space character width bool kerning; QString icon_text; // text to be drawn in the symbol's icon // (END) DON'T CHANGE THE ORDER OF THESE FIELDS BEFORE SOLVING [tickets:#428] bool framing; const MapColor* framing_color; int framing_mode; int framing_line_half_width; int framing_shadow_x_offset; int framing_shadow_y_offset; // OCAD compatibility features bool line_below; const MapColor* line_below_color; int line_below_width; int line_below_distance; std::vector custom_tabs; double tab_interval; /// default tab interval length in text coordinates }; } // namespace OpenOrienteering #endif mapper-0.8.1.1/src/core/virtual_coord_vector.cpp000066400000000000000000000021641325266516600216430ustar00rootroot00000000000000/* * Copyright 2015 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "virtual_coord_vector.h" #include "core/map_coord.h" namespace OpenOrienteering { //### VirtualCoordVector ### MapCoordF VirtualCoordVector::fromMapCoordF(size_type index) const { return (*coords)[index]; } MapCoordF VirtualCoordVector::fromMapCoord(size_type index) const { return MapCoordF(flags[index]); } } // namespace OpenOrienteering mapper-0.8.1.1/src/core/virtual_coord_vector.h000066400000000000000000000134551325266516600213150ustar00rootroot00000000000000/* * Copyright 2015 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_VIRTUAL_COORD_VECTOR_H #define OPENORIENTEERING_VIRTUAL_COORD_VECTOR_H #include #include "map_coord.h" namespace OpenOrienteering { /** * @brief The VirtualFlagsVector class provides read-only access to a MapCoordVector. * * This is an utility class which is used as a public member in VirtualCoordVector. * Its public API provides read-only STL-style access to a map coordinates vector. */ class VirtualFlagsVector { friend class VirtualCoordVector; private: // The pointer to the vector coordinate of coordinates. Never nullptr. const MapCoordVector* coords; // To be used by VirtualCoordVector only VirtualFlagsVector(const VirtualFlagsVector&) = default; // To be used by VirtualCoordVector only VirtualFlagsVector& operator=(const VirtualFlagsVector&) = default; public: /** * A reaonably sized unsigned integer type for map coord vector sizes and indexes. */ using size_type = quint32; /** * Constructs a new accessor for the given vector of MapCoord. */ explicit VirtualFlagsVector(const MapCoordVector& coords); /** * Returns a direct reference to the underlying coordinates. */ const MapCoordVector& data() const; // STL-style API (incomplete) bool empty() const; size_type size() const; MapCoordVector::const_reference operator[](size_type index) const; MapCoordVector::const_reference back() const; }; /** * @brief The VirtualCoordVector class provides read-only access to possible distinct coordinates and flags. * * Some algorithms in Mapper need to be applied not only to original integer coordinate lists, * but sometimes also to transformed floating-point coordinates sharing the same flags. * This class provides uniform access to coordinates and flags in two cases: * * (a) Coordinates and flags coming from the same MapCoordVector, and * * (b) Coordinates coming from a MapCoordVectorF, and flags from a MapCoordVector. */ class VirtualCoordVector { public: /** * A reaonably sized unsigned integer type for map coord vector sizes and indexes. */ using size_type = VirtualFlagsVector::size_type; /** * An read-only accessor to the flags which provides an operator[](size_type). */ // Provides the coordinates in case (a). VirtualFlagsVector flags; private: // A pointer to the MapCoordVectorF which provides the coordinates in case (b). const MapCoordVectorF* coords; // A pointer to the actual coordinate access implemenation MapCoordF (VirtualCoordVector::*coords_access)(size_type index) const; public: /** * Creates another accessor for the data managed by the given prototype. */ VirtualCoordVector(const VirtualCoordVector& prototype) = default; /** * Creates an accessor for the flags and coordinates in coords (case a). */ explicit VirtualCoordVector(const MapCoordVector& coords); /** * Creates an accessor for the given flags and the coordinates in coords (case b). */ explicit VirtualCoordVector(const MapCoordVector& flags, const MapCoordVectorF& coords); /** * Causes this accessor to serve the same data as rhs. */ VirtualCoordVector& operator=(const VirtualCoordVector& rhs) = default; // STL-style API (incomplete, and with some differences, such as returning by value) bool empty() const; size_type size() const; MapCoordF operator[](size_type index) const; MapCoordF back() const; private: // Returns a coordinate from coords vector. MapCoordF fromMapCoordF(size_type index) const; // Returns a coordinate from the flags accessors. MapCoordF fromMapCoord(size_type index) const; }; // ### VirtualFlagsVector inline code ### inline VirtualFlagsVector::VirtualFlagsVector(const MapCoordVector& coords) : coords { &coords } { // nothing else } inline const MapCoordVector& VirtualFlagsVector::data() const { return *coords; } inline bool VirtualFlagsVector::empty() const { return coords->empty(); } inline VirtualFlagsVector::size_type VirtualFlagsVector::size() const { return coords->size(); } inline MapCoordVector::const_reference VirtualFlagsVector::operator[](size_type index) const { return (*coords)[index]; } inline MapCoordVector::const_reference VirtualFlagsVector::back() const { return coords->back(); } // ### VirtualCoordVector inline code ### inline VirtualCoordVector::VirtualCoordVector(const MapCoordVector& coords) : flags { coords } , coords { nullptr } , coords_access(&VirtualCoordVector::fromMapCoord) { // nothing else } inline VirtualCoordVector::VirtualCoordVector(const MapCoordVector& flags, const MapCoordVectorF& coords) : flags { flags } , coords { &coords } , coords_access(&VirtualCoordVector::fromMapCoordF) { // nothing else } inline bool VirtualCoordVector::empty() const { return flags.empty(); } inline VirtualCoordVector::size_type VirtualCoordVector::size() const { return flags.size(); } inline MapCoordF VirtualCoordVector::operator[](size_type index) const { return (this->*coords_access)(index); } inline MapCoordF VirtualCoordVector::back() const { return (this->*coords_access)(size()-1); } } // namespace OpenOrienteering #endif mapper-0.8.1.1/src/core/virtual_path.cpp000066400000000000000000000470371325266516600201170ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2012-2015 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "virtual_path.h" #include "util/util.h" namespace OpenOrienteering { namespace { /** * Global position error threshold for approximating * bezier curves with straight segments. * TODO: make configurable */ const PathCoord::length_type bezier_error = 0.005; /** * Global maximum length of generated PathCoord segments for curves. * * This ensures that even for flat curves, segments will be generated regularly. * This is important because while a curve may be flat, the mapping from the * curve parameter to real position is not linear, which would result in problems. * This is counteracted by generating many segments. */ const PathCoord::length_type bezier_segment_maxlen_squared = 1.0; } // namespace // ### PathCoord ### // static PathCoord::length_type PathCoord::bezierError() { return bezier_error; } // ### PathCoordVector ### PathCoordVector::PathCoordVector(const MapCoordVector& coords) : virtual_coords(coords) { // nothing else } PathCoordVector::PathCoordVector(const MapCoordVector& flags, const MapCoordVectorF& coords) : virtual_coords(flags, coords) { // nothing else } PathCoordVector::PathCoordVector(const VirtualCoordVector& coords) : virtual_coords(coords) { // nothing else } VirtualCoordVector::size_type PathCoordVector::update(VirtualCoordVector::size_type part_start) { auto& flags = virtual_coords.flags; auto part_end = virtual_coords.size() - 1; if (part_start <= part_end) { Q_ASSERT(virtual_coords.size() == flags.size()); if (flags[part_start].isHolePoint()) { auto pos = virtual_coords[0]; qWarning("PathCoordVector at %g %g (mm) has an invalid hole at index %d.", pos.x(), -pos.y(), part_start); } clear(); if (empty() || (part_start > 0 && flags[part_start-1].isHolePoint())) { emplace_back(virtual_coords[part_start], part_start, 0.0, 0.0); } for (auto index = part_start + 1; index <= part_end; ++index) { if (flags[index-1].isCurveStart()) { Q_ASSERT(index+2 <= part_end); // Add curve coordinates curveToPathCoord(virtual_coords[index-1], virtual_coords[index], virtual_coords[index+1], virtual_coords[index+2], index-1, 0, 1); index += 2; } // Add end point const PathCoord& prev = back(); emplace_back(virtual_coords[index], index, 0.0, prev.clen + prev.pos.distanceTo(virtual_coords[index])); Q_ASSERT(back().index == index); if (index < part_end && flags[index].isHolePoint()) { part_end = index; } } } return part_end; } bool PathCoordVector::isClosed() const { return virtual_coords.flags[back().index].isClosePoint(); } PathCoordVector::size_type PathCoordVector::findNextDashPoint(PathCoordVector::size_type first) const { // Get behind the current point auto prev_index = (*this)[first].index; auto last = size() - 1; while (first != last && (*this)[first].index == prev_index) { ++first; } // Find a dash point or hole point while (first != last) { const auto& current_flags = virtual_coords.flags[(*this)[first].index]; if (current_flags.isDashPoint() || current_flags.isHolePoint()) break; ++first; } return first; } PathCoord::length_type PathCoordVector::length() const { Q_ASSERT(!empty()); return back().clen; } double PathCoordVector::calculateArea() const { Q_ASSERT(!empty()); auto area = 0.0; auto end_index = size() - 1; auto j = end_index; // The last vertex is the 'previous' one to the first for (auto i = 0u; i <= end_index; ++i) { area += ((*this)[j].pos.x() + (*this)[i].pos.x()) * ((*this)[j].pos.y() - (*this)[i].pos.y()); j = i; } return qAbs(area) / 2; } QRectF PathCoordVector::calculateExtent() const { QRectF extent(0.0, 0.0, -1.0, 0.0); if (!empty()) { auto pc = begin(); extent = QRectF(pc->pos.x(), pc->pos.y(), 0.0001, 0.0001); auto last = end(); for (++pc; pc != last; ++pc) { rectInclude(extent, pc->pos); } Q_ASSERT(extent.isValid()); } return extent; } bool PathCoordVector::intersectsBox(const QRectF& box) const { bool result = false; if (!empty()) { auto last_pos = front().pos; result = std::any_of(begin()+1, end(), [&box, &last_pos](const PathCoord& pc) { auto pos = pc.pos; auto result = lineIntersectsRect(box, last_pos, pos); /// \todo Implement this here, used nowhere else last_pos = pos; return result; }); } return result; } bool PathCoordVector::isPointInside(MapCoordF coord) const { bool inside = false; if (size() > 2) { auto last_pos = back().pos; for(const auto& path_coord : *this) { auto pos = path_coord.pos; if ( ((pos.y() > coord.y()) != (last_pos.y() > coord.y())) && (coord.x() < (last_pos.x() - pos.x()) * (coord.y() - pos.y()) / (last_pos.y() - pos.y()) + pos.x()) ) { inside = !inside; } last_pos = pos; } } return inside; } void PathCoordVector::curveToPathCoord( MapCoordF c0, MapCoordF c1, MapCoordF c2, MapCoordF c3, MapCoordVector::size_type edge_start, float p0, float p1 ) { // Common auto p_half = (p0 + p1) * 0.5; MapCoordF c12((c1.x() + c2.x()) * 0.5, (c1.y() + c2.y()) * 0.5); auto inner_len_sq = c0.distanceSquaredTo(c3); auto outer_len = [&]() { return c0.distanceTo(c1) + c1.distanceTo(c2) + c2.distanceTo(c3); }; if (inner_len_sq <= bezier_segment_maxlen_squared && outer_len() - sqrt(inner_len_sq) <= bezier_error) { const PathCoord& prev = back(); emplace_back(c12, edge_start, p_half, prev.clen + float(prev.pos.distanceTo(c12))); } else { // Split in two MapCoordF c01((c0.x() + c1.x()) * 0.5f, (c0.y() + c1.y()) * 0.5f); MapCoordF c23((c2.x() + c3.x()) * 0.5f, (c2.y() + c3.y()) * 0.5f); MapCoordF c012((c01.x() + c12.x()) * 0.5f, (c01.y() + c12.y()) * 0.5f); MapCoordF c123((c12.x() + c23.x()) * 0.5f, (c12.y() + c23.y()) * 0.5f); MapCoordF c0123((c012.x() + c123.x()) * 0.5f, (c012.y() + c123.y()) * 0.5f); curveToPathCoord(c0, c01, c012, c0123, edge_start, p0, p_half); curveToPathCoord(c0123, c123, c23, c3, edge_start, p_half, p1); } } //### VirtualPath ### VirtualPath::VirtualPath(const MapCoordVector& coords) : VirtualPath(coords, 0, coords.size()-1) { // nothing else } VirtualPath::VirtualPath(const MapCoordVector& coords, size_type first, size_type last) : coords(coords) , path_coords(coords) , first_index(first) , last_index(last) { // nothing else } VirtualPath::VirtualPath(const VirtualCoordVector& coords) : VirtualPath(coords, 0, coords.size()-1) { // nothing else } VirtualPath::VirtualPath(const VirtualCoordVector& coords, size_type first, size_type last) : coords(coords) , path_coords(coords) , first_index(first) , last_index(last) { // nothing else } VirtualPath::VirtualPath(const MapCoordVector& flags, const MapCoordVectorF& coords) : VirtualPath(flags, coords, 0, coords.size()-1) { // nothing else } VirtualPath::VirtualPath(const MapCoordVector& flags, const MapCoordVectorF& coords, size_type first, size_type last) : coords(flags, coords) , path_coords(flags, coords) , first_index(first) , last_index(last) { // nothing else } VirtualPath::size_type VirtualPath::countRegularNodes() const { size_type num_regular_points = 0; for (auto i = first_index; i <= last_index; ++i) { ++num_regular_points; if (coords.flags[i].isCurveStart()) i += 2; } if (num_regular_points && isClosed()) --num_regular_points; return num_regular_points; } bool VirtualPath::isClosed() const { Q_ASSERT(last_index < coords.size()); Q_ASSERT(last_index >= first_index); return coords.flags[last_index].isClosePoint(); } PathCoord VirtualPath::findClosestPointTo( MapCoordF coord, float& distance_squared, float distance_bound_squared, size_type start_index, size_type end_index ) const { Q_ASSERT(!path_coords.empty()); auto result = path_coords.front(); // Find upper bound for distance. for (const auto& path_coord : path_coords) { if (path_coord.index > end_index) break; if (path_coord.index < start_index) continue; auto to_coord = coord - path_coord.pos; auto dist_sq = to_coord.lengthSquared(); if (dist_sq < distance_bound_squared) { distance_bound_squared = dist_sq; result = path_coord; } } // Check between this coord and the next one. distance_squared = distance_bound_squared; auto last = end(path_coords)-1; for (auto pc = begin(path_coords); pc != last; ++pc) { if (pc->index > end_index) break; if (pc->index < start_index) continue; auto pos = pc->pos; auto next_pc = pc+1; auto next_pos = next_pc->pos; auto tangent = next_pos - pos; tangent.normalize(); auto to_coord = coord - pos; float dist_along_line = MapCoordF::dotProduct(to_coord, tangent); if (dist_along_line <= 0) { if (to_coord.lengthSquared() < distance_squared) { distance_squared = to_coord.lengthSquared(); result = *pc; } continue; } float line_length = next_pc->clen - pc->clen; if (dist_along_line >= line_length) { if (coord.distanceSquaredTo(next_pos) < distance_squared) { distance_squared = coord.distanceSquaredTo(next_pos); result = *next_pc; } continue; } auto right = tangent.perpRight(); float dist_from_line = MapCoordF::dotProduct(right, to_coord); auto dist_from_line_sq = dist_from_line * dist_from_line; if (dist_from_line_sq < distance_squared) { distance_squared = dist_from_line_sq; result.clen = pc->clen + dist_along_line; result.index = pc->index; auto factor = dist_along_line / line_length; if (next_pc->index == pc->index) result.param = pc->param + (next_pc->param - pc->param) * factor; else result.param = pc->param + (1.0 - pc->param) * factor; /// \todo verify if (coords.flags[result.index].isCurveStart()) { MapCoordF unused; PathCoord::splitBezierCurve(MapCoordF(coords.flags[result.index]), MapCoordF(coords.flags[result.index+1]), MapCoordF(coords.flags[result.index+2]), MapCoordF(coords.flags[result.index+3]), result.param, unused, unused, result.pos, unused, unused); } else { result.pos = pos + (next_pos - pos) * factor; } } } return result; } VirtualPath::size_type VirtualPath::prevCoordIndex(size_type base_index) const { Q_ASSERT(base_index >= first_index); size_type result = first_index; if (Q_UNLIKELY(base_index > last_index)) { result = last_index; } else if (base_index > first_index) { result = base_index - 1; for (auto i = 1; i <= 3; ++i) { auto alternative = base_index - i; if (alternative < first_index || alternative > last_index) break; if (coords.flags[alternative].isCurveStart()) result = alternative; } } return result; } VirtualPath::size_type VirtualPath::nextCoordIndex(size_type base_index) const { Q_ASSERT(base_index <= last_index); size_type result = base_index + 1; if (Q_UNLIKELY(result > last_index)) { result = last_index; } if (Q_UNLIKELY(result <= first_index)) { result = first_index; } else { for (auto i = 0; i < 2; ++i) { if (coords.flags[base_index].isCurveStart()) { result = base_index + 3; break; } if (base_index == first_index) break; --base_index; } } return result; } MapCoordF VirtualPath::calculateTangent(size_type i) const { bool ok_to_coord, ok_to_next; MapCoordF to_coord = calculateIncomingTangent(i, ok_to_coord); MapCoordF to_next = calculateOutgoingTangent(i, ok_to_next); if (!ok_to_next) { to_next = to_coord; } else if (ok_to_coord) { to_coord.normalize(); to_next.normalize(); to_next += to_coord; } return to_next; } std::pair VirtualPath::calculateTangentScaling(size_type i) const { bool ok_to_coord, ok_to_next; MapCoordF to_coord = calculateIncomingTangent(i, ok_to_coord); MapCoordF to_next = calculateOutgoingTangent(i, ok_to_next); auto scaling = 1.0; if (!ok_to_next) { to_next = to_coord; } else if (ok_to_coord) { to_coord.normalize(); to_next.normalize(); if (to_next == -to_coord) { to_next = to_next.perpRight(); scaling = std::numeric_limits::infinity(); } else { to_next += to_coord; to_next.normalize(); scaling = 1.0/MapCoordF::dotProduct(to_next, to_coord); } } return std::make_pair(to_next, scaling); } MapCoordF VirtualPath::calculateTangent( size_type i, bool backward, bool& ok) const { if (backward) return calculateIncomingTangent(i, ok); else return calculateOutgoingTangent(i, ok); } MapCoordF VirtualPath::calculateIncomingTangent( size_type i, bool& ok) const { ok = true; MapCoordF tangent; for (auto k = i; k > first_index; ) /// \todo Implement unsigned { --k; tangent = coords[i] - coords[k]; if (tangent.lengthSquared() > PathCoord::tangentEpsilonSquared()) return tangent; } if (isClosed() && last_index > i+1) { // Continue from end for (auto k = last_index; k > i; --k) { tangent = coords[i] - coords[k]; if (tangent.lengthSquared() > PathCoord::tangentEpsilonSquared()) return tangent; } } ok = false; return tangent; } MapCoordF VirtualPath::calculateOutgoingTangent( size_type i, bool& ok) const { ok = true; MapCoordF tangent; for (auto k = i+1; k <= last_index; ++k) { tangent = coords[k] - coords[i]; if (tangent.lengthSquared() > PathCoord::tangentEpsilonSquared()) return tangent; } if (isClosed()) { // Continue from start for (auto k = first_index; k < i; ++k) { tangent = coords[k] - coords[i]; if (tangent.lengthSquared() > PathCoord::tangentEpsilonSquared()) return tangent; } } ok = false; return tangent; } void VirtualPath::copy( const SplitPathCoord& first, const SplitPathCoord& last, MapCoordVector& out_coords ) const { Q_ASSERT(!empty()); Q_ASSERT(first.path_coords == &path_coords); Q_ASSERT(last.path_coords == &path_coords); auto& flags = coords.flags; // Handle first coordinate and its flags. if (out_coords.empty() || out_coords.back().isHolePoint() || !out_coords.back().isPositionEqualTo(MapCoord(first.pos)) ) { out_coords.emplace_back(first.pos); } else { out_coords.back().setHolePoint(false); out_coords.back().setClosePoint(false); } if (first.index == last.index) { out_coords.back().setCurveStart(last.is_curve_end && last.param != first.param); } else { out_coords.back().setCurveStart(first.is_curve_start); auto stop_index = last.index; if (last.param == 0.0) { stop_index -= last.is_curve_end ? 3 : 1; } auto index = first.index + 1; if (first.is_curve_start) { out_coords.emplace_back(first.curve_start[0]); out_coords.emplace_back(first.curve_start[1]); index += 2; } for (; index <= stop_index; ++index) { out_coords.emplace_back(coords[index]); out_coords.back().setFlags(flags[index].flags()); } } if (out_coords.back().isCurveStart()) { Q_ASSERT(last.is_curve_end); out_coords.emplace_back(last.curve_end[0]); out_coords.emplace_back(last.curve_end[1]); } out_coords.emplace_back(last.pos); if (last.param == 0.0) { out_coords.back().setHolePoint(flags[last.index].isHolePoint()); out_coords.back().setClosePoint(flags[last.index].isClosePoint()); } } void VirtualPath::copy( const SplitPathCoord& first, const SplitPathCoord& last, MapCoordVector& out_flags, MapCoordVectorF& out_coords) const { Q_ASSERT(!empty()); Q_ASSERT(first.path_coords == &path_coords); Q_ASSERT(last.path_coords == &path_coords); Q_ASSERT(out_coords.size() == out_flags.size()); auto& flags = coords.flags; // Handle first coordinate and its flags. if (out_coords.empty() || out_flags.back().isHolePoint() || out_coords.back() != first.pos) { out_coords.emplace_back(first.pos); out_flags.emplace_back(); } else { out_flags.back().setHolePoint(false); out_flags.back().setClosePoint(false); } if (first.index == last.index) { out_flags.back().setCurveStart(last.is_curve_end && last.param != first.param); } else { out_flags.back().setCurveStart(first.is_curve_start); auto stop_index = last.index; if (last.param == 0.0) { stop_index -= last.is_curve_end ? 3 : 1; } auto index = first.index + 1; if (first.is_curve_start && index < stop_index) { out_flags.emplace_back(); out_coords.emplace_back(first.curve_start[0]); out_flags.emplace_back(); out_coords.emplace_back(first.curve_start[1]); index += 2; } for (; index <= stop_index; ++index) { out_flags.emplace_back(flags[index]); out_coords.emplace_back(coords[index]); } } if (out_flags.back().isCurveStart()) { Q_ASSERT(last.is_curve_end); out_flags.emplace_back(); out_coords.emplace_back(last.curve_end[0]); out_flags.emplace_back(); out_coords.emplace_back(last.curve_end[1]); } out_flags.emplace_back(); out_coords.emplace_back(last.pos); if (last.param == 0.0) { out_flags.back().setHolePoint(flags[last.index].isHolePoint()); out_flags.back().setClosePoint(flags[last.index].isClosePoint()); } Q_ASSERT(out_coords.size() == out_flags.size()); } // This algorithm must (nearly) match VirtualPath::copy(). // (It will always copy the length of the starting point, but // VirtualPath::copy() might skip this point in some cases.) void VirtualPath::copyLengths( const SplitPathCoord& first, const SplitPathCoord& last, std::vector& out_lengths ) const { Q_ASSERT(!empty()); Q_ASSERT(first.path_coords == &path_coords); Q_ASSERT(last.path_coords == &path_coords); auto& flags = coords.flags; // Handle first coordinate out_lengths.emplace_back(first.clen); bool after_curve_start = false; if (first.index == last.index) { after_curve_start = last.is_curve_end && last.param != first.param; } else { after_curve_start = first.is_curve_start; auto stop_index = last.index; if (last.param == 0.0) { stop_index -= last.is_curve_end ? 3 : 1; } auto index = first.index + 1; if (first.is_curve_start && index < stop_index) { after_curve_start = false; out_lengths.emplace_back(first.clen); out_lengths.emplace_back(first.clen); index += 2; } auto path_coord_index = first.path_coord_index; for (; index <= stop_index; ++index) { while (path_coords[path_coord_index].index < index) ++path_coord_index; if (path_coords[path_coord_index].index == index) { // Normal node out_lengths.emplace_back(path_coords[path_coord_index].clen); after_curve_start = flags[index].isCurveStart(); } else { // Control point out_lengths.emplace_back(out_lengths.back()); after_curve_start = false; } } } if (after_curve_start) { auto length = out_lengths.back(); out_lengths.emplace_back(length); out_lengths.emplace_back(length); } out_lengths.push_back(last.clen); } } // namespace OpenOrienteering mapper-0.8.1.1/src/core/virtual_path.h000066400000000000000000000226521325266516600175600ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2012-2015 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_VIRTUAL_PATH_H #define OPENORIENTEERING_VIRTUAL_PATH_H #include #include #include #include #include "core/map_coord.h" #include "core/path_coord.h" #include "core/virtual_coord_vector.h" // IWYU pragma: no_forward_declare QRectF namespace OpenOrienteering { class PathCoordVector : public std::vector { private: friend class SplitPathCoord; friend class VirtualPath; VirtualCoordVector virtual_coords; public: PathCoordVector(const MapCoordVector& coords); PathCoordVector(const MapCoordVector& flags, const MapCoordVectorF& coords); PathCoordVector(const VirtualCoordVector& coords); private: PathCoordVector(const PathCoordVector&) = default; PathCoordVector(PathCoordVector&&) = default; PathCoordVector& operator=(const PathCoordVector&) = default; PathCoordVector& operator=(PathCoordVector&&) = default; const VirtualFlagsVector& flags() const; const VirtualCoordVector& coords() const; bool isClosed() const; public: /** * Updates the path coords from the flags/coords, starting at first. * * \return The index after the last element of this part. */ VirtualCoordVector::size_type update(VirtualCoordVector::size_type first); /** * Finds the index of the next dash point after first, or returns size()-1. * * \todo Consider return a SplitPathCoord (cf. actual usage). */ size_type findNextDashPoint(PathCoordVector::size_type first) const; /** * Finds the path coordinate index with or just before the given length. */ size_type lowerBound( PathCoord::length_type length, size_type first, size_type last ) const; /** * Finds the path coordinate index with or just after the given length. */ size_type upperBound( PathCoord::length_type length, size_type first, size_type last ) const; /** * Returns the length of the path. */ PathCoord::length_type length() const; /** * Calculates the area of this part. */ double calculateArea() const; QRectF calculateExtent() const; bool intersectsBox(const QRectF& box) const; bool isPointInside(MapCoordF coord) const; private: /** * Recursive approximation of a bezier curve by polygonal segments. */ void curveToPathCoord( MapCoordF c0, MapCoordF c1, MapCoordF c2, MapCoordF c3, MapCoordVector::size_type edge_start, float p0, float p1 ); }; /** * A VirtualPath class represents a single path out of a sequence of coords and flags. * * It provides a PathCoordVector which is is a polyline approximation of the path * (i.e. no curves) and provides metadata such as length for each point of the path. */ class VirtualPath { public: /** A reaonably sized unsigned integer type for coord vector sizes and indexes. */ using size_type = VirtualCoordVector::size_type; /** The underlying coordinates and flags. */ VirtualCoordVector coords; /** The derived flattened coordinates and meta data. */ PathCoordVector path_coords; /** Index of first coordinate of this path in the coords. */ size_type first_index; /** Index of the last coordinate of this part in the coords. */ size_type last_index; explicit VirtualPath(const MapCoordVector& coords); VirtualPath(const MapCoordVector& coords, size_type first, size_type last); explicit VirtualPath(const VirtualCoordVector& coords); VirtualPath(const VirtualCoordVector& coords, size_type first, size_type last); VirtualPath(const MapCoordVector& flags, const MapCoordVectorF& coords); VirtualPath(const MapCoordVector& flags, const MapCoordVectorF& coords, size_type first, size_type last); VirtualPath(const VirtualPath&) = default; VirtualPath(VirtualPath&&) = default; protected: VirtualPath& operator=(const VirtualPath&) = default; VirtualPath& operator=(VirtualPath&&) = default; public: /** * Returns true if there are no nodes in this path. */ bool empty() const; /** * Returns the number of coordinates in this path. * * \see countRegularNodes() */ size_type size() const; /** * Calculates the number of regular nodes in this path. * * Regular nodes exclude close points and curve handles. */ size_type countRegularNodes() const; /** * Returns true if the path is closed. * * For closed paths, the last coordinate is at the same position as the * first coordinate of the path,and has the "close point" flag set. * These coords will move together when moved by the user, appearing as * just one coordinate. * * Parts of PathObjects can be closed and opened with PathPart::setClosed() * or PathPart::connectEnds(). * * Objects with area symbols must always be closed. */ bool isClosed() const; /** * Returns the length of the path. */ PathCoord::length_type length() const; /** * Calculates the area of this part. */ double calculateArea() const; QRectF calculateExtent() const; bool intersectsBox(const QRectF& box) const; bool isPointInside(MapCoordF coord) const; PathCoord findClosestPointTo( MapCoordF coord, float& distance_squared, float distance_bound_squared, size_type start_index, size_type end_index) const; /** * Determines the index of the previous regular coordinate. * * Regular coordinates exclude bezier control points and close points. * * @param base_index The index of a regular coordinate from which to start. */ size_type prevCoordIndex(size_type base_index) const; /** * Determines the index of the next regular coordinate. * * Regular coordinates exclude bezier control points and close points. * * @param base_index The index of a regular coordinate from which to start. */ size_type nextCoordIndex(size_type base_index) const; MapCoordF calculateTangent(size_type i) const; std::pair calculateTangentScaling(size_type i) const; /** * Calculates the path tangent at the given MapCoord index. * * Takes care of cases where successive points are at equal positions. * * @param i Index of the coordinate where to query the tangent. * @param backward If false, returns the forward tangent, if true, returns * the backward tangent. Makes a difference at sharp corners. * @param ok Is set to true if the tangent can be found correctly, false if * failing to find the tangent. */ MapCoordF calculateTangent( size_type i, bool backward, bool& ok ) const; /** * Similar to calculateTangent(). */ MapCoordF calculateIncomingTangent( size_type i, bool& ok ) const; /** * Similar to calculateTangent(). */ MapCoordF calculateOutgoingTangent( size_type i, bool& ok ) const; void copy( const SplitPathCoord& first, const SplitPathCoord& last, MapCoordVector& out_coords ) const; void copy( const SplitPathCoord& first, const SplitPathCoord& last, MapCoordVector& out_flags, MapCoordVectorF& out_coords ) const; void copyLengths( const SplitPathCoord& first, const SplitPathCoord& last, std::vector& out_lengths ) const; }; // ### PathCoordVector inline code ### inline const VirtualFlagsVector& PathCoordVector::flags() const { return virtual_coords.flags; } inline const VirtualCoordVector& PathCoordVector::coords() const { return virtual_coords; } inline PathCoordVector::size_type PathCoordVector::lowerBound( PathCoord::length_type length, PathCoordVector::size_type first, PathCoordVector::size_type last ) const { auto index = first; while (index != last) { ++index; if ((*this)[index].clen < length) { --index; break; } } return index; } inline PathCoordVector::size_type PathCoordVector::upperBound( PathCoord::length_type length, PathCoordVector::size_type first, PathCoordVector::size_type last ) const { auto index = first; while (index != last && (*this)[index].clen < length) { ++index; } return index; } // ### VirtualPath inline code ### inline bool VirtualPath::empty() const { return last_index+1 == first_index; } inline VirtualPath::size_type VirtualPath::size() const { return last_index - first_index + 1; } inline PathCoord::length_type VirtualPath::length() const { return path_coords.length(); } inline double VirtualPath::calculateArea() const { return path_coords.calculateArea(); } inline QRectF VirtualPath::calculateExtent() const { return path_coords.calculateExtent(); } inline bool VirtualPath::intersectsBox(const QRectF& box) const { return path_coords.intersectsBox(box); } inline bool VirtualPath::isPointInside(MapCoordF coord) const { return path_coords.isPointInside(coord); } } // namespace OpenOrienteering #endif mapper-0.8.1.1/src/fileformats/000077500000000000000000000000001325266516600162615ustar00rootroot00000000000000mapper-0.8.1.1/src/fileformats/file_format.cpp000066400000000000000000000047651325266516600212700ustar00rootroot00000000000000/* * Copyright 2012, 2013 Pete Curtis * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "file_format.h" #include namespace OpenOrienteering { // ### FileFormatException ### // virtual FileFormatException::~FileFormatException() noexcept = default; // virtual const char* FileFormatException::what() const noexcept { return msg_c.constData(); } // ### FileFormat ### FileFormat::FileFormat(FileFormat::FileType file_type, const char* id, const QString& description, const QString& file_extension, FileFormat::FormatFeatures features) : file_type(file_type), format_id(id), format_description(description), format_features(features) { Q_ASSERT(file_type != 0); Q_ASSERT(qstrlen(id) > 0); Q_ASSERT(!description.isEmpty()); if (!file_extension.isEmpty()) addExtension(file_extension); } FileFormat::~FileFormat() = default; void FileFormat::addExtension(const QString& file_extension) { file_extensions << file_extension; format_filter = QString::fromLatin1("%1 (*.%2)").arg(format_description, file_extensions.join(QString::fromLatin1(" *."))); } bool FileFormat::understands(const unsigned char *buffer, std::size_t sz) const { Q_UNUSED(buffer); Q_UNUSED(sz); return false; } Importer *FileFormat::createImporter(QIODevice* stream, Map *map, MapView *view) const { Q_UNUSED(stream); Q_UNUSED(map); Q_UNUSED(view); throw FileFormatException(QCoreApplication::translate("OpenOrienteering::Importer", "Format (%1) does not support import").arg(description())); } Exporter *FileFormat::createExporter(QIODevice* stream, Map *map, MapView *view) const { Q_UNUSED(stream); Q_UNUSED(map); Q_UNUSED(view); throw FileFormatException(QCoreApplication::translate("OpenOrienteering::Exporter", "Format (%1) does not support export").arg(description())); } } // namespace OpenOrienteering mapper-0.8.1.1/src/fileformats/file_format.h000066400000000000000000000222241325266516600207230ustar00rootroot00000000000000/* * Copyright 2012, 2013 Pete Curtis * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_FILE_FORMAT_H #define OPENORIENTEERING_FILE_FORMAT_H #include #include #include #include #include #include #include #include class QIODevice; namespace OpenOrienteering { class Exporter; class Importer; class Map; class MapView; /** An exception type thrown by an importer or exporter if it encounters a fatal error. */ class FileFormatException : public std::exception // clazy:exclude=copyable-polymorphic { public: /** Creates a new exception with the given message * \param message a text describing the exceptional event that has occurred. */ FileFormatException(const QString& message = QString()); /** Creates a new exception with the given message * \param message a text describing the exceptional event that has occurred. */ FileFormatException(const char* message); /** Copy-constructor (C++ FAQ 17.17). */ FileFormatException(const FileFormatException& other) noexcept; /** Destroys the exception object. */ ~FileFormatException() noexcept override; /** Returns the message as a QString. */ const QString& message() const noexcept; /** Returns the message as a C string. */ const char* what() const noexcept override; private: QString const msg; QByteArray const msg_c; }; /** Describes a file format understood by this application. Each file format has an ID * (an internal, non-translated string); a description (translated); a file extension * (non-translated); and methods to indicate their support for import or export. Formats * are installed into a FormatRegistry, and can be looked up in a variety of ways. * * A typical FileFormat subclass will look like this: * * \code * class MyCustomFileFormat : public FileFormat { * public: * MyCustomFileFormat : FileFormat("custom", ::OpenOrienteering::ImportExport::tr("Custom file"), "custom", true, true) { * } * * Importer *createImporter(QIODevice* stream, Map *map, MapView *view) const { * return new CustomImporter(stream, map, view); * } * * Exporter *createExporter(QIODevice* stream, Map *map, MapView *view) const { * return new CustomExporter(stream, map, view); * } * } * \endcode */ class FileFormat { public: /** File type enumeration. * * Each file type shall use a distinct bit so that they may be OR-combined. * * Currently the program is only used for mapping, and "Map" is the only * element. "Course" or "Event" are possible additions. */ enum FileType { MapFile = 0x01, OgrFile = 0x02, ///< Geospatial vector data supported by OGR AllFiles = MapFile, ///< All types which can be handled by an editor. EndOfFileTypes }; /** A type which handles OR-combinations of file types. */ Q_DECLARE_FLAGS(FileTypes, FileType) /** File format features. * * Each feature shall use a distinct bit so that they may be OR-combined. */ enum FormatFeatureFlag { ImportSupported = 0x01, ExportSupported = 0x02, ExportLossy = 0x04, EndOfFormatFeatures }; /** A type which handles OR-combinations of format implementation features. */ Q_DECLARE_FLAGS(FormatFeatures, FormatFeatureFlag) /** Creates a new file format with the given parameters. * * Don't use a leading dot on the file extension. * */ FileFormat(FileType file_type, const char* id, const QString& description, const QString& file_extension, FormatFeatures features); FileFormat(const FileFormat&) = delete; FileFormat(FileFormat&&) = delete; /** Destroys the file format information. */ virtual ~FileFormat(); FileFormat& operator=(const FileFormat&) = delete; FileFormat& operator=(FileFormat&&) = delete; /** Registers an alternative file name extension. * It is used by the filter. */ void addExtension(const QString& file_extension); /** Returns the type of file. */ FileType fileType() const; /** Returns the internal ID of the file format. */ const char* id() const; /** Returns a short human-readable description of the file format. */ const QString& description() const; /** Returns the primary file name extension used by this file format. * * This shall only be used for constructing new file names. Otherwise * you will probably need to use fileExtensions(). */ const QString& primaryExtension() const; /** Returns all file name extension supported by this file format. */ const QStringList& fileExtensions() const; /** Returns the filter that represents this format in file dialogs. */ const QString& filter() const; /** Returns true if this file format supports importing a map from its associated file type. */ bool supportsImport() const; /** Returns true if this file format supports exporting a map to its associated file type. */ bool supportsExport() const; /** Returns true if an exporter for this file format is potentially lossy, i.e., if the exported * file cannot fully represent all aspects of the internal OO map objects. This flag is used by * the application to warn the user before saving to a lossy file type. */ bool isExportLossy() const; /** Returns true if this file format believes it is capable of understanding a file that * starts with the given byte sequence. "Magic" numbers and version information is commonly * placed at the beginning of a file, and this method is used by the application to pre-screen * for a suitable Importer. If there is any doubt about whether the file format can successfully * process a file, this method should return false. */ virtual bool understands(const unsigned char *buffer, std::size_t sz) const; /** Creates an Importer that will read a map file from the given stream into the given map and view. * The caller can then call doImport() in the returned object to start the import process. The caller * is responsible for deleting the Importer when it's finished. * * If the Importer could not be created, then this method should throw a FormatException. */ virtual Importer* createImporter(QIODevice* stream, Map *map, MapView *view) const; /** Creates an Exporter that will save the given map and view into the given stream. * The caller can then call doExport() in the returned object to start the export process. The caller * is responsible for deleting the Exporter when it's finished. * * If the Exporter could not be created, then this method should throw a FormatException. */ virtual Exporter *createExporter(QIODevice* stream, Map *map, MapView *view) const; private: FileType file_type; const char* format_id; QString format_description; QStringList file_extensions; QString format_filter; FormatFeatures format_features; }; // ### FileFormatException inline and header code ### inline FileFormatException::FileFormatException(const QString& message) : msg(message) , msg_c(message.toLocal8Bit()) { // Nothing } inline FileFormatException::FileFormatException(const char* message) : msg(QString::fromLatin1(message)) , msg_c(message) { // Nothing } inline FileFormatException::FileFormatException(const FileFormatException& other) noexcept : msg(other.msg) , msg_c(other.msg_c) { // Nothing } inline const QString& FileFormatException::message() const noexcept { return msg; } // ### FileFormat inline and header code ### inline FileFormat::FileType FileFormat::fileType() const { return file_type; } inline const char* FileFormat::id() const { return format_id; } inline const QString& FileFormat::description() const { return format_description; } inline const QString& FileFormat::primaryExtension() const { Q_ASSERT(file_extensions.size() > 0); // by constructor return file_extensions[0]; } inline const QStringList& FileFormat::fileExtensions() const { return file_extensions; } inline const QString& FileFormat::filter() const { return format_filter; } inline bool FileFormat::supportsImport() const { return format_features.testFlag(FileFormat::ImportSupported); } inline bool FileFormat::supportsExport() const { return format_features.testFlag(FileFormat::ExportSupported); } inline bool FileFormat::isExportLossy() const { return format_features.testFlag(FileFormat::ExportLossy); } } // namespace OpenOrienteering Q_DECLARE_OPERATORS_FOR_FLAGS(OpenOrienteering::FileFormat::FileTypes) Q_DECLARE_OPERATORS_FOR_FLAGS(OpenOrienteering::FileFormat::FormatFeatures) #endif // OPENORIENTEERING_FILE_FORMAT_H mapper-0.8.1.1/src/fileformats/file_format_registry.cpp000066400000000000000000000053131325266516600232060ustar00rootroot00000000000000/* * Copyright 2012, 2013 Pete Curtis * Copyright 2013, 2015, 2016 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "file_format_registry.h" #include #include "fileformats/file_format.h" namespace OpenOrienteering { FileFormatRegistry FileFormats; // ### FormatRegistry ### FileFormatRegistry::FileFormatRegistry() noexcept : default_format_id{ nullptr } { // nothing else } FileFormatRegistry::~FileFormatRegistry() { for (auto format : fmts) delete format; } void FileFormatRegistry::registerFormat(FileFormat *format) { fmts.push_back(format); if (fmts.size() == 1) default_format_id = format->id(); Q_ASSERT(findFormatForFilename(QLatin1String("filename.") + format->primaryExtension()) != nullptr); // There may be more than one format! Q_ASSERT(findFormatByFilter(format->filter()) == format); // The filter shall be unique at least by description. } std::unique_ptr FileFormatRegistry::unregisterFormat(const FileFormat* format) { std::unique_ptr ret; auto it = std::find(begin(fmts), end(fmts), format); if (it != end(fmts)) { ret.reset(*it); fmts.erase(it); } return ret; } const FileFormat *FileFormatRegistry::findFormat(const char* id) const { for (auto format : fmts) { if (qstrcmp(format->id(), id) == 0) return format; } return nullptr; } const FileFormat *FileFormatRegistry::findFormatByFilter(const QString& filter) const { for (auto format : fmts) { // Compare only before closing ')'. Needed for QTBUG 51712 workaround in // file_dialog.cpp, and warranted by Q_ASSERT in registerFormat(). if (filter.startsWith(format->filter().leftRef(format->filter().length()-1))) return format; } return nullptr; } const FileFormat *FileFormatRegistry::findFormatForFilename(const QString& filename) const { QString file_extension = QFileInfo(filename).suffix(); for (auto format : fmts) { if (format->fileExtensions().contains(file_extension, Qt::CaseInsensitive)) return format; } return nullptr; } } // namespace OpenOrienteering mapper-0.8.1.1/src/fileformats/file_format_registry.h000066400000000000000000000063471325266516600226630ustar00rootroot00000000000000/* * Copyright 2012, 2013 Pete Curtis * Copyright 2013, 2016 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_FILE_FORMAT_REGISTRY_H #define OPENORIENTEERING_FILE_FORMAT_REGISTRY_H #include #include #include namespace OpenOrienteering { class FileFormat; /** Provides a registry for file formats, and takes ownership of the supported format objects. */ class FileFormatRegistry { public: /** Creates an empty file format registry. */ FileFormatRegistry() noexcept; FileFormatRegistry(const FileFormatRegistry&) = delete; /** Destroys a file format registry. */ ~FileFormatRegistry(); FileFormatRegistry& operator=(const FileFormatRegistry&) = delete; /** Returns an immutable list of the available file formats. */ inline const std::vector &formats() const { return fmts; } /** Finds a file format with the given internal ID, or returns nullptr if no format * is found. */ const FileFormat *findFormat(const char* id) const; /** Finds a file format which implements the given filter, or returns nullptr if no * format is found. * * Only the file format's filter string before the closing ')' is taken into * account for matching, i.e. the given parameter 'filter' may contain * additional extensions following the original ones. */ const FileFormat *findFormatByFilter(const QString& filter) const; /** Finds a file format whose file extension matches the file extension of the given * path, or returns nullptr if no matching format is found. */ const FileFormat *findFormatForFilename(const QString& filename) const; /** Returns the ID of default file format for this registry. This will automatically * be set to the first registered format. */ const char* defaultFormat() const { return default_format_id; } /** Registers a new file format. The registry takes ownership of the provided Format. */ void registerFormat(FileFormat *format); /** * Unregisters a file format. * * Returns a non-const pointer to the file format and transfers ownership to the caller. */ std::unique_ptr unregisterFormat(const FileFormat *format); private: std::vector fmts; const char* default_format_id; }; /** A FileFormatRegistry that is globally defined for convenience. Within the scope of a single * application, you can simply use calls of the form "FileFormats.findFormat(...)". */ extern FileFormatRegistry FileFormats; } // namespace OpenOrienteering #endif // OPENORIENTEERING_FILE_FORMAT_REGISTRY_H mapper-0.8.1.1/src/fileformats/file_import_export.cpp000066400000000000000000000120121325266516600226730ustar00rootroot00000000000000/* * Copyright 2012, 2013 Pete Curtis * Copyright 2013, 2014 Thomas Schöps * Copyright 2013-2015 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "file_import_export.h" #include #include "core/map.h" #include "core/map_part.h" #include "core/map_view.h" #include "core/objects/object.h" #include "core/symbols/line_symbol.h" #include "core/symbols/point_symbol.h" #include "core/symbols/symbol.h" #include "fileformats/file_format.h" #include "templates/template.h" namespace OpenOrienteering { // ### ImportExport ### ImportExport::~ImportExport() = default; QVariant ImportExport::option(const QString& name) const { if (!options.contains(name)) throw FileFormatException(::OpenOrienteering::ImportExport::tr("No such option: %1", "No such import / export option").arg(name)); return options[name]; } // ### Importer ### Importer::~Importer() = default; void Importer::doImport(bool load_symbols_only, const QString& map_path) { if (view) view->setTemplateLoadingBlocked(true); import(load_symbols_only); // Object post processing: // - make sure that there is no object without symbol // - make sure that all area-only path objects are closed // - make sure that there are no special points in wrong places (e.g. curve starts inside curves) for (int p = 0; p < map->getNumParts(); ++p) { MapPart* part = map->getPart(p); for (int o = 0; o < part->getNumObjects(); ++o) { Object* object = part->getObject(o); if (object->getSymbol() == nullptr) { addWarning(::OpenOrienteering::Importer::tr("Found an object without symbol.")); if (object->getType() == Object::Point) object->setSymbol(map->getUndefinedPoint(), true); else if (object->getType() == Object::Path) object->setSymbol(map->getUndefinedLine(), true); else { // There is no undefined symbol for this type of object, delete the object part->deleteObject(o, false); --o; continue; } } if (object->getType() == Object::Path) { PathObject* path = object->asPath(); Symbol::Type contained_types = path->getSymbol()->getContainedTypes(); if (contained_types & Symbol::Area && !(contained_types & Symbol::Line)) path->closeAllParts(); path->normalize(); } } } if (auto deleted = map->deleteIrregularObjects()) { addWarning(tr("Dropped %n irregular object(s).", nullptr, int(deleted))); } // Symbol post processing for (int i = 0; i < map->getNumSymbols(); ++i) { if (!map->getSymbol(i)->loadFinished(map)) throw FileFormatException(::OpenOrienteering::Importer::tr("Error during symbol post-processing.")); } // Template loading: try to find all template files if (view) view->setTemplateLoadingBlocked(false); bool have_lost_template = false; for (int i = 0; i < map->getNumTemplates(); ++i) { Template* temp = map->getTemplate(i); bool found_in_map_dir = false; if (!temp->tryToFindTemplateFile(map_path, &found_in_map_dir)) { have_lost_template = true; } else if (!view || view->getTemplateVisibility(temp).visible) { if (!temp->loadTemplateFile(false)) { addWarning(tr("Failed to load template '%1', reason: %2") .arg(temp->getTemplateFilename(), temp->errorString())); } else { auto error_string = temp->errorString(); if (found_in_map_dir) { error_string.prepend( ::OpenOrienteering::Importer::tr( "Template \"%1\" has been loaded from the map's directory instead of" " the relative location to the map file where it was previously." ).arg(temp->getTemplateFilename()) + QLatin1Char('\n') ); } if (!error_string.isEmpty()) { addWarning(tr("Warnings when loading template '%1':\n%2") .arg(temp->getTemplateFilename(), temp->errorString())); } } } } if (have_lost_template) { #if defined(Q_OS_ANDROID) addWarning(tr("At least one template file could not be found.")); #else addWarning(tr("At least one template file could not be found.") + QLatin1Char(' ') + tr("Click the red template name(s) in the Templates -> Template setup window to locate the template file name(s).")); #endif } } void Importer::finishImport() { // Nothing, not inlined } // ### Exporter ### Exporter::~Exporter() = default; } // namespace OpenOrienteering mapper-0.8.1.1/src/fileformats/file_import_export.h000066400000000000000000000174651325266516600223610ustar00rootroot00000000000000/* * Copyright 2012, 2013 Pete Curtis * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_IMPORT_EXPORT_H #define OPENORIENTEERING_IMPORT_EXPORT_H #include #include #include #include #include class QIODevice; namespace OpenOrienteering { class Map; class MapView; /** Abstract base class for both importer and exporters; provides support for configuring the map and * view to manipulate, setting and retrieving options, and collecting a list of warnings. * * Subclasses should define default values for options they intend to use in their constructors, * by calling setOption() with the relevant values. There is no such thing as an "implicit default" * for options. */ class ImportExport { Q_DECLARE_TR_FUNCTIONS(OpenOrienteering::ImportExport) public: /** Creates a new importer or exporter with the given input stream, map, and view. */ ImportExport(QIODevice* stream, Map *map, MapView *view); ImportExport(const ImportExport&) = delete; ImportExport(ImportExport&&) = delete; /** Destroys an importer or exporter. */ virtual ~ImportExport(); /** Returns the current list of warnings collected by this object. */ const std::vector &warnings() const; /** Sets an option in this importer or exporter. */ void setOption(const QString& name, const QVariant& value); /** Retrieves the value of an options in this importer or exporter. If the option does not have * a value - either a default value assigned in the constructor, or a custom value assigned * through setOption() - then a FormatException will be thrown. */ QVariant option(const QString& name) const; protected: /** Adds an import/export warning to the current list of warnings. The provided message * should be translated. */ void addWarning(const QString& str); protected: /// The input / output stream QIODevice* stream; /// The Map to import or export Map *map; /// The MapView to import or export MapView *view; private: /// A list of options for the import/export QHash options; /// A list of warnings std::vector warn; }; /** * Represents an action that the user must take to successfully complete an import. * TODO: Currently not fully implemented, as this should be done using the * planned ProblemWidget instead, which works independently of import / export. */ class ImportAction { // Nothing }; /** Base class for all importers. An Importer has the following lifecycle: * -# The Importer is constructed, with pointers to the map and view. The Importer * should also set default values for any options it will read. The base class * will throw an exception if the importer reads an option that does not have a value. * -# setOption() will be called zero or more times to customize the options. * -# doImport() will be called to perform the initial import. The implementation of * this method will try to populate the map and view from the given file, and may * optionally register action items for the user via addAction(). * -# If action items are present, then they will be presented to the user. Each * action item will have its satisfy() method called with the user's choice. * -# finishImport() will be called. If any action items were created, this method * should finish the import based on the values supplied by the user. */ class Importer : public ImportExport { Q_DECLARE_TR_FUNCTIONS(OpenOrienteering::Importer) public: /** Creates a new Importer with the given output stream, map, and view. */ Importer(QIODevice* stream, Map *map, MapView *view); /** Destroys this Importer. */ ~Importer() override; /** Returns the current list of action items. */ inline const std::vector &actions() const; /** Begins the import process. The implementation of this method should read the file * and populate the map and view from it. If a fatal error is encountered (such as a * missing or corrupt file), than it should throw a FormatException. If the import can * proceed, but information might be lost in the process, than it should call * addWarning() with a translated, useful description of the issue. The line between * errors and warnings is somewhat of a judgement call on the part of the author, but * generally an Importer should not succeed unless the map is populated sufficiently * to be useful. */ void doImport(bool load_symbols_only, const QString& map_path = QString()); /** Once all action items are satisfied, this method should be called to complete the * import process. This class defines a default implementation, that does nothing. */ virtual void finishImport(); protected: /** Implementation of doImport(). */ virtual void import(bool load_symbols_only) = 0; /** Adds an action item to the current list. */ inline void addAction(const ImportAction &action); private: /// A list of action items that must be resolved before the import can be completed std::vector act; }; /** Base class for all exporters. An Exporter has the following lifecycle: * * 1. The Exporter is constructed, with pointers to the filename, map and view. The Exporter * should also set default values for any options it will read. The base class * will throw an exception if the exporter reads an option that does not have a value. * 2. setOption() will be called zero or more times to customize the options. * 3. doExport() will be called to perform the export. */ class Exporter : public ImportExport { Q_DECLARE_TR_FUNCTIONS(OpenOrienteering::Exporter) public: /** Creates a new Importer with the given i/o stream, map, and view. */ Exporter(QIODevice* stream, Map *map, MapView *view); /** Destroys the current Exporter. */ ~Exporter() override; /** Exports the map and view to the given file. If a fatal error is encountered (such as a * permission problem), than this method should throw a FormatException. If the export can * proceed, but information might be lost in the process, than it should call * addWarning() with a translated, useful description of the issue. */ virtual void doExport() = 0; }; // ### ImportExport inline code ### inline ImportExport::ImportExport(QIODevice* stream, Map* map, MapView* view) : stream(stream), map(map), view(view) { // Nothing } inline const std::vector< QString >& ImportExport::warnings() const { return warn; } inline void ImportExport::addWarning(const QString& str) { warn.push_back(str); } inline void ImportExport::setOption(const QString& name, const QVariant& value) { options[name] = value; } // ### Importer inline code ### inline Importer::Importer(QIODevice* stream, Map* map, MapView* view) : ImportExport(stream, map, view) { // Nothing } inline const std::vector< ImportAction >& Importer::actions() const { return act; } inline void Importer::addAction(const ImportAction& action) { act.push_back(action); } // ### Exporter ### inline Exporter::Exporter(QIODevice* stream, Map* map, MapView* view) : ImportExport(stream, map, view) { // Nothing } } // namespace OpenOrienteering #endif // OPENORIENTEERING_IMPORT_EXPORT_H mapper-0.8.1.1/src/fileformats/native_file_format.cpp000066400000000000000000000330251325266516600226250ustar00rootroot00000000000000/* * Copyright 2012 Pete Curtis * Copyright 2012, 2013 Thomas Schöps * Copyright 2012-2015 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef NO_NATIVE_FILE_FORMAT #include "native_file_format.h" #include #include "core/georeferencing.h" #include "core/map.h" #include "core/map_color.h" #include "core/map_grid.h" #include "core/map_printer.h" #include "core/map_view.h" #include "core/symbols/symbol.h" #include "fileformats/file_import_export.h" #include "templates/template.h" #include "templates/template_image.h" #include "undo/undo_manager.h" #include "util/util.h" namespace OpenOrienteering { // ### NativeFileImport declaration ### /** An Importer for the native file format. This class delegates to the load() and loadImpl() methods of the * model objects, but long-term this can be refactored out of the model into this class. * * \deprecated */ class NativeFileImport : public Importer { public: /** Creates a new native file importer. */ NativeFileImport(QIODevice* stream, Map *map, MapView *view); /** Destroys this importer. */ ~NativeFileImport() override; protected: /** Imports a native file. */ void import(bool load_symbols_only) override; }; // ### GPSProjectionParameters ### /** * Legacy parameters for an ellipsoid and an orthographic projection of * ellipsoid coordinates to 2D map (template) coordinates. */ struct GPSProjectionParameters { double a; // ellipsoidal semi-major axis double b; // ellipsoidal semi-minor axis double e_sq; // eccentricity of the ellipsoid (squared) double center_latitude; // latitude which gives map coordinate zero double center_longitude; // longitude which gives map coordinate zero double sin_center_latitude; // sine of center_latitude double cos_center_latitude; // cosine of center_latitude double v0; // prime vertical radius of curvature at latitude of origin center_latitude }; // ### NativeFileFormat ### const int NativeFileFormat::least_supported_file_format_version = 0; const int NativeFileFormat::current_file_format_version = 30; const char NativeFileFormat::magic_bytes[4] = {0x4F, 0x4D, 0x41, 0x50}; // "OMAP" NativeFileFormat::NativeFileFormat() : FileFormat(FileFormat::MapFile, "native (deprecated)", ::OpenOrienteering::ImportExport::tr("OpenOrienteering Mapper").append(QLatin1String(" pre-0.5")), QString::fromLatin1("omap"), FileFormat::ImportSupported) { // Nothing } bool NativeFileFormat::understands(const unsigned char *buffer, std::size_t sz) const { // The first four bytes of the file must be 'OMAP'. return (sz >= 4 && memcmp(buffer, magic_bytes, 4) == 0); } Importer *NativeFileFormat::createImporter(QIODevice* stream, Map* map, MapView* view) const { return new NativeFileImport(stream, map, view); } // ### NativeFileImport ### NativeFileImport::NativeFileImport(QIODevice* stream, Map *map, MapView *view) : Importer(stream, map, view) { } NativeFileImport::~NativeFileImport() { } void NativeFileImport::import(bool load_symbols_only) { addWarning(::OpenOrienteering::Importer::tr("This file uses an obsolete format. " "Support for this format is to be removed from this program soon. " "To be able to open the file in the future, save it again.")); MapCoord::boundsOffset().reset(true); char buffer[4]; stream->read(buffer, 4); // read the magic int version; stream->read((char*)&version, sizeof(int)); if (version < 0) { addWarning(::OpenOrienteering::Importer::tr("Invalid file format version.")); } else if (version < NativeFileFormat::least_supported_file_format_version) { throw FileFormatException(::OpenOrienteering::Importer::tr("Unsupported old file format version. Please use an older program version to load and update the file.")); } else if (version > NativeFileFormat::current_file_format_version) { throw FileFormatException(::OpenOrienteering::Importer::tr("Unsupported new file format version. Some map features will not be loaded or saved by this version of the program. Consider updating.")); } if (version <= 16) { Georeferencing georef; stream->read((char*)&georef.scale_denominator, sizeof(int)); if (version >= 15) loadString(stream, map->map_notes); bool gps_projection_params_set; // obsolete stream->read((char*)&gps_projection_params_set, sizeof(bool)); GPSProjectionParameters gps_projection_parameters; // obsolete stream->read((char*)&gps_projection_parameters, sizeof(GPSProjectionParameters)); if (gps_projection_params_set) { LatLon ref_point = LatLon::fromRadiant(gps_projection_parameters.center_latitude, gps_projection_parameters.center_longitude); georef.setGeographicRefPoint(ref_point); } *map->georeferencing = georef; } else if (version >= 17) { loadString(stream, map->map_notes); Georeferencing georef; stream->read((char*)&georef.scale_denominator, sizeof(int)); double value; if (version >= 18) { stream->read((char*)&value, sizeof(double)); georef.declination = Georeferencing::roundDeclination(value); } stream->read((char*)&value, sizeof(double)); georef.grivation = Georeferencing::roundDeclination(value); georef.grivation_error = value - georef.grivation; double x,y; stream->read((char*)&x, sizeof(double)); stream->read((char*)&y, sizeof(double)); georef.map_ref_point = MapCoord(x,y); stream->read((char*)&x, sizeof(double)); stream->read((char*)&y, sizeof(double)); georef.projected_ref_point = QPointF(x,y); loadString(stream, georef.projected_crs_id); loadString(stream, georef.projected_crs_spec); stream->read((char*)&y, sizeof(double)); stream->read((char*)&x, sizeof(double)); georef.geographic_ref_point = LatLon::fromRadiant(y, x); QString geographic_crs_id, geographic_crs_spec; loadString(stream, geographic_crs_id); // reserved for geographic crs id loadString(stream, geographic_crs_spec); // reserved for full geographic crs specification if (geographic_crs_spec != Georeferencing::geographic_crs_spec) { addWarning( ::OpenOrienteering::Importer::tr("The geographic coordinate reference system of the map was \"%1\". This CRS is not supported. Using \"%2\"."). arg(geographic_crs_spec, Georeferencing::geographic_crs_spec) ); } if (version <= 17) georef.initDeclination(); // Correctly set georeferencing state georef.setProjectedCRS(georef.projected_crs_id, georef.projected_crs_spec); *map->georeferencing = georef; } if (version >= 24) map->setGrid(MapGrid().load(stream, version)); map->renderable_options = Symbol::RenderNormal; if (version >= 25) { bool area_hatching_enabled, baseline_view_enabled; stream->read((char*)&area_hatching_enabled, sizeof(bool)); stream->read((char*)&baseline_view_enabled, sizeof(bool)); if (area_hatching_enabled) map->renderable_options |= Symbol::RenderAreasHatched; if (baseline_view_enabled) map->renderable_options |= Symbol::RenderBaselines; } if (version >= 6) { bool print_params_set; stream->read((char*)&print_params_set, sizeof(bool)); if (print_params_set) { MapPrinterConfig printer_config(*map); stream->read((char*)&printer_config.page_format.orientation, sizeof(int)); stream->read((char*)&printer_config.page_format.paper_size, sizeof(int)); float resolution; stream->read((char*)&resolution, sizeof(float)); printer_config.options.resolution = qRound(resolution); stream->read((char*)&printer_config.options.show_templates, sizeof(bool)); if (version >= 24) stream->read((char*)&printer_config.options.show_grid, sizeof(bool)); else printer_config.options.show_grid = false; stream->read((char*)&printer_config.center_print_area, sizeof(bool)); float print_area_left, print_area_top, print_area_width, print_area_height; stream->read((char*)&print_area_left, sizeof(float)); stream->read((char*)&print_area_top, sizeof(float)); stream->read((char*)&print_area_width, sizeof(float)); stream->read((char*)&print_area_height, sizeof(float)); printer_config.print_area = QRectF(print_area_left, print_area_top, print_area_width, print_area_height); if (version >= 26) { bool print_different_scale_enabled; stream->read((char*)&print_different_scale_enabled, sizeof(bool)); stream->read((char*)&printer_config.options.scale, sizeof(int)); if (!print_different_scale_enabled) printer_config.options.scale = map->getScaleDenominator(); } map->setPrinterConfig(printer_config); } } if (version >= 16) { stream->read((char*)&map->image_template_use_meters_per_pixel, sizeof(bool)); stream->read((char*)&map->image_template_meters_per_pixel, sizeof(double)); stream->read((char*)&map->image_template_dpi, sizeof(double)); stream->read((char*)&map->image_template_scale, sizeof(double)); } // Load colors int num_colors; stream->read((char*)&num_colors, sizeof(int)); map->color_set->colors.resize(num_colors); for (int i = 0; i < num_colors; ++i) { int priority; stream->read((char*)&priority, sizeof(int)); MapColor* color = new MapColor(priority); MapColorCmyk cmyk; stream->read((char*)&cmyk.c, sizeof(float)); stream->read((char*)&cmyk.m, sizeof(float)); stream->read((char*)&cmyk.y, sizeof(float)); stream->read((char*)&cmyk.k, sizeof(float)); color->setCmyk(cmyk); float opacity; stream->read((char*)&opacity, sizeof(float)); color->setOpacity(opacity); QString name; loadString(stream, name); color->setName(name); map->color_set->colors[i] = color; } // Load symbols int num_symbols; stream->read((char*)&num_symbols, sizeof(int)); map->symbols.resize(num_symbols); for (int i = 0; i < num_symbols; ++i) { QScopedValueRollback offset { MapCoord::boundsOffset() }; MapCoord::boundsOffset().reset(false); int symbol_type; stream->read((char*)&symbol_type, sizeof(int)); Symbol* symbol = Symbol::getSymbolForType(static_cast(symbol_type)); if (!symbol) { throw FileFormatException(::OpenOrienteering::Importer::tr("Error while loading a symbol with type %2.").arg(symbol_type)); } if (!symbol->load(stream, version, map)) { throw FileFormatException(::OpenOrienteering::Importer::tr("Error while loading a symbol.")); } map->symbols[i] = symbol; } if (!load_symbols_only) { // Load templates stream->read((char*)&map->first_front_template, sizeof(int)); int num_templates; stream->read((char*)&num_templates, sizeof(int)); map->templates.resize(num_templates); for (int i = 0; i < num_templates; ++i) { QString path; loadString(stream, path); auto temp = Template::templateForFile(path, map); if (!temp) temp.reset(new TemplateImage(path, map)); // fallback if (version >= 27) { loadString(stream, path); temp->setTemplateRelativePath(path); } temp->loadTemplateConfiguration(stream, version); map->templates[i] = temp.release(); } if (version >= 28) { int num_closed_templates; stream->read((char*)&num_closed_templates, sizeof(int)); map->closed_templates.resize(num_closed_templates); for (int i = 0; i < num_closed_templates; ++i) { QString path; loadString(stream, path); auto temp = Template::templateForFile(path, map); if (!temp) temp.reset(new TemplateImage(path, map)); // fallback loadString(stream, path); temp->setTemplateRelativePath(path); temp->loadTemplateConfiguration(stream, version); map->closed_templates[i] = temp.release(); } } // Restore widgets and views if (view) { view->load(stream, version); } else { MapView tmp{ map }; tmp.load(stream, version); } // Load undo steps if (version >= 7) { if (!map->undoManager().load(stream, version)) { throw FileFormatException(::OpenOrienteering::Importer::tr("Error while loading undo steps.")); } } // Load parts stream->read((char*)&map->current_part_index, sizeof(int)); int num_parts; if (stream->read((char*)&num_parts, sizeof(int)) < (int)sizeof(int)) { throw FileFormatException(::OpenOrienteering::Importer::tr("Error while reading map part count.")); } delete map->parts[0]; map->parts.resize(num_parts); for (int i = 0; i < num_parts; ++i) { MapPart* part = new MapPart({}, map); if (!part->load(stream, version, map)) { delete part; throw FileFormatException(::OpenOrienteering::Importer::tr("Error while loading map part %2.").arg(i+1)); } map->parts[i] = part; } } emit map->currentMapPartIndexChanged(map->current_part_index); emit map->currentMapPartChanged(map->getPart(map->current_part_index)); } } // namespace OpenOrienteering #endif mapper-0.8.1.1/src/fileformats/native_file_format.h000066400000000000000000000044441325266516600222750ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps, Pete Curtis * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef NATIVE_FILE_FORMAT_H #define NATIVE_FILE_FORMAT_H #ifndef NO_NATIVE_FILE_FORMAT #include #include "fileformats/file_format.h" class QIODevice; class Importer; namespace OpenOrienteering { class Map; class MapView; /** Provides a description of the old native file format. * This is an (architecture-dependent) binary packed format * with a file extension of "omap", and internal versioning. * * \deprecated */ class NativeFileFormat : public FileFormat { public: /** Creates a new file format representing the native type. */ NativeFileFormat(); /** Returns true if the file starts with the magic byte sequence "OMAP" (0x4f 0x4d 0x41 0x50). */ bool understands(const unsigned char *buffer, std::size_t sz) const override; /** Creates an importer for this file type. */ Importer *createImporter(QIODevice* stream, Map *map, MapView *view) const override; #ifdef MAPPER_ENABLE_NATIVE_EXPORTER /** Creates an exporter for this file type. */ Exporter *createExporter(QIODevice* stream, Map *map, MapView *view) const; #endif /** Constant describing the earliest OMAP version supported by this file format. */ static const int least_supported_file_format_version; /** Constant describing the current OMAP version used by this file format for saving. */ static const int current_file_format_version; /** The file magic: "OMAP" */ static const char magic_bytes[4]; }; } // namespace OpenOrienteering #endif // NO_NATIVE_FILE_FORMAT #endif // NATIVE_FILE_FORMAT_H mapper-0.8.1.1/src/fileformats/ocad8_file_format.cpp000066400000000000000000003116371325266516600223450ustar00rootroot00000000000000/* * Copyright 2012, 2013 Pete Curtis * Copyright 2013-2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "ocad8_file_format.h" #include "ocad8_file_format_p.h" #include #include #include #include #include #include #include #include #include "settings.h" #include "core/georeferencing.h" #include "core/map.h" #include "core/map_color.h" #include "core/map_coord.h" #include "core/map_part.h" #include "core/map_view.h" #include "core/objects/object.h" #include "core/objects/text_object.h" #include "core/symbols/symbol.h" #include "core/symbols/area_symbol.h" #include "core/symbols/combined_symbol.h" #include "core/symbols/line_symbol.h" #include "core/symbols/point_symbol.h" #include "core/symbols/text_symbol.h" #include "fileformats/xml_file_format.h" #include "fileformats/file_import_export.h" #include "templates/template.h" #include "templates/template_image.h" #include "templates/template_map.h" #include "util/util.h" #include "util/encoding.h" namespace OpenOrienteering { // ### OCAD8FileFormat ### OCAD8FileFormat::OCAD8FileFormat() : FileFormat(MapFile, "OCAD78", ::OpenOrienteering::ImportExport::tr("OCAD Versions 7, 8"), QString::fromLatin1("ocd"), ImportSupported | ExportSupported | ExportLossy) { // Nothing } bool OCAD8FileFormat::understands(const unsigned char* buffer, std::size_t sz) const { // The first two bytes of the file must be AD 0C. if (sz >= 2 && buffer[0] == 0xAD && buffer[1] == 0x0C) return true; return false; } Importer* OCAD8FileFormat::createImporter(QIODevice* stream, Map *map, MapView *view) const { return new OCAD8FileImport(stream, map, view); } Exporter* OCAD8FileFormat::createExporter(QIODevice* stream, Map* map, MapView* view) const { return new OCAD8FileExport(stream, map, view); } // ### OCAD8FileImport ### OCAD8FileImport::OCAD8FileImport(QIODevice* stream, Map* map, MapView* view) : Importer(stream, map, view), file(nullptr) { ocad_init(); const QByteArray enc_name = Settings::getInstance().getSetting(Settings::General_Local8BitEncoding).toByteArray(); encoding_1byte = Util::codecForName(enc_name); if (!encoding_1byte) encoding_1byte = QTextCodec::codecForLocale(); encoding_2byte = QTextCodec::codecForName("UTF-16LE"); offset_x = offset_y = 0; } OCAD8FileImport::~OCAD8FileImport() { ocad_shutdown(); } bool OCAD8FileImport::isRasterImageFile(const QString &filename) { int dot_pos = filename.lastIndexOf(QLatin1Char('.')); if (dot_pos < 0) return false; QString extension = filename.right(filename.length() - dot_pos - 1).toLower(); return QImageReader::supportedImageFormats().contains(extension.toLatin1()); } void OCAD8FileImport::import(bool load_symbols_only) { //qint64 start = QDateTime::currentMSecsSinceEpoch(); u32 size = stream->bytesAvailable(); u8* buffer = (u8*)malloc(size); if (!buffer) throw FileFormatException(tr("Could not allocate buffer.")); if (stream->read((char*)buffer, size) != size) throw FileFormatException(::OpenOrienteering::Importer::tr("Could not read file: %1").arg(stream->errorString())); int err = ocad_file_open_memory(&file, buffer, size); if (err != 0) throw FileFormatException(::OpenOrienteering::Importer::tr("Could not read file: %1").arg(tr("libocad returned %1").arg(err))); if (file->header->major <= 5 || file->header->major >= 9) throw FileFormatException(::OpenOrienteering::Importer::tr("Could not read file: %1").arg(tr("OCAD files of version %1 are not supported!").arg(file->header->major))); //qDebug() << "file version is" << file->header->major << ", type is" // << ((file->header->ftype == 2) ? "normal" : "other"); //qDebug() << "map scale is" << file->setup->scale; // Scale and georeferencing parameters Georeferencing georef; georef.setScaleDenominator(file->setup->scale); georef.setProjectedRefPoint(QPointF(file->setup->offsetx, file->setup->offsety)); if (qAbs(file->setup->angle) >= 0.01) /* degrees */ { georef.setGrivation(file->setup->angle); } map->setGeoreferencing(georef); map->setMapNotes(convertCString((const char*)file->buffer + file->header->infopos, file->header->infosize, false)); // TODO: print parameters // Load the separations to a temporary stack std::vector< MapColor* > separations; int num_separations = ocad_separation_count(file); #if 1 addWarning(tr("%n color separation(s) were skipped, reason: Import disabled.", "", num_separations)); num_separations = 0; #endif if (num_separations < 0) { addWarning(tr("Could not load the spot color definitions, error: %1").arg(num_separations)); num_separations = 0; } separations.reserve(num_separations); for (int i = 0; i < num_separations; i++) { const OCADColorSeparation *ocad_separation = ocad_separation_at(file, i); MapColor* color = new MapColor(convertPascalString(ocad_separation->sep_name), MapColor::Reserved); color->setSpotColorName(convertPascalString(ocad_separation->sep_name).toUpper()); // OCD stores CMYK values as integers from 0-200. const MapColorCmyk cmyk( 0.005f * ocad_separation->cyan, 0.005f * ocad_separation->magenta, 0.005f * ocad_separation->yellow, 0.005f * ocad_separation->black ); color->setCmyk(cmyk); color->setOpacity(1.0f); separations.push_back(color); } // Load colors int num_colors = ocad_color_count(file); for (int i = 0; i < num_colors; i++) { OCADColor *ocad_color = ocad_color_at(file, i); MapColor* color = new MapColor(convertPascalString(ocad_color->name), map->color_set->colors.size()); // OCD stores CMYK values as integers from 0-200. MapColorCmyk cmyk( 0.005f * ocad_color->cyan, 0.005f * ocad_color->magenta, 0.005f * ocad_color->yellow, 0.005f * ocad_color->black ); color->setCmyk(cmyk); color->setOpacity(1.0f); SpotColorComponents components; for (int j = 0; j < num_separations; ++j) { const u8& ocad_halftone = ocad_color->spot[j]; if (ocad_halftone <= 200) { float halftone = 0.005f * ocad_halftone; components.reserve(std::size_t(num_separations)); // reserves only once for same capacity components.push_back(SpotColorComponent(separations[j], halftone)); // clazy:exclude=reserve-candidates } } if (!components.empty()) { color->setSpotColorComposition(components); const MapColorCmyk cmyk(color->getCmyk()); color->setCmykFromSpotColors(); if (cmyk != color->getCmyk()) // The color's CMYK was customized. color->setCmyk(cmyk); } if (i == 0 && color->isBlack() && color->getName() == QLatin1String("Registration black") && XMLFileFormat::active_version >= 6 ) { delete color; color = nullptr; color_index[ocad_color->number] = Map::getRegistrationColor(); addWarning(tr("Color \"Registration black\" is imported as a special color.")); // NOTE: This does not make a difference in output // as long as no spot colors are created, // but as a special color, it is protected from modification, // and it will be saved as number 0 in OCD export. } else { map->color_set->colors.push_back(color); color_index[ocad_color->number] = color; } } // Insert the spot colors into the map for (int i = 0; i < num_separations; ++i) { map->addColor(separations[i], map->color_set->colors.size()); } // Load symbols for (OCADSymbolIndex *idx = ocad_symidx_first(file); idx; idx = ocad_symidx_next(file, idx)) { for (int i = 0; i < 256; i++) { OCADSymbol *ocad_symbol = ocad_symbol_at(file, idx, i); if (ocad_symbol && ocad_symbol->number != 0) { Symbol *symbol = nullptr; if (ocad_symbol->type == OCAD_POINT_SYMBOL) { symbol = importPointSymbol((OCADPointSymbol *)ocad_symbol); } else if (ocad_symbol->type == OCAD_LINE_SYMBOL) { symbol = importLineSymbol((OCADLineSymbol *)ocad_symbol); } else if (ocad_symbol->type == OCAD_AREA_SYMBOL) { symbol = importAreaSymbol((OCADAreaSymbol *)ocad_symbol); } else if (ocad_symbol->type == OCAD_TEXT_SYMBOL) { symbol = importTextSymbol((OCADTextSymbol *)ocad_symbol); } else if (ocad_symbol->type == OCAD_RECT_SYMBOL) { RectangleInfo* rect = importRectSymbol((OCADRectSymbol *)ocad_symbol); map->symbols.push_back(rect->border_line); if (rect->has_grid) { map->symbols.push_back(rect->inner_line); map->symbols.push_back(rect->text); } continue; } if (symbol) { map->symbols.push_back(symbol); symbol_index[ocad_symbol->number] = symbol; } else { addWarning(tr("Unable to import symbol \"%3\" (%1.%2)") .arg(ocad_symbol->number / 10).arg(ocad_symbol->number % 10) .arg(convertPascalString(ocad_symbol->name))); } } } } if (!load_symbols_only) { // Load objects // Place all objects into a single OCAD import part MapPart* part = new MapPart(tr("OCAD import layer"), map); for (OCADObjectIndex *idx = ocad_objidx_first(file); idx; idx = ocad_objidx_next(file, idx)) { for (int i = 0; i < 256; i++) { OCADObjectEntry *entry = ocad_object_entry_at(file, idx, i); OCADObject *ocad_obj = ocad_object(file, entry); if (ocad_obj) { Object *object = importObject(ocad_obj, part); if (object) { part->objects.push_back(object); } } } } delete map->parts[0]; map->parts[0] = part; map->current_part_index = 0; // Load templates map->templates.clear(); for (OCADStringIndex *idx = ocad_string_index_first(file); idx; idx = ocad_string_index_next(file, idx)) { for (int i = 0; i < 256; i++) { OCADStringEntry *entry = ocad_string_entry_at(file, idx, i); if (entry->type != 0 && entry->size > 0) importString(entry); } } map->first_front_template = map->templates.size(); // Templates in front of the map are not supported by OCD // Fill view with relevant fields from OCD file if (view) { if (file->setup->zoom >= MapView::zoom_out_limit && file->setup->zoom <= MapView::zoom_in_limit) view->setZoom(file->setup->zoom); s32 buf[3]; ocad_point(buf, &file->setup->center); MapCoord center_pos; convertPoint(center_pos, buf[0], buf[1]); view->setCenter(center_pos); } // TODO: read template visibilities /* int num_template_visibilities; file->read((char*)&num_template_visibilities, sizeof(int)); for (int i = 0; i < num_template_visibilities; ++i) { int pos; file->read((char*)&pos, sizeof(int)); TemplateVisibility* vis = getTemplateVisibility(map->getTemplate(pos)); file->read((char*)&vis->visible, sizeof(bool)); file->read((char*)&vis->opacity, sizeof(float)); } } */ // Undo steps are not supported in OCAD } ocad_file_close(file); //qint64 elapsed = QDateTime::currentMSecsSinceEpoch() - start; //qDebug() << "OCAD map imported:"<getNumSymbols()<<"symbols and"<getNumObjects()<<"objects in"<currentMapPartIndexChanged(map->current_part_index); emit map->currentMapPartChanged(map->getPart(map->current_part_index)); } void OCAD8FileImport::setStringEncodings(const char *narrow, const char *wide) { encoding_1byte = QTextCodec::codecForName(narrow); encoding_2byte = QTextCodec::codecForName(wide); } Symbol *OCAD8FileImport::importPointSymbol(const OCADPointSymbol *ocad_symbol) { PointSymbol *symbol = importPattern(ocad_symbol->ngrp, (OCADPoint *)ocad_symbol->pts); fillCommonSymbolFields(symbol, (OCADSymbol *)ocad_symbol); symbol->setRotatable(ocad_symbol->base_flags & 1); return symbol; } Symbol *OCAD8FileImport::importLineSymbol(const OCADLineSymbol *ocad_symbol) { LineSymbol* line_for_borders = nullptr; // Import a main line? LineSymbol* main_line = nullptr; if (ocad_symbol->dmode == 0 || ocad_symbol->width > 0) { main_line = new LineSymbol(); line_for_borders = main_line; fillCommonSymbolFields(main_line, (OCADSymbol *)ocad_symbol); // Basic line options main_line->line_width = convertSize(ocad_symbol->width); main_line->color = convertColor(ocad_symbol->color); // Cap and join styles if (ocad_symbol->ends == 0) { main_line->cap_style = LineSymbol::FlatCap; main_line->join_style = LineSymbol::BevelJoin; } else if (ocad_symbol->ends == 1) { main_line->cap_style = LineSymbol::RoundCap; main_line->join_style = LineSymbol::RoundJoin; } else if (ocad_symbol->ends == 2) { main_line->cap_style = LineSymbol::PointedCap; main_line->join_style = LineSymbol::BevelJoin; } else if (ocad_symbol->ends == 3) { main_line->cap_style = LineSymbol::PointedCap; main_line->join_style = LineSymbol::RoundJoin; } else if (ocad_symbol->ends == 4) { main_line->cap_style = LineSymbol::FlatCap; main_line->join_style = LineSymbol::MiterJoin; } else if (ocad_symbol->ends == 6) { main_line->cap_style = LineSymbol::PointedCap; main_line->join_style = LineSymbol::MiterJoin; } if (main_line->cap_style == LineSymbol::PointedCap) { if (ocad_symbol->bdist != ocad_symbol->edist) addWarning(tr("In dashed line symbol %1, pointed cap lengths for begin and end are different (%2 and %3). Using %4.") .arg(0.1 * ocad_symbol->number).arg(ocad_symbol->bdist).arg(ocad_symbol->edist).arg((ocad_symbol->bdist + ocad_symbol->edist) / 2)); main_line->pointed_cap_length = convertSize((ocad_symbol->bdist + ocad_symbol->edist) / 2); // FIXME: Different lengths for start and end length of pointed line ends are not supported yet, so take the average main_line->join_style = LineSymbol::RoundJoin; // NOTE: while the setting may be different (see what is set in the first place), OCAD always draws round joins if the line cap is pointed! } // Handle the dash pattern if( ocad_symbol->gap > 0 || ocad_symbol->gap2 > 0 ) { main_line->dashed = true; // Detect special case if (ocad_symbol->gap2 > 0 && ocad_symbol->gap == 0) { main_line->dash_length = convertSize(ocad_symbol->len - ocad_symbol->gap2); main_line->break_length = convertSize(ocad_symbol->gap2); if (!(ocad_symbol->elen >= ocad_symbol->len / 2 - 1 && ocad_symbol->elen <= ocad_symbol->len / 2 + 1)) addWarning(tr("In dashed line symbol %1, the end length cannot be imported correctly.").arg(0.1 * ocad_symbol->number)); if (ocad_symbol->egap != 0) addWarning(tr("In dashed line symbol %1, the end gap cannot be imported correctly.").arg(0.1 * ocad_symbol->number)); } else { if (ocad_symbol->len != ocad_symbol->elen) { if (ocad_symbol->elen >= ocad_symbol->len / 2 - 1 && ocad_symbol->elen <= ocad_symbol->len / 2 + 1) main_line->half_outer_dashes = true; else addWarning(tr("In dashed line symbol %1, main and end length are different (%2 and %3). Using %4.") .arg(0.1 * ocad_symbol->number).arg(ocad_symbol->len).arg(ocad_symbol->elen).arg(ocad_symbol->len)); } main_line->dash_length = convertSize(ocad_symbol->len); main_line->break_length = convertSize(ocad_symbol->gap); if (ocad_symbol->gap2 > 0) { main_line->dashes_in_group = 2; if (ocad_symbol->gap2 != ocad_symbol->egap) addWarning(tr("In dashed line symbol %1, gaps D and E are different (%2 and %3). Using %4.") .arg(0.1 * ocad_symbol->number).arg(ocad_symbol->gap2).arg(ocad_symbol->egap).arg(ocad_symbol->gap2)); main_line->in_group_break_length = convertSize(ocad_symbol->gap2); main_line->dash_length = (main_line->dash_length - main_line->in_group_break_length) / 2; } } } else { main_line->segment_length = convertSize(ocad_symbol->len); main_line->end_length = convertSize(ocad_symbol->elen); } } // Import a 'framing' line? LineSymbol* framing_line = nullptr; if (ocad_symbol->fwidth > 0) { framing_line = new LineSymbol(); if (!line_for_borders) line_for_borders = framing_line; fillCommonSymbolFields(framing_line, (OCADSymbol *)ocad_symbol); // Basic line options framing_line->line_width = convertSize(ocad_symbol->fwidth); framing_line->color = convertColor(ocad_symbol->fcolor); // Cap and join styles if (ocad_symbol->fstyle == 0) { framing_line->cap_style = LineSymbol::FlatCap; framing_line->join_style = LineSymbol::BevelJoin; } else if (ocad_symbol->fstyle == 1) { framing_line->cap_style = LineSymbol::RoundCap; framing_line->join_style = LineSymbol::RoundJoin; } else if (ocad_symbol->fstyle == 4) { framing_line->cap_style = LineSymbol::FlatCap; framing_line->join_style = LineSymbol::MiterJoin; } } // Import a 'double' line? bool has_border_line = ocad_symbol->lwidth > 0 || ocad_symbol->rwidth > 0; LineSymbol *double_line = nullptr; if (ocad_symbol->dmode != 0 && (ocad_symbol->dflags & 1 || (has_border_line && !line_for_borders))) { double_line = new LineSymbol(); line_for_borders = double_line; fillCommonSymbolFields(double_line, (OCADSymbol *)ocad_symbol); double_line->line_width = convertSize(ocad_symbol->dwidth); if (ocad_symbol->dflags & 1) double_line->color = convertColor(ocad_symbol->dcolor); else double_line->color = nullptr; double_line->cap_style = LineSymbol::FlatCap; double_line->join_style = LineSymbol::MiterJoin; double_line->segment_length = convertSize(ocad_symbol->len); double_line->end_length = convertSize(ocad_symbol->elen); } // Border lines if (has_border_line) { Q_ASSERT(line_for_borders); line_for_borders->have_border_lines = true; LineSymbolBorder& border = line_for_borders->getBorder(); LineSymbolBorder& right_border = line_for_borders->getRightBorder(); // Border color and width border.color = convertColor(ocad_symbol->lcolor); border.width = convertSize(ocad_symbol->lwidth); border.shift = convertSize(ocad_symbol->lwidth) / 2 + (convertSize(ocad_symbol->dwidth) - line_for_borders->line_width) / 2; right_border.color = convertColor(ocad_symbol->rcolor); right_border.width = convertSize(ocad_symbol->rwidth); right_border.shift = convertSize(ocad_symbol->rwidth) / 2 + (convertSize(ocad_symbol->dwidth) - line_for_borders->line_width) / 2; // The borders may be dashed if (ocad_symbol->dgap > 0 && ocad_symbol->dmode > 1) { border.dashed = true; border.dash_length = convertSize(ocad_symbol->dlen); border.break_length = convertSize(ocad_symbol->dgap); // If ocad_symbol->dmode == 2, only the left border should be dashed if (ocad_symbol->dmode > 2) { right_border.dashed = border.dashed; right_border.dash_length = border.dash_length; right_border.break_length = border.break_length; } } } // Create point symbols along line; middle ("normal") dash, corners, start, and end. LineSymbol* symbol_line = main_line ? main_line : double_line; // Find the line to attach the symbols to if (!symbol_line) { main_line = new LineSymbol(); symbol_line = main_line; fillCommonSymbolFields(main_line, (OCADSymbol *)ocad_symbol); main_line->segment_length = convertSize(ocad_symbol->len); main_line->end_length = convertSize(ocad_symbol->elen); } OCADPoint * symbolptr = (OCADPoint *)ocad_symbol->pts; symbol_line->mid_symbol = importPattern( ocad_symbol->smnpts, symbolptr); symbol_line->mid_symbols_per_spot = ocad_symbol->snum; symbol_line->mid_symbol_distance = convertSize(ocad_symbol->sdist); symbolptr += ocad_symbol->smnpts; if( ocad_symbol->ssnpts > 0 ) { //symbol_line->dash_symbol = importPattern( ocad_symbol->ssnpts, symbolptr); symbolptr += ocad_symbol->ssnpts; } if( ocad_symbol->scnpts > 0 ) { symbol_line->dash_symbol = importPattern( ocad_symbol->scnpts, symbolptr); symbol_line->dash_symbol->setName(QCoreApplication::translate("OpenOrienteering::LineSymbolSettings", "Dash symbol")); symbolptr += ocad_symbol->scnpts; } if( ocad_symbol->sbnpts > 0 ) { symbol_line->start_symbol = importPattern( ocad_symbol->sbnpts, symbolptr); symbol_line->start_symbol->setName(QCoreApplication::translate("OpenOrienteering::LineSymbolSettings", "Start symbol")); symbolptr += ocad_symbol->sbnpts; } if( ocad_symbol->senpts > 0 ) { symbol_line->end_symbol = importPattern( ocad_symbol->senpts, symbolptr); } // FIXME: not really sure how this translates... need test cases symbol_line->minimum_mid_symbol_count = 0; //1 + ocad_symbol->smin; symbol_line->minimum_mid_symbol_count_when_closed = 0; //1 + ocad_symbol->smin; symbol_line->show_at_least_one_symbol = false; // NOTE: this works in a different way than OCAD's 'at least X symbols' setting (per-segment instead of per-object) // Suppress dash symbol at line ends if both start symbol and end symbol exist, // but don't create a warning unless a dash symbol is actually defined // and the line symbol is not Mapper's 799 Simple orienteering course. if (symbol_line->start_symbol && symbol_line->end_symbol) { symbol_line->setSuppressDashSymbolAtLineEnds(true); if (symbol_line->dash_symbol && symbol_line->getNumberComponent(0) != 799) addWarning(tr("Line symbol %1: suppressing dash symbol at line ends.").arg(QString::number(0.1 * ocad_symbol->number) + QLatin1Char(' ') + symbol_line->getName())); } // TODO: taper fields (tmode and tlast) if (!main_line && !framing_line) return double_line; else if (!double_line && !framing_line) return main_line; else if (!main_line && !double_line) return framing_line; else { CombinedSymbol* full_line = new CombinedSymbol(); fillCommonSymbolFields(full_line, (OCADSymbol *)ocad_symbol); full_line->setNumParts(3); int part = 0; if (main_line) { full_line->setPart(part++, main_line, true); main_line->setHidden(false); main_line->setProtected(false); } if (double_line) { full_line->setPart(part++, double_line, true); double_line->setHidden(false); double_line->setProtected(false); } if (framing_line) { full_line->setPart(part++, framing_line, true); framing_line->setHidden(false); framing_line->setProtected(false); } full_line->setNumParts(part); return full_line; } } Symbol *OCAD8FileImport::importAreaSymbol(const OCADAreaSymbol *ocad_symbol) { AreaSymbol *symbol = new AreaSymbol(); fillCommonSymbolFields(symbol, (OCADSymbol *)ocad_symbol); // Basic area symbol fields: minimum_area, color symbol->minimum_area = 0; symbol->color = ocad_symbol->fill ? convertColor(ocad_symbol->color) : nullptr; symbol->patterns.clear(); AreaSymbol::FillPattern *pat = nullptr; // Hatching if (ocad_symbol->hmode > 0) { int n = symbol->patterns.size(); symbol->patterns.resize(n + 1); pat = &(symbol->patterns[n]); pat->type = AreaSymbol::FillPattern::LinePattern; pat->angle = convertRotation(ocad_symbol->hangle1); pat->flags = AreaSymbol::FillPattern::Option::Rotatable; pat->line_spacing = convertSize(ocad_symbol->hdist + ocad_symbol->hwidth); pat->line_offset = 0; pat->line_color = convertColor(ocad_symbol->hcolor); pat->line_width = convertSize(ocad_symbol->hwidth); if (ocad_symbol->hmode == 2) { // Second hatch, same as the first, just a different angle symbol->patterns.push_back(*pat); symbol->patterns.back().angle = convertRotation(ocad_symbol->hangle2); } } if (ocad_symbol->pmode > 0) { // OCAD 8 has a "staggered" pattern mode, where successive rows are shifted width/2 relative // to each other. We need to simulate this in Mapper with two overlapping patterns, each with // twice the height. The second is then offset by width/2, height/2. auto spacing = convertSize(ocad_symbol->pheight); if (ocad_symbol->pmode == 2) spacing *= 2; int n = symbol->patterns.size(); symbol->patterns.resize(n + 1); pat = &(symbol->patterns[n]); pat->type = AreaSymbol::FillPattern::PointPattern; pat->angle = convertRotation(ocad_symbol->pangle); pat->flags = AreaSymbol::FillPattern::Option::Rotatable; pat->point_distance = convertSize(ocad_symbol->pwidth); pat->line_spacing = spacing; pat->line_offset = 0; pat->offset_along_line = 0; // FIXME: somebody needs to own this symbol and be responsible for deleting it // Right now it looks like a potential memory leak pat->point = importPattern(ocad_symbol->npts, (OCADPoint *)ocad_symbol->pts); if (ocad_symbol->pmode == 2) { int n = symbol->patterns.size(); symbol->patterns.resize(n + 1); pat = &(symbol->patterns[n]); pat->type = AreaSymbol::FillPattern::PointPattern; pat->angle = convertRotation(ocad_symbol->pangle); pat->flags = AreaSymbol::FillPattern::Option::Rotatable; pat->point_distance = convertSize(ocad_symbol->pwidth); pat->line_spacing = spacing; pat->line_offset = pat->line_spacing / 2; pat->offset_along_line = pat->point_distance / 2; pat->point = importPattern(ocad_symbol->npts, (OCADPoint *)ocad_symbol->pts); } } return symbol; } Symbol *OCAD8FileImport::importTextSymbol(const OCADTextSymbol *ocad_symbol) { TextSymbol *symbol = new TextSymbol(); fillCommonSymbolFields(symbol, (OCADSymbol *)ocad_symbol); symbol->font_family = convertPascalString(ocad_symbol->font); // FIXME: font mapping? symbol->color = convertColor(ocad_symbol->color); double d_font_size = (0.1 * ocad_symbol->dpts) / 72.0 * 25.4; symbol->font_size = qRound(1000 * d_font_size); symbol->bold = (ocad_symbol->bold >= 550) ? true : false; symbol->italic = (ocad_symbol->italic) ? true : false; symbol->underline = false; symbol->paragraph_spacing = convertSize(ocad_symbol->pspace); symbol->character_spacing = ocad_symbol->cspace / 100.0; symbol->kerning = false; symbol->line_below = ocad_symbol->under; symbol->line_below_color = convertColor(ocad_symbol->ucolor); symbol->line_below_width = convertSize(ocad_symbol->uwidth); symbol->line_below_distance = convertSize(ocad_symbol->udist); symbol->custom_tabs.resize(ocad_symbol->ntabs); for (int i = 0; i < ocad_symbol->ntabs; ++i) symbol->custom_tabs[i] = convertSize(ocad_symbol->tab[i]); int halign = (int)TextObject::AlignHCenter; if (ocad_symbol->halign == 0) halign = (int)TextObject::AlignLeft; else if (ocad_symbol->halign == 1) halign = (int)TextObject::AlignHCenter; else if (ocad_symbol->halign == 2) halign = (int)TextObject::AlignRight; else if (ocad_symbol->halign == 3) { // TODO: implement justified alignment addWarning(tr("During import of text symbol %1: ignoring justified alignment").arg(0.1 * ocad_symbol->number)); } text_halign_map[symbol] = halign; if (ocad_symbol->bold != 400 && ocad_symbol->bold != 700) { addWarning(tr("During import of text symbol %1: ignoring custom weight (%2)") .arg(0.1 * ocad_symbol->number).arg(ocad_symbol->bold)); } if (ocad_symbol->cspace != 0) { addWarning(tr("During import of text symbol %1: custom character spacing is set, its implementation does not match OCAD's behavior yet") .arg(0.1 * ocad_symbol->number)); } if (ocad_symbol->wspace != 100) { addWarning(tr("During import of text symbol %1: ignoring custom word spacing (%2%)") .arg(0.1 * ocad_symbol->number).arg(ocad_symbol->wspace)); } if (ocad_symbol->indent1 != 0 || ocad_symbol->indent2 != 0) { addWarning(tr("During import of text symbol %1: ignoring custom indents (%2/%3)") .arg(0.1 * ocad_symbol->number).arg(ocad_symbol->indent1).arg(ocad_symbol->indent2)); } if (ocad_symbol->fmode > 0) { symbol->framing = true; symbol->framing_color = convertColor(ocad_symbol->fcolor); if (ocad_symbol->fmode == 1) { symbol->framing_mode = TextSymbol::ShadowFraming; symbol->framing_shadow_x_offset = convertSize(ocad_symbol->fdx); symbol->framing_shadow_y_offset = -1 * convertSize(ocad_symbol->fdy); } else if (ocad_symbol->fmode == 2) { symbol->framing_mode = TextSymbol::LineFraming; symbol->framing_line_half_width = convertSize(ocad_symbol->fdpts); } else { addWarning(tr("During import of text symbol %1: ignoring text framing (mode %2)") .arg(0.1 * ocad_symbol->number).arg(ocad_symbol->fmode)); } } symbol->updateQFont(); // Convert line spacing double absolute_line_spacing = d_font_size * 0.01 * ocad_symbol->lspace; symbol->line_spacing = absolute_line_spacing / (symbol->getFontMetrics().lineSpacing() / symbol->calculateInternalScaling()); return symbol; } OCAD8FileImport::RectangleInfo* OCAD8FileImport::importRectSymbol(const OCADRectSymbol* ocad_symbol) { RectangleInfo rect; rect.border_line = new LineSymbol(); fillCommonSymbolFields(rect.border_line, (OCADSymbol *)ocad_symbol); rect.border_line->line_width = convertSize(ocad_symbol->width); rect.border_line->color = convertColor(ocad_symbol->color); rect.border_line->cap_style = LineSymbol::FlatCap; rect.border_line->join_style = LineSymbol::RoundJoin; rect.corner_radius = 0.001 * convertSize(ocad_symbol->corner); rect.has_grid = ocad_symbol->flags & 1; if (rect.has_grid) { rect.inner_line = new LineSymbol(); fillCommonSymbolFields(rect.inner_line, (OCADSymbol *)ocad_symbol); rect.inner_line->setNumberComponent(2, 1); rect.inner_line->line_width = qRound(1000 * 0.15); rect.inner_line->color = rect.border_line->color; rect.text = new TextSymbol(); fillCommonSymbolFields(rect.text, (OCADSymbol *)ocad_symbol); rect.text->setNumberComponent(2, 2); rect.text->font_family = QString::fromLatin1("Arial"); rect.text->font_size = qRound(1000 * (15 / 72.0 * 25.4)); rect.text->color = rect.border_line->color; rect.text->bold = true; rect.text->updateQFont(); rect.number_from_bottom = ocad_symbol->flags & 2; rect.cell_width = 0.001 * convertSize(ocad_symbol->cwidth); rect.cell_height = 0.001 * convertSize(ocad_symbol->cheight); rect.unnumbered_cells = ocad_symbol->gcells; rect.unnumbered_text = convertPascalString(ocad_symbol->gtext); } return &rectangle_info.insert(ocad_symbol->number, rect).value(); } PointSymbol *OCAD8FileImport::importPattern(s16 npts, OCADPoint *pts) { PointSymbol *symbol = new PointSymbol(); symbol->rotatable = true; OCADPoint *p = pts, *end = pts + npts; while (p < end) { OCADSymbolElement *elt = (OCADSymbolElement *)p; int element_index = symbol->getNumElements(); bool multiple_elements = p + (2 + elt->npts) < end || p > pts; if (elt->type == OCAD_DOT_ELEMENT) { int inner_radius = (int)convertSize(elt->diameter) / 2; if (inner_radius > 0) { PointSymbol* element_symbol = multiple_elements ? (new PointSymbol()) : symbol; element_symbol->inner_color = convertColor(elt->color); element_symbol->inner_radius = inner_radius; element_symbol->outer_color = nullptr; element_symbol->outer_width = 0; if (multiple_elements) { element_symbol->rotatable = false; PointObject* element_object = new PointObject(element_symbol); element_object->coords.resize(1); symbol->addElement(element_index, element_object, element_symbol); } } } else if (elt->type == OCAD_CIRCLE_ELEMENT) { int inner_radius = (int)convertSize(elt->diameter) / 2 - (int)convertSize(elt->width); int outer_width = (int)convertSize(elt->width); if (outer_width > 0 && inner_radius > 0) { PointSymbol* element_symbol = (multiple_elements) ? (new PointSymbol()) : symbol; element_symbol->inner_color = nullptr; element_symbol->inner_radius = inner_radius; element_symbol->outer_color = convertColor(elt->color); element_symbol->outer_width = outer_width; if (multiple_elements) { element_symbol->rotatable = false; PointObject* element_object = new PointObject(element_symbol); element_object->coords.resize(1); symbol->addElement(element_index, element_object, element_symbol); } } } else if (elt->type == OCAD_LINE_ELEMENT) { LineSymbol* element_symbol = new LineSymbol(); element_symbol->line_width = convertSize(elt->width); element_symbol->color = convertColor(elt->color); PathObject* element_object = new PathObject(element_symbol); fillPathCoords(element_object, false, elt->npts, elt->pts); element_object->recalculateParts(); symbol->addElement(element_index, element_object, element_symbol); } else if (elt->type == OCAD_AREA_ELEMENT) { AreaSymbol* element_symbol = new AreaSymbol(); element_symbol->color = convertColor(elt->color); PathObject* element_object = new PathObject(element_symbol); fillPathCoords(element_object, true, elt->npts, elt->pts); element_object->recalculateParts(); symbol->addElement(element_index, element_object, element_symbol); } p += (2 + elt->npts); } return symbol; } void OCAD8FileImport::fillCommonSymbolFields(Symbol *symbol, const OCADSymbol *ocad_symbol) { // common fields are name, number, description, helper_symbol, hidden/protected status symbol->setName(convertPascalString(ocad_symbol->name)); symbol->setNumberComponent(0, ocad_symbol->number / 10); symbol->setNumberComponent(1, ocad_symbol->number % 10); symbol->setNumberComponent(2, -1); symbol->setIsHelperSymbol(false); // no such thing in OCAD if (ocad_symbol->status & 1) symbol->setProtected(true); if (ocad_symbol->status & 2) symbol->setHidden(true); } Object *OCAD8FileImport::importObject(const OCADObject* ocad_object, MapPart* part) { Symbol* symbol; if (!symbol_index.contains(ocad_object->symbol)) { if (!rectangle_info.contains(ocad_object->symbol)) { if (ocad_object->type == 1) symbol = map->getUndefinedPoint(); else if (ocad_object->type == 2 || ocad_object->type == 3) symbol = map->getUndefinedLine(); else if (ocad_object->type == 4 || ocad_object->type == 5) symbol = map->getUndefinedText(); else { addWarning(tr("Unable to load object")); return nullptr; } } else { if (!importRectangleObject(ocad_object, part, rectangle_info[ocad_object->symbol])) addWarning(tr("Unable to import rectangle object")); return nullptr; } } else symbol = symbol_index[ocad_object->symbol]; if (symbol->getType() == Symbol::Point) { PointObject *p = new PointObject(); p->symbol = symbol; // extra properties: rotation PointSymbol* point_symbol = reinterpret_cast(symbol); if (point_symbol->isRotatable()) p->setRotation(convertRotation(ocad_object->angle)); else if (ocad_object->angle != 0) { if (!point_symbol->isSymmetrical()) { point_symbol->setRotatable(true); p->setRotation(convertRotation(ocad_object->angle)); } } // only 1 coordinate is allowed, enforce it even if the OCAD object claims more. fillPathCoords(p, false, 1, ocad_object->pts); p->setMap(map); return p; } else if (symbol->getType() == Symbol::Text) { TextObject *t = new TextObject(symbol); // extra properties: rotation, horizontalAlignment, verticalAlignment, text t->setRotation(convertRotation(ocad_object->angle)); t->setHorizontalAlignment((TextObject::HorizontalAlignment)text_halign_map.value(symbol)); t->setVerticalAlignment(TextObject::AlignBaseline); const char *text_ptr = (const char *)(ocad_object->pts + ocad_object->npts); std::size_t text_len = sizeof(OCADPoint) * ocad_object->ntext; if (ocad_object->unicode) t->setText(convertWideCString(text_ptr, text_len, true)); else t->setText(convertCString(text_ptr, text_len, true)); // Text objects need special path translation if (!fillTextPathCoords(t, reinterpret_cast(symbol), ocad_object->npts, (OCADPoint *)ocad_object->pts)) { addWarning(tr("Not importing text symbol, couldn't figure out path' (npts=%1): %2") .arg(ocad_object->npts).arg(t->getText())); delete t; return nullptr; } t->setMap(map); return t; } else if (symbol->getType() == Symbol::Line || symbol->getType() == Symbol::Area || symbol->getType() == Symbol::Combined) { PathObject *p = new PathObject(symbol); p->setPatternRotation(convertRotation(ocad_object->angle)); // Normal path fillPathCoords(p, symbol->getType() == Symbol::Area, ocad_object->npts, ocad_object->pts); p->recalculateParts(); p->setMap(map); return p; } return nullptr; } bool OCAD8FileImport::importRectangleObject(const OCADObject* ocad_object, MapPart* part, const OCAD8FileImport::RectangleInfo& rect) { if (ocad_object->npts != 4) return false; // Convert corner points #ifdef Q_CC_CLANG #pragma clang diagnostic push #pragma clang diagnostic ignored "-Warray-bounds" #endif s32 buf[3]; ocad_point(buf, &(ocad_object->pts[3])); MapCoord top_left; convertPoint(top_left, buf[0], buf[1]); ocad_point(buf, &(ocad_object->pts[0])); MapCoord bottom_left; convertPoint(bottom_left, buf[0], buf[1]); ocad_point(buf, &(ocad_object->pts[2])); MapCoord top_right; convertPoint(top_right, buf[0], buf[1]); ocad_point(buf, &(ocad_object->pts[1])); MapCoord bottom_right; convertPoint(bottom_right, buf[0], buf[1]); #ifdef Q_CC_CLANG #pragma clang diagnostic pop #endif MapCoordF top_left_f = MapCoordF(top_left); MapCoordF top_right_f = MapCoordF(top_right); MapCoordF bottom_left_f = MapCoordF(bottom_left); MapCoordF bottom_right_f = MapCoordF(bottom_right); MapCoordF right = MapCoordF(top_right.x() - top_left.x(), top_right.y() - top_left.y()); double angle = right.angle(); MapCoordF down = MapCoordF(bottom_left.x() - top_left.x(), bottom_left.y() - top_left.y()); right.normalize(); down.normalize(); // Create border line MapCoordVector coords; if (rect.corner_radius == 0) { coords.emplace_back(top_left); coords.emplace_back(top_right); coords.emplace_back(bottom_right); coords.emplace_back(bottom_left); } else { double handle_radius = (1 - BEZIER_KAPPA) * rect.corner_radius; coords.emplace_back(top_right_f - right * rect.corner_radius, MapCoord::CurveStart); coords.emplace_back(top_right_f - right * handle_radius); coords.emplace_back(top_right_f + down * handle_radius); coords.emplace_back(top_right_f + down * rect.corner_radius); coords.emplace_back(bottom_right_f - down * rect.corner_radius, MapCoord::CurveStart); coords.emplace_back(bottom_right_f - down * handle_radius); coords.emplace_back(bottom_right_f - right * handle_radius); coords.emplace_back(bottom_right_f - right * rect.corner_radius); coords.emplace_back(bottom_left_f + right * rect.corner_radius, MapCoord::CurveStart); coords.emplace_back(bottom_left_f + right * handle_radius); coords.emplace_back(bottom_left_f - down * handle_radius); coords.emplace_back(bottom_left_f - down * rect.corner_radius); coords.emplace_back(top_left_f + down * rect.corner_radius, MapCoord::CurveStart); coords.emplace_back(top_left_f + down * handle_radius); coords.emplace_back(top_left_f + right * handle_radius); coords.emplace_back(top_left_f + right * rect.corner_radius); } PathObject *border_path = new PathObject(rect.border_line, coords, map); border_path->parts().front().setClosed(true, false); part->objects.push_back(border_path); if (rect.has_grid && rect.cell_width > 0 && rect.cell_height > 0) { // Calculate grid sizes double width = top_left.distanceTo(top_right); double height = top_left.distanceTo(bottom_left); int num_cells_x = qMax(1, qRound(width / rect.cell_width)); int num_cells_y = qMax(1, qRound(height / rect.cell_height)); float cell_width = width / num_cells_x; float cell_height = height / num_cells_y; // Create grid lines coords.resize(2); for (int x = 1; x < num_cells_x; ++x) { coords[0] = MapCoord(top_left_f + x * cell_width * right); coords[1] = MapCoord(bottom_left_f + x * cell_width * right); PathObject *path = new PathObject(rect.inner_line, coords, map); part->objects.push_back(path); } for (int y = 1; y < num_cells_y; ++y) { coords[0] = MapCoord(top_left_f + y * cell_height * down); coords[1] = MapCoord(top_right_f + y * cell_height * down); PathObject *path = new PathObject(rect.inner_line, coords, map); part->objects.push_back(path); } // Create grid text if (height >= rect.cell_height / 2) { for (int y = 0; y < num_cells_y; ++y) { for (int x = 0; x < num_cells_x; ++x) { int cell_num; QString cell_text; if (rect.number_from_bottom) cell_num = y * num_cells_x + x + 1; else cell_num = (num_cells_y - 1 - y) * num_cells_x + x + 1; if (cell_num > num_cells_x * num_cells_y - rect.unnumbered_cells) cell_text = rect.unnumbered_text; else cell_text = QString::number(cell_num); TextObject* object = new TextObject(rect.text); object->setMap(map); object->setText(cell_text); object->setRotation(-angle); object->setHorizontalAlignment(TextObject::AlignLeft); object->setVerticalAlignment(TextObject::AlignTop); double position_x = (x + 0.07f) * cell_width; double position_y = (y + 0.04f) * cell_height + rect.text->getFontMetrics().ascent() / rect.text->calculateInternalScaling() - rect.text->getFontSize(); object->setAnchorPosition(top_left_f + position_x * right + position_y * down); part->objects.push_back(object); //pts[0].Y -= rectinfo.gridText.FontAscent - rectinfo.gridText.FontEmHeight; } } } } return true; } void OCAD8FileImport::importString(OCADStringEntry *entry) { OCADCString *ocad_str = ocad_string(file, entry); if (entry->type == 8) { // Template importTemplate(ocad_str); } // TODO: parse more types of strings, maybe the print parameters? } Template *OCAD8FileImport::importTemplate(OCADCString* ocad_str) { Template* templ = nullptr; QByteArray data(ocad_str->str); // copies the data. QString filename = encoding_1byte->toUnicode(data.left(data.indexOf('\t', 0))); QString clean_path = QDir::cleanPath(QString(filename).replace(QLatin1Char('\\'), QLatin1Char('/'))); QString extension = QFileInfo(clean_path).suffix(); if (extension.compare(QLatin1String("ocd"), Qt::CaseInsensitive) == 0) { templ = new TemplateMap(clean_path, map); } else if (QImageReader::supportedImageFormats().contains(extension.toLatin1())) { templ = new TemplateImage(clean_path, map); } else { addWarning(tr("Unable to import template: background \"%1\" doesn't seem to be a raster image").arg(filename)); return nullptr; } OCADBackground background = importBackground(data); MapCoord c; convertPoint(c, background.trnx, background.trny); templ->setTemplatePosition(c); templ->setTemplateRotation(M_PI / 180 * background.angle); templ->setTemplateScaleX(convertTemplateScale(background.sclx)); templ->setTemplateScaleY(convertTemplateScale(background.scly)); map->templates.insert(map->templates.begin(), templ); if (view) { auto opacity = qMax(0.0, qMin(1.0, 0.01 * (100 - background.dimming))); view->setTemplateVisibility(templ, { float(opacity), bool(background.s) }); } return templ; } // A more flexible reimplementation of libocad's ocad_to_background(). OCADBackground OCAD8FileImport::importBackground(const QByteArray& data) { unsigned int num_angles = 0; OCADBackground background; background.filename = data.data(); // tab-terminated, not 0-terminated! background.trnx = 0; background.trny = 0; background.angle = 0.0; background.sclx = 100.0; background.scly = 100.0; background.dimming = 0; background.s = 1; int i = data.indexOf('\t', 0); while (i >= 0) { double value; bool ok; int next_i = data.indexOf('\t', i+1); int len = (next_i > 0 ? next_i : data.length()) - i - 2; switch (data[i+1]) { case 'x': background.trnx = qRound(data.mid(i + 2, len).toDouble()); break; case 'y': background.trny = qRound(data.mid(i + 2, len).toDouble()); break; case 'a': case 'b': // TODO: use the distinct angles correctly, not just the average background.angle += data.mid(i + 2, len).toDouble(&ok); if (ok) ++num_angles; break; case 'u': value = data.mid(i + 2, len).toDouble(&ok); if (ok && qAbs(value) >= 0.0001) background.sclx = value; break; case 'v': value = data.mid(i + 2, len).toDouble(&ok); if (ok && qAbs(value) >= 0.0001) background.scly = value; break; case 'd': background.dimming = data.mid(i + 2, len).toInt(); break; case 's': background.s = data.mid(i + 2, len).toInt(); break; default: ; // nothing } i = next_i; } if (num_angles) background.angle = background.angle / num_angles; return background; } Template *OCAD8FileImport::importRasterTemplate(const OCADBackground &background) { QString filename(encoding_1byte->toUnicode(background.filename)); filename = QDir::cleanPath(filename.replace(QLatin1Char('\\'), QLatin1Char('/'))); if (isRasterImageFile(filename)) { TemplateImage* templ = new TemplateImage(filename, map); MapCoord c; convertPoint(c, background.trnx, background.trny); templ->setTemplateX(c.nativeX()); templ->setTemplateY(c.nativeY()); templ->setTemplateRotation(M_PI / 180 * background.angle); templ->setTemplateScaleX(convertTemplateScale(background.sclx)); templ->setTemplateScaleY(convertTemplateScale(background.scly)); // FIXME: import template view parameters: background.dimming and background.transparent // TODO: import template visibility return templ; } else { addWarning(tr("Unable to import template: background \"%1\" doesn't seem to be a raster image").arg(filename)); } return nullptr; } void OCAD8FileImport::setPathHolePoint(Object *object, int i) { // Look for curve start points before the current point and apply hole point only if no such point is there. // This prevents hole points in the middle of a curve caused by incorrect map objects. if (i >= 1 && object->coords[i].isCurveStart()) ; //object->coords[i-1].setHolePoint(true); else if (i >= 2 && object->coords[i-1].isCurveStart()) ; //object->coords[i-2].setHolePoint(true); else if (i >= 3 && object->coords[i-2].isCurveStart()) ; //object->coords[i-3].setHolePoint(true); else object->coords[i].setHolePoint(true); } /** Translates the OCAD path given in the last two arguments into an Object. */ void OCAD8FileImport::fillPathCoords(Object *object, bool is_area, u16 npts, const OCADPoint *pts) { object->coords.resize(npts); s32 buf[3]; for (int i = 0; i < npts; i++) { ocad_point(buf, &(pts[i])); MapCoord &coord = object->coords[i]; convertPoint(coord, buf[0], buf[1]); // We can support CurveStart, HolePoint, DashPoint. // CurveStart needs to be applied to the main point though, not the control point, and // hole points need to bet set as the last point of a part of an area object instead of the first point of the next part if (buf[2] & PX_CTL1 && i > 0) object->coords[i-1].setCurveStart(true); if ((buf[2] & (PY_DASH << 8)) || (buf[2] & (PY_CORNER << 8))) coord.setDashPoint(true); if (buf[2] & (PY_HOLE << 8)) setPathHolePoint(object, is_area ? (i - 1) : i); } // For path objects, create closed parts where the position of the last point is equal to that of the first point if (object->getType() == Object::Path) { int start = 0; for (int i = 0; i < (int)object->coords.size(); ++i) { if (!object->coords[i].isHolePoint() && i < (int)object->coords.size() - 1) continue; if (object->coords[i].isPositionEqualTo(object->coords[start])) { MapCoord coord = object->coords[start]; coord.setCurveStart(false); coord.setHolePoint(true); coord.setClosePoint(true); object->coords[i] = coord; } start = i + 1; } } } /** Translates an OCAD text object path into a Mapper text object specifier, if possible. * If successful, sets either 1 or 2 coordinates in the text object and returns true. * If the OCAD path was not importable, leaves the TextObject alone and returns false. */ bool OCAD8FileImport::fillTextPathCoords(TextObject *object, TextSymbol *symbol, u16 npts, OCADPoint *pts) { // text objects either have 1 point (free anchor) or 2 (midpoint/size) // OCAD appears to always have 5 or 4 points (possible single anchor, then 4 corner coordinates going clockwise from anchor). if (npts == 0) return false; if (npts == 4) { // Box text s32 buf[3]; ocad_point(buf, &(pts[3])); MapCoord top_left; convertPoint(top_left, buf[0], buf[1]); ocad_point(buf, &(pts[0])); MapCoord bottom_left; convertPoint(bottom_left, buf[0], buf[1]); ocad_point(buf, &(pts[2])); MapCoord top_right; convertPoint(top_right, buf[0], buf[1]); // According to Purple Pen source code: OCAD adds an extra internal leading (incorrectly). QFontMetricsF metrics = symbol->getFontMetrics(); double top_adjust = -symbol->getFontSize() + (metrics.ascent() + metrics.descent() + 0.5) / symbol->calculateInternalScaling(); MapCoordF adjust_vector = MapCoordF(top_adjust * sin(object->getRotation()), top_adjust * cos(object->getRotation())); top_left = MapCoord(top_left.x() + adjust_vector.x(), top_left.y() + adjust_vector.y()); top_right = MapCoord(top_right.x() + adjust_vector.x(), top_right.y() + adjust_vector.y()); object->setBox((bottom_left.nativeX() + top_right.nativeX()) / 2, (bottom_left.nativeY() + top_right.nativeY()) / 2, top_left.distanceTo(top_right), top_left.distanceTo(bottom_left)); object->setVerticalAlignment(TextObject::AlignTop); } else { // Single anchor text if (npts != 5) addWarning(tr("Trying to import a text object with unknown coordinate format")); s32 buf[3]; ocad_point(buf, &(pts[0])); // anchor point MapCoord coord; convertPoint(coord, buf[0], buf[1]); object->setAnchorPosition(coord.nativeX(), coord.nativeY()); object->setVerticalAlignment(TextObject::AlignBaseline); } return true; } /** Converts a single-byte-per-character, length-payload string to a QString. * * The byte sequence will be: LEN C0 C1 C2 C3... * * Obviously this will only hold up to 255 characters. By default, we interpret the * bytes using Windows-1252, as that's the most likely candidate for OCAD files in * the wild. */ QString OCAD8FileImport::convertPascalString(const char *p) { int len = *((unsigned char *)p); return encoding_1byte->toUnicode((p + 1), len); } /** Converts a single-byte-per-character, zero-terminated string to a QString. * * The byte sequence will be: C0 C1 C2 C3 ... 00. n describes the maximum * length (in bytes) that will be scanned for a zero terminator; if none is found, * the string will be truncated at that location. */ QString OCAD8FileImport::convertCString(const char *p, std::size_t n, bool ignore_first_newline) { size_t i = 0; for (; i < n; i++) { if (p[i] == 0) break; } if (ignore_first_newline && n >= 2 && p[0] == '\r' && p[1] == '\n') { // Remove "\r\n" at the beginning of texts, somehow OCAD seems to add this sometimes but ignores it p += 2; i -= 2; } return encoding_1byte->toUnicode(p, i); } /** Converts a two-byte-per-character, zero-terminated string to a QString. By default, * we interpret the bytes using UTF-16LE, as that's the most likely candidate for * OCAD files in the wild. * * The byte sequence will be: L0 H0 L1 H1 L2 H2... 00 00. n describes the maximum * length (in bytes) that will be scanned for a zero terminator; if none is found, * the string will be truncated at that location. */ QString OCAD8FileImport::convertWideCString(const char *p, std::size_t n, bool ignore_first_newline) { const u16 *q = (const u16 *)p; size_t i = 0; for (; i < n; i++) { if (q[i] == 0) break; } if (ignore_first_newline && n >= 4 && p[0] == '\r' && p[2] == '\n') { // Remove "\r\n" at the beginning of texts, somehow OCAD seems to add this sometimes but ignores it p += 4; i -= 2; } return encoding_2byte->toUnicode(p, i * 2); } float OCAD8FileImport::convertRotation(int angle) { // OCAD uses tenths of a degree, counterclockwise // BUG: if sin(rotation) is < 0 for a hatched area pattern, the pattern's createRenderables() will go into an infinite loop. // So until that's fixed, we keep a between 0 and PI double a = (M_PI / 180) * (0.1f * angle); while (a < 0) a += 2 * M_PI; //if (a < 0 || a > M_PI) qDebug() << "Found angle" << a; return (float)a; } void OCAD8FileImport::convertPoint(MapCoord &coord, s32 ocad_x, s32 ocad_y) { // Recover from broken coordinate export from Mapper 0.6.2 ... 0.6.4 (#749) // Cf. broken::convertPointMember below: // The values -4 ... -1 (-0.004 mm ... -0.001 mm) were converted to 0x80000000u instead of 0. // This is the maximum value. Thus it is okay to assume it won't occur in regular data, // and we can safely replace it with 0 here. // But the input parameter were already subject to right shift in ocad_point ... constexpr auto invalid_value = s32(0x80000000u) >> 8; // ... so we use this value here. if (ocad_x == invalid_value) ocad_x = 0; if (ocad_y == invalid_value) ocad_y = 0; // OCAD uses hundredths of a millimeter. // oo-mapper uses 1/1000 mm coord.setNativeX(offset_x + (qint32)ocad_x * 10); // Y-axis is flipped. coord.setNativeY(offset_y + (qint32)ocad_y * (-10)); } qint32 OCAD8FileImport::convertSize(int ocad_size) { // OCAD uses hundredths of a millimeter. // oo-mapper uses 1/1000 mm return ((qint32)ocad_size) * 10; } const MapColor *OCAD8FileImport::convertColor(int color) { if (!color_index.contains(color)) { addWarning(tr("Color id not found: %1, ignoring this color").arg(color)); return nullptr; } else return color_index[color]; } double OCAD8FileImport::convertTemplateScale(double ocad_scale) { return ocad_scale * 0.01; // millimeters(on map) per pixel } // ### OCAD8FileExport ### OCAD8FileExport::OCAD8FileExport(QIODevice* stream, Map* map, MapView* view) : Exporter(stream, map, view), uses_registration_color(false), file(nullptr) { ocad_init(); encoding_1byte = QTextCodec::codecForName("Windows-1252"); encoding_2byte = QTextCodec::codecForName("UTF-16LE"); origin_point_object = new PointObject(); } OCAD8FileExport::~OCAD8FileExport() { delete origin_point_object; } void OCAD8FileExport::doExport() { uses_registration_color = map->isColorUsedByASymbol(map->getRegistrationColor()); if (map->getNumColors() > (uses_registration_color ? 255 : 256)) throw FileFormatException(tr("The map contains more than 256 colors which is not supported by ocd version 8.")); // Create struct in memory int err = ocad_file_new(&file); if (err != 0) throw FileFormatException(::OpenOrienteering::Exporter::tr("Could not create new file: %1").arg(tr("libocad returned %1").arg(err))); // Check for a neccessary offset (and add related warnings early). auto area_offset = calculateAreaOffset(); // Fill header struct OCADFileHeader* header = file->header; *(((u8*)&header->magic) + 0) = 0xAD; *(((u8*)&header->magic) + 1) = 0x0C; header->ftype = 2; header->major = 8; header->minor = 0; if (map->getMapNotes().size() > 0) { header->infosize = map->getMapNotes().length() + 1; ocad_file_reserve(file, header->infosize); header->infopos = &file->buffer[file->size] - file->buffer; convertCString(map->getMapNotes(), &file->buffer[file->size], header->infosize); file->size += header->infosize; } // Fill setup struct OCADSetup* setup = file->setup; if (view) { setup->center = convertPoint(view->center() - area_offset); setup->zoom = view->getZoom(); } else setup->zoom = 1; // Scale and georeferencing parameters const Georeferencing& georef = map->getGeoreferencing(); setup->scale = georef.getScaleDenominator(); const QPointF offset(georef.toProjectedCoords(area_offset)); setup->offsetx = offset.x(); setup->offsety = offset.y(); setup->angle = georef.getGrivation(); // TODO: print parameters // Colors int ocad_color_index = 0; if (uses_registration_color) { addWarning(tr("Registration black is exported as a regular color.")); ++file->header->ncolors; OCADColor *ocad_color = ocad_color_at(file, ocad_color_index); ocad_color->number = ocad_color_index; const MapColor* color = Map::getRegistrationColor(); const MapColorCmyk& cmyk = color->getCmyk(); ocad_color->cyan = qRound(1 / 0.005f * cmyk.c); ocad_color->magenta = qRound(1 / 0.005f * cmyk.m); ocad_color->yellow = qRound(1 / 0.005f * cmyk.y); ocad_color->black = qRound(1 / 0.005f * cmyk.k); convertPascalString(QString::fromLatin1("Registration black"), ocad_color->name, 32); // not translated ++ocad_color_index; } for (int i = 0; i < map->getNumColors(); i++) { ++file->header->ncolors; OCADColor *ocad_color = ocad_color_at(file, ocad_color_index); ocad_color->number = ocad_color_index; const MapColor* color = map->getColor(i); const MapColorCmyk& cmyk = color->getCmyk(); ocad_color->cyan = qRound(1 / 0.005f * cmyk.c); ocad_color->magenta = qRound(1 / 0.005f * cmyk.m); ocad_color->yellow = qRound(1 / 0.005f * cmyk.y); ocad_color->black = qRound(1 / 0.005f * cmyk.k); convertPascalString(color->getName(), ocad_color->name, 32); ++ocad_color_index; } // Symbols for (int i = 0; i < map->getNumSymbols(); ++i) { const Symbol* symbol = map->getSymbol(i); s16 index = -1; if (symbol->getType() == Symbol::Point) index = exportPointSymbol(symbol->asPoint()); else if (symbol->getType() == Symbol::Line) index = exportLineSymbol(symbol->asLine()); else if (symbol->getType() == Symbol::Area) index = exportAreaSymbol(symbol->asArea()); else if (symbol->getType() == Symbol::Text) index = exportTextSymbol(symbol->asText()); else if (symbol->getType() == Symbol::Combined) ; // This is done as a second pass to ensure that all dependencies are added to the symbol_index else Q_ASSERT(false); if (index >= 0) { std::set number; number.insert(index); symbol_index.insert(symbol, number); } } // Separate pass for combined symbols for (int i = 0; i < map->getNumSymbols(); ++i) { const Symbol* symbol = map->getSymbol(i); if (symbol->getType() == Symbol::Combined) symbol_index.insert(symbol, exportCombinedSymbol(static_cast(symbol))); } // Objects OCADObject* ocad_object = ocad_object_alloc(nullptr); for (int l = 0; l < map->getNumParts(); ++l) { for (int o = 0; o < map->getPart(l)->getNumObjects(); ++o) { memset(ocad_object, 0, sizeof(OCADObject) - sizeof(OCADPoint) + 8 * (ocad_object->npts + ocad_object->ntext)); Object* object = map->getPart(l)->getObject(o); std::unique_ptr duplicate; if (area_offset.nativeX() != 0 || area_offset.nativeY() != 0) { // Create a safely managed duplicate and move it as needed. duplicate.reset(object->duplicate()); duplicate->move(-area_offset); object = duplicate.get(); } object->update(); // Fill some common entries of object struct OCADPoint* coord_buffer = ocad_object->pts; if (object->getType() != Object::Text) ocad_object->npts = exportCoordinates(object->getRawCoordinateVector(), &coord_buffer, object->getSymbol()); else ocad_object->npts = exportTextCoordinates(static_cast(object), &coord_buffer); if (object->getType() == Object::Point) { PointObject* point = static_cast(object); ocad_object->angle = convertRotation(point->getRotation()); } else if (object->getType() == Object::Path) { if (object->getSymbol()->getType() == Symbol::Area) { PathObject* path = static_cast(object); // Known issue: In OCD format, pattern rotatability is all // or nothing. In Mapper, it is an option per pattern. if (path->getSymbol()->asArea()->hasRotatableFillPattern()) ocad_object->angle = convertRotation(path->getPatternRotation()); if (path->getPatternOrigin() != MapCoord(0, 0)) addWarning(tr("Unable to export fill pattern shift for an area object")); } } else if (object->getType() == Object::Text) { TextObject* text = static_cast(object); ocad_object->unicode = 1; ocad_object->angle = convertRotation(text->getRotation()); int num_letters = convertWideCString(text->getText(), (unsigned char*)coord_buffer, 8 * (OCAD_MAX_OBJECT_PTS - ocad_object->npts)); ocad_object->ntext = qCeil(num_letters / 4.0f); } // Insert an object into the map for every symbol contained in the symbol_index std::set index_set; if (symbol_index.contains(object->getSymbol())) index_set = symbol_index[object->getSymbol()]; else index_set.insert(-1); // export as undefined symbol for (const auto index : index_set) { s16 index_to_use = index; // For text objects, check if we have to change / create a new text symbol because of the formatting if (object->getType() == Object::Text && symbol_index.contains(object->getSymbol())) { TextObject* text_object = static_cast(object); const TextSymbol* text_symbol = static_cast(object->getSymbol()); if (!text_format_map.contains(text_symbol)) { // Adjust the formatting in the first created symbol to this object OCADTextSymbol* ocad_text_symbol = (OCADTextSymbol*)ocad_symbol(file, index); setTextSymbolFormatting(ocad_text_symbol, text_object); TextFormatList new_list; new_list.push_back(std::make_pair(text_object->getHorizontalAlignment(), index)); text_format_map.insert(text_symbol, new_list); } else { // Check if this formatting has already been created as symbol. // If yes, use this symbol, else create a new symbol TextFormatList& format_list = text_format_map[text_symbol]; bool found = false; for (size_t i = 0, end = format_list.size(); i < end; ++i) { if (format_list[i].first == text_object->getHorizontalAlignment()) { index_to_use = format_list[i].second; found = true; break; } } if (!found) { // Copy the symbol and adjust the formatting // TODO: insert these symbols directly after the original symbols OCADTextSymbol* ocad_text_symbol = (OCADTextSymbol*)ocad_symbol(file, index); OCADTextSymbol* new_symbol = (OCADTextSymbol*)ocad_symbol_new(file, ocad_text_symbol->size); // Get the pointer to the first symbol again as it might have changed during ocad_symbol_new() ocad_text_symbol = (OCADTextSymbol*)ocad_symbol(file, index); memcpy(new_symbol, ocad_text_symbol, ocad_text_symbol->size); setTextSymbolFormatting(new_symbol, text_object); // Give the new symbol a unique number while (symbol_numbers.find(new_symbol->number) != symbol_numbers.end()) ++new_symbol->number; symbol_numbers.insert(new_symbol->number); index_to_use = new_symbol->number; // Store packed new_symbol->number in separate variable first, // otherwise when compiling for Android this causes the error: // cannot bind packed field 'new_symbol->_OCADTextSymbol::number' to 'short int&' s16 new_symbol_number = new_symbol->number; format_list.push_back(std::make_pair(text_object->getHorizontalAlignment(), new_symbol_number)); } } } ocad_object->symbol = index_to_use; if (object->getType() == Object::Point) ocad_object->type = 1; else if (object->getType() == Object::Path) { OCADSymbol* ocad_sym = ocad_symbol(file, index_to_use); if (!ocad_sym) ocad_object->type = 2; // This case is for undefined lines; TODO: make another case for undefined areas, as soon as they are implemented else if (ocad_sym->type == 2) ocad_object->type = 2; // Line else //if (ocad_symbol->type == 3) ocad_object->type = 3; // Area } else if (object->getType() == Object::Text) { TextObject* text_object = static_cast(object); if (text_object->hasSingleAnchor()) ocad_object->type = 4; else ocad_object->type = 5; } OCADObjectEntry* entry; ocad_object_add(file, ocad_object, &entry); // This is done internally by libocad (in a slightly more imprecise way using the extent specified in the symbol) //entry->rect.min = convertPoint(MapCoord(object->getExtent().topLeft())); //entry->rect.max = convertPoint(MapCoord(object->getExtent().bottomRight())); entry->npts = ocad_object->npts + ocad_object->ntext; //entry->symbol = index_to_use; } } } // Templates for (int i = map->getNumTemplates() - 1; i >= 0; --i) { const Template* temp = map->getTemplate(i); QString template_path = temp->getTemplatePath(); if (qstrcmp(temp->getTemplateType(), "TemplateImage") == 0 || QFileInfo(template_path).suffix().compare(QLatin1String("ocd"), Qt::CaseInsensitive) == 0) { // FIXME: export template view parameters double a = temp->getTemplateRotation() * 180 / M_PI; int d = 0; int o = 0; int p = 0; int s = 1; // enabled int t = 0; OCADPoint pos = convertPoint(temp->getTemplateX()-area_offset.nativeX(), temp->getTemplateY()-area_offset.nativeY()); int x = pos.x >> 8; int y = pos.y >> 8; double u = convertTemplateScale(temp->getTemplateScaleX()); double v = convertTemplateScale(temp->getTemplateScaleY()); template_path.replace(QLatin1Char('/'), QLatin1Char('\\')); QString string; auto template_path_8bit = encoding_1byte->fromUnicode(template_path); string.sprintf("%s\ts%d\tx%d\ty%d\ta%f\tu%f\tv%f\td%d\tp%d\tt%d\to%d", template_path_8bit.data(), s, x, y, a, u, v, d, p, t, o ); OCADStringEntry* entry = ocad_string_entry_new(file, string.length() + 1); entry->type = 8; convertCString(string, file->buffer + entry->ptr, entry->size); } else { addWarning(tr("Unable to export template: file type of \"%1\" is not supported yet").arg(temp->getTemplateFilename())); } } stream->write((char*)file->buffer, file->size); ocad_file_close(file); } MapCoord OCAD8FileExport::calculateAreaOffset() { auto area_offset = QPointF{}; // Attention: When changing ocd_bounds, update the warning messages, too. auto ocd_bounds = QRectF{QPointF{-2000, -2000}, QPointF{2000, 2000}}; auto objects_extent = map->calculateExtent(); if (!ocd_bounds.contains(objects_extent)) { if (objects_extent.width() < ocd_bounds.width() && objects_extent.height() < ocd_bounds.height()) { // The extent fits into the limited area. addWarning(tr("Coordinates are adjusted to fit into the OCAD 8 drawing area (-2 m ... 2 m).")); area_offset = objects_extent.center(); } else { // The extent is too wide to fit. // Only move the objects if they are completely outside the drawing area. // This avoids repeated moves on open/save/close cycles. if (!objects_extent.intersects(ocd_bounds)) { addWarning(tr("Coordinates are adjusted to fit into the OCAD 8 drawing area (-2 m ... 2 m).")); std::size_t count = 0; auto calculate_average_center = [&area_offset, &count](Object* object) { area_offset *= qreal(count)/qreal(count+1); ++count; area_offset += object->getExtent().center() / count; }; map->applyOnAllObjects(calculate_average_center); } addWarning(tr("Some coordinates remain outside of the OCAD 8 drawing area." " They might be unreachable in OCAD.")); } if (area_offset.manhattanLength() > 0) { // Round offset to 100 m in projected coordinates, to avoid crude grid offset. constexpr auto unit = 100; auto projected_offset = map->getGeoreferencing().toProjectedCoords(MapCoordF(area_offset)); projected_offset.rx() = qreal(qRound(projected_offset.x()/unit)) * unit; projected_offset.ry() = qreal(qRound(projected_offset.y()/unit)) * unit; area_offset = map->getGeoreferencing().toMapCoordF(projected_offset); } } return MapCoord{area_offset}; } void OCAD8FileExport::exportCommonSymbolFields(const Symbol* symbol, OCADSymbol* ocad_symbol, int size) { ocad_symbol->size = (s16)size; convertPascalString(symbol->getPlainTextName(), ocad_symbol->name, 32); ocad_symbol->number = symbol->getNumberComponent(0) * 10; if (symbol->getNumberComponent(1) >= 0) ocad_symbol->number += (symbol->getNumberComponent(1) % 10); // Symbol number 0.0 is not valid if (ocad_symbol->number == 0) ocad_symbol->number = 1; // Ensure uniqueness of the symbol number while (symbol_numbers.find(ocad_symbol->number) != symbol_numbers.end()) ++ocad_symbol->number; symbol_numbers.insert(ocad_symbol->number); if (symbol->isProtected()) ocad_symbol->status |= 1; if (symbol->isHidden()) ocad_symbol->status |= 2; // Set of used colors u8 bitmask = 1; u8* bitpos = ocad_symbol->colors; for (int c = 0; c < map->getNumColors(); ++c) { if (symbol->containsColor(map->getColor(c))) *bitpos |= bitmask; bitmask = bitmask << 1; if (bitmask == 0) { bitmask = 1; ++bitpos; } } exportSymbolIcon(symbol, ocad_symbol->icon); } void OCAD8FileExport::exportSymbolIcon(const Symbol* symbol, u8 ocad_icon[]) { // Icon: 22x22 with 4 bit color code, origin at bottom left constexpr int icon_size = 22; QImage image = symbol->createIcon(*map, icon_size, false) .convertToFormat(QImage::Format_ARGB32_Premultiplied); auto process_pixel = [&image](int x, int y)->int { // Apply premultiplied pixel on white background auto premultiplied = image.pixel(x, y); auto alpha = qAlpha(premultiplied); auto r = 255 - alpha + qRed(premultiplied); auto g = 255 - alpha + qGreen(premultiplied); auto b = 255 - alpha + qBlue(premultiplied); auto pixel = qRgb(r, g, b); // Ordered dithering 2x2 threshold matrix, adjusted for o-map halftones static int threshold[4] = { 24, 192, 136, 80 }; auto palette_color = getOcadColor(pixel); switch (palette_color) { case 0: // Black to gray (50%) return qGray(pixel) < 128-threshold[(x%2 + 2*(y%2))]/2 ? 0 : 7; case 7: // Gray (50%) to light gray return qGray(pixel) < 192-threshold[(x%2 + 2*(y%2))]/4 ? 7 : 8; case 8: // Light gray to white return qGray(pixel) < 256-threshold[(x%2 + 2*(y%2))]/4 ? 8 : 15; case 15: // Pure white return palette_color; default: // Color to white return QColor(pixel).saturation() >= threshold[(x%2 + 2*(y%2))] ? palette_color : 15; } }; for (int y = icon_size - 1; y >= 0; --y) { for (int x = 0; x < icon_size; x += 2) { auto first = process_pixel(x, y); auto second = process_pixel(x+1, y); *(ocad_icon++) = u8((first << 4) + second); } ocad_icon++; } } int OCAD8FileExport::getPatternSize(const PointSymbol* point) { if (!point) return 0; int npts = 0; for (int i = 0; i < point->getNumElements(); ++i) { int factor = 1; if (point->getElementSymbol(i)->getType() == Symbol::Point) { factor = 0; const PointSymbol* point_symbol = static_cast(point->getElementSymbol(i)); if (point_symbol->getInnerRadius() > 0 && point_symbol->getInnerColor()) ++factor; if (point_symbol->getOuterWidth() > 0 && point_symbol->getOuterColor()) ++factor; } npts += factor * (2 + point->getElementObject(i)->getRawCoordinateVector().size()); } if (point->getInnerRadius() > 0 && point->getInnerColor()) npts += 2 + 1; if (point->getOuterWidth() > 0 && point->getOuterColor()) npts += 2 + 1; return npts * sizeof(OCADPoint); } s16 OCAD8FileExport::exportPattern(const PointSymbol* point, OCADPoint** buffer) { if (!point) return 0; s16 num_coords = exportSubPattern(origin_point_object, point, buffer); for (int i = 0; i < point->getNumElements(); ++i) { num_coords += exportSubPattern(point->getElementObject(i), point->getElementSymbol(i), buffer); } return num_coords; } s16 OCAD8FileExport::exportSubPattern(const Object* object, const Symbol* symbol, OCADPoint** buffer) { s16 num_coords = 0; OCADSymbolElement* element = (OCADSymbolElement*)*buffer; if (symbol->getType() == Symbol::Point) { const PointSymbol* point_symbol = static_cast(symbol); if (point_symbol->getInnerRadius() > 0 && point_symbol->getInnerColor()) { element->type = 4; element->color = convertColor(point_symbol->getInnerColor()); element->diameter = convertSize(2 * point_symbol->getInnerRadius()); (*buffer) += 2; element->npts = exportCoordinates(object->getRawCoordinateVector(), buffer, point_symbol); num_coords += 2 + element->npts; } if (point_symbol->getOuterWidth() > 0 && point_symbol->getOuterColor()) { element = (OCADSymbolElement*)*buffer; element->type = 3; element->color = convertColor(point_symbol->getOuterColor()); element->width = convertSize(point_symbol->getOuterWidth()); element->diameter = convertSize(2 * point_symbol->getInnerRadius() + 2 * point_symbol->getOuterWidth()); (*buffer) += 2; element->npts = exportCoordinates(object->getRawCoordinateVector(), buffer, point_symbol); num_coords += 2 + element->npts; } } else if (symbol->getType() == Symbol::Line) { const LineSymbol* line_symbol = static_cast(symbol); element->type = 1; if (line_symbol->getCapStyle() == LineSymbol::RoundCap) element->flags |= 1; else if (line_symbol->getJoinStyle() == LineSymbol::MiterJoin) element->flags |= 4; element->color = convertColor(line_symbol->getColor()); element->width = convertSize(line_symbol->getLineWidth()); (*buffer) += 2; element->npts = exportCoordinates(object->getRawCoordinateVector(), buffer, line_symbol); num_coords += 2 + element->npts; } else if (symbol->getType() == Symbol::Area) { const AreaSymbol* area_symbol = static_cast(symbol); element->type = 2; element->color = convertColor(area_symbol->getColor()); (*buffer) += 2; element->npts = exportCoordinates(object->getRawCoordinateVector(), buffer, area_symbol); num_coords += 2 + element->npts; } else Q_ASSERT(false); return num_coords; } s16 OCAD8FileExport::exportPointSymbol(const PointSymbol* point) { int data_size = (sizeof(OCADPointSymbol) - sizeof(OCADPoint)) + getPatternSize(point); OCADPointSymbol* ocad_symbol = (OCADPointSymbol*)ocad_symbol_new(file, data_size); exportCommonSymbolFields(point, (OCADSymbol*)ocad_symbol, data_size); ocad_symbol->type = OCAD_POINT_SYMBOL; ocad_symbol->extent = getPointSymbolExtent(point); if (ocad_symbol->extent <= 0) ocad_symbol->extent = 100; if (point->isRotatable()) ocad_symbol->base_flags |= 1; ocad_symbol->ngrp = (data_size - (sizeof(OCADPointSymbol) - sizeof(OCADPoint))) / 8; OCADPoint* pattern_buffer = ocad_symbol->pts; exportPattern(point, &pattern_buffer); Q_ASSERT((u8*)ocad_symbol + data_size == (u8*)pattern_buffer); return ocad_symbol->number; } s16 OCAD8FileExport::exportLineSymbol(const LineSymbol* line) { int data_size = (sizeof(OCADLineSymbol) - sizeof(OCADPoint)) + getPatternSize(line->getStartSymbol()) + getPatternSize(line->getEndSymbol()) + getPatternSize(line->getMidSymbol()) + getPatternSize(line->getDashSymbol()); OCADLineSymbol* ocad_symbol = (OCADLineSymbol*)ocad_symbol_new(file, data_size); exportCommonSymbolFields(line, (OCADSymbol*)ocad_symbol, data_size); // Basic settings ocad_symbol->type = OCAD_LINE_SYMBOL; s16 extent = convertSize(0.5f * line->getLineWidth()); if (line->hasBorder()) extent = qMax(extent, (s16)convertSize(0.5f * line->getLineWidth() + line->getBorder().shift + 0.5f * line->getBorder().width)); extent = qMax(extent, getPointSymbolExtent(line->getStartSymbol())); extent = qMax(extent, getPointSymbolExtent(line->getEndSymbol())); extent = qMax(extent, getPointSymbolExtent(line->getMidSymbol())); extent = qMax(extent, getPointSymbolExtent(line->getDashSymbol())); ocad_symbol->extent = extent; ocad_symbol->color = convertColor(line->getColor()); if (line->getColor()) ocad_symbol->width = convertSize(line->getLineWidth()); // Cap and Join if (line->getCapStyle() == LineSymbol::FlatCap && line->getJoinStyle() == LineSymbol::BevelJoin) ocad_symbol->ends = 0; else if (line->getCapStyle() == LineSymbol::RoundCap && line->getJoinStyle() == LineSymbol::RoundJoin) ocad_symbol->ends = 1; else if (line->getCapStyle() == LineSymbol::PointedCap && line->getJoinStyle() == LineSymbol::BevelJoin) ocad_symbol->ends = 2; else if (line->getCapStyle() == LineSymbol::PointedCap && line->getJoinStyle() == LineSymbol::RoundJoin) ocad_symbol->ends = 3; else if (line->getCapStyle() == LineSymbol::FlatCap && line->getJoinStyle() == LineSymbol::MiterJoin) ocad_symbol->ends = 4; else if (line->getCapStyle() == LineSymbol::PointedCap && line->getJoinStyle() == LineSymbol::MiterJoin) ocad_symbol->ends = 6; else { addWarning(tr("In line symbol \"%1\", cannot represent cap/join combination.").arg(line->getPlainTextName())); // Decide based on the caps if (line->getCapStyle() == LineSymbol::FlatCap) ocad_symbol->ends = 0; else if (line->getCapStyle() == LineSymbol::RoundCap) ocad_symbol->ends = 1; else if (line->getCapStyle() == LineSymbol::PointedCap) ocad_symbol->ends = 3; else if (line->getCapStyle() == LineSymbol::SquareCap) ocad_symbol->ends = 0; } if (line->getCapStyle() == LineSymbol::PointedCap) { ocad_symbol->bdist = convertSize(line->getPointedCapLength()); ocad_symbol->edist = convertSize(line->getPointedCapLength()); } // Dash pattern if (line->isDashed()) { if (line->getMidSymbol() && !line->getMidSymbol()->isEmpty()) { if (line->getDashesInGroup() > 1) addWarning(tr("In line symbol \"%1\", neglecting the dash grouping.").arg(line->getPlainTextName())); ocad_symbol->len = convertSize(line->getDashLength() + line->getBreakLength()); ocad_symbol->elen = ocad_symbol->len / 2; ocad_symbol->gap2 = convertSize(line->getBreakLength()); } else { if (line->getDashesInGroup() > 1) { if (line->getDashesInGroup() > 2) addWarning(tr("In line symbol \"%1\", the number of dashes in a group has been reduced to 2.").arg(line->getPlainTextName())); ocad_symbol->len = convertSize(2 * line->getDashLength() + line->getInGroupBreakLength()); ocad_symbol->elen = convertSize(2 * line->getDashLength() + line->getInGroupBreakLength()); ocad_symbol->gap = convertSize(line->getBreakLength()); ocad_symbol->gap2 = convertSize(line->getInGroupBreakLength()); ocad_symbol->egap = ocad_symbol->gap2; } else { ocad_symbol->len = convertSize(line->getDashLength()); ocad_symbol->elen = ocad_symbol->len / (line->getHalfOuterDashes() ? 2 : 1); ocad_symbol->gap = convertSize(line->getBreakLength()); } } } else { ocad_symbol->len = convertSize(line->getSegmentLength()); ocad_symbol->elen = convertSize(line->getEndLength()); } ocad_symbol->smin = line->getShowAtLeastOneSymbol() ? 0 : -1; // Double line if (line->hasBorder() && (line->getBorder().isVisible() || line->getRightBorder().isVisible())) { ocad_symbol->dwidth = convertSize(line->getLineWidth() - line->getBorder().width + 2 * line->getBorder().shift); if (line->getBorder().dashed && !line->getRightBorder().dashed) ocad_symbol->dmode = 2; else ocad_symbol->dmode = line->getBorder().dashed ? 3 : 1; // ocad_symbol->dflags ocad_symbol->lwidth = convertSize(line->getBorder().width); ocad_symbol->rwidth = convertSize(line->getRightBorder().width); ocad_symbol->lcolor = convertColor(line->getBorder().color); ocad_symbol->rcolor = convertColor(line->getRightBorder().color); if (line->getBorder().dashed) { ocad_symbol->dlen = convertSize(line->getBorder().dash_length); ocad_symbol->dgap = convertSize(line->getBorder().break_length); } else if (line->getRightBorder().dashed) { ocad_symbol->dlen = convertSize(line->getRightBorder().dash_length); ocad_symbol->dgap = convertSize(line->getRightBorder().break_length); } if (((line->getBorder().dashed && line->getRightBorder().dashed) && (line->getBorder().dash_length != line->getRightBorder().dash_length || line->getBorder().break_length != line->getRightBorder().break_length)) || (!line->getBorder().dashed && line->getRightBorder().dashed)) { addWarning(tr("In line symbol \"%1\", cannot export the borders correctly.").arg(line->getPlainTextName())); } } // Mid symbol OCADPoint* pattern_buffer = ocad_symbol->pts; ocad_symbol->smnpts = exportPattern(line->getMidSymbol(), &pattern_buffer); ocad_symbol->snum = line->getMidSymbolsPerSpot(); ocad_symbol->sdist = convertSize(line->getMidSymbolDistance()); // No secondary symbol ocad_symbol->ssnpts = 0; // Export dash symbol as corner symbol ocad_symbol->scnpts = exportPattern(line->getDashSymbol(), &pattern_buffer); // Start symbol ocad_symbol->sbnpts = exportPattern(line->getStartSymbol(), &pattern_buffer); // End symbol ocad_symbol->senpts = exportPattern(line->getEndSymbol(), &pattern_buffer); Q_ASSERT((u8*)ocad_symbol + data_size == (u8*)pattern_buffer); return ocad_symbol->number; } s16 OCAD8FileExport::exportAreaSymbol(const AreaSymbol* area) { int data_size = (sizeof(OCADAreaSymbol) - sizeof(OCADPoint)); for (int i = 0, end = area->getNumFillPatterns(); i < end; ++i) { if (area->getFillPattern(i).type == AreaSymbol::FillPattern::PointPattern) { data_size += getPatternSize(area->getFillPattern(i).point); break; } } OCADAreaSymbol* ocad_symbol = (OCADAreaSymbol*)ocad_symbol_new(file, data_size); exportCommonSymbolFields(area, (OCADSymbol*)ocad_symbol, data_size); // Basic settings ocad_symbol->type = OCAD_AREA_SYMBOL; ocad_symbol->extent = 0; if (area->getColor()) { ocad_symbol->fill = 1; ocad_symbol->color = convertColor(area->getColor()); } // Hatch ocad_symbol->hmode = 0; for (int i = 0, end = area->getNumFillPatterns(); i < end; ++i) { const AreaSymbol::FillPattern& pattern = area->getFillPattern(i); if (pattern.type == AreaSymbol::FillPattern::LinePattern) { if ( (ocad_symbol->hmode == 1 && ocad_symbol->hcolor != convertColor(pattern.line_color)) || ocad_symbol->hmode == 2 ) { addWarning(tr("In area symbol \"%1\", skipping a fill pattern.").arg(area->getPlainTextName())); continue; } if (pattern.rotatable()) ocad_symbol->base_flags |= 1; ++ocad_symbol->hmode; if (ocad_symbol->hmode == 1) { ocad_symbol->hcolor = convertColor(pattern.line_color); ocad_symbol->hwidth = convertSize(pattern.line_width); ocad_symbol->hdist = convertSize(pattern.line_spacing - pattern.line_width); ocad_symbol->hangle1 = convertRotation(pattern.angle); } else if (ocad_symbol->hmode == 2) { ocad_symbol->hwidth = (ocad_symbol->hwidth + convertSize(pattern.line_width)) / 2; ocad_symbol->hdist = (ocad_symbol->hdist + convertSize(pattern.line_spacing - pattern.line_width)) / 2; ocad_symbol->hangle2 = convertRotation(pattern.angle); } } } // Struct PointSymbol* point_pattern = nullptr; for (int i = 0, end = area->getNumFillPatterns(); i < end; ++i) { const AreaSymbol::FillPattern& pattern = area->getFillPattern(i); if (pattern.type == AreaSymbol::FillPattern::PointPattern) { if (pattern.rotatable()) ocad_symbol->base_flags |= 1; ++ocad_symbol->pmode; if (ocad_symbol->pmode == 1) { ocad_symbol->pwidth = convertSize(pattern.point_distance); ocad_symbol->pheight = convertSize(pattern.line_spacing); ocad_symbol->pangle = convertRotation(pattern.angle); point_pattern = pattern.point; } else if (ocad_symbol->pmode == 2) { // NOTE: This is only a heuristic which works for the orienteering symbol sets, not a real conversion, which would be impossible in most cases. // There are no further checks done to find out if the conversion is applicable because with these checks, already a tiny (not noticeable) error // in the symbol definition would make it take the wrong choice. addWarning(tr("In area symbol \"%1\", assuming a \"shifted rows\" point pattern. This might be correct as well as incorrect.").arg(area->getPlainTextName())); if (pattern.line_offset != 0) ocad_symbol->pheight /= 2; else ocad_symbol->pwidth /= 2; break; } } } if (point_pattern) { OCADPoint* pattern_buffer = ocad_symbol->pts; ocad_symbol->npts = exportPattern(point_pattern, &pattern_buffer); Q_ASSERT((u8*)ocad_symbol + data_size == (u8*)pattern_buffer); } return ocad_symbol->number; } s16 OCAD8FileExport::exportTextSymbol(const TextSymbol* text) { int data_size = sizeof(OCADTextSymbol); OCADTextSymbol* ocad_symbol = (OCADTextSymbol*)ocad_symbol_new(file, data_size); exportCommonSymbolFields(text, (OCADSymbol*)ocad_symbol, data_size); ocad_symbol->type = OCAD_TEXT_SYMBOL; ocad_symbol->subtype = 1; ocad_symbol->extent = 0; convertPascalString(text->getFontFamily(), ocad_symbol->font, 32); ocad_symbol->color = convertColor(text->getColor()); ocad_symbol->dpts = qRound(10 * text->getFontSize() / 25.4 * 72.0); ocad_symbol->bold = text->isBold() ? 700 : 400; ocad_symbol->italic = text->isItalic() ? 1 : 0; //ocad_symbol->charset ocad_symbol->cspace = convertSize(1000 * text->getCharacterSpacing()); if (ocad_symbol->cspace != 0) addWarning(tr("In text symbol %1: custom character spacing is set, its implementation does not match OCAD's behavior yet").arg(text->getPlainTextName())); ocad_symbol->wspace = 100; ocad_symbol->halign = 0; // Default value, we might have to change this or even create copies of this symbol with other alignments later double absolute_line_spacing = text->getLineSpacing() * (text->getFontMetrics().lineSpacing() / text->calculateInternalScaling()); ocad_symbol->lspace = qRound(absolute_line_spacing / (text->getFontSize() * 0.01)); ocad_symbol->pspace = convertSize(1000 * text->getParagraphSpacing()); if (text->isUnderlined()) addWarning(tr("In text symbol %1: ignoring underlining").arg(text->getPlainTextName())); if (text->usesKerning()) addWarning(tr("In text symbol %1: ignoring kerning").arg(text->getPlainTextName())); ocad_symbol->under = text->hasLineBelow() ? 1 : 0; ocad_symbol->ucolor = convertColor(text->getLineBelowColor()); ocad_symbol->uwidth = convertSize(1000 * text->getLineBelowWidth()); ocad_symbol->udist = convertSize(1000 * text->getLineBelowDistance()); ocad_symbol->ntabs = text->getNumCustomTabs(); for (int i = 0; i < qMin((s16)32, ocad_symbol->ntabs); ++i) ocad_symbol->tab[i] = convertSize(text->getCustomTab(i)); if (text->getFramingMode() != TextSymbol::NoFraming && text->getFramingColor()) { ocad_symbol->fcolor = convertColor(text->getFramingColor()); if (text->getFramingMode() == TextSymbol::ShadowFraming) { ocad_symbol->fmode = 1; ocad_symbol->fdx = convertSize(text->getFramingShadowXOffset()); ocad_symbol->fdy = -1 * convertSize(text->getFramingShadowYOffset()); } else if (text->getFramingMode() == TextSymbol::LineFraming) { ocad_symbol->fmode = 2; ocad_symbol->fdpts = convertSize(text->getFramingLineHalfWidth()); } else Q_ASSERT(false); } return ocad_symbol->number; } void OCAD8FileExport::setTextSymbolFormatting(OCADTextSymbol* ocad_symbol, TextObject* formatting) { if (formatting->getHorizontalAlignment() == TextObject::AlignLeft) ocad_symbol->halign = 0; else if (formatting->getHorizontalAlignment() == TextObject::AlignHCenter) ocad_symbol->halign = 1; else if (formatting->getHorizontalAlignment() == TextObject::AlignRight) ocad_symbol->halign = 2; } std::set< s16 > OCAD8FileExport::exportCombinedSymbol(const CombinedSymbol* combination) { // Insert public parts std::vector map_bitfield; map_bitfield.assign(map->getNumSymbols(), false); map_bitfield[map->findSymbolIndex(combination)] = true; map->determineSymbolUseClosure(map_bitfield); std::set result; for (size_t i = 0, end = map_bitfield.size(); i < end; ++i) { if (map_bitfield[i] && symbol_index.contains(map->getSymbol(i))) { result.insert(symbol_index[map->getSymbol(i)].begin(), symbol_index[map->getSymbol(i)].end()); } } // Insert private parts for (int i = 0; i < combination->getNumParts(); ++i) { if (combination->isPartPrivate(i)) { const Symbol* part = combination->getPart(i); int index = 0; if (part->getType() == Symbol::Line) index = exportLineSymbol(part->asLine()); else if (part->getType() == Symbol::Area) index = exportAreaSymbol(part->asArea()); else Q_ASSERT(false); result.insert(index); } } return result; } u16 OCAD8FileExport::exportCoordinates(const MapCoordVector& coords, OCADPoint** buffer, const Symbol* symbol) { s16 num_points = 0; bool curve_start = false; bool hole_point = false; bool curve_continue = false; for (size_t i = 0, end = coords.size(); i < end; ++i) { const MapCoord& point = coords[i]; OCADPoint p = convertPoint(point); if (point.isDashPoint()) { if (!symbol || symbol->getType() != Symbol::Line) p.y |= PY_CORNER; else { const LineSymbol* line_symbol = static_cast(symbol); if ((line_symbol->getDashSymbol() == nullptr || line_symbol->getDashSymbol()->isEmpty()) && line_symbol->isDashed()) p.y |= PY_DASH; else p.y |= PY_CORNER; } } if (curve_start) p.x |= PX_CTL1; if (hole_point) p.y |= PY_HOLE; if (curve_continue) p.x |= PX_CTL2; curve_continue = curve_start; curve_start = point.isCurveStart(); hole_point = point.isHolePoint(); **buffer = p; ++(*buffer); ++num_points; } return num_points; } u16 OCAD8FileExport::exportTextCoordinates(TextObject* object, OCADPoint** buffer) { if (object->getNumLines() == 0) return 0; QTransform text_to_map = object->calcTextToMapTransform(); QTransform map_to_text = object->calcMapToTextTransform(); if (object->hasSingleAnchor()) { // Create 5 coordinates: // 0 - baseline anchor point // 1 - bottom left // 2 - bottom right // 3 - top right // 4 - top left QPointF anchor = QPointF(object->getAnchorCoordF()); QPointF anchor_text = map_to_text.map(anchor); TextObjectLineInfo* line0 = object->getLineInfo(0); **buffer = convertPoint(MapCoord(text_to_map.map(QPointF(anchor_text.x(), line0->line_y)))); ++(*buffer); QRectF bounding_box_text; for (int i = 0; i < object->getNumLines(); ++i) { TextObjectLineInfo* info = object->getLineInfo(i); rectIncludeSafe(bounding_box_text, QPointF(info->line_x, info->line_y - info->ascent)); rectIncludeSafe(bounding_box_text, QPointF(info->line_x + info->width, info->line_y + info->descent)); } **buffer = convertPoint(MapCoord(text_to_map.map(bounding_box_text.bottomLeft()))); ++(*buffer); **buffer = convertPoint(MapCoord(text_to_map.map(bounding_box_text.bottomRight()))); ++(*buffer); **buffer = convertPoint(MapCoord(text_to_map.map(bounding_box_text.topRight()))); ++(*buffer); **buffer = convertPoint(MapCoord(text_to_map.map(bounding_box_text.topLeft()))); ++(*buffer); return 5; } else { // As OCD 8 only supports Top alignment, we have to replace the top box coordinates by the top coordinates of the first line const TextSymbol* text_symbol = static_cast(object->getSymbol()); QFontMetricsF metrics = text_symbol->getFontMetrics(); double internal_scaling = text_symbol->calculateInternalScaling(); TextObjectLineInfo* line0 = object->getLineInfo(0); double new_top = (object->getVerticalAlignment() == TextObject::AlignTop) ? (-object->getBoxHeight() / 2) : ((line0->line_y - line0->ascent) / internal_scaling); // Account for extra internal leading double top_adjust = -text_symbol->getFontSize() + (metrics.ascent() + metrics.descent() + 0.5) / internal_scaling; new_top = new_top - top_adjust; QTransform transform; transform.rotate(-object->getRotation() * 180 / M_PI); **buffer = convertPoint(MapCoord(transform.map(QPointF(-object->getBoxWidth() / 2, object->getBoxHeight() / 2)) + object->getAnchorCoordF())); ++(*buffer); **buffer = convertPoint(MapCoord(transform.map(QPointF(object->getBoxWidth() / 2, object->getBoxHeight() / 2)) + object->getAnchorCoordF())); ++(*buffer); **buffer = convertPoint(MapCoord(transform.map(QPointF(object->getBoxWidth() / 2, new_top)) + object->getAnchorCoordF())); ++(*buffer); **buffer = convertPoint(MapCoord(transform.map(QPointF(-object->getBoxWidth() / 2, new_top)) + object->getAnchorCoordF())); ++(*buffer); return 4; } } // static int OCAD8FileExport::getOcadColor(QRgb rgb) { static const QColor ocad_colors[16] = { QColor( 0, 0, 0).toHsv(), QColor(128, 0, 0).toHsv(), QColor(0, 128, 0).toHsv(), QColor(128, 128, 0).toHsv(), QColor( 0, 0, 128).toHsv(), QColor(128, 0, 128).toHsv(), QColor( 0, 128, 128).toHsv(), QColor(128, 128, 128).toHsv(), QColor(192, 192, 192).toHsv(), QColor(255, 0, 0).toHsv(), QColor( 0, 255, 0).toHsv(), QColor(255, 255, 0).toHsv(), QColor( 0, 0, 255).toHsv(), QColor(255, 0, 255).toHsv(), QColor( 0, 255, 255).toHsv(), QColor(255, 255, 255).toHsv() }; Q_ASSERT(qAlpha(rgb) == 255); // Quick return for frequent values if (rgb == qRgb(255, 255, 255)) return 15; else if (rgb == qRgb(0, 0, 0)) return 0; QColor color = QColor(rgb).toHsv(); if (color.hue() == -1 || color.saturation() < 32) { auto gray = qGray(rgb); // qGray is used for dithering if (gray >= 192) return 8; if (gray >= 128) return 7; return 0; } int best_index = 0; auto best_distance = std::numeric_limits::max(); for (auto i : { 1, 2, 3, 4, 5, 6, 9, 10, 11, 12, 13, 14 }) { // True color int hue_dist = qAbs(color.hue() - ocad_colors[i].hue()); hue_dist = qMin(hue_dist, 360 - hue_dist); auto distance = qPow(hue_dist, 2) + 0.1 * qPow(color.saturation() - ocad_colors[i].saturation(), 2) + 0.1 * qPow(color.value() - ocad_colors[i].value(), 2); // (Too much) manual tweaking for orienteering colors if (i == 1) distance *= 1.5; // Dark red else if (i == 3) distance *= 2; // Olive else if (i == 11) distance *= 2; // Yellow else if (i == 9) distance *= 3; // Red is unlikely if (distance < best_distance) { best_distance = distance; best_index = i; } } return best_index; } s16 OCAD8FileExport::getPointSymbolExtent(const PointSymbol* symbol) { if (!symbol) return 0; QRectF extent; for (int i = 0; i < symbol->getNumElements(); ++i) { QScopedPointer object(symbol->getElementObject(i)->duplicate()); object->setSymbol(symbol->getElementSymbol(i), true); object->update(); rectIncludeSafe(extent, object->getExtent()); object->clearRenderables(); } float float_extent = 0.5f * qMax(extent.width(), extent.height()); if (symbol->getInnerColor()) float_extent = qMax(float_extent, 0.001f * symbol->getInnerRadius()); if (symbol->getOuterColor()) float_extent = qMax(float_extent, 0.001f * (symbol->getInnerRadius() + symbol->getOuterWidth())); return convertSize(1000 * float_extent); } void OCAD8FileExport::convertPascalString(const QString& text, char* buffer, int buffer_size) { Q_ASSERT(buffer_size <= 256); // not possible to store a bigger length in the first byte int max_size = buffer_size - 1; if (text.length() > max_size) addStringTruncationWarning(text, max_size); QByteArray data = encoding_1byte->fromUnicode(text); int min_size = qMin(text.length(), max_size); *((unsigned char *)buffer) = min_size; memcpy(buffer + 1, data.data(), min_size); } void OCAD8FileExport::convertCString(const QString& text, unsigned char* buffer, int buffer_size) { if (text.length() + 1 > buffer_size) addStringTruncationWarning(text, buffer_size - 1); QByteArray data = encoding_1byte->fromUnicode(text); int min_size = qMin(buffer_size - 1, data.length()); memcpy(buffer, data.data(), min_size); buffer[min_size] = 0; } int OCAD8FileExport::convertWideCString(const QString& text, unsigned char* buffer, int buffer_size) { // Convert text to Windows-OCAD format: // - if it starts with a newline, add another // - convert \n to \r\n QString exported_text; if (text.startsWith(QLatin1Char('\n'))) exported_text = QLatin1Char('\n') + text; else exported_text = text; exported_text.replace(QLatin1Char('\n'), QLatin1String("\r\n")); if (2 * (exported_text.length() + 1) > buffer_size) addStringTruncationWarning(exported_text, buffer_size - 1); // Do not add a byte order mark by using QTextCodec::IgnoreHeader QTextEncoder* encoder = encoding_2byte->makeEncoder(QTextCodec::IgnoreHeader); QByteArray data = encoder->fromUnicode(exported_text); delete encoder; int min_size = qMin(buffer_size - 2, data.length()); memcpy(buffer, data.data(), min_size); buffer[min_size] = 0; buffer[min_size + 1] = 0; return min_size + 2; } int OCAD8FileExport::convertRotation(float angle) { return qRound(10 * (angle * 180 / M_PI)); } namespace { constexpr s32 convertPointMember(s32 value) { return (value < -5) ? s32(0x80000000u | ((0x7fffffu & u32((value-4)/10)) << 8)) : s32((0x7fffffu & u32((value+5)/10)) << 8); } // convertPointMember() shall round half up. Q_STATIC_ASSERT(convertPointMember(-16) == s32(0xfffffe00u)); // __ down __ Q_STATIC_ASSERT(convertPointMember(-15) == s32(0xffffff00u)); // up Q_STATIC_ASSERT(convertPointMember( -6) == s32(0xffffff00u)); // __ down __ Q_STATIC_ASSERT(convertPointMember( -5) == s32(0x00000000u)); // up Q_STATIC_ASSERT(convertPointMember( -1) == s32(0x00000000u)); // up Q_STATIC_ASSERT(convertPointMember( 0) == s32(0x00000000u)); // unchanged Q_STATIC_ASSERT(convertPointMember( +1) == s32(0x00000000u)); // down Q_STATIC_ASSERT(convertPointMember( +4) == s32(0x00000000u)); // __ down __ Q_STATIC_ASSERT(convertPointMember( +5) == s32(0x00000100u)); // up Q_STATIC_ASSERT(convertPointMember(+14) == s32(0x00000100u)); // __ down __ Q_STATIC_ASSERT(convertPointMember(+15) == s32(0x00000200u)); // up #ifdef MAPPER_DEVELOPMENT_BUILD namespace broken { // Previous, broken implementation (#749) // Left here for reference. constexpr s32 convertPointMember(s32 value) { return (value < 0) ? (0x80000000 | ((0x7fffffu & ((value-5)/10)) << 8)) : ((0x7fffffu & ((value+5)/10)) << 8); } } // Actual behaviour of the broken implementation Q_STATIC_ASSERT(broken::convertPointMember(-16) == s32(0xfffffe00u)); // down Q_STATIC_ASSERT(broken::convertPointMember(-15) == s32(0xfffffe00u)); // __ down __ (should be up) Q_STATIC_ASSERT(broken::convertPointMember(-14) == s32(0xffffff00u)); // up Q_STATIC_ASSERT(broken::convertPointMember( -6) == s32(0xffffff00u)); // down Q_STATIC_ASSERT(broken::convertPointMember( -5) == s32(0xffffff00u)); // __ down __ (should be up) Q_STATIC_ASSERT(broken::convertPointMember( -4) == s32(0x80000000u)); // wrong (should be 0x00000000u) Q_STATIC_ASSERT(broken::convertPointMember( -3) == s32(0x80000000u)); // wrong (should be 0x00000000u) Q_STATIC_ASSERT(broken::convertPointMember( -2) == s32(0x80000000u)); // wrong (should be 0x00000000u) Q_STATIC_ASSERT(broken::convertPointMember( -1) == s32(0x80000000u)); // wrong (should be 0x00000000u) Q_STATIC_ASSERT(broken::convertPointMember( 0) == s32(0x00000000u)); // unchanged Q_STATIC_ASSERT(broken::convertPointMember( +1) == s32(0x00000000u)); // down Q_STATIC_ASSERT(broken::convertPointMember( +4) == s32(0x00000000u)); // __ down __ Q_STATIC_ASSERT(broken::convertPointMember( +5) == s32(0x00000100u)); // up Q_STATIC_ASSERT(broken::convertPointMember(+14) == s32(0x00000100u)); // __ down __ Q_STATIC_ASSERT(broken::convertPointMember(+15) == s32(0x00000200u)); // up #endif } OCADPoint OCAD8FileExport::convertPoint(qint32 x, qint32 y) { return { convertPointMember(x), convertPointMember(-y) }; } OCADPoint OCAD8FileExport::convertPoint(const MapCoord& coord) { return convertPoint(coord.nativeX(), coord.nativeY()); } s32 OCAD8FileExport::convertSize(qint32 size) { return (s32)((size+5) / 10); } s16 OCAD8FileExport::convertColor(const MapColor* color) const { int index = map->findColorIndex(color); if (index >= 0) { return uses_registration_color ? (index + 1) : index; } return 0; } double OCAD8FileExport::convertTemplateScale(double mapper_scale) { return mapper_scale * (1 / 0.01); } void OCAD8FileExport::addStringTruncationWarning(const QString& text, int truncation_pos) { QString temp = text; temp.insert(truncation_pos, QLatin1String("|||")); addWarning(tr("String truncated (truncation marked with three '|'): %1").arg(temp)); } } // namespace OpenOrienteering mapper-0.8.1.1/src/fileformats/ocad8_file_format.h000066400000000000000000000027071325266516600220050ustar00rootroot00000000000000/* * Copyright 2012, 2013 Pete Curtis * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_OCAD8_FILE_FORMAT_H #define OPENORIENTEERING_OCAD8_FILE_FORMAT_H #include #include "file_format.h" class QIODevice; namespace OpenOrienteering { class Exporter; class Importer; class Map; class MapView; /** Representation of the format used by OCAD 8. */ class OCAD8FileFormat : public FileFormat { public: OCAD8FileFormat(); bool understands(const unsigned char *buffer, std::size_t sz) const override; Importer* createImporter(QIODevice* stream, Map *map, MapView *view) const override; Exporter* createExporter(QIODevice* stream, Map* map, MapView* view) const override; }; } // namespace OpenOrienteering #endif // OCAD8_FILE_IMPORT_H mapper-0.8.1.1/src/fileformats/ocad8_file_format_p.h000066400000000000000000000210511325266516600223150ustar00rootroot00000000000000/* * Copyright 2012, 2013 Pete Curtis * Copyright 2014, 2015 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_FILE_FORMAT_OCAD_P_H #define OPENORIENTEERING_FILE_FORMAT_OCAD_P_H #include #include #include #include "core/map_coord.h" #include "fileformats/file_import_export.h" #include "libocad/libocad.h" namespace OpenOrienteering { class Map; class MapColor; class MapPart; class Object; class PointObject; class TextObject; class Symbol; class AreaSymbol; class CombinedSymbol; class LineSymbol; class PointSymbol; class Template; class TextSymbol; /** Importer for OCD version 8 files. */ class OCAD8FileImport : public Importer { friend class OcdFileImport; Q_DECLARE_TR_FUNCTIONS(OpenOrienteering::OCAD8FileImport) private: /// Information about an OCAD rectangle symbol struct RectangleInfo { LineSymbol* border_line; double corner_radius; bool has_grid; // Only valid if has_grid is true LineSymbol* inner_line; TextSymbol* text; bool number_from_bottom; double cell_width; double cell_height; int unnumbered_cells; QString unnumbered_text; }; public: OCAD8FileImport(QIODevice* stream, Map *map, MapView *view); ~OCAD8FileImport() override; void setStringEncodings(const char *narrow, const char *wide = "UTF-16LE"); protected: void import(bool load_symbols_only) override; // Symbol import Symbol *importPointSymbol(const OCADPointSymbol *ocad_symbol); Symbol *importLineSymbol(const OCADLineSymbol *ocad_symbol); Symbol *importAreaSymbol(const OCADAreaSymbol *ocad_symbol); Symbol *importTextSymbol(const OCADTextSymbol *ocad_symbol); RectangleInfo *importRectSymbol(const OCADRectSymbol *ocad_symbol); // Object import Object *importObject(const OCADObject *ocad_object, MapPart* part); bool importRectangleObject(const OCADObject* ocad_object, MapPart* part, const RectangleInfo& rect); // String import virtual void importString(OCADStringEntry *entry); Template *importTemplate(OCADCString* ocad_str); OCADBackground importBackground(const QByteArray& data); /// @deprecated Replaced by Template *importTemplate(OCADCString* string). Template *importRasterTemplate(const OCADBackground &background); // Some helper functions that are used in multiple places PointSymbol *importPattern(s16 npts, OCADPoint *pts); void fillCommonSymbolFields(Symbol *symbol, const OCADSymbol *ocad_symbol); void setPathHolePoint(Object *object, int i); void fillPathCoords(Object* object, bool is_area, u16 npts, const OCADPoint* pts); bool fillTextPathCoords(TextObject* object, TextSymbol* symbol, u16 npts, OCADPoint* pts); // Unit conversion functions QString convertPascalString(const char *p); QString convertCString(const char *p, std::size_t n, bool ignore_first_newline); QString convertWideCString(const char *p, std::size_t n, bool ignore_first_newline); float convertRotation(int angle); void convertPoint(MapCoord &c, s32 ocad_x, s32 ocad_y); qint32 convertSize(int ocad_size); const MapColor *convertColor(int color); double convertTemplateScale(double ocad_scale); static bool isRasterImageFile(const QString &filename); private: /// Handle to the open OCAD file OCADFile *file; /// Character encoding to use for 1-byte (narrow) strings QTextCodec *encoding_1byte; /// Character encoding to use for 2-byte (wide) strings QTextCodec *encoding_2byte; /// maps OCAD color number to oo-mapper color object QHash color_index; /// maps OCAD symbol number to oo-mapper symbol object QHash symbol_index; /// maps OO Mapper text symbol pointer to OCAD text symbol horizontal alignment (stored in objects instead of symbols in OO Mapper) QHash text_halign_map; /// maps OCAD symbol number to rectangle information struct QHash rectangle_info; /// Offset between OCAD map origin and Mapper map origin (in Mapper coordinates) qint64 offset_x, offset_y; }; /** Exporter for OCD version 8 files. */ class OCAD8FileExport : public Exporter { Q_DECLARE_TR_FUNCTIONS(OpenOrienteering::OCAD8FileExport) public: OCAD8FileExport(QIODevice* stream, Map *map, MapView *view); ~OCAD8FileExport() override; void doExport() override; protected: // Determines an offset for moving objects to the OCD drawing area. MapCoord calculateAreaOffset(); // Symbol export void exportCommonSymbolFields(const Symbol* symbol, OCADSymbol* ocad_symbol, int size); void exportSymbolIcon(const Symbol* symbol, u8 ocad_icon[]); int getPatternSize(const PointSymbol* point); s16 exportPattern(const PointSymbol* point, OCADPoint** buffer); // returns the number of written coordinates, including the headers s16 exportSubPattern(const Object* object, const Symbol* symbol, OCADPoint** buffer); s16 exportPointSymbol(const PointSymbol* point); s16 exportLineSymbol(const LineSymbol* line); s16 exportAreaSymbol(const AreaSymbol* area); s16 exportTextSymbol(const TextSymbol* text); void setTextSymbolFormatting(OCADTextSymbol* ocad_symbol, TextObject* formatting); std::set exportCombinedSymbol(const CombinedSymbol* combination); // Helper functions /// Returns the number of exported coordinates. If not nullptr, the given symbol is used to determine the meaning of dash points. u16 exportCoordinates(const MapCoordVector& coords, OCADPoint** buffer, const Symbol* symbol); u16 exportTextCoordinates(TextObject* object, OCADPoint** buffer); static int getOcadColor(QRgb rgb); s16 getPointSymbolExtent(const PointSymbol* symbol); // Conversion functions void convertPascalString(const QString& text, char* buffer, int buffer_size); void convertCString(const QString& text, unsigned char* buffer, int buffer_size); /// Returns the number of bytes written into buffer int convertWideCString(const QString& text, unsigned char* buffer, int buffer_size); int convertRotation(float angle); OCADPoint convertPoint(qint32 x, qint32 y); /// Attention: this ignores the coordinate flags! OCADPoint convertPoint(const MapCoord& coord); s32 convertSize(qint32 size); s16 convertColor(const MapColor* color) const; double convertTemplateScale(double mapper_scale); private: /** Indicates that the map uses the special registration color. */ bool uses_registration_color; /// Handle to the open OCAD file OCADFile *file; /// Character encoding to use for 1-byte (narrow) strings QTextCodec *encoding_1byte; /// Character encoding to use for 2-byte (wide) strings QTextCodec *encoding_2byte; /// Set of used symbol numbers. Needed to ensure uniqueness of the symbol number as Mapper does not enforce it, /// but the indexing of symbols in OCAD depends on it. std::set symbol_numbers; /// Maps OO Mapper symbol pointer to a list of OCAD symbol numbers. /// Usually the list contains only one entry, except for combined symbols, /// for which it contains the indices of all basic parts QHash > symbol_index; /// In .ocd 8, text alignment needs to be specified in the text symbols instead of objects, so it is possible /// that multiple ocd text symbols have to be created for one native TextSymbol. /// This structure maps text symbols to lists containing information about the already created ocd symbols. /// The first member in each pair just gives information about the alignment option used for the symbol indexed by the /// second part of the pair. /// If there is no entry for a TextSymbol in this map yet, no object using this symbol has been encountered yet, /// no no specific formatting was set in the corresponding symbol (which has to be looked up using symbol_index). typedef std::vector< std::pair< int, s16 > > TextFormatList; QHash text_format_map; /// Helper object for pattern export PointObject* origin_point_object; void addStringTruncationWarning(const QString& text, int truncation_pos); }; } // namespace OpenOrienteering #endif mapper-0.8.1.1/src/fileformats/ocd_file_export.cpp000066400000000000000000000025271325266516600221400ustar00rootroot00000000000000/* * Copyright 2016 Kai Pastor * * Some parts taken from file_format_oc*d8{.h,_p.h,cpp} which are * Copyright 2012 Pete Curtis * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "ocd_file_export.h" #include "fileformats/ocad8_file_format_p.h" namespace OpenOrienteering { OcdFileExport::OcdFileExport(QIODevice* stream, Map* map, MapView* view) : Exporter { stream, map, view } { // nothing else } OcdFileExport::~OcdFileExport() = default; void OcdFileExport::doExport() { OCAD8FileExport delegate { stream, map, view }; delegate.doExport(); for (auto&& w : delegate.warnings()) { addWarning(w); } } } // namespace OpenOrienteering mapper-0.8.1.1/src/fileformats/ocd_file_export.h000066400000000000000000000027721325266516600216070ustar00rootroot00000000000000/* * Copyright 2016 Kai Pastor * * Some parts taken from file_format_oc*d8{.h,_p.h,cpp} which are * Copyright 2012 Pete Curtis * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_OCD_FILE_EXPORT_H #define OPENORIENTEERING_OCD_FILE_EXPORT_H #include #include "fileformats/file_import_export.h" class QIODevice; namespace OpenOrienteering { class Map; class MapView; /** * An exporter for OCD files. */ class OcdFileExport : public Exporter { Q_DECLARE_TR_FUNCTIONS(OpenOrienteering::OcdFileExport) public: OcdFileExport(QIODevice* stream, Map *map, MapView *view); ~OcdFileExport() override; /** * Exports an OCD file. * * For now, this simply uses the OCAD8FileExport class. */ void doExport() override; }; } // namespace OpenOrienteering #endif mapper-0.8.1.1/src/fileformats/ocd_file_format.cpp000066400000000000000000000040551325266516600221050ustar00rootroot00000000000000/* * Copyright 2013-2016 Kai Pastor * * Some parts taken from file_format_oc*d8{.h,_p.h,cpp} which are * Copyright 2012 Pete Curtis * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "ocd_file_format.h" #include #include #include #include #include "fileformats/file_import_export.h" #include "fileformats/ocd_file_export.h" #include "fileformats/ocd_file_import.h" namespace OpenOrienteering { // ### OcdFileFormat ### OcdFileFormat::OcdFileFormat() : FileFormat { MapFile, "OCD", ::OpenOrienteering::ImportExport::tr("OCAD"), QString::fromLatin1("ocd"), ImportSupported | ExportSupported | ExportLossy } { // Nothing } bool OcdFileFormat::understands(const unsigned char* buffer, std::size_t sz) const { // The first two bytes of the file must be 0x0cad in litte endian ordner. // This test will refuse to understand OCD files on big endian systems: // The importer's current implementation won't work there. return (sz >= 2 && *reinterpret_cast(buffer) == 0x0cad); } Importer* OcdFileFormat::createImporter(QIODevice* stream, Map *map, MapView *view) const { return new OcdFileImport(stream, map, view); } Exporter* OcdFileFormat::createExporter(QIODevice* stream, Map* map, MapView* view) const { return new OcdFileExport(stream, map, view); } } // namespace OpenOrienteering mapper-0.8.1.1/src/fileformats/ocd_file_format.h000066400000000000000000000034401325266516600215470ustar00rootroot00000000000000/* * Copyright 2013, 2016 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_OCD_FILE_FORMAT #define OPENORIENTEERING_OCD_FILE_FORMAT #include #include "fileformats/file_format.h" class QIODevice; namespace OpenOrienteering { class Exporter; class Importer; class Map; class MapView; /** * The map file format known as OC*D. */ class OcdFileFormat : public FileFormat { public: /** * Constructs a new OcdFileFormat. */ OcdFileFormat(); /** * Detects whether the buffer may be the start of a valid OCD file. * * At the moment, it requires at least two bytes of data. * It will return false if compiled for a big endian system. */ bool understands(const unsigned char *buffer, std::size_t sz) const override; /// \copydoc FileFormat::createImporter() Importer* createImporter(QIODevice* stream, Map *map, MapView *view) const override; /// \copydoc FileFormat::createExporter() Exporter* createExporter(QIODevice* stream, Map* map, MapView* view) const override; }; } // namespace OpenOrienteering #endif // OPENORIENTEERING_OCD_FILE_FORMAT mapper-0.8.1.1/src/fileformats/ocd_file_import.cpp000066400000000000000000002116541325266516600221340ustar00rootroot00000000000000/* * Copyright 2013-2017 Kai Pastor * * Some parts taken from file_format_oc*d8{.h,_p.h,cpp} which are * Copyright 2012 Pete Curtis * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "ocd_file_import.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "settings.h" #include "core/crs_template.h" #include "core/georeferencing.h" #include "core/map.h" #include "core/map_color.h" #include "core/map_grid.h" #include "core/map_part.h" #include "core/map_view.h" #include "core/objects/text_object.h" #include "core/symbols/area_symbol.h" #include "core/symbols/combined_symbol.h" #include "core/symbols/line_symbol.h" #include "core/symbols/point_symbol.h" #include "core/symbols/symbol.h" #include "core/symbols/text_symbol.h" #include "fileformats/file_format.h" #include "fileformats/ocad8_file_format_p.h" #include "fileformats/ocd_types_v10.h" #include "fileformats/ocd_types_v11.h" #include "fileformats/ocd_types_v12.h" #include "fileformats/ocd_types_v9.h" #include "templates/template.h" #include "templates/template_image.h" #include "templates/template_map.h" #include "util/encoding.h" #include "util/util.h" namespace OpenOrienteering { namespace { static QTextCodec* codecFromSettings() { const auto& settings = Settings::getInstance(); const auto name = settings.getSetting(Settings::General_Local8BitEncoding).toByteArray(); return Util::codecForName(name); } } // namespace OcdFileImport::OcdImportedPathObject::~OcdImportedPathObject() { // nothing, not inlined } OcdFileImport::OcdFileImport(QIODevice* stream, Map* map, MapView* view) : Importer { stream, map, view } , delegate { nullptr } , custom_8bit_encoding { codecFromSettings() } { if (!custom_8bit_encoding) { addWarning(tr("Encoding '%1' is not available. Check the settings.")); custom_8bit_encoding = QTextCodec::codecForLocale(); } } OcdFileImport::~OcdFileImport() { // nothing } void OcdFileImport::setCustom8BitEncoding(QTextCodec* encoding) { custom_8bit_encoding = encoding; } QString OcdFileImport::convertOcdString(const QChar* src, uint maxlen) const { auto last = src; if (src) { while (maxlen && *last != 0) { last ++; --maxlen; } } QTextCodec* utf16 = QTextCodec::codecForName("UTF-16LE"); Q_ASSERT(utf16); auto decoder = std::unique_ptr(utf16->makeDecoder(QTextCodec::ConvertInvalidToNull)); return decoder->toUnicode(reinterpret_cast(src), 2*int(last - src)); } void OcdFileImport::addSymbolWarning(const AreaSymbol* symbol, const QString& warning) { addWarning( tr("In area symbol %1 '%2': %3"). arg(symbol->getNumberAsString(), symbol->getName(), warning) ); } void OcdFileImport::addSymbolWarning(const LineSymbol* symbol, const QString& warning) { addWarning( tr("In line symbol %1 '%2': %3"). arg(symbol->getNumberAsString(), symbol->getName(), warning) ); } void OcdFileImport::addSymbolWarning(const TextSymbol* symbol, const QString& warning) { addWarning( tr("In text symbol %1 '%2': %3"). arg(symbol->getNumberAsString(), symbol->getName(), warning) ); } #ifndef NDEBUG // Heuristic detection of implementation errors template< > inline qint64 OcdFileImport::convertLength< quint8 >(quint8 ocd_length) const { // OC*D uses hundredths of a millimeter. // oo-mapper uses 1/1000 mm if (ocd_length > 200) qDebug("quint8 has value %d, might be qint8 %d", ocd_length, qint8(ocd_length)); return qint64(ocd_length) * 10; } template< > inline qint64 OcdFileImport::convertLength< quint16 >(quint16 ocd_length) const { // OC*D uses hundredths of a millimeter. // oo-mapper uses 1/1000 mm if (ocd_length > 50000) qDebug("quint16 has value %d, might be qint16 %d", ocd_length, qint16(ocd_length)); return qint64(ocd_length) * 10; } template< > inline qint64 OcdFileImport::convertLength< quint32 >(quint32 ocd_length) const { // OC*D uses hundredths of a millimeter. // oo-mapper uses 1/1000 mm if (ocd_length > 3000000) qDebug("quint32 has value %d, might be qint32 %d", ocd_length, qint32(ocd_length)); return qint64(ocd_length) * 10; } #endif // !NDEBUG void OcdFileImport::importImplementationLegacy(bool load_symbols_only) { QBuffer new_stream(&buffer); new_stream.open(QIODevice::ReadOnly); delegate.reset(new OCAD8FileImport(&new_stream, map, view)); delegate->import(load_symbols_only); for (auto&& w : delegate->warnings()) { addWarning(w); } for (auto&& a : delegate->actions()) { addAction(a); } } template< class F > void OcdFileImport::importImplementation(bool load_symbols_only) { const OcdFile file(buffer); #ifdef MAPPER_DEVELOPMENT_BUILD if (!qApp->applicationName().endsWith(QLatin1String("Test"))) { qDebug("*** OcdFileImport: Importing a version %d.%d file", file.header()->version, file.header()->subversion); for (const auto& string : file.strings()) { qDebug(" %d \t%s", string.type, qPrintable(convertOcdString< typename F::Encoding >(file[string]))); } } #endif map->setSymbolSetId(QStringLiteral("OCD")); importGeoreferencing(file); importColors(file); importSymbols(file); if (!load_symbols_only) { importExtras(file); importObjects(file); importTemplates(file); if (view) { importView(file); } } } void OcdFileImport::importGeoreferencing(const OcdFile& file) { const Ocd::FileHeaderV8* header = file.header(); const Ocd::SetupV8* setup = reinterpret_cast< const Ocd::SetupV8* >(file.byteArray().data() + header->setup_pos); Georeferencing georef; georef.setScaleDenominator(qRound(setup->map_scale)); georef.setProjectedRefPoint(QPointF(setup->real_offset_x, setup->real_offset_y)); if (std::abs(setup->real_angle) >= 0.01) /* degrees */ { georef.setGrivation(setup->real_angle); } map->setGeoreferencing(georef); } template< class F > void OcdFileImport::importGeoreferencing(const OcdFile< F >& file) { handleStrings(file, { { 1039, &OcdFileImport::importGeoreferencing } }); } void OcdFileImport::importGeoreferencing(const QString& param_string, int /*ocd_version*/) { const QChar* unicode = param_string.unicode(); Georeferencing georef; QString combined_grid_zone; QPointF proj_ref_point; bool x_ok = false, y_ok = false; int i = param_string.indexOf(QLatin1Char('\t'), 0); ; // skip first word for this entry type while (i >= 0) { bool ok; int next_i = param_string.indexOf(QLatin1Char('\t'), i+1); int len = (next_i > 0 ? next_i : param_string.length()) - i - 2; const QString param_value = QString::fromRawData(unicode+i+2, len); // no copying! switch (param_string[i+1].toLatin1()) { case 'm': { double scale = param_value.toDouble(&ok); if (ok && scale >= 0) georef.setScaleDenominator(qRound(scale)); } break; case 'a': { double angle = param_value.toDouble(&ok); if (ok && qAbs(angle) >= 0.01) georef.setGrivation(angle); } break; case 'x': proj_ref_point.setX(param_value.toDouble(&x_ok)); break; case 'y': proj_ref_point.setY(param_value.toDouble(&y_ok)); break; case 'd': { auto spacing = param_value.toDouble(&ok); if (ok && spacing >= 0.001) { auto grid = map->getGrid(); grid.setUnit(MapGrid::MetersInTerrain); grid.setHorizontalSpacing(spacing); grid.setVerticalSpacing(spacing); map->setGrid(grid); } } break; case 'i': combined_grid_zone = param_value; break; case '\t': // empty item, fall through default: ; // nothing } i = next_i; } if (!combined_grid_zone.isEmpty()) { applyGridAndZone(georef, combined_grid_zone); } if (x_ok && y_ok) { georef.setProjectedRefPoint(proj_ref_point, false); } map->setGeoreferencing(georef); } void OcdFileImport::applyGridAndZone(Georeferencing& georef, const QString& combined_grid_zone) { bool zone_ok = false; const CRSTemplate* crs_template = nullptr; QString id; QString spec; std::vector values; if (combined_grid_zone.startsWith(QLatin1String("20"))) { auto zone = combined_grid_zone.midRef(2).toUInt(&zone_ok); zone_ok &= (zone >= 1 && zone <= 60); if (zone_ok) { id = QLatin1String{"UTM"}; crs_template = CRSTemplateRegistry().find(id); values.reserve(1); values.push_back(QString::number(zone)); } } else if (combined_grid_zone.startsWith(QLatin1String("80"))) { auto zone = combined_grid_zone.midRef(2).toUInt(&zone_ok); if (zone_ok) { id = QLatin1String{"Gauss-Krueger, datum: Potsdam"}; crs_template = CRSTemplateRegistry().find(id); values.reserve(1); values.push_back(QString::number(zone)); } } else if (combined_grid_zone == QLatin1String("6005")) { id = QLatin1String{"EPSG"}; crs_template = CRSTemplateRegistry().find(id); values.reserve(1); values.push_back(QLatin1String{"3067"}); } else if (combined_grid_zone == QLatin1String("14001")) { id = QLatin1String{"EPSG"}; crs_template = CRSTemplateRegistry().find(id); values.reserve(1); values.push_back(QLatin1String{"21781"}); } else if (combined_grid_zone == QLatin1String("1000")) { return; } if (crs_template) { spec = crs_template->specificationTemplate(); auto param = crs_template->parameters().begin(); for (const auto& value : values) { for (const auto& spec_value : (*param)->specValues(value)) { spec = spec.arg(spec_value); } ++param; } } if (spec.isEmpty()) { addWarning(tr("Could not load the coordinate reference system '%1'.").arg(combined_grid_zone)); } else { georef.setProjectedCRS(id, spec, std::move(values)); } } void OcdFileImport::importColors(const OcdFile& file) { const Ocd::SymbolHeaderV8 & symbol_header = file.header()->symbol_header; int num_colors = symbol_header.num_colors; for (int i = 0; i < num_colors && i < 256; i++) { const Ocd::ColorInfoV8& color_info = symbol_header.color_info[i]; const QString name = convertOcdString(color_info.name); int color_pos = map->getNumColors(); auto color = new MapColor(name, color_pos); // OC*D stores CMYK values as integers from 0-200. MapColorCmyk cmyk; cmyk.c = 0.005f * color_info.cmyk.cyan; cmyk.m = 0.005f * color_info.cmyk.magenta; cmyk.y = 0.005f * color_info.cmyk.yellow; cmyk.k = 0.005f * color_info.cmyk.black; color->setCmyk(cmyk); color->setOpacity(1.0f); map->addColor(color, color_pos); color_index[color_info.number] = color; } addWarning(OcdFileImport::tr("Spot color information was ignored.")); } template< class F > void OcdFileImport::importColors(const OcdFile< F >& file) { handleStrings(file, { { 9, &OcdFileImport::importColor } }); addWarning(OcdFileImport::tr("Spot color information was ignored.")); } void OcdFileImport::importColor(const QString& param_string, int /*ocd_version*/) { const QChar* unicode = param_string.unicode(); int i = param_string.indexOf(QLatin1Char('\t'), 0); const QString name = param_string.left(qMax(-1, i)); // copied int number; bool number_ok = false; MapColorCmyk cmyk { 0.0, 0.0, 0.0, 0.0 }; bool overprinting = false; float opacity = 1.0f; while (i >= 0) { float f_value; int i_value; bool ok; int next_i = param_string.indexOf(QLatin1Char('\t'), i+1); int len = (next_i > 0 ? next_i : param_string.length()) - i - 2; const QString param_value = QString::fromRawData(unicode+i+2, len); // no copying! switch (param_string[i+1].toLatin1()) { case '\t': // empty item break; case 'n': number = param_value.toInt(&number_ok); break; case 'c': f_value = param_value.toFloat(&ok); if (ok && f_value >= 0 && f_value <= 100) cmyk.c = 0.01f * f_value; break; case 'm': f_value = param_value.toFloat(&ok); if (ok && f_value >= 0 && f_value <= 100) cmyk.m = 0.01f * f_value; break; case 'y': f_value = param_value.toFloat(&ok); if (ok && f_value >= 0 && f_value <= 100) cmyk.y = 0.01f * f_value; break; case 'k': f_value = param_value.toFloat(&ok); if (ok && f_value >= 0 && f_value <= 100) cmyk.k = 0.01f * f_value; break; case 'o': i_value = param_value.toInt(&ok); if (ok) overprinting = i_value; break; case 't': f_value = param_value.toFloat(&ok); if (ok && f_value >= 0.f && f_value <= 100.f) opacity = 0.01f * f_value; break; default: ; // nothing } i = next_i; } if (!number_ok) return; int color_pos = map->getNumColors(); auto color = new MapColor(name, color_pos); color->setCmyk(cmyk); color->setKnockout(!overprinting); color->setOpacity(opacity); map->addColor(color, color_pos); color_index[number] = color; } namespace { quint16 symbolType(const Ocd::BaseSymbolV8& ocd_symbol) { if (ocd_symbol.type == Ocd::SymbolTypeLine && ocd_symbol.type2 == 1) return Ocd::SymbolTypeLineText; return ocd_symbol.type; } template< class T > quint8 symbolType(const T& ocd_symbol) { return ocd_symbol.type; } } template< class F > void OcdFileImport::importSymbols(const OcdFile< F >& file) { auto ocd_version = file.header()->version; for (const auto& ocd_symbol : file.symbols()) { // When extra symbols are created, we want to insert the main symbol // before them. That is why pos needs to be determined first. auto pos = map->getNumSymbols(); Symbol* symbol = nullptr; switch (symbolType(ocd_symbol)) { case Ocd::SymbolTypePoint: symbol = importPointSymbol(reinterpret_cast(ocd_symbol), ocd_version); break; case Ocd::SymbolTypeLine: symbol = importLineSymbol(reinterpret_cast(ocd_symbol), ocd_version); break; case Ocd::SymbolTypeArea: symbol = importAreaSymbol(reinterpret_cast(ocd_symbol), ocd_version); break; case Ocd::SymbolTypeText: symbol = importTextSymbol(reinterpret_cast(ocd_symbol), ocd_version); break; case Ocd::SymbolTypeRectangle_V8: case Ocd::SymbolTypeRectangle_V9: symbol = importRectangleSymbol(reinterpret_cast(ocd_symbol)); break; case Ocd::SymbolTypeLineText: symbol = importLineTextSymbol(reinterpret_cast(ocd_symbol), ocd_version); break; default: addWarning(OcdFileImport::tr("Unable to import symbol %1.%2 \"%3\": %4") . arg(ocd_symbol.number / F::BaseSymbol::symbol_number_factor) . arg(ocd_symbol.number % F::BaseSymbol::symbol_number_factor) . arg(convertOcdString(ocd_symbol.description)). arg(OcdFileImport::tr("Unsupported type \"%1\".").arg(ocd_symbol.type)) ); continue; } map->addSymbol(symbol, pos); symbol_index[ocd_symbol.number] = symbol; } resolveSubsymbols(); } void OcdFileImport::resolveSubsymbols() { for (auto i = 0; i < map->getNumSymbols(); ++i) { auto symbol = map->getSymbol(i); if (symbol->getType() == Symbol::Combined) { auto combined = symbol->asCombined(); if (combined->getNumParts() == 2) { auto number = combined->getPart(1)->getNumberComponent(2); if (number >= 0 && symbol_index.contains(static_cast(number))) { combined->setPart(1, symbol_index[static_cast(number)], false); } } } } } void OcdFileImport::importObjects(const OcdFile& file) { auto ocd_version = file.header()->version; MapPart* part = map->getCurrentPart(); Q_ASSERT(part); for (const auto& object_entry : file.objects()) { if (object_entry.symbol) { if (auto object = importObject(file[object_entry], part, ocd_version)) part->addObject(object, part->getNumObjects()); } } } template< class F > void OcdFileImport::importObjects(const OcdFile< F >& file) { auto ocd_version = file.header()->version; MapPart* part = map->getCurrentPart(); Q_ASSERT(part); for (const auto& object_entry : file.objects()) { if ( object_entry.symbol && object_entry.status != Ocd::ObjectDeleted && object_entry.status != Ocd::ObjectDeletedForUndo ) { if (auto object = importObject(file[object_entry], part, ocd_version)) part->addObject(object, part->getNumObjects()); } } } template< class F > void OcdFileImport::importTemplates(const OcdFile< F >& file) { handleStrings(file, { { 8, &OcdFileImport::importTemplate } }); } void OcdFileImport::importTemplate(const QString& param_string, int ocd_version) { const QChar* unicode = param_string.unicode(); int i = param_string.indexOf(QLatin1Char('\t'), 0); const QString filename = QString::fromRawData(unicode, qMax(-1, i)); const QString clean_path = QDir::cleanPath(QString(filename).replace(QLatin1Char('\\'), QLatin1Char('/'))); const QString extension = QFileInfo(clean_path).suffix().toLower(); Template* templ = nullptr; if (extension.compare(QLatin1String("ocd")) == 0) { templ = new TemplateMap(clean_path, map); } else if (QImageReader::supportedImageFormats().contains(extension.toLatin1())) { templ = new TemplateImage(clean_path, map); } else { addWarning(tr("Unable to import template: \"%1\" is not a supported template type.").arg(filename)); return; } // 8 or 9 or 10 ? Only tested with 8 and 11 double scale_factor = (ocd_version <= 8) ? 0.01 : 1.0; unsigned int num_rotation_params = 0; double rotation = 0.0; double scale_x = 1.0; double scale_y = 1.0; int dimming = 0; bool visible = false; while (i >= 0) { double value; bool ok; int next_i = param_string.indexOf(QLatin1Char('\t'), i+1); int len = (next_i > 0 ? next_i : param_string.length()) - i - 2; const QString param_value = QString::fromRawData(unicode+i+2, len); // no copying! switch (param_string[i+1].toLatin1()) { case '\t': // empty item break; case 'x': value = param_value.toDouble(&ok); if (ok) templ->setTemplateX(qRound64(value*1000*scale_factor)); break; case 'y': value = param_value.toDouble(&ok); if (ok) templ->setTemplateY(-qRound64(value*1000*scale_factor)); break; case 'a': case 'b': // TODO: use the distinct angles correctly, not just the average rotation += param_value.toDouble(&ok); if (ok) ++num_rotation_params; break; case 'u': value = param_value.toDouble(&ok); if (ok && qAbs(value) >= 0.0000000001) scale_x = value; break; case 'v': value = param_value.toDouble(&ok); if (ok && qAbs(value) >= 0.0000000001) scale_y = value; break; case 'd': dimming = param_value.toInt(); break; case 's': visible = param_value.toInt(); break; default: ; // nothing } i = next_i; } if (num_rotation_params) templ->setTemplateRotation(Georeferencing::degToRad(rotation / num_rotation_params)); templ->setTemplateScaleX(scale_x * scale_factor); templ->setTemplateScaleY(scale_y * scale_factor); int template_pos = map->getFirstFrontTemplate(); map->addTemplate(templ, 0); map->setFirstFrontTemplate(template_pos+1); if (view) { auto opacity = qMax(0.0, qMin(1.0, 0.01 * (100 - dimming))); view->setTemplateVisibility(templ, { float(opacity), visible }); } } void OcdFileImport::importExtras(const OcdFile& file) { const Ocd::FileHeaderV8* header = file.header(); map->setMapNotes(convertOcdString< Ocd::FormatV8::Encoding >(file.byteArray().data() + header->info_pos, header->info_size)); } template< class F > void OcdFileImport::importExtras(const OcdFile< F >& file) { map->setMapNotes({ }); handleStrings(file, extraStringHandlers); } const std::initializer_list OcdFileImport::extraStringHandlers = { { 11, &OcdFileImport::appendNotes }, { 1061, &OcdFileImport::appendNotes } }; void OcdFileImport::appendNotes(const QString& param_string, int ocd_version) { QString notes = map->getMapNotes(); notes.append(param_string); if (ocd_version <= 10) notes.append(QLatin1Char('\n')); map->setMapNotes(notes); } void OcdFileImport::importView(const OcdFile& file) { if (view) { const Ocd::FileHeaderV8* header = file.header(); const Ocd::SetupV8* setup = reinterpret_cast< const Ocd::SetupV8* >(file.byteArray().data() + header->setup_pos); if (setup->zoom >= MapView::zoom_out_limit && setup->zoom <= MapView::zoom_in_limit) view->setZoom(setup->zoom); view->setCenter(convertOcdPoint(setup->center)); } } template< class F > void OcdFileImport::importView(const OcdFile< F >& file) { handleStrings(file, { { 1030, &OcdFileImport::importView } }); } void OcdFileImport::importView(const QString& param_string, int /*ocd_version*/) { const QChar* unicode = param_string.unicode(); bool zoom_ok = false; double zoom=1.0, offset_x=0.0, offset_y=0.0; int i = param_string.indexOf(QLatin1Char('\t'), 0); ; // skip first word for this entry type while (i >= 0) { int next_i = param_string.indexOf(QLatin1Char('\t'), i+1); int len = (next_i > 0 ? next_i : param_string.length()) - i - 2; const QString param_value = QString::fromRawData(unicode+i+2, len); // no copying! switch (param_string[i+1].toLatin1()) { case '\t': // empty item break; case 'x': { offset_x = param_value.toDouble(); break; } case 'y': { offset_y = param_value.toDouble(); break; } case 'z': { zoom = param_value.toDouble(&zoom_ok); break; } default: ; // nothing } i = next_i; } if (view) { view->setCenter(MapCoord(offset_x, -offset_y)); if (zoom_ok) { view->setZoom(zoom); } } } template< class S > void OcdFileImport::setupBaseSymbol(Symbol* symbol, const S& ocd_symbol) { typedef typename S::BaseSymbol BaseSymbol; const BaseSymbol& base_symbol = ocd_symbol.base; // common fields are name, number, description, helper_symbol, hidden/protected status symbol->setName(convertOcdString(base_symbol.description)); symbol->setNumberComponent(0, base_symbol.number / BaseSymbol::symbol_number_factor); symbol->setNumberComponent(1, base_symbol.number % BaseSymbol::symbol_number_factor); symbol->setNumberComponent(2, -1); symbol->setIsHelperSymbol(false); symbol->setProtected(base_symbol.status & Ocd::SymbolProtected); symbol->setHidden(base_symbol.status & Ocd::SymbolHidden); } template< class S > PointSymbol* OcdFileImport::importPointSymbol(const S& ocd_symbol, int ocd_version) { auto symbol = new OcdImportedPointSymbol(); setupBaseSymbol(symbol, ocd_symbol); setupPointSymbolPattern(symbol, ocd_symbol.data_size, ocd_symbol.begin_of_elements, ocd_version); symbol->setRotatable(ocd_symbol.base.flags & 1); return symbol; } template< class S > Symbol* OcdFileImport::importLineSymbol(const S& ocd_symbol, int ocd_version) { using LineStyle = Ocd::LineSymbolCommonV8; OcdImportedLineSymbol* line_for_borders = nullptr; // Import a main line? OcdImportedLineSymbol* main_line = nullptr; if (ocd_symbol.common.double_mode == 0 || ocd_symbol.common.line_width > 0) { main_line = importLineSymbolBase(ocd_symbol.common); setupBaseSymbol(main_line, ocd_symbol); line_for_borders = main_line; } // Import a 'framing' line? OcdImportedLineSymbol* framing_line = nullptr; if (ocd_symbol.common.framing_width > 0 && ocd_version >= 7) { framing_line = importLineSymbolFraming(ocd_symbol.common, main_line); setupBaseSymbol(framing_line, ocd_symbol); if (!line_for_borders) line_for_borders = framing_line; } // Import a 'double' line? bool has_border_line = (ocd_symbol.common.double_mode != 0) && (ocd_symbol.common.double_left_width > 0 || ocd_symbol.common.double_right_width > 0); OcdImportedLineSymbol *double_line = nullptr; if (ocd_symbol.common.double_flags & LineStyle::DoubleFillColorOn || (has_border_line && !line_for_borders) ) { double_line = importLineSymbolDoubleBorder(ocd_symbol.common); setupBaseSymbol(double_line, ocd_symbol); line_for_borders = double_line; } else if (ocd_symbol.common.double_flags & LineStyle::DoubleBackgroundColorOn) { auto symbol = std::unique_ptr(importLineSymbolDoubleBorder(ocd_symbol.common)); addSymbolWarning(symbol.get(), OcdFileImport::tr("Unsupported line style '%1'.").arg(QLatin1String("LineStyle::DoubleBackgroundColorOn")) ); } // Border lines if (has_border_line) { Q_ASSERT(line_for_borders); setupLineSymbolForBorder(line_for_borders, ocd_symbol.common); } // Create point symbols along line; middle ("normal") dash, corners, start, and end. OcdImportedLineSymbol* symbol_line = main_line ? main_line : double_line; // Find the line to attach the symbols to if (!symbol_line) { main_line = new OcdImportedLineSymbol(); symbol_line = main_line; setupBaseSymbol(main_line, ocd_symbol); main_line->segment_length = convertLength(ocd_symbol.common.main_length); main_line->end_length = convertLength(ocd_symbol.common.end_length); } setupLineSymbolPointSymbol(symbol_line, ocd_symbol.common, ocd_symbol.begin_of_elements, ocd_version); // TODO: taper fields (tmode and tlast) if (!main_line && !framing_line) { return double_line; } else if (!double_line && !framing_line) { return main_line; } else if (!main_line && !double_line) { return framing_line; } else { auto full_line = new CombinedSymbol(); setupBaseSymbol(full_line, ocd_symbol); mergeLineSymbol(full_line, main_line, framing_line, double_line); addSymbolWarning(symbol_line, OcdFileImport::tr("This symbol cannot be saved as a proper OCD symbol again.")); return full_line; } } OcdFileImport::OcdImportedLineSymbol* OcdFileImport::importLineSymbolBase(const Ocd::LineSymbolCommonV8& attributes) { using LineStyle = Ocd::LineSymbolCommonV8; // Basic line options auto symbol = new OcdImportedLineSymbol(); symbol->line_width = convertLength(attributes.line_width); symbol->color = convertColor(attributes.line_color); // Cap and join styles switch (attributes.line_style) { default: addSymbolWarning( symbol, tr("Unsupported line style '%1'."). arg(attributes.line_style) ); // fall through case LineStyle::BevelJoin_FlatCap: symbol->join_style = LineSymbol::BevelJoin; symbol->cap_style = LineSymbol::FlatCap; break; case LineStyle::RoundJoin_RoundCap: symbol->join_style = LineSymbol::RoundJoin; symbol->cap_style = LineSymbol::RoundCap; break; case LineStyle::BevelJoin_PointedCap: symbol->join_style = LineSymbol::BevelJoin; symbol->cap_style = LineSymbol::PointedCap; break; case LineStyle::RoundJoin_PointedCap: symbol->join_style = LineSymbol::RoundJoin; symbol->cap_style = LineSymbol::PointedCap; break; case LineStyle::MiterJoin_FlatCap: symbol->join_style = LineSymbol::MiterJoin; symbol->cap_style = LineSymbol::FlatCap; break; case LineStyle::MiterJoin_PointedCap: symbol->join_style = LineSymbol::MiterJoin; symbol->cap_style = LineSymbol::PointedCap; break; } if (symbol->cap_style == LineSymbol::PointedCap) { auto ocd_length = attributes.dist_from_start; if (attributes.dist_from_start != attributes.dist_from_end) { // FIXME: Different lengths for start and end length of pointed line ends are not supported yet, so take the average ocd_length = (attributes.dist_from_start + attributes.dist_from_end) / 2; addSymbolWarning( symbol, tr("Different lengths for pointed caps at begin (%1 mm) and end (%2 mm) are not supported. Using %3 mm."). arg(locale.toString(0.001f * convertLength(attributes.dist_from_start)), locale.toString(0.001f * convertLength(attributes.dist_from_end)), locale.toString(0.001f * convertLength(ocd_length))) ); } symbol->pointed_cap_length = convertLength(ocd_length); symbol->join_style = LineSymbol::RoundJoin; // NOTE: while the setting may be different (see what is set in the first place), OC*D always draws round joins if the line cap is pointed! } // Handle the dash pattern if (attributes.main_gap || attributes.sec_gap) { if (!attributes.main_length) { // Invalid dash pattern addSymbolWarning( symbol, tr("The dash pattern cannot be imported correctly.") ); } else if (attributes.sec_gap && !attributes.main_gap) { // Special case main_gap == 0 symbol->dashed = true; symbol->dash_length = convertLength(attributes.main_length) - convertLength(attributes.sec_gap); symbol->break_length = convertLength(attributes.sec_gap); if (attributes.end_length) { if (qAbs(qint32(attributes.main_length) - 2*attributes.end_length) > 1) { // End length not equal to 0.5 * main length addSymbolWarning( symbol, tr("The dash pattern's end length (%1 mm) cannot be imported correctly. Using %2 mm."). arg(locale.toString(0.001f * convertLength(attributes.end_length)), locale.toString(0.001f * symbol->dash_length)) ); } if (attributes.end_gap) { addSymbolWarning( symbol, tr("The dash pattern's end gap (%1 mm) cannot be imported correctly. Using %2 mm."). arg(locale.toString(0.001f * convertLength(attributes.end_gap)), locale.toString(0.001f * symbol->break_length)) ); } } } else { // Standard case symbol->dashed = true; symbol->dash_length = convertLength(attributes.main_length); symbol->break_length = convertLength(attributes.main_gap); if (attributes.end_length && attributes.end_length != attributes.main_length) { if (attributes.main_length && 0.75 >= double(attributes.end_length) / double(attributes.main_length)) { // End length max. 75 % of main length symbol->half_outer_dashes = true; } if (qAbs(qint32(attributes.main_length) - 2*attributes.end_length) > 1) { // End length not equal to 0.5 * main length addSymbolWarning( symbol, tr("The dash pattern's end length (%1 mm) cannot be imported correctly. Using %2 mm."). arg(locale.toString(0.001f * convertLength(attributes.end_length)), locale.toString(0.001f * (symbol->half_outer_dashes ? (symbol->dash_length/2) : symbol->dash_length))) ); } } if (attributes.sec_gap) { symbol->dashes_in_group = 2; symbol->in_group_break_length = convertLength(attributes.sec_gap); symbol->dash_length = (symbol->dash_length - symbol->in_group_break_length) / 2; if (attributes.end_length && attributes.end_gap != attributes.sec_gap) { addSymbolWarning( symbol, tr("The dash pattern's end gap (%1 mm) cannot be imported correctly. Using %2 mm."). arg(locale.toString(0.001f * convertLength(attributes.end_gap)), locale.toString(0.001f * symbol->in_group_break_length)) ); } } } } else { symbol->segment_length = convertLength(attributes.main_length); symbol->end_length = convertLength(attributes.end_length); } return symbol; } OcdFileImport::OcdImportedLineSymbol* OcdFileImport::importLineSymbolFraming(const Ocd::LineSymbolCommonV8& attributes, const LineSymbol* main_line) { using LineStyle = Ocd::LineSymbolCommonV8; // Basic line options auto framing_line = new OcdImportedLineSymbol(); framing_line->line_width = convertLength(attributes.framing_width); framing_line->color = convertColor(attributes.framing_color); // Cap and join styles switch (attributes.framing_style) { case LineStyle::BevelJoin_FlatCap: framing_line->join_style = LineSymbol::BevelJoin; framing_line->cap_style = LineSymbol::FlatCap; break; case LineStyle::RoundJoin_RoundCap: framing_line->join_style = LineSymbol::RoundJoin; framing_line->cap_style = LineSymbol::RoundCap; break; case LineStyle::MiterJoin_FlatCap: framing_line->join_style = LineSymbol::MiterJoin; framing_line->cap_style = LineSymbol::FlatCap; break; default: addSymbolWarning( main_line, tr("Unsupported framing line style '%1'."). arg(attributes.line_style) ); } return framing_line; } OcdFileImport::OcdImportedLineSymbol* OcdFileImport::importLineSymbolDoubleBorder(const Ocd::LineSymbolCommonV8& attributes) { using LineStyle = Ocd::LineSymbolCommonV8; auto double_line = new OcdImportedLineSymbol(); double_line->line_width = convertLength(attributes.double_width); double_line->cap_style = LineSymbol::FlatCap; double_line->join_style = LineSymbol::MiterJoin; double_line->segment_length = convertLength(attributes.main_length); double_line->end_length = convertLength(attributes.end_length); if (attributes.double_flags & LineStyle::DoubleFillColorOn) double_line->color = convertColor(attributes.double_color); else double_line->color = nullptr; return double_line; } void OcdFileImport::setupLineSymbolForBorder(OcdFileImport::OcdImportedLineSymbol* line_for_borders, const Ocd::LineSymbolCommonV8& attributes) { line_for_borders->have_border_lines = true; LineSymbolBorder& border = line_for_borders->getBorder(); LineSymbolBorder& right_border = line_for_borders->getRightBorder(); // Border color and width border.color = convertColor(attributes.double_left_color); border.width = convertLength(attributes.double_left_width); border.shift = convertLength(attributes.double_left_width) / 2 + (convertLength(attributes.double_width) - line_for_borders->line_width) / 2; right_border.color = convertColor(attributes.double_right_color); right_border.width = convertLength(attributes.double_right_width); right_border.shift = convertLength(attributes.double_right_width) / 2 + (convertLength(attributes.double_width) - line_for_borders->line_width) / 2; // The borders may be dashed if (attributes.double_gap > 0 && attributes.double_mode > 1) { border.dashed = true; border.dash_length = convertLength(attributes.double_length); border.break_length = convertLength(attributes.double_gap); // If ocd_symbol->dmode == 2, only the left border should be dashed if (attributes.double_mode > 2) { right_border.dashed = border.dashed; right_border.dash_length = border.dash_length; right_border.break_length = border.break_length; } } } void OcdFileImport::setupLineSymbolPointSymbol(OcdFileImport::OcdImportedLineSymbol* line_symbol, const Ocd::LineSymbolCommonV8& attributes, const Ocd::PointSymbolElementV8* elements, int ocd_version) { const Ocd::OcdPoint32* coords = reinterpret_cast(elements); line_symbol->mid_symbols_per_spot = attributes.num_prim_sym; line_symbol->mid_symbol_distance = convertLength(attributes.prim_sym_dist); line_symbol->mid_symbol = new OcdImportedPointSymbol(); setupPointSymbolPattern(line_symbol->mid_symbol, attributes.primary_data_size, elements, ocd_version); coords += attributes.primary_data_size; if (attributes.secondary_data_size > 0) { //symbol_line->dash_symbol = importPattern( ocd_symbol->ssnpts, symbolptr); coords += attributes.secondary_data_size; addSymbolWarning(line_symbol, tr("Skipped secondary point symbol.")); } if (attributes.corner_data_size > 0) { line_symbol->dash_symbol = new OcdImportedPointSymbol(); setupPointSymbolPattern(line_symbol->dash_symbol, attributes.corner_data_size, reinterpret_cast(coords), ocd_version); line_symbol->dash_symbol->setName(QCoreApplication::translate("OpenOrienteering::LineSymbolSettings", "Dash symbol")); coords += attributes.corner_data_size; } if (attributes.start_data_size > 0) { line_symbol->start_symbol = new OcdImportedPointSymbol(); setupPointSymbolPattern(line_symbol->start_symbol, attributes.start_data_size, reinterpret_cast(coords), ocd_version); line_symbol->start_symbol->setName(QCoreApplication::translate("OpenOrienteering::LineSymbolSettings", "Start symbol")); coords += attributes.start_data_size; } if (attributes.end_data_size > 0) { line_symbol->end_symbol = new OcdImportedPointSymbol(); setupPointSymbolPattern(line_symbol->end_symbol, attributes.end_data_size, reinterpret_cast(coords), ocd_version); line_symbol->end_symbol->setName(QCoreApplication::translate("OpenOrienteering::LineSymbolSettings", "End symbol")); } // FIXME: not really sure how this translates... need test cases line_symbol->minimum_mid_symbol_count = 0; //1 + ocd_symbol->smin; line_symbol->minimum_mid_symbol_count_when_closed = 0; //1 + ocd_symbol->smin; line_symbol->show_at_least_one_symbol = false; // NOTE: this works in a different way than OC*D's 'at least X symbols' setting (per-segment instead of per-object) // Suppress dash symbol at line ends if both start symbol and end symbol exist, // but don't create a warning unless a dash symbol is actually defined // and the line symbol is not Mapper's 799 Simple orienteering course. if (line_symbol->start_symbol && line_symbol->end_symbol) { line_symbol->setSuppressDashSymbolAtLineEnds(true); if (line_symbol->dash_symbol && line_symbol->getNumberComponent(0) != 799) { addSymbolWarning(line_symbol, tr("Suppressing dash symbol at line ends.")); } } } void OcdFileImport::mergeLineSymbol(CombinedSymbol* full_line, LineSymbol* main_line, LineSymbol* framing_line, LineSymbol* double_line) { full_line->setNumParts(3); // reserve int part = 0; if (main_line) { full_line->setPart(part++, main_line, true); main_line->setHidden(false); main_line->setProtected(false); } if (double_line) { full_line->setPart(part++, double_line, true); double_line->setHidden(false); double_line->setProtected(false); } if (framing_line) { full_line->setPart(part++, framing_line, true); framing_line->setHidden(false); framing_line->setProtected(false); } full_line->setNumParts(part); } Symbol* OcdFileImport::importAreaSymbol(const Ocd::AreaSymbolV8& ocd_symbol, int ocd_version) { Q_ASSERT(ocd_version <= 8); auto symbol = new OcdImportedAreaSymbol(); setupBaseSymbol(symbol, ocd_symbol); setupAreaSymbolCommon( symbol, ocd_symbol.fill_on, ocd_symbol.common, ocd_symbol.data_size, ocd_symbol.begin_of_elements, ocd_version); return symbol; } template< class S > Symbol* OcdFileImport::importAreaSymbol(const S& ocd_symbol, int ocd_version) { Q_ASSERT(ocd_version >= 9); auto symbol = new OcdImportedAreaSymbol(); setupBaseSymbol(symbol, ocd_symbol); setupAreaSymbolCommon( symbol, ocd_symbol.common.fill_on_V9, ocd_symbol.common, ocd_symbol.data_size, ocd_symbol.begin_of_elements, ocd_version); if (!ocd_symbol.common.border_on_V9) { return symbol; } if (ocd_symbol.border_symbol == ocd_symbol.base.number) { addSymbolWarning(symbol, OcdFileImport::tr("The border of this symbol could not be loaded.")); return symbol; } auto combined = new CombinedSymbol(); setupBaseSymbol(combined, ocd_symbol); combined->setNumParts(2); combined->setPart(0, symbol, true); auto border = map->getUndefinedLine()->duplicate(); border->setNumberComponent(0, symbol->getNumberComponent(0)); border->setNumberComponent(1, symbol->getNumberComponent(1)); border->setNumberComponent(2, static_cast(ocd_symbol.border_symbol)); combined->setPart(1, border, true); addSymbolWarning(symbol, OcdFileImport::tr("This symbol cannot be saved as a proper OCD symbol again.")); return combined; } void OcdFileImport::setupAreaSymbolCommon(OcdImportedAreaSymbol* symbol, bool fill_on, const Ocd::AreaSymbolCommonV8& ocd_symbol, std::size_t data_size, const Ocd::PointSymbolElementV8* elements, int ocd_version) { // Basic area symbol fields: minimum_area, color symbol->minimum_area = 0; symbol->color = fill_on ? convertColor(ocd_symbol.fill_color) : nullptr; symbol->patterns.clear(); symbol->patterns.reserve(4); // Hatching if (ocd_symbol.hatch_mode != Ocd::HatchNone) { AreaSymbol::FillPattern pattern; pattern.type = AreaSymbol::FillPattern::LinePattern; pattern.angle = convertAngle(ocd_symbol.hatch_angle_1); pattern.setRotatable(true); pattern.line_spacing = convertLength(ocd_symbol.hatch_dist); pattern.line_offset = 0; pattern.line_color = convertColor(ocd_symbol.hatch_color); pattern.line_width = convertLength(ocd_symbol.hatch_line_width); if (ocd_version <= 8) { pattern.line_spacing += pattern.line_width; } symbol->patterns.push_back(pattern); if (ocd_symbol.hatch_mode == Ocd::HatchCross) { // Second hatch, same as the first, just a different angle pattern.angle = convertAngle(ocd_symbol.hatch_angle_2); symbol->patterns.push_back(pattern); } } if (ocd_symbol.structure_mode != Ocd::StructureNone) { AreaSymbol::FillPattern pattern; pattern.type = AreaSymbol::FillPattern::PointPattern; pattern.angle = convertAngle(ocd_symbol.structure_angle); pattern.setRotatable(true); pattern.point_distance = convertLength(ocd_symbol.structure_width); pattern.line_spacing = convertLength(ocd_symbol.structure_height); pattern.line_offset = 0; pattern.offset_along_line = 0; // FIXME: somebody needs to own this symbol and be responsible for deleting it // Right now it looks like a potential memory leak pattern.point = new OcdImportedPointSymbol(); setupPointSymbolPattern(pattern.point, data_size, elements, ocd_version); // OC*D 8 has a "staggered" pattern mode, where successive rows are shifted width/2 relative // to each other. We need to simulate this in Mapper with two overlapping patterns, each with // twice the height. The second is then offset by width/2, height/2. if (ocd_symbol.structure_mode == Ocd::StructureShiftedRows) { pattern.line_spacing *= 2; symbol->patterns.push_back(pattern); pattern.line_offset = pattern.line_spacing / 2; pattern.offset_along_line = pattern.point_distance / 2; pattern.point = pattern.point->duplicate()->asPoint(); } symbol->patterns.push_back(pattern); } } template< class S > TextSymbol* OcdFileImport::importTextSymbol(const S& ocd_symbol, int /*ocd_version*/) { auto symbol = new OcdImportedTextSymbol(); setupBaseSymbol(symbol, ocd_symbol); setBasicAttributes(symbol, convertOcdString(ocd_symbol.font_name), ocd_symbol.basic); setSpecialAttributes(symbol, ocd_symbol.special); setFraming(symbol, ocd_symbol.framing); return symbol; } template< class S > TextSymbol* OcdFileImport::importLineTextSymbol(const S& ocd_symbol, int /*ocd_version*/) { auto symbol = new OcdImportedTextSymbol(); setupBaseSymbol(symbol, ocd_symbol); setBasicAttributes(symbol, convertOcdString(ocd_symbol.font_name), ocd_symbol.basic); setFraming(symbol, ocd_symbol.framing); addSymbolWarning(symbol, OcdFileImport::tr("Line text symbols are not yet supported. Marking the symbol as hidden.")); symbol->setHidden(true); return symbol; } template< class S > LineSymbol* OcdFileImport::importRectangleSymbol(const S& ocd_symbol) { auto symbol = new OcdImportedLineSymbol(); setupBaseSymbol(symbol, ocd_symbol); symbol->line_width = convertLength(ocd_symbol.line_width); symbol->color = convertColor(ocd_symbol.line_color); symbol->cap_style = LineSymbol::RoundCap; symbol->join_style = LineSymbol::RoundJoin; auto rect = RectangleInfo(); rect.border_line = symbol; rect.corner_radius = 0.001 * convertLength(ocd_symbol.corner_radius); rect.has_grid = ocd_symbol.grid_flags & 1; if (rect.has_grid) { auto inner_line = new OcdImportedLineSymbol(); setupBaseSymbol(inner_line, ocd_symbol); inner_line->setNumberComponent(2, 1); // TODO: Dynamic inner_line->line_width = qRound(1000 * 0.15); inner_line->color = symbol->color; map->addSymbol(inner_line, map->getNumSymbols()); auto text = new OcdImportedTextSymbol(); setupBaseSymbol(text, ocd_symbol); text->setNumberComponent(2, 2); // TODO: Dynamic text->font_family = QString::fromLatin1("Arial"); text->font_size = qRound(1000 * (15 / 72.0 * 25.4)); text->color = symbol->color; text->bold = true; text->updateQFont(); map->addSymbol(text, map->getNumSymbols()); rect.inner_line = inner_line; rect.text = text; rect.number_from_bottom = ocd_symbol.grid_flags & 2; rect.cell_width = 0.001 * convertLength(ocd_symbol.cell_width); rect.cell_height = 0.001 * convertLength(ocd_symbol.cell_height); rect.unnumbered_cells = ocd_symbol.unnumbered_cells; rect.unnumbered_text = convertOcdString(ocd_symbol.unnumbered_text); } rectangle_info.insert(ocd_symbol.base.number, rect); return symbol; } void OcdFileImport::setupPointSymbolPattern(PointSymbol* symbol, std::size_t data_size, const Ocd::PointSymbolElementV8* elements, int version) { Q_ASSERT(symbol); symbol->setRotatable(true); bool base_symbol_used = false; for (std::size_t i = 0; i < data_size; i += 2) { const Ocd::PointSymbolElementV8* element = reinterpret_cast(&reinterpret_cast(elements)[i]); const Ocd::OcdPoint32* const coords = reinterpret_cast(elements) + i + 2; switch (element->type) { case Ocd::PointSymbolElementV8::TypeDot: if (element->diameter > 0) { bool can_use_base_symbol = (!base_symbol_used && (!element->num_coords || (!coords[0].x && !coords[0].y))); PointSymbol* working_symbol = can_use_base_symbol ? symbol : new PointSymbol(); working_symbol->setInnerColor(convertColor(element->color)); working_symbol->setInnerRadius(convertLength(element->diameter) / 2); working_symbol->setOuterColor(nullptr); working_symbol->setOuterWidth(0); if (can_use_base_symbol) { base_symbol_used = true; } else { working_symbol->setRotatable(false); auto element_object = new PointObject(working_symbol); if (element->num_coords) { const MapCoord coord = convertOcdPoint(coords[0]); element_object->setPosition(coord.nativeX(), coord.nativeY()); } symbol->addElement(symbol->getNumElements(), element_object, working_symbol); } } break; case Ocd::PointSymbolElementV8::TypeCircle: { decltype(element->diameter) element_radius = (version <= 8) ? (element->diameter / 2 - element->line_width) : ((element->diameter - element->line_width) / 2); if (element_radius > 0 && element->line_width > 0) { bool can_use_base_symbol = (!base_symbol_used && (!element->num_coords || (!coords[0].x && !coords[0].y))); PointSymbol* working_symbol = can_use_base_symbol ? symbol : new PointSymbol(); working_symbol->setInnerColor(nullptr); working_symbol->setInnerRadius(convertLength(element_radius)); working_symbol->setOuterColor(convertColor(element->color)); working_symbol->setOuterWidth(convertLength(element->line_width)); if (can_use_base_symbol) { base_symbol_used = true; } else { working_symbol->setRotatable(false); auto element_object = new PointObject(working_symbol); if (element->num_coords) { const MapCoord coord = convertOcdPoint(coords[0]); element_object->setPosition(coord.nativeX(), coord.nativeY()); } symbol->addElement(symbol->getNumElements(), element_object, working_symbol); } } break; } case Ocd::PointSymbolElementV8::TypeLine: if (element->line_width > 0) { auto element_symbol = new OcdImportedLineSymbol(); element_symbol->line_width = convertLength(element->line_width); element_symbol->color = convertColor(element->color); // The flags variable doesn't seem to contain individual flags. switch (element->flags) { default: qDebug("Ocd::PointSymbolElementV8: Unknown flags value %d", element->flags); // fall through case Ocd::PointSymbolElementV8::NoFlags: element_symbol->setCapStyle(LineSymbol::FlatCap); element_symbol->setJoinStyle(LineSymbol::BevelJoin); break; case Ocd::PointSymbolElementV8::RoundStyle: element_symbol->setCapStyle(LineSymbol::RoundCap); element_symbol->setJoinStyle(LineSymbol::RoundJoin); break; case Ocd::PointSymbolElementV8::FlatMiterStyle: element_symbol->setCapStyle(LineSymbol::FlatCap); element_symbol->setJoinStyle(LineSymbol::MiterJoin); break; } auto element_object = new OcdImportedPathObject(element_symbol); fillPathCoords(element_object, false, element->num_coords, coords); element_object->recalculateParts(); symbol->addElement(symbol->getNumElements(), element_object, element_symbol); } break; case Ocd::PointSymbolElementV8::TypeArea: { auto element_symbol = new OcdImportedAreaSymbol(); element_symbol->color = convertColor(element->color); auto element_object = new OcdImportedPathObject(element_symbol); fillPathCoords(element_object, true, element->num_coords, coords); element_object->recalculateParts(); symbol->addElement(symbol->getNumElements(), element_object, element_symbol); } break; default: ; // TODO: not-supported warning } i += element->num_coords; } } template< class O > Object* OcdFileImport::importObject(const O& ocd_object, MapPart* part, int ocd_version) { Symbol* symbol = nullptr; if (ocd_object.symbol >= 0) { symbol = symbol_index[ocd_object.symbol]; } if (!symbol) { switch (ocd_object.type) { case 1: symbol = map->getUndefinedPoint(); break; case 2: case 3: symbol = map->getUndefinedLine(); break; case 4: case 5: symbol = map->getUndefinedText(); break; default: addWarning(OcdFileImport::tr("Unable to load object")); qDebug() << "Undefined object type" << ocd_object.type << " for object of symbol" << ocd_object.symbol; return nullptr; } } if (symbol->getType() == Symbol::Line && rectangle_info.contains(ocd_object.symbol)) { Object* object = importRectangleObject(ocd_object, part, rectangle_info[ocd_object.symbol]); if (!object) addWarning(OcdFileImport::tr("Unable to import rectangle object")); return object; } if (symbol->getType() == Symbol::Point) { auto p = new PointObject(); p->setSymbol(symbol, true); // extra properties: rotation auto point_symbol = reinterpret_cast(symbol); if (point_symbol->isRotatable()) { p->setRotation(convertAngle(ocd_object.angle)); } else if (ocd_object.angle != 0) { if (!point_symbol->isSymmetrical()) { point_symbol->setRotatable(true); p->setRotation(convertAngle(ocd_object.angle)); } } const MapCoord pos = convertOcdPoint(ocd_object.coords[0]); p->setPosition(pos.nativeX(), pos.nativeY()); p->setMap(map); return p; } else if (symbol->getType() == Symbol::Text) { auto t = new TextObject(symbol); t->setText(getObjectText(ocd_object, ocd_version)); t->setRotation(convertAngle(ocd_object.angle)); t->setHorizontalAlignment(text_halign_map.value(symbol)); // Vertical alignment is set in fillTextPathCoords(). // Text objects need special path translation if (!fillTextPathCoords(t, reinterpret_cast(symbol), ocd_object.num_items, reinterpret_cast(ocd_object.coords))) { addWarning(OcdFileImport::tr("Not importing text symbol, couldn't figure out path' (npts=%1): %2") .arg(ocd_object.num_items).arg(t->getText())); delete t; return nullptr; } t->setMap(map); return t; } else if (symbol->getType() == Symbol::Line || symbol->getType() == Symbol::Area || symbol->getType() == Symbol::Combined) { auto p = new OcdImportedPathObject(symbol); p->setPatternRotation(convertAngle(ocd_object.angle)); // Normal path fillPathCoords(p, symbol->getType() == Symbol::Area, ocd_object.num_items, reinterpret_cast(ocd_object.coords)); p->recalculateParts(); p->setMap(map); return p; } return nullptr; } QString OcdFileImport::getObjectText(const Ocd::ObjectV8& ocd_object, int ocd_version) const { auto input = ocd_object.coords + ocd_object.num_items; auto maxlen = uint(sizeof(Ocd::OcdPoint32) * ocd_object.num_text); QString object_text; if (ocd_object.unicode && ocd_version >= 8) { object_text = convertOcdString(reinterpret_cast(input), maxlen/2); } else { object_text = convertOcdString(reinterpret_cast(input), maxlen); } // Remove leading "\r\n" if (object_text.startsWith(QLatin1String("\r\n"))) { object_text.remove(0, 2); } return object_text; } template< class O > inline QString OcdFileImport::getObjectText(const O& ocd_object, int /*ocd_version*/) const { auto data = reinterpret_cast(ocd_object.coords + ocd_object.num_items); if (data[0] == QLatin1Char{'\r'} && data[1] == QLatin1Char{'\n'}) data += 2; return QString(data); } template< class O > Object* OcdFileImport::importRectangleObject(const O& ocd_object, MapPart* part, const OcdFileImport::RectangleInfo& rect) { if (ocd_object.num_items != 4) { qDebug() << "importRectangleObject called with num_items =" << ocd_object.num_items << "for object of symbol" << ocd_object.symbol; if (ocd_object.num_items != 5) // 5 coords are handled like 4 coords now return nullptr; } return importRectangleObject(ocd_object.coords, part, rect); } Object* OcdFileImport::importRectangleObject(const Ocd::OcdPoint32* ocd_points, MapPart* part, const OcdFileImport::RectangleInfo& rect) { // Convert corner points MapCoord bottom_left = convertOcdPoint(ocd_points[0]); MapCoord bottom_right = convertOcdPoint(ocd_points[1]); MapCoord top_right = convertOcdPoint(ocd_points[2]); MapCoord top_left = convertOcdPoint(ocd_points[3]); MapCoordF top_left_f = MapCoordF(top_left); MapCoordF top_right_f = MapCoordF(top_right); MapCoordF bottom_left_f = MapCoordF(bottom_left); MapCoordF bottom_right_f = MapCoordF(bottom_right); MapCoordF right = MapCoordF(top_right.x() - top_left.x(), top_right.y() - top_left.y()); auto angle = right.angle(); MapCoordF down = MapCoordF(bottom_left.x() - top_left.x(), bottom_left.y() - top_left.y()); right.normalize(); down.normalize(); // Create border line MapCoordVector coords; if (qIsNull(rect.corner_radius)) { coords.emplace_back(top_left); coords.emplace_back(top_right); coords.emplace_back(bottom_right); coords.emplace_back(bottom_left); } else { double handle_radius = (1 - BEZIER_KAPPA) * rect.corner_radius; coords.emplace_back(top_right_f - right * rect.corner_radius, MapCoord::CurveStart); coords.emplace_back(top_right_f - right * handle_radius); coords.emplace_back(top_right_f + down * handle_radius); coords.emplace_back(top_right_f + down * rect.corner_radius); coords.emplace_back(bottom_right_f - down * rect.corner_radius, MapCoord::CurveStart); coords.emplace_back(bottom_right_f - down * handle_radius); coords.emplace_back(bottom_right_f - right * handle_radius); coords.emplace_back(bottom_right_f - right * rect.corner_radius); coords.emplace_back(bottom_left_f + right * rect.corner_radius, MapCoord::CurveStart); coords.emplace_back(bottom_left_f + right * handle_radius); coords.emplace_back(bottom_left_f - down * handle_radius); coords.emplace_back(bottom_left_f - down * rect.corner_radius); coords.emplace_back(top_left_f + down * rect.corner_radius, MapCoord::CurveStart); coords.emplace_back(top_left_f + down * handle_radius); coords.emplace_back(top_left_f + right * handle_radius); coords.emplace_back(top_left_f + right * rect.corner_radius); } auto border_path = new PathObject(rect.border_line, coords, map); border_path->parts().front().setClosed(true, false); if (rect.has_grid && rect.cell_width > 0 && rect.cell_height > 0) { // Calculate grid sizes auto width = top_left.distanceTo(top_right); auto height = top_left.distanceTo(bottom_left); int num_cells_x = qMax(1, qRound(width / rect.cell_width)); int num_cells_y = qMax(1, qRound(height / rect.cell_height)); auto cell_width = width / num_cells_x; auto cell_height = height / num_cells_y; // Create grid lines coords.resize(2); for (int x = 1; x < num_cells_x; ++x) { coords[0] = MapCoord(top_left_f + x * cell_width * right); coords[1] = MapCoord(bottom_left_f + x * cell_width * right); auto path = new PathObject(rect.inner_line, coords, map); part->addObject(path, part->getNumObjects()); } for (int y = 1; y < num_cells_y; ++y) { coords[0] = MapCoord(top_left_f + y * cell_height * down); coords[1] = MapCoord(top_right_f + y * cell_height * down); auto path = new PathObject(rect.inner_line, coords, map); part->addObject(path, part->getNumObjects()); } // Create grid text if (height >= rect.cell_height / 2) { for (int y = 0; y < num_cells_y; ++y) { for (int x = 0; x < num_cells_x; ++x) { int cell_num; QString cell_text; if (rect.number_from_bottom) cell_num = y * num_cells_x + x + 1; else cell_num = (num_cells_y - 1 - y) * num_cells_x + x + 1; if (cell_num > num_cells_x * num_cells_y - rect.unnumbered_cells) cell_text = rect.unnumbered_text; else cell_text = QString::number(cell_num); auto object = new TextObject(rect.text); object->setMap(map); object->setText(cell_text); object->setRotation(-angle); object->setHorizontalAlignment(TextObject::AlignLeft); object->setVerticalAlignment(TextObject::AlignTop); auto position_x = (x + qreal(0.07)) * cell_width; auto position_y = (y + qreal(0.04)) * cell_height + rect.text->getFontMetrics().ascent() / rect.text->calculateInternalScaling() - rect.text->getFontSize(); object->setAnchorPosition(top_left_f + position_x * right + position_y * down); part->addObject(object, part->getNumObjects()); //pts[0].Y -= rectinfo.gridText.FontAscent - rectinfo.gridText.FontEmHeight; } } } } return border_path; } void OcdFileImport::setPathHolePoint(OcdImportedPathObject *object, quint32 pos) { // Look for curve start points before the current point and apply hole point only if no such point is there. // This prevents hole points in the middle of a curve caused by incorrect map objects. if (pos >= 1 && object->coords[pos].isCurveStart()) ; //object->coords[i-1].setHolePoint(true); else if (pos >= 2 && object->coords[pos-1].isCurveStart()) ; //object->coords[i-2].setHolePoint(true); else if (pos >= 3 && object->coords[pos-2].isCurveStart()) ; //object->coords[i-3].setHolePoint(true); else if (pos > 0) // Don't start with hole point. object->coords[pos].setHolePoint(true); } void OcdFileImport::setPointFlags(OcdImportedPathObject* object, quint32 pos, bool is_area, const Ocd::OcdPoint32& ocd_point) { // We can support CurveStart, HolePoint, DashPoint. // CurveStart needs to be applied to the main point though, not the control point, and // hole points need to bet set as the last point of a part of an area object instead of the first point of the next part if (ocd_point.x & Ocd::OcdPoint32::FlagCtl1 && pos > 0) object->coords[pos-1].setCurveStart(true); if ((ocd_point.y & Ocd::OcdPoint32::FlagDash) || (ocd_point.y & Ocd::OcdPoint32::FlagCorner)) object->coords[pos].setDashPoint(true); if (ocd_point.y & Ocd::OcdPoint32::FlagHole) { if (!is_area) setPathHolePoint(object, pos); else if (pos > 0) setPathHolePoint(object, pos - 1); } } /** Translates the OC*D path given in the last two arguments into an Object. */ void OcdFileImport::fillPathCoords(OcdImportedPathObject *object, bool is_area, quint32 num_points, const Ocd::OcdPoint32* ocd_points) { object->coords.resize(num_points); for (auto i = 0u; i < num_points; i++) { object->coords[i] = convertOcdPoint(ocd_points[i]); setPointFlags(object, i, is_area, ocd_points[i]); } // For path objects, create closed parts where the position of the last point is equal to that of the first point if (object->getType() == Object::Path) { size_t start = 0; for (size_t i = 0; i < object->coords.size(); ++i) { if (!object->coords[i].isHolePoint() && i < object->coords.size() - 1) continue; if (object->coords[i].isPositionEqualTo(object->coords[start])) { MapCoord coord = object->coords[start]; coord.setCurveStart(false); coord.setHolePoint(true); coord.setClosePoint(true); object->coords[i] = coord; } switch (i - start) { default: object->coords[i-2].setCurveStart(false); // fall through case 1: object->coords[i-1].setCurveStart(false); // fall through case 0: ; // nothing } start = i + 1; } } } /** Translates an OCAD text object path into a Mapper text object specifier, if possible. * If successful, sets either 1 or 2 coordinates in the text object and returns true. * If the OCAD path was not importable, leaves the TextObject alone and returns false. */ bool OcdFileImport::fillTextPathCoords(TextObject *object, TextSymbol *symbol, quint32 npts, const Ocd::OcdPoint32 *ocd_points) { // text objects either have 1 point (free anchor) or 2 (midpoint/size) // OCAD appears to always have 5 or 4 points (possible single anchor, then 4 corner coordinates going clockwise from anchor). if (npts == 0) return false; if (npts == 4) { // Box text MapCoord bottom_left = convertOcdPoint(ocd_points[0]); MapCoord top_right = convertOcdPoint(ocd_points[2]); MapCoord top_left = convertOcdPoint(ocd_points[3]); // According to Purple Pen source code: OC*D adds an extra internal leading (incorrectly). QFontMetricsF metrics = symbol->getFontMetrics(); double top_adjust = -symbol->getFontSize() + (metrics.ascent() + metrics.descent() + 0.5) / symbol->calculateInternalScaling(); MapCoordF adjust_vector = MapCoordF(top_adjust * sin(object->getRotation()), top_adjust * cos(object->getRotation())); top_left = MapCoord(top_left.x() + adjust_vector.x(), top_left.y() + adjust_vector.y()); top_right = MapCoord(top_right.x() + adjust_vector.x(), top_right.y() + adjust_vector.y()); object->setBox((bottom_left.nativeX() + top_right.nativeX()) / 2, (bottom_left.nativeY() + top_right.nativeY()) / 2, top_left.distanceTo(top_right), top_left.distanceTo(bottom_left)); object->setVerticalAlignment(TextObject::AlignTop); } else { // Single anchor text if (npts != 5) addWarning(tr("Trying to import a text object with unknown coordinate format")); // anchor point MapCoord coord = convertOcdPoint(ocd_points[0]); object->setAnchorPosition(coord.nativeX(), coord.nativeY()); object->setVerticalAlignment(text_valign_map.value(symbol)); } return true; } void OcdFileImport::setBasicAttributes(OcdFileImport::OcdImportedTextSymbol* symbol, const QString& font_name, const Ocd::BasicTextAttributesV8& attributes) { symbol->font_family = font_name; symbol->color = convertColor(attributes.color); symbol->font_size = qRound(100.0 * attributes.font_size / 72.0 * 25.4); symbol->bold = (attributes.font_weight>= 550); symbol->italic = attributes.font_italic; symbol->underline = false; symbol->kerning = false; symbol->line_below = false; symbol->custom_tabs.resize(0); if (attributes.font_weight != 400 && attributes.font_weight != 700) { addSymbolWarning(symbol, tr("Ignoring custom weight (%1).").arg(attributes.font_weight)); } switch (attributes.alignment & Ocd::HAlignMask) { case Ocd::HAlignLeft: text_halign_map[symbol] = TextObject::AlignLeft; break; case Ocd::HAlignRight: text_halign_map[symbol] = TextObject::AlignRight; break; case Ocd::HAlignJustified: /// \todo Implement justified alignment addSymbolWarning(symbol, tr("Justified alignment is not supported.")); // fall through default: text_halign_map[symbol] = TextObject::AlignHCenter; } switch (attributes.alignment & Ocd::VAlignMask) { case Ocd::VAlignTop: text_valign_map[symbol] = TextObject::AlignTop; break; case Ocd::VAlignMiddle: text_valign_map[symbol] = TextObject::AlignVCenter; break; default: addSymbolWarning(symbol, tr("Vertical alignment '%1' is not supported.").arg(attributes.alignment & Ocd::VAlignMask)); // fall through case Ocd::VAlignBottom: text_valign_map[symbol] = TextObject::AlignBaseline; } if (attributes.char_spacing != 0) { symbol->character_spacing = attributes.char_spacing / 100.0f; addSymbolWarning(symbol, tr("Custom character spacing may be incorrect.")); } if (attributes.word_spacing != 100) { addSymbolWarning(symbol, tr("Ignoring custom word spacing (%1 %).").arg(attributes.word_spacing)); } symbol->updateQFont(); } void OcdFileImport::setSpecialAttributes(OcdFileImport::OcdImportedTextSymbol* symbol, const Ocd::SpecialTextAttributesV8& attributes) { // Convert line spacing double absolute_line_spacing = 0.00001 * symbol->font_size * attributes.line_spacing; symbol->line_spacing = absolute_line_spacing / (symbol->getFontMetrics().lineSpacing() / symbol->calculateInternalScaling()); symbol->paragraph_spacing = convertLength(attributes.para_spacing); symbol->line_below = attributes.line_below_on; symbol->line_below_color = convertColor(attributes.line_below_color); symbol->line_below_width = convertLength(attributes.line_below_width); symbol->line_below_distance = convertLength(attributes.line_below_offset); symbol->custom_tabs.resize(attributes.num_tabs); for (auto i = 0u; i < attributes.num_tabs; ++i) symbol->custom_tabs[i] = convertLength(attributes.tab_pos[i]); if (attributes.indent_first_line != 0 || attributes.indent_other_lines != 0) { addSymbolWarning(symbol, tr("Ignoring custom indents (%1/%2).").arg(attributes.indent_first_line).arg(attributes.indent_other_lines)); } } void OcdFileImport::setFraming(OcdFileImport::OcdImportedTextSymbol* symbol, const Ocd::FramingAttributesV8& framing) { switch (framing.mode) { case Ocd::FramingShadow: symbol->framing = true; symbol->framing_mode = TextSymbol::ShadowFraming; symbol->framing_color = convertColor(framing.color); symbol->framing_shadow_x_offset = convertLength(framing.offset_x); symbol->framing_shadow_y_offset = -1 * convertLength(framing.offset_y); break; case Ocd::FramingLine: // since V7 symbol->framing = true; symbol->framing_mode = TextSymbol::LineFraming; symbol->framing_line_half_width = convertLength(framing.line_width); break; case Ocd::FramingRectangle: default: addSymbolWarning(symbol, tr("Ignoring text framing (mode %1).").arg(framing.mode)); // fall through case Ocd::FramingNone: symbol->framing = false; } } void OcdFileImport::import(bool load_symbols_only) { Q_ASSERT(buffer.isEmpty()); buffer.clear(); buffer.append(stream->readAll()); if (buffer.isEmpty()) throw FileFormatException(::OpenOrienteering::Importer::tr("Could not read file: %1").arg(stream->errorString())); if (size_t(buffer.size()) < sizeof(Ocd::FormatGeneric::FileHeader)) throw FileFormatException(::OpenOrienteering::Importer::tr("Could not read file: %1").arg(tr("Invalid data."))); const OcdFile generic_file(buffer); if (generic_file.header()->vendor_mark != 0x0cad) // This also tests correct endianess... throw FileFormatException(::OpenOrienteering::Importer::tr("Could not read file: %1").arg(tr("Invalid data."))); int version = generic_file.header()->version; switch (version) { case 6: case 7: case 8: // Note: Version 6 and 7 do have some differences, which will need to be // handled in the version 8 implementation by looking up the // actual format version in the file header. if (Settings::getInstance().getSetting(Settings::General_NewOcd8Implementation).toBool()) importImplementation< Ocd::FormatV8 >(load_symbols_only); else importImplementationLegacy(load_symbols_only); break; case 9: case 10: using FormatV10Assumption = std::is_same; Q_STATIC_ASSERT(FormatV10Assumption::value); importImplementation< Ocd::FormatV9 >(load_symbols_only); break; case 11: importImplementation< Ocd::FormatV11 >(load_symbols_only); break; case 12: importImplementation< Ocd::FormatV12 >(load_symbols_only); break; default: throw FileFormatException( ::OpenOrienteering::Importer::tr("Could not read file: %1"). arg(tr("OCD files of version %1 are not supported!").arg(version)) ); } } void OcdFileImport::finishImport() { if (delegate) { // The current warnings and actions are already propagated. auto warnings_size = ptrdiff_t(delegate->warnings().size()); auto actions_size = ptrdiff_t(delegate->actions().size()); delegate->finishImport(); // Propagate new warnings and actions from the delegate to this importer. std::for_each(begin(delegate->warnings()) + warnings_size, end(delegate->warnings()), [this](const QString& w) { addWarning(w); }); std::for_each(begin(delegate->actions()) + actions_size, end(delegate->actions()), [this](const ImportAction& a) { addAction(a); }); } } template< class F > void OcdFileImport::handleStrings(const OcdFile& file, std::initializer_list handlers) { for (const auto& string : file.strings()) { for (const auto& handler : handlers) { if (string.type == handler.type) { (this->*handler.callback)(convertOcdString(file[string]), file.header()->version); } } } } } // namespace OpenOrienteering mapper-0.8.1.1/src/fileformats/ocd_file_import.h000066400000000000000000000326751325266516600216050ustar00rootroot00000000000000/* * Copyright 2013-2016 Kai Pastor * * Some parts taken from file_format_oc*d8{.h,_p.h,cpp} which are * Copyright 2012 Pete Curtis * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_OCD_FILE_IMPORT #define OPENORIENTEERING_OCD_FILE_IMPORT #include #include #include #include #include #include #include #include #include #include #include #include #include "core/map_coord.h" #include "core/objects/object.h" #include "core/objects/text_object.h" #include "core/symbols/area_symbol.h" #include "core/symbols/line_symbol.h" #include "core/symbols/point_symbol.h" #include "core/symbols/text_symbol.h" #include "fileformats/file_import_export.h" #include "fileformats/ocd_types.h" #include "fileformats/ocd_types_v8.h" // IWYU pragma: keep class QChar; class QIODevice; namespace OpenOrienteering { class CombinedSymbol; class Georeferencing; class Map; class MapColor; class MapPart; class MapView; class OCAD8FileImport; class Symbol; /** * An map file importer for OC*D files. */ class OcdFileImport : public Importer { Q_DECLARE_TR_FUNCTIONS(OpenOrienteering::OcdFileImport) protected: /// Information about an OC*D rectangle symbol struct RectangleInfo { LineSymbol* border_line; double corner_radius; bool has_grid; // Only valid if has_grid is true LineSymbol* inner_line; TextSymbol* text; bool number_from_bottom; double cell_width; double cell_height; int unnumbered_cells; QString unnumbered_text; }; // Helper classes that provide to core classes' protected members class OcdImportedAreaSymbol : public AreaSymbol { friend class OcdFileImport; }; class OcdImportedLineSymbol : public LineSymbol { friend class OcdFileImport; }; class OcdImportedPointSymbol : public PointSymbol { friend class OcdFileImport; }; class OcdImportedTextSymbol : public TextSymbol { friend class OcdFileImport; }; class OcdImportedPathObject : public PathObject { friend class OcdFileImport; public: OcdImportedPathObject(Symbol* symbol = nullptr) : PathObject(symbol) { } OcdImportedPathObject(const OcdImportedPathObject&) = delete; OcdImportedPathObject(OcdImportedPathObject&&) = delete; OcdImportedPathObject& operator=(const OcdImportedPathObject&) = delete; OcdImportedPathObject& operator=(OcdImportedPathObject&&) = delete; ~OcdImportedPathObject() override; }; public: OcdFileImport(QIODevice* stream, Map *map, MapView *view); ~OcdFileImport() override; void setCustom8BitEncoding(QTextCodec* encoding); template< std::size_t N > QString convertOcdString(const Ocd::PascalString& src) const; template< std::size_t N > QString convertOcdString(const Ocd::Utf8PascalString& src) const; template< std::size_t N > QString convertOcdString(const Ocd::Utf16PascalString& src) const; template< class E > QString convertOcdString(const char* src, uint len) const; template< class E > QString convertOcdString(const QByteArray& data) const; QString convertOcdString(const QChar* src, uint maxlen) const; MapCoord convertOcdPoint(const Ocd::OcdPoint32& ocd_point) const; float convertAngle(int ocd_angle) const; int convertLength(qint16 ocd_length) const; int convertLength(quint16 ocd_length) const; template< class T, class R = qint64 > R convertLength(T ocd_length) const; MapColor* convertColor(int ocd_color); void addSymbolWarning(const AreaSymbol* symbol, const QString& warning); void addSymbolWarning(const LineSymbol* symbol, const QString& warning); void addSymbolWarning(const TextSymbol* symbol, const QString& warning); void finishImport() override; protected: void import(bool load_symbols_only) override; void importImplementationLegacy(bool load_symbols_only); template< class F > void importImplementation(bool load_symbols_only); struct StringHandler { using Callback = void (OcdFileImport::*)(const QString&, int); qint32 type; Callback callback; }; template< class F > void handleStrings(const OcdFile< F >& file, std::initializer_list handlers); void importGeoreferencing(const OcdFile& file); template< class F > void importGeoreferencing(const OcdFile< F >& file); /// Imports string 1039. void importGeoreferencing(const QString& param_string, int ocd_version); /// Imports string 1039 field i. void applyGridAndZone(Georeferencing& georef, const QString& combined_grid_zone); void importColors(const OcdFile& file); template< class F > void importColors(const OcdFile< F >& file); void importColor(const QString& param_string, int ocd_version); template< class F > void importSymbols(const OcdFile< F >& file); void resolveSubsymbols(); void importObjects(const OcdFile& file); template< class F > void importObjects(const OcdFile< F >& file); template< class F > void importTemplates(const OcdFile< F >& file); void importTemplate(const QString& param_string, int ocd_version); void importExtras(const OcdFile& file); template< class F > void importExtras(const OcdFile< F >& file); static const std::initializer_list extraStringHandlers; void appendNotes(const QString& param_string, int ocd_version); void importView(const OcdFile& file); template< class F > void importView(const OcdFile< F >& file); void importView(const QString& param_string, int ocd_version); // Symbol import template< class S > PointSymbol* importPointSymbol(const S& ocd_symbol, int ocd_version); template< class S > Symbol* importLineSymbol(const S& ocd_symbol, int ocd_version); OcdImportedLineSymbol* importLineSymbolBase(const Ocd::LineSymbolCommonV8& attributes); OcdImportedLineSymbol* importLineSymbolFraming(const Ocd::LineSymbolCommonV8& attributes, const LineSymbol* main_line); OcdImportedLineSymbol* importLineSymbolDoubleBorder(const Ocd::LineSymbolCommonV8& attributes); void setupLineSymbolForBorder(OcdImportedLineSymbol* line_for_borders, const Ocd::LineSymbolCommonV8& attributes); void setupLineSymbolPointSymbol(OcdImportedLineSymbol* line_symbol, const Ocd::LineSymbolCommonV8& attributes, const Ocd::PointSymbolElementV8* elements, int ocd_version); void mergeLineSymbol(CombinedSymbol* full_line, LineSymbol* main_line, LineSymbol* framing_line, LineSymbol* double_line); Symbol* importAreaSymbol(const Ocd::AreaSymbolV8& ocd_symbol, int ocd_version); template< class S > Symbol* importAreaSymbol(const S& ocd_symbol, int ocd_version); void setupAreaSymbolCommon( OcdImportedAreaSymbol* symbol, bool fill_on, const Ocd::AreaSymbolCommonV8& ocd_symbol, std::size_t data_size, const Ocd::PointSymbolElementV8* elements, int ocd_version ); template< class S > TextSymbol* importTextSymbol(const S& ocd_symbol, int ocd_version); template< class S > TextSymbol* importLineTextSymbol(const S& ocd_symbol, int ocd_version); template< class S > LineSymbol* importRectangleSymbol(const S& ocd_symbol); template< class S > void setupBaseSymbol(Symbol* symbol, const S& ocd_symbol); void setupPointSymbolPattern(PointSymbol* symbol, std::size_t data_size, const Ocd::PointSymbolElementV8* elements, int version); // Object import template< class O > Object* importObject(const O& ocd_object, MapPart* part, int ocd_version); QString getObjectText(const Ocd::ObjectV8& ocd_object, int ocd_version) const; template< class O > QString getObjectText(const O& ocd_object, int ocd_version) const; template< class O > Object* importRectangleObject(const O& ocd_object, MapPart* part, const OcdFileImport::RectangleInfo& rect); Object* importRectangleObject(const Ocd::OcdPoint32* ocd_points, MapPart* part, const OcdFileImport::RectangleInfo& rect); // Some helper functions that are used in multiple places void setPointFlags(OcdImportedPathObject* object, quint32 pos, bool is_area, const Ocd::OcdPoint32& ocd_point); void setPathHolePoint(OcdFileImport::OcdImportedPathObject* object, quint32 pos); void fillPathCoords(OcdFileImport::OcdImportedPathObject* object, bool is_area, quint32 num_points, const Ocd::OcdPoint32* ocd_points); bool fillTextPathCoords(TextObject* object, TextSymbol* symbol, quint32 npts, const Ocd::OcdPoint32* ocd_points); void setBasicAttributes(OcdImportedTextSymbol* symbol, const QString& font_name, const Ocd::BasicTextAttributesV8& attributes); void setSpecialAttributes(OcdImportedTextSymbol* symbol, const Ocd::SpecialTextAttributesV8& attributes); void setFraming(OcdImportedTextSymbol* symbol, const Ocd::FramingAttributesV8& framing); protected: /// The locale is used for number formatting. QLocale locale; QByteArray buffer; QScopedPointer< OCAD8FileImport > delegate; /// Character encoding to use for 1-byte (narrow) strings QTextCodec *custom_8bit_encoding; /// maps OCD color number to oo-mapper color object QHash color_index; /// maps OCD symbol number to oo-mapper symbol object QHash symbol_index; /// maps OO Mapper text symbol pointer to OCD defined horizontal alignment (stored in objects instead of symbols in OO Mapper) QHash text_halign_map; /// maps OO Mapper text symbol pointer to OCD defined vertical alignment (stored in objects instead of symbols in OO Mapper) QHash text_valign_map; /// maps OCD symbol number to rectangle information struct QHash rectangle_info; }; // ### OcdFileImport inline code ### template< std::size_t N > QString OcdFileImport::convertOcdString(const Ocd::PascalString& src) const { return custom_8bit_encoding->toUnicode(src.data, src.length); } template< std::size_t N > QString OcdFileImport::convertOcdString(const Ocd::Utf8PascalString& src) const { return QString::fromUtf8(src.data, src.length); } template< std::size_t N > QString OcdFileImport::convertOcdString(const Ocd::Utf16PascalString& src) const { Q_STATIC_ASSERT(N <= std::numeric_limits::max() / 2); return convertOcdString(src.data, N); } template< > inline QString OcdFileImport::convertOcdString< Ocd::Custom8BitEncoding >(const char* src, uint len) const { len = qMin(uint(std::numeric_limits::max()), qstrnlen(src, len)); return custom_8bit_encoding->toUnicode(src, int(len)); } template< > inline QString OcdFileImport::convertOcdString< Ocd::Utf8Encoding >(const char* src, uint len) const { len = qMin(uint(std::numeric_limits::max()), qstrnlen(src, len)); return QString::fromUtf8(src, int(len)); } template< class E > QString OcdFileImport::convertOcdString(const QByteArray& data) const { return OcdFileImport::convertOcdString< E >(data.constData(), data.length()); } inline MapCoord OcdFileImport::convertOcdPoint(const Ocd::OcdPoint32& ocd_point) const { qint32 ocad_x = ocd_point.x >> 8; qint32 ocad_y = ocd_point.y >> 8; // Recover from broken coordinate export from Mapper 0.6.2 ... 0.6.4 (#749) // Cf. broken::convertPointMember in file_format_ocad8.cpp: // The values -4 ... -1 (-0.004 mm ... -0.001 mm) were converted to 0x80000000u instead of 0. // This is the maximum value. Thus it is okay to assume it won't occur in regular data, // and we can safely replace it with 0 here. // But the input parameter were already subject to right shift ... constexpr auto invalid_value = qint32(0x80000000u) >> 8; // ... so we use this value here. if (ocad_x == invalid_value) ocad_x = 0; if (ocad_y == invalid_value) ocad_y = 0; return MapCoord::fromNative(ocad_x * 10, ocad_y * -10); } inline float OcdFileImport::convertAngle(int ocd_angle) const { // OC*D uses tenths of a degree, counterclockwise // BUG: if sin(rotation) is < 0 for a hatched area pattern, the pattern's createRenderables() will go into an infinite loop. // So until that's fixed, we keep a between 0 and PI return (M_PI / 1800) * ((ocd_angle + 3600) % 3600); } inline int OcdFileImport::convertLength(qint16 ocd_length) const { return convertLength(ocd_length); } inline int OcdFileImport::convertLength(quint16 ocd_length) const { return convertLength(ocd_length); } template< class T, class R > inline R OcdFileImport::convertLength(T ocd_length) const { // OC*D uses hundredths of a millimeter. // oo-mapper uses 1/1000 mm return static_cast(ocd_length) * 10; } inline MapColor *OcdFileImport::convertColor(int ocd_color) { if (!color_index.contains(ocd_color)) { addWarning(tr("Color id not found: %1, ignoring this color").arg(ocd_color)); return nullptr; } return color_index[ocd_color]; } } // namespace OpenOrienteering #endif // OPENORIENTEERING_OCD_FILE_IMPORT mapper-0.8.1.1/src/fileformats/ocd_types.cpp000066400000000000000000000027251325266516600207640ustar00rootroot00000000000000/* * Copyright 2013, 2016 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "ocd_types.h" #include "ocd_types_v8.h" #include "ocd_types_v9.h" #include "ocd_types_v10.h" #include "ocd_types_v11.h" #include "ocd_types_v12.h" namespace Ocd { // Verify at compile time that a double is 8 bytes big. Q_STATIC_ASSERT(sizeof(double) == 8); // Verify at compile time that data structures are packed, not aligned. Q_STATIC_ASSERT(sizeof(FileHeaderGeneric) == 8); Q_STATIC_ASSERT(sizeof(FormatV8::FileHeader) - sizeof(SymbolHeaderV8) == 48); Q_STATIC_ASSERT(sizeof(FormatV9::FileHeader) == 48); Q_STATIC_ASSERT(sizeof(FormatV10::FileHeader) == 48); Q_STATIC_ASSERT(sizeof(FormatV11::FileHeader) == 48); Q_STATIC_ASSERT(sizeof(FormatV12::FileHeader) == 60); } mapper-0.8.1.1/src/fileformats/ocd_types.h000066400000000000000000000457421325266516600204370ustar00rootroot00000000000000/* * Copyright 2013, 2015, 2016 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_OCD_TYPES_H #define OPENORIENTEERING_OCD_TYPES_H #include #include #include #include // Helper macro #define RESERVED_MEMBER_CONCAT2(A,B) A ## B // Helper macro #define RESERVED_MEMBER_CONCAT(A,B) RESERVED_MEMBER_CONCAT2(A,B) /** * A macro which generates a unique member name of the format * reserved_member_LINE_NUMBER. */ #define RESERVED_MEMBER RESERVED_MEMBER_CONCAT( reserved_member_ , __LINE__ ) namespace Ocd { /** OCD filetypes */ enum FileType { Map = 0x0000001, CourseSetting = 0x0000002 }; // This pragma should be supported by msvc, gcc, clang [-fms-compatibility] #pragma pack(push, 1) /** * A trait for strings and file formats that use a custom 8 bit encoding. */ struct Custom8BitEncoding { // nothing }; /** * A trait for strings and file formats that use UTF-8 encoding. */ struct Utf8Encoding { // nothing }; /** * A string of max. N characters with a pascal-style binary representation: * the first byte indicates the length, * the following N bytes contain the actual character data. */ template< std::size_t N > struct PascalString { unsigned char length; char data[N]; }; /** * A UTF-8-encoded string of max. N characters with a pascal-style binary representation: * the first byte indicates the length, * the following N bytes contain the actual character data. */ template< std::size_t N > struct Utf8PascalString { unsigned char length; char data[N]; }; /** * A UTF-16LE-encoded string of max. N characters, zero-terminated. */ template< std::size_t N > struct Utf16PascalString { QChar data[N]; }; /** * The generic header at the beginning of all supported OCD file formats. * * For implementation efficiency, this header is generalized in comparison * to the upstream documentation: * * - Until V8, the format actually used a 16 bit file type field called * "section mark", but no file status field. * - Until V9, the format actually used a 16 bit subversion field, but no * subsubversion field. */ struct FileHeaderGeneric { quint16 vendor_mark; quint8 file_type; /// aka "section mark" until V8 quint8 file_status_V9; /// \since V9 quint16 version; quint8 subversion; quint8 subsubversion_V10; /// \since V10 }; /** * An IndexBlock collects 256 index entries, and the file position of the * next index block if more index entries exist. */ template< class E > struct IndexBlock { typedef E IndexEntryType; quint32 next_block; IndexEntryType entries[256]; }; /** * An index entry for a parameter string. */ struct ParameterStringIndexEntry { quint32 pos; quint32 size; qint32 type; quint32 obj_index; }; /** * The parameter string trait. * * OCD strings are raw data, so this is more a trait rather than an actual * structure. */ struct ParameterString { typedef ParameterStringIndexEntry IndexEntryType; }; /** * The OCD file point data type. * * The coordinates are not raw 32 bit signed integers but contain flags * in the lowest 8 bits. */ struct OcdPoint32 { qint32 x; qint32 y; // Flags in x coordinate enum XFlags { FlagCtl1 = 0x01, FlagCtl2 = 0x02, FlagLeft = 0x04 }; // Flags in Y coordinate enum YFlags { FlagCorner = 0x01, FlagHole = 0x02, FlagRight = 0x04, FlagDash = 0x08 }; }; #pragma pack(pop) /** * Symbol type values. */ enum SymbolType { SymbolTypePoint = 1, SymbolTypeLine = 2, SymbolTypeArea = 3, SymbolTypeText = 4, SymbolTypeRectangle_V8 = 5, /// Until V8 SymbolTypeLineText = 6, /// \since V9 SymbolTypeRectangle_V9 = 7 /// \since V9 }; /** * Status flags for symbols. */ enum SymbolStatus { SymbolNormal = 0, SymbolProtected = 1, SymbolHidden = 2 }; /** * Status values for objects. */ enum ObjectStatus { ObjectDeleted = 0, ObjectNormal = 1, ObjectHidden = 2, ObjectDeletedForUndo = 3 }; /** * Text alignment flags. */ enum TextAlignment { HAlignMask = 0x03, HAlignLeft = 0x00, HAlignCenter = 0x01, HAlignRight = 0x02, HAlignJustified = 0x03, /// All-line for line text symbols VAlignMask = 0x0c, /// \since V10 VAlignBottom = 0x00, /// \since V10 VAlignMiddle = 0x04, /// \since V10 VAlignTop = 0x08 /// \since V10 }; /** * Area hatch mode values. */ enum HatchMode { HatchNone = 0, HatchSingle = 1, HatchCross = 2 }; /** * Area pattern structure values. */ enum StructureMode { StructureNone = 0, StructureAlignedRows = 1, StructureShiftedRows = 2 }; /** * */ enum FramingMode { FramingNone = 0, FramingShadow = 1, /// Somehow different in older versions FramingLine = 2, /// \since V7 FramingRectangle = 3 /// \since V8; Not for line text symbols }; /** * A generic OCD file format trait. * * It is suitable for detecting the actual format. */ struct FormatGeneric { static constexpr int version() { return -1; } typedef FileHeaderGeneric FileHeader; struct BaseSymbol { typedef quint32 IndexEntryType; }; struct Object { typedef quint32 IndexEntryType; }; }; } // Forward declaration, needed for class FirstIndexBlock. template< class F > class OcdFile; /** * A template class which provides an operator() returning the first index * block for a particular OCD entity index. * * The generic template is not meant to be used but will trigger an error. * Specializations must be provided for entity types to be supported. * * @param F: the type defining the file format version type * @param T: the entity type (string, symbol, object) */ template< class F, class T > class FirstIndexBlock { public: quint32 operator()(const OcdFile* file) const; }; /** * A template class which provides an operator() returning the first string * index block. * * @param F: the type defining the file format version type */ template< class F > class FirstIndexBlock { public: quint32 operator()(const OcdFile* file) const; }; /** * A template class which provides an operator() returning the first symbol * index block. * * @param F: the type defining the file format version type */ template< class F > class FirstIndexBlock { public: quint32 operator()(const OcdFile* file) const; }; /** * A template class which provides an operator() returning the first object * index block. * * @param F: the type defining the file format version type */ template< class F > class FirstIndexBlock { public: quint32 operator()(const OcdFile* file) const; }; /** * A template class which provides an iterator for OCD entity indices. * * @param F: the type defining the file format version type * @param T: the entity type (string, symbol, object) * @param E: the index entry type */ template< class F, class T, class E > class OcdEntityIndexIterator { public: typedef F FileFormat; typedef T EntityType; typedef E EntryType; typedef Ocd::IndexBlock IndexBlock; OcdEntityIndexIterator(const OcdFile* file, const IndexBlock* first_block); const OcdEntityIndexIterator& operator++(); const E& operator*() const; const E* operator->() const; bool operator==(const OcdEntityIndexIterator& rhs) const; bool operator!=(const OcdEntityIndexIterator& rhs) const; private: const OcdFile* data; const IndexBlock* block; std::size_t index; }; /** * A template class which provides an iterator for OCD entity indices, * specialized for the case where the index entry is just the quint32 file * position of the entity data. * * @param F: the type defining the file format version type * @param T: the entity type (string, symbol, object) */ template< class F, class T > class OcdEntityIndexIterator { public: typedef F FileFormat; typedef T EntityType; typedef quint32 EntryType; typedef Ocd::IndexBlock IndexBlock; OcdEntityIndexIterator(const OcdFile* file, const IndexBlock* first_block); const OcdEntityIndexIterator& operator++(); const T& operator*() const; const T* operator->() const; bool operator==(const OcdEntityIndexIterator& rhs) const; bool operator!=(const OcdEntityIndexIterator& rhs) const; private: const OcdFile* data; const IndexBlock* block; std::size_t index; }; /** * A template class for dealing with OCD entity indices. * * Instances of this data do not actually copy any data, but rather provide * an STL container like interface for raw data. The interfaces allows for * forward iterating over the index for the given entity type. * * @param F: the type defining the file format version type * @param T: the entity type (string, symbol, object) */ template< class F, class T > class OcdEntityIndex { public: /** The actual file format version type, reexported. */ typedef F FileFormat; /** The actual entity type. */ typedef T EntityType; /** The index entry type for the entity type. */ typedef typename T::IndexEntryType EntryType; /** The index iterator type. */ typedef OcdEntityIndexIterator iterator; /** * Constructs an entity index object. * * You must call setData() before using the container interface. */ OcdEntityIndex(); /** * Destroys the object. */ ~OcdEntityIndex(); /** * Sets the raw file data to which the object provides access. * * The data is not copied and must not be deleted as long as the index * and its iterators are in use. */ void setData(const OcdFile* file); /** * Returns a forward iterator to the beginning. */ iterator begin() const; /** * Returns a forward iterator to the end. */ iterator end() const; private: const OcdFile* data; }; /** * A template class for dealing with OCD files. * * @param F: the type defining the actual file format version */ template< class F > class OcdFile { public: /** The actual file format version type, reexported. */ typedef F Format; /** The actual file header type. */ typedef typename F::FileHeader FileHeader; /** The actual string index type. */ typedef OcdEntityIndex< F, Ocd::ParameterString > StringIndex; /** The actual symbol index type. */ typedef OcdEntityIndex< F, typename F::BaseSymbol > SymbolIndex; /** The actual object index type. */ typedef OcdEntityIndex< F, typename F::Object > ObjectIndex; /** * Constructs a new object for the file contents given by data. * * We try to avoid copying the data by using the implicit sharing provided * by QByteArray. */ OcdFile(const QByteArray& data); /** * Destructs the object. */ ~OcdFile() {} /** * Returns the raw data. */ const QByteArray& byteArray() const; /** * Returns a const reference to the byte specified by file_pos. */ const char& operator[](quint32 file_pos) const; /** * Returns a pointer to the file header. */ const FileHeader* header() const; /** * Returns a const reference to the parameter string index. */ const StringIndex& strings() const; /** * Returns the raw data of the string. */ const QByteArray operator[](const typename StringIndex::EntryType& string) const; /** * Returns a const reference to the symbol index. */ const SymbolIndex& symbols() const; /** * Returns a const reference to the object index. */ const ObjectIndex& objects() const; /** * Returns a const referenc to the object referenced by an object index iterator. */ const typename F::Object& operator[](const typename ObjectIndex::EntryType& object_entry) const; private: QByteArray byte_array; StringIndex string_index; SymbolIndex symbol_index; ObjectIndex object_index; }; // ### FirstIndexBlock implementation ### // Unknown entity type T: Return 0. template< class F, class T > quint32 FirstIndexBlock::operator()(const OcdFile* file) const { Q_UNUSED(file); T* valid_entity_type = nullptr; Q_ASSERT(valid_entity_type); return 0; } template< class F > quint32 FirstIndexBlock::operator()(const OcdFile* file) const { return (file->header()->version < 8) ? 0 : file->header()->first_string_block; } template< class F > quint32 FirstIndexBlock::operator()(const OcdFile* file) const { return file->header()->first_symbol_block; } template< class F > quint32 FirstIndexBlock::operator()(const OcdFile* file) const { return file->header()->first_object_block; } // ### OcdEntityIndexIterator implementation ### template< class F, class T, class E > OcdEntityIndexIterator::OcdEntityIndexIterator(const OcdFile* file, const IndexBlock* first_block) : data(nullptr) , block(nullptr) , index(0) { if (file && first_block) { data = file; block = first_block; if (data && block->entries[index].pos == 0) this->operator++(); } } template< class F, class T, class E > const OcdEntityIndexIterator& OcdEntityIndexIterator::operator++() { if (data) { do { ++index; if (index == 256) { index = 0; quint32 next_block = block->next_block; if (next_block == 0) { block = nullptr; data = nullptr; } else if (Q_UNLIKELY(next_block >= (unsigned int)data->byteArray().size())) { qWarning("OcdEntityIndexIterator: Next index block is out of bounds"); block = nullptr; data = nullptr; } else { block = reinterpret_cast(&(*data)[next_block]); } } } while (block && !block->entries[index].pos); } return *this; } template< class F, class T, class E > inline const E& OcdEntityIndexIterator::operator*() const { return block->entries[index]; } template< class F, class T, class E > inline const E* OcdEntityIndexIterator::operator->() const { return &(block->entries[index]); } template< class F, class T, class E > inline bool OcdEntityIndexIterator::operator==(const OcdEntityIndexIterator& rhs) const { return (data == rhs.data && block == rhs.block && index == rhs.index); } template< class F, class T, class E > inline bool OcdEntityIndexIterator::operator!=(const OcdEntityIndexIterator& rhs) const { return (data != rhs.data || block != rhs.block || index != rhs.index); } // ### OcdEntityIndexIterator specialization ### template< class F, class T > OcdEntityIndexIterator::OcdEntityIndexIterator(const OcdFile* file, const IndexBlock* first_block) : data(nullptr), block(nullptr), index(0) { if (file && first_block) { data = file; block = first_block; if (data && block->entries[index] == 0) this->operator++(); } } template< class F, class T > const OcdEntityIndexIterator& OcdEntityIndexIterator::operator++() { if (data) { do { ++index; if (index == 256) { index = 0; quint32 next_block = block->next_block; if (next_block) { block = reinterpret_cast(&(*data)[next_block]); } else { block = nullptr; data = nullptr; } } } while ( data && !block->entries[index] ); } return *this; } template< class F, class T > inline const T& OcdEntityIndexIterator::operator*() const { return reinterpret_cast((*data)[block->entries[index]]); } template< class F, class T > inline const T* OcdEntityIndexIterator::operator->() const { return reinterpret_cast(&(*data)[block->entries[index]]); } template< class F, class T > inline bool OcdEntityIndexIterator::operator==(const OcdEntityIndexIterator& rhs) const { return (data == rhs.data && block == rhs.block && index == rhs.index); } template< class F, class T > inline bool OcdEntityIndexIterator::operator!=(const OcdEntityIndexIterator& rhs) const { return (data != rhs.data || block != rhs.block || index != rhs.index); } // ### OcdEntityIndex implementation ### template< class F, class T > OcdEntityIndex::OcdEntityIndex() : data(nullptr) { } template< class F, class T > OcdEntityIndex::~OcdEntityIndex() { } template< class F, class T > void OcdEntityIndex::setData(const OcdFile< F >* file) { data = file; } template< class F, class T > typename OcdEntityIndex::iterator OcdEntityIndex::begin() const { Q_ASSERT(data); quint32 file_pos = FirstIndexBlock()(data); iterator it(data, reinterpret_cast(file_pos ? &(*data)[file_pos] : nullptr)); return it; } template< class F, class T > typename OcdEntityIndex::iterator OcdEntityIndex::end() const { static iterator it(nullptr, nullptr); return it; } // ### OcdFile implementation ### template< class F > inline OcdFile::OcdFile(const QByteArray& data) { byte_array = data; Q_ASSERT(byte_array.constData() == data.constData()); // No deep copy string_index.setData(this); symbol_index.setData(this); object_index.setData(this); } template< class F > inline const QByteArray& OcdFile::byteArray() const { return byte_array; } template< class F > inline const char& OcdFile::operator[](quint32 file_pos) const { return *(byte_array.constData() + file_pos); } template< class F > inline const typename F::FileHeader* OcdFile::header() const { return (byte_array.size() < int(sizeof(FileHeader))) ? nullptr : reinterpret_cast(byte_array.constData()); } template< class F > inline const typename OcdFile::StringIndex& OcdFile::strings() const { return string_index; } template< class F > inline const QByteArray OcdFile::operator[](const typename OcdFile::StringIndex::EntryType& string) const { return QByteArray::fromRawData(&(*this)[string.pos], string.size); } template< class F > inline const typename OcdFile::SymbolIndex& OcdFile::symbols() const { return symbol_index; } template< class F > inline const typename OcdFile::ObjectIndex& OcdFile::objects() const { return object_index; } template< class F > inline const typename F::Object& OcdFile::operator[](const typename OcdFile::ObjectIndex::EntryType& object_entry) const { return reinterpret_cast((*this)[object_entry.pos]); } #endif // OPENORIENTEERING_OCD_TYPES_H mapper-0.8.1.1/src/fileformats/ocd_types_v10.h000066400000000000000000000017671325266516600211240ustar00rootroot00000000000000/* * Copyright 2013, 2015, 2016 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_OCD_TYPES_V10 #define OPENORIENTEERING_OCD_TYPES_V10 #include "ocd_types.h" #include "ocd_types_v9.h" namespace Ocd { using FormatV10 = FormatV9; } #endif // OPENORIENTEERING_OCD_TYPES_V10_H mapper-0.8.1.1/src/fileformats/ocd_types_v11.h000066400000000000000000000071701325266516600211170ustar00rootroot00000000000000/* * Copyright 2013, 2015, 2016 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_OCD_TYPES_V11_H #define OPENORIENTEERING_OCD_TYPES_V11_H #include "ocd_types.h" #include "ocd_types_v10.h" namespace Ocd { #pragma pack(push, 1) struct BaseSymbolV11 { using IndexEntryType = quint32; static const int symbol_number_factor = 1000; quint32 size; quint32 number; quint8 type; quint8 flags; quint8 selected; quint8 status; quint8 tool; quint8 cs_mode; quint8 cs_type; quint8 cd_flags; qint32 extent; quint32 file_pos; quint8 RESERVED_MEMBER[2]; quint16 num_colors; quint16 colors[14]; Utf16PascalString<64> description; quint8 icon_bits[484]; quint16 group[64]; }; struct PointSymbolV11 { using BaseSymbol = BaseSymbolV11; using Element = FormatV10::PointSymbol::Element; BaseSymbol base; quint16 data_size; quint16 RESERVED_MEMBER; Element begin_of_elements[1]; }; struct LineSymbolV11 { using BaseSymbol = BaseSymbolV11; using Element = FormatV10::LineSymbol::Element; BaseSymbol base; LineSymbolCommonV8 common; Element begin_of_elements[1]; }; struct AreaSymbolV11 { using BaseSymbol = BaseSymbolV11; using Element = FormatV10::AreaSymbol::Element; BaseSymbol base; quint32 border_symbol; AreaSymbolCommonV8 common; quint16 RESERVED_MEMBER; quint16 data_size; Element begin_of_elements[1]; }; struct TextSymbolV11 { using BaseSymbol = BaseSymbolV11; BaseSymbol base; Utf8PascalString<31> font_name; BasicTextAttributesV8 basic; SpecialTextAttributesV8 special; quint16 RESERVED_MEMBER; FramingAttributesV8 framing; }; struct LineTextSymbolV11 { using BaseSymbol = BaseSymbolV11; BaseSymbol base; Utf8PascalString<31> font_name; BasicTextAttributesV8 basic; FramingAttributesV8 framing; }; struct RectangleSymbolV11 { using BaseSymbol = BaseSymbolV11; BaseSymbol base; quint16 line_color; quint16 line_width; quint16 corner_radius; quint16 grid_flags; quint16 cell_width; quint16 cell_height; quint16 RESERVED_MEMBER[2]; quint16 unnumbered_cells; Utf8PascalString<3> unnumbered_text; quint16 line_style; Utf8PascalString<31> RESERVED_MEMBER; quint16 RESERVED_MEMBER; quint16 font_size_V10; /// \since V10 quint16 RESERVED_MEMBER[4]; }; #pragma pack(pop) /** OCD file format version 11 trait. */ struct FormatV11 { using FileHeader = FormatV10::FileHeader; using BaseSymbol = BaseSymbolV11; using PointSymbol = PointSymbolV11; using LineSymbol = LineSymbolV11; using AreaSymbol = AreaSymbolV11; using TextSymbol = TextSymbolV11; using LineTextSymbol = LineTextSymbolV11; using RectangleSymbol = RectangleSymbolV11; using Object = FormatV10::Object; using Encoding = Utf8Encoding; }; } #endif // OPENORIENTEERING_OCD_TYPES_V11_H mapper-0.8.1.1/src/fileformats/ocd_types_v12.h000066400000000000000000000050601325266516600211140ustar00rootroot00000000000000/* * Copyright 2013, 2015, 2016 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_OCD_TYPES_V12_H #define OPENORIENTEERING_OCD_TYPES_V12_H #include "ocd_types.h" #include "ocd_types_v11.h" namespace Ocd { #pragma pack(push, 1) struct FileHeaderV12 : public FormatV11::FileHeader { quint32 RESERVED_MEMBER[2]; quint32 first_multi_rep_block; }; struct AreaSymbolV12 { using BaseSymbol = FormatV11::BaseSymbol; using Element = FormatV11::PointSymbol::Element; BaseSymbol base; quint32 border_symbol; AreaSymbolCommonV8 common; quint8 structure_variation_x; quint8 structure_variation_y; quint16 structure_minimum_dist; quint16 RESERVED_MEMBER; quint16 data_size; Element begin_of_elements[1]; }; struct ObjectV12 { using IndexEntryType = FormatV11::Object::IndexEntryType; quint32 symbol; quint8 type; quint8 customer; qint16 angle; qint32 color; quint16 line_width; quint16 diam_flags; quint32 server_object_id; quint32 height; quint64 creation_date; quint32 multi_rep_id; quint64 modification_date; quint32 num_items; quint16 num_text; quint16 object_string_length; quint16 db_link_length; quint8 object_string_type; quint8 RESERVED_MEMBER; OcdPoint32 coords[1]; }; #pragma pack(pop) /** OCD file format version 11 trait. */ struct FormatV12 { using FileHeader = FileHeaderV12; using BaseSymbol = FormatV11::BaseSymbol; using PointSymbol = FormatV11::PointSymbol; using LineSymbol = FormatV11::LineSymbol; using AreaSymbol = AreaSymbolV12; using TextSymbol = FormatV11::TextSymbol; using LineTextSymbol = FormatV11::LineTextSymbol; using RectangleSymbol = FormatV11::RectangleSymbol; using Object = ObjectV12; using Encoding = Utf8Encoding; }; } #endif // OPENORIENTEERING_OCD_TYPES_V12_H mapper-0.8.1.1/src/fileformats/ocd_types_v8.h000066400000000000000000000234561325266516600210520ustar00rootroot00000000000000/* * Copyright 2013, 2015, 2016 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_OCD_TYPES_V8_H #define OPENORIENTEERING_OCD_TYPES_V8_H #include "ocd_types.h" namespace Ocd { #pragma pack(push, 1) struct CmykV8 { quint8 cyan; quint8 magenta; quint8 yellow; quint8 black; }; struct ColorInfoV8 { quint16 number; quint16 RESERVED_MEMBER; CmykV8 cmyk; PascalString<31> name; quint8 separations[32]; }; struct SeparationInfoV8 { PascalString<15> name; CmykV8 cmyk; quint16 raster_freq; quint16 raster_angle; }; struct SymbolHeaderV8 { quint16 num_colors; quint16 num_separations; quint16 cyan_freq; quint16 cyan_angle; quint16 magenta_freq; quint16 magenta_angle; quint16 yellow_freq; quint16 yellow_angle; quint16 black_freq; quint16 black_angle; quint16 RESERVED_MEMBER[2]; ColorInfoV8 color_info[256]; SeparationInfoV8 separation_info[32]; }; struct FileHeaderV8 : public FileHeaderGeneric { quint32 first_symbol_block; quint32 first_object_block; quint32 setup_pos; quint32 setup_size; quint32 info_pos; quint32 info_size; quint32 first_string_block; quint32 RESERVED_MEMBER[3]; SymbolHeaderV8 symbol_header; }; struct BaseSymbolV8 { using IndexEntryType = quint32; static const int symbol_number_factor = 10; quint16 size; quint16 number; quint16 type; quint8 type2; quint8 flags; quint16 extent; quint8 selected; quint8 status; quint16 RESERVED_MEMBER[2]; qint32 file_pos; quint8 colors[32]; PascalString<31> description; quint8 icon_bits[264]; }; struct PointSymbolElementV8 { quint16 type; quint16 flags; quint16 color; qint16 line_width; qint16 diameter; quint16 num_coords; quint32 RESERVED_MEMBER; enum PointSymbolElementTypes { TypeLine = 1, TypeArea = 2, TypeCircle = 3, TypeDot = 4 }; enum Flags { NoFlags = 0, RoundStyle = 1, FlatMiterStyle = 4, }; }; struct PointSymbolV8 { using BaseSymbol = BaseSymbolV8; using Element = PointSymbolElementV8; BaseSymbol base; quint16 data_size; quint16 RESERVED_MEMBER; Element begin_of_elements[1]; }; struct LineSymbolCommonV8 { quint16 line_color; quint16 line_width; quint16 line_style; qint16 dist_from_start; qint16 dist_from_end; qint16 main_length; qint16 end_length; qint16 main_gap; qint16 sec_gap; qint16 end_gap; qint16 min_sym; qint16 num_prim_sym; qint16 prim_sym_dist; quint16 double_mode; quint16 double_flags; quint16 double_color; quint16 double_left_color; quint16 double_right_color; qint16 double_width; qint16 double_left_width; qint16 double_right_width; qint16 double_length; qint16 double_gap; quint16 double_background_color_V11; /// \since V11 quint16 RESERVED_MEMBER[2]; quint16 dec_mode; quint16 dec_last; quint16 RESERVED_MEMBER; quint16 framing_color; qint16 framing_width; quint16 framing_style; quint16 primary_data_size; quint16 secondary_data_size; quint16 corner_data_size; quint16 start_data_size; quint16 end_data_size; quint8 active_symbols_V11; /// \since V11 quint8 RESERVED_MEMBER; enum LineStyleFlag { BevelJoin_FlatCap = 0, RoundJoin_RoundCap = 1, BevelJoin_PointedCap = 2, RoundJoin_PointedCap = 3, MiterJoin_FlatCap = 4, MiterJoin_PointedCap = 6 }; enum DoubleLineFlag { DoubleFillColorOn = 1, DoubleBackgroundColorOn = 2 }; }; struct LineSymbolV8 { using BaseSymbol = BaseSymbolV8; using Element = PointSymbolElementV8; BaseSymbol base; LineSymbolCommonV8 common; Element begin_of_elements[1]; }; struct AreaSymbolCommonV8 { quint16 fill_color; quint16 hatch_mode; quint16 hatch_color; quint16 hatch_line_width; quint16 hatch_dist; qint16 hatch_angle_1; qint16 hatch_angle_2; quint8 fill_on_V9; /// \since V9 quint8 border_on_V9; /// \since V9 quint8 structure_mode; quint8 structure_draw_V12; /// \since V12 quint16 structure_width; quint16 structure_height; qint16 structure_angle; }; struct AreaSymbolV8 { using BaseSymbol = BaseSymbolV8; using Element = PointSymbolElementV8; BaseSymbol base; quint16 area_flags; quint16 fill_on; AreaSymbolCommonV8 common; quint16 RESERVED_MEMBER; quint16 data_size; Element begin_of_elements[1]; }; struct BasicTextAttributesV8 { quint16 color; quint16 font_size; quint16 font_weight; quint8 font_italic; quint8 charset_V8_ONLY; /// V8 text symbols only quint16 char_spacing; quint16 word_spacing; quint16 alignment; }; struct SpecialTextAttributesV8 { quint16 line_spacing; qint16 para_spacing; quint16 indent_first_line; quint16 indent_other_lines; quint16 num_tabs; quint32 tab_pos[32]; quint16 line_below_on; quint16 line_below_color; quint16 line_below_width; quint16 line_below_offset; }; struct FramingAttributesV8 { quint8 mode; /// 16 bit in V8 quint8 line_style_V9; /// \since V9 quint8 point_symbol_on_V10; /// \since V10 quint32 point_symbol_number_V10; /// \since V10 char RESERVED_MEMBER[19]; quint16 border_left_V9; /// \since V9; TextSymbol only quint16 border_bottom_V9; /// \since V9; TextSymbol only quint16 border_right_V9; /// \since V9; TextSymbol only quint16 border_top_V9; /// \since V9; TextSymbol only quint16 color; quint16 line_width; quint16 font_weight; /// TextSymbol only quint16 italic; /// TextSymbol only quint16 offset_x; quint16 offset_y; }; struct TextSymbolV8 { using BaseSymbol = BaseSymbolV8; BaseSymbol base; PascalString<31> font_name; BasicTextAttributesV8 basic; SpecialTextAttributesV8 special; quint16 RESERVED_MEMBER; FramingAttributesV8 framing; }; struct LineTextSymbolV8 /// \todo use and test... { using BaseSymbol = BaseSymbolV8; BaseSymbol base; PascalString<31> font_name; BasicTextAttributesV8 basic; FramingAttributesV8 framing; }; struct RectangleSymbolV8 { using BaseSymbol = BaseSymbolV8; BaseSymbol base; quint16 line_color; quint16 line_width; quint16 corner_radius; quint16 grid_flags; quint16 cell_width; quint16 cell_height; quint16 RESERVED_MEMBER[2]; quint16 unnumbered_cells; PascalString<3> unnumbered_text; quint16 RESERVED_MEMBER; PascalString<31> RESERVED_MEMBER; quint16 RESERVED_MEMBER[6]; }; struct ObjectIndexEntryV8 { OcdPoint32 bottom_left_bound; OcdPoint32 top_right_bound; quint32 pos; quint16 size_MISC; /// Different interpretation for version < 8 qint16 symbol; }; struct ObjectV8 { using IndexEntryType = ObjectIndexEntryV8; quint16 symbol; quint8 type; quint8 unicode; quint16 num_items; quint16 num_text; qint16 angle; quint16 RESERVED_MEMBER; quint32 RESERVED_MEMBER; PascalString<15> reserved; OcdPoint32 coords[1]; }; typedef double PascalDouble; struct GpsPointV8 { OcdPoint32 map_point; PascalDouble lat; PascalDouble lon; PascalString<15> name; }; struct PrintSetupV8 { OcdPoint32 bottom_left; OcdPoint32 top_right; quint16 grid_enabled; qint16 grid_color; qint16 overlap_x; qint16 overlap_y; PascalDouble scale; quint16 intensity; quint16 line_width; quint16 RESERVED_MEMBER[4]; }; struct ExportSetupV8 { OcdPoint32 bottom_left; OcdPoint32 top_right; }; struct ZoomRectV8 { PascalDouble zoom; OcdPoint32 center; }; struct SetupV8 { OcdPoint32 center; PascalDouble grid_dist; quint16 work_mode; quint16 line_mode; quint16 edit_mode; qint16 selected_symbol; PascalDouble map_scale; PascalDouble real_offset_x; PascalDouble real_offset_y; PascalDouble real_angle; PascalDouble real_grid; PascalDouble gps_angle; GpsPointV8 gps_adjustment[12]; quint32 num_gps_adjustments; PascalDouble RESERVED_MEMBER[2]; OcdPoint32 RESERVED_MEMBER; char RESERVED_MEMBER[256]; quint16 RESERVED_MEMBER[2]; PascalDouble RESERVED_MEMBER; OcdPoint32 RESERVED_MEMBER; PascalDouble RESERVED_MEMBER; PrintSetupV8 print_setup; ExportSetupV8 export_setup; PascalDouble zoom; ZoomRectV8 zoom_history[8]; quint32 zoom_history_size; // V6 header ends here, but Mapper doesn't use the following fields. quint16 real_coords_IGNORED; char filename_IGNORED[256]; quint16 hatch_areas_IGNORED; quint16 dim_templates_IGNORED; quint16 hide_templates_IGNORED; quint16 template_mode_IGNORED; qint16 template_color_IGNORED; }; #pragma pack(pop) /** OCD file format version 8 trait. */ struct FormatV8 { using FileHeader = FileHeaderV8; using BaseSymbol = BaseSymbolV8; using PointSymbol = PointSymbolV8; using LineSymbol = LineSymbolV8; using AreaSymbol = AreaSymbolV8; using TextSymbol = TextSymbolV8; using LineTextSymbol = LineTextSymbolV8; using RectangleSymbol = RectangleSymbolV8; using Object = ObjectV8; using Encoding = Custom8BitEncoding; }; } #endif // OPENORIENTEERING_OCD_TYPES_V8_H mapper-0.8.1.1/src/fileformats/ocd_types_v9.h000066400000000000000000000117741325266516600210530ustar00rootroot00000000000000/* * Copyright 2013, 2015, 2016 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_OCD_TYPES_V9_H #define OPENORIENTEERING_OCD_TYPES_V9_H #include "ocd_types.h" #include "ocd_types_v8.h" namespace Ocd { #pragma pack(push, 1) struct FileHeaderV9 : public FileHeaderGeneric { quint32 first_symbol_block; quint32 first_object_block; quint32 offline_sync_serial_V11; /// \since V11 quint32 current_file_version_V12; /// \since V12 quint32 RESERVED_MEMBER[2]; quint32 first_string_block; quint32 file_name_pos; quint32 file_name_size; quint32 RESERVED_MEMBER; }; struct BaseSymbolV9 { using IndexEntryType = quint32; static const int symbol_number_factor = 1000; quint32 size; quint32 number; quint8 type; quint8 flags; quint8 selected; quint8 status; quint8 tool; quint8 cs_mode; quint8 cs_type; quint8 cd_flags; qint32 extent; qint32 file_pos; quint16 group; quint16 num_colors; quint16 colors[14]; PascalString<31> description; quint8 icon_bits[484]; }; struct PointSymbolV9 { using BaseSymbol = BaseSymbolV9; using Element = FormatV8::PointSymbol::Element; BaseSymbol base; quint16 data_size; quint16 RESERVED_MEMBER; Element begin_of_elements[1]; }; struct LineSymbolV9 { using BaseSymbol = BaseSymbolV9; using Element = FormatV8::LineSymbol::Element; BaseSymbol base; LineSymbolCommonV8 common; Element begin_of_elements[1]; }; struct AreaSymbolV9 { using BaseSymbol = BaseSymbolV9; using Element = FormatV8::AreaSymbol::Element; BaseSymbol base; quint32 border_symbol; AreaSymbolCommonV8 common; quint16 RESERVED_MEMBER; quint16 data_size; Element begin_of_elements[1]; }; struct TextSymbolV9 { using BaseSymbol = BaseSymbolV9; BaseSymbol base; PascalString<31> font_name; BasicTextAttributesV8 basic; SpecialTextAttributesV8 special; quint16 RESERVED_MEMBER; FramingAttributesV8 framing; }; struct LineTextSymbolV9 { using BaseSymbol = BaseSymbolV9; BaseSymbol base; PascalString<31> font_name; BasicTextAttributesV8 basic; FramingAttributesV8 framing; }; struct RectangleSymbolV9 { using BaseSymbol = BaseSymbolV9; BaseSymbol base; quint16 line_color; quint16 line_width; quint16 corner_radius; quint16 grid_flags; quint16 cell_width; quint16 cell_height; quint16 RESERVED_MEMBER[2]; quint16 unnumbered_cells; PascalString<3> unnumbered_text; quint16 RESERVED_MEMBER; PascalString<31> RESERVED_MEMBER; quint16 RESERVED_MEMBER; quint16 font_size_V10; /// \since V10 quint16 RESERVED_MEMBER[4]; }; struct ObjectIndexEntryV9 { OcdPoint32 bottom_left_bound; OcdPoint32 top_right_bound; quint32 pos; quint32 size; qint32 symbol; quint8 type; quint8 encryption_mode_V11; /// \since V11 quint8 status; quint8 view_type; quint16 color; quint16 group_V11; /// \since V11 quint16 layer; quint8 layout_font_V11_ONLY; /// only in V11 quint8 RESERVED_MEMBER; }; struct ObjectV9 { using IndexEntryType = ObjectIndexEntryV9; quint32 symbol; quint8 type; quint8 customer_V11; /// \since V11 qint16 angle; quint32 num_items; quint16 num_text; quint8 mark_V11; /// \since V11 quint8 snapping_mark_V11; /// \since V11 quint32 color; quint16 line_width; quint16 diam_flags; // The usage of the following 16 bytes has changed significantly in the // versions 9 to 12. This is an abstraction, capturing what seems most // relevant. quint32 RESERVED_MEMBER; /// V11: Server object ID quint32 height_V11; /// \since V11; unit: 1/256 mm quint32 RESERVED_MEMBER; quint32 height_V10_ONLY; /// V10 only; unit: mm OcdPoint32 coords[1]; }; #pragma pack(pop) /** OCD file format version 9 trait. */ struct FormatV9 { using FileHeader = FileHeaderV9; using BaseSymbol = BaseSymbolV9; using PointSymbol = PointSymbolV9; using LineSymbol = LineSymbolV9; using AreaSymbol = AreaSymbolV9; using TextSymbol = TextSymbolV9; using LineTextSymbol = LineTextSymbolV9; using RectangleSymbol = RectangleSymbolV9; using Object = ObjectV9; using Encoding = Custom8BitEncoding; }; } #endif // OPENORIENTEERING_OCD_TYPES_V9_H mapper-0.8.1.1/src/fileformats/xml_file_format.cpp000066400000000000000000000755461325266516600221550ustar00rootroot00000000000000/* * Copyright 2012 Pete Curtis * Copyright 2012, 2013 Thomas Schöps * Copyright 2012-2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "xml_file_format.h" #include "xml_file_format_p.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "settings.h" #include "core/georeferencing.h" #include "core/map.h" #include "core/map_color.h" #include "core/map_coord.h" #include "core/map_grid.h" #include "core/map_part.h" #include "core/map_printer.h" // IWYU pragma: keep #include "core/map_view.h" #include "core/symbols/line_symbol.h" #include "core/symbols/point_symbol.h" #include "core/symbols/symbol.h" #include "fileformats/file_import_export.h" #include "templates/template.h" #include "undo/undo_manager.h" #include "util/xml_stream_util.h" namespace OpenOrienteering { // ### XMLFileFormat definition ### constexpr int XMLFileFormat::minimum_version = 2; constexpr int XMLFileFormat::current_version = 7; int XMLFileFormat::active_version = 5; // updated by XMLFileExporter::doExport() namespace { const char* magic_string = "= len && qstrncmp(reinterpret_cast(buffer), magic_string, len) == 0); } Importer *XMLFileFormat::createImporter(QIODevice* stream, Map *map, MapView *view) const { return new XMLFileImporter(stream, map, view); } Exporter *XMLFileFormat::createExporter(QIODevice* stream, Map *map, MapView *view) const { return new XMLFileExporter(stream, map, view); } // ### A namespace which collects various string constants of type QLatin1String. ### namespace literal { static const QLatin1String map("map"); static const QLatin1String version("version"); static const QLatin1String notes("notes"); static const QLatin1String barrier("barrier"); static const QLatin1String required("required"); static const QLatin1String count("count"); static const QLatin1String current("current"); static const QLatin1String georeferencing("georeferencing"); static const QLatin1String colors("colors"); static const QLatin1String color("color"); static const QLatin1String priority("priority"); static const QLatin1String name("name"); static const QLatin1String method("method"); static const QLatin1String spotcolor("spotcolor"); static const QLatin1String cmyk("cmyk"); static const QLatin1String rgb("rgb"); static const QLatin1String custom("custom"); static const QLatin1String spotcolors("spotcolors"); static const QLatin1String knockout("knockout"); static const QLatin1String namedcolor("namedcolor"); static const QLatin1String component("component"); static const QLatin1String factor("factor"); static const QLatin1String c("c"); static const QLatin1String m("m"); static const QLatin1String y("y"); static const QLatin1String k("k"); static const QLatin1String r("r"); static const QLatin1String g("g"); static const QLatin1String b("b"); static const QLatin1String opacity("opacity"); static const QLatin1String symbols("symbols"); static const QLatin1String id("id"); static const QLatin1String symbol("symbol"); static const QLatin1String parts("parts"); static const QLatin1String part("part"); static const QLatin1String templates("templates"); static const QLatin1String template_string("template"); static const QLatin1String first_front_template("first_front_template"); static const QLatin1String defaults("defaults"); static const QLatin1String use_meters_per_pixel("use_meters_per_pixel"); static const QLatin1String meters_per_pixel("meters_per_pixel"); static const QLatin1String dpi("dpi"); static const QLatin1String scale("scale"); static const QLatin1String view("view"); static const QLatin1String area_hatching_enabled("area_hatching_enabled"); static const QLatin1String baseline_view_enabled("baseline_view_enabled"); static const QLatin1String grid("grid"); static const QLatin1String map_view("map_view"); static const QLatin1String print("print"); static const QLatin1String undo("undo"); static const QLatin1String redo("redo"); } // ### XMLFileExporter definition ### XMLFileExporter::XMLFileExporter(QIODevice* stream, Map *map, MapView *view) : Exporter(stream, map, view), xml(stream) { // Determine auto-formatting default from filename, if possible. auto file = qobject_cast(stream); bool auto_formatting = (file && file->fileName().contains(QLatin1String(".xmap"))); setOption(QString::fromLatin1("autoFormatting"), auto_formatting); } void XMLFileExporter::doExport() { if (option(QString::fromLatin1("autoFormatting")).toBool()) xml.setAutoFormatting(true); #ifdef MAPPER_ENABLE_COMPATIBILITY int current_version = XMLFileFormat::current_version; bool retain_compatibility = Settings::getInstance().getSetting(Settings::General_RetainCompatiblity).toBool(); XMLFileFormat::active_version = retain_compatibility ? 5 : current_version; if (XMLFileFormat::active_version < 6 && map->getNumParts() != 1) { throw FileFormatException(tr("Older versions of Mapper do not support multiple map parts. To save the map in compatibility mode, you must first merge all map parts.")); } #else XMLFileFormat::active_version = XMLFileFormat::current_version; #endif xml.writeDefaultNamespace(mapperNamespace()); xml.writeStartDocument(); writeLineBreak(xml); { XmlElementWriter map_element(xml, literal::map); map_element.writeAttribute(literal::version, XMLFileFormat::active_version); writeLineBreak(xml); xml.writeTextElement(literal::notes, map->getMapNotes()); writeLineBreak(xml); exportGeoreferencing(); exportColors(); writeLineBreak(xml); XmlElementWriter* barrier = nullptr; if (XMLFileFormat::active_version >= 6) { // Prevent Mapper versions < 0.6.0 from crashing // when compatibilty mode is NOT activated // Incompatible feature: dense coordinates barrier = new XmlElementWriter(xml, literal::barrier); barrier->writeAttribute(literal::version, 6); barrier->writeAttribute(literal::required, "0.6.0"); writeLineBreak(xml); } exportSymbols(); writeLineBreak(xml); exportMapParts(); writeLineBreak(xml); exportTemplates(); writeLineBreak(xml); exportView(); writeLineBreak(xml); exportPrint(); delete barrier; writeLineBreak(xml); if (Settings::getInstance().getSetting(Settings::General_SaveUndoRedo).toBool()) { { // Prevent Mapper versions < 0.6.0 from crashing // when compatibilty mode IS activated // Incompatible feature: new undo step types XmlElementWriter barrier(xml, literal::barrier); barrier.writeAttribute(literal::version, 6); barrier.writeAttribute(literal::required, "0.6.0"); writeLineBreak(xml); exportUndo(); exportRedo(); } writeLineBreak(xml); } } xml.writeEndDocument(); } void XMLFileExporter::exportGeoreferencing() { map->getGeoreferencing().save(xml); writeLineBreak(xml); } void XMLFileExporter::exportColors() { XmlElementWriter all_colors_element(xml, literal::colors); std::size_t num_colors = map->color_set->colors.size(); all_colors_element.writeAttribute(literal::count, num_colors); for (std::size_t i = 0; i < num_colors; ++i) { writeLineBreak(xml); MapColor* color = map->color_set->colors[i]; const MapColorCmyk &cmyk = color->getCmyk(); XmlElementWriter color_element(xml, literal::color); color_element.writeAttribute(literal::priority, color->getPriority()); color_element.writeAttribute(literal::name, color->getName()); color_element.writeAttribute(literal::c, cmyk.c, 3); color_element.writeAttribute(literal::m, cmyk.m, 3); color_element.writeAttribute(literal::y, cmyk.y, 3); color_element.writeAttribute(literal::k, cmyk.k, 3); color_element.writeAttribute(literal::opacity, color->getOpacity(), 3); if (color->getSpotColorMethod() != MapColor::UndefinedMethod) { XmlElementWriter spotcolors_element(xml, literal::spotcolors); spotcolors_element.writeAttribute(literal::knockout, color->getKnockout()); SpotColorComponent component; switch(color->getSpotColorMethod()) { case MapColor::SpotColor: xml.writeTextElement(literal::namedcolor, color->getSpotColorName()); break; case MapColor::CustomColor: for (auto&& component : color->getComponents()) { XmlElementWriter component_element(xml, literal::component); component_element.writeAttribute(literal::factor, component.factor); component_element.writeAttribute(literal::spotcolor, component.spot_color->getPriority()); } break; default: ; // nothing } } { XmlElementWriter cmyk_element(xml, literal::cmyk); switch(color->getCmykColorMethod()) { case MapColor::SpotColor: cmyk_element.writeAttribute(literal::method, literal::spotcolor); break; case MapColor::RgbColor: cmyk_element.writeAttribute(literal::method, literal::rgb); break; default: cmyk_element.writeAttribute(literal::method, literal::custom); } } { XmlElementWriter rgb_element(xml, literal::rgb); switch(color->getRgbColorMethod()) { case MapColor::SpotColor: rgb_element.writeAttribute(literal::method, literal::spotcolor); break; case MapColor::CmykColor: rgb_element.writeAttribute(literal::method, literal::cmyk); break; default: rgb_element.writeAttribute(literal::method, literal::custom); } const MapColorRgb &rgb = color->getRgb(); rgb_element.writeAttribute(literal::r, rgb.r, 3); rgb_element.writeAttribute(literal::g, rgb.g, 3); rgb_element.writeAttribute(literal::b, rgb.b, 3); } } writeLineBreak(xml); } void XMLFileExporter::exportSymbols() { XmlElementWriter symbols_element(xml, literal::symbols); auto id = map->symbolSetId(); int num_symbols = map->getNumSymbols(); symbols_element.writeAttribute(literal::count, num_symbols); if (!id.isEmpty()) symbols_element.writeAttribute(literal::id, id); for (int i = 0; i < num_symbols; ++i) { writeLineBreak(xml); map->getSymbol(i)->save(xml, *map); } writeLineBreak(xml); } void XMLFileExporter::exportMapParts() { XmlElementWriter parts_element(xml, literal::parts); auto num_parts = std::size_t(map->getNumParts()); parts_element.writeAttribute(literal::count, num_parts); parts_element.writeAttribute(literal::current, map->current_part_index); for (auto i = 0u; i < num_parts; ++i) { writeLineBreak(xml); map->getPart(i)->save(xml); } writeLineBreak(xml); } void XMLFileExporter::exportTemplates() { // Update the relative paths of templates if (auto file = qobject_cast(stream)) { auto filename = file->fileName(); auto map_dir = QFileInfo(filename).absoluteDir(); if (!filename.isEmpty() && map_dir.exists()) { for (int i = 0; i < map->getNumTemplates(); ++i) { auto temp = map->getTemplate(i); if (temp->getTemplateState() != Template::Invalid) temp->setTemplateRelativePath(map_dir.relativeFilePath(temp->getTemplatePath())); } for (int i = 0; i < map->getNumClosedTemplates(); ++i) { auto temp = map->getClosedTemplate(i); if (temp->getTemplateState() != Template::Invalid) temp->setTemplateRelativePath(map_dir.relativeFilePath(temp->getTemplatePath())); } } } XmlElementWriter templates_element(xml, literal::templates); int num_templates = map->getNumTemplates() + map->getNumClosedTemplates(); templates_element.writeAttribute(literal::count, num_templates); templates_element.writeAttribute(literal::first_front_template, map->first_front_template); for (int i = 0; i < map->getNumTemplates(); ++i) { writeLineBreak(xml); map->getTemplate(i)->saveTemplateConfiguration(xml, true); } for (int i = 0; i < map->getNumClosedTemplates(); ++i) { writeLineBreak(xml); map->getClosedTemplate(i)->saveTemplateConfiguration(xml, false); } { writeLineBreak(xml); XmlElementWriter defaults_element(xml, literal::defaults); defaults_element.writeAttribute(literal::use_meters_per_pixel, map->image_template_use_meters_per_pixel); defaults_element.writeAttribute(literal::meters_per_pixel, map->image_template_meters_per_pixel); defaults_element.writeAttribute(literal::dpi, map->image_template_dpi); defaults_element.writeAttribute(literal::scale, map->image_template_scale); } writeLineBreak(xml); } void XMLFileExporter::exportView() { XmlElementWriter view_element(xml, literal::view); view_element.writeAttribute(literal::area_hatching_enabled, bool(map->renderable_options & Symbol::RenderAreasHatched)); view_element.writeAttribute(literal::baseline_view_enabled, bool(map->renderable_options & Symbol::RenderBaselines)); writeLineBreak(xml); map->getGrid().save(xml); if (view) { writeLineBreak(xml); view->save(xml, literal::map_view); } writeLineBreak(xml); } void XMLFileExporter::exportPrint() { if (map->hasPrinterConfig()) { map->printerConfig().save(xml, literal::print); writeLineBreak(xml); } } void XMLFileExporter::exportUndo() { map->undoManager().saveUndo(xml); writeLineBreak(xml); } void XMLFileExporter::exportRedo() { map->undoManager().saveRedo(xml); writeLineBreak(xml); } // ### XMLFileImporter definition ### XMLFileImporter::XMLFileImporter(QIODevice* stream, Map *map, MapView *view) : Importer(stream, map, view), xml(stream) { //NOP } void XMLFileImporter::addWarningUnsupportedElement() { addWarning(tr("Unsupported element: %1 (line %2 column %3)"). arg(xml.name().toString()). arg(xml.lineNumber()). arg(xml.columnNumber()) ); } void XMLFileImporter::import(bool load_symbols_only) { if (!xml.readNextStartElement() || xml.name() != literal::map) { xml.raiseError(::OpenOrienteering::Importer::tr("Unsupported file format.")); } XmlElementReader map_element(xml); const int version = map_element.attribute(literal::version); if (version < 1) xml.raiseError(::OpenOrienteering::Importer::tr("Invalid file format version.")); else if (version < XMLFileFormat::minimum_version) xml.raiseError(::OpenOrienteering::Importer::tr("Unsupported old file format version. Please use an older program version to load and update the file.")); else if (version > XMLFileFormat::current_version) addWarning(::OpenOrienteering::Importer::tr("Unsupported new file format version. Some map features will not be loaded or saved by this version of the program.")); QScopedValueRollback rollback { MapCoord::boundsOffset() }; MapCoord::boundsOffset().reset(true); georef_offset_adjusted = false; importElements(load_symbols_only); auto offset = MapCoord::boundsOffset(); if (!load_symbols_only && !offset.isZero()) { addWarning(tr("Some coordinates were out of bounds for printing. Map content was adjusted.")); MapCoordF offset_f { offset.x / 1000.0, offset.y / 1000.0 }; // Apply the offset auto printer_config = map->printerConfig(); auto& print_area = printer_config.print_area; print_area.translate( -offset_f ); // Verify the adjusted print area, and readjust if necessary if (print_area.top() <= -1000000.0 || print_area.bottom() > 1000000.0) print_area.moveTop(-print_area.width() / 2); if (print_area.left() <= -1000000.0 || print_area.right() > 1000000.0) print_area.moveLeft(-print_area.width() / 2); map->setPrinterConfig(printer_config); if (!georef_offset_adjusted) { // We need to adjust the georeferencing. auto georef = map->getGeoreferencing(); auto ref_point = MapCoordF { georef.getMapRefPoint() }; auto new_projected = georef.toProjectedCoords(ref_point + offset_f); georef.setProjectedRefPoint(new_projected, false); map->setGeoreferencing(georef); } } } void XMLFileImporter::importElements(bool load_symbols_only) { while (xml.readNextStartElement()) { const QStringRef name(xml.name()); if (name == literal::colors) importColors(); else if (name == literal::symbols) importSymbols(); else if (name == literal::georeferencing) importGeoreferencing(load_symbols_only); else if (name == literal::view) importView(); else if (name == literal::barrier) { XmlElementReader barrier(xml); if (barrier.attribute(literal::version) > XMLFileFormat::current_version) { QString required_version = barrier.attribute(literal::required); if (required_version.isEmpty()) required_version = tr("unknown"); addWarning(tr("Parts of this file cannot be read by this version of Mapper. Minimum required version: %1").arg(required_version)); xml.skipCurrentElement(); } else { importElements(load_symbols_only); } } else if (load_symbols_only) xml.skipCurrentElement(); /****************************************************** * The remainder is skipped when loading a symbol set! * ******************************************************/ else if (name == literal::notes) importMapNotes(); else if (name == literal::parts) importMapParts(); else if (name == literal::templates) importTemplates(); else if (name == literal::print) importPrint(); else if (name == literal::undo) importUndo(); else if (name == literal::redo) importRedo(); else { addWarningUnsupportedElement(); xml.skipCurrentElement(); } } if (xml.error()) throw FileFormatException( tr("Error at line %1 column %2: %3") .arg(xml.lineNumber()) .arg(xml.columnNumber()) .arg(xml.errorString()) ); } void XMLFileImporter::importMapNotes() { auto recovery = XmlRecoveryHelper(xml); map->setMapNotes(xml.readElementText()); if (xml.hasError() && recovery()) { addWarning(tr("Some invalid characters had to be removed.")); map->setMapNotes(xml.readElementText()); } } void XMLFileImporter::importGeoreferencing(bool load_symbols_only) { Q_ASSERT(xml.name() == literal::georeferencing); bool check_for_offset = MapCoord::boundsOffset().check_for_offset; Georeferencing georef; georef.load(xml, load_symbols_only); map->setGeoreferencing(georef); if (!georef.isValid()) { QString error_text = georef.getErrorText(); if (error_text.isEmpty()) error_text = tr("Unknown error"); addWarning(tr("Unsupported or invalid georeferencing specification '%1': %2"). arg(georef.getProjectedCRSSpec(), error_text)); } if (MapCoord::boundsOffset().isZero()) // Georeferencing was not adjusted on import. MapCoord::boundsOffset().reset(check_for_offset); else if (check_for_offset) // Georeferencing was adjusted on import, before other coordinates. georef_offset_adjusted = true; } /** Helper for delayed actions */ struct XMLFileImporterColorBacklogItem { MapColor* color; // color which needs updating SpotColorComponents components; // components of the color bool knockout; bool cmyk_from_spot; // determine CMYK from spot bool rgb_from_spot; // determine RGB from spot XMLFileImporterColorBacklogItem(MapColor* color) : color(color), knockout(false), cmyk_from_spot(false), rgb_from_spot(false) {} }; typedef std::vector XMLFileImporterColorBacklog; void XMLFileImporter::importColors() { XmlElementReader all_colors_element(xml); auto num_colors = all_colors_element.attribute(literal::count); Map::ColorVector& colors(map->color_set->colors); colors.reserve(qMin(num_colors, std::size_t(100))); // 100 is not a limit XMLFileImporterColorBacklog backlog; backlog.reserve(colors.size()); while (xml.readNextStartElement()) { if (xml.name() == literal::color) { XmlElementReader color_element(xml); MapColor* color = new MapColor( color_element.attribute(literal::name), color_element.attribute(literal::priority) ); if (color_element.hasAttribute(literal::opacity)) color->setOpacity(color_element.attribute(literal::opacity)); MapColorCmyk cmyk; cmyk.c = color_element.attribute(literal::c); cmyk.m = color_element.attribute(literal::m); cmyk.y = color_element.attribute(literal::y); cmyk.k = color_element.attribute(literal::k); bool knockout = false; SpotColorComponents components; QString cmyk_method; QString rgb_method; MapColorRgb rgb; while (xml.readNextStartElement()) { if (xml.name() == literal::spotcolors) { XmlElementReader spotcolors_element(xml); knockout = spotcolors_element.attribute(literal::knockout); while(xml.readNextStartElement()) { if (xml.name() == literal::namedcolor) { color->setSpotColorName(xml.readElementText()); color->setKnockout(knockout); } else if (xml.name() == literal::component) { XmlElementReader component_element(xml); SpotColorComponent component; component.factor = component_element.attribute(literal::factor); // We can't know if the spot color is already loaded. Create a temporary proxy. component.spot_color = new MapColor(component_element.attribute(literal::spotcolor)); components.push_back(component); } else xml.skipCurrentElement(); // unsupported } } else if (xml.name() == literal::cmyk) { XmlElementReader cmyk_element(xml); cmyk_method = cmyk_element.attribute(literal::method); } else if (xml.name() == literal::rgb) { XmlElementReader rgb_element(xml); rgb_method = rgb_element.attribute(literal::method); rgb.r = rgb_element.attribute(literal::r); rgb.g = rgb_element.attribute(literal::g); rgb.b = rgb_element.attribute(literal::b); } else { xml.skipCurrentElement(); // unsupported } } if (cmyk_method == literal::custom) { color->setCmyk(cmyk); if (rgb_method == literal::cmyk) color->setRgbFromCmyk(); } if (rgb_method == literal::custom) { color->setRgb(rgb); if (cmyk_method == literal::rgb) color->setCmykFromRgb(); } if (!components.empty()) { backlog.push_back(XMLFileImporterColorBacklogItem(color)); XMLFileImporterColorBacklogItem& item = backlog.back(); item.components = components; item.knockout = knockout; item.cmyk_from_spot = (cmyk_method == literal::spotcolor); item.rgb_from_spot = (rgb_method == literal::spotcolor); } else if (knockout && !color->getKnockout()) { addWarning(tr("Could not set knockout property of color '%1'.").arg(color->getName())); } colors.push_back(color); } else { addWarningUnsupportedElement(); xml.skipCurrentElement(); } } if (num_colors > 0 && num_colors != colors.size()) addWarning(tr("Expected %1 colors, found %2."). arg(num_colors). arg(colors.size()) ); // All spot colors are loaded at this point. // Now deal with depending color compositions from the backlog. for (auto&& item : backlog) { // Process the list of spot color components. SpotColorComponents out_components; for (auto&& in_component : item.components) { const MapColor* out_color = map->getColor(in_component.spot_color->getPriority()); if (!out_color || out_color->getSpotColorMethod() != MapColor::SpotColor) { addWarning(tr("Spot color %1 not found while processing %2 (%3)."). arg(in_component.spot_color->getPriority()). arg(item.color->getPriority()). arg(item.color->getName()) ); continue; // Drop this color, invalid reference } out_components.push_back(in_component); SpotColorComponent& out_component = out_components.back(); out_component.spot_color = out_color; // That is the major point! delete in_component.spot_color; // Delete the temporary proxy. } // Update the current color item.color->setSpotColorComposition(out_components); item.color->setKnockout(item.knockout); if (item.cmyk_from_spot) item.color->setCmykFromSpotColors(); if (item.rgb_from_spot) item.color->setRgbFromSpotColors(); if (item.knockout && !item.color->getKnockout()) { addWarning(tr("Could not set knockout property of color '%1'.").arg(item.color->getName())); } } } void XMLFileImporter::importSymbols() { QScopedValueRollback offset { MapCoord::boundsOffset() }; MapCoord::boundsOffset().reset(false); XmlElementReader symbols_element(xml); map->setSymbolSetId(symbols_element.attribute(literal::id)); auto num_symbols = symbols_element.attribute(literal::count); map->symbols.reserve(qMin(num_symbols, std::size_t(1000))); // 1000 is not a limit symbol_dict[QString::number(map->findSymbolIndex(map->getUndefinedPoint()))] = map->getUndefinedPoint(); symbol_dict[QString::number(map->findSymbolIndex(map->getUndefinedLine()))] = map->getUndefinedLine(); while (xml.readNextStartElement()) { if (xml.name() == literal::symbol) { map->symbols.push_back(Symbol::load(xml, *map, symbol_dict)); } else { addWarningUnsupportedElement(); xml.skipCurrentElement(); } } if (num_symbols > 0 && num_symbols != map->symbols.size()) addWarning(tr("Expected %1 symbols, found %2."). arg(num_symbols). arg(map->symbols.size()) ); } void XMLFileImporter::importMapParts() { XmlElementReader mapparts_element(xml); auto num_parts = mapparts_element.attribute(literal::count); auto current_part_index = mapparts_element.attribute(literal::current); map->parts.clear(); map->parts.reserve(qMin(num_parts, std::size_t(20))); // 20 is not a limit while (xml.readNextStartElement()) { if (xml.name() == literal::part) { auto recovery = XmlRecoveryHelper(xml); auto part = MapPart::load(xml, *map, symbol_dict); if (xml.hasError() && recovery()) { addWarning(tr("Some invalid characters had to be removed.")); delete part; part = MapPart::load(xml, *map, symbol_dict); } map->parts.push_back(part); } else { addWarningUnsupportedElement(); xml.skipCurrentElement(); } } if (current_part_index < map->parts.size()) map->current_part_index = current_part_index; if (num_parts > 0 && num_parts != map->parts.size()) addWarning(tr("Expected %1 map parts, found %2."). arg(num_parts). arg(map->parts.size()) ); emit map->currentMapPartIndexChanged(map->current_part_index); emit map->currentMapPartChanged(map->getPart(map->current_part_index)); } void XMLFileImporter::importTemplates() { Q_ASSERT(xml.name() == literal::templates); XmlElementReader templates_element(xml); int first_front_template = templates_element.attribute(literal::first_front_template); auto num_templates = templates_element.attribute(literal::count); map->templates.reserve(qMin(num_templates, std::size_t(20))); // 20 is not a limit map->closed_templates.reserve(qMin(num_templates, std::size_t(20))); // 20 is not a limit while (xml.readNextStartElement()) { if (xml.name() == literal::template_string) { bool opened = true; auto temp = Template::loadTemplateConfiguration(xml, *map, opened); if (opened) map->templates.push_back(temp.release()); else map->closed_templates.push_back(temp.release()); } else if (xml.name() == literal::defaults) { XmlElementReader defaults_element(xml); map->image_template_use_meters_per_pixel = defaults_element.attribute(literal::use_meters_per_pixel); map->image_template_meters_per_pixel = defaults_element.attribute(literal::meters_per_pixel); map->image_template_dpi = defaults_element.attribute(literal::dpi); map->image_template_scale = defaults_element.attribute(literal::scale); } else { qDebug("Unsupported element: %s", qPrintable(xml.qualifiedName().toString())); xml.skipCurrentElement(); } } map->first_front_template = qMax(0, qMin(map->getNumTemplates(), first_front_template)); } void XMLFileImporter::importView() { Q_ASSERT(xml.name() == literal::view); XmlElementReader view_element(xml); if (view_element.attribute(literal::area_hatching_enabled)) map->renderable_options |= Symbol::RenderAreasHatched; if (view_element.attribute(literal::baseline_view_enabled)) map->renderable_options |= Symbol::RenderBaselines; while (xml.readNextStartElement()) { if (xml.name() == literal::grid) { map->setGrid(MapGrid().load(xml)); } else if (xml.name() == literal::map_view) { if (view) view->load(xml); } else { xml.skipCurrentElement(); // unsupported } } } void XMLFileImporter::importPrint() { Q_ASSERT(xml.name() == literal::print); try { map->setPrinterConfig(MapPrinterConfig(*map, xml)); } catch (FileFormatException& e) { addWarning(::OpenOrienteering::ImportExport::tr("Error while loading the printing configuration at %1:%2: %3") .arg(xml.lineNumber()).arg(xml.columnNumber()).arg(e.message())); } } void XMLFileImporter::importUndo() { if (!Settings::getInstance().getSetting(Settings::General_SaveUndoRedo).toBool()) { xml.skipCurrentElement(); return; } try { map->undoManager().loadUndo(xml, symbol_dict); } catch (FileFormatException& e) { addWarning(::OpenOrienteering::ImportExport::tr("Error while loading the undo/redo steps at %1:%2: %3") .arg(xml.lineNumber()).arg(xml.columnNumber()).arg(e.message())); map->undoManager().clear(); } } void XMLFileImporter::importRedo() { if (!Settings::getInstance().getSetting(Settings::General_SaveUndoRedo).toBool()) { xml.skipCurrentElement(); return; } try { map->undoManager().loadRedo(xml, symbol_dict); } catch (FileFormatException& e) { addWarning(::OpenOrienteering::ImportExport::tr("Error while loading the undo/redo steps at %1:%2: %3") .arg(xml.lineNumber()).arg(xml.columnNumber()).arg(e.message())); map->undoManager().clear(); } } } // namespace OpenOrienteering mapper-0.8.1.1/src/fileformats/xml_file_format.h000066400000000000000000000043251325266516600216050ustar00rootroot00000000000000/* * Copyright 2012, 2013, 2014 Pete Curtis, Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_FILE_FORMAT_XML_H #define OPENORIENTEERING_FILE_FORMAT_XML_H #include #include "fileformats/file_format.h" class QIODevice; namespace OpenOrienteering { class Exporter; class Importer; class Map; class MapView; /** @brief Interface for dealing with XML files of maps. */ class XMLFileFormat : public FileFormat { public: /** @brief Creates a new file format of type XML. */ XMLFileFormat(); /** @brief Returns true if the file starts with the character sequence ". */ #ifndef OPENORIENTEERING_FILE_FORMAT_XML_P_H #define OPENORIENTEERING_FILE_FORMAT_XML_P_H #include #include #include #include "core/symbols/symbol.h" #include "fileformats/file_import_export.h" namespace OpenOrienteering { /** Map exporter for the xml based map format. */ class XMLFileExporter : public Exporter { Q_DECLARE_TR_FUNCTIONS(OpenOrienteering::XMLFileExporter) public: XMLFileExporter(QIODevice* stream, Map *map, MapView *view); ~XMLFileExporter() override {} void doExport() override; protected: void exportGeoreferencing(); void exportColors(); void exportSymbols(); void exportMapParts(); void exportTemplates(); void exportView(); void exportPrint(); void exportUndo(); void exportRedo(); private: QXmlStreamWriter xml; }; /** Map importer for the xml based map format. */ class XMLFileImporter : public Importer { Q_DECLARE_TR_FUNCTIONS(OpenOrienteering::XMLFileImporter) public: XMLFileImporter(QIODevice* stream, Map *map, MapView *view); ~XMLFileImporter() override {} protected: void import(bool load_symbols_only) override; void importElements(bool load_symbols_only); void addWarningUnsupportedElement(); void importMapNotes(); void importGeoreferencing(bool load_symbols_only); void importColors(); void importSymbols(); void importMapParts(); void importTemplates(); void importView(); void importPrint(); void importUndo(); void importRedo(); QXmlStreamReader xml; SymbolDictionary symbol_dict; bool georef_offset_adjusted; }; } // namespace OpenOrienteering #endif mapper-0.8.1.1/src/gdal/000077500000000000000000000000001325266516600146555ustar00rootroot00000000000000mapper-0.8.1.1/src/gdal/CMakeLists.txt000066400000000000000000000042651325266516600174240ustar00rootroot00000000000000# # Copyright 2016-2017 Kai Pastor # # This file is part of OpenOrienteering. # # OpenOrienteering is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # OpenOrienteering is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with OpenOrienteering. If not, see . find_package(GDAL REQUIRED) find_package(Qt5Core REQUIRED) find_package(Qt5Gui REQUIRED) find_package(Qt5Widgets REQUIRED) set(CMAKE_AUTOMOC ON) # Extra header to be shown in the IDE or to be translated set(MAPPER_GDAL_HEADERS ogr_file_format_p.h ) set(MAPPER_GDAL_SOURCES gdal_manager.cpp gdal_settings_page.cpp ogr_file_format.cpp ogr_template.cpp mapper-osmconf.ini ) mapper_translations_sources(${MAPPER_GDAL_HEADERS} ${MAPPER_GDAL_SOURCES}) add_library(mapper-gdal STATIC ${MAPPER_GDAL_HEADERS} ${MAPPER_GDAL_SOURCES} "${PROJECT_BINARY_DIR}/gdal/mapper-osmconf.ini") target_compile_definitions(mapper-gdal PRIVATE QT_NO_CAST_FROM_ASCII QT_NO_CAST_TO_ASCII QT_USE_QSTRINGBUILDER ) target_compile_definitions(mapper-gdal INTERFACE MAPPER_USE_GDAL) target_include_directories(mapper-gdal PRIVATE "${GDAL_INCLUDE_DIR}" "${PROJECT_SOURCE_DIR}/src") target_link_libraries(mapper-gdal "${GDAL_LIBRARY}" Qt5::Core Qt5::Gui Qt5::Widgets) set_target_properties(mapper-gdal PROPERTIES PREFIX "") install(FILES mapper-osmconf.ini DESTINATION "${MAPPER_DATA_DESTINATION}/gdal") # Let mapper-osmconf.ini be found via "data:/gdal" during development. add_custom_command(OUTPUT "${PROJECT_BINARY_DIR}/gdal/mapper-osmconf.ini" COMMAND "${CMAKE_COMMAND}" -E make_directory "gdal" COMMAND "${CMAKE_COMMAND}" -E copy "${CMAKE_CURRENT_SOURCE_DIR}/mapper-osmconf.ini" "gdal/mapper-osmconf.ini" WORKING_DIRECTORY "${PROJECT_BINARY_DIR}" ) mapper-0.8.1.1/src/gdal/gdal_manager.cpp000066400000000000000000000221361325266516600177660ustar00rootroot00000000000000/* * Copyright 2016-2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "gdal_manager.h" #include #include // IWYU pragma: keep #include #include #include #include #include #include #include #include #include #include #include "util/backports.h" namespace OpenOrienteering { class GdalManager::GdalManagerPrivate { public: const QString gdal_manager_group{ QStringLiteral("GdalManager") }; const QString gdal_configuration_group{ QStringLiteral("GdalConfiguration") }; const QString gdal_dxf_key{ QStringLiteral("dxf") }; const QString gdal_gpx_key{ QStringLiteral("gpx") }; const QString gdal_osm_key{ QStringLiteral("osm") }; GdalManagerPrivate() : dirty{ true } { // GDAL 2.0: GDALAllRegister(); OGRRegisterAll(); } GdalManagerPrivate(const GdalManagerPrivate&) = delete; void configure() { if (dirty) update(); } void setFormatEnabled(GdalManager::FileFormat format, bool enabled) { QString key; switch (format) { case GdalManager::DXF: key = gdal_dxf_key; break; case GdalManager::GPX: key = gdal_gpx_key; break; case GdalManager::OSM: key = gdal_osm_key; break; } QSettings settings; settings.beginGroup(gdal_manager_group); settings.setValue(key, QVariant{ enabled }); dirty = true; } bool isFormatEnabled(FileFormat format) const { QString key; switch (format) { case GdalManager::DXF: key = gdal_dxf_key; break; case GdalManager::GPX: key = gdal_gpx_key; break; case GdalManager::OSM: key = gdal_osm_key; break; } QSettings settings; settings.beginGroup(gdal_manager_group); return !settings.contains(key) || settings.value(key).toBool(); } const std::vector& supportedRasterExtensions() const { /// \todo static std::vector ret; return ret; } const std::vector& supportedVectorExtensions() const { if (dirty) const_cast(this)->update(); return enabled_vector_extensions; } QStringList parameterKeys() const { if (dirty) const_cast(this)->update(); return applied_parameters; } QString parameterValue(const QString& key) const { if (dirty) const_cast(this)->update(); QSettings settings; settings.beginGroup(gdal_configuration_group); return settings.value(key).toString(); } void setParameterValue(const QString& key, const QString& value) { QSettings settings; settings.beginGroup(gdal_configuration_group); settings.setValue(key, QVariant{ value }); dirty = true; } void unsetParameter(const QString& key) { QSettings settings; settings.beginGroup(gdal_configuration_group); settings.remove(key); dirty = true; } private: void update() { QSettings settings; #ifdef GDAL_DMD_EXTENSIONS // GDAL >= 2.0 settings.beginGroup(gdal_manager_group); auto count = GDALGetDriverCount(); enabled_vector_extensions.clear(); enabled_vector_extensions.reserve(std::size_t(count)); for (auto i = 0; i < count; ++i) { auto driver_data = GDALGetDriver(i); auto type = GDALGetMetadataItem(driver_data, GDAL_DCAP_VECTOR, nullptr); if (qstrcmp(type, "YES") != 0) continue; // Skip write-only drivers. auto cap_open = GDALGetMetadataItem(driver_data, GDAL_DCAP_OPEN, nullptr); if (qstrcmp(cap_open, "YES") != 0) continue; auto extensions_raw = GDALGetMetadataItem(driver_data, GDAL_DMD_EXTENSIONS, nullptr); auto extensions = QByteArray::fromRawData(extensions_raw, int(qstrlen(extensions_raw))); for (auto pos = 0; pos >= 0; ) { auto start = pos ? pos + 1 : 0; pos = extensions.indexOf(' ', start); auto extension = extensions.mid(start, pos - start); if (extension.isEmpty()) continue; if (extension == "dxf" && !settings.value(gdal_dxf_key).toBool()) continue; if (extension == "gpx" && !settings.value(gdal_gpx_key).toBool()) continue; if (extension == "osm" && !settings.value(gdal_osm_key).toBool()) continue; enabled_vector_extensions.emplace_back(extension); } } settings.endGroup(); #else // GDAL < 2.0 does not provide the supported extensions static const std::vector default_extensions = { "shp", "dbf", /* "dxf", */ /* "gpx", */ /* "osm", */ "pbf", }; enabled_vector_extensions.reserve(default_extensions.size() + 3); enabled_vector_extensions = default_extensions; settings.beginGroup(gdal_manager_group); if (settings.value(gdal_dxf_key).toBool()) enabled_vector_extensions.push_back("dxf"); if (settings.value(gdal_gpx_key).toBool()) enabled_vector_extensions.push_back("gpx"); if (settings.value(gdal_osm_key).toBool()) enabled_vector_extensions.push_back("osm"); settings.endGroup(); #endif // Using osmconf.ini to detect a directory with data from gdal. The // data:/gdal directory will always exist, due to mapper-osmconf.ini. auto osm_conf_ini = QFileInfo(QLatin1String("data:/gdal/osmconf.ini")); if (osm_conf_ini.exists()) { auto gdal_data = osm_conf_ini.absolutePath(); Q_ASSERT(!gdal_data.contains(QStringLiteral("data:"))); // The user may overwrite this default in the settings. CPLSetConfigOption("GDAL_DATA", QDir::toNativeSeparators(gdal_data).toLocal8Bit()); } settings.beginGroup(gdal_configuration_group); const char* defaults[][2] = { { "CPL_DEBUG", "OFF" }, { "USE_PROJ_480_FEATURES", "YES" }, { "OSM_USE_CUSTOM_INDEXING", "NO" }, { "GPX_ELE_AS_25D", "YES" }, }; for (const auto setting : defaults) { const auto key = QString::fromLatin1(setting[0]); if (!settings.contains(key)) { settings.setValue(key, QVariant{QLatin1String(setting[1])}); } } osm_conf_ini = QFileInfo(QLatin1String("data:/gdal/mapper-osmconf.ini")); if (osm_conf_ini.exists()) { auto osm_conf_ini_path = QDir::toNativeSeparators(osm_conf_ini.absoluteFilePath()).toLocal8Bit(); auto key = QString::fromLatin1("OSM_CONFIG_FILE"); auto update_settings = !settings.contains(key); if (!update_settings) { auto current = settings.value(key).toByteArray(); settings.beginGroup(QLatin1String("default")); auto current_default = settings.value(key).toByteArray(); settings.endGroup(); update_settings = (current == current_default && current != osm_conf_ini_path); } if (update_settings) { settings.setValue(key, osm_conf_ini_path); settings.beginGroup(QLatin1String("default")); settings.setValue(key, osm_conf_ini_path); settings.endGroup(); } } auto new_parameters = settings.childKeys(); new_parameters.sort(); for (const auto& parameter : qAsConst(new_parameters)) { CPLSetConfigOption(parameter.toLatin1().constData(), settings.value(parameter).toByteArray().constData()); } for (const auto& parameter : qAsConst(applied_parameters)) { if (!new_parameters.contains(parameter) && parameter != QLatin1String{ "GDAL_DATA" }) { CPLSetConfigOption(parameter.toLatin1().constData(), nullptr); } } applied_parameters.swap(new_parameters); CPLFinderClean(); // force re-initialization of file finding tools dirty = false; } mutable bool dirty; mutable std::vector enabled_vector_extensions; mutable QStringList applied_parameters; }; // ### GdalManager ### GdalManager::GdalManager() { static GdalManagerPrivate manager; p = &manager; } void GdalManager::configure() { p->configure(); } void GdalManager::setFormatEnabled(GdalManager::FileFormat format, bool enabled) { p->setFormatEnabled(format, enabled); } bool GdalManager::isFormatEnabled(GdalManager::FileFormat format) const { return p->isFormatEnabled(format); } const std::vector&GdalManager::supportedRasterExtensions() const { return p->supportedRasterExtensions(); } const std::vector& GdalManager::supportedVectorExtensions() const { return p->supportedVectorExtensions(); } QStringList GdalManager::parameterKeys() const { return p->parameterKeys(); } QString GdalManager::parameterValue(const QString& key) const { return p->parameterValue(key); } void GdalManager::setParameterValue(const QString& key, const QString& value) { p->setParameterValue(key, value); } void GdalManager::unsetParameter(const QString& key) { p->unsetParameter(key); } } // namespace OpenOrienteering mapper-0.8.1.1/src/gdal/gdal_manager.h000066400000000000000000000052521325266516600174330ustar00rootroot00000000000000/* * Copyright 2016 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_GDAL_MANAGER_H #define OPENORIENTEERING_GDAL_MANAGER_H #include class QByteArray; class QString; class QStringList; namespace OpenOrienteering { /** * A utility class which takes care of GDAL settings and options. * * This class provides lists of extensions supported via GDAL in Mapper. * It sets and updates GDAL configuration parameters from Mapper's settings. * * There is no need to keep objects of this class for an extended life time: * instantiation is cheap; the actual state is shared and retained. */ class GdalManager { private: class GdalManagerPrivate; GdalManagerPrivate* p; public: enum FileFormat { DXF, GPX, OSM }; /** * Constructs a new manager object. */ GdalManager(); /** * Sets the GDAL configuration from Mapper's defaults and settings. */ void configure(); /** * Enables or disables handling of a particular file format by GDAL/OGR. */ void setFormatEnabled(FileFormat format, bool enabled); /** * Returns if GDAL/OGR will handle a particular file format. */ bool isFormatEnabled(FileFormat format) const; /** * Returns the file name extensions for supported raster formats. */ const std::vector& supportedRasterExtensions() const; /** * Returns the file name extensions for supported vector formats. */ const std::vector& supportedVectorExtensions() const; /** * Returns the list of GDAL configuration parameters. */ QStringList parameterKeys() const; /** * Returns a GDAL configuration parameter value. */ QString parameterValue(const QString& key) const; /** * Sets a GDAL configuration parameter value. */ void setParameterValue(const QString& key, const QString& value); /** * Unsets a GDAL configuration parameter value. */ void unsetParameter(const QString& key); }; } // namespace OpenOrienteering #endif // OPENORIENTEERING_GDAL_MANAGER_H mapper-0.8.1.1/src/gdal/gdal_settings_page.cpp000066400000000000000000000137571325266516600212210ustar00rootroot00000000000000/* * Copyright 2016-2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "gdal_settings_page.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "fileformats/file_format_registry.h" #include "gdal/gdal_manager.h" #include "gdal/ogr_file_format.h" #include "gui/util_gui.h" #include "util/backports.h" namespace OpenOrienteering { GdalSettingsPage::GdalSettingsPage(QWidget* parent) : SettingsPage(parent) { auto form_layout = new QFormLayout(); form_layout->addRow(Util::Headline::create(tr("Import with GDAL/OGR:"))); import_dxf = new QCheckBox(tr("DXF")); form_layout->addRow(import_dxf); import_gpx = new QCheckBox(tr("GPX")); form_layout->addRow(import_gpx); import_osm = new QCheckBox(tr("OSM")); form_layout->addRow(import_osm); form_layout->addItem(Util::SpacerItem::create(this)); form_layout->addRow(Util::Headline::create(tr("Configuration"))); auto layout = new QVBoxLayout(this); layout->addLayout(form_layout); parameters = new QTableWidget(1, 2); parameters->verticalHeader()->hide(); parameters->setHorizontalHeaderLabels({ tr("Parameter"), tr("Value") }); auto header_view = parameters->horizontalHeader(); header_view->setSectionResizeMode(0, QHeaderView::Stretch); header_view->setSectionResizeMode(1, QHeaderView::Stretch); header_view->setSectionsClickable(false); layout->addWidget(parameters, 1); updateWidgets(); connect(parameters, &QTableWidget::cellChanged, this, &GdalSettingsPage::cellChange); } GdalSettingsPage::~GdalSettingsPage() { // nothing, not inlined } QString GdalSettingsPage::title() const { return tr("GDAL/OGR"); } void GdalSettingsPage::apply() { GdalManager manager; manager.setFormatEnabled(GdalManager::DXF, import_dxf->isChecked()); manager.setFormatEnabled(GdalManager::GPX, import_gpx->isChecked()); manager.setFormatEnabled(GdalManager::OSM, import_osm->isChecked()); // The file format constructor establishes the extensions. auto format = new OgrFileFormat(); FileFormats.unregisterFormat(FileFormats.findFormat(format->id())); FileFormats.registerFormat(format); const auto old_parameters = manager.parameterKeys(); QStringList new_parameters; new_parameters.reserve(parameters->rowCount()); for (int row = 0, end = parameters->rowCount(); row < end; ++row) { auto key = parameters->item(row, 0)->text().trimmed(); if (!key.isEmpty()) { new_parameters.append(key); auto value = parameters->item(row, 1)->text(); manager.setParameterValue(key, value.trimmed()); } } for (const auto& key : qAsConst(old_parameters)) { if (!new_parameters.contains(key)) { manager.unsetParameter(key); } } } void GdalSettingsPage::reset() { updateWidgets(); } void GdalSettingsPage::updateWidgets() { GdalManager manager; import_dxf->setChecked(manager.isFormatEnabled(GdalManager::DXF)); import_gpx->setChecked(manager.isFormatEnabled(GdalManager::GPX)); import_osm->setChecked(manager.isFormatEnabled(GdalManager::OSM)); auto options = manager.parameterKeys(); options.sort(); parameters->setRowCount(options.size() + 1); auto row = 0; for (const auto& item : qAsConst(options)) { auto key_item = new QTableWidgetItem(item); parameters->setItem(row, 0, key_item); auto value_item = new QTableWidgetItem(manager.parameterValue(item)); parameters->setItem(row, 1, value_item); ++row; } parameters->setRowCount(row+1); parameters->setItem(row, 0, new QTableWidgetItem()); parameters->setItem(row, 1, new QTableWidgetItem()); } void GdalSettingsPage::cellChange(int row, int column) { const QString key = parameters->item(row, 0)->text().trimmed(); const QString value = parameters->item(row, 1)->text(); if (column == 1 && key.isEmpty()) { // Shall not happen qWarning("Empty key for modified tag value!"); } else if (column == 0) { QSignalBlocker block(parameters); parameters->item(row, 0)->setText(key); // trimmed auto last_row = parameters->rowCount() - 1; int duplicate = findDuplicateKey(key, row); if (key.isEmpty()) { if (row == last_row) { parameters->item(row, 1)->setText({ }); } else { parameters->model()->removeRow(row); parameters->setCurrentCell(row, 0); parameters->setFocus(); } } else if (duplicate != row) { if (row == last_row) { parameters->item(row, 0)->setText({ }); parameters->item(row, 0)->setText({ }); } else { parameters->model()->removeRow(row); } parameters->setCurrentCell(duplicate, 0); parameters->setFocus(); } else { if (row == last_row) { parameters->setRowCount(last_row + 2); parameters->setItem(last_row + 1, 0, new QTableWidgetItem()); parameters->setItem(last_row + 1, 1, new QTableWidgetItem()); } if (value.isEmpty()) { GdalManager manager; parameters->item(row, 1)->setText(manager.parameterValue(key)); } } } } int GdalSettingsPage::findDuplicateKey(const QString& key, int row) const { for (int i = 0, end = parameters->rowCount(); i < end; ++i) { if (i != row && parameters->item(i, 0)->text() == key) { row = i; break; } } return row; } } // namespace OpenOrienteering mapper-0.8.1.1/src/gdal/gdal_settings_page.h000066400000000000000000000030431325266516600206510ustar00rootroot00000000000000/* * Copyright 2016-2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_GDAL_SETTINGS_PAGE_H #define OPENORIENTEERING_GDAL_SETTINGS_PAGE_H #include #include #include "gui/widgets/settings_page.h" class QCheckBox; class QTableWidget; class QWidget; namespace OpenOrienteering { class GdalSettingsPage : public SettingsPage { Q_OBJECT public: explicit GdalSettingsPage(QWidget* parent = nullptr); ~GdalSettingsPage() override; QString title() const override; void apply() override; void reset() override; protected: void updateWidgets(); void cellChange(int row, int column); int findDuplicateKey(const QString& key, int row) const; private: QCheckBox* import_dxf; QCheckBox* import_gpx; QCheckBox* import_osm; QTableWidget* parameters; }; } // namespace OpenOrienteering #endif mapper-0.8.1.1/src/gdal/mapper-osmconf.ini000066400000000000000000000133521325266516600203100ustar00rootroot00000000000000# # Configuration file for OSM import # # Customized for OpenOrienteering Mapper # # put here the name of keys for ways that are assumed to be polygons if they are closed # see http://wiki.openstreetmap.org/wiki/Map_Features closed_ways_are_polygons=aeroway,amenity,boundary,building,craft,geological,historic,landuse,leisure,military,natural,office,place,shop,sport,tourism # comment to avoid laundering of keys ( ':' turned into '_' ) #attribute_name_laundering=yes # uncomment to report all nodes, including the ones without any (significant) tag #report_all_nodes=yes # uncomment to report all ways, including the ones without any (significant) tag #report_all_ways=yes [points] # common attributes osm_id=yes osm_version=no osm_timestamp=yes osm_uid=no osm_user=no osm_changeset=no # keys to report as OGR fields attributes=access,aerialway,aeroway,amenity,area,barrier,building,cycleway,foot,geological,highway,historic,intermittent,landuse,leaf_type,leisure,location,man_made,military,name,natural,pipeline,power,railway,ruins,sport,surface,shop,smoothness,ref,tourism,tracktype,tunnel,waterway,wood:age,wood:density # keys that, alone, are not significant enough to report a node as a OGR point unsignificant=created_by,converted_by,source,time,ele,attribution # keys that should NOT be reported in the "other_tags" field ignore=created_by,converted_by,source,time,note,openGeoDB:,fixme,FIXME # uncomment to avoid creation of "other_tags" field #other_tags=no # uncomment to create "all_tags" field. "all_tags" and "other_tags" are exclusive #all_tags=yes [lines] # common attributes osm_id=yes osm_version=no osm_timestamp=yes osm_uid=no osm_user=no osm_changeset=no # keys to report as OGR fields attributes=access,aerialway,aeroway,amenity,area,barrier,building,cycleway,foot,geological,highway,historic,intermittent,landuse,leaf_type,leisure,location,man_made,military,name,natural,pipeline,power,railway,ruins,sport,surface,shop,smoothness,ref,tourism,tracktype,tunnel,waterway,wood:age,wood:density # type of attribute 'foo' can be changed with something like #foo_type=Integer/Real/String/DateTime # keys that should NOT be reported in the "other_tags" field ignore=created_by,converted_by,source,time,note,openGeoDB:,fixme,FIXME # uncomment to avoid creation of "other_tags" field #other_tags=no # uncomment to create "all_tags" field. "all_tags" and "other_tags" are exclusive #all_tags=yes #computed_attributes must appear before the keywords _type and _sql computed_attributes=z_order z_order_type=Integer # Formula based on https://github.com/openstreetmap/osm2pgsql/blob/master/style.lua#L13 # [foo] is substituted by value of tag foo. When substitution is not wished, the [ character can be escaped with \[ in literals z_order_sql="SELECT (CASE [highway] WHEN 'minor' THEN 3 WHEN 'road' THEN 3 WHEN 'unclassified' THEN 3 WHEN 'residential' THEN 3 WHEN 'tertiary_link' THEN 4 WHEN 'tertiary' THEN 4 WHEN 'secondary_link' THEN 6 WHEN 'secondary' THEN 6 WHEN 'primary_link' THEN 7 WHEN 'primary' THEN 7 WHEN 'trunk_link' THEN 8 WHEN 'trunk' THEN 8 WHEN 'motorway_link' THEN 9 WHEN 'motorway' THEN 9 ELSE 0 END) + (CASE WHEN [bridge] IN ('yes', 'true', '1') THEN 10 ELSE 0 END) + (CASE WHEN [tunnel] IN ('yes', 'true', '1') THEN -10 ELSE 0 END) + (CASE WHEN [railway] IS NOT NULL THEN 5 ELSE 0 END) + (CASE WHEN [layer] IS NOT NULL THEN 10 * CAST([layer] AS INTEGER) ELSE 0 END)" [multipolygons] # common attributes # note: for multipolygons, osm_id=yes instantiates a osm_id field for the id of relations # and a osm_way_id field for the id of closed ways. Both fields are exclusively set. osm_id=yes osm_version=no osm_timestamp=yes osm_uid=no osm_user=no osm_changeset=no # keys to report as OGR fields attributes=access,aerialway,aeroway,amenity,area,barrier,building,cycleway,foot,geological,highway,historic,intermittent,landuse,leaf_type,leisure,location,man_made,military,name,natural,pipeline,power,railway,ruins,sport,surface,shop,smoothness,ref,tourism,tracktype,tunnel,waterway,wood:age,wood:density # keys that should NOT be reported in the "other_tags" field ignore=area,created_by,converted_by,source,time,note,openGeoDB:,fixme,FIXME # uncomment to avoid creation of "other_tags" field #other_tags=no # uncomment to create "all_tags" field. "all_tags" and "other_tags" are exclusive #all_tags=yes [multilinestrings] # common attributes osm_id=yes osm_version=no osm_timestamp=yes osm_uid=no osm_user=no osm_changeset=no # keys to report as OGR fields attributes=access,aerialway,aeroway,amenity,area,barrier,building,cycleway,foot,geological,highway,historic,intermittent,landuse,leaf_type,leisure,location,man_made,military,name,natural,pipeline,power,railway,ruins,sport,surface,shop,smoothness,ref,tourism,tracktype,tunnel,waterway,wood:age,wood:density # keys that should NOT be reported in the "other_tags" field ignore=area,created_by,converted_by,source,time,note,openGeoDB:,fixme,FIXME # uncomment to avoid creation of "other_tags" field #other_tags=no # uncomment to create "all_tags" field. "all_tags" and "other_tags" are exclusive #all_tags=yes [other_relations] # common attributes osm_id=yes osm_version=no osm_timestamp=yes osm_uid=no osm_user=no osm_changeset=no # keys to report as OGR fields attributes=access,aerialway,aeroway,amenity,area,barrier,building,cycleway,foot,geological,highway,historic,intermittent,landuse,leaf_type,leisure,location,man_made,military,name,natural,pipeline,power,railway,ruins,sport,surface,shop,smoothness,ref,tourism,tracktype,tunnel,waterway,wood:age,wood:density # keys that should NOT be reported in the "other_tags" field ignore=area,created_by,converted_by,source,time,note,openGeoDB:,fixme,FIXME # uncomment to avoid creation of "other_tags" field #other_tags=no # uncomment to create "all_tags" field. "all_tags" and "other_tags" are exclusive #all_tags=yes mapper-0.8.1.1/src/gdal/ogr_file_format.cpp000066400000000000000000001121601325266516600205200ustar00rootroot00000000000000/* * Copyright 2016-2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "ogr_file_format.h" #include "ogr_file_format_p.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "core/georeferencing.h" #include "core/latlon.h" #include "core/map.h" #include "core/map_color.h" #include "core/map_coord.h" #include "core/map_part.h" #include "core/objects/object.h" #include "core/objects/text_object.h" #include "core/symbols/area_symbol.h" #include "core/symbols/line_symbol.h" #include "core/symbols/point_symbol.h" #include "core/symbols/symbol.h" #include "core/symbols/text_symbol.h" #include "fileformats/file_import_export.h" #include "gdal/gdal_manager.h" // IWYU pragma: no_forward_declare QFile namespace OpenOrienteering { namespace ogr { class OGRDataSourceHDeleter { public: void operator()(OGRDataSourceH data_source) const { OGRReleaseDataSource(data_source); } }; /** A convenience class for OGR C API datasource handles, similar to std::unique_ptr. */ using unique_datasource = std::unique_ptr::type, OGRDataSourceHDeleter>; class OGRFeatureHDeleter { public: void operator()(OGRFeatureH feature) const { OGR_F_Destroy(feature); } }; /** A convenience class for OGR C API feature handles, similar to std::unique_ptr. */ using unique_feature = std::unique_ptr::type, OGRFeatureHDeleter>; class OGRGeometryHDeleter { public: void operator()(OGRGeometryH geometry) const { OGR_G_DestroyGeometry(geometry); } }; /** A convenience class for OGR C API geometry handles, similar to std::unique_ptr. */ using unique_geometry = std::unique_ptr::type, OGRGeometryHDeleter>; } // namespace ogr namespace { void applyPenWidth(OGRStyleToolH tool, LineSymbol* line_symbol) { int is_null; auto pen_width = OGR_ST_GetParamDbl(tool, OGRSTPenWidth, &is_null); if (!is_null) { Q_ASSERT(OGR_ST_GetUnit(tool) == OGRSTUMM); if (pen_width <= 0.01) pen_width = 0.1; line_symbol->setLineWidth(pen_width); } } void applyPenCap(OGRStyleToolH tool, LineSymbol* line_symbol) { int is_null; auto pen_cap = OGR_ST_GetParamStr(tool, OGRSTPenCap, &is_null); if (!is_null) { switch (pen_cap[0]) { case 'p': line_symbol->setCapStyle(LineSymbol::SquareCap); break; case 'r': line_symbol->setCapStyle(LineSymbol::RoundCap); break; default: ; } } } void applyPenJoin(OGRStyleToolH tool, LineSymbol* line_symbol) { int is_null; auto pen_join = OGR_ST_GetParamStr(tool, OGRSTPenJoin, &is_null); if (!is_null) { switch (pen_join[0]) { case 'b': line_symbol->setJoinStyle(LineSymbol::BevelJoin); break; case 'r': line_symbol->setJoinStyle(LineSymbol::RoundJoin); break; default: ; } } } void applyPenPattern(OGRStyleToolH tool, LineSymbol* line_symbol) { int is_null; auto raw_pattern = OGR_ST_GetParamStr(tool, OGRSTPenPattern, &is_null); if (!is_null) { auto pattern = QString::fromLatin1(raw_pattern); auto sub_pattern_re = QRegularExpression(QString::fromLatin1("([0-9.]+)([a-z]*) *([0-9.]+)([a-z]*)")); auto match = sub_pattern_re.match(pattern); double length_0{}, length_1{}; bool ok = match.hasMatch(); if (ok) length_0 = match.capturedRef(1).toDouble(&ok); if (ok) length_1 = match.capturedRef(3).toDouble(&ok); if (ok) { /// \todo Apply units from capture 2 and 4 line_symbol->setDashed(true); line_symbol->setDashLength(qMax(100, qRound(length_0 * 1000))); line_symbol->setBreakLength(qMax(100, qRound(length_1 * 1000))); } else { qDebug("OgrFileFormat: Failed to parse dash pattern '%s'", raw_pattern); } } } #if 0 int getFontSize(const char* font_size_string) { auto pattern = QString::fromLatin1(font_size_string); auto sub_pattern_re = QRegularExpression(QString::fromLatin1("([0-9.]+)([a-z]*)")); auto match = sub_pattern_re.match(pattern); double font_size; bool ok = match.hasMatch(); if (ok) font_size = match.capturedRef(1).toDouble(&ok); if (ok) { auto unit = match.capturedRef(2).toUtf8(); if (!unit.isEmpty()) { if (unit == "pt") { } else if (unit == "px") { } else { qDebug("OgrFileFormat: Unsupported font size unit '%s'", unit.constData()); } } } else { qDebug("OgrFileFormat: Failed to parse font size '%s'", font_size_string); font_size = 0; } return font_size; } #endif void applyLabelAnchor(int anchor, TextObject* text_object) { auto v_align = (anchor - 1) / 3; switch (v_align) { case 0: text_object->setVerticalAlignment(TextObject::AlignBaseline); break; case 1: text_object->setVerticalAlignment(TextObject::AlignVCenter); break; case 2: text_object->setVerticalAlignment(TextObject::AlignTop); break; case 3: text_object->setVerticalAlignment(TextObject::AlignBottom); break; default: Q_UNREACHABLE(); } auto h_align = (anchor - 1) % 3; switch (h_align) { case 0: text_object->setHorizontalAlignment(TextObject::AlignLeft); break; case 1: text_object->setHorizontalAlignment(TextObject::AlignHCenter); break; case 2: text_object->setHorizontalAlignment(TextObject::AlignRight); break; default: Q_UNREACHABLE(); } } QString toPrettyWkt(OGRSpatialReferenceH spatial_reference) { char* srs_wkt_raw = nullptr; OSRExportToPrettyWkt(spatial_reference, &srs_wkt_raw, 0); auto srs_wkt = QString::fromLocal8Bit(srs_wkt_raw); CPLFree(srs_wkt_raw); return srs_wkt; } } // namespace // ### OgrFileFormat ### OgrFileFormat::OgrFileFormat() : FileFormat(OgrFile, "OGR", ::OpenOrienteering::ImportExport::tr("Geospatial vector data"), QString{}, ImportSupported) { for (const auto& extension : GdalManager().supportedVectorExtensions()) addExtension(QString::fromLatin1(extension)); } bool OgrFileFormat::understands(const unsigned char* /*buffer*/, std::size_t /*size*/) const { return true; } Importer* OgrFileFormat::createImporter(QIODevice* stream, Map *map, MapView *view) const { return new OgrFileImport(stream, map, view); } // ### OgrFileImport ### OgrFileImport::OgrFileImport(QIODevice* stream, Map* map, MapView* view, UnitType unit_type) : Importer(stream, map, view) , manager{ OGR_SM_Create(nullptr) } , unit_type{ unit_type } , georeferencing_import_enabled{ true } { GdalManager().configure(); setOption(QLatin1String{ "Separate layers" }, QVariant{ false }); // OGR feature style defaults default_pen_color = new MapColor(tr("Purple"), 0); default_pen_color->setSpotColorName(QLatin1String{"PURPLE"}); default_pen_color->setCmyk({0.2f, 1.0, 0.0, 0.0}); default_pen_color->setRgbFromCmyk(); map->addColor(default_pen_color, 0); auto default_brush_color = new MapColor(default_pen_color->getName() + QLatin1String(" 50%"), 0); default_brush_color->setSpotColorComposition({ {default_pen_color, 0.5f} }); default_brush_color->setCmykFromSpotColors(); default_brush_color->setRgbFromSpotColors(); map->addColor(default_brush_color, 1); default_point_symbol = new PointSymbol(); default_point_symbol->setName(tr("Point")); default_point_symbol->setNumberComponent(0, 1); default_point_symbol->setInnerColor(default_pen_color); default_point_symbol->setInnerRadius(500); // (um) map->addSymbol(default_point_symbol, 0); default_line_symbol = new LineSymbol(); default_line_symbol->setName(tr("Line")); default_line_symbol->setNumberComponent(0, 2); default_line_symbol->setColor(default_pen_color); default_line_symbol->setLineWidth(0.1); // (0.1 mm, nearly cosmetic) default_line_symbol->setCapStyle(LineSymbol::FlatCap); default_line_symbol->setJoinStyle(LineSymbol::MiterJoin); map->addSymbol(default_line_symbol, 1); default_area_symbol = new AreaSymbol(); default_area_symbol->setName(tr("Area")); default_area_symbol->setNumberComponent(0, 3); default_area_symbol->setColor(default_brush_color); map->addSymbol(default_area_symbol, 2); default_text_symbol = new TextSymbol(); default_text_symbol->setName(tr("Text")); default_text_symbol->setNumberComponent(0, 4); default_text_symbol->setColor(default_pen_color); map->addSymbol(default_text_symbol, 3); } OgrFileImport::~OgrFileImport() = default; // not inlined void OgrFileImport::setGeoreferencingImportEnabled(bool enabled) { georeferencing_import_enabled = enabled; } ogr::unique_srs OgrFileImport::srsFromMap() { auto srs = ogr::unique_srs(OSRNewSpatialReference(nullptr)); auto& georef = map->getGeoreferencing(); if (georef.isValid() && !georef.isLocal()) { OSRSetProjCS(srs.get(), "Projected map SRS"); OSRSetWellKnownGeogCS(srs.get(), "WGS84"); auto spec = QByteArray(georef.getProjectedCRSSpec().toLatin1() + " +wktext"); auto error = OSRImportFromProj4(srs.get(), spec); if (!error) return srs; addWarning(tr("Unable to setup \"%1\" SRS for GDAL: %2") .arg(QString::fromLatin1(spec), QString::number(error))); srs.reset(OSRNewSpatialReference(nullptr)); } OSRSetLocalCS(srs.get(), "Local SRS"); return srs; } void OgrFileImport::import(bool load_symbols_only) { auto file = qobject_cast(stream); if (!file) { throw FileFormatException("Internal error"); /// \todo Review design and/or message } auto filename = file->fileName(); // GDAL 2.0: ... = GDALOpenEx(template_path.toLatin1(), GDAL_OF_VECTOR, nullptr, nullptr, nullptr); auto data_source = ogr::unique_datasource(OGROpen(filename.toUtf8().constData(), 0, nullptr)); if (!data_source) { throw FileFormatException(::OpenOrienteering::Importer::tr("Could not read '%1': %2") .arg(filename, QString::fromLatin1(CPLGetLastErrorMsg()))); } if (auto driver = OGR_DS_GetDriver(data_source.get())) { if (auto driver_name = OGR_Dr_GetName(driver)) { map->setSymbolSetId(QString::fromLatin1(driver_name)); } } empty_geometries = 0; no_transformation = 0; failed_transformation = 0; unsupported_geometry_type = 0; too_few_coordinates = 0; if (georeferencing_import_enabled) map_srs = importGeoreferencing(data_source.get()); else map_srs = srsFromMap(); importStyles(data_source.get()); if (!load_symbols_only) { QScopedValueRollback rollback { MapCoord::boundsOffset() }; MapCoord::boundsOffset().reset(true); auto num_layers = OGR_DS_GetLayerCount(data_source.get()); for (int i = 0; i < num_layers; ++i) { auto layer = OGR_DS_GetLayer(data_source.get(), i); if (!layer) { addWarning(tr("Unable to load layer %1.").arg(i)); continue; } if (qstrcmp(OGR_L_GetName(layer), "track_points") == 0) { // Skip GPX track points as points. Track line is separate. /// \todo Use hooks and delegates per file format continue; } auto part = map->getCurrentPart(); if (option(QLatin1String("Separate layers")).toBool()) { if (num_layers > 0) { if (part->getNumObjects() == 0) { part->setName(QString::fromUtf8(OGR_L_GetName(layer))); } else { part = new MapPart(QString::fromUtf8(OGR_L_GetName(layer)), map); auto index = std::size_t(map->getNumParts()); map->addPart(part, index); map->setCurrentPartIndex(index); } } } importLayer(part, layer); } const auto& offset = MapCoord::boundsOffset(); if (!offset.isZero()) { // We need to adjust the georeferencing. auto offset_f = MapCoordF { offset.x / 1000.0, offset.y / 1000.0 }; auto georef = map->getGeoreferencing(); auto ref_point = MapCoordF { georef.getMapRefPoint() }; auto new_projected = georef.toProjectedCoords(ref_point + offset_f); georef.setProjectedRefPoint(new_projected, false); map->setGeoreferencing(georef); } } if (empty_geometries) { addWarning(tr("Unable to load %n objects, reason: %1", nullptr, empty_geometries) .arg(tr("Empty geometry."))); } if (no_transformation) { addWarning(tr("Unable to load %n objects, reason: %1", nullptr, no_transformation) .arg(tr("Can't determine the coordinate transformation: %1").arg(QString::fromUtf8(CPLGetLastErrorMsg())))); } if (failed_transformation) { addWarning(tr("Unable to load %n objects, reason: %1", nullptr, failed_transformation) .arg(tr("Failed to transform the coordinates."))); } if (unsupported_geometry_type) { addWarning(tr("Unable to load %n objects, reason: %1", nullptr, unsupported_geometry_type) .arg(tr("Unknown or unsupported geometry type."))); } if (too_few_coordinates) { addWarning(tr("Unable to load %n objects, reason: %1", nullptr, too_few_coordinates) .arg(tr("Not enough coordinates."))); } } ogr::unique_srs OgrFileImport::importGeoreferencing(OGRDataSourceH data_source) { auto no_srs = true; auto local_srs = ogr::unique_srs { nullptr }; auto suitable_srs = ogr::unique_srs { nullptr }; char* projected_srs_spec = { nullptr }; auto orthographic = ogr::unique_srs { OSRNewSpatialReference(nullptr) }; OSRSetProjCS(orthographic.get(), "Orthographic SRS"); OSRSetWellKnownGeogCS(orthographic.get(), "WGS84"); OSRSetOrthographic(orthographic.get(), 0.0, 0.0, 0.0, 0.0); // Find any SRS which can be transformed to our orthographic SRS, // but prefer a projected SRS. auto num_layers = OGR_DS_GetLayerCount(data_source); for (int i = 0; i < num_layers; ++i) { auto layer = OGR_DS_GetLayer(data_source, i); if (!layer) continue; auto spatial_reference = OGR_L_GetSpatialRef(layer); if (!spatial_reference) continue; no_srs = false; if (OSRIsLocal(spatial_reference)) { if (!local_srs) local_srs.reset(OSRClone(spatial_reference)); continue; } auto transformation = OCTNewCoordinateTransformation(spatial_reference, orthographic.get()); if (!transformation) { addWarning(tr("Cannot use this spatial reference:\n%1").arg(toPrettyWkt(spatial_reference))); continue; } OCTDestroyCoordinateTransformation(transformation); if (OSRIsProjected(spatial_reference)) { char *srs_spec = nullptr; auto error = OSRExportToProj4(spatial_reference, &srs_spec); if (!error) { projected_srs_spec = srs_spec; // transfer ownership suitable_srs.reset(OSRClone(spatial_reference)); break; } CPLFree(srs_spec); } if (!suitable_srs) suitable_srs.reset(OSRClone(spatial_reference)); } if (projected_srs_spec) { // Found a suitable projected SRS auto georef = map->getGeoreferencing(); // copy georef.setProjectedCRS(QStringLiteral("PROJ.4"), QString::fromLatin1(projected_srs_spec)); map->setGeoreferencing(georef); CPLFree(projected_srs_spec); return suitable_srs; } else if (suitable_srs) { // Found a suitable SRS but it is not projected. // Setting up a local orthographic projection. auto center = calcAverageLatLon(data_source); auto latitude = 0.001 * qRound(1000 * center.latitude()); auto longitude = 0.001 * qRound(1000 * center.longitude()); auto ortho_georef = Georeferencing(); ortho_georef.setScaleDenominator(int(map->getScaleDenominator())); ortho_georef.setProjectedCRS(QString{}, QString::fromLatin1("+proj=ortho +datum=WGS84 +ellps=WGS84 +units=m +lat_0=%1 +lon_0=%2 +no_defs") .arg(latitude, 0, 'f') .arg(longitude, 0, 'f') ); ortho_georef.setProjectedRefPoint({}, false); ortho_georef.setDeclination(map->getGeoreferencing().getDeclination()); map->setGeoreferencing(ortho_georef); return srsFromMap(); } else if (local_srs || no_srs) { auto georef = Georeferencing(); georef.setScaleDenominator(int(map->getScaleDenominator())); georef.setDeclination(map->getGeoreferencing().getDeclination()); map->setGeoreferencing(georef); return local_srs ? std::move(local_srs) : srsFromMap(); } else { throw FileFormatException(tr("The geospatial data has no suitable spatial reference.")); } } void OgrFileImport::importStyles(OGRDataSourceH data_source) { //auto style_table = OGR_DS_GetStyleTable(data_source); Q_UNUSED(data_source) } void OgrFileImport::importLayer(MapPart* map_part, OGRLayerH layer) { Q_ASSERT(map_part); auto feature_definition = OGR_L_GetLayerDefn(layer); OGR_L_ResetReading(layer); while (auto feature = ogr::unique_feature(OGR_L_GetNextFeature(layer))) { auto geometry = OGR_F_GetGeometryRef(feature.get()); if (!geometry || OGR_G_IsEmpty(geometry)) { ++empty_geometries; continue; } importFeature(map_part, feature_definition, feature.get(), geometry); } } void OgrFileImport::importFeature(MapPart* map_part, OGRFeatureDefnH feature_definition, OGRFeatureH feature, OGRGeometryH geometry) { to_map_coord = &OgrFileImport::fromProjected; auto new_srs = OGR_G_GetSpatialReference(geometry); if (new_srs && data_srs != new_srs) { // New SRS, indeed. auto transformation = ogr::unique_transformation{ OCTNewCoordinateTransformation(new_srs, map_srs.get()) }; if (!transformation) { ++no_transformation; return; } // Commit change to data srs and coordinate transformation data_srs = new_srs; data_transform = std::move(transformation); } if (new_srs) { auto error = OGR_G_Transform(geometry, data_transform.get()); if (error) { ++failed_transformation; return; } } else if (unit_type == UnitOnPaper) { to_map_coord = &OgrFileImport::fromDrawing; } auto objects = importGeometry(feature, geometry); for (auto object : objects) { map_part->addObject(object); if (!feature_definition) continue; auto num_fields = OGR_FD_GetFieldCount(feature_definition); for (int i = 0; i < num_fields; ++i) { auto value = OGR_F_GetFieldAsString(feature, i); if (value && qstrlen(value) > 0) { auto field_definition = OGR_FD_GetFieldDefn(feature_definition, i); object->setTag(QString::fromUtf8(OGR_Fld_GetNameRef(field_definition)), QString::fromUtf8(value)); } } } } OgrFileImport::ObjectList OgrFileImport::importGeometry(OGRFeatureH feature, OGRGeometryH geometry) { ObjectList result; auto geometry_type = wkbFlatten(OGR_G_GetGeometryType(geometry)); switch (geometry_type) { case OGRwkbGeometryType::wkbPoint: if (auto object = importPointGeometry(feature, geometry)) result = { object }; break; case OGRwkbGeometryType::wkbLineString: if (auto object = importLineStringGeometry(feature, geometry)) result = { object }; break; case OGRwkbGeometryType::wkbPolygon: if (auto object = importPolygonGeometry(feature, geometry)) result = { object }; break; case OGRwkbGeometryType::wkbGeometryCollection: case OGRwkbGeometryType::wkbMultiLineString: case OGRwkbGeometryType::wkbMultiPoint: case OGRwkbGeometryType::wkbMultiPolygon: result = importGeometryCollection(feature, geometry); break; default: qDebug("OgrFileImport: Unknown or unsupported geometry type: %d", geometry_type); ++unsupported_geometry_type; } return result; } OgrFileImport::ObjectList OgrFileImport::importGeometryCollection(OGRFeatureH feature, OGRGeometryH geometry) { ObjectList result; auto num_geometries = OGR_G_GetGeometryCount(geometry); result.reserve(std::size_t(num_geometries)); for (int i = 0; i < num_geometries; ++i) { auto tmp = importGeometry(feature, OGR_G_GetGeometryRef(geometry, i)); result.insert(result.end(), begin(tmp), end(tmp)); } return result; } Object* OgrFileImport::importPointGeometry(OGRFeatureH feature, OGRGeometryH geometry) { auto style = OGR_F_GetStyleString(feature); auto symbol = getSymbol(Symbol::Point, style); if (symbol->getType() == Symbol::Point) { auto object = new PointObject(symbol); object->setPosition(toMapCoord(OGR_G_GetX(geometry, 0), OGR_G_GetY(geometry, 0))); return object; } else if (symbol->getType() == Symbol::Text) { const auto& description = symbol->getDescription(); auto length = description.length(); auto split = description.indexOf(QLatin1Char(' ')); Q_ASSERT(split > 0); Q_ASSERT(split < length); auto label = description.right(length - split - 1); if (label.startsWith(QLatin1Char{'{'}) && label.endsWith(QLatin1Char{'}'})) { label.remove(0, 1); label.chop(1); int index = OGR_F_GetFieldIndex(feature, label.toLatin1().constData()); if (index >= 0) { label = QString::fromUtf8(OGR_F_GetFieldAsString(feature, index)); } } if (!label.isEmpty()) { auto object = new TextObject(symbol); object->setAnchorPosition(toMapCoord(OGR_G_GetX(geometry, 0), OGR_G_GetY(geometry, 0))); // DXF observation label.replace(QRegularExpression(QString::fromLatin1("(\\\\[^;]*;)*"), QRegularExpression::MultilineOption), QString{}); label.replace(QLatin1String("^I"), QLatin1String("\t")); object->setText(label); bool ok; auto anchor = QStringRef(&description, 1, 2).toInt(&ok); if (ok) { applyLabelAnchor(anchor, object); } auto angle = QStringRef(&description, 3, split-3).toFloat(&ok); if (ok) { object->setRotation(qDegreesToRadians(angle)); } return object; } } return nullptr; } PathObject* OgrFileImport::importLineStringGeometry(OGRFeatureH feature, OGRGeometryH geometry) { auto managed_geometry = ogr::unique_geometry(nullptr); if (OGR_G_GetGeometryType(geometry) != wkbLineString) { geometry = OGR_G_ForceToLineString(OGR_G_Clone(geometry)); managed_geometry.reset(geometry); } auto num_points = OGR_G_GetPointCount(geometry); if (num_points < 2) { ++too_few_coordinates; return nullptr; } auto style = OGR_F_GetStyleString(feature); auto object = new PathObject(getSymbol(Symbol::Line, style)); for (int i = 0; i < num_points; ++i) { object->addCoordinate(toMapCoord(OGR_G_GetX(geometry, i), OGR_G_GetY(geometry, i))); } return object; } PathObject* OgrFileImport::importPolygonGeometry(OGRFeatureH feature, OGRGeometryH geometry) { auto num_geometries = OGR_G_GetGeometryCount(geometry); if (num_geometries < 1) { ++too_few_coordinates; return nullptr; } auto outline = OGR_G_GetGeometryRef(geometry, 0); auto managed_outline = ogr::unique_geometry(nullptr); if (OGR_G_GetGeometryType(outline) != wkbLineString) { outline = OGR_G_ForceToLineString(OGR_G_Clone(outline)); managed_outline.reset(outline); } auto num_points = OGR_G_GetPointCount(outline); if (num_points < 3) { ++too_few_coordinates; return nullptr; } auto style = OGR_F_GetStyleString(feature); auto object = new PathObject(getSymbol(Symbol::Area, style)); for (int i = 0; i < num_points; ++i) { object->addCoordinate(toMapCoord(OGR_G_GetX(outline, i), OGR_G_GetY(outline, i))); } for (int g = 1; g < num_geometries; ++g) { bool start_new_part = true; auto hole = /*OGR_G_ForceToLineString*/(OGR_G_GetGeometryRef(geometry, g)); auto num_points = OGR_G_GetPointCount(hole); for (int i = 0; i < num_points; ++i) { object->addCoordinate(toMapCoord(OGR_G_GetX(hole, i), OGR_G_GetY(hole, i)), start_new_part); start_new_part = false; } } object->closeAllParts(); return object; } Symbol* OgrFileImport::getSymbol(Symbol::Type type, const char* raw_style_string) { auto style_string = QByteArray::fromRawData(raw_style_string, qstrlen(raw_style_string)); Symbol* symbol = nullptr; switch (type) { case Symbol::Point: case Symbol::Text: symbol = point_symbols.value(style_string); if (!symbol) symbol = getSymbolForPointGeometry(style_string); if (!symbol) symbol = default_point_symbol; break; case Symbol::Combined: /// \todo // fall through case Symbol::Line: symbol = line_symbols.value(style_string); if (!symbol) symbol = getLineSymbol(style_string); if (!symbol) symbol = default_line_symbol; break; case Symbol::Area: symbol = area_symbols.value(style_string); if (!symbol) symbol = getAreaSymbol(style_string); if (!symbol) symbol = default_area_symbol; break; case Symbol::NoSymbol: case Symbol::AllSymbols: Q_UNREACHABLE(); } Q_ASSERT(symbol); return symbol; } MapColor* OgrFileImport::makeColor(OGRStyleToolH tool, const char* color_string) { auto key = QByteArray::fromRawData(color_string, qstrlen(color_string)); auto color = colors.value(key); if (!color) { int r, g, b, a; auto success = OGR_ST_GetRGBFromString(tool, color_string, &r, &g, &b, &a); if (!success) { color = default_pen_color; } else if (a > 0) { color = new MapColor(QString::fromUtf8(color_string), map->getNumColors()); color->setRgb(QColor{ r, g, b }); color->setCmykFromRgb(); map->addColor(color, map->getNumColors()); } key.detach(); colors.insert(key, color); } return color; } void OgrFileImport::applyPenColor(OGRStyleToolH tool, LineSymbol* line_symbol) { int is_null; auto color_string = OGR_ST_GetParamStr(tool, OGRSTPenColor, &is_null); if (!is_null) { auto color = makeColor(tool, color_string); if (color) line_symbol->setColor(color); else line_symbol->setHidden(true); } } void OgrFileImport::applyBrushColor(OGRStyleToolH tool, AreaSymbol* area_symbol) { int is_null; auto color_string = OGR_ST_GetParamStr(tool, OGRSTBrushFColor, &is_null); if (!is_null) { auto color = makeColor(tool, color_string); if (color) area_symbol->setColor(color); else area_symbol->setHidden(true); } } Symbol* OgrFileImport::getSymbolForPointGeometry(const QByteArray& style_string) { if (style_string.isEmpty()) return nullptr; auto manager = this->manager.get(); auto data = style_string.constData(); if (!OGR_SM_InitStyleString(manager, data)) return nullptr; auto num_parts = OGR_SM_GetPartCount(manager, data); if (!num_parts) return nullptr; Symbol* symbol = nullptr; for (int i = 0; !symbol && i < num_parts; ++i) { auto tool = OGR_SM_GetPart(manager, i, nullptr); if (!tool) continue; OGR_ST_SetUnit(tool, OGRSTUMM, map->getScaleDenominator()); auto type = OGR_ST_GetType(tool); switch (type) { case OGRSTCBrush: case OGRSTCPen: case OGRSTCSymbol: symbol = getSymbolForOgrSymbol(tool, style_string); break; case OGRSTCLabel: symbol = getSymbolForLabel(tool, style_string); break; default: ; } OGR_ST_Destroy(tool); } return symbol; } LineSymbol* OgrFileImport::getLineSymbol(const QByteArray& style_string) { if (style_string.isEmpty()) return nullptr; auto manager = this->manager.get(); auto data = style_string.constData(); if (!OGR_SM_InitStyleString(manager, data)) return nullptr; auto num_parts = OGR_SM_GetPartCount(manager, data); if (!num_parts) return nullptr; LineSymbol* symbol = nullptr; for (int i = 0; !symbol && i < num_parts; ++i) { auto tool = OGR_SM_GetPart(manager, i, nullptr); if (!tool) continue; OGR_ST_SetUnit(tool, OGRSTUMM, map->getScaleDenominator()); auto type = OGR_ST_GetType(tool); switch (type) { case OGRSTCPen: symbol = getSymbolForPen(tool, style_string); break; default: ; } OGR_ST_Destroy(tool); } return symbol; } AreaSymbol* OgrFileImport::getAreaSymbol(const QByteArray& style_string) { if (style_string.isEmpty()) return nullptr; auto manager = this->manager.get(); auto data = style_string.constData(); if (!OGR_SM_InitStyleString(manager, data)) return nullptr; auto num_parts = OGR_SM_GetPartCount(manager, data); if (!num_parts) return nullptr; AreaSymbol* symbol = nullptr; for (int i = 0; !symbol && i < num_parts; ++i) { auto tool = OGR_SM_GetPart(manager, i, nullptr); if (!tool) continue; OGR_ST_SetUnit(tool, OGRSTUMM, map->getScaleDenominator()); auto type = OGR_ST_GetType(tool); switch (type) { case OGRSTCBrush: symbol = getSymbolForBrush(tool, style_string); break; default: ; } OGR_ST_Destroy(tool); } return symbol; } PointSymbol* OgrFileImport::getSymbolForOgrSymbol(OGRStyleToolH tool, const QByteArray& style_string) { auto raw_tool_key = OGR_ST_GetStyleString(tool); auto tool_key = QByteArray::fromRawData(raw_tool_key, qstrlen(raw_tool_key)); auto symbol = point_symbols.value(tool_key); if (symbol && symbol->getType() == Symbol::Point) return static_cast(symbol); int color_key; switch (OGR_ST_GetType(tool)) { case OGRSTCBrush: color_key = OGRSTBrushFColor; break; case OGRSTCPen: color_key = OGRSTPenColor; break; case OGRSTCSymbol: color_key = OGRSTSymbolColor; break; default: return nullptr; }; int is_null; auto color_string = OGR_ST_GetParamStr(tool, color_key, &is_null); if (is_null) return nullptr; auto point_symbol = static_cast(default_point_symbol->duplicate()); auto color = makeColor(tool, color_string); if (color) point_symbol->setInnerColor(color); else point_symbol->setHidden(true); auto key = style_string; key.detach(); point_symbols.insert(key, point_symbol); if (key != tool_key) { tool_key.detach(); point_symbols.insert(tool_key, point_symbol); } map->addSymbol(point_symbol, map->getNumSymbols()); return point_symbol; } TextSymbol* OgrFileImport::getSymbolForLabel(OGRStyleToolH tool, const QByteArray& /*style_string*/) { Q_ASSERT(OGR_ST_GetType(tool) == OGRSTCLabel); int is_null; auto label_string = OGR_ST_GetParamStr(tool, OGRSTLabelTextString, &is_null); if (is_null) return nullptr; auto color_string = OGR_ST_GetParamStr(tool, OGRSTLabelFColor, &is_null); auto font_size_string = OGR_ST_GetParamStr(tool, OGRSTLabelSize, &is_null); // Don't use the style string as a key: The style contains the label. QByteArray key; key.reserve(qstrlen(color_string) + qstrlen(font_size_string) + 1); key.append(color_string); key.append(font_size_string); auto text_symbol = static_cast(text_symbols.value(key)); if (!text_symbol) { text_symbol = static_cast(default_text_symbol->duplicate()); auto color = makeColor(tool, color_string); if (color) text_symbol->setColor(color); else text_symbol->setHidden(true); auto font_size = OGR_ST_GetParamDbl(tool, OGRSTLabelSize, &is_null); if (!is_null && font_size > 0.0) text_symbol->scale(font_size / text_symbol->getFontSize()); key.detach(); text_symbols.insert(key, text_symbol); map->addSymbol(text_symbol, map->getNumSymbols()); } auto anchor = qBound(1, OGR_ST_GetParamNum(tool, OGRSTLabelAnchor, &is_null), 12); if (is_null) anchor = 1; auto angle = OGR_ST_GetParamDbl(tool, OGRSTLabelAngle, &is_null); if (is_null) angle = 0.0; QString description; description.reserve(qstrlen(label_string) + 100); description.append(QString::number(100 + anchor)); description.append(QString::number(angle, 'g', 1)); description.append(QLatin1Char(' ')); description.append(QString::fromUtf8(label_string)); text_symbol->setDescription(description); return text_symbol; } LineSymbol* OgrFileImport::getSymbolForPen(OGRStyleToolH tool, const QByteArray& style_string) { Q_ASSERT(OGR_ST_GetType(tool) == OGRSTCPen); auto raw_tool_key = OGR_ST_GetStyleString(tool); auto tool_key = QByteArray::fromRawData(raw_tool_key, qstrlen(raw_tool_key)); auto symbol = line_symbols.value(tool_key); if (symbol && symbol->getType() == Symbol::Line) return static_cast(symbol); auto line_symbol = static_cast(default_line_symbol->duplicate()); applyPenColor(tool, line_symbol); applyPenWidth(tool, line_symbol); applyPenCap(tool, line_symbol); applyPenJoin(tool, line_symbol); applyPenPattern(tool, line_symbol); auto key = style_string; key.detach(); line_symbols.insert(key, line_symbol); if (key != tool_key) { tool_key.detach(); line_symbols.insert(tool_key, line_symbol); } map->addSymbol(line_symbol, map->getNumSymbols()); return line_symbol; } AreaSymbol* OgrFileImport::getSymbolForBrush(OGRStyleToolH tool, const QByteArray& style_string) { Q_ASSERT(OGR_ST_GetType(tool) == OGRSTCBrush); auto raw_tool_key = OGR_ST_GetStyleString(tool); auto tool_key = QByteArray::fromRawData(raw_tool_key, qstrlen(raw_tool_key)); auto symbol = area_symbols.value(tool_key); if (symbol && symbol->getType() == Symbol::Area) return static_cast(symbol); auto area_symbol = static_cast(default_area_symbol->duplicate()); applyBrushColor(tool, area_symbol); auto key = style_string; key.detach(); area_symbols.insert(key, area_symbol); if (key != tool_key) { tool_key.detach(); area_symbols.insert(tool_key, area_symbol); } map->addSymbol(area_symbol, map->getNumSymbols()); return area_symbol; } MapCoord OgrFileImport::fromDrawing(double x, double y) const { return MapCoord::load(x, -y, MapCoord::Flags{}); } MapCoord OgrFileImport::fromProjected(double x, double y) const { return MapCoord::load(map->getGeoreferencing().toMapCoordF(QPointF{ x, y }), MapCoord::Flags{}); } // static bool OgrFileImport::checkGeoreferencing(QFile& file, const Georeferencing& georef) { if (georef.isLocal() || !georef.isValid()) return false; GdalManager(); auto filename = file.fileName(); // GDAL 2.0: ... = GDALOpenEx(template_path.toLatin1(), GDAL_OF_VECTOR, nullptr, nullptr, nullptr); auto data_source = ogr::unique_datasource(OGROpen(filename.toUtf8().constData(), 0, nullptr)); if (!data_source) { throw FileFormatException(::OpenOrienteering::Importer::tr("Could not read '%1': %2") .arg(filename, QString::fromLatin1(CPLGetLastErrorMsg()))); } return checkGeoreferencing(data_source.get(), georef); } // static bool OgrFileImport::checkGeoreferencing(OGRDataSourceH data_source, const Georeferencing& georef) { auto spec = QByteArray(georef.getProjectedCRSSpec().toLatin1() + " +wktext"); auto map_srs = ogr::unique_srs { OSRNewSpatialReference(nullptr) }; OSRSetProjCS(map_srs.get(), "Projected map SRS"); OSRSetWellKnownGeogCS(map_srs.get(), "WGS84"); OSRImportFromProj4(map_srs.get(), spec.constData()); bool suitable_srs_found = false; auto num_layers = OGR_DS_GetLayerCount(data_source); for (int i = 0; i < num_layers; ++i) { if (auto layer = OGR_DS_GetLayer(data_source, i)) { if (auto spatial_reference = OGR_L_GetSpatialRef(layer)) { auto transformation = OCTNewCoordinateTransformation(spatial_reference, map_srs.get()); if (!transformation) { qDebug("Failed to transform this SRS:\n%s", qPrintable(toPrettyWkt(spatial_reference))); return false; } OCTDestroyCoordinateTransformation(transformation); suitable_srs_found = true; } } } return suitable_srs_found; } // static LatLon OgrFileImport::calcAverageLatLon(QFile& file) { GdalManager(); auto filename = file.fileName(); // GDAL 2.0: ... = GDALOpenEx(template_path.toLatin1(), GDAL_OF_VECTOR, nullptr, nullptr, nullptr); auto data_source = ogr::unique_datasource(OGROpen(filename.toUtf8().constData(), 0, nullptr)); if (!data_source) { throw FileFormatException(::OpenOrienteering::Importer::tr("Could not read '%1': %2") .arg(filename, QString::fromLatin1(CPLGetLastErrorMsg()))); } return calcAverageLatLon(data_source.get()); } // static LatLon OgrFileImport::calcAverageLatLon(OGRDataSourceH data_source) { auto geo_srs = ogr::unique_srs { OSRNewSpatialReference(nullptr) }; OSRSetWellKnownGeogCS(geo_srs.get(), "WGS84"); auto num_coords = 0u; double x = 0, y = 0; auto num_layers = OGR_DS_GetLayerCount(data_source); for (int i = 0; i < num_layers; ++i) { if (auto layer = OGR_DS_GetLayer(data_source, i)) { auto spatial_reference = OGR_L_GetSpatialRef(layer); if (!spatial_reference) continue; auto transformation = ogr::unique_transformation{ OCTNewCoordinateTransformation(spatial_reference, geo_srs.get()) }; if (!transformation) continue; OGR_L_ResetReading(layer); while (auto feature = ogr::unique_feature(OGR_L_GetNextFeature(layer))) { auto geometry = OGR_F_GetGeometryRef(feature.get()); if (!geometry || OGR_G_IsEmpty(geometry)) continue; auto error = OGR_G_Transform(geometry, transformation.get()); if (error) continue; auto num_points = OGR_G_GetPointCount(geometry); for (int i = 0; i < num_points; ++i) { x += OGR_G_GetX(geometry, i); y += OGR_G_GetY(geometry, i); ++num_coords; } } } } return num_coords ? LatLon{ y / num_coords, x / num_coords } : LatLon{}; } } // namespace OpenOrienteering mapper-0.8.1.1/src/gdal/ogr_file_format.h000066400000000000000000000036501325266516600201700ustar00rootroot00000000000000/* * Copyright 2016-2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_OGR_FILE_FORMAT_H #define OPENORIENTEERING_OGR_FILE_FORMAT_H #include #include "fileformats/file_format.h" class QIODevice; namespace OpenOrienteering { class Importer; class Map; class MapView; /** * A FileFormat for geospatial vector data supported by OGR. * * Geospatial vector data cannot be loaded as a regular (OpenOrienteering) Map * because it has no scale. However, it typically has a spatial reference, and * so it can be imported into an existing map. This is the major reason for * implementing the OGR support as a FileFormat. */ class OgrFileFormat : public FileFormat { public: /** * Constructs a new OgrFileFormat. */ OgrFileFormat(); /** * Always returns true. * * There is no cheap way to determine the answer via OGR. */ bool understands(const unsigned char* buffer, std::size_t size) const override; /** * Creates an importer object and configures it for the given input stream * and output map and view. */ Importer* createImporter(QIODevice* stream, Map *map, MapView *view) const override; }; } // namespace OpenOrienteering #endif // OPENORIENTEERING_OGR_FILE_FORMAT_H mapper-0.8.1.1/src/gdal/ogr_file_format_p.h000066400000000000000000000172601325266516600205110ustar00rootroot00000000000000/* * Copyright 2016-2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_OGR_FILE_FORMAT_P_H #define OPENORIENTEERING_OGR_FILE_FORMAT_P_H #include #include #include #include // The GDAL/OGR C API is more stable than the C++ API. #include #include #include "core/map_coord.h" #include "core/symbols/symbol.h" #include "fileformats/file_import_export.h" class QFile; namespace OpenOrienteering { class AreaSymbol; class Georeferencing; class LatLon; class LineSymbol; class MapColor; class MapPart; class Object; class PathObject; class PointObject; class PointSymbol; class TextSymbol; namespace ogr { class OGRCoordinateTransformationHDeleter { public: void operator()(OGRCoordinateTransformationH ct) const { OCTDestroyCoordinateTransformation(ct); } }; /** * A convenience class for OGR C API coordinate transformation handles, * similar to std::unique_ptr. */ using unique_transformation = std::unique_ptr::type, OGRCoordinateTransformationHDeleter>; class OGRSpatialReferenceHDeleter { public: void operator()(OGRSpatialReferenceH srs) const { OSRDestroySpatialReference(srs); } }; /** * A convenience class for OGR C API SRS handles, similar to std::unique_ptr. */ using unique_srs = std::unique_ptr::type, OGRSpatialReferenceHDeleter>; class OGRStyleMgrHDeleter { public: void operator()(OGRStyleMgrH manager) const { OGR_SM_Destroy(manager); } }; /** A convenience class for OGR C API feature handles, similar to std::unique_ptr. */ using unique_stylemanager = std::unique_ptr::type, OGRStyleMgrHDeleter>; } /** * An Importer for geospatial vector data supported by OGR. * * OGR needs to know the filename. The filename can be either derived from * a QFile passed as stream to the constructor, or set directly through the * option "filename". * * The option "separate_layers" will cause OGR layers to be imported as distinct * map parts if set to true. */ class OgrFileImport : public Importer { Q_DECLARE_TR_FUNCTIONS(OpenOrienteering::OgrFileImport) public: /** * The unit type indicates the coordinate system the data units refers to. */ enum UnitType { UnitOnGround, ///< Data refers to real dimensions. Includes geograghic CS. UnitOnPaper ///< Data refers to dimensions in the (printed) map. }; /** * A Pointer to a function which creates a MapCoordF from double coordinates. */ using MapCoordConstructor = MapCoord (OgrFileImport::*)(double, double) const; /** * Constructs a new importer. */ OgrFileImport(QIODevice* stream, Map *map, MapView *view, UnitType unit_type = UnitOnGround); ~OgrFileImport() override; /** * Enables the import of georeferencing from the geospatial data. * * If this import is not enabled, the georeferencing of the Map given to * the constructor will be used instead. */ void setGeoreferencingImportEnabled(bool enabled); /** * Tests if the file's spatial references can be used with the given georeferencing. * * This returns true only if all layers' spatial references can be * transformed to the spatial reference systems represented by georef. * It will always return false for a local or invalid Georeferencing. */ static bool checkGeoreferencing(QFile& file, const Georeferencing& georef); /** * Calculates the average geographic coordinates (WGS84) of the file. */ static LatLon calcAverageLatLon(QFile& file); protected: ogr::unique_srs srsFromMap(); /** * Tests if the file's spatial references can be used with the given georeferencing. * * This returns true only if all layers' spatial references can be * transformed to the spatial reference systems represented by georef. * It will always return false for a local or invalid Georeferencing. */ static bool checkGeoreferencing(OGRDataSourceH data_source, const Georeferencing& georef); void import(bool load_symbols_only) override; ogr::unique_srs importGeoreferencing(OGRDataSourceH data_source); void importStyles(OGRDataSourceH data_source); void importLayer(MapPart* map_part, OGRLayerH layer); void importFeature(MapPart* map_part, OGRFeatureDefnH feature_definition, OGRFeatureH feature, OGRGeometryH geometry); using ObjectList = std::vector; ObjectList importGeometry(OGRFeatureH feature, OGRGeometryH geometry); ObjectList importGeometryCollection(OGRFeatureH feature, OGRGeometryH geometry); Object* importPointGeometry(OGRFeatureH feature, OGRGeometryH geometry); PathObject* importLineStringGeometry(OGRFeatureH feature, OGRGeometryH geometry); PathObject* importPolygonGeometry(OGRFeatureH feature, OGRGeometryH geometry); Symbol* getSymbol(Symbol::Type type, const char* raw_style_string); MapColor* makeColor(OGRStyleToolH tool, const char* color_string); void applyPenColor(OGRStyleToolH tool, LineSymbol* line_symbol); void applyBrushColor(OGRStyleToolH tool, AreaSymbol* area_symbol); MapCoord toMapCoord(double x, double y) const; /** * A MapCoordConstructor which interpretes the given coordinates in millimeters on paper. */ MapCoord fromDrawing(double x, double y) const; /** * A MapCoordConstructor which interpretes the given coordinates as projected. */ MapCoord fromProjected(double x, double y) const; static LatLon calcAverageLatLon(OGRDataSourceH data_source); private: Symbol* getSymbolForPointGeometry(const QByteArray& style_string); LineSymbol* getLineSymbol(const QByteArray& style_string); AreaSymbol* getAreaSymbol(const QByteArray& style_string); PointSymbol* getSymbolForOgrSymbol(OGRStyleToolH tool, const QByteArray& style_string); TextSymbol* getSymbolForLabel(OGRStyleToolH tool, const QByteArray& style_string); LineSymbol* getSymbolForPen(OGRStyleToolH tool, const QByteArray& style_string); AreaSymbol* getSymbolForBrush(OGRStyleToolH tool, const QByteArray& style_string); QHash point_symbols; PointSymbol* default_point_symbol; QHash text_symbols; TextSymbol* default_text_symbol; QHash line_symbols; LineSymbol* default_line_symbol; QHash area_symbols; AreaSymbol* default_area_symbol; QHash colors; MapColor* default_pen_color; MapCoordConstructor to_map_coord; ogr::unique_srs map_srs; OGRSpatialReferenceH data_srs; ogr::unique_transformation data_transform; ogr::unique_stylemanager manager; int empty_geometries; int no_transformation; int failed_transformation; int unsupported_geometry_type; int too_few_coordinates; UnitType unit_type; bool georeferencing_import_enabled; }; // ### inline code ### inline MapCoord OgrFileImport::toMapCoord(double x, double y) const { return (this->*to_map_coord)(x, y); } } // namespace OpenOrienteering #endif // OPENORIENTEERING_OGR_FILE_FORMAT_P_H mapper-0.8.1.1/src/gdal/ogr_template.cpp000066400000000000000000000335341325266516600200530ustar00rootroot00000000000000/* * Copyright 2016-2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "ogr_template.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "core/georeferencing.h" #include "core/latlon.h" #include "core/map.h" #include "core/map_coord.h" #include "core/objects/object.h" #include "fileformats/file_format.h" #include "gdal/gdal_manager.h" #include "gdal/ogr_file_format_p.h" #include "gui/georeferencing_dialog.h" #include "sensors/gps_track.h" #include "templates/template.h" #include "templates/template_positioning_dialog.h" #include "templates/template_track.h" namespace OpenOrienteering { namespace { namespace literal { const auto crs_spec = QLatin1String("crs_spec"); const auto georeferencing = QLatin1String("georeferencing"); const auto projected_crs_spec = QLatin1String("projected_crs_spec"); } std::unique_ptr getDataGeoreferencing(QFile& file, const Georeferencing& initial_georef) { Map tmp_map; tmp_map.setGeoreferencing(initial_georef); OgrFileImport importer{ &file, &tmp_map, nullptr, OgrFileImport::UnitOnGround}; importer.setGeoreferencingImportEnabled(true); importer.doImport(true); return std::make_unique(tmp_map.getGeoreferencing()); } bool preserveRefPoints(Georeferencing& data_georef, const Georeferencing& initial_georef) { // Keep a configured local reference point from initial_georef? auto data_crs_spec = data_georef.getProjectedCRSSpec(); if ((!initial_georef.isValid() || initial_georef.isLocal()) && data_georef.isValid() && !data_georef.isLocal() && data_georef.getProjectedRefPoint() == QPointF{} && data_georef.getMapRefPoint() == MapCoord{} && data_crs_spec.contains(QLatin1String("+proj=ortho")) && !data_crs_spec.contains(QLatin1String("+x_0=")) && !data_crs_spec.contains(QLatin1String("+y_0=")) ) { data_crs_spec.append(QString::fromLatin1(" +x_0=%1 +y_0=%2") .arg(initial_georef.getProjectedRefPoint().x(), 0, 'f', 2) .arg(initial_georef.getProjectedRefPoint().y(), 0, 'f', 2) ); data_georef.setProjectedCRS({}, data_crs_spec); data_georef.setProjectedRefPoint(initial_georef.getProjectedRefPoint()); data_georef.setMapRefPoint(initial_georef.getMapRefPoint()); return true; } return false; } } // namespace const std::vector& OgrTemplate::supportedExtensions() { return GdalManager().supportedVectorExtensions(); } OgrTemplate::OgrTemplate(const QString& path, Map* map) : TemplateMap(path, map) { // nothing else const Georeferencing& georef = map->getGeoreferencing(); connect(&georef, &Georeferencing::projectionChanged, this, &OgrTemplate::mapTransformationChanged); connect(&georef, &Georeferencing::transformationChanged, this, &OgrTemplate::mapTransformationChanged); } OgrTemplate::~OgrTemplate() { if (template_state == Loaded) unloadTemplateFile(); } const char* OgrTemplate::getTemplateType() const { return "OgrTemplate"; } std::unique_ptr OgrTemplate::makeOrthographicGeoreferencing(QFile& file) { // Is the template's SRS orthographic, or can it be converted? /// \todo Use the template's datum etc. instead of WGS84? auto georef = std::make_unique(); georef->setScaleDenominator(int(map->getGeoreferencing().getScaleDenominator())); georef->setProjectedCRS(QString{}, QStringLiteral("+proj=ortho +datum=WGS84 +ellps=WGS84 +units=m +no_defs")); if (OgrFileImport::checkGeoreferencing(file, *georef)) { auto center = OgrFileImport::calcAverageLatLon(file); georef->setProjectedCRS(QString{}, QString::fromLatin1("+proj=ortho +datum=WGS84 +ellps=WGS84 +units=m +lat_0=%1 +lon_0=%2 +no_defs") .arg(center.latitude()).arg(center.longitude())); georef->setProjectedRefPoint({}, false); } else { georef.reset(); } return georef; } bool OgrTemplate::preLoadConfiguration(QWidget* dialog_parent) try { is_georeferenced = false; explicit_georef.reset(); track_crs_spec.clear(); transform = {}; updateTransformationMatrices(); auto ends_with_any_of = [](const QString& path, const std::vector& list) -> bool { using namespace std; return any_of(begin(list), end(list), [path](const QByteArray& extension) { return path.endsWith(QLatin1String(extension), Qt::CaseInsensitive); } ); }; template_track_compatibility = ends_with_any_of(template_path, TemplateTrack::supportedExtensions()); QFile file{ template_path }; auto data_georef = std::unique_ptr(); auto& initial_georef = map->getGeoreferencing(); if (!initial_georef.isValid() || initial_georef.isLocal()) { // The map doesn't have a proper georeferencing. // Is there a good SRS in the data? try { data_georef = getDataGeoreferencing(file, initial_georef); } catch (FileFormatException&) {} if (data_georef && data_georef->isValid() && !data_georef->isLocal()) { // If yes, does the user want to use this for the map? auto keep_projected = false; if (template_track_compatibility) data_georef->setGrivation(0); else keep_projected = preserveRefPoints(*data_georef, initial_georef); GeoreferencingDialog dialog(dialog_parent, map, data_georef.get()); if (keep_projected) dialog.setKeepProjectedRefCoords(); else dialog.setKeepGeographicRefCoords(); dialog.exec(); } } auto& georef = map->getGeoreferencing(); // initial_georef might be outdated. if (georef.isValid() && !georef.isLocal()) { // The map has got a proper georeferencing. // Can the template's SRS be converted to the map's CRS? if (OgrFileImport::checkGeoreferencing(file, map->getGeoreferencing())) { is_georeferenced = true; return true; } } // Is the template's SRS orthographic, or can it be converted? if (data_georef && !data_georef->getProjectedCRSSpec().contains(QLatin1String("+proj=ortho"))) { data_georef.reset(); } if (!data_georef) { data_georef = makeOrthographicGeoreferencing(file); } if (data_georef) { if (template_track_compatibility) data_georef->setGrivation(0); else preserveRefPoints(*data_georef, initial_georef); explicit_georef = std::move(data_georef); } TemplatePositioningDialog dialog(dialog_parent); if (dialog.exec() == QDialog::Rejected) return false; center_in_view = dialog.centerOnView(); use_real_coords = dialog.useRealCoords(); if (use_real_coords) { transform.template_scale_x = transform.template_scale_y = dialog.getUnitScale(); updateTransformationMatrices(); } return true; } catch (FileFormatException& e) { setErrorString(e.message()); return false; } bool OgrTemplate::loadTemplateFileImpl(bool configuring) try { QFile file{ template_path }; auto new_template_map = std::make_unique(); auto unit_type = use_real_coords ? OgrFileImport::UnitOnGround : OgrFileImport::UnitOnPaper; OgrFileImport importer{ &file, new_template_map.get(), nullptr, unit_type }; const auto& map_georef = map->getGeoreferencing(); if (template_track_compatibility) { if (configuring) { if (is_georeferenced) { // Data is to be transformed to the map CRS directly. track_crs_spec = Georeferencing::geographic_crs_spec; projected_crs_spec.clear(); } else if (explicit_georef) { // Data is to be transformed to the projected CRS. track_crs_spec = Georeferencing::geographic_crs_spec; projected_crs_spec = explicit_georef->getProjectedCRSSpec(); } else { track_crs_spec.clear(); projected_crs_spec.clear(); } } else { if (is_georeferenced) { // Data is to be transformed to the map CRS directly. Q_ASSERT(projected_crs_spec.isEmpty()); } else if (!track_crs_spec.contains(QLatin1String("+proj=latlong"))) { // Nothing to do with this configuration Q_ASSERT(projected_crs_spec.isEmpty()); } else if (!explicit_georef) { // Data is to be transformed to the projected CRS. if (projected_crs_spec.isEmpty()) { // Need to create an orthographic projection. // For a transition period, copy behaviour from // TemplateTrack::loadTemplateFileImpl(), // TemplateTrack::calculateLocalGeoreferencing(). // This is an extra expense (by loading the data twice), but // only until the map (projected_crs_spec) is saved once. TemplateTrack track{template_path, map}; if (track.track.loadFrom(template_path, false)) { projected_crs_spec = track.calculateLocalGeoreferencing(); } else { // If the TemplateTrack approach failed, use local approach. explicit_georef = makeOrthographicGeoreferencing(file); projected_crs_spec = explicit_georef->getProjectedCRSSpec(); } } if (!explicit_georef) { explicit_georef.reset(new Georeferencing()); explicit_georef->setScaleDenominator(int(map_georef.getScaleDenominator())); explicit_georef->setProjectedCRS(QString{}, projected_crs_spec); explicit_georef->setProjectedRefPoint({}, false); } } } } if (is_georeferenced || !explicit_georef) { new_template_map->setGeoreferencing(map_georef); } else { new_template_map->setGeoreferencing(*explicit_georef); } const auto pp0 = new_template_map->getGeoreferencing().getProjectedRefPoint(); importer.setGeoreferencingImportEnabled(false); importer.doImport(false, template_path); // MapCoord bounds handling may have moved the paper position of the // template data during import. The template position might need to be // adjusted accordingly. // However, this will happen again the next time the template is loaded. // So this adjustment must not affect the saved configuration. const auto pm0 = new_template_map->getGeoreferencing().toMapCoords(pp0); const auto pm1 = new_template_map->getGeoreferencing().getMapRefPoint(); setTemplatePositionOffset(pm1 - pm0); setTemplateMap(std::move(new_template_map)); const auto& warnings = importer.warnings(); if (!warnings.empty()) { QString message; message.reserve((warnings.back().length()+1) * int(warnings.size())); for (const auto& warning : warnings) { message.append(warning); message.append(QLatin1String{"\n"}); } message.chop(1); setErrorString(message); } return true; } catch (FileFormatException& e) { setErrorString(e.message()); return false; } bool OgrTemplate::postLoadConfiguration(QWidget* dialog_parent, bool& out_center_in_view) { Q_UNUSED(dialog_parent) out_center_in_view = center_in_view; return true; } void OgrTemplate::mapProjectionChanged() { if (is_georeferenced && template_state == Template::Loaded) reloadLater(); } void OgrTemplate::mapTransformationChanged() { if (is_georeferenced) { if (template_state != Template::Loaded) return; if (templateMap()->getScaleDenominator() != map->getScaleDenominator()) { // We can't know how to correctly scale symbol dimension. reloadLater(); return; } QTransform t = templateMap()->getGeoreferencing().mapToProjected(); t *= map->getGeoreferencing().projectedToMap(); templateMap()->applyOnAllObjects([&t](Object* o) { o->transform(t); }); templateMap()->setGeoreferencing(map->getGeoreferencing()); } else if (explicit_georef) { template_track_compatibility = false; } else if (template_state == Template::Loaded) { template_track_compatibility = false; if (!explicit_georef) { explicit_georef = std::make_unique(templateMap()->getGeoreferencing()); resetTemplatePositionOffset(); } } } void OgrTemplate::reloadLater() { if (reload_pending) return; if (template_state == Loaded) templateMap()->clear(); // no expensive operations before reloading QTimer::singleShot(0, this, SLOT(reload())); reload_pending = true; } void OgrTemplate::reload() { if (template_state == Loaded) unloadTemplateFile(); loadTemplateFile(false); reload_pending = false; } OgrTemplate* OgrTemplate::duplicateImpl() const { auto copy = new OgrTemplate(template_path, map); if (template_state == Loaded) copy->loadTemplateFileImpl(false); return copy; } bool OgrTemplate::loadTypeSpecificTemplateConfiguration(QXmlStreamReader& xml) { if (xml.name() == literal::georeferencing) { explicit_georef.reset(new Georeferencing()); explicit_georef->load(xml, false); } else if (xml.name() == literal::crs_spec) { track_crs_spec = xml.readElementText(); template_track_compatibility = true; } else if (xml.name() == literal::projected_crs_spec) { projected_crs_spec = xml.readElementText(); template_track_compatibility = true; } else { xml.skipCurrentElement(); } return true; } void OgrTemplate::saveTypeSpecificTemplateConfiguration(QXmlStreamWriter& xml) const { if (template_track_compatibility) { xml.writeTextElement(literal::crs_spec, track_crs_spec); if (!projected_crs_spec.isEmpty()) xml.writeTextElement(literal::projected_crs_spec, projected_crs_spec); } else if (explicit_georef) { explicit_georef->save(xml); } } } // namespace OpenOrienteering mapper-0.8.1.1/src/gdal/ogr_template.h000066400000000000000000000062731325266516600175200ustar00rootroot00000000000000/* * Copyright 2016-2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_OGR_TEMPLATE_H #define OPENORIENTEERING_OGR_TEMPLATE_H #include #include #include #include #include "templates/template_map.h" class QByteArray; class QFile; class QWidget; class QXmlStreamReader; class QXmlStreamWriter; namespace OpenOrienteering { class Georeferencing; class Map; /** * A Template which displays a file supported by OGR * (geospatial vector data). */ class OgrTemplate : public TemplateMap { Q_OBJECT public: static const std::vector& supportedExtensions(); OgrTemplate(const QString& path, Map* map); ~OgrTemplate() override; const char* getTemplateType() const override; std::unique_ptr makeOrthographicGeoreferencing(QFile& file); bool preLoadConfiguration(QWidget* dialog_parent) override; /** * Loads the geospatial vector data into the template_map. * * If is_georereferenced is true, the template_map will be configured to use * the georeferencing of the map given in the constructor, and OgrFileFormat * will let OGR do coordinate transformations as needed. * * If is_georeferenced is false and an explicit_georef is defined, the * template_map will be configured to use this particular georeferencing * to produce a projection of the original data. * * Otherwise, the data will be handled as raw map or paper data, depending on * use_real_coords. */ bool loadTemplateFileImpl(bool configuring) override; bool postLoadConfiguration(QWidget* dialog_parent, bool& out_center_in_view) override; protected: void reloadLater(); protected slots: void reload(); protected: void mapProjectionChanged(); void mapTransformationChanged(); OgrTemplate* duplicateImpl() const override; bool loadTypeSpecificTemplateConfiguration(QXmlStreamReader& xml) override; void saveTypeSpecificTemplateConfiguration(QXmlStreamWriter& xml) const override; private: std::unique_ptr explicit_georef; QString track_crs_spec; // (limited) TemplateTrack compatibility QString projected_crs_spec; // (limited) TemplateTrack compatibility bool template_track_compatibility { false }; // transient bool use_real_coords { true }; // transient bool center_in_view { false }; // transient bool reload_pending { false }; // transient }; } // namespace OpenOrienteering #endif // OPENORIENTEERING_OGR_TEMPLATE_H mapper-0.8.1.1/src/global.cpp000066400000000000000000000031261325266516600157140ustar00rootroot00000000000000/* * Copyright 2012, 2013, 2014 Thomas Schöps * Copyright 2012-2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "global.h" #include "mapper_config.h" // IWYU pragma: keep #include "fileformats/file_format_registry.h" #include "fileformats/native_file_format.h" #include "fileformats/xml_file_format.h" #include "fileformats/ocd_file_format.h" #include "gdal/ogr_file_format.h" namespace OpenOrienteering { void doStaticInitializations() { // Register the supported file formats FileFormats.registerFormat(new XMLFileFormat()); #ifndef MAPPER_BIG_ENDIAN FileFormats.registerFormat(new OcdFileFormat()); #endif #ifdef MAPPER_USE_GDAL FileFormats.registerFormat(new OgrFileFormat()); #endif #ifndef MAPPER_BIG_ENDIAN #ifndef NO_NATIVE_FILE_FORMAT FileFormats.registerFormat(new NativeFileFormat()); // TODO: Remove before release 1.0 #endif #endif } } // namespace OpenOrienteering mapper-0.8.1.1/src/global.h000066400000000000000000000020561325266516600153620ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_GLOBAL_H #define OPENORIENTEERING_GLOBAL_H namespace OpenOrienteering { /** * This is called at startup in main() and by the test cases * to do the global initializations. */ void doStaticInitializations(); } // namespace OpenOrienteering #endif mapper-0.8.1.1/src/gui/000077500000000000000000000000001325266516600145325ustar00rootroot00000000000000mapper-0.8.1.1/src/gui/about_dialog.cpp000066400000000000000000000145221325266516600176730ustar00rootroot00000000000000/* * Copyright 2012, 2013, 2014 Thomas Schöps * Copyright 2012-2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "about_dialog.h" #include #include #include #include "mapper_config.h" namespace OpenOrienteering { namespace { /** * @brief An URL identifying the main "about" page. * * The main page's text will be set directly, thus not having a valid URL. * But an empty URL will be ignored by QTextBrowser's history, leading to * unexpected behaviour of backward navigation. */ const QUrl& aboutPageUrl() { static auto url = QUrl(QString::fromLatin1("#ABOUT")); return url; } /** * Puts the items of a QStringList into an HTML block or a sequence of blocks. */ QString formatBlock(const QStringList& items) { #if defined(Q_OS_ANDROID) // or any other small-screen device QString block = QLatin1String("

    ") + items.join(QString::fromLatin1(", ")) + QLatin1String("

    "); #else QString block; block.reserve(100 + 30 * items.size()); block.append(QLatin1String("
    ")); constexpr int columns = 3; const int rows = (int)ceil((double)items.size() / columns); for (int i = 0, row = 1; i < items.size(); ++i) { block.append(items[i]); if (rows != row) { block.append(QString::fromLatin1("
    ")); ++row; } else if (i < items.size()) { block.append(QString::fromLatin1("
       ")); row = 1; } } block.append(QString::fromLatin1("
    ")); #endif return block; } } // namespace AboutDialog::AboutDialog(QWidget* parent) : TextBrowserDialog(aboutPageUrl(), parent) { text_browser->setHtml(about()); text_browser->document()->adjustSize(); updateWindowTitle(); } void AboutDialog::sourceChanged(const QUrl& url) { if (url == aboutPageUrl()) text_browser->setHtml(about()); } void AboutDialog::updateWindowTitle() { QString title = text_browser->documentTitle(); if (title.isEmpty()) title = tr("About %1").arg(APP_NAME); setWindowTitle(title); } QString AboutDialog::about() { static QStringList developers_list( QStringList() << QString::fromLatin1("Peter Curtis (2012-2013)") << QString::fromLatin1("Kai Pastor") << QString::fromUtf8("Thomas Schöps (2012-2014, %1)") ); static QStringList contributors_list( QStringList() << QString::fromLatin1("Arrizal Amin") << QString::fromLatin1("Javier Arufe") << QString::fromLatin1("Eric Boulet") << QString::fromLatin1("Jon Cundill") << QString::fromUtf8("Sławomir Cygler") << QString::fromLatin1("Jan Dalheimer") << QString::fromLatin1("Davide De Nardis") << QString::fromLatin1("Eugeniy Fedirets") << QString::fromLatin1("Joao Franco") << QString::fromLatin1("Pavel Fric") << QString::fromLatin1("Naofumi Fukue") << QString::fromLatin1("Anders Gressli") << QString::fromLatin1("Peter Hoban") << QString::fromLatin1("Henrik Johansson") << QString::fromLatin1("Panu Karhu") << QString::fromLatin1("Oskar Karlin") << QString::fromLatin1("Nikolay Korotkiy") << QString::fromLatin1("Mitchell Krome") << QString::fromUtf8("Matthias Kühlewein") << QString::fromLatin1("Albin Larsson") << QString::fromUtf8("István Marczis") << QString::fromLatin1("Tojo Masaya") << QString::fromLatin1("Yevhen Mazur") << QString::fromLatin1("Fraser Mills") << QString::fromLatin1("Vincent Poinsignon") << QString::fromLatin1("Russell Porter") << QString::fromLatin1("Adhika Setya Pramudita") << QString::fromLatin1("Christopher Schive") << QString::fromLatin1("Arif Suryawan") << QString::fromLatin1("Jan-Gerard van der Toorn") << QString::fromLatin1("Semyon Yakimov") << QString::fromLatin1("Aivars Zogla") ); QString mapper_about = QString::fromLatin1( "" "%0" "" "" "" "" "
    " "

    %1

    " "

    " "%3
    " "%4

    " "

    Copyright (C) 2018 The OpenOrienteering developers

    " "

    %5

    " "

    %6

    " "

    %7

    " "

    %8

    %9" "

     
    %10

    %11" "" ).arg( tr("About %1").arg(APP_NAME), // %0 qApp->applicationDisplayName(), // %1 tr("A free software for drawing orienteering maps"), // %3 QString::fromLatin1("http://openorienteering.org/apps/mapper/"), // %4 tr("This program is free software: you can redistribute it " "and/or modify it under the terms of the " "GNU General Public License (GPL), version 3, " "as published by the Free Software Foundation.").arg(QString::fromLatin1("href=\"file:doc:/common-licenses/GPL-3.txt\"")), // %5 // %6 tr("This program is distributed in the hope that it will be useful, " "but WITHOUT ANY WARRANTY; without even the implied warranty of " "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the " "GNU General Public License (GPL), version 3, for " "more details.").arg(QString::fromLatin1("href=\"file:doc:/common-licenses/GPL-3.txt#L589\"")), // %6 tr("All about licenses, copyright notices, conditions and disclaimers.").arg(QString::fromLatin1("href=\"file:doc:/licensing.html\"")) // %7 ).arg( tr("The OpenOrienteering developers in alphabetical order:"), // %8 formatBlock(developers_list).arg(tr("(project initiator)").replace(QLatin1Char('('), QString{}).replace(QLatin1Char(')'), QString{})), // %9 tr("For contributions, thanks to:"), // %10 formatBlock(contributors_list) // %11 ); return mapper_about; } } // namespace OpenOrienteering mapper-0.8.1.1/src/gui/about_dialog.h000066400000000000000000000033231325266516600173350ustar00rootroot00000000000000/* * Copyright 2012, 2013, 2014 Thomas Schöps * Copyright 2012-2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_ABOUT_DIALOG_H #define OPENORIENTEERING_ABOUT_DIALOG_H #include #include class QUrl; class QWidget; #include "gui/text_browser_dialog.h" namespace OpenOrienteering { /** * @brief A dialog which shows information about Mapper and its components. */ class AboutDialog : public TextBrowserDialog { Q_OBJECT public: /** * @brief Construct a new AboutDialog. */ AboutDialog(QWidget* parent = nullptr); /** * @brief Returns the basic information about this software. * The return string may contain HTML formatting. */ static QString about(); protected: /** * @brief Sets custom HTML content when the URL identifies the first page. */ void sourceChanged(const QUrl& url) override; /** * @brief Updates the window title from the current document title. */ void updateWindowTitle() override; }; } // namespace OpenOrienteering #endif mapper-0.8.1.1/src/gui/autosave_dialog.cpp000066400000000000000000000117411325266516600204100ustar00rootroot00000000000000/* * Copyright 2014 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "autosave_dialog.h" #include #include #include #include #include #include #include #include "main_window.h" namespace OpenOrienteering { AutosaveDialog::AutosaveDialog(const QString& path, const QString& autosave_path, const QString& actual_path, MainWindow* parent, Qt::WindowFlags f) : QDialog(parent, f) , main_window(parent) , original_path(path) , autosave_path(autosave_path) , resolved(false) { const QString text_template = QString::fromLatin1("%1
    %2
    %3"); QFileInfo autosaved_file_info(autosave_path); autosaved_text.setHtml(text_template. arg(tr("Autosaved file"), autosaved_file_info.lastModified().toLocalTime().toString(), tr("%n bytes", 0, autosaved_file_info.size()))); QFileInfo user_saved_file_info(path); user_saved_text.setHtml(text_template. arg(tr("File saved by the user"), user_saved_file_info.lastModified().toLocalTime().toString(), tr("%n bytes", 0, user_saved_file_info.size())) ); layout = new QVBoxLayout(); setLayout(layout); setWindowTitle(tr("File recovery")); QString intro_text = tr("File %1 was not properly closed. At the moment, there are two versions:"); QLabel* label = new QLabel(intro_text.arg(QString::fromLatin1("%1").arg(user_saved_file_info.fileName()))); label->setWordWrap(true); layout->addWidget(label); list_widget = new QListWidget(); list_widget->setItemDelegate(new TextDocItemDelegate(this, this)); list_widget->setSelectionMode(QAbstractItemView::SingleSelection); QListWidgetItem* item = new QListWidgetItem(list_widget, QListWidgetItem::UserType); item->setData(Qt::UserRole, QVariant(1)); item = new QListWidgetItem(list_widget, QListWidgetItem::UserType); item->setData(Qt::UserRole, QVariant(2)); layout->addWidget(list_widget); label = new QLabel(tr("Save the active file to remove the conflicting version.")); label->setWordWrap(true); layout->addWidget(label); setSelectedPath(actual_path); connect(list_widget, &QListWidget::currentRowChanged, this, &AutosaveDialog::currentRowChanged, Qt::QueuedConnection); #if defined(Q_OS_ANDROID) setWindowState((windowState() & ~(Qt::WindowMinimized | Qt::WindowFullScreen)) | Qt::WindowMaximized); #endif } AutosaveDialog::~AutosaveDialog() { // Nothing } // slot int AutosaveDialog::exec() { QDialogButtonBox button_box(QDialogButtonBox::Open | QDialogButtonBox::Cancel); layout->addWidget(&button_box); connect(&button_box, &QDialogButtonBox::accepted, this, &QDialog::accept); connect(&button_box, &QDialogButtonBox::rejected, this, &QDialog::reject); const int result = QDialog::exec(); resolved = true; return result; } // slot void AutosaveDialog::autosaveConflictResolved() { resolved = true; if (!isModal()) close(); } void AutosaveDialog::closeEvent(QCloseEvent *event) { if (main_window && !resolved) event->setAccepted(main_window->closeFile()); else event->setAccepted(resolved); } QString AutosaveDialog::selectedPath() const { const int row = list_widget->currentRow(); switch (row) { case 0: return autosave_path; break; case 1: return original_path; break; case -1: break; // Nothing selected? default: qWarning("Undefined index"); } return QString(); } // slot void AutosaveDialog::setSelectedPath(const QString& path) { if (path == original_path) list_widget->setCurrentRow(1); else if (path == autosave_path) list_widget->setCurrentRow(0); else list_widget->setCurrentRow(-1); } void AutosaveDialog::currentRowChanged(int row) { switch (row) { case 0: emit pathSelected(autosave_path); break; case 1: emit pathSelected(original_path); break; case -1: return; // Nothing selected? default: qWarning("Undefined index"); } } const QTextDocument* AutosaveDialog::textDoc(const QModelIndex& index) const { const QTextDocument* ret = nullptr; bool ok = true; int i = index.data(Qt::UserRole).toInt(&ok); if (ok) { switch (i) { case 1: ret = &autosaved_text; break; case 2: ret = &user_saved_text; break; default: qWarning("Undefined index"); } } else { qWarning("Invalid data for UserRole"); } return ret; } } // namespace OpenOrienteering mapper-0.8.1.1/src/gui/autosave_dialog.h000066400000000000000000000067371325266516600200660ustar00rootroot00000000000000/* * Copyright 2014 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_AUTOSAVE_DIALOG_H #define OPENORIENTEERING_AUTOSAVE_DIALOG_H #include #include #include #include #include #include "util/item_delegates.h" class QBoxLayout; class QCloseEvent; class QListWidget; class QModelIndex; namespace OpenOrienteering { class MainWindow; /** * @brief A dialog for selection of an autosaved file vs. a user-saved file. * * This dialog may be used a modal dialog (via exec()) or as a non-modal dialog * (via show()). */ class AutosaveDialog : public QDialog, public TextDocItemDelegate::Provider { Q_OBJECT public: /** * @brief Constructs the dialog. * * @param original_path The path of the file which was originally saved by the user. * @param autosave_path The path of the file which was autosaved. * @param actual_path The path which is currently selected. * @param parent The parent window. */ AutosaveDialog(const QString& original_path, const QString& autosave_path, const QString& actual_path, MainWindow* parent = nullptr, Qt::WindowFlags f = 0); /** * Destructor. */ ~AutosaveDialog() override; /** * @brief Returns the currently selected path. */ QString selectedPath() const; /** * @brief Provides the text documents for the list widget items. * * @param index The model index for which the text documents is requested for. * @return A QTextDocument representing the list item, or nullptr. */ const QTextDocument* textDoc(const QModelIndex& index) const override; public slots: /** * @brief Shows this dialog as a modal dialog. * * @override * * @return The result (QDialog::DialogCode). */ int exec() override; /** * @brief Sets the selected item to the one representing the given path. * * If the path does not match either item, nothing is selected. */ void setSelectedPath(const QString& path); /** * @brief Informs the dialog that the conflict is resolved. * * This will close a non-modal dialog. */ void autosaveConflictResolved(); signals: /** * @brief This signal is emitted when the user selects another item. * * @param path The path which belongs to the newly selected item. */ void pathSelected(const QString& path); protected: /** * @brief Ignores the QCloseEvent. * * @override */ void closeEvent(QCloseEvent* event) override; private slots: void currentRowChanged(int row); private: MainWindow* const main_window; const QString original_path; const QString autosave_path; bool resolved; QTextDocument autosaved_text; QTextDocument user_saved_text; QBoxLayout* layout; QListWidget* list_widget; }; } // namespace OpenOrienteering #endif // OPENORIENTEERING_AUTOSAVE_DIALOG_H mapper-0.8.1.1/src/gui/color_dialog.cpp000066400000000000000000000574711325266516600177110ustar00rootroot00000000000000/* * Copyright 2012-2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "color_dialog.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "core/map.h" #include "core/map_color.h" #include "gui/util_gui.h" #include "gui/widgets/color_dropdown.h" #include "util/backports.h" #include "util/util.h" #include "util/translation_util.h" namespace OpenOrienteering { namespace { constexpr QSize coloriconSize() { return QSize{32, 32}; } } // namespace ColorDialog::ColorDialog(const Map& map, const MapColor& source_color, QWidget* parent, Qt::WindowFlags f) : QDialog(parent, f) , map(map) , source_color(source_color) , color(source_color) , color_modified(false) , react_to_changes(true) { setWindowTitle(tr("Edit map color")); setSizeGripEnabled(true); color_preview_label = new QLabel(); color_name_label = new QLabel(); mc_name_edit = new QLineEdit(); language_combo = new QComboBox(); name_edit_button = new QPushButton(tr("Edit")); prof_color_layout = new QGridLayout(); int col = 0; prof_color_layout->setColumnStretch(col, 1); prof_color_layout->setColumnStretch(col+1, 3); int row = 0; prof_color_layout->addWidget(Util::Headline::create(tr("Spot color printing")), row, col, 1, 2); auto spot_color_options = new QButtonGroup(this); ++row; full_tone_option = new QRadioButton(tr("Defines a spot color:")); spot_color_options->addButton(full_tone_option, MapColor::SpotColor); prof_color_layout->addWidget(full_tone_option, row, col, 1, 2); ++row; sc_name_edit = new QLineEdit(); prof_color_layout->addWidget(sc_name_edit, row, col, 1, 2); ++row; composition_option = new QRadioButton(tr("Mixture of spot colors (screens and overprint):")); spot_color_options->addButton(composition_option, MapColor::CustomColor); prof_color_layout->addWidget(composition_option, row, col, 1, 2); std::size_t num_components = 0 /*color.getComponents().size()*/; // FIXME: cleanup components_row0 = row+1; components_col0 = col; component_colors.resize(num_components+1); component_halftone.resize(num_components+1); for (std::size_t i = 0; i <= num_components; i++) { ++row; component_colors[i] = new ColorDropDown(&map, &color, true); component_colors[i]->removeColor(&source_color); prof_color_layout->addWidget(component_colors[i], row, col); component_halftone[i] = Util::SpinBox::create(1, 0.0, 100.0, tr("%"), 10.0); prof_color_layout->addWidget(component_halftone[i], row, col+1); } ++row; knockout_option = new QCheckBox(tr("Knockout: erases lower colors")); prof_color_layout->addWidget(knockout_option, row, col, 1, 2); knockout_option->setEnabled(false); row = 0; col += 2; prof_color_layout->setColumnStretch(col, 1); const int spacing = style()->pixelMetric(QStyle::PM_LayoutTopMargin); prof_color_layout->addItem(new QSpacerItem(3*spacing, spacing), row, col, 7, 1); row = 0; col +=1; prof_color_layout->setColumnStretch(col, 1); prof_color_layout->setColumnStretch(col+1, 3); prof_color_layout->addWidget(Util::Headline::create(tr("CMYK")), row, col, 1, 2); auto cmyk_color_options = new QButtonGroup(this); ++row; cmyk_spot_color_option = new QRadioButton(tr("Calculate from spot colors")); cmyk_color_options->addButton(cmyk_spot_color_option, MapColor::SpotColor); prof_color_layout->addWidget(cmyk_spot_color_option, row, col, 1, 2); ++row; evaluate_rgb_option = new QRadioButton(tr("Calculate from RGB color")); cmyk_color_options->addButton(evaluate_rgb_option, MapColor::RgbColor); prof_color_layout->addWidget(evaluate_rgb_option, row, col, 1, 2); ++row; custom_cmyk_option = new QRadioButton(tr("Custom process color:")); cmyk_color_options->addButton(custom_cmyk_option, MapColor::CustomColor); prof_color_layout->addWidget(custom_cmyk_option, row, col, 1, 2); ++row; c_edit = Util::SpinBox::create(1, 0.0, 100.0, tr("%"), 10.0); prof_color_layout->addWidget(new QLabel(tr("Cyan")), row, col); prof_color_layout->addWidget(c_edit, row, col+1); ++row; m_edit = Util::SpinBox::create(1, 0.0, 100.0, tr("%"), 10.0); prof_color_layout->addWidget(new QLabel(tr("Magenta")), row, col); prof_color_layout->addWidget(m_edit, row, col+1); ++row; y_edit = Util::SpinBox::create(1, 0.0, 100.0, tr("%"), 10.0); prof_color_layout->addWidget(new QLabel(tr("Yellow")), row, col); prof_color_layout->addWidget(y_edit, row, col+1); ++row; k_edit = Util::SpinBox::create(1, 0.0, 100.0, tr("%"), 10.0); prof_color_layout->addWidget(new QLabel(tr("Black")), row, col); prof_color_layout->addWidget(k_edit, row, col+1); ++row; stretch_row0 = row; stretch_col0 = col; stretch = new QWidget(); prof_color_layout->addWidget(stretch, row, col); prof_color_layout->setRowStretch(row, 1); auto prof_color_widget = new QWidget(); prof_color_widget->setLayout(prof_color_layout); prof_color_widget->setObjectName(QString::fromLatin1("professional")); auto desktop_layout = new QGridLayout(); col = 0; desktop_layout->setColumnStretch(col, 1); desktop_layout->setColumnStretch(col+1, 3); row = 0; desktop_layout->addWidget(Util::Headline::create(tr("RGB")), row, col, 1, 2); auto rgb_color_options = new QButtonGroup(this); ++row; rgb_spot_color_option = new QRadioButton(tr("Calculate from spot colors")); rgb_color_options->addButton(rgb_spot_color_option, MapColor::SpotColor); desktop_layout->addWidget(rgb_spot_color_option, row, col, 1, 2); ++row; evaluate_cmyk_option = new QRadioButton(tr("Calculate from CMYK color")); rgb_color_options->addButton(evaluate_cmyk_option, MapColor::CmykColor); desktop_layout->addWidget(evaluate_cmyk_option, row, col, 1, 2); ++row; custom_rgb_option = new QRadioButton(tr("Custom RGB color:")); rgb_color_options->addButton(custom_rgb_option, MapColor::CustomColor); desktop_layout->addWidget(custom_rgb_option, row, col, 1, 2); ++row; r_edit = Util::SpinBox::create(1, 0.0, 255.0, {}, 5); desktop_layout->addWidget(new QLabel(tr("Red")), row, col); desktop_layout->addWidget(r_edit, row, col+1); ++row; g_edit = Util::SpinBox::create(1, 0.0, 255.0, {}, 5); desktop_layout->addWidget(new QLabel(tr("Green")), row, col); desktop_layout->addWidget(g_edit, row, col+1); ++row; b_edit = Util::SpinBox::create(1, 0.0, 255.0, {}, 5); desktop_layout->addWidget(new QLabel(tr("Blue")), row, col); desktop_layout->addWidget(b_edit, row, col+1); ++row; html_edit = new QLineEdit(); desktop_layout->addWidget(new QLabel(tr("#RRGGBB")), row, col); desktop_layout->addWidget(html_edit, row, col+1); ++row; desktop_layout->addWidget(new QWidget(), row, col); desktop_layout->setRowStretch(row, 1); row = 0; col += 2; desktop_layout->setColumnStretch(col, 7); desktop_layout->addItem(new QSpacerItem(3*spacing, spacing), row, col, 7, 1); auto desktop_color_widget = new QWidget(); desktop_color_widget->setLayout(desktop_layout); desktop_color_widget->setObjectName(QString::fromLatin1("desktop")); properties_widget = new QTabWidget(); properties_widget->addTab(desktop_color_widget, tr("Desktop")); properties_widget->addTab(prof_color_widget, tr("Professional printing")); auto button_box = new QDialogButtonBox(QDialogButtonBox::Cancel | QDialogButtonBox::Ok | QDialogButtonBox::Reset | QDialogButtonBox::Help); ok_button = button_box->button(QDialogButtonBox::Ok); reset_button = button_box->button(QDialogButtonBox::Reset); connect(button_box, &QDialogButtonBox::rejected, this, &QDialog::reject); connect(button_box, &QDialogButtonBox::accepted, this, &ColorDialog::accept); connect(reset_button, &QAbstractButton::clicked, this, &ColorDialog::reset); connect(button_box->button(QDialogButtonBox::Help), &QAbstractButton::clicked, this, &ColorDialog::showHelp); auto layout = new QGridLayout(); row = 0; col = 0; layout->addWidget(color_preview_label, row, col); col++; layout->addWidget(color_name_label, row, col, 1, 4); row++; col = 0; layout->addWidget(new QLabel(QApplication::translate("OpenOrienteering::MapSymbolTranslation", "Text source:")), row, col, 1, 2); col+=2; layout->addWidget(language_combo, row, col); col++; layout->addWidget(name_edit_button, row, col); row++; col = 0; layout->addWidget(new QLabel(tr("Name")), row, col, 1, 2); col+=2; layout->addWidget(mc_name_edit, row, col, 1, 3); row++; col = 0; layout->addWidget(properties_widget, row, col, 1, 5); row++; layout->addWidget(button_box, row, col, 1, 5); layout->setColumnStretch(4, 1); setLayout(layout); reset(); updateButtons(); connect(language_combo, QOverload::of(&QComboBox::currentIndexChanged), this, &ColorDialog::languageChanged); connect(name_edit_button, &QPushButton::clicked, this, &ColorDialog::editClicked); connect(mc_name_edit, &QLineEdit::textChanged, this, &ColorDialog::mapColorNameChanged); connect(spot_color_options, QOverload::of(&QButtonGroup::buttonClicked), this, &ColorDialog::spotColorTypeChanged); connect(sc_name_edit, &QLineEdit::textChanged, this, &ColorDialog::spotColorNameChanged); for (std::size_t i = 0; i < component_colors.size(); i++) { connect(component_colors[i], QOverload::of(&ColorDropDown::currentIndexChanged), this, &ColorDialog::spotColorCompositionChanged); connect(component_halftone[i], QOverload::of(&QDoubleSpinBox::valueChanged), this, &ColorDialog::spotColorCompositionChanged); } connect(knockout_option, &QAbstractButton::clicked, this, &ColorDialog::knockoutChanged); connect(cmyk_color_options, QOverload::of(&QButtonGroup::buttonClicked), this, &ColorDialog::cmykColorTypeChanged); connect(c_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &ColorDialog::cmykValueChanged); connect(m_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &ColorDialog::cmykValueChanged); connect(y_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &ColorDialog::cmykValueChanged); connect(k_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &ColorDialog::cmykValueChanged); connect(rgb_color_options, QOverload::of(&QButtonGroup::buttonClicked), this, &ColorDialog::rgbColorTypeChanged); connect(r_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &ColorDialog::rgbValueChanged); connect(g_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &ColorDialog::rgbValueChanged); connect(b_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &ColorDialog::rgbValueChanged); QSettings settings; settings.beginGroup(QString::fromLatin1("ColorDialog")); QString default_view = settings.value(QString::fromLatin1("view")).toString(); settings.endGroup(); properties_widget->setCurrentWidget(properties_widget->findChild(default_view)); } ColorDialog::~ColorDialog() = default; void ColorDialog::updateColorLabel() { auto name = map.translate(color.getName()); if (name.isEmpty()) name = tr("- unnamed -"); color_name_label->setText(QLatin1String("") + name + QLatin1String("")); } void ColorDialog::updateWidgets() { react_to_changes = false; QPixmap pixmap(coloriconSize()); pixmap.fill(colorWithOpacity(color)); color_preview_label->setPixmap(pixmap); sc_name_edit->setText(color.getSpotColorName()); const MapColorCmyk& cmyk = color.getCmyk(); c_edit->setValue(100.0 * double(cmyk.c)); m_edit->setValue(100.0 * double(cmyk.m)); y_edit->setValue(100.0 * double(cmyk.y)); k_edit->setValue(100.0 * double(cmyk.k)); knockout_option->setChecked(color.getKnockout()); if (color.getSpotColorMethod() == MapColor::SpotColor) { full_tone_option->setChecked(true); sc_name_edit->setEnabled(true); knockout_option->setEnabled(true); cmyk_spot_color_option->setEnabled(false); if (cmyk_spot_color_option->isChecked()) custom_cmyk_option->setChecked(true); rgb_spot_color_option->setEnabled(false); if (rgb_spot_color_option->isChecked()) custom_rgb_option->setChecked(true); } else if (color.getSpotColorMethod() == MapColor::CustomColor) { composition_option->setChecked(true); sc_name_edit->setEnabled(false); knockout_option->setEnabled(true); cmyk_spot_color_option->setEnabled(true); rgb_spot_color_option->setEnabled(true); } else { composition_option->setChecked(true); sc_name_edit->setEnabled(false); cmyk_spot_color_option->setEnabled(false); if (cmyk_spot_color_option->isChecked()) { custom_cmyk_option->setChecked(true); } cmyk_spot_color_option->setEnabled(false); if (rgb_spot_color_option->isChecked()) { custom_rgb_option->setChecked(true); } rgb_spot_color_option->setEnabled(false); } const SpotColorComponents& color_components = color.getComponents(); auto num_components = color_components.size(); auto num_editors = component_colors.size(); for (auto i = num_components+1; i < num_editors; ++i) { prof_color_layout->removeWidget(component_colors[i]); delete component_colors[i]; prof_color_layout->removeWidget(component_halftone[i]); delete component_halftone[i]; } if (num_editors != num_components+1) { prof_color_layout->removeWidget(knockout_option); prof_color_layout->addWidget(knockout_option, components_row0+int(num_components)+1, components_col0); } component_colors.resize(num_components+1); component_halftone.resize(num_components+1); for (auto i = num_editors; i <= num_components; ++i) { component_colors[i] = new ColorDropDown(&map, &color, true); component_colors[i]->removeColor(&source_color); prof_color_layout->addWidget(component_colors[i], components_row0+int(i), components_col0); connect(component_colors[i], QOverload::of(&ColorDropDown::currentIndexChanged), this, &ColorDialog::spotColorCompositionChanged); component_halftone[i] = Util::SpinBox::create(1, 0.0, 100.0, tr("%"), 10.0); prof_color_layout->addWidget(component_halftone[i], components_row0+int(i), components_col0+1); connect(component_halftone[i], QOverload::of(&QDoubleSpinBox::valueChanged), this, &ColorDialog::spotColorCompositionChanged); } num_editors = component_colors.size(); bool enable_component = composition_option->isChecked(); for (std::size_t i = 0; i < num_editors; i++) { bool have_component = (i < num_components); const MapColor* component_color = have_component ? color_components[i].spot_color : nullptr; component_colors[i]->setEnabled(enable_component); component_colors[i]->setColor(component_color); bool enable_halftone = enable_component && have_component; auto component_factor = enable_halftone ? double(color_components[i].factor) : 0.0; component_halftone[i]->setEnabled(enable_halftone); component_halftone[i]->setValue(component_factor * 100.0); prof_color_layout->setRowStretch(components_row0 + int(i), 0); enable_component = enable_component && enable_halftone; } // At least one component must be editable to create a composition if (color.getSpotColorMethod() == MapColor::UndefinedMethod) component_colors[0]->setEnabled(true); auto stretch_row = qMax(stretch_row0, components_row0+int(num_editors)); if (stretch_row != stretch_row0) { prof_color_layout->removeWidget(stretch); prof_color_layout->addWidget(stretch, stretch_row, stretch_col0); prof_color_layout->setRowStretch(stretch_row, 1); } bool custom_cmyk = false; if (color.getCmykColorMethod() == MapColor::SpotColor) { cmyk_spot_color_option->setChecked(true); } else if (color.getCmykColorMethod() == MapColor::RgbColor) { evaluate_rgb_option->setChecked(true); } else if (color.getCmykColorMethod() == MapColor::CustomColor) { custom_cmyk_option->setChecked(true); custom_cmyk = true; } c_edit->setEnabled(custom_cmyk); m_edit->setEnabled(custom_cmyk); y_edit->setEnabled(custom_cmyk); k_edit->setEnabled(custom_cmyk); const MapColorRgb& rgb = color.getRgb(); r_edit->setValue(255.0 * double(rgb.r)); g_edit->setValue(255.0 * double(rgb.g)); b_edit->setValue(255.0 * double(rgb.b)); html_edit->setText(QColor(rgb).name()); bool custom_rgb = false; if (color.getRgbColorMethod() == MapColor::SpotColor) { rgb_spot_color_option->setChecked(true); } else if (color.getRgbColorMethod() == MapColor::CmykColor) { evaluate_cmyk_option->setChecked(true); } else if (color.getRgbColorMethod() == MapColor::CustomColor) { custom_rgb_option->setChecked(true); custom_rgb = true; } r_edit->setEnabled(custom_rgb); g_edit->setEnabled(custom_rgb); b_edit->setEnabled(custom_rgb); html_edit->setEnabled(false); // TODO: Editor react_to_changes = true; } void ColorDialog::accept() { QSettings settings; settings.beginGroup(QString::fromLatin1("ColorDialog")); settings.setValue(QString::fromLatin1("view"), properties_widget->currentWidget()->objectName()); settings.endGroup(); QDialog::accept(); } void ColorDialog::reset() { color = source_color; updateWidgets(); language_combo->clear(); language_combo->addItem(QApplication::translate("OpenOrienteering::MapSymbolTranslation", "Map (%1)") .arg(QApplication::translate("OpenOrienteering::MapSymbolTranslation", "undefined language"))); auto name = color.getName(); auto display_name = map.raw_translation(name); if (display_name.isEmpty()) { language_combo->setEnabled(false); name_edit_button->setEnabled(false); mc_name_edit->setText(name); mc_name_edit->setEnabled(true); } else { auto language = TranslationUtil::languageFromSettings(QSettings()); if (!language.isValid()) { language.displayName = QApplication::translate("OpenOrienteering::MapSymbolTranslation", "undefined language"); } language_combo->addItem(QApplication::translate("OpenOrienteering::MapSymbolTranslation", "Translation (%1)").arg(language.displayName)); language_combo->setCurrentIndex(1); language_combo->setEnabled(true); name_edit_button->setEnabled(true); mc_name_edit->setText(display_name); mc_name_edit->setEnabled(false); } setColorModified(false); } void ColorDialog::setColorModified(bool modified) { updateColorLabel(); if (color_modified != modified) { color_modified = modified; updateButtons(); } } void ColorDialog::updateButtons() { ok_button->setEnabled(color_modified); reset_button->setEnabled(color_modified); } void ColorDialog::showHelp() { Util::showHelp(this, "color_dock_widget.html", "editor"); } void ColorDialog::languageChanged() { auto name = color.getName(); if (language_combo->currentIndex() == 1) { name = map.raw_translation(name); } QSignalBlocker block(mc_name_edit); mc_name_edit->setText(name); } void ColorDialog::editClicked() { int result; auto question = QString{}; if (language_combo->currentIndex() == 1) { question = QApplication::translate("OpenOrienteering::MapSymbolTranslation", "Before editing, the stored text will be " "replaced with the current translation. " "Do you want to continue?"); } else { question = QApplication::translate("OpenOrienteering::MapSymbolTranslation", "After modifying the stored text, " "the translation may no longer be found. " "Do you want to continue?"); } result = QMessageBox::warning(this, tr("Warning"), question, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); if (result == QMessageBox::Yes) { language_combo->setEnabled(false); name_edit_button->setEnabled(false); mc_name_edit->setEnabled(true); if (language_combo->currentIndex() == 1) { { QSignalBlocker block(language_combo); language_combo->setCurrentIndex(0); } auto name = mc_name_edit->text(); if (name.isEmpty()) { QSignalBlocker block(mc_name_edit); name = color.getName(); mc_name_edit->setText(name); } color.setName(name); } setColorModified(true); } } // slot void ColorDialog::mapColorNameChanged() { if (!react_to_changes) return; color.setName(mc_name_edit->text()); setColorModified(); } void ColorDialog::spotColorTypeChanged(int id) { if (!react_to_changes) return; QString name; switch (id) { case MapColor::SpotColor: name = color.getName(); if (name.isEmpty()) name = QLatin1Char('?'); color.setSpotColorName(name); break; case MapColor::CustomColor: if (source_color.getSpotColorMethod() == MapColor::CustomColor) color.setSpotColorComposition(source_color.getComponents()); else color.setSpotColorComposition({}); break; default: ; // nothing } updateWidgets(); setColorModified(true); } void ColorDialog::spotColorNameChanged() { if (!react_to_changes) return; Q_ASSERT(full_tone_option->isChecked()); color.setSpotColorName(sc_name_edit->text()); setColorModified(); } void ColorDialog::spotColorCompositionChanged() { if (!react_to_changes) return; SpotColorComponents components; SpotColorComponent component; auto num_editors = component_colors.size(); components.reserve(num_editors); for (std::size_t i = 0; i < num_editors; i++) { if (!component_colors[i]->isEnabled()) break; const MapColor* spot_color = component_colors[i]->color(); if (!spot_color) continue; component.spot_color = spot_color; component.factor = float(component_halftone[i]->value() / 100); components.push_back(component); } color.setSpotColorComposition(components); updateWidgets(); setColorModified(true); } void ColorDialog::knockoutChanged() { if (!react_to_changes) return; color.setKnockout(knockout_option->isChecked()); setColorModified(); } void ColorDialog::cmykColorTypeChanged(int id) { if (!react_to_changes) return; switch (id) { case MapColor::SpotColor: color.setCmykFromSpotColors(); break; case MapColor::RgbColor: // color.setRgb(color.getRgb()); color.setCmykFromRgb(); break; case MapColor::CustomColor: color.setCmyk(color.getCmyk()); break; default: ; // nothing } updateWidgets(); setColorModified(true); } void ColorDialog::cmykValueChanged() { if (!react_to_changes) return; if (custom_cmyk_option->isChecked()) { color.setCmyk( MapColorCmyk( float(c_edit->value()/100), float(m_edit->value()/100), float(y_edit->value()/100), float(k_edit->value()/100) ) ); updateWidgets(); setColorModified(true); } } void ColorDialog::rgbColorTypeChanged(int id) { if (!react_to_changes) return; switch (id) { case MapColor::SpotColor: color.setRgbFromSpotColors(); break; case MapColor::CmykColor: // color.setCmyk(color.getCmyk()); color.setRgbFromCmyk(); break; case MapColor::CustomColor: color.setRgb(color.getRgb()); break; default: ; // nothing } updateWidgets(); setColorModified(true); } void ColorDialog::rgbValueChanged() { if (!react_to_changes) return; if (custom_rgb_option->isChecked()) { color.setRgb( MapColorRgb( float(r_edit->value()/255), float(g_edit->value()/255), float(b_edit->value()/255) ) ); updateWidgets(); setColorModified(true); } } } // namespace OpenOrienteering mapper-0.8.1.1/src/gui/color_dialog.h000066400000000000000000000065661325266516600173550ustar00rootroot00000000000000/* * Copyright 2012, 2013, 2014, 2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_COLOR_DIALOG_H #define OPENORIENTEERING_COLOR_DIALOG_H #include #include #include #include #include "core/map_color.h" class QAbstractButton; class QCheckBox; class QComboBox; class QDoubleSpinBox; class QGridLayout; class QLabel; class QLineEdit; class QPushButton; class QRadioButton; class QTabWidget; class QWidget; namespace OpenOrienteering { class ColorDropDown; class Map; /** * A dialog for editing a single map color. */ class ColorDialog: public QDialog { Q_OBJECT public: /** Constructs a new dialog for the given map and color. */ ColorDialog(const Map& map, const MapColor& source_color, QWidget* parent = nullptr, Qt::WindowFlags f = 0); ~ColorDialog() override; /** * Returns the edited color. */ const MapColor& getColor() const { return color; } protected slots: void accept() override; void reset(); void showHelp(); void languageChanged(); void editClicked(); void mapColorNameChanged(); void spotColorTypeChanged(int id); void spotColorNameChanged(); void spotColorCompositionChanged(); void knockoutChanged(); void cmykColorTypeChanged(int id); void cmykValueChanged(); void rgbColorTypeChanged(int id); void rgbValueChanged(); void setColorModified() { setColorModified(true); } protected: void setColorModified(bool modified); void updateColorLabel(); void updateWidgets(); void updateButtons(); const Map& map; const MapColor& source_color; MapColor color; bool color_modified; bool react_to_changes; QLabel* color_preview_label; QLabel* color_name_label; QLineEdit* mc_name_edit; QComboBox* language_combo; QPushButton* name_edit_button; QRadioButton* full_tone_option; QRadioButton* composition_option; QLineEdit* sc_name_edit; QCheckBox* knockout_option; QRadioButton* cmyk_spot_color_option; QRadioButton* evaluate_rgb_option; QRadioButton* custom_cmyk_option; QDoubleSpinBox* c_edit; QDoubleSpinBox* m_edit; QDoubleSpinBox* y_edit; QDoubleSpinBox* k_edit; QRadioButton* rgb_spot_color_option; QRadioButton* evaluate_cmyk_option; QRadioButton* custom_rgb_option; QDoubleSpinBox* r_edit; QDoubleSpinBox* g_edit; QDoubleSpinBox* b_edit; QLineEdit* html_edit; QTabWidget* properties_widget; QAbstractButton* ok_button; QAbstractButton* reset_button; std::vector< ColorDropDown* > component_colors; std::vector< QDoubleSpinBox* > component_halftone; int components_row0; int components_col0; QGridLayout* prof_color_layout; int stretch_row0; int stretch_col0; QWidget* stretch; }; } // namespace OpenOrienteering #endif mapper-0.8.1.1/src/gui/configure_grid_dialog.cpp000066400000000000000000000247641325266516600215600ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2014, 2016, 2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "configure_grid_dialog.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "core/georeferencing.h" #include "core/map.h" #include "gui/util_gui.h" #include "util/backports.h" // IWYU pragma: keep namespace OpenOrienteering { class MapCoordF; ConfigureGridDialog::ConfigureGridDialog(QWidget* parent, const Map& map, bool grid_visible) : QDialog(parent, Qt::WindowSystemMenuHint | Qt::WindowTitleHint) , map(map) , grid(map.getGrid()) , grid_visible(grid_visible) , current_color(grid.getColor()) , current_unit(grid.getUnit()) { setWindowTitle(tr("Configure grid")); show_grid_check = new QCheckBox(tr("Show grid")); snap_to_grid_check = new QCheckBox(tr("Snap to grid")); choose_color_button = new QPushButton(tr("Choose...")); display_mode_combo = new QComboBox(); display_mode_combo->addItem(tr("All lines"), int(MapGrid::AllLines)); display_mode_combo->addItem(tr("Horizontal lines"), int(MapGrid::HorizontalLines)); display_mode_combo->addItem(tr("Vertical lines"), int(MapGrid::VerticalLines)); mag_north_radio = new QRadioButton(tr("Align with magnetic north")); grid_north_radio = new QRadioButton(tr("Align with grid north")); true_north_radio = new QRadioButton(tr("Align with true north")); auto rotate_label = new QLabel(tr("Additional rotation (counter-clockwise):")); additional_rotation_edit = Util::SpinBox::create(Georeferencing::declinationPrecision(), -360, +360, trUtf8("°")); additional_rotation_edit->setWrapping(true); unit_combo = new QComboBox(); unit_combo->addItem(tr("meters in terrain"), int(MapGrid::MetersInTerrain)); unit_combo->addItem(tr("millimeters on map"), int(MapGrid::MillimetersOnMap)); auto horz_spacing_label = new QLabel(tr("Horizontal spacing:")); horz_spacing_edit = Util::SpinBox::create(1, 0.1, Util::InputProperties::max()); auto vert_spacing_label = new QLabel(tr("Vertical spacing:")); vert_spacing_edit = Util::SpinBox::create(1, 0.1, Util::InputProperties::max()); origin_label = new QLabel(); auto horz_offset_label = new QLabel(tr("Horizontal offset:")); horz_offset_edit = Util::SpinBox::create(1, Util::InputProperties::min(), Util::InputProperties::max()); auto vert_offset_label = new QLabel(tr("Vertical offset:")); vert_offset_edit = Util::SpinBox::create(1, Util::InputProperties::min(), Util::InputProperties::max()); auto button_box = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::Help, Qt::Horizontal); show_grid_check->setChecked(grid_visible); snap_to_grid_check->setChecked(grid.isSnappingEnabled()); display_mode_combo->setCurrentIndex(display_mode_combo->findData(int(grid.getDisplayMode()))); if (grid.getAlignment() == MapGrid::MagneticNorth) mag_north_radio->setChecked(true); else if (grid.getAlignment() == MapGrid::GridNorth) grid_north_radio->setChecked(true); else // if (grid.getAlignment() == MapGrid::TrueNorth) true_north_radio->setChecked(true); additional_rotation_edit->setValue(grid.getAdditionalRotation() * 180 / M_PI); unit_combo->setCurrentIndex(unit_combo->findData(current_unit)); horz_spacing_edit->setValue(grid.getHorizontalSpacing()); vert_spacing_edit->setValue(grid.getVerticalSpacing()); horz_offset_edit->setValue(grid.getHorizontalOffset()); vert_offset_edit->setValue(-1 * grid.getVerticalOffset()); auto layout = new QFormLayout(); layout->addRow(show_grid_check); layout->addRow(snap_to_grid_check); layout->addRow(tr("Line color:"), choose_color_button); layout->addRow(tr("Display:"), display_mode_combo); layout->addItem(Util::SpacerItem::create(this)); layout->addRow(Util::Headline::create(tr("Alignment"))); layout->addRow(mag_north_radio); layout->addRow(grid_north_radio); layout->addRow(true_north_radio); layout->addRow(rotate_label, additional_rotation_edit); layout->addItem(Util::SpacerItem::create(this)); layout->addRow(Util::Headline::create(tr("Positioning"))); layout->addRow(tr("Unit:", "measurement unit"), unit_combo); layout->addRow(horz_spacing_label, horz_spacing_edit); layout->addRow(vert_spacing_label, vert_spacing_edit); layout->addItem(Util::SpacerItem::create(this)); layout->addRow(origin_label); layout->addRow(horz_offset_label, horz_offset_edit); layout->addRow(vert_offset_label, vert_offset_edit); layout->addItem(Util::SpacerItem::create(this)); layout->addRow(button_box); setLayout(layout); updateStates(); updateColorDisplay(); connect(show_grid_check, &QAbstractButton::clicked, this, &ConfigureGridDialog::updateStates); connect(choose_color_button, &QAbstractButton::clicked, this, &ConfigureGridDialog::chooseColor); connect(display_mode_combo, QOverload::of(&QComboBox::currentIndexChanged), this, &ConfigureGridDialog::updateStates); connect(mag_north_radio, &QAbstractButton::clicked, this, &ConfigureGridDialog::updateStates); connect(grid_north_radio, &QAbstractButton::clicked, this, &ConfigureGridDialog::updateStates); connect(true_north_radio, &QAbstractButton::clicked, this, &ConfigureGridDialog::updateStates); connect(unit_combo, QOverload::of(&QComboBox::currentIndexChanged), this, &ConfigureGridDialog::unitChanged); connect(button_box, &QDialogButtonBox::helpRequested, this, &ConfigureGridDialog::showHelp); connect(button_box, &QDialogButtonBox::accepted, this, &ConfigureGridDialog::okClicked); connect(button_box, &QDialogButtonBox::rejected, this, &QDialog::reject); } ConfigureGridDialog::~ConfigureGridDialog() = default; void ConfigureGridDialog::chooseColor() { qDebug() << qAlpha(current_color); QColor new_color = QColorDialog::getColor(current_color, this, tr("Choose grid line color"), QColorDialog::ShowAlphaChannel); if (new_color.isValid()) { current_color = new_color.rgba(); updateColorDisplay(); } } void ConfigureGridDialog::updateColorDisplay() { int icon_size = style()->pixelMetric(QStyle::PM_SmallIconSize); QPixmap pixmap(icon_size, icon_size); pixmap.fill(current_color); QIcon icon(pixmap); choose_color_button->setIcon(icon); } void ConfigureGridDialog::unitChanged(int index) { auto unit = MapGrid::Unit(unit_combo->itemData(index).toInt()); if (unit != current_unit) { current_unit = unit; double factor = 1.0; switch (current_unit) { case MapGrid::MetersInTerrain: factor = 0.001 * map.getScaleDenominator(); break; case MapGrid::MillimetersOnMap: factor = 1000.0 / map.getScaleDenominator(); break; default: Q_ASSERT(!"Illegal unit"); } for (auto editor : { horz_spacing_edit, vert_spacing_edit, horz_offset_edit, vert_offset_edit }) { editor->setValue(editor->value() * factor); } } updateStates(); } void ConfigureGridDialog::okClicked() { grid_visible = show_grid_check->isChecked(); grid.setSnappingEnabled(snap_to_grid_check->isChecked()); grid.setColor(current_color); grid.setDisplayMode(MapGrid::DisplayMode(display_mode_combo->itemData(display_mode_combo->currentIndex()).toInt())); if (mag_north_radio->isChecked()) grid.setAlignment(MapGrid::MagneticNorth); else if (grid_north_radio->isChecked()) grid.setAlignment(MapGrid::GridNorth); else // if (true_north_radio->isChecked()) grid.setAlignment(MapGrid::TrueNorth); grid.setAdditionalRotation(additional_rotation_edit->value() * M_PI / 180); grid.setUnit(current_unit); grid.setHorizontalSpacing(horz_spacing_edit->value()); grid.setVerticalSpacing(vert_spacing_edit->value()); grid.setHorizontalOffset(horz_offset_edit->value()); grid.setVerticalOffset(-1 * vert_offset_edit->value()); accept(); } void ConfigureGridDialog::updateStates() { MapGrid::DisplayMode display_mode = MapGrid::DisplayMode(display_mode_combo->itemData(display_mode_combo->currentIndex()).toInt()); choose_color_button->setEnabled(show_grid_check->isChecked()); display_mode_combo->setEnabled(show_grid_check->isChecked()); snap_to_grid_check->setEnabled(show_grid_check->isChecked()); mag_north_radio->setEnabled(show_grid_check->isChecked()); grid_north_radio->setEnabled(show_grid_check->isChecked()); true_north_radio->setEnabled(show_grid_check->isChecked()); additional_rotation_edit->setEnabled(show_grid_check->isChecked()); unit_combo->setEnabled(show_grid_check->isChecked()); QString unit_suffix = QLatin1Char(' ') + ((current_unit == MapGrid::MetersInTerrain) ? tr("m", "meters") : tr("mm", "millimeters")); horz_spacing_edit->setEnabled(show_grid_check->isChecked() && display_mode != MapGrid::HorizontalLines); horz_spacing_edit->setSuffix(unit_suffix); vert_spacing_edit->setEnabled(show_grid_check->isChecked() && display_mode != MapGrid::VerticalLines); vert_spacing_edit->setSuffix(unit_suffix); QString origin_text = tr("Origin at: %1"); if (mag_north_radio->isChecked() || true_north_radio->isChecked()) origin_text = origin_text.arg(tr("paper coordinates origin")); else // if (grid_north_radio->isChecked()) origin_text = origin_text.arg(tr("projected coordinates origin")); origin_label->setText(origin_text); horz_offset_edit->setEnabled(show_grid_check->isChecked() && display_mode != MapGrid::HorizontalLines); horz_offset_edit->setSuffix(unit_suffix); vert_offset_edit->setEnabled(show_grid_check->isChecked() && display_mode != MapGrid::VerticalLines); vert_offset_edit->setSuffix(unit_suffix); } void ConfigureGridDialog::showHelp() { Util::showHelp(this, "grid.html"); } } // namespace OpenOrienteering mapper-0.8.1.1/src/gui/configure_grid_dialog.h000066400000000000000000000044631325266516600212170ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2014, 2015 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_CONFIGURE_GRID_DIALOG_H #define OPENORIENTEERING_CONFIGURE_GRID_DIALOG_H #include #include #include #include "core/map_grid.h" class QCheckBox; class QComboBox; class QDoubleSpinBox; class QLabel; class QPushButton; class QRadioButton; class QWidget; namespace OpenOrienteering { class Map; class ConfigureGridDialog : public QDialog { Q_OBJECT public: ConfigureGridDialog(QWidget* parent, const Map& map, bool grid_visible); ~ConfigureGridDialog() override; const MapGrid& resultGrid() const; bool gridVisible() const; private slots: void chooseColor(); void updateColorDisplay(); void unitChanged(int index); void okClicked(); void updateStates(); void showHelp(); private: QCheckBox* show_grid_check; QCheckBox* snap_to_grid_check; QPushButton* choose_color_button; QComboBox* display_mode_combo; QRadioButton* mag_north_radio; QRadioButton* grid_north_radio; QRadioButton* true_north_radio; QDoubleSpinBox* additional_rotation_edit; QComboBox* unit_combo; QDoubleSpinBox* horz_spacing_edit; QDoubleSpinBox* vert_spacing_edit; QLabel* origin_label; QDoubleSpinBox* horz_offset_edit; QDoubleSpinBox* vert_offset_edit; const Map& map; MapGrid grid; bool grid_visible; QRgb current_color; MapGrid::Unit current_unit; }; inline const MapGrid& ConfigureGridDialog::resultGrid() const { return grid; } inline bool ConfigureGridDialog::gridVisible() const { return grid_visible; } } // namespace OpenOrienteering #endif mapper-0.8.1.1/src/gui/file_dialog.cpp000066400000000000000000000064741325266516600175070ustar00rootroot00000000000000/* * Copyright 2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "file_dialog.h" #include #include #include #include // IWYU pragma: keep #include #ifndef QTBUG_51712_QUIRK_ENABLED # if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID) && !defined(QT_TESTLIB_LIB) # define QTBUG_51712_QUIRK_ENABLED 1 # else # define QTBUG_51712_QUIRK_ENABLED 0 # endif #endif #if QTBUG_51712_QUIRK_ENABLED # include # include # include # include # include # include # include # include # include #endif namespace { constexpr int max_filter_length = 100; } // namespace namespace OpenOrienteering { bool FileDialog::needUpperCaseExtensions() { #if QTBUG_51712_QUIRK_ENABLED auto platform_theme = QGuiApplicationPrivate::platformTheme(); if (platform_theme && platform_theme->usePlatformNativeDialog(QPlatformTheme::DialogType::FileDialog)) { std::unique_ptr helper { platform_theme->createPlatformDialogHelper(QPlatformTheme::DialogType::FileDialog) }; if (helper) return qstrncmp(helper->metaObject()->className(), "QGtk", 4) == 0; } #endif return false; } void FileDialog::adjustParameters(QString& filter, QFileDialog::Options& options) { using std::begin; using std::end; static const auto separator = QString::fromLatin1(";;"); #if QT_VERSION >= 0x50400 const auto filters = filter.splitRef(separator); #else const auto filters = filter.split(separator); #endif bool has_long_filters = std::any_of(begin(filters), end(filters), [](auto&& item) { return item.length() > max_filter_length; }); #if QTBUG_51712_QUIRK_ENABLED static auto need_upper_case = needUpperCaseExtensions(); if (need_upper_case) { QStringList new_filters; new_filters.reserve(filter.size()); for (auto&& item : filters) { QString new_item; new_item.reserve(2 * item.length()); auto split_0 = item.indexOf(QLatin1Char('(')); auto split_1 = item.lastIndexOf(QLatin1Char(')')); new_item.append(item.left(split_1)); new_item.append(QLatin1Char(' ')); #if QT_VERSION >= 0x50400 new_item.append(item.mid(split_0+1).toString().toUpper()); #else new_item.append(item.mid(split_0+1).toUpper()); #endif new_filters.append(new_item); if (new_item.length() > max_filter_length) has_long_filters = true; } filter = new_filters.join(separator); } #endif if (has_long_filters) options |= QFileDialog::HideNameFilterDetails; } } // namespace OpenOrienteering mapper-0.8.1.1/src/gui/file_dialog.h000066400000000000000000000053521325266516600171460ustar00rootroot00000000000000/* * Copyright 2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_UTIL_FILE_DIALOG_H #define OPENORIENTEERING_UTIL_FILE_DIALOG_H #include #include class QWidget; namespace OpenOrienteering { /** * A collection of file dialog utility functions. */ namespace FileDialog { /** * Returns true if upper case extensions have to be added explicitly * to filters in file dialogs. */ bool needUpperCaseExtensions(); /** * Adjusts filter and options for file dialogs. * * Adds upper case version of the extension when needed. * Sets QFileDialog::HideNameFilterDetails when the length of any particular * filter exceeds a certain treshold. */ void adjustParameters(QString& filter, QFileDialog::Options& options); /** * Calls QFileDialog::getOpenFileName with adjusted parameters. * * \see adjustParameters, QFileDialog::getOpenFileName */ inline QString getOpenFileName(QWidget* parent = nullptr, const QString& caption = {}, const QString& dir = {}, QString filter = {}, QString* selected_filter = nullptr, QFileDialog::Options options = {}) { adjustParameters(filter, options); return QFileDialog::getOpenFileName(parent, caption, dir, filter, selected_filter, options); } /** * Calls QFileDialog::getSaveFileName with adjusted parameters. * * \see adjustParameters, QFileDialog::getSaveFileName */ inline QString getSaveFileName(QWidget* parent = nullptr, const QString& caption = {}, const QString& dir = {}, QString filter = {}, QString* selected_filter = nullptr, QFileDialog::Options options = {}) { adjustParameters(filter, options); return QFileDialog::getSaveFileName(parent, caption, dir, filter, selected_filter, options); } } // namespace FileDialog } // namespace OpenOrienteering #endif mapper-0.8.1.1/src/gui/georeferencing_dialog.cpp000066400000000000000000000621401325266516600215420ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2012-2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "georeferencing_dialog.h" #include #include #include #include #include #include #include // IWYU pragma: keep #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // IWYU pragma: no_include #if defined(QT_NETWORK_LIB) #include #include #include #endif #include "core/crs_template.h" #include "core/georeferencing.h" #include "core/latlon.h" #include "core/map.h" #include "gui/main_window.h" #include "gui/map/map_dialog_rotate.h" #include "gui/map/map_editor.h" #include "gui/widgets/crs_selector.h" #include "gui/util_gui.h" #include "util/scoped_signals_blocker.h" namespace OpenOrienteering { // ### GeoreferencingDialog ### GeoreferencingDialog::GeoreferencingDialog(MapEditorController* controller, const Georeferencing* initial, bool allow_no_georeferencing) : GeoreferencingDialog(controller->getWindow(), controller, controller->getMap(), initial, allow_no_georeferencing) { // nothing else } GeoreferencingDialog::GeoreferencingDialog(QWidget* parent, Map* map, const Georeferencing* initial, bool allow_no_georeferencing) : GeoreferencingDialog(parent, nullptr, map, initial, allow_no_georeferencing) { // nothing else } GeoreferencingDialog::GeoreferencingDialog( QWidget* parent, MapEditorController* controller, Map* map, const Georeferencing* initial, bool allow_no_georeferencing ) : QDialog(parent, Qt::WindowSystemMenuHint | Qt::WindowTitleHint) , controller(controller) , map(map) , initial_georef(initial ? initial : &map->getGeoreferencing()) , georef(new Georeferencing(*initial_georef)) , allow_no_georeferencing(allow_no_georeferencing) , tool_active(false) , declination_query_in_progress(false) , grivation_locked(!initial_georef->isValid() || initial_georef->getState() != Georeferencing::Normal) , original_declination(0.0) { if (!grivation_locked) original_declination = initial_georef->getDeclination(); setWindowTitle(tr("Map Georeferencing")); setWindowModality(Qt::WindowModal); // Create widgets auto map_crs_label = Util::Headline::create(tr("Map coordinate reference system")); crs_selector = new CRSSelector(*georef, nullptr); crs_selector->addCustomItem(tr("- local -"), Georeferencing::Local); status_label = new QLabel(tr("Status:")); status_field = new QLabel(); /*: The grid scale factor is the ratio between a length in the grid plane and the corresponding length on the curved earth model. It is applied as a factor to ground distances to get grid plane distances. */ auto scale_factor_label = new QLabel(tr("Grid scale factor:")); scale_factor_edit = Util::SpinBox::create(Georeferencing::scaleFactorPrecision(), 0.001, 1000.0); auto reference_point_label = Util::Headline::create(tr("Reference point")); ref_point_button = new QPushButton(tr("&Pick on map")); int ref_point_button_width = ref_point_button->sizeHint().width(); auto geographic_datum_label = new QLabel(tr("(Datum: WGS84)")); int geographic_datum_label_width = geographic_datum_label->sizeHint().width(); map_x_edit = Util::SpinBox::create(tr("mm")); map_y_edit = Util::SpinBox::create(tr("mm")); ref_point_button->setEnabled(controller); auto map_ref_layout = new QHBoxLayout(); map_ref_layout->addWidget(map_x_edit, 1); map_ref_layout->addWidget(new QLabel(tr("X", "x coordinate")), 0); map_ref_layout->addWidget(map_y_edit, 1); map_ref_layout->addWidget(new QLabel(tr("Y", "y coordinate")), 0); if (ref_point_button_width < geographic_datum_label_width) map_ref_layout->addSpacing(geographic_datum_label_width - ref_point_button_width); map_ref_layout->addWidget(ref_point_button, 0); easting_edit = Util::SpinBox::create(tr("m")); northing_edit = Util::SpinBox::create(tr("m")); auto projected_ref_layout = new QHBoxLayout(); projected_ref_layout->addWidget(easting_edit, 1); projected_ref_layout->addWidget(new QLabel(tr("E", "west / east")), 0); projected_ref_layout->addWidget(northing_edit, 1); projected_ref_layout->addWidget(new QLabel(tr("N", "north / south")), 0); projected_ref_layout->addSpacing(qMax(ref_point_button_width, geographic_datum_label_width)); projected_ref_label = new QLabel(); lat_edit = Util::SpinBox::create(8, -90.0, +90.0, trUtf8("°")); lon_edit = Util::SpinBox::create(8, -180.0, +180.0, trUtf8("°")); auto geographic_ref_layout = new QHBoxLayout(); geographic_ref_layout->addWidget(lat_edit, 1); geographic_ref_layout->addWidget(new QLabel(tr("N", "north")), 0); geographic_ref_layout->addWidget(lon_edit, 1); geographic_ref_layout->addWidget(new QLabel(tr("E", "east")), 0); if (geographic_datum_label_width < ref_point_button_width) geographic_ref_layout->addSpacing(ref_point_button_width - geographic_datum_label_width); geographic_ref_layout->addWidget(geographic_datum_label, 0); show_refpoint_label = new QLabel(tr("Show reference point in:")); link_label = new QLabel(); link_label->setOpenExternalLinks(true); keep_projected_radio = new QRadioButton(tr("Projected coordinates")); keep_geographic_radio = new QRadioButton(tr("Geographic coordinates")); if (georef->getState() == Georeferencing::Normal && georef->isValid()) { keep_geographic_radio->setChecked(true); } else { keep_geographic_radio->setEnabled(false); keep_projected_radio->setCheckable(true); } auto map_north_label = Util::Headline::create(tr("Map north")); declination_edit = Util::SpinBox::create(Georeferencing::declinationPrecision(), -180.0, +180.0, trUtf8("°")); declination_button = new QPushButton(tr("Lookup...")); auto declination_layout = new QHBoxLayout(); declination_layout->addWidget(declination_edit, 1); declination_layout->addWidget(declination_button, 0); grivation_label = new QLabel(); buttons_box = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::Reset | QDialogButtonBox::Help, Qt::Horizontal); reset_button = buttons_box->button(QDialogButtonBox::Reset); reset_button->setEnabled(initial); auto help_button = buttons_box->button(QDialogButtonBox::Help); auto edit_layout = new QFormLayout(); edit_layout->addRow(map_crs_label); edit_layout->addRow(tr("&Coordinate reference system:"), crs_selector); crs_selector->setDialogLayout(edit_layout); edit_layout->addRow(status_label, status_field); edit_layout->addRow(scale_factor_label, scale_factor_edit); edit_layout->addItem(Util::SpacerItem::create(this)); edit_layout->addRow(reference_point_label); edit_layout->addRow(tr("Map coordinates:"), map_ref_layout); edit_layout->addRow(projected_ref_label, projected_ref_layout); edit_layout->addRow(tr("Geographic coordinates:"), geographic_ref_layout); edit_layout->addRow(show_refpoint_label, link_label); edit_layout->addRow(show_refpoint_label, link_label); edit_layout->addRow(tr("On CRS changes, keep:"), keep_projected_radio); edit_layout->addRow({}, keep_geographic_radio); edit_layout->addItem(Util::SpacerItem::create(this)); edit_layout->addRow(map_north_label); edit_layout->addRow(tr("Declination:"), declination_layout); edit_layout->addRow(tr("Grivation:"), grivation_label); auto layout = new QVBoxLayout(); layout->addLayout(edit_layout); layout->addStretch(); layout->addSpacing(16); layout->addWidget(buttons_box); setLayout(layout); connect(crs_selector, &CRSSelector::crsChanged, this, &GeoreferencingDialog::crsEdited); using TakingDoubleArgument = void (QDoubleSpinBox::*)(double); connect(scale_factor_edit, (TakingDoubleArgument)&QDoubleSpinBox::valueChanged, this, &GeoreferencingDialog::scaleFactorEdited); connect(map_x_edit, (TakingDoubleArgument)&QDoubleSpinBox::valueChanged, this, &GeoreferencingDialog::mapRefChanged); connect(map_y_edit, (TakingDoubleArgument)&QDoubleSpinBox::valueChanged, this, &GeoreferencingDialog::mapRefChanged); connect(ref_point_button, &QPushButton::clicked, this, &GeoreferencingDialog::selectMapRefPoint); connect(easting_edit, (TakingDoubleArgument)&QDoubleSpinBox::valueChanged, this, &GeoreferencingDialog::eastingNorthingEdited); connect(northing_edit, (TakingDoubleArgument)&QDoubleSpinBox::valueChanged, this, &GeoreferencingDialog::eastingNorthingEdited); connect(lat_edit, (TakingDoubleArgument)&QDoubleSpinBox::valueChanged, this, &GeoreferencingDialog::latLonEdited); connect(lon_edit, (TakingDoubleArgument)&QDoubleSpinBox::valueChanged, this, &GeoreferencingDialog::latLonEdited); connect(keep_geographic_radio, &QRadioButton::toggled, this, &GeoreferencingDialog::keepCoordsChanged); connect(declination_edit, (TakingDoubleArgument)&QDoubleSpinBox::valueChanged, this, &GeoreferencingDialog::declinationEdited); connect(declination_button, &QPushButton::clicked, this, &GeoreferencingDialog::requestDeclination); connect(buttons_box, &QDialogButtonBox::accepted, this, &GeoreferencingDialog::accept); connect(buttons_box, &QDialogButtonBox::rejected, this, &GeoreferencingDialog::reject); connect(reset_button, &QPushButton::clicked, this, &GeoreferencingDialog::reset); connect(help_button, &QPushButton::clicked, this, &GeoreferencingDialog::showHelp); connect(georef.data(), &Georeferencing::stateChanged, this, &GeoreferencingDialog::georefStateChanged); connect(georef.data(), &Georeferencing::transformationChanged, this, &GeoreferencingDialog::transformationChanged); connect(georef.data(), &Georeferencing::projectionChanged, this, &GeoreferencingDialog::projectionChanged); connect(georef.data(), &Georeferencing::declinationChanged, this, &GeoreferencingDialog::declinationChanged); transformationChanged(); georefStateChanged(); declinationChanged(); } GeoreferencingDialog::~GeoreferencingDialog() { if (tool_active) controller->setOverrideTool(nullptr); } // slot void GeoreferencingDialog::georefStateChanged() { const QSignalBlocker block(crs_selector); switch (georef->getState()) { case Georeferencing::Local: crs_selector->setCurrentItem(Georeferencing::Local); keep_geographic_radio->setEnabled(false); keep_projected_radio->setChecked(true); break; default: qDebug() << "Unhandled georeferencing state:" << georef->getState(); // fall through case Georeferencing::Normal: projectionChanged(); keep_geographic_radio->setEnabled(true); } updateWidgets(); } // slot void GeoreferencingDialog::transformationChanged() { ScopedMultiSignalsBlocker block( map_x_edit, map_y_edit, easting_edit, northing_edit, scale_factor_edit ); map_x_edit->setValue(georef->getMapRefPoint().x()); map_y_edit->setValue(-1 * georef->getMapRefPoint().y()); easting_edit->setValue(georef->getProjectedRefPoint().x()); northing_edit->setValue(georef->getProjectedRefPoint().y()); scale_factor_edit->setValue(georef->getGridScaleFactor()); updateGrivation(); } // slot void GeoreferencingDialog::projectionChanged() { ScopedMultiSignalsBlocker block( crs_selector, lat_edit, lon_edit ); if (georef->getState() == Georeferencing::Normal) { const std::vector< QString >& parameters = georef->getProjectedCRSParameters(); auto temp = CRSTemplateRegistry().find(georef->getProjectedCRSId()); if (!temp || temp->parameters().size() != parameters.size()) { // The CRS id is not there anymore or the number of parameters has changed. // Enter as custom spec. crs_selector->setCurrentCRS(CRSTemplateRegistry().find(QString::fromLatin1("PROJ.4")), { georef->getProjectedCRSSpec() }); } else { crs_selector->setCurrentCRS(temp, parameters); } } LatLon latlon = georef->getGeographicRefPoint(); double latitude = latlon.latitude(); double longitude = latlon.longitude(); lat_edit->setValue(latitude); lon_edit->setValue(longitude); QString osm_link = QString::fromLatin1("http://www.openstreetmap.org/?lat=%1&lon=%2&zoom=18&layers=M"). arg(latitude).arg(longitude); QString worldofo_link = QString::fromLatin1("http://maps.worldofo.com/?zoom=15&lat=%1&lng=%2"). arg(latitude).arg(longitude); link_label->setText( tr("OpenStreetMap | World of O Maps"). arg(osm_link, worldofo_link) ); QString error = georef->getErrorText(); if (error.length() == 0) status_field->setText(tr("valid")); else status_field->setText(QLatin1String("") + error + QLatin1String("")); } // slot void GeoreferencingDialog::declinationChanged() { const QSignalBlocker block(declination_edit); declination_edit->setValue(georef->getDeclination()); } void GeoreferencingDialog::requestDeclination(bool no_confirm) { if (georef->isLocal()) return; /// \todo Move URL (template) to settings. QString user_url(QString::fromLatin1("https://www.ngdc.noaa.gov/geomag-web/")); QUrl service_url(user_url + QLatin1String("calculators/calculateDeclination")); LatLon latlon(georef->getGeographicRefPoint()); if (!no_confirm) { int result = QMessageBox::question(this, tr("Online declination lookup"), trUtf8("The magnetic declination for the reference point %1° %2° will now be retrieved from %3. Do you want to continue?"). arg(latlon.latitude()).arg(latlon.longitude()).arg(user_url), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes ); if (result != QMessageBox::Yes) return; } QUrlQuery query; QDate today = QDate::currentDate(); query.addQueryItem(QString::fromLatin1("lat1"), QString::number(latlon.latitude())); query.addQueryItem(QString::fromLatin1("lon1"), QString::number(latlon.longitude())); query.addQueryItem(QString::fromLatin1("startYear"), QString::number(today.year())); query.addQueryItem(QString::fromLatin1("startMonth"), QString::number(today.month())); query.addQueryItem(QString::fromLatin1("startDay"), QString::number(today.day())); #if defined(Q_OS_WIN) || defined(Q_OS_MACOS) || defined(Q_OS_ANDROID) || !defined(QT_NETWORK_LIB) // No QtNetwork or no OpenSSL: open result in system browser. query.addQueryItem(QString::fromLatin1("resultFormat"), QString::fromLatin1("html")); service_url.setQuery(query); QDesktopServices::openUrl(service_url); #else // Use result directly query.addQueryItem(QString::fromLatin1("resultFormat"), QString::fromLatin1("xml")); service_url.setQuery(query); declination_query_in_progress = true; updateDeclinationButton(); auto network = new QNetworkAccessManager(this); connect(network, &QNetworkAccessManager::finished, this, &GeoreferencingDialog::declinationReplyFinished); network->get(QNetworkRequest(service_url)); #endif } void GeoreferencingDialog::setMapRefPoint(MapCoord coords) { georef->setMapRefPoint(coords); reset_button->setEnabled(true); } void GeoreferencingDialog::setKeepProjectedRefCoords() { keep_projected_radio->setChecked(true); reset_button->setEnabled(true); } void GeoreferencingDialog::setKeepGeographicRefCoords() { keep_geographic_radio->setChecked(true); reset_button->setEnabled(true); } void GeoreferencingDialog::toolDeleted() { tool_active = false; } void GeoreferencingDialog::showHelp() { Util::showHelp(parentWidget(), "georeferencing.html"); } void GeoreferencingDialog::reset() { grivation_locked = ( !initial_georef->isValid() || initial_georef->getState() != Georeferencing::Normal ); if (!grivation_locked) original_declination = initial_georef->getDeclination(); *georef.data() = *initial_georef; reset_button->setEnabled(false); } void GeoreferencingDialog::accept() { float declination_change_degrees = georef->getDeclination() - initial_georef->getDeclination(); if ( !grivation_locked && declination_change_degrees != 0 && (map->getNumObjects() > 0 || map->getNumTemplates() > 0) ) { int result = QMessageBox::question(this, tr("Declination change"), tr("The declination has been changed. Do you want to rotate the map content accordingly, too?"), QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel); if (result == QMessageBox::Cancel) { return; } else if (result == QMessageBox::Yes) { RotateMapDialog dialog(this, map); dialog.setWindowModality(Qt::WindowModal); dialog.setRotationDegrees(declination_change_degrees); dialog.setRotateAroundGeorefRefPoint(); dialog.setAdjustDeclination(false); dialog.showAdjustDeclination(false); int result = dialog.exec(); if (result == QDialog::Rejected) return; } } map->setGeoreferencing(*georef); QDialog::accept(); } void GeoreferencingDialog::updateWidgets() { ref_point_button->setEnabled(controller); if (crs_selector->currentCRSTemplate()) projected_ref_label->setText(crs_selector->currentCRSTemplate()->coordinatesName(crs_selector->parameters()) + QLatin1Char(':')); else projected_ref_label->setText(tr("Local coordinates:")); bool geographic_coords_enabled = crs_selector->currentCustomItem() != Georeferencing::Local; status_label->setVisible(geographic_coords_enabled); status_field->setVisible(geographic_coords_enabled); lat_edit->setEnabled(geographic_coords_enabled); lon_edit->setEnabled(geographic_coords_enabled); link_label->setEnabled(geographic_coords_enabled); //keep_geographic_radio->setEnabled(geographic_coords_enabled); updateDeclinationButton(); buttons_box->button(QDialogButtonBox::Ok)->setEnabled(georef->isValid()); } void GeoreferencingDialog::updateDeclinationButton() { /* bool dialog_enabled = crs_edit->getSelectedCustomItemId() != 0; bool proj_spec_visible = crs_edit->getSelectedCustomItemId() == 1; bool geographic_coords_enabled = dialog_enabled && (proj_spec_visible || crs_edit->getSelectedCustomItemId() == -1); */ bool enabled = lat_edit->isEnabled() && !declination_query_in_progress; declination_button->setEnabled(enabled); declination_button->setText(declination_query_in_progress ? tr("Loading...") : tr("Lookup...")); } void GeoreferencingDialog::updateGrivation() { QString text = trUtf8("%1 °", "degree value").arg(QLocale().toString(georef->getGrivation(), 'f', Georeferencing::declinationPrecision())); if (grivation_locked) text.append(QString::fromLatin1(" (%1)").arg(tr("locked"))); grivation_label->setText(text); } void GeoreferencingDialog::crsEdited() { Georeferencing georef_copy = *georef; auto crs_template = crs_selector->currentCRSTemplate(); auto spec = crs_selector->currentCRSSpec(); auto selected_item_id = crs_selector->currentCustomItem(); switch (selected_item_id) { default: qWarning("Unsupported CRS item id"); // fall through case Georeferencing::Local: // Local georef_copy.setState(Georeferencing::Local); break; case -1: // CRS from list Q_ASSERT(crs_template); georef_copy.setProjectedCRS(crs_template->id(), spec, crs_selector->parameters()); georef_copy.setState(Georeferencing::Normal); // Allow invalid spec if (keep_geographic_radio->isChecked()) georef_copy.setGeographicRefPoint(georef->getGeographicRefPoint(), !grivation_locked); else georef_copy.setProjectedRefPoint(georef->getProjectedRefPoint(), !grivation_locked); break; } // Apply all changes at once *georef = georef_copy; reset_button->setEnabled(true); } void GeoreferencingDialog::scaleFactorEdited() { const QSignalBlocker block{scale_factor_edit}; georef->setGridScaleFactor(scale_factor_edit->value()); reset_button->setEnabled(true); } void GeoreferencingDialog::selectMapRefPoint() { if (controller) { controller->setOverrideTool(new GeoreferencingTool(this, controller)); tool_active = true; hide(); } } void GeoreferencingDialog::mapRefChanged() { MapCoord coord(map_x_edit->value(), -1 * map_y_edit->value()); setMapRefPoint(coord); } void GeoreferencingDialog::eastingNorthingEdited() { const QSignalBlocker block1(keep_geographic_radio), block2(keep_projected_radio); double easting = easting_edit->value(); double northing = northing_edit->value(); georef->setProjectedRefPoint(QPointF(easting, northing), !grivation_locked); keep_projected_radio->setChecked(true); reset_button->setEnabled(true); } void GeoreferencingDialog::latLonEdited() { const QSignalBlocker block1(keep_geographic_radio), block2(keep_projected_radio); double latitude = lat_edit->value(); double longitude = lon_edit->value(); georef->setGeographicRefPoint(LatLon(latitude, longitude), !grivation_locked); keep_geographic_radio->setChecked(true); reset_button->setEnabled(true); } void GeoreferencingDialog::keepCoordsChanged() { if (grivation_locked && keep_geographic_radio->isChecked()) { grivation_locked = false; original_declination = georef->getDeclination(); updateGrivation(); } reset_button->setEnabled(true); } void GeoreferencingDialog::declinationEdited(double value) { if (grivation_locked) { grivation_locked = false; original_declination = georef->getDeclination(); updateGrivation(); } georef->setDeclination(value); reset_button->setEnabled(true); } void GeoreferencingDialog::declinationReplyFinished(QNetworkReply* reply) { #if defined(QT_NETWORK_LIB) declination_query_in_progress = false; updateDeclinationButton(); QString error_string; if (reply->error() != QNetworkReply::NoError) { error_string = reply->errorString(); } else { QXmlStreamReader xml(reply); while (xml.readNextStartElement()) { if (xml.name() == QLatin1String("maggridresult")) { while(xml.readNextStartElement()) { if (xml.name() == QLatin1String("result")) { while (xml.readNextStartElement()) { if (xml.name() == QLatin1String("declination")) { QString text = xml.readElementText(QXmlStreamReader::IncludeChildElements); bool ok; double declination = text.toDouble(&ok); if (ok) { declination_edit->setValue(Georeferencing::roundDeclination(declination)); return; } else { error_string = tr("Could not parse data.") + QLatin1Char(' '); } } xml.skipCurrentElement(); // child of result } } xml.skipCurrentElement(); // child of mapgridresult } } else if (xml.name() == QLatin1String("errors")) { error_string.append(xml.readElementText(QXmlStreamReader::IncludeChildElements) + QLatin1Char(' ')); } xml.skipCurrentElement(); // child of root } if (xml.error() != QXmlStreamReader::NoError) { error_string.append(xml.errorString()); } else if (error_string.isEmpty()) { error_string = tr("Declination value not found."); } } int result = QMessageBox::critical(this, tr("Online declination lookup"), tr("The online declination lookup failed:\n%1").arg(error_string), QMessageBox::Retry | QMessageBox::Close, QMessageBox::Close ); if (result == QMessageBox::Retry) requestDeclination(true); #else Q_UNUSED(reply) #endif } // ### GeoreferencingTool ### GeoreferencingTool::GeoreferencingTool(GeoreferencingDialog* dialog, MapEditorController* controller, QAction* action) : MapEditorTool(controller, Other, action) , dialog(dialog) { // nothing } GeoreferencingTool::~GeoreferencingTool() { dialog->toolDeleted(); } void GeoreferencingTool::init() { setStatusBarText(tr("Click: Set the reference point. Right click: Cancel.")); MapEditorTool::init(); } bool GeoreferencingTool::mousePressEvent(QMouseEvent* event, MapCoordF /*map_coord*/, MapWidget* /*widget*/) { bool handled = false; switch (event->button()) { case Qt::LeftButton: case Qt::RightButton: handled = true; break; default: ; // nothing } return handled; } bool GeoreferencingTool::mouseReleaseEvent(QMouseEvent* event, MapCoordF map_coord, MapWidget* /*widget*/) { bool handled = false; switch (event->button()) { case Qt::LeftButton: dialog->setMapRefPoint(MapCoord(map_coord)); // fall through case Qt::RightButton: QTimer::singleShot(0, dialog, SIGNAL(exec())); // clazy:exclude=old-style-connect handled = true; break; default: ; // nothing } return handled; } const QCursor& GeoreferencingTool::getCursor() const { static auto const cursor = scaledToScreen(QCursor{ QPixmap{ QString::fromLatin1(":/images/cursor-crosshair.png") }, 11, 11 }); return cursor; } } // namespace OpenOrienteering mapper-0.8.1.1/src/gui/georeferencing_dialog.h000066400000000000000000000210301325266516600212000ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2012-2015 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_GEOREFERENCING_DIALOG_H #define OPENORIENTEERING_GEOREFERENCING_DIALOG_H #include #include #include #include "core/map_coord.h" #include "tools/tool.h" class QAction; class QCursor; class QDialogButtonBox; class QDoubleSpinBox; class QLabel; class QMouseEvent; class QPushButton; class QRadioButton; class QNetworkReply; class QWidget; namespace OpenOrienteering { class CRSSelector; class Georeferencing; class Map; class MapEditorController; class MapWidget; /** * A GeoreferencingDialog allows the user to adjust the georeferencing properties * of a map. */ class GeoreferencingDialog : public QDialog { Q_OBJECT public: /** * Constructs a new georeferencing dialog for the map handled by the given * controller. The optional parameter initial allows to override the current * properties of the map's georeferencing. The parameter * allow_no_georeferencing determines if the okay button can * be clicked while "- none -" is selected. */ GeoreferencingDialog(MapEditorController* controller, const Georeferencing* initial = nullptr, bool allow_no_georeferencing = true); /** * Constructs a new georeferencing dialog for the given map. The optional * parameter initial allows to override the current properties of the map's * georeferencing. Since the dialog will not know a MapEditorController, * it will not allow to select a new reference point from the map. * The parameter allow_no_georeferencing determines if the okay button can * be clicked while "- none -" is selected. */ GeoreferencingDialog(QWidget* parent, Map* map, const Georeferencing* initial = nullptr, bool allow_no_georeferencing = true); protected: /** * Constructs a new georeferencing dialog. * * The map parameter must not be nullptr, and it must not be a different * map than the one handled by controller. * * @param parent A parent widget. * @param controller A controller which operates on the map. * @param map The map. * @param initial An override of the map's georeferencing * @param allow_no_georeferencing Determines if the okay button can be * be clicked while "- none -" is selected. */ GeoreferencingDialog( QWidget* parent, MapEditorController* controller, Map* map, const Georeferencing* initial, bool allow_no_georeferencing ); public: /** * Releases resources. */ ~GeoreferencingDialog() override; /** * Updates the dialog from georeferencing state changes. */ void georefStateChanged(); /** * Moves transformation properties from the georeferencing to the widgets. */ void transformationChanged(); /** * Moves projection properties from the georeferencing to the widgets. */ void projectionChanged(); /** * Updates the declination widget from the georeferencing. */ void declinationChanged(); /** * Triggers an online request for the magnetic declination. * * @param no_confirm If true, the user will not be asked for confirmation. */ void requestDeclination(bool no_confirm = false); /** * Sets the map coordinates of the reference point */ void setMapRefPoint(MapCoord coords); /** * Activates the "keep projected reference point coordinates on CRS changes" radio button. */ void setKeepProjectedRefCoords(); /** * Activates the "keep geographic reference point coordinates on CRS changes" radio button. */ void setKeepGeographicRefCoords(); /** * Notifies the dialog that the active GeoreferencingTool was deleted. */ void toolDeleted(); /** * Opens this dialog's help page. */ void showHelp(); /** * Resets all input fields to the values in the map's Georeferencing. * * This will also reset initial values passed to the constructor. */ void reset(); /** * Pushes the changes from the dialog to the map's Georeferencing * and closes the dialog. The dialog's result is set to QDialog::Accepted, * and the active exec() function will return. */ void accept() override; protected: /** * Updates enabled / disabled states of all widgets. */ void updateWidgets(); /** * Updates enabled / disabled state and text of the declination query button. */ void updateDeclinationButton(); /** * Notifies the dialog of a change in the CRS configuration. */ void crsEdited(); /** * Notifies the dialog of a change in the grid scale factor. */ void scaleFactorEdited(); /** * Hides the dialog and activates a GeoreferencingTool for selecting * the reference point on the map. */ void selectMapRefPoint(); /** * Notifies the dialog of a change in the map reference point fields. */ void mapRefChanged(); /** * Notifies the dialog of a change in the easting / northing fields. */ void eastingNorthingEdited(); /** * Notifies the dialog of change of the keep-coords buttons. */ void keepCoordsChanged(); /** * Notifies the dialog of a change in the latitude / longitude fields. */ void latLonEdited(); /** * Notifies the dialog of a change in the declination field. */ void declinationEdited(double value); /** * Handles replies from the online declination service. */ void declinationReplyFinished(QNetworkReply* reply); /** * Updates the grivation field from the underlying Georeferencing. */ void updateGrivation(); private: /* Internal state */ MapEditorController* const controller; Map* const map; const Georeferencing* initial_georef; QScopedPointer georef; // A working copy of the current or given initial Georeferencing bool allow_no_georeferencing; bool tool_active; bool declination_query_in_progress; bool grivation_locked; double original_declination; /* GUI elements */ CRSSelector* crs_selector; QLabel* status_label; QLabel* status_field; QDoubleSpinBox* scale_factor_edit; QDoubleSpinBox* map_x_edit; QDoubleSpinBox* map_y_edit; QPushButton* ref_point_button; QLabel* projected_ref_label; QDoubleSpinBox* easting_edit; QDoubleSpinBox* northing_edit; QDoubleSpinBox* lat_edit; QDoubleSpinBox* lon_edit; QLabel* show_refpoint_label; QLabel* link_label; QRadioButton* keep_projected_radio; QRadioButton* keep_geographic_radio; QDoubleSpinBox* declination_edit; QPushButton* declination_button; QLabel* grivation_label; QDialogButtonBox* buttons_box; QPushButton* reset_button; }; /** * GeoreferencingTool is a helper to the GeoreferencingDialog which allows * the user to select the position of the reference point on the map * The GeoreferencingDialog hides when it activates this tool. The tool * takes care of reactivating the dialog. */ class GeoreferencingTool : public MapEditorTool { Q_OBJECT public: /** * Constructs a new tool for the given dialog and controller. */ GeoreferencingTool( GeoreferencingDialog* dialog, MapEditorController* controller, QAction* action = nullptr ); /** * Notifies the dialog that the tool is deleted. */ ~GeoreferencingTool() override; /** * Activates the tool. */ void init() override; /** * Consumes left and right clicks. They are handled in mouseReleaseEvent. */ bool mousePressEvent(QMouseEvent* event, MapCoordF map_coord, MapWidget* widget) override; /** * Reacts to the user activity by sending the reference point coordinates * to the dialog (on left click) and reactivating the dialog. */ bool mouseReleaseEvent(QMouseEvent* event, MapCoordF map_coord, MapWidget* widget) override; /** * Returns the mouse cursor that will be shown when the tool is active. */ const QCursor& getCursor() const override; private: GeoreferencingDialog* const dialog; }; } // namespace OpenOrienteering #endif mapper-0.8.1.1/src/gui/home_screen_controller.cpp000066400000000000000000000076521325266516600220020ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps, Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "home_screen_controller.h" #include #include #include "settings.h" #include "gui/main_window.h" #include "gui/widgets/home_screen_widget.h" namespace OpenOrienteering { HomeScreenController::HomeScreenController() : widget(nullptr) , current_tip(-1) { // nothing } HomeScreenController::~HomeScreenController() = default; void HomeScreenController::attach(MainWindow* window) { this->window = window; if (MainWindow::mobileMode()) { widget = new HomeScreenWidgetMobile(this); } else { widget = new HomeScreenWidgetDesktop(this); window->statusBar()->hide(); window->setStatusBarText(QString{}); } window->setCentralWidget(widget); connect(&Settings::getInstance(), &Settings::settingsChanged, this, &HomeScreenController::readSettings); readSettings(); } void HomeScreenController::detach() { if (!MainWindow::mobileMode()) { window->statusBar()->show(); } window->setCentralWidget(nullptr); widget->deleteLater(); Settings::getInstance().setSetting(Settings::HomeScreen_CurrentTip, current_tip); } void HomeScreenController::readSettings() { Settings& settings = Settings::getInstance(); // FIXME: settings should be const widget->setRecentFiles(settings.getSettingCached(Settings::General_RecentFilesList).toStringList()); widget->setOpenMRUFileChecked(settings.getSettingCached(Settings::General_OpenMRUFile).toBool()); bool tips_visible = settings.getSettingCached(Settings::HomeScreen_TipsVisible).toBool(); widget->setTipsVisible(tips_visible); if (tips_visible) { if (current_tip < 0) { current_tip = settings.getSettingCached(Settings::HomeScreen_CurrentTip).toInt(); goToNextTip(); } else { // Settings changed. goToTip(current_tip); } } } void HomeScreenController::setOpenMRUFile(bool state) { Settings::getInstance().setSetting(Settings::General_OpenMRUFile, state); } void HomeScreenController::clearRecentFiles() { Settings::getInstance().remove(Settings::General_RecentFilesList); } void HomeScreenController::setTipsVisible(bool state) { Settings::getInstance().setSetting(Settings::HomeScreen_TipsVisible, state); } void HomeScreenController::goToNextTip() { goToTip(current_tip + 1); } void HomeScreenController::goToPreviousTip() { goToTip(current_tip - 1); } void HomeScreenController::goToTip(int index) { static QStringList tips; if (tips.isEmpty()) { // Normally, this will be read only once. QFile file(QString::fromLatin1("doc:tip-of-the-day/tips.txt")); if (file.open(QIODevice::ReadOnly)) { while (!file.atEnd()) { QString tip(QString::fromUtf8(file.readLine().constData())); if (tip.endsWith(QLatin1Char('\n'))) tip.chop(1); if (!tip.isEmpty()) tips.push_back(tip); } } } if (tips.isEmpty()) { // Some error may have occurred during reading the tips file. // Display a welcome text. widget->setTipOfTheDay(QString::fromLatin1("

    %1

    ").arg(tr("Welcome to OpenOrienteering Mapper!"))); } else { Q_ASSERT(tips.count() > 0); while (index < 0) index += tips.count(); current_tip = index % tips.count(); widget->setTipOfTheDay(tips[current_tip]); } } } // namespace OpenOrienteering mapper-0.8.1.1/src/gui/home_screen_controller.h000066400000000000000000000050061325266516600214360ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps, Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_HOME_SCREEN_CONTROLLER_H #define OPENORIENTEERING_HOME_SCREEN_CONTROLLER_H #include "main_window_controller.h" #include namespace OpenOrienteering { class AbstractHomeScreenWidget; class MainWindow; /** * The controller of the OpenOrienteering Mapper home screen. * The OpenOrienteering Mapper home screen is shown when no document is open, * for example after the program is started for the first time. */ class HomeScreenController : public MainWindowController { Q_OBJECT public: /** Creates a new HomeScreenController. */ HomeScreenController(); /** Destroys the HomeScreenController and its children. */ ~HomeScreenController() override; /** Activates the HomeScreenController for the given main window. */ void attach(MainWindow* window) override; /** Detaches the HomeScreenController from its main window. */ void detach() override; public slots: /** (Re-)reads the settings. */ void readSettings(); /** Clears the application's list of recently opened files. */ void clearRecentFiles(); /** Sets whether to open the most recently used file on startup. */ void setOpenMRUFile(bool state); /** Sets the visiblity of the tip-of-the-day to state. */ void setTipsVisible(bool state); /** Moves to the tip following the current tip-of-the-day. */ void goToPreviousTip(); /** Moves to the tip preceding the current tip-of-the-day. */ void goToNextTip(); /** Moves to the tip-of-the-day given by index. */ void goToTip(int index); protected: /** The widget owned and controlled by this HomeScreenController. */ AbstractHomeScreenWidget* widget; /** The index of the tip-of-the-day currently displayed. */ int current_tip; }; } // namespace OpenOrienteering #endif mapper-0.8.1.1/src/gui/main_window.cpp000066400000000000000000001034661325266516600175630ustar00rootroot00000000000000/* * Copyright 2012, 2013, 2014 Thomas Schöps * Copyright 2012-2018 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "main_window.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(Q_OS_ANDROID) # include # include # include # include # include #endif #include #include "settings.h" #include "core/map.h" #include "core/map_view.h" #include "core/symbols/symbol.h" #include "fileformats/file_format.h" #include "fileformats/file_format_registry.h" #include "gui/about_dialog.h" #include "gui/autosave_dialog.h" #include "gui/file_dialog.h" #include "gui/home_screen_controller.h" #include "gui/settings_dialog.h" #include "gui/util_gui.h" #include "gui/map/map_editor.h" #include "gui/map/new_map_dialog.h" #include "undo/undo_manager.h" #include "util/util.h" #include "util/backports.h" namespace OpenOrienteering { constexpr int MainWindow::max_recent_files; int MainWindow::num_open_files = 0; MainWindow::MainWindow(QWidget* parent, Qt::WindowFlags flags) : MainWindow { true, parent, flags } { // nothing else } MainWindow::MainWindow(bool as_main_window, QWidget* parent, Qt::WindowFlags flags) : QMainWindow { parent, flags } , controller { nullptr } , create_menu { as_main_window } , show_menu { create_menu && !mobileMode() } , shortcuts_blocked { false } , general_toolbar { nullptr } , file_menu { nullptr } , has_opened_file { false } , has_unsaved_changes { false } , has_autosave_conflict { false } , maximized_before_fullscreen { false } , homescreen_disabled { false } { setWindowIcon(QIcon(QString::fromLatin1(":/images/mapper.png"))); setAttribute(Qt::WA_DeleteOnClose); status_label = new QLabel(); statusBar()->addWidget(status_label, 1); statusBar()->setSizeGripEnabled(as_main_window); if (mobileMode()) statusBar()->hide(); central_widget = new QStackedWidget(this); QMainWindow::setCentralWidget(central_widget); if (as_main_window) loadWindowSettings(); #if defined(Q_OS_ANDROID) // Needed to catch Qt::Key_Back, cf. MainWindow::eventFilter() qApp->installEventFilter(this); #else installEventFilter(this); #endif connect(&Settings::getInstance(), &Settings::settingsChanged, this, &MainWindow::settingsChanged); connect(qApp, &QGuiApplication::applicationStateChanged, this, &MainWindow::applicationStateChanged); } MainWindow::~MainWindow() { if (controller) { controller->detach(); delete controller; delete general_toolbar; } } void MainWindow::settingsChanged() { updateRecentFileActions(); } void MainWindow::applicationStateChanged() { #ifdef Q_OS_ANDROID // The Android app may be started or resumed when the user triggers a suitable "intent". if (QGuiApplication::applicationState() == Qt::ApplicationActive) { auto activity = QtAndroid::androidActivity(); auto intent_path = activity.callObjectMethod("takeIntentPath").toString(); if (!intent_path.isEmpty()) { const auto local_file = QUrl(intent_path).toLocalFile(); if (!hasOpenedFile()) { openPathLater(local_file); } else if (currentPath() != local_file) { showStatusBarMessage(tr("You must close the current file before you can open another one.")); } return; } } #endif // Only on startup, we may need to load the most recently used file. static bool starting_up = true; if (starting_up) { starting_up = false; QSettings settings; if (path_backlog.isEmpty() && settings.value(QLatin1String("openMRUFile")).toBool()) { const auto files = settings.value(QLatin1String("recentFileList")).toStringList(); if (!files.isEmpty()) openPathLater(files[0]); } } } QString MainWindow::appName() const { return APP_NAME; } #ifndef Q_OS_ANDROID bool MainWindow::mobileMode() { static bool mobile_mode = qEnvironmentVariableIsSet("MAPPER_MOBILE_GUI") ? (qgetenv("MAPPER_MOBILE_GUI") != "0") : 0; return mobile_mode; } #endif void MainWindow::setCentralWidget(QWidget* widget) { if (widget) { // Main window shall not resize to central widget size hint. widget->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored); int index = central_widget->addWidget(widget); central_widget->setCurrentIndex(index); } if (central_widget->count() > 1) { QWidget* w = central_widget->widget(0); central_widget->removeWidget(w); w->deleteLater(); } } void MainWindow::setHomeScreenDisabled(bool disabled) { homescreen_disabled = disabled; } void MainWindow::setController(MainWindowController* new_controller) { setController(new_controller, false); setCurrentPath({}); } void MainWindow::setController(MainWindowController* new_controller, const QString& path) { setController(new_controller, true); setCurrentPath(path); } void MainWindow::setController(MainWindowController* new_controller, bool has_file) { if (controller) { controller->detach(); delete controller; controller = nullptr; if (show_menu) menuBar()->clear(); delete general_toolbar; general_toolbar = nullptr; } has_opened_file = has_file; shortcuts_blocked = false; if (create_menu) createFileMenu(); controller = new_controller; controller->attach(this); if (create_menu) createHelpMenu(); #if defined(Q_OS_MACOS) // Defeat Qt's menu text heuristic, as a workaround for QTBUG-30812. // Changing an action's menu role (to QAction::NoRole) after it was // added to the menu is unsupported and triggers crashes (#1077). // Instead, we defeat the heuristic by adding a zero width space at the // beginning and the end of the text of every action in the menus. const auto menubar_actions = menuBar()->actions(); for (auto action : menubar_actions) { if (const auto menu = action->menu()) { const auto menu_actions = menu->actions(); for (auto action : menu_actions) { static const auto zwsp = QString::fromUtf8("\u200B"); action->setText(zwsp + action->text() + zwsp); } } } // Needed to activate the menu bar changes if (isVisible() && qApp->activeWindow() == this) { // Force a menu synchronisation, // QCocoaMenuBar::updateMenuBarImmediately(), // via QCocoaNativeInterface::onAppFocusWindowChanged(). qApp->focusWindowChanged(qApp->focusWindow()); } #endif setHasAutosaveConflict(false); setHasUnsavedChanges(false); } void MainWindow::createFileMenu() { QAction* new_act = new QAction(QIcon(QString::fromLatin1(":/images/new.png")), tr("&New"), this); new_act->setShortcuts(QKeySequence::New); new_act->setStatusTip(tr("Create a new map")); new_act->setWhatsThis(Util::makeWhatThis("file_menu.html")); connect(new_act, &QAction::triggered, this, &MainWindow::showNewMapWizard); QAction* open_act = new QAction(QIcon(QString::fromLatin1(":/images/open.png")), tr("&Open..."), this); open_act->setShortcuts(QKeySequence::Open); open_act->setStatusTip(tr("Open an existing file")); open_act->setWhatsThis(Util::makeWhatThis("file_menu.html")); connect(open_act, &QAction::triggered, this, &MainWindow::showOpenDialog); open_recent_menu = new QMenu(tr("Open &recent"), this); open_recent_menu->setWhatsThis(Util::makeWhatThis("file_menu.html")); for (auto& action : recent_file_act) { action = new QAction(this); connect(action, &QAction::triggered, this, &MainWindow::openRecentFile); } open_recent_menu_inserted = false; // NOTE: if you insert something between open_recent_menu and save_act, adjust updateRecentFileActions()! save_act = new QAction(QIcon(QString::fromLatin1(":/images/save.png")), tr("&Save"), this); save_act->setShortcuts(QKeySequence::Save); save_act->setWhatsThis(Util::makeWhatThis("file_menu.html")); connect(save_act, &QAction::triggered, this, &MainWindow::save); auto save_as_act = new QAction(tr("Save &as..."), this); if (QKeySequence::keyBindings(QKeySequence::SaveAs).empty()) save_as_act->setShortcut(tr("Ctrl+Shift+S")); else save_as_act->setShortcuts(QKeySequence::SaveAs); save_as_act->setWhatsThis(Util::makeWhatThis("file_menu.html")); connect(save_as_act, &QAction::triggered, this, &MainWindow::showSaveAsDialog); settings_act = new QAction(tr("Settings..."), this); settings_act->setShortcut(QKeySequence::Preferences); settings_act->setMenuRole(QAction::PreferencesRole); connect(settings_act, &QAction::triggered, this, &MainWindow::showSettings); close_act = new QAction(QIcon(QString::fromLatin1(":/images/close.png")), tr("Close"), this); close_act->setShortcut(QKeySequence::Close); close_act->setStatusTip(tr("Close this file")); close_act->setWhatsThis(Util::makeWhatThis("file_menu.html")); connect(close_act, &QAction::triggered, this, &MainWindow::closeFile); QAction* exit_act = new QAction(tr("E&xit"), this); exit_act->setShortcuts(QKeySequence::Quit); exit_act->setStatusTip(tr("Exit the application")); exit_act->setMenuRole(QAction::QuitRole); exit_act->setWhatsThis(Util::makeWhatThis("file_menu.html")); connect(exit_act, &QAction::triggered, qApp, &QApplication::closeAllWindows); if (show_menu) { file_menu = menuBar()->addMenu(tr("&File")); } else { delete file_menu; file_menu = new QMenu(this); } file_menu->setWhatsThis(Util::makeWhatThis("file_menu.html")); file_menu->addAction(new_act); file_menu->addAction(open_act); file_menu->addAction(save_act); file_menu->addAction(save_as_act); file_menu->addSeparator(); file_menu->addAction(settings_act); file_menu->addSeparator(); file_menu->addAction(close_act); file_menu->addAction(exit_act); general_toolbar = new QToolBar(tr("General")); general_toolbar->setObjectName(QString::fromLatin1("General toolbar")); general_toolbar->addAction(new_act); general_toolbar->addAction(open_act); general_toolbar->addAction(save_act); save_act->setEnabled(has_opened_file); save_as_act->setEnabled(has_opened_file); close_act->setEnabled(has_opened_file); updateRecentFileActions(); } void MainWindow::createHelpMenu() { // Help menu QAction* manualAct = new QAction(QIcon(QString::fromLatin1(":/images/help.png")), tr("Open &Manual"), this); manualAct->setStatusTip(tr("Show the help file for this application")); manualAct->setShortcut(QKeySequence::HelpContents); connect(manualAct, &QAction::triggered, this, &MainWindow::showHelp); QAction* aboutAct = new QAction(tr("&About %1").arg(appName()), this); aboutAct->setStatusTip(tr("Show information about this application")); aboutAct->setMenuRole(QAction::AboutRole); connect(aboutAct, &QAction::triggered, this, &MainWindow::showAbout); QAction* aboutQtAct = new QAction(tr("About &Qt"), this); aboutQtAct->setStatusTip(tr("Show information about Qt")); aboutQtAct->setMenuRole(QAction::AboutQtRole); connect(aboutQtAct, &QAction::triggered, qApp, QApplication::aboutQt); if (show_menu) { QMenu* helpMenu = menuBar()->addMenu(tr("&Help")); helpMenu->addAction(manualAct); helpMenu->addAction(QWhatsThis::createAction(this)); helpMenu->addSeparator(); helpMenu->addAction(aboutAct); helpMenu->addAction(aboutQtAct); } } void MainWindow::setCurrentPath(const QString& path) { Q_ASSERT(has_opened_file || path.isEmpty()); if (path != current_path) { QString window_file_path; current_path.clear(); if (has_opened_file) { window_file_path = QFileInfo(path).canonicalFilePath(); if (window_file_path.isEmpty()) window_file_path = tr("Unsaved file"); else current_path = window_file_path; } setWindowFilePath(window_file_path); } else if (!windowFilePath().isEmpty() && !has_opened_file) { setWindowFilePath({}); } } void MainWindow::setMostRecentlyUsedFile(const QString& path) { if (!path.isEmpty()) { Settings& settings = Settings::getInstance(); // Update least recently used directory const QString open_directory = QFileInfo(path).canonicalPath(); QSettings().setValue(QString::fromLatin1("openFileDirectory"), open_directory); // Update recent file lists QStringList files = settings.getSettingCached(Settings::General_RecentFilesList).toStringList(); files.removeAll(path); files.prepend(path); if (files.size() > max_recent_files) files.erase(files.begin() + max_recent_files, files.end()); settings.setSetting(Settings::General_RecentFilesList, files); } } void MainWindow::setHasUnsavedChanges(bool value) { if (hasOpenedFile()) { has_unsaved_changes = value; setAutosaveNeeded(has_unsaved_changes && !has_autosave_conflict); } setWindowModified(has_unsaved_changes); } void MainWindow::setStatusBarText(const QString& text) { status_label->setText(text); status_label->setToolTip(text); } void MainWindow::showStatusBarMessage(const QString& text, int timeout) { #if defined(Q_OS_ANDROID) QAndroidJniObject java_string = QAndroidJniObject::fromString(text); QAndroidJniObject::callStaticMethod( "org/openorienteering/mapper/MapperActivity", "showToast", "(Ljava/lang/String;I)V", java_string.object(), timeout); #else statusBar()->showMessage(text, timeout); #endif } void MainWindow::clearStatusBarMessage() { #if defined(Q_OS_ANDROID) QAndroidJniObject::callStaticMethod( "org/openorienteering/mapper/MapperActivity", "hideToast", "()V"); #else statusBar()->clearMessage(); #endif } void MainWindow::setShortcutsBlocked(bool blocked) { shortcuts_blocked = blocked; } bool MainWindow::closeFile() { bool closed = !has_opened_file || showSaveOnCloseDialog(); if (closed) { if (has_opened_file) { num_open_files--; has_opened_file = false; } if (homescreen_disabled || num_open_files > 0) close(); else setController(new HomeScreenController()); } return closed; } bool MainWindow::event(QEvent* event) { if (event->type() == QEvent::ShortcutOverride && shortcutsBlocked()) event->accept(); return QMainWindow::event(event); } void MainWindow::closeEvent(QCloseEvent* event) { if (!has_opened_file) { saveWindowSettings(); event->accept(); } else if (showSaveOnCloseDialog()) { if (has_opened_file) { num_open_files--; has_opened_file = false; } saveWindowSettings(); event->accept(); } else { event->ignore(); } } void MainWindow::keyPressEvent(QKeyEvent* event) { if (controller && controller->keyPressEventFilter(event)) { // Event filtered, stop handling return; } QMainWindow::keyPressEvent(event); } void MainWindow::keyReleaseEvent(QKeyEvent* event) { if (controller && controller->keyReleaseEventFilter(event)) { // Event filtered, stop handling return; } QMainWindow::keyReleaseEvent(event); } bool MainWindow::showSaveOnCloseDialog() { if (has_opened_file && (has_unsaved_changes || has_autosave_conflict)) { // Show the window in case it is minimized setWindowState( (windowState() & ~Qt::WindowMinimized) | Qt::WindowActive); raise(); activateWindow(); QMessageBox::StandardButton ret; if (!has_unsaved_changes && actual_path != autosavePath(currentPath())) { ret = QMessageBox::warning(this, appName(), tr("Do you want to remove the autosaved version?"), QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel); } else { ret = QMessageBox::warning(this, appName(), tr("The file has been modified.\n" "Do you want to save your changes?"), QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel); } switch (ret) { case QMessageBox::Cancel: return false; case QMessageBox::Discard: if (has_autosave_conflict) setHasAutosaveConflict(false); else removeAutosaveFile(); break; case QMessageBox::Save: if (!save()) return false; // fall through case QMessageBox::Yes: setHasAutosaveConflict(false); removeAutosaveFile(); break; case QMessageBox::No: setHasAutosaveConflict(false); break; default: qWarning("Unsupported return value from message box"); break; } } return true; } void MainWindow::saveWindowSettings() { #if !defined(Q_OS_ANDROID) QSettings settings; settings.beginGroup(QString::fromLatin1("MainWindow")); settings.setValue(QString::fromLatin1("pos"), pos()); settings.setValue(QString::fromLatin1("size"), size()); settings.setValue(QString::fromLatin1("maximized"), isMaximized()); settings.endGroup(); #endif } void MainWindow::loadWindowSettings() { #if defined(Q_OS_ANDROID) // Always show the window on the whole available area on Android resize(QApplication::desktop()->availableGeometry().size()); #else QSettings settings; settings.beginGroup(QString::fromLatin1("MainWindow")); QPoint pos = settings.value(QString::fromLatin1("pos"), QPoint(100, 100)).toPoint(); QSize size = settings.value(QString::fromLatin1("size"), QSize(800, 600)).toSize(); bool maximized = settings.value(QString::fromLatin1("maximized"), false).toBool(); settings.endGroup(); move(pos); resize(size); if (maximized) setWindowState((windowState() & ~(Qt::WindowMinimized | Qt::WindowFullScreen)) | Qt::WindowMaximized); // Cf. QWidget::showMaximized() #endif } MainWindow* MainWindow::findMainWindow(const QString& file_name) { QString canonical_file_path = QFileInfo(file_name).canonicalFilePath(); if (canonical_file_path.isEmpty()) return nullptr; const auto top_level_widgets = qApp->topLevelWidgets(); for (auto widget : top_level_widgets) { MainWindow* other = qobject_cast(widget); if (other && other->currentPath() == canonical_file_path) return other; } return nullptr; } void MainWindow::showNewMapWizard() { NewMapDialog newMapDialog(this); newMapDialog.setWindowModality(Qt::WindowModal); newMapDialog.exec(); if (newMapDialog.result() == QDialog::Rejected) return; Map* new_map = new Map(); MapView tmp_view { nullptr, new_map }; QString symbol_set_path = newMapDialog.getSelectedSymbolSetPath(); if (symbol_set_path.isEmpty()) { new_map->setScaleDenominator(newMapDialog.getSelectedScale()); } else { new_map->loadFrom(symbol_set_path, this, &tmp_view, true); if (new_map->getScaleDenominator() != newMapDialog.getSelectedScale()) { if (QMessageBox::question(this, tr("Warning"), tr("The selected map scale is 1:%1, but the chosen symbol set has a nominal scale of 1:%2.\n\nDo you want to scale the symbols to the selected scale?").arg(newMapDialog.getSelectedScale()).arg(new_map->getScaleDenominator()), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { double factor = double(new_map->getScaleDenominator()) / newMapDialog.getSelectedScale(); new_map->scaleAllSymbols(factor); } new_map->setScaleDenominator(newMapDialog.getSelectedScale()); } for (int i = new_map->getNumSymbols(); i > 0; i = qMin(i, new_map->getNumSymbols())) { --i; auto symbol = new_map->getSymbol(i); if (symbol->isHidden() && !new_map->existsObjectWithSymbol(symbol)) { new_map->deleteSymbol(i); } } } auto map_view = new MapView { new_map }; map_view->setGridVisible(tmp_view.isGridVisible()); new_map->setHasUnsavedChanges(false); new_map->undoManager().clear(); MainWindow* new_window = hasOpenedFile() ? new MainWindow() : this; new_window->setWindowFilePath(tr("Unsaved file")); new_window->setController(new MapEditorController(MapEditorController::MapEditor, new_map, map_view), QString()); new_window->show(); new_window->raise(); new_window->activateWindow(); num_open_files++; } void MainWindow::showOpenDialog() { QString path = getOpenFileName(this, tr("Open file"), FileFormat::AllFiles); if (!path.isEmpty()) openPath(path); } bool MainWindow::openPath(const QString &path) { // Empty path does nothing. This also helps with the single instance application code. if (path.isEmpty()) return true; #ifdef Q_OS_ANDROID showStatusBarMessage(tr("Opening %1").arg(QFileInfo(path).fileName())); #else MainWindow* const existing = findMainWindow(path); if (existing) { existing->show(); existing->raise(); existing->activateWindow(); return true; } #endif // Check a blocker that prevents immediate re-opening of crashing files. // Needed for stopping auto-loading a crashing file on startup. static const QString reopen_blocker = QString::fromLatin1("open_in_progress"); QSettings settings; const QString open_in_progress(settings.value(reopen_blocker).toString()); if (open_in_progress == path) { int result = QMessageBox::warning(this, tr("Crash warning"), tr("It seems that %1 crashed the last time this file was opened:
    " "%2

    " "Really retry to open it?") .arg(appName(), path), QMessageBox::Yes | QMessageBox::No); settings.remove(reopen_blocker); if (result == QMessageBox::No) return false; } settings.setValue(reopen_blocker, path); settings.sync(); MainWindowController* const new_controller = MainWindowController::controllerForFile(path); if (!new_controller) { QMessageBox::warning(this, tr("Error"), tr("Cannot open file:\n%1\n\nFile format not recognized.").arg(path)); settings.remove(reopen_blocker); return false; } QString new_actual_path = path; QString autosave_path = Autosave::autosavePath(path); bool new_autosave_conflict = QFileInfo::exists(autosave_path); if (new_autosave_conflict) { #if defined(Q_OS_ANDROID) // Assuming small screen, showing dialog before opening the file AutosaveDialog* autosave_dialog = new AutosaveDialog(path, autosave_path, autosave_path, this); int result = autosave_dialog->exec(); new_actual_path = (result == QDialog::Accepted) ? autosave_dialog->selectedPath() : QString(); delete autosave_dialog; #else // Assuming large screen, dialog will be shown while the autosaved file is open new_actual_path = autosave_path; #endif } if (new_actual_path.isEmpty() || !new_controller->load(new_actual_path, this)) { delete new_controller; settings.remove(reopen_blocker); return false; } MainWindow* open_window = this; #if !defined(Q_OS_ANDROID) if (has_opened_file) open_window = new MainWindow(); #endif open_window->setController(new_controller, path); open_window->actual_path = new_actual_path; open_window->setHasAutosaveConflict(new_autosave_conflict); open_window->setHasUnsavedChanges(false); open_window->setVisible(true); // Respect the window flags set by new_controller. open_window->raise(); num_open_files++; settings.remove(reopen_blocker); setMostRecentlyUsedFile(path); #if !defined(Q_OS_ANDROID) // Assuming large screen. Android handled above. if (new_autosave_conflict) { auto autosave_dialog = new AutosaveDialog(path, autosave_path, new_actual_path, open_window, Qt::WindowTitleHint | Qt::CustomizeWindowHint); autosave_dialog->move(open_window->rect().right() - autosave_dialog->width(), open_window->rect().top()); autosave_dialog->show(); autosave_dialog->raise(); connect(autosave_dialog, &AutosaveDialog::pathSelected, open_window, &MainWindow::switchActualPath); connect(open_window, &MainWindow::actualPathChanged, autosave_dialog, &AutosaveDialog::setSelectedPath); connect(open_window, &MainWindow::autosaveConflictResolved, autosave_dialog, &AutosaveDialog::autosaveConflictResolved); } #endif open_window->activateWindow(); return true; } void MainWindow::switchActualPath(const QString& path) { if (path == actual_path) { return; } int ret = QMessageBox::Ok; if (has_unsaved_changes) { ret = QMessageBox::warning(this, appName(), tr("The file has been modified.\n" "Do you want to discard your changes?"), QMessageBox::Discard | QMessageBox::Cancel); } if (ret != QMessageBox::Cancel) { const QString& current_path = currentPath(); MainWindowController* const new_controller = MainWindowController::controllerForFile(current_path); if (new_controller && new_controller->load(path, this)) { setController(new_controller, current_path); actual_path = path; setHasUnsavedChanges(false); } } emit actualPathChanged(actual_path); activateWindow(); } void MainWindow::openPathLater(const QString& path) { path_backlog.push_back(path); QTimer::singleShot(10, this, SLOT(openPathBacklog())); // clazy:exclude=old-style-connect } void MainWindow::openPathBacklog() { for (const auto& path : qAsConst(path_backlog)) openPath(path); path_backlog.clear(); } void MainWindow::openRecentFile() { if (auto action = qobject_cast(sender())) openPath(action->data().toString()); } void MainWindow::updateRecentFileActions() { if (! create_menu) return; QStringList files = Settings::getInstance().getSettingCached(Settings::General_RecentFilesList).toStringList(); int num_recent_files = qMin(files.size(), max_recent_files); open_recent_menu->clear(); for (int i = 0; i < num_recent_files; ++i) { QString text = tr("&%1 %2").arg(i + 1).arg(QFileInfo(files[i]).fileName()); recent_file_act[i]->setText(text); recent_file_act[i]->setData(files[i]); open_recent_menu->addAction(recent_file_act[i]); } if (num_recent_files > 0 && !open_recent_menu_inserted) file_menu->insertMenu(save_act, open_recent_menu); else if (!(num_recent_files > 0) && open_recent_menu_inserted) file_menu->removeAction(open_recent_menu->menuAction()); open_recent_menu_inserted = num_recent_files > 0; } void MainWindow::setHasAutosaveConflict(bool value) { if (has_autosave_conflict != value) { has_autosave_conflict = value; setAutosaveNeeded(has_unsaved_changes && !has_autosave_conflict); if (!has_autosave_conflict) emit autosaveConflictResolved(); } } bool MainWindow::removeAutosaveFile() const { if (!currentPath().isEmpty() && !has_autosave_conflict) { QFile autosave_file(autosavePath(currentPath())); return !autosave_file.exists() || autosave_file.remove(); } return false; } Autosave::AutosaveResult MainWindow::autosave() { QString path = currentPath(); if (path.isEmpty() || !controller) { return Autosave::PermanentFailure; } else if (controller->isEditingInProgress()) { return Autosave::TemporaryFailure; } else { showStatusBarMessage(tr("Autosaving..."), 0); if (controller->exportTo(autosavePath(currentPath()))) { // Success clearStatusBarMessage(); return Autosave::Success; } else { // Failure showStatusBarMessage(tr("Autosaving failed!"), 6000); return Autosave::PermanentFailure; } } } bool MainWindow::save() { return savePath(currentPath()); } bool MainWindow::savePath(const QString &path) { if (!controller) return false; if (path.isEmpty()) return showSaveAsDialog(); const FileFormat *format = FileFormats.findFormatForFilename(path); if (format->isExportLossy()) { QString message = tr("This map is being saved as a \"%1\" file. Information may be lost.\n\nPress Yes to save in this format.\nPress No to choose a different format.").arg(format->description()); int result = QMessageBox::warning(this, tr("Warning"), message, QMessageBox::Yes, QMessageBox::No); if (result != QMessageBox::Yes) return showSaveAsDialog(); } if (!controller->save(path)) return false; setMostRecentlyUsedFile(path); setHasAutosaveConflict(false); removeAutosaveFile(); if (path != currentPath()) { setCurrentPath(path); removeAutosaveFile(); } setHasUnsavedChanges(false); return true; } QString MainWindow::getOpenFileName(QWidget* parent, const QString& title, FileFormat::FileTypes types) { // Get the saved directory to start in, defaulting to the user's home directory. QSettings settings; QString open_directory = settings.value(QString::fromLatin1("openFileDirectory"), QDir::homePath()).toString(); // Build the list of supported file filters based on the file format registry QString filters, extensions; if (types.testFlag(FileFormat::MapFile)) { for (auto format : FileFormats.formats()) { if (format->supportsImport()) { if (filters.isEmpty()) { filters = format->filter(); extensions = QLatin1String("*.") + format->fileExtensions().join(QString::fromLatin1(" *.")); } else { filters = filters + QLatin1String(";;") + format->filter(); extensions = extensions + QLatin1String(" *.") + format->fileExtensions().join(QString::fromLatin1(" *.")); } } } filters = tr("All maps") + QLatin1String(" (") + extensions + QLatin1String(");;") + filters + QLatin1String(";;"); } filters += tr("All files") + QLatin1String(" (*.*)"); QString path = FileDialog::getOpenFileName(parent, title, open_directory, filters); QFileInfo info(path); return info.canonicalFilePath(); } bool MainWindow::showSaveAsDialog() { if (!controller) return false; // Try current directory first QFileInfo current(currentPath()); QString save_directory = current.canonicalPath(); if (save_directory.isEmpty()) { // revert to least recently used directory or home directory. QSettings settings; save_directory = settings.value(QString::fromLatin1("openFileDirectory"), QDir::homePath()).toString(); } // Build the list of supported file filters based on the file format registry QString filters; for (auto format : FileFormats.formats()) { if (format->supportsExport()) { if (filters.isEmpty()) filters = format->filter(); else filters = filters + QLatin1String(";;") + format->filter(); } } QString filter; // will be set to the selected filter by QFileDialog QString path = FileDialog::getSaveFileName(this, tr("Save file"), save_directory, filters, &filter); // On Windows, when the user enters "sample", we get "sample.omap *.xmap". // (Fixed in upstream qtbase/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp // Wednesday March 20 2013 in commit 426f2cc.) // This results in an error later, because "*" is not a valid character. // But it is reasonable to apply the workaround to all platforms, // due to the special meaning of "*" in shell patterns. const int extensions_quirk = path.indexOf(QLatin1String(" *.")); if (extensions_quirk >= 0) { path.truncate(extensions_quirk); } if (path.isEmpty()) return false; const FileFormat *format = FileFormats.findFormatByFilter(filter); if (!format) { QMessageBox::information(this, tr("Error"), tr("File could not be saved:") + QLatin1Char('\n') + tr("There was a problem in determining the file format.") + QLatin1Char('\n') + QLatin1Char('\n') + tr("Please report this as a bug.") ); return false; } // Ensure that the provided filename has a correct file extension. // Among other things, this will ensure that FileFormats.formatForFilename() // returns the same thing the user selected in the dialog. // QString selected_extension = "." + format->primaryExtension(); QStringList selected_extensions(format->fileExtensions()); selected_extensions.replaceInStrings(QRegExp(QString::fromLatin1("^")), QString::fromLatin1(".")); bool has_extension = std::any_of(selected_extensions.constBegin(), selected_extensions.constEnd(), [&path](const auto& selected_extension) { return path.endsWith(selected_extension, Qt::CaseInsensitive); }); if (!has_extension) path += QLatin1Char('.') + format->primaryExtension(); // Ensure that the file name matches the format. Q_ASSERT(format->fileExtensions().contains(QFileInfo(path).suffix())); // Fails when using different formats for import and export: // Q_ASSERT(FileFormats.findFormatForFilename(path) == format); return savePath(path); } void MainWindow::toggleFullscreenMode() { if (isFullScreen()) { showNormal(); if (maximized_before_fullscreen) showMaximized(); } else { maximized_before_fullscreen = isMaximized(); showFullScreen(); } } void MainWindow::showSettings() { SettingsDialog dialog(this); dialog.exec(); } void MainWindow::showAbout() { AboutDialog about_dialog(this); about_dialog.exec(); } void MainWindow::showHelp() { Util::showHelp(this); } void MainWindow::linkClicked(const QString &link) { if (link.compare(QLatin1String("settings:"), Qt::CaseInsensitive) == 0) showSettings(); else if (link.compare(QLatin1String("help:"), Qt::CaseInsensitive) == 0) showHelp(); else if (link.compare(QLatin1String("about:"), Qt::CaseInsensitive) == 0) showAbout(); else if (link.startsWith(QLatin1String("examples:"), Qt::CaseInsensitive)) openPathLater(QLatin1String("data:/examples/") + link.midRef(9)); else QDesktopServices::openUrl(link); } bool MainWindow::eventFilter(QObject *object, QEvent *event) { Q_UNUSED(object) switch(event->type()) { case QEvent::WhatsThisClicked: { QWhatsThisClickedEvent* e = static_cast(event); Util::showHelp(this, e->href()); }; break; #if defined(Q_OS_ANDROID) case QEvent::KeyRelease: if (static_cast(event)->key() == Qt::Key_Back && hasOpenedFile()) { /* Don't let Qt close the application in * QGuiApplicationPrivate::processKeyEvent() while a file is opened. * * This must be the application-wide event filter in order to * catch Qt::Key_Back from popup menus (such as template list, * overflow actions). * * Any widgets that want to handle Qt::Key_Back need to watch * for QEvent::KeyPress. */ event->accept(); return true; } break; #endif default: ; // nothing } return false; } } // namespace OpenOrienteering mapper-0.8.1.1/src/gui/main_window.h000066400000000000000000000356151325266516600172300ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2013-2016 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_MAIN_WINDOW_H #define OPENORIENTEERING_MAIN_WINDOW_H #include #include #include #include #include #include "core/autosave.h" #include "fileformats/file_format.h" class QAction; class QCloseEvent; class QEvent; class QKeyEvent; class QLabel; class QMenu; class QStackedWidget; class QToolBar; class QWidget; namespace OpenOrienteering { class MainWindowController; /** * The MainWindow class provides the generic application window. * * It always has an active controller (class MainWindowController) * which provides the specific window content and behaviours. * The controller can be exchanged while the window is visible. */ class MainWindow : public QMainWindow, private Autosave { Q_OBJECT public: /** * Creates a new main window. */ explicit MainWindow(QWidget* parent = nullptr, Qt::WindowFlags flags = Qt::WindowFlags()); private: /** * Creates a new main window. * * The flag as_main_window is a contradiction to the general intent of this * class. The value fals is used only once, in SymbolSettingDialog. For this * case, it disables some features such as the main menu. * * \todo Refactor to remove the flag as_main_window. */ explicit MainWindow(bool as_main_window, QWidget* parent = nullptr, Qt::WindowFlags flags = Qt::WindowFlags()); friend class SymbolSettingDialog; public: /** Destroys a main window. */ ~MainWindow() override; /** Returns the application's localized name. */ QString appName() const; /** * Returns whether the window is operating in mobile mode. * * On the desktop, the default (desktop) mode may be overwritten by * setting the environment variable MAPPER_MOBILE_GUI to 0 or 1. * * For Android, this evaluates to constexpr true so that the compiler * may optimize away desktop code in conditional blocks. */ #ifndef Q_OS_ANDROID static bool mobileMode(); #else static constexpr bool mobileMode() { return true; } #endif /** * Changes the controller. * * The new controller does not edit a file. */ void setController(MainWindowController* new_controller); /** * Changes the controller. * * The new controller edits the file with the given path. * The path may be empty for a new (unnamed) file. */ void setController(MainWindowController* new_controller, const QString& path); private: void setController(MainWindowController* new_controller, bool has_file); public: /** Returns the current controller. */ MainWindowController* getController() const; /** Returns the canonical path of the currently open file or * an empty string if no file is open. */ const QString& currentPath() const; /** Registers the given path as most recently used file. * * The path is added at (or moved to) the top of the list of most recently * used files, and the directory is saved as most recently used directory. */ static void setMostRecentlyUsedFile(const QString& path); /** Returns true if a file is opened in this main window. */ bool hasOpenedFile() const; /** Returns true if the opened file is marked as having unsaved changes. */ bool hasUnsavedChanges() const; /** Sets the text in the status bar. */ void setStatusBarText(const QString& text); /** Shows a temporary message in the status bar. */ void showStatusBarMessage(const QString& text, int timeout = 0); /** Clears temporary messages set in the status bar with showStatusBarMessage(). */ void clearStatusBarMessage(); /** * Blocks shortcuts. * * During text input, it may be neccessary to disable shortcuts. * * @param blocked true for blocking shortcuts, false for normal behaviour. */ void setShortcutsBlocked(bool blocked); /** Returns true if shortcuts are currently disabled. */ bool shortcutsBlocked() const; /** Returns the main window's file menu so that it can be extended. */ QMenu* getFileMenu() const; /** Returns an QAction which serves as extension point in the file menu. */ QAction* getFileMenuExtensionAct() const; /** Returns the save action. */ QAction* getSaveAct() const; /** Returns the close action. */ QAction* getCloseAct() const; /** * Returns a general toolbar with standard file actions (new, open, save). * * The MainWindowController is responsible to add it to the main window. * It will be destroyed (and recreated) when the controller changes. */ QToolBar* getGeneralToolBar() const; /** Open the file with the given path after all events have been processed. * May open a new main window. * If loading is successful, the selected path will become * the [new] window's current path. */ void openPathLater(const QString &path); /** Save the content of the main window. * @param path the path where to save. */ bool savePath(const QString &path); /** Shows the open file dialog for the given file type(s) and returns the chosen file * or an empty string if the dialog is aborted. */ static QString getOpenFileName(QWidget* parent, const QString& title, FileFormat::FileTypes types); /** * Sets the MainWindow's effective central widget. * * Any previously set widget will be hidden and scheduled for deletion. * * Hides an implementation in QMainWindow which causes problems with * dock widgets when switching from home screen widget to map widget. * NEVER call QMainWindow::setCentralWidget(...) on a MainWindow. */ void setCentralWidget(QWidget* widget); /** * Indicates whether the home screen is disabled. * * Normally the last main window will return to the home screen when a file * is closed. When the home screen is disabled, the last window will be * closed instead. */ bool homeScreenDisabled() const; /** * Sets whether to show the home screen after closing the last file. * * @see homeScreenDisabled() */ void setHomeScreenDisabled(bool disabled); public slots: /** * Reacts to application state changes. * * On Android, when the application state becomes Qt::ApplicationActive, * this method looks for the Android activity's current intent and triggers * the loading of a given file (if there is not already another file loaded). * * In general, when called for the first time after application start, it * opens the most recently used file, unless this feature is disabled in the * settings, and unless other files are registered for opening (i.e. files * given as command line parameters.) */ void applicationStateChanged(); /** * Show a wizard for creating new maps. * * May open a new main window. */ void showNewMapWizard(); /** * Show a file-open dialog and load the select file. * * May open a new main window. * If loading is successful, the selected path will become * the [new] window's current path. */ void showOpenDialog(); /** * Show a file-save dialog. * * If saving is successful, the selected path will become * this window's current path. * * @return true if saving was succesful, false otherwise */ bool showSaveAsDialog(); /** * Open the file with the given path. * * May open a new main window. * If loading is successful, the selected path will become * the [new] window's current path. * * @return true if loading was succesful, false otherwise */ bool openPath(const QString &path); /** * Open the file specified in the sending action's data. * * This is intended for opening recent files. */ void openRecentFile(); /** * Notify the main window of a change to the list of recent files. */ void updateRecentFileActions(); /** * Save the current content to the current path. * * This will trigger a file-save dialog if the current path is not set (i.e. empty). */ bool save(); /** Save the current content to the current path. */ Autosave::AutosaveResult autosave() override; /** * Close the file currently opened. * * If there are changes to the current file, the user will be asked if he * wants to save it - the user may even cancel the closing of the file. * * This will close the window unless this is the last window. * * @return True if the file was actually closed, false otherwise. */ bool closeFile(); /** Toggle between normal window and fullscreen mode. */ void toggleFullscreenMode(); /** Show the settings dialog. */ void showSettings(); /** Show the about dialog. */ void showAbout(); /** Show the index page of the manual in the help browser. */ void showHelp(); /** Open a link. * This is called when the user clicks on a link in the UI, * e.g. in the tip of the day. * * @param link the target URI */ void linkClicked(const QString &link); /** * Notifies this window of unsaved changes. * * If the controller was set as having an opened file, setting this value to * true will start the autosave countdown if the previous value was false. * * This will update the window title via QWidget::setWindowModified(). */ void setHasUnsavedChanges(bool value); signals: /** * This signal is emitted when the actual path changes. * * @see switchActualPath() */ void actualPathChanged(const QString &path); /** * This signal is emitted when an autosave conflict gets resolved. * * @see setHasAutosaveConflict() */ void autosaveConflictResolved(); protected slots: /** * Switches to a different controller and loads the given path. * * This method is meant for switching between an original file and * autosaved versions. It does not touch current_path. The class of the new * controller is determined from the current_path (i.e. original file). * * If the given path is the current actual_path, no change is made. * * If the currently loaded file was modified, the user is asked whether he * really wants to switch to another file which means loosing the changes * he had made. */ void switchActualPath(const QString &path); /** * Open the files which have been registered by openPathLater(). */ void openPathBacklog(); /** * Listens to configuration changes. */ void settingsChanged(); protected: /** * Sets the path of the file edited by this windows' controller. * * This will update the window title via QWidget::setWindowFilePath(). * * If the controller was not set as having an opened file, * the path must be empty. */ void setCurrentPath(const QString& path); /** * Notifies the windows of autosave conflicts. * * An autosave conflict is the situation where a autosaved file exists * when the original file is opened. This autosaved file indicates that * the original file was not properly closed, i.e. the software crashed * before closing. */ void setHasAutosaveConflict(bool value); /** * Removes the autosave file if it exists. * * Returns true if the file was removed or didn't exist, false otherwise. */ bool removeAutosaveFile() const; bool event(QEvent* event) override; void closeEvent(QCloseEvent *event) override; void keyPressEvent(QKeyEvent* event) override; void keyReleaseEvent(QKeyEvent* event) override; bool eventFilter(QObject* object, QEvent* event) override; private: static constexpr int max_recent_files = 10; /** * Conditionally shows a dialog for saving pending changes. * * If this main window has an opened file with unsaved changes, shows * a dialog which lets the user save the file, discard the changes or * cancel. * * Returns true if the window can be closed, false otherwise. */ bool showSaveOnCloseDialog(); /** Saves the window position and state. */ void saveWindowSettings(); /** Loads the window position and state. */ void loadWindowSettings(); void createFileMenu(); void createHelpMenu(); static MainWindow* findMainWindow(const QString& file_name); /// The active controller MainWindowController* controller; const bool create_menu; bool show_menu; bool shortcuts_blocked; QToolBar* general_toolbar; QMenu* file_menu; QAction* save_act; QMenu* open_recent_menu; bool open_recent_menu_inserted; QAction* recent_file_act[max_recent_files]; QAction* settings_act; QAction* close_act; QLabel* status_label; /// Canonical path to the currently open file or an empty string if the file was not saved yet ("untitled") QString current_path; /// The actual path loaded by the editor. @see switchActualPath() QString actual_path; /// Does the main window display a file? If yes, new controllers will be opened in new main windows instead of replacing the active controller of this one bool has_opened_file; /// If this window has an opened file: does this file have unsaved changes? bool has_unsaved_changes; /// Indicates the presence of an autosave conflict. @see setHasAutosaveConflict() bool has_autosave_conflict; /// Was the window maximized before going into fullscreen mode? In this case, we have to show it maximized again when leaving fullscreen mode. bool maximized_before_fullscreen; bool homescreen_disabled; /// Number of active main windows. The last window shall not close on File > Close. static int num_open_files; /// The central widget which never changes during a MainWindow's lifecycle QStackedWidget* central_widget; /// A list of paths to be opened later QStringList path_backlog; }; // ### MainWindow inline code ### inline MainWindowController* MainWindow::getController() const { return controller; } inline const QString& MainWindow::currentPath() const { return current_path; } inline bool MainWindow::hasOpenedFile() const { return has_opened_file; } inline bool MainWindow::hasUnsavedChanges() const { return has_unsaved_changes; } inline bool MainWindow::shortcutsBlocked() const { return shortcuts_blocked; } inline QMenu* MainWindow::getFileMenu() const { return file_menu; } inline QAction* MainWindow::getFileMenuExtensionAct() const { return settings_act; } inline QAction* MainWindow::getSaveAct() const { return save_act; } inline QAction* MainWindow::getCloseAct() const { return close_act; } inline QToolBar* MainWindow::getGeneralToolBar() const { return general_toolbar; } inline bool MainWindow::homeScreenDisabled() const { return homescreen_disabled; } } // namespace OpenOrienteering #endif mapper-0.8.1.1/src/gui/main_window_controller.cpp000066400000000000000000000041251325266516600220160ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2014 Thomas Schöps, Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "main_window_controller.h" #include #include "fileformats/file_format.h" #include "fileformats/file_format_registry.h" #include "gui/map/map_editor.h" namespace OpenOrienteering { MainWindowController::~MainWindowController() = default; bool MainWindowController::save(const QString& path) { Q_UNUSED(path); return false; } bool MainWindowController::exportTo(const QString& path, const FileFormat* format) { Q_UNUSED(path); Q_UNUSED(format); return false; } bool MainWindowController::load(const QString& path, QWidget* dialog_parent) { Q_UNUSED(path); Q_UNUSED(dialog_parent); return false; } void MainWindowController::detach() { // nothing } bool MainWindowController::isEditingInProgress() const { return false; } bool MainWindowController::keyPressEventFilter(QKeyEvent* event) { Q_UNUSED(event); return false; } bool MainWindowController::keyReleaseEventFilter(QKeyEvent* event) { Q_UNUSED(event); return false; } MainWindowController* MainWindowController::controllerForFile(const QString& filename) { const FileFormat* format = FileFormats.findFormatForFilename(filename); if (format && format->supportsImport()) return new MapEditorController(MapEditorController::MapEditor); return nullptr; } } // namespace OpenOrienteering mapper-0.8.1.1/src/gui/main_window_controller.h000066400000000000000000000101221325266516600214550ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2014 Thomas Schöps, Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_MAIN_WINDOW_CONTROLLER_H #define OPENORIENTEERING_MAIN_WINDOW_CONTROLLER_H #include #include class QKeyEvent; class QWidget; namespace OpenOrienteering { class MainWindow; class FileFormat; /** A MainWindowController provides the specific content and * behaviours for a main window, for example map drawing or * course setting functions. */ class MainWindowController : public QObject { Q_OBJECT public: ~MainWindowController() override; /** Save to a file. * @param path the path to save to * @return true if saving was sucessful, false on errors */ virtual bool save(const QString& path); /** Export to a file, but don't change modified state * with regard to the original file. * @param path the path to export to * @param format the file format (automatically determined if nullptr) * @return true if saving was sucessful, false on errors */ virtual bool exportTo(const QString& path, const FileFormat* format = nullptr); /** Load from a file. * @param path the path to load from * @param dialog_parent Alternative parent widget for all dialogs. * If nullptr, implementations should use MainWindowController::window. * @return true if loading was sucessful, false on errors */ virtual bool load(const QString& path, QWidget* dialog_parent = nullptr); /** Attach the controller to a main window. * The controller should create its user interface here. */ virtual void attach(MainWindow* window) = 0; /** Detach the controller from a main window. * The controller should delete its user interface here. */ virtual void detach(); /** * Returns true when editing is in progress. * * "Editing in progress" means the file is an "unstable" state where no * global operations like save, undo, redo shall not be applied. */ virtual bool isEditingInProgress() const; /** * @brief Receives key press events from the main window. * * QKeyEvent starts with isAccepted() == true, so the return value of this * function decides if the event shall be stopped from being handled further. * * The default implementation simply returns false. * * @return True if the event shall be stopped from being handled further, false otherwise. */ virtual bool keyPressEventFilter(QKeyEvent* event); /** * @brief Receives key release events from the main window. * * QKeyEvent starts with isAccepted() == true, so the return value of this * function decides if the event shall be stopped from being handled further. * * The default implementation simply returns false. * * @return True if the event shall be stopped from being handled further, false otherwise. */ virtual bool keyReleaseEventFilter(QKeyEvent* event); /** Get the main window this controller is attached to. */ inline MainWindow* getWindow() const; /** Get a controller suitable for a particular file. * @param filename the name of the file * @return a MainWindowController that is able to load the file */ static MainWindowController* controllerForFile(const QString& filename); protected: MainWindow* window; }; //### MainWindowController inline code ### inline MainWindow* MainWindowController::getWindow() const { return window; } } // namespace OpenOrienteering #endif mapper-0.8.1.1/src/gui/map/000077500000000000000000000000001325266516600153075ustar00rootroot00000000000000mapper-0.8.1.1/src/gui/map/map_dialog_rotate.cpp000066400000000000000000000136611325266516600214740ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "map_dialog_rotate.h" #include #include #include #include #include #include #include #include #include #include #include #include "core/georeferencing.h" #include "core/map.h" #include "core/map_coord.h" #include "templates/template.h" #include "gui/util_gui.h" namespace OpenOrienteering { RotateMapDialog::RotateMapDialog(QWidget* parent, Map* map) : QDialog(parent, Qt::WindowSystemMenuHint | Qt::WindowTitleHint), map(map) { setWindowTitle(tr("Rotate map")); QFormLayout* layout = new QFormLayout(); layout->addRow(Util::Headline::create(tr("Rotation parameters"))); rotation_edit = Util::SpinBox::create(Georeferencing::declinationPrecision(), -180.0, +180.0, trUtf8("°")); rotation_edit->setWrapping(true); layout->addRow(tr("Angle (counter-clockwise):"), rotation_edit); layout->addRow(new QLabel(tr("Rotate around:"))); center_origin_radio = new QRadioButton(tr("Map coordinate system origin", "Rotation center point")); center_origin_radio->setChecked(true); layout->addRow(center_origin_radio); center_georef_radio = new QRadioButton(tr("Georeferencing reference point", "Rotation center point")); if (!map->getGeoreferencing().isValid()) center_georef_radio->setEnabled(false); layout->addRow(center_georef_radio); center_other_radio = new QRadioButton(tr("Other point,", "Rotation center point")); other_x_edit = Util::SpinBox::create(tr("mm")); other_y_edit = Util::SpinBox::create(tr("mm")); auto other_center_layout = new QHBoxLayout(); other_center_layout->addWidget(center_other_radio); other_center_layout->addWidget(new QLabel(tr("X:", "x coordinate")), 0); other_center_layout->addWidget(other_x_edit, 1); other_center_layout->addWidget(new QLabel(tr("Y:", "y coordinate")), 0); other_center_layout->addWidget(other_y_edit, 1); layout->addRow(other_center_layout); layout->addItem(Util::SpacerItem::create(this)); layout->addRow(Util::Headline::create(tr("Options"))); adjust_georeferencing_check = new QCheckBox(tr("Adjust georeferencing reference point")); if (map->getGeoreferencing().isValid()) adjust_georeferencing_check->setChecked(true); else adjust_georeferencing_check->setEnabled(false); layout->addRow(adjust_georeferencing_check); adjust_declination_check = new QCheckBox(tr("Adjust georeferencing declination")); if (map->getGeoreferencing().isValid()) adjust_declination_check->setChecked(true); else adjust_declination_check->setEnabled(false); layout->addRow(adjust_declination_check); adjust_templates_check = new QCheckBox(tr("Rotate non-georeferenced templates")); bool have_non_georeferenced_template = false; for (int i = 0; i < map->getNumTemplates() && !have_non_georeferenced_template; ++i) have_non_georeferenced_template = !map->getTemplate(i)->isTemplateGeoreferenced(); for (int i = 0; i < map->getNumClosedTemplates() && !have_non_georeferenced_template; ++i) have_non_georeferenced_template = !map->getClosedTemplate(i)->isTemplateGeoreferenced(); if (have_non_georeferenced_template) adjust_templates_check->setChecked(true); else adjust_templates_check->setEnabled(false); layout->addRow(adjust_templates_check); layout->addItem(Util::SpacerItem::create(this)); QDialogButtonBox* button_box = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal); layout->addRow(button_box); setLayout(layout); connect(center_origin_radio, &QAbstractButton::clicked, this, &RotateMapDialog::updateWidgets); connect(center_georef_radio, &QAbstractButton::clicked, this, &RotateMapDialog::updateWidgets); connect(center_other_radio, &QAbstractButton::clicked, this, &RotateMapDialog::updateWidgets); connect(button_box, &QDialogButtonBox::accepted, this, &RotateMapDialog::okClicked); connect(button_box, &QDialogButtonBox::rejected, this, &QDialog::reject); updateWidgets(); } void RotateMapDialog::setRotationDegrees(float rotation) { rotation_edit->setValue(rotation); } void RotateMapDialog::setRotateAroundGeorefRefPoint() { if (center_georef_radio->isEnabled()) { center_georef_radio->setChecked(true); updateWidgets(); } } void RotateMapDialog::setAdjustDeclination(bool adjust) { adjust_declination_check->setChecked(adjust); } void RotateMapDialog::showAdjustDeclination(bool show) { adjust_declination_check->setVisible(show); } void RotateMapDialog::updateWidgets() { other_x_edit->setEnabled(center_other_radio->isChecked()); other_y_edit->setEnabled(center_other_radio->isChecked()); adjust_georeferencing_check->setEnabled(!center_georef_radio->isChecked()); } void RotateMapDialog::okClicked() { double rotation = M_PI * rotation_edit->value() / 180; MapCoord center = MapCoord(0, 0); if (center_georef_radio->isChecked()) center = map->getGeoreferencing().getMapRefPoint(); else if (center_other_radio->isChecked()) center = MapCoord(other_x_edit->value(), -1 * other_y_edit->value()); map->rotateMap(rotation, center, adjust_georeferencing_check->isChecked(), adjust_declination_check->isChecked(), adjust_templates_check->isChecked()); accept(); } } // namespace OpenOrienteering mapper-0.8.1.1/src/gui/map/map_dialog_rotate.h000066400000000000000000000041731325266516600211370ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_MAP_DIALOG_ROTATE_H #define OPENORIENTEERING_MAP_DIALOG_ROTATE_H #include #include class QDoubleSpinBox; class QCheckBox; class QRadioButton; class QWidget; namespace OpenOrienteering { class Map; /** * Dialog for rotating the whole map around a point. */ class RotateMapDialog : public QDialog { Q_OBJECT public: /** Creates a new RotateMapDialog. */ RotateMapDialog(QWidget* parent, Map* map); /** Sets the rotation angle in degrees in the corresponding widget. */ void setRotationDegrees(float rotation); /** Enables the setting to rotate around the georeferencing reference point. */ void setRotateAroundGeorefRefPoint(); /** Checks or unchecks the setting to adjust the georeferencing declination. */ void setAdjustDeclination(bool adjust); /** Sets the visibility of the setting to adjust the georeferencing declination. */ void showAdjustDeclination(bool show); private slots: void updateWidgets(); void okClicked(); private: QDoubleSpinBox* rotation_edit; QRadioButton* center_origin_radio; QRadioButton* center_georef_radio; QRadioButton* center_other_radio; QDoubleSpinBox* other_x_edit; QDoubleSpinBox* other_y_edit; QCheckBox* adjust_georeferencing_check; QCheckBox* adjust_declination_check; QCheckBox* adjust_templates_check; Map* map; }; } // namespace OpenOrienteering #endif mapper-0.8.1.1/src/gui/map/map_dialog_scale.cpp000066400000000000000000000131501325266516600212560ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "map_dialog_scale.h" #include #include #include #include #include #include #include #include "core/georeferencing.h" #include "core/map.h" #include "templates/template.h" #include "gui/util_gui.h" namespace OpenOrienteering { ScaleMapDialog::ScaleMapDialog(QWidget* parent, Map* map) : QDialog(parent, Qt::WindowSystemMenuHint | Qt::WindowTitleHint), map(map) { setWindowTitle(tr("Change map scale")); QFormLayout* layout = new QFormLayout(); layout->addRow(Util::Headline::create(tr("Scaling parameters"))); scale_edit = new QLineEdit(QString::number(map->getScaleDenominator())); scale_edit->setValidator(new QIntValidator(1, 9999999, scale_edit)); layout->addRow(tr("New scale: 1 :"), scale_edit); layout->addRow(new QLabel(tr("Scaling center:"))); center_origin_radio = new QRadioButton(tr("Map coordinate system origin", "Scaling center point")); center_origin_radio->setChecked(true); layout->addRow(center_origin_radio); center_georef_radio = new QRadioButton(tr("Georeferencing reference point", "Scaling center point")); if (!map->getGeoreferencing().isValid()) center_georef_radio->setEnabled(false); layout->addRow(center_georef_radio); center_other_radio = new QRadioButton(tr("Other point,", "Scaling center point")); other_x_edit = Util::SpinBox::create(tr("mm")); other_y_edit = Util::SpinBox::create(tr("mm")); QHBoxLayout* other_center_layout = new QHBoxLayout(); other_center_layout->addWidget(center_other_radio); other_center_layout->addWidget(new QLabel(tr("X:", "x coordinate")), 0); other_center_layout->addWidget(other_x_edit, 1); other_center_layout->addWidget(new QLabel(tr("Y:", "y coordinate")), 0); other_center_layout->addWidget(other_y_edit, 1); layout->addRow(other_center_layout); layout->addItem(Util::SpacerItem::create(this)); layout->addRow(Util::Headline::create(tr("Options"))); adjust_symbols_check = new QCheckBox(tr("Scale symbol sizes")); if (map->getNumSymbols() > 0) adjust_symbols_check->setChecked(true); else adjust_symbols_check->setEnabled(false); layout->addRow(adjust_symbols_check); adjust_objects_check = new QCheckBox(tr("Scale map object positions")); if (map->getNumObjects() > 0) adjust_objects_check->setChecked(true); else adjust_objects_check->setEnabled(false); layout->addRow(adjust_objects_check); adjust_georeferencing_check = new QCheckBox(tr("Adjust georeferencing reference point")); if (map->getGeoreferencing().isValid()) adjust_georeferencing_check->setChecked(true); else adjust_georeferencing_check->setEnabled(false); layout->addRow(adjust_georeferencing_check); adjust_templates_check = new QCheckBox(tr("Scale non-georeferenced templates")); bool have_non_georeferenced_template = false; for (int i = 0; i < map->getNumTemplates() && !have_non_georeferenced_template; ++i) have_non_georeferenced_template = !map->getTemplate(i)->isTemplateGeoreferenced(); for (int i = 0; i < map->getNumClosedTemplates() && !have_non_georeferenced_template; ++i) have_non_georeferenced_template = !map->getClosedTemplate(i)->isTemplateGeoreferenced(); if (have_non_georeferenced_template) adjust_templates_check->setChecked(true); else adjust_templates_check->setEnabled(false); layout->addRow(adjust_templates_check); layout->addItem(Util::SpacerItem::create(this)); QDialogButtonBox* button_box = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal); ok_button = button_box->button(QDialogButtonBox::Ok); layout->addRow(button_box); setLayout(layout); connect(center_origin_radio, &QAbstractButton::clicked, this, &ScaleMapDialog::updateWidgets); connect(center_georef_radio, &QAbstractButton::clicked, this, &ScaleMapDialog::updateWidgets); connect(center_other_radio, &QAbstractButton::clicked, this, &ScaleMapDialog::updateWidgets); connect(button_box, &QDialogButtonBox::accepted, this, &ScaleMapDialog::okClicked); connect(button_box, &QDialogButtonBox::rejected, this, &QDialog::reject); updateWidgets(); } void ScaleMapDialog::updateWidgets() { other_x_edit->setEnabled(center_other_radio->isChecked()); other_y_edit->setEnabled(center_other_radio->isChecked()); adjust_georeferencing_check->setEnabled(!center_georef_radio->isChecked()); } void ScaleMapDialog::okClicked() { int scale = scale_edit->text().toInt(); MapCoord center = MapCoord(0, 0); if (center_georef_radio->isChecked()) center = map->getGeoreferencing().getMapRefPoint(); else if (center_other_radio->isChecked()) center = MapCoord(other_x_edit->value(), -1 * other_y_edit->value()); map->changeScale(scale, center, adjust_symbols_check->isChecked(), adjust_objects_check->isChecked(), adjust_georeferencing_check->isChecked(), adjust_templates_check->isChecked()); accept(); } } // namespace OpenOrienteering mapper-0.8.1.1/src/gui/map/map_dialog_scale.h000066400000000000000000000033261325266516600207270ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_MAP_DIALOG_SCALE_H #define OPENORIENTEERING_MAP_DIALOG_SCALE_H #include #include class QLineEdit; class QCheckBox; class QPushButton; class QRadioButton; class QDoubleSpinBox; class QWidget; namespace OpenOrienteering { class Map; /** * Dialog for scaling the whole map. */ class ScaleMapDialog : public QDialog { Q_OBJECT public: /** Creates a new ScaleMapDialog. */ ScaleMapDialog(QWidget* parent, Map* map); private slots: void updateWidgets(); void okClicked(); private: QLineEdit* scale_edit; QRadioButton* center_origin_radio; QRadioButton* center_georef_radio; QRadioButton* center_other_radio; QDoubleSpinBox* other_x_edit; QDoubleSpinBox* other_y_edit; QCheckBox* adjust_symbols_check; QCheckBox* adjust_objects_check; QCheckBox* adjust_georeferencing_check; QCheckBox* adjust_templates_check; QPushButton* ok_button; Map* map; }; } // namespace OpenOrienteering #endif mapper-0.8.1.1/src/gui/map/map_editor.cpp000066400000000000000000004370441325266516600201520ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2012-2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "map_editor.h" #include "map_editor_p.h" #include #include #include #include #include #include #include // IWYU pragma: no_include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // IWYU pragma: keep #include #include #include // IWYU pragma: keep #include #include #include #include #include #include #include #include #include // IWYU pragma: no_include #include #include #include #include #include #include #include #include #include #include #include #include // IWYU pragma: keep #include #include #include #include #include #include #include #include #include #include #ifdef Q_OS_ANDROID #include #endif #include "settings.h" #include "core/georeferencing.h" #include "core/map.h" #include "core/map_coord.h" #include "core/map_part.h" #include "core/map_view.h" #include "core/objects/boolean_tool.h" #include "core/objects/object.h" #include "core/objects/object_operations.h" #include "core/symbols/point_symbol.h" #include "core/symbols/area_symbol.h" #include "core/symbols/symbol.h" #include "core/symbols/symbol_icon_decorator.h" #include "fileformats/file_format.h" #include "fileformats/file_format_registry.h" #include "gui/configure_grid_dialog.h" #include "gui/file_dialog.h" #include "gui/georeferencing_dialog.h" #include "gui/main_window.h" #include "gui/print_widget.h" #include "gui/text_browser_dialog.h" #include "gui/util_gui.h" #include "gui/map/map_dialog_rotate.h" #include "gui/map/map_dialog_scale.h" #include "gui/map/map_editor_activity.h" #include "gui/map/map_find_feature.h" #include "gui/map/map_widget.h" #include "gui/symbols/replace_symbol_set_dialog.h" #include "gui/widgets/action_grid_bar.h" #include "gui/widgets/color_list_widget.h" #include "gui/widgets/compass_display.h" #include "gui/widgets/key_button_bar.h" // IWYU pragma: keep #include "gui/widgets/measure_widget.h" #include "gui/widgets/symbol_widget.h" #include "gui/widgets/tags_widget.h" #include "gui/widgets/template_list_widget.h" #include "sensors/compass.h" #include "sensors/gps_display.h" #include "sensors/gps_temporary_markers.h" #include "sensors/gps_track_recorder.h" #include "templates/template.h" #include "templates/template_dialog_reopen.h" #include "templates/template_position_dock_widget.h" #include "templates/template_tool_paint.h" #include "templates/template_track.h" #include "tools/cut_tool.h" #include "tools/cut_hole_tool.h" #include "tools/cutout_tool.h" #include "tools/distribute_points_tool.h" #include "tools/draw_circle_tool.h" #include "tools/draw_freehand_tool.h" #include "tools/draw_path_tool.h" #include "tools/draw_point_tool.h" #include "tools/draw_point_gps_tool.h" #include "tools/draw_rectangle_tool.h" #include "tools/draw_text_tool.h" #include "tools/edit_point_tool.h" #include "tools/edit_line_tool.h" #include "tools/fill_tool.h" #include "tools/pan_tool.h" #include "tools/rotate_pattern_tool.h" #include "tools/rotate_tool.h" #include "tools/scale_tool.h" #include "tools/tool.h" #include "undo/map_part_undo.h" #include "undo/object_undo.h" #include "undo/undo.h" #include "undo/undo_manager.h" #include "util/backports.h" // IWYU pragma: keep namespace OpenOrienteering { namespace { /** * Creates a partial, resizable widget overlay over the main window. * * This widget is meant to be used as a dock widget replacement in the * mobile app. In landscape mode, the child widget is placed on the right * side, spanning the full height. In portrait mode, the child is placed * on the top, spanning the full width. */ QSplitter* createDockWidgetSubstitute(MainWindow* window, QWidget* child) { auto splitter = new QSplitter(window); splitter->setChildrenCollapsible(false); auto placeholder = new QWidget(); splitter->setAttribute(Qt::WA_NoSystemBackground, true); placeholder->setAttribute(Qt::WA_NoSystemBackground, true); child->setAutoFillBackground(true); auto geometry = window->geometry(); splitter->setGeometry(geometry); if (geometry.height() > geometry.width()) { splitter->setOrientation(Qt::Vertical); splitter->addWidget(child); splitter->addWidget(placeholder); } else { splitter->setOrientation(Qt::Horizontal); splitter->addWidget(placeholder); splitter->addWidget(child); } return splitter; } template bool containsPathObject(const T& container) { return std::any_of(begin(container), end(container), [](const auto object) { return object->getType() == Object::Path; }); } } // namespace namespace MimeType { /// The MIME type of Mapper data QString OpenOrienteeringObjects() { return QStringLiteral("openorienteering/objects"); } } // namespace MimeType // ### MapEditorController ### MapEditorController::MapEditorController(OperatingMode mode, Map* map, MapView* map_view) : MainWindowController() , mobile_mode(MainWindow::mobileMode()) , active_symbol(nullptr) , template_list_widget(nullptr) , mappart_remove_act(nullptr) , mappart_merge_act(nullptr) , mappart_merge_menu(nullptr) , mappart_move_menu(nullptr) , mappart_selector_box(nullptr) , mappart_merge_mapper(new QSignalMapper(this)) , mappart_move_mapper(new QSignalMapper(this)) { this->mode = mode; this->map = nullptr; main_view = nullptr; symbol_widget = nullptr; window = nullptr; editing_in_progress = false; cut_hole_menu = nullptr; if (map) setMapAndView(map, map_view ? map_view : new MapView(this, map)); editor_activity = nullptr; current_tool = nullptr; override_tool = nullptr; last_painted_on_template = nullptr; paste_act = nullptr; reopen_template_act = nullptr; overprinting_simulation_act = nullptr; toolbar_view = nullptr; toolbar_mapparts = nullptr; toolbar_drawing = nullptr; toolbar_editing = nullptr; toolbar_advanced_editing = nullptr; print_dock_widget = nullptr; measure_dock_widget = nullptr; symbol_dock_widget = nullptr; statusbar_zoom_frame = nullptr; statusbar_cursorpos_label = nullptr; gps_display = nullptr; gps_track_recorder = nullptr; compass_display = nullptr; gps_marker_display = nullptr; actionsById[""] = new QAction(this); // dummy action connect(mappart_merge_mapper, QOverload::of(&QSignalMapper::mapped), this, &MapEditorController::mergeCurrentMapPartTo); connect(mappart_move_mapper, QOverload::of(&QSignalMapper::mapped), this, &MapEditorController::reassignObjectsToMapPart); } MapEditorController::~MapEditorController() { paste_act = nullptr; delete current_tool; delete override_tool; delete editor_activity; delete toolbar_view; delete toolbar_drawing; delete toolbar_editing; delete toolbar_advanced_editing; delete toolbar_mapparts; delete print_dock_widget; delete measure_dock_widget; if (color_dock_widget) delete color_dock_widget; delete symbol_dock_widget; if (template_dock_widget) delete template_dock_widget; if (tags_dock_widget) delete tags_dock_widget; delete cut_hole_menu; delete mappart_merge_act; delete mappart_merge_menu; delete mappart_move_menu; for (TemplatePositionDockWidget* widget : qAsConst(template_position_widgets)) delete widget; delete gps_display; delete gps_track_recorder; delete compass_display; delete map; } bool MapEditorController::isInMobileMode() const { return mobile_mode; } void MapEditorController::setTool(MapEditorTool* new_tool) { if (current_tool) { if (current_tool->editingInProgress()) { current_tool->finishEditing(); } current_tool->deleteLater(); } if (!override_tool) { map->clearDrawingBoundingBox(); window->setStatusBarText(QString{}); } current_tool = new_tool; if (current_tool && !override_tool) { current_tool->init(); } if (!override_tool) map_widget->setTool(current_tool); } void MapEditorController::setEditTool() { if (!current_tool || current_tool->toolType() != MapEditorTool::EditPoint) setTool(new EditPointTool(this, edit_tool_act)); } void MapEditorController::setOverrideTool(MapEditorTool* new_override_tool) { if (override_tool == new_override_tool) return; if (override_tool) { if (override_tool->editingInProgress()) { override_tool->finishEditing(); } delete override_tool; } map->clearDrawingBoundingBox(); window->setStatusBarText(QString{}); override_tool = new_override_tool; if (override_tool) { override_tool->init(); } else if (current_tool) { current_tool->init(); } map_widget->setTool(override_tool ? override_tool : current_tool); } MapEditorTool* MapEditorController::getDefaultDrawToolForSymbol(const Symbol* symbol) { if (!symbol) return new EditPointTool(this, edit_tool_act); else if (symbol->getType() == Symbol::Point) return new DrawPointTool(this, draw_point_act); else if (symbol->getType() == Symbol::Line || symbol->getType() == Symbol::Area || symbol->getType() == Symbol::Combined) return new DrawPathTool(this, draw_path_act, false, true); else if (symbol->getType() == Symbol::Text) return new DrawTextTool(this, draw_text_act); else Q_ASSERT(false); return nullptr; } void MapEditorController::setEditingInProgress(bool value) { if (value != editing_in_progress) { editing_in_progress = value; // Widgets map_widget->setGesturesEnabled(!editing_in_progress); Q_ASSERT(symbol_widget); symbol_widget->setEnabled(!editing_in_progress); if (isInMobileMode()) mobile_symbol_selector_action->setEnabled(!editing_in_progress); if (color_dock_widget) color_dock_widget->widget()->setEnabled(!editing_in_progress); if (mappart_selector_box) mappart_selector_box->setEnabled(!editing_in_progress); // Edit menu undo_act->setEnabled(!editing_in_progress && map->undoManager().canUndo()); redo_act->setEnabled(!editing_in_progress && map->undoManager().canRedo()); updatePasteAvailability(); select_all_act->setEnabled(!editing_in_progress); select_nothing_act->setEnabled(!editing_in_progress); invert_selection_act->setEnabled(!editing_in_progress); select_by_current_symbol_act->setEnabled(!editing_in_progress); find_feature->setEnabled(!editing_in_progress); // Map menu georeferencing_act->setEnabled(!editing_in_progress); scale_map_act->setEnabled(!editing_in_progress); rotate_map_act->setEnabled(!editing_in_progress); map_notes_act->setEnabled(!editing_in_progress); // Map menu, continued const int num_parts = map->getNumParts(); mappart_add_act->setEnabled(!editing_in_progress); mappart_rename_act->setEnabled(!editing_in_progress && num_parts > 0); mappart_remove_act->setEnabled(!editing_in_progress && num_parts > 1); mappart_move_menu->setEnabled(!editing_in_progress && num_parts > 1); mappart_merge_act->setEnabled(!editing_in_progress && num_parts > 1); mappart_merge_menu->setEnabled(!editing_in_progress && num_parts > 1); // Symbol menu symbol_set_id_act->setEnabled(!editing_in_progress); scale_all_symbols_act->setEnabled(!editing_in_progress); load_symbols_from_act->setEnabled(!editing_in_progress); load_crt_act->setEnabled(!editing_in_progress); updateObjectDependentActions(); updateSymbolDependentActions(); updateSymbolAndObjectDependentActions(); } } bool MapEditorController::isEditingInProgress() const { return editing_in_progress; } void MapEditorController::setEditorActivity(MapEditorActivity* new_activity) { delete editor_activity; map->clearActivityBoundingBox(); editor_activity = new_activity; if (editor_activity) editor_activity->init(); map_widget->setActivity(editor_activity); } void MapEditorController::addTemplatePositionDockWidget(Template* temp) { Q_ASSERT(!existsTemplatePositionDockWidget(temp)); auto dock_widget = new TemplatePositionDockWidget(temp, this, window); addFloatingDockWidget(dock_widget); template_position_widgets.insert(temp, dock_widget); } void MapEditorController::removeTemplatePositionDockWidget(Template* temp) { emit templatePositionDockWidgetClosed(temp); delete getTemplatePositionDockWidget(temp); int num_deleted = template_position_widgets.remove(temp); Q_ASSERT(num_deleted == 1); Q_UNUSED(num_deleted); } void MapEditorController::showPopupWidget(QWidget* child_widget, const QString& title) { if (mobile_mode) { // FIXME: This is used for KeyButtonBar only // and not related to mobile_mode! QSize size = child_widget->sizeHint(); QRect map_widget_rect = map_widget->rect(); child_widget->setParent(map_widget); child_widget->setGeometry( qMax(0, qRound(map_widget_rect.center().x() - 0.5f * size.width())), qMax(0, map_widget_rect.bottom() - size.height()), qMin(size.width(), map_widget_rect.width()), qMin(size.height(), map_widget_rect.height()) ); child_widget->show(); } else { auto dock_widget = new QDockWidget(title, window); dock_widget->setFeatures(dock_widget->features() & ~QDockWidget::DockWidgetClosable); dock_widget->setWidget(child_widget); // Show dock in floating state dock_widget->setFloating(true); dock_widget->show(); dock_widget->setGeometry(window->geometry().left() + 40, window->geometry().top() + 100, dock_widget->width(), dock_widget->height()); } } void MapEditorController::deletePopupWidget(QWidget* child_widget) { if (mobile_mode) { delete child_widget; } else { // Delete the dock widget delete child_widget->parentWidget(); } } bool MapEditorController::save(const QString& path) { if (map) { if (editing_in_progress) { QMessageBox::warning(window, tr("Editing in progress"), tr("The map is currently being edited. Please finish the edit operation before saving.")); return false; } bool success = map->saveTo(path, main_view); if (success) window->showStatusBarMessage(tr("Map saved"), 1000); return success; } else return false; } bool MapEditorController::exportTo(const QString& path, const FileFormat* format) { if (map && !editing_in_progress) { return map->exportTo(path, main_view, format); } return false; } bool MapEditorController::load(const QString& path, QWidget* dialog_parent) { if (!dialog_parent) dialog_parent = window; if (!map) { map = new Map(); main_view = new MapView(this, map); } bool success = map->loadFrom(path, dialog_parent, main_view); if (success) { setMapAndView(map, main_view); } else { delete map; map = nullptr; main_view = nullptr; } return success; } void MapEditorController::attach(MainWindow* window) { print_dock_widget = nullptr; measure_dock_widget = nullptr; color_dock_widget = nullptr; symbol_dock_widget = nullptr; std::function zoom_display_function; this->window = window; if (mode == MapEditor) { window->setHasUnsavedChanges(map->hasUnsavedChanges()); } connect(map, &Map::hasUnsavedChanged, window, &MainWindow::setHasUnsavedChanges); #ifdef Q_OS_ANDROID QAndroidJniObject::callStaticMethod("org/openorienteering/mapper/MapperActivity", "lockOrientation", "()V"); #endif if (mobile_mode) { window->setWindowState(window->windowState() | Qt::WindowFullScreen); zoom_display_function = [window](const QString& text) { window->showStatusBarMessage(text, 1000); }; } else { // Add zoom / cursor position field to status bar auto statusbar_zoom_icon = new QLabel(); auto fontmetrics = statusbar_zoom_icon->fontMetrics(); auto pixmap = QPixmap(QLatin1String(":/images/magnifying-glass.png")); auto scale = qreal(fontmetrics.height()) / pixmap.height(); if (scale < 0.9 || scale > 1.1) pixmap = pixmap.scaledToHeight(qRound(scale * pixmap.height()), Qt::SmoothTransformation); statusbar_zoom_icon->setPixmap(pixmap); auto statusbar_zoom_label = new QLabel(); statusbar_zoom_label->setMinimumWidth(fontmetrics.width(QLatin1String(" 0.333x"))); statusbar_zoom_label->setAlignment(Qt::AlignRight | Qt::AlignVCenter); statusbar_zoom_label->setFrameShape(QFrame::NoFrame); zoom_display_function = [statusbar_zoom_label](const QString& text) { statusbar_zoom_label->setText(text); }; statusbar_zoom_frame = new QFrame(); #ifdef Q_OS_WIN statusbar_zoom_frame->setFrameShape(QFrame::NoFrame); #else statusbar_zoom_frame->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken); #endif auto statusbar_zoom_frame_layout = new QHBoxLayout(); statusbar_zoom_frame_layout->setMargin(0); statusbar_zoom_frame_layout->setSpacing(0); statusbar_zoom_frame_layout->addSpacing(1); statusbar_zoom_frame_layout->addWidget(statusbar_zoom_icon); statusbar_zoom_frame_layout->addWidget(statusbar_zoom_label); statusbar_zoom_frame->setLayout(statusbar_zoom_frame_layout); statusbar_cursorpos_label = new QLabel(); #ifdef Q_OS_WIN statusbar_cursorpos_label->setFrameShape(QFrame::NoFrame); #else statusbar_cursorpos_label->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken); #endif statusbar_cursorpos_label->setMinimumWidth(fontmetrics.width(QLatin1String("-3,333.33 -333.33 (mm)"))); statusbar_cursorpos_label->setAlignment(Qt::AlignRight | Qt::AlignVCenter); window->statusBar()->addPermanentWidget(statusbar_zoom_frame); window->statusBar()->addPermanentWidget(statusbar_cursorpos_label); } // Create map widget map_widget = new MapWidget(mode == MapEditor, mode == SymbolEditor); map_widget->setMapView(main_view); map_widget->setZoomDisplay(zoom_display_function); // Create menu and toolbar together, so actions can be inserted into one or both if (mode == MapEditor) { gps_display = new GPSDisplay(map_widget, map->getGeoreferencing()); gps_marker_display = new GPSTemporaryMarkers(map_widget, gps_display); createActions(); if (mobile_mode) { createMobileGUI(); compass_display = new CompassDisplay(map_widget->parentWidget()); compass_display->setVisible(false); } else { map_widget->setCursorposLabel(statusbar_cursorpos_label); window->setCentralWidget(map_widget); createMenuAndToolbars(); restoreWindowState(); } } else if (mode == SymbolEditor) { map_widget->setCursorposLabel(statusbar_cursorpos_label); window->setCentralWidget(map_widget); } // Update enabled/disabled state for the tools ... updateWidgets(); templateAvailabilityChanged(); // ... and make sure it is kept up to date for copy/paste connect(QApplication::clipboard(), &QClipboard::changed, this, &MapEditorController::clipboardChanged); clipboardChanged(QClipboard::Clipboard); if (mode == MapEditor) { // Create/show the dock widgets if (mobile_mode) { createSymbolWidget(window); } else { symbol_window_act->trigger(); createColorWindow(); createTemplateWindow(); createTagEditor(); if (map->getNumColors() == 0) QTimer::singleShot(0, color_dock_widget, SLOT(show())); // clazy:exclude=old-style-connect } // Auto-select the edit tool edit_tool_act->setChecked(true); setEditTool(); // Set the coordinates display mode coordsDisplayChanged(); } else { // Set the coordinates display mode map_widget->setCoordsDisplay(MapWidget::MAP_COORDS); } } QAction* MapEditorController::newAction(const char* id, const QString &tr_text, QObject* receiver, const char* slot, const char* icon, const QString& tr_tip, const char* whats_this_link) { Q_ASSERT(window); // Qt documentation recommends that actions are created as children of the window they are used in. QAction* action = new QAction(icon ? QIcon(QLatin1String(":/images/") + QLatin1String(icon)) : QIcon(), tr_text, window); if (!tr_tip.isEmpty()) action->setStatusTip(tr_tip); if (whats_this_link) action->setWhatsThis(Util::makeWhatThis(whats_this_link)); if (receiver) QObject::connect(action, SIGNAL(triggered()), receiver, slot); actionsById[id] = action; return action; } QAction* MapEditorController::newCheckAction(const char* id, const QString &tr_text, QObject* receiver, const char* slot, const char* icon, const QString& tr_tip, const char* whats_this_link) { auto action = newAction(id, tr_text, nullptr, nullptr, icon, tr_tip, whats_this_link); action->setCheckable(true); if (receiver) QObject::connect(action, SIGNAL(triggered(bool)), receiver, slot); return action; } QAction* MapEditorController::newToolAction(const char* id, const QString &tr_text, QObject* receiver, const char* slot, const char* icon, const QString& tr_tip, const char* whats_this_link) { Q_ASSERT(window); // Qt documentation recommends that actions are created as children of the window they are used in. QAction* action = new MapEditorToolAction(icon ? QIcon(QLatin1String(":/images/") + QLatin1String(icon)) : QIcon(), tr_text, window); if (!tr_tip.isEmpty()) action->setStatusTip(tr_tip); if (whats_this_link) action->setWhatsThis(Util::makeWhatThis(whats_this_link)); if (receiver) QObject::connect(action, SIGNAL(activated()), receiver, slot); actionsById[id] = action; return action; } QAction* MapEditorController::findAction(const char* id) { if (!actionsById.contains(id)) return actionsById[""]; else return actionsById[id]; } QAction* MapEditorController::getAction(const char* id) { return actionsById.contains(id) ? actionsById[id] : nullptr; } void MapEditorController::assignKeyboardShortcuts() { // Standard keyboard shortcuts findAction("print")->setShortcut(QKeySequence::Print); findAction("undo")->setShortcut(QKeySequence::Undo); findAction("redo")->setShortcut(QKeySequence::Redo); findAction("cut")->setShortcut(QKeySequence::Cut); findAction("copy")->setShortcut(QKeySequence::Copy); findAction("paste")->setShortcut(QKeySequence::Paste); // Custom keyboard shortcuts // QKeySequence::Deselect is empty for Windows, so be explicit all select-* findAction("select-all")->setShortcut(QKeySequence(tr("Ctrl+A"))); findAction("select-nothing")->setShortcut(QKeySequence(tr("Ctrl+Shift+A"))); findAction("invert-selection")->setShortcut(QKeySequence(tr("Ctrl+I"))); findAction("showgrid")->setShortcut(QKeySequence(tr("G"))); findAction("zoomin")->setShortcuts(QList() << QKeySequence(Qt::Key_F7) << QKeySequence(Qt::Key_Plus) << QKeySequence(Qt::KeypadModifier + Qt::Key_Plus)); findAction("zoomout")->setShortcuts(QList() << QKeySequence(Qt::Key_F8) << QKeySequence(Qt::Key_Minus) << QKeySequence(Qt::KeypadModifier + Qt::Key_Minus)); findAction("hatchareasview")->setShortcut(QKeySequence(Qt::Key_F2)); findAction("baselineview")->setShortcut(QKeySequence(Qt::Key_F3)); findAction("hidealltemplates")->setShortcut(QKeySequence(Qt::Key_F10)); findAction("overprintsimulation")->setShortcut(QKeySequence(Qt::Key_F4)); findAction("fullscreen")->setShortcut(QKeySequence(Qt::Key_F11)); tags_window_act->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_6)); color_window_act->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_7)); symbol_window_act->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_8)); template_window_act->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_9)); findAction("editobjects")->setShortcut(QKeySequence(tr("E"))); findAction("editlines")->setShortcut(QKeySequence(tr("L"))); findAction("drawpoint")->setShortcut(QKeySequence(tr("S"))); findAction("drawpath")->setShortcut(QKeySequence(tr("P"))); findAction("drawcircle")->setShortcut(QKeySequence(tr("O"))); findAction("drawrectangle")->setShortcut(QKeySequence(tr("Ctrl+R"))); findAction("drawfill")->setShortcut(QKeySequence(tr("F"))); findAction("drawtext")->setShortcut(QKeySequence(tr("T"))); findAction("duplicate")->setShortcut(QKeySequence(tr("D"))); findAction("switchsymbol")->setShortcut(QKeySequence(tr("Ctrl+G"))); findAction("fillborder")->setShortcut(QKeySequence(tr("Ctrl+F"))); findAction("switchdashes")->setShortcut(QKeySequence(tr("Ctrl+D"))); findAction("connectpaths")->setShortcut(QKeySequence(tr("C"))); findAction("rotateobjects")->setShortcut(QKeySequence(tr("R"))); findAction("scaleobjects")->setShortcut(QKeySequence(tr("Z"))); findAction("cutobject")->setShortcut(QKeySequence(tr("K"))); findAction("cuthole")->setShortcut(QKeySequence(tr("H"))); findAction("measure")->setShortcut(QKeySequence(tr("M"))); findAction("booleanunion")->setShortcut(QKeySequence(tr("U"))); findAction("converttocurves")->setShortcut(QKeySequence(tr("N"))); findAction("simplify")->setShortcut(QKeySequence(tr("Ctrl+M"))); } void MapEditorController::createActions() { // Define all the actions, saving them into variables as necessary. Can also get them by ID. #ifdef QT_PRINTSUPPORT_LIB auto print_act_mapper = new QSignalMapper(this); connect(print_act_mapper, QOverload::of(&QSignalMapper::mapped), this, QOverload::of(&MapEditorController::printClicked)); print_act = newAction("print", tr("Print..."), print_act_mapper, SLOT(map()), "print.png", QString{}, "file_menu.html"); print_act_mapper->setMapping(print_act, PrintWidget::PRINT_TASK); export_image_act = newAction("export-image", tr("&Image"), print_act_mapper, SLOT(map()), nullptr, QString{}, "file_menu.html"); print_act_mapper->setMapping(export_image_act, PrintWidget::EXPORT_IMAGE_TASK); export_pdf_act = newAction("export-pdf", tr("&PDF"), print_act_mapper, SLOT(map()), nullptr, QString{}, "file_menu.html"); print_act_mapper->setMapping(export_pdf_act, PrintWidget::EXPORT_PDF_TASK); #else print_act = nullptr; export_image_act = nullptr; export_pdf_act = nullptr; #endif undo_act = newAction("undo", tr("Undo"), this, SLOT(undo()), "undo.png", tr("Undo the last step"), "edit_menu.html"); redo_act = newAction("redo", tr("Redo"), this, SLOT(redo()), "redo.png", tr("Redo the last step"), "edit_menu.html"); cut_act = newAction("cut", tr("Cu&t"), this, SLOT(cut()), "cut.png", QString{}, "edit_menu.html"); copy_act = newAction("copy", tr("C&opy"), this, SLOT(copy()), "copy.png", QString{}, "edit_menu.html"); paste_act = newAction("paste", tr("&Paste"), this, SLOT(paste()), "paste", QString{}, "edit_menu.html"); delete_act = newAction("delete", tr("Delete"), this, SLOT(deleteClicked()), "delete.png", QString{}, "toolbars.html#delete"); select_all_act = newAction("select-all", tr("Select all"), this, SLOT(selectAll()), nullptr, QString{}, "edit_menu.html"); select_nothing_act = newAction("select-nothing", tr("Select nothing"), this, SLOT(selectNothing()), nullptr, QString{}, "edit_menu.html"); invert_selection_act = newAction("invert-selection", tr("Invert selection"), this, SLOT(invertSelection()), nullptr, QString{}, "edit_menu.html"); select_by_current_symbol_act = newAction("select-by-symbol", QApplication::translate("OpenOrienteering::SymbolRenderWidget", "Select all objects with selected symbols"), this, SLOT(selectByCurrentSymbols()), nullptr, QString{}, "edit_menu.html"); find_feature.reset(new MapFindFeature(*this)); clear_undo_redo_history_act = newAction("clearundoredohistory", tr("Clear undo / redo history"), this, SLOT(clearUndoRedoHistory()), nullptr, tr("Clear the undo / redo history to reduce map file size."), "edit_menu.html"); show_grid_act = newCheckAction("showgrid", tr("Show grid"), this, SLOT(showGrid()), "grid.png", QString{}, "grid.html"); configure_grid_act = newAction("configuregrid", tr("Configure grid..."), this, SLOT(configureGrid()), "grid.png", QString{}, "grid.html"); pan_act = newToolAction("panmap", tr("Pan"), this, SLOT(pan()), "move.png", QString{}, "view_menu.html"); move_to_gps_pos_act = newAction("movegps", tr("Move to my location"), this, SLOT(moveToGpsPos()), "move-to-gps.png", QString{}, "view_menu.html"); move_to_gps_pos_act->setEnabled(false); zoom_in_act = newAction("zoomin", tr("Zoom in"), this, SLOT(zoomIn()), "view-zoom-in.png", QString{}, "view_menu.html"); zoom_out_act = newAction("zoomout", tr("Zoom out"), this, SLOT(zoomOut()), "view-zoom-out.png", QString{}, "view_menu.html"); show_all_act = newAction("showall", tr("Show whole map"), this, SLOT(showWholeMap()), "view-show-all.png", QString{}, "view_menu.html"); fullscreen_act = newAction("fullscreen", tr("Toggle fullscreen mode"), window, SLOT(toggleFullscreenMode()), nullptr, QString{}, "view_menu.html"); custom_zoom_act = newAction("setzoom", tr("Set custom zoom factor..."), this, SLOT(setCustomZoomFactorClicked()), nullptr, QString{}, "view_menu.html"); hatch_areas_view_act = newCheckAction("hatchareasview", tr("Hatch areas"), this, SLOT(hatchAreas(bool)), nullptr, QString{}, "view_menu.html"); baseline_view_act = newCheckAction("baselineview", tr("Baseline view"), this, SLOT(baselineView(bool)), nullptr, QString{}, "view_menu.html"); hide_all_templates_act = newCheckAction("hidealltemplates", tr("Hide all templates"), this, SLOT(hideAllTemplates(bool)), nullptr, QString{}, "view_menu.html"); overprinting_simulation_act = newCheckAction("overprintsimulation", tr("Overprinting simulation"), this, SLOT(overprintingSimulation(bool)), nullptr, QString{}, "view_menu.html"); symbol_window_act = newCheckAction("symbolwindow", tr("Symbol window"), this, SLOT(showSymbolWindow(bool)), "symbols.png", tr("Show/Hide the symbol window"), "symbol_dock_widget.html"); color_window_act = newCheckAction("colorwindow", tr("Color window"), this, SLOT(showColorWindow(bool)), "colors.png", tr("Show/Hide the color window"), "color_dock_widget.html"); symbol_set_id_act = newAction("symbol-set-id", tr("Symbol set ID..."), this, SLOT(symbolSetIdClicked()), nullptr, tr("Edit the symbol set ID"), nullptr); load_symbols_from_act = newAction("loadsymbols", tr("Replace symbol set..."), this, SLOT(loadSymbolsFromClicked()), nullptr, tr("Replace the symbols with those from another map file"), "symbol_replace_dialog.html"); load_crt_act = newAction("loadcrt", tr("Load CRT file..."), this, SLOT(loadCrtClicked()), nullptr, tr("Assign new symbols by cross-reference table"), "symbol_replace_dialog.html"); /*QAction* load_colors_from_act = newAction("loadcolors", tr("Load colors from..."), this, SLOT(loadColorsFromClicked()), nullptr, tr("Replace the colors with those from another map file"));*/ scale_all_symbols_act = newAction("scaleall", tr("Scale all symbols..."), this, SLOT(scaleAllSymbolsClicked()), nullptr, tr("Scale the whole symbol set"), "map_menu.html"); georeferencing_act = newAction("georef", tr("Georeferencing..."), this, SLOT(editGeoreferencing()), nullptr, QString{}, "georeferencing.html"); scale_map_act = newAction("scalemap", tr("Change map scale..."), this, SLOT(scaleMapClicked()), "tool-scale.png", tr("Change the map scale and adjust map objects and symbol sizes"), "map_menu.html"); rotate_map_act = newAction("rotatemap", tr("Rotate map..."), this, SLOT(rotateMapClicked()), "tool-rotate.png", tr("Rotate the whole map"), "map_menu.html"); map_notes_act = newAction("mapnotes", tr("Map notes..."), this, SLOT(mapNotesClicked()), nullptr, QString{}, "map_menu.html"); template_window_act = newCheckAction("templatewindow", tr("Template setup window"), this, SLOT(showTemplateWindow(bool)), "templates", tr("Show/Hide the template window"), "templates_menu.html"); //QAction* template_config_window_act = newCheckAction("templateconfigwindow", tr("Template configurations window"), this, SLOT(showTemplateConfigurationsWindow(bool)), "window-new", tr("Show/Hide the template configurations window")); //QAction* template_visibilities_window_act = newCheckAction("templatevisibilitieswindow", tr("Template visibilities window"), this, SLOT(showTemplateVisbilitiesWindow(bool)), "window-new", tr("Show/Hide the template visibilities window")); open_template_act = newAction("opentemplate", tr("Open template..."), this, SLOT(openTemplateClicked()), nullptr, QString{}, "templates_menu.html"); reopen_template_act = newAction("reopentemplate", tr("Reopen template..."), this, SLOT(reopenTemplateClicked()), nullptr, QString{}, "templates_menu.html"); tags_window_act = newCheckAction("tagswindow", tr("Tag editor"), this, SLOT(showTagsWindow(bool)), "window-new", tr("Show/Hide the tag editor window"), "tag_editor.html"); edit_tool_act = newToolAction("editobjects", tr("Edit objects"), this, SLOT(editToolClicked()), "tool-edit.png", QString{}, "toolbars.html#tool_edit_point"); edit_line_tool_act = newToolAction("editlines", tr("Edit lines"), this, SLOT(editLineToolClicked()), "tool-edit-line.png", QString{}, "toolbars.html#tool_edit_line"); draw_point_act = newToolAction("drawpoint", tr("Set point objects"), this, SLOT(drawPointClicked()), "draw-point.png", QString{}, "toolbars.html#tool_draw_point"); draw_path_act = newToolAction("drawpath", tr("Draw paths"), this, SLOT(drawPathClicked()), "draw-path.png", QString{}, "toolbars.html#tool_draw_path"); draw_circle_act = newToolAction("drawcircle", tr("Draw circles and ellipses"), this, SLOT(drawCircleClicked()), "draw-circle.png", QString{}, "toolbars.html#tool_draw_circle"); draw_rectangle_act = newToolAction("drawrectangle", tr("Draw rectangles"), this, SLOT(drawRectangleClicked()), "draw-rectangle.png", QString{}, "toolbars.html#tool_draw_rectangle"); draw_freehand_act = newToolAction("drawfreehand", tr("Draw free-handedly"), this, SLOT(drawFreehandClicked()), "draw-freehand.png", QString{}, "toolbars.html#tool_draw_freehand"); // TODO: documentation draw_fill_act = newToolAction("drawfill", tr("Fill bounded areas"), this, SLOT(drawFillClicked()), "tool-fill.png", QString{}, "toolbars.html#tool_draw_fill"); // TODO: documentation draw_text_act = newToolAction("drawtext", tr("Write text"), this, SLOT(drawTextClicked()), "draw-text.png", QString{}, "toolbars.html#tool_draw_text"); duplicate_act = newAction("duplicate", tr("Duplicate"), this, SLOT(duplicateClicked()), "tool-duplicate.png", QString{}, "toolbars.html#duplicate"); switch_symbol_act = newAction("switchsymbol", tr("Switch symbol"), this, SLOT(switchSymbolClicked()), "tool-switch-symbol.png", QString{}, "toolbars.html#switch_symbol"); fill_border_act = newAction("fillborder", tr("Fill / Create border"), this, SLOT(fillBorderClicked()), "tool-fill-border.png", QString{}, "toolbars.html#fill_create_border"); switch_dashes_act = newAction("switchdashes", tr("Switch dash direction"), this, SLOT(switchDashesClicked()), "tool-switch-dashes", QString{}, "toolbars.html#switch_dashes"); connect_paths_act = newAction("connectpaths", tr("Connect paths"), this, SLOT(connectPathsClicked()), "tool-connect-paths.png", QString{}, "toolbars.html#connect"); cut_tool_act = newToolAction("cutobject", tr("Cut object"), this, SLOT(cutClicked()), "tool-cut.png", QString{}, "toolbars.html#cut_tool"); cut_hole_act = newToolAction("cuthole", tr("Cut free form hole"), this, SLOT(cutHoleClicked()), "tool-cut-hole.png", QString{}, "toolbars.html#cut_hole"); // cut hole using a path cut_hole_circle_act = newToolAction("cutholecircle", tr("Cut round hole"), this, SLOT(cutHoleCircleClicked()), "tool-cut-hole.png", QString{}, "toolbars.html#cut_hole"); cut_hole_rectangle_act = newToolAction("cutholerectangle", tr("Cut rectangular hole"), this, SLOT(cutHoleRectangleClicked()), "tool-cut-hole.png", QString{}, "toolbars.html#cut_hole"); cut_hole_menu = new QMenu(tr("Cut hole")); cut_hole_menu->setIcon(QIcon(QString::fromLatin1(":/images/tool-cut-hole.png"))); cut_hole_menu->addAction(cut_hole_act); cut_hole_menu->addAction(cut_hole_circle_act); cut_hole_menu->addAction(cut_hole_rectangle_act); rotate_act = newToolAction("rotateobjects", tr("Rotate objects"), this, SLOT(rotateClicked()), "tool-rotate.png", QString{}, "toolbars.html#rotate"); rotate_pattern_act = newToolAction("rotatepatterns", tr("Rotate pattern"), this, SLOT(rotatePatternClicked()), "tool-rotate-pattern.png", QString{}, "toolbars.html#tool_rotate_pattern"); scale_act = newToolAction("scaleobjects", tr("Scale objects"), this, SLOT(scaleClicked()), "tool-scale.png", QString{}, "toolbars.html#scale"); measure_act = newCheckAction("measure", tr("Measure lengths and areas"), this, SLOT(measureClicked(bool)), "tool-measure.png", QString{}, "toolbars.html#measure"); boolean_union_act = newAction("booleanunion", tr("Unify areas"), this, SLOT(booleanUnionClicked()), "tool-boolean-union.png", QString{}, "toolbars.html#unify_areas"); boolean_intersection_act = newAction("booleanintersection", tr("Intersect areas"), this, SLOT(booleanIntersectionClicked()), "tool-boolean-intersection.png", QString{}, "toolbars.html#intersect_areas"); boolean_difference_act = newAction("booleandifference", tr("Cut away from area"), this, SLOT(booleanDifferenceClicked()), "tool-boolean-difference.png", QString{}, "toolbars.html#area_difference"); boolean_xor_act = newAction("booleanxor", tr("Area XOr"), this, SLOT(booleanXOrClicked()), "tool-boolean-xor.png", QString{}, "toolbars.html#area_xor"); boolean_merge_holes_act = newAction("booleanmergeholes", tr("Merge area holes"), this, SLOT(booleanMergeHolesClicked()), "tool-boolean-merge-holes.png", QString{}, "toolbars.html#area_merge_holes"); // TODO:documentation convert_to_curves_act = newAction("converttocurves", tr("Convert to curves"), this, SLOT(convertToCurvesClicked()), "tool-convert-to-curves.png", QString{}, "toolbars.html#convert_to_curves"); simplify_path_act = newAction("simplify", tr("Simplify path"), this, SLOT(simplifyPathClicked()), "tool-simplify-path.png", QString{}, "toolbars.html#simplify_path"); cutout_physical_act = newToolAction("cutoutphysical", tr("Cutout"), this, SLOT(cutoutPhysicalClicked()), "tool-cutout-physical.png", QString{}, "toolbars.html#cutout_physical"); cutaway_physical_act = newToolAction("cutawayphysical", tr("Cut away"), this, SLOT(cutawayPhysicalClicked()), "tool-cutout-physical-inner.png", QString{}, "toolbars.html#cutaway_physical"); distribute_points_act = newAction("distributepoints", tr("Distribute points along path"), this, SLOT(distributePointsClicked()), "tool-distribute-points.png", QString{}, "toolbars.html#distribute_points"); // TODO: write documentation paint_on_template_act = new QAction(QIcon(QString::fromLatin1(":/images/pencil.png")), tr("Paint on template"), this); paint_on_template_act->setCheckable(true); paint_on_template_act->setWhatsThis(Util::makeWhatThis("toolbars.html#draw_on_template")); connect(paint_on_template_act, &QAction::triggered, this, &MapEditorController::paintOnTemplateClicked); paint_on_template_settings_act = new QAction(QIcon(QString::fromLatin1(":/images/paint-on-template-settings.png")), tr("Paint on template settings"), this); paint_on_template_settings_act->setWhatsThis(Util::makeWhatThis("toolbars.html#draw_on_template")); connect(paint_on_template_settings_act, &QAction::triggered, this, &MapEditorController::paintOnTemplateSelectClicked); touch_cursor_action = newCheckAction("touchcursor", tr("Enable touch cursor"), map_widget, SLOT(enableTouchCursor(bool)), "tool-touch-cursor.png", QString{}, "toolbars.html#touch_cursor"); // TODO: write documentation gps_display_action = newCheckAction("gpsdisplay", tr("Enable GPS display"), this, SLOT(enableGPSDisplay(bool)), "tool-gps-display.png", QString{}, "toolbars.html#gps_display"); // TODO: write documentation gps_display_action->setEnabled(map->getGeoreferencing().isValid() && ! map->getGeoreferencing().isLocal()); gps_distance_rings_action = newCheckAction("gpsdistancerings", tr("Enable GPS distance rings"), this, SLOT(enableGPSDistanceRings(bool)), "gps-distance-rings.png", QString{}, "toolbars.html#gps_distance_rings"); // TODO: write documentation gps_distance_rings_action->setEnabled(false); draw_point_gps_act = newToolAction("drawpointgps", tr("Set point object at GPS position"), this, SLOT(drawPointGPSClicked()), "draw-point-gps.png", QString{}, "toolbars.html#tool_draw_point_gps"); // TODO: write documentation draw_point_gps_act->setEnabled(false); gps_temporary_point_act = newAction("gpstemporarypoint", tr("Set temporary marker at GPS position"), this, SLOT(gpsTemporaryPointClicked()), "gps-temporary-point.png", QString{}, "toolbars.html#gps_temporary_point"); // TODO: write documentation gps_temporary_point_act->setEnabled(false); gps_temporary_path_act = newCheckAction("gpstemporarypath", tr("Create temporary path at GPS position"), this, SLOT(gpsTemporaryPathClicked(bool)), "gps-temporary-path.png", QString{}, "toolbars.html#gps_temporary_path"); // TODO: write documentation gps_temporary_path_act->setEnabled(false); gps_temporary_clear_act = newAction("gpstemporaryclear", tr("Clear temporary GPS markers"), this, SLOT(gpsTemporaryClearClicked()), "gps-temporary-clear.png", QString{}, "toolbars.html#gps_temporary_clear"); // TODO: write documentation gps_temporary_clear_act->setEnabled(false); compass_action = newCheckAction("compassdisplay", tr("Enable compass display"), this, SLOT(enableCompassDisplay(bool)), "compass.png", QString{}, "toolbars.html#compass_display"); // TODO: write documentation align_map_with_north_act = newCheckAction("alignmapwithnorth", tr("Align map with north"), this, SLOT(alignMapWithNorth(bool)), "rotate-map.png", QString{}, "toolbars.html#align_map_with_north"); // TODO: write documentation mappart_add_act = newAction("addmappart", tr("Add new part..."), this, SLOT(addMapPart())); mappart_rename_act = newAction("renamemappart", tr("Rename current part..."), this, SLOT(renameMapPart())); mappart_remove_act = newAction("removemappart", tr("Remove current part"), this, SLOT(removeMapPart())); mappart_merge_act = newAction("mergemapparts", tr("Merge all parts"), this, SLOT(mergeAllMapParts())); import_act = newAction("import", tr("Import..."), this, SLOT(importClicked()), nullptr, QString{}, "file_menu.html"); map_coordinates_act = new QAction(tr("Map coordinates"), this); map_coordinates_act->setCheckable(true); projected_coordinates_act = new QAction(tr("Projected coordinates"), this); projected_coordinates_act->setCheckable(true); geographic_coordinates_act = new QAction(tr("Latitude/Longitude (Dec)"), this); geographic_coordinates_act->setCheckable(true); geographic_coordinates_dms_act = new QAction(tr("Latitude/Longitude (DMS)"), this); geographic_coordinates_dms_act->setCheckable(true); auto coordinates_group = new QActionGroup(this); coordinates_group->addAction(map_coordinates_act); coordinates_group->addAction(projected_coordinates_act); coordinates_group->addAction(geographic_coordinates_act); coordinates_group->addAction(geographic_coordinates_dms_act); QObject::connect(coordinates_group, &QActionGroup::triggered, this, &MapEditorController::coordsDisplayChanged); map_coordinates_act->setChecked(true); QObject::connect(&map->getGeoreferencing(), &Georeferencing::projectionChanged, this, &MapEditorController::projectionChanged); projectionChanged(); copy_coords_act = newAction("copy-coords", tr("Copy position"), this, SLOT(copyDisplayedCoords()), "copy-coords.png", tr("Copy position to clipboard.")); } void MapEditorController::createMenuAndToolbars() { statusbar_cursorpos_label->setContextMenuPolicy(Qt::ActionsContextMenu); statusbar_cursorpos_label->addAction(map_coordinates_act); statusbar_cursorpos_label->addAction(projected_coordinates_act); statusbar_cursorpos_label->addAction(geographic_coordinates_act); statusbar_cursorpos_label->addAction(geographic_coordinates_dms_act); // Refactored so we can do custom key bindings in the future assignKeyboardShortcuts(); // Extend file menu QMenu* file_menu = window->getFileMenu(); QAction* insertion_act = window->getFileMenuExtensionAct(); #ifdef QT_PRINTSUPPORT_LIB file_menu->insertAction(insertion_act, print_act); file_menu->insertSeparator(insertion_act); insertion_act = print_act; #endif file_menu->insertAction(insertion_act, import_act); #ifdef QT_PRINTSUPPORT_LIB QMenu* export_menu = new QMenu(tr("&Export as..."), file_menu); export_menu->addAction(export_image_act); export_menu->addAction(export_pdf_act); file_menu->insertMenu(insertion_act, export_menu); #endif file_menu->insertSeparator(insertion_act); // Edit menu QMenu* edit_menu = window->menuBar()->addMenu(tr("&Edit")); edit_menu->setWhatsThis(Util::makeWhatThis("edit_menu.html")); edit_menu->addAction(undo_act); edit_menu->addAction(redo_act); edit_menu->addSeparator(); edit_menu->addAction(cut_act); edit_menu->addAction(copy_act); edit_menu->addAction(paste_act); edit_menu->addAction(delete_act); edit_menu->addSeparator(); edit_menu->addAction(select_all_act); edit_menu->addAction(select_nothing_act); edit_menu->addAction(invert_selection_act); edit_menu->addAction(select_by_current_symbol_act); edit_menu->addSeparator(); edit_menu->addAction(find_feature->showDialogAction()); edit_menu->addAction(find_feature->findNextAction()); edit_menu->addSeparator(); edit_menu->addAction(clear_undo_redo_history_act); // View menu QMenu* view_menu = window->menuBar()->addMenu(tr("&View")); view_menu->setWhatsThis(Util::makeWhatThis("view_menu.html")); view_menu->addAction(pan_act); view_menu->addAction(zoom_in_act); view_menu->addAction(zoom_out_act); view_menu->addAction(show_all_act); view_menu->addAction(custom_zoom_act); view_menu->addSeparator(); view_menu->addAction(show_grid_act); view_menu->addAction(hatch_areas_view_act); view_menu->addAction(baseline_view_act); view_menu->addAction(overprinting_simulation_act); view_menu->addAction(hide_all_templates_act); view_menu->addSeparator(); QMenu* coordinates_menu = new QMenu(tr("Display coordinates as..."), view_menu); coordinates_menu->addAction(map_coordinates_act); coordinates_menu->addAction(projected_coordinates_act); coordinates_menu->addAction(geographic_coordinates_act); coordinates_menu->addAction(geographic_coordinates_dms_act); view_menu->addMenu(coordinates_menu); view_menu->addSeparator(); view_menu->addAction(fullscreen_act); view_menu->addSeparator(); view_menu->addAction(tags_window_act); view_menu->addAction(color_window_act); view_menu->addAction(symbol_window_act); view_menu->addAction(template_window_act); // Tools menu QMenu *tools_menu = window->menuBar()->addMenu(tr("&Tools")); tools_menu->setWhatsThis(Util::makeWhatThis("tools_menu.html")); tools_menu->addAction(edit_tool_act); tools_menu->addAction(edit_line_tool_act); tools_menu->addAction(draw_point_act); tools_menu->addAction(draw_path_act); tools_menu->addAction(draw_circle_act); tools_menu->addAction(draw_rectangle_act); tools_menu->addAction(draw_freehand_act); tools_menu->addAction(draw_fill_act); tools_menu->addAction(draw_text_act); tools_menu->addAction(duplicate_act); tools_menu->addAction(switch_symbol_act); tools_menu->addAction(fill_border_act); tools_menu->addAction(switch_dashes_act); tools_menu->addAction(connect_paths_act); tools_menu->addAction(boolean_union_act); tools_menu->addAction(boolean_intersection_act); tools_menu->addAction(boolean_difference_act); tools_menu->addAction(boolean_xor_act); tools_menu->addAction(boolean_merge_holes_act); tools_menu->addAction(cut_tool_act); tools_menu->addMenu(cut_hole_menu); tools_menu->addAction(rotate_act); tools_menu->addAction(rotate_pattern_act); tools_menu->addAction(scale_act); tools_menu->addAction(measure_act); tools_menu->addAction(convert_to_curves_act); tools_menu->addAction(simplify_path_act); tools_menu->addAction(cutout_physical_act); tools_menu->addAction(cutaway_physical_act); tools_menu->addAction(distribute_points_act); tools_menu->addAction(touch_cursor_action); // Map menu QMenu* map_menu = window->menuBar()->addMenu(tr("M&ap")); map_menu->setWhatsThis(Util::makeWhatThis("map_menu.html")); map_menu->addAction(georeferencing_act); map_menu->addAction(configure_grid_act); map_menu->addSeparator(); map_menu->addAction(scale_map_act); map_menu->addAction(rotate_map_act); map_menu->addAction(map_notes_act); map_menu->addSeparator(); updateMapPartsUI(); map_menu->addAction(mappart_add_act); map_menu->addAction(mappart_rename_act); map_menu->addAction(mappart_remove_act); map_menu->addMenu(mappart_move_menu); map_menu->addMenu(mappart_merge_menu); map_menu->addAction(mappart_merge_act); // Symbols menu QMenu* symbols_menu = window->menuBar()->addMenu(tr("Sy&mbols")); symbols_menu->setWhatsThis(Util::makeWhatThis("symbols_menu.html")); symbols_menu->addAction(symbol_window_act); symbols_menu->addAction(color_window_act); symbols_menu->addSeparator(); symbols_menu->addAction(symbol_set_id_act); symbols_menu->addAction(scale_all_symbols_act); symbols_menu->addAction(load_symbols_from_act); symbols_menu->addAction(load_crt_act); /*symbols_menu->addAction(load_colors_from_act);*/ // Templates menu QMenu* template_menu = window->menuBar()->addMenu(tr("&Templates")); template_menu->setWhatsThis(Util::makeWhatThis("templates_menu.html")); template_menu->addAction(template_window_act); /*template_menu->addAction(template_config_window_act); template_menu->addAction(template_visibilities_window_act);*/ template_menu->addSeparator(); template_menu->addAction(open_template_act); template_menu->addAction(reopen_template_act); // Extend and activate general toolbar QToolBar* main_toolbar = window->getGeneralToolBar(); #ifdef QT_PRINTSUPPORT_LIB main_toolbar->addAction(print_act); main_toolbar->addSeparator(); #endif main_toolbar->addAction(cut_act); main_toolbar->addAction(copy_act); main_toolbar->addAction(paste_act); main_toolbar->addSeparator(); main_toolbar->addAction(undo_act); main_toolbar->addAction(redo_act); window->addToolBar(main_toolbar); // View toolbar toolbar_view = window->addToolBar(tr("View")); toolbar_view->setObjectName(QString::fromLatin1("View toolbar")); auto grid_button = new QToolButton(); grid_button->setCheckable(true); grid_button->setDefaultAction(show_grid_act); grid_button->setPopupMode(QToolButton::MenuButtonPopup); auto grid_menu = new QMenu(grid_button); grid_menu->addAction(tr("Configure grid...")); grid_button->setMenu(grid_menu); connect(grid_menu, &QMenu::triggered, this, &MapEditorController::configureGrid); toolbar_view->addWidget(grid_button); toolbar_view->addSeparator(); toolbar_view->addAction(pan_act); toolbar_view->addAction(zoom_in_act); toolbar_view->addAction(zoom_out_act); toolbar_view->addAction(show_all_act); // MapParts toolbar toolbar_mapparts = window->addToolBar(tr("Map parts")); toolbar_mapparts->setObjectName(QString::fromLatin1("Map parts toolbar")); if (!mappart_selector_box) { mappart_selector_box = new QComboBox(toolbar_mapparts); mappart_selector_box->setToolTip(tr("Map parts")); } mappart_selector_box->setSizeAdjustPolicy(QComboBox::AdjustToContents); connect(mappart_selector_box, QOverload::of(&QComboBox::currentIndexChanged), this, &MapEditorController::changeMapPart); toolbar_mapparts->addWidget(mappart_selector_box); window->addToolBarBreak(); // Drawing toolbar toolbar_drawing = window->addToolBar(tr("Drawing")); toolbar_drawing->setObjectName(QString::fromLatin1("Drawing toolbar")); toolbar_drawing->addAction(edit_tool_act); toolbar_drawing->addAction(edit_line_tool_act); toolbar_drawing->addAction(draw_point_act); toolbar_drawing->addAction(draw_path_act); toolbar_drawing->addAction(draw_circle_act); toolbar_drawing->addAction(draw_rectangle_act); toolbar_drawing->addAction(draw_freehand_act); toolbar_drawing->addAction(draw_fill_act); toolbar_drawing->addAction(draw_text_act); toolbar_drawing->addSeparator(); auto paint_on_template_button = new QToolButton(); paint_on_template_button->setCheckable(true); paint_on_template_button->setDefaultAction(paint_on_template_act); paint_on_template_button->setPopupMode(QToolButton::MenuButtonPopup); auto paint_on_template_menu = new QMenu(paint_on_template_button); paint_on_template_menu->addAction(tr("Select template...")); paint_on_template_button->setMenu(paint_on_template_menu); connect(paint_on_template_menu, &QMenu::triggered, this, &MapEditorController::paintOnTemplateSelectClicked); toolbar_drawing->addWidget(paint_on_template_button); // Editing toolbar toolbar_editing = window->addToolBar(tr("Editing")); toolbar_editing->setObjectName(QString::fromLatin1("Editing toolbar")); toolbar_editing->addAction(delete_act); toolbar_editing->addAction(duplicate_act); toolbar_editing->addAction(switch_symbol_act); toolbar_editing->addAction(fill_border_act); toolbar_editing->addAction(switch_dashes_act); toolbar_editing->addAction(connect_paths_act); toolbar_editing->addAction(boolean_union_act); toolbar_editing->addAction(cut_tool_act); auto cut_hole_button = new QToolButton(); cut_hole_button->setCheckable(true); cut_hole_button->setToolButtonStyle(Qt::ToolButtonIconOnly); cut_hole_button->setDefaultAction(cut_hole_act); cut_hole_button->setPopupMode(QToolButton::MenuButtonPopup); cut_hole_button->setMenu(cut_hole_menu); toolbar_editing->addWidget(cut_hole_button); toolbar_editing->addAction(rotate_act); toolbar_editing->addAction(rotate_pattern_act); toolbar_editing->addAction(scale_act); toolbar_editing->addAction(measure_act); // Advanced editing toolbar toolbar_advanced_editing = window->addToolBar(tr("Advanced editing")); toolbar_advanced_editing->setObjectName(QString::fromLatin1("Advanved editing toolbar")); toolbar_advanced_editing->addAction(cutout_physical_act); toolbar_advanced_editing->addAction(cutaway_physical_act); toolbar_advanced_editing->addAction(convert_to_curves_act); toolbar_advanced_editing->addAction(simplify_path_act); toolbar_advanced_editing->addAction(distribute_points_act); toolbar_advanced_editing->addAction(boolean_intersection_act); toolbar_advanced_editing->addAction(boolean_difference_act); toolbar_advanced_editing->addAction(boolean_xor_act); toolbar_advanced_editing->addAction(boolean_merge_holes_act); QWidget* context_menu = map_widget->getContextMenu(); context_menu->addAction(edit_tool_act); context_menu->addAction(draw_point_act); context_menu->addAction(draw_path_act); context_menu->addAction(draw_rectangle_act); context_menu->addAction(cut_tool_act); context_menu->addAction(cut_hole_act); context_menu->addAction(switch_dashes_act); context_menu->addAction(connect_paths_act); context_menu->addAction(copy_coords_act); } void MapEditorController::createMobileGUI() { // Create mobile-specific actions mobile_symbol_selector_action = new QAction(tr("Select symbol"), this); connect(mobile_symbol_selector_action, &QAction::triggered, this, &MapEditorController::mobileSymbolSelectorClicked); mobile_symbol_button_menu = new QMenu(window); mobile_symbol_button_menu->addAction(QString{}); // reserved for symbol name auto description_action = mobile_symbol_button_menu->addAction(QApplication::translate("OpenOrienteering::SymbolPropertiesWidget", "Description")); connect(description_action, &QAction::triggered, this, [this]() { auto symbol = symbol_widget->getSingleSelectedSymbol(); auto document = QString{ symbol->getNumberAsString() + QLatin1Char(' ') + QLatin1String("") + symbol->getName() + QLatin1String("\n\n") + symbol->getDescription() }; // Cf. SymbolToolTip::scheduleShow document.replace(QLatin1Char('\n'), QStringLiteral("
    ")); document.remove(QLatin1Char('\r')); TextBrowserDialog description_dialog(document, window); description_dialog.exec(); }); mobile_symbol_button_menu->addSeparator(); auto hide_symbol_action = mobile_symbol_button_menu->addAction(QApplication::translate("OpenOrienteering::SymbolRenderWidget", "Hide objects with this symbol")); hide_symbol_action->setCheckable(true); connect(hide_symbol_action, &QAction::triggered, this, [this](bool value) { auto symbol = symbol_widget->getSingleSelectedSymbol(); symbol->setHidden(value); if (!value && map->removeSymbolFromSelection(symbol, false)) map->emitSelectionChanged(); map->updateAllMapWidgets(); map->setSymbolsDirty(); selectedSymbolsChanged(); }); auto protected_symbol_action = mobile_symbol_button_menu->addAction(QApplication::translate("OpenOrienteering::SymbolRenderWidget", "Protect objects with this symbol")); protected_symbol_action->setCheckable(true); connect(protected_symbol_action, &QAction::triggered, this, [this](bool value) { auto symbol = symbol_widget->getSingleSelectedSymbol(); symbol->setProtected(value); if (!value && map->removeSymbolFromSelection(symbol, false)) map->emitSelectionChanged(); map->setSymbolsDirty(); selectedSymbolsChanged(); }); QAction* hide_top_bar_action = new QAction(QIcon(QString::fromLatin1(":/images/arrow-thin-upleft.png")), tr("Hide top bar"), this); connect(hide_top_bar_action, &QAction::triggered, this, &MapEditorController::hideTopActionBar); QAction* show_top_bar_action = new QAction(QIcon(QString::fromLatin1(":/images/arrow-thin-downright.png")), tr("Show top bar"), this); connect(show_top_bar_action, &QAction::triggered, this, &MapEditorController::showTopActionBar); Q_ASSERT(mappart_selector_box); QAction* mappart_action = new QAction(QIcon(QString::fromLatin1(":/images/map-parts.png")), tr("Map parts"), this); connect(mappart_action, &QAction::triggered, this, [this, mappart_action]() { auto mappart_button = top_action_bar->getButtonForAction(mappart_action); if (!mappart_button) mappart_button = top_action_bar->getButtonForAction(top_action_bar->getOverflowAction()); mappart_selector_box->setGeometry(mappart_button->geometry()); mappart_selector_box->showPopup(); }); connect(mappart_selector_box, QOverload::of(&QComboBox::currentIndexChanged), this, &MapEditorController::changeMapPart); // Create button for showing the top bar again after hiding it int icon_size_pixel = qRound(Util::mmToPixelLogical(10)); const int button_icon_size = icon_size_pixel - 12; QSize icon_size = QSize(button_icon_size, button_icon_size); QIcon icon = show_top_bar_action->icon(); QPixmap pixmap = icon.pixmap(icon_size, QIcon::Normal, QIcon::Off); if (pixmap.width() < button_icon_size) { pixmap = pixmap.scaled(icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); icon.addPixmap(pixmap); show_top_bar_action->setIcon(icon); } show_top_bar_button = new QToolButton(window); show_top_bar_button->setDefaultAction(show_top_bar_action); show_top_bar_button->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); show_top_bar_button->setAutoRaise(true); show_top_bar_button->setIconSize(icon_size); show_top_bar_button->setGeometry(0, 0, button_icon_size, button_icon_size); // Create bottom action bar bottom_action_bar = new ActionGridBar(ActionGridBar::Horizontal, 2); // Left side int col = 0; bottom_action_bar->addAction(zoom_in_act, 0, col); bottom_action_bar->addAction(pan_act, 1, col++); bottom_action_bar->addAction(zoom_out_act, 0, col); auto zoom_out_button = bottom_action_bar->getButtonForAction(zoom_out_act); auto mobile_zoom_out_menu = new QMenu(zoom_out_button); auto zoom_1x_action = mobile_zoom_out_menu->addAction(tr("1x zoom")); connect(zoom_1x_action, &QAction::triggered, this, [this]() { main_view->setZoom(1); }); auto zoom_2x_action = mobile_zoom_out_menu->addAction(tr("2x zoom")); connect(zoom_2x_action, &QAction::triggered, this, [this]() { main_view->setZoom(2); }); zoom_out_button->setMenu(mobile_zoom_out_menu); bottom_action_bar->addAction(move_to_gps_pos_act, 1, col++); bottom_action_bar->addAction(gps_temporary_path_act, 0, col); bottom_action_bar->addAction(gps_temporary_point_act, 1, col++); bottom_action_bar->addAction(gps_temporary_clear_act, 0, col++); bottom_action_bar->addAction(paint_on_template_act, 0, col); bottom_action_bar->addAction(paint_on_template_settings_act, 1, col++); // Right side bottom_action_bar->addActionAtEnd(mobile_symbol_selector_action, 0, 1, 2, 2); auto button = bottom_action_bar->getButtonForAction(mobile_symbol_selector_action); button->setPopupMode(QToolButton::DelayedPopup); col = 2; bottom_action_bar->addActionAtEnd(draw_point_act, 0, col); bottom_action_bar->addActionAtEnd(draw_point_gps_act, 1, col++); bottom_action_bar->addActionAtEnd(draw_path_act, 0, col); bottom_action_bar->addActionAtEnd(draw_freehand_act, 1, col++); bottom_action_bar->addActionAtEnd(draw_rectangle_act, 0, col); bottom_action_bar->addActionAtEnd(draw_circle_act, 1, col++); //bottom_action_bar->addActionAtEnd(draw_fill_act, 0, col); bottom_action_bar->addActionAtEnd(draw_text_act, 1, col++); // Create top action bar top_action_bar = new ActionGridBar(ActionGridBar::Horizontal, 2); // Left side col = 0; top_action_bar->addAction(hide_top_bar_action, 0, col); top_action_bar->addAction(window->getSaveAct(), 1, col++); top_action_bar->addAction(compass_action, 0, col); top_action_bar->addAction(gps_display_action, 1, col++); top_action_bar->addAction(gps_distance_rings_action, 0, col); top_action_bar->addAction(align_map_with_north_act, 1, col++); top_action_bar->addAction(show_grid_act, 0, col); top_action_bar->addAction(show_all_act, 1, col++); // Right side col = 0; top_action_bar->addActionAtEnd(window->getCloseAct(), 0, col); top_action_bar->addActionAtEnd(top_action_bar->getOverflowAction(), 1, col++); top_action_bar->addActionAtEnd(redo_act, 0, col); top_action_bar->addActionAtEnd(undo_act, 1, col++); top_action_bar->addActionAtEnd(touch_cursor_action, 0, col); top_action_bar->addActionAtEnd(template_window_act, 1, col++); top_action_bar->addActionAtEnd(edit_tool_act, 0, col); top_action_bar->addActionAtEnd(edit_line_tool_act, 1, col++); top_action_bar->addActionAtEnd(delete_act, 0, col); top_action_bar->addActionAtEnd(duplicate_act, 1, col++); top_action_bar->addActionAtEnd(switch_symbol_act, 0, col); top_action_bar->addActionAtEnd(fill_border_act, 1, col++); top_action_bar->addActionAtEnd(switch_dashes_act, 0, col); top_action_bar->addActionAtEnd(boolean_union_act, 1, col++); top_action_bar->addActionAtEnd(cut_tool_act, 0, col); top_action_bar->addActionAtEnd(connect_paths_act, 1, col++); top_action_bar->addActionAtEnd(rotate_act, 0, col); top_action_bar->addActionAtEnd(cut_hole_act, 1, col++); top_action_bar->addActionAtEnd(scale_act, 0, col); top_action_bar->addActionAtEnd(rotate_pattern_act, 1, col++); top_action_bar->addActionAtEnd(convert_to_curves_act, 0, col); top_action_bar->addActionAtEnd(simplify_path_act, 1, col++); top_action_bar->addActionAtEnd(distribute_points_act, 0, col); top_action_bar->addActionAtEnd(boolean_difference_act, 1, col++); top_action_bar->addActionAtEnd(measure_act, 0, col); top_action_bar->addActionAtEnd(boolean_merge_holes_act, 1, col++); top_action_bar->addActionAtEnd(mappart_action, 1, col++); bottom_action_bar->setToUseOverflowActionFrom(top_action_bar); top_action_bar->setParent(map_widget); auto container_widget = new QWidget(); auto layout = new QVBoxLayout(); layout->setMargin(0); layout->setSpacing(0); layout->addWidget(map_widget, 1); layout->addWidget(bottom_action_bar); container_widget->setLayout(layout); window->setCentralWidget(container_widget); } void MapEditorController::detach() { // Terminate all editing setTool(nullptr); setOverrideTool(nullptr); saveWindowState(); // Avoid a crash triggered by pressing Ctrl-W during loading. if (nullptr != symbol_dock_widget) window->removeDockWidget(symbol_dock_widget); else if (nullptr != symbol_widget) delete symbol_widget; delete gps_display; gps_display = nullptr; delete gps_track_recorder; gps_track_recorder = nullptr; delete compass_display; compass_display = nullptr; delete gps_marker_display; gps_marker_display = nullptr; find_feature.reset(nullptr); window->setCentralWidget(nullptr); delete map_widget; delete statusbar_zoom_frame; delete statusbar_cursorpos_label; #ifdef Q_OS_ANDROID QAndroidJniObject::callStaticMethod("org/openorienteering/mapper/MapperActivity", "unlockOrientation", "()V"); #endif if (mobile_mode) window->setWindowState(window->windowState() & ~Qt::WindowFullScreen); } void MapEditorController::saveWindowState() { if (!mobile_mode && mode != SymbolEditor) { QSettings settings; settings.beginGroup(QString::fromUtf8(metaObject()->className())); settings.setValue(QString::fromLatin1("state"), window->saveState()); } } void MapEditorController::restoreWindowState() { if (!mobile_mode) { QSettings settings; settings.beginGroup(QString::fromUtf8(metaObject()->className())); window->restoreState(settings.value(QString::fromLatin1("state")).toByteArray()); if (toolbar_mapparts && mappart_selector_box) toolbar_mapparts->setVisible(mappart_selector_box->count() > 1); } } bool MapEditorController::keyPressEventFilter(QKeyEvent* event) { #if defined(Q_OS_ANDROID) if (event->key() == Qt::Key_Back) { if (symbol_widget && symbol_widget->isVisible()) { mobileSymbolSelectorFinished(); return true; } if (isEditingInProgress() && !map_widget->findChild()) { QKeyEvent escape_pressed{ QEvent::KeyPress, Qt::Key_Escape, event->modifiers() }; QCoreApplication::sendEvent(window, &escape_pressed); QKeyEvent escape_released{ QEvent::KeyRelease, Qt::Key_Escape, event->modifiers() }; QCoreApplication::sendEvent(window, &escape_released); return true; } } #endif return map_widget->keyPressEventFilter(event); } bool MapEditorController::keyReleaseEventFilter(QKeyEvent* event) { return map_widget->keyReleaseEventFilter(event); } void MapEditorController::printClicked(int task) { #ifdef QT_PRINTSUPPORT_LIB if (!print_dock_widget) { print_dock_widget = new EditorDockWidget(QString{}, nullptr, this, window); print_dock_widget->setAllowedAreas(Qt::NoDockWidgetArea); print_dock_widget->toggleViewAction()->setVisible(false); print_widget = new PrintWidget(map, window, main_view, this, print_dock_widget); connect(print_dock_widget, &QDockWidget::visibilityChanged, this, [this]() { print_widget->setActive(print_dock_widget->isVisible()); } ); connect(print_widget, &PrintWidget::closeClicked, print_dock_widget, &QWidget::close); connect(print_widget, &PrintWidget::finished, print_dock_widget, &QWidget::close); connect(print_widget, &PrintWidget::taskChanged, print_dock_widget, &QWidget::setWindowTitle); print_dock_widget->setWidget(print_widget); print_dock_widget->setObjectName(QString::fromLatin1("Print dock widget")); addFloatingDockWidget(print_dock_widget); } print_widget->setActive(true); // Make sure to save the state before setting the task. print_widget->setTask((PrintWidget::TaskFlags)task); print_dock_widget->show(); print_dock_widget->raise(); #else Q_UNUSED(task) QMessageBox::warning(window, tr("Error"), tr("Print / Export is not available in this program version!")); #endif } void MapEditorController::undo() { doUndo(false); } void MapEditorController::redo() { doUndo(true); } void MapEditorController::doUndo(bool redo) { if ((!redo && !map->undoManager().canUndo()) || (redo && !map->undoManager().canRedo())) { // This should not happen as the action should be deactivated in this case! QMessageBox::critical(window, tr("Error"), tr("No undo steps available.")); return; } if (redo) map->undoManager().redo(window); else map->undoManager().undo(window); } void MapEditorController::cut() { copy(); //: Past tense. Displayed when an Edit > Cut operation is completed. window->showStatusBarMessage(tr("Cut %n object(s)", nullptr, map->getNumSelectedObjects()), 2000); map->deleteSelectedObjects(); } void MapEditorController::copy() { if (map->getNumSelectedObjects() == 0) return; // Create map containing required objects and their symbol and color dependencies Map copy_map; copy_map.setScaleDenominator(map->getScaleDenominator()); std::vector symbol_filter; symbol_filter.assign(map->getNumSymbols(), false); for (const auto object : map->selectedObjects()) { int symbol_index = map->findSymbolIndex(object->getSymbol()); if (symbol_index >= 0) symbol_filter[symbol_index] = true; } // Copy all colors. This improves preservation of relative order during paste. copy_map.importMap(map, Map::ColorImport, window); // Export symbols and colors into copy_map QHash symbol_map; copy_map.importMap(map, Map::MinimalSymbolImport, window, &symbol_filter, -1, true, &symbol_map); // Duplicate all selected objects into copy map for (const auto object : map->selectedObjects()) { auto new_object = object->duplicate(); if (symbol_map.contains(new_object->getSymbol())) new_object->setSymbol(symbol_map.value(new_object->getSymbol()), true); copy_map.addObject(new_object); } // Save map to memory QBuffer buffer; if (!copy_map.exportToIODevice(&buffer)) { QMessageBox::warning(nullptr, tr("Error"), tr("An internal error occurred, sorry!")); return; } // Put buffer into clipboard auto mime_data = new QMimeData(); mime_data->setData(MimeType::OpenOrienteeringObjects(), buffer.data()); QApplication::clipboard()->setMimeData(mime_data); // Show message window->showStatusBarMessage(tr("Copied %n object(s)", nullptr, map->getNumSelectedObjects()), 2000); } void MapEditorController::paste() { if (editing_in_progress) return; if (!QApplication::clipboard()->mimeData()->hasFormat(MimeType::OpenOrienteeringObjects())) { QMessageBox::warning(nullptr, tr("Error"), tr("There are no objects in clipboard which could be pasted!")); return; } // Get buffer from clipboard QByteArray byte_array = QApplication::clipboard()->mimeData()->data(MimeType::OpenOrienteeringObjects()); QBuffer buffer(&byte_array); buffer.open(QIODevice::ReadOnly); // Create map from buffer Map paste_map; if (!paste_map.importFromIODevice(&buffer)) { QMessageBox::warning(nullptr, tr("Error"), tr("An internal error occurred, sorry!")); return; } // Move objects in paste_map so their bounding box center is at this map's viewport center. // This makes the pasted objects appear at the center of the viewport. QRectF paste_extent = paste_map.calculateExtent(true, false, nullptr); auto offset = main_view->center() - paste_extent.center(); MapPart* part = paste_map.getCurrentPart(); for (int i = 0; i < part->getNumObjects(); ++i) part->getObject(i)->move(offset); // Import pasted map. Do not blindly import all colors. map->importMap(&paste_map, Map::MinimalObjectImport, window); // Show message window->showStatusBarMessage(tr("Pasted %n object(s)", nullptr, paste_map.getNumObjects()), 2000); } void MapEditorController::clearUndoRedoHistory() { map->undoManager().clear(); map->setOtherDirty(); } void MapEditorController::spotColorPresenceChanged(bool has_spot_colors) { if (overprinting_simulation_act) { if (has_spot_colors) { overprinting_simulation_act->setEnabled(true); } else { overprinting_simulation_act->setChecked(false); overprinting_simulation_act->setEnabled(false); } } } void MapEditorController::showGrid() { main_view->setGridVisible(show_grid_act->isChecked()); } void MapEditorController::configureGrid() { ConfigureGridDialog dialog(window, *map, show_grid_act->isChecked()); dialog.setWindowModality(Qt::WindowModal); if (dialog.exec() == QDialog::Accepted) { map->setGrid(dialog.resultGrid()); if (dialog.gridVisible() != show_grid_act->isChecked()) show_grid_act->trigger(); } } void MapEditorController::pan() { setTool(new PanTool(this, pan_act)); } void MapEditorController::moveToGpsPos() { if (!gps_display->hasValidPosition()) return; auto cur_gps_pos = gps_display->getLatestGPSCoord(); main_view->setCenter({ cur_gps_pos.x(), cur_gps_pos.y() }); } void MapEditorController::zoomIn() { main_view->zoomSteps(1); } void MapEditorController::zoomOut() { main_view->zoomSteps(-1); } void MapEditorController::setCustomZoomFactorClicked() { bool ok; double factor = QInputDialog::getDouble(window, tr("Set custom zoom factor"), tr("Zoom factor:"), main_view->getZoom(), MapView::zoom_out_limit, MapView::zoom_in_limit, 3, &ok); if (!ok || factor == main_view->getZoom()) return; main_view->setZoom(factor); } void MapEditorController::hatchAreas(bool checked) { map->setAreaHatchingEnabled(checked); // Update all areas map->applyOnMatchingObjects(&Object::forceUpdate, ObjectOp::ContainsSymbolType{Symbol::Area}); } void MapEditorController::baselineView(bool checked) { map->setBaselineViewEnabled(checked); map->updateAllObjects(); } void MapEditorController::hideAllTemplates(bool checked) { hide_all_templates_act->setChecked(checked); main_view->setAllTemplatesHidden(checked); } void MapEditorController::overprintingSimulation(bool checked) { if (checked && !map->hasSpotColors()) { checked = false; } main_view->setOverprintingSimulationEnabled(checked); } void MapEditorController::coordsDisplayChanged() { if (geographic_coordinates_dms_act->isChecked()) map_widget->setCoordsDisplay(MapWidget::GEOGRAPHIC_COORDS_DMS); else if (geographic_coordinates_act->isChecked()) map_widget->setCoordsDisplay(MapWidget::GEOGRAPHIC_COORDS); else if (projected_coordinates_act->isChecked()) map_widget->setCoordsDisplay(MapWidget::PROJECTED_COORDS); else map_widget->setCoordsDisplay(MapWidget::MAP_COORDS); } void MapEditorController::copyDisplayedCoords() { QApplication::clipboard()->setText(statusbar_cursorpos_label->text()); } void MapEditorController::projectionChanged() { const Georeferencing& geo(map->getGeoreferencing()); projected_coordinates_act->setText(geo.getProjectedCoordinatesName()); bool enable_geographic = !geo.isLocal(); geographic_coordinates_act->setEnabled(enable_geographic); geographic_coordinates_dms_act->setEnabled(enable_geographic); if (!enable_geographic && (map_widget->getCoordsDisplay() == MapWidget::GEOGRAPHIC_COORDS || map_widget->getCoordsDisplay() == MapWidget::GEOGRAPHIC_COORDS_DMS)) { map_widget->setCoordsDisplay(MapWidget::MAP_COORDS); map_coordinates_act->setChecked(true); } } void MapEditorController::showSymbolWindow(bool show) { if (!symbol_dock_widget) { symbol_dock_widget = new EditorDockWidget(tr("Symbols"), symbol_window_act, this, window); createSymbolWidget(); symbol_dock_widget->setObjectName(QString::fromLatin1("Symbol dock widget")); if (!window->restoreDockWidget(symbol_dock_widget)) window->addDockWidget(Qt::RightDockWidgetArea, symbol_dock_widget, Qt::Vertical); } symbol_dock_widget->setVisible(show); } void MapEditorController::createColorWindow() { Q_ASSERT(!color_dock_widget); color_dock_widget = new EditorDockWidget(tr("Colors"), color_window_act, this, window); color_dock_widget->setWidget(new ColorListWidget(map, window, color_dock_widget)); color_dock_widget->widget()->setEnabled(!editing_in_progress); color_dock_widget->setObjectName(QString::fromLatin1("Color dock widget")); if (!window->restoreDockWidget(color_dock_widget)) window->addDockWidget(Qt::LeftDockWidgetArea, color_dock_widget, Qt::Vertical); color_dock_widget->setVisible(false); } void MapEditorController::showColorWindow(bool show) { if (!color_dock_widget) createColorWindow(); color_dock_widget->setVisible(show); } void MapEditorController::symbolSetIdClicked() { bool ok; auto id = QInputDialog::getText(window, tr("Symbol set ID"), tr("Edit the symbol set ID:"), QLineEdit::Normal, map->symbolSetId(), &ok); if (ok) map->setSymbolSetId(id); } void MapEditorController::loadSymbolsFromClicked() { ReplaceSymbolSetDialog::showDialog(window, *map); } void MapEditorController::loadCrtClicked() { ReplaceSymbolSetDialog::showDialogForCRT(window, *map, *map); } void MapEditorController::loadColorsFromClicked() { // TODO } void MapEditorController::scaleAllSymbolsClicked() { bool ok; double percent = QInputDialog::getDouble(window, tr("Scale all symbols"), tr("Scale to percentage:"), 100, 0, 999999, 6, &ok); if (!ok || percent == 100) return; map->scaleAllSymbols(percent / 100.0); } void MapEditorController::scaleMapClicked() { ScaleMapDialog dialog(window, map); dialog.setWindowModality(Qt::WindowModal); dialog.exec(); } void MapEditorController::rotateMapClicked() { RotateMapDialog dialog(window, map); dialog.setWindowModality(Qt::WindowModal); dialog.exec(); } void MapEditorController::mapNotesClicked() { QDialog dialog(window, Qt::WindowSystemMenuHint | Qt::WindowTitleHint); dialog.setWindowTitle(tr("Map notes")); dialog.setWindowModality(Qt::WindowModal); auto text_edit = new QTextEdit(); text_edit->setPlainText(map->getMapNotes()); QPushButton* cancel_button = new QPushButton(tr("Cancel")); QPushButton* ok_button = new QPushButton(QIcon(QString::fromLatin1(":/images/arrow-right.png")), tr("OK")); ok_button->setDefault(true); auto buttons_layout = new QHBoxLayout(); buttons_layout->addWidget(cancel_button); buttons_layout->addStretch(1); buttons_layout->addWidget(ok_button); auto layout = new QVBoxLayout(); layout->addWidget(text_edit); layout->addLayout(buttons_layout); dialog.setLayout(layout); connect(cancel_button, &QAbstractButton::clicked, &dialog, &QDialog::reject); connect(ok_button, &QAbstractButton::clicked, &dialog, &QDialog::accept); if (dialog.exec() == QDialog::Accepted) { if (text_edit->toPlainText() != map->getMapNotes()) { map->setMapNotes(text_edit->toPlainText()); map->setHasUnsavedChanges(true); } } } void MapEditorController::createTemplateWindow() { Q_ASSERT(!template_dock_widget); template_list_widget = new TemplateListWidget(map, main_view, this); connect(hide_all_templates_act, &QAction::toggled, template_list_widget, &TemplateListWidget::setAllTemplatesHidden); if (isInMobileMode()) { template_dock_widget = createDockWidgetSubstitute(window, template_list_widget); connect(template_list_widget, &TemplateListWidget::closeClicked, this, [this]() { showTemplateWindow(false); }); } else { auto dock_widget = new EditorDockWidget(tr("Templates"), template_window_act, this, window); dock_widget->setWidget(template_list_widget); dock_widget->setObjectName(QString::fromLatin1("Templates dock widget")); if (!window->restoreDockWidget(dock_widget)) window->addDockWidget(Qt::RightDockWidgetArea, dock_widget, Qt::Vertical); dock_widget->setVisible(false); template_dock_widget = dock_widget; } } void MapEditorController::showTemplateWindow(bool show) { if (!template_dock_widget) createTemplateWindow(); template_window_act->setChecked(show); template_dock_widget->setVisible(show); } void MapEditorController::openTemplateClicked() { auto new_template = TemplateListWidget::showOpenTemplateDialog(window, this); if (new_template) { hideAllTemplates(false); showTemplateWindow(true); // FIXME: this should be done through the core map, not through the UI template_list_widget->addTemplateAt(new_template.release(), -1); } } void MapEditorController::reopenTemplateClicked() { hideAllTemplates(false); QString map_directory = window->currentPath(); if (!map_directory.isEmpty()) map_directory = QFileInfo(map_directory).canonicalPath(); auto dialog = new ReopenTemplateDialog(window, map, map_directory); dialog->setWindowModality(Qt::WindowModal); dialog->exec(); delete dialog; } void MapEditorController::templateAvailabilityChanged() { // Nothing } void MapEditorController::closedTemplateAvailabilityChanged() { if (reopen_template_act) reopen_template_act->setEnabled(map->getNumClosedTemplates() > 0); } void MapEditorController::createTagEditor() { Q_ASSERT(!tags_dock_widget); auto tags_widget = new TagsWidget(map, main_view, this); tags_dock_widget = new EditorDockWidget(tr("Tag Editor"), tags_window_act, this, window); tags_dock_widget->setWidget(tags_widget); tags_dock_widget->setObjectName(QString::fromLatin1("Tag editor dock widget")); if (!window->restoreDockWidget(tags_dock_widget)) window->addDockWidget(Qt::RightDockWidgetArea, tags_dock_widget, Qt::Vertical); tags_dock_widget->setVisible(false); } void MapEditorController::showTagsWindow(bool show) { if (!tags_dock_widget) createTagEditor(); tags_window_act->setChecked(show); tags_dock_widget->setVisible(show); } void MapEditorController::editGeoreferencing() { if (georeferencing_dialog.isNull()) { auto dialog = new GeoreferencingDialog(this); georeferencing_dialog.reset(dialog); connect(dialog, &QDialog::finished, this, &MapEditorController::georeferencingDialogFinished); } georeferencing_dialog->exec(); } void MapEditorController::georeferencingDialogFinished() { georeferencing_dialog.take()->deleteLater(); map->updateAllMapWidgets(); bool gps_display_possible = map->getGeoreferencing().isValid() && ! map->getGeoreferencing().isLocal(); if (!gps_display_possible) { gps_display_action->setChecked(false); if (gps_display) enableGPSDisplay(false); } gps_display_action->setEnabled(gps_display_possible); } void MapEditorController::selectedSymbolsChanged() { Symbol* symbol = symbol_widget->getSingleSelectedSymbol(); if (mobile_mode) { auto symbol_button = bottom_action_bar->getButtonForAction(mobile_symbol_selector_action); // (Re-)create the mobile_symbol_selector_action icon QSize icon_size = bottom_action_bar->getIconSize(2, 2); QPixmap pixmap(icon_size); pixmap.fill(Qt::white); if (symbol_widget->selectedSymbolsCount() != 1) { QFont font(window->font()); font.setPixelSize(icon_size.height() / 5); QPainter painter(&pixmap); painter.setFont(font); QString text = (symbol_widget->selectedSymbolsCount() == 0) ? //: Keep it short. Should not be much longer per line than the longest word in the original. tr("No\nsymbol\nselected") : //: Keep it short. Should not be much longer per line than the longest word in the original. tr("Multiple\nsymbols\nselected"); painter.drawText(pixmap.rect(), Qt::AlignCenter, text); symbol_button->setMenu(nullptr); } else //if (symbol_widget->getNumSelectedSymbols() == 1) { auto image = symbol->createIcon(*map, qMin(icon_size.width(), icon_size.height())); if (symbol->isHidden() || symbol->isProtected()) { QPainter p(&image); if (symbol->isHidden()) HiddenSymbolDecorator(icon_size.width()).draw(p); if (symbol->isProtected()) ProtectedSymbolDecorator(icon_size.width()).draw(p); } pixmap = QPixmap::fromImage(image); symbol_button->setMenu(mobile_symbol_button_menu); const auto actions = mobile_symbol_button_menu->actions(); int i = 0; actions[i]->setText(symbol->getNumberAsString() + QLatin1Char(' ') + symbol->getPlainTextName()); actions[++i]->setVisible(!symbol->getDescription().isEmpty()); ++i; // separator actions[++i]->setChecked(symbol->isHidden()); actions[++i]->setChecked(symbol->isProtected()); } mobile_symbol_selector_action->setIcon(QIcon(pixmap)); } // FIXME: Postpone switch of active symbol while editing is progress if (active_symbol != symbol) { active_symbol = symbol; if (symbol && !symbol->isHidden() && !symbol->isProtected() && current_tool) { // Auto-switch to a draw tool when selecting a symbol under certain conditions if (current_tool->toolType() == MapEditorTool::Pan || ((current_tool->toolType() == MapEditorTool::EditLine || current_tool->toolType() == MapEditorTool::EditPoint) && map->getNumSelectedObjects() == 0)) { current_tool->switchToDefaultDrawTool(active_symbol); } } if (symbol) window->showStatusBarMessage(symbol->getNumberAsString() + QLatin1Char(' ') + symbol->getPlainTextName(), 1000); } // Even when the symbol (pointer) hasn't changed, // the symbol's visibility may constitute a change. emit activeSymbolChanged(active_symbol); updateSymbolDependentActions(); updateSymbolAndObjectDependentActions(); } void MapEditorController::objectSelectionChanged() { if (mode != MapEditor) return; updateObjectDependentActions(); // Automatic symbol selection of selected objects if (symbol_widget && !editing_in_progress) { bool uniform_symbol_selected = true; const Symbol* uniform_symbol = nullptr; for (const auto object : map->selectedObjects()) { const auto symbol = object->getSymbol(); if (!uniform_symbol) { uniform_symbol = symbol; } else if (uniform_symbol != symbol) { uniform_symbol_selected = false; break; } } if (uniform_symbol_selected && Settings::getInstance().getSettingCached(Settings::MapEditor_ChangeSymbolWhenSelecting).toBool()) symbol_widget->selectSingleSymbol(uniform_symbol); } updateSymbolAndObjectDependentActions(); } void MapEditorController::updateSymbolDependentActions() { const Symbol* symbol = activeSymbol(); const Symbol::Type type = (symbol && !editing_in_progress) ? symbol->getType() : Symbol::NoSymbol; updateDrawPointGPSAvailability(); draw_point_act->setEnabled(type == Symbol::Point && !symbol->isHidden()); draw_point_act->setStatusTip(tr("Place point objects on the map.") + (draw_point_act->isEnabled() ? QString{} : QString(QLatin1Char(' ') + tr("Select a point symbol to be able to use this tool.")))); draw_path_act->setEnabled((type == Symbol::Line || type == Symbol::Area || type == Symbol::Combined) && !symbol->isHidden()); draw_path_act->setStatusTip(tr("Draw polygonal and curved lines.") + (draw_path_act->isEnabled() ? QString{} : QString(QLatin1Char(' ') + tr("Select a line, area or combined symbol to be able to use this tool.")))); draw_circle_act->setEnabled(draw_path_act->isEnabled()); draw_circle_act->setStatusTip(tr("Draw circles and ellipses.") + (draw_circle_act->isEnabled() ? QString{} : QString(QLatin1Char(' ') + tr("Select a line, area or combined symbol to be able to use this tool.")))); draw_rectangle_act->setEnabled(draw_path_act->isEnabled()); draw_rectangle_act->setStatusTip(tr("Draw rectangles.") + (draw_rectangle_act->isEnabled() ? QString{} : QString(QLatin1Char(' ') + tr("Select a line, area or combined symbol to be able to use this tool.")))); draw_freehand_act->setEnabled(draw_path_act->isEnabled()); draw_freehand_act->setStatusTip(tr("Draw paths free-handedly.") + (draw_freehand_act->isEnabled() ? QString{} : QString(QLatin1Char(' ') + tr("Select a line, area or combined symbol to be able to use this tool.")))); draw_fill_act->setEnabled(draw_path_act->isEnabled()); draw_fill_act->setStatusTip(tr("Fill bounded areas.") + (draw_fill_act->isEnabled() ? QString{} : QString(QLatin1Char(' ') + tr("Select a line, area or combined symbol to be able to use this tool.")))); draw_text_act->setEnabled(type == Symbol::Text && !symbol->isHidden()); draw_text_act->setStatusTip(tr("Write text on the map.") + (draw_text_act->isEnabled() ? QString{} : QString(QLatin1Char(' ') + tr("Select a text symbol to be able to use this tool.")))); } void MapEditorController::updateObjectDependentActions() { bool have_multiple_parts = map->getNumParts() > 1; bool have_selection = map->getNumSelectedObjects() > 0 && !editing_in_progress; bool single_object_selected = map->getNumSelectedObjects() == 1 && !editing_in_progress; bool have_line = false; bool have_area = false; bool have_area_with_holes = false; bool have_rotatable_pattern = false; bool have_rotatable_point = false; int num_selected_paths = 0; bool first_selected_is_path = have_selection && map->getFirstSelectedObject()->getType() == Object::Path; bool uniform_symbol_selected = true; const Symbol* uniform_symbol = nullptr; const Symbol* first_selected_symbol= have_selection ? map->getFirstSelectedObject()->getSymbol() : nullptr; std::vector< bool > symbols_in_selection(map->getNumSymbols(), false); if (!editing_in_progress) { for (const auto object : map->selectedObjects()) { const auto symbol = object->getSymbol(); int symbol_index = map->findSymbolIndex(symbol); if (symbol_index >= 0 && symbol_index < (int)symbols_in_selection.size()) symbols_in_selection[symbol_index] = true; if (uniform_symbol_selected) { if (!uniform_symbol) { uniform_symbol = symbol; } else if (uniform_symbol != symbol) { uniform_symbol = nullptr; uniform_symbol_selected = false; } } if (symbol->getType() == Symbol::Point) { have_rotatable_point |= symbol->asPoint()->isRotatable(); } else if (Symbol::areTypesCompatible(symbol->getType(), Symbol::Area)) { ++num_selected_paths; if (symbol->getType() == Symbol::Area) { have_rotatable_pattern |= symbol->asArea()->hasRotatableFillPattern(); } int const contained_types = symbol->getContainedTypes(); if (contained_types & Symbol::Line) { have_line = true; } if (contained_types & Symbol::Area) { have_area = true; have_area_with_holes |= object->asPath()->parts().size() > 1; } } } if (have_area && !have_rotatable_pattern) { map->determineSymbolUseClosure(symbols_in_selection); for (std::size_t i = 0, end = symbols_in_selection.size(); i < end; ++i) { if (symbols_in_selection[i]) { Symbol* symbol = map->getSymbol(i); if (symbol->getType() == Symbol::Area) { have_rotatable_pattern = symbol->asArea()->hasRotatableFillPattern(); break; } } } } } // have_selection cut_act->setEnabled(have_selection); copy_act->setEnabled(have_selection); delete_act->setEnabled(have_selection); delete_act->setStatusTip(tr("Deletes the selected objects.") + (delete_act->isEnabled() ? QString{} : QString(QLatin1Char(' ') + tr("Select at least one object to activate this tool.")))); duplicate_act->setEnabled(have_selection); duplicate_act->setStatusTip(tr("Duplicate the selected objects.") + (duplicate_act->isEnabled() ? QString{} : QString(QLatin1Char(' ') + tr("Select at least one object to activate this tool.")))); rotate_act->setEnabled(have_selection); rotate_act->setStatusTip(tr("Rotate the selected objects.") + (rotate_act->isEnabled() ? QString{} : QString(QLatin1Char(' ') + tr("Select at least one object to activate this tool.")))); scale_act->setEnabled(have_selection); scale_act->setStatusTip(tr("Scale the selected objects.") + (scale_act->isEnabled() ? QString{} : QString(QLatin1Char(' ') + tr("Select at least one object to activate this tool.")))); mappart_move_menu->setEnabled(have_selection && have_multiple_parts); // have_rotatable_pattern || have_rotatable_point rotate_pattern_act->setEnabled(have_rotatable_pattern || have_rotatable_point); rotate_pattern_act->setStatusTip(tr("Set the direction of area fill patterns or point objects.") + (rotate_pattern_act->isEnabled() ? QString{} : QString(QLatin1Char(' ') + tr("Select an area object with rotatable fill pattern or a rotatable point object to activate this tool.")))); // have_line switch_dashes_act->setEnabled(have_line); switch_dashes_act->setStatusTip(tr("Switch the direction of symbols on line objects.") + (switch_dashes_act->isEnabled() ? QString{} : QString(QLatin1Char(' ') + tr("Select at least one line object to activate this tool.")))); connect_paths_act->setEnabled(have_line); connect_paths_act->setStatusTip(tr("Connect endpoints of paths which are close together.") + (connect_paths_act->isEnabled() ? QString{} : QString(QLatin1Char(' ') + tr("Select at least one line object to activate this tool.")))); // have_are || have_line cut_tool_act->setEnabled(have_area || have_line); cut_tool_act->setStatusTip(tr("Cut the selected objects into smaller parts.") + (cut_tool_act->isEnabled() ? QString{} : QString(QLatin1Char(' ') + tr("Select at least one line or area object to activate this tool.")))); convert_to_curves_act->setEnabled(have_area || have_line); convert_to_curves_act->setStatusTip(tr("Turn paths made of straight segments into smooth bezier splines.") + (convert_to_curves_act->isEnabled() ? QString{} : QString(QLatin1Char(' ') + tr("Select a path object to activate this tool.")))); simplify_path_act->setEnabled(have_area || have_line); simplify_path_act->setStatusTip(tr("Reduce the number of points in path objects while trying to retain their shape.") + (simplify_path_act->isEnabled() ? QString{} : QString(QLatin1Char(' ') + tr("Select a path object to activate this tool.")))); // cut_hole_enabled const bool cut_hole_enabled = single_object_selected && have_area; cut_hole_act->setEnabled(cut_hole_enabled); cut_hole_act->setStatusTip(tr("Cut a hole into the selected area object.") + (cut_hole_act->isEnabled() ? QString{} : QString(QLatin1Char(' ') + tr("Select a single area object to activate this tool.")))); cut_hole_circle_act->setEnabled(cut_hole_enabled); cut_hole_circle_act->setStatusTip(cut_hole_act->statusTip()); cut_hole_rectangle_act->setEnabled(cut_hole_enabled); cut_hole_rectangle_act->setStatusTip(cut_hole_act->statusTip()); cut_hole_menu->setEnabled(cut_hole_enabled); // boolean_prerequisite [&& x] bool const boolean_prerequisite = first_selected_is_path && num_selected_paths >= 2; QString const extra_status_tip = QLatin1Char(' ') + ( boolean_prerequisite ? tr("Resulting symbol: %1 %2.").arg(first_selected_symbol->getNumberAsString(), first_selected_symbol->getPlainTextName()) : tr("Select at least two area or path objects activate this tool.") ); boolean_union_act->setEnabled(boolean_prerequisite); boolean_union_act->setStatusTip(tr("Unify overlapping objects.") + extra_status_tip); boolean_intersection_act->setEnabled(boolean_prerequisite); boolean_intersection_act->setStatusTip(tr("Remove all parts which are not overlaps with the first selected object.") + extra_status_tip); boolean_difference_act->setEnabled(boolean_prerequisite); boolean_difference_act->setStatusTip(tr("Remove overlapped parts of the first selected object.") + (boolean_prerequisite ? QString{} : extra_status_tip)); boolean_xor_act->setEnabled(boolean_prerequisite); boolean_xor_act->setStatusTip(tr("Remove all parts which overlap the first selected object.") + extra_status_tip); // special boolean_merge_holes_act->setEnabled(single_object_selected && have_area_with_holes); boolean_merge_holes_act->setStatusTip(tr("Merge area holes together, or merge holes with the object boundary to cut out this part.") + (boolean_merge_holes_act->isEnabled() ? QString{} : QString(QLatin1Char(' ') + tr("Select one area object with holes to activate this tool.")))); // cutout_enabled bool const cutout_enabled = single_object_selected && (have_area || have_line) && !have_area_with_holes && (*(map->selectedObjectsBegin()))->asPath()->parts().front().isClosed(); cutout_physical_act->setEnabled(cutout_enabled); cutout_physical_act->setStatusTip(tr("Create a cutout of some objects or the whole map.") + (cutout_physical_act->isEnabled() ? QString{} : QString(QLatin1Char(' ') + tr("Select a closed path object as cutout shape to activate this tool.")))); cutaway_physical_act->setEnabled(cutout_enabled); cutaway_physical_act->setStatusTip(tr("Cut away some objects or everything in a limited area.") + (cutaway_physical_act->isEnabled() ? QString{} : QString(QLatin1Char(' ') + tr("Select a closed path object as cutout shape to activate this tool.")))); } void MapEditorController::updateSymbolAndObjectDependentActions() { const Symbol* single_symbol = activeSymbol(); bool single_symbol_compatible = false; bool single_symbol_different = false; if (!editing_in_progress) { map->getSelectionToSymbolCompatibility(single_symbol, single_symbol_compatible, single_symbol_different); } switch_symbol_act->setEnabled(single_symbol_compatible && single_symbol_different); switch_symbol_act->setStatusTip(tr("Switches the symbol of the selected objects to the selected symbol.") + (switch_symbol_act->isEnabled() ? QString{} : QString(QLatin1Char(' ') + tr("Select at least one object and a fitting, different symbol to activate this tool.")))); fill_border_act->setEnabled(single_symbol_compatible && single_symbol_different); fill_border_act->setStatusTip(tr("Fill the selected lines or create a border for the selected areas.") + (fill_border_act->isEnabled() ? QString{} : QString(QLatin1Char(' ') + tr("Select at least one object and a fitting, different symbol to activate this tool.")))); distribute_points_act->setEnabled(single_symbol && single_symbol->getType() == Symbol::Point && containsPathObject(map->selectedObjects())); distribute_points_act->setStatusTip(tr("Places evenly spaced point objects along an existing path object") + (distribute_points_act->isEnabled() ? QString{} : QString(QLatin1Char(' ') + tr("Select at least one path object and a single point symbol to activate this tool.")))); } void MapEditorController::undoStepAvailabilityChanged() { if (mode != MapEditor) return; undo_act->setEnabled(map->undoManager().canUndo()); redo_act->setEnabled(map->undoManager().canRedo()); clear_undo_redo_history_act->setEnabled(undo_act->isEnabled() || redo_act->isEnabled()); } void MapEditorController::clipboardChanged(QClipboard::Mode mode) { if (mode == QClipboard::Clipboard) updatePasteAvailability(); } void MapEditorController::updatePasteAvailability() { if (paste_act) { paste_act->setEnabled( QApplication::clipboard()->mimeData() && QApplication::clipboard()->mimeData()->hasFormat(MimeType::OpenOrienteeringObjects()) && !editing_in_progress); } } void MapEditorController::showWholeMap() { QRectF map_extent = map->calculateExtent(true, !main_view->areAllTemplatesHidden(), main_view); map_widget->adjustViewToRect(map_extent, MapWidget::ContinuousZoom); } void MapEditorController::editToolClicked() { setEditTool(); } void MapEditorController::editLineToolClicked() { if (!current_tool || current_tool->toolType() != MapEditorTool::EditLine) setTool(new EditLineTool(this, edit_line_tool_act)); } void MapEditorController::drawPointClicked() { setTool(new DrawPointTool(this, draw_point_act)); } void MapEditorController::drawPathClicked() { setTool(new DrawPathTool(this, draw_path_act, false, true)); } void MapEditorController::drawCircleClicked() { setTool(new DrawCircleTool(this, draw_circle_act, false)); } void MapEditorController::drawRectangleClicked() { setTool(new DrawRectangleTool(this, draw_rectangle_act, false)); } void MapEditorController::drawFreehandClicked() { setTool(new DrawFreehandTool(this, draw_freehand_act, false)); } void MapEditorController::drawFillClicked() { setTool(new FillTool(this, draw_fill_act)); } void MapEditorController::drawTextClicked() { setTool(new DrawTextTool(this, draw_text_act)); } void MapEditorController::deleteClicked() { if (editing_in_progress) return; map->deleteSelectedObjects(); } void MapEditorController::duplicateClicked() { Q_ASSERT(!map->selectedObjects().empty()); std::vector new_objects; new_objects.reserve(map->getNumSelectedObjects()); for (const auto object : map->selectedObjects()) { Object* duplicate = object->duplicate(); map->addObject(duplicate); new_objects.push_back(duplicate); } auto undo_step = new DeleteObjectsUndoStep(map); MapPart* part = map->getCurrentPart(); map->clearObjectSelection(false); for (const auto object : new_objects) { undo_step->addObject(part->findObjectIndex(object)); map->addObjectToSelection(object, object == new_objects.back()); } map->setObjectsDirty(); map->push(undo_step); setEditTool(); window->showStatusBarMessage(tr("Duplicated %n object(s)", nullptr, int(new_objects.size())), 2000); } void MapEditorController::switchSymbolClicked() { SwitchSymbolUndoStep* switch_step = nullptr; ReplaceObjectsUndoStep* replace_step = nullptr; AddObjectsUndoStep* add_step = nullptr; DeleteObjectsUndoStep* delete_step = nullptr; std::vector old_objects; std::vector new_objects; MapPart* part = map->getCurrentPart(); Symbol* symbol = activeSymbol(); bool close_paths = false, split_up = false; Symbol::Type contained_types = symbol->getContainedTypes(); if (contained_types & Symbol::Area && !(contained_types & Symbol::Line)) close_paths = true; else if (contained_types & Symbol::Line && !(contained_types & Symbol::Area)) split_up = true; if (close_paths) { replace_step = new ReplaceObjectsUndoStep(map); } else if (split_up) { add_step = new AddObjectsUndoStep(map); delete_step = new DeleteObjectsUndoStep(map); } else { switch_step = new SwitchSymbolUndoStep(map); } for (const auto object : map->selectedObjects()) { if (close_paths) { replace_step->addObject(part->findObjectIndex(object), object->duplicate()); } else if (split_up) { add_step->addObject(part->findObjectIndex(object), object); old_objects.push_back(object); } else { switch_step->addObject(part->findObjectIndex(object), object->getSymbol()); } if (!split_up) { object->setSymbol(symbol, true); } if (object->getType() == Object::Path) { PathObject* path_object = object->asPath(); if (close_paths) { path_object->closeAllParts(); } else if (split_up) { for (const auto& part : path_object->parts()) { auto new_object = new PathObject { part }; new_object->setSymbol(symbol, true); new_objects.push_back(new_object); } Q_ASSERT(!new_objects.empty()); if (!new_objects.empty()) { new_objects.front()->setTags(path_object->tags()); } } } object->update(); } if (split_up) { map->clearObjectSelection(false); for (auto object : old_objects) { map->deleteObject(object, true); } for (auto object : new_objects) { map->addObject(object); map->addObjectToSelection(object, false); } map->emitSelectionChanged(); // Do not merge this loop into the upper one; // theoretically undo step indices could be wrong this way. for (auto object : new_objects) { delete_step->addObject(part->findObjectIndex(object)); } } map->setObjectsDirty(); if (close_paths) { map->push(replace_step); } else if (split_up) { auto combined_step = new CombinedUndoStep(map); combined_step->push(add_step); combined_step->push(delete_step); map->push(combined_step); } else { map->push(switch_step); } map->emitSelectionEdited(); // Also emit selectionChanged, as symbols of selected objects changed map->emitSelectionChanged(); } void MapEditorController::fillBorderClicked() { Symbol* symbol = activeSymbol(); std::vector new_objects; new_objects.reserve(map->getNumSelectedObjects()); bool close_paths = false, split_up = false; Symbol::Type contained_types = symbol->getContainedTypes(); if (contained_types & Symbol::Area && !(contained_types & Symbol::Line)) close_paths = true; else if (contained_types & Symbol::Line && !(contained_types & Symbol::Area)) split_up = true; auto undo_step = new DeleteObjectsUndoStep(map); MapPart* part = map->getCurrentPart(); for (const auto object : map->selectedObjects()) { if (split_up && object->getType() == Object::Path) { PathObject* path_object = object->asPath(); for (const auto& part : path_object->parts()) { auto new_object = new PathObject { part }; new_object->setSymbol(symbol, true); map->addObject(new_object); new_objects.push_back(new_object); } } else { Object* duplicate = object->duplicate(); duplicate->setSymbol(symbol, true); if (close_paths && duplicate->getType() == Object::Path) { PathObject* path_object = duplicate->asPath(); path_object->closeAllParts(); } map->addObject(duplicate); new_objects.push_back(duplicate); } } map->setObjectsDirty(); map->clearObjectSelection(false); for (int i = 0; i < (int)new_objects.size(); ++i) { map->addObjectToSelection(new_objects[i], i == (int)new_objects.size() - 1); undo_step->addObject(part->findObjectIndex(new_objects[i])); } map->push(undo_step); } void MapEditorController::selectObjectsClicked(bool select_exclusively) { bool selection_changed = false; if (select_exclusively) { if (map->getNumSelectedObjects() > 0) selection_changed = true; map->clearObjectSelection(false); } bool object_selected = false; MapPart* part = map->getCurrentPart(); for (int i = 0, size = part->getNumObjects(); i < size; ++i) { Object* object = part->getObject(i); if (symbol_widget->isSymbolSelected(object->getSymbol()) && !(!select_exclusively && map->isObjectSelected(object))) { map->addObjectToSelection(object, false); object_selected = true; } } selection_changed |= object_selected; if (selection_changed) map->emitSelectionChanged(); if (object_selected) { if (current_tool && current_tool->isDrawTool()) setEditTool(); } else { QMessageBox::warning(window, tr("Object selection"), tr("No objects were selected because there are no objects with the selected symbols.")); } } void MapEditorController::deselectObjectsClicked() { bool selection_changed = false; MapPart* part = map->getCurrentPart(); for (int i = 0, size = part->getNumObjects(); i < size; ++i) { Object* object = part->getObject(i); if (symbol_widget->isSymbolSelected(object->getSymbol()) && map->isObjectSelected(object)) { map->removeObjectFromSelection(object, false); selection_changed = true; } } if (selection_changed) { map->emitSelectionChanged(); if (current_tool && current_tool->isDrawTool()) setEditTool(); } } void MapEditorController::selectAll() { auto num_selected_objects = map->getNumSelectedObjects(); map->clearObjectSelection(false); map->getCurrentPart()->applyOnAllObjects([this](Object* object) { map->addObjectToSelection(object, false); }); if (map->getNumSelectedObjects() != num_selected_objects) { map->emitSelectionChanged(); if (current_tool && current_tool->isDrawTool()) setEditTool(); } } void MapEditorController::selectNothing() { if (map->getNumSelectedObjects() > 0) map->clearObjectSelection(true); } void MapEditorController::invertSelection() { auto selection = Map::ObjectSelection{ map->selectedObjects() }; map->clearObjectSelection(false); map->getCurrentPart()->applyOnAllObjects([this, &selection](Object* object) { if (selection.find(object) == end(selection)) map->addObjectToSelection(object, false); }); if (map->getCurrentPart()->getNumObjects() > 0) { map->emitSelectionChanged(); if (current_tool && current_tool->isDrawTool()) setEditTool(); } } void MapEditorController::selectByCurrentSymbols() { selectObjectsClicked(true); } void MapEditorController::switchDashesClicked() { auto undo_step = new SwitchDashesUndoStep(map); MapPart* part = map->getCurrentPart(); for (const auto object : map->selectedObjects()) { if (object->getSymbol()->getContainedTypes() & Symbol::Line) { PathObject* path = reinterpret_cast(object); path->reverse(); object->update(); undo_step->addObject(part->findObjectIndex(object)); } } map->setObjectsDirty(); map->push(undo_step); map->emitSelectionEdited(); } /// \todo Review use of container API float connectPaths_FindClosestEnd(const std::vector& objects, PathObject* a, int a_index, PathPartVector::size_type path_part_a, bool path_part_a_begin, PathObject** out_b, int* out_b_index, int* out_path_part_b, bool* out_path_part_b_begin) { float best_dist_sq = std::numeric_limits::max(); for (int i = a_index; i < (int)objects.size(); ++i) { PathObject* b = reinterpret_cast(objects[i]); if (b->getSymbol() != a->getSymbol()) continue; auto num_parts = b->parts().size(); for (PathPartVector::size_type path_part_b = (a == b) ? path_part_a : 0; path_part_b < num_parts; ++path_part_b) { PathPart& part = b->parts()[path_part_b]; if (!part.isClosed()) { for (int begin = 0; begin < 2; ++begin) { bool path_part_b_begin = (begin == 0); if (a == b && path_part_a == path_part_b && path_part_a_begin == path_part_b_begin) continue; MapCoord& coord_a = a->getCoordinate(path_part_a_begin ? a->parts()[path_part_a].first_index : (a->parts()[path_part_a].last_index)); MapCoord& coord_b = b->getCoordinate(path_part_b_begin ? b->parts()[path_part_b].first_index : (b->parts()[path_part_b].last_index)); float distance_sq = coord_a.distanceSquaredTo(coord_b); if (distance_sq < best_dist_sq) { best_dist_sq = distance_sq; *out_b = b; *out_b_index = i; *out_path_part_b = path_part_b; *out_path_part_b_begin = path_part_b_begin; if (distance_sq == 0) return 0; } } } } } return best_dist_sq; } void MapEditorController::connectPathsClicked() { std::vector objects; std::vector undo_objects; std::vector deleted_objects; // Collect all objects in question objects.reserve(map->getNumSelectedObjects()); undo_objects.reserve(map->getNumSelectedObjects()); for (const auto object : map->selectedObjects()) { if (object->getSymbol()->getContainedTypes() & Symbol::Line && object->getType() == Object::Path) { object->update(); objects.push_back(object); undo_objects.push_back(nullptr); } } AddObjectsUndoStep* add_step = nullptr; MapPart* part = map->getCurrentPart(); while (true) { // Find the closest pair of open ends of objects with the same symbol, // which is closer than a threshold PathObject* best_object_a = nullptr; PathObject* best_object_b = nullptr; int best_object_a_index = 0; int best_object_b_index = 0; auto best_part_a = PathPartVector::size_type { 0 }; auto best_part_b = PathPartVector::size_type { 0 }; bool best_part_a_begin = false; bool best_part_b_begin = false; float best_dist_sq = std::numeric_limits::max(); for (int i = 0; i < (int)objects.size(); ++i) { PathObject* a = reinterpret_cast(objects[i]); // Choose connection threshold as maximum of 0.35mm, 1.5 * largest line extent, and 6 pixels // TODO: instead of 6 pixels, use a physical size as soon as screen dpi is in the settings auto close_distance_sq = qMax(0.35, 1.5 * a->getSymbol()->calculateLargestLineExtent()); close_distance_sq = qMax(close_distance_sq, 0.001 * main_view->pixelToLength(6)); close_distance_sq = qPow(close_distance_sq, 2); auto num_parts = a->parts().size(); for (PathPartVector::size_type path_part_a = 0; path_part_a < num_parts; ++path_part_a) { PathPart& part = a->parts()[path_part_a]; if (!part.isClosed()) { PathObject* b; int b_index; int path_part_b; bool path_part_b_begin; float distance_sq; for (int begin = 0; begin < 2; ++begin) { bool path_part_a_begin = (begin == 0); distance_sq = connectPaths_FindClosestEnd(objects, a, i, path_part_a, path_part_a_begin, &b, &b_index, &path_part_b, &path_part_b_begin); if (distance_sq <= close_distance_sq && distance_sq < best_dist_sq) { best_dist_sq = distance_sq; best_object_a = a; best_object_b = b; best_object_a_index = i; best_object_b_index = b_index; best_part_a = path_part_a; best_part_b = path_part_b; best_part_a_begin = path_part_a_begin; best_part_b_begin = path_part_b_begin; } } } } } // Abort if no possible connections found if (best_dist_sq == std::numeric_limits::max()) break; // Create undo objects for a and b if (!undo_objects[best_object_a_index]) undo_objects[best_object_a_index] = best_object_a->duplicate(); if (!undo_objects[best_object_b_index]) undo_objects[best_object_b_index] = best_object_b->duplicate(); // Connect the best parts if (best_part_a_begin && best_part_b_begin) { best_object_b->parts()[best_part_b].reverse(); best_object_a->connectPathParts(best_part_a, best_object_b, best_part_b, true); } else if (best_part_a_begin && !best_part_b_begin) { if (best_object_a == best_object_b && best_part_a == best_part_b) best_object_a->parts()[best_part_a].connectEnds(); else best_object_a->connectPathParts(best_part_a, best_object_b, best_part_b, true); } else if (!best_part_a_begin && best_part_b_begin) { if (best_object_a == best_object_b && best_part_a == best_part_b) best_object_a->parts()[best_part_a].connectEnds(); else best_object_a->connectPathParts(best_part_a, best_object_b, best_part_b, false); } else //if (!best_part_a_begin && !best_part_b_begin) { best_object_b->parts()[best_part_b].reverse(); best_object_a->connectPathParts(best_part_a, best_object_b, best_part_b, false); } if (best_object_a != best_object_b) { // Copy all remaining parts of object b over to a best_object_a->getCoordinate(best_object_a->getCoordinateCount() - 1).setHolePoint(true); for (auto i = PathPartVector::size_type { 0 }; i < best_object_b->parts().size(); ++i) { if (i != best_part_b) best_object_a->appendPathPart(best_object_b->parts()[i]); } // Create an add step for b int b_index_in_part = part->findObjectIndex(best_object_b); deleted_objects.push_back(best_object_b); if (!add_step) add_step = new AddObjectsUndoStep(map); add_step->addObject(b_index_in_part, undo_objects[best_object_b_index]); undo_objects[best_object_b_index] = nullptr; // Delete b from the active list objects.erase(objects.begin() + best_object_b_index); undo_objects.erase(undo_objects.begin() + best_object_b_index); } else { // Delete the part which has been merged with another part if (best_part_a != best_part_b) best_object_a->deletePart(best_part_b); } } // Create undo step? ReplaceObjectsUndoStep* replace_step = nullptr; for (int i = 0; i < (int)undo_objects.size(); ++i) { if (undo_objects[i]) { // The object was changed, update the new version objects[i]->forceUpdate(); /// @todo get rid of force if possible // Add the old version to the undo step if (!replace_step) replace_step = new ReplaceObjectsUndoStep(map); replace_step->addObject(part->findObjectIndex(objects[i]), undo_objects[i]); } } if (add_step) { for (auto object : deleted_objects) { map->removeObjectFromSelection(object, false); map->getCurrentPart()->deleteObject(object, false); } } if (add_step || replace_step) { auto undo_step = new CombinedUndoStep(map); if (replace_step) undo_step->push(replace_step); if (add_step) undo_step->push(add_step); map->push(undo_step); map->setObjectsDirty(); } map->emitSelectionChanged(); map->emitSelectionEdited(); } void MapEditorController::cutClicked() { setTool(new CutTool(this, cut_tool_act)); } void MapEditorController::cutHoleClicked() { setTool(new CutHoleTool(this, cut_hole_act, CutHoleTool::Path)); } void MapEditorController::cutHoleCircleClicked() { setTool(new CutHoleTool(this, cut_hole_circle_act, CutHoleTool::Circle)); } void MapEditorController::cutHoleRectangleClicked() { setTool(new CutHoleTool(this, cut_hole_rectangle_act, CutHoleTool::Rect)); } void MapEditorController::rotateClicked() { setTool(new RotateTool(this, rotate_act)); } void MapEditorController::rotatePatternClicked() { setTool(new RotatePatternTool(this, rotate_pattern_act)); } void MapEditorController::scaleClicked() { setTool(new ScaleTool(this, scale_act)); } void MapEditorController::measureClicked(bool checked) { if (!measure_dock_widget) { measure_dock_widget = new EditorDockWidget(tr("Measure"), measure_act, this, window); measure_dock_widget->toggleViewAction()->setVisible(false); auto measure_widget = new MeasureWidget(map); measure_dock_widget->setWidget(measure_widget); measure_dock_widget->setObjectName(QString::fromLatin1("Measure dock widget")); addFloatingDockWidget(measure_dock_widget); } measure_dock_widget->setVisible(checked); } void MapEditorController::booleanUnionClicked() { if (!BooleanTool(BooleanTool::Union, map).executePerSymbol()) QMessageBox::warning(window, tr("Error"), tr("Unification failed.")); } void MapEditorController::booleanIntersectionClicked() { if (!BooleanTool(BooleanTool::Intersection, map).execute()) QMessageBox::warning(window, tr("Error"), tr("Intersection failed.")); } void MapEditorController::booleanDifferenceClicked() { if (!BooleanTool(BooleanTool::Difference, map).execute()) QMessageBox::warning(window, tr("Error"), tr("Difference failed.")); } void MapEditorController::booleanXOrClicked() { if (!BooleanTool(BooleanTool::XOr, map).execute()) QMessageBox::warning(window, tr("Error"), tr("XOr failed.")); } void MapEditorController::booleanMergeHolesClicked() { if (map->getNumSelectedObjects() != 1) return; if (!BooleanTool(BooleanTool::MergeHoles, map).execute()) QMessageBox::warning(window, tr("Error"), tr("Merging holes failed.")); } void MapEditorController::convertToCurvesClicked() { auto undo_step = new ReplaceObjectsUndoStep(map); MapPart* part = map->getCurrentPart(); for (const auto object : map->selectedObjects()) { if (object->getType() != Object::Path) continue; PathObject* path = object->asPath(); PathObject* undo_duplicate = nullptr; if (path->convertToCurves(&undo_duplicate)) { undo_step->addObject(part->findObjectIndex(path), undo_duplicate); // TODO: make threshold configurable? const auto threshold = 0.08; path->simplify(nullptr, threshold); } path->update(); } if (undo_step->isEmpty()) delete undo_step; else { map->setObjectsDirty(); map->push(undo_step); map->emitSelectionEdited(); } } void MapEditorController::simplifyPathClicked() { // TODO: make threshold configurable! const auto threshold = 0.1; auto undo_step = new ReplaceObjectsUndoStep(map); MapPart* part = map->getCurrentPart(); for (const auto object : map->selectedObjects()) { if (object->getType() != Object::Path) continue; PathObject* path = object->asPath(); PathObject* undo_duplicate = nullptr; if (path->simplify(&undo_duplicate, threshold)) undo_step->addObject(part->findObjectIndex(path), undo_duplicate); path->update(); } if (undo_step->isEmpty()) delete undo_step; else { map->setObjectsDirty(); map->push(undo_step); map->emitSelectionEdited(); } } void MapEditorController::cutoutPhysicalClicked() { setTool(new CutoutTool(this, cutout_physical_act, false)); } void MapEditorController::cutawayPhysicalClicked() { setTool(new CutoutTool(this, cutaway_physical_act, true)); } void MapEditorController::distributePointsClicked() { Q_ASSERT(activeSymbol()->getType() == Symbol::Point); PointSymbol* point = activeSymbol()->asPoint(); DistributePointsTool::Settings settings; if (!DistributePointsTool::showSettingsDialog(window, point, settings)) return; // Create points along paths std::vector created_objects; for (const auto object : map->selectedObjects()) { if (object->getType() == Object::Path) DistributePointsTool::execute(object->asPath(), point, settings, created_objects); } if (created_objects.empty()) return; // Add points to map for (auto o : created_objects) map->addObject(o); // Create undo step and select new objects map->clearObjectSelection(false); MapPart* part = map->getCurrentPart(); auto delete_step = new DeleteObjectsUndoStep(map); for (std::size_t i = 0; i < created_objects.size(); ++i) { Object* object = created_objects[i]; delete_step->addObject(part->findObjectIndex(object)); map->addObjectToSelection(object, i == created_objects.size() - 1); } map->push(delete_step); map->setObjectsDirty(); } void MapEditorController::addFloatingDockWidget(QDockWidget* dock_widget) { if (!window->restoreDockWidget(dock_widget)) { dock_widget->setFloating(true); // We must set the size from the sizeHint() ourselves, // otherwise QDockWidget may use the minimum size. QRect geometry(window->pos(), dock_widget->sizeHint()); geometry.translate(window->width() - geometry.width(), window->centralWidget()->y()); const int max_height = window->centralWidget()->height(); if (geometry.height() > max_height) geometry.setHeight(max_height); dock_widget->setGeometry(geometry); connect(dock_widget, &QDockWidget::dockLocationChanged, this, &MapEditorController::saveWindowState); } } void MapEditorController::paintOnTemplateClicked(bool checked) { if (!checked) finishPaintOnTemplate(); else if (last_painted_on_template) paintOnTemplate(last_painted_on_template); else paintOnTemplateSelectClicked(); } void MapEditorController::paintOnTemplateSelectClicked() { PaintOnTemplateSelectDialog paintDialog(map, main_view, last_painted_on_template, window); paintDialog.setWindowModality(Qt::WindowModal); if (paintDialog.exec() == QDialog::Accepted) { last_painted_on_template = paintDialog.getSelectedTemplate(); paintOnTemplate(last_painted_on_template); } } void MapEditorController::enableGPSDisplay(bool enable) { if (enable) { gps_display->startUpdates(); // Create gps_track_recorder if we can determine a template track filename constexpr int gps_track_draw_update_interval = 10 * 1000; // in milliseconds if (! window->currentPath().isEmpty()) { // Find or create a template for the track with a specific name QString gpx_file_path = QFileInfo(window->currentPath()).absoluteDir().canonicalPath() + QLatin1Char('/') + QFileInfo(window->currentPath()).completeBaseName() + QLatin1String(" - GPS-") + QDate::currentDate().toString(Qt::ISODate) + QLatin1String(".gpx"); bool new_template = true; // Indicates that we really add a new template. TemplateTrack* track = nullptr; int template_index = 0; for ( ; template_index < map->getNumTemplates(); ++template_index) { auto temp = map->getTemplate(template_index); if (temp->getTemplatePath() == gpx_file_path) { // There is a template for this track. new_template = false; track = qobject_cast(temp); if (!track) { // Need to replace the template at template_index map->setTemplateAreaDirty(template_index); map->deleteTemplate(template_index); } break; } } if (!track) { track = new TemplateTrack(gpx_file_path, map); map->addTemplate(track, template_index); } if (track->getTemplateState() != Template::Loaded) { track->loadTemplateFile(false); } track->configureForGPSTrack(); map->setTemplateAreaDirty(template_index); if (new_template) { // When the map is saved, the new track must be saved even if it is empty. track->setHasUnsavedChanges(true); map->setTemplatesDirty(); } gps_track_recorder = new GPSTrackRecorder(gps_display, track, gps_track_draw_update_interval, map_widget); } } else { gps_display->stopUpdates(); delete gps_track_recorder; gps_track_recorder = nullptr; } gps_display->setVisible(enable); if (! enable) { gps_marker_display->stopPath(); gps_temporary_path_act->setChecked(false); } gps_distance_rings_action->setEnabled(enable); gps_temporary_point_act->setEnabled(enable); gps_temporary_path_act->setEnabled(enable); updateDrawPointGPSAvailability(); } void MapEditorController::enableGPSDistanceRings(bool enable) { gps_display->enableDistanceRings(enable); } void MapEditorController::updateDrawPointGPSAvailability() { const Symbol* symbol = activeSymbol(); draw_point_gps_act->setEnabled(symbol && gps_display->isVisible() && symbol->getType() == Symbol::Point && !symbol->isHidden()); move_to_gps_pos_act->setEnabled(gps_display->isVisible()); } void MapEditorController::drawPointGPSClicked() { setTool(new DrawPointGPSTool(gps_display, this, draw_point_gps_act)); } void MapEditorController::gpsTemporaryPointClicked() { if (gps_marker_display->addPoint()) gps_temporary_clear_act->setEnabled(true); } void MapEditorController::gpsTemporaryPathClicked(bool enable) { if (enable) { gps_marker_display->startPath(); gps_temporary_clear_act->setEnabled(true); } else gps_marker_display->stopPath(); } void MapEditorController::gpsTemporaryClearClicked() { if (QMessageBox::question(window, tr("Clear temporary markers"), tr("Are you sure you want to delete all temporary GPS markers? This cannot be undone."), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) return; gps_marker_display->stopPath(); gps_temporary_path_act->setChecked(false); gps_marker_display->clear(); gps_temporary_clear_act->setEnabled(false); } void MapEditorController::enableCompassDisplay(bool enable) { if (!compass_display || !show_top_bar_button || !top_action_bar) { Q_ASSERT(!"Prerequisites must be initialized"); } else if (enable) { auto size = compass_display->sizeHint(); if (top_action_bar->isVisible()) compass_display->setGeometry(0, top_action_bar->size().height(), size.width(), size.height()); else compass_display->setGeometry(show_top_bar_button->size().width(), 0, size.width(), size.height()); connect(&Compass::getInstance(), &Compass::azimuthChanged, compass_display, &CompassDisplay::setAzimuth); compass_display->show(); } else { disconnect(&Compass::getInstance(), &Compass::azimuthChanged, compass_display, &CompassDisplay::setAzimuth); compass_display->hide(); } } void MapEditorController::alignMapWithNorth(bool enable) { const int update_interval = 1000; // milliseconds if (enable) { Compass::getInstance().startUsage(); gps_display->enableHeadingIndicator(true); connect(&align_map_with_north_timer, &QTimer::timeout, this, &MapEditorController::alignMapWithNorthUpdate); align_map_with_north_timer.start(update_interval); alignMapWithNorthUpdate(); } else { align_map_with_north_timer.disconnect(); align_map_with_north_timer.stop(); gps_display->enableHeadingIndicator(false); Compass::getInstance().stopUsage(); main_view->setRotation(0); } } void MapEditorController::alignMapWithNorthUpdate() { // Time in milliseconds for which the rotation should not be updated after // the user interacted with the map widget const int interaction_time_threshold = 1500; if (map_widget->getTimeSinceLastInteraction() < interaction_time_threshold) return; // Set map rotation main_view->setRotation(M_PI / -180.0 * Compass::getInstance().getCurrentAzimuth()); } void MapEditorController::hideTopActionBar() { top_action_bar->hide(); show_top_bar_button->show(); show_top_bar_button->raise(); compass_display->move(show_top_bar_button->size().width(), 0); } void MapEditorController::showTopActionBar() { show_top_bar_button->hide(); top_action_bar->show(); top_action_bar->raise(); compass_display->move(0, top_action_bar->size().height()); } void MapEditorController::mobileSymbolSelectorClicked() { // NOTE: this does not handle window resizes, however it is assumed that in // mobile mode there is no window, instead the application is running fullscreen. symbol_widget->setGeometry(window->rect()); symbol_widget->raise(); symbol_widget->show(); connect(symbol_widget, &SymbolWidget::selectedSymbolsChanged, this, &MapEditorController::mobileSymbolSelectorFinished); } void MapEditorController::mobileSymbolSelectorFinished() { disconnect(symbol_widget, &SymbolWidget::selectedSymbolsChanged, this, &MapEditorController::mobileSymbolSelectorFinished); symbol_widget->hide(); } void MapEditorController::updateMapPartsUI() { if (!mappart_selector_box) { mappart_selector_box = new QComboBox(); mappart_selector_box->setToolTip(tr("Map parts")); } if (!mappart_merge_menu) { mappart_merge_menu = new QMenu(); mappart_merge_menu->setTitle(tr("Merge this part with")); } if (!mappart_move_menu) { mappart_move_menu = new QMenu(); mappart_move_menu->setTitle(tr("Move selected objects to")); } const QSignalBlocker selector_box_blocker(mappart_selector_box); mappart_selector_box->clear(); mappart_merge_menu->clear(); mappart_move_menu->clear(); const int count = map ? map->getNumParts() : 0; const bool have_multiple_parts = (count > 1); mappart_merge_menu->setEnabled(have_multiple_parts); mappart_move_menu->setEnabled(have_multiple_parts && map->getNumSelectedObjects() > 0); if (mappart_remove_act) { mappart_remove_act->setEnabled(have_multiple_parts); mappart_merge_act->setEnabled(have_multiple_parts); } if (toolbar_mapparts && !toolbar_mapparts->isVisible()) { toolbar_mapparts->setVisible(have_multiple_parts); } if (count > 0) { const int current = map->getCurrentPartIndex(); for (int i = 0; i < count; ++i) { QString part_name = map->getPart(i)->getName(); mappart_selector_box->addItem(part_name); if (i != current) { auto action = new QAction(part_name, this); mappart_merge_mapper->setMapping(action, i); connect(action, QOverload::of(&QAction::triggered), mappart_merge_mapper, QOverload<>::of(&QSignalMapper::map)); mappart_merge_menu->addAction(action); action = new QAction(part_name, this); mappart_move_mapper->setMapping(action, i); connect(action, QOverload::of(&QAction::triggered), mappart_move_mapper, QOverload<>::of(&QSignalMapper::map)); mappart_move_menu->addAction(action); } } mappart_selector_box->setCurrentIndex(current); } } void MapEditorController::addMapPart() { bool accepted = false; QString name = QInputDialog::getText( window, tr("Add new part..."), tr("Enter the name of the map part:"), QLineEdit::Normal, QString(), &accepted ); if (accepted && !name.isEmpty()) { auto part = new MapPart(name, map); map->addPart(part, map->getCurrentPartIndex() + 1); map->setCurrentPart(part); map->push(new MapPartUndoStep(map, MapPartUndoStep::RemoveMapPart, part)); } } void MapEditorController::removeMapPart() { auto part = map->getCurrentPart(); QMessageBox::StandardButton button = QMessageBox::question( window, tr("Remove current part"), tr("Do you want to remove map part \"%1\" and all its objects?").arg(part->getName()), QMessageBox::Yes | QMessageBox::No ); if (button == QMessageBox::Yes) { auto index = map->getCurrentPartIndex(); UndoStep* undo_step = new MapPartUndoStep(map, MapPartUndoStep::AddMapPart, index); auto i = part->getNumObjects(); if (i > 0) { auto add_step = new AddObjectsUndoStep(map); do { --i; auto object = part->getObject(i); add_step->addObject(i, object); part->deleteObject(object, true); } while (i > 0); auto combined_step = new CombinedUndoStep(map); combined_step->push(add_step); combined_step->push(undo_step); undo_step = combined_step; } map->push(undo_step); map->removePart(index); } } void MapEditorController::renameMapPart() { bool accepted = false; QString name = QInputDialog::getText( window, tr("Rename current part..."), tr("Enter the name of the map part:"), QLineEdit::Normal, map->getCurrentPart()->getName(), &accepted ); if (accepted && !name.isEmpty()) { map->push(new MapPartUndoStep(map, MapPartUndoStep::ModifyMapPart, map->getCurrentPartIndex())); map->getCurrentPart()->setName(name); } } void MapEditorController::changeMapPart(int index) { if (index >= 0) { map->setCurrentPartIndex(std::size_t(index)); window->showStatusBarMessage(tr("Switched to map part '%1'.").arg(map->getCurrentPart()->getName()), 1000); } } void MapEditorController::reassignObjectsToMapPart(int target) { auto current = map->getCurrentPartIndex(); auto begin = map->reassignObjectsToMapPart(map->selectedObjectsBegin(), map->selectedObjectsEnd(), current, target); auto undo = new SwitchPartUndoStep(map, target, current); for (std::size_t i = begin, end = map->getPart(target)->getNumObjects(); i < end; ++i) undo->addObject(i); map->push(undo); } void MapEditorController::mergeCurrentMapPartTo(int target) { MapPart* const source_part = map->getCurrentPart(); MapPart* const target_part = map->getPart(target); const QMessageBox::StandardButton button = QMessageBox::question( window, tr("Merge map parts"), tr("Do you want to move all objects from map part \"%1\" to \"%2\", " "and to remove \"%1\"?") .arg(source_part->getName(), target_part->getName()), QMessageBox::Yes | QMessageBox::No ); if (button == QMessageBox::Yes) { // Beware that the source part is removed, and // the target part's index might change during merge. auto source = map->getCurrentPartIndex(); UndoStep* add_part_step = new MapPartUndoStep(map, MapPartUndoStep::AddMapPart, source); auto begin = map->mergeParts(source, target); auto switch_part_undo = new SwitchPartUndoStep(map, target, source); for (std::size_t i = begin, end = target_part->getNumObjects(); i < end; ++i) switch_part_undo->addObject(i); auto undo = new CombinedUndoStep(map); undo->push(switch_part_undo); undo->push(add_part_step); map->push(undo); } } void MapEditorController::mergeAllMapParts() { QString const name = map->getCurrentPart()->getName(); const QMessageBox::StandardButton button = QMessageBox::question( window, tr("Merge map parts"), tr("Do you want to move all objects to map part \"%1\", and to remove all other map parts?").arg(name), QMessageBox::Yes | QMessageBox::No ); if (button == QMessageBox::Yes) { auto undo = new CombinedUndoStep(map); // For simplicity, we merge to the first part, // but keep the properties (i.e. name) of the current part. map->setCurrentPartIndex(0); MapPart* target_part = map->getPart(0); for (auto i = map->getNumParts() - 1; i > 0; --i) { UndoStep* add_part_step = new MapPartUndoStep(map, MapPartUndoStep::AddMapPart, i); auto begin = map->mergeParts(i, 0); auto switch_part_undo = new SwitchPartUndoStep(map, 0, i); for (std::size_t j = begin, end = target_part->getNumObjects(); j < end; ++j) switch_part_undo->addObject(j); undo->push(switch_part_undo); undo->push(add_part_step); } undo->push(new MapPartUndoStep(map, MapPartUndoStep::ModifyMapPart, 0)); target_part->setName(name); map->push(undo); } } void MapEditorController::paintOnTemplate(Template* temp) { auto tool = qobject_cast(getTool()); if (!tool) { tool = new PaintOnTemplateTool(this, paint_on_template_act); setTool(tool); } hideAllTemplates(false); auto vis = main_view->getTemplateVisibility(temp); vis.visible = true; main_view->setTemplateVisibility(temp, vis); temp->setTemplateAreaDirty(); tool->setTemplate(temp); } void MapEditorController::finishPaintOnTemplate() { if (auto tool = qobject_cast(current_tool)) { tool->deactivate(); } } void MapEditorController::templateAdded(int /*pos*/, const Template* /*temp*/) { if (map->getNumTemplates() == 1) templateAvailabilityChanged(); } void MapEditorController::templateDeleted(int /*pos*/, const Template* /*temp*/) { if (map->getNumTemplates() == 0) templateAvailabilityChanged(); } void MapEditorController::setMapAndView(Map* map, MapView* map_view) { Q_ASSERT(map); Q_ASSERT(map_view); if (this->map) { this->map->disconnect(this); if (symbol_widget) { this->map->disconnect(symbol_widget); } updateMapPartsUI(); } this->map = map; this->main_view = map_view; connect(&map->undoManager(), &UndoManager::canRedoChanged, this, &MapEditorController::undoStepAvailabilityChanged); connect(&map->undoManager(), &UndoManager::canUndoChanged, this, &MapEditorController::undoStepAvailabilityChanged); connect(map, &Map::objectSelectionChanged, this, &MapEditorController::objectSelectionChanged); connect(map, &Map::templateAdded, this, &MapEditorController::templateAdded); connect(map, &Map::templateDeleted, this, &MapEditorController::templateDeleted); connect(map, &Map::closedTemplateAvailabilityChanged, this, &MapEditorController::closedTemplateAvailabilityChanged); connect(map, &Map::spotColorPresenceChanged, this, &MapEditorController::spotColorPresenceChanged); connect(map, &Map::currentMapPartChanged, this, &MapEditorController::updateMapPartsUI); connect(map, &Map::mapPartAdded, this, &MapEditorController::updateMapPartsUI); connect(map, &Map::mapPartChanged, this, &MapEditorController::updateMapPartsUI); connect(map, &Map::mapPartDeleted, this, &MapEditorController::updateMapPartsUI); if (symbol_widget) { delete symbol_widget; symbol_widget = nullptr; createSymbolWidget(); } if (window) { updateWidgets(); } } void MapEditorController::updateWidgets() { undoStepAvailabilityChanged(); objectSelectionChanged(); if (mode == MapEditor) { if (main_view) { show_grid_act->setChecked(main_view->isGridVisible()); hide_all_templates_act->setChecked(main_view->areAllTemplatesHidden()); overprinting_simulation_act->setChecked(main_view->isOverprintingSimulationEnabled()); } if (map) { hatch_areas_view_act->setChecked(map->isAreaHatchingEnabled()); baseline_view_act->setChecked(map->isBaselineViewEnabled()); closedTemplateAvailabilityChanged(); spotColorPresenceChanged(map->hasSpotColors()); updateMapPartsUI(); } } } void MapEditorController::createSymbolWidget(QWidget* parent) { if (!symbol_widget) { symbol_widget = new SymbolWidget(map, mobile_mode, parent); connect(symbol_widget, &SymbolWidget::switchSymbolClicked, this, &MapEditorController::switchSymbolClicked); connect(symbol_widget, &SymbolWidget::fillBorderClicked, this, &MapEditorController::fillBorderClicked); connect(symbol_widget, &SymbolWidget::selectObjectsClicked, this, &MapEditorController::selectObjectsClicked); connect(symbol_widget, &SymbolWidget::deselectObjectsClicked, this, &MapEditorController::deselectObjectsClicked); connect(symbol_widget, &SymbolWidget::selectedSymbolsChanged, this, &MapEditorController::selectedSymbolsChanged); if (symbol_dock_widget) { Q_ASSERT(!parent); symbol_dock_widget->setWidget(symbol_widget); } selectedSymbolsChanged(); } } void MapEditorController::importClicked() { QSettings settings; QString import_directory = settings.value(QString::fromLatin1("importFileDirectory"), QDir::homePath()).toString(); QStringList map_names; QStringList map_extensions; for (auto format : FileFormats.formats()) { if (!format->supportsImport()) continue; map_names.push_back(format->primaryExtension().toUpper()); map_extensions.append(format->fileExtensions()); } map_names.removeDuplicates(); map_extensions.removeDuplicates(); QString filename = FileDialog::getOpenFileName( window, tr("Import %1, GPX, OSM or DXF file").arg( map_names.join(QString::fromLatin1(", "))), import_directory, QString::fromLatin1("%1 (%2 *.gpx *.osm *.dxf);;%3 (*.*)").arg( tr("Importable files"), QLatin1String("*.") + map_extensions.join(QString::fromLatin1(" *.")), tr("All files")) ); if (filename.isEmpty() || filename.isNull()) return; settings.setValue(QString::fromLatin1("importFileDirectory"), QFileInfo(filename).canonicalPath()); bool success = false; auto map_format = FileFormats.findFormatForFilename(filename); if (map_format) { // Map format recognized by filename extension importMapFile(filename, true); // Error reporting in Map::loadFrom() return; } else if (filename.endsWith(QLatin1String(".dxf"), Qt::CaseInsensitive) || filename.endsWith(QLatin1String(".gpx"), Qt::CaseInsensitive) || filename.endsWith(QLatin1String(".osm"), Qt::CaseInsensitive)) { // Fallback: Legacy geo file import importGeoFile(filename); return; // Error reporting in Track::import() } else if (importMapFile(filename, false)) { // Last resort: Map format recognition by try-and-error success = true; } else { QMessageBox::critical(window, tr("Error"), tr("Cannot import the selected file because its file format is not supported.")); return; } if (!success) { /// \todo Reword message (not a map file here). Requires new translations QMessageBox::critical(window, tr("Error"), tr("Cannot import the selected map file because it could not be loaded.")); } } bool MapEditorController::importGeoFile(const QString& filename) { TemplateTrack temp(filename, map); return !temp.configureAndLoad(window, main_view) || temp.import(window); } bool MapEditorController::importMapFile(const QString& filename, bool show_errors) { Map imported_map; imported_map.setScaleDenominator(map->getScaleDenominator()); // for non-scaled geodata if (!imported_map.loadFrom(filename, window, nullptr, false, show_errors)) return false; if (imported_map.symbolSetId() != map->symbolSetId()) { auto crt_filename = filename; crt_filename.replace(filename.lastIndexOf(QLatin1Char('.')), 4, QLatin1String(".crt")); if (!QFileInfo::exists(crt_filename)) crt_filename.replace(filename.lastIndexOf(QLatin1Char('.')), 4, QLatin1String(".CRT")); if (!QFileInfo::exists(crt_filename)) crt_filename = ReplaceSymbolSetDialog::discoverCrtFile(imported_map.symbolSetId(), map->symbolSetId()); if (QFileInfo::exists(crt_filename)) { QFile crt_file{ crt_filename }; if (!crt_file.open(QFile::ReadOnly)) { QMessageBox::warning(window, ::OpenOrienteering::Map::tr("Error"), ::OpenOrienteering::Map::tr("Cannot open file:\n%1\nfor reading.").arg(crt_filename)); return false; } if (!ReplaceSymbolSetDialog::showDialogForCRT(window, imported_map, *map, crt_file)) { auto choice = QMessageBox::question(window, ::OpenOrienteering::Map::tr("Import..."), ::OpenOrienteering::Map::tr("Symbol replacement was canceled.\n" "Import the data anyway?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No); if (choice == QMessageBox::No) return false; } } } map->importMap(&imported_map, Map::MinimalObjectImport | Map::GeorefImport, window); return true; } // slot void MapEditorController::setViewOptionsEnabled(bool enabled) { pan_act->setEnabled(enabled); show_grid_act->setEnabled(enabled); // hatch_areas_view_act->setEnabled(enabled); // baseline_view_act->setEnabled(enabled); overprinting_simulation_act->setEnabled(enabled); hide_all_templates_act->setEnabled(enabled); } // ### EditorDockWidget ### EditorDockWidget::EditorDockWidget(const QString& title, QAction* action, MapEditorController* editor, QWidget* parent) : QDockWidget(title, parent) , action(action) , editor(editor) { if (editor) { connect(this, &EditorDockWidget::dockLocationChanged, editor, &MapEditorController::saveWindowState); } if (action) { connect(toggleViewAction(), &QAction::toggled, action, &QAction::setChecked); } #ifdef Q_OS_ANDROID size_grip = new QSizeGrip(this); #endif } bool EditorDockWidget::event(QEvent* event) { switch (event->type()) { case QEvent::ShortcutOverride: if (editor->getWindow()->shortcutsBlocked()) event->accept(); break; case QEvent::Show: QTimer::singleShot(0, this, SLOT(raise())); // clazy:exclude=old-style-connect break; default: ; // nothing } return QDockWidget::event(event); } void EditorDockWidget::resizeEvent(QResizeEvent* event) { #ifdef Q_OS_ANDROID QRect rect(QPoint(0,0), size_grip->sizeHint()); rect.moveBottomRight(geometry().bottomRight() - geometry().topLeft()); int fw = style()->pixelMetric(QStyle::PM_DockWidgetFrameWidth, 0, this); rect.translate(-fw, -fw); size_grip->setGeometry(rect); size_grip->raise(); #endif QDockWidget::resizeEvent(event); } // ### MapEditorToolAction ### MapEditorToolAction::MapEditorToolAction(const QIcon& icon, const QString& text, QObject* parent) : QAction(icon, text, parent) { setCheckable(true); connect(this, &QAction::triggered, this, &MapEditorToolAction::triggeredImpl); } void MapEditorToolAction::triggeredImpl(bool checked) { if (checked) emit activated(); else setChecked(true); } } // namespace OpenOrienteering mapper-0.8.1.1/src/gui/map/map_editor.h000066400000000000000000000605701325266516600176130ustar00rootroot00000000000000/* * Copyright 2012, 2013, 2014 Thomas Schöps * Copyright 2013-2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_MAP_EDITOR_H #define OPENORIENTEERING_MAP_EDITOR_H #include #include #include #include #include #include #include #include #include "gui/main_window_controller.h" class QAction; class QByteArray; class QComboBox; class QDockWidget; class QFrame; class QKeyEvent; class QLabel; class QMenu; class QSignalMapper; // IWYU pragma: no_forward_declare QString class QToolBar; class QToolButton; class QWidget; namespace OpenOrienteering { class ActionGridBar; class CompassDisplay; class EditorDockWidget; class FileFormat; class GPSDisplay; class GPSTemporaryMarkers; class GPSTrackRecorder; class GeoreferencingDialog; class MainWindow; class Map; class MapEditorActivity; class MapEditorTool; class MapFindFeature; class MapView; class MapWidget; class PrintWidget; class ReopenTemplateDialog; class Symbol; class SymbolWidget; class Template; class TemplateListWidget; class TemplatePositionDockWidget; /** * MainWindowController for editing a map. * * Creates menus and toolbars, manages editing tools, * dock widgets, and much more. */ class MapEditorController : public MainWindowController { friend class Map; Q_OBJECT public: /** See MapEditorController constructor. */ enum OperatingMode { MapEditor = 0, SymbolEditor = 1 }; /** * Constructs a new MapEditorController for a map. * * @param mode Normally, MapEditor should be used. However, as a HACK the * MapEditorController is also used in the symbol editor for the preview. * In this case, SymbolEditor is passed to disable showing the menus, * toolbars, etc. * @param map A Map which is to be edited by the controller. * @param map_view A MapView for the given map. * * \todo Review/remove mode hack. * \todo Document and fix ownership of map and map_view. Double deletes waiting... */ MapEditorController(OperatingMode mode, Map* map = nullptr, MapView* map_view = nullptr); /** Destroys the MapEditorController. */ ~MapEditorController() override; /** Returns if the editor is in mobile mode. */ bool isInMobileMode() const; /** * Changes to new_tool as the new active tool. * If there is a current tool before, calls deleteLater() on it. * new_tool may be nullptr, but it is unusual to have no active tool, so * consider setEditTool() instead. */ void setTool(MapEditorTool* new_tool); /** * Shortcut to change to the point edit tool as new active tool. * See setTool(). */ void setEditTool(); /** * Sets new_override_tool as the new active override tool. * This takes precedence over all tools set via setTool(). * new_override_tool may be nullptr, which disables using an override tool * and re-enables the normal tool set via setTool(). */ void setOverrideTool(MapEditorTool* new_override_tool); /** Returns the current tool. */ inline MapEditorTool* getTool() const {return current_tool;} /** Returns the default drawing tool for a given symbol. */ MapEditorTool* getDefaultDrawToolForSymbol(const Symbol* symbol); /** * @brief Returns the active symbol, or nullptr. * * The active symbol is the single symbol which is to be used by drawing * tools and actions. * * It there is no active symbol, this function returns nullptr. */ Symbol* activeSymbol() const; /** * If this is set to true (usually by the current tool), * undo/redo and saving the map is deactivated. * * This is important if the map is in an "unstable" state temporarily. */ void setEditingInProgress(bool value); /** * Returns true when editing is in progress. * @see setEditingInProgress */ bool isEditingInProgress() const override; /** * Adds a a floating dock widget to the main window. * Adjusts some geometric properties. */ void addFloatingDockWidget(QDockWidget* dock_widget); /** * Sets the current editor activity. * new_activity may be nullptr to disable the current editor activity. */ void setEditorActivity(MapEditorActivity* new_activity); /** Returns the current editor activity. */ inline MapEditorActivity* getEditorActivity() const {return editor_activity;} /** Returns the map on which this controller operates. */ inline Map* getMap() const {return map;} /** Returns the main map widget (which is currently the only map widget). */ inline MapWidget* getMainWidget() const {return map_widget;} /** Returns this controller's symbol widget, where the symbol selection happens. */ inline SymbolWidget* getSymbolWidget() const {return symbol_widget;} /** Returns if a template position dock widget exists for a template. */ inline bool existsTemplatePositionDockWidget(Template* temp) const {return template_position_widgets.contains(temp);} /** Returns the template position dock widget for a template. */ inline TemplatePositionDockWidget* getTemplatePositionDockWidget(Template* temp) const {return template_position_widgets.value(temp);} /** Adds a template position dock widget for the given template. */ void addTemplatePositionDockWidget(Template* temp); /** * Removes the template position dock widget for the template. * * Should be called by the dock widget if it is closed or the * template deleted; deletes the dock widget. */ void removeTemplatePositionDockWidget(Template* temp); /** * Shows the given widget in a popup window with specified title. * * In the desktop version, the widget is shown inside a dock widget. * In the mobile version, the widget is shown as a popup over the map, * ignoring the title. * * Make sure that the child widget has a reasonable size hint. */ void showPopupWidget(QWidget* child_widget, const QString& title); /** * Deletes the given popup widget, which was previously shown with * showPopupWidget(). */ void deletePopupWidget(QWidget* child_widget); /** * Returns the action identified by id if it exists, or nullptr. * This allows the reuse of the controller's actions in dock widgets etc. */ QAction* getAction(const char* id); /** Override from MainWindowController */ bool save(const QString& path) override; /** Override from MainWindowController */ bool exportTo(const QString& path, const FileFormat* format = nullptr) override; /** Override from MainWindowController */ bool load(const QString& path, QWidget* dialog_parent = nullptr) override; /** Override from MainWindowController */ void attach(MainWindow* window) override; /** Override from MainWindowController */ void detach() override; /** * @copybrief MainWindowController::keyPressEventFilter * This implementation passes the event to MapWidget::keyPressEventFilter. */ bool keyPressEventFilter(QKeyEvent* event) override; /** * @copybrief MainWindowController::keyReleaseEventFilter * This implementation passes the event to MapWidget::keyReleaseEventFilter. */ bool keyReleaseEventFilter(QKeyEvent* event) override; public slots: /** * Makes the print/export dock widget visible, and configures it for * the given task (which is of type PrintWidget::TaskFlags). */ void printClicked(int task); /** Undoes the last object edit step. */ void undo(); /** Redoes the last object edit step */ void redo(); /** Cuts the selected object(s). */ void cut(); /** Copies the selecte object(s). */ void copy(); /** Pastes the object(s) from the clipboard. */ void paste(); /** Empties the undo / redo history to save space. */ void clearUndoRedoHistory(); /** Toggles visivbility of the map grid. */ void showGrid(); /** Shows the map grid configuration dialog. */ void configureGrid(); /** Activates the pan tool. */ void pan(); /** Moves view to GPS position. */ void moveToGpsPos(); /** Zooms in in the current map widget. */ void zoomIn(); /** Zooms out in the current map widget. */ void zoomOut(); /** Shows the dialog to set a custom zoom factor in the current map widget. */ void setCustomZoomFactorClicked(); /** Sets the hatch areas view option. */ void hatchAreas(bool checked); /** Sets the baseline view option. */ void baselineView(bool checked); /** Sets the "hide all templates" view option. */ void hideAllTemplates(bool checked); /** Sets the overprinting simulation view option. */ void overprintingSimulation(bool checked); /** Adjusts the coordinates display of the map widget to the selected option. */ void coordsDisplayChanged(); /** Copies the displayed coordinates to the clipboard. */ void copyDisplayedCoords(); /** Shows or hides the symbol pane. */ void showSymbolWindow(bool show); /** Shows or hides the color dock widget. */ void showColorWindow(bool show); /** Shows a dialog for changing the symbol set ID. */ void symbolSetIdClicked(); /** Shows the "load symbols from" dialog. */ void loadSymbolsFromClicked(); /** Loads a CRT file and shows the symbol replacement dialog. */ void loadCrtClicked(); /** TODO: not implemented yet. */ void loadColorsFromClicked(); /** Shows the "scale all symbols" dialog. */ void scaleAllSymbolsClicked(); /** Shows the ScaleMapDialog. */ void scaleMapClicked(); /** Shows the RotateMapDialog. */ void rotateMapClicked(); /** Shows the dialog to enter map notes. */ void mapNotesClicked(); /** Shows or hides the template setup dock widget. */ void showTemplateWindow(bool show); /** Shows a file selector to open a template. */ void openTemplateClicked(); /** Shows the ReopenTemplateDialog. */ void reopenTemplateClicked(); /** Adjusts action availability based on the presence of templates */ void templateAvailabilityChanged(); /** Adjusts action availability based on the presence of closed templates */ void closedTemplateAvailabilityChanged(); /** Shows or hides the tags editor dock widget. */ void showTagsWindow(bool show); /** Shows the GeoreferencingDialog. */ void editGeoreferencing(); /** * Makes the editor aware of a change of the selected symbols. */ void selectedSymbolsChanged(); /** * Makes the editor aware of a change of the selected object. */ void objectSelectionChanged(); /** Adjusts the enabled state of the undo / redo actions. */ void undoStepAvailabilityChanged(); /** Adjusts the enabled state of the paste action (specific signature required). */ void clipboardChanged(QClipboard::Mode mode); /** Adjusts the enabled state of the paste action. */ void updatePasteAvailability(); /** * Checks the presence of spot colors, * and to disables overprinting simulation if there are no spot colors. */ void spotColorPresenceChanged(bool has_spot_colors); /** Adjusts the view in the current map widget to show the whole map. */ void showWholeMap(); /** Activates the point edit tool. */ void editToolClicked(); /** Activates the line edit tool. */ void editLineToolClicked(); /** Activates the draw point tool. */ void drawPointClicked(); /** Activates the draw path tool. */ void drawPathClicked(); /** Activates the draw circle tool. */ void drawCircleClicked(); /** Activates the draw rectangle tool. */ void drawRectangleClicked(); /** Activates the draw freehand tool. */ void drawFreehandClicked(); /** Activates the draw fill tool. */ void drawFillClicked(); /** Activates the draw text tool. */ void drawTextClicked(); /** Deletes the selected object(s) */ void deleteClicked(); /** Duplicates the selected object(s) */ void duplicateClicked(); /** Switches the symbol of the selected object(s) to the selected symbol. */ void switchSymbolClicked(); /** Creates duplicates of the selected object(s) and assigns them the selected symbol. */ void fillBorderClicked(); /** Selects all objects with the selected symbol(s) */ void selectObjectsClicked(bool select_exclusively); /** Deselects all objects with the selected symbol(s) */ void deselectObjectsClicked(); /** Selects all objects in the current map part. */ void selectAll(); /** Clears the object selection. */ void selectNothing(); /** Inverts in the object selection in the current map part. */ void invertSelection(); /** Selects all objects having the current selected symbols. */ void selectByCurrentSymbols(); /** * Reverses the selected object(s) direcction(s), * thus switching dash directions for lines. */ void switchDashesClicked(); /** Connects close endpoints of selected lines */ void connectPathsClicked(); /** Activates the cut tool */ void cutClicked(); /** Activates the cut hole tool */ void cutHoleClicked(); /** Activates the cut circular hole tool */ void cutHoleCircleClicked(); /** Activates the cut rectangular hole tool */ void cutHoleRectangleClicked(); /** Activates the rotate tool */ void rotateClicked(); /** Activates the rotate pattern tool */ void rotatePatternClicked(); /** Activates the scale tool */ void scaleClicked(); /** Shows or hides the MeasureWidget */ void measureClicked(bool checked); /** Calculates the union of selected same-symbol area objects */ void booleanUnionClicked(); /** Calculates the intersection of selected same-symbol area objects */ void booleanIntersectionClicked(); /** Calculates the difference of selected area objects from the first selected area object */ void booleanDifferenceClicked(); /** Calculates the boolean XOr of selected same-symbol area objects */ void booleanXOrClicked(); /** Merges holes of the (single) selected area object */ void booleanMergeHolesClicked(); /** Converts selected polygonal paths to curves */ void convertToCurvesClicked(); /** Tries to remove points of selected paths while retaining their shape */ void simplifyPathClicked(); /** Activates the physical cutout tool */ void cutoutPhysicalClicked(); /** Activates the physical cutout tool (inversed) */ void cutawayPhysicalClicked(); /** Executes the "distribute points along path" action. * The prerequisites for using the tool must be given. */ void distributePointsClicked(); /** Shows or hides the paint-on-template widget */ void paintOnTemplateClicked(bool checked); /** Shows the template selection dialog for for the paint-on-template functionality */ void paintOnTemplateSelectClicked(); /** Enables or disables GPS display. */ void enableGPSDisplay(bool enable); /** Enables or disables showing distance rings when GPS display is active. */ void enableGPSDistanceRings(bool enable); /** Updates availability of the GPS point drawing tool. */ void updateDrawPointGPSAvailability(); /** Switches to the GPS point drawing tool. */ void drawPointGPSClicked(); /** Sets a temporary marker at the GPS position. */ void gpsTemporaryPointClicked(); /** Draws a temporary path at the GPS position. */ void gpsTemporaryPathClicked(bool enable); /** Clears temporary GPS markers. */ void gpsTemporaryClearClicked(); /** Enables or disables digital compass display. */ void enableCompassDisplay(bool enable); /** Enables or disables map auto-rotation according to compass. */ void alignMapWithNorth(bool enable); /** Called regularly after enabled with alignMapWithNorth() to update the map rotation. */ void alignMapWithNorthUpdate(); /** For mobile UI: hides the top action bar. */ void hideTopActionBar(); /** For mobile UI: shows the top action bar again after hiding it. */ void showTopActionBar(); /** For mobile UI: shows the symbol selection screen. */ void mobileSymbolSelectorClicked(); /** Counterpart to mobileSymbolSelectorClicked(). */ void mobileSymbolSelectorFinished(); /** Creates and adds a new map part */ void addMapPart(); /** Removes the current map part */ void removeMapPart(); /** Renames the current map part */ void renameMapPart(); /** Moves all selected objects to a different map part */ void reassignObjectsToMapPart(int target); /** Merges the current map part with another one */ void mergeCurrentMapPartTo(int target); /** Merges all map parts into the current one. */ void mergeAllMapParts(); /** Updates action enabled states after a template has been added */ void templateAdded(int pos, const Template* temp); /** Updates action enabled states after a template has been deleted */ void templateDeleted(int pos, const Template* temp); /** Imports a track file (GPX, DXF, OSM, ...) into the map */ bool importGeoFile(const QString& filename); /** Imports a map file into the loaded map */ bool importMapFile(const QString& filename, bool show_errors); /** Shows the import file selector and imports the selected file, if any. */ void importClicked(); /** Sets the enabled state of actions which change how the map is rendered, * such as with grid, with templates, with overprinting simulation. */ void setViewOptionsEnabled(bool enabled = true); /** Save the current toolbar and dock widget positions */ void saveWindowState(); /** Restore previously saved toolbar and dock widget positions */ void restoreWindowState(); signals: /** * @brief Indicates a change of the active symbol. * @param symbol The new active symbol, or nullptr. */ void activeSymbolChanged(const Symbol* symbol); void templatePositionDockWidgetClosed(Template* temp); protected: /** * Adjusts the enabled state of various actions * after the selected symbol(s) have changed. * * In addition, it disables actions as long as some editing is in progress. * * The caller shall also call updateSymbolAndObjectDependentActions(). */ void updateSymbolDependentActions(); /** * Adjusts the enabled state of various actions * after the selected object(s) have changed. * * In addition, it disables actions as long as some editing is in progress. * * The caller shall also call updateSymbolAndObjectDependentActions(). */ void updateObjectDependentActions(); /** * Adjusts the enabled state of various actions * after the selected symbol(s) or object(s) have changed. * * In addition, it disables actions as long as some editing is in progress. */ void updateSymbolAndObjectDependentActions(); protected slots: void projectionChanged(); void georeferencingDialogFinished(); /** * Sets the map's current part. */ void changeMapPart(int index); /** * Updates all UI components related to map parts. */ void updateMapPartsUI(); private: void setMapAndView(Map* map, MapView* map_view); /// Updates enabled state of all widgets void updateWidgets(); void createSymbolWidget(QWidget* parent = nullptr); void createColorWindow(); void createTemplateWindow(); void createTagEditor(); QAction* newAction(const char* id, const QString& tr_text, QObject* receiver, const char* slot, const char* icon = nullptr, const QString& tr_tip = QString{}, const char* whats_this_link = nullptr); QAction* newCheckAction(const char* id, const QString& tr_text, QObject* receiver, const char* slot, const char* icon = nullptr, const QString& tr_tip = QString{}, const char* whats_this_link = nullptr); QAction* newToolAction(const char* id, const QString& tr_text, QObject* receiver, const char* slot, const char* icon = nullptr, const QString& tr_tip = QString{}, const char* whats_this_link = nullptr); QAction* findAction(const char* id); void assignKeyboardShortcuts(); void createActions(); void createMenuAndToolbars(); void createMobileGUI(); void paintOnTemplate(Template* temp); void finishPaintOnTemplate(); void doUndo(bool redo); Map* map; MapView* main_view; MapWidget* map_widget; OperatingMode mode; bool mobile_mode; MapEditorTool* current_tool; MapEditorTool* override_tool; MapEditorActivity* editor_activity; Symbol* active_symbol; bool editing_in_progress; // Action handling QHash actionsById; EditorDockWidget* print_dock_widget; PrintWidget* print_widget; QAction* print_act; QAction* export_image_act; QAction* export_pdf_act; QAction* undo_act; QAction* redo_act; QAction* cut_act; QAction* copy_act; QAction* paste_act; QAction* delete_act; QAction* select_all_act; QAction* select_nothing_act; QAction* invert_selection_act; QAction* select_by_current_symbol_act; std::unique_ptr find_feature; QAction* clear_undo_redo_history_act; QAction* pan_act; QAction* move_to_gps_pos_act; QAction* zoom_in_act; QAction* zoom_out_act; QAction* show_all_act; QAction* fullscreen_act; QAction* custom_zoom_act; QAction* show_grid_act; QAction* configure_grid_act; QAction* hatch_areas_view_act; QAction* baseline_view_act; QAction* hide_all_templates_act; QAction* overprinting_simulation_act; QAction* map_coordinates_act; QAction* projected_coordinates_act; QAction* geographic_coordinates_act; QAction* geographic_coordinates_dms_act; QAction* scale_all_symbols_act; QAction* georeferencing_act; QAction* scale_map_act; QAction* rotate_map_act; QAction* map_notes_act; QAction* symbol_set_id_act; QAction* color_window_act; QPointer color_dock_widget; QAction* load_symbols_from_act; QAction* load_crt_act; QAction* symbol_window_act; EditorDockWidget* symbol_dock_widget; SymbolWidget* symbol_widget; QAction* template_window_act; QPointer template_dock_widget; TemplateListWidget* template_list_widget; QAction* open_template_act; QAction* reopen_template_act; QAction* tags_window_act; QPointer tags_dock_widget; QAction* edit_tool_act; QAction* edit_line_tool_act; QAction* draw_point_act; QAction* draw_path_act; QAction* draw_circle_act; QAction* draw_rectangle_act; QAction* draw_freehand_act; QAction* draw_fill_act; QAction* draw_text_act; QAction* duplicate_act; QAction* switch_symbol_act; QAction* fill_border_act; QAction* switch_dashes_act; QAction* connect_paths_act; QAction* cut_tool_act; QMenu* cut_hole_menu; QAction* cut_hole_act; QAction* cut_hole_circle_act; QAction* cut_hole_rectangle_act; QAction* rotate_act; QAction* rotate_pattern_act; QAction* scale_act; QAction* measure_act; EditorDockWidget* measure_dock_widget; QAction* boolean_union_act; QAction* boolean_intersection_act; QAction* boolean_difference_act; QAction* boolean_xor_act; QAction* boolean_merge_holes_act; QAction* convert_to_curves_act; QAction* simplify_path_act; QAction* cutout_physical_act; QAction* cutaway_physical_act; QAction* distribute_points_act; QAction* paint_on_template_act; QAction* paint_on_template_settings_act; Template* last_painted_on_template; QAction* touch_cursor_action; QAction* gps_display_action; QAction* gps_distance_rings_action; QAction* draw_point_gps_act; QAction* gps_temporary_point_act; QAction* gps_temporary_path_act; QAction* gps_temporary_clear_act; GPSTemporaryMarkers* gps_marker_display; GPSDisplay* gps_display; GPSTrackRecorder* gps_track_recorder; QAction* compass_action; CompassDisplay* compass_display; QAction* align_map_with_north_act; QTimer align_map_with_north_timer; QAction* mappart_add_act; QAction* mappart_rename_act; QAction* mappart_remove_act; QAction* mappart_merge_act; QMenu* mappart_merge_menu; QMenu* mappart_move_menu; QAction* import_act; QFrame* statusbar_zoom_frame; QLabel* statusbar_cursorpos_label; QAction* copy_coords_act; QToolBar* toolbar_view; QToolBar* toolbar_drawing; QToolBar* toolbar_editing; QToolBar* toolbar_advanced_editing; QToolBar* toolbar_mapparts; // For mobile UI ActionGridBar* bottom_action_bar; ActionGridBar* top_action_bar; QToolButton* show_top_bar_button; QAction* mobile_symbol_selector_action; QMenu* mobile_symbol_button_menu; QComboBox* mappart_selector_box; QScopedPointer georeferencing_dialog; QScopedPointer reopen_template_dialog; QHash template_position_widgets; QSignalMapper* mappart_merge_mapper; QSignalMapper* mappart_move_mapper; }; //### MapEditorController inline code ### inline Symbol* MapEditorController::activeSymbol() const { return active_symbol; } } // namespace OpenOrienteering #endif mapper-0.8.1.1/src/gui/map/map_editor_activity.cpp000066400000000000000000000020421325266516600220500ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "map_editor_activity.h" namespace OpenOrienteering { MapEditorActivity::~MapEditorActivity() = default; void MapEditorActivity::init() { // nothing } void MapEditorActivity::draw(QPainter*, MapWidget*) { // nothing } } // namespace OpenOrienteering mapper-0.8.1.1/src/gui/map/map_editor_activity.h000066400000000000000000000047211325266516600215230ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_MAP_EDITOR_ACTIVITY_H #define OPENORIENTEERING_MAP_EDITOR_ACTIVITY_H #include class QPainter; namespace OpenOrienteering { class MapWidget; /** * Represents a type of editing activity, e.g. template position adjustment. * Only one activity can be active at a time. * * This is for example used to close the template adjustment window when * selecting an edit tool. * It can also be used to paint activity-specific graphics onto the map. */ class MapEditorActivity : public QObject { Q_OBJECT public: ~MapEditorActivity() override; /** * All initializations apart from setting variables like the activity object * should be done here instead of in the constructor, as at the time init() * is called, the old activity was properly destroyed * (including reseting the activity drawing). */ virtual void init(); /** * Sets the "activity object", which is a void pointer which can be * used for various purposes (such as identifying the activity). */ void setActivityObject(void* address); /** * Returns the "activity object". * @see setActivityObject() */ inline void* getActivityObject() const; /** * All dynamic drawings must be drawn here using the given painter. * Drawing is only possible in the area specified * by calling map->setActivityBoundingBox(). */ virtual void draw(QPainter* painter, MapWidget* widget); protected: void* activity_object; }; //### MapEditorActivity inline code ### inline void MapEditorActivity::setActivityObject(void* address) { activity_object = address; } inline void* MapEditorActivity::getActivityObject() const { return activity_object; } } // namespace OpenOrienteering #endif mapper-0.8.1.1/src/gui/map/map_editor_p.h000066400000000000000000000040111325266516600201160ustar00rootroot00000000000000/* * Copyright 2012, 2013, 2014 Thomas Schöps * Copyright 2013, 2014, 2015, 2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_MAP_EDITOR_P_H #define OPENORIENTEERING_MAP_EDITOR_P_H #include #include class QEvent; class QIcon; class QObject; class QResizeEvent; class QSizeGrip; // IWYU pragma: keep class QString; class QWidget; namespace OpenOrienteering { class MapEditorController; class Template; /** * Custom QDockWidget which unchecks the associated menu action when closed * and delivers a notification to its child */ class EditorDockWidget : public QDockWidget { Q_OBJECT public: EditorDockWidget(const QString& title, QAction* action, MapEditorController* editor, QWidget* parent = nullptr); protected: bool event(QEvent* event) override; void resizeEvent(QResizeEvent *event) override; private: QAction* action; MapEditorController* editor; #ifdef Q_OS_ANDROID QSizeGrip* size_grip; #endif }; /** * Helper class which disallows deselecting the checkable action by the user */ class MapEditorToolAction : public QAction { Q_OBJECT public: MapEditorToolAction(const QIcon& icon, const QString& text, QObject* parent); signals: void activated(); private slots: void triggeredImpl(bool checked); }; } // namespace OpenOrienteering #endif mapper-0.8.1.1/src/gui/map/map_find_feature.cpp000066400000000000000000000154271325266516600213140ustar00rootroot00000000000000/* * Copyright 2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "map_find_feature.h" #include #include #include #include #include #include #include #include // IWYU pragma: keep #include #include #include #include #include #include "core/map.h" #include "core/map_part.h" #include "core/objects/object_query.h" #include "gui/main_window.h" #include "gui/util_gui.h" #include "gui/map/map_editor.h" #include "gui/widgets/tag_select_widget.h" namespace OpenOrienteering { class Object; MapFindFeature::MapFindFeature(MapEditorController& controller) : QObject{nullptr} , controller{controller} { show_action = new QAction(tr("&Find..."), this); // QKeySequence::Find may be Ctrl+F, which conflicts with "Fill / Create Border" //show_action->setShortcut(QKeySequence::Find); //action->setStatusTip(tr_tip); show_action->setWhatsThis(Util::makeWhatThis("edit_menu.html")); connect(show_action, &QAction::triggered, this, &MapFindFeature::showDialog); find_next_action = new QAction(tr("Find &next"), this); // QKeySequence::FindNext may be F3, which conflicts with "Baseline view" //find_next_action->setShortcut(QKeySequence::FindNext); //action->setStatusTip(tr_tip); find_next_action->setWhatsThis(Util::makeWhatThis("edit_menu.html")); connect(find_next_action, &QAction::triggered, this, &MapFindFeature::findNext); } MapFindFeature::~MapFindFeature() = default; // not inlined void MapFindFeature::setEnabled(bool enabled) { show_action->setEnabled(enabled); find_next_action->setEnabled(enabled); } void MapFindFeature::showDialog() { auto window = controller.getWindow(); if (!window) return; if (!find_dialog) { find_dialog = new QDialog(window); find_dialog->setWindowTitle(tr("Find objects")); text_edit = new QTextEdit; text_edit->setLineWrapMode(QTextEdit::WidgetWidth); tag_selector = new TagSelectWidget; auto find_next = new QPushButton(tr("&Find next")); connect(find_next, &QPushButton::clicked, this, &MapFindFeature::findNext); auto find_all = new QPushButton(tr("Find &all")); connect(find_all, &QPushButton::clicked, this, &MapFindFeature::findAll); auto tags_button = new QPushButton(tr("Query editor")); tags_button->setCheckable(true); tag_selector_buttons = tag_selector->makeButtons(); tag_selector_buttons->setEnabled(false); auto button_box = new QDialogButtonBox(QDialogButtonBox::Close | QDialogButtonBox::Help); connect(button_box, &QDialogButtonBox::rejected, &*find_dialog, &QDialog::hide); connect(button_box->button(QDialogButtonBox::Help), &QPushButton::clicked, this, &MapFindFeature::showHelp); editor_stack = new QStackedLayout(); editor_stack->addWidget(text_edit); editor_stack->addWidget(tag_selector); connect(tags_button, &QAbstractButton::toggled, this, &MapFindFeature::tagSelectorToggled); auto layout = new QGridLayout; layout->addLayout(editor_stack, 0, 0, 6, 1); layout->addWidget(find_next, 0, 1, 1, 1); layout->addWidget(find_all, 1, 1, 1, 1); layout->addWidget(tags_button, 3, 1, 1, 1); layout->addWidget(tag_selector_buttons, 5, 1, 1, 1); layout->addWidget(button_box, 6, 0, 1, 2); find_dialog->setLayout(layout); } find_dialog->show(); find_dialog->raise(); find_dialog->activateWindow(); } ObjectQuery MapFindFeature::makeQuery() const { auto query = ObjectQuery{}; if (find_dialog) { if (editor_stack->currentIndex() == 0) { auto text = text_edit->toPlainText().trimmed(); if (!text.isEmpty()) { query = ObjectQueryParser().parse(text); if (!query || query.getOperator() == ObjectQuery::OperatorSearch) query = ObjectQuery{ ObjectQuery(ObjectQuery::OperatorSearch, text), ObjectQuery::OperatorOr, ObjectQuery(ObjectQuery::OperatorObjectText, text) }; } } else { query = tag_selector->makeQuery(); } } return query; } void MapFindFeature::findNext() { auto map = controller.getMap(); auto first_object = map->getFirstSelectedObject(); map->clearObjectSelection(false); Object* next_object = nullptr; auto query = makeQuery(); if (!query) { if (auto window = controller.getWindow()) window->showStatusBarMessage(OpenOrienteering::TagSelectWidget::tr("Invalid query"), 2000); return; } auto search = [&first_object, &next_object, &query](Object* object) { if (!next_object) { if (first_object) { if (object == first_object) first_object = nullptr; } else if (query(object)) { next_object = object; } } }; // Start from selected object map->getCurrentPart()->applyOnAllObjects(search); if (!next_object) { // Start from first object first_object = nullptr; map->getCurrentPart()->applyOnAllObjects(search); } map->clearObjectSelection(false); if (next_object) map->addObjectToSelection(next_object, false); map->emitSelectionChanged(); if (!map->selectedObjects().empty()) controller.setEditTool(); } void MapFindFeature::findAll() { auto map = controller.getMap(); map->clearObjectSelection(false); auto query = makeQuery(); if (!query) { controller.getWindow()->showStatusBarMessage(OpenOrienteering::TagSelectWidget::tr("Invalid query"), 2000); return; } map->getCurrentPart()->applyOnMatchingObjects([map](auto object) { map->addObjectToSelection(object, false); }, std::cref(query)); map->emitSelectionChanged(); controller.getWindow()->showStatusBarMessage(OpenOrienteering::TagSelectWidget::tr("%n object(s) selected", nullptr, map->getNumSelectedObjects()), 2000); if (!map->selectedObjects().empty()) controller.setEditTool(); } void MapFindFeature::showHelp() const { Util::showHelp(controller.getWindow(), "find_objects.html"); } // slot void MapFindFeature::tagSelectorToggled(bool active) { editor_stack->setCurrentIndex(active ? 1 : 0); tag_selector_buttons->setEnabled(active); if (!active) { auto text = tag_selector->makeQuery().toString(); if (!text.isEmpty()) text_edit->setText(text); } } } // namespace OpenOrienteering mapper-0.8.1.1/src/gui/map/map_find_feature.h000066400000000000000000000046161325266516600207570ustar00rootroot00000000000000/* * Copyright 2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_MAP_FIND_FEATURE_H #define OPENORIENTEERING_MAP_FIND_FEATURE_H #include #include #include class QAction; class QDialog; class QStackedLayout; class QTextEdit; class QWidget; namespace OpenOrienteering { class MapEditorController; class ObjectQuery; class TagSelectWidget; /** * Provides an interactive feature for finding objects in the map. * * The search text from the provided dialog is trimmed and parsed as an * ObjectQuery. If parsing fails, the trimmed text is used to find any matching * text in tag keys, tag values, text object content or symbol name. */ class MapFindFeature : public QObject { Q_OBJECT public: MapFindFeature(MapEditorController& controller); ~MapFindFeature() override; void setEnabled(bool enabled); QAction* showDialogAction() { return show_action; } QAction* findNextAction() { return find_next_action; } private: void showDialog(); ObjectQuery makeQuery() const; void findNext(); void findAll(); void showHelp() const; void tagSelectorToggled(bool active); MapEditorController& controller; QPointer find_dialog; // child of controller's window QStackedLayout* editor_stack = nullptr; // child of find_dialog QTextEdit* text_edit = nullptr; // child of find_dialog TagSelectWidget* tag_selector = nullptr; // child of find_dialog QWidget* tag_selector_buttons = nullptr; // child of find_dialog QAction* show_action = nullptr; // child of this QAction* find_next_action = nullptr; // child of this Q_DISABLE_COPY(MapFindFeature) }; } // namespace OpenOrienteering #endif mapper-0.8.1.1/src/gui/map/map_widget.cpp000066400000000000000000001057541325266516600201470ustar00rootroot00000000000000/* * Copyright 2012-2014 Thomas Schöps * Copyright 2013-2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "map_widget.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "settings.h" #include "core/georeferencing.h" #include "core/latlon.h" #include "core/map.h" #include "core/renderables/renderable.h" #include "gui/touch_cursor.h" #include "gui/map/map_editor_activity.h" #include "gui/widgets/action_grid_bar.h" #include "gui/widgets/key_button_bar.h" #include "gui/widgets/pie_menu.h" #include "sensors/gps_display.h" #include "sensors/gps_temporary_markers.h" #include "templates/template.h" // IWYU pragma: keep #include "tools/tool.h" #include "util/backports.h" // IWYU pragma: keep #include "util/util.h" class QGesture; // IWYU pragma: no_forward_declare QPinchGesture namespace OpenOrienteering { MapWidget::MapWidget(bool show_help, bool force_antialiasing, QWidget* parent) : QWidget(parent) , view(nullptr) , tool(nullptr) , activity(nullptr) , coords_type(MAP_COORDS) , cursorpos_label(nullptr) , show_help(show_help) , force_antialiasing(force_antialiasing) , dragging(false) , pinching(false) , pinching_factor(1.0) , below_template_cache_dirty_rect(rect()) , above_template_cache_dirty_rect(rect()) , map_cache_dirty_rect(rect()) , drawing_dirty_rect_border(0) , activity_dirty_rect_border(0) , last_mouse_release_time(QTime::currentTime()) , current_pressed_buttons(0) , gps_display(nullptr) , marker_display(nullptr) { context_menu = new PieMenu(this); // context_menu->setMinimumActionCount(8); // context_menu->setIconSize(24); setAttribute(Qt::WA_OpaquePaintEvent); setAttribute(Qt::WA_AcceptTouchEvents, true); setGesturesEnabled(true); setAutoFillBackground(false); setMouseTracking(true); setFocusPolicy(Qt::ClickFocus); setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); } MapWidget::~MapWidget() { // nothing, not inlined } void MapWidget::setMapView(MapView* view) { if (this->view != view) { if (this->view) { auto map = view->getMap(); map->removeMapWidget(this); disconnect(this->view, &MapView::viewChanged, this, &MapWidget::viewChanged); disconnect(this->view, &MapView::panOffsetChanged, this, &MapWidget::setPanOffset); disconnect(this->view, &MapView::visibilityChanged, this, &MapWidget::updateEverything); } this->view = view; if (view) { connect(this->view, &MapView::viewChanged, this, &MapWidget::viewChanged); connect(this->view, &MapView::panOffsetChanged, this, &MapWidget::setPanOffset); connect(this->view, &MapView::visibilityChanged, this, &MapWidget::updateEverything); auto map = this->view->getMap(); map->addMapWidget(this); } update(); } } void MapWidget::setTool(MapEditorTool* tool) { // Redraw if touch cursor usage changes bool redrawTouchCursor = (touch_cursor && this->tool && tool && (this->tool->usesTouchCursor() || tool->usesTouchCursor())); this->tool = tool; if (tool) setCursor(tool->getCursor()); else unsetCursor(); if (redrawTouchCursor) touch_cursor->updateMapWidget(false); } void MapWidget::setActivity(MapEditorActivity* activity) { this->activity = activity; } void MapWidget::setGesturesEnabled(bool enabled) { gestures_enabled = enabled; if (enabled) { grabGesture(Qt::PinchGesture); } else { ungrabGesture(Qt::PinchGesture); } } void MapWidget::applyMapTransform(QPainter* painter) const { painter->translate(width() / 2.0 + getMapView()->panOffset().x(), height() / 2.0 + getMapView()->panOffset().y()); painter->setWorldTransform(getMapView()->worldTransform(), true); } QRectF MapWidget::viewportToView(const QRect& input) const { return QRectF(input.left() - 0.5*width() - pan_offset.x(), input.top() - 0.5*height() - pan_offset.y(), input.width(), input.height()); } QPointF MapWidget::viewportToView(QPoint input) const { return QPointF(input.x() - 0.5*width() - pan_offset.x(), input.y() - 0.5*height() - pan_offset.y()); } QPointF MapWidget::viewportToView(QPointF input) const { return QPointF(input.x() - 0.5*width() - pan_offset.x(), input.y() - 0.5*height() - pan_offset.y()); } QRectF MapWidget::viewToViewport(const QRectF& input) const { return QRectF(input.left() + 0.5*width() + pan_offset.x(), input.top() + 0.5*height() + pan_offset.y(), input.width(), input.height()); } QRectF MapWidget::viewToViewport(const QRect& input) const { return QRectF(input.left() + 0.5*width() + pan_offset.x(), input.top() + 0.5*height() + pan_offset.y(), input.width(), input.height()); } QPointF MapWidget::viewToViewport(QPoint input) const { return QPointF(input.x() + 0.5*width() + pan_offset.x(), input.y() + 0.5*height() + pan_offset.y()); } QPointF MapWidget::viewToViewport(QPointF input) const { return QPointF(input.x() + 0.5*width() + pan_offset.x(), input.y() + 0.5*height() + pan_offset.y()); } MapCoord MapWidget::viewportToMap(QPoint input) const { return view->viewToMap(viewportToView(input)); } MapCoordF MapWidget::viewportToMapF(QPoint input) const { return view->viewToMapF(viewportToView(input)); } MapCoordF MapWidget::viewportToMapF(QPointF input) const { return view->viewToMapF(viewportToView(input)); } QPointF MapWidget::mapToViewport(MapCoord input) const { return viewToViewport(view->mapToView(input)); } QPointF MapWidget::mapToViewport(MapCoordF input) const { return viewToViewport(view->mapToView(input)); } QRectF MapWidget::mapToViewport(const QRectF& input) const { QRectF result; rectIncludeSafe(result, mapToViewport(input.topLeft())); rectIncludeSafe(result, mapToViewport(input.bottomRight())); if (view->getRotation() != 0) { rectIncludeSafe(result, mapToViewport(input.topRight())); rectIncludeSafe(result, mapToViewport(input.bottomLeft())); } return result; } void MapWidget::viewChanged(MapView::ChangeFlags changes) { setDrawingBoundingBox(drawing_dirty_rect_map, drawing_dirty_rect_border, true); setActivityBoundingBox(activity_dirty_rect_map, activity_dirty_rect_border, true); updateEverything(); if (changes.testFlag(MapView::ZoomChange)) updateZoomDisplay(); } void MapWidget::setPanOffset(QPoint offset) { pan_offset = offset; update(); } void MapWidget::startDragging(QPoint cursor_pos) { Q_ASSERT(!dragging); Q_ASSERT(!pinching); dragging = true; drag_start_pos = cursor_pos; normal_cursor = cursor(); setCursor(Qt::ClosedHandCursor); } void MapWidget::updateDragging(QPoint cursor_pos) { Q_ASSERT(dragging); view->setPanOffset(cursor_pos - drag_start_pos); } void MapWidget::finishDragging(QPoint cursor_pos) { Q_ASSERT(dragging); dragging = false; view->finishPanning(cursor_pos - drag_start_pos); setCursor(normal_cursor); } void MapWidget::cancelDragging() { dragging = false; view->setPanOffset(QPoint()); setCursor(normal_cursor); } qreal MapWidget::startPinching(QPoint center) { Q_ASSERT(!dragging); Q_ASSERT(!pinching); pinching = true; drag_start_pos = center; pinching_center = center; pinching_factor = 1.0; return pinching_factor; } void MapWidget::updatePinching(QPoint center, qreal factor) { Q_ASSERT(pinching); pinching_center = center; pinching_factor = factor; updateZoomDisplay(); update(); } void MapWidget::finishPinching(QPoint center, qreal factor) { pinching = false; view->finishPanning(center - drag_start_pos); view->setZoom(factor * view->getZoom(), viewportToView(center)); } void MapWidget::cancelPinching() { pinching = false; pinching_factor = 1.0; update(); } void MapWidget::moveMap(int steps_x, int steps_y) { if (steps_x != 0 || steps_y != 0) { try { constexpr auto move_factor = 0.25; auto offset = MapCoord::fromNative64( qRound64(view->pixelToLength(width() * steps_x * move_factor)), qRound64(view->pixelToLength(height() * steps_y * move_factor)) ); view->setCenter(view->center() + offset); } catch (std::range_error&) { // Do nothing } } } void MapWidget::ensureVisibilityOfRect(QRectF map_rect, ZoomOption zoom_option) { // Amount in pixels that is scrolled "too much" if the rect is not completely visible // TODO: change to absolute size using dpi value const int pixel_border = 70; auto viewport_rect = mapToViewport(map_rect).toAlignedRect(); // TODO: this method assumes that the viewport is not rotated. if (rect().contains(viewport_rect.topLeft()) && rect().contains(viewport_rect.bottomRight())) return; auto offset = MapCoordF{ 0, 0 }; if (viewport_rect.left() < 0) offset.rx() = view->pixelToLength(viewport_rect.left() - pixel_border) / 1000.0; else if (viewport_rect.right() > width()) offset.rx() = view->pixelToLength(viewport_rect.right() - width() + pixel_border) / 1000.0; if (viewport_rect.top() < 0) offset.ry() = view->pixelToLength(viewport_rect.top() - pixel_border) / 1000.0; else if (viewport_rect.bottom() > height()) offset.ry() = view->pixelToLength(viewport_rect.bottom() - height() + pixel_border) / 1000.0; if (!qIsNull(offset.lengthSquared())) view->setCenter(view->center() + offset); // If the rect is still not completely in view, we have to zoom out viewport_rect = mapToViewport(map_rect).toAlignedRect(); if (!(rect().contains(viewport_rect.topLeft()) && rect().contains(viewport_rect.bottomRight()))) adjustViewToRect(map_rect, zoom_option); } void MapWidget::adjustViewToRect(QRectF map_rect, ZoomOption zoom_option) { view->setCenter(MapCoord{ map_rect.center() }); if (map_rect.isValid()) { // NOTE: The loop is an inelegant way to fight inaccuracies that occur somewhere ... const int pixel_border = 15; const float initial_zoom = view->getZoom(); for (int i = 0; i < 10; ++i) { float zoom_factor = qMin(height() / (view->lengthToPixel(1000.0 * map_rect.height()) + 2*pixel_border), width() / (view->lengthToPixel(1000.0 * map_rect.width()) + 2*pixel_border)); float zoom = view->getZoom() * zoom_factor; if (zoom_option == DiscreteZoom) { zoom = pow(2, 0.5 * floor(2.0 * (std::log2(zoom) - std::log2(initial_zoom))) + std::log2(initial_zoom)); } view->setZoom(zoom); } } } void MapWidget::moveDirtyRect(QRect& dirty_rect, qreal x, qreal y) { if (dirty_rect.isValid()) dirty_rect = dirty_rect.translated(x, y).intersected(rect()); } void MapWidget::markTemplateCacheDirty(const QRectF& view_rect, int pixel_border, bool front_cache) { QRect& cache_dirty_rect = front_cache ? above_template_cache_dirty_rect : below_template_cache_dirty_rect; QRectF viewport_rect = viewToViewport(view_rect); QRect integer_rect = QRect(viewport_rect.left() - (1+pixel_border), viewport_rect.top() - (1+pixel_border), viewport_rect.width() + 2*(1+pixel_border), viewport_rect.height() + 2*(1+pixel_border)); if (!integer_rect.intersects(rect())) return; if (cache_dirty_rect.isValid()) cache_dirty_rect = cache_dirty_rect.united(integer_rect); else cache_dirty_rect = integer_rect; update(integer_rect); } void MapWidget::markObjectAreaDirty(const QRectF& map_rect) { updateMapRect(map_rect, 0, map_cache_dirty_rect); } void MapWidget::setDrawingBoundingBox(QRectF map_rect, int pixel_border, bool do_update) { Q_UNUSED(do_update); clearDrawingBoundingBox(); if (map_rect.isValid()) { drawing_dirty_rect_map = map_rect; drawing_dirty_rect_border = pixel_border; updateMapRect(drawing_dirty_rect_map, drawing_dirty_rect_border, drawing_dirty_rect); } } void MapWidget::clearDrawingBoundingBox() { drawing_dirty_rect_map.setWidth(0); if (drawing_dirty_rect.isValid()) { update(drawing_dirty_rect); drawing_dirty_rect.setWidth(0); } } void MapWidget::setActivityBoundingBox(QRectF map_rect, int pixel_border, bool do_update) { Q_UNUSED(do_update); clearActivityBoundingBox(); if (map_rect.isValid()) { activity_dirty_rect_map = map_rect; activity_dirty_rect_border = pixel_border; updateMapRect(activity_dirty_rect_map, activity_dirty_rect_border, activity_dirty_rect); } } void MapWidget::clearActivityBoundingBox() { activity_dirty_rect_map.setWidth(0); if (activity_dirty_rect.isValid()) { update(activity_dirty_rect); activity_dirty_rect.setWidth(0); } } void MapWidget::updateDrawing(const QRectF& map_rect, int pixel_border) { QRect viewport_rect = calculateViewportBoundingBox(map_rect, pixel_border); if (viewport_rect.intersects(rect())) update(viewport_rect); } void MapWidget::updateMapRect(const QRectF& map_rect, int pixel_border, QRect& cache_dirty_rect) { QRect viewport_rect = calculateViewportBoundingBox(map_rect, pixel_border); updateViewportRect(viewport_rect, cache_dirty_rect); } void MapWidget::updateViewportRect(QRect viewport_rect, QRect& cache_dirty_rect) { if (viewport_rect.intersects(rect())) { if (cache_dirty_rect.isValid()) cache_dirty_rect = cache_dirty_rect.united(viewport_rect); else cache_dirty_rect = viewport_rect; update(viewport_rect); } } void MapWidget::updateDrawingLater(const QRectF& map_rect, int pixel_border) { QRect viewport_rect = calculateViewportBoundingBox(map_rect, pixel_border); if (viewport_rect.intersects(rect())) { if (!cached_update_rect.isValid()) { // Start the update timer QTimer::singleShot(15, this, SLOT(updateDrawingLaterSlot())); // clazy:exclude=old-style-connect } // NOTE: this may require a mutex for concurrent access with updateDrawingLaterSlot()? rectIncludeSafe(cached_update_rect, viewport_rect); } } void MapWidget::updateDrawingLaterSlot() { updateEverythingInRect(cached_update_rect); cached_update_rect = QRect(); } void MapWidget::updateEverything() { map_cache_dirty_rect = rect(); below_template_cache_dirty_rect = map_cache_dirty_rect; above_template_cache_dirty_rect = map_cache_dirty_rect; update(map_cache_dirty_rect); } void MapWidget::updateEverythingInRect(const QRect& dirty_rect) { rectIncludeSafe(map_cache_dirty_rect, dirty_rect); rectIncludeSafe(below_template_cache_dirty_rect, dirty_rect); rectIncludeSafe(above_template_cache_dirty_rect, dirty_rect); update(dirty_rect); } QRect MapWidget::calculateViewportBoundingBox(const QRectF& map_rect, int pixel_border) const { QRectF view_rect = view->calculateViewBoundingBox(map_rect); view_rect.adjust(-pixel_border, -pixel_border, +pixel_border, +pixel_border); return viewToViewport(view_rect).toAlignedRect(); } void MapWidget::setZoomDisplay(std::function setter) { this->zoom_display = setter; updateZoomDisplay(); } void MapWidget::setCursorposLabel(QLabel* cursorpos_label) { this->cursorpos_label = cursorpos_label; } void MapWidget::updateZoomDisplay() { if (zoom_display) { auto zoom = view->getZoom(); if (pinching) zoom *= pinching_factor; zoom_display(tr("%1x", "Zoom factor").arg(zoom, 0, 'g', 3)); } } void MapWidget::setCoordsDisplay(CoordsType type) { coords_type = type; updateCursorposLabel(last_cursor_pos); } void MapWidget::updateCursorposLabel(const MapCoordF pos) { last_cursor_pos = pos; if (!cursorpos_label) return; if (coords_type == MAP_COORDS) { cursorpos_label->setText( QStringLiteral("%1 %2 (%3)"). arg(locale().toString(pos.x(), 'f', 2), locale().toString(-pos.y(), 'f', 2), tr("mm", "millimeters")) ); } else { const Georeferencing& georef = view->getMap()->getGeoreferencing(); bool ok = true; if (coords_type == PROJECTED_COORDS) { const QPointF projected_point(georef.toProjectedCoords(pos)); if (qAbs(georef.getGridScaleFactor() - 1.0) < 0.02) { // Grid unit differs less than 2% from meter. cursorpos_label->setText( QStringLiteral("%1 %2 (%3)"). arg(QString::number(projected_point.x(), 'f', 0), QString::number(projected_point.y(), 'f', 0), tr("m", "meters")) ); } else { cursorpos_label->setText( QStringLiteral("%1 %2"). arg(QString::number(projected_point.x(), 'f', 0), QString::number(projected_point.y(), 'f', 0)) ); } } else if (coords_type == GEOGRAPHIC_COORDS) { const LatLon lat_lon(georef.toGeographicCoords(pos, &ok)); cursorpos_label->setText( QString::fromUtf8("%1° %2°"). arg(locale().toString(lat_lon.latitude(), 'f', 6), locale().toString(lat_lon.longitude(), 'f', 6)) ); } else if (coords_type == GEOGRAPHIC_COORDS_DMS) { const LatLon lat_lon(georef.toGeographicCoords(pos, &ok)); cursorpos_label->setText( QStringLiteral("%1 %2"). arg(georef.degToDMS(lat_lon.latitude()), georef.degToDMS(lat_lon.longitude())) ); } else { // shall never happen ok = false; } if (!ok) cursorpos_label->setText(tr("Error")); } } int MapWidget::getTimeSinceLastInteraction() { if (current_pressed_buttons != 0) return 0; else return last_mouse_release_time.msecsTo(QTime::currentTime()); } void MapWidget::setGPSDisplay(GPSDisplay* gps_display) { this->gps_display = gps_display; } void MapWidget::setTemporaryMarkerDisplay(GPSTemporaryMarkers* marker_display) { this->marker_display = marker_display; } QWidget* MapWidget::getContextMenu() { return context_menu; } QSize MapWidget::sizeHint() const { return QSize(640, 480); } void MapWidget::showHelpMessage(QPainter* painter, const QString& text) const { painter->fillRect(rect(), QColor(Qt::gray)); QFont font = painter->font(); int pixel_size = font.pixelSize(); if (pixel_size > 0) { font.setPixelSize(pixel_size * 2); } else { pixel_size = font.pointSize(); font.setPointSize(pixel_size * 2); } font.setBold(true); painter->setFont(font); painter->drawText(QRect(0, 0, width(), height()), Qt::AlignCenter, text); } bool MapWidget::event(QEvent* event) { switch (event->type()) { case QEvent::Gesture: gestureEvent(static_cast(event)); return event->isAccepted(); case QEvent::TouchBegin: case QEvent::TouchUpdate: case QEvent::TouchEnd: case QEvent::TouchCancel: if (static_cast(event)->touchPoints().count() >= 2) return true; break; case QEvent::KeyPress: // No focus changing in QWidget::event if Tab is handled by tool. if (static_cast(event)->key() == Qt::Key_Tab && keyPressEventFilter(static_cast(event))) return true; break; default: ; // nothing } return QWidget::event(event); } void MapWidget::gestureEvent(QGestureEvent* event) { if (tool && tool->gestureEvent(event, this)) { event->accept(); return; } if (QGesture* gesture = event->gesture(Qt::PinchGesture)) { QPinchGesture* pinch = static_cast(gesture); QPoint center = pinch->centerPoint().toPoint(); qreal factor = pinch->totalScaleFactor(); switch (pinch->state()) { case Qt::GestureStarted: if (dragging) cancelDragging(); if (pinching) cancelPinching(); if (tool) tool->gestureStarted(); factor = startPinching(center); pinch->setTotalScaleFactor(factor); break; case Qt::GestureUpdated: updatePinching(center, factor); break; case Qt::GestureFinished: finishPinching(center, factor); break; case Qt::GestureCanceled: cancelPinching(); break; default: Q_UNREACHABLE(); // unknown gesture state } event->accept(); } else { event->ignore(); } } void MapWidget::paintEvent(QPaintEvent* event) { // Draw on the widget QPainter painter(this); QRect exposed = event->rect(); if (!view) { painter.fillRect(exposed, QColor(Qt::gray)); return; } // No colors, symbols, or objects? Provide a litte help message ... bool no_contents = view->getMap()->getNumObjects() == 0 && view->getMap()->getNumTemplates() == 0 && !view->isGridVisible(); QTransform transform = painter.worldTransform(); // Update all dirty caches // TODO: It would be an idea to do these updates in a background thread and use the old caches in the meantime updateAllDirtyCaches(); QRect target = exposed; if (pinching) { // Just draw the scaled map and templates painter.fillRect(exposed, QColor(Qt::gray)); painter.translate(pinching_center.x(), pinching_center.y()); painter.scale(pinching_factor, pinching_factor); painter.translate(-drag_start_pos.x(), -drag_start_pos.y()); } else if (pan_offset != QPoint()) { // Background color if (pan_offset.x() > 0) painter.fillRect(QRect(0, pan_offset.y(), pan_offset.x(), height() - pan_offset.y()), QColor(Qt::gray)); else if (pan_offset.x() < 0) painter.fillRect(QRect(width() + pan_offset.x(), pan_offset.y(), -pan_offset.x(), height() - pan_offset.y()), QColor(Qt::gray)); if (pan_offset.y() > 0) painter.fillRect(QRect(0, 0, width(), pan_offset.y()), QColor(Qt::gray)); else if (pan_offset.y() < 0) painter.fillRect(QRect(0, height() + pan_offset.y(), width(), -pan_offset.y()), QColor(Qt::gray)); target.translate(pan_offset); } if (!view->areAllTemplatesHidden() && isBelowTemplateVisible() && !below_template_cache.isNull() && view->getMap()->getFirstFrontTemplate() > 0) { painter.drawImage(target, below_template_cache, exposed); } else if (show_help && no_contents) { painter.save(); painter.setTransform(transform); if (view->getMap()->getNumColors() == 0) showHelpMessage(&painter, tr("Empty map!\n\nStart by defining some colors:\nSelect Symbols -> Color window to\nopen the color dialog and\ndefine the colors there.")); else if (view->getMap()->getNumSymbols() == 0) showHelpMessage(&painter, tr("No symbols!\n\nNow define some symbols:\nRight-click in the symbol bar\nand select \"New symbol\"\nto create one.")); else showHelpMessage(&painter, tr("Ready to draw!\n\nStart drawing or load a base map.\nTo load a base map, click\nTemplates -> Open template...") + QLatin1String("\n\n") + tr("Hint: Hold the middle mouse button to drag the map,\nzoom using the mouse wheel, if available.")); painter.restore(); } else { painter.fillRect(target, Qt::white); } const auto map_visibility = view->effectiveMapVisibility(); if (!map_cache.isNull() && map_visibility.visible) { qreal saved_opacity = painter.opacity(); painter.setOpacity(map_visibility.opacity); painter.drawImage(target, map_cache, exposed); painter.setOpacity(saved_opacity); } if (!view->areAllTemplatesHidden() && isAboveTemplateVisible() && !above_template_cache.isNull() && view->getMap()->getNumTemplates() - view->getMap()->getFirstFrontTemplate() > 0) painter.drawImage(target, above_template_cache, exposed); //painter.setClipRect(exposed); // Show current drawings if (activity_dirty_rect.isValid()) activity->draw(&painter, this); if (drawing_dirty_rect.isValid()) tool->draw(&painter, this); // Draw temporary GPS marker display if (marker_display) marker_display->paint(&painter); // Draw GPS display if (gps_display) gps_display->paint(&painter); // Draw touch cursor if (touch_cursor && tool && tool->usesTouchCursor()) touch_cursor->paint(&painter); painter.setWorldTransform(transform, false); } void MapWidget::resizeEvent(QResizeEvent* event) { map_cache_dirty_rect = rect(); below_template_cache_dirty_rect = map_cache_dirty_rect; above_template_cache_dirty_rect = map_cache_dirty_rect; if (map_cache.width() < map_cache_dirty_rect.width() || map_cache.height() < map_cache_dirty_rect.height()) { map_cache = QImage(); below_template_cache = QImage(); above_template_cache = QImage(); } for (QObject* const child : children()) { if (QWidget* child_widget = qobject_cast(child)) { child_widget->resize(event->size().width(), child_widget->sizeHint().height()); } else if (QWidget* child_widget = qobject_cast(child)) { QSize size = child_widget->sizeHint(); QRect map_widget_rect = rect(); child_widget->setGeometry( qMax(0, qRound(map_widget_rect.center().x() - 0.5f * size.width())), qMax(0, map_widget_rect.bottom() - size.height()), qMin(size.width(), map_widget_rect.width()), qMin(size.height(), map_widget_rect.height()) ); } } QWidget::resizeEvent(event); } void MapWidget::mousePressEvent(QMouseEvent* event) { current_pressed_buttons = event->buttons(); if (touch_cursor && tool && tool->usesTouchCursor()) { touch_cursor->mousePressEvent(event); if (event->type() == QEvent::MouseMove) { _mouseMoveEvent(event); return; } } _mousePressEvent(event); } void MapWidget::_mousePressEvent(QMouseEvent* event) { if (dragging || pinching) { event->accept(); return; } if (tool && tool->mousePressEvent(event, view->viewToMapF(viewportToView(event->pos())), this)) { event->accept(); return; } if (event->button() == Qt::MiddleButton) { startDragging(event->pos()); event->accept(); } else if (event->button() == Qt::RightButton) { if (!context_menu->isEmpty()) context_menu->popup(event->globalPos()); } } void MapWidget::mouseMoveEvent(QMouseEvent* event) { if (touch_cursor && tool && tool->usesTouchCursor()) { if (!touch_cursor->mouseMoveEvent(event)) return; } _mouseMoveEvent(event); } void MapWidget::_mouseMoveEvent(QMouseEvent* event) { if (pinching) { event->accept(); return; } else if (dragging) { updateDragging(event->pos()); return; } else { updateCursorposLabel(view->viewToMapF(viewportToView(event->pos()))); } if (tool && tool->mouseMoveEvent(event, view->viewToMapF(viewportToView(event->pos())), this)) { event->accept(); return; } } void MapWidget::mouseReleaseEvent(QMouseEvent* event) { current_pressed_buttons = event->buttons(); last_mouse_release_time = QTime::currentTime(); if (touch_cursor && tool && tool->usesTouchCursor()) { if (!touch_cursor->mouseReleaseEvent(event)) return; } _mouseReleaseEvent(event); } void MapWidget::_mouseReleaseEvent(QMouseEvent* event) { if (dragging) { finishDragging(event->pos()); event->accept(); return; } if (tool && tool->mouseReleaseEvent(event, view->viewToMapF(viewportToView(event->pos())), this)) { event->accept(); return; } } void MapWidget::mouseDoubleClickEvent(QMouseEvent* event) { if (touch_cursor && tool && tool->usesTouchCursor()) { if (!touch_cursor->mouseDoubleClickEvent(event)) return; } _mouseDoubleClickEvent(event); } void MapWidget::_mouseDoubleClickEvent(QMouseEvent* event) { if (tool && tool->mouseDoubleClickEvent(event, view->viewToMapF(viewportToView(event->pos())), this)) { event->accept(); return; } QWidget::mouseDoubleClickEvent(event); } void MapWidget::wheelEvent(QWheelEvent* event) { if (event->orientation() == Qt::Vertical) { if (view) { auto degrees = event->delta() / 8.0; auto num_steps = degrees / 15.0; auto cursor_pos_view = viewportToView(event->pos()); bool preserve_cursor_pos = (event->modifiers() & Qt::ControlModifier) == 0; if (num_steps < 0 && !Settings::getInstance().getSettingCached(Settings::MapEditor_ZoomOutAwayFromCursor).toBool()) preserve_cursor_pos = !preserve_cursor_pos; if (preserve_cursor_pos) { view->zoomSteps(num_steps, cursor_pos_view); } else { view->zoomSteps(num_steps); updateCursorposLabel(view->viewToMapF(cursor_pos_view)); } // Send a mouse move event to the current tool as zooming out can move the mouse position on the map if (tool) { QMouseEvent mouse_event{ QEvent::HoverMove, event->pos(), Qt::NoButton, QApplication::mouseButtons(), Qt::NoModifier }; tool->mouseMoveEvent(&mouse_event, view->viewToMapF(cursor_pos_view), this); } } event->accept(); } else event->ignore(); } void MapWidget::leaveEvent(QEvent* event) { if (tool) tool->leaveEvent(event); } bool MapWidget::keyPressEventFilter(QKeyEvent* event) { if (tool && tool->keyPressEvent(event)) { return true; } switch (event->key()) { case Qt::Key_F6: if (dragging) finishDragging(mapFromGlobal(QCursor::pos())); else startDragging(mapFromGlobal(QCursor::pos())); return true; case Qt::Key_Up: moveMap(0, -1); return true; case Qt::Key_Down: moveMap(0, 1); return true; case Qt::Key_Left: moveMap(-1, 0); return true; case Qt::Key_Right: moveMap(1, 0); return true; default: return false; } } bool MapWidget::keyReleaseEventFilter(QKeyEvent* event) { if (tool && tool->keyReleaseEvent(event)) { return true; // NOLINT } return false; } QVariant MapWidget::inputMethodQuery(Qt::InputMethodQuery property) const { return inputMethodQuery(property, {}); } QVariant MapWidget::inputMethodQuery(Qt::InputMethodQuery property, const QVariant& argument) const { QVariant result; if (tool) result = tool->inputMethodQuery(property, argument); if (!result.isValid()) result = QWidget::inputMethodQuery(property); return result; } void MapWidget::inputMethodEvent(QInputMethodEvent* event) { if (tool) tool->inputMethodEvent(event); } void MapWidget::enableTouchCursor(bool enabled) { if (enabled && !touch_cursor) { touch_cursor.reset(new TouchCursor(this)); } else if (!enabled && touch_cursor) { touch_cursor->updateMapWidget(false); touch_cursor.reset(nullptr); } } void MapWidget::focusOutEvent(QFocusEvent* event) { if (tool) tool->focusOutEvent(event); QWidget::focusOutEvent(event); } void MapWidget::contextMenuEvent(QContextMenuEvent* event) { if (event->reason() == QContextMenuEvent::Mouse) { // HACK: Ignore context menu events caused by the mouse, because right click // events need to be sent to the current tool first. event->ignore(); return; } if (!context_menu->isEmpty()) context_menu->popup(event->globalPos()); event->accept(); } bool MapWidget::containsVisibleTemplate(int first_template, int last_template) const { if (first_template > last_template) return false; // no template visible Map* map = view->getMap(); for (int i = first_template; i <= last_template; ++i) { if (view->isTemplateVisible(map->getTemplate(i))) return true; } return false; } inline bool MapWidget::isAboveTemplateVisible() const { return containsVisibleTemplate(view->getMap()->getFirstFrontTemplate(), view->getMap()->getNumTemplates() - 1); } inline bool MapWidget::isBelowTemplateVisible() const { return containsVisibleTemplate(0, view->getMap()->getFirstFrontTemplate() - 1); } void MapWidget::updateTemplateCache(QImage& cache, QRect& dirty_rect, int first_template, int last_template, bool use_background) { Q_ASSERT(containsVisibleTemplate(first_template, last_template)); if (cache.isNull()) { // Lazy allocation of cache image cache = QImage(size(), QImage::Format_ARGB32_Premultiplied); dirty_rect = rect(); } else { // Make sure not to use a bigger draw rect than necessary dirty_rect = dirty_rect.intersected(rect()); } // Start drawing QPainter painter(&cache); painter.setClipRect(dirty_rect); // Fill with background color (TODO: make configurable) if (use_background) painter.fillRect(dirty_rect, Qt::white); else { QPainter::CompositionMode mode = painter.compositionMode(); painter.setCompositionMode(QPainter::CompositionMode_Clear); painter.fillRect(dirty_rect, Qt::transparent); painter.setCompositionMode(mode); } // Draw templates painter.translate(width() / 2.0, height() / 2.0); painter.setWorldTransform(view->worldTransform(), true); Map* map = view->getMap(); QRectF map_view_rect = view->calculateViewedRect(viewportToView(dirty_rect)); map->drawTemplates(&painter, map_view_rect, first_template, last_template, view, true); dirty_rect.setWidth(-1); // => !dirty_rect.isValid() } void MapWidget::updateMapCache(bool use_background) { if (map_cache.isNull()) { // Lazy allocation of cache image map_cache = QImage(size(), QImage::Format_ARGB32_Premultiplied); map_cache_dirty_rect = rect(); } else { // Make sure not to use a bigger draw rect than necessary map_cache_dirty_rect = map_cache_dirty_rect.intersected(rect()); } // Start drawing QPainter painter; painter.begin(&map_cache); painter.setClipRect(map_cache_dirty_rect); // Fill with background color (TODO: make configurable) if (use_background) { painter.fillRect(map_cache_dirty_rect, Qt::white); } else { QPainter::CompositionMode mode = painter.compositionMode(); painter.setCompositionMode(QPainter::CompositionMode_Clear); painter.fillRect(map_cache_dirty_rect, Qt::transparent); painter.setCompositionMode(mode); } RenderConfig::Options options(RenderConfig::Screen | RenderConfig::HelperSymbols); bool use_antialiasing = force_antialiasing || Settings::getInstance().getSettingCached(Settings::MapDisplay_Antialiasing).toBool(); if (use_antialiasing) painter.setRenderHint(QPainter::Antialiasing); else options |= RenderConfig::DisableAntialiasing | RenderConfig::ForceMinSize; Map* map = view->getMap(); QRectF map_view_rect = view->calculateViewedRect(viewportToView(map_cache_dirty_rect)); RenderConfig config = { *map, map_view_rect, view->calculateFinalZoomFactor(), options, 1.0 }; painter.translate(width() / 2.0, height() / 2.0); painter.setWorldTransform(view->worldTransform(), true); #ifndef Q_OS_ANDROID if (view->isOverprintingSimulationEnabled()) map->drawOverprintingSimulation(&painter, config); else #endif map->draw(&painter, config); if (view->isGridVisible()) map->drawGrid(&painter, map_view_rect, true); // Finish drawing painter.end(); map_cache_dirty_rect.setWidth(-1); // => !map_cache_dirty_rect.isValid() } void MapWidget::updateAllDirtyCaches() { if (map_cache_dirty_rect.isValid()) updateMapCache(false); if (!view->areAllTemplatesHidden()) { if (below_template_cache_dirty_rect.isValid() && isBelowTemplateVisible()) updateTemplateCache(below_template_cache, below_template_cache_dirty_rect, 0, view->getMap()->getFirstFrontTemplate() - 1, true); if (above_template_cache_dirty_rect.isValid() && isAboveTemplateVisible()) updateTemplateCache(above_template_cache, above_template_cache_dirty_rect, view->getMap()->getFirstFrontTemplate(), view->getMap()->getNumTemplates() - 1, false); } } void MapWidget::shiftCache(int sx, int sy, QImage& cache) { if (!cache.isNull()) { QImage new_cache(cache.size(), cache.format()); QPainter painter(&new_cache); painter.setCompositionMode(QPainter::CompositionMode_Source); painter.drawImage(sx, sy, cache); painter.end(); cache = new_cache; } } void MapWidget::shiftCache(int sx, int sy, QPixmap& cache) { if (!cache.isNull()) { cache.scroll(sx, sy, cache.rect()); } } } // namespace OpenOrienteering mapper-0.8.1.1/src/gui/map/map_widget.h000066400000000000000000000474121325266516600176100ustar00rootroot00000000000000/* * Copyright 2012-2014 Thomas Schöps * Copyright 2013-2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_MAP_WIDGET_H #define OPENORIENTEERING_MAP_WIDGET_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "core/map_coord.h" #include "core/map_view.h" class QContextMenuEvent; class QEvent; class QFocusEvent; class QGestureEvent; class QInputMethodEvent; class QKeyEvent; class QLabel; class QMouseEvent; class QPaintEvent; class QPainter; class QPixmap; class QResizeEvent; class QWheelEvent; namespace OpenOrienteering { class GPSDisplay; class GPSTemporaryMarkers; class MapEditorActivity; class MapEditorTool; class PieMenu; class TouchCursor; /** * QWidget for displaying a map. Needs a pointer to a MapView which defines * the view properties. * * For faster display, the widget keeps some cached image internally which * are of the same size as the widget area. If then for example the map changes, * the other caches do not need to be redrawn. *
      *
    • The map cache contains the currently visible part of the map
    • *
    • The below template cache contains the currently * visible part of all templates below the map
    • *
    • The above template cache contains the currently * visible part of all templates above the map
    • *
    */ class MapWidget : public QWidget { Q_OBJECT friend class MapView; public: /** Describes different display formats for coordinates. */ enum CoordsType { /** Map coordinates: millimeters on map paper */ MAP_COORDS, /** Projected coordinates, e.g. UTM */ PROJECTED_COORDS, /** Geographic WGS84 coordinates */ GEOGRAPHIC_COORDS, /** Geographic WGS84 coordinates in degrees, minutes, seconds */ GEOGRAPHIC_COORDS_DMS }; /** Describes how a zoom level can be determined. */ enum ZoomOption { ContinuousZoom, ///< Allow any zoom value in the valid range. DiscreteZoom, ///< Adjust the zoom to the closes valid step. }; /** * Constructs a new MapWidget. * * @param show_help If set to true, the map widget shows help texts for * empty maps. * @param force_antialiasing If set to true, the map widget uses antialiasing * for display, even if it is disabled in the program settings. * Useful for the symbol editor. * @param parent Optional QWidget parent. */ MapWidget(bool show_help, bool force_antialiasing, QWidget* parent = nullptr); /** Destructs the MapWidget. */ ~MapWidget() override; /** Sets the map view to use for display. Does not take ownership of the view. */ void setMapView(MapView* view); /** Returns the map view used for display. */ MapView* getMapView() const; /** Sets the tool to use in this widget. Does not take ownership of the tool. */ void setTool(MapEditorTool* tool); /** Sets the activity to use in this widget. Does not take ownership of the activity. */ void setActivity(MapEditorActivity* activity); /** * @brief Enables or disables gesture recognition. * * MapWidget can recognize gestures, such as two-finger gestures for panning * and zooming. However, this may disturb the work with editing tools. So gestures * may be disabled. * * @param enabled If true, enables gesture recognition. Otherwise gestures are disabled. */ void setGesturesEnabled(bool enabled); /** * @brief Returns true if gesture recognition is enabled. * */ bool gesturesEnabled() const; /** * Applies the complete transform to the painter which enables to draw * map objects with map coordinates and have them correctly displayed in * the widget with the settings of the used MapView. */ void applyMapTransform(QPainter* painter) const; // Coordinate transformations /** Maps viewport (GUI) coordinates to view coordinates (see MapView). */ QRectF viewportToView(const QRect& input) const; /** Maps viewport (GUI) coordinates to view coordinates (see MapView). */ QPointF viewportToView(QPoint input) const; /** Maps viewport (GUI) coordinates to view coordinates (see MapView). */ QPointF viewportToView(QPointF input) const; /** Maps view coordinates (see MapView) to viewport (GUI) coordinates. */ QRectF viewToViewport(const QRectF& input) const; /** Maps view coordinates (see MapView) to viewport (GUI) coordinates. */ QRectF viewToViewport(const QRect& input) const; /** Maps view coordinates (see MapView) to viewport (GUI) coordinates. */ QPointF viewToViewport(QPoint input) const; /** Maps view coordinates (see MapView) to viewport (GUI) coordinates. */ QPointF viewToViewport(QPointF input) const; /** Maps viewport (GUI) coordinates to map coordinates. */ MapCoord viewportToMap(QPoint input) const; /** Maps viewport (GUI) coordinates to map coordinates. */ MapCoordF viewportToMapF(QPoint input) const; /** Maps viewport (GUI) coordinates to map coordinates. */ MapCoordF viewportToMapF(QPointF input) const; /** Maps map coordinates to viewport (GUI) coordinates. */ QPointF mapToViewport(MapCoord input) const; /** Maps map coordinates to viewport (GUI) coordinates. */ QPointF mapToViewport(MapCoordF input) const; /** Maps map coordinates to viewport (GUI) coordinates. */ QPointF mapToViewport(QPointF input) const; /** Maps map coordinates to viewport (GUI) coordinates. */ QRectF mapToViewport(const QRectF& input) const; /** Notifies the MapWidget of the view having zoomed, moved or rotated. */ void viewChanged(MapView::ChangeFlags changes); /** * Returns the current offset (in pixel) during a map pan operation. */ QPoint panOffset() const; /** * Sets the current offset (in pixel) during a map pan operation. */ void setPanOffset(QPoint offset); /** * Adjusts the viewport so the given rect is inside the view. */ void ensureVisibilityOfRect(QRectF map_rect, ZoomOption zoom_option); // clazy:exclude=function-args-by-ref /** * Sets the view so the rect is centered and zooomed to fill the widget. */ void adjustViewToRect(QRectF map_rect, ZoomOption zoom_option); // clazy:exclude=function-args-by-ref /** * Mark a rectangular region of a template cache as "dirty", i.e. redraw needed. * This rect is united with possible previous dirty rects of that cache. * @param view_rect Affected rect in view coordinates. * @param pixel_border Additional affected extent around the view rect in * pixels. Allows to specify zoom-independent extents. * @param front_cache If set to true, invalidates the cache for templates * in front of the map, else invalidates the cache for templates behind the map. */ void markTemplateCacheDirty(const QRectF& view_rect, int pixel_border, bool front_cache); /** * Mark a rectangular region given in map coordinates of the map cache * as dirty, i.e. redraw needed. * This rect is united with possible previous dirty rects of that cache. */ void markObjectAreaDirty(const QRectF& map_rect); /** * Set the given rect as bounding box for the current drawing, i.e. the * graphical display of the active tool. * NOTE: Unlike for markTemplateCacheDirty(), multiple calls to * these methodsdo not result in uniting all given rects, * instead only the last rect is used! * Pass QRect() to disable the current drawing. * @param map_rect Affected rect in map coordinates. * @param pixel_border Additional affected extent around the map rect in * pixels. Allows to specify zoom-independent extents. * @param do_update If set to true, triggers a redraw of the widget. */ void setDrawingBoundingBox(QRectF map_rect, int pixel_border, bool do_update); // clazy:exclude=function-args-by-ref /** * Removes the area set with setDrawingBoundingBox() and triggers a redraw * of the widget, if needed. */ void clearDrawingBoundingBox(); /** Analogon to setDrawingBoundingBox() for activities. */ void setActivityBoundingBox(QRectF map_rect, int pixel_border, bool do_update); // clazy:exclude=function-args-by-ref /** Analogon to clearDrawingBoundingBox() for activities. */ void clearActivityBoundingBox(); /** * Triggers a redraw of the MapWidget at the given area. * @param map_rect Affected rect in map coordinates. * @param pixel_border Additional affected extent around the map rect in * pixels. Allows to specify zoom-independent extents. */ void updateDrawing(const QRectF& map_rect, int pixel_border); /** * Triggers a redraw of the MapWidget at the given area. */ void updateMapRect(const QRectF& map_rect, int pixel_border, QRect& cache_dirty_rect); /** * Triggers a redraw of the MapWidget at the given area. */ void updateViewportRect(QRect viewport_rect, QRect& cache_dirty_rect); /** * Variant of updateDrawing() which waits for some milliseconds before * calling update() in order to avoid excessive redraws. */ void updateDrawingLater(const QRectF& map_rect, int pixel_border); /** * Invalidates all caches and redraws the whole widget. Very slow, try to * avoid this. */ void updateEverything(); /** * Sets all "dirty" region markers to the given rect in viewport coordinates * and triggers a redraw of the MapWidget there. */ void updateEverythingInRect(const QRect& dirty_rect); /** * Sets the function which will be called to display zoom information. */ void setZoomDisplay(std::function setter); /** Specify the label where the MapWidget will display cursor position information. */ void setCursorposLabel(QLabel* cursorpos_label); /** * Specify the system and format for displaying coordinates in * the cursorpos label. See CoordsType for the available types. */ void setCoordsDisplay(CoordsType type); /** Returns the coordinate display type set by setCoordsDisplay(). */ inline CoordsType getCoordsDisplay() const; /** Returns the time in milliseconds since the last user interaction * (mouse press or drag) with the widget. */ int getTimeSinceLastInteraction(); /** Sets the GPS display to use. This is called internally by the GPSDisplay constructor. */ void setGPSDisplay(GPSDisplay* gps_display); /** Sets the GPS temporary markers display to use. This is called internally by the GPSTemporaryMarkers constructor. */ void setTemporaryMarkerDisplay(GPSTemporaryMarkers* marker_display); /** Returns the widget's context menu widget. */ QWidget* getContextMenu(); /** Returns the widget's preferred size. */ QSize sizeHint() const override; /** * @copybrief MainWindowController::keyPressEventFilter * Delegates the keyPress to the active tool, or handles some shortcuts itself. */ bool keyPressEventFilter(QKeyEvent* event); /** * @copybrief MainWindowController::keyPressEventFilter * Delegates the keyRelease to the active tool. */ bool keyReleaseEventFilter(QKeyEvent* event); /** * Support function for input methods. */ QVariant inputMethodQuery(Qt::InputMethodQuery property) const override; public slots: /** * Support function for input methods. * * This two-argument form is undocumented but attempted to call in * QInputMethod::queryFocusObject before doing the query via an event. */ QVariant inputMethodQuery(Qt::InputMethodQuery property, const QVariant& argument) const; /** Enables or disables the touch cursor. */ void enableTouchCursor(bool enabled); signals: /** * Support function for input methods. */ void cursorPositionChanged(); private slots: void updateDrawingLaterSlot(); protected: bool event(QEvent *event) override; virtual void gestureEvent(QGestureEvent* event); void paintEvent(QPaintEvent* event) override; void resizeEvent(QResizeEvent* event) override; // Mouse input void mousePressEvent(QMouseEvent* event) override; void _mousePressEvent(QMouseEvent* event); void mouseMoveEvent(QMouseEvent* event) override; void _mouseMoveEvent(QMouseEvent* event); void mouseReleaseEvent(QMouseEvent* event) override; void _mouseReleaseEvent(QMouseEvent* event); void mouseDoubleClickEvent(QMouseEvent* event) override; void _mouseDoubleClickEvent(QMouseEvent* event); void wheelEvent(QWheelEvent* event) override; void leaveEvent(QEvent* event) override; // Key input (see also slots) void inputMethodEvent(QInputMethodEvent *event) override; void focusOutEvent(QFocusEvent* event) override; void contextMenuEvent(QContextMenuEvent* event) override; private: /** Checks if there is a visible template in the range * from first_template to last_template. */ bool containsVisibleTemplate(int first_template, int last_template) const; /** Checks if there is any visible template above the map. */ bool isAboveTemplateVisible() const; /** Checks if there is any visible template below the map. */ bool isBelowTemplateVisible() const; /** * Redraws the template cache. * @param cache Reference to pointer to the cache. * @param dirty_rect Rectangle of the cache to redraw, in viewport coordinates. * @param first_template Lowest template index to draw. * @param last_template Highest template index to draw. * @param use_background If set to true, fills the cache with white before * drawing the templates, else makes it transparent. */ void updateTemplateCache(QImage& cache, QRect& dirty_rect, int first_template, int last_template, bool use_background); /** * Redraws the map cache in the map cache dirty rect. * @param use_background If set to true, fills the cache with white before * drawing the map, else makes it transparent. */ void updateMapCache(bool use_background); /** Redraws all dirty caches. */ void updateAllDirtyCaches(); /** Shifts the content in the cache by the given amount of pixels. */ void shiftCache(int sx, int sy, QImage& cache); void shiftCache(int sx, int sy, QPixmap& cache); /** * Calculates the bounding box of the given map coordinates rect and * additional pixel extent in integer viewport coordinates. */ QRect calculateViewportBoundingBox(const QRectF& map_rect, int pixel_border) const; /** Internal method for setting a part of a cache as dirty. */ void setDynamicBoundingBox(QRectF map_rect, int pixel_border, QRect& dirty_rect_old, QRectF& dirty_rect_new, int& dirty_rect_new_border, bool do_update); /** Internal method for removing the dirty state of a cache. */ void clearDynamicBoundingBox(QRect& dirty_rect_old, QRectF& dirty_rect_new, int& dirty_rect_new_border); /** Moves the dirty rect by the given amount of pixels. */ void moveDirtyRect(QRect& dirty_rect, qreal x, qreal y); /** Starts a dragging interaction at the given cursor position. */ void startDragging(QPoint cursor_pos); /** Submits a new cursor position during a dragging interaction. */ void updateDragging(QPoint cursor_pos); /** Ends a dragging interaction at the given cursor position. */ void finishDragging(QPoint cursor_pos); /** Cancels a dragging interaction. */ void cancelDragging(); /** Starts a pinching interaction at the given cursor position. * Returns the initial zoom factor. */ qreal startPinching(QPoint center); /** Updates a pinching interaction at the given cursor position. */ void updatePinching(QPoint center, qreal factor); /** Ends a pinching interaction at the given cursor position. */ void finishPinching(QPoint center, qreal factor); /** Cancels a pinching interaction. */ void cancelPinching(); /** Moves the map a given number of big "steps" in x and/or y direction. */ void moveMap(int steps_x, int steps_y); /** Draws a help message at the center of the MapWidget. */ void showHelpMessage(QPainter* painter, const QString& text) const; /** * Updates the content of the zoom display. * * \see setZoomDisplay() */ void updateZoomDisplay(); /** Updates the content of the cursorpos label, set by setCursorposLabel(). */ void updateCursorposLabel(const MapCoordF pos); MapView* view; MapEditorTool* tool; MapEditorActivity* activity; CoordsType coords_type; std::function zoom_display; QLabel* cursorpos_label; QLabel* objecttag_label; MapCoordF last_cursor_pos; bool show_help; bool force_antialiasing; // Dragging (interaction) bool dragging; QPoint drag_start_pos; /** Cursor used when not dragging */ QCursor normal_cursor; // Pinching (interaction) bool pinching; qreal pinching_factor; QPoint pinching_center; // Panning (operation) QPoint pan_offset; // Template caches /** Cache for templates below map layer */ QImage below_template_cache; QRect below_template_cache_dirty_rect; /** Cache for templates above map layer */ QImage above_template_cache; QRect above_template_cache_dirty_rect; /** Map layer cache */ QImage map_cache; QRect map_cache_dirty_rect; // Dirty regions for drawings (tools) and activities /** Dirty rect for the current tool, in viewport coordinates (pixels). */ QRect drawing_dirty_rect; /** Dirty rect for the current tool, in map coordinates. */ QRectF drawing_dirty_rect_map; /** Additional pixel border for the tool dirty rect, in pixels. */ int drawing_dirty_rect_border; /** Dirty rect for the current activity, in viewport coordinates (pixels). */ QRect activity_dirty_rect; /** Dirty rect for the current activity, in map coordinates. */ QRectF activity_dirty_rect_map; /** Additional pixel border for the activity dirty rect, in pixels. */ int activity_dirty_rect_border; /** Cached updates */ QRect cached_update_rect; /** Right-click menu */ PieMenu* context_menu; /** Optional touch cursor for mobile devices */ QScopedPointer touch_cursor; /** For checking for interaction with the widget: the last QTime where * a mouse release event happened. Check for current_pressed_buttons == 0 * and a last_mouse_release_time a given time interval in the past to check * whether the user interacts or recently interacted with the widget. */ QTime last_mouse_release_time; int current_pressed_buttons; /** Optional GPS display */ GPSDisplay* gps_display; /** Optional temporary GPS marker display. */ GPSTemporaryMarkers* marker_display; /** @brief Indicates whether gesture recognition is enabled. */ bool gestures_enabled; }; // ### MapWidget inline code ### inline MapView* MapWidget::getMapView() const { return view; } inline bool MapWidget::gesturesEnabled() const { return gestures_enabled; } inline QPointF MapWidget::mapToViewport(QPointF input) const { // This is a convenience method for situations when we have got a plain QPointF. // We rely on MapCoordF adding nothing but functions to its base, QPointF. static_assert(std::is_base_of::value, "MapCoordF must be derived from QPointF"); static_assert(!std::has_virtual_destructor::value, "MapCoordF and its base must not have virtual members"); static_assert(sizeof(QPointF) == sizeof(MapCoordF), "MapCoordF must have the same size as QPointF"); return mapToViewport(static_cast(input)); } inline QPoint MapWidget::panOffset() const { return pan_offset; } inline MapWidget::CoordsType MapWidget::getCoordsDisplay() const { return coords_type; } } // namespace OpenOrienteering #endif mapper-0.8.1.1/src/gui/map/new_map_dialog.cpp000066400000000000000000000230561325266516600207660ustar00rootroot00000000000000/* * Copyright 2011-2013 Thomas Schöps * Copyright 2012-2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "new_map_dialog.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "fileformats/file_format.h" #include "fileformats/file_format_registry.h" #include "gui/file_dialog.h" #include "gui/util_gui.h" #include "util/util.h" // IWYU pragma: no_forward_declare QLabel // IWYU pragma: no_forward_declare QVBoxLayout namespace OpenOrienteering { NewMapDialog::NewMapDialog(QWidget* parent) : QDialog(parent, Qt::WindowSystemMenuHint | Qt::WindowTitleHint) { this->setWhatsThis(Util::makeWhatThis("new_map.html")); setWindowTitle(tr("Create new map")); auto form_layout = new QFormLayout(); form_layout->addRow(new QLabel(tr("Choose the scale and symbol set for the new map."))); form_layout->addItem(Util::SpacerItem::create(this)); scale_combo = new QComboBox(); scale_combo->setEditable(true); scale_combo->setValidator(new QIntValidator(1, 9999999, scale_combo)); form_layout->addRow(tr("Scale: 1 : "), scale_combo); /// \todo Fix form layout dependency auto layout = new QVBoxLayout(); layout->addLayout(form_layout); layout->addWidget(new QLabel(tr("Symbol sets:"))); symbol_set_list = new QListWidget(); layout->addWidget(symbol_set_list, 1); symbol_set_matching = new QCheckBox(tr("Only show symbol sets matching the selected scale")); layout->addWidget(symbol_set_matching); layout->addItem(Util::SpacerItem::create(this)); auto button_box = new QDialogButtonBox(QDialogButtonBox::Cancel | QDialogButtonBox::Ok); create_button = button_box->button(QDialogButtonBox::Ok); create_button->setIcon(QIcon(QString::fromLatin1(":/images/arrow-right.png"))); create_button->setText(tr("Create")); layout->addWidget(button_box); setLayout(layout); loadSymbolSetMap(); for (auto& item : symbol_set_map) { if (item.first.toInt() != 0) scale_combo->addItem(item.first); } QSettings settings; settings.beginGroup(QString::fromLatin1("NewMapDialog")); const auto default_scale = settings.value(QString::fromLatin1("DefaultScale"), QVariant(10000)).toString(); const auto matching = settings.value(QString::fromLatin1("OnlyMatchingSymbolSets"), QVariant(true)).toBool(); settings.endGroup(); scale_combo->setEditText(default_scale); symbol_set_matching->setChecked(matching); connect(scale_combo, &QComboBox::editTextChanged, this, &NewMapDialog::updateSymbolSetList); connect(symbol_set_list, &QListWidget::itemDoubleClicked, this, &NewMapDialog::symbolSetDoubleClicked); connect(symbol_set_matching, &QCheckBox::stateChanged, this, &NewMapDialog::updateSymbolSetList); connect(button_box, &QDialogButtonBox::rejected, this, &QDialog::reject); connect(button_box, &QDialogButtonBox::accepted, this, &NewMapDialog::createClicked); updateSymbolSetList(); } NewMapDialog::~NewMapDialog() = default; unsigned int NewMapDialog::getSelectedScale() const { return scale_combo->currentText().toUInt(); } QString NewMapDialog::getSelectedSymbolSetPath() const { QListWidgetItem* item = symbol_set_list->currentItem(); if (! item || ! item->data(Qt::UserRole).isValid()) { // FIXME: add proper error handling for release builds, or remove. Q_ASSERT(false); return QString{}; } return item->data(Qt::UserRole).toString(); } void NewMapDialog::accept() { QSettings settings; settings.beginGroup(QString::fromLatin1("NewMapDialog")); settings.setValue(QString::fromLatin1("DefaultScale"), getSelectedScale()); settings.setValue(QString::fromLatin1("OnlyMatchingSymbolSets"), symbol_set_matching->isChecked()); settings.endGroup(); QDialog::accept(); } void NewMapDialog::updateSymbolSetList() { QString scale = scale_combo->currentText(); if (scale.toInt() == 0) { create_button->setEnabled(false); symbol_set_list->setEnabled(false); return; } create_button->setEnabled(true); symbol_set_list->setEnabled(true); symbol_set_list->clear(); auto item = new QListWidgetItem(tr("Empty symbol set")); item->setData(Qt::UserRole, QVariant(QString{})); item->setIcon(QIcon(QString::fromLatin1(":/images/new.png"))); symbol_set_list->addItem(item); QIcon control(QString::fromLatin1(":/images/control.png")); auto it = symbol_set_map.find(scale); if (it != symbol_set_map.end()) { for (auto&& symbol_set : it->second) { item = new QListWidgetItem(symbol_set.completeBaseName()); item->setData(Qt::UserRole, symbol_set.canonicalFilePath()); item->setIcon(control); symbol_set_list->addItem(item); } } if (! symbol_set_matching->isChecked()) { for (it = symbol_set_map.begin(); it != symbol_set_map.end(); ++it ) { if (it->first == scale) continue; bool is_scale = (it->first.toInt() > 0); QString remark = QLatin1String(" (") + QLatin1String(is_scale ? ("1 : ") : "") + it->first + QLatin1Char(')'); for (auto&& symbol_set : it->second) { item = new QListWidgetItem(symbol_set.completeBaseName() + remark); item->setData(Qt::UserRole, symbol_set.canonicalFilePath()); item->setIcon(control); symbol_set_list->addItem(item); } } } load_from_file = new QListWidgetItem(tr("Load symbol set from a file...")); load_from_file->setData(Qt::UserRole, qVariantFromValue(nullptr)); load_from_file->setIcon(QIcon(QString::fromLatin1(":/images/open.png"))); symbol_set_list->addItem(load_from_file); // Select second row, which usually is the first (and only) symbol set symbol_set_list->setCurrentRow(1); } void NewMapDialog::symbolSetDoubleClicked(QListWidgetItem* item) { symbol_set_list->setCurrentItem(item); if (item == load_from_file) showFileDialog(); else accept(); } void NewMapDialog::createClicked() { QListWidgetItem* item = symbol_set_list->currentItem(); if (item == load_from_file) showFileDialog(); else accept(); } void NewMapDialog::showFileDialog() { // Get the saved directory to start in, defaulting to the user's home directory. QString open_directory = QSettings().value(QString::fromLatin1("openFileDirectory"), QDir::homePath()).toString(); // Build the list of supported file filters based on the file format registry QString filters, extensions; for (auto format : FileFormats.formats()) { if (format->supportsImport()) { if (filters.isEmpty()) { filters = format->filter(); extensions = QLatin1String("*.") + format->fileExtensions().join(QString::fromLatin1(" *.")); } else { filters = filters + QLatin1String(";;") + format->filter(); extensions = extensions + QLatin1String(" *.") + format->fileExtensions().join(QString::fromLatin1(" *.")); } } } filters = tr("All symbol set files") + QLatin1String(" (") + extensions + QLatin1String(");;") + filters + QLatin1String(";;") + tr("All files") + QLatin1String(" (*.*)"); QString path = FileDialog::getOpenFileName(this, tr("Load symbol set from a file..."), open_directory, filters); path = QFileInfo(path).canonicalFilePath(); if (path.isEmpty()) return; load_from_file->setData(Qt::UserRole, path); accept(); } void NewMapDialog::loadSymbolSetMap() { loadSymbolSetDir(QDir(QDir::homePath() + QLatin1String("/my symbol sets"))); const auto locations = QDir::searchPaths(QLatin1String("data")); for (const auto& symbol_set_dir : locations) { loadSymbolSetDir(QDir(symbol_set_dir + QLatin1String("/symbol sets"))); } } void NewMapDialog::loadSymbolSetDir(const QDir& symbol_set_dir) { QStringList subdirs = symbol_set_dir.entryList(QDir::Dirs | QDir::Hidden | QDir::NoSymLinks | QDir::NoDotAndDotDot, QDir::NoSort); for (auto&& dir_name : subdirs) { //int scale = dir_name.toInt(); //if (scale == 0) //{ // qDebug() << dir_name + ": not a valid map scale denominator, using it as group name."; //} QDir subdir(symbol_set_dir); if (!subdir.cd(dir_name)) { qDebug("%s: cannot access this directory.", qPrintable(dir_name)); continue; } QStringList symbol_set_filters; for (auto format : FileFormats.formats()) { if (format->supportsImport()) symbol_set_filters << QStringList(format->fileExtensions()).replaceInStrings(QRegExp(QString(QLatin1Char{'^'})), QString::fromLatin1("*.")); } subdir.setNameFilters(symbol_set_filters); QFileInfoList symbol_set_files = subdir.entryInfoList(QDir::Files | QDir::Hidden | QDir::NoSymLinks | QDir::NoDotAndDotDot, QDir::Name); auto item = symbol_set_map.emplace(dir_name, QFileInfoList{}).first; item->second.append(symbol_set_files); } } } // namespace OpenOrienteering mapper-0.8.1.1/src/gui/map/new_map_dialog.h000066400000000000000000000071411325266516600204300ustar00rootroot00000000000000/* * Copyright 2011-2013 Thomas Schöps * Copyright 2012, 2013, 2015, 2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_NEW_MAP_DIALOG_H #define OPENORIENTEERING_NEW_MAP_DIALOG_H #include #include #include #include #include class QCheckBox; class QComboBox; class QDir; class QListWidget; class QListWidgetItem; class QPushButton; class QWidget; namespace OpenOrienteering { /** * Dialog for creating a new map. * Shows scale and symbol set selection. */ class NewMapDialog : public QDialog { Q_OBJECT public: /** Constructs a dialog for scale and symbol set of a new map. */ NewMapDialog(QWidget* parent = nullptr); ~NewMapDialog() override; /** Get the denominator of the chosen map scale. * For a scale of 1:10000, this function returns 10000. */ unsigned int getSelectedScale() const; /** Get the full path of the selected symbol set. * Returns an empty string if the map shall be created with an empty symbol set. */ QString getSelectedSymbolSetPath() const; public slots: /** Updates the list of symbol sets for the chosen map scale [denominator]. */ void updateSymbolSetList(); /** Accepts a selected symbol set, or triggers the file dialog. */ void symbolSetDoubleClicked(QListWidgetItem* item); /** Accepts the selected symbol set, or triggers the file dialog. */ void createClicked(); /** Hides the dialog and accepts the input. */ void accept() override; protected: struct SymbolSetKeyCompare { bool operator() (const QString& a, const QString& b) const { bool ok1, ok2; int a_int = a.toInt(&ok1); int b_int = b.toInt(&ok2); if (ok1) { if (ok2) return a_int < b_int; else return true; } else { if (ok2) return false; else return a.compare(b); } } }; /** A type for mapping map scales to lists of symbol sets. */ typedef std::map SymbolSetMap; /** Loads all available symbol. */ void loadSymbolSetMap(); /** Adds the symbol sets from a particular base directory. */ void loadSymbolSetDir(const QDir& symbol_set_dir); /** Open a dialog for loading a symbol set from a file. */ void showFileDialog(); private: /** A mapping from map scales to lists of matching symbol set. */ SymbolSetMap symbol_set_map; // scale to vector of symbol set names; TODO: store that globally / dir watcher /** The map scale input widget. */ QComboBox* scale_combo; /** The symbol set selection widget. */ QListWidget* symbol_set_list; /** The list item for loading a symbol set from a file. */ QListWidgetItem* load_from_file; /** This check box controls whether only matching or all symbol sets are displayed. */ QCheckBox* symbol_set_matching; /** The button for accepting the selected map scale and symbol set. */ QPushButton* create_button; }; } // namespace OpenOrienteering #endif mapper-0.8.1.1/src/gui/modifier_key.cpp000066400000000000000000000044241325266516600177100ustar00rootroot00000000000000/* * Copyright 2013, 2015 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "modifier_key.h" #include #include #include namespace OpenOrienteering { ModifierKey::ModifierKey(int key) : native_text { QKeySequence((int)key).toString(QKeySequence::NativeText) } { if (native_text.endsWith(QLatin1Char('+'))) { native_text.chop(1); } } ModifierKey::ModifierKey(Qt::KeyboardModifiers keys) : ModifierKey { (int)keys } { // nothing else } ModifierKey::ModifierKey(Qt::Key key) : ModifierKey { (int)key } { // nothing else } const ModifierKey& ModifierKey::alt() { static const ModifierKey key(Qt::AltModifier); return key; } const ModifierKey& ModifierKey::control() { static const ModifierKey key(Qt::ControlModifier); return key; } const ModifierKey& ModifierKey::controlShift() { static const ModifierKey key(Qt::ControlModifier | Qt::ShiftModifier); return key; } const ModifierKey& ModifierKey::meta() { static const ModifierKey key(Qt::MetaModifier); return key; } const ModifierKey& ModifierKey::shift() { static const ModifierKey key(Qt::ShiftModifier); return key; } const ModifierKey& ModifierKey::space() { static const ModifierKey key(Qt::Key_Space); return key; } const ModifierKey& ModifierKey::return_key() { static const ModifierKey key(Qt::Key_Return); return key; } const ModifierKey& ModifierKey::backspace() { static const ModifierKey key(Qt::Key_Backspace); return key; } const ModifierKey& ModifierKey::escape() { static const ModifierKey key(Qt::Key_Escape); return key; } } // namespace OpenOrienteering mapper-0.8.1.1/src/gui/modifier_key.h000066400000000000000000000067611325266516600173630ustar00rootroot00000000000000/* * Copyright 2013, 2015 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_MODIFIER_KEY_H #define OPENORIENTEERING_MODIFIER_KEY_H #include #include namespace OpenOrienteering { /** * A class that helps to deal efficiently with platform and localization issues * of modifier keys. * * It is based on QKeySequence::toString(QKeySequence::NativeText) which provides * localization and deals with swapping Ctrl and Cmd on Mac OS X. In contrast * to QKeySequence, ModifierKey has an implicit operator for casting to QString, * and it removes the trailing '+' from pseudo key sequences which consist of * modifier keys only. Static methods provide efficient translations of the * pure modifier keys. * * For true QKeySequences, call QKeySequence::toString(QKeySequence::NativeText) * directly. * * On Windows and Linux, the keys will be displayed as word ("Shift" etc.). * On OS X, the keys will be displayed as graphical symbols ("⇧", i.e. * Unicode character U+21E7, etc.). * * Synopsis: * * QString text = tr("%1+Click to add a point.").arg(ModifierKey::control()); * QString more = tr("%1+Click to select a point.").arg(ModifierKey(Qt::ALT + Qt::ShiftModifier)); * * // BUT: * QString help = help_action.shortcut().toString(QKeySequence::NativeText); */ class ModifierKey { protected: /** Constructs a new ModifierKey for the given key. */ explicit ModifierKey(int key); public: /** Constructs a new ModifierKey for the given combination of KeyboardModifiers. */ explicit ModifierKey(Qt::KeyboardModifiers keys); /** Constructs a new ModifierKey for the given key. */ explicit ModifierKey(Qt::Key key); /** Returns a string representation for user interface purposes. * * This operator is intented to be used for implicit type casts. */ operator QString() const; /** Returns a shared Alt modifier key. */ static const ModifierKey& alt(); /** Returns a shared Control modifier key. */ static const ModifierKey& control(); /** Returns a shared Control+Shift modifier key. */ static const ModifierKey& controlShift(); /** Returns a shared Meta modifier key. */ static const ModifierKey& meta(); /** Returns a shared Shift modifier key. */ static const ModifierKey& shift(); /** Returns a shared Space key. */ static const ModifierKey& space(); /** Returns a shared Return key. */ static const ModifierKey& return_key(); /** Returns a shared Backspace key. */ static const ModifierKey& backspace(); /** Returns a shared Escape modifier key. */ static const ModifierKey& escape(); private: /** The native text (localized, adapted to the system). */ QString native_text; }; // Inline implementation inline ModifierKey::operator QString() const { return native_text; } } // namespace OpenOrienteering #endif mapper-0.8.1.1/src/gui/print_progress_dialog.cpp000066400000000000000000000041201325266516600216320ustar00rootroot00000000000000/* * Copyright 2013-2015 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifdef QT_PRINTSUPPORT_LIB #include "print_progress_dialog.h" #include #include #include #include "core/map_printer.h" namespace OpenOrienteering { PrintProgressDialog::PrintProgressDialog(MapPrinter* map_printer, QWidget* parent, Qt::WindowFlags f) : QProgressDialog(parent, f) , map_printer(map_printer) { setWindowModality(Qt::ApplicationModal); // Required for OSX, cf. QTBUG-40112 setRange(0, 100); setMinimumDuration(0); setValue(0); Q_ASSERT(map_printer); connect(map_printer, &MapPrinter::printProgress, this, &PrintProgressDialog::setProgress); connect(this, &PrintProgressDialog::canceled, map_printer, &MapPrinter::cancelPrintMap); } PrintProgressDialog::~PrintProgressDialog() { // nothing, not inlined } void PrintProgressDialog::paintRequested(QPrinter* printer) { if (!map_printer->printMap(printer)) { QMessageBox::warning( parentWidget(), tr("Printing", "PrintWidget"), tr("An error occurred during processing.", "PrintWidget"), QMessageBox::Ok, QMessageBox::Ok ); } } void PrintProgressDialog::setProgress(int value, const QString& status) { setLabelText(status); setValue(value); if (!isVisible() && value < maximum()) { show(); } QApplication::processEvents(); // Drawing and Cancel events } } // namespace OpenOrienteering #endif mapper-0.8.1.1/src/gui/print_progress_dialog.h000066400000000000000000000046541325266516600213130ustar00rootroot00000000000000/* * Copyright 2013-2015 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifdef QT_PRINTSUPPORT_LIB #ifndef OPENORIENTEERING_PRINT_WIDGET_P_H #define OPENORIENTEERING_PRINT_WIDGET_P_H #include #include #include #include class QPrinter; class QWidget; namespace OpenOrienteering { class MapPrinter; /** * PrintProgressDialog is a variation of QProgressDialog to be used with MapPrinter. * * PrintProgressDialog connects to the MapPrint::printMapProgress() signal. * It provides a paintRequested slot which is to be connected to the * corresponding QPrintPreviewDialog signal. * * This dialog is modal (for the application) by default. */ class PrintProgressDialog : public QProgressDialog { Q_OBJECT public: /** * Constructs a new dialog for the given MapPrinter. * * map_printer must not be nullptr. */ PrintProgressDialog(MapPrinter* map_printer, QWidget* parent = nullptr, Qt::WindowFlags f = 0); /** * Destructor. */ ~PrintProgressDialog() override; public slots: /** * Listens to and forwards paint requests. * * Shows an error message if printing fails. */ void paintRequested(QPrinter* printer); protected slots: /** * Listens to printing progress messages. * * Shows the dialog if it was hidden, and processes events before returning. * This makes it possible to react on the dialog's Cancel button, and to * draw UI updates. * * @param value The progress, from 0 (not started) to 100 (finished). * @param message The text to be shown as a label to the progress. */ void setProgress(int value, const QString& message); private: MapPrinter* const map_printer; }; #endif } // namespace OpenOrienteering #endif // QT_PRINTSUPPORT_LIB mapper-0.8.1.1/src/gui/print_tool.cpp000066400000000000000000000255151325266516600174370ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2012-2016 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifdef QT_PRINTSUPPORT_LIB #include "print_tool.h" #include #include #include "core/map.h" #include "core/map_printer.h" #include "gui/map/map_editor.h" #include "gui/map/map_widget.h" namespace OpenOrienteering { PrintTool::PrintTool(MapEditorController* editor, MapPrinter* map_printer) : MapEditorTool { editor, Other, nullptr } , map_printer { map_printer } , region { Unknown } , dragging { false } { Q_ASSERT(editor); Q_ASSERT(map_printer); connect(map_printer, &MapPrinter::printAreaChanged, this, &PrintTool::updatePrintArea); connect(map_printer, &MapPrinter::pageFormatChanged, this, &PrintTool::updatePrintArea); // Page breaks may change upon scale changes. connect(map_printer, &MapPrinter::optionsChanged, this, &PrintTool::updatePrintArea); } PrintTool::~PrintTool() { // nothing, not inlined } void PrintTool::init() { setStatusBarText(tr("Drag: Move the map, the print area or the area's borders. ")); updatePrintArea(); MapEditorTool::init(); } const QCursor& PrintTool::getCursor() const { static auto const cursor = QCursor{ Qt::ArrowCursor }; return cursor; } bool PrintTool::mousePressEvent(QMouseEvent* event, MapCoordF map_coord, MapWidget* widget) { if (event->button() == Qt::LeftButton) { mouseMoved(map_coord, widget); dragging = true; click_pos = event->pos(); click_pos_map = map_coord; if (region == Inside || region == Outside) widget->setCursor(Qt::ClosedHandCursor); return true; } if (event->button() == Qt::RightButton) { return true; // disable context menu } return false; } bool PrintTool::mouseMoveEvent(QMouseEvent* event, MapCoordF map_coord, MapWidget* widget) { if (dragging && event->buttons() & Qt::LeftButton) { if (region == Outside) { mapWidget()->getMapView()->setPanOffset(event->pos() - click_pos); } else { updateDragging(map_coord); } return true; } mouseMoved(map_coord, widget); return false; } bool PrintTool::mouseReleaseEvent(QMouseEvent* event, MapCoordF map_coord, MapWidget* widget) { if (dragging && event->button() == Qt::LeftButton) { if (region == Outside) { mapWidget()->getMapView()->finishPanning(event->pos() - click_pos); } else { updateDragging(map_coord); } dragging = false; region = Unknown; // forces mouseMoved() to update cursor and status mouseMoved(map_coord, widget); return true; } return false; } void PrintTool::draw(QPainter* painter, MapWidget* widget) { painter->save(); QRect view_area = QRect(0, 0, widget->width(), widget->height()); QRect print_area = widget->mapToViewport(map_printer->getPrintArea()).toRect(); qreal scale_adjustment = map_printer->getScaleAdjustment(); QSizeF page_size = widget->mapToViewport(map_printer->getPageFormat().page_rect).size() / scale_adjustment; // Strongly darken the region outside the print area painter->setBrush(QColor(0, 0, 0, 160)); painter->setPen(Qt::NoPen); QPainterPath outside_path; outside_path.addRect(view_area); outside_path.addRect(view_area.intersected(print_area)); painter->drawPath(outside_path); Q_ASSERT(!map_printer->horizontalPagePositions().empty()); Q_ASSERT(!map_printer->verticalPagePositions().empty()); QPointF outer_top_left = widget->mapToViewport( QPointF( map_printer->horizontalPagePositions().front(), map_printer->verticalPagePositions().front() ) ); QPointF outer_bottom_right = widget->mapToViewport( QPointF( map_printer->horizontalPagePositions().back(), map_printer->verticalPagePositions().back() ) ); outer_bottom_right += QPointF(page_size.width(), page_size.height()); QRectF outer_rect(outer_top_left, outer_bottom_right); // Draw red lines for page breaks QColor top_left_margin_color(255, 0, 0, 160); QColor bottom_right_margin_color(255, 128, 128, 160); painter->setBrush(Qt::NoBrush); // The relative length of the page dimensions to be actual drawn. QSizeF drawing_size(page_size * scale_adjustment); if (map_printer->horizontalPagePositions().size() > 1) { drawing_size.setWidth(drawing_size.width() * 0.9 * ( map_printer->horizontalPagePositions()[1] - map_printer->horizontalPagePositions()[0] ) / map_printer->getPageFormat().page_rect.width() ); } if (map_printer->verticalPagePositions().size() > 1) { drawing_size.setHeight(drawing_size.height() * 0.9 * ( map_printer->verticalPagePositions()[1] - map_printer->verticalPagePositions()[0] ) / map_printer->getPageFormat().page_rect.height() ); } int h = 0; for (auto hpos : map_printer->horizontalPagePositions()) { ++h; if (h > 100) // Don't visualize too many pages. break; int v = 0; for (auto vpos : map_printer->verticalPagePositions()) { ++v; if (h+v > 100) // Don't visualize too many pages. break; QPointF pos = widget->mapToViewport(MapCoordF(hpos, vpos)); painter->setPen(top_left_margin_color); // Left vertical line painter->drawLine(QLineF{pos.x(), pos.y(), pos.x(), pos.y()+drawing_size.height()}); // Top horizontal line painter->drawLine(QLineF{pos.x(), pos.y(), pos.x()+drawing_size.width(), pos.y()}); pos += QPointF(page_size.width(), page_size.height()); painter->setPen(bottom_right_margin_color); // Right vertical line painter->drawLine(QLineF{pos.x(), pos.y()-drawing_size.height(), pos.x(), pos.y()}); // Bottom horizontal line painter->drawLine(QLineF{pos.x()-drawing_size.width(), pos.y(), pos.x(), pos.y()}); } } painter->setPen(top_left_margin_color); painter->drawLine(QLineF{outer_rect.left(), outer_rect.top(), outer_rect.left(), outer_rect.bottom()}); painter->drawLine(QLineF{outer_rect.left(), outer_rect.top(), outer_rect.right(), outer_rect.top()}); painter->setPen(bottom_right_margin_color); painter->drawLine(QLineF{outer_rect.right(), outer_rect.top(), outer_rect.right(), outer_rect.bottom()}); painter->drawLine(QLineF{outer_rect.left(), outer_rect.bottom(), outer_rect.right(), outer_rect.bottom()}); QRectF print_area_f(print_area); QPen marker(Qt::red); marker.setWidth(4); painter->setPen(marker); painter->setOpacity(0.5); if (region == Inside) { painter->drawRect(print_area_f); } if (region & LeftBorder) { painter->drawLine(print_area_f.topLeft(), print_area_f.bottomLeft()); } if (region & TopBorder) { painter->drawLine(print_area_f.topLeft(), print_area_f.topRight()); } if (region & RightBorder) { painter->drawLine(print_area_f.topRight(), print_area_f.bottomRight()); } if (region & BottomBorder) { painter->drawLine(print_area_f.bottomLeft(), print_area_f.bottomRight()); } painter->restore(); } void PrintTool::updatePrintArea() { // The print area visualization is updated by redrawing the whole map. // TODO: Replace with a more explicit way of marking the whole map area as dirty. editor->getMap()->setDrawingBoundingBox(QRectF(-1000000, -1000000, 2000000, 2000000), 0); } void PrintTool::updateDragging(MapCoordF mouse_pos_map) { QPointF delta = QPointF(mouse_pos_map - click_pos_map); QRectF area = map_printer->getPrintArea(); switch (region) { case Inside: area.moveTopLeft(area.topLeft() + delta); break; case LeftBorder: area.setLeft(area.left() + delta.rx()); break; case TopLeftCorner: area.setTopLeft(area.topLeft() + delta); break; case TopBorder: area.setTop(area.top() + delta.ry()); break; case TopRightCorner: area.setTopRight(area.topRight() + delta); break; case RightBorder: area.setRight(area.right() + delta.rx()); break; case BottomRightCorner: area.setBottomRight(area.bottomRight() + delta); break; case BottomBorder: area.setBottom(area.bottom() + delta.ry()); break; case BottomLeftCorner: area.setBottomLeft(area.bottomLeft() + delta); break; case Outside: Q_ASSERT(false); // Handled outside. case Unknown: ; // Nothing } if (area.left() < area.right() && area.top() < area.bottom()) { map_printer->setPrintArea(area); click_pos_map = mouse_pos_map; } } void PrintTool::mouseMoved(MapCoordF mouse_pos_map, MapWidget* widget) { Q_ASSERT(!dragging); // No change while dragging! static const qreal margin_width = 16.0; static const qreal outer_margin = 8.0; QRectF print_area = widget->mapToViewport(map_printer->getPrintArea()); print_area.adjust(-outer_margin, -outer_margin, outer_margin, outer_margin); QPointF mouse_pos = widget->mapToViewport(mouse_pos_map); int new_region = Outside; if (print_area.contains(mouse_pos)) { new_region = Inside; if (mouse_pos.rx() < print_area.left() + margin_width) { new_region |= LeftBorder; } else if (mouse_pos.rx() > print_area.right() - margin_width) { new_region |= RightBorder; } if (mouse_pos.ry() < print_area.top() + margin_width) { new_region |= TopBorder; } else if (mouse_pos.ry() > print_area.bottom() - margin_width) { new_region |= BottomBorder; } } if (new_region != region) { region = InteractionRegion(new_region); switch (region) { case Inside: setStatusBarText(tr("Drag: Move the print area. ")); widget->setCursor(Qt::OpenHandCursor); break; case Outside: setStatusBarText(tr("Drag: Move the map. ")); widget->setCursor(Qt::ArrowCursor); break; case LeftBorder: case RightBorder: setStatusBarText(tr("Drag: Move the print area's border. ")); widget->setCursor(Qt::SizeHorCursor); break; case TopBorder: case BottomBorder: setStatusBarText(tr("Drag: Move the print area's border. ")); widget->setCursor(Qt::SizeVerCursor); break; case TopLeftCorner: case BottomRightCorner: setStatusBarText(tr("Drag: Move the print area's borders. ")); widget->setCursor(Qt::SizeFDiagCursor); break; case TopRightCorner: case BottomLeftCorner: setStatusBarText(tr("Drag: Move the print area's borders. ")); widget->setCursor(Qt::SizeBDiagCursor); break; case Unknown: setStatusBarText(tr("Drag: Move the map, the print area or the area's borders. ")); widget->setCursor(Qt::ArrowCursor); } updatePrintArea(); } } } // namespace OpenOrienteering #endif // QT_PRINTSUPPORT_LIB mapper-0.8.1.1/src/gui/print_tool.h000066400000000000000000000074161325266516600171040ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2012-2016 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_PRINT_TOOL_H #define OPENORIENTEERING_PRINT_TOOL_H #ifdef QT_PRINTSUPPORT_LIB #include #include #include "core/map_coord.h" #include "tools/tool.h" class QCursor; class QMouseEvent; class QPainter; namespace OpenOrienteering { class MapEditorController; class MapPrinter; class MapWidget; /** * The PrintTool lets the user see and modify the print area on the map * by dragging * * It interacts with a MapEditorController and a PrintWidget which are set in * the constructor. */ class PrintTool : public MapEditorTool { Q_OBJECT public: /** Constructs a new PrintTool to configure the given map printer in the * context of the editor. * * The parameters must not be null. */ PrintTool(MapEditorController* editor, MapPrinter* map_printer); ~PrintTool() override; /** Notifies the tool that it becomes active. */ void init() override; /** Always returns the tool's default cursor. */ const QCursor& getCursor() const override; /** Starts a dragging interaction. */ bool mousePressEvent(QMouseEvent* event, MapCoordF map_coord, MapWidget* widget) override; /** Updates the state of a running dragging interaction. When not dragging, * it will update the cursor to indicate a possible interaction. */ bool mouseMoveEvent(QMouseEvent* event, MapCoordF map_coord, MapWidget* widget) override; /** Finishes dragging interactions. */ bool mouseReleaseEvent(QMouseEvent* event, MapCoordF map_coord, MapWidget* widget) override; /** Draws a visualization of the print area the map widget. */ void draw(QPainter* painter, MapWidget* widget) override; public slots: /** Updates the print area visualization in the map editors. */ void updatePrintArea(); protected: /** Modifies the print area while dragging. * This must not be called when the region is Outside. */ void updateDragging(MapCoordF mouse_pos_map); /** Updates the current interaction region. * This must not be called during dragging. */ void mouseMoved(MapCoordF mouse_pos_map, MapWidget* widget); /** Regions of interaction with the print area. */ enum InteractionRegion { Inside = 0x00, Outside = 0x01, LeftBorder = 0x02, TopLeftCorner = 0x06, TopBorder = 0x04, TopRightCorner = 0x0c, RightBorder = 0x08, BottomRightCorner = 0x18, BottomBorder = 0x10, BottomLeftCorner = 0x12, Unknown = 0xFF }; /** The map printer this tool is operation on. */ MapPrinter* const map_printer; /** The region of the print area where the current interaction takes place. */ InteractionRegion region; /** Indicates whether an interaction is taking place at the moment. */ bool dragging; /** The screen position where the initial click was made. */ QPoint click_pos; /** The map position where the initial click was made. */ MapCoordF click_pos_map; }; #endif // QT_PRINTSUPPORT_LIB } // namespace OpenOrienteering #endif // OPENORIENTEERING_PRINT_TOOL_H mapper-0.8.1.1/src/gui/print_widget.cpp000066400000000000000000001305561325266516600177470ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2012-2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifdef QT_PRINTSUPPORT_LIB #include "print_widget.h" #include #include #include #include #include // IWYU pragma: keep #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "core/georeferencing.h" #include "core/map.h" #include "core/map_printer.h" #include "core/map_view.h" #include "gui/file_dialog.h" #include "gui/main_window.h" #include "gui/print_progress_dialog.h" #include "gui/print_tool.h" #include "gui/util_gui.h" #include "gui/map/map_editor.h" #include "gui/map/map_widget.h" #include "templates/template.h" // IWYU pragma: keep #include "templates/world_file.h" #include "util/backports.h" #include "util/scoped_signals_blocker.h" namespace OpenOrienteering { namespace { QToolButton* createPrintModeButton(const QIcon& icon, const QString& label, QWidget* parent = nullptr) { static const QSize icon_size(48,48); auto button = new QToolButton(parent); button->setAutoRaise(true); button->setCheckable(true); button->setIconSize(icon_size); button->setIcon(icon); button->setText(label); button->setToolButtonStyle(Qt::ToolButtonTextUnderIcon); return button; } } // namespace //### PrintWidget ### PrintWidget::PrintWidget(Map* map, MainWindow* main_window, MapView* main_view, MapEditorController* editor, QWidget* parent) : QWidget { parent } , task { UNDEFINED_TASK } , map { map } , map_printer { new MapPrinter(*map, main_view) } , main_window { main_window } , main_view { main_view } , editor { editor } , print_tool { nullptr } , active { false } { Q_ASSERT(main_window); layout = new QFormLayout(); target_combo = new QComboBox(); target_combo->setMinimumWidth(1); // Not zero, but not as long as the items layout->addRow(Util::Headline::create(tr("Printer:")), target_combo); if (PlatformPrinterProperties::dialogSupported()) { printer_properties_button = new QToolButton(); printer_properties_button->setText(tr("Properties")); layout->addRow(nullptr, printer_properties_button); } else { printer_properties_button = nullptr; } paper_size_combo = new QComboBox(); layout->addRow(tr("Page format:"), paper_size_combo); auto page_size_widget = new QWidget(); auto page_size_layout = new QHBoxLayout(); page_size_widget->setLayout(page_size_layout); page_size_layout->setMargin(0); page_width_edit = Util::SpinBox::create(1, 0.1, 1000.0, tr("mm"), 1.0); page_width_edit->setEnabled(false); page_size_layout->addWidget(page_width_edit, 1); page_size_layout->addWidget(new QLabel(QString::fromLatin1("x")), 0); page_height_edit = Util::SpinBox::create(1, 0.1, 1000.0, tr("mm"), 1.0); page_height_edit->setEnabled(false); page_size_layout->addWidget(page_height_edit, 1); layout->addRow({}, page_size_widget); page_orientation_widget = new QWidget(); auto page_orientation_layout = new QHBoxLayout(); page_orientation_layout->setContentsMargins(QMargins()); page_orientation_widget->setLayout(page_orientation_layout); auto portrait_button = new QRadioButton(tr("Portrait")); page_orientation_layout->addWidget(portrait_button); auto landscape_button = new QRadioButton(tr("Landscape")); page_orientation_layout->addWidget(landscape_button); page_orientation_group = new QButtonGroup(this); page_orientation_group->addButton(portrait_button, QPrinter::Portrait); page_orientation_group->addButton(landscape_button, QPrinter::Landscape); layout->addRow(tr("Page orientation:"), page_orientation_widget); copies_edit = Util::SpinBox::create(1, 99999); layout->addRow(tr("Copies:"), copies_edit); layout->addItem(Util::SpacerItem::create(this)); policy_combo = new QComboBox(); policy_combo->addItem(tr("Single page"), SinglePage); policy_combo->addItem(tr("Custom area"), CustomArea); layout->addRow(Util::Headline::create(tr("Map area:")), policy_combo); // or print/export area center_check = new QCheckBox(tr("Center print area")); layout->addRow(center_check); left_edit = Util::SpinBox::create(2, -999999.9, 999999.9, tr("mm"), 1.0); layout->addRow(tr("Left:"), left_edit); top_edit = Util::SpinBox::create(2, -999999.9, 999999.9, tr("mm"), 1.0); layout->addRow(tr("Top:"), top_edit); width_edit = Util::SpinBox::create(2, -999999.9, 999999.9, tr("mm"), 1.0); layout->addRow(tr("Width:"), width_edit); height_edit = Util::SpinBox::create(2, -999999.9, 999999.9, tr("mm"), 1.0); layout->addRow(tr("Height:"), height_edit); overlap_edit = Util::SpinBox::create(2, -999999.9, 999999.9, tr("mm"), 1.0); layout->addRow(tr("Page overlap:"), overlap_edit); layout->addItem(Util::SpacerItem::create(this)); layout->addRow(Util::Headline::create(tr("Options"))); auto mode_widget = new QWidget(); auto mode_layout = new QHBoxLayout(); mode_widget->setLayout(mode_layout); mode_layout->setMargin(0); vector_mode_button = createPrintModeButton(QIcon(QString::fromLatin1(":/images/print-mode-vector.png")), tr("Vector\ngraphics")); raster_mode_button = createPrintModeButton(QIcon(QString::fromLatin1(":/images/print-mode-raster.png")), tr("Raster\ngraphics")); separations_mode_button = createPrintModeButton(QIcon(QString::fromLatin1(":/images/print-mode-separations.png")), tr("Color\nseparations")); vector_mode_button->setChecked(true); auto mode_button_group = new QButtonGroup(this); mode_button_group->addButton(vector_mode_button); mode_button_group->addButton(raster_mode_button); mode_button_group->addButton(separations_mode_button); mode_layout->addWidget(vector_mode_button); mode_layout->addWidget(raster_mode_button); mode_layout->addWidget(separations_mode_button); mode_layout->addStretch(1); layout->addRow(tr("Mode:"), mode_widget); dpi_combo = new QComboBox(); dpi_combo->setEditable(true); dpi_combo->setValidator(new QRegExpValidator(QRegExp(QLatin1String("^[1-9]\\d{0,4}$|^[1-9]\\d{0,4} ")+tr("dpi")+QLatin1Char('$')), dpi_combo)); // TODO: Implement spinbox-style " dpi" suffix layout->addRow(tr("Resolution:"), dpi_combo); different_scale_check = new QCheckBox(tr("Print in different scale:")); // Limit the difference between nominal and printing scale in order to limit the number of page breaks. int min_scale = qMax(1, int(map->getScaleDenominator() / 10000) * 100); different_scale_edit = Util::SpinBox::create(min_scale, std::numeric_limits::max(), {}, 500); different_scale_edit->setPrefix(QString::fromLatin1("1 : ")); different_scale_edit->setEnabled(false); int different_scale_height = qMax( different_scale_edit->minimumSizeHint().height(), different_scale_check->minimumSizeHint().height() ); different_scale_check->setMinimumHeight(different_scale_height); different_scale_edit->setMinimumHeight(different_scale_height); layout->addRow(different_scale_check, different_scale_edit); // this must be created before its value is used to determine the default setting of page_orientation_combo show_templates_check = new QCheckBox(tr("Show templates")); auto templates_warning_layout = new QHBoxLayout(); QIcon warning_icon = style()->standardIcon(QStyle::SP_MessageBoxWarning); templates_warning_icon = new QLabel(); int pixmap_size = qBound(8, style()->pixelMetric(QStyle::PM_IndicatorHeight), 32); templates_warning_icon->setPixmap(warning_icon.pixmap(QSize(pixmap_size, pixmap_size))); templates_warning_layout->addWidget(templates_warning_icon); templates_warning_text = new QLabel(tr("Template appearance may differ.")); templates_warning_layout->addWidget(templates_warning_text, 1); layout->addRow(show_templates_check, templates_warning_layout); show_grid_check = new QCheckBox(tr("Show grid")); layout->addRow(show_grid_check); overprinting_check = new QCheckBox(tr("Simulate overprinting")); layout->addRow(overprinting_check); world_file_check = new QCheckBox(tr("Save world file")); layout->addRow(world_file_check); world_file_check->hide(); color_mode_combo = new QComboBox(); color_mode_combo->setEditable(false); color_mode_combo->addItem(tr("Default"), QVariant()); color_mode_combo->addItem(tr("Device CMYK (experimental)"), QVariant(true)); layout->addRow(tr("Color mode:"), color_mode_combo); scrolling_content = new QWidget(); scrolling_content->setLayout(layout); auto outer_layout = new QVBoxLayout(); outer_layout->setContentsMargins(QMargins()); scroll_area = new QScrollArea(); scroll_area->setWidget(scrolling_content); scroll_area->setWidgetResizable(true); scroll_area->setMinimumWidth((scrolling_content->sizeHint() + scroll_area->verticalScrollBar()->sizeHint()).width()); outer_layout->addWidget(scroll_area); button_box = new QDialogButtonBox(); QStyleOption style_option(QStyleOption::Version, QStyleOption::SO_DockWidget); button_box->layout()->setContentsMargins( style()->pixelMetric(QStyle::PM_LayoutLeftMargin, &style_option), style()->pixelMetric(QStyle::PM_LayoutTopMargin, &style_option), style()->pixelMetric(QStyle::PM_LayoutRightMargin, &style_option), style()->pixelMetric(QStyle::PM_LayoutBottomMargin, &style_option) ); preview_button = new QPushButton(tr("Preview...")); button_box->addButton(preview_button, QDialogButtonBox::ActionRole); print_button = new QPushButton(tr("Print")); button_box->addButton(print_button, QDialogButtonBox::ActionRole); // Use a distinct export button. // Changing the text at runtime causes distortions on Mac OS X. export_button = new QPushButton(tr("Export...")); export_button->hide(); button_box->addButton(export_button, QDialogButtonBox::ActionRole); auto close_button = button_box->addButton(QDialogButtonBox::Close); outer_layout->addWidget(button_box); setLayout(outer_layout); connect(target_combo, QOverload::of(&QComboBox::currentIndexChanged), this, &PrintWidget::targetChanged); if (printer_properties_button) connect(printer_properties_button, &QAbstractButton::clicked, this, &PrintWidget::propertiesClicked, Qt::QueuedConnection); connect(paper_size_combo, QOverload::of(&QComboBox::currentIndexChanged), this, &PrintWidget::paperSizeChanged); connect(page_width_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &PrintWidget::paperDimensionsChanged); connect(page_orientation_group, QOverload::of(&QButtonGroup::buttonClicked), this, &PrintWidget::pageOrientationChanged); connect(page_height_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &PrintWidget::paperDimensionsChanged); connect(top_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &PrintWidget::printAreaMoved); connect(left_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &PrintWidget::printAreaMoved); connect(width_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &PrintWidget::printAreaResized); connect(height_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &PrintWidget::printAreaResized); connect(overlap_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &PrintWidget::overlapEdited); connect(mode_button_group, QOverload::of(&QButtonGroup::buttonClicked), this, &PrintWidget::printModeChanged); connect(dpi_combo->lineEdit(), &QLineEdit::textEdited, this, &PrintWidget::resolutionEdited); connect(dpi_combo, QOverload::of(&QComboBox::currentIndexChanged), this, &PrintWidget::resolutionEdited); connect(different_scale_check, &QAbstractButton::clicked, this, &PrintWidget::differentScaleClicked); connect(different_scale_edit, QOverload::of(&QSpinBox::valueChanged), this, &PrintWidget::differentScaleEdited); connect(show_templates_check, &QAbstractButton::clicked, this, &PrintWidget::showTemplatesClicked); connect(show_grid_check, &QAbstractButton::clicked, this, &PrintWidget::showGridClicked); connect(overprinting_check, &QAbstractButton::clicked, this, &PrintWidget::overprintingClicked); connect(color_mode_combo, &QComboBox::currentTextChanged, this, &PrintWidget::colorModeChanged); connect(preview_button, &QAbstractButton::clicked, this, &PrintWidget::previewClicked); connect(print_button, &QAbstractButton::clicked, this, &PrintWidget::printClicked); connect(export_button, &QAbstractButton::clicked, this, &PrintWidget::printClicked); connect(close_button, &QAbstractButton::clicked, this, &PrintWidget::closeClicked); policy = map_printer->config().single_page_print_area ? SinglePage : CustomArea; policy_combo->setCurrentIndex(policy_combo->findData(policy)); connect(policy_combo, QOverload::of(&QComboBox::currentIndexChanged), this, &PrintWidget::printAreaPolicyChanged); center_check->setChecked(map_printer->config().center_print_area); connect(center_check, &QAbstractButton::clicked, this, &PrintWidget::applyCenterPolicy); setPageFormat(map_printer->getPageFormat()); connect(map_printer, &MapPrinter::pageFormatChanged, this, &PrintWidget::setPageFormat); connect(map_printer, &MapPrinter::optionsChanged, this, &PrintWidget::setOptions); spotColorPresenceChanged(map->hasSpotColors()); connect(map, &Map::spotColorPresenceChanged, this, &PrintWidget::spotColorPresenceChanged); setPrintArea(map_printer->getPrintArea()); connect(map_printer, &MapPrinter::printAreaChanged, this, &PrintWidget::setPrintArea); connect(map_printer, &MapPrinter::targetChanged, this, &PrintWidget::setTarget); connect(this, &PrintWidget::finished, this, &PrintWidget::savePrinterConfig); } PrintWidget::~PrintWidget() { delete map_printer; } QSize PrintWidget::sizeHint() const { QSize size = QWidget::sizeHint(); size.setHeight(scrolling_content->sizeHint().height() + 2 * scroll_area->frameWidth() + button_box->sizeHint().height() + layout->horizontalSpacing() ); return size; } // slot void PrintWidget::setTask(PrintWidget::TaskFlags type) { if (task != type) { task = type; bool is_print_task = type==PRINT_TASK; bool is_multipage = type.testFlag(MULTIPAGE_FLAG); layout->labelForField(target_combo)->setVisible(is_print_task); target_combo->setVisible(is_print_task); layout->labelForField(copies_edit)->setVisible(is_multipage); copies_edit->setVisible(is_multipage); policy_combo->setVisible(is_multipage); updateTargets(); switch (type) { case PRINT_TASK: // Reset values which are typically modified for exporting if (policy == SinglePage && !map->printerConfig().single_page_print_area) { policy = CustomArea; policy_combo->setCurrentIndex(policy_combo->findData(policy)); } if (map_printer->getPageFormat().paper_size == QPrinter::Custom) { map_printer->setPaperSize(map->printerConfig().page_format.paper_size); } // TODO: Set target to most recently used printer emit taskChanged(tr("Print")); break; case EXPORT_PDF_TASK: map_printer->setTarget(MapPrinter::pdfTarget()); emit taskChanged(tr("PDF export")); break; case EXPORT_IMAGE_TASK: map_printer->setTarget(MapPrinter::imageTarget()); policy = SinglePage; if (policy_combo->itemData(policy_combo->currentIndex()) != policy) { map_printer->setCustomPaperSize(map_printer->getPrintAreaPaperSize()); policy_combo->setCurrentIndex(policy_combo->findData(policy)); } emit taskChanged(tr("Image export")); break; default: emit taskChanged(QString{}); } } } // slot void PrintWidget::savePrinterConfig() const { MapPrinterConfig printer_config(map_printer->config()); printer_config.center_print_area = center_check->isChecked(); if (task.testFlag(MULTIPAGE_FLAG)) { printer_config.single_page_print_area = policy == SinglePage; } if (task.testFlag(EXPORT_IMAGE_TASK)) { // Don't override the printer page format from the custom image format. printer_config.page_format = map->printerConfig().page_format; } map->setPrinterConfig(printer_config); } // slot void PrintWidget::setActive(bool active) { if (this->active != active) { this->active = active; if (active) { // Save the current state of the map view. saved_view_state.clear(); QXmlStreamWriter writer(&saved_view_state); main_view->save(writer, QLatin1String("saved_view"), false); editor->setViewOptionsEnabled(false); // Printers may have been added or removed. updateTargets(); // Update the map view from the current options setOptions(map_printer->getOptions()); connect(main_view, &MapView::visibilityChanged, this, &PrintWidget::onVisibilityChanged); // Set reasonable zoom. bool zoom_to_map = true; if (zoom_to_map) { // Ensure the visibility of the whole map. auto map_extent = map->calculateExtent(true, !main_view->areAllTemplatesHidden(), main_view); editor->getMainWidget()->ensureVisibilityOfRect(map_extent, MapWidget::ContinuousZoom); } else { // Ensure the visibility of the print area. auto print_area(map_printer->getPrintArea()); editor->getMainWidget()->ensureVisibilityOfRect(print_area, MapWidget::ContinuousZoom); } // Activate PrintTool. if (!print_tool) { print_tool = new PrintTool(editor, map_printer); } editor->setOverrideTool(print_tool); editor->setEditingInProgress(true); } else { disconnect(main_view, &MapView::visibilityChanged, this, &PrintWidget::onVisibilityChanged); editor->setEditingInProgress(false); editor->setOverrideTool(nullptr); print_tool = nullptr; // Restore view QXmlStreamReader reader(saved_view_state); reader.readNextStartElement(); main_view->load(reader); editor->setViewOptionsEnabled(true); } } } void PrintWidget::updateTargets() { QVariant current_target = target_combo->itemData(target_combo->currentIndex()); const auto saved_printer = map_printer->getTarget(); const QString saved_printer_name = saved_printer ? saved_printer->printerName() : QString{}; int saved_target_index = -1; int default_printer_index = -1; { const QSignalBlocker block(target_combo); target_combo->clear(); if (task == PRINT_TASK) { // Exporters target_combo->addItem(tr("Save to PDF"), QVariant(int(PdfExporter))); target_combo->insertSeparator(target_combo->count()); target_combo->setCurrentIndex(0); // Printers auto default_printer_name = QPrinterInfo::defaultPrinterName(); printers = QPrinterInfo::availablePrinterNames(); for (int i = 0; i < printers.size(); ++i) { const QString& name = printers[i]; if (name == saved_printer_name) saved_target_index = target_combo->count(); if (name == default_printer_name) default_printer_index = target_combo->count(); target_combo->addItem(name, i); } } // Restore selected target if possible and exit on success if (current_target.isValid()) { int index = target_combo->findData(current_target); if (index >= 0) { target_combo->setCurrentIndex(index); return; } } if (saved_target_index >= 0) // Restore saved target if possible target_combo->setCurrentIndex(saved_target_index); else if (default_printer_index >= 0) // Set default printer as current target target_combo->setCurrentIndex(default_printer_index); // Explicitly invoke signal handler targetChanged(target_combo->currentIndex()); } } // slot void PrintWidget::setTarget(const QPrinterInfo* target) { int target_index = printers.size()-1; if (target == MapPrinter::pdfTarget()) target_index = PdfExporter; else if (target == MapPrinter::imageTarget()) target_index = ImageExporter; else { for (; target_index >= 0; target_index--) { if (target && printers[target_index] == target->printerName()) break; else if (!target) break; } } target_combo->setCurrentIndex(target_combo->findData(QVariant(target_index))); updatePaperSizes(target); updateResolutions(target); bool supports_pages = (target != MapPrinter::imageTarget()); bool supports_copies = (supports_pages && target && QPrinter(*target).supportsMultipleCopies()); copies_edit->setEnabled(supports_copies); layout->labelForField(copies_edit)->setEnabled(supports_copies); bool is_printer = map_printer->isPrinter(); print_button->setVisible(is_printer); print_button->setDefault(is_printer); export_button->setVisible(!is_printer); export_button->setDefault(!is_printer); if (printer_properties_button) printer_properties_button->setEnabled(is_printer); world_file_check->setVisible(!is_printer); // If MapCoord (0,0) maps to projected (0,0), then there is probably // no point in writing a world file. world_file_check->setChecked(!map->getGeoreferencing().toProjectedCoords(MapCoordF{}).isNull()); bool is_image_target = target == MapPrinter::imageTarget(); vector_mode_button->setEnabled(!is_image_target); separations_mode_button->setEnabled(!is_image_target && map->hasSpotColors()); if (is_image_target) { raster_mode_button->setChecked(true); printModeChanged(raster_mode_button); } updateColorMode(); } // slot void PrintWidget::targetChanged(int index) const { if (index < 0) return; int target_index = target_combo->itemData(index).toInt(); Q_ASSERT(target_index >= -2); Q_ASSERT(target_index < printers.size()); if (target_index == PdfExporter) map_printer->setTarget(MapPrinter::pdfTarget()); else if (target_index == ImageExporter) map_printer->setTarget(MapPrinter::imageTarget()); else { auto info = QPrinterInfo::printerInfo(printers[target_index]); map_printer->setTarget(&info); } } // slot void PrintWidget::propertiesClicked() { if (map_printer && map_printer->isPrinter()) { std::shared_ptr buffer; // must not be destroyed before printer. auto printer = map_printer->makePrinter(); Q_ASSERT(printer->outputFormat() == QPrinter::NativeFormat); if (PlatformPrinterProperties::execDialog(printer.get(), buffer, this) == QDialog::Accepted) map_printer->takePrinterSettings(printer.get()); } } void PrintWidget::updatePaperSizes(const QPrinterInfo* target) const { QString prev_paper_size_name = paper_size_combo->currentText(); bool have_custom_size = false; { const QSignalBlocker block(paper_size_combo); paper_size_combo->clear(); QList size_list; if (target) size_list = target->supportedPaperSizes(); if (size_list.isEmpty()) size_list = defaultPaperSizes(); for (auto size : qAsConst(size_list)) { if (size == QPrinter::Custom) have_custom_size = true; // add it once after all other entires else paper_size_combo->addItem(toString(size), size); } if (have_custom_size) paper_size_combo->addItem(toString(QPrinter::Custom), QPrinter::Custom); int paper_size_index = paper_size_combo->findData(map_printer->getPageFormat().paper_size); if (!prev_paper_size_name.isEmpty()) { paper_size_index = paper_size_combo->findText(prev_paper_size_name); } paper_size_combo->setCurrentIndex(qMax(0, paper_size_index)); paperSizeChanged(paper_size_combo->currentIndex()); } } // slot void PrintWidget::setPageFormat(const MapPrinterPageFormat& format) { ScopedMultiSignalsBlocker block( paper_size_combo, page_orientation_group, page_width_edit, page_height_edit, overlap_edit ); paper_size_combo->setCurrentIndex(paper_size_combo->findData(format.paper_size)); page_orientation_group->button(format.orientation)->setChecked(true); page_width_edit->setValue(format.paper_dimensions.width()); page_width_edit->setEnabled(format.paper_size == QPrinter::Custom); page_height_edit->setValue(format.paper_dimensions.height()); page_height_edit->setEnabled(format.paper_size == QPrinter::Custom); // We only have a single overlap edit field, but MapPrinter supports // distinct horizontal and vertical overlap. Choose the minimum. overlap_edit->setValue(qMin(format.h_overlap, format.v_overlap)); applyPrintAreaPolicy(); } // slot void PrintWidget::paperSizeChanged(int index) const { if (index >= 0) { QPrinter::PaperSize paper_size = QPrinter::PaperSize(paper_size_combo->itemData(index).toInt()); map_printer->setPaperSize(paper_size); } } // slot void PrintWidget::paperDimensionsChanged() const { const QSizeF dimensions(page_width_edit->value(), page_height_edit->value()); map_printer->setCustomPaperSize(dimensions); } // slot void PrintWidget::pageOrientationChanged(int id) const { if (id == QPrinter::Portrait || id == QPrinter::Landscape) { map_printer->setPageOrientation((id == QPrinter::Portrait) ? MapPrinterPageFormat::Portrait : MapPrinterPageFormat::Landscape); } } // slot void PrintWidget::printAreaPolicyChanged(int index) { policy = PrintAreaPolicy(policy_combo->itemData(index).toInt()); applyPrintAreaPolicy(); } // slot void PrintWidget::applyPrintAreaPolicy() const { if (policy == SinglePage) { setOverlapEditEnabled(false); auto print_area = map_printer->getPrintArea(); print_area.setSize(map_printer->getPageRectPrintAreaSize()); if (center_check->isChecked()) centerOnMap(print_area); map_printer->setPrintArea(print_area); } else { setOverlapEditEnabled(true); } } // slot void PrintWidget::applyCenterPolicy() const { if (center_check->isChecked()) { auto print_area = map_printer->getPrintArea(); centerOnMap(print_area); map_printer->setPrintArea(print_area); } } void PrintWidget::centerOnMap(QRectF& area) const { auto map_extent = map->calculateExtent(false, show_templates_check->isChecked(), main_view); area.moveLeft(map_extent.center().x() - area.width() / 2); area.moveTop(map_extent.center().y() - area.height() / 2); } // slot void PrintWidget::setPrintArea(const QRectF& area) { ScopedMultiSignalsBlocker block( left_edit, top_edit, width_edit, height_edit ); left_edit->setValue(area.left()); top_edit->setValue(-area.top()); // Flip sign! width_edit->setValue(area.width()); height_edit->setValue(area.height()); if (center_check->isChecked()) { auto centered_area = map_printer->getPrintArea(); centerOnMap(centered_area); if ( qAbs(centered_area.left() - area.left()) > 0.005 || qAbs(centered_area.top() - area.top()) > 0.005 ) { // No longer centered. center_check->setChecked(false); } } if (policy == SinglePage) { if (map_printer->getPageFormat().paper_size == QPrinter::Custom || !task.testFlag(MULTIPAGE_FLAG)) { // Update custom paper size from print area size QSizeF area_dimensions = area.size() * map_printer->getScaleAdjustment(); if (map_printer->getPageFormat().page_rect.size() != area_dimensions) { // Don't force a custom paper size unless necessary map_printer->setCustomPaperSize(area_dimensions); } } else { QSizeF page_dimensions = map_printer->getPageRectPrintAreaSize(); if ( qAbs(area.width() - page_dimensions.width()) > 0.005 || qAbs(area.height() - page_dimensions.height()) > 0.005 ) { // No longer single page. block << policy_combo; policy_combo->setCurrentIndex(policy_combo->findData(CustomArea)); center_check->setChecked(false); setOverlapEditEnabled(true); } } } } // slot void PrintWidget::printAreaMoved() { auto area = map_printer->getPrintArea(); area.moveLeft(left_edit->value()); area.moveTop(-top_edit->value()); // Flip sign! map_printer->setPrintArea(area); } // slot void PrintWidget::printAreaResized() { auto area = map_printer->getPrintArea(); area.setWidth(width_edit->value()); area.setHeight(height_edit->value()); map_printer->setPrintArea(area); } // slot void PrintWidget::overlapEdited(double overlap) { map_printer->setOverlap(overlap, overlap); } void PrintWidget::setOverlapEditEnabled(bool state) const { overlap_edit->setEnabled(state); layout->labelForField(overlap_edit)->setEnabled(state); } // slot void PrintWidget::setOptions(const MapPrinterOptions& options) { using namespace Util::TristateCheckbox; ScopedMultiSignalsBlocker block( dpi_combo->lineEdit(), show_templates_check, show_grid_check, overprinting_check, color_mode_combo, vector_mode_button, raster_mode_button, separations_mode_button, different_scale_check, different_scale_edit ); switch (options.mode) { default: qWarning("Unhandled MapPrinterMode"); // fall through in release build case MapPrinterOptions::Vector: vector_mode_button->setChecked(true); setEnabledAndChecked(show_templates_check, options.show_templates); setEnabledAndChecked(show_grid_check, options.show_grid); setDisabledAndChecked(overprinting_check, options.simulate_overprinting); main_view->setAllTemplatesHidden(!options.show_templates); main_view->setGridVisible(options.show_grid); main_view->setOverprintingSimulationEnabled(false); break; case MapPrinterOptions::Raster: raster_mode_button->setChecked(true); setEnabledAndChecked(show_templates_check, options.show_templates); setEnabledAndChecked(show_grid_check, options.show_grid); setEnabledAndChecked(overprinting_check, options.simulate_overprinting); main_view->setAllTemplatesHidden(!options.show_templates); main_view->setGridVisible(options.show_grid); main_view->setOverprintingSimulationEnabled(options.simulate_overprinting); break; case MapPrinterOptions::Separations: separations_mode_button->setChecked(true); setDisabledAndChecked(show_templates_check, options.show_templates); setDisabledAndChecked(show_grid_check, options.show_grid); setDisabledAndChecked(overprinting_check, options.simulate_overprinting); main_view->setAllTemplatesHidden(true); main_view->setGridVisible(false); main_view->setOverprintingSimulationEnabled(true); break; } switch (options.color_mode) { default: qWarning("Unhandled ColorMode"); // fall through in release build case MapPrinterOptions::DefaultColorMode: color_mode_combo->setCurrentIndex(0); break; case MapPrinterOptions::DeviceCmyk: color_mode_combo->setCurrentIndex(1); break; } checkTemplateConfiguration(); updateColorMode(); static QString dpi_template(QLatin1String("%1 ") + tr("dpi")); dpi_combo->setEditText(dpi_template.arg(options.resolution)); if (options.scale != map->getScaleDenominator()) { different_scale_check->setChecked(true); different_scale_edit->setEnabled(true); } auto scale = int(options.scale); different_scale_edit->setValue(scale); differentScaleEdited(scale); } void PrintWidget::onVisibilityChanged() { map_printer->setPrintTemplates(!main_view->areAllTemplatesHidden()); map_printer->setPrintGrid(main_view->isGridVisible()); map_printer->setSimulateOverprinting(main_view->isOverprintingSimulationEnabled()); } void PrintWidget::updateResolutions(const QPrinterInfo* target) const { static const QList default_resolutions(QList() << 150 << 300 << 600 << 1200); // Numeric resolution list QList supported_resolutions; if (target) { QPrinter pr(*target, QPrinter::HighResolution); supported_resolutions = pr.supportedResolutions(); if (supported_resolutions.size() == 1 && supported_resolutions[0] == 72) { // X11/CUPS supported_resolutions.clear(); } } if (supported_resolutions.isEmpty()) supported_resolutions = default_resolutions; // Resolution list item with unit "dpi" static QString dpi_template(QLatin1String("%1 ") + tr("dpi")); QStringList resolutions; resolutions.reserve(supported_resolutions.size()); for (auto resolution : qAsConst(supported_resolutions)) resolutions << dpi_template.arg(resolution); QString dpi_text = dpi_combo->currentText(); { const QSignalBlocker block(dpi_combo); dpi_combo->clear(); dpi_combo->addItems(resolutions); } dpi_combo->lineEdit()->setText(dpi_text.isEmpty() ? dpi_template.arg(600) : dpi_text); } void PrintWidget::updateColorMode() { bool enable = map_printer->getTarget() == MapPrinter::pdfTarget() && !raster_mode_button->isChecked(); color_mode_combo->setEnabled(enable); layout->labelForField(color_mode_combo)->setEnabled(enable); if (!enable) color_mode_combo->setCurrentIndex(0); } // slot void PrintWidget::resolutionEdited() { auto resolution_text = dpi_combo->currentText(); auto index_of_space = resolution_text.indexOf(QLatin1Char(' ')); auto dpi_value = resolution_text.leftRef(index_of_space).toInt(); if (dpi_value > 0) { auto pos = dpi_combo->lineEdit()->cursorPosition(); map_printer->setResolution(dpi_value); dpi_combo->lineEdit()->setCursorPosition(pos); } } // slot void PrintWidget::differentScaleClicked(bool checked) { if (!checked) different_scale_edit->setValue(int(map->getScaleDenominator())); different_scale_edit->setEnabled(checked); } // slot void PrintWidget::differentScaleEdited(int value) { map_printer->setScale(static_cast(value)); applyPrintAreaPolicy(); if (different_scale_edit->value() < 500) { different_scale_edit->setSingleStep(500 - different_scale_edit->value()); } else { different_scale_edit->setSingleStep(500); } } // slot void PrintWidget::spotColorPresenceChanged(bool has_spot_colors) { separations_mode_button->setEnabled(has_spot_colors); if (!has_spot_colors && separations_mode_button->isChecked()) { map_printer->setMode(MapPrinterOptions::Vector); } } // slot void PrintWidget::printModeChanged(QAbstractButton* button) { if (button == vector_mode_button) { map_printer->setMode(MapPrinterOptions::Vector); } else if (button == raster_mode_button) { map_printer->setMode(MapPrinterOptions::Raster); } else { map_printer->setMode(MapPrinterOptions::Separations); } } // slot void PrintWidget::showTemplatesClicked(bool checked) { map_printer->setPrintTemplates(checked); checkTemplateConfiguration(); } void PrintWidget::checkTemplateConfiguration() { bool visibility = vector_mode_button->isChecked() && show_templates_check->isChecked(); templates_warning_icon->setVisible(visibility); templates_warning_text->setVisible(visibility); } // slot void PrintWidget::showGridClicked(bool checked) { map_printer->setPrintGrid(checked); } // slot void PrintWidget::overprintingClicked(bool checked) { map_printer->setSimulateOverprinting(checked); } void PrintWidget::colorModeChanged() { if (color_mode_combo->currentData().toBool()) map_printer->setColorMode(MapPrinterOptions::DeviceCmyk); else map_printer->setColorMode(MapPrinterOptions::DefaultColorMode); } // slot void PrintWidget::previewClicked() { #if defined(Q_OS_ANDROID) // Qt for Android has no QPrintPreviewDialog QMessageBox::warning(this, tr("Error"), tr("Not supported on Android.")); #else if (checkForEmptyMap()) return; auto printer = map_printer->makePrinter(); if (!printer) { QMessageBox::warning(this, tr("Error"), tr("Failed to prepare the preview.")); return; } printer->setCreator(main_window->appName()); printer->setDocName(QFileInfo(main_window->currentPath()).baseName()); QPrintPreviewDialog preview(printer.get(), editor->getWindow()); preview.setWindowModality(Qt::ApplicationModal); // Required for OSX, cf. QTBUG-40112 PrintProgressDialog progress(map_printer, editor->getWindow()); progress.setWindowTitle(tr("Print Preview Progress")); connect(&preview, &QPrintPreviewDialog::paintRequested, &progress, &PrintProgressDialog::paintRequested); // Doesn't work as expected, on OSX at least. //connect(&progress, &QProgressDialog::canceled, &preview, &QPrintPreviewDialog::reject); preview.exec(); #endif } // slot void PrintWidget::printClicked() { if (checkForEmptyMap()) return; if (map->isAreaHatchingEnabled() || map->isBaselineViewEnabled()) { if (QMessageBox::question(this, tr("Warning"), tr("A non-standard view mode is activated. " "Are you sure to print / export the map like this?"), QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Cancel) return; } if (map_printer->getTarget() == MapPrinter::imageTarget()) exportToImage(); else if (map_printer->getTarget() == MapPrinter::pdfTarget()) exportToPdf(); else print(); } void PrintWidget::exportToImage() { static const QString filter_template(QString::fromLatin1("%1 (%2)")); QStringList filters = { filter_template.arg(tr("PNG"), QString::fromLatin1("*.png")), filter_template.arg(tr("BMP"), QString::fromLatin1("*.bmp")), filter_template.arg(tr("TIFF"), QString::fromLatin1("*.tif *.tiff")), filter_template.arg(tr("JPEG"), QString::fromLatin1("*.jpg *.jpeg")), tr("All files (*.*)") }; QString path = FileDialog::getSaveFileName(this, tr("Export map ..."), {}, filters.join(QString::fromLatin1(";;"))); if (path.isEmpty()) return; if (!path.endsWith(QLatin1String(".png"), Qt::CaseInsensitive) && !path.endsWith(QLatin1String(".bmp"), Qt::CaseInsensitive) && !path.endsWith(QLatin1String(".tif"), Qt::CaseInsensitive) && !path.endsWith(QLatin1String(".tiff"), Qt::CaseInsensitive) && !path.endsWith(QLatin1String(".jpg"), Qt::CaseInsensitive) && !path.endsWith(QLatin1String(".jpeg"), Qt::CaseInsensitive) ) { path.append(QString::fromLatin1(".png")); } qreal pixel_per_mm = map_printer->getOptions().resolution / 25.4; int print_width = qRound(map_printer->getPrintAreaPaperSize().width() * pixel_per_mm); int print_height = qRound(map_printer->getPrintAreaPaperSize().height() * pixel_per_mm); QImage image(print_width, print_height, QImage::Format_ARGB32_Premultiplied); if (image.isNull()) { QMessageBox::warning(this, tr("Error"), tr("Failed to prepare the image. Not enough memory.")); return; } int dots_per_meter = qRound(pixel_per_mm * 1000); image.setDotsPerMeterX(dots_per_meter); image.setDotsPerMeterY(dots_per_meter); #if 0 // Pointless unless drawPage drives the event loop and sends progress PrintProgressDialog progress(map_printer, main_window); progress.setWindowTitle(tr("Export map ...")); #endif // Export the map QPainter p(&image); map_printer->drawPage(&p, map_printer->getOptions().resolution, map_printer->getPrintArea(), true, &image); p.end(); if (!image.save(path)) { QMessageBox::warning(this, tr("Error"), tr("Failed to save the image. Does the path exist? Do you have sufficient rights?")); } else { main_window->showStatusBarMessage(tr("Exported successfully to %1").arg(path), 4000); if (world_file_check->isChecked()) exportWorldFile(path); /// \todo Handle errors emit finished(0); } return; } void PrintWidget::exportWorldFile(const QString& path) const { const auto& georef = map->getGeoreferencing(); const auto& ref_transform = georef.mapToProjected(); qreal pixel_per_mm = (map_printer->getOptions().resolution / 25.4) * map_printer->getScaleAdjustment(); const auto center_of_pixel = QPointF{0.5/pixel_per_mm, 0.5/pixel_per_mm}; const auto top_left = georef.toProjectedCoords(MapCoord{map_printer->getPrintArea().topLeft() + center_of_pixel}); const auto xscale = ref_transform.m11() / pixel_per_mm; const auto yscale = ref_transform.m22() / pixel_per_mm; const auto xskew = ref_transform.m12() / pixel_per_mm; const auto yskew = ref_transform.m21() / pixel_per_mm; QTransform final_wld(xscale, yskew, 0, xskew, yscale, 0, top_left.x(), top_left.y()); WorldFile wld(final_wld); wld.save(WorldFile::pathForImage(path)); } void PrintWidget::exportToPdf() { auto printer = map_printer->makePrinter(); if (!printer) { QMessageBox::warning(this, tr("Error"), tr("Failed to prepare the PDF export.")); return; } printer->setOutputFormat(QPrinter::PdfFormat); printer->setNumCopies(copies_edit->value()); printer->setCreator(main_window->appName()); printer->setDocName(QFileInfo(main_window->currentPath()).baseName()); static const QString filter_template(QString::fromLatin1("%1 (%2)")); QStringList filters = { filter_template.arg(tr("PDF"), QString::fromLatin1("*.pdf")), tr("All files (*.*)") }; QString path = FileDialog::getSaveFileName(this, tr("Export map ..."), {}, filters.join(QString::fromLatin1(";;"))); if (path.isEmpty()) { return; } else if (!path.endsWith(QLatin1String(".pdf"), Qt::CaseInsensitive)) { path.append(QLatin1String(".pdf")); } printer->setOutputFileName(path); PrintProgressDialog progress(map_printer, main_window); progress.setWindowTitle(tr("Export map ...")); // Export the map if (!map_printer->printMap(printer.get())) { QFile(path).remove(); QMessageBox::warning(this, tr("Error"), tr("Failed to finish the PDF export.")); } else if (!progress.wasCanceled()) { main_window->showStatusBarMessage(tr("Exported successfully to %1").arg(path), 4000); emit finished(0); } else { QFile(path).remove(); main_window->showStatusBarMessage(tr("Canceled."), 4000); } } void PrintWidget::print() { auto printer = map_printer->makePrinter(); if (!printer) { QMessageBox::warning(this, tr("Error"), tr("Failed to prepare the printing.")); return; } printer->setNumCopies(copies_edit->value()); printer->setCreator(main_window->appName()); printer->setDocName(QFileInfo(main_window->currentPath()).baseName()); PrintProgressDialog progress(map_printer, main_window); progress.setWindowTitle(tr("Printing Progress")); // Print the map if (!map_printer->printMap(printer.get())) { QMessageBox::warning(main_window, tr("Error"), tr("An error occurred during printing.")); } else if (!progress.wasCanceled()) { main_window->showStatusBarMessage(tr("Successfully created print job"), 4000); emit finished(0); } else if (printer->abort()) { main_window->showStatusBarMessage(tr("Canceled."), 4000); } else { QMessageBox::warning(main_window, tr("Error"), tr("The print job could not be stopped.")); } } QList PrintWidget::defaultPaperSizes() const { // TODO: Learn from user's past choices, present reduced list unless asked for more. static QList default_paper_sizes(QList() << QPrinter::A4 << QPrinter::Letter << QPrinter::Legal << QPrinter::Executive << QPrinter::A0 << QPrinter::A1 << QPrinter::A2 << QPrinter::A3 << QPrinter::A5 << QPrinter::A6 << QPrinter::A7 << QPrinter::A8 << QPrinter::A9 << QPrinter::B0 << QPrinter::B1 << QPrinter::B10 << QPrinter::B2 << QPrinter::B3 << QPrinter::B4 << QPrinter::B5 << QPrinter::B6 << QPrinter::B7 << QPrinter::B8 << QPrinter::B9 << QPrinter::C5E << QPrinter::Comm10E << QPrinter::DLE << QPrinter::Folio << QPrinter::Ledger << QPrinter::Tabloid << QPrinter::Custom ); return default_paper_sizes; } QString PrintWidget::toString(QPrinter::PaperSize size) { #if defined(Q_OS_ANDROID) // Qt for Android has no QPrintDialog Q_UNUSED(size); return tr("Unknown", "Paper size"); #else const QHash< int, const char*>& paper_size_names = MapPrinter::paperSizeNames(); if (paper_size_names.contains(size)) // These translations are not used in QPrintDialog, // but in Qt's qpagesetupdialog_unix.cpp. return QPrintDialog::tr(paper_size_names[size]); else return tr("Unknown", "Paper size"); #endif } bool PrintWidget::checkForEmptyMap() { if (map_printer->isOutputEmpty()) { QMessageBox::warning(this, tr("Error"), tr("The map area is empty. Output canceled.")); return true; } return false; } } // namespace OpenOrienteering #endif // QT_PRINTSUPPORT_LIB mapper-0.8.1.1/src/gui/print_widget.h000066400000000000000000000222161325266516600174050ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2012-2016 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifdef QT_PRINTSUPPORT_LIB #ifndef OPENORIENTEERING_PRINT_WIDGET_H #define OPENORIENTEERING_PRINT_WIDGET_H #include #include #include #include // IWYU pragma: no_include #include #include #include #include class QAbstractButton; class QButtonGroup; class QCheckBox; class QComboBox; class QDialogButtonBox; class QDoubleSpinBox; class QFormLayout; class QLabel; class QPushButton; class QPrinterInfo; class QRectF; class QScrollArea; class QSpinBox; class QToolButton; namespace OpenOrienteering { class MainWindow; class Map; class MapEditorController; class MapPrinter; class MapPrinterOptions; class MapPrinterPageFormat; class MapView; class PrintTool; /** * The print widget lets the user adjust targets and parameters * for printing and export. */ class PrintWidget : public QWidget { Q_OBJECT public: enum TaskFlag { EXPORT_FLAG = 0x02, MULTIPAGE_FLAG = 0x04, UNDEFINED_TASK = 0x00, PRINT_TASK = 0x14, // 0x10 | 0x04 EXPORT_PDF_TASK = 0x26, // 0x20 | 0x04 | 0x02 EXPORT_IMAGE_TASK = 0x42, // 0x40 | 0x02 END_JOB_TYPE }; Q_DECLARE_FLAGS(TaskFlags, TaskFlag) /** Constructs a new print widget. */ PrintWidget(Map* map, MainWindow* main_window, MapView* main_view, MapEditorController* editor, QWidget* parent = nullptr); /** Destroys the widget. */ ~PrintWidget() override; /** Indicates the default widget size. */ QSize sizeHint() const override; /** Returns a translated name for the given paper size. */ static QString toString(QPrinter::PaperSize size); public slots: /** Changes the type of the print or export task. */ void setTask(TaskFlags type); /** Saves the print or export settings. */ void savePrinterConfig() const; /** * Sets the active state of the print widget. * * When the widget becomes active, it saves the map view state and * activates a tool on the map editor which allows to move the print area. * When the widget becomes inactive, the tool is removed, and the map view * state is restored. */ void setActive(bool active); /** Sets the widget's (print/export) target. */ void setTarget(const QPrinterInfo* target); /** Sets the format of a single page. */ void setPageFormat(const MapPrinterPageFormat& format); /** Sets the exported area. */ void setPrintArea(const QRectF& area); /** Sets output options: resolution, overprinting. */ void setOptions(const MapPrinterOptions& options); /** Listens to view feature changes. */ void onVisibilityChanged(); signals: /** * This signal is emitted when the type of task changes. * It may be used to set a window title. */ void taskChanged(const QString& name); /** * This signal is emitted when a print or export job has been started * and finished. * * The signal is not emitted when the widget is hidden * (cf. QDialog::finished(int result) ). */ void finished(int result); /** * This signal is emitted when the dialog's close button is clicked. */ void closeClicked(); protected slots: /** This slot reacts to changes of the target combobox. */ void targetChanged(int index) const; /** Opens a dialog to change printer properties. */ void propertiesClicked(); /** This slot reacts to changes of the paper size combobox. */ void paperSizeChanged(int index) const; /** This slot reacts to changes of the paper dimension widgets. */ void paperDimensionsChanged() const; /** This slot reacts to changes of the page orientation widget. */ void pageOrientationChanged(int id) const; /** This slot reacts to changes of the print area policy combobox. */ void printAreaPolicyChanged(int index); /** This slot applies the map area policy to the current area. */ void applyPrintAreaPolicy() const; /** This slot applies the center map area policy to the current area. */ void applyCenterPolicy() const; /** This slot reacts to changes of print area position widgets. */ void printAreaMoved(); /** This slot reacts to changes of print area size widget. */ void printAreaResized(); /** This slot reacts to changes to the page overlap widget. */ void overlapEdited(double overlap); /** This slot is called when the resolution widget signals that editing finished. */ void resolutionEdited(); /** This slot enables the alternative scale widget, or resets it. */ void differentScaleClicked(bool checked); /** This slot reacts to changes in the alternative scale widget. */ void differentScaleEdited(int value); /** This slot reacts to changes in the presence of spot colors in the map. * The following features are enabled only when spot colors are present: * - spot color separations * - overprinting simulation */ void spotColorPresenceChanged(bool has_spot_colors); /** This slot reacts to changes in the print mode buttons. */ void printModeChanged(QAbstractButton* button); /** This slot reacts to changes of the "Show template" option. */ void showTemplatesClicked(bool checked); /** This slot reacts to changes of the "Show grid" option. */ void showGridClicked(bool checked); /** This slot reacts to changes of the "Simulate overprinting" option. */ void overprintingClicked(bool checked); /** This slot reacts to changes of the "Color mode" option. */ void colorModeChanged(); /** Opens a preview window. */ void previewClicked(); /** Starts printing and terminates this dialog. */ void printClicked(); protected: /** Alternative policies of handling the print area. */ enum PrintAreaPolicy { SinglePage = 2, CustomArea = 4 }; /** Re-initializes the list of print/export targets. */ void updateTargets(); /** Updates the list of paper sizes from the given target. */ void updatePaperSizes(const QPrinterInfo* target) const; /** Updates the list of resolutions from the given target. */ void updateResolutions(const QPrinterInfo* target) const; /** Updates the color mode combobox from target and mode settings. */ void updateColorMode(); /** A list of paper sizes which is used when the target does not specify * supported paper sizes. */ QList defaultPaperSizes() const; /** Moves the given rectangle to a position where it is centered on the * map for the current output options. */ void centerOnMap(QRectF& area) const; /** Shows a warning and returns true if the output would be empty. */ bool checkForEmptyMap(); /** Sets the enabled state of the page overlap field. */ void setOverlapEditEnabled(bool state = true) const; /** Checks whether the template order warning needs to be displayed. */ void checkTemplateConfiguration(); /** Exports to an image file. */ void exportToImage(); /** Export a world file */ void exportWorldFile(const QString& path) const; /** Exports to a PDF file. */ void exportToPdf(); /** Print to a printer. */ void print(); private: enum Exporters { PdfExporter = -1, ImageExporter = -2 }; TaskFlags task; QFormLayout* layout; QWidget* scrolling_content; QScrollArea* scroll_area; QDialogButtonBox* button_box; QComboBox* target_combo; QToolButton* printer_properties_button; QComboBox* paper_size_combo; QWidget* page_orientation_widget; QButtonGroup* page_orientation_group; QSpinBox* copies_edit; QDoubleSpinBox* page_width_edit; QDoubleSpinBox* page_height_edit; QComboBox* dpi_combo; QCheckBox* show_templates_check; QLabel* templates_warning_icon; QLabel* templates_warning_text; QCheckBox* show_grid_check; QCheckBox* overprinting_check; QCheckBox* world_file_check; QCheckBox* different_scale_check; QSpinBox* different_scale_edit; QComboBox* color_mode_combo; QComboBox* policy_combo; QCheckBox* center_check; QDoubleSpinBox* left_edit; QDoubleSpinBox* top_edit; QDoubleSpinBox* width_edit; QDoubleSpinBox* height_edit; QDoubleSpinBox* overlap_edit; QToolButton* vector_mode_button; QToolButton* raster_mode_button; QToolButton* separations_mode_button; QPushButton* preview_button; QPushButton* print_button; QPushButton* export_button; QStringList printers; PrintAreaPolicy policy; Map* map; MapPrinter* map_printer; MainWindow* main_window; MapView* main_view; MapEditorController* editor; PrintTool* print_tool; QString saved_view_state; bool active; }; #endif } // namespace OpenOrienteering Q_DECLARE_OPERATORS_FOR_FLAGS(OpenOrienteering::PrintWidget::TaskFlags) #endif mapper-0.8.1.1/src/gui/select_crs_dialog.cpp000066400000000000000000000100421325266516600207000ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2012-2015 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "select_crs_dialog.h" #include #include #include #include #include #include #include #include #include #include "core/georeferencing.h" #include "gui/util_gui.h" #include "gui/widgets/crs_selector.h" namespace OpenOrienteering { namespace { enum SpecialCRS { SameAsMap = 1, Local = 2, Geographic = 3 }; } // namespace SelectCRSDialog::SelectCRSDialog( const Georeferencing& georef, QWidget* parent, GeorefAlternatives alternatives, const QString& description ) : QDialog(parent, Qt::WindowSystemMenuHint | Qt::WindowTitleHint) , georef(georef) { setWindowModality(Qt::WindowModal); setWindowTitle(tr("Select coordinate reference system")); crs_selector = new CRSSelector(georef, nullptr); if (georef.isLocal()) crs_selector->clear(); if (alternatives.testFlag(TakeFromMap) && !georef.isLocal()) { crs_selector->addCustomItem(tr("Same as map"), SpecialCRS::SameAsMap); crs_selector->setCurrentIndex(0); // TakeFromMap } if (alternatives.testFlag(Local) || georef.isLocal()) { crs_selector->addCustomItem(tr("Local"), SpecialCRS::Local); crs_selector->setCurrentIndex(0); // TakeFromMap or Local, both is fine. } if (alternatives.testFlag(Geographic) && !georef.isLocal()) crs_selector->addCustomItem(tr("Geographic coordinates (WGS84)"), SpecialCRS::Geographic); status_label = new QLabel(); button_box = new QDialogButtonBox(QDialogButtonBox::Cancel | QDialogButtonBox::Ok); auto form_layout = new QFormLayout(); if (!description.isEmpty()) { form_layout->addRow(new QLabel(description)); form_layout->addItem(Util::SpacerItem::create(this)); } form_layout->addRow(QCoreApplication::translate("OpenOrienteering::GeoreferencingDialog", "&Coordinate reference system:"), crs_selector); form_layout->addRow(tr("Status:"), status_label); form_layout->addItem(Util::SpacerItem::create(this)); crs_selector->setDialogLayout(form_layout); auto layout = new QVBoxLayout(); layout->addLayout(form_layout, 1); layout->addWidget(button_box, 0); setLayout(layout); connect(crs_selector, &CRSSelector::crsChanged, this, &SelectCRSDialog::updateWidgets); connect(button_box, &QDialogButtonBox::accepted, this, &SelectCRSDialog::accept); connect(button_box, &QDialogButtonBox::rejected, this, &SelectCRSDialog::reject); updateWidgets(); } QString SelectCRSDialog::currentCRSSpec() const { QString spec; switch (crs_selector->currentCustomItem()) { case SpecialCRS::SameAsMap: spec = georef.getProjectedCRSSpec(); break; case SpecialCRS::Local: // nothing break; case SpecialCRS::Geographic: spec = Georeferencing::geographic_crs_spec; break; default: spec = crs_selector->currentCRSSpec(); } return spec; } void SelectCRSDialog::updateWidgets() { Georeferencing georef; auto spec = currentCRSSpec(); auto valid = spec.isEmpty() || georef.setProjectedCRS({}, spec); button_box->button(QDialogButtonBox::Ok)->setEnabled(valid); if (valid) status_label->setText(tr("valid")); else status_label->setText(QLatin1String("") + georef.getErrorText() + QLatin1String("")); } } // namespace OpenOrienteering mapper-0.8.1.1/src/gui/select_crs_dialog.h000066400000000000000000000046631325266516600203610ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2012-2015 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_SELECT_CRS_DIALOG_H #define OPENORIENTEERING_SELECT_CRS_DIALOG_H #include #include #include #include class QDialogButtonBox; class QLabel; class QWidget; namespace OpenOrienteering { class CRSSelector; class Georeferencing; /** Dialog to select a coordinate reference system (CRS) */ class SelectCRSDialog : public QDialog { Q_OBJECT public: /** * Georeferencing alternatives */ enum GeorefAlternative { TakeFromMap = 1 << 0, Local = 1 << 1, Geographic = 1 << 2, None = 0 }; Q_DECLARE_FLAGS(GeorefAlternatives, GeorefAlternative) /** * Creates a SelectCRSDialog. * * @param georef A default georeferencing (usually the map's one). * @param parent The parent widget. * @param alternatives The georeferencing alternatives to be offered. * @param description Optional description text for the dialog. * Should explain what the selected CRS will be used for. */ SelectCRSDialog( const Georeferencing& georef, QWidget* parent, GeorefAlternatives alternatives, const QString& description = QString() ); /** * Returns the current CRS specification string. */ QString currentCRSSpec() const; protected: /** * Update the status field and enables/disables the OK button. */ void updateWidgets(); private: const Georeferencing& georef; CRSSelector* crs_selector; QLabel* status_label; QDialogButtonBox* button_box; }; } // namespace OpenOrienteering Q_DECLARE_OPERATORS_FOR_FLAGS(OpenOrienteering::SelectCRSDialog::GeorefAlternatives) #endif mapper-0.8.1.1/src/gui/settings_dialog.cpp000066400000000000000000000136621325266516600204250ustar00rootroot00000000000000/* * Copyright 2012, 2013 Jan Dalheimer * Copyright 2012-2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "settings_dialog.h" #include #include #include // IWYU pragma: keep #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "gui/main_window.h" #include "gui/util_gui.h" #include "gui/widgets/editor_settings_page.h" #include "gui/widgets/general_settings_page.h" #include "gui/widgets/settings_page.h" #include "util/backports.h" // IWYU pragma: keep #ifdef MAPPER_USE_GDAL # include "gdal/gdal_settings_page.h" #endif namespace OpenOrienteering { SettingsDialog::SettingsDialog(QWidget* parent) : QDialog { parent } , tab_widget { nullptr } , stack_widget { nullptr } { setWindowTitle(tr("Settings")); auto layout = new QVBoxLayout(this); auto buttons = QDialogButtonBox::StandardButtons{ QDialogButtonBox::Ok }; if (MainWindow::mobileMode()) { if (parent) setGeometry(parent->geometry()); stack_widget = new QStackedWidget(); layout->addWidget(stack_widget, 1); auto menu_widget = new QToolBar(); menu_widget->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); menu_widget->setOrientation(Qt::Vertical); stack_widget->addWidget(menu_widget); } else { buttons |= QDialogButtonBox::Reset | QDialogButtonBox::Cancel | QDialogButtonBox::Help; tab_widget = new QTabWidget(); #ifndef Q_OS_MACOS tab_widget->setDocumentMode(true); #endif layout->addWidget(tab_widget, 1); } button_box = new QDialogButtonBox(buttons, Qt::Horizontal); connect(button_box, &QDialogButtonBox::clicked, this, &SettingsDialog::buttonPressed); if (MainWindow::mobileMode()) { int left, top, right, bottom; layout->getContentsMargins(&left, &top, &right, &bottom); layout->setContentsMargins(0, 0, 0, 0); layout->setSpacing(0); auto l = new QVBoxLayout(); l->setContentsMargins(left, top, right, bottom); l->addWidget(button_box); layout->addLayout(l); } else { layout->addWidget(button_box); } addPages(); } SettingsDialog::~SettingsDialog() = default; void SettingsDialog::closeEvent(QCloseEvent* event) { if (MainWindow::mobileMode()) callOnAllPages(&SettingsPage::apply); QDialog::closeEvent(event); } void SettingsDialog::keyPressEvent(QKeyEvent* event) { switch (event->key()) { case Qt::Key_Back: case Qt::Key_Escape: if (MainWindow::mobileMode() && stack_widget->currentIndex() > 0) { stack_widget->setCurrentIndex(0); auto buttons = button_box->standardButtons(); button_box->setStandardButtons((buttons & ~QDialogButtonBox::Reset) | QDialogButtonBox::Help); return; } break; default: ; // nothing } QDialog::keyPressEvent(event); } void SettingsDialog::addPages() { addPage(new GeneralSettingsPage(this)); addPage(new EditorSettingsPage(this)); #ifdef MAPPER_USE_GDAL addPage(new GdalSettingsPage(this)); #endif } void SettingsDialog::addPage(SettingsPage* page) { if (MainWindow::mobileMode()) { if (auto form_layout = qobject_cast(page->layout())) { form_layout->setRowWrapPolicy(QFormLayout::WrapAllRows); auto labels = page->findChildren(); for (auto label : qAsConst(labels)) label->setWordWrap(true); } page->setMaximumWidth(width()); auto scrollarea = new QScrollArea(); scrollarea->setFrameShape(QFrame::NoFrame); QScroller::grabGesture(scrollarea, QScroller::TouchGesture); scrollarea->setWidget(page); stack_widget->addWidget(scrollarea); auto menu_widget = qobject_cast(stack_widget->widget(0)); auto action = menu_widget->addAction(page->title()); connect(action, &QAction::triggered, this, [this, scrollarea]() { stack_widget->setCurrentWidget(scrollarea); scrollarea->ensureVisible(0, 0); button_box->setStandardButtons(button_box->standardButtons() | QDialogButtonBox::Reset); } ); } else { tab_widget->addTab(page, page->title()); } } void SettingsDialog::callOnAllPages(void (SettingsPage::*member)()) { auto pages = MainWindow::mobileMode() ? stack_widget->findChildren() : tab_widget->findChildren(); for (auto page : qAsConst(pages)) (page->*member)(); } void SettingsDialog::buttonPressed(QAbstractButton* button) { QDialogButtonBox::StandardButton id = button_box->standardButton(button); switch (id) { case QDialogButtonBox::Ok: callOnAllPages(&SettingsPage::apply); if (MainWindow::mobileMode() && stack_widget->currentIndex() > 0) { stack_widget->setCurrentIndex(0); button_box->setStandardButtons(button_box->standardButtons() & ~QDialogButtonBox::Reset); } else { this->accept(); } break; case QDialogButtonBox::Reset: callOnAllPages(&SettingsPage::reset); break; case QDialogButtonBox::Cancel: this->reject(); break; case QDialogButtonBox::Help: Util::showHelp(this, QLatin1String("settings.html")); break; default: qDebug("%s: Unexpected button '0x%x'", Q_FUNC_INFO, id); } } } // namespace OpenOrienteering mapper-0.8.1.1/src/gui/settings_dialog.h000066400000000000000000000045411325266516600200660ustar00rootroot00000000000000/* * Copyright 2012, 2013 Jan Dalheimer * Copyright 2013-2016 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_SETTINGS_DIALOG_H #define OPENORIENTEERING_SETTINGS_DIALOG_H #include #include class QAbstractButton; class QCloseEvent; class QDialogButtonBox; class QKeyEvent; class QStackedWidget; class QTabWidget; class QWidget; namespace OpenOrienteering { class SettingsPage; /** * A dialog for editing Mapper's settings. */ class SettingsDialog : public QDialog { Q_OBJECT public: /** * Constructs a new settings dialog. */ explicit SettingsDialog(QWidget* parent = nullptr); /** * Destroys the settings dialog. */ ~SettingsDialog() override; protected: /** * Adds all known pages to the dialog. * * This function is called from the constructor. It may be overridden to * provide dialogs with different pages. */ virtual void addPages(); /** * Adds a single page to the dialog. */ void addPage(SettingsPage* page); /** * Calls a SettingsPage member function on all pages. */ void callOnAllPages(void (SettingsPage::*member)()); void closeEvent(QCloseEvent* event) override; void keyPressEvent(QKeyEvent* event) override; private slots: /** * Reacts to dialog buttons (OK, Cancel, Rest). */ void buttonPressed(QAbstractButton* button); private: /** * A tab widget which holds all pages in desktop mode. */ QTabWidget* tab_widget; /** * A stack widget which holds all pages in mobile mode. */ QStackedWidget* stack_widget; /** * The box of standard dialog buttons. */ QDialogButtonBox* button_box; }; } // namespace OpenOrienteering #endif mapper-0.8.1.1/src/gui/symbols/000077500000000000000000000000001325266516600162225ustar00rootroot00000000000000mapper-0.8.1.1/src/gui/symbols/area_symbol_settings.cpp000066400000000000000000000473541325266516600231600ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2012-2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "area_symbol_settings.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "core/map.h" #include "core/symbols/symbol.h" #include "core/symbols/point_symbol.h" #include "gui/symbols/point_symbol_editor_widget.h" #include "gui/symbols/symbol_setting_dialog.h" #include "gui/widgets/color_dropdown.h" #include "gui/util_gui.h" #include "util/backports.h" #include "util/scoped_signals_blocker.h" namespace OpenOrienteering { // ### AreaSymbol ### SymbolPropertiesWidget* AreaSymbol::createPropertiesWidget(SymbolSettingDialog* dialog) { return new AreaSymbolSettings(this, dialog); } // ### AreaSymbolSettings ### AreaSymbolSettings::AreaSymbolSettings(AreaSymbol* symbol, SymbolSettingDialog* dialog) : SymbolPropertiesWidget(symbol, dialog), symbol(symbol) { map = dialog->getPreviewMap(); controller = dialog->getPreviewController(); auto general_layout = new QFormLayout(); color_edit = new ColorDropDown(map); general_layout->addRow(tr("Area color:"), color_edit); minimum_size_edit = Util::SpinBox::create(1, 0.0, 999999.9, trUtf8("mm²")); general_layout->addRow(tr("Minimum size:"), minimum_size_edit); general_layout->addItem(Util::SpacerItem::create(this)); auto fill_patterns_list_layout = new QVBoxLayout(); fill_patterns_list_layout->addWidget(Util::Headline::create(tr("Fills")), 0); pattern_list = new QListWidget(); fill_patterns_list_layout->addWidget(pattern_list, 1); del_pattern_button = new QPushButton(QIcon(QString::fromLatin1(":/images/minus.png")), QString{}); add_pattern_button = new QToolButton(); add_pattern_button->setIcon(QIcon(QString::fromLatin1(":/images/plus.png"))); add_pattern_button->setToolButtonStyle(Qt::ToolButtonIconOnly); add_pattern_button->setPopupMode(QToolButton::InstantPopup); add_pattern_button->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed)); add_pattern_button->setMinimumSize(del_pattern_button->sizeHint()); auto add_fill_button_menu = new QMenu(add_pattern_button); add_fill_button_menu->addAction(tr("Line fill"), this, SLOT(addLinePattern())); add_fill_button_menu->addAction(tr("Pattern fill"), this, SLOT(addPointPattern())); add_pattern_button->setMenu(add_fill_button_menu); auto buttons_layout = new QHBoxLayout(); buttons_layout->addStretch(1); buttons_layout->addWidget(add_pattern_button); buttons_layout->addWidget(del_pattern_button); buttons_layout->addStretch(1); fill_patterns_list_layout->addLayout(buttons_layout, 0); auto fill_pattern_layout = new QFormLayout(); pattern_name_edit = new QLabel(); fill_pattern_layout->addRow(pattern_name_edit); fill_pattern_layout->addItem(Util::SpacerItem::create(this)); /* From here, stacked widgets are used to unify the layout of pattern type dependant fields. */ auto single_line_headline = new QStackedWidget(); connect(this, &AreaSymbolSettings::switchPatternEdits, single_line_headline, &QStackedWidget::setCurrentIndex); fill_pattern_layout->addRow(single_line_headline); auto single_line_label1 = new QStackedWidget(); connect(this, &AreaSymbolSettings::switchPatternEdits, single_line_label1, &QStackedWidget::setCurrentIndex); auto single_line_edit1 = new QStackedWidget(); connect(this, &AreaSymbolSettings::switchPatternEdits, single_line_edit1, &QStackedWidget::setCurrentIndex); fill_pattern_layout->addRow(single_line_label1, single_line_edit1); auto single_line_label2 = new QStackedWidget(); connect(this, &AreaSymbolSettings::switchPatternEdits, single_line_label2, &QStackedWidget::setCurrentIndex); auto single_line_edit2 = new QStackedWidget(); connect(this, &AreaSymbolSettings::switchPatternEdits, single_line_edit2, &QStackedWidget::setCurrentIndex); fill_pattern_layout->addRow(single_line_label2, single_line_edit2); auto single_line_label3 = new QStackedWidget(); connect(this, &AreaSymbolSettings::switchPatternEdits, single_line_label3, &QStackedWidget::setCurrentIndex); pattern_line_offset_edit = Util::SpinBox::create(3, -999999.9, 999999.9, tr("mm")); fill_pattern_layout->addRow(single_line_label3, pattern_line_offset_edit); fill_pattern_layout->addItem(Util::SpacerItem::create(this)); auto parallel_lines_headline = new QStackedWidget(); connect(this, &AreaSymbolSettings::switchPatternEdits, parallel_lines_headline, &QStackedWidget::setCurrentIndex); fill_pattern_layout->addRow(parallel_lines_headline); auto parallel_lines_label1 = new QStackedWidget(); connect(this, &AreaSymbolSettings::switchPatternEdits, parallel_lines_label1, &QStackedWidget::setCurrentIndex); pattern_spacing_edit = Util::SpinBox::create(3, 0.0, 999999.9, tr("mm")); fill_pattern_layout->addRow(parallel_lines_label1, pattern_spacing_edit); /* Stacked widgets, index 0: line pattern fields */ single_line_headline->addWidget(Util::Headline::create(tr("Single line"))); auto pattern_linewidth_label = new QLabel(tr("Line width:")); pattern_linewidth_edit = Util::SpinBox::create(3, 0.0, 999999.9, tr("mm")); single_line_label1->addWidget(pattern_linewidth_label); single_line_edit1 ->addWidget(pattern_linewidth_edit); auto pattern_color_label = new QLabel(tr("Line color:")); pattern_color_edit = new ColorDropDown(map); single_line_label2->addWidget(pattern_color_label); single_line_edit2 ->addWidget(pattern_color_edit); single_line_label3->addWidget(new QLabel(tr("Line offset:"))); parallel_lines_headline->addWidget(Util::Headline::create(tr("Parallel lines"))); parallel_lines_label1->addWidget(new QLabel(tr("Line spacing:"))); /* Stacked widgets, index 1: point pattern fields */ single_line_headline->addWidget(Util::Headline::create(tr("Single row"))); auto pattern_pointdist_label = new QLabel(tr("Pattern interval:")); pattern_pointdist_edit = Util::SpinBox::create(3, 0, 999999.9, tr("mm")); single_line_label1->addWidget(pattern_pointdist_label); single_line_edit1 ->addWidget(pattern_pointdist_edit); auto pattern_offset_along_line_label = new QLabel(tr("Pattern offset:")); pattern_offset_along_line_edit = Util::SpinBox::create(3, -999999.9, 999999.9, tr("mm")); single_line_label2->addWidget(pattern_offset_along_line_label); single_line_edit2 ->addWidget(pattern_offset_along_line_edit); single_line_label3->addWidget(new QLabel(tr("Row offset:"))); parallel_lines_headline->addWidget(Util::Headline::create(tr("Parallel rows"))); parallel_lines_label1->addWidget(new QLabel(tr("Row spacing:"))); /* General fields again. Not stacked. */ fill_pattern_layout->addItem(Util::SpacerItem::create(this)); fill_pattern_layout->addRow(Util::Headline::create(tr("Fill rotation"))); pattern_angle_edit = Util::SpinBox::create(1, 0.0, 360.0, trUtf8("°")); pattern_angle_edit->setWrapping(true); fill_pattern_layout->addRow(tr("Angle:"), pattern_angle_edit); pattern_rotatable_check = new QCheckBox(tr("adjustable per object")); fill_pattern_layout->addRow(QString{}, pattern_rotatable_check); fill_pattern_layout->addItem(Util::SpacerItem::create(this)); /* Stacked widgets again. */ auto clipping_headline = new QStackedWidget(); connect(this, &AreaSymbolSettings::switchPatternEdits, clipping_headline, &QStackedWidget::setCurrentIndex); fill_pattern_layout->addRow(clipping_headline); auto clipping_edit = new QStackedWidget(); connect(this, &AreaSymbolSettings::switchPatternEdits, clipping_edit, &QStackedWidget::setCurrentIndex); fill_pattern_layout->addRow(clipping_edit); // Line pattern: Clipping settings not used clipping_headline->addWidget(new QWidget()); clipping_edit->addWidget(new QWidget()); // Point pattern clipping clipping_headline->addWidget(Util::Headline::create(tr("Element drawing at boundary"))); pattern_clipping_edit = new QComboBox(); pattern_clipping_edit->addItem(tr("Clip elements at the boundary."), AreaSymbol::FillPattern::Default); pattern_clipping_edit->addItem(tr("Draw elements if the center is inside the boundary."), AreaSymbol::FillPattern::NoClippingIfCenterInside); pattern_clipping_edit->addItem(tr("Draw elements if any point is inside the boundary."), AreaSymbol::FillPattern::NoClippingIfPartiallyInside); pattern_clipping_edit->addItem(tr("Draw elements if all points are inside the boundary."), AreaSymbol::FillPattern::NoClippingIfCompletelyInside); clipping_edit->addWidget(pattern_clipping_edit); auto fill_patterns_layout = new QHBoxLayout(); fill_patterns_layout->addLayout(fill_patterns_list_layout, 1); fill_patterns_layout->addItem(Util::SpacerItem::create(this)); fill_patterns_layout->addLayout(fill_pattern_layout, 3); auto layout = new QVBoxLayout(); layout->addLayout(general_layout); layout->addLayout(fill_patterns_layout); auto area_tab = new QWidget(); area_tab->setLayout(layout); addPropertiesGroup(tr("Area settings"), area_tab); connect(color_edit, QOverload::of(&QComboBox::currentIndexChanged), this, &AreaSymbolSettings::colorChanged); connect(minimum_size_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &AreaSymbolSettings::minimumSizeChanged); connect(del_pattern_button, &QAbstractButton::clicked, this, &AreaSymbolSettings::deleteActivePattern); connect(pattern_list, &QListWidget::currentRowChanged, this, &AreaSymbolSettings::selectPattern); connect(pattern_angle_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &AreaSymbolSettings::patternAngleChanged); connect(pattern_rotatable_check, &QAbstractButton::clicked, this, &AreaSymbolSettings::patternRotatableClicked); connect(pattern_spacing_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &AreaSymbolSettings::patternSpacingChanged); connect(pattern_line_offset_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &AreaSymbolSettings::patternLineOffsetChanged); connect(pattern_offset_along_line_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &AreaSymbolSettings::patternOffsetAlongLineChanged); connect(pattern_color_edit, QOverload::of(&QComboBox::currentIndexChanged), this, &AreaSymbolSettings::patternColorChanged); connect(pattern_linewidth_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &AreaSymbolSettings::patternLineWidthChanged); connect(pattern_pointdist_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &AreaSymbolSettings::patternPointDistChanged); connect(pattern_clipping_edit, QOverload::of(&QComboBox::currentIndexChanged), this, [this](){ active_pattern->setClipping(AreaSymbol::FillPattern::Options(pattern_clipping_edit->currentData(Qt::UserRole).toInt())); emit propertiesModified(); }); updateAreaGeneral(); updatePatternWidgets(); loadPatterns(); } AreaSymbolSettings::~AreaSymbolSettings() = default; void AreaSymbolSettings::reset(Symbol* symbol) { Q_ASSERT(symbol->getType() == Symbol::Area); clearPatterns(); SymbolPropertiesWidget::reset(symbol); this->symbol = reinterpret_cast(symbol); updateAreaGeneral(); loadPatterns(); } void AreaSymbolSettings::updateAreaGeneral() { ScopedMultiSignalsBlocker block(color_edit, minimum_size_edit); color_edit->setColor(symbol->getColor()); minimum_size_edit->setValue(0.001 * symbol->minimum_area); } void AreaSymbolSettings::clearPatterns() { pattern_list->clear(); for (const auto& pattern : symbol->patterns) { if (pattern.type == AreaSymbol::FillPattern::PointPattern) removePropertiesGroup(2); } } void AreaSymbolSettings::loadPatterns() { Q_ASSERT(pattern_list->count() == 0); Q_ASSERT(count() == 2); // General + Area tab for (const auto& pattern : symbol->patterns) { pattern_list->addItem(pattern.name); if (pattern.type == AreaSymbol::FillPattern::PointPattern) { auto editor = new PointSymbolEditorWidget(controller, pattern.point, 16); connect(editor, &PointSymbolEditorWidget::symbolEdited, this, &SymbolPropertiesWidget::propertiesModified ); addPropertiesGroup(pattern.name, editor); } } updatePatternNames(); pattern_list->setCurrentRow(0); } void AreaSymbolSettings::updatePatternNames() { int i = 0, line_pattern_num = 0, point_pattern_num = 0; for (auto& pattern : symbol->patterns) { if (pattern.type == AreaSymbol::FillPattern::PointPattern) { ++point_pattern_num; QString name = tr("Pattern fill %1").arg(point_pattern_num); pattern.name = name; pattern.point->setName(name); pattern_list->item(i)->setText(name); renamePropertiesGroup(point_pattern_num + 1, name); } else { ++line_pattern_num; QString name = tr("Line fill %1").arg(line_pattern_num); pattern.name = name; pattern_list->item(i)->setText(pattern.name); } ++i; } } void AreaSymbolSettings::updatePatternWidgets() { int index = pattern_list->currentRow(); bool pattern_active = (index >= 0); if (pattern_active) active_pattern = symbol->patterns.begin() + index; del_pattern_button->setEnabled(pattern_active); auto pattern = pattern_active ? &*active_pattern : new AreaSymbol::FillPattern(); pattern_name_edit->setText(pattern_active ? (QLatin1String("") + active_pattern->name + QLatin1String("")) : tr("No fill selected")); { ScopedMultiSignalsBlocker block( pattern_angle_edit, pattern_rotatable_check, pattern_spacing_edit, pattern_line_offset_edit ); pattern_angle_edit->setValue(qRadiansToDegrees(pattern->angle)); pattern_angle_edit->setEnabled(pattern_active); pattern_rotatable_check->setChecked(pattern->rotatable()); pattern_rotatable_check->setEnabled(pattern_active); pattern_spacing_edit->setValue(0.001 * pattern->line_spacing); pattern_spacing_edit->setEnabled(pattern_active); pattern_line_offset_edit->setValue(0.001 * pattern->line_offset); pattern_line_offset_edit->setEnabled(pattern_active); } switch (pattern->type) { case AreaSymbol::FillPattern::LinePattern: { emit switchPatternEdits(0); ScopedMultiSignalsBlocker block(pattern_linewidth_edit, pattern_color_edit); pattern_linewidth_edit->setValue(0.001 * pattern->line_width); pattern_linewidth_edit->setEnabled(pattern_active); pattern_color_edit->setColor(pattern->line_color); pattern_color_edit->setEnabled(pattern_active); break; } case AreaSymbol::FillPattern::PointPattern: { emit switchPatternEdits(1); ScopedMultiSignalsBlocker block( pattern_offset_along_line_edit, pattern_pointdist_edit, pattern_clipping_edit ); pattern_offset_along_line_edit->setValue(0.001 * pattern->offset_along_line); pattern_offset_along_line_edit->setEnabled(pattern_active); pattern_pointdist_edit->setValue(0.001 * pattern->point_distance); pattern_pointdist_edit->setEnabled(pattern_active); pattern_clipping_edit->setCurrentIndex(pattern_clipping_edit->findData(int(pattern->clipping()))); break; } } if (!pattern_active) delete pattern; } void AreaSymbolSettings::addLinePattern() { addPattern(AreaSymbol::FillPattern::LinePattern); } void AreaSymbolSettings::addPointPattern() { addPattern(AreaSymbol::FillPattern::PointPattern); } void AreaSymbolSettings::addPattern(AreaSymbol::FillPattern::Type type) { int index = pattern_list->currentRow() + 1; if (index < 0) index = int(symbol->patterns.size()); active_pattern = symbol->patterns.insert(symbol->patterns.begin() + index, AreaSymbol::FillPattern()); auto temp_name = QString::fromLatin1("new"); pattern_list->insertItem(index, temp_name); Q_ASSERT(int(symbol->patterns.size()) == pattern_list->count()); active_pattern->type = type; if (type == AreaSymbol::FillPattern::PointPattern) { active_pattern->point = new PointSymbol(); active_pattern->point->setRotatable(true); auto editor = new PointSymbolEditorWidget(controller, active_pattern->point, 16); connect(editor, &PointSymbolEditorWidget::symbolEdited, this, &SymbolPropertiesWidget::propertiesModified ); if (pattern_list->currentRow() == int(symbol->patterns.size()) - 1) { addPropertiesGroup(temp_name, editor); } else { int group_index = indexOfPropertiesGroup(symbol->patterns[pattern_list->currentRow()+1].name); insertPropertiesGroup(group_index, temp_name, editor); } } else if (map->getNumColors() > 0) { // Default color for lines active_pattern->line_color = map->getColor(0); } updatePatternNames(); emit propertiesModified(); pattern_list->setCurrentRow(index); } void AreaSymbolSettings::deleteActivePattern() { int index = pattern_list->currentRow(); if (index < 0) { qWarning("AreaSymbolSettings::deleteActivePattern(): no fill selected."); return; } if (active_pattern->type == AreaSymbol::FillPattern::PointPattern) { removePropertiesGroup(active_pattern->name); delete active_pattern->point; active_pattern->point = nullptr; } symbol->patterns.erase(active_pattern); delete pattern_list->takeItem(index); updatePatternNames(); updatePatternWidgets(); emit propertiesModified(); index = pattern_list->currentRow(); selectPattern(index); } void AreaSymbolSettings::selectPattern(int index) { active_pattern = symbol->patterns.begin() + index; updatePatternWidgets(); } void AreaSymbolSettings::colorChanged() { symbol->color = color_edit->color(); emit propertiesModified(); } void AreaSymbolSettings::minimumSizeChanged(double value) { symbol->minimum_area = qRound(1000.0 * value); emit propertiesModified(); } void AreaSymbolSettings::patternAngleChanged(double value) { active_pattern->angle = qDegreesToRadians(value); emit propertiesModified(); } void AreaSymbolSettings::patternRotatableClicked(bool checked) { active_pattern->setRotatable(checked); emit propertiesModified(); } void AreaSymbolSettings::patternSpacingChanged(double value) { active_pattern->line_spacing = qRound(1000.0 * value); emit propertiesModified(); } void AreaSymbolSettings::patternLineOffsetChanged(double value) { active_pattern->line_offset = qRound(1000.0 * value); emit propertiesModified(); } void AreaSymbolSettings::patternOffsetAlongLineChanged(double value) { Q_ASSERT(active_pattern->type == AreaSymbol::FillPattern::PointPattern); active_pattern->offset_along_line = qRound(1000.0 * value); emit propertiesModified(); } void AreaSymbolSettings::patternColorChanged() { Q_ASSERT(active_pattern->type == AreaSymbol::FillPattern::LinePattern); active_pattern->line_color = pattern_color_edit->color(); emit propertiesModified(); } void AreaSymbolSettings::patternLineWidthChanged(double value) { Q_ASSERT(active_pattern->type == AreaSymbol::FillPattern::LinePattern); active_pattern->line_width = qRound(1000.0 * value); emit propertiesModified(); } void AreaSymbolSettings::patternPointDistChanged(double value) { Q_ASSERT(active_pattern->type == AreaSymbol::FillPattern::PointPattern); active_pattern->point_distance = qRound(1000.0 * value); emit propertiesModified(); } } // namespace OpenOrienteering mapper-0.8.1.1/src/gui/symbols/area_symbol_settings.h000066400000000000000000000061541325266516600226160ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2012-2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_AREA_SYMBOL_SETTINGS_H #define OPENORIENTEERING_AREA_SYMBOL_SETTINGS_H #include #include #include "core/symbols/area_symbol.h" #include "gui/symbols/symbol_properties_widget.h" class QCheckBox; class QComboBox; class QDoubleSpinBox; class QLabel; class QListWidget; class QPushButton; class QToolButton; namespace OpenOrienteering { class ColorDropDown; class Map; class MapEditorController; class Symbol; class SymbolSettingDialog; class AreaSymbolSettings : public SymbolPropertiesWidget { Q_OBJECT public: AreaSymbolSettings(AreaSymbol* symbol, SymbolSettingDialog* dialog); ~AreaSymbolSettings() override; void reset(Symbol* symbol) override; /** * Updates the general area fields (not related to patterns) */ void updateAreaGeneral(); void addPattern(AreaSymbol::FillPattern::Type type); signals: void switchPatternEdits(int layer); public slots: void selectPattern(int index); void addLinePattern(); void addPointPattern(); void deleteActivePattern(); protected: void clearPatterns(); void loadPatterns(); void updatePatternNames(); void updatePatternWidgets(); protected slots: void colorChanged(); void minimumSizeChanged(double value); void patternAngleChanged(double value); void patternRotatableClicked(bool checked); void patternSpacingChanged(double value); void patternLineOffsetChanged(double value); void patternOffsetAlongLineChanged(double value); void patternColorChanged(); void patternLineWidthChanged(double value); void patternPointDistChanged(double value); private: AreaSymbol* symbol; Map* map; MapEditorController* controller; std::vector::iterator active_pattern; ColorDropDown* color_edit; QDoubleSpinBox* minimum_size_edit; QListWidget* pattern_list; QToolButton* add_pattern_button; QPushButton* del_pattern_button; QLabel* pattern_name_edit; QDoubleSpinBox* pattern_angle_edit; QCheckBox* pattern_rotatable_check; QDoubleSpinBox* pattern_spacing_edit; QDoubleSpinBox* pattern_line_offset_edit; QDoubleSpinBox* pattern_offset_along_line_edit; ColorDropDown* pattern_color_edit; QDoubleSpinBox* pattern_linewidth_edit; QDoubleSpinBox* pattern_pointdist_edit; QComboBox* pattern_clipping_edit; }; } // namespace OpenOrienteering #endif mapper-0.8.1.1/src/gui/symbols/combined_symbol_settings.cpp000066400000000000000000000171331325266516600240200ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2012-2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "combined_symbol_settings.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "core/symbols/combined_symbol.h" #include "core/symbols/symbol.h" #include "gui/symbols/symbol_setting_dialog.h" #include "gui/widgets/symbol_dropdown.h" #include "util/backports.h" namespace OpenOrienteering { // ### CombinedSymbol ### SymbolPropertiesWidget* CombinedSymbol::createPropertiesWidget(SymbolSettingDialog* dialog) { return new CombinedSymbolSettings(this, dialog); } // ### CombinedSymbolSettings ### struct CombinedSymbolSettings::Widgets { QLabel* label; SymbolDropDown* edit; QPushButton* button; }; CombinedSymbolSettings::CombinedSymbolSettings(CombinedSymbol* symbol, SymbolSettingDialog* dialog) : SymbolPropertiesWidget(symbol, dialog) , symbol(symbol) { auto source_symbol = static_cast(dialog->getUnmodifiedSymbol()); auto source_map = dialog->getSourceMap(); auto layout = new QFormLayout(); number_edit = new QSpinBox(); number_edit->setRange(2, qMax(5, symbol->getNumParts())); number_edit->setValue(symbol->getNumParts()); connect(number_edit, QOverload::of(&QSpinBox::valueChanged), this, &CombinedSymbolSettings::numberChanged); layout->addRow(tr("&Number of parts:"), number_edit); widgets.resize(std::size_t(number_edit->maximum())); for (auto i = 0u; i < widgets.size(); ++i) { auto label = new QLabel(tr("Symbol %1:").arg(i+1)); auto edit = new SymbolDropDown(source_map, Symbol::Line | Symbol::Area | Symbol::Combined, (symbol->parts.size() > i) ? symbol->parts[i] : nullptr, source_symbol); edit->addCustomItem(tr("- Private line symbol -"), 1); edit->addCustomItem(tr("- Private area symbol -"), 2); connect(edit, QOverload::of(&SymbolDropDown::currentIndexChanged), this, &CombinedSymbolSettings::symbolChanged); auto button = new QPushButton(tr("Edit private symbol...")); connect(button, QOverload::of(&QAbstractButton::clicked), this, QOverload<>::of(&CombinedSymbolSettings::editButtonClicked)); auto row_layout = new QHBoxLayout(); row_layout->addWidget(edit); row_layout->addWidget(button); layout->addRow(label, row_layout); widgets[i] = { label, edit, button }; } updateContents(); auto widget = new QWidget; widget->setLayout(layout); addPropertiesGroup(tr("Combination settings"), widget); } CombinedSymbolSettings::~CombinedSymbolSettings() = default; void CombinedSymbolSettings::reset(Symbol* symbol) { if (Q_UNLIKELY(symbol->getType() != Symbol::Combined)) { qWarning("Not a combined symbol: %s", symbol ? "nullptr" : qPrintable(symbol->getPlainTextName())); return; } SymbolPropertiesWidget::reset(symbol); this->symbol = static_cast(symbol); updateContents(); } void CombinedSymbolSettings::updateContents() { auto num_parts = symbol->parts.size(); for (auto i = 0u; i < num_parts; ++i) { auto& w = widgets[i]; auto is_private = symbol->isPartPrivate(int(i)); QSignalBlocker block{w.edit}; w.label->setVisible(true); if (is_private) w.edit->setCustomItem((symbol->parts[i]->getType() == Symbol::Line) ? 1 : 2); else w.edit->setSymbol(symbol->parts[i]); w.edit->setVisible(true); w.button->setEnabled(is_private); w.button->setVisible(true); } for (auto i = num_parts; i < widgets.size(); ++i) { auto& w = widgets[i]; w.label->setVisible(false); w.edit->setSymbol(nullptr); w.edit->setVisible(false); w.button->setVisible(false); } QSignalBlocker block{number_edit}; number_edit->setValue(int(num_parts)); } void CombinedSymbolSettings::numberChanged(int value) { int old_num_items = symbol->getNumParts(); if (old_num_items == value) return; value = qMin(int(widgets.size()), value); symbol->setNumParts(value); for (auto i = std::size_t(old_num_items); i < std::size_t(value); ++i) { // This item appears now. auto& w = widgets[i]; QSignalBlocker block{w.edit}; w.label->setVisible(true); w.edit->setSymbol(nullptr); w.edit->setVisible(true); w.button->setEnabled(false); w.button->setVisible(true); } for (auto i = std::size_t(value); i < std::size_t(old_num_items); ++i) { // This item disappears now. auto& w = widgets[i]; w.label->setVisible(false); w.edit->setVisible(false); w.button->setVisible(false); } emit propertiesModified(); } void CombinedSymbolSettings::symbolChanged() { const auto edit = sender(); auto widget = std::find_if(begin(widgets), end(widgets), [edit](const auto& w) { return w.edit == edit; }); if (widget == end(widgets)) return; auto index = int(std::distance(begin(widgets), widget)); if (widget->edit->symbol()) { symbol->setPart(index, widget->edit->symbol(), false); } else if (widget->edit->customID() > 0) { // Changing to a private symbol Symbol::Type new_symbol_type; if (widget->edit->customID() == 1) new_symbol_type = Symbol::Line; else // if (symbol_edits[index]->customID() == 2) new_symbol_type = Symbol::Area; Symbol* new_symbol = nullptr; if (symbol->getPart(index) && new_symbol_type == symbol->getPart(index)->getType()) { if (QMessageBox::question(this, tr("Change from public to private symbol"), tr("Take the old symbol as template for the private symbol?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { new_symbol = symbol->getPart(index)->duplicate(); } } if (!new_symbol) new_symbol = Symbol::getSymbolForType(new_symbol_type); symbol->setPart(index, new_symbol, true); showEditDialog(index); } else { symbol->setPart(index, nullptr, false); } widget->button->setEnabled(symbol->isPartPrivate(index)); emit propertiesModified(); } void CombinedSymbolSettings::editButtonClicked() { const auto button = sender(); auto widget = std::find_if(begin(widgets), end(widgets), [button](const auto& w) { return w.button == button; }); if (widget != end(widgets)) showEditDialog(int(std::distance(begin(widgets), widget))); } void CombinedSymbolSettings::showEditDialog(int index) { if (Q_UNLIKELY(!symbol->isPartPrivate(index))) { qWarning("Symbol settings dialog requested for non-private subsymbol %d", index); return; } auto part = std::unique_ptr(symbol->getPart(index)->duplicate()); SymbolSettingDialog sub_dialog(part.get(), dialog->getSourceMap(), this); sub_dialog.setWindowModality(Qt::WindowModal); if (sub_dialog.exec() == QDialog::Accepted) { symbol->setPart(index, sub_dialog.getNewSymbol().release(), true); emit propertiesModified(); } } } // namespace OpenOrienteering mapper-0.8.1.1/src/gui/symbols/combined_symbol_settings.h000066400000000000000000000033641325266516600234660ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2012-2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_COMBINED_SYMBOL_SETTINGS_H #define OPENORIENTEERING_COMBINED_SYMBOL_SETTINGS_H #include #include #include "gui/symbols/symbol_properties_widget.h" class QSpinBox; namespace OpenOrienteering { class CombinedSymbol; class Symbol; class SymbolSettingDialog; class CombinedSymbolSettings : public SymbolPropertiesWidget { Q_OBJECT public: CombinedSymbolSettings(CombinedSymbol* symbol, SymbolSettingDialog* dialog); ~CombinedSymbolSettings() override; void reset(Symbol* symbol) override; /** * Updates the content and state of all input fields. */ void updateContents(); protected: void addRow(unsigned int index); void numberChanged(int value); void symbolChanged(); void editButtonClicked(); void showEditDialog(int index); private: CombinedSymbol* symbol; QSpinBox* number_edit; struct Widgets; std::vector widgets; }; } // namespace OpenOrienteering #endif mapper-0.8.1.1/src/gui/symbols/line_symbol_settings.cpp000066400000000000000000000724151325266516600231730ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2012-2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "line_symbol_settings.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "core/symbols/symbol.h" #include "core/symbols/line_symbol.h" #include "core/symbols/point_symbol.h" #include "gui/util_gui.h" #include "gui/symbols/point_symbol_editor_widget.h" #include "gui/symbols/symbol_setting_dialog.h" #include "gui/widgets/color_dropdown.h" #include "util/backports.h" // IWYU pragma: keep #include "util/util.h" namespace OpenOrienteering { // ### LineSymbol ### SymbolPropertiesWidget* LineSymbol::createPropertiesWidget(SymbolSettingDialog* dialog) { return new LineSymbolSettings(this, dialog); } // ### LineSymbolSettings ### LineSymbolSettings::LineSymbolSettings(LineSymbol* symbol, SymbolSettingDialog* dialog) : SymbolPropertiesWidget(symbol, dialog), symbol(symbol), dialog(dialog) { auto map = dialog->getPreviewMap(); auto line_tab = new QWidget(); auto layout = new QGridLayout(); layout->setColumnStretch(1, 1); line_tab->setLayout(layout); auto width_label = new QLabel(tr("Line width:")); width_edit = Util::SpinBox::create(2, 0.0, 999999.9, tr("mm")); auto color_label = new QLabel(tr("Line color:")); color_edit = new ColorDropDown(map, symbol->getColor()); int row = 0, col = 0; layout->addWidget(width_label, row, col++); layout->addWidget(width_edit, row, col); row++; col = 0; layout->addWidget(color_label, row, col++); layout->addWidget(color_edit, row, col, 1, -1); auto minimum_length_label = new QLabel(tr("Minimum line length:")); minimum_length_edit = Util::SpinBox::create(2, 0.0, 999999.9, tr("mm")); auto line_cap_label = new QLabel(tr("Line cap:")); line_cap_combo = new QComboBox(); line_cap_combo->addItem(tr("flat"), QVariant(LineSymbol::FlatCap)); line_cap_combo->addItem(tr("round"), QVariant(LineSymbol::RoundCap)); line_cap_combo->addItem(tr("square"), QVariant(LineSymbol::SquareCap)); line_cap_combo->addItem(tr("pointed"), QVariant(LineSymbol::PointedCap)); pointed_cap_length_label = new QLabel(tr("Cap length:")); pointed_cap_length_edit = Util::SpinBox::create(2, 0.0, 999999.9, tr("mm")); auto line_join_label = new QLabel(tr("Line join:")); line_join_combo = new QComboBox(); line_join_combo->addItem(tr("miter"), QVariant(LineSymbol::MiterJoin)); line_join_combo->addItem(tr("round"), QVariant(LineSymbol::RoundJoin)); line_join_combo->addItem(tr("bevel"), QVariant(LineSymbol::BevelJoin)); dashed_check = new QCheckBox(tr("Line is dashed")); border_check = new QCheckBox(tr("Enable border lines")); line_settings_list = { minimum_length_label, minimum_length_edit, line_cap_label, line_cap_combo, line_join_label, line_join_combo, pointed_cap_length_label, pointed_cap_length_edit, border_check, }; row++; col = 0; layout->addWidget(minimum_length_label, row, col++); layout->addWidget(minimum_length_edit, row, col, 1, -1); row++; col = 0; layout->addWidget(line_cap_label, row, col++); layout->addWidget(line_cap_combo, row, col, 1, -1); row++; col = 0; layout->addWidget(pointed_cap_length_label, row, col++); layout->addWidget(pointed_cap_length_edit, row, col, 1, -1); row++; col = 0; layout->addWidget(line_join_label, row, col++); layout->addWidget(line_join_combo, row, col, 1, -1); row++; col = 0; layout->addWidget(new QWidget(), row, col, 1, -1); row++; col = 0; layout->addWidget(Util::Headline::create(tr("Dashed line")), row, col++, 1, -1); row++; col = 0; layout->addWidget(dashed_check, row, col, 1, -1); auto dash_length_label = new QLabel(tr("Dash length:")); dash_length_edit = Util::SpinBox::create(2, 0.0, 999999.9, tr("mm")); auto break_length_label = new QLabel(tr("Break length:")); break_length_edit = Util::SpinBox::create(2, 0.0, 999999.9, tr("mm")); auto dash_group_label = new QLabel(tr("Dashes grouped together:")); dash_group_combo = new QComboBox(); dash_group_combo->addItem(tr("none"), QVariant(1)); dash_group_combo->addItem(tr("2"), QVariant(2)); dash_group_combo->addItem(tr("3"), QVariant(3)); dash_group_combo->addItem(tr("4"), QVariant(4)); in_group_break_length_label = new QLabel(tr("In-group break length:")); in_group_break_length_edit = Util::SpinBox::create(2, 0.0, 999999.9, tr("mm")); half_outer_dashes_check = new QCheckBox(tr("Half length of first and last dash")); dashed_widget_list = { dash_length_label, dash_length_edit, break_length_label, break_length_edit, dash_group_label, dash_group_combo, in_group_break_length_label, in_group_break_length_edit, half_outer_dashes_check, }; row++; col = 0; layout->addWidget(dash_length_label, row, col++); layout->addWidget(dash_length_edit, row, col, 1, -1); row++; col = 0; layout->addWidget(break_length_label, row, col++); layout->addWidget(break_length_edit, row, col, 1, -1); row++; col = 0; layout->addWidget(dash_group_label, row, col++); layout->addWidget(dash_group_combo, row, col, 1, -1); row++; col = 0; layout->addWidget(in_group_break_length_label, row, col++); layout->addWidget(in_group_break_length_edit, row, col, 1, -1); row++; col = 0; layout->addWidget(half_outer_dashes_check, row, col, 1, -1); row++; col = 0; layout->addWidget(new QWidget(), row, col, 1, -1); row++; col = 0; layout->addWidget(Util::Headline::create(tr("Mid symbols")), row, col, 1, -1); auto mid_symbol_per_spot_label = new QLabel(tr("Mid symbols per spot:")); mid_symbol_per_spot_edit = Util::SpinBox::create(1, 99); mid_symbol_distance_label = new QLabel(tr("Mid symbol distance:")); mid_symbol_distance_edit = Util::SpinBox::create(2, 0.0, 999999.9, tr("mm")); mid_symbol_widget_list = { mid_symbol_per_spot_label, mid_symbol_per_spot_edit, mid_symbol_distance_label, mid_symbol_distance_edit, }; row++; col = 0; layout->addWidget(mid_symbol_per_spot_label, row, col++); layout->addWidget(mid_symbol_per_spot_edit, row, col, 1, -1); row++; col = 0; layout->addWidget(mid_symbol_distance_label, row, col++); layout->addWidget(mid_symbol_distance_edit, row, col, 1, -1); auto segment_length_label = new QLabel(tr("Distance between spots:")); segment_length_edit = Util::SpinBox::create(2, 0.0, 999999.9, tr("mm")); auto end_length_label = new QLabel(tr("Distance from line end:")); end_length_edit = Util::SpinBox::create(2, 0.0, 999999.9, tr("mm")); show_at_least_one_symbol_check = new QCheckBox(tr("Show at least one mid symbol")); auto minimum_mid_symbol_count_label = new QLabel(tr("Minimum mid symbol count:")); minimum_mid_symbol_count_edit = Util::SpinBox::create(0, 99); auto minimum_mid_symbol_count_when_closed_label = new QLabel(tr("Minimum mid symbol count when closed:")); minimum_mid_symbol_count_when_closed_edit = Util::SpinBox::create(0, 99); undashed_widget_list = { segment_length_label, segment_length_edit, end_length_label, end_length_edit, show_at_least_one_symbol_check, minimum_mid_symbol_count_label, minimum_mid_symbol_count_edit, minimum_mid_symbol_count_when_closed_label, minimum_mid_symbol_count_when_closed_edit, }; row++; col = 0; layout->addWidget(segment_length_label, row, col++); layout->addWidget(segment_length_edit, row, col, 1, -1); row++; col = 0; layout->addWidget(end_length_label, row, col++); layout->addWidget(end_length_edit, row, col, 1, -1); row++; col = 0; layout->addWidget(show_at_least_one_symbol_check, row, col, 1, -1); row++; col = 0; layout->addWidget(minimum_mid_symbol_count_label, row, col++); layout->addWidget(minimum_mid_symbol_count_edit, row, col, 1, -1); row++; col = 0; layout->addWidget(minimum_mid_symbol_count_when_closed_label, row, col++); layout->addWidget(minimum_mid_symbol_count_when_closed_edit, row, col, 1, -1); row++; col = 0; layout->addWidget(new QWidget(), row, col, 1, -1); row++; col = 0; layout->addWidget(Util::Headline::create(tr("Borders")), row, col++, 1, -1); row++; col = 0; layout->addWidget(border_check, row, col, 1, -1); different_borders_check = new QCheckBox(tr("Different borders on left and right sides")); row++; col = 0; layout->addWidget(different_borders_check, row, col, 1, -1); auto left_border_label = Util::Headline::create(tr("Left border:")); row++; col = 0; layout->addWidget(left_border_label, row, col++, 1, -1); createBorderWidgets(symbol->getBorder(), map, row, col, layout, border_widgets); auto right_border_label = Util::Headline::create(tr("Right border:")); row++; col = 0; layout->addWidget(right_border_label, row, col++, 1, -1); createBorderWidgets(symbol->getRightBorder(), map, row, col, layout, right_border_widgets); border_widget_list.reserve(1 + border_widgets.widget_list.size()); border_widget_list.push_back(different_borders_check); border_widget_list.insert(border_widget_list.end(), begin(border_widgets.widget_list), end(border_widgets.widget_list)); different_borders_widget_list.reserve(2 + right_border_widgets.widget_list.size()); different_borders_widget_list.push_back(left_border_label); different_borders_widget_list.push_back(right_border_label); different_borders_widget_list.insert(different_borders_widget_list.end(), begin(right_border_widgets.widget_list), end(right_border_widgets.widget_list)); row++; col = 0; layout->addWidget(new QWidget(), row, col, 1, -1); row++; col = 0; layout->addWidget(Util::Headline::create(tr("Dash symbol")), row, col++, 1, -1); supress_dash_symbol_check = new QCheckBox(tr("Suppress the dash symbol at line start and line end")); row++; col = 0; layout->addWidget(supress_dash_symbol_check, row, col, 1, -1); scale_dash_symbol_check = new QCheckBox(tr("Scale the dash symbol at corners")); row++; col = 0; layout->addWidget(scale_dash_symbol_check, row, col, 1, -1); row++; layout->setRowStretch(row, 1); const int line_tab_width = line_tab->sizeHint().width(); scroll_area = new QScrollArea(); scroll_area->setWidget(line_tab); scroll_area->setWidgetResizable(true); scroll_area->setFrameShape(QFrame::NoFrame); scroll_area->setMinimumWidth(line_tab_width + scroll_area->verticalScrollBar()->sizeHint().width()); addPropertiesGroup(tr("Line settings"), scroll_area); PointSymbolEditorWidget* point_symbol_editor = nullptr; auto controller = dialog->getPreviewController(); symbol->ensurePointSymbols(tr("Start symbol"), tr("Mid symbol"), tr("End symbol"), tr("Dash symbol")); for (auto point_symbol : { symbol->getStartSymbol(), symbol->getMidSymbol(), symbol->getEndSymbol(), symbol->getDashSymbol() }) { point_symbol_editor = new PointSymbolEditorWidget(controller, point_symbol, 16); addPropertiesGroup(point_symbol->getName(), point_symbol_editor); connect(point_symbol_editor, &PointSymbolEditorWidget::symbolEdited, this, &LineSymbolSettings::pointSymbolEdited); } updateStates(); updateContents(); connect(width_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &LineSymbolSettings::widthChanged); connect(color_edit, QOverload::of(&QComboBox::currentIndexChanged), this, &LineSymbolSettings::colorChanged); connect(minimum_length_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &LineSymbolSettings::minimumDimensionsEdited); connect(line_cap_combo, QOverload::of(&QComboBox::currentIndexChanged), this, &LineSymbolSettings::lineCapChanged); connect(line_join_combo, QOverload::of(&QComboBox::currentIndexChanged), this, &LineSymbolSettings::lineJoinChanged); connect(pointed_cap_length_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &LineSymbolSettings::pointedLineCapLengthChanged); connect(dashed_check, &QAbstractButton::clicked, this, &LineSymbolSettings::dashedChanged); connect(segment_length_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &LineSymbolSettings::segmentLengthChanged); connect(end_length_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &LineSymbolSettings::endLengthChanged); connect(show_at_least_one_symbol_check, &QAbstractButton::clicked, this, &LineSymbolSettings::showAtLeastOneSymbolChanged); connect(minimum_mid_symbol_count_edit, QOverload::of(&QSpinBox::valueChanged), this, &LineSymbolSettings::minimumDimensionsEdited); connect(minimum_mid_symbol_count_when_closed_edit, QOverload::of(&QSpinBox::valueChanged), this, &LineSymbolSettings::minimumDimensionsEdited); connect(dash_length_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &LineSymbolSettings::dashLengthChanged); connect(break_length_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &LineSymbolSettings::breakLengthChanged); connect(dash_group_combo, QOverload::of(&QComboBox::currentIndexChanged), this, &LineSymbolSettings::dashGroupsChanged); connect(in_group_break_length_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &LineSymbolSettings::inGroupBreakLengthChanged); connect(half_outer_dashes_check, &QAbstractButton::clicked, this, &LineSymbolSettings::halfOuterDashesChanged); connect(mid_symbol_per_spot_edit, QOverload::of(&QSpinBox::valueChanged), this, &LineSymbolSettings::midSymbolsPerDashChanged); connect(mid_symbol_distance_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &LineSymbolSettings::midSymbolDistanceChanged); connect(border_check, &QAbstractButton::clicked, this, &LineSymbolSettings::borderCheckClicked); connect(different_borders_check, &QAbstractButton::clicked, this, &LineSymbolSettings::differentBordersClicked); connect(supress_dash_symbol_check, &QAbstractButton::clicked, this, &LineSymbolSettings::suppressDashSymbolClicked); connect(scale_dash_symbol_check, &QCheckBox::clicked, this, &LineSymbolSettings::scaleDashSymbolClicked); } LineSymbolSettings::~LineSymbolSettings() = default; void LineSymbolSettings::pointSymbolEdited() { emit propertiesModified(); updateStates(); } void LineSymbolSettings::widthChanged(double value) { symbol->line_width = qRound(1000 * value); emit propertiesModified(); updateStates(); } void LineSymbolSettings::colorChanged() { symbol->color = color_edit->color(); emit propertiesModified(); updateStates(); } void LineSymbolSettings::minimumDimensionsEdited() { symbol->minimum_length = qRound(1000.0 * minimum_length_edit->value()); symbol->minimum_mid_symbol_count = minimum_mid_symbol_count_edit->value(); symbol->minimum_mid_symbol_count_when_closed = minimum_mid_symbol_count_when_closed_edit->value(); emit propertiesModified(); } void LineSymbolSettings::lineCapChanged(int index) { symbol->cap_style = LineSymbol::CapStyle(line_cap_combo->itemData(index).toInt()); emit propertiesModified(); updateStates(); } void LineSymbolSettings::lineJoinChanged(int index) { symbol->join_style = LineSymbol::JoinStyle(line_join_combo->itemData(index).toInt()); emit propertiesModified(); } void LineSymbolSettings::pointedLineCapLengthChanged(double value) { symbol->pointed_cap_length = qRound(1000.0 * value); emit propertiesModified(); } void LineSymbolSettings::dashedChanged(bool checked) { symbol->dashed = checked; if (!symbol->color) { symbol->break_length = 0; break_length_edit->setValue(0); } emit propertiesModified(); updateStates(); if (checked && symbol->color) ensureWidgetVisible(half_outer_dashes_check); } void LineSymbolSettings::segmentLengthChanged(double value) { symbol->segment_length = qRound(1000.0 * value); emit propertiesModified(); } void LineSymbolSettings::endLengthChanged(double value) { symbol->end_length = qRound(1000.0 * value); emit propertiesModified(); updateStates(); } void LineSymbolSettings::showAtLeastOneSymbolChanged(bool checked) { symbol->show_at_least_one_symbol = checked; emit propertiesModified(); } void LineSymbolSettings::dashLengthChanged(double value) { symbol->dash_length = qRound(1000.0 * value); emit propertiesModified(); } void LineSymbolSettings::breakLengthChanged(double value) { symbol->break_length = qRound(1000.0 * value); emit propertiesModified(); } void LineSymbolSettings::dashGroupsChanged(int index) { symbol->dashes_in_group = dash_group_combo->itemData(index).toInt(); if (symbol->dashes_in_group > 1) { symbol->half_outer_dashes = false; half_outer_dashes_check->setChecked(false); } emit propertiesModified(); updateStates(); } void LineSymbolSettings::inGroupBreakLengthChanged(double value) { symbol->in_group_break_length = qRound(1000.0 * value); emit propertiesModified(); } void LineSymbolSettings::halfOuterDashesChanged(bool checked) { symbol->half_outer_dashes = checked; emit propertiesModified(); } void LineSymbolSettings::midSymbolsPerDashChanged(int value) { symbol->mid_symbols_per_spot = qMax(1, value); emit propertiesModified(); updateStates(); } void LineSymbolSettings::midSymbolDistanceChanged(double value) { symbol->mid_symbol_distance = qRound(1000.0 * value); emit propertiesModified(); } void LineSymbolSettings::borderCheckClicked(bool checked) { symbol->have_border_lines = checked; emit propertiesModified(); updateStates(); if (checked) { if (symbol->areBordersDifferent()) ensureWidgetVisible(right_border_widgets.break_length_edit); else ensureWidgetVisible(border_widgets.break_length_edit); } } void LineSymbolSettings::differentBordersClicked(bool checked) { if (checked) { updateStates(); blockSignalsRecursively(this, true); updateBorderContents(symbol->getRightBorder(), right_border_widgets); blockSignalsRecursively(this, false); ensureWidgetVisible(right_border_widgets.break_length_edit); } else { symbol->getRightBorder().assign(symbol->getBorder(), nullptr); emit propertiesModified(); updateStates(); } } void LineSymbolSettings::borderChanged() { updateBorder(symbol->getBorder(), border_widgets); if (different_borders_check->isChecked()) updateBorder(symbol->getRightBorder(), right_border_widgets); else symbol->getRightBorder().assign(symbol->getBorder(), nullptr); emit propertiesModified(); } void LineSymbolSettings::suppressDashSymbolClicked(bool checked) { symbol->suppress_dash_symbol_at_ends = checked; emit propertiesModified(); } void LineSymbolSettings::scaleDashSymbolClicked(bool checked) { symbol->setScaleDashSymbol(checked); emit propertiesModified(); } void LineSymbolSettings::createBorderWidgets(LineSymbolBorder& border, Map* map, int& row, int col, QGridLayout* layout, BorderWidgets& widgets) { auto width_label = new QLabel(tr("Border width:")); widgets.width_edit = Util::SpinBox::create(2, 0.0, 999999.9, tr("mm")); auto color_label = new QLabel(tr("Border color:")); widgets.color_edit = new ColorDropDown(map, border.color); auto shift_label = new QLabel(tr("Border shift:")); widgets.shift_edit = Util::SpinBox::create(2, 0.0, 999999.9, tr("mm")); widgets.dashed_check = new QCheckBox(tr("Border is dashed")); widgets.dashed_check->setChecked(border.dashed); widgets.widget_list = { width_label, widgets.width_edit, color_label, widgets.color_edit, shift_label, widgets.shift_edit, widgets.dashed_check }; row++; col = 0; layout->addWidget(width_label, row, col++); layout->addWidget(widgets.width_edit, row, col, 1, -1); row++; col = 0; layout->addWidget(color_label, row, col++); layout->addWidget(widgets.color_edit, row, col, 1, -1); row++; col = 0; layout->addWidget(shift_label, row, col++); layout->addWidget(widgets.shift_edit, row, col, 1, -1); row++; col = 0; layout->addWidget(widgets.dashed_check, row, col, 1, -1); auto dash_length_label = new QLabel(tr("Border dash length:")); widgets.dash_length_edit = Util::SpinBox::create(2, 0.0, 999999.9, tr("mm")); auto break_length_label = new QLabel(tr("Border break length:")); widgets.break_length_edit = Util::SpinBox::create(2, 0.0, 999999.9, tr("mm")); widgets.dash_widget_list = { dash_length_label, widgets.dash_length_edit, break_length_label, widgets.break_length_edit, }; row++; col = 0; layout->addWidget(dash_length_label, row, col++); layout->addWidget(widgets.dash_length_edit, row, col, 1, -1); row++; col = 0; layout->addWidget(break_length_label, row, col++); layout->addWidget(widgets.break_length_edit, row, col, 1, -1); connect(widgets.width_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &LineSymbolSettings::borderChanged); connect(widgets.color_edit, QOverload::of(&QComboBox::currentIndexChanged), this, &LineSymbolSettings::borderChanged); connect(widgets.shift_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &LineSymbolSettings::borderChanged); connect(widgets.dashed_check, &QAbstractButton::clicked, this, &LineSymbolSettings::borderChanged); connect(widgets.dash_length_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &LineSymbolSettings::borderChanged); connect(widgets.break_length_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &LineSymbolSettings::borderChanged); } void LineSymbolSettings::updateBorder(LineSymbolBorder& border, LineSymbolSettings::BorderWidgets& widgets) { bool ensure_last_widget_visible = false; border.width = qRound(1000.0 * widgets.width_edit->value()); border.color = widgets.color_edit->color(); border.shift = qRound(1000.0 * widgets.shift_edit->value()); if (widgets.dashed_check->isChecked() && !border.dashed) ensure_last_widget_visible = true; border.dashed = widgets.dashed_check->isChecked(); border.dash_length = qRound(1000.0 * widgets.dash_length_edit->value()); border.break_length = qRound(1000.0 * widgets.break_length_edit->value()); updateStates(); if (ensure_last_widget_visible) ensureWidgetVisible(widgets.break_length_edit); } void LineSymbolSettings::updateBorderContents(LineSymbolBorder& border, LineSymbolSettings::BorderWidgets& widgets) { Q_ASSERT(this->signalsBlocked()); widgets.width_edit->setValue(0.001 * border.width); widgets.color_edit->setColor(border.color); widgets.shift_edit->setValue(0.001 * border.shift); widgets.dashed_check->setChecked(border.dashed); widgets.dash_length_edit->setValue(0.001 * border.dash_length); widgets.break_length_edit->setValue(0.001 * border.break_length); } void LineSymbolSettings::ensureWidgetVisible(QWidget* widget) { widget_to_ensure_visible = widget; QTimer::singleShot(0, this, SLOT(ensureWidgetVisible())); // clazy:exclude=old-style-connect (needs Qt 5.4) } void LineSymbolSettings::ensureWidgetVisible() { scroll_area->ensureWidgetVisible(widget_to_ensure_visible, 5, 5); } void LineSymbolSettings::updateStates() { const bool symbol_active = symbol->line_width > 0; color_edit->setEnabled(symbol_active); const bool line_active = symbol_active && symbol->color; for (auto line_settings_widget : line_settings_list) { line_settings_widget->setEnabled(line_active); } if (line_active && symbol->cap_style != LineSymbol::PointedCap) { pointed_cap_length_label->setEnabled(false); pointed_cap_length_edit->setEnabled(false); } const bool line_dashed = symbol->dashed && symbol->color; if (line_dashed) { for (auto undashed_widget : undashed_widget_list) { undashed_widget->setVisible(false); } for (auto dashed_widget : dashed_widget_list) { dashed_widget->setVisible(true); dashed_widget->setEnabled(line_active); } in_group_break_length_label->setEnabled(line_active && symbol->dashes_in_group > 1); in_group_break_length_edit->setEnabled(line_active && symbol->dashes_in_group > 1); half_outer_dashes_check->setEnabled(line_active && symbol->dashes_in_group == 1); } else { for (auto undashed_widget : undashed_widget_list) { undashed_widget->setVisible(true); undashed_widget->setEnabled(!symbol->mid_symbol->isEmpty()); } show_at_least_one_symbol_check->setEnabled(show_at_least_one_symbol_check->isEnabled() && symbol->end_length > 0); for (auto dashed_widget : dashed_widget_list) { dashed_widget->setVisible(false); } } for (auto mid_symbol_widget : mid_symbol_widget_list) { mid_symbol_widget->setEnabled(!symbol->mid_symbol->isEmpty()); } mid_symbol_distance_label->setEnabled(mid_symbol_distance_label->isEnabled() && symbol->mid_symbols_per_spot > 1); mid_symbol_distance_edit->setEnabled(mid_symbol_distance_edit->isEnabled() && symbol->mid_symbols_per_spot > 1); const bool border_active = symbol_active && symbol->have_border_lines; for (auto border_widget : border_widget_list) { border_widget->setVisible(border_active); border_widget->setEnabled(border_active); } for (auto border_dash_widget : border_widgets.dash_widget_list) { border_dash_widget->setVisible(border_active); border_dash_widget->setEnabled(border_active && symbol->getBorder().dashed); } const bool different_borders = border_active && different_borders_check->isChecked(); for (auto different_border_widget : different_borders_widget_list) { different_border_widget->setVisible(different_borders); different_border_widget->setEnabled(different_borders); } for (auto border_dash_widget : right_border_widgets.dash_widget_list) { border_dash_widget->setVisible(different_borders); border_dash_widget->setEnabled(different_borders && symbol->getRightBorder().dashed); } } void LineSymbolSettings::updateContents() { blockSignalsRecursively(this, true); width_edit->setValue(0.001 * symbol->getLineWidth()); color_edit->setColor(symbol->getColor()); minimum_length_edit->setValue(0.001 * symbol->minimum_length); line_cap_combo->setCurrentIndex(line_cap_combo->findData(symbol->cap_style)); pointed_cap_length_edit->setValue(0.001 * symbol->pointed_cap_length); line_join_combo->setCurrentIndex(line_join_combo->findData(symbol->join_style)); dashed_check->setChecked(symbol->dashed); border_check->setChecked(symbol->have_border_lines); different_borders_check->setChecked(symbol->areBordersDifferent()); dash_length_edit->setValue(0.001 * symbol->dash_length); break_length_edit->setValue(0.001 * symbol->break_length); dash_group_combo->setCurrentIndex(dash_group_combo->findData(QVariant(symbol->dashes_in_group))); in_group_break_length_edit->setValue(0.001 * symbol->in_group_break_length); half_outer_dashes_check->setChecked(symbol->half_outer_dashes); mid_symbol_per_spot_edit->setValue(symbol->mid_symbols_per_spot); mid_symbol_distance_edit->setValue(0.001 * symbol->mid_symbol_distance); segment_length_edit->setValue(0.001 * symbol->segment_length); end_length_edit->setValue(0.001 * symbol->end_length); show_at_least_one_symbol_check->setChecked(symbol->show_at_least_one_symbol); minimum_mid_symbol_count_edit->setValue(symbol->minimum_mid_symbol_count); minimum_mid_symbol_count_when_closed_edit->setValue(symbol->minimum_mid_symbol_count_when_closed); updateBorderContents(symbol->getBorder(), border_widgets); updateBorderContents(symbol->getRightBorder(), right_border_widgets); supress_dash_symbol_check->setChecked(symbol->getSuppressDashSymbolAtLineEnds()); scale_dash_symbol_check->setChecked(symbol->getScaleDashSymbol()); blockSignalsRecursively(this, false); /* PointSymbolEditorWidget* point_symbol_editor = nullptr; auto controller = dialog->getPreviewController(); QList point_symbols; point_symbols << symbol->getStartSymbol() << symbol->getMidSymbol() << symbol->getEndSymbol() << symbol->getDashSymbol(); for (auto point_symbol : point_symbols) { point_symbol_editor = new PointSymbolEditorWidget(controller, point_symbol, 16); addPropertiesGroup(point_symbol->getName(), point_symbol_editor); connect(point_symbol_editor, SIGNAL(symbolEdited()), this, SLOT(pointSymbolEdited())); } */ updateStates(); } void LineSymbolSettings::reset(Symbol* symbol) { Q_ASSERT(symbol->getType() == Symbol::Line); SymbolPropertiesWidget::reset(symbol); auto old_symbol = this->symbol; this->symbol = reinterpret_cast(symbol); PointSymbolEditorWidget* point_symbol_editor = nullptr; auto controller = dialog->getPreviewController(); int current = currentIndex(); setUpdatesEnabled(false); this->symbol->ensurePointSymbols(tr("Start symbol"), tr("Mid symbol"), tr("End symbol"), tr("Dash symbol")); for (auto point_symbol : { this->symbol->getStartSymbol(), this->symbol->getMidSymbol(), this->symbol->getEndSymbol(), this->symbol->getDashSymbol() }) { point_symbol_editor = new PointSymbolEditorWidget(controller, point_symbol, 16); connect(point_symbol_editor, &PointSymbolEditorWidget::symbolEdited, this, &LineSymbolSettings::pointSymbolEdited); int index = indexOfPropertiesGroup(point_symbol->getName()); // existing symbol editor removePropertiesGroup(index); insertPropertiesGroup(index, point_symbol->getName(), point_symbol_editor); if (index == current) setCurrentIndex(current); } updateContents(); setUpdatesEnabled(true); old_symbol->cleanupPointSymbols(); } } // namespace OpenOrienteering mapper-0.8.1.1/src/gui/symbols/line_symbol_settings.h000066400000000000000000000125561325266516600226400ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2012-2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_LINE_SYMBOL_SETTINGS_H #define OPENORIENTEERING_LINE_SYMBOL_SETTINGS_H #include #include #include "gui/symbols/symbol_properties_widget.h" class QCheckBox; class QComboBox; class QDoubleSpinBox; class QGridLayout; class QLabel; class QScrollArea; class QSpinBox; class QWidget; namespace OpenOrienteering { class ColorDropDown; class LineSymbol; struct LineSymbolBorder; class Map; class Symbol; class SymbolSettingDialog; class LineSymbolSettings : public SymbolPropertiesWidget { Q_OBJECT public: LineSymbolSettings(LineSymbol* symbol, SymbolSettingDialog* dialog); ~LineSymbolSettings() override; void reset(Symbol* symbol) override; /** * Updates the content and state of all input fields. */ void updateContents(); protected: struct BorderWidgets { std::vector widget_list; QDoubleSpinBox* width_edit; ColorDropDown* color_edit; QDoubleSpinBox* shift_edit; QCheckBox* dashed_check; std::vector dash_widget_list; QDoubleSpinBox* dash_length_edit; QDoubleSpinBox* break_length_edit; }; /** * Creates the widgets for one border. */ void createBorderWidgets(LineSymbolBorder& border, Map* map, int& row, int col, QGridLayout* layout, BorderWidgets& widgets); /** * Updates the border settings from the values in the widgets. */ void updateBorder(LineSymbolBorder& border, BorderWidgets& widgets); void updateBorderContents(LineSymbolBorder& border, BorderWidgets& widgets); /** * Ensures that a particular widget is visible in the scoll area. */ void ensureWidgetVisible(QWidget* widget); /** * Adjusts the visibility and enabled state of all UI parts. * There is a large number of dependencies between different elements * of the line settings. This method handles them all. */ void updateStates(); protected slots: /** * Notifies this settings widget that one of the symbols has been modified. */ void pointSymbolEdited(); void widthChanged(double value); void colorChanged(); void minimumDimensionsEdited(); void lineCapChanged(int index); void lineJoinChanged(int index); void pointedLineCapLengthChanged(double value); void dashedChanged(bool checked); void segmentLengthChanged(double value); void endLengthChanged(double value); void showAtLeastOneSymbolChanged(bool checked); void dashLengthChanged(double value); void breakLengthChanged(double value); void dashGroupsChanged(int index); void inGroupBreakLengthChanged(double value); void halfOuterDashesChanged(bool checked); void midSymbolsPerDashChanged(int value); void midSymbolDistanceChanged(double value); void borderCheckClicked(bool checked); void differentBordersClicked(bool checked); void borderChanged(); void suppressDashSymbolClicked(bool checked); void scaleDashSymbolClicked(bool checked); private slots: /** Ensure that a predetermined widget is visible in the scoll area. * The widget is set in advance by ensureWidgetVisible(QWidget* widget). */ void ensureWidgetVisible(); private: LineSymbol* symbol; SymbolSettingDialog* dialog; QDoubleSpinBox* width_edit; ColorDropDown* color_edit; QDoubleSpinBox* minimum_length_edit; // enabled if line_width > 0 && color std::vector line_settings_list; QComboBox* line_cap_combo; QComboBox* line_join_combo; QLabel* pointed_cap_length_label; QDoubleSpinBox* pointed_cap_length_edit; QCheckBox* dashed_check; // dashed == false && mid_symbol std::vector undashed_widget_list; QDoubleSpinBox* segment_length_edit; QDoubleSpinBox* end_length_edit; QCheckBox* show_at_least_one_symbol_check; QSpinBox* minimum_mid_symbol_count_edit; QSpinBox* minimum_mid_symbol_count_when_closed_edit; // dashed == true std::vector dashed_widget_list; QDoubleSpinBox* dash_length_edit; QDoubleSpinBox* break_length_edit; QComboBox* dash_group_combo; QLabel* in_group_break_length_label; QDoubleSpinBox* in_group_break_length_edit; QCheckBox* half_outer_dashes_check; // mid_symbol std::vector mid_symbol_widget_list; QSpinBox* mid_symbol_per_spot_edit; QLabel* mid_symbol_distance_label; QDoubleSpinBox* mid_symbol_distance_edit; // enabled if line_width > 0 std::vector border_widget_list; QCheckBox* border_check; QCheckBox* different_borders_check; std::vector different_borders_widget_list; BorderWidgets border_widgets; BorderWidgets right_border_widgets; QCheckBox* supress_dash_symbol_check; QCheckBox* scale_dash_symbol_check; QScrollArea* scroll_area; QWidget* widget_to_ensure_visible; }; } // namespace OpenOrienteering #endif mapper-0.8.1.1/src/gui/symbols/point_symbol_editor_widget.cpp000066400000000000000000001055571325266516600243720ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2012-2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "point_symbol_editor_widget.h" #include #include #include // IWYU pragma: no_include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "core/map.h" #include "core/objects/object.h" #include "core/symbols/area_symbol.h" #include "core/symbols/line_symbol.h" #include "core/symbols/point_symbol.h" #include "core/symbols/symbol.h" #include "gui/modifier_key.h" #include "gui/map/map_editor.h" #include "gui/map/map_widget.h" #include "gui/util_gui.h" #include "gui/widgets/color_dropdown.h" #include "util/backports.h" // IWYU pragma: no_forward_declare QBoxLayout // IWYU pragma: no_forward_declare QGridLayout // IWYU pragma: no_forward_declare QHBoxLayout // IWYU pragma: no_forward_declare QMenu // IWYU pragma: no_forward_declare QTableWidgetItem // IWYU pragma: no_forward_declare QVBoxLayout namespace OpenOrienteering { PointSymbolEditorWidget::PointSymbolEditorWidget(MapEditorController* controller, PointSymbol* symbol, qreal offset_y, bool permanent_preview, QWidget* parent) : QWidget(parent) , symbol(symbol) , object_origin_coord(0, offset_y) , offset_y(offset_y) , controller(controller) , permanent_preview(permanent_preview) { map = controller->getMap(); midpoint_object = nullptr; if (permanent_preview) { midpoint_object = new PointObject(symbol); midpoint_object->setPosition(object_origin_coord); map->addObject(midpoint_object); } oriented_to_north = new QCheckBox(tr("Always oriented to north (not rotatable)")); oriented_to_north->setChecked(!symbol->rotatable); QLabel* elements_label = Util::Headline::create(tr("Elements")); element_list = new QListWidget(); initElementList(); delete_element_button = new QPushButton(QIcon(QString::fromLatin1(":/images/minus.png")), QString{}); QToolButton* add_element_button = new QToolButton(); add_element_button->setIcon(QIcon(QString::fromLatin1(":/images/plus.png"))); add_element_button->setToolButtonStyle(Qt::ToolButtonIconOnly); add_element_button->setPopupMode(QToolButton::InstantPopup); add_element_button->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed)); add_element_button->setMinimumSize(delete_element_button->sizeHint()); QMenu* add_element_button_menu = new QMenu(add_element_button); add_element_button_menu->addAction(tr("Point"), this, SLOT(addPointClicked())); add_element_button_menu->addAction(tr("Line"), this, SLOT(addLineClicked())); add_element_button_menu->addAction(tr("Area"), this, SLOT(addAreaClicked())); add_element_button->setMenu(add_element_button_menu); center_all_elements_button = new QPushButton(tr("Center all elements")); QLabel* current_element_label = Util::Headline::create(tr("Current element")); element_properties_widget = new QStackedWidget(); // Point (circle) point_properties = new QWidget(); QLabel* point_inner_radius_label = new QLabel(tr("Diameter a:")); point_inner_radius_edit = Util::SpinBox::create(2, 0.0, 99999.9, tr("mm")); QLabel* point_inner_color_label = new QLabel(tr("Inner color:")); point_inner_color_edit = new ColorDropDown(map); QLabel* point_outer_width_label = new QLabel(tr("Outer width b:")); point_outer_width_edit = Util::SpinBox::create(2, 0.0, 99999.9, tr("mm")); QLabel* point_outer_color_label = new QLabel(tr("Outer color:")); point_outer_color_edit = new ColorDropDown(map); QLabel* explanation_label = new QLabel(); explanation_label->setPixmap(QPixmap(QString::fromLatin1(":/images/symbol_point_explanation.png"))); QGridLayout* point_layout = new QGridLayout(); point_layout->setContentsMargins(0, 0, 0, 0); point_layout->addWidget(point_inner_radius_label, 0, 0); point_layout->addWidget(point_inner_radius_edit, 0, 1); point_layout->addWidget(point_inner_color_label, 1, 0); point_layout->addWidget(point_inner_color_edit, 1, 1); point_layout->addWidget(new QWidget(), 2, 0, 1, -1); point_layout->addWidget(point_outer_width_label, 3, 0); point_layout->addWidget(point_outer_width_edit, 3, 1); point_layout->addWidget(point_outer_color_label, 4, 0); point_layout->addWidget(point_outer_color_edit, 4, 1); point_layout->addWidget(explanation_label, 0, 2, 5, 1); point_layout->setRowStretch(6, 1); point_layout->setColumnStretch(1,1); point_properties->setLayout(point_layout); element_properties_widget->addWidget(point_properties); // Line line_properties = new QWidget(); QLabel* line_width_label = new QLabel(tr("Line width:")); line_width_edit = Util::SpinBox::create(3, 0.0, 99999.9, tr("mm")); QLabel* line_color_label = new QLabel(tr("Line color:")); line_color_edit = new ColorDropDown(map); QLabel* line_cap_label = new QLabel(tr("Line cap:")); line_cap_edit = new QComboBox(); line_cap_edit->addItem(tr("flat"), QVariant(LineSymbol::FlatCap)); line_cap_edit->addItem(tr("round"), QVariant(LineSymbol::RoundCap)); line_cap_edit->addItem(tr("square"), QVariant(LineSymbol::SquareCap)); //line_cap_edit->addItem(tr("pointed"), QVariant(LineSymbol::PointedCap)); // this would require another input field for the cap length QLabel* line_join_label = new QLabel(tr("Line join:")); line_join_edit = new QComboBox(); line_join_edit->addItem(tr("miter"), QVariant(LineSymbol::MiterJoin)); line_join_edit->addItem(tr("round"), QVariant(LineSymbol::RoundJoin)); line_join_edit->addItem(tr("bevel"), QVariant(LineSymbol::BevelJoin)); line_closed_check = new QCheckBox(tr("Line closed")); QGridLayout* line_layout = new QGridLayout(); line_layout->setContentsMargins(0, 0, 0, 0); line_layout->addWidget(line_width_label, 0, 0); line_layout->addWidget(line_width_edit, 0, 1); line_layout->addWidget(line_color_label, 1, 0); line_layout->addWidget(line_color_edit, 1, 1); line_layout->addWidget(line_cap_label, 2, 0); line_layout->addWidget(line_cap_edit, 2, 1); line_layout->addWidget(line_join_label, 3, 0); line_layout->addWidget(line_join_edit, 3, 1); line_layout->addWidget(line_closed_check, 4, 0, 1, 2); line_layout->setRowStretch(5, 1); line_layout->setColumnStretch(1,1); line_properties->setLayout(line_layout); element_properties_widget->addWidget(line_properties); // Area area_properties = new QWidget(); QLabel* area_color_label = new QLabel(tr("Area color:")); area_color_edit = new ColorDropDown(map); QGridLayout* area_layout = new QGridLayout(); area_layout->setContentsMargins(0, 0, 0, 0); area_layout->addWidget(area_color_label, 0, 0); area_layout->addWidget(area_color_edit, 0, 1); area_layout->setRowStretch(1, 1); area_layout->setColumnStretch(1,1); area_properties->setLayout(area_layout); element_properties_widget->addWidget(area_properties); // Coordinates coords_label = new QLabel(tr("Coordinates:")); coords_table = new QTableWidget(0, 3); coords_table->setEditTriggers(QAbstractItemView::AllEditTriggers); coords_table->setSelectionBehavior(QAbstractItemView::SelectRows); coords_table->setHorizontalHeaderLabels(QStringList() << tr("X") << tr("Y") << tr("Curve start")); coords_table->verticalHeader()->setVisible(false); QHeaderView* header_view = coords_table->horizontalHeader(); header_view->setSectionResizeMode(0, QHeaderView::Interactive); header_view->setSectionResizeMode(1, QHeaderView::Interactive); header_view->setSectionResizeMode(2, QHeaderView::ResizeToContents); header_view->setSectionsClickable(false); add_coord_button = new QPushButton(QIcon(QString::fromLatin1(":/images/plus.png")), QString{}); delete_coord_button = new QPushButton(QIcon(QString::fromLatin1(":/images/minus.png")), QString{}); center_coords_button = new QPushButton(tr("Center by coordinate average")); // Layout QBoxLayout* left_layout = new QVBoxLayout(); left_layout->addWidget(elements_label); left_layout->addWidget(element_list, 1); QGridLayout* element_buttons_layout = new QGridLayout(); element_buttons_layout->setColumnStretch(0, 1); element_buttons_layout->addWidget(add_element_button, 1, 2); element_buttons_layout->addWidget(delete_element_button, 1, 3); element_buttons_layout->addWidget(center_all_elements_button, 2, 1, 1, 4); element_buttons_layout->setColumnStretch(5, 1); left_layout->addLayout(element_buttons_layout); QBoxLayout* right_layout = new QVBoxLayout(); right_layout->setMargin(0); right_layout->addWidget(current_element_label); right_layout->addWidget(element_properties_widget); right_layout->addSpacing(16); right_layout->addWidget(coords_label); right_layout->addWidget(coords_table); QHBoxLayout* coords_buttons_layout = new QHBoxLayout(); coords_buttons_layout->addWidget(add_coord_button); coords_buttons_layout->addWidget(delete_coord_button); coords_buttons_layout->addStretch(1); coords_buttons_layout->addWidget(center_coords_button); right_layout->addLayout(coords_buttons_layout); QBoxLayout* columns_layout = new QHBoxLayout; columns_layout->addLayout(left_layout); columns_layout->addSpacing(16); columns_layout->addLayout(right_layout); QVBoxLayout* layout = new QVBoxLayout(); layout->addWidget(oriented_to_north); layout->addSpacerItem(Util::SpacerItem::create(this)); layout->addLayout(columns_layout); setLayout(layout); // Connections connect(oriented_to_north, &QCheckBox::clicked, this, &PointSymbolEditorWidget::orientedToNorthClicked); connect(element_list, &QListWidget::currentRowChanged, this, &PointSymbolEditorWidget::changeElement); connect(delete_element_button, &QPushButton::clicked, this, &PointSymbolEditorWidget::deleteCurrentElement); connect(center_all_elements_button, &QPushButton::clicked, this, &PointSymbolEditorWidget::centerAllElements); connect(point_inner_radius_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &PointSymbolEditorWidget::pointInnerRadiusChanged); connect(point_inner_color_edit, QOverload::of(&QComboBox::currentIndexChanged), this, &PointSymbolEditorWidget::pointInnerColorChanged); connect(point_outer_width_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &PointSymbolEditorWidget::pointOuterWidthChanged); connect(point_outer_color_edit, QOverload::of(&QComboBox::currentIndexChanged), this, &PointSymbolEditorWidget::pointOuterColorChanged); connect(line_width_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &PointSymbolEditorWidget::lineWidthChanged); connect(line_color_edit, QOverload::of(&QComboBox::currentIndexChanged), this, &PointSymbolEditorWidget::lineColorChanged); connect(line_cap_edit, QOverload::of(&QComboBox::currentIndexChanged), this, &PointSymbolEditorWidget::lineCapChanged); connect(line_join_edit, QOverload::of(&QComboBox::currentIndexChanged), this, &PointSymbolEditorWidget::lineJoinChanged); connect(line_closed_check, &QCheckBox::clicked, this, &PointSymbolEditorWidget::lineClosedClicked); connect(area_color_edit, QOverload::of(&QComboBox::currentIndexChanged), this, &PointSymbolEditorWidget::areaColorChanged); connect(coords_table, &QTableWidget::currentCellChanged, this, &PointSymbolEditorWidget::updateDeleteCoordButton); connect(coords_table, &QTableWidget::cellChanged, this, &PointSymbolEditorWidget::coordinateChanged); connect(add_coord_button, &QPushButton::clicked, this, &PointSymbolEditorWidget::addCoordClicked); connect(delete_coord_button, &QPushButton::clicked, this, &PointSymbolEditorWidget::deleteCoordClicked); connect(center_coords_button, &QPushButton::clicked, this, &PointSymbolEditorWidget::centerCoordsClicked); } PointSymbolEditorWidget::~PointSymbolEditorWidget() { if (isVisible()) setEditorActive(false); if (permanent_preview) map->deleteObject(midpoint_object, false); } void PointSymbolEditorWidget::setEditorActive(bool active) { if (active) { if (!permanent_preview && !midpoint_object) { midpoint_object = new PointObject(symbol); midpoint_object->setPosition(object_origin_coord); map->addObject(midpoint_object); } map->updateAllObjectsWithSymbol(symbol); controller->setTool(new PointSymbolEditorTool(controller, this)); activity = new PointSymbolEditorActivity(map, this); controller->setEditorActivity(activity); changeElement(element_list->currentRow()); } else { controller->setTool(nullptr); controller->setEditorActivity(nullptr); if (!permanent_preview && midpoint_object) { map->deleteObject(midpoint_object, false); midpoint_object = nullptr; } } } void PointSymbolEditorWidget::setVisible(bool visible) { setEditorActive(visible); QWidget::setVisible(visible); } bool PointSymbolEditorWidget::changeCurrentCoordinate(MapCoordF new_coord) { Object* object = getCurrentElementObject(); if (object == midpoint_object) return false; if (object->getType() == Object::Point) { auto point = static_cast(object); MapCoordF coord = point->getCoordF(); coord.setX(new_coord.x()); coord.setY(new_coord.y() - offset_y); point->setPosition(coord); } else { auto table_row = coords_table->currentRow(); if (table_row < 0) return false; PathObject* path = object->asPath(); auto coord_index = MapCoordVector::size_type(table_row); Q_ASSERT(coord_index < path->getCoordinateCount()); MapCoord coord = path->getCoordinate(coord_index); coord.setX(new_coord.x()); coord.setY(new_coord.y() - offset_y); path->setCoordinate(coord_index, coord); } updateCoordsTable(); map->updateAllObjectsWithSymbol(symbol); emit symbolEdited(); return true; } bool PointSymbolEditorWidget::addCoordinate(MapCoordF new_coord) { Object* object = getCurrentElementObject(); if (object == midpoint_object) return false; if (object->getType() == Object::Point) return changeCurrentCoordinate(new_coord); Q_ASSERT(object->getType() == Object::Path); auto path = static_cast(object); auto table_row = coords_table->currentRow(); if (table_row < 0) table_row = coords_table->rowCount(); else ++table_row; auto coord_index = MapCoordVector::size_type(table_row); path->addCoordinate(coord_index, { new_coord.x(), new_coord.y() - offset_y }); if (path->getCoordinateCount() == 1) { if (object->getSymbol()->getType() == Symbol::Area) path->parts().front().setClosed(true, false); } updateCoordsTable(); coords_table->setCurrentItem(coords_table->item(table_row, (coords_table->currentColumn() < 0) ? 0 : coords_table->currentColumn())); map->updateAllObjectsWithSymbol(symbol); emit symbolEdited(); return true; } void PointSymbolEditorWidget::initElementList() { element_list->clear(); element_list->addItem(tr("[Midpoint]")); // NOTE: Is that item needed? for (int i = 0; i < symbol->getNumElements(); ++i) { Symbol* element_symbol = symbol->getElementSymbol(i); element_list->addItem(getLabelForSymbol(element_symbol)); } element_list->setCurrentRow(0); } void PointSymbolEditorWidget::orientedToNorthClicked(bool checked) { symbol->rotatable = !checked; emit symbolEdited(); } void PointSymbolEditorWidget::changeElement(int row) { delete_element_button->setEnabled(row > 0); // must not remove first row center_all_elements_button->setEnabled(symbol->getNumElements() > 0); if (row >= 0) { Symbol* element_symbol = getCurrentElementSymbol(); Object* object = getCurrentElementObject(); if (object->getType() == Object::Path) { auto path = static_cast(object); if (element_symbol->getType() == Symbol::Line) { line_width_edit->blockSignals(true); line_color_edit->blockSignals(true); line_cap_edit->blockSignals(true); line_join_edit->blockSignals(true); line_closed_check->blockSignals(true); auto line = static_cast(element_symbol); line_width_edit->setValue(0.001 * line->getLineWidth()); line_color_edit->setColor(line->getColor()); line_cap_edit->setCurrentIndex(line_cap_edit->findData(QVariant(line->getCapStyle()))); line_join_edit->setCurrentIndex(line_join_edit->findData(QVariant(line->getJoinStyle()))); const auto& parts = path->parts(); line_closed_check->setChecked(!parts.empty() && parts.front().isClosed()); line_closed_check->setEnabled(!parts.empty()); line_width_edit->blockSignals(false); line_color_edit->blockSignals(false); line_cap_edit->blockSignals(false); line_join_edit->blockSignals(false); line_closed_check->blockSignals(false); element_properties_widget->setCurrentWidget(line_properties); } else if (element_symbol->getType() == Symbol::Area) { area_color_edit->blockSignals(true); auto area = static_cast(element_symbol); area_color_edit->setColor(area->getColor()); area_color_edit->blockSignals(false); element_properties_widget->setCurrentWidget(area_properties); } coords_label->setEnabled(true); coords_table->setEnabled(true); coords_table->setColumnHidden(2, false); add_coord_button->setEnabled(true); center_coords_button->setEnabled(path->getCoordinateCount() > 0); } else { point_inner_radius_edit->blockSignals(true); point_inner_color_edit->blockSignals(true); point_outer_width_edit->blockSignals(true); point_outer_width_edit->blockSignals(true); auto point = static_cast(element_symbol); point_inner_radius_edit->setValue(2 * 0.001 * point->getInnerRadius()); point_inner_color_edit->setColor(point->getInnerColor()); point_outer_width_edit->setValue(0.001 * point->getOuterWidth()); point_outer_color_edit->setColor(point->getOuterColor()); point_inner_radius_edit->blockSignals(false); point_inner_color_edit->blockSignals(false); point_outer_width_edit->blockSignals(false); point_outer_width_edit->blockSignals(false); element_properties_widget->setCurrentWidget(point_properties); coords_table->setColumnHidden(2, true); coords_label->setEnabled(row > 0); coords_table->setEnabled(row > 0); add_coord_button->setEnabled(false); center_coords_button->setEnabled(row > 0); } coords_table->clearContents(); } if (row > 0) updateCoordsTable(); else coords_table->setRowCount(0); delete_coord_button->setEnabled(false); } void PointSymbolEditorWidget::addPointClicked() { PointSymbol* new_point = new PointSymbol(); PointObject* new_object = new PointObject(new_point); insertElement(new_object, new_point); } void PointSymbolEditorWidget::addLineClicked() { LineSymbol* new_line = new LineSymbol(); PathObject* new_object = new PathObject(new_line); insertElement(new_object, new_line); } void PointSymbolEditorWidget::addAreaClicked() { AreaSymbol* new_area = new AreaSymbol(); PathObject* new_object = new PathObject(new_area); insertElement(new_object, new_area); } void PointSymbolEditorWidget::deleteCurrentElement() { int row = element_list->currentRow(); Q_ASSERT(row > 0); delete element_list->item(row); symbol->deleteElement(row - 1); map->updateAllObjectsWithSymbol(symbol); emit symbolEdited(); } void PointSymbolEditorWidget::centerAllElements() { bool has_coordinates = false; auto min_x = std::numeric_limits::max(); auto max_x = std::numeric_limits::min(); auto min_y = std::numeric_limits::max(); auto max_y = std::numeric_limits::min(); for (int i = 0; i < symbol->getNumElements(); ++i) { Object* object = symbol->getElementObject(i); for (const auto& coord : object->getRawCoordinateVector()) { min_x = std::min(min_x, coord.nativeX()); min_y = std::min(min_y, coord.nativeY()); max_x = std::max(max_x, coord.nativeX()); max_y = std::max(max_y, coord.nativeY()); has_coordinates = true; } } if (has_coordinates) { auto center_x = (min_x + max_x) / 2; auto center_y = (min_y + max_y) / 2; for (int i = 0; i < symbol->getNumElements(); ++i) { auto object = symbol->getElementObject(i); object->move(-center_x, -center_y); object->update(); } } if (element_list->currentRow() > 0) updateCoordsTable(); emit symbolEdited(); } void PointSymbolEditorWidget::pointInnerRadiusChanged(double value) { auto symbol = static_cast(getCurrentElementSymbol()); symbol->inner_radius = qRound(1000 * 0.5 * value); map->updateAllObjectsWithSymbol(symbol); emit symbolEdited(); } void PointSymbolEditorWidget::pointInnerColorChanged() { auto symbol = static_cast(getCurrentElementSymbol()); symbol->inner_color = point_inner_color_edit->color(); map->updateAllObjectsWithSymbol(symbol); emit symbolEdited(); } void PointSymbolEditorWidget::pointOuterWidthChanged(double value) { auto symbol = static_cast(getCurrentElementSymbol()); symbol->outer_width = qRound(1000 * value); map->updateAllObjectsWithSymbol(symbol); emit symbolEdited(); } void PointSymbolEditorWidget::pointOuterColorChanged() { auto symbol = static_cast(getCurrentElementSymbol()); symbol->outer_color = point_outer_color_edit->color(); map->updateAllObjectsWithSymbol(symbol); emit symbolEdited(); } void PointSymbolEditorWidget::lineWidthChanged(double value) { auto symbol = static_cast(getCurrentElementSymbol()); symbol->line_width = qRound(1000 * value); map->updateAllObjectsWithSymbol(symbol); emit symbolEdited(); } void PointSymbolEditorWidget::lineColorChanged() { auto symbol = static_cast(getCurrentElementSymbol()); symbol->color = line_color_edit->color(); map->updateAllObjectsWithSymbol(symbol); emit symbolEdited(); } void PointSymbolEditorWidget::lineCapChanged(int index) { auto symbol = static_cast(getCurrentElementSymbol()); symbol->cap_style = static_cast(line_cap_edit->itemData(index).toInt()); map->updateAllObjectsWithSymbol(symbol); emit symbolEdited(); } void PointSymbolEditorWidget::lineJoinChanged(int index) { auto symbol = static_cast(getCurrentElementSymbol()); symbol->join_style = static_cast(line_join_edit->itemData(index).toInt()); map->updateAllObjectsWithSymbol(symbol); emit symbolEdited(); } void PointSymbolEditorWidget::lineClosedClicked(bool checked) { Object* object = getCurrentElementObject(); Q_ASSERT(object->getType() == Object::Path); auto path = static_cast(object); if (!checked && path->getCoordinateCount() >= 4 && path->getCoordinate(path->getCoordinateCount() - 4).isCurveStart()) path->getCoordinate(path->getCoordinateCount() - 4).setCurveStart(false); Q_ASSERT(!path->parts().empty()); path->parts().front().setClosed(checked, true); if (!checked) path->deleteCoordinate(path->getCoordinateCount() - 1, false); updateCoordsTable(); map->updateAllObjectsWithSymbol(symbol); emit symbolEdited(); } void PointSymbolEditorWidget::areaColorChanged() { auto symbol = static_cast(getCurrentElementSymbol()); symbol->color = area_color_edit->color(); map->updateAllObjectsWithSymbol(symbol); emit symbolEdited(); } void PointSymbolEditorWidget::coordinateChanged(int row, int column) { Object* object = getCurrentElementObject(); if (!object || !midpoint_object) return; auto coord_index = MapCoordVector::size_type(row); if (column < 2) { auto coord = MapCoord{}; if (object->getType() == Object::Point) { auto point = static_cast(object); coord = point->getCoord(); } else if (object->getType() == Object::Path) { auto path = static_cast(object); Q_ASSERT(coord_index < path->getCoordinateCount()); coord = path->getCoordinate(coord_index); } QLocale locale; auto ok = false; auto new_value = qRound(1000 * locale.toDouble(coords_table->item(row, column)->text(), &ok)); if (ok) { if (column == 0) coord.setNativeX(new_value); else coord.setNativeY(-new_value); if (object->getType() == Object::Point) { auto point = static_cast(object); point->setPosition(coord); } else if (object->getType() == Object::Path) { auto path = static_cast(object); path->setCoordinate(coord_index, coord); } map->updateAllObjectsWithSymbol(symbol); emit symbolEdited(); } // Update, needed in cases of rounding and error coords_table->blockSignals(true); coords_table->item(row, column)->setText(locale.toString((column == 0) ? coord.x() : -coord.y(), 'f', 3)); coords_table->blockSignals(false); } else { Q_ASSERT(object->getType() == Object::Path); auto path = static_cast(object); Q_ASSERT(coord_index < path->getCoordinateCount()); MapCoord coord = path->getCoordinate(coord_index); coord.setCurveStart(coords_table->item(row, column)->checkState() == Qt::Checked); path->setCoordinate(coord_index, coord); updateCoordsTable(); map->updateAllObjectsWithSymbol(symbol); emit symbolEdited(); } } void PointSymbolEditorWidget::addCoordClicked() { Object* object = getCurrentElementObject(); Q_ASSERT(object->getType() == Object::Path); auto path = static_cast(object); if (coords_table->currentRow() < 0) path->addCoordinate(MapCoordVector::size_type(coords_table->rowCount()), MapCoord(0, 0)); else path->addCoordinate(MapCoordVector::size_type(coords_table->currentRow()) + 1, path->getCoordinate(MapCoordVector::size_type(coords_table->currentRow()))); int row = (coords_table->currentRow() < 0) ? coords_table->rowCount() : (coords_table->currentRow() + 1); updateCoordsTable(); // NOTE: incremental updates (to the curve start boxes) would be possible but mean some implementation effort coords_table->setCurrentItem(coords_table->item(row, coords_table->currentColumn())); } void PointSymbolEditorWidget::deleteCoordClicked() { Object* object = getCurrentElementObject(); Q_ASSERT(object->getType() == Object::Path); auto path = static_cast(object); int row = coords_table->currentRow(); if (row < 0) return; path->deleteCoordinate(MapCoordVector::size_type(row), false); updateCoordsTable(); // NOTE: incremental updates (to the curve start boxes) would be possible but mean some implementation effort center_coords_button->setEnabled(path->getCoordinateCount() > 0); updateDeleteCoordButton(); map->updateAllObjectsWithSymbol(symbol); emit symbolEdited(); } void PointSymbolEditorWidget::centerCoordsClicked() { Object* object = getCurrentElementObject(); if (object->getType() == Object::Point) { auto point = static_cast(object); point->setPosition(0, 0); } else { Q_ASSERT(object->getType() == Object::Path); auto path = static_cast(object); MapCoordF center = MapCoordF(0, 0); auto size = path->getCoordinateCount(); auto change_size = (!path->parts().empty() && path->parts().front().isClosed()) ? (size - 1) : size; Q_ASSERT(change_size > 0); for (auto i = 0u; i < change_size; ++i) center = MapCoordF(center.x() + path->getCoordinate(i).x(), center.y() + path->getCoordinate(i).y()); center = MapCoordF(center.x() / change_size, center.y() / change_size); path->move(MapCoord{-center}); path->update(); } updateCoordsTable(); map->updateAllObjectsWithSymbol(symbol); emit symbolEdited(); } void PointSymbolEditorWidget::updateCoordsTable() { Object* object = getCurrentElementObject(); int num_rows; if (object->getType() == Object::Point) num_rows = 1; else { auto path = static_cast(object); num_rows = int(path->getCoordinateCount()); if (num_rows > 0 && path->parts().front().isClosed()) --num_rows; if (path->getSymbol()->getType() == Symbol::Line) line_closed_check->setEnabled(num_rows > 0); } coords_table->setRowCount(num_rows); for (int i = 0; i < num_rows; ++i) addCoordsRow(i); center_coords_button->setEnabled(num_rows > 0); } void PointSymbolEditorWidget::addCoordsRow(int row) { coords_table->setRowHeight(row, coords_table->fontMetrics().height() + 2); coords_table->blockSignals(true); for (int c = 0; c < 3; ++c) { if (!coords_table->item(row, c)) { QTableWidgetItem* item = new QTableWidgetItem(); if (c < 2) { item->setFlags(Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable); item->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter); } coords_table->setItem(row, c, item); } } updateCoordsRow(row); coords_table->blockSignals(false); } void PointSymbolEditorWidget::updateCoordsRow(int row) { Q_ASSERT(element_list->currentRow() > 0); Object* object = getCurrentElementObject(); auto coord_index = MapCoordVector::size_type(row); MapCoordF coordF(0, 0); if (object->getType() == Object::Point) coordF = static_cast(object)->getCoordF(); else if (object->getType() == Object::Path) coordF = MapCoordF(static_cast(object)->getCoordinate(coord_index)); QLocale locale; coords_table->item(row, 0)->setText(locale.toString(coordF.x(), 'f', 3)); coords_table->item(row, 1)->setText(locale.toString(-coordF.y(), 'f', 3)); if (object->getType() == Object::Path) { auto path = static_cast(object); bool has_curve_start_box = coord_index+3 < path->getCoordinateCount() && (!path->getCoordinate(coord_index+1).isCurveStart() && !path->getCoordinate(coord_index+2).isCurveStart()) && (row <= 0 || !path->getCoordinate(coord_index-1).isCurveStart()) && (row <= 1 || !path->getCoordinate(coord_index-2).isCurveStart()); if (has_curve_start_box) { coords_table->item(row, 2)->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable); coords_table->item(row, 2)->setCheckState(path->getCoordinate(coord_index).isCurveStart() ? Qt::Checked : Qt::Unchecked); return; } } if (coords_table->item(row, 2)->flags() & Qt::ItemIsUserCheckable) coords_table->setItem(row, 2, new QTableWidgetItem()); // remove checkbox by replacing the item with a new one - is there a better way? coords_table->item(row, 2)->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); } void PointSymbolEditorWidget::updateDeleteCoordButton() { const bool has_coords = coords_table->rowCount() > 0; const bool is_point = getCurrentElementObject()->getType() == Object::Point; bool part_of_curve = false; if (!is_point) { auto path = static_cast(getCurrentElementObject()); for (int i = 1; i < 4; i++) { int row = coords_table->currentRow() - i; if (row >= 0 && path->getCoordinate(MapCoordVector::size_type(row)).isCurveStart()) part_of_curve = true; } } delete_coord_button->setEnabled(has_coords && !is_point && !part_of_curve); } void PointSymbolEditorWidget::insertElement(Object* object, Symbol* element_symbol) { int row = (element_list->currentRow() < 0) ? element_list->count() : (element_list->currentRow() + 1); int pos = row - 1; symbol->addElement(pos, object, element_symbol); element_list->insertItem(row, getLabelForSymbol(element_symbol)); element_list->setCurrentRow(row); map->updateAllObjectsWithSymbol(symbol); emit symbolEdited(); } QString PointSymbolEditorWidget::getLabelForSymbol(const Symbol* symbol) const { if (symbol->getType() == Symbol::Point) return tr("Point"); // FIXME: This is rather a circle. else if (symbol->getType() == Symbol::Line) return tr("Line"); else if (symbol->getType() == Symbol::Area) return tr("Area"); Q_ASSERT(false); return tr("Unknown"); } Object* PointSymbolEditorWidget::getCurrentElementObject() { if (element_list->currentRow() > 0) return symbol->getElementObject(element_list->currentRow() - 1); else return midpoint_object; } Symbol* PointSymbolEditorWidget::getCurrentElementSymbol() { if (element_list->currentRow() > 0) return symbol->getElementSymbol(element_list->currentRow() - 1); else return symbol; } // ### PointSymbolEditorTool ### PointSymbolEditorTool::PointSymbolEditorTool(MapEditorController* editor, PointSymbolEditorWidget* symbol_editor) : MapEditorTool(editor, Other) , symbol_editor(symbol_editor) { // nothing } PointSymbolEditorTool::~PointSymbolEditorTool() = default; void PointSymbolEditorTool::init() { setStatusBarText(tr("Click: Add a coordinate. %1+Click: Change the selected coordinate. ").arg(ModifierKey::control())); MapEditorTool::init(); } bool PointSymbolEditorTool::mousePressEvent(QMouseEvent* event, MapCoordF map_coord, MapWidget* map_widget) { Q_UNUSED(map_widget); if (event->button() == Qt::LeftButton) { if (event->modifiers() & Qt::ControlModifier) symbol_editor->changeCurrentCoordinate(map_coord); else symbol_editor->addCoordinate(map_coord); return true; } return false; } const QCursor& PointSymbolEditorTool::getCursor() const { static auto const cursor = scaledToScreen(QCursor{ QPixmap(QString::fromLatin1(":/images/cursor-crosshair.png")), 11, 11 }); return cursor; } // ### PointSymbolEditorActivity ### const int PointSymbolEditorActivity::cross_radius = 5; PointSymbolEditorActivity::PointSymbolEditorActivity(Map* map, PointSymbolEditorWidget* symbol_editor) : MapEditorActivity() , map(map) , symbol_editor(symbol_editor) { // nothing } PointSymbolEditorActivity::~PointSymbolEditorActivity() = default; void PointSymbolEditorActivity::init() { update(); } void PointSymbolEditorActivity::update() { QRectF rect = QRectF(0.0, symbol_editor->offset_y, 0.0, 0.0); map->setActivityBoundingBox(rect, cross_radius + 1); } void PointSymbolEditorActivity::draw(QPainter* painter, MapWidget* map_widget) { QPoint midpoint = map_widget->mapToViewport(symbol_editor->object_origin_coord).toPoint(); QPen pen = QPen(Qt::white); pen.setWidth(3); painter->setPen(pen); painter->drawLine(midpoint + QPoint(0, -cross_radius), midpoint + QPoint(0, cross_radius)); painter->drawLine(midpoint + QPoint(-cross_radius, 0), midpoint + QPoint(cross_radius, 0)); pen.setWidth(0); pen.setColor(Qt::black); painter->setPen(pen); painter->drawLine(midpoint + QPoint(0, -cross_radius), midpoint + QPoint(0, cross_radius)); painter->drawLine(midpoint + QPoint(-cross_radius, 0), midpoint + QPoint(cross_radius, 0)); } } // namespace OpenOrienteering mapper-0.8.1.1/src/gui/symbols/point_symbol_editor_widget.h000066400000000000000000000136611325266516600240310ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_SYMBOL_POINT_EDITOR_H #define OPENORIENTEERING_SYMBOL_POINT_EDITOR_H #include #include #include #include #include "core/map_coord.h" #include "gui/map/map_editor_activity.h" #include "tools/tool.h" class QCheckBox; class QComboBox; class QCursor; class QDoubleSpinBox; class QLabel; class QListWidget; class QMouseEvent; class QPainter; class QPushButton; class QStackedWidget; class QTableWidget; namespace OpenOrienteering { class ColorDropDown; class Map; class MapEditorController; class MapWidget; class Object; class PointObject; class PointSymbol; class PointSymbolEditorActivity; class Symbol; /** A Widget for editing point symbol definitions */ class PointSymbolEditorWidget : public QWidget { Q_OBJECT friend class PointSymbolEditorActivity; public: /** Construct a new widget. * @param controller The controller of the preview map * @param symbol The point symbol to be edited * @param offset_y The vertical offset of the point symbol preview/editor from the origin * @param permanent_preview A flag indicating wheter the preview shall be visible even if the editor is not visible */ PointSymbolEditorWidget(MapEditorController* controller, PointSymbol* symbol, qreal offset_y = 0, bool permanent_preview = false, QWidget* parent = 0); ~PointSymbolEditorWidget() override; /** Add a coordinate to the current element. * @return true if successful */ bool addCoordinate(MapCoordF new_coord); /** Change the current coordinate of the current element. * @return true if successful */ bool changeCurrentCoordinate(MapCoordF new_coord); /** Activate the editor in the map preview. */ void setEditorActive(bool active); /** Request to hide or show the editor. */ void setVisible(bool visible) override; signals: /** This signal gets emitted whenever the symbol appearance is modified. */ void symbolEdited(); private slots: void orientedToNorthClicked(bool checked); void changeElement(int row); void addPointClicked(); void addLineClicked(); void addAreaClicked(); void deleteCurrentElement(); void centerAllElements(); void pointInnerRadiusChanged(double value); void pointInnerColorChanged(); void pointOuterWidthChanged(double value); void pointOuterColorChanged(); void lineWidthChanged(double value); void lineColorChanged(); void lineCapChanged(int index); void lineJoinChanged(int index); void lineClosedClicked(bool checked); void areaColorChanged(); void updateDeleteCoordButton(); void coordinateChanged(int row, int column); void addCoordClicked(); void deleteCoordClicked(); void centerCoordsClicked(); private: void initElementList(); void updateCoordsTable(); void addCoordsRow(int row); void updateCoordsRow(int row); void insertElement(Object* object, Symbol* symbol); QString getLabelForSymbol(const Symbol* symbol) const; Symbol* getCurrentElementSymbol(); Object* getCurrentElementObject(); PointSymbol* const symbol; PointObject* midpoint_object; const MapCoordF object_origin_coord; QCheckBox* oriented_to_north; QListWidget* element_list; QPushButton* delete_element_button; QPushButton* center_all_elements_button; QStackedWidget* element_properties_widget; QWidget* point_properties; QDoubleSpinBox* point_inner_radius_edit; ColorDropDown* point_inner_color_edit; QDoubleSpinBox* point_outer_width_edit; ColorDropDown* point_outer_color_edit; QWidget* line_properties; QDoubleSpinBox* line_width_edit; ColorDropDown* line_color_edit; QComboBox* line_cap_edit; QComboBox* line_join_edit; QCheckBox* line_closed_check; QWidget* area_properties; ColorDropDown* area_color_edit; QLabel* coords_label; QTableWidget* coords_table; QPushButton* add_coord_button; QPushButton* delete_coord_button; QPushButton* center_coords_button; const qreal offset_y; PointSymbolEditorActivity* activity; Map* map; MapEditorController* controller; const bool permanent_preview; }; /** * PointSymbolEditorActivity allows to add or modify coordinates of point symbol elements * by clicking in the map. */ class PointSymbolEditorTool : public MapEditorTool { Q_OBJECT public: PointSymbolEditorTool(MapEditorController* editor, PointSymbolEditorWidget* symbol_editor); ~PointSymbolEditorTool() override; void init() override; bool mousePressEvent(QMouseEvent* event, MapCoordF map_coord, MapWidget* map_widget) override; const QCursor& getCursor() const override; private: PointSymbolEditorWidget* const symbol_editor; }; /** * PointSymbolEditorActivity draws a small cross in the origin of the map coordinate system. * * \todo Fix that thes cross may cover the symbol at small scales. */ class PointSymbolEditorActivity : public MapEditorActivity { Q_OBJECT public: PointSymbolEditorActivity(Map* map, PointSymbolEditorWidget* symbol_editor); ~PointSymbolEditorActivity() override; void init() override; void update(); void draw(QPainter* painter, MapWidget* map_widget) override; private: Map* const map; PointSymbolEditorWidget* const symbol_editor; static const int cross_radius; // NOTE: This could be a configuration option. }; } // namespace OpenOrienteering #endif mapper-0.8.1.1/src/gui/symbols/point_symbol_settings.cpp000066400000000000000000000054201325266516600233650ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2012-2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "point_symbol_settings.h" #include #include #include #include #include "core/symbols/point_symbol.h" #include "core/symbols/symbol.h" #include "gui/symbols/point_symbol_editor_widget.h" #include "gui/symbols/symbol_setting_dialog.h" namespace OpenOrienteering { // ### PointSymbol ### SymbolPropertiesWidget* PointSymbol::createPropertiesWidget(SymbolSettingDialog* dialog) { return new PointSymbolSettings(this, dialog); } // ### PointSymbolSettings ### PointSymbolSettings::PointSymbolSettings(PointSymbol* symbol, SymbolSettingDialog* dialog) : SymbolPropertiesWidget(symbol, dialog), symbol(symbol) { symbol_editor = new PointSymbolEditorWidget(dialog->getPreviewController(), symbol, 0, true); connect(symbol_editor, &PointSymbolEditorWidget::symbolEdited, this, &SymbolPropertiesWidget::propertiesModified ); layout = new QVBoxLayout(); layout->addWidget(symbol_editor); point_tab = new QWidget(); point_tab->setLayout(layout); addPropertiesGroup(tr("Point symbol"), point_tab); connect(this, &QTabWidget::currentChanged, this, &PointSymbolSettings::tabChanged); } PointSymbolSettings::~PointSymbolSettings() = default; void PointSymbolSettings::reset(Symbol* symbol) { if (Q_UNLIKELY(symbol->getType() != Symbol::Point)) { qWarning("Not a point symbol: %s", symbol ? "nullptr" : qPrintable(symbol->getPlainTextName())); return; } SymbolPropertiesWidget::reset(symbol); this->symbol = static_cast(symbol); layout->removeWidget(symbol_editor); delete(symbol_editor); symbol_editor = new PointSymbolEditorWidget(dialog->getPreviewController(), this->symbol, 0, true); connect(symbol_editor, &PointSymbolEditorWidget::symbolEdited, this, &SymbolPropertiesWidget::propertiesModified ); layout->addWidget(symbol_editor); } void PointSymbolSettings::tabChanged(int /*index*/) { symbol_editor->setEditorActive( currentWidget()==point_tab ); } } // namespace OpenOrienteering mapper-0.8.1.1/src/gui/symbols/point_symbol_settings.h000066400000000000000000000030641325266516600230340ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2012-2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_POINT_SYMBOL_SETTINGS_H #define OPENORIENTEERING_POINT_SYMBOL_SETTINGS_H #include #include "gui/symbols/symbol_properties_widget.h" class QVBoxLayout; class QWidget; namespace OpenOrienteering { class PointSymbol; class PointSymbolEditorWidget; class Symbol; class SymbolSettingDialog; class PointSymbolSettings : public SymbolPropertiesWidget { Q_OBJECT public: PointSymbolSettings(PointSymbol* symbol, SymbolSettingDialog* dialog); ~PointSymbolSettings() override; void reset(Symbol* symbol) override; public slots: void tabChanged(int index); private: PointSymbol* symbol; PointSymbolEditorWidget* symbol_editor; QVBoxLayout* layout; QWidget* point_tab; }; } // namespace OpenOrienteering #endif mapper-0.8.1.1/src/gui/symbols/replace_symbol_set_dialog.cpp000066400000000000000000000550501325266516600241250ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "replace_symbol_set_dialog.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "settings.h" #include "core/map.h" #include "core/map_color.h" #include "core/objects/object.h" #include "core/objects/object_query.h" #include "core/objects/symbol_rule_set.h" #include "core/symbols/line_symbol.h" #include "core/symbols/point_symbol.h" #include "core/symbols/symbol.h" #include "core/symbols/text_symbol.h" #include "fileformats/file_format.h" #include "gui/file_dialog.h" #include "gui/main_window.h" #include "gui/util_gui.h" #include "gui/widgets/symbol_dropdown.h" #include "util/util.h" #include "util/backports.h" // IWYU pragma: no_forward_declare QColor // IWYU pragma: no_forward_declare QFormLayout // IWYU pragma: no_forward_declare QLabel // IWYU pragma: no_forward_declare QTableWidgetItem namespace OpenOrienteering { ReplaceSymbolSetDialog::ReplaceSymbolSetDialog(QWidget* parent, Map& object_map, const Map& symbol_set, SymbolRuleSet& replacements, Mode mode) : QDialog{ parent, Qt::WindowSystemMenuHint | Qt::WindowTitleHint } , object_map{ object_map } , symbol_set{ symbol_set } , replacements{ replacements } , import_all_check{ nullptr } , delete_unused_symbols_check{ nullptr } , delete_unused_colors_check{ nullptr } , preserve_symbol_states_check{ nullptr } , mode( &object_map==&symbol_set ? AssignByPattern : mode ) { QFormLayout* form_layout = nullptr; auto mapping_menu = new QMenu(this); QStringList horizontal_headers; horizontal_headers.reserve(2); if (mode == ReplaceSymbolSet) { setWindowTitle(tr("Replace symbol set")); form_layout = new QFormLayout(); QLabel* desc_label = new QLabel(tr("Configure how the symbols should be replaced, and which.")); form_layout->addRow(desc_label); form_layout->addItem(Util::SpacerItem::create(this)); import_all_check = new QCheckBox(tr("Import all new symbols, even if not used as replacement")); import_all_check->setChecked(true); form_layout->addRow(import_all_check); preserve_symbol_states_check = new QCheckBox(tr("Keep the symbols' hidden / protected states of the old symbol set")); preserve_symbol_states_check->setChecked(true); form_layout->addRow(preserve_symbol_states_check); delete_unused_symbols_check = new QCheckBox(tr("Delete original symbols which are unused after the replacement")); delete_unused_symbols_check->setChecked(true); form_layout->addRow(delete_unused_symbols_check); delete_unused_colors_check = new QCheckBox(tr("Delete unused colors after the replacement")); delete_unused_colors_check->setChecked(true); form_layout->addRow(delete_unused_colors_check); id_edit = new QComboBox(); id_edit->setEditable(true); auto new_id = symbol_set.symbolSetId(); if (!new_id.isEmpty()) id_edit->addItem(new_id); auto old_id = object_map.symbolSetId(); if (!old_id.isEmpty() && old_id != new_id) id_edit->addItem(old_id); id_edit->setCurrentIndex(0); form_layout->addRow(tr("Edit the symbol set ID:"), id_edit); form_layout->addItem(Util::SpacerItem::create(this)); horizontal_headers << tr("Original") << tr("Replacement"); auto action = mapping_menu->addAction(tr("Match replacement symbols by symbol number")); connect(action, &QAction::triggered, this, &ReplaceSymbolSetDialog::matchByNumber); action = mapping_menu->addAction(tr("Match by symbol name")); connect(action, &QAction::triggered, this, &ReplaceSymbolSetDialog::matchByName); } else { setWindowTitle(tr("Assign new symbols")); horizontal_headers << tr("Pattern") << tr("Replacement"); } mapping_table = new QTableWidget(); mapping_table->verticalHeader()->setVisible(false); mapping_table->setColumnCount(2); mapping_table->setHorizontalHeaderLabels(horizontal_headers); mapping_table->horizontalHeader()->setSectionsClickable(false); mapping_table->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch); mapping_table->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Stretch); mapping_table->setEditTriggers(QAbstractItemView::AllEditTriggers); auto action = mapping_menu->addAction(tr("Clear replacements")); connect(action, &QAction::triggered, this, &ReplaceSymbolSetDialog::resetReplacements); mapping_menu->addSeparator(); action = mapping_menu->addAction(QIcon{QLatin1String{":/images/open.png"}}, tr("Open CRT file...")); connect(action, &QAction::triggered, this, QOverload<>::of(&ReplaceSymbolSetDialog::openCrtFile)); action = mapping_menu->addAction(QIcon{QLatin1String{":/images/save.png"}}, tr("Save CRT file...")); connect(action, &QAction::triggered, this, &ReplaceSymbolSetDialog::saveCrtFile); auto button_box = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::Help, Qt::Horizontal); auto mapping_button = button_box->addButton(tr("Symbol mapping:").replace(QLatin1Char{':'}, QString{}), QDialogButtonBox::ActionRole); void(tr("Symbol mapping")); /// \todo Switch translation mapping_button->setMenu(mapping_menu); auto layout = new QVBoxLayout(this); if (form_layout) layout->addLayout(form_layout); layout->addWidget(mapping_table); layout->addItem(Util::SpacerItem::create(this)); layout->addWidget(button_box); setLayout(layout); connect(button_box, &QDialogButtonBox::helpRequested, this, &ReplaceSymbolSetDialog::showHelp); connect(button_box, &QDialogButtonBox::accepted, this, &QDialog::accept); connect(button_box, &QDialogButtonBox::rejected, this, &QDialog::reject); if (replacements.empty() && mode == ReplaceSymbolSet) { this->replacements = SymbolRuleSet::forOriginalSymbols(object_map); this->replacements.matchQuerySymbolName(symbol_set); } updateMappingTable(); } ReplaceSymbolSetDialog::~ReplaceSymbolSetDialog() { // nothing, not inlined } void ReplaceSymbolSetDialog::showHelp() { Util::showHelp(this, "symbol_replace_dialog.html"); } void ReplaceSymbolSetDialog::matchByName() { replacements.matchQuerySymbolName(symbol_set); updateMappingTable(); } void ReplaceSymbolSetDialog::matchByNumber() { replacements.matchQuerySymbolNumber(symbol_set); updateMappingTable(); } void ReplaceSymbolSetDialog::resetReplacements() { for (auto& item : replacements) { item.symbol = nullptr; item.type = SymbolRule::NoAssignment; } updateMappingTable(); } void ReplaceSymbolSetDialog::openCrtFile() { auto dir = QLatin1String{"data:/symbol sets"}; auto filter = QString{tr("CRT file") + QLatin1String{" (*.crt)"}}; QString path = FileDialog::getOpenFileName(this, tr("Open CRT file..."), dir, filter); if (!path.isEmpty()) openCrtFile(path); } void ReplaceSymbolSetDialog::openCrtFile(const QString& path) { QFile crt_file{path}; crt_file.open(QIODevice::ReadOnly); QTextStream stream{ &crt_file }; auto new_replacements = SymbolRuleSet::loadCrt(stream, symbol_set); if (stream.status() == QTextStream::Ok) { // Postprocess CRT for (auto& item : new_replacements) { if (item.type == SymbolRule::NoAssignment || item.query.getOperator() != ObjectQuery::OperatorSearch) continue; Q_ASSERT(item.symbol); auto operands = item.query.tagOperands(); if (!operands || operands->value.isEmpty()) continue; // Find original symbol number matching the pattern for (int i = 0; i < object_map.getNumSymbols(); ++i) { auto symbol = object_map.getSymbol(i); if (symbol->getNumberAsString() == operands->value) { if (Symbol::areTypesCompatible(symbol->getType(), item.symbol->getType())) item.query = { symbol }; break; } } // Find inconsistencies if (item.query.getOperator() == ObjectQuery::OperatorSymbol) { auto has_conflict = [&item](const auto& other)->bool { return &other != &item && other.type != SymbolRule::NoAssignment && item.query == other.query && item.symbol != other.symbol; }; if (std::any_of(begin(new_replacements), end(new_replacements), has_conflict)) { stream.setStatus(QTextStream::ReadCorruptData); auto error_msg = tr("There are multiple replacements for symbol %1.") .arg(item.query.symbolOperand()->getNumberAsString()); QMessageBox::warning(this, ::OpenOrienteering::Map::tr("Error"), tr("Cannot open file:\n%1\n\n%2").arg(path, error_msg) ); return; } } } // Apply for (auto& item : new_replacements) { for (auto& current : replacements) { if (item.query == current.query) { if (item.type != SymbolRule::NoAssignment) { current.symbol = item.symbol; current.type = item.type; item = {}; } break; } } } if (mode == AssignByPattern) { for (auto&& item : new_replacements) { if (item.query.getOperator() != ObjectQuery::OperatorInvalid) { replacements.emplace_back(std::move(item)); } } } updateMappingTable(); } } bool ReplaceSymbolSetDialog::saveCrtFile() { /// \todo Choose user-writable directory. auto dir = QLatin1String{"data:/symbol sets"}; auto filter = QString{tr("CRT file") + QLatin1String{" (*.crt)"}}; QString path = FileDialog::getSaveFileName(this, tr("Save CRT file..."), dir, filter); if (!path.isEmpty()) { updateMappingFromTable(); QFile crt_file{path}; crt_file.open(QIODevice::WriteOnly); QTextStream stream{ &crt_file }; replacements.writeCrt(stream); if (stream.pos() != -1) { return true; } /// \todo Reused translation, consider generalized context QMessageBox::warning(this, ::OpenOrienteering::Map::tr("Error"), tr("Cannot save file:\n%1\n\n%2") .arg(path, crt_file.errorString()) ); } return false; } void ReplaceSymbolSetDialog::done(int r) { updateMappingFromTable(); if (std::any_of(begin(replacements), end(replacements), [](const auto& item) { return item.type == SymbolRule::ManualAssignment; })) { auto save_crt = QMessageBox::warning(this, qApp->applicationDisplayName(), tr("The cross reference table has been modified.\n" "Do you want to save your changes?"), // QGnomeThema label for Discard: "Close without saving" QMessageBox::Save | QMessageBox::No | QMessageBox::Cancel); switch (save_crt) { case QMessageBox::Cancel: return; case QMessageBox::Discard: case QMessageBox::No: break; case QMessageBox::Save: if (!saveCrtFile()) return; break; default: Q_UNREACHABLE(); } } QDialog::done(r); } void ReplaceSymbolSetDialog::updateMappingTable() { mapping_table->setUpdatesEnabled(false); mapping_table->clearContents(); mapping_table->setRowCount(int(replacements.size())); symbol_widget_delegates.clear(); symbol_widget_delegates.resize(replacements.size()); std::size_t row = 0; for (const auto& item : replacements) { const Symbol* original_symbol = nullptr; auto original_icon = QImage{}; auto original_string = QString{}; auto compatible_symbols = int(Symbol::AllSymbols); const auto replacement_symbol = item.symbol; if (item.query.getOperator() == ObjectQuery::OperatorSymbol && item.query.symbolOperand()) { original_symbol = item.query.symbolOperand(); original_string = Util::plainText(object_map.translate(original_symbol->getName())); original_string.prepend(original_symbol->getNumberAsString() + QLatin1Char(' ')); original_icon = original_symbol->getIcon(&object_map); compatible_symbols = Symbol::getCompatibleTypes(original_symbol->getType()); } else { if (replacement_symbol) { switch (replacement_symbol->getType()) { case Symbol::Area: original_icon = object_map.getUndefinedLine()->getIcon(&object_map); original_string = QGuiApplication::translate("OpenOrienteering::SymbolRenderWidget", "Area"); compatible_symbols = Symbol::getCompatibleTypes(replacement_symbol->getType()); break; case Symbol::Combined: original_icon = object_map.getUndefinedLine()->getIcon(&object_map); original_string = QGuiApplication::translate("OpenOrienteering::SymbolRenderWidget", "Combined"); compatible_symbols = Symbol::getCompatibleTypes(replacement_symbol->getType()); break; case Symbol::Line: original_icon = object_map.getUndefinedLine()->getIcon(&object_map); original_string = QGuiApplication::translate("OpenOrienteering::SymbolRenderWidget", "Line"); compatible_symbols = Symbol::getCompatibleTypes(replacement_symbol->getType()); break; case Symbol::Point: original_icon = object_map.getUndefinedPoint()->getIcon(&object_map); original_string = QGuiApplication::translate("OpenOrienteering::SymbolRenderWidget", "Point"); compatible_symbols = Symbol::getCompatibleTypes(replacement_symbol->getType()); break; case Symbol::Text: original_icon = object_map.getUndefinedText()->getIcon(&object_map); original_string = QGuiApplication::translate("OpenOrienteering::SymbolRenderWidget", "Text"); compatible_symbols = Symbol::getCompatibleTypes(replacement_symbol->getType()); break; case Symbol::AllSymbols: case Symbol::NoSymbol: ; // no icon, no string } } const Symbol* unique_symbol = nullptr; auto matching_types = int(Symbol::NoSymbol); auto update_matching = [&unique_symbol, &matching_types](Object* object) { if (auto symbol = object->getSymbol()) { if (matching_types == Symbol::NoSymbol) { unique_symbol = symbol; matching_types = Symbol::getCompatibleTypes(symbol->getType()); } else { if (symbol != unique_symbol) unique_symbol = nullptr; matching_types |= Symbol::getCompatibleTypes(symbol->getType()); } } }; object_map.applyOnMatchingObjects(update_matching, std::cref(item.query)); if (matching_types != Symbol::NoSymbol) { compatible_symbols = matching_types; if (unique_symbol) { original_symbol = unique_symbol; original_string = Util::plainText(object_map.translate(original_symbol->getName())); original_string.prepend(original_symbol->getNumberAsString() + QLatin1Char(' ')); original_icon = original_symbol->getIcon(&object_map); } } if (original_icon.isNull()) { auto side_length = Settings::getInstance().getSymbolWidgetIconSizePx(); original_icon = QImage{side_length, side_length, QImage::Format_ARGB32_Premultiplied}; auto color = object_map.getUndefinedColor()->operator const QColor &(); color.setAlphaF(0.5); original_icon.fill(color); } switch (item.query.getOperator()) { case ObjectQuery::OperatorInvalid: case ObjectQuery::OperatorSymbol: break; default: original_string = item.query.toString(); } } auto original_item = new QTableWidgetItem(original_string); original_item->setFlags(Qt::ItemIsEnabled); // make item non-editable QVariantList original_item_data = QVariantList() << qVariantFromValue(&object_map) << qVariantFromValue(original_symbol); original_item->setData(Qt::UserRole, QVariant(original_item_data)); original_item->setData(Qt::DecorationRole, original_icon); mapping_table->setItem(int(row), 0, original_item); // Note on const: this is not a const method. QTableWidgetItem* replacement_item; if (replacement_symbol) replacement_item = new QTableWidgetItem(replacement_symbol->getNumberAsString() + QLatin1Char(' ') + Util::plainText(symbol_set.translate(replacement_symbol->getName()))); else replacement_item = new QTableWidgetItem(tr("- None -")); QVariantList replacement_item_data = QVariantList() << qVariantFromValue(&symbol_set) << qVariantFromValue(replacement_symbol); replacement_item->setData(Qt::UserRole, QVariant(replacement_item_data)); if (replacement_symbol) replacement_item->setData(Qt::DecorationRole, replacement_symbol->getIcon(&symbol_set)); mapping_table->setItem(int(row), 1, replacement_item); //auto hide = (mode == ModeCRT && original_symbol->isHidden()); //mapping_table->setRowHidden(row, hide); symbol_widget_delegates[row].reset(new SymbolDropDownDelegate(compatible_symbols)); mapping_table->setItemDelegateForRow(int(row), symbol_widget_delegates[row].get()); ++row; } mapping_table->setUpdatesEnabled(true); } void ReplaceSymbolSetDialog::updateMappingFromTable() { Q_ASSERT(int(replacements.size()) == mapping_table->rowCount()); auto item = begin(replacements); for (int row = 0; row < mapping_table->rowCount(); ++row) { auto replacement_symbol = mapping_table->item(row, 1)->data(Qt::UserRole).toList().at(1).value(); if (item->symbol != replacement_symbol) { item->symbol = replacement_symbol; item->type = SymbolRule::ManualAssignment; } ++item; } } // static bool ReplaceSymbolSetDialog::showDialog(QWidget* parent, Map& object_map) { auto symbol_set = std::unique_ptr{}; while (!symbol_set) { auto path = MainWindow::getOpenFileName(parent, tr("Choose map file to load symbols from"), FileFormat::MapFile); if (path.isEmpty()) return false; symbol_set.reset(new Map); if (!symbol_set->loadFrom(path, parent, nullptr, true)) { QMessageBox::warning(parent, tr("Error"), tr("Cannot load map file, aborting.")); return false; } if (symbol_set->getScaleDenominator() != object_map.getScaleDenominator()) { if (QMessageBox::warning(parent, tr("Warning"), tr("The chosen symbol set has a scale of 1:%1, while the map scale is 1:%2. Do you really want to choose this set?").arg(symbol_set->getScaleDenominator()).arg(object_map.getScaleDenominator()), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) { symbol_set.reset(nullptr); } } } auto replacements = SymbolRuleSet::forOriginalSymbols(object_map); replacements.matchQuerySymbolName(*symbol_set); auto flags_from_dialog = [](const ReplaceSymbolSetDialog& dialog){ auto options = SymbolRuleSet::Options{}; if (dialog.import_all_check->isChecked()) options |= SymbolRuleSet::ImportAllSymbols; if (dialog.preserve_symbol_states_check->isChecked()) options |= SymbolRuleSet::PreserveSymbolState; if (!dialog.delete_unused_symbols_check->isChecked()) options |= SymbolRuleSet::KeepUnusedSymbols; if (!dialog.delete_unused_colors_check->isChecked()) options |= SymbolRuleSet::KeepUnusedColors; return options; }; ReplaceSymbolSetDialog dialog(parent, object_map, *symbol_set, replacements, ReplaceSymbolSet); dialog.setWindowModality(Qt::WindowModal); auto crt_file = discoverCrtFile(object_map.symbolSetId(), symbol_set->symbolSetId()); if (QFileInfo::exists(crt_file)) { dialog.show(); dialog.openCrtFile(crt_file); } auto result = dialog.exec(); switch (result) { case QDialog::Accepted: replacements.squeezed().apply(object_map, *symbol_set, flags_from_dialog(dialog)); object_map.setSymbolSetId(dialog.id_edit->currentText()); return true; case QDialog::Rejected: return false; } Q_UNREACHABLE(); } // static bool ReplaceSymbolSetDialog::showDialogForCRT(QWidget* parent, Map& object_map, const Map& symbol_set) { auto replacements = SymbolRuleSet{}; ReplaceSymbolSetDialog dialog(parent, object_map, symbol_set, replacements, AssignByPattern); dialog.setWindowModality(Qt::WindowModal); dialog.show(); dialog.openCrtFile(); auto result = dialog.exec(); switch (result) { case QDialog::Accepted: replacements.squeezed().apply(object_map, symbol_set); return true; case QDialog::Rejected: return false; } Q_UNREACHABLE(); } // static bool ReplaceSymbolSetDialog::showDialogForCRT(QWidget* parent, Map& object_map, const Map& symbol_set, QIODevice& crt_file) { QTextStream stream{ &crt_file }; auto replacements = SymbolRuleSet::loadCrt(stream, symbol_set); if (stream.status() != QTextStream::Ok) { QMessageBox::warning(parent, tr("Error"), tr("Cannot load CRT file, aborting.")); return false; } for (auto& item : replacements) { if (item.query.getOperator() != ObjectQuery::OperatorSearch) continue; auto operands = item.query.tagOperands(); if (operands && !operands->value.isEmpty()) { // Construct layer queries item.query = { QLatin1String("Layer"), ObjectQuery::OperatorIs, operands->value }; } } /// \todo Collect source symbols for all patterns, for source icon in dialog ReplaceSymbolSetDialog dialog(parent, object_map, symbol_set, replacements, AssignByPattern); dialog.setWindowModality(Qt::WindowModal); auto result = dialog.exec(); switch (result) { case QDialog::Accepted: replacements.squeezed().apply(object_map, symbol_set); return true; case QDialog::Rejected: return false; } Q_UNREACHABLE(); } // static QString ReplaceSymbolSetDialog::discoverCrtFile(const QString& source_id, const QString& target_id) { QString name = QLatin1String("data:/symbol sets/") + source_id + QLatin1Char('-') + target_id + QLatin1String(".crt"); #ifdef MAPPER_DEVELOPMENT_BUILD if (!QFileInfo::exists(name)) name = QLatin1String("data:/symbol sets/COPY_OF_") + source_id + QLatin1Char('-') + target_id + QLatin1String(".crt"); #endif return name; } } // namespace OpenOrienteering mapper-0.8.1.1/src/gui/symbols/replace_symbol_set_dialog.h000066400000000000000000000064461325266516600235770ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_REPLACE_SYMBOL_SET_DIALOG_H #define OPENORIENTEERING_REPLACE_SYMBOL_SET_DIALOG_H #include #include #include #include class QCheckBox; class QIODevice; class QComboBox; class QTableWidget; class QWidget; namespace OpenOrienteering { class Map; class SymbolDropDownDelegate; class SymbolRuleSet; /** * Dialog and tool for replacing the map's symbol set with another. * * Lets the user choose options and possibly even choose the replacement * for every single symbol. */ class ReplaceSymbolSetDialog : public QDialog { Q_OBJECT public: /** * Lets the user select a symbol set file, and shows the dialog. * * Returns true if the replacement has been finished, false if aborted. */ static bool showDialog(QWidget* parent, Map& object_map); /** * Lets the user select a CRT file, and shows the dialog. * * Returns true if the replacement has been finished, false if aborted. */ static bool showDialogForCRT(QWidget* parent, Map& object_map, const Map& symbol_set); /** * Shows the dialog for a given object map, symbol map, and cross reference file. * * Returns true if the replacement has been finished, false if aborted. */ static bool showDialogForCRT(QWidget* parent, Map& object_map, const Map& symbol_set, QIODevice& crt_file); /** * Returns the suggested path (including name) for finding a CRT file * for the given symbol set IDs. */ static QString discoverCrtFile(const QString& source_id, const QString& target_id); private: enum Mode { ReplaceSymbolSet, ///< Replace current current symbols AssignByPattern, ///< Assign new symbols based on a pattern }; ReplaceSymbolSetDialog(QWidget* parent, Map& object_map, const Map& symbol_set, SymbolRuleSet& replacements, Mode mode); ~ReplaceSymbolSetDialog() override; void showHelp(); void matchByName(); void matchByNumber(); void resetReplacements(); void openCrtFile(); void openCrtFile(const QString& path); bool saveCrtFile(); void done(int r) override; void updateMappingTable(); void updateMappingFromTable(); Map& object_map; const Map& symbol_set; SymbolRuleSet& replacements; QCheckBox* import_all_check; QCheckBox* delete_unused_symbols_check; QCheckBox* delete_unused_colors_check; QCheckBox* preserve_symbol_states_check; QComboBox* id_edit; QTableWidget* mapping_table; std::vector> symbol_widget_delegates; Mode mode; }; } // namespace OpenOrienteering #endif mapper-0.8.1.1/src/gui/symbols/symbol_properties_widget.cpp000066400000000000000000000240511325266516600240540ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2012-2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "symbol_properties_widget.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "core/map.h" #include "core/symbols/symbol.h" #include "gui/symbols/symbol_setting_dialog.h" #include "util/translation_util.h" #include "util/backports.h" // IWYU pragma: no_forward_declare QGridLayout // IWYU pragma: no_forward_declare QLabel namespace OpenOrienteering { // ### Symbol ### SymbolPropertiesWidget* Symbol::createPropertiesWidget(SymbolSettingDialog* dialog) { return new SymbolPropertiesWidget(this, dialog); } // ### SymbolPropertiesWidget ### SymbolPropertiesWidget::SymbolPropertiesWidget(Symbol* symbol, SymbolSettingDialog* dialog) : QTabWidget() , dialog(dialog) { auto generalTab = new QWidget(); auto layout = new QGridLayout(); generalTab->setLayout(layout); auto number_label = new QLabel(tr("Number:")); number_editors.resize(Symbol::number_components, nullptr); for (auto& editor : number_editors) { editor = new QLineEdit{}; editor->setMaximumWidth(60); editor->setValidator(new QIntValidator(0, 99999, editor)); } auto language_label = new QLabel(QApplication::translate("OpenOrienteering::MapSymbolTranslation", "Text source:")); language_combo = new QComboBox(); edit_button = new QPushButton(tr("Edit")); auto name_label = new QLabel(tr("Name:")); name_edit = new QLineEdit(); auto description_label = new QLabel(tr("Description:")); description_edit = new QTextEdit(); helper_symbol_check = new QCheckBox(tr("Helper symbol (not shown in finished map)")); SymbolPropertiesWidget::reset(symbol); for (auto editor : number_editors) connect(editor, &QLineEdit::textEdited, this, &SymbolPropertiesWidget::numberChanged); connect(language_combo, QOverload::of(&QComboBox::currentIndexChanged), this, &SymbolPropertiesWidget::languageChanged); connect(edit_button, &QPushButton::clicked, this, &SymbolPropertiesWidget::editClicked); connect(name_edit, &QLineEdit::textEdited, this, &SymbolPropertiesWidget::nameChanged); connect(description_edit, &QTextEdit::textChanged, this, &SymbolPropertiesWidget::descriptionChanged); connect(helper_symbol_check, &QAbstractButton::clicked, this, &SymbolPropertiesWidget::helperSymbolChanged); connect(this, &SymbolPropertiesWidget::propertiesModified, dialog, [dialog]() { dialog->setSymbolModified(true); }); int row = 0, col = 0; // 1st col layout->addWidget(number_label, row, col++); // 5 cols for (auto editor : number_editors) { layout->addWidget(editor, row, col++); if (editor != number_editors.back()) layout->addWidget(new QLabel(QString(QLatin1Char{'.'})), row, col++); } // 7th col ++col; // 8th col layout->setColumnStretch(col++, 1); auto num_col = col; row++; col = 0; layout->addWidget(language_label, row, col++); layout->addWidget(language_combo, row, col++, 1, num_col-3); layout->addWidget(edit_button, row, num_col-2); row++; col = 0; layout->addWidget(name_label, row, col++); layout->addWidget(name_edit, row, col++, 1, num_col-1); row++; col = 0; layout->addWidget(description_label, row, col, 1, num_col); row++; col = 0; layout->addWidget(description_edit, row, col, 1, num_col); row++; col = 0; layout->addWidget(helper_symbol_check, row, col, 1, num_col); addPropertiesGroup(tr("General"), generalTab); } SymbolPropertiesWidget::~SymbolPropertiesWidget() = default; void SymbolPropertiesWidget::addPropertiesGroup(const QString& name, QWidget* widget) { addTab(widget, name); } void SymbolPropertiesWidget::insertPropertiesGroup(int index, const QString& name, QWidget* widget) { insertTab(index, widget, name); } void SymbolPropertiesWidget::removePropertiesGroup(int index) { auto tab_contents = widget(index); removeTab(index); delete tab_contents; } void SymbolPropertiesWidget::removePropertiesGroup(const QString& name) { auto index = indexOfPropertiesGroup(name); removePropertiesGroup(index); } void SymbolPropertiesWidget::renamePropertiesGroup(int index, const QString& new_name) { setTabText(index, new_name); } void SymbolPropertiesWidget::renamePropertiesGroup(const QString& old_name, const QString& new_name) { auto index = indexOfPropertiesGroup(old_name); setTabText(index, new_name); } int SymbolPropertiesWidget::indexOfPropertiesGroup(const QString& name) const { for (int i = 0; i < count(); i++) { if (tabText(i) == name) return i; } return -1; } void SymbolPropertiesWidget::numberChanged(const QString& text) { bool editors_enabled = true; int i = 0; for (auto editor : number_editors) { editor->setEnabled(editors_enabled); if (editor == sender()) symbol->setNumberComponent(i, text.isEmpty() ? -1 : text.toInt()); if (editors_enabled && editor->text().isEmpty()) editors_enabled = false; ++i; } emit propertiesModified(); } void SymbolPropertiesWidget::languageChanged() { updateTextEdits(); } void SymbolPropertiesWidget::editClicked() { int result; auto question = QString{}; if (language_combo->currentIndex() == 1) { question = QApplication::translate("OpenOrienteering::MapSymbolTranslation", "Before editing, the stored text will be " "replaced with the current translation. " "Do you want to continue?"); } else { question = QApplication::translate("OpenOrienteering::MapSymbolTranslation", "After modifying the stored text, " "the translation may no longer be found. " "Do you want to continue?"); } result = QMessageBox::warning(dialog, tr("Warning"), question, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); if (result == QMessageBox::Yes) { language_combo->setEnabled(false); edit_button->setEnabled(false); name_edit->setEnabled(true); description_edit->setEnabled(true); if (language_combo->currentIndex() == 1) { { QSignalBlocker block(language_combo); language_combo->setCurrentIndex(0); } auto name = name_edit->text(); if (name.isEmpty()) { QSignalBlocker block(name_edit); name = symbol->getName(); name_edit->setText(name); } if (description_edit->document()->isEmpty()) { QSignalBlocker block(description_edit); description_edit->setText(symbol->getDescription()); } symbol->setName(name); symbol->setDescription(description_edit->toPlainText()); } emit propertiesModified(); } } void SymbolPropertiesWidget::nameChanged(const QString& text) { symbol->setName(text); emit propertiesModified(); } void SymbolPropertiesWidget::descriptionChanged() { symbol->setDescription(description_edit->toPlainText()); emit propertiesModified(); } void SymbolPropertiesWidget::helperSymbolChanged(bool checked) { symbol->setIsHelperSymbol(checked); emit propertiesModified(); } void SymbolPropertiesWidget::updateTextEdits() { auto name = symbol->getName(); auto description = symbol->getDescription(); if (language_combo->currentIndex() == 1) { name = dialog->getSourceMap()->raw_translation(name); description = dialog->getSourceMap()->raw_translation(description); } { QSignalBlocker block(name_edit); name_edit->setText(name); } { QSignalBlocker block(description_edit); description_edit->setText(description); } } void SymbolPropertiesWidget::reset(Symbol* symbol) { QSignalBlocker block(this); this->symbol = symbol; bool editors_enabled = true; int i = 0; for (auto editor : number_editors) { editor->setEnabled(editors_enabled); int value = symbol->getNumberComponent(i); if (value >= 0 && editors_enabled) { editor->setText(QString::number(value)); } else { editor->setText({}); editors_enabled = false; } ++i; } language_combo->clear(); auto name_translated = dialog->getSourceMap()->raw_translation(symbol->getName()); auto description_translated = dialog->getSourceMap()->raw_translation(symbol->getDescription()); ///: The language of the symbol name in the map file is not defined explicitly. language_combo->addItem(QApplication::translate("OpenOrienteering::MapSymbolTranslation", "Map (%1)"). arg(QApplication::translate("OpenOrienteering::MapSymbolTranslation", "undefined language"))); if (name_translated.isEmpty() && description_translated.isEmpty()) { language_combo->setEnabled(false); edit_button->setEnabled(false); name_edit->setEnabled(true); description_edit->setEnabled(true); } else { auto language = TranslationUtil::languageFromSettings(QSettings()); if (!language.isValid()) { language.displayName = QApplication::translate("OpenOrienteering::MapSymbolTranslation", "undefined language"); } language_combo->addItem(QApplication::translate("OpenOrienteering::MapSymbolTranslation", "Translation (%1)").arg(language.displayName)); language_combo->setEnabled(true); language_combo->setCurrentIndex(1); edit_button->setEnabled(true); name_edit->setEnabled(false); description_edit->setEnabled(false); } updateTextEdits(); helper_symbol_check->setChecked(symbol->isHelperSymbol()); } } // namespace OpenOrienteering mapper-0.8.1.1/src/gui/symbols/symbol_properties_widget.h000066400000000000000000000057161325266516600235300ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2012-2014, 2016, 2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_SYMBOL_PROPERTIES_WIDGET_H #define OPENORIENTEERING_SYMBOL_PROPERTIES_WIDGET_H #include #include #include #include class QCheckBox; class QComboBox; class QLineEdit; class QPushButton; class QTextEdit; class QWidget; namespace OpenOrienteering { class Symbol; class SymbolSettingDialog; /** * A widget for editing groups of properties of a symbol. */ class SymbolPropertiesWidget : public QTabWidget { Q_OBJECT public: /** Construct a new widget containing only a general properties group. * @param symbol the symbol to be customized */ SymbolPropertiesWidget(Symbol* symbol, SymbolSettingDialog* dialog); ~SymbolPropertiesWidget() override; /** Add a widget as a named group of properties */ void addPropertiesGroup(const QString& name, QWidget* widget); void insertPropertiesGroup(int index, const QString& name, QWidget* widget); void removePropertiesGroup(int index); void removePropertiesGroup(const QString& name); void renamePropertiesGroup(int index, const QString& new_name); void renamePropertiesGroup(const QString& old_name, const QString& new_name); int indexOfPropertiesGroup(const QString& name) const; /** Get the edited symbol */ inline Symbol* getSymbol() { return symbol; } QString getHelpSection() const { return QString(); } /** * Changes the edited symbol and resets the input values. * When overriding this method, make sure to call SymbolPropertiesWidget::reset(). */ virtual void reset(Symbol* symbol); signals: void propertiesModified(); protected slots: void numberChanged(const QString& text); void languageChanged(); void editClicked(); void nameChanged(const QString& text); void descriptionChanged(); void helperSymbolChanged(bool checked); protected: void updateTextEdits(); Symbol* symbol; SymbolSettingDialog* const dialog; std::vector number_editors; QComboBox* language_combo; QPushButton* edit_button; QLineEdit* name_edit; QTextEdit* description_edit; QCheckBox* helper_symbol_check; }; } // namespace OpenOrienteering #endif // OPENORIENTEERING_SYMBOL_PROPERTIES_WIDGET_H mapper-0.8.1.1/src/gui/symbols/symbol_setting_dialog.cpp000066400000000000000000000402151325266516600233110ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2012-2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "symbol_setting_dialog.h" #include #include #include #include #include #include #include #include #include #include #include "core/map.h" #include "core/objects/object.h" #include "core/objects/text_object.h" #include "core/symbols/combined_symbol.h" #include "core/symbols/line_symbol.h" #include "core/symbols/symbol.h" #include "core/symbols/text_symbol.h" #include "gui/main_window.h" #include "gui/util_gui.h" #include "gui/map/map_editor.h" #include "gui/map/map_widget.h" #include "gui/symbols/symbol_properties_widget.h" #include "gui/widgets/template_list_widget.h" #include "templates/template.h" #include "templates/template_image.h" namespace OpenOrienteering { SymbolSettingDialog::SymbolSettingDialog(const Symbol* source_symbol, Map* source_map, QWidget* parent) : QDialog(parent, Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::WindowMaximizeButtonHint) , source_map(source_map) , source_symbol(source_symbol) , source_symbol_copy(source_symbol->duplicate()) // don't rely on external entity , symbol(source_symbol->duplicate()) , symbol_modified(false) { setWindowTitle(tr("Symbol settings")); setSizeGripEnabled(true); symbol->setHidden(false); symbol_icon_label = new QLabel(); symbol_icon_label->setPixmap(QPixmap::fromImage(symbol->getIcon(source_map))); symbol_text_label = new QLabel(); updateSymbolLabel(); auto button_box = new QDialogButtonBox(QDialogButtonBox::Cancel | QDialogButtonBox::Ok | QDialogButtonBox::Reset | QDialogButtonBox::Help); ok_button = button_box->button(QDialogButtonBox::Ok); reset_button = button_box->button(QDialogButtonBox::Reset); connect(button_box, &QDialogButtonBox::rejected, this, &QDialog::reject); connect(button_box, &QDialogButtonBox::accepted, this, &QDialog::accept); connect(reset_button, &QAbstractButton::clicked, this, &SymbolSettingDialog::reset); connect(button_box->button(QDialogButtonBox::Help), &QAbstractButton::clicked, this, &SymbolSettingDialog::showHelp); preview_map = new Map(); preview_map->setSymbolSetId(source_map->symbolSetId()); preview_map->useColorsFrom(source_map); preview_map->setScaleDenominator(source_map->getScaleDenominator()); createPreviewMap(); preview_widget = new MainWindow(false); preview_controller = new MapEditorController(MapEditorController::SymbolEditor, preview_map); preview_widget->setController(preview_controller); preview_map_view = preview_controller->getMainWidget()->getMapView(); double zoom_factor = 1; if (symbol->getType() == Symbol::Point) zoom_factor = 8; else if (symbol->getType() == Symbol::Line || symbol->getType() == Symbol::Combined) zoom_factor = 2; preview_map_view->setZoom(zoom_factor * preview_map_view->getZoom()); properties_widget.reset(symbol->createPropertiesWidget(this)); QVBoxLayout* preview_layout = nullptr; if (symbol->getType() == Symbol::Point) { Q_UNUSED(QT_TR_NOOP("Template:")) /// \todo Switch the following line to Util::Headline::create auto template_label = new QLabel(tr("Template: ")); template_file_label = new QLabel(tr("(none)")); auto load_template_button = new QPushButton(tr("Open...")); center_template_button = new QToolButton(); center_template_button->setText(tr("Center template...")); center_template_button->setToolButtonStyle(Qt::ToolButtonTextOnly); center_template_button->setPopupMode(QToolButton::InstantPopup); center_template_button->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed)); auto center_template_menu = new QMenu(center_template_button); center_template_menu->addAction(tr("bounding box on origin"), this, SLOT(centerTemplateBBox())); center_template_menu->addAction(tr("center of gravity on origin"), this, SLOT(centerTemplateGravity())); center_template_button->setMenu(center_template_menu); center_template_button->setEnabled(false); auto template_layout = new QHBoxLayout(); template_layout->addWidget(template_label); template_layout->addWidget(template_file_label, 1); template_layout->addWidget(load_template_button); template_layout->addWidget(center_template_button); preview_layout = new QVBoxLayout(); preview_layout->setContentsMargins(0, 0, 0, 0); preview_layout->addLayout(template_layout); preview_layout->addWidget(preview_widget); connect(load_template_button, &QAbstractButton::clicked, this, &SymbolSettingDialog::loadTemplateClicked); } else { template_file_label = nullptr; center_template_button = nullptr; } auto left_layout = new QGridLayout(); left_layout->addWidget(symbol_icon_label, 0, 0); left_layout->addWidget(symbol_text_label, 0, 1); left_layout->addWidget(&*properties_widget, 1, 0, 1, 2); left_layout->addWidget(button_box, 2, 0, 1, 2); left_layout->setColumnStretch(1, 1); auto left = new QWidget(); left->setLayout(left_layout); left->layout(); QWidget* right = preview_widget; if (preview_layout) { right = new QWidget(); right->setLayout(preview_layout); } else { right->setMinimumWidth(300); } auto splitter = new QSplitter(); splitter->addWidget(left); splitter->setCollapsible(0, false); splitter->addWidget(right); splitter->setCollapsible(1, true); auto layout = new QHBoxLayout(); layout->setContentsMargins(0, 0, 0, 0); layout->addWidget(splitter); setLayout(layout); updateButtons(); } SymbolSettingDialog::~SymbolSettingDialog() = default; // not inlined std::unique_ptr SymbolSettingDialog::getNewSymbol() const { auto clone = std::unique_ptr(symbol->duplicate()); clone->setHidden(source_symbol_copy->isHidden()); return clone; } void SymbolSettingDialog::updatePreview() { symbol->resetIcon(); symbol_icon_label->setPixmap(QPixmap::fromImage(symbol->getIcon(source_map))); preview_map->updateAllObjects(); } void SymbolSettingDialog::loadTemplateClicked() { auto new_template = TemplateListWidget::showOpenTemplateDialog(this, preview_controller); if (new_template) { if (preview_map->getNumTemplates() > 0) { // Delete old template preview_map->setTemplateAreaDirty(0); preview_map->deleteTemplate(0); } preview_map->setFirstFrontTemplate(1); auto temp = new_template.release(); // avoid double release after addTemplate preview_map->addTemplate(temp, 0); preview_map_view->setTemplateVisibility(temp, { 1, true }); preview_map->setTemplateAreaDirty(0); template_file_label->setText(temp->getTemplateFilename()); center_template_button->setEnabled(qstrcmp(temp->getTemplateType(), "TemplateImage") == 0); } } void SymbolSettingDialog::centerTemplateBBox() { Q_ASSERT(preview_map->getNumTemplates() == 1); auto temp = preview_map->getTemplate(0); preview_map->setTemplateAreaDirty(0); temp->setTemplatePosition(temp->templatePosition() - MapCoord { temp->calculateTemplateBoundingBox().center() } ); preview_map->setTemplateAreaDirty(0); } void SymbolSettingDialog::centerTemplateGravity() { Q_ASSERT(preview_map->getNumTemplates() == 1); auto temp = preview_map->getTemplate(0); auto image = reinterpret_cast(temp); QColor background_color = QColorDialog::getColor(Qt::white, this, tr("Select background color")); if (!background_color.isValid()) return; auto map_center_current = MapCoord { temp->templateToMap(image->calcCenterOfGravity(background_color.rgb())) }; preview_map->setTemplateAreaDirty(0); temp->setTemplatePosition(temp->templatePosition() - map_center_current); preview_map->setTemplateAreaDirty(0); } void SymbolSettingDialog::createPreviewMap() { symbol_icon_label->setPixmap(QPixmap::fromImage(symbol->getIcon(source_map))); for (auto& object : preview_objects) preview_map->deleteObject(object, false); preview_objects.clear(); if (symbol->getType() == Symbol::Line) { auto line = reinterpret_cast(&*symbol); const int num_lines = 15; const auto min_length = 0.5; const auto max_length = 15.0; const auto x_offset = -0.5 * max_length; const auto y_offset = (0.001 * qMax(600, line->getLineWidth())) * 3.5; auto y_start = 0 - (y_offset * (num_lines - 1)); for (int i = 0; i < num_lines; ++i) { auto length = min_length + (max_length - min_length) * i / (num_lines - 1); auto y = y_start + i * y_offset; auto path = new PathObject(line); path->addCoordinate(0, { x_offset - length, y }); path->addCoordinate(1, { x_offset, y }); preview_map->addObject(path); preview_objects.push_back(path); } const int num_circular_lines = 12; const auto inner_radius = 4; auto center = MapCoordF { 2*inner_radius + 0.5 * max_length, 0.5 * y_start }; for (int i = 0; i < num_circular_lines; ++i) { auto angle = M_PI * 2 * i / num_circular_lines; auto direction = MapCoordF::fromPolar(1.0, angle); auto length = min_length + (max_length - min_length) * i / (num_circular_lines - 1); auto path = new PathObject(line); path->addCoordinate(0, MapCoord(center + direction * inner_radius)); path->addCoordinate(1, MapCoord(center + direction * (inner_radius + length))); preview_map->addObject(path); preview_objects.push_back(path); } const auto snake_min_x = -1.5 * max_length; const auto snake_max_x = 1.5 * max_length; const auto snake_max_y = qMin(0.5 * y_start - inner_radius - max_length, y_start) - 4; const auto snake_min_y = snake_max_y - 6; const int snake_steps = 8u; auto path = new PathObject(line); for (auto i = 0u; i < snake_steps; ++i) { MapCoord coord(snake_min_x + (snake_max_x - snake_min_x) * i / (snake_steps-1), snake_min_y + (i % 2) * (snake_max_y - snake_min_y)); coord.setDashPoint(true); path->addCoordinate(i, coord); } preview_map->addObject(path); preview_objects.push_back(path); const auto curve_min_x = snake_min_x; const auto curve_max_x = snake_max_x; const auto curve_max_y = snake_min_y - 4; const auto curve_min_y = curve_max_y - 6; path = new PathObject(line); MapCoord coord = MapCoord(curve_min_x, curve_min_y); coord.setCurveStart(true); path->addCoordinate(coord); coord = MapCoord(curve_min_x + (curve_max_x - curve_min_x) / 6, curve_max_y); path->addCoordinate(coord); coord = MapCoord(curve_min_x + 2 * (curve_max_x - curve_min_x) / 6, curve_max_y); path->addCoordinate(coord); coord = MapCoord(curve_min_x + 3 * (curve_max_x - curve_min_x) / 6, 0.5 * (curve_min_y + curve_max_y)); coord.setCurveStart(true); path->addCoordinate(coord); coord = MapCoord(curve_min_x + 4 * (curve_max_x - curve_min_x) / 6, curve_min_y); path->addCoordinate(coord); coord = MapCoord(curve_min_x + 5 * (curve_max_x - curve_min_x) / 6, curve_min_y); path->addCoordinate(coord); coord = MapCoord(curve_max_x, curve_max_y); path->addCoordinate(coord); preview_map->addObject(path); preview_objects.push_back(path); // Debug objects #if 0 // Closed path, rectangular PathObject* path; MapCoord coord; path = new PathObject(preview_map, line); coord = MapCoord(-20, -10); //coord.setCurveStart(true); path->addCoordinate(coord); coord = MapCoord(20, -10); path->addCoordinate(coord); coord = MapCoord(20, 10); path->addCoordinate(coord); coord = MapCoord(-20, 10); path->addCoordinate(coord); path->setPathClosed(true); preview_map->addObject(path); preview_objects.push_back(path); // Closed path, curve PathObject* path; MapCoord coord; path = new PathObject(preview_map, line); path->setPathClosed(true); coord = MapCoord(-20, 10); coord.setCurveStart(true); path->addCoordinate(coord); coord = MapCoord(-10, 0); path->addCoordinate(coord); coord = MapCoord(-10, 10); path->addCoordinate(coord); preview_map->addObject(path); preview_objects.push_back(path); // Path with holes PathObject* path; MapCoord coord; path = new PathObject(preview_map, line); coord = MapCoord(-20, 10); coord.setCurveStart(true); path->addCoordinate(coord); coord = MapCoord(-10, 0); path->addCoordinate(coord); coord = MapCoord(0, -10); path->addCoordinate(coord); coord = MapCoord(10, 10); path->addCoordinate(coord); coord = MapCoord(20, 10); coord.setHolePoint(true); path->addCoordinate(coord); coord = MapCoord(30, 10); path->addCoordinate(coord); coord = MapCoord(40, 10); path->addCoordinate(coord); preview_map->addObject(path); preview_objects.push_back(path); #endif } else if (symbol->getType() == Symbol::Area) { const qreal half_radius = 8; const qreal offset_y = 0; auto path = new PathObject(&*symbol); path->addCoordinate(0, { -half_radius, -half_radius + offset_y }); path->addCoordinate(1, { 0.0, -half_radius + offset_y }); path->addCoordinate(2, { half_radius, offset_y }); path->addCoordinate(3, { half_radius, half_radius + offset_y }); path->addCoordinate(4, { -half_radius, half_radius + offset_y }); preview_map->addObject(path); preview_objects.push_back(path); } else if (symbol->getType() == Symbol::Text) { auto text_symbol = reinterpret_cast(&*symbol); const QString string = tr("The quick brown fox\ntakes the routechoice\nto jump over the lazy dog\n1234567890"); auto object = new TextObject(text_symbol); object->setAnchorPosition(0, 0); object->setText(string); object->setHorizontalAlignment(TextObject::AlignHCenter); object->setVerticalAlignment(TextObject::AlignVCenter); preview_map->addObject(object); preview_objects.push_back(object); } else if (symbol->getType() == Symbol::Combined) { const auto radius = 5.0; auto flags = MapCoord::Flags{}; const auto* combined = symbol->asCombined(); for (int i = 0; i < combined->getNumParts(); ++i) { auto part = combined->getPart(i); if (part && part->getType() == Symbol::Line && part->asLine()->getDashSymbol()) { flags |= MapCoord::DashPoint; break; } } auto path = new PathObject(&*symbol); for (auto i = 0u; i < 5u; ++i) path->addCoordinate(i, MapCoord(sin(2*M_PI * i/5.0) * radius, -cos(2*M_PI * i/5.0) * radius, flags)); path->parts().front().setClosed(true, false); preview_map->addObject(path); preview_objects.push_back(path); } } void SymbolSettingDialog::showHelp() { QByteArray fragment; fragment.reserve(100); fragment = "editor"; if (properties_widget->currentIndex() > 0) { fragment = "symbol-type-"; fragment.append(QByteArray::number(symbol->getType())); } Util::showHelp(preview_controller->getWindow(), "symbol_dock_widget.html", fragment); } void SymbolSettingDialog::reset() { auto duplicate = std::unique_ptr(source_symbol_copy->duplicate()); swap(symbol, duplicate); symbol->setHidden(false); createPreviewMap(); properties_widget->reset(&*symbol); setSymbolModified(false); } void SymbolSettingDialog::setSymbolModified(bool modified) { symbol_modified = modified; updateSymbolLabel(); updatePreview(); updateButtons(); } void SymbolSettingDialog::updateSymbolLabel() { QString number = symbol->getNumberAsString(); if (number.isEmpty()) number = QString::fromLatin1("???"); QString name = source_map->translate(symbol->getName()); if (name.isEmpty()) name = tr("- unnamed -"); symbol_text_label->setText(QLatin1String("") + number + QLatin1String(" ") + name + QLatin1String("")); } void SymbolSettingDialog::updateButtons() { ok_button->setEnabled(symbol_modified && symbol->getNumberComponent(0)>=0 && !symbol->getName().isEmpty()); reset_button->setEnabled(symbol_modified); } } // namespace OpenOrienteering mapper-0.8.1.1/src/gui/symbols/symbol_setting_dialog.h000066400000000000000000000106301325266516600227540ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_SYMBOL_SETTING_DIALOG_H #define OPENORIENTEERING_SYMBOL_SETTING_DIALOG_H #include #include #include #include class QLabel; class QPushButton; class QToolButton; class QWidget; namespace OpenOrienteering { class MainWindow; class Map; class MapEditorController; class MapView; class Object; class Symbol; class SymbolPropertiesWidget; /** * A dialog for editing symbol properties. * * The specific symbol properties must be edited in tabs provided by the symbol. */ class SymbolSettingDialog : public QDialog { Q_OBJECT public: /** * Constructs a new dialog for a given symbol and map. */ SymbolSettingDialog(const Symbol* source_symbol, Map* source_map, QWidget* parent); /** * Destructs the dialog and cleans up temporary objects. */ ~SymbolSettingDialog() override; /** * Returns a copy of the currently edited symbol. */ std::unique_ptr getNewSymbol() const; /** * Returns a pointer to the unmodifed symbol which is currently edited. * Use getUnmodifiedSymbolCopy() instead if you need to access the object! */ inline const Symbol* getUnmodifiedSymbol() const { return source_symbol; } /** * Returns a pointer to a copy of the unmodifed symbol which is currently edited. */ inline const Symbol* getUnmodifiedSymbolCopy() const { return &*source_symbol_copy; } /** * Returns true if the edited symbol has modfications. */ inline bool isSymbolModified() const { return symbol_modified; } /** * Returns the Map of the unmodified symbol. */ inline Map* getSourceMap() { return source_map; } /** * Returns the preview map of this dialog. */ inline Map* getPreviewMap() { return preview_map; } /** * Return the MapEditorController which handles this dialog's preview map. */ inline MapEditorController* getPreviewController() { return preview_controller; } public slots: /** * Shows this dialog's help page. */ void showHelp(); /** * Resets the edited symbol to the state of the unmodified symbol from the map. */ void reset(); /** * Sets the modification status of the dialog, and triggers a screen update. */ void setSymbolModified(bool modified = true); /** * Updates the label which is the headline to the dialog. */ void updateSymbolLabel(); /** * Updates the preview from the current symbol settings. */ void updatePreview(); /** * Updates the state (enabled/disabled) of the dialog buttons. */ void updateButtons(); protected slots: /** * Opens a dialog for loading a template to the preview map. */ void loadTemplateClicked(); /** * Centers the preview map template relative to its bounding box. */ void centerTemplateBBox(); /** * Centers the preview map template relative to its center of gravity, * leaving out a selected background color. */ void centerTemplateGravity(); protected: /** * Populates the preview map for the symbol. */ void createPreviewMap(); private: Map* source_map; const Symbol* source_symbol; std::unique_ptr source_symbol_copy; // properties_widget must be deleted before symbol. std::unique_ptr symbol; std::unique_ptr properties_widget; Map* preview_map; MainWindow* preview_widget; MapView* preview_map_view; MapEditorController* preview_controller; std::vector preview_objects; QLabel* template_file_label; QToolButton* center_template_button; QPushButton* ok_button; QPushButton* reset_button; QLabel* symbol_icon_label; QLabel* symbol_text_label; bool symbol_modified; }; } // namespace OpenOrienteering #endif mapper-0.8.1.1/src/gui/symbols/text_symbol_settings.cpp000066400000000000000000000500771325266516600232300ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2012-2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "text_symbol_settings.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "core/symbols/symbol.h" #include "core/symbols/text_symbol.h" #include "gui/util_gui.h" #include "gui/symbols/symbol_setting_dialog.h" #include "gui/widgets/color_dropdown.h" #include "util/backports.h" namespace OpenOrienteering { // ### DetermineFontSizeDialog ### /** * \todo Move translation items to TextSymbolSettings class. */ class DetermineFontSizeDialog { public: Q_DECLARE_TR_FUNCTIONS(OpenOrienteering::DetermineFontSizeDialog) }; // ### TextSymbol ### SymbolPropertiesWidget* TextSymbol::createPropertiesWidget(SymbolSettingDialog* dialog) { return new TextSymbolSettings(this, dialog); } // ### TextSymbolSettings ### TextSymbolSettings::TextSymbolSettings(TextSymbol* symbol, SymbolSettingDialog* dialog) : SymbolPropertiesWidget(symbol, dialog), symbol(symbol), dialog(dialog) { auto map = dialog->getPreviewMap(); react_to_changes = true; auto text_tab = new QWidget(); addPropertiesGroup(tr("Text settings"), text_tab); auto layout = new QFormLayout(); text_tab->setLayout(layout); font_edit = new QFontComboBox(); layout->addRow(tr("Font family:"), font_edit); // 0.04 pt font size is app. 0.01 mm letter height, the non-zero minimum font_size_edit = Util::SpinBox::create(2, 0.04, 40000.0, tr("pt")); layout->addRow(tr("Font size:"), font_size_edit); auto letter_size_layout = new QHBoxLayout(); letter_size_layout->setMargin(0); letter_size_layout->addWidget(new QLabel(::OpenOrienteering::DetermineFontSizeDialog::tr("Letter:"))); //: "A" is the default letter which is used for determining letter height. letter_edit = new QLineEdit(DetermineFontSizeDialog::tr("A")); letter_edit->setMaxLength(3); letter_size_layout->addWidget(letter_edit); letter_size_layout->addSpacing(8); letter_size_layout->addWidget(new QLabel(::OpenOrienteering::DetermineFontSizeDialog::tr("Height:"))); letter_size_edit = Util::SpinBox::create(2, 0.01, 10000.0, tr("mm")); letter_size_layout->addWidget(letter_size_edit); layout->addRow(new QWidget(), letter_size_layout); layout->addItem(Util::SpacerItem::create(this)); color_edit = new ColorDropDown(map, symbol->getColor()); layout->addRow(tr("Text color:"), color_edit); auto text_style_layout = new QVBoxLayout(); bold_check = new QCheckBox(tr("bold")); text_style_layout->addWidget(bold_check); italic_check = new QCheckBox(tr("italic")); text_style_layout->addWidget(italic_check); underline_check = new QCheckBox(tr("underlined")); text_style_layout->addWidget(underline_check); layout->addRow(tr("Text style:"), text_style_layout); layout->addItem(Util::SpacerItem::create(this)); line_spacing_edit = Util::SpinBox::create(1, 0.0, 999999.9, tr("%")); layout->addRow(tr("Line spacing:"), line_spacing_edit); paragraph_spacing_edit = Util::SpinBox::create(2, -999999.9, 999999.9, tr("mm")); layout->addRow(tr("Paragraph spacing:"), paragraph_spacing_edit); character_spacing_edit = Util::SpinBox::create(1, -999999.9, 999999.9, tr("%")); layout->addRow(tr("Character spacing:"), character_spacing_edit); kerning_check = new QCheckBox(tr("Kerning")); layout->addRow(QString{}, kerning_check); layout->addItem(Util::SpacerItem::create(this)); icon_text_edit = new QLineEdit(); icon_text_edit->setMaxLength(3); layout->addRow(tr("Symbol icon text:"), icon_text_edit); layout->addItem(Util::SpacerItem::create(this)); framing_check = new QCheckBox(tr("Framing")); layout->addRow(framing_check); ocad_compat_check = new QCheckBox(tr("OCAD compatibility settings")); layout->addRow(ocad_compat_check); framing_widget = new QWidget(); addPropertiesGroup(tr("Framing"), framing_widget); auto framing_layout = new QFormLayout(); framing_widget->setLayout(framing_layout); framing_color_edit = new ColorDropDown(map, symbol->getFramingColor()); framing_layout->addRow(tr("Framing color:"), framing_color_edit); framing_line_radio = new QRadioButton(tr("Line framing")); framing_layout->addRow(framing_line_radio); framing_line_half_width_edit = Util::SpinBox::create(1, 0.0, 999999.9); framing_layout->addRow(tr("Width:"), framing_line_half_width_edit); framing_shadow_radio = new QRadioButton(tr("Shadow framing")); framing_layout->addRow(framing_shadow_radio); framing_shadow_x_offset_edit = Util::SpinBox::create(1, -999999.9, 999999.9); framing_layout->addRow(tr("Left/Right Offset:"), framing_shadow_x_offset_edit); framing_shadow_y_offset_edit = Util::SpinBox::create(1, -999999.9, 999999.9); framing_layout->addRow(tr("Top/Down Offset:"), framing_shadow_y_offset_edit); ocad_compat_widget = new QWidget(); addPropertiesGroup(tr("OCAD compatibility"), ocad_compat_widget); auto ocad_compat_layout = new QFormLayout(); ocad_compat_widget->setLayout(ocad_compat_layout); ocad_compat_layout->addRow(Util::Headline::create(tr("Line below paragraphs"))); line_below_check = new QCheckBox(tr("enabled")); ocad_compat_layout->addRow(line_below_check); line_below_width_edit = Util::SpinBox::create(2, 0.0, 999999.9, tr("mm")); ocad_compat_layout->addRow(tr("Line width:"), line_below_width_edit); line_below_color_edit = new ColorDropDown(map); ocad_compat_layout->addRow(tr("Line color:"), line_below_color_edit); line_below_distance_edit = Util::SpinBox::create(2, 0.0, 999999.9, tr("mm")); ocad_compat_layout->addRow(tr("Distance from baseline:"), line_below_distance_edit); ocad_compat_layout->addItem(Util::SpacerItem::create(this)); ocad_compat_layout->addRow(Util::Headline::create(tr("Custom tabulator positions"))); custom_tab_list = new QListWidget(); ocad_compat_layout->addRow(custom_tab_list); auto custom_tabs_button_layout = new QHBoxLayout(); custom_tab_add = new QPushButton(QIcon(QStringLiteral(":/images/plus.png")), QString{}); custom_tabs_button_layout->addWidget(custom_tab_add); custom_tab_remove = new QPushButton(QIcon(QStringLiteral(":/images/minus.png")), QString{}); custom_tab_remove->setEnabled(false); custom_tabs_button_layout->addWidget(custom_tab_remove); custom_tabs_button_layout->addStretch(1); ocad_compat_layout->addRow(custom_tabs_button_layout); updateGeneralContents(); updateFramingContents(); updateCompatibilityContents(); connect(font_edit, &QFontComboBox::currentFontChanged, this, &TextSymbolSettings::fontChanged); connect(font_size_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &TextSymbolSettings::fontSizeChanged); connect(letter_edit, &QLineEdit::textEdited, this, &TextSymbolSettings::letterSizeChanged); connect(letter_size_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &TextSymbolSettings::letterSizeChanged); connect(color_edit, QOverload::of(&QComboBox::currentIndexChanged), this, &TextSymbolSettings::colorChanged); connect(bold_check, &QAbstractButton::clicked, this, &TextSymbolSettings::checkToggled); connect(italic_check, &QAbstractButton::clicked, this, &TextSymbolSettings::checkToggled); connect(underline_check, &QAbstractButton::clicked, this, &TextSymbolSettings::checkToggled); connect(line_spacing_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &TextSymbolSettings::spacingChanged); connect(paragraph_spacing_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &TextSymbolSettings::spacingChanged); connect(character_spacing_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &TextSymbolSettings::spacingChanged); connect(kerning_check, &QAbstractButton::clicked, this, &TextSymbolSettings::checkToggled); connect(icon_text_edit, &QLineEdit::textEdited, this, &TextSymbolSettings::iconTextEdited); connect(framing_check, &QAbstractButton::clicked, this, &TextSymbolSettings::framingCheckClicked); connect(ocad_compat_check, &QAbstractButton::clicked, this, &TextSymbolSettings::ocadCompatibilityButtonClicked); connect(framing_color_edit, QOverload::of(&QComboBox::currentIndexChanged), this, &TextSymbolSettings::framingColorChanged); connect(framing_line_radio, &QAbstractButton::clicked, this, &TextSymbolSettings::framingModeChanged); connect(framing_line_half_width_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &TextSymbolSettings::framingSettingChanged); connect(framing_shadow_radio, &QAbstractButton::clicked, this, &TextSymbolSettings::framingModeChanged); connect(framing_shadow_x_offset_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &TextSymbolSettings::framingSettingChanged); connect(framing_shadow_y_offset_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &TextSymbolSettings::framingSettingChanged); connect(line_below_check, &QAbstractButton::clicked, this, &TextSymbolSettings::lineBelowCheckClicked); connect(line_below_color_edit, QOverload::of(&QComboBox::currentIndexChanged), this, &TextSymbolSettings::lineBelowSettingChanged); connect(line_below_width_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &TextSymbolSettings::lineBelowSettingChanged); connect(line_below_distance_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &TextSymbolSettings::lineBelowSettingChanged); connect(custom_tab_list, &QListWidget::currentRowChanged, this, &TextSymbolSettings::customTabRowChanged); connect(custom_tab_add, &QAbstractButton::clicked, this, &TextSymbolSettings::addCustomTabClicked); connect(custom_tab_remove, &QAbstractButton::clicked, this, &TextSymbolSettings::removeCustomTabClicked); } TextSymbolSettings::~TextSymbolSettings() = default; void TextSymbolSettings::fontChanged(const QFont& font) { if (!react_to_changes) return; symbol->font_family = font.family(); symbol->updateQFont(); updateLetterSizeEdit(); emit propertiesModified(); } void TextSymbolSettings::fontSizeChanged(double value) { if (!react_to_changes) return; symbol->font_size = qRound(value * 25400.0 / 72.0); symbol->updateQFont(); updateLetterSizeEdit(); emit propertiesModified(); } void TextSymbolSettings::updateFontSizeEdit() { QSignalBlocker block { font_size_edit }; font_size_edit->setValue(0.01 * qRound(symbol->font_size * 7.2 / 25.4)); } void TextSymbolSettings::letterSizeChanged() { auto letter_height = calculateLetterHeight(); if (letter_height > 0.0) { symbol->font_size = qRound(1000.0 * letter_size_edit->value() / letter_height); symbol->updateQFont(); updateFontSizeEdit(); emit propertiesModified(); } } qreal TextSymbolSettings::calculateLetterHeight() const { QPainterPath path; path.addText(0.0, 0.0, symbol->getQFont(), letter_edit->text()); return path.boundingRect().height() / TextSymbol::internal_point_size; } void TextSymbolSettings::updateLetterSizeEdit() { auto letter_height = calculateLetterHeight(); if (letter_height > 0.0) { QSignalBlocker block { letter_size_edit }; letter_size_edit->setValue(0.01 * qRound(symbol->font_size * letter_height / 10.0)); } } void TextSymbolSettings::colorChanged() { if (!react_to_changes) return; symbol->color = color_edit->color(); symbol->updateQFont(); emit propertiesModified(); } void TextSymbolSettings::checkToggled(bool checked) { Q_UNUSED(checked); if (!react_to_changes) return; symbol->bold = bold_check->isChecked(); symbol->italic = italic_check->isChecked(); symbol->underline = underline_check->isChecked(); symbol->kerning = kerning_check->isChecked(); symbol->updateQFont(); emit propertiesModified(); } void TextSymbolSettings::spacingChanged(double value) { Q_UNUSED(value); if (!react_to_changes) return; symbol->line_spacing = 0.01 * line_spacing_edit->value(); symbol->paragraph_spacing = qRound(1000.0 * paragraph_spacing_edit->value()); symbol->character_spacing = 0.01 * character_spacing_edit->value(); symbol->updateQFont(); emit propertiesModified(); } void TextSymbolSettings::iconTextEdited(const QString& text) { if (!react_to_changes) return; symbol->icon_text = text; emit propertiesModified(); } void TextSymbolSettings::framingColorChanged() { if (!react_to_changes) return; symbol->framing_color = framing_color_edit->color(); updateFramingContents(); emit propertiesModified(); } void TextSymbolSettings::framingModeChanged() { if (!react_to_changes) return; if (framing_line_radio->isChecked()) symbol->framing_mode = TextSymbol::LineFraming; else if (framing_shadow_radio->isChecked()) symbol->framing_mode = TextSymbol::ShadowFraming; updateFramingContents(); emit propertiesModified(); } void TextSymbolSettings::framingSettingChanged() { if (!react_to_changes) return; symbol->framing_line_half_width = qRound(1000.0 * framing_line_half_width_edit->value()); symbol->framing_shadow_x_offset = qRound(1000.0 * framing_shadow_x_offset_edit->value()); symbol->framing_shadow_y_offset = qRound(-1000.0 * framing_shadow_y_offset_edit->value()); emit propertiesModified(); } void TextSymbolSettings::framingCheckClicked(bool checked) { if (!react_to_changes) return; setTabEnabled(indexOf(framing_widget), checked); symbol->framing = checked; emit propertiesModified(); } void TextSymbolSettings::ocadCompatibilityButtonClicked(bool checked) { if (!react_to_changes) return; setTabEnabled(indexOf(ocad_compat_widget), checked); updateCompatibilityCheckEnabled(); } void TextSymbolSettings::lineBelowCheckClicked(bool checked) { if (!react_to_changes) return; symbol->line_below = checked; if (checked) { // Set defaults such that the line becomes visible if (symbol->line_below_width == 0) line_below_width_edit->setValue(0.1); if (!symbol->line_below_color) line_below_color_edit->setCurrentIndex(1); } symbol->updateQFont(); emit propertiesModified(); line_below_color_edit->setEnabled(checked); line_below_width_edit->setEnabled(checked); line_below_distance_edit->setEnabled(checked); updateCompatibilityCheckEnabled(); } void TextSymbolSettings::lineBelowSettingChanged() { if (!react_to_changes) return; symbol->line_below_color = line_below_color_edit->color(); symbol->line_below_width = qRound(1000.0 * line_below_width_edit->value()); symbol->line_below_distance = qRound(1000.0 * line_below_distance_edit->value()); symbol->updateQFont(); emit propertiesModified(); updateCompatibilityCheckEnabled(); } void TextSymbolSettings::customTabRowChanged(int row) { custom_tab_remove->setEnabled(row >= 0); } void TextSymbolSettings::addCustomTabClicked() { bool ok = false; // FIXME: Unit of measurement display in unusual way double position = QInputDialog::getDouble(dialog, tr("Add custom tabulator"), QStringLiteral("%1 (%2)").arg(tr("Position:"), tr("mm")), 0, 0, 999999, 3, &ok); if (ok) { int int_position = qRound(1000 * position); int row = symbol->getNumCustomTabs(); for (int i = 0; i < symbol->getNumCustomTabs(); ++i) { if (int_position == symbol->custom_tabs[i]) return; // do not add a double position else if (int_position < symbol->custom_tabs[i]) { row = i; break; } } custom_tab_list->insertItem(row, locale().toString(position, 'g', 3) + QLatin1Char(' ') + tr("mm")); custom_tab_list->setCurrentRow(row); symbol->custom_tabs.insert(symbol->custom_tabs.begin() + row, int_position); emit propertiesModified(); updateCompatibilityCheckEnabled(); } } void TextSymbolSettings::removeCustomTabClicked() { if (auto item = custom_tab_list->currentItem()) { int row = custom_tab_list->row(item); delete custom_tab_list->item(row); custom_tab_list->setCurrentRow(row - 1); symbol->custom_tabs.erase(symbol->custom_tabs.begin() + row); emit propertiesModified(); updateCompatibilityCheckEnabled(); } } void TextSymbolSettings::updateGeneralContents() { react_to_changes = false; font_edit->setCurrentFont(QFont(symbol->font_family)); updateFontSizeEdit(); updateLetterSizeEdit(); color_edit->setColor(symbol->color); bold_check->setChecked(symbol->bold); italic_check->setChecked(symbol->italic); underline_check->setChecked(symbol->underline); line_spacing_edit->setValue(100.0 * symbol->line_spacing); paragraph_spacing_edit->setValue(0.001 * symbol->paragraph_spacing); character_spacing_edit->setValue(100 * symbol->character_spacing); kerning_check->setChecked(symbol->kerning); icon_text_edit->setText(symbol->getIconText()); framing_check->setChecked(symbol->framing); ocad_compat_check->setChecked(symbol->line_below || symbol->getNumCustomTabs() > 0); react_to_changes = true; framingCheckClicked(framing_check->isChecked()); ocadCompatibilityButtonClicked(ocad_compat_check->isChecked()); } void TextSymbolSettings::updateCompatibilityCheckEnabled() { ocad_compat_check->setEnabled(!symbol->line_below && symbol->getNumCustomTabs() == 0); } void TextSymbolSettings::updateFramingContents() { react_to_changes = false; framing_color_edit->setColor(symbol->framing_color); framing_line_radio->setChecked(symbol->framing_mode == TextSymbol::LineFraming); framing_line_half_width_edit->setValue(0.001 * symbol->framing_line_half_width); framing_shadow_radio->setChecked(symbol->framing_mode == TextSymbol::ShadowFraming); framing_shadow_x_offset_edit->setValue(0.001 * symbol->framing_shadow_x_offset); framing_shadow_y_offset_edit->setValue(-0.001 * symbol->framing_shadow_y_offset); framing_line_radio->setEnabled(symbol->framing_color); framing_shadow_radio->setEnabled(symbol->framing_color); if (!symbol->framing_color) { framing_line_half_width_edit->setEnabled(false); framing_shadow_x_offset_edit->setEnabled(false); framing_shadow_y_offset_edit->setEnabled(false); } else { framing_line_half_width_edit->setEnabled(symbol->framing_mode == TextSymbol::LineFraming); framing_shadow_x_offset_edit->setEnabled(symbol->framing_mode == TextSymbol::ShadowFraming); framing_shadow_y_offset_edit->setEnabled(symbol->framing_mode == TextSymbol::ShadowFraming); } react_to_changes = true; } void TextSymbolSettings::updateCompatibilityContents() { react_to_changes = false; line_below_check->setChecked(symbol->line_below); line_below_width_edit->setValue(0.001 * symbol->line_below_width); line_below_color_edit->setColor(symbol->line_below_color); line_below_distance_edit->setValue(0.001 * symbol->line_below_distance); line_below_color_edit->setEnabled(symbol->line_below); line_below_width_edit->setEnabled(symbol->line_below); line_below_distance_edit->setEnabled(symbol->line_below); custom_tab_list->clear(); for (int i = 0; i < symbol->getNumCustomTabs(); ++i) custom_tab_list->addItem(locale().toString(0.001 * symbol->getCustomTab(i), 'g', 3) + QLatin1Char(' ') + tr("mm")); react_to_changes = true; } void TextSymbolSettings::reset(Symbol* symbol) { Q_ASSERT(symbol->getType() == Symbol::Text); SymbolPropertiesWidget::reset(symbol); this->symbol = reinterpret_cast(symbol); updateGeneralContents(); updateFramingContents(); updateCompatibilityContents(); } } // namespace OpenOrienteering mapper-0.8.1.1/src/gui/symbols/text_symbol_settings.h000066400000000000000000000070541325266516600226720ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2012-2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_TEXT_SYMBOL_SETTINGS_H #define OPENORIENTEERING_TEXT_SYMBOL_SETTINGS_H #include #include #include #include "gui/symbols/symbol_properties_widget.h" class QCheckBox; class QDoubleSpinBox; class QFont; class QFontComboBox; class QLineEdit; class QListWidget; class QPushButton; class QRadioButton; class QWidget; namespace OpenOrienteering { class ColorDropDown; class Symbol; class SymbolSettingDialog; class TextSymbol; class TextSymbolSettings : public SymbolPropertiesWidget { Q_OBJECT public: TextSymbolSettings(TextSymbol* symbol, SymbolSettingDialog* dialog); ~TextSymbolSettings() override; void reset(Symbol* symbol) override; void updateGeneralContents(); void updateFramingContents(); void updateCompatibilityCheckEnabled(); void updateCompatibilityContents(); protected slots: void fontChanged(const QFont& font); void fontSizeChanged(double value); void letterSizeChanged(); void colorChanged(); void checkToggled(bool checked); void spacingChanged(double value); void iconTextEdited(const QString& text); void framingCheckClicked(bool checked); void framingColorChanged(); void framingModeChanged(); void framingSettingChanged(); void ocadCompatibilityButtonClicked(bool checked); void lineBelowCheckClicked(bool checked); void lineBelowSettingChanged(); void customTabRowChanged(int row); void addCustomTabClicked(); void removeCustomTabClicked(); protected: void updateFontSizeEdit(); void updateLetterSizeEdit(); qreal calculateLetterHeight() const; private: TextSymbol* symbol; SymbolSettingDialog* dialog; ColorDropDown* color_edit; QFontComboBox* font_edit; QDoubleSpinBox* font_size_edit; QLineEdit* letter_edit; QDoubleSpinBox* letter_size_edit; QCheckBox* bold_check; QCheckBox* italic_check; QCheckBox* underline_check; QDoubleSpinBox* line_spacing_edit; QDoubleSpinBox* paragraph_spacing_edit; QDoubleSpinBox* character_spacing_edit; QCheckBox* kerning_check; QLineEdit* icon_text_edit; QCheckBox* framing_check; QCheckBox* ocad_compat_check; QWidget* framing_widget; ColorDropDown* framing_color_edit; QRadioButton* framing_line_radio; QDoubleSpinBox* framing_line_half_width_edit; QRadioButton* framing_shadow_radio; QDoubleSpinBox* framing_shadow_x_offset_edit; QDoubleSpinBox* framing_shadow_y_offset_edit; QWidget* ocad_compat_widget; QCheckBox* line_below_check; QDoubleSpinBox* line_below_width_edit; ColorDropDown* line_below_color_edit; QDoubleSpinBox* line_below_distance_edit; QListWidget* custom_tab_list; QPushButton* custom_tab_add; QPushButton* custom_tab_remove; bool react_to_changes; }; } // namespace OpenOrienteering #endif mapper-0.8.1.1/src/gui/task_dialog.cpp000066400000000000000000000051741325266516600175260ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "task_dialog.h" #include #include #include #include #include #include #include #include #include "util/backports.h" namespace OpenOrienteering { TaskDialog::TaskDialog(QWidget* parent, const QString& title, const QString& text, QDialogButtonBox::StandardButtons buttons) : QDialog(parent, Qt::WindowSystemMenuHint | Qt::WindowTitleHint) { setWindowTitle(title); QLabel* text_label = nullptr; if (!text.isEmpty()) text_label = new QLabel(text); button_box = nullptr; if (buttons != QDialogButtonBox::NoButton) button_box = new QDialogButtonBox(buttons); layout = new QVBoxLayout(); if (text_label) layout->addWidget(text_label); if (button_box) layout->addWidget(button_box); setLayout(layout); signal_mapper = new QSignalMapper(this); connect(signal_mapper, QOverload::of(&QSignalMapper::mapped), this, QOverload::of(&TaskDialog::buttonClicked)); connect(button_box, &QDialogButtonBox::clicked, this, QOverload::of(&TaskDialog::buttonClicked)); } QCommandLinkButton* TaskDialog::addCommandButton(const QString& text, const QString& description) { QCommandLinkButton* button = new QCommandLinkButton(text, description); signal_mapper->setMapping(button, button); connect(button, QOverload::of(&QAbstractButton::clicked), signal_mapper, QOverload<>::of(&QSignalMapper::map)); layout->insertWidget(layout->count() - (button_box ? 1 : 0), button); return button; } void TaskDialog::buttonClicked(QWidget* button) { buttonClicked(static_cast(button)); } void TaskDialog::buttonClicked(QAbstractButton* button) { clicked_button = button; if (button_box->buttonRole(clicked_button) == QDialogButtonBox::RejectRole) reject(); else accept(); } } // namespace OpenOrienteering mapper-0.8.1.1/src/gui/task_dialog.h000066400000000000000000000064331325266516600171720ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_UTIL_TASK_DIALOG_H #define OPENORIENTEERING_UTIL_TASK_DIALOG_H #include #include #include #include class QAbstractButton; class QCommandLinkButton; class QPushButton; class QSignalMapper; class QVBoxLayout; class QWidget; namespace OpenOrienteering { /** * Shows a dialog similar to a message box, but with the option to add * one or multiple command buttons to be able to offer more and well-described * actions to the user. * * Using this class might look like this: * TaskDialog dialog(parent, "Title", "Text", QDialogButtonBox::Abort); * QAbstractButton* do_button = dialog.addCommandButton("Do it", "description ..."); * QAbstractButton* dont_button = dialog.addCommandButton("Don't do it", "description ..."); * dialog.exec(); * if (dialog.clicked_button() == do_button) * ... * else if (dialog.clicked_button() == dont_button) * ... * else // if (dialog.clicked_button() == dialog.button(QDialogButtonBox::Abort)) * ... */ class TaskDialog : public QDialog { Q_OBJECT public: /** * Constructs a TaskDialog. * * @param parent QWidget parent for the dialog. * @param title Dialog title. * @param text Text which will be displayed in a label at the top. * @param buttons Dialog standard buttons. */ TaskDialog( QWidget* parent, const QString& title, const QString& text, QDialogButtonBox::StandardButtons buttons = QDialogButtonBox::NoButton ); /** * Adds a command button to the dialog. Returns a pointer to the created button. * * @param text The action text, will be emphasized * @param description Additional description text for the action, optional */ QCommandLinkButton* addCommandButton( const QString& text, const QString& description ); /** After the dialog has been shown, returns the button which * was clicked by the user. */ inline QAbstractButton* clickedButton() const { return clicked_button; } /** Returns a button pointer for a specific standard dialog button. * Use this for comparing the pointer with the result of clickedButton(). */ inline QPushButton* button(QDialogButtonBox::StandardButton which) const { return button_box->button(which); } private slots: void buttonClicked(QWidget* button); void buttonClicked(QAbstractButton* button); private: QAbstractButton* clicked_button; QVBoxLayout* layout; QDialogButtonBox* button_box; QSignalMapper* signal_mapper; }; } // namespace OpenOrienteering #endif mapper-0.8.1.1/src/gui/text_browser_dialog.cpp000066400000000000000000000110511325266516600213020ustar00rootroot00000000000000/* * Copyright 2012, 2013, 2014 Thomas Schöps * Copyright 2012-2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "text_browser_dialog.h" #include #include #include #include // IWYU pragma: keep #include #include #include #include "gui/widgets/text_browser.h" #include "util/backports.h" namespace OpenOrienteering { TextBrowserDialog::TextBrowserDialog(QWidget* parent) : QDialog(parent) , text_browser(new TextBrowser()) { if (parent) { setWindowModality(Qt::WindowModal); } QVBoxLayout* layout = new QVBoxLayout(); setLayout(layout); int left, top, right, bottom; layout->getContentsMargins(&left, &top, &right, &bottom); layout->setContentsMargins(0, 0, 0, 0); layout->setSpacing(0); text_browser->setOpenExternalLinks(true); layout->addWidget(text_browser); QHBoxLayout* buttons_layout = new QHBoxLayout(); buttons_layout->setContentsMargins(left, top, right, bottom); QPushButton* back_button = new QPushButton(QIcon(QStringLiteral(":/images/arrow-left.png")), QApplication::translate("QFileDialog", "Back")); back_button->setEnabled(false); buttons_layout->addWidget(back_button); buttons_layout->addStretch(1); QPushButton* close_button = new QPushButton(QApplication::translate("QPlatformTheme", "Close")); close_button->setDefault(true); buttons_layout->addWidget(close_button); layout->addLayout(buttons_layout); connect(text_browser, &QTextBrowser::sourceChanged, this, &TextBrowserDialog::sourceChanged); connect(text_browser, &QTextBrowser::textChanged, this, &TextBrowserDialog::updateWindowTitle); connect(text_browser, &QTextBrowser::backwardAvailable, back_button, &TextBrowserDialog::setEnabled); connect(text_browser, QOverload::of(&QTextBrowser::highlighted), this, &TextBrowserDialog::highlighted); connect(back_button, &QPushButton::clicked, text_browser, &QTextBrowser::backward); connect(close_button, &QPushButton::clicked, this, &TextBrowserDialog::accept); #if defined(Q_OS_ANDROID) QScroller::grabGesture(text_browser->viewport(), QScroller::TouchGesture); // Disable selection, so that it doesn't interfere with scrolling text_browser->setTextInteractionFlags(Qt::TextInteractionFlags(Qt::TextBrowserInteraction) & ~Qt::TextSelectableByMouse); // Note: Only the above combination of QScroller::LeftMouseButtonGesture // and ~Qt::TextSelectableByMouse seems to achieve the desired behaviour // (touch-scrolling without selecting text.) setWindowState((windowState() & ~(Qt::WindowMinimized | Qt::WindowFullScreen)) | Qt::WindowMaximized); #endif } TextBrowserDialog::TextBrowserDialog(const QUrl& initial_url, QWidget* parent) : TextBrowserDialog(parent) { text_browser->setSource(initial_url); text_browser->document()->adjustSize(); // needed for sizeHint() } TextBrowserDialog::TextBrowserDialog(const QString& text, QWidget* parent) : TextBrowserDialog(parent) { text_browser->setText(text); text_browser->document()->adjustSize(); // needed for sizeHint() } TextBrowserDialog::~TextBrowserDialog() = default; QSize TextBrowserDialog::sizeHint() const { QSize size = text_browser->document()->size().toSize(); if (text_browser->verticalScrollBar()) size.rwidth() += text_browser->verticalScrollBar()->width(); return size; } void TextBrowserDialog::sourceChanged(const QUrl&) { ; // Nothing, to be overriden in subclasses } void TextBrowserDialog::updateWindowTitle() { setWindowTitle(text_browser->documentTitle()); } void TextBrowserDialog::highlighted(const QString& link) { if (link.isEmpty()) { QToolTip::hideText(); } else { /// @todo: Position near mouse pointer auto tooltip_pos = pos() + text_browser->pos(); tooltip_pos.ry() += text_browser->height(); QToolTip::showText(tooltip_pos, link, this, {}); } } } // namespace OpenOrienteering mapper-0.8.1.1/src/gui/text_browser_dialog.h000066400000000000000000000043521325266516600207550ustar00rootroot00000000000000/* * Copyright 2012, 2013, 2014 Thomas Schöps * Copyright 2012-2015, 2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_TEXT_BROWSER_DIALOG_H #define OPENORIENTEERING_TEXT_BROWSER_DIALOG_H #include #include #include #include class QWidget; class QTextBrowser; class QUrl; namespace OpenOrienteering { /** * @brief A dialog for basic browsing of HTML pages. */ class TextBrowserDialog : public QDialog { Q_OBJECT protected: explicit TextBrowserDialog(QWidget* parent = nullptr); public: /** * @brief Construct a new dialog and loads the text from the initial_url. */ TextBrowserDialog(const QUrl& initial_url, QWidget* parent = nullptr); /** * @brief Construct a new dialog and sets the given text. */ TextBrowserDialog(const QString& text, QWidget* parent = nullptr); ~TextBrowserDialog() override; protected slots: /** * @brief Sets custom HTML content when the URL identifies the first page. */ virtual void sourceChanged(const QUrl& url); /** * @brief Updates the window title from the current document title. */ virtual void updateWindowTitle(); /** * @brief Displays a tooltip showing the link if is an external document. */ void highlighted(const QString& link); protected: /** * @brief Returns a size hint based on the text browser's content. */ QSize sizeHint() const override; /** * @brief The TextBrowser is the main widget of this dialog. */ QTextBrowser* const text_browser; }; } // namespace OpenOrienteering #endif mapper-0.8.1.1/src/gui/touch_cursor.cpp000066400000000000000000000137521325266516600177650ustar00rootroot00000000000000/* * Copyright 2013 Thomas Schöps * Copyright 2015 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "touch_cursor.h" #include #include #include "gui/map/map_widget.h" #include "gui/util_gui.h" namespace OpenOrienteering { // TODO: convert these to settings const float touch_pos_offset_mm = 25; const float control_ring_radius_mm = 9.5f; TouchCursor::TouchCursor(MapWidget* map_widget) : visible(false) , left_button_pressed(false) , last_pressed_button(NoButton) , map_widget(map_widget) { // nothing } void TouchCursor::mousePressEvent(QMouseEvent* event) { if (event->button() != Qt::LeftButton) return; last_touch_pos = event->pos(); first_move_event_received = false; ControlID control_id = NoButton; if (!visible || !touchedControl(event->pos(), &control_id)) { // Jump to position updateMapWidget(false); QPoint cursor_pos = event->pos() - QPoint(0, touchPosOffsetPx()); last_cursor_pos = cursor_pos; cursor_coord = map_widget->viewportToMapF(cursor_pos); visible = true; updateMapWidget(false); *event = QMouseEvent( QEvent::MouseMove, cursor_pos, Qt::NoButton, event->buttons() & ~Qt::LeftButton, event->modifiers()); last_pressed_button = NoButton; } else if (control_id == LeftButton) { *event = QMouseEvent( QEvent::MouseButtonPress, map_widget->mapToViewport(cursor_coord), event->button(), event->buttons(), event->modifiers()); left_button_pressed = true; last_pressed_button = LeftButton; last_cursor_pos = map_widget->mapToViewport(cursor_coord); } } bool TouchCursor::mouseMoveEvent(QMouseEvent* event) { if (!(event->buttons() & Qt::LeftButton)) return false; if (last_pressed_button != NoButton && !first_move_event_received) { first_move_event_received = true; last_touch_pos = event->pos(); return false; } updateMapWidget(true); QPointF cursor_pos; if (last_pressed_button == LeftButton) cursor_pos = last_cursor_pos + (event->pos() - last_touch_pos); else cursor_pos = event->pos() - QPoint(0, touchPosOffsetPx()); last_touch_pos = event->pos(); last_cursor_pos = cursor_pos; cursor_coord = map_widget->viewportToMapF(cursor_pos); *event = QMouseEvent( QEvent::MouseMove, cursor_pos, left_button_pressed ? event->button() : Qt::NoButton, left_button_pressed ? event->buttons() : (event->buttons() & ~Qt::LeftButton), event->modifiers()); updateMapWidget(true); return true; } bool TouchCursor::mouseReleaseEvent(QMouseEvent* event) { if (event->button() != Qt::LeftButton) return true; if (left_button_pressed) { *event = QMouseEvent( QEvent::MouseButtonRelease, map_widget->mapToViewport(cursor_coord), event->button(), event->buttons(), event->modifiers()); left_button_pressed = false; return true; } else return false; } bool TouchCursor::mouseDoubleClickEvent(QMouseEvent* event) { if (last_pressed_button == LeftButton) { *event = QMouseEvent( QEvent::MouseButtonDblClick, map_widget->mapToViewport(cursor_coord), event->button(), event->buttons(), event->modifiers()); return true; } else return false; } void TouchCursor::paint(QPainter* painter) { if (!visible) return; QPointF cursor_pos = map_widget->mapToViewport(cursor_coord); // Draw cursor QPixmap cursor_pixmap = map_widget->cursor().pixmap(); if (!cursor_pixmap.isNull()) painter->drawPixmap(cursor_pos - map_widget->cursor().hotSpot(), cursor_pixmap); else { // TODO: better standard "cursor"? float cursor_radius = standardCursorRadiusPx(); painter->setPen(QPen(Qt::gray, controlRingStrokeRadiusPx())); painter->setBrush(Qt::NoBrush); painter->drawLine(cursor_pos - QPointF(cursor_radius, 0), cursor_pos + QPointF(cursor_radius, 0)); painter->drawLine(cursor_pos - QPointF(0, cursor_radius), cursor_pos + QPointF(0, cursor_radius)); } // Draw move handle / left button painter->setPen(QPen(Qt::gray, controlRingStrokeRadiusPx())); painter->setBrush(Qt::NoBrush); painter->drawEllipse(cursor_pos + QPointF(0, touchPosOffsetPx()), controlRingRadiusPx(), controlRingRadiusPx()); } bool TouchCursor::touchedControl(QPoint pos, TouchCursor::ControlID* out_id) { QPointF cursor_pos = map_widget->mapToViewport(cursor_coord); QPointF control_ring_center = cursor_pos + QPointF(0, touchPosOffsetPx()); QPointF dist_to_center = pos - control_ring_center; if (dist_to_center.x()*dist_to_center.x() + dist_to_center.y()*dist_to_center.y() < controlRingRadiusPx()*controlRingRadiusPx()) { *out_id = LeftButton; return true; } return false; } void TouchCursor::updateMapWidget(bool delayed) { QRectF fake_rect = QRectF(cursor_coord.x(), cursor_coord.y(), 0.0001f, 0.0001f); float pixel_border = qMax(touchPosOffsetPx() + controlRingRadiusPx(), standardCursorRadiusPx()) + controlRingStrokeRadiusPx() + 1; if (delayed) map_widget->updateDrawingLater(fake_rect, pixel_border); else map_widget->updateDrawing(fake_rect, pixel_border); } float TouchCursor::touchPosOffsetPx() const { return Util::mmToPixelLogical(touch_pos_offset_mm); } float TouchCursor::controlRingRadiusPx() const { return Util::mmToPixelLogical(control_ring_radius_mm); } float TouchCursor::controlRingStrokeRadiusPx() const { return Util::mmToPixelLogical(0.5f); } float TouchCursor::standardCursorRadiusPx() const { return Util::mmToPixelLogical(1.5f); } } // namespace OpenOrienteering mapper-0.8.1.1/src/gui/touch_cursor.h000066400000000000000000000056211325266516600174260ustar00rootroot00000000000000/* * Copyright 2013 Thomas Schöps * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_TOUCH_CURSOR_H #define OPENORIENTEERING_TOUCH_CURSOR_H #include #include #include "core/map_coord.h" class QMouseEvent; class QPainter; namespace OpenOrienteering { class MapWidget; /** * Handles drawing and controlling a helper cursor inside the map widget for the mobile UI. */ class TouchCursor { public: /** List of IDs for controls attached to the cursor. */ enum ControlID { LeftButton = 0, NoButton }; /** Constructs a touch cursor for a map widget. */ TouchCursor(MapWidget* map_widget); /** * Notifies the cursor of the event, possibly modifying it. * Attention: the event type may be changed to a mouse move event. */ void mousePressEvent(QMouseEvent* event); /** * Notifies the cursor of the event, possibly modifying it. * Returns true if the map widget should further process event. */ bool mouseMoveEvent(QMouseEvent* event); /** * Notifies the cursor of the event, possibly modifying it. * Returns true if the map widget should further process event. */ bool mouseReleaseEvent(QMouseEvent* event); /** * Notifies the cursor of the event, possibly modifying it. * Returns true if the map widget should further process event. */ bool mouseDoubleClickEvent(QMouseEvent* event); /** Paints the cursor. */ void paint(QPainter* painter); /** Issues a (delayed) redraw of the cursor. */ void updateMapWidget(bool delayed); private: /** * Checks if a touch at pos is inside a control. If yes, returns true and * sets out_id to the ID of the touched control. */ bool touchedControl(QPoint pos, ControlID* out_id); /** Returns the touch point offset from the cursor in pixels. */ float touchPosOffsetPx() const; /** Returns the radius of the control ring in pixels */ float controlRingRadiusPx() const; float controlRingStrokeRadiusPx() const; float standardCursorRadiusPx() const; bool visible; bool left_button_pressed; bool first_move_event_received; ControlID last_pressed_button; MapCoordF cursor_coord; QPointF last_cursor_pos; QPoint last_touch_pos; MapWidget* map_widget; }; } // namespace OpenOrienteering #endif mapper-0.8.1.1/src/gui/util_gui.cpp000066400000000000000000000246371325266516600170730ustar00rootroot00000000000000/* * Copyright 2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "util_gui.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mapper_config.h" #include "settings.h" #include "gui/text_browser_dialog.h" // IWYU pragma: keep namespace OpenOrienteering { DoubleValidator::DoubleValidator(double bottom, double top, QObject* parent, int decimals) : QDoubleValidator(bottom, top, decimals, parent) { // Don't cause confusion, accept only English formatting setLocale(QLocale(QLocale::English)); } DoubleValidator::~DoubleValidator() = default; QValidator::State DoubleValidator::validate(QString& input, int& pos) const { // Transform thousands group separators into decimal points to avoid confusion input.replace(QLatin1Char(','), QLatin1Char('.')); return QDoubleValidator::validate(input, pos); } namespace Util { // Implementation moved to settings.cpp qreal mmToPixelPhysical(qreal millimeters); // Implementation moved to settings.cpp qreal pixelToMMPhysical(qreal pixels); // Implementation moved to settings.cpp qreal mmToPixelLogical(qreal millimeters); // Implementation moved to settings.cpp qreal pixelToMMLogical(qreal pixels); // Implementation moved to settings.cpp bool isAntialiasingRequired(qreal ppi); // Implementation moved to settings.cpp bool isAntialiasingRequired(); void showHelp(QWidget* dialog_parent, const char* filename_latin1, const char* anchor_latin1) { showHelp(dialog_parent, QString::fromLatin1(filename_latin1) + QLatin1Char('#') + QString::fromLatin1(anchor_latin1)); } void showHelp(QWidget* dialog_parent, const char* file_and_anchor_latin1) { showHelp(dialog_parent, QString::fromLatin1(file_and_anchor_latin1)); } void showHelp(QWidget* dialog_parent, const QString& file_and_anchor) { #if defined(Q_OS_ANDROID) const QString manual_path = QLatin1String("doc:/manual/") + file_and_anchor; const QUrl help_url = QUrl::fromLocalFile(manual_path); TextBrowserDialog help_dialog(help_url, dialog_parent); help_dialog.exec(); return; #else const auto base_url = QLatin1String("qthelp://" MAPPER_HELP_NAMESPACE "/manual/"); static QProcess assistant_process; if (assistant_process.state() == QProcess::Running) { QString command = QLatin1String("setSource ") + base_url + file_and_anchor + QLatin1Char('\n'); assistant_process.write(command.toLatin1()); } else { auto help_collection = QFileInfo{ QString::fromUtf8("doc:Mapper " APP_VERSION " Manual.qhc") }; auto compiled_help = QFileInfo{ QString::fromUtf8("doc:Mapper " APP_VERSION " Manual.qch") }; if (!help_collection.exists() || !compiled_help.exists()) { QMessageBox::warning(dialog_parent, QApplication::translate("OpenOrienteering::Util", "Error"), QApplication::translate("OpenOrienteering::Util", "Failed to locate the help files.")); return; } #if defined(Q_OS_MACOS) const auto assistant = QString::fromLatin1("Assistant"); #else const auto assistant = QString::fromLatin1("assistant"); #endif auto assistant_path = QStandardPaths::findExecutable(assistant, { QCoreApplication::applicationDirPath() }); if (assistant_path.isEmpty()) assistant_path = QStandardPaths::findExecutable(assistant); if (assistant_path.isEmpty()) { QMessageBox::warning(dialog_parent, QApplication::translate("OpenOrienteering::Util", "Error"), QApplication::translate("OpenOrienteering::Util", "Failed to locate the help browser (\"Qt Assistant\").")); return; } // Try to start the Qt Assistant process QStringList args; args << QLatin1String("-collectionFile") << QDir::toNativeSeparators(help_collection.absoluteFilePath()) << QLatin1String("-showUrl") << (base_url + file_and_anchor) << QLatin1String("-enableRemoteControl"); if ( QGuiApplication::platformName() == QLatin1String("xcb") || QGuiApplication::platformName().isEmpty() ) { // Use the modern 'fusion' style instead of the default "windows" // style on X11. args << QLatin1String("-style") << QLatin1String("fusion"); } #if defined(Q_OS_LINUX) auto env = QProcessEnvironment::systemEnvironment(); env.insert(QLatin1String("QT_SELECT"), QLatin1String("5")); // #541 env.insert(QLatin1String("LANG"), Settings::getInstance().getSetting(Settings::General_Language).toString()); assistant_process.setProcessEnvironment(env); #endif assistant_process.start(assistant_path, args); // FIXME: Calling waitForStarted() from the main thread might cause the user interface to freeze. if (!assistant_process.waitForStarted()) { QMessageBox msg_box; msg_box.setIcon(QMessageBox::Warning); msg_box.setWindowTitle(QApplication::translate("OpenOrienteering::Util", "Error")); msg_box.setText(QApplication::translate("OpenOrienteering::Util", "Failed to launch the help browser (\"Qt Assistant\").")); msg_box.setStandardButtons(QMessageBox::Ok); auto details = assistant_process.readAllStandardError(); if (! details.isEmpty()) msg_box.setDetailedText(QString::fromLocal8Bit(details)); msg_box.exec(); } } #endif } QString makeWhatThis(const char* reference_latin1) { //: This "See more" is displayed as a link to the manual in What's-this tooltips. return QStringLiteral("%2").arg( QString::fromLatin1(reference_latin1), QApplication::translate("OpenOrienteering::Util", "See more...") ); } QString InputProperties::unit() { return QCoreApplication::translate("OpenOrienteering::UnitOfMeasurement", "mm", "millimeters"); } QString InputProperties::unit() { return QCoreApplication::translate("OpenOrienteering::UnitOfMeasurement", "m", "meters"); } QLabel* Headline::create(const QString& text) { return new QLabel(QLatin1String("") + text + QLatin1String("")); } QLabel* Headline::create(const char* text_utf8) { return create(QString::fromUtf8(text_utf8)); } QSpacerItem* SpacerItem::create(const QWidget* widget) { const int spacing = widget->style()->pixelMetric(QStyle::PM_LayoutTopMargin); return new QSpacerItem(spacing, spacing); } namespace SpinBox { #ifndef NDEBUG /** * Returns the maximum number of digits in a spinbox which is regarded * as normal. Exceedings this number in Util::SpinBox::create() will * print a runtime warning in development builds. */ constexpr int max_digits() { return 13; } #endif QSpinBox* create(int min, int max, const QString &unit, int step) { auto box = new QSpinBox(); box->setRange(min, max); const auto space = QLatin1Char{' '}; if (unit.startsWith(space)) box->setSuffix(unit); else if (unit.length() > 0) box->setSuffix(space + unit); if (step > 0) box->setSingleStep(step); #ifndef NDEBUG if (box->locale().toString(min).remove(box->locale().groupSeparator()).length() > max_digits()) qDebug().nospace() << "WARNING: Util::SpinBox::create() will create a very large widget because of min=" << box->locale().toString(min); if (box->locale().toString(max).remove(box->locale().groupSeparator()).length() > max_digits()) qDebug().nospace() << "WARNING: Util::SpinBox::create() will create a very large widget because of max=" << box->locale().toString(max); #endif return box; } QDoubleSpinBox* create(int decimals, double min, double max, const QString &unit, double step) { auto box = new QDoubleSpinBox(); box->setDecimals(decimals); box->setRange(min, max); const auto space = QLatin1Char{' '}; if (unit.startsWith(space)) box->setSuffix(unit); else if (unit.length() > 0) box->setSuffix(space + unit); if (step > 0.0) box->setSingleStep(step); else { switch (decimals) { case 0: box->setSingleStep(1.0); break; case 1: box->setSingleStep(0.1); break; default: box->setSingleStep(5.0 * pow(10.0, -decimals)); } } #ifndef NDEBUG if (box->textFromValue(min).remove(box->locale().groupSeparator()).length() > max_digits()) qDebug().nospace() << "WARNING: Util::SpinBox::create() will create a very large widget because of min=" << box->locale().toString(min, 'f', decimals); if (box->textFromValue(max).remove(box->locale().groupSeparator()).length() > max_digits()) qDebug().nospace() << "WARNING: Util::SpinBox::create() will create a very large widget because of max=" << box->locale().toString(max, 'f', decimals); #endif return box; } } namespace TristateCheckbox { void setDisabledAndChecked(QCheckBox* checkbox, bool checked) { Q_ASSERT(checkbox); checkbox->setEnabled(false); checkbox->setTristate(true); checkbox->setCheckState(checked ? Qt::PartiallyChecked : Qt::Unchecked); } void setEnabledAndChecked(QCheckBox* checkbox, bool checked) { Q_ASSERT(checkbox); checkbox->setEnabled(true); checkbox->setChecked(checked); checkbox->setTristate(false); } } QString plainText(QString maybe_markup) { if (maybe_markup.contains(QLatin1Char('<'))) { QTextDocument doc; doc.setHtml(maybe_markup); maybe_markup = doc.toPlainText(); } return maybe_markup; } } // namespace Util } // namespace OpenOrienteering mapper-0.8.1.1/src/gui/util_gui.h000066400000000000000000000205451325266516600165320ustar00rootroot00000000000000/* * Copyright 2012, 2013, 2015, 2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_UTIL_GUI_H #define OPENORIENTEERING_UTIL_GUI_H #include #include #include #include class QCheckBox; class QDoubleSpinBox; class QLabel; class QObject; class QSpacerItem; class QSpinBox; class QWidget; namespace OpenOrienteering { class MapCoordF; /** Double validator for line edit widgets, * ensures that only valid doubles can be entered. */ class DoubleValidator : public QDoubleValidator // clazy:exclude=missing-qobject-macro { public: DoubleValidator(double bottom, double top = 10e10, QObject* parent = nullptr, int decimals = 20); ~DoubleValidator() override; State validate(QString& input, int& pos) const override; }; /** * A collection of GUI utility functions. */ namespace Util { /** * Converts millimeters to pixels using the physical dpi setting of * Mapper's settings. This should be used to calculate sizes of map elements. * @sa mmToPixelLogical() */ qreal mmToPixelPhysical(qreal millimeters); /** Inverse of mmToPixelPhysical(). */ qreal pixelToMMPhysical(qreal pixels); /** * Converts millimeters to pixels using the "logical" dpi setting of * the operating system. This should be used to calculate sizes of UI * elements. * @sa mmToPixelPhysical() */ qreal mmToPixelLogical(qreal millimeters); /** Inverse of mmToPixelLogical(). */ qreal pixelToMMLogical(qreal pixels); /** Returns true for low-dpi screens, false for high-dpi screens. */ bool isAntialiasingRequired(); /** Returns true for low-dpi screens, false for high-dpi screens. */ bool isAntialiasingRequired(qreal ppi); /** * Show the manual in Qt assistant. * * @param filename_latin1 the name of the manual page html file * @param anchor_latin1 the anchor in the specified file to jump to */ void showHelp(QWidget* dialog_parent, const char* filename_latin1, const char* anchor_latin1); /** * Show the manual in Qt assistant. * * The anchor may be left out or given with the filename. * * @param file_and_anchor_latin1 the name of the manual page html file, optionally including an anchor */ void showHelp(QWidget* dialog_parent, const char* file_and_anchor_latin1 = "index.html"); /** * Show the manual in Qt assistant. * * The anchor may be left out or given with the filename. * * @param file_and_anchor the name of the manual page html file, optionally including an anchor */ void showHelp(QWidget* dialog_parent, const QString& file_and_anchor); /** * Creates a What's-this text "See more" linking to the given page and * fragment in the manual. */ QString makeWhatThis(const char* reference_latin1); /** * Provides information about the properties of Mapper types * for the purpose of customizing input widgets. * * The generic class is empty. * Template specializations provide the actual values. * See InputProperties for an example. */ template< class T > struct InputProperties { // intentionally left empty }; /** * Provides information about the properties of MapCoordF * for the purpose of customizing input widgets. */ template< > struct InputProperties< MapCoordF > { /** The underlying fundamental type. */ typedef double basetype; /** The minimum input value. */ constexpr static double min() noexcept { return -99999999.99; } /** The maximum input value. */ constexpr static double max() noexcept { return +99999999.99; } /** The spinbox step width. */ constexpr static double step() noexcept { return 1.0; } /** The number of decimals. */ constexpr static int decimals() noexcept { return 2; } /** The unit of measurement, translated in context UnitOfMeasurement. */ static QString unit(); }; /** Identifies the type double representing real meters */ struct RealMeters { // intentionally left empty }; /** * Provides information about the type double representing real meters * for the purpose of customizing input widgets. */ template< > struct InputProperties< RealMeters > { /** The underlying fundamental type. */ typedef double basetype; /** The minimum input value. */ static constexpr double min() noexcept { return -99999999.99; } /** The maximum input value. */ constexpr static double max() noexcept { return +99999999.99; } /** The spinbox step width. */ constexpr static double step() noexcept { return 1.0; } /** The number of decimals. */ constexpr static int decimals() noexcept { return 2; } /** The unit of measurement, translated in context UnitOfMeasurement. */ static QString unit(); }; namespace Headline { /** * Creates a QLabel which is styled as a headline. * * This headline is intended for use in dialogs. */ QLabel* create(const QString& text); /** * Creates a QLabel which is styled as a headline. * * This headline is intended for use in dialogs. */ QLabel* create(const char* text_utf8); } namespace SpacerItem { /** * Creates a QSpacerItem which takes up a style dependent width * and height. * * This spacer item is intended for use with QFormLayout which * does not offer a direct mean for extra spacing. */ QSpacerItem* create(const QWidget* widget); } namespace SpinBox { /** * Creates and initializes a QSpinBox. * * This method allows to initialize the most frequent options of * QSpinBox in a single call: * the lower and upper bound of the valid range, * the unit of measurement (optional), * the step width of the spinbox buttons (optional). */ QSpinBox* create(int min, int max, const QString &unit = {}, int step = 0); /** * Creates and initializes a QDoubleSpinBox. * * This method allows to initialize the most frequent options of * QDoubleSpinBox in a single call: * the number of decimals, * the lower and upper bound of the valid range, * the unit of measurement (optional), * the step width of the spinbox buttons (optional; dependent on * the number of decimals if not specified). */ QDoubleSpinBox* create(int decimals, double min, double max, const QString &unit = {}, double step = 0.0); /** * Creates and initializes a QDoubleSpinBox. * * This method allows to initialize the most frequent options of * QDoubleSpinBox in a single call, determining the actual properties * via InputProperties. */ template< class T > QDoubleSpinBox* create() { typedef InputProperties P; return create(P::decimals(), P::min(), P::max(), P::unit(), P::step()); } /** * @deprecated Transitional method. * * Creates and initializes a QDoubleSpinBox. * * This method allows to initialize the most frequent options of * QDoubleSpinBox in a single call, determining the actual properties * via InputProperties. * * The unit of measurement is taken from the actual parameter. This is * meant to support the transition from code where the translation of * units still exists in the context of the client code, instead of * in the context UnitOfMeasurement. */ template< class T > QDoubleSpinBox* create(const QString& unit) { typedef InputProperties P; return create(P::decimals(), P::min(), P::max(), unit, P::step()); } } namespace TristateCheckbox { void setDisabledAndChecked(QCheckBox* checkbox, bool checked); void setEnabledAndChecked(QCheckBox* checkbox, bool checked); } /** * Remove any HTML markup from the input text. * * \see QTextDocument::toPlainText */ QString plainText(QString maybe_markup); } } // namespace OpenOrienteering #endif mapper-0.8.1.1/src/gui/widgets/000077500000000000000000000000001325266516600162005ustar00rootroot00000000000000mapper-0.8.1.1/src/gui/widgets/action_grid_bar.cpp000066400000000000000000000175701325266516600220240ustar00rootroot00000000000000/* * Copyright 2013 Thomas Schöps * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "action_grid_bar.h" #include #include #include #include #include #include #include "settings.h" #include "gui/util_gui.h" namespace OpenOrienteering { ActionGridBar::ActionGridBar(Direction direction, int rows, QWidget* parent) : QWidget(parent) { this->direction = direction; this->rows = rows; next_id = 0; // Will be calculated in resizeEvent() cols = 1; if (direction == Horizontal) setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); else setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding); // Create overflow action overflow_action = new QAction(QIcon(QString::fromLatin1(":/images/three-dots.png")), tr("Show remaining items"), this); connect(overflow_action, &QAction::triggered, this, &ActionGridBar::overflowActionClicked); overflow_button = nullptr; overflow_menu = new QMenu(this); include_overflow_from_list.push_back(this); } int ActionGridBar::getRows() const { return rows; } int ActionGridBar::getCols() const { return cols; } void ActionGridBar::addAction(QAction* action, int row, int col, int row_span, int col_span, bool at_end) { // Determine icon size (important for high-dpi screens). // Use a somewhat smaller size than what would cover the whole icon to // account for the (assumed) button border. QSize icon_size = getIconSize(row_span, col_span); // Ensure that the icon of the given action is big enough. If not, scale it up. // NOTE: Here, row_span == col_span is assumed. QIcon icon = action->icon(); QPixmap pixmap = icon.pixmap(icon_size, QIcon::Normal, QIcon::Off); if (! pixmap.isNull() && pixmap.width() < icon_size.width()) { pixmap = pixmap.scaled(icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); icon.addPixmap(pixmap); action->setIcon(icon); } // Add the item GridItem newItem; newItem.id = next_id ++; newItem.action = action; newItem.button = new QToolButton(); newItem.button->setDefaultAction(action); newItem.button->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); newItem.button->setAutoRaise(true); newItem.button->setIconSize(icon_size); newItem.button_hidden = false; newItem.row = row; newItem.col = col; newItem.row_span = row_span; newItem.col_span = col_span; newItem.at_end = at_end; items.push_back(newItem); // If this is the overflow action, remember the button. if (action == overflow_action) overflow_button = newItem.button; } void ActionGridBar::addActionAtEnd(QAction* action, int row, int col, int row_span, int col_span) { addAction(action, row, col, row_span, col_span, true); } QSize ActionGridBar::getIconSize(int row_span, int col_span) const { int icon_size_pixel_row = qRound(row_span * Util::mmToPixelLogical(Settings::getInstance().getSettingCached(Settings::ActionGridBar_ButtonSizeMM).toFloat())); int icon_size_pixel_col = qRound(col_span * Util::mmToPixelLogical(Settings::getInstance().getSettingCached(Settings::ActionGridBar_ButtonSizeMM).toFloat())); const int button_icon_size_row = icon_size_pixel_row - 12; const int button_icon_size_col = icon_size_pixel_col - 12; if (direction == Horizontal) return QSize(button_icon_size_row, button_icon_size_col); else return QSize(button_icon_size_col, button_icon_size_row); } QAction* ActionGridBar::getOverflowAction() const { return overflow_action; } void ActionGridBar::setToUseOverflowActionFrom(ActionGridBar* other_bar) { other_bar->include_overflow_from_list.push_back(this); } QToolButton* ActionGridBar::getButtonForAction(QAction* action) { for (auto& item : items) { if (item.action == action) return item.button_hidden ? nullptr : item.button; } return nullptr; } QSize ActionGridBar::sizeHint() const { int height_px = Util::mmToPixelLogical(rows * Settings::getInstance().getSettingCached(Settings::ActionGridBar_ButtonSizeMM).toFloat()); if (direction == Horizontal) return QSize(100, height_px); else return QSize(height_px, 100); } bool ActionGridBar::compareItemPtrId(ActionGridBar::GridItem* a, ActionGridBar::GridItem* b) { return a->id < b->id; } void ActionGridBar::overflowActionClicked() { overflow_menu->clear(); for (const auto source_bar : include_overflow_from_list) { for (const auto hidden_item : source_bar->hidden_items) overflow_menu->addAction(hidden_item->action); } if (overflow_button) overflow_menu->popup(overflow_button->mapToGlobal(QPoint(0, overflow_button->height()))); else overflow_menu->popup(mapToGlobal(QPoint(0, height()))); } void ActionGridBar::resizeEvent(QResizeEvent* event) { hidden_items.clear(); int length_px = (direction == Horizontal) ? width() : height(); float length_millimeters = Util::pixelToMMLogical(length_px); cols = qMax(1, qFloor(length_millimeters / Settings::getInstance().getSettingCached(Settings::ActionGridBar_ButtonSizeMM).toFloat())); delete layout(); QGridLayout* new_layout = new QGridLayout(this); new_layout->setContentsMargins(0, 0, 0, 0); new_layout->setSpacing(0); for (size_t i = 0, end = items.size(); i < end; ++ i) { GridItem& item = items[i]; int resulting_col = item.at_end ? (cols - 1 - item.col) : item.col; bool hidden = item.row >= rows || item.col >= cols; if (! hidden) { // Check for collisions with other items for (size_t k = 0; k < items.size(); ++ k) { if (i == k) continue; GridItem& other = items[k]; int resulting_col_other = other.at_end ? (cols - 1 - other.col) : other.col; if (item.row == other.row && resulting_col == resulting_col_other) { // Check which item "wins" this spot and which will be hidden if (item.at_end == other.at_end) qDebug("Warning: two items set to same position in ActionGridBar, this case is not handled!"); if ((item.at_end && resulting_col <= cols / 2) || (! item.at_end && resulting_col > cols / 2)) { hidden = true; break; } } } } if (hidden) { item.button->hide(); item.button_hidden = true; hidden_items.push_back(&item); continue; } if (direction == Horizontal) { new_layout->addWidget( item.button, item.row, resulting_col, qMin(item.row_span, rows - item.row), qMin(item.col_span, cols - resulting_col) ); } else { new_layout->addWidget( item.button, resulting_col, item.row, qMin(item.col_span, cols - resulting_col), qMin(item.row_span, rows - item.row) ); } if (item.button_hidden) { item.button->setVisible(true); item.button_hidden = false; } item.button->updateGeometry(); } // Set row/col strech. The first and last row/col acts as margin in case // the available area is not a multiple of the button size. if (direction == Horizontal) { for (int i = 0; i < cols; ++ i) new_layout->setColumnStretch(i, 1); for (int i = 0; i < rows; ++ i) new_layout->setRowStretch(i, 1); } else { for (int i = 0; i < cols; ++ i) new_layout->setRowStretch(i, 1); for (int i = 0; i < rows; ++ i) new_layout->setColumnStretch(i, 1); } overflow_action->setEnabled(! hidden_items.empty()); std::sort(hidden_items.begin(), hidden_items.end(), compareItemPtrId); event->accept(); } } // namespace OpenOrienteering mapper-0.8.1.1/src/gui/widgets/action_grid_bar.h000066400000000000000000000071311325266516600214610ustar00rootroot00000000000000/* * Copyright 2013 Thomas Schöps * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_ACTION_GRID_BAR_H #define OPENORIENTEERING_ACTION_GRID_BAR_H #include #include #include #include class QAction; class QMenu; class QResizeEvent; class QToolButton; namespace OpenOrienteering { /** * A toolbar with a grid layout, whose button size depends on the ppi. */ class ActionGridBar : public QWidget { Q_OBJECT public: enum Direction { Horizontal = 0, Vertical }; /** * Constructs a new ActionGridBar. * * After constructions, add items and either insert the overflow action * with addAction(getOverflowAction(), ...) or set another ActionGridBar * to include the overflow items. * * @param direction Direction of the toolbar. * @param height_items Number of rows in the direction opposite to the main * toolbar direction. */ ActionGridBar(Direction direction, int height_items, QWidget* parent = nullptr); /** Returns the number of grid rows. */ int getRows() const; /** Returns the number of grid columns. */ int getCols() const; /** Adds an action to the grid. */ void addAction(QAction* action, int row, int col, int row_span = 1, int col_span = 1, bool at_end = false); /** Adds an action to the grid, starting from the opposite direction. */ void addActionAtEnd(QAction* action, int row, int col, int row_span = 1, int col_span = 1); /** Returns the size of the button icons. */ QSize getIconSize(int row_span = 1, int col_span = 1) const; /** Returns the overflow action (to be inserted into the action bar with addAction()). * The overflow action is enabled if there are items which do not fit into * the action bar. On click, it shows a list of those actions. */ QAction* getOverflowAction() const; /** Configures this bar to put its overflow actions into another bar. */ void setToUseOverflowActionFrom(ActionGridBar* other_bar); /** Finds and returns the button corresponding to the given action or nullptr * if either the action has not been inserted into the action bar, * or the button is hidden because of a collision. */ QToolButton* getButtonForAction(QAction* action); QSize sizeHint() const override; protected slots: void overflowActionClicked(); protected: struct GridItem { int id; // sequential id for sorting in overflow item chooser int row; int col; int row_span; int col_span; bool at_end; QAction* action; QToolButton* button; bool button_hidden; }; static bool compareItemPtrId(GridItem* a, GridItem* b); void resizeEvent(QResizeEvent* event) override; Direction direction; int rows; int cols; std::vector< GridItem > items; int next_id; QAction* overflow_action; QToolButton* overflow_button; QMenu* overflow_menu; std::vector< GridItem* > hidden_items; std::vector< ActionGridBar* > include_overflow_from_list; }; } // namespace OpenOrienteering #endif mapper-0.8.1.1/src/gui/widgets/color_dropdown.cpp000066400000000000000000000105271325266516600217430ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2015, 2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "color_dropdown.h" #include #include #include #include #include #include #include "core/map.h" #include "core/map_color.h" namespace OpenOrienteering { ColorDropDown::ColorDropDown(const Map* map, const MapColor* initial_color, bool spot_colors_only, QWidget* parent) : QComboBox(parent) , map(map) , spot_colors_only(spot_colors_only) { addItem(tr("- none -"), QVariant::fromValue(nullptr)); int icon_size = style()->pixelMetric(QStyle::PM_SmallIconSize); QPixmap pixmap(icon_size, icon_size); int initial_index = 0; int num_colors = map->getNumColors(); for (int i = 0; i < num_colors; ++i) { const MapColor* color = map->getColor(i); if (spot_colors_only && color->getSpotColorMethod() != MapColor::SpotColor) continue; if (initial_color == color) initial_index = count(); pixmap.fill(colorWithOpacity(*color)); QString name = spot_colors_only ? color->getSpotColorName() : map->translate(color->getName()); addItem(QIcon(pixmap), name, QVariant::fromValue(color)); } if (!spot_colors_only) { const int count = this->count(); if (count > 0) { insertSeparator(count); } const MapColor* color = Map::getRegistrationColor(); pixmap.fill(*color); addItem(QIcon(pixmap), color->getName(), QVariant::fromValue(color)); } setCurrentIndex(initial_index); connect(map, &Map::colorAdded, this, &ColorDropDown::onColorAdded); connect(map, &Map::colorChanged, this, &ColorDropDown::onColorChanged); connect(map, &Map::colorDeleted, this, &ColorDropDown::onColorDeleted); } ColorDropDown::~ColorDropDown() = default; const MapColor* ColorDropDown::color() const { return itemData(currentIndex()).value(); } void ColorDropDown::setColor(const MapColor* color) { setCurrentIndex(findData(QVariant::fromValue(color))); } void ColorDropDown::addColor(const MapColor* color) { if (!spot_colors_only || color->getSpotColorMethod() == MapColor::SpotColor) { int pos = 0; for (; pos < count(); ++pos) { const MapColor* c = itemData(pos).value(); if (c && c->getPriority() > color->getPriority()) break; } int icon_size = style()->pixelMetric(QStyle::PM_SmallIconSize); QPixmap pixmap(icon_size, icon_size); pixmap.fill(*color); insertItem(pos, map->translate(color->getName()), QVariant::fromValue(color)); setItemData(pos, pixmap, Qt::DecorationRole); } } void ColorDropDown::updateColor(const MapColor* color) { if (!spot_colors_only || color->getSpotColorMethod() == MapColor::SpotColor) { int pos = 0; for (; pos < count(); ++pos) { if (itemData(pos).value() == color) break; } if (pos < count()) { int icon_size = style()->pixelMetric(QStyle::PM_SmallIconSize); QPixmap pixmap(icon_size, icon_size); pixmap.fill(*color); setItemText(pos, map->translate(color->getName())); setItemData(pos, pixmap, Qt::DecorationRole); } else { addColor(color); } } else { removeColor(color); } } void ColorDropDown::removeColor(const MapColor* color) { for (int pos = 0; pos < count(); ++pos) { if (itemData(pos).value() == color) { if (currentIndex() == pos) setCurrentIndex(0); removeItem(pos); break; } } } void ColorDropDown::onColorAdded(int, const MapColor* color) { addColor(color); } void ColorDropDown::onColorChanged(int, const MapColor* color) { updateColor(color); } void ColorDropDown::onColorDeleted(int, const MapColor* color) { removeColor(color); } } // namespace OpenOrienteering mapper-0.8.1.1/src/gui/widgets/color_dropdown.h000066400000000000000000000041711325266516600214060ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2015, 2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_COLOR_DROPDOWN_H #define OPENORIENTEERING_COLOR_DROPDOWN_H #include #include class QWidget; namespace OpenOrienteering { class Map; class MapColor; /** * A combobox which lets the user select a map color. */ class ColorDropDown : public QComboBox { Q_OBJECT public: /** * Constructs a new ColorDropDown for the colors of the given map. * If spot_colors_only is true, it will only display fulltone spot colors. */ ColorDropDown(const Map* map, const MapColor* initial_color = nullptr, bool spot_colors_only = false, QWidget* parent = nullptr); /** Destructor. */ ~ColorDropDown() override; /** Returns the selected color or nullptr if no color selected. */ const MapColor* color() const; /** Sets the selection to the given color. */ void setColor(const MapColor* color); /** Adds a color to the list. */ void addColor(const MapColor* color); /** Updates a color in the list. */ void updateColor(const MapColor* color); /** Removes a color from the list. */ void removeColor(const MapColor* color); protected: void onColorAdded(int, const MapColor* color); void onColorChanged(int, const MapColor* color); void onColorDeleted(int, const MapColor* color); const Map* map; const bool spot_colors_only; }; } // namespace OpenOrienteering #endif mapper-0.8.1.1/src/gui/widgets/color_list_widget.cpp000066400000000000000000000371101325266516600224220ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2012-2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "color_list_widget.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "core/map.h" #include "core/map_color.h" #include "gui/color_dialog.h" #include "gui/main_window.h" #include "gui/util_gui.h" #include "gui/widgets/segmented_button_layout.h" #include "util/item_delegates.h" #include "util/util.h" // IWYU pragma: no_forward_declare QTableWidgetItem namespace OpenOrienteering { ColorListWidget::ColorListWidget(Map* map, MainWindow* window, QWidget* parent) : QWidget(parent) , map(map) , window(window) { react_to_changes = true; setWhatsThis(Util::makeWhatThis("color_dock_widget.html")); // Color table color_table = new QTableWidget(map->getNumColors(), 7); color_table->setEditTriggers(QAbstractItemView::SelectedClicked | QAbstractItemView::AnyKeyPressed); color_table->setSelectionMode(QAbstractItemView::SingleSelection); color_table->setSelectionBehavior(QAbstractItemView::SelectRows); color_table->verticalHeader()->setVisible(false); color_table->setHorizontalHeaderLabels(QStringList() << QString{} << tr("Name") << tr("Spot color") << tr("CMYK") << tr("RGB") << tr("K.o.") << tr("Opacity") ); color_table->setItemDelegateForColumn(0, new ColorItemDelegate(this)); color_table->setItemDelegateForColumn(6, new PercentageDelegate(this)); color_table->setColumnHidden(6, true); auto new_button_menu = new QMenu(this); (void) new_button_menu->addAction(tr("New"), this, SLOT(newColor())); duplicate_action = new_button_menu->addAction(tr("Duplicate"), this, SLOT(duplicateColor())); duplicate_action->setIcon(QIcon(QString::fromLatin1(":/images/tool-duplicate.png"))); // Buttons auto new_button = newToolButton(QIcon(QString::fromLatin1(":/images/plus.png")), tr("New")); new_button->setPopupMode(QToolButton::DelayedPopup); // or MenuButtonPopup new_button->setMenu(new_button_menu); delete_button = newToolButton(QIcon(QString::fromLatin1(":/images/minus.png")), tr("Delete")); auto add_remove_layout = new SegmentedButtonLayout(); add_remove_layout->addWidget(new_button); add_remove_layout->addWidget(delete_button); move_up_button = newToolButton(QIcon(QString::fromLatin1(":/images/arrow-up.png")), tr("Move Up")); move_up_button->setAutoRepeat(true); move_down_button = newToolButton(QIcon(QString::fromLatin1(":/images/arrow-down.png")), tr("Move Down")); move_down_button->setAutoRepeat(true); auto up_down_layout = new SegmentedButtonLayout(); up_down_layout->addWidget(move_up_button); up_down_layout->addWidget(move_down_button); // TODO: In Mapper >= 0.6, switch to ColorWidget (or generic) translation context. edit_button = newToolButton(QIcon(QString::fromLatin1(":/images/settings.png")), QApplication::translate("OpenOrienteering::MapEditorController", "&Edit").remove(QLatin1Char('&'))); edit_button->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); auto help_button = newToolButton(QIcon(QString::fromLatin1(":/images/help.png")), tr("Help")); help_button->setAutoRaise(true); // The buttons row layout auto buttons_group_layout = new QHBoxLayout(); buttons_group_layout->addLayout(add_remove_layout); buttons_group_layout->addLayout(up_down_layout); buttons_group_layout->addWidget(edit_button); buttons_group_layout->addWidget(new QLabel(QString::fromLatin1(" ")), 1); buttons_group_layout->addWidget(help_button); // The layout of all components below the table auto bottom_layout = new QVBoxLayout(); QStyleOption style_option(QStyleOption::Version, QStyleOption::SO_DockWidget); bottom_layout->setContentsMargins( style()->pixelMetric(QStyle::PM_LayoutLeftMargin, &style_option) / 2, 0, // Covered by the main layout's spacing. style()->pixelMetric(QStyle::PM_LayoutRightMargin, &style_option) / 2, style()->pixelMetric(QStyle::PM_LayoutBottomMargin, &style_option) / 2 ); bottom_layout->addLayout(buttons_group_layout); bottom_layout->addWidget(new QLabel(tr("Double-click a color value to open a dialog."))); // The main layout auto layout = new QVBoxLayout(); layout->setContentsMargins(QMargins()); layout->addWidget(color_table, 1); layout->addLayout(bottom_layout); setLayout(layout); for (int i = 0; i < map->getNumColors(); ++i) addRow(i); auto header_view = color_table->horizontalHeader(); header_view->setSectionResizeMode(QHeaderView::Interactive); header_view->resizeSections(QHeaderView::ResizeToContents); header_view->setSectionResizeMode(0, QHeaderView::Fixed); // Color header_view->resizeSection(0, 32); header_view->setSectionResizeMode(1, QHeaderView::Stretch); // Name header_view->setSectionResizeMode(1, QHeaderView::Interactive); // Spot colors header_view->setSectionResizeMode(5, QHeaderView::Fixed); // Knockout header_view->resizeSection(5, 32); header_view->setSectionsClickable(false); currentCellChange(color_table->currentRow(), 0, 0, 0); // enable / disable move color buttons // Connections connect(color_table, &QTableWidget::cellChanged, this, &ColorListWidget::cellChange); connect(color_table, &QTableWidget::currentCellChanged, this, &ColorListWidget::currentCellChange); connect(color_table, &QTableWidget::cellDoubleClicked, this, &ColorListWidget::editCurrentColor); connect(new_button, &QAbstractButton::clicked, this, &ColorListWidget::newColor); connect(delete_button, &QAbstractButton::clicked, this, &ColorListWidget::deleteColor); connect(move_up_button, &QAbstractButton::clicked, this, &ColorListWidget::moveColorUp); connect(move_down_button, &QAbstractButton::clicked, this, &ColorListWidget::moveColorDown); connect(edit_button, &QAbstractButton::clicked, this, &ColorListWidget::editCurrentColor); connect(help_button, &QAbstractButton::clicked, this, &ColorListWidget::showHelp); connect(map, &Map::colorAdded, this, &ColorListWidget::colorAdded); connect(map, &Map::colorChanged, this, &ColorListWidget::colorChanged); connect(map, &Map::colorDeleted, this, &ColorListWidget::colorDeleted); } ColorListWidget::~ColorListWidget() = default; void ColorListWidget::showEvent(QShowEvent* event) { if (!event->spontaneous()) { // Update name, because translation may be changed with new symbol set for (int i = 0, count = color_table->rowCount(); i < count; ++i) { auto color = map->getColor(i); auto item = color_table->item(i, 1); item->setText(map->translate(color->getName())); } } } QToolButton* ColorListWidget::newToolButton(const QIcon& icon, const QString& text) { auto button = new QToolButton(); button->setToolButtonStyle(Qt::ToolButtonIconOnly); button->setToolTip(text); button->setIcon(icon); button->setText(text); button->setWhatsThis(whatsThis()); return button; } void ColorListWidget::newColor() { int row = color_table->currentRow(); if (row < 0) row = color_table->rowCount(); map->addColor(new MapColor(), row); map->updateAllObjects(); editCurrentColor(); } void ColorListWidget::deleteColor() { int row = color_table->currentRow(); Q_ASSERT(row >= 0); if (row < 0) return; // In release mode // Show a warning if the color is used if (map->isColorUsedByASymbol(map->getColor(row))) { if (QMessageBox::warning(this, tr("Confirmation"), tr("The map contains symbols with this color. Deleting it will remove the color from these objects! Do you really want to do that?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) return; } map->deleteColor(row); map->setColorsDirty(); map->updateAllObjects(); } void ColorListWidget::duplicateColor() { int row = color_table->currentRow(); Q_ASSERT(row >= 0); if (row < 0) return; // In release mode auto new_color = new MapColor(*map->getColor(row)); //: Future replacement for COLOR_NAME + " (Duplicate)", for better localization. void(tr("%1 (duplicate)")); /// \todo Switch translation new_color->setName(map->translate(new_color->getName()) + tr(" (Duplicate)")); map->addColor(new_color, row); map->updateAllObjects(); editCurrentColor(); } void ColorListWidget::moveColorUp() { int row = color_table->currentRow(); Q_ASSERT(row >= 1); if (row < 1) return; // In release mode auto above_color = map->getMapColor(row - 1); auto cur_color = map->getMapColor(row); map->setColor(cur_color, row - 1); map->setColor(above_color, row); updateRow(row - 1); updateRow(row); color_table->setCurrentCell(row - 1, color_table->currentColumn()); map->setColorsDirty(); map->updateAllObjects(); } void ColorListWidget::moveColorDown() { int row = color_table->currentRow(); Q_ASSERT(row < color_table->rowCount() - 1); if (row >= color_table->rowCount() - 1) return; // In release mode auto below_color = map->getMapColor(row + 1); auto cur_color = map->getMapColor(row); map->setColor(cur_color, row + 1); map->setColor(below_color, row); updateRow(row + 1); updateRow(row); color_table->setCurrentCell(row + 1, color_table->currentColumn()); map->setColorsDirty(); map->updateAllObjects(); } // slot void ColorListWidget::editCurrentColor() { int row = color_table->currentRow(); if (row >= 0) { auto color = map->getMapColor(row); ColorDialog dialog(*map, *color, this); dialog.setWindowModality(Qt::WindowModal); int result = dialog.exec(); if (result == QDialog::Accepted) { *color = dialog.getColor(); map->setColor(color, row); // trigger colorChanged signal map->setColorsDirty(); map->updateAllObjects(); } } } void ColorListWidget::showHelp() const { Util::showHelp(window, "color_dock_widget.html"); } void ColorListWidget::cellChange(int row, int column) { if (!react_to_changes) return; react_to_changes = false; auto color = map->getMapColor(row); auto text = color_table->item(row, column)->text().trimmed(); if (column == 1) { color->setName(text); react_to_changes = true; } else if (column == 6) // Opacity { auto opacity = color_table->item(row, column)->data(Qt::DisplayRole).toFloat(); if (!qFuzzyCompare(1.0f+opacity, 1.0f+color->getOpacity())) { color->setOpacity(qBound(0.0f, opacity, 1.0f)); updateRow(row); } react_to_changes = true; } else { react_to_changes = true; return; } map->setColor(color, row); // trigger colorChanged signal map->setColorsDirty(); map->updateAllObjects(); } void ColorListWidget::currentCellChange(int current_row, int current_column, int previous_row, int previous_column) { Q_UNUSED(current_column); Q_UNUSED(previous_row); Q_UNUSED(previous_column); if (!react_to_changes) return; bool valid_row = (current_row >= 0); delete_button->setEnabled(valid_row); duplicate_action->setEnabled(valid_row); move_up_button->setEnabled(valid_row && current_row >= 1); move_down_button->setEnabled(valid_row && current_row < color_table->rowCount() - 1); edit_button->setEnabled(valid_row); } void ColorListWidget::colorAdded(int index, const MapColor* color) { Q_UNUSED(color); color_table->insertRow(index); addRow(index); if (index < color_table->rowCount() - 1) { updateRow(index + 1); } color_table->setCurrentCell(index, 1); } void ColorListWidget::colorChanged(int index, const MapColor* color) { Q_UNUSED(color); updateRow(index); } void ColorListWidget::colorDeleted(int index, const MapColor* color) { Q_UNUSED(color); color_table->removeRow(index); currentCellChange(color_table->currentRow(), color_table->currentColumn(), -1, -1); } void ColorListWidget::addRow(int row) { react_to_changes = false; QTableWidgetItem* item; for (int col = 0; col < color_table->columnCount(); ++col) { item = new QTableWidgetItem(); item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); // TODO: replace "define" with "edit" item->setToolTip(tr("Double click to define the color")); color_table->setItem(row, col, item); } #if 0 // Interfers with translation // Name item = color_table->item(row, 1); item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable); item->setToolTip(tr("Click to select the name and click again to edit.")); #endif // Opacity item = color_table->item(row, 6); item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable); react_to_changes = true; updateRow(row); } void ColorListWidget::updateRow(int row) { react_to_changes = false; const auto color = map->getColor(row); auto color_with_opacity = colorWithOpacity(*color); // Color preview auto item = color_table->item(row, 0); item->setBackground(color_with_opacity); // Name item = color_table->item(row, 1); item->setText(map->translate(color->getName())); // Spot color item = color_table->item(row, 2); item->setText(color->getSpotColorName()); switch (color->getSpotColorMethod()) { case MapColor::SpotColor: item->setData(Qt::DecorationRole, color_with_opacity); break; default: item->setData(Qt::DecorationRole, QColor(Qt::transparent)); } // CMYK item = color_table->item(row, 3); item->setToolTip(tr("Double click to define the color")); const MapColorCmyk& cmyk = color->getCmyk(); QLocale l; item->setText(QString::fromLatin1("%1/%2/%3/%4").arg( l.toString(100*cmyk.c, 'g', 3), l.toString(100*cmyk.m, 'g', 3), l.toString(100*cmyk.y, 'g', 3), l.toString(100*cmyk.k, 'g', 3))); switch (color->getCmykColorMethod()) { case MapColor::SpotColor: case MapColor::RgbColor: item->setForeground(palette().color(QPalette::Disabled, QPalette::Text)); item->setData(Qt::DecorationRole, QColor(Qt::transparent)); break; default: item->setForeground(palette().color(QPalette::Active, QPalette::Text)); item->setData(Qt::DecorationRole, colorWithOpacity(color->getCmyk(), color->getOpacity())); } // RGB item = color_table->item(row, 4); item->setText(QColor(color->getRgb()).name()); item->setToolTip(item->text()); switch (color->getRgbColorMethod()) { case MapColor::SpotColor: case MapColor::CmykColor: item->setForeground(palette().color(QPalette::Disabled, QPalette::Text)); item->setData(Qt::DecorationRole, QColor(Qt::transparent)); break; default: item->setForeground(palette().color(QPalette::Active, QPalette::Text)); item->setData(Qt::DecorationRole, colorWithOpacity(color->getRgb(), color->getOpacity())); } // Knockout item = color_table->item(row, 5); item->setCheckState(color->getKnockout() ? Qt::Checked : Qt::Unchecked); item->setForeground(palette().color(QPalette::Disabled, QPalette::Text)); // Opacity item = color_table->item(row, 6); item->setData(Qt::DisplayRole, color->getOpacity()); react_to_changes = true; } } // namespace OpenOrienteering mapper-0.8.1.1/src/gui/widgets/color_list_widget.h000066400000000000000000000050621325266516600220700ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2012, 2013, 2014, 2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_COLOR_LIST_WIDGET_H #define OPENORIENTEERING_COLOR_LIST_WIDGET_H #include #include #include class QAction; class QIcon; class QShowEvent; class QTableWidget; class QToolButton; namespace OpenOrienteering { class MainWindow; class Map; class MapColor; /** * A widget showing the list of map colors and some edit buttons at the bottom. * Enables to define new colors and edit or delete existing colors. * This widget is used inside a dock widget. */ class ColorListWidget : public QWidget { Q_OBJECT public: /** Creates a new ColorWidget for a given map and MainWindow. */ ColorListWidget(Map* map, MainWindow* window, QWidget* parent = nullptr); /** Destroys the ColorWidget. */ ~ColorListWidget() override; protected slots: void newColor(); void deleteColor(); void duplicateColor(); void moveColorUp(); void moveColorDown(); void editCurrentColor(); void showHelp() const; void cellChange(int row, int column); void currentCellChange(int current_row, int current_column, int previous_row, int previous_column); void colorAdded(int index, const MapColor* color); void colorChanged(int index, const MapColor* color); void colorDeleted(int index, const MapColor* color); protected: void showEvent(QShowEvent* event) override; QToolButton* newToolButton(const QIcon& icon, const QString& text); private: void addRow(int row); void updateRow(int row); // Color list QTableWidget* color_table; QAction* duplicate_action; // Buttons QToolButton* delete_button; QToolButton* move_up_button; QToolButton* move_down_button; QToolButton* edit_button; Map* const map; MainWindow* const window; bool react_to_changes; }; } // namespace OpenOrienteering #endif mapper-0.8.1.1/src/gui/widgets/compass_display.cpp000066400000000000000000000063071325266516600221040ustar00rootroot00000000000000/* * Copyright 2013 Thomas Schöps * Copyright 2015 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "compass_display.h" #include #include #include "gui/util_gui.h" namespace OpenOrienteering { CompassDisplay::CompassDisplay(QWidget* parent) : QWidget(parent) , azimuth(qSNaN()) , last_update_time(QTime::currentTime()) { setAttribute(Qt::WA_NoSystemBackground, true); } CompassDisplay::~CompassDisplay() { // nothing, not inlined } void CompassDisplay::setAzimuth(float azimuth_deg) { constexpr int update_interval = 200; QTime current_time = QTime::currentTime(); if (qAbs(last_update_time.msecsTo(current_time)) >= update_interval && azimuth != azimuth_deg) { last_update_time = current_time; azimuth = azimuth_deg; update(); if (auto parent = parentWidget()) parent->update(geometry()); } } QSize CompassDisplay::sizeHint() const { auto width = qRound(Util::mmToPixelLogical(20.0)); return QSize(width, width); } void CompassDisplay::showEvent(QShowEvent*) { azimuth = qSNaN(); } void CompassDisplay::hideEvent(QHideEvent*) { // nothing } void CompassDisplay::paintEvent(QPaintEvent*) { QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); auto margin = Util::mmToPixelLogical(1.0f); QRectF bounding_box = { {0,0}, size() }; bounding_box.adjust(margin, margin, -margin, -margin); QPointF midpoint = bounding_box.center(); QPointF endpoint = QPointF(midpoint.x(), bounding_box.top()); QLineF line(midpoint, endpoint); auto have_value = !qIsNaN(azimuth); // Draw alignment cue constexpr qreal max_alignment_angle = 10; if (have_value && qAbs(azimuth) < max_alignment_angle) { float alignment_factor = (max_alignment_angle - qAbs(azimuth)) / max_alignment_angle; float radius = alignment_factor * 0.25f * bounding_box.height(); painter.setPen(Qt::NoPen); painter.setBrush(Qt::green); painter.drawEllipse(line.pointAt(0.5f), radius, radius); } // Draw up marker float line_width = Util::mmToPixelLogical(0.3f); painter.setPen(QPen(Qt::white, line_width)); painter.setBrush(Qt::NoBrush); painter.drawLine(line); painter.setPen(QPen(Qt::black, line_width)); painter.drawLine(midpoint + QPointF(line_width, 0), endpoint + QPointF(line_width, 0)); painter.drawLine(midpoint + QPointF(-1 * line_width, 0), endpoint + QPointF(-1 * line_width, 0)); // Draw needle if (have_value) { painter.setPen(QPen(Qt::red, line_width)); line.setAngle(90 + azimuth); painter.drawLine(line); } } } // namespace OpenOrienteering mapper-0.8.1.1/src/gui/widgets/compass_display.h000066400000000000000000000037331325266516600215510ustar00rootroot00000000000000/* * Copyright 2013 Thomas Schöps * Copyright 2015 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_COMPASS_DISPLAY_H #define OPENORIENTEERING_COMPASS_DISPLAY_H #include #include #include #include #include class QHideEvent; class QPaintEvent; class QShowEvent; namespace OpenOrienteering { /** * A widget which displays a compass. * * By default, this widget is transparent (Qt::WA_NoSystemBackground), i.e. it * it does not draw a background. For proper background drawing, it shall not be * a child but a sibling of the background widget. */ class CompassDisplay : public QWidget { Q_OBJECT public: /** * Creates a compass display. */ CompassDisplay(QWidget* parent = nullptr); /** * Destructor. */ ~CompassDisplay() override; /** * Sets the compass direction, and updates the widget. * * This does nothing unless at least 200 ms elapsed since the last change. */ void setAzimuth(float azimuth_deg); QSize sizeHint() const override; protected: void showEvent(QShowEvent* event) override; void hideEvent(QHideEvent* event) override; void paintEvent(QPaintEvent* event) override; qreal azimuth; QTime last_update_time; }; } // namespace OpenOrienteering #endif mapper-0.8.1.1/src/gui/widgets/crs_param_widgets.cpp000066400000000000000000000056711325266516600224120ustar00rootroot00000000000000/* * Copyright 2015 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "crs_param_widgets.h" #include #include #include #include #include #include #include #include #include "core/crs_template.h" #include "core/crs_template_implementation.h" #include "core/georeferencing.h" // IWYU pragma: no_forward_declare QCompleter // IWYU pragma: no_forward_declare QHBoxLayout // IWYU pragma: no_forward_declare QPushButton namespace OpenOrienteering { UTMZoneEdit::UTMZoneEdit(CRSParameterWidgetObserver& observer, QWidget* parent) : QWidget(parent) , observer(observer) { static const QRegExp zone_regexp(QString::fromLatin1("(?:[0-5]?[1-9]|[1-6]0)(?: [NS])?")); static QStringList zone_list; if (zone_list.isEmpty()) { zone_list.reserve((60 + 9) * 2); for (int i = 1; i <= 60; ++i) { QString zone = QString::number(i); zone_list << QString::fromLatin1("%1 N").arg(zone) << QString::fromLatin1("%1 S").arg(zone); if (i < 10) zone_list << QString::fromLatin1("0%1 N").arg(zone) << QString::fromLatin1("0%1 S").arg(zone); } } line_edit = new QLineEdit(); line_edit->setValidator(new QRegExpValidator(zone_regexp, line_edit)); QCompleter* completer = new QCompleter(zone_list, line_edit); completer->setMaxVisibleItems(4); line_edit->setCompleter(completer); connect(line_edit, &QLineEdit::textChanged, this, &UTMZoneEdit::textEdited); QPushButton* button = new QPushButton(tr("Calculate")); connect(button, &QPushButton::clicked, this, &UTMZoneEdit::calculateValue); QHBoxLayout* layout = new QHBoxLayout(); layout->setMargin(0); layout->addWidget(line_edit, 1); layout->addWidget(button, 0); setLayout(layout); calculateValue(); } UTMZoneEdit::~UTMZoneEdit() { // nothing, not inlined } QString UTMZoneEdit::text() const { return line_edit->text(); } void UTMZoneEdit::setText(const QString& value) { line_edit->setText(value); } bool UTMZoneEdit::calculateValue() { auto georef = observer.georeferencing(); auto zone = CRSTemplates::UTMZoneParameter::calculateUTMZone(georef.getGeographicRefPoint()); if (!zone.isNull()) { setText(zone.toString()); } return !zone.isNull(); } } // namespace OpenOrienteering mapper-0.8.1.1/src/gui/widgets/crs_param_widgets.h000066400000000000000000000027011325266516600220460ustar00rootroot00000000000000/* * Copyright 2015 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_CRS_PARAM_WIDGETS_H #define OPENORIENTEERING_CRS_PARAM_WIDGETS_H #include #include #include class QLineEdit; namespace OpenOrienteering { class CRSParameterWidgetObserver; class UTMZoneEdit : public QWidget { Q_OBJECT public: UTMZoneEdit(CRSParameterWidgetObserver& observer, QWidget* parent = nullptr); ~UTMZoneEdit() override; QString text() const; void setText(const QString& text); bool calculateValue(); signals: void textEdited(const QString& text); // TODO: rename to textChanged, see QLineEdit private: CRSParameterWidgetObserver& observer; QLineEdit* line_edit; }; } // namespace OpenOrienteering #endif mapper-0.8.1.1/src/gui/widgets/crs_selector.cpp000066400000000000000000000220211325266516600213700ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2012-2015 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "crs_selector.h" #include #include #include #include #include #include #include #include #include #include #include #include "core/crs_template.h" #include "core/georeferencing.h" namespace OpenOrienteering { // Helper functions for parameter widgets namespace { static const char* crsParameterWidgetProperty = "CRS parameter widget"; static const char* crsParameterKeyProperty = "CRS parameter key"; inline void setParameterWidget(QWidget* w, bool value = true) { w->setProperty(crsParameterWidgetProperty, QVariant(value)); } inline bool isParameterWidget(const QWidget* w) { return w->property(crsParameterWidgetProperty).toBool(); } inline void setParameterKey(QWidget* w, const QString& value) { w->setProperty(crsParameterKeyProperty, QVariant(value)); } inline QString ParameterKey(const QWidget* w) { return w->property(crsParameterKeyProperty).toString(); } } // namespace CRSSelector::CRSSelector(const Georeferencing& georef, QWidget* parent) : QComboBox(parent) , georef(georef) , dialog_layout(nullptr) , num_custom_items(0) , configured_crs(nullptr) { for (auto&& crs : CRSTemplateRegistry().list()) { addItem(crs->name(), QVariant(crs->id())); } } CRSSelector::~CRSSelector() = default; void CRSSelector::setDialogLayout(QFormLayout* dialog_layout) { Q_ASSERT(dialog_layout && !this->dialog_layout); this->dialog_layout = dialog_layout; using TakingIntArgument = void (QComboBox::*)(int); connect(this, (TakingIntArgument)&QComboBox::currentIndexChanged, this, &CRSSelector::crsSelectionChanged); } void CRSSelector::addCustomItem(const QString& text, unsigned short id) { insertItem(num_custom_items, text, QVariant(int(id))); if (num_custom_items == 0) insertSeparator(1); ++num_custom_items; } const CRSTemplate* CRSSelector::currentCRSTemplate() const { const CRSTemplate* crs = nullptr; auto item_data = itemData(currentIndex()); if (item_data.type() == QVariant::String) crs = CRSTemplateRegistry().find(item_data.toString()); return crs; } QString CRSSelector::currentCRSSpec() const { QString spec; if (auto crs = currentCRSTemplate()) { spec = crs->specificationTemplate(); auto field_values = parameters(); Q_ASSERT(field_values.size() == crs->parameters().size()); auto field_value = begin(field_values); for (auto&& param : crs->parameters()) { for (auto&& value : param->specValues(*field_value)) spec = spec.arg(value); ++field_value; } } return spec; } int CRSSelector::currentCustomItem() const { int id = -1; QVariant item_data = itemData(currentIndex()); if (item_data.type() == QVariant::Int) id = item_data.toInt(); return id; } void CRSSelector::setCurrentCRS(const CRSTemplate* crs, const std::vector& values) { Q_ASSERT(crs); if (crs) { Q_ASSERT(crs->parameters().size() == values.size()); int index = findData(QVariant(crs->id())); setCurrentIndex(index); // Explicit call because signals may be blocked. if (crs == currentCRSTemplate()) { configureParameterFields(crs, values); } } } void CRSSelector::setCurrentItem(unsigned short id) { QSignalBlocker block(this); int index = findData(QVariant(id)); setCurrentIndex(index); // Explicit call because signals may be blocked. if (currentCustomItem() == int(id)) { configureParameterFields(nullptr, {}); } } std::vector CRSSelector::parameters() const { std::vector values; auto crs = currentCRSTemplate(); if (crs && configured_crs == crs) { values.reserve(crs->parameters().size()); int row; QFormLayout::ItemRole role; dialog_layout->getWidgetPosition(const_cast(this), &row, &role); for (auto&& param : crs->parameters()) { ++row; auto field = dialog_layout->itemAt(row, QFormLayout::FieldRole)->widget(); Q_ASSERT(isParameterWidget(field)); values.push_back(param->value(field)); } } else if (crs) { values.resize(crs->parameters().size()); } return values; } const Georeferencing& CRSSelector::georeferencing() const { return georef; } void CRSSelector::crsSelectionChanged() { configureParameterFields(); emit crsChanged(); } void CRSSelector::crsParameterEdited() { emit crsChanged(); } void CRSSelector::configureParameterFields() { if (dialog_layout) { auto crs = currentCRSTemplate(); if (!crs) configureParameterFields(crs, {}); else if (crs->id() == georef.getProjectedCRSId()) configureParameterFields(crs, georef.getProjectedCRSParameters()); else configureParameterFields(crs, std::vector(crs->parameters().size())); } } void CRSSelector::configureParameterFields(const CRSTemplate* crs, const std::vector& values) { if (crs != configured_crs && dialog_layout) { removeParameterFields(); addParameterFields(crs); } if (crs && configured_crs == crs && !values.empty()) { int row; QFormLayout::ItemRole role; dialog_layout->getWidgetPosition(this, &row, &role); // Set the new parameter values. auto parameters = crs->parameters(); Q_ASSERT(parameters.size() == values.size()); auto parameter = begin(parameters); auto value = begin(values); for (++row; dialog_layout->rowCount() > row && value != end(values); ++row) { auto field_item = dialog_layout->itemAt(row, QFormLayout::FieldRole); if (!field_item) continue; auto field = field_item->widget(); if (!field || !isParameterWidget(field)) continue; QSignalBlocker block(field); (*parameter)->setValue(field, *value); ++value; ++parameter; } } } void CRSSelector::addParameterFields(const CRSTemplate* crs) { Q_ASSERT(!configured_crs); if (dialog_layout && crs && crs != configured_crs) { int row; QFormLayout::ItemRole role; dialog_layout->getWidgetPosition(this, &row, &role); // Add the labels and fields of the new parameters. for (auto&& parameter : crs->parameters()) { ++row; auto field = parameter->createEditor(*this); setParameterWidget(field, true); setParameterKey(field, parameter->id()); auto label = new QLabel(parameter->name() + QLatin1Char(':')); if (dialog_layout->itemAt(row, QFormLayout::FieldRole)) { dialog_layout->insertRow(row, label, field); } else { dialog_layout->setWidget(row, QFormLayout::LabelRole, label); dialog_layout->setWidget(row, QFormLayout::FieldRole, field); } } configured_crs = crs; } } void CRSSelector::removeParameterFields() { if (dialog_layout) { int crs_row; QFormLayout::ItemRole role; dialog_layout->getWidgetPosition(this, &crs_row, &role); // Remove the labels and fields of the old parameters. for (int row = dialog_layout->rowCount()-1; row > crs_row; --row) { auto field_item = dialog_layout->itemAt(row, QFormLayout::FieldRole); if (!field_item) continue; auto field = field_item->widget(); if (!field || !isParameterWidget(field)) continue; auto label_item = dialog_layout->itemAt(row, QFormLayout::LabelRole); if (auto label = label_item->widget()) { delete dialog_layout->takeAt(dialog_layout->indexOf(label)); delete label; } delete dialog_layout->takeAt(dialog_layout->indexOf(field)); delete field; } configured_crs = nullptr; } } void CRSSelector::changeEvent(QEvent* event) { switch (event->type()) { case QEvent::EnabledChange: if (dialog_layout) { bool enabled = isEnabled(); if (auto label = dialog_layout->labelForField(this)) label->setEnabled(enabled); int row; QFormLayout::ItemRole role; dialog_layout->getWidgetPosition(this, &row, &role); // Remove the labels and fields of the old parameters. for (++row; dialog_layout->rowCount() > row; ++row) { auto field_item = dialog_layout->itemAt(row, QFormLayout::FieldRole); if (!field_item) continue; auto field = field_item->widget(); if (!field || !isParameterWidget(field)) continue; auto label_item = dialog_layout->itemAt(row, QFormLayout::LabelRole); if (auto label = label_item->widget()) label->setEnabled(enabled); field->setEnabled(enabled); } configured_crs = nullptr; } break; default: ; // nothing } } } // namespace OpenOrienteering mapper-0.8.1.1/src/gui/widgets/crs_selector.h000066400000000000000000000115231325266516600210420ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2012-2015 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_CRS_SELECTOR_H #define OPENORIENTEERING_CRS_SELECTOR_H #include #include #include #include #include "core/crs_template.h" class QEvent; class QFormLayout; class QWidget; namespace OpenOrienteering { class Georeferencing; /** * Combobox for selecting projected coordinate reference system (CRS). * * It operates on the list of CRS templates in the CRSTemplateRegistry. * However, it accepts custom items which are prepended to and separated from * the default items. * * This is more than just a simple widget. CRSSelector is meant to be inserted * into a QFormLayout. Upon CRS selection changes it will add and remove extra * lines below itself, for editing CRS parameters. * * \todo Consider making this a QWidget which has got a QCombobox - the public * QComboBox API should better not be exposed. The combobox' signals could * no longer be block from clients. */ class CRSSelector : public QComboBox, public CRSParameterWidgetObserver { Q_OBJECT public: /** * Constructor. * * The dialog parameter must not be nullptr. It is passed to parameter widgets. * Ownership is taken only by the parent widget if given. */ CRSSelector(const Georeferencing& georef, QWidget* parent = nullptr); /** * Destructor. */ ~CRSSelector() override; /** * Sets the QFormLayout which this field is part of. * * When the selected CRS is changed, or when configureParameterFields() * is called explicitly, CRSSelector will add extra lines below its own row * for editing CRS parameters. * * This is to be called once. However, it will not create the parameter * fields for the current selection. */ void setDialogLayout(QFormLayout* dialog_layout); /** * Adds a custom item with the given text and id at the top of the list. */ void addCustomItem(const QString& text, unsigned short id); /** * Returns the selected CRS template, or nullptr if a custom item is selected. */ const CRSTemplate* currentCRSTemplate() const; /** * Returns the selected CRS specification string, * or an empty string if a custom item is selected. */ QString currentCRSSpec() const; /** * Returns the id of the selected custom item, * or -1 if a normal item is selected. */ int currentCustomItem() const; /** * Selects the given standard item, and sets the parameters. */ void setCurrentCRS(const CRSTemplate* crs, const std::vector& values); /** * Selects the given custom item. */ void setCurrentItem(unsigned short id); /** * Returns the list of CRS configuration parameter values. */ std::vector parameters() const; /** * Provides the current georeferencing. */ const Georeferencing& georeferencing() const override; signals: /** * Emitted when the user changes the CRS or its parameters. */ void crsChanged(); protected: /** * Listens to changes of the selected CRS. */ void crsSelectionChanged(); /** * Listens to changes of CRS parameters. */ void crsParameterEdited() override; /** * Updates the parameter fields in the dialog_layout, * according to the selected CRS. */ void configureParameterFields(); /** * Updates the parameter fields in the dialog_layout, * according to the given CRS and values. */ void configureParameterFields(const CRSTemplate* crs, const std::vector& values); /** * Adds parameter fields to the dialog_layout, * according to the given crs. * * There must be no other parameter fields in the dialog, * i.e. removeParameterFields() needs to be called before. */ void addParameterFields(const CRSTemplate* crs); /** * Removes all parameter fields from the dialog_layout. */ void removeParameterFields(); /** * Propagates enabling/disabling to the parameter widgets. */ void changeEvent(QEvent* event) override; private: const Georeferencing& georef; QFormLayout* dialog_layout; int num_custom_items; const CRSTemplate* configured_crs; }; } // namespace OpenOrienteering #endif mapper-0.8.1.1/src/gui/widgets/editor_settings_page.cpp000066400000000000000000000176511325266516600231200ustar00rootroot00000000000000/* * Copyright 2012, 2013 Jan Dalheimer * Copyright 2012-2016 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "editor_settings_page.h" #include #include #include #include #include #include #include #include #include "settings.h" #include "gui/modifier_key.h" #include "gui/util_gui.h" #include "gui/widgets/settings_page.h" namespace OpenOrienteering { EditorSettingsPage::EditorSettingsPage(QWidget* parent) : SettingsPage(parent) { auto layout = new QFormLayout(this); icon_size = Util::SpinBox::create(1, 25, tr("mm", "millimeters")); layout->addRow(tr("Symbol icon size:"), icon_size); antialiasing = new QCheckBox(tr("High quality map display (antialiasing)"), this); antialiasing->setToolTip(tr("Antialiasing makes the map look much better, but also slows down the map display")); layout->addRow(antialiasing); text_antialiasing = new QCheckBox(tr("High quality text display in map (antialiasing), slow"), this); text_antialiasing->setToolTip(tr("Antialiasing makes the map look much better, but also slows down the map display")); layout->addRow(text_antialiasing); tolerance = Util::SpinBox::create(0, 50, tr("mm", "millimeters")); layout->addRow(tr("Click tolerance:"), tolerance); snap_distance = Util::SpinBox::create(0, 100, tr("mm", "millimeters")); layout->addRow(tr("Snap distance (%1):").arg(ModifierKey::shift()), snap_distance); fixed_angle_stepping = Util::SpinBox::create(1, 180, trUtf8("°", "Degree sign for angles")); layout->addRow(tr("Stepping of fixed angle mode (%1):").arg(ModifierKey::control()), fixed_angle_stepping); select_symbol_of_objects = new QCheckBox(tr("When selecting an object, automatically select its symbol, too")); layout->addRow(select_symbol_of_objects); zoom_out_away_from_cursor = new QCheckBox(tr("Zoom away from cursor when zooming out")); layout->addRow(zoom_out_away_from_cursor); draw_last_point_on_right_click = new QCheckBox(tr("Drawing tools: set last point on finishing with right click")); layout->addRow(draw_last_point_on_right_click); keep_settings_of_closed_templates = new QCheckBox(tr("Templates: keep settings of closed templates")); layout->addRow(keep_settings_of_closed_templates); layout->addItem(Util::SpacerItem::create(this)); layout->addRow(Util::Headline::create(tr("Edit tool:"))); edit_tool_delete_bezier_point_action = new QComboBox(); edit_tool_delete_bezier_point_action->addItem(tr("Retain old shape"), (int)Settings::DeleteBezierPoint_RetainExistingShape); edit_tool_delete_bezier_point_action->addItem(tr("Reset outer curve handles"), (int)Settings::DeleteBezierPoint_ResetHandles); edit_tool_delete_bezier_point_action->addItem(tr("Keep outer curve handles"), (int)Settings::DeleteBezierPoint_KeepHandles); layout->addRow(tr("Action on deleting a curve point with %1:").arg(ModifierKey::control()), edit_tool_delete_bezier_point_action); edit_tool_delete_bezier_point_action_alternative = new QComboBox(); edit_tool_delete_bezier_point_action_alternative->addItem(tr("Retain old shape"), (int)Settings::DeleteBezierPoint_RetainExistingShape); edit_tool_delete_bezier_point_action_alternative->addItem(tr("Reset outer curve handles"), (int)Settings::DeleteBezierPoint_ResetHandles); edit_tool_delete_bezier_point_action_alternative->addItem(tr("Keep outer curve handles"), (int)Settings::DeleteBezierPoint_KeepHandles); layout->addRow(tr("Action on deleting a curve point with %1:").arg(ModifierKey::controlShift()), edit_tool_delete_bezier_point_action_alternative); layout->addItem(Util::SpacerItem::create(this)); layout->addRow(Util::Headline::create(tr("Rectangle tool:"))); rectangle_helper_cross_radius = Util::SpinBox::create(0, 999999, tr("mm", "millimeters")); layout->addRow(tr("Radius of helper cross:"), rectangle_helper_cross_radius); rectangle_preview_line_width = new QCheckBox(tr("Preview the width of lines with helper cross")); layout->addRow(rectangle_preview_line_width); connect(antialiasing, &QAbstractButton::toggled, text_antialiasing, &QCheckBox::setEnabled); updateWidgets(); } EditorSettingsPage::~EditorSettingsPage() { // nothing, not inlined } QString EditorSettingsPage::title() const { return tr("Editor"); } void EditorSettingsPage::apply() { setSetting(Settings::SymbolWidget_IconSizeMM, icon_size->value()); setSetting(Settings::MapDisplay_Antialiasing, antialiasing->isChecked()); setSetting(Settings::MapDisplay_TextAntialiasing, text_antialiasing->isChecked()); setSetting(Settings::MapEditor_ClickToleranceMM, tolerance->value()); setSetting(Settings::MapEditor_SnapDistanceMM, snap_distance->value()); setSetting(Settings::MapEditor_FixedAngleStepping, fixed_angle_stepping->value()); setSetting(Settings::MapEditor_ChangeSymbolWhenSelecting, select_symbol_of_objects->isChecked()); setSetting(Settings::MapEditor_ZoomOutAwayFromCursor, zoom_out_away_from_cursor->isChecked()); setSetting(Settings::MapEditor_DrawLastPointOnRightClick, draw_last_point_on_right_click->isChecked()); setSetting(Settings::Templates_KeepSettingsOfClosed, keep_settings_of_closed_templates->isChecked()); setSetting(Settings::EditTool_DeleteBezierPointAction, edit_tool_delete_bezier_point_action->currentData()); setSetting(Settings::EditTool_DeleteBezierPointActionAlternative, edit_tool_delete_bezier_point_action_alternative->currentData()); setSetting(Settings::RectangleTool_HelperCrossRadiusMM, rectangle_helper_cross_radius->value()); setSetting(Settings::RectangleTool_PreviewLineWidth, rectangle_preview_line_width->isChecked()); } void EditorSettingsPage::reset() { updateWidgets(); } void EditorSettingsPage::updateWidgets() { icon_size->setValue(getSetting(Settings::SymbolWidget_IconSizeMM).toInt()); antialiasing->setChecked(getSetting(Settings::MapDisplay_Antialiasing).toBool()); text_antialiasing->setEnabled(antialiasing->isChecked()); text_antialiasing->setChecked(getSetting(Settings::MapDisplay_TextAntialiasing).toBool()); tolerance->setValue(getSetting(Settings::MapEditor_ClickToleranceMM).toInt()); snap_distance->setValue(getSetting(Settings::MapEditor_SnapDistanceMM).toInt()); fixed_angle_stepping->setValue(getSetting(Settings::MapEditor_FixedAngleStepping).toInt()); select_symbol_of_objects->setChecked(getSetting(Settings::MapEditor_ChangeSymbolWhenSelecting).toBool()); zoom_out_away_from_cursor->setChecked(getSetting(Settings::MapEditor_ZoomOutAwayFromCursor).toBool()); draw_last_point_on_right_click->setChecked(getSetting(Settings::MapEditor_DrawLastPointOnRightClick).toBool()); keep_settings_of_closed_templates->setChecked(getSetting(Settings::Templates_KeepSettingsOfClosed).toBool()); edit_tool_delete_bezier_point_action->setCurrentIndex(edit_tool_delete_bezier_point_action->findData(getSetting(Settings::EditTool_DeleteBezierPointAction).toInt())); edit_tool_delete_bezier_point_action_alternative->setCurrentIndex(edit_tool_delete_bezier_point_action_alternative->findData(getSetting(Settings::EditTool_DeleteBezierPointActionAlternative).toInt())); rectangle_helper_cross_radius->setValue(getSetting(Settings::RectangleTool_HelperCrossRadiusMM).toInt()); rectangle_preview_line_width->setChecked(getSetting(Settings::RectangleTool_PreviewLineWidth).toBool()); } } // namespace OpenOrienteering mapper-0.8.1.1/src/gui/widgets/editor_settings_page.h000066400000000000000000000036401325266516600225560ustar00rootroot00000000000000/* * Copyright 2012, 2013 Jan Dalheimer * Copyright 2013-2016 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_EDITOR_SETTINGS_PAGE_H #define OPENORIENTEERING_EDITOR_SETTINGS_PAGE_H #include #include #include "settings_page.h" class QCheckBox; class QComboBox; class QSpinBox; class QWidget; namespace OpenOrienteering { class EditorSettingsPage : public SettingsPage { Q_OBJECT public: explicit EditorSettingsPage(QWidget* parent = nullptr); ~EditorSettingsPage() override; QString title() const override; void apply() override; void reset() override; protected: void updateWidgets(); private: QSpinBox* icon_size; QCheckBox* antialiasing; QCheckBox* text_antialiasing; QSpinBox* tolerance; QSpinBox* snap_distance; QSpinBox* fixed_angle_stepping; QCheckBox* select_symbol_of_objects; QCheckBox* zoom_out_away_from_cursor; QCheckBox* draw_last_point_on_right_click; QCheckBox* keep_settings_of_closed_templates; QComboBox* edit_tool_delete_bezier_point_action; QComboBox* edit_tool_delete_bezier_point_action_alternative; QSpinBox* rectangle_helper_cross_radius; QCheckBox* rectangle_preview_line_width; }; } // namespace OpenOrienteering #endif mapper-0.8.1.1/src/gui/widgets/general_settings_page.cpp000066400000000000000000000346041325266516600232440ustar00rootroot00000000000000/* * Copyright 2012, 2013 Jan Dalheimer * Copyright 2012-2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "general_settings_page.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // IWYU pragma: keep #include #include #include #include #include #include #include #include #include #include "settings.h" #include "gui/file_dialog.h" #include "gui/main_window.h" #include "gui/util_gui.h" #include "gui/widgets/home_screen_widget.h" #include "gui/widgets/settings_page.h" #include "util/translation_util.h" namespace OpenOrienteering { GeneralSettingsPage::GeneralSettingsPage(QWidget* parent) : SettingsPage(parent) , translation_file(getSetting(Settings::General_TranslationFile).toString()) { auto layout = new QFormLayout(this); layout->addRow(Util::Headline::create(tr("Appearance"))); auto language_widget = new QWidget(); auto language_layout = new QHBoxLayout(language_widget); language_layout->setContentsMargins({}); layout->addRow(tr("Language:"), language_widget); language_box = new QComboBox(this); language_layout->addWidget(language_box); auto language_file_button = new QToolButton(); if (MainWindow::mobileMode()) { language_file_button->setVisible(false); } else { language_file_button->setIcon(QIcon(QLatin1String(":/images/open.png"))); } language_layout->addWidget(language_file_button); layout->addItem(Util::SpacerItem::create(this)); layout->addRow(Util::Headline::create(tr("Screen"))); auto ppi_widget = new QWidget(); auto ppi_layout = new QHBoxLayout(ppi_widget); ppi_layout->setContentsMargins({}); layout->addRow(tr("Pixels per inch:"), ppi_widget); ppi_edit = Util::SpinBox::create(2, 0.01, 9999); ppi_layout->addWidget(ppi_edit); auto ppi_calculate_button = new QToolButton(); ppi_calculate_button->setIcon(QIcon(QLatin1String(":/images/settings.png"))); ppi_layout->addWidget(ppi_calculate_button); layout->addItem(Util::SpacerItem::create(this)); layout->addRow(Util::Headline::create(tr("Program start"))); open_mru_check = new QCheckBox(::OpenOrienteering::AbstractHomeScreenWidget::tr("Open most recently used file")); layout->addRow(open_mru_check); tips_visible_check = new QCheckBox(::OpenOrienteering::AbstractHomeScreenWidget::tr("Show tip of the day")); layout->addRow(tips_visible_check); layout->addItem(Util::SpacerItem::create(this)); layout->addRow(Util::Headline::create(tr("Saving files"))); compatibility_check = new QCheckBox(tr("Retain compatibility with Mapper %1").arg(QLatin1String("0.5"))); #ifdef MAPPER_ENABLE_COMPATIBILITY layout->addRow(compatibility_check); #else // Let compatibility_check be valid, but not leak connect(this, &QObject::destroyed, compatibility_check, &QObject::deleteLater); #endif undo_check = new QCheckBox(tr("Save undo/redo history")); layout->addRow(undo_check); autosave_check = new QCheckBox(tr("Save information for automatic recovery")); layout->addRow(autosave_check); autosave_interval_edit = Util::SpinBox::create(1, 120, tr("min", "unit minutes"), 1); layout->addRow(tr("Recovery information saving interval:"), autosave_interval_edit); layout->addItem(Util::SpacerItem::create(this)); layout->addRow(Util::Headline::create(tr("File import and export"))); QStringList available_codecs; available_codecs.append(tr("Default")); encoding_box = new QComboBox(); encoding_box->setEditable(true); encoding_box->addItem(available_codecs.first()); encoding_box->addItem(QString::fromLatin1("Windows-1252")); // Serves as an example, not translated. const auto available_codecs_raw = QTextCodec::availableCodecs(); available_codecs.reserve(available_codecs_raw.size()); for (const QByteArray& item : available_codecs_raw) { available_codecs.append(QString::fromUtf8(item)); } if (available_codecs.size() > 1) { available_codecs.sort(Qt::CaseInsensitive); available_codecs.removeDuplicates(); encoding_box->addItem(tr("More...")); } auto completer = new QCompleter(available_codecs, this); completer->setModelSorting(QCompleter::CaseInsensitivelySortedModel); completer->setCaseSensitivity(Qt::CaseInsensitive); completer->setCompletionMode(QCompleter::UnfilteredPopupCompletion); encoding_box->setCompleter(completer); layout->addRow(tr("8-bit encoding:"), encoding_box); ocd_importer_check = new QCheckBox(tr("Use the new OCD importer also for version 8 files").replace(QLatin1Char('8'), QString::fromLatin1("6-8"))); layout->addRow(ocd_importer_check); updateWidgets(); connect(language_file_button, &QAbstractButton::clicked, this, &GeneralSettingsPage::openTranslationFileDialog); connect(ppi_calculate_button, &QAbstractButton::clicked, this, &GeneralSettingsPage::openPPICalculationDialog); connect(encoding_box, &QComboBox::currentTextChanged, this, &GeneralSettingsPage::encodingChanged); connect(autosave_check, &QAbstractButton::toggled, autosave_interval_edit, &QWidget::setEnabled); connect(autosave_check, &QAbstractButton::toggled, layout->labelForField(autosave_interval_edit), &QWidget::setEnabled); } GeneralSettingsPage::~GeneralSettingsPage() = default; QString GeneralSettingsPage::title() const { return tr("General"); } void GeneralSettingsPage::apply() { auto language = language_box->currentData().toString(); if (language != getSetting(Settings::General_Language).toString() || translation_file != getSetting(Settings::General_TranslationFile).toString()) { // Show an message box in the new language. TranslationUtil translation(language, translation_file); auto new_language = QLocale(translation.code()).language(); switch (new_language) { case QLocale::AnyLanguage: case QLocale::C: case QLocale::English: QMessageBox::information(window(), QLatin1String("Notice"), QLatin1String("The program must be restarted for the language change to take effect!")); break; default: qApp->installEventFilter(this); qApp->installTranslator(&translation.getQtTranslator()); qApp->installTranslator(&translation.getAppTranslator()); QMessageBox::information(window(), tr("Notice"), tr("The program must be restarted for the language change to take effect!")); qApp->removeTranslator(&translation.getAppTranslator()); qApp->removeTranslator(&translation.getQtTranslator()); qApp->removeEventFilter(this); } setSetting(Settings::General_Language, translation.code()); setSetting(Settings::General_TranslationFile, translation_file); #if defined(Q_OS_MACOS) // The native [file] dialogs will use the first element of the // AppleLanguages array in the application's .plist file - // and this file is also the one used by QSettings. QSettings().setValue(QString::fromLatin1("AppleLanguages"), QStringList{ translation.code() }); #endif } setSetting(Settings::General_OpenMRUFile, open_mru_check->isChecked()); setSetting(Settings::HomeScreen_TipsVisible, tips_visible_check->isChecked()); setSetting(Settings::General_NewOcd8Implementation, ocd_importer_check->isChecked()); setSetting(Settings::General_RetainCompatiblity, compatibility_check->isChecked()); setSetting(Settings::General_SaveUndoRedo, undo_check->isChecked()); setSetting(Settings::General_PixelsPerInch, ppi_edit->value()); auto encoding = encoding_box->currentText().toLatin1(); if (QLatin1String(encoding) == encoding_box->itemText(0) || !QTextCodec::codecForName(encoding)) { encoding = "Default"; } setSetting(Settings::General_Local8BitEncoding, encoding); int interval = autosave_interval_edit->value(); if (!autosave_check->isChecked()) interval = -interval; setSetting(Settings::General_AutosaveInterval, interval); } void GeneralSettingsPage::reset() { translation_file = getSetting(Settings::General_TranslationFile).toString(); updateWidgets(); } void GeneralSettingsPage::updateLanguageBox(QVariant code) { auto languages = TranslationUtil::availableLanguages(); std::sort(begin(languages), end(languages)); const QSignalBlocker block(language_box); language_box->clear(); for (const auto& language : languages) language_box->addItem(language.displayName, language.code); // If there is an explicit translation file, make sure it is in the box. auto language = TranslationUtil::languageFromFilename(translation_file); if (language.isValid()) { auto index = language_box->findData(language.code); if (index < 0) language_box->addItem(language.displayName, language.code); } // Select current language auto index = language_box->findData(code); if (index < 0) { code = QString::fromLatin1("en"); index = language_box->findData(code); } language_box->setCurrentIndex(index); } void GeneralSettingsPage::updateWidgets() { updateLanguageBox(getSetting(Settings::General_Language)); ppi_edit->setValue(getSetting(Settings::General_PixelsPerInch).toDouble()); open_mru_check->setChecked(getSetting(Settings::General_OpenMRUFile).toBool()); tips_visible_check->setChecked(getSetting(Settings::HomeScreen_TipsVisible).toBool()); compatibility_check->setChecked(getSetting(Settings::General_RetainCompatiblity).toBool()); undo_check->setChecked(getSetting(Settings::General_SaveUndoRedo).toBool()); int autosave_interval = getSetting(Settings::General_AutosaveInterval).toInt(); autosave_check->setChecked(autosave_interval > 0); autosave_interval_edit->setEnabled(autosave_interval > 0); autosave_interval_edit->setValue(qAbs(autosave_interval)); auto encoding = getSetting(Settings::General_Local8BitEncoding).toByteArray(); if (encoding != "Default" && QTextCodec::codecForName(encoding)) { encoding_box->setCurrentText(QString::fromLatin1(encoding)); } ocd_importer_check->setChecked(getSetting(Settings::General_NewOcd8Implementation).toBool()); } // slot void GeneralSettingsPage::encodingChanged(const QString& input) { if (input == tr("More...")) { const QSignalBlocker block(encoding_box); encoding_box->setCurrentText(last_encoding_input); encoding_box->completer()->setCompletionPrefix(last_encoding_input); encoding_box->completer()->complete(); } else { // Inline completition, in addition to UnfilteredPopupCompletition // Don't complete after pressing Backspace or Del if (!last_encoding_input.startsWith(input)) { if (last_matching_completition.startsWith(input)) encoding_box->completer()->setCompletionPrefix(last_matching_completition); auto text = encoding_box->completer()->currentCompletion(); auto line_edit = encoding_box->lineEdit(); if (text.startsWith(input) && line_edit) { const QSignalBlocker block(encoding_box); auto pos = line_edit->cursorPosition(); line_edit->setText(text); line_edit->setSelection(text.length(), pos - text.length()); last_encoding_input = input.left(pos); last_matching_completition = text; return; } } last_encoding_input = input; } } // slot void GeneralSettingsPage::openTranslationFileDialog() { QString filename = translation_file; if (filename.isEmpty()) filename = getSetting(Settings::General_TranslationFile).toString(); filename = FileDialog::getOpenFileName(this, tr("Open translation"), filename, tr("Translation files (*.qm)")); if (!filename.isNull()) { auto language = TranslationUtil::languageFromFilename(filename); if (!language.isValid()) { QMessageBox::critical(this, tr("Open translation"), tr("The selected file is not a valid translation.") ); } else { translation_file = filename; updateLanguageBox(language.code); } } } // slot void GeneralSettingsPage::openPPICalculationDialog() { int primary_screen_width = QApplication::primaryScreen()->size().width(); int primary_screen_height = QApplication::primaryScreen()->size().height(); double screen_diagonal_pixels = double(qSqrt(primary_screen_width*primary_screen_width + primary_screen_height*primary_screen_height)); double old_ppi = ppi_edit->value(); double old_screen_diagonal_inches = screen_diagonal_pixels / old_ppi; auto dialog = new QDialog(window(), Qt::WindowSystemMenuHint | Qt::WindowTitleHint); if (MainWindow::mobileMode()) { dialog->setGeometry(window()->geometry()); } auto layout = new QVBoxLayout(dialog); auto form_layout = new QFormLayout(); auto resolution_display = new QLabel(tr("%1 x %2").arg(primary_screen_width).arg(primary_screen_height)); form_layout->addRow(tr("Primary screen resolution in pixels:"), resolution_display); auto size_edit = Util::SpinBox::create(2, 0.01, 9999); size_edit->setValue(old_screen_diagonal_inches); form_layout->addRow(tr("Primary screen size in inches (diagonal):"), size_edit); layout->addLayout(form_layout); layout->addItem(Util::SpacerItem::create(this)); auto button_box = new QDialogButtonBox(QDialogButtonBox::Cancel | QDialogButtonBox::Ok); layout->addWidget(button_box); connect(button_box, &QDialogButtonBox::accepted, dialog, &QDialog::accept); connect(button_box, &QDialogButtonBox::rejected, dialog, &QDialog::reject); dialog->exec(); if (dialog->result() == QDialog::Accepted) { ppi_edit->setValue(screen_diagonal_pixels / size_edit->value()); } } bool GeneralSettingsPage::eventFilter(QObject* /* watched */, QEvent* event) { if (event->type() == QEvent::LanguageChange) return true; // NOLINT return false; } } // namespace OpenOrienteering mapper-0.8.1.1/src/gui/widgets/general_settings_page.h000066400000000000000000000043661325266516600227130ustar00rootroot00000000000000/* * Copyright 2012, 2013 Jan Dalheimer * Copyright 2013-2016 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_GENERAL_SETTINGS_PAGE_H #define OPENORIENTEERING_GENERAL_SETTINGS_PAGE_H #include #include #include #include "settings_page.h" class QCheckBox; class QComboBox; class QDoubleSpinBox; class QEvent; class QSpinBox; class QWidget; namespace OpenOrienteering { class GeneralSettingsPage : public SettingsPage { Q_OBJECT public: explicit GeneralSettingsPage(QWidget* parent = nullptr); ~GeneralSettingsPage() override; QString title() const override; void apply() override; void reset() override; protected: /** * Adds the available languages to the language combo box, * and sets the current element. */ void updateLanguageBox(QVariant code); void updateWidgets(); /** * This event filter stops LanguageChange events. */ bool eventFilter(QObject* watched, QEvent* event) override; private slots: void openTranslationFileDialog(); void openPPICalculationDialog(); void encodingChanged(const QString& input); private: QString translation_file; QString last_encoding_input; QString last_matching_completition; QComboBox* language_box; QDoubleSpinBox* ppi_edit; QCheckBox* open_mru_check; QCheckBox* tips_visible_check; QCheckBox* compatibility_check; QCheckBox* undo_check; QCheckBox* autosave_check; QSpinBox* autosave_interval_edit; QComboBox* encoding_box; QCheckBox* ocd_importer_check; }; } // namespace OpenOrienteering #endif mapper-0.8.1.1/src/gui/widgets/home_screen_widget.cpp000066400000000000000000000447311325266516600225470ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2012-2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "home_screen_widget.h" #include // IWYU pragma: keep #include #include #include #include #include #include #include #include #include #include #include #include #include "core/storage_location.h" // IWYU pragma: keep #include "fileformats/file_format_registry.h" #include "gui/home_screen_controller.h" #include "gui/main_window.h" #include "gui/settings_dialog.h" namespace OpenOrienteering { //### AbstractHomeScreenWidget ### AbstractHomeScreenWidget::AbstractHomeScreenWidget(HomeScreenController* controller, QWidget* parent) : QWidget(parent), controller(controller) { Q_ASSERT(controller->getWindow()); } AbstractHomeScreenWidget::~AbstractHomeScreenWidget() { // nothing } QLabel* AbstractHomeScreenWidget::makeHeadline(const QString& text, QWidget* parent) const { QLabel* title_label = new QLabel(text, parent); QFont title_font = title_label->font(); int pixel_size = title_font.pixelSize(); if (pixel_size > 0) { title_font.setPixelSize(pixel_size * 2); } else { pixel_size = title_font.pointSize(); title_font.setPointSize(pixel_size * 2); } title_font.setBold(true); title_label->setFont(title_font); title_label->setAlignment(Qt::AlignLeft | Qt::AlignTop); return title_label; } QAbstractButton* AbstractHomeScreenWidget::makeButton(const QString& text, QWidget* parent) const { QAbstractButton* button = new QCommandLinkButton(text, parent); QFont button_font = button->font(); int pixel_size = button_font.pixelSize(); if (pixel_size > 0) { button_font.setPixelSize(pixel_size * 3 / 2); } else { pixel_size = button_font.pointSize(); button_font.setPointSize(pixel_size * 3 / 2); } button->setFont(button_font); return button; } QAbstractButton* AbstractHomeScreenWidget::makeButton(const QString& text, const QIcon& icon, QWidget* parent) const { QAbstractButton* button = makeButton(text, parent); button->setIcon(icon); return button; } //### HomeScreenWidgetDesktop ### HomeScreenWidgetDesktop::HomeScreenWidgetDesktop(HomeScreenController* controller, QWidget* parent) : AbstractHomeScreenWidget(controller, parent) { QLabel* title_label = new QLabel(QString::fromLatin1("")); title_label->setAlignment(Qt::AlignCenter); QWidget* menu_widget = makeMenuWidget(controller, parent); QWidget* recent_files_widget = makeRecentFilesWidget(controller, parent); QWidget* tips_widget = makeTipsWidget(controller, parent); QGridLayout* layout = new QGridLayout(); layout->setSpacing(2 * layout->spacing()); layout->addWidget(title_label, 0, 0, 1, 2); layout->addWidget(menu_widget, 1, 0, 2, 1); layout->addWidget(recent_files_widget, 1, 1); layout->setRowStretch(1, 4); layout->addWidget(tips_widget, 2, 1); layout->setRowStretch(2, 3); setLayout(layout); setAutoFillBackground(false); } HomeScreenWidgetDesktop::~HomeScreenWidgetDesktop() { // nothing } QWidget* HomeScreenWidgetDesktop::makeMenuWidget(HomeScreenController* controller, QWidget* parent) { MainWindow* window = controller->getWindow(); QVBoxLayout* menu_layout = new QVBoxLayout(); QLabel* menu_headline = makeHeadline(tr("Activities")); menu_layout->addWidget(menu_headline); QAbstractButton* button_new_map = makeButton( tr("Create a new map ..."), QIcon(QString::fromLatin1(":/images/new.png"))); menu_layout->addWidget(button_new_map); QAbstractButton* button_open_map = makeButton( tr("Open map ..."), QIcon(QString::fromLatin1(":/images/open.png"))); menu_layout->addWidget(button_open_map); menu_layout->addStretch(1); QAbstractButton* button_settings = makeButton( tr("Settings"), QIcon(QString::fromLatin1(":/images/settings.png"))); menu_layout->addWidget(button_settings); QAbstractButton* button_about = makeButton( tr("About %1", "As in 'About OpenOrienteering Mapper'").arg(window->appName()), QIcon(QString::fromLatin1(":/images/about.png"))); menu_layout->addWidget(button_about); QAbstractButton* button_help = makeButton( tr("Help"), QIcon(QString::fromLatin1(":/images/help.png"))); menu_layout->addWidget(button_help); QAbstractButton* button_exit = makeButton( tr("Exit"), QIcon(QString::fromLatin1(":/qt-project.org/styles/commonstyle/images/standardbutton-close-32.png"))); // From Qt5 menu_layout->addWidget(button_exit); connect(button_new_map, &QAbstractButton::clicked, window, &MainWindow::showNewMapWizard); connect(button_open_map, &QAbstractButton::clicked, window, &MainWindow::showOpenDialog); connect(button_settings, &QAbstractButton::clicked, window, &MainWindow::showSettings); connect(button_about, &QAbstractButton::clicked, window, &MainWindow::showAbout); connect(button_help, &QAbstractButton::clicked, window, &MainWindow::showHelp); connect(button_exit, &QAbstractButton::clicked, qApp, &QApplication::closeAllWindows); QWidget* menu_widget = new QWidget(parent); menu_widget->setLayout(menu_layout); menu_widget->setAutoFillBackground(true); return menu_widget; } QWidget* HomeScreenWidgetDesktop::makeRecentFilesWidget(HomeScreenController* controller, QWidget* parent) { QGridLayout* recent_files_layout = new QGridLayout(); QLabel* recent_files_headline = makeHeadline(tr("Recent maps")); recent_files_layout->addWidget(recent_files_headline, 0, 0, 1, 2); recent_files_list = new QListWidget(); QFont list_font = recent_files_list->font(); int pixel_size = list_font.pixelSize(); if (pixel_size > 0) { list_font.setPixelSize(pixel_size * 3 / 2); } else { pixel_size = list_font.pointSize(); list_font.setPointSize(pixel_size * 3 / 2); } recent_files_list->setFont(list_font); recent_files_list->setSpacing(pixel_size/2); recent_files_list->setCursor(Qt::PointingHandCursor); recent_files_list->setStyleSheet(QString::fromLatin1(" \ QListWidget::item:hover { \ color: palette(highlighted-text); \ background: palette(highlight); \ } ")); recent_files_layout->addWidget(recent_files_list, 1, 0, 1, 2); open_mru_file_check = new QCheckBox(tr("Open most recently used file on start")); recent_files_layout->addWidget(open_mru_file_check, 2, 0, 1, 1); QPushButton* clear_list_button = new QPushButton(tr("Clear list")); recent_files_layout->addWidget(clear_list_button, 2, 1, 1, 1); recent_files_layout->setRowStretch(1, 1); recent_files_layout->setColumnStretch(0, 1); connect(recent_files_list, &QListWidget::itemClicked, this, &HomeScreenWidgetDesktop::recentFileClicked); connect(open_mru_file_check, &QAbstractButton::clicked, controller, &HomeScreenController::setOpenMRUFile); connect(clear_list_button, &QAbstractButton::clicked, controller, &HomeScreenController::clearRecentFiles); QWidget* recent_files_widget = new QWidget(parent); recent_files_widget->setLayout(recent_files_layout); recent_files_widget->setAutoFillBackground(true); return recent_files_widget; } QWidget* HomeScreenWidgetDesktop::makeTipsWidget(HomeScreenController* controller, QWidget* parent) { QGridLayout* tips_layout = new QGridLayout(); QWidget* tips_headline = makeHeadline(tr("Tip of the day")); tips_layout->addWidget(tips_headline, 0, 0, 1, 3); tips_label = new QLabel(); tips_label->setWordWrap(true); tips_label->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); tips_check = new QCheckBox(tr("Show tip of the day")); tips_check->setChecked(true); tips_layout->addWidget(tips_check, 2, 0, 1, 1); tips_layout->addWidget(tips_label, 1, 0, 1, 3); QPushButton* prev_button = new QPushButton(QIcon(QString::fromLatin1(":/images/arrow-left.png")), tr("Previous")); tips_layout->addWidget(prev_button, 2, 1, 1, 1); QPushButton* next_button = new QPushButton(QIcon(QString::fromLatin1(":/images/arrow-right.png")), tr("Next")); tips_layout->addWidget(next_button, 2, 2, 1, 1); tips_layout->setRowStretch(1, 1); tips_layout->setColumnStretch(0, 1); tips_children.reserve(4); tips_children.push_back(tips_headline); tips_children.push_back(tips_label); tips_children.push_back(prev_button); tips_children.push_back(next_button); MainWindow* window = controller->getWindow(); connect(tips_label, &QLabel::linkActivated, window, &MainWindow::linkClicked); connect(tips_check, &QAbstractButton::clicked, controller, &HomeScreenController::setTipsVisible); connect(prev_button, &QAbstractButton::clicked, controller, &HomeScreenController::goToPreviousTip); connect(next_button, &QAbstractButton::clicked, controller, &HomeScreenController::goToNextTip); QWidget* tips_widget = new QWidget(parent); tips_widget->setLayout(tips_layout); tips_widget->setAutoFillBackground(true); return tips_widget; } void HomeScreenWidgetDesktop::setRecentFiles(const QStringList& files) { recent_files_list->clear(); for (auto&& file : files) { QListWidgetItem* new_item = new QListWidgetItem(QFileInfo(file).fileName()); new_item->setData(Qt::UserRole, file); new_item->setToolTip(file); recent_files_list->addItem(new_item); } } void HomeScreenWidgetDesktop::recentFileClicked(QListWidgetItem* item) { setEnabled(false); qApp->processEvents(QEventLoop::ExcludeUserInputEvents); QString path = item->data(Qt::UserRole).toString(); controller->getWindow()->openPath(path); setEnabled(true); } void HomeScreenWidgetDesktop::paintEvent(QPaintEvent*) { // Background QPainter p(this); p.setPen(Qt::NoPen); p.setBrush(Qt::gray); p.drawRect(rect()); } void HomeScreenWidgetDesktop::setOpenMRUFileChecked(bool state) { open_mru_file_check->setChecked(state); } void HomeScreenWidgetDesktop::setTipOfTheDay(const QString& text) { tips_label->setText(text); } void HomeScreenWidgetDesktop::setTipsVisible(bool state) { QGridLayout* layout = qobject_cast(this->layout()); for (auto widget : tips_children) { widget->setVisible(state); } if (layout) layout->setRowStretch(2, state ? 3 : 0); tips_check->setChecked(state); } //### HomeScreenWidgetMobile ### HomeScreenWidgetMobile::HomeScreenWidgetMobile(HomeScreenController* controller, QWidget* parent) : AbstractHomeScreenWidget(controller, parent) { title_pixmap = QPixmap::fromImage(QImage(QString::fromLatin1(":/images/title.png"))); title_label = new QLabel(); title_label->setPixmap(title_pixmap); title_label->setAlignment(Qt::AlignCenter); QWidget* file_list_widget = makeFileListWidget(controller, parent); examples_button = new QPushButton(tr("Examples")); connect(examples_button, &QPushButton::clicked, this, &HomeScreenWidgetMobile::showExamples); auto settings_button = new QPushButton(HomeScreenWidgetDesktop::tr("Settings")); connect(settings_button, &QPushButton::clicked, controller->getWindow(), &MainWindow::showSettings); QPushButton* help_button = new QPushButton(HomeScreenWidgetDesktop::tr("Help")); connect(help_button, &QPushButton::clicked, controller->getWindow(), &MainWindow::showHelp); QPushButton* about_button = new QPushButton(tr("About Mapper")); connect(about_button, &QPushButton::clicked, controller->getWindow(), &MainWindow::showAbout); QHBoxLayout* buttons_layout = new QHBoxLayout(); buttons_layout->setContentsMargins(0, 0, 0, 0); buttons_layout->addWidget(examples_button); buttons_layout->addStretch(1); buttons_layout->addWidget(settings_button); buttons_layout->addWidget(help_button); buttons_layout->addWidget(about_button); QGridLayout* layout = new QGridLayout(); layout->setSpacing(2 * layout->spacing()); layout->addWidget(title_label, 0, 0); layout->addWidget(file_list_widget, 1, 0); layout->addLayout(buttons_layout, 2, 0); setLayout(layout); setAutoFillBackground(false); } HomeScreenWidgetMobile::~HomeScreenWidgetMobile() { // nothing } void HomeScreenWidgetMobile::resizeEvent(QResizeEvent* event) { Q_UNUSED(event); adjustTitlePixmapSize(); } void HomeScreenWidgetMobile::adjustTitlePixmapSize() { QSize label_size = title_label->size(); int scaled_width = qRound(title_pixmap.devicePixelRatio() * label_size.width()); if (title_pixmap.width() > scaled_width) { if (title_label->pixmap()->width() != scaled_width) { label_size.setHeight(title_pixmap.height()); title_label->setPixmap(title_pixmap.scaled(label_size, Qt::KeepAspectRatio, Qt::SmoothTransformation)); } } else if (title_label->pixmap()->width() != title_pixmap.width()) { title_label->setPixmap(title_pixmap); } } void HomeScreenWidgetMobile::setRecentFiles(const QStringList& files) { Q_UNUSED(files); // nothing } void HomeScreenWidgetMobile::setOpenMRUFileChecked(bool state) { Q_UNUSED(state); // nothing } void HomeScreenWidgetMobile::setTipOfTheDay(const QString& text) { Q_UNUSED(text); // nothing } void HomeScreenWidgetMobile::setTipsVisible(bool state) { Q_UNUSED(state); // nothing } // slot void HomeScreenWidgetMobile::showExamples() { int old_size = file_list->count(); const auto paths = QDir::searchPaths(QLatin1String("data")); for(const auto& path : paths) { addFilesToFileList(file_list, path + QLatin1String("/examples")); } if (file_list->count() > old_size) { file_list_stack->setCurrentIndex(0); file_list->setCurrentRow(old_size, QItemSelectionModel::ClearAndSelect); examples_button->setEnabled(false); } } void HomeScreenWidgetMobile::showSettings() { auto window = this->window(); SettingsDialog dialog(window); dialog.setGeometry(window->geometry()); dialog.exec(); } void HomeScreenWidgetMobile::fileClicked(QListWidgetItem* item) { QString hint_text = item->data(Qt::UserRole+1).toString(); if (!hint_text.isEmpty()) QMessageBox::warning(this, ::OpenOrienteering::MainWindow::tr("Warning"), hint_text.arg(item->data(Qt::DisplayRole).toString())); setEnabled(false); qApp->processEvents(QEventLoop::ExcludeUserInputEvents); QString path = item->data(Qt::UserRole).toString(); controller->getWindow()->openPath(path); setEnabled(true); } QWidget* HomeScreenWidgetMobile::makeFileListWidget(HomeScreenController* controller, QWidget* parent) { Q_UNUSED(controller); Q_UNUSED(parent); QGridLayout* file_list_layout = new QGridLayout(); QLabel* file_list_headline = makeHeadline(tr("File list")); file_list_layout->addWidget(file_list_headline, 0, 0, 1, 2); file_list_stack = new QStackedLayout(); file_list_layout->addLayout(file_list_stack, 1, 0, 1, 2); file_list_layout->setRowStretch(1, 1); file_list = new QListWidget(); QScroller::grabGesture(file_list->viewport(), QScroller::TouchGesture); QFont list_font = file_list->font(); int pixel_size = list_font.pixelSize(); if (pixel_size > 0) { list_font.setPixelSize(pixel_size * 3 / 2); } else { pixel_size = list_font.pointSize(); list_font.setPointSize(pixel_size * 3 / 2); } file_list->setFont(list_font); file_list->setSpacing(pixel_size/2); file_list->setCursor(Qt::PointingHandCursor); file_list->setStyleSheet(QString::fromLatin1(" \ QListWidget::item:hover { \ color: palette(highlighted-text); \ background: palette(highlight); \ } ")); file_list_stack->addWidget(file_list); // Look for map files at device-specific locations #ifdef Q_OS_ANDROID const auto style = file_list->style(); StorageLocation::refresh(); const auto locations = StorageLocation::knownLocations(); for (const auto& location : *locations) { QIcon icon; QString hint_text; switch (location.hint()) { case StorageLocation::HintApplication: icon = style->standardIcon(QStyle::SP_MessageBoxInformation); hint_text = StorageLocation::fileHintTextTemplate(location.hint()); break; case StorageLocation::HintReadOnly: icon = style->standardIcon(QStyle::SP_MessageBoxWarning); hint_text = StorageLocation::fileHintTextTemplate(location.hint()); break; default: break; } QDirIterator it(location.path(), QDir::Files | QDir::NoDotAndDotDot, QDirIterator::Subdirectories); while (it.hasNext()) { it.next(); auto format = FileFormats.findFormatForFilename(it.filePath()); if (!format || !format->supportsExport()) continue; QListWidgetItem* new_item = new QListWidgetItem(it.fileInfo().fileName()); new_item->setData(Qt::UserRole, it.filePath()); new_item->setToolTip(it.filePath()); if (Q_LIKELY( location.hint() == StorageLocation::HintReadOnly || it.fileInfo().isWritable() )) { new_item->setIcon(icon); new_item->setData(Qt::UserRole+1, hint_text); } else { new_item->setIcon(style->standardIcon(QStyle::SP_MessageBoxWarning)); new_item->setData(Qt::UserRole+1, StorageLocation::fileHintTextTemplate(StorageLocation::HintReadOnly)); } file_list->addItem(new_item); } } #endif if (file_list->count() == 0) { // Remove the incorrect part from the existing translated text. /// \todo Review the text for placing the first maps auto label_text = tr("No map files found!

    Copy map files to a top-level folder named 'OOMapper' on the device or a memory card."); label_text.truncate(label_text.indexOf(QLatin1String("
    "))); QLabel* message_label = new QLabel(label_text); message_label->setWordWrap(true); file_list_stack->addWidget(message_label); file_list_stack->setCurrentWidget(message_label); } connect(file_list, &QListWidget::itemClicked, this, &HomeScreenWidgetMobile::fileClicked); QWidget* file_list_widget = new QWidget(); file_list_widget->setLayout(file_list_layout); file_list_widget->setAutoFillBackground(true); return file_list_widget; } void HomeScreenWidgetMobile::addFilesToFileList(QListWidget* file_list, const QString& path) { QDirIterator it(path, QDir::Files | QDir::NoDotAndDotDot, QDirIterator::Subdirectories); while (it.hasNext()) { it.next(); if (FileFormats.findFormatForFilename(it.filePath()) == nullptr) continue; QListWidgetItem* new_item = new QListWidgetItem(it.fileInfo().fileName()); new_item->setData(Qt::UserRole, it.filePath()); new_item->setToolTip(it.filePath()); file_list->addItem(new_item); } } } // namespace OpenOrienteering mapper-0.8.1.1/src/gui/widgets/home_screen_widget.h000066400000000000000000000150711325266516600222070ustar00rootroot00000000000000/* * Copyright 2012, 2013, 2014 Thomas Schöps, Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_HOME_SCREEN_WIDGET_H #define OPENORIENTEERING_HOME_SCREEN_WIDGET_H #include #include #include #include #include #include class QAbstractButton; class QCheckBox; class QIcon; class QLabel; class QListWidget; class QListWidgetItem; class QPaintEvent; class QPushButton; class QResizeEvent; class QStackedLayout; namespace OpenOrienteering { class HomeScreenController; /** * The user interface of the OpenOrienteering Mapper home screen. * The OpenOrienteering Mapper home screen is shown when no document is open, * for example after the program is started for the first time. */ class AbstractHomeScreenWidget : public QWidget { Q_OBJECT public: /** Creates a new AbstractHomeScreenWidget for the given controller. */ AbstractHomeScreenWidget(HomeScreenController* controller, QWidget* parent = nullptr); /** Destroys the AbstractHomeScreenWidget. */ ~AbstractHomeScreenWidget() override; public slots: /** Sets the list of recent files. */ virtual void setRecentFiles(const QStringList& files) = 0; /** Sets the "checked" state of the control for opening * the most recently used file on startup. */ virtual void setOpenMRUFileChecked(bool state) = 0; /** Sets the text of the the tip-of-the-day. */ virtual void setTipOfTheDay(const QString& text) = 0; /** Sets the visiblity of the tip-of-the-day, and * sets the "checked" state of the control for displaying the tip. */ virtual void setTipsVisible(bool state) = 0; protected: /** Returns a QLabel for displaying a headline in the home screen. */ QLabel* makeHeadline(const QString& text, QWidget* parent = nullptr) const; /** Returns a button with the given text * for triggering a top level activity in the home screen. * There will be no icon or a default icon. */ QAbstractButton* makeButton(const QString& text, QWidget* parent = nullptr) const; /** Returns a button with the given text and icon * for triggering a top level activity in the home screen. */ QAbstractButton* makeButton(const QString& text, const QIcon& icon, QWidget* parent = nullptr) const; HomeScreenController* controller; }; /** * Implementation of AbstractHomeScreenWidget for desktop PCs. */ class HomeScreenWidgetDesktop : public AbstractHomeScreenWidget { Q_OBJECT public: /** Creates a new HomeScreenWidgetDesktop for the given controller. */ HomeScreenWidgetDesktop(HomeScreenController* controller, QWidget* parent = nullptr); /** Destroys the HomeScreenWidgetDesktop. */ ~HomeScreenWidgetDesktop() override; public slots: /** Sets the list of recent files. */ void setRecentFiles(const QStringList& files) override; /** Sets the "checked" state of the control for opening * the most recently used file on startup. */ void setOpenMRUFileChecked(bool state) override; /** Sets the text of the the tip-of-the-day. */ void setTipOfTheDay(const QString& text) override; /** Sets the visiblity of the tip-of-the-day, and * sets the "checked" state of the control for displaying the tip. */ void setTipsVisible(bool state) override; protected slots: /** Opens a file when its is list item is clicked. */ void recentFileClicked(QListWidgetItem* item); protected: /** Draws the home screen background when a paint event occurs. */ void paintEvent(QPaintEvent* event) override; /** Creates the activities widget. */ QWidget* makeMenuWidget(HomeScreenController* controller, QWidget* parent = nullptr); /** Creates the recent files widget. */ QWidget* makeRecentFilesWidget(HomeScreenController* controller, QWidget* parent = nullptr); /** Creates the tip-of-the-day widget. */ QWidget* makeTipsWidget(HomeScreenController* controller, QWidget* parent = nullptr); QListWidget* recent_files_list; QCheckBox* open_mru_file_check; QLabel* tips_label; QCheckBox* tips_check; std::vector tips_children; }; /** * Implementation of AbstractHomeScreenWidget for mobile devices. */ class HomeScreenWidgetMobile : public AbstractHomeScreenWidget { Q_OBJECT public: /** Creates a new HomeScreenWidgetMobile for the given controller. */ HomeScreenWidgetMobile(HomeScreenController* controller, QWidget* parent = nullptr); /** Destroys the HomeScreenWidgetMobile. */ ~HomeScreenWidgetMobile() override; public slots: /** Sets the list of recent files. */ void setRecentFiles(const QStringList& files) override; /** Sets the "checked" state of the control for opening * the most recently used file on startup. */ void setOpenMRUFileChecked(bool state) override; /** Sets the text of the the tip-of-the-day. */ void setTipOfTheDay(const QString& text) override; /** Sets the visiblity of the tip-of-the-day, and * sets the "checked" state of the control for displaying the tip. */ void setTipsVisible(bool state) override; /** Adds the examples to the list of files * if they are not already there. */ void showExamples(); /** Shows the settings dialog, adjusted for small screens. */ void showSettings(); protected slots: /** Opens a file when its is list item is clicked. */ void fileClicked(QListWidgetItem* item); protected: /** Triggers title image adjustment on resize events. */ void resizeEvent(QResizeEvent* event) override; /** Resizes the title image to fit both the labels width and height */ void adjustTitlePixmapSize(); /** Creates the file list widget. */ QWidget* makeFileListWidget(HomeScreenController* controller, QWidget* parent = nullptr); /** Iterates over all files at the given path and adds all map files to the list */ void addFilesToFileList(QListWidget* file_list, const QString& path); private: QPixmap title_pixmap; QLabel* title_label; QStackedLayout* file_list_stack; QListWidget* file_list; QPushButton* examples_button; }; } // namespace OpenOrienteering #endif mapper-0.8.1.1/src/gui/widgets/key_button_bar.cpp000066400000000000000000000165471325266516600217300ustar00rootroot00000000000000/* * Copyright 2014 Thomas Schöps * Copyright 2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "key_button_bar.h" #include #include #include #include #include #include #include #include #include #include #include #include #include namespace OpenOrienteering { namespace { int modifierFromKeyCode(Qt::Key key_code) { switch(key_code) { case Qt::Key_Control: return Qt::ControlModifier; case Qt::Key_Shift: return Qt::ShiftModifier; case Qt::Key_Alt: return Qt::AltModifier; default: return Qt::NoModifier; } Q_UNREACHABLE(); } int keyFromModifierCode(Qt::KeyboardModifier modifier) { switch(modifier) { case Qt::ControlModifier: return Qt::Key_Control; case Qt::ShiftModifier: return Qt::Key_Shift; case Qt::AltModifier: return Qt::Key_Alt; default: return 0; } Q_UNREACHABLE(); } } // namespace KeyButtonBar::KeyButtonBar(QWidget* receiver, QWidget* parent, Qt::WindowFlags f) : QWidget(parent, f) , receiver(receiver) , active_modifiers(QGuiApplication::keyboardModifiers()) { layout = new QHBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); layout->setSpacing(layout->spacing() / 2); setLayout(layout); receiver->installEventFilter(this); } KeyButtonBar::~KeyButtonBar() = default; QToolButton* KeyButtonBar::addKeyButton(Qt::Key key_code, const QString& text, const QIcon& icon) { return addKeyButton(key_code, Qt::NoModifier, text, icon); } QToolButton* KeyButtonBar::addKeyButton(Qt::Key key_code, Qt::KeyboardModifier modifier_code, const QString& text, const QIcon& icon) { Q_ASSERT(key_code); Q_ASSERT(!modifier_code || !modifierFromKeyCode(key_code)); auto button = new QToolButton(); button->setText(text); if (!icon.isNull()) button->setIcon(icon); layout->addWidget(button); button_infos.push_back({button, key_code, modifier_code}); connect(button, &QAbstractButton::clicked, this, &KeyButtonBar::sendKeyPressAndRelease); return button; } QToolButton* KeyButtonBar::addModifierButton(Qt::KeyboardModifier modifier_code, const QString& text, const QIcon& icon) { Q_ASSERT(modifier_code); auto button = new QToolButton(); button->setText(text); button->setCheckable(true); if (!icon.isNull()) button->setIcon(icon); layout->addWidget(button); auto key_code = keyFromModifierCode(modifier_code); Q_ASSERT(key_code); button_infos.push_back({button, Qt::Key(key_code), modifier_code}); connect(button, &QAbstractButton::clicked, this, &KeyButtonBar::sendModifierChange); return button; } bool KeyButtonBar::eventFilter(QObject* /*watched*/, QEvent* event) { if (!active) return false; using std::begin; using std::end; auto find_info = [this](int key_code) -> ButtonInfo* { auto info = std::find_if(begin(button_infos), end(button_infos), [key_code](const auto& info) { return info.key == key_code; }); return info == end(button_infos) ? nullptr : &*info; }; switch (event->type()) { case QEvent::KeyPress: { auto final_modifiers = active_modifiers; auto key_event = static_cast(event); const auto key = Qt::Key(key_event->key()); if (auto modifier = modifierFromKeyCode(key)) { if (active_modifiers & modifier) return true; // Modifier already active. Consume this event. final_modifiers |= Qt::KeyboardModifier(modifier); if (auto info = find_info(key)) info->button->setChecked(true); } key_event->setModifiers(Qt::KeyboardModifiers(active_modifiers)); active_modifiers = final_modifiers; } break; case QEvent::KeyRelease: { auto final_modifiers = active_modifiers; auto key_event = static_cast(event); const auto key = Qt::Key(key_event->key()); if (auto modifier = modifierFromKeyCode(key)) { if (!(active_modifiers & modifier)) return true; // Modifier already inactive. Consume this event. final_modifiers &= ~Qt::KeyboardModifier(modifier); if (auto info = find_info(key)) info->button->setChecked(false); } key_event->setModifiers(Qt::KeyboardModifiers(active_modifiers)); active_modifiers = final_modifiers; } break; case QEvent::MouseMove: case QEvent::MouseButtonPress: case QEvent::MouseButtonRelease: case QEvent::MouseButtonDblClick: // Probably add more input events later. static_cast(event)->setModifiers(Qt::KeyboardModifiers(active_modifiers)); break; default: ; // nothing } return false; } void KeyButtonBar::showEvent(QShowEvent* event) { if (!event->spontaneous()) active = true; } void KeyButtonBar::hideEvent(QHideEvent* event) { if (!event->spontaneous()) active = false; } void KeyButtonBar::sendKeyPressAndRelease() { using std::begin; using std::end; auto button = reinterpret_cast(sender()); auto info = std::find_if(begin(button_infos), end(button_infos), [button](const auto& info) { return info.button == button; }); Q_ASSERT(info != end(button_infos)); auto original_modifiers = active_modifiers; if (info->modifier && !active_modifiers.testFlag(info->modifier)) { sendKeyPressEvent(Qt::Key(keyFromModifierCode(info->modifier))); Q_ASSERT(active_modifiers & info->modifier); } sendKeyPressEvent(info->key); sendKeyReleaseEvent(info->key); if (active_modifiers != original_modifiers) { sendKeyReleaseEvent(Qt::Key(keyFromModifierCode(info->modifier))); Q_ASSERT(!(active_modifiers & info->modifier)); } } void KeyButtonBar::sendModifierChange(bool checked) { using std::begin; using std::end; auto button = reinterpret_cast(sender()); auto info = std::find_if(begin(button_infos), end(button_infos), [button](const auto& info) { return info.button == button; }); Q_ASSERT(info != end(button_infos)); Q_ASSERT(info->modifier); if (checked) { if (!active_modifiers.testFlag(info->modifier)) { sendKeyPressEvent(info->key); } Q_ASSERT(active_modifiers & info->modifier); } else { if (active_modifiers.testFlag(info->modifier)) { sendKeyReleaseEvent(info->key); } } Q_ASSERT(active_modifiers.testFlag(info->modifier) == checked); } void KeyButtonBar::sendKeyPressEvent(Qt::Key key_code) { QKeyEvent event(QEvent::KeyPress, key_code, active_modifiers); QCoreApplication::sendEvent(receiver, &event); } void KeyButtonBar::sendKeyReleaseEvent(Qt::Key key_code) { QKeyEvent event(QEvent::KeyRelease, key_code, active_modifiers); QCoreApplication::sendEvent(receiver, &event); } } // namespace OpenOrienteering mapper-0.8.1.1/src/gui/widgets/key_button_bar.h000066400000000000000000000105231325266516600213610ustar00rootroot00000000000000/* * Copyright 2014 Thomas Schöps * Copyright 2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_KEY_BUTTON_BAR_H #define OPENORIENTEERING_KEY_BUTTON_BAR_H #include #include #include #include #include #include #include class QEvent; class QHBoxLayout; class QHideEvent; class QShowEvent; class QToolButton; namespace OpenOrienteering { /** * Shows a set of buttons for simulating keyboard input. * * This is used to simulate keys in Mapper's mobile GUI where a physical * keyboard is not present. * * For modifier keys, the GUI buttons indicate wether the keys are in pressed * state or not. However, the labels are not expected to display the name of the * key but the associated tool behaviour. The tools need to make sure that the * actual behaviour is consistent with the indicated state. */ class KeyButtonBar : public QWidget { Q_OBJECT public: /** * Constructs a new button bar acting on the given receiver. * * The constructor will install this object as an event filter on the given * receiver, independent of object ownership. */ KeyButtonBar(QWidget* receiver, QWidget* parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); ~KeyButtonBar() override; /** * Adds a button for a regular key. */ QToolButton* addKeyButton(Qt::Key key_code, const QString& text, const QIcon& icon = {}); /** * Adds a button for a combination of a regular key with a modifier key. * * By default, the button is not checkable and sends a sequence of a press * event and a relase event for the given key_code. If a modifier_code * different from Qt::NoModifier is given, and this modifier key is not * already active, a press event for the corresponding modifier key code is * sent first, and a release event for the modifier key code is send after * the the event for the key_code. * * Application code may turn this into a checkable button if it also takes * care of maintaining the state. */ QToolButton* addKeyButton(Qt::Key key_code, Qt::KeyboardModifier modifier_code, const QString& text, const QIcon& icon = {}); /** Adds a checkable button for a modifier key. * * The button sends a key press event when the user activates the checked * state, and it sends a key release event when the user unsets the checked * state. * * Upon regular input events for the corresponding modifier key, the checked * state of the button is updated accordingly. */ QToolButton* addModifierButton(Qt::KeyboardModifier modifier_code, const QString& text, const QIcon& icon = {}); /** * Returns the currently active set of modifier keys. */ Qt::KeyboardModifiers keyboardModifiers() const { return active_modifiers; } /** * The event filter synchronizes the buttons' state and changes with input events. * * It acts on the receiver widget given in the KeyButtonBar constructor. */ bool eventFilter(QObject* watched, QEvent* event) override; protected: void showEvent(QShowEvent* event) override; void hideEvent(QHideEvent* event) override; private: void sendKeyPressAndRelease(); void sendModifierChange(bool checked); void sendKeyPressEvent(Qt::Key key_code); void sendKeyReleaseEvent(Qt::Key key_code); struct ButtonInfo { QToolButton* button; Qt::Key key; Qt::KeyboardModifier modifier; }; QVarLengthArray button_infos; QWidget* receiver; QHBoxLayout* layout; Qt::KeyboardModifiers active_modifiers; bool active = false; Q_DISABLE_COPY(KeyButtonBar) }; } // namespace OpenOrienteering #endif // OPENORIENTEERING_KEY_BUTTON_BAR_H mapper-0.8.1.1/src/gui/widgets/mapper_proxystyle.cpp000066400000000000000000000123531325266516600225160ustar00rootroot00000000000000/* * Copyright 2013-2016 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "mapper_proxystyle.h" #include #include #include #include // IWYU pragma: keep #include #include #include #include #include #include #include "segmented_button_layout.h" namespace OpenOrienteering { MapperProxyStyle::MapperProxyStyle(QStyle* base_style) : QProxyStyle(base_style) { ; // Nothing } MapperProxyStyle::~MapperProxyStyle() { ; // Nothing, not inlined } void MapperProxyStyle::drawPrimitive(QStyle::PrimitiveElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget) const { auto overridden_element = element; switch (element) { case PE_IndicatorButtonDropDown: overridden_element = PE_PanelButtonTool; // Enforce same appearence. Workaround QWindowsStyle quirk. // fall through case PE_PanelButtonCommand: case PE_PanelButtonBevel: case PE_PanelButtonTool: if (int segment = widget ? widget->property("segment").toInt() : 0) { drawSegmentedButton(segment, overridden_element, option, painter, widget); return; } break; #ifdef Q_OS_ANDROID case QStyle::PE_IndicatorItemViewItemCheck: if (option->state.testFlag(QStyle::State_NoChange) || !option->state.testFlag(QStyle::State_Enabled)) { auto item = qstyleoption_cast(option); auto o = QStyleOptionViewItem{ *item }; o.state |= QStyle::State_Enabled; if (option->state.testFlag(QStyle::State_NoChange)) { o.state &= ~QStyle::State_NoChange; o.state |= QStyle::State_On; } auto opacity = painter->opacity(); painter->setOpacity(0.4); QProxyStyle::drawPrimitive(element, &o, painter, widget); painter->setOpacity(opacity); return; } break; #endif default: ; // Nothing } QProxyStyle::drawPrimitive(element, option, painter, widget); } void MapperProxyStyle::drawSegmentedButton(int segment, QStyle::PrimitiveElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget) const { painter->save(); // Background (to be clipped by the widget) int left_adj = (segment & SegmentedButtonLayout::LeftNeighbor) ? 4 : 0; int right_adj = (segment & SegmentedButtonLayout::RightNeighbor) ? 4 : 0; if (option->rect.left()) { // Subcomponent clipping painter->setClipRect(option->rect, Qt::IntersectClip); left_adj = 4; } QStyleOption mod_option(*option); mod_option.rect.adjust(-left_adj, 0, right_adj, 0); QProxyStyle::drawPrimitive(element, &mod_option, painter, widget); // Segment separators painter->setOpacity((option->state & QStyle::State_Enabled) ? 0.5 : 0.2); int frame_width = proxy()->pixelMetric(PM_DefaultFrameWidth, option, widget); mod_option.rect = option->rect.adjusted(0, frame_width, 0, -frame_width); if (left_adj) { if (option->state & QStyle::State_Sunken) painter->setPen(option->palette.dark().color()); else painter->setPen(option->palette.light().color()); painter->drawLine(option->rect.left(), option->rect.top(), option->rect.left(), option->rect.bottom()); } if (right_adj) { if (option->state & QStyle::State_Sunken) painter->setPen(option->palette.light().color()); else painter->setPen(option->palette.dark().color()); painter->drawLine(option->rect.right(), option->rect.top(), option->rect.right(), option->rect.bottom()); } painter->restore(); } int MapperProxyStyle::pixelMetric(PixelMetric metric, const QStyleOption* option, const QWidget* widget) const { switch (metric) { #ifdef Q_OS_ANDROID case QStyle::PM_ButtonIconSize: { static int s = qMax(QProxyStyle::pixelMetric(metric), QProxyStyle::pixelMetric(QStyle::PM_IndicatorWidth)); return s; } case QStyle::PM_SplitterWidth: { static int s = (QProxyStyle::pixelMetric(metric), QProxyStyle::pixelMetric(QStyle::PM_IndicatorWidth)) / 2; return s; } #endif #ifdef Q_OS_MACOS case QStyle::PM_ToolBarIconSize: { static int s = (QProxyStyle::pixelMetric(metric) + QProxyStyle::pixelMetric(QStyle::PM_SmallIconSize)) / 2; return s; } #endif default: break; } return QProxyStyle::pixelMetric(metric, option, widget); } int MapperProxyStyle::styleHint(QStyle::StyleHint hint, const QStyleOption* option, const QWidget* widget, QStyleHintReturn* return_data) const { switch (hint) { #ifdef Q_OS_ANDROID case QStyle::SH_FormLayoutWrapPolicy: return QFormLayout::WrapLongRows; #endif default: break; } return QProxyStyle::styleHint(hint, option, widget, return_data); } } // namespace OpenOrienteering mapper-0.8.1.1/src/gui/widgets/mapper_proxystyle.h000066400000000000000000000057331325266516600221670ustar00rootroot00000000000000/* * Copyright 2013, 2016 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_MAPPER_PROXYSTYLE_H #define OPENORIENTEERING_MAPPER_PROXYSTYLE_H #include #include #include #include class QPainter; class QStyleHintReturn; class QStyleOption; class QWidget; namespace OpenOrienteering { /** * MapperProxyStyle customizes the platform's base style. * * It supports the implementation of missing UI elements, and * it adjustes the size of some elements to more practical values. */ class MapperProxyStyle : public QProxyStyle { Q_OBJECT public: /** * Constructs a new MapperProxyStyle. * * Being a QProxyStyle, MapperProxyStyle takes ownership of the base style, * if given. */ MapperProxyStyle(QStyle* base_style = nullptr); /** * Destroys the object. */ ~MapperProxyStyle() override; /** * Draws the given primitive element. * * Implements rendering of segmented buttons for all platforms. * * On Android: * - QStyle::PE_IndicatorItemViewItemCheck is modified for disabled and tristate checkboxes. */ void drawPrimitive(PrimitiveElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget = nullptr) const override; /** * Returns some adjusted pixel metrics. * * On OS X: * - QStyle::PM_ToolBarIconSize is adjusted (reduced) towards QStyle::PM_SmallIconSize. * * On Android: * - QStyle::PM_ButtonIconSize is enlarged to QStyle::PM_IndicatorWidth (checkbox size), * - QStyle::PM_ToolBarIconSize is adjusted (enlarged) towards QStyle::PM_SmallIconSize. */ int pixelMetric(PixelMetric metric, const QStyleOption* option = nullptr, const QWidget* widget = nullptr) const override; /** * Returns adjusted style hints. * * On Android: * - QStyle::SH_FormLayoutWrapPolicy is QFormLayout::QFormLayout::WrapLongRows. */ int styleHint(StyleHint hint, const QStyleOption* option = nullptr, const QWidget* widget = nullptr, QStyleHintReturn* return_data = nullptr) const override; private: void drawSegmentedButton(int segment, PrimitiveElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget) const; Q_DISABLE_COPY(MapperProxyStyle) }; } // namespace OpenOrienteering #endif // OPENORIENTEERING_MAPPER_PROXYSTYLE_H mapper-0.8.1.1/src/gui/widgets/measure_widget.cpp000066400000000000000000000130441325266516600217120ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2012-2015 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "measure_widget.h" #include #include #include "core/map.h" #include "core/objects/object.h" #include "core/symbols/symbol.h" #include "core/symbols/area_symbol.h" #include "core/symbols/line_symbol.h" namespace OpenOrienteering { MeasureWidget::MeasureWidget(Map* map, QWidget* parent) : QTextBrowser(parent) , map(map) { QScroller::grabGesture(viewport(), QScroller::TouchGesture); connect(map, &Map::objectSelectionChanged, this, &MeasureWidget::objectSelectionChanged); connect(map, &Map::selectedObjectEdited, this, &MeasureWidget::objectSelectionChanged); connect(map, &Map::symbolChanged, this, &MeasureWidget::objectSelectionChanged); objectSelectionChanged(); } MeasureWidget::~MeasureWidget() = default; void MeasureWidget::objectSelectionChanged() { QString headline; // inline HTML QString body; // HTML blocks QString extra_text; // inline HTML auto& selected_objects = map->selectedObjects(); if (selected_objects.empty()) { extra_text = tr("No object selected."); } else if (selected_objects.size() > 1) { extra_text = tr("%1 objects selected.").arg(locale().toString(map->getNumSelectedObjects())); } else { const Object* object = *begin(selected_objects); const Symbol* symbol = object->getSymbol(); headline = symbol->getNumberAsString() + QLatin1Char(' ') + symbol->getName(); if (object->getType() != Object::Path) { extra_text = tr("The selected object is not a path."); } else { body = QLatin1String{ "" }; static const QString table_row{ QLatin1String{ "" } }; double paper_to_real = 0.001 * map->getScaleDenominator(); object->update(); const PathPartVector& parts = static_cast(object)->parts(); Q_ASSERT(!parts.empty()); auto paper_length = parts.front().length(); auto real_length = paper_length * paper_to_real; auto paper_length_text = locale().toString(paper_length, 'f', 2); auto real_length_text = locale().toString(real_length, 'f', 0); if (symbol->getContainedTypes() & Symbol::Area) { body.append(table_row.arg(tr("Boundary length:"), paper_length_text, tr("mm", "millimeters"), real_length_text, tr("m", "meters"))); auto paper_area = parts.front().calculateArea(); if (parts.size() > 1) { paper_area *= 2; for (const auto& part : parts) paper_area -= part.calculateArea(); } double real_area = paper_area * paper_to_real * paper_to_real; auto paper_area_text = locale().toString(paper_area, 'f', 2); auto real_area_text = locale().toString(real_area, 'f', 0); body.append(table_row.arg(tr("Area:"), paper_area_text, trUtf8("mm²", "square millimeters"), real_area_text , trUtf8("m²", "square meters"))); auto minimum_area = 0.0; auto minimum_area_text = QString{ }; if (symbol->getType() == Symbol::Area) { minimum_area = 0.001 * static_cast(symbol)->getMinimumArea(); minimum_area_text = locale().toString(minimum_area, 'f', 2); } if (paper_area < minimum_area && paper_area_text != minimum_area_text) { extra_text = QLatin1String("") + tr("This object is too small.") + QLatin1String("
    ") + tr("The minimimum area is %1 %2.").arg(minimum_area_text, trUtf8("mm²")) + QLatin1String("
    "); } extra_text.append(tr("Note: Boundary length and area are correct only if there are no self-intersections and holes are used as such.")); } else { body.append(table_row.arg(tr("Length:"), paper_length_text, tr("mm", "millimeters"), real_length_text, tr("m", "meters"))); auto minimum_length = 0.0; auto minimum_length_text = QString{ }; if (symbol->getType() == Symbol::Line) { minimum_length = 0.001 * static_cast(symbol)->getMinimumLength(); minimum_length_text = locale().toString(minimum_length, 'f', 2); } if (paper_length < minimum_length && paper_length_text != minimum_length_text) { extra_text = QLatin1String("") + tr("This line is too short.") + QLatin1String("
    ") + tr("The minimum length is %1 %2.").arg(minimum_length_text, tr("mm")); } } body.append(QLatin1String("
    %1%2 %3(%4 %5)
    ")); } } if (!extra_text.isEmpty()) body.append(QLatin1String("

    ") + extra_text + QLatin1String("

    ")); setHtml(QLatin1String("

    ") + headline + QLatin1String("

    ") + body); } } // namespace OpenOrienteering mapper-0.8.1.1/src/gui/widgets/measure_widget.h000066400000000000000000000031141325266516600213540ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2015 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_MEASURE_WIDGET_H #define OPENORIENTEERING_MEASURE_WIDGET_H #include #include class QWidget; namespace OpenOrienteering { class Map; /** * The widget which is shown in a dock widget when the measure tool is active. * Displays information about the currently selected objects. */ class MeasureWidget : public QTextBrowser { Q_OBJECT public: /** Creates a new MeasureWidget for a given map. */ MeasureWidget(Map* map, QWidget* parent = nullptr); /** Destroys the MeasureWidget. */ ~MeasureWidget() override; protected slots: /** * Is called when the object selection in the map changes. * Updates the widget content. */ void objectSelectionChanged(); private: Map* map; }; } // namespace OpenOrienteering #endif mapper-0.8.1.1/src/gui/widgets/pie_menu.cpp000066400000000000000000000226521325266516600205140ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2014 Thomas Schöps, Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "pie_menu.h" #include #include #include #include #include #include #include #include "settings.h" #include "util/backports.h" namespace OpenOrienteering { PieMenu::PieMenu(QWidget* parent) : QWidget(parent, Qt::Popup | Qt::FramelessWindowHint), // NOTE: use Qt::Window for debugging to avoid mouse grab minimum_action_count(3), icon_size(24), active_action(nullptr), actions_changed(true), clicked(false) { setCursor(QCursor(Qt::ArrowCursor)); setAttribute(Qt::WA_OpaquePaintEvent); setAttribute(Qt::WA_ShowWithoutActivating); setAutoFillBackground(false); setMouseTracking(true); auto scale = Settings::getInstance().getSetting(Settings::General_PixelsPerInch).toReal() / 96.0; if (scale > 1.5) { icon_size = qRound(icon_size * scale); } updateCachedState(); } int PieMenu::minimumActionCount() const { return minimum_action_count; } void PieMenu::setMinimumActionCount(int count) { if (count >= 3 && count != minimum_action_count) { minimum_action_count = count; actions_changed = true; update(); } } int PieMenu::visibleActionCount() const { const auto actions = this->actions(); return int(std::count_if(actions.begin(), actions.end(), [](const auto action) { return action->isVisible() && !action->isSeparator(); })); } bool PieMenu::isEmpty() const { return visibleActionCount() == 0; } void PieMenu::clear() { const auto actions = this->actions(); for (auto action : actions) removeAction(action); } int PieMenu::iconSize() const { return icon_size; } void PieMenu::setIconSize(int icon_size) { if (icon_size > 0) { this->icon_size = icon_size; actions_changed = true; update(); } } QAction* PieMenu::actionAt(const QPoint& pos) const { const auto actions = this->actions(); for (auto action : actions) { if (geometries.contains(action) && geometries[action].area.containsPoint(pos, Qt::WindingFill)) { return action; } } return nullptr; } PieMenu::ItemGeometry PieMenu::actionGeometry(QAction* action) const { ItemGeometry geometry; if (geometries.contains(action)) geometry = geometries[action]; return geometry; } QAction* PieMenu::activeAction() const { return active_action; } void PieMenu::setActiveAction(QAction* action) { QAction* const prev_action = active_action; active_action = (action && action->isEnabled() && action->isVisible() && !action->isSeparator()) ? action : nullptr; if (isVisible()) { if (active_action && active_action != prev_action) { active_action->activate(QAction::Hover); emit hovered(active_action); active_action->showStatusText(0); } else if (prev_action && !active_action) { QString empty_string; QStatusTipEvent e(empty_string); QApplication::sendEvent(parent(), &e); } update(); } } void PieMenu::popup(const QPoint pos) { updateCachedState(); // We need the current total_radius. QPoint cursor_pos = QCursor::pos(); QRect screen_rect = qApp->desktop()->availableGeometry(cursor_pos); if (cursor_pos.x() > screen_rect.right() - total_radius) cursor_pos.setX(screen_rect.right() - total_radius); else if (cursor_pos.x() < total_radius) cursor_pos.setX(total_radius); if (cursor_pos.y() > screen_rect.bottom() - total_radius) cursor_pos.setY(screen_rect.bottom() - total_radius); else if (cursor_pos.y() < total_radius) cursor_pos.setY(total_radius); setGeometry(pos.x() - total_radius, pos.y() - total_radius, 2 * total_radius, 2 * total_radius); clicked = false; active_action = nullptr; emit aboutToShow(); show(); } void PieMenu::actionEvent(QActionEvent* event) { if (event->type() == QEvent::ActionRemoved) { QAction* const action = event->action(); geometries.remove(action); if (action == active_action) setActiveAction(nullptr); } actions_changed = true; update(); QWidget::actionEvent(event); } void PieMenu::hideEvent(QHideEvent* event) { if (!event->spontaneous()) { emit aboutToHide(); setActiveAction(nullptr); QString empty_string; QStatusTipEvent e(empty_string); QApplication::sendEvent(parent(), &e); } QWidget::hideEvent(event); } void PieMenu::mouseMoveEvent(QMouseEvent* event) { setActiveAction(actionAt(event->pos())); event->accept(); } void PieMenu::mousePressEvent(QMouseEvent* event) { if (event->button() == Qt::LeftButton || event->button() == Qt::RightButton) { if (mask().contains(event->pos())) { clicked = true; setActiveAction(actionAt(event->pos())); } else { hide(); } event->accept(); return; } QWidget::mousePressEvent(event); } void PieMenu::mouseReleaseEvent(QMouseEvent* event) { if (event->button() == Qt::LeftButton || event->button() == Qt::RightButton) { if (active_action) { Q_ASSERT(active_action->isEnabled()); Q_ASSERT(active_action->isVisible()); Q_ASSERT(!active_action->isSeparator()); active_action->activate(QAction::Trigger); emit triggered(active_action); hide(); } else if (clicked && !mask().contains(event->pos())) { hide(); } clicked = false; event->accept(); return; } QWidget::mouseReleaseEvent(event); } void PieMenu::paintEvent(QPaintEvent* event) { updateCachedState(); QStyleOptionMenuItem option; option.initFrom(this); QPalette& palette = option.palette; palette.setCurrentColorGroup(QPalette::Active); // Draw on the widget QPainter painter(this); painter.setClipRect(event->rect()); painter.setRenderHint(QPainter::Antialiasing, true); // Background QPen pen(palette.color(QPalette::Dark)); pen.setWidth(1); painter.setPen(pen); painter.setBrush(palette.brush(QPalette::Button)); painter.drawConvexPolygon(outer_border); painter.setBrush(Qt::NoBrush); painter.drawConvexPolygon(inner_border); // Items const auto actions = this->actions(); for (auto action : actions) { if (!geometries.contains(action)) continue; QPolygon area = geometries[action].area; QIcon::Mode mode = QIcon::Normal; if (action->isChecked()) { mode = QIcon::Selected; QPen pen(palette.color(QPalette::Dark)); pen.setWidth(1); painter.setPen(pen); painter.setBrush(palette.color(QPalette::Button).darker(120)); painter.setOpacity(1.0); painter.drawConvexPolygon(area); } else if (action->isEnabled() && action == active_action) { mode = QIcon::Active; QPen pen(palette.color(QPalette::Dark)); pen.setWidth(1); painter.setPen(pen); painter.setBrush(clicked ? palette.color(QPalette::Button).darker(120) : palette.color(QPalette::Button).lighter(120)); painter.setOpacity(1.0); painter.drawConvexPolygon(area); } else if (!action->isEnabled()) { mode = QIcon::Disabled; painter.setOpacity(0.4); } else { painter.setOpacity(1.0); } // Draw icon QPixmap pixmap = action->icon().pixmap(icon_size, mode); QPoint midpoint = geometries[action].icon_pos; painter.drawPixmap(QPoint(midpoint.x() - pixmap.width() / 2, midpoint.y() - pixmap.height() / 2), pixmap); } } void PieMenu::updateCachedState() { if (actions_changed) { int action_count = qMax(minimum_action_count, visibleActionCount()); outer_border.resize(action_count); inner_border.resize(action_count); double half_angle = M_PI / action_count; double inner_radius = 0.5 * icon_size; double inner_corner_radius = inner_radius / cos(half_angle); double icon_padding_inner = 0.7 * icon_size; double icon_radius = inner_radius + icon_padding_inner + 0.5 * icon_size; double icon_padding_outer = 0.2 * icon_size; double outer_corner_radius = (inner_radius + icon_padding_inner + icon_size + icon_padding_outer) / cos(half_angle); total_radius = qCeil(outer_corner_radius); // The total shape for (int i = 0; i < action_count; ++i) { double angle = (2*i + 1) * half_angle; inner_border.setPoint(i, getPoint(inner_corner_radius, angle)); outer_border.setPoint(i, getPoint(outer_corner_radius, angle)); } setMask(outer_border.subtracted(inner_border)); // The items int i = 0; const auto actions = this->actions(); for (auto action : actions) { if (action->isVisible() && !action->isSeparator()) { ItemGeometry geometry; geometry.area.resize(4); double angle = (2*i - 1) * half_angle; geometry.area.setPoint(0, getPoint(inner_corner_radius, angle)); geometry.area.setPoint(1, inner_border.point(i)); geometry.area.setPoint(2, outer_border.point(i)); geometry.area.setPoint(3, getPoint(outer_corner_radius, angle)); angle = 2*i * half_angle; geometry.icon_pos = getPoint(icon_radius, angle); geometries[action] = geometry; ++i; } } actions_changed = false; } } } // namespace OpenOrienteering mapper-0.8.1.1/src/gui/widgets/pie_menu.h000066400000000000000000000113431325266516600201540ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2014 Thomas Schöps, Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_PIE_MENU_H #define OPENORIENTEERING_PIE_MENU_H #include #include #include #include #include #include #include class QAction; class QActionEvent; class QHideEvent; class QMouseEvent; class QPaintEvent; namespace OpenOrienteering { /** * Displays a pie menu. * * This class has an API and behavior similar to QMenu, * but is neither a subclass nor a full replacement. */ class PieMenu : public QWidget { Q_OBJECT public: /** Stores the geometry of a particular item in the PieMenu. */ struct ItemGeometry { /** The area covered by the item. */ QPolygon area; /** The position of the center of the item. */ QPoint icon_pos; }; /** Constructs a pie menu with default settings. */ PieMenu(QWidget* parent = nullptr); /** Returns the minimum number of actions in the menu. */ int minimumActionCount() const; /** Sets the minimum number of actions in the menu (must be at least three). */ void setMinimumActionCount(int count); /** Returns the current number of visible actions. */ int visibleActionCount() const; /** Removes all actions from the menu. * Other than QMenu::clear(), this will not immediately delete any action, * even if owned by this object. (Actions owned by this object will be * deleted when this objected is deleted.) * @see QMenu::clear() */ void clear(); /** Returns true if there are no visible actions in the menu, * false otherwise. * @see QMenu::isEmpty */ bool isEmpty() const; /** Returns the icon size (single dimension). */ int iconSize() const; /** Sets the icon size (single dimension). * @param icon_size the new size (at least 1) */ void setIconSize(int icon_size); /** Returns the action at pos. * Returns nullptr if there is no action at this place. */ QAction* actionAt(const QPoint& pos) const; /** Returns the geometry of the given action. * The action must be enabled and visible. * Otherwise the geometry will be empty. * @see QMenu::actionGeometry */ ItemGeometry actionGeometry(QAction* action) const; /** Returns the currently highlighted action. * Returns nullptr if no action is highlighted. * @see QMenu::activeAction */ QAction* activeAction() const; /** Sets the currently highlighted action. * The action must be enabled and visible. * Otherwise there will be no highlighted action. * @see QMenu::setActiveAction */ void setActiveAction(QAction* action); /** Shows the menu at the given absolute position. * @see QMenu::popup */ void popup(const QPoint pos); signals: /** This signal is emitted just before the menu is shown. * @see QMenu::aboutToShow */ void aboutToShow(); /** This signal is emitted just before the menu is hidden. * @see QMenu::aboutToHide */ void aboutToHide(); /** This signal is emitted when a menu item is highlighted. * @see QMenu::hovered */ void hovered(QAction* action); /** This signal is emitted when a menu item's action is triggered. * @see QMenu::triggered */ void triggered(QAction* action); protected: void actionEvent(QActionEvent* event) override; void hideEvent(QHideEvent* event) override; void mouseMoveEvent(QMouseEvent* event) override; void mousePressEvent(QMouseEvent* event) override; void mouseReleaseEvent(QMouseEvent* event) override; void paintEvent(QPaintEvent* event) override; void updateCachedState(); QPoint getPoint(double radius, double angle) const; int minimum_action_count; int icon_size; QAction* active_action; bool actions_changed; bool clicked; int total_radius; QPolygon outer_border; QPolygon inner_border; QHash< QAction*, ItemGeometry > geometries; }; // ### PieMenu inline code ### inline QPoint PieMenu::getPoint(double radius, double angle) const { return QPoint(total_radius + qRound(radius * -sin(angle)), total_radius + qRound(radius * -cos(angle))); } } // namespace OpenOrienteering #endif mapper-0.8.1.1/src/gui/widgets/segmented_button_layout.cpp000066400000000000000000000035431325266516600236540ustar00rootroot00000000000000/* * Copyright 2013 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "segmented_button_layout.h" #include #include // IWYU pragma: keep #include namespace OpenOrienteering { SegmentedButtonLayout::SegmentedButtonLayout() : QHBoxLayout() { setContentsMargins(0, 0, 0, 0); setSpacing(0); } SegmentedButtonLayout::SegmentedButtonLayout(QWidget* parent) : QHBoxLayout(parent) { setContentsMargins(0, 0, 0, 0); setSpacing(0); } SegmentedButtonLayout::~SegmentedButtonLayout() { ; // Nothing, not inlined } void SegmentedButtonLayout::invalidate() { int num_items = count(); QWidget* first_widget = nullptr; QWidget* widget = nullptr; for (int i = 0; i < num_items; ++i) { widget = itemAt(i)->widget(); if (!widget) continue; // widget->setStyle(segmented_style); if (!first_widget) { first_widget = widget; widget->setProperty("segment", RightNeighbor); } else { widget->setProperty("segment", BothNeighbors); } } if (widget) widget->setProperty("segment", (widget == first_widget) ? NoNeighbors : LeftNeighbor); QHBoxLayout::invalidate(); } } // namespace OpenOrienteering mapper-0.8.1.1/src/gui/widgets/segmented_button_layout.h000066400000000000000000000041431325266516600233160ustar00rootroot00000000000000/* * Copyright 2013 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_SEGMENTED_BUTTON_LAYOUT_H #define OPENORIENTEERING_SEGMENTED_BUTTON_LAYOUT_H #include #include #include class QWidget; namespace OpenOrienteering { /** * SegmentedButtonLayout is a horizontal box layout with no margin and no * spacing which will mark the contained widgets as being segments having a * left and/or right neighbor. * * MapperProxyStyle uses this information to make buttons from a single * SegmentedButtonLayout appear as a single segmented button. */ class SegmentedButtonLayout : public QHBoxLayout { Q_OBJECT public: /** * Constructs a new SegmentedButtonLayout. */ SegmentedButtonLayout(); /** * Constructs a new SegmentedButtonLayout for the given parent. */ explicit SegmentedButtonLayout(QWidget* parent); /** * Destroys the object. */ ~SegmentedButtonLayout() override; /** * Resets the information about neighboring segments and any other cached * information about the layout. */ void invalidate() override; /** * Types of segment neighborhood. */ enum Segment { NoNeighbors = 0x00, RightNeighbor = 0x01, LeftNeighbor = 0x02, BothNeighbors = RightNeighbor | LeftNeighbor }; private: Q_DISABLE_COPY(SegmentedButtonLayout) }; } // namespace OpenOrienteering #endif // OPENORIENTEERING_SEGMENTED_BUTTON_LAYOUT_H mapper-0.8.1.1/src/gui/widgets/settings_page.cpp000066400000000000000000000020111325266516600215320ustar00rootroot00000000000000/* * Copyright 2012 Jan Dalheimer * Copyright 2012-2016 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "settings_page.h" namespace OpenOrienteering { SettingsPage::SettingsPage(QWidget* parent) : QWidget(parent) { // nothing } SettingsPage::~SettingsPage() = default; } // namespace OpenOrienteering mapper-0.8.1.1/src/gui/widgets/settings_page.h000066400000000000000000000043041325266516600212060ustar00rootroot00000000000000/* * Copyright 2012 Jan Dalheimer * Copyright 2013-2016 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_SETTINGS_PAGE_H #define OPENORIENTEERING_SETTINGS_PAGE_H #include #include #include #include #include "settings.h" namespace OpenOrienteering { /** * A widget which serves as a page in the SettingsDialog. */ class SettingsPage : public QWidget { Q_OBJECT public: /** * Constructs a new settings page. */ explicit SettingsPage(QWidget* parent = nullptr); /** * Destructor. */ ~SettingsPage() override; /** * Returns the title of this page. */ virtual QString title() const = 0; public slots: /** * This slot is to transfer the input fields to the settings. */ virtual void apply() = 0; /** * This slot is to reset all input widgets to the state of the settings. */ virtual void reset() = 0; protected: /** * Convenience function for retrieving a setting. */ static QVariant getSetting(Settings::SettingsEnum setting); /** * Convenience function for changing a setting. */ template static void setSetting(Settings::SettingsEnum setting, T value); }; inline QVariant SettingsPage::getSetting(Settings::SettingsEnum setting) { return Settings::getInstance().getSetting(setting); } template void SettingsPage::setSetting(Settings::SettingsEnum setting, T value) { Settings::getInstance().setSetting(setting, QVariant{ value }); } } // namespace OpenOrienteering #endif mapper-0.8.1.1/src/gui/widgets/symbol_dropdown.cpp000066400000000000000000000124451325266516600221330ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2012, 2013, 2014, 2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "symbol_dropdown.h" #include #include #include #include #include #include #include #include #include #include #include "core/map.h" #include "core/symbols/combined_symbol.h" #include "core/symbols/symbol.h" #include "gui/util_gui.h" #include "util/backports.h" // allow explicit use of Symbol pointers in QVariant Q_DECLARE_METATYPE(OpenOrienteering::Symbol*) namespace OpenOrienteering { // ### SymbolDropDown ### SymbolDropDown::SymbolDropDown(const Map* map, int filter, const Symbol* initial_symbol, const Symbol* excluded_symbol, QWidget* parent) : QComboBox(parent) { num_custom_items = 0; addItem(tr("- none -"), QVariant::fromValue(nullptr)); int size = map->getNumSymbols(); for (int i = 0; i < size; ++i) { const auto symbol = map->getSymbol(i); if (!(symbol->getType() & filter)) continue; if (symbol == excluded_symbol) continue; if (symbol->getType() == Symbol::Combined) // TODO: if point objects start to be able to contain objects of other ordinary symbols, add a check for these here, too, to prevent circular references { auto combined_symbol = static_cast(symbol); if (combined_symbol->containsSymbol(excluded_symbol)) continue; } auto symbol_name = QString{symbol->getNumberAsString() + QLatin1Char(' ') + Util::plainText(map->translate(symbol->getName()))}; addItem(QPixmap::fromImage(symbol->getIcon(map)), symbol_name, QVariant::fromValue(symbol)); } setSymbol(initial_symbol); } SymbolDropDown::~SymbolDropDown() = default; const Symbol* SymbolDropDown::symbol() const { auto data = itemData(currentIndex()); if (data.canConvert()) return data.value(); else return nullptr; } void SymbolDropDown::setSymbol(const Symbol* symbol) { setCurrentIndex(findData(QVariant::fromValue(symbol))); } void SymbolDropDown::addCustomItem(const QString& text, int id) { insertItem(1 + num_custom_items, text, QVariant{id}); ++num_custom_items; } int SymbolDropDown::customID() const { auto data = itemData(currentIndex()); if (data.canConvert()) return data.value(); else return -1; } void SymbolDropDown::setCustomItem(int id) { setCurrentIndex(findData(QVariant{id})); } // ### SymbolDropDownDelegate ### SymbolDropDownDelegate::SymbolDropDownDelegate(int symbol_type_filter, QObject* parent) : QItemDelegate{parent} , symbol_type_filter{symbol_type_filter} { // nothing else } SymbolDropDownDelegate::~SymbolDropDownDelegate() = default; QWidget* SymbolDropDownDelegate::createEditor(QWidget* parent, const QStyleOptionViewItem&, const QModelIndex& index) const { auto list = index.data(Qt::UserRole).toList(); auto widget = new SymbolDropDown(list.at(0).value(), symbol_type_filter, list.at(1).value(), nullptr, parent); connect(widget, QOverload::of(&QComboBox::currentIndexChanged), this, &SymbolDropDownDelegate::emitCommitData); return widget; } void SymbolDropDownDelegate::setEditorData(QWidget* editor, const QModelIndex& index) const { auto widget = static_cast(editor); auto symbol = index.data(Qt::UserRole).toList().at(1).value(); widget->setSymbol(symbol); } void SymbolDropDownDelegate::setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const { auto widget = static_cast(editor); auto symbol = widget->symbol(); auto list = index.data(Qt::UserRole).toList(); list[1] = qVariantFromValue(symbol); model->setData(index, list, Qt::UserRole); if (symbol) { auto map = list.at(0).value(); auto text = QString{symbol->getNumberAsString() + QLatin1Char(' ') + Util::plainText(map->translate(symbol->getName()))}; model->setData(index, QVariant{text}, Qt::EditRole); model->setData(index, symbol->getIcon(list[0].value()), Qt::DecorationRole); } else { model->setData(index, tr("- None -"), Qt::EditRole); model->setData(index, {}, Qt::DecorationRole); } } void SymbolDropDownDelegate::updateEditorGeometry(QWidget* editor, const QStyleOptionViewItem& option, const QModelIndex&) const { editor->setGeometry(option.rect); } void SymbolDropDownDelegate::emitCommitData() { emit commitData(qobject_cast(sender())); } } // namespace OpenOrienteering mapper-0.8.1.1/src/gui/widgets/symbol_dropdown.h000066400000000000000000000065121325266516600215760ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2012, 2013, 2014, 2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_SYMBOL_DROPDOWN_H #define OPENORIENTEERING_SYMBOL_DROPDOWN_H #include #include #include #include // IWYU pragma: no_include class QAbstractItemModel; class QModelIndex; class QStyleOptionViewItem; class QWidget; namespace OpenOrienteering { class Map; class Symbol; /** * Drop down combobox for selecting a symbol. */ class SymbolDropDown : public QComboBox { Q_OBJECT public: /** * Creates a SymbolDropDown. * @param map Map in which to choose a symbol. * @param filter Bitwise-or combination of the allowed Symbol::Type types. * @param initial_symbol Initial choice or nullptr for "- none -". * @param excluded_symbol Symbol to exclude from the list or nullptr. * @param parent QWidget parent. */ SymbolDropDown(const Map* map, int filter, const Symbol* initial_symbol = nullptr, const Symbol* excluded_symbol = nullptr, QWidget* parent = nullptr); ~SymbolDropDown() override; /** Returns the selected symbol or nullptr if no symbol selected */ const Symbol* symbol() const; /** Sets the selection to the given symbol */ void setSymbol(const Symbol* symbol); /** Adds a custom text item below the topmost "- none -" which * can be identified by the given id. */ void addCustomItem(const QString& text, int id); /** Returns the id of the current item if it is a custom item, * or -1 otherwise */ int customID() const; /** Sets the selection to the custom item with the given id */ void setCustomItem(int id); protected slots: // TODO: react to changes in the map (not important as long as that cannot // happen as long as a SymbolDropDown is shown, which is the case currently) private: int num_custom_items; }; /** * Qt item delegate for SymbolDropDown. */ class SymbolDropDownDelegate : public QItemDelegate { Q_OBJECT public: SymbolDropDownDelegate(int symbol_type_filter, QObject* parent = nullptr); ~SymbolDropDownDelegate() override; QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const override; void setEditorData(QWidget* editor, const QModelIndex& index) const override; void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const override; void updateEditorGeometry(QWidget* editor, const QStyleOptionViewItem& option, const QModelIndex& index) const override; private slots: void emitCommitData(); private: const int symbol_type_filter; }; } // namespace OpenOrienteering #endif mapper-0.8.1.1/src/gui/widgets/symbol_render_widget.cpp000066400000000000000000000747171325266516600231330ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2014 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "symbol_render_widget.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "settings.h" #include "core/map.h" #include "core/objects/object.h" #include "core/symbols/area_symbol.h" #include "core/symbols/combined_symbol.h" #include "core/symbols/line_symbol.h" #include "core/symbols/point_symbol.h" #include "core/symbols/symbol.h" #include "core/symbols/symbol_icon_decorator.h" #include "core/symbols/text_symbol.h" #include "gui/symbols/symbol_setting_dialog.h" #include "gui/widgets/symbol_tooltip.h" #include "util/backports.h" #include "util/overriding_shortcut.h" namespace OpenOrienteering { namespace MimeType { /// The index of a symbol during drag-and-drop const QString OpenOrienteeringSymbolIndex() { return QStringLiteral("openorienteering/symbol_index"); } /// Symbol definitions const QString OpenOrienteeringSymbols() { return QStringLiteral("openorienteering/symbols"); } } // namespace MimeType //### SymbolRenderWidget ### SymbolRenderWidget::SymbolRenderWidget(Map* map, bool mobile_mode, QWidget* parent) : QWidget(parent) , map(map) , mobile_mode(mobile_mode) , selection_locked(false) , dragging(false) , current_symbol_index(-1) , hover_symbol_index(-1) , last_drop_pos(-1) , last_drop_row(-1) , icon_size(Settings::getInstance().getSymbolWidgetIconSizePx()) , icons_per_row(6) , num_rows(5) , preferred_size(icons_per_row * icon_size, num_rows * icon_size) , hidden_symbol_decoration(new HiddenSymbolDecorator(icon_size)) , protected_symbol_decoration(new ProtectedSymbolDecorator(icon_size)) { setBackgroundRole(QPalette::Base); setMouseTracking(true); setFocusPolicy(Qt::ClickFocus); setAcceptDrops(true); QShortcut* description_shortcut = new OverridingShortcut( QKeySequence(tr("F1", "Shortcut for displaying the symbol's description")), window() ); tooltip = new SymbolToolTip(this, description_shortcut); // TODO: Use a placeholder in the literal and pass the actual shortcut's string representation. setStatusTip(tr("For symbols with description, press F1 while the tooltip is visible to show it")); context_menu = new QMenu(this); QMenu* new_menu = new QMenu(tr("New symbol"), context_menu); new_menu->setIcon(QIcon(QStringLiteral(":/images/plus.png"))); /*QAction* new_point_action =*/ new_menu->addAction(tr("Point"), this, SLOT(newPointSymbol())); /*QAction* new_line_action =*/ new_menu->addAction(tr("Line"), this, SLOT(newLineSymbol())); /*QAction* new_area_action =*/ new_menu->addAction(tr("Area"), this, SLOT(newAreaSymbol())); /*QAction* new_text_action =*/ new_menu->addAction(tr("Text"), this, SLOT(newTextSymbol())); /*QAction* new_combined_action =*/ new_menu->addAction(tr("Combined"), this, SLOT(newCombinedSymbol())); context_menu->addMenu(new_menu); edit_action = context_menu->addAction(tr("Edit"), this, SLOT(editSymbol())); duplicate_action = context_menu->addAction(QIcon(QStringLiteral(":/images/tool-duplicate.png")), tr("Duplicate"), this, SLOT(duplicateSymbol())); delete_action = context_menu->addAction(QIcon(QStringLiteral(":/images/minus.png")), tr("Delete"), this, SLOT(deleteSymbols())); scale_action = context_menu->addAction(QIcon(QStringLiteral(":/images/tool-scale.png")), tr("Scale..."), this, SLOT(scaleSymbol())); context_menu->addSeparator(); copy_action = context_menu->addAction(QIcon(QStringLiteral(":/images/copy.png")), tr("Copy"), this, SLOT(copySymbols())); paste_action = context_menu->addAction(QIcon(QStringLiteral(":/images/paste.png")), tr("Paste"), this, SLOT(pasteSymbols())); context_menu->addSeparator(); switch_symbol_action = context_menu->addAction(QIcon(QStringLiteral(":/images/tool-switch-symbol.png")), tr("Switch symbol of selected objects"), this, SIGNAL(switchSymbolClicked())); fill_border_action = context_menu->addAction(QIcon(QStringLiteral(":/images/tool-fill-border.png")), tr("Fill / Create border for selected objects"), this, SIGNAL(fillBorderClicked())); // text will be filled in by updateContextMenuState() select_objects_action = context_menu->addAction(QIcon(QStringLiteral(":/images/tool-edit.png")), {}, this, SLOT(selectObjectsExclusively())); select_objects_additionally_action = context_menu->addAction(QIcon(QStringLiteral(":/images/tool-edit.png")), {}, this, SLOT(selectObjectsAdditionally())); deselect_objects_action = context_menu->addAction(QIcon(QStringLiteral(":/images/tool-edit.png")), {}, this, SLOT(deselectObjects())); context_menu->addSeparator(); hide_action = context_menu->addAction({}, this, SLOT(setSelectedSymbolVisibility(bool))); hide_action->setCheckable(true); protect_action = context_menu->addAction({}, this, SLOT(setSelectedSymbolProtection(bool))); protect_action->setCheckable(true); context_menu->addSeparator(); QMenu* select_menu = new QMenu(tr("Select symbols"), context_menu); select_menu->addAction(tr("Select all"), this, SLOT(selectAll())); select_menu->addAction(tr("Select unused"), this, SLOT(selectUnused())); select_menu->addSeparator(); select_menu->addAction(tr("Invert selection"), this, SLOT(invertSelection())); context_menu->addMenu(select_menu); QMenu* sort_menu = new QMenu(tr("Sort symbols"), context_menu); sort_menu->addAction(tr("Sort by number"), this, SLOT(sortByNumber())); sort_menu->addAction(tr("Sort by primary color"), this, SLOT(sortByColor())); sort_menu->addAction(tr("Sort by primary color priority"), this, SLOT(sortByColorPriority())); sort_manual_action = sort_menu->addAction(tr("Enable drag and drop")); sort_manual_action->setCheckable(true); context_menu->addMenu(sort_menu); connect(map, &Map::colorDeleted, this, QOverload<>::of(&QWidget::update)); connect(map, &Map::symbolAdded, this, &SymbolRenderWidget::updateAll); connect(map, &Map::symbolDeleted, this, &SymbolRenderWidget::symbolDeleted); connect(map, &Map::symbolChanged, this, &SymbolRenderWidget::symbolChanged); connect(map, &Map::symbolIconChanged, this, &SymbolRenderWidget::updateSingleIcon); connect(map, &Map::symbolIconZoomChanged, this, &SymbolRenderWidget::updateAll); connect(&Settings::getInstance(), &Settings::settingsChanged, this, &SymbolRenderWidget::settingsChanged); } SymbolRenderWidget::~SymbolRenderWidget() { ; // nothing } void SymbolRenderWidget::symbolDeleted(int pos, const Symbol *old_symbol) { Q_UNUSED(old_symbol); std::set::iterator it = selected_symbols.find(pos); if (it != selected_symbols.end()) { // Remove pos std::set::iterator new_it = it; ++new_it; selected_symbols.erase(it); // Renumber successors for (it = new_it; it != selected_symbols.end(); it = ++new_it) { new_it = selected_symbols.insert(*it - 1).first; selected_symbols.erase(it); } emitGuarded_selectedSymbolsChanged(); } adjustLayout(); update(); } void SymbolRenderWidget::symbolChanged(int pos, const Symbol* new_symbol, const Symbol* old_symbol) { Q_UNUSED(new_symbol); Q_UNUSED(old_symbol); updateSingleIcon(pos); if (isSymbolSelected(pos)) emitGuarded_selectedSymbolsChanged(); } void SymbolRenderWidget::updateAll() { adjustLayout(); update(); } void SymbolRenderWidget::updateSingleIcon(int i) { if (i >= 0) { QPoint pos = iconPosition(i); update(pos.x(), pos.y(), icon_size, icon_size); } } void SymbolRenderWidget::settingsChanged() { const auto new_size = Settings::getInstance().getSymbolWidgetIconSizePx(); if (icon_size != new_size) { for (int i = 0; i < map->getNumSymbols(); ++i) { auto symbol = map->getSymbol(i); if (symbol->getIcon(map).width() != new_size) symbol->resetIcon(); } updateAll(); } } QSize SymbolRenderWidget::sizeHint() const { return preferred_size; } void SymbolRenderWidget::adjustLayout() { auto old_icon_size = icon_size; icon_size = Settings::getInstance().getSymbolWidgetIconSizePx() + 1; // Allow symbol widget to be that much wider than the viewport int overflow = icon_size / 3; icons_per_row = qMax(1, (width() + overflow) / icon_size); num_rows = qMax(1, (map->getNumSymbols() + icons_per_row -1) / icons_per_row); setFixedHeight(num_rows * icon_size); if (old_icon_size != icon_size) { hidden_symbol_decoration.reset(new HiddenSymbolDecorator(icon_size)); protected_symbol_decoration.reset(new ProtectedSymbolDecorator(icon_size)); } } void SymbolRenderWidget::emitGuarded_selectedSymbolsChanged() { QScopedValueRollback save(selection_locked); selection_locked = true; emit selectedSymbolsChanged(); } const Symbol* SymbolRenderWidget::singleSelectedSymbol() const { if (selected_symbols.size() != 1) return nullptr; return static_cast(map)->getSymbol(*(selected_symbols.begin())); } Symbol* SymbolRenderWidget::singleSelectedSymbol() { if (selected_symbols.size() != 1) return nullptr; return map->getSymbol(*(selected_symbols.begin())); } bool SymbolRenderWidget::isSymbolSelected(const Symbol* symbol) const { return isSymbolSelected(map->findSymbolIndex(symbol)); } bool SymbolRenderWidget::isSymbolSelected(int i) const { return selected_symbols.find(i) != selected_symbols.end(); } void SymbolRenderWidget::selectSingleSymbol(const Symbol *symbol) { int index = map->findSymbolIndex(symbol); if (index >= 0) selectSingleSymbol(index); } void SymbolRenderWidget::selectSingleSymbol(int i) { if (selection_locked) return; if (selected_symbols.size() == 1 && *selected_symbols.begin() == i) return; // Symbol already selected as only selection updateSelectedIcons(); selected_symbols.clear(); if (i >= 0) { selected_symbols.insert(i); updateSingleIcon(i); } current_symbol_index = i; emitGuarded_selectedSymbolsChanged(); } void SymbolRenderWidget::hover(QPoint pos) { int i = symbolIndexAt(pos); if (i != hover_symbol_index) { updateSingleIcon(hover_symbol_index); hover_symbol_index = i; updateSingleIcon(hover_symbol_index); if (hover_symbol_index >= 0) { Symbol* symbol = map->getSymbol(hover_symbol_index); if (tooltip->getSymbol() != symbol) { const QRect icon_rect(mapToGlobal(iconPosition(hover_symbol_index)), QSize(icon_size, icon_size)); tooltip->scheduleShow(symbol, map, icon_rect, mobile_mode); } } else { tooltip->reset(); } } } QPoint SymbolRenderWidget::iconPosition(int i) const { return QPoint((i % icons_per_row) * icon_size, (i / icons_per_row) * icon_size); } int SymbolRenderWidget::symbolIndexAt(QPoint pos) const { int i = -1; int icon_in_row = pos.x() / icon_size; if (icon_in_row < icons_per_row) { i = icon_in_row + icons_per_row * (pos.y() / icon_size); if (i >= map->getNumSymbols()) i = -1; } return i; } void SymbolRenderWidget::updateSelectedIcons() { for (auto symbol_index : selected_symbols) updateSingleIcon(symbol_index); } bool SymbolRenderWidget::dropPosition(QPoint pos, int& row, int& pos_in_row) { row = pos.y() / icon_size; if (row >= num_rows) { row = -1; pos_in_row = -1; return false; } pos_in_row = (pos.x() + icon_size / 2) / icon_size; if (pos_in_row > icons_per_row) { pos_in_row = icons_per_row; } int global_pos = row * icons_per_row + pos_in_row; if (global_pos > map->getNumSymbols()) { row = -1; pos_in_row = -1; return false; } return true; } QRect SymbolRenderWidget::dropIndicatorRect(int row, int pos_in_row) { const int rect_width = 3; return QRect(pos_in_row * icon_size - rect_width / 2 - 1, row * icon_size - 1, rect_width, icon_size + 2); } void SymbolRenderWidget::drawIcon(QPainter &painter, int i) const { painter.save(); Symbol* symbol = map->getSymbol(i); painter.drawImage(0, 0, symbol->getIcon(map)); if (isSymbolSelected(i) || i == current_symbol_index) { painter.setOpacity(0.5); QPen pen(Qt::white); pen.setWidth(2); pen.setJoinStyle(Qt::MiterJoin); painter.setPen(pen); painter.drawRect(QRectF(1.5f, 1.5f, icon_size - 5, icon_size - 5)); painter.setOpacity(1.0); } if (symbol->isProtected()) protected_symbol_decoration->draw(painter); if (symbol->isHidden()) hidden_symbol_decoration->draw(painter); if (isSymbolSelected(i)) { QPen pen(qRgb(12, 0, 255)); pen.setWidth(2); pen.setJoinStyle(Qt::MiterJoin); painter.setPen(pen); painter.drawRect(QRectF(0.5f, 0.5f, icon_size - 3, icon_size - 3)); if (i == hover_symbol_index) { QPen pen(qRgb(255, 150, 0)); pen.setWidth(2); pen.setDashPattern(QVector() << 1 << 2); pen.setJoinStyle(Qt::MiterJoin); painter.setPen(pen); painter.drawRect(QRectF(0.5f, 0.5f, icon_size - 3, icon_size - 3)); } else if (i == current_symbol_index) { QPen pen(Qt::white); pen.setDashPattern(QVector() << 2 << 2); painter.setPen(pen); painter.drawRect(1, 1, icon_size - 4, icon_size - 4); } } else if (i == hover_symbol_index) { QPen pen(qRgb(255, 150, 0)); pen.setWidth(2); pen.setJoinStyle(Qt::MiterJoin); painter.setPen(pen); painter.drawRect(QRectF(0.5f, 0.5f, icon_size - 3, icon_size - 3)); } painter.setPen(Qt::gray); painter.drawLine(QPoint(0, icon_size - 1), QPoint(icon_size - 1, icon_size - 1)); painter.drawLine(QPoint(icon_size - 1, 0), QPoint(icon_size - 1, icon_size - 2)); painter.restore(); } void SymbolRenderWidget::paintEvent(QPaintEvent* event) { QRect event_rect = event->rect().adjusted(-icon_size, -icon_size, 0, 0); QPainter painter(this); painter.setPen(Qt::gray); int x = 0; int y = 0; for (int i = 0; i < map->getNumSymbols(); ++i) { if (event_rect.contains(x,y)) { painter.save(); painter.translate(x, y); drawIcon(painter, i); painter.restore(); } x += icon_size; if (x >= icons_per_row * icon_size) { x = 0; y += icon_size; } } // Drop indicator? if (last_drop_pos >= 0 && last_drop_row >= 0) { QRect drop_rect = dropIndicatorRect(last_drop_row, last_drop_pos); painter.setPen(qRgb(255, 150, 0)); painter.setBrush(Qt::NoBrush); painter.drawRect(drop_rect.left(), drop_rect.top(), drop_rect.width() - 1, drop_rect.height() - 1); } painter.end(); } void SymbolRenderWidget::resizeEvent(QResizeEvent* event) { if (width() != event->oldSize().width()) { adjustLayout(); } } void SymbolRenderWidget::mouseMoveEvent(QMouseEvent* event) { if (mobile_mode) { if (event->buttons() & Qt::LeftButton) { hover(event->pos()); if ((event->pos() - last_click_pos).manhattanLength() < Settings::getInstance().getStartDragDistancePx()) return; dragging = sort_manual_action->isChecked(); } } else { if (event->buttons() & Qt::LeftButton && current_symbol_index >= 0 && sort_manual_action->isChecked()) { if ((event->pos() - last_click_pos).manhattanLength() < Settings::getInstance().getStartDragDistancePx()) return; tooltip->hide(); QDrag* drag = new QDrag(this); QMimeData* mime_data = new QMimeData(); QByteArray data; data.append((const char*)¤t_symbol_index, sizeof(int)); mime_data->setData(MimeType::OpenOrienteeringSymbolIndex(), data); drag->setMimeData(mime_data); drag->exec(Qt::MoveAction); } else if (event->button() == Qt::NoButton) { hover(event->pos()); } } event->accept(); } void SymbolRenderWidget::mousePressEvent(QMouseEvent* event) { dragging = false; if (mobile_mode) { tooltip->hide(); last_click_pos = event->pos(); hover(event->pos()); return; } updateSingleIcon(current_symbol_index); int old_symbol_index = current_symbol_index; current_symbol_index = symbolIndexAt(event->pos()); updateSingleIcon(current_symbol_index); if (event->button() == Qt::LeftButton || event->button() == Qt::RightButton) { if (event->modifiers() & Qt::ControlModifier) { if (current_symbol_index >= 0) { if (!isSymbolSelected(current_symbol_index)) selected_symbols.insert(current_symbol_index); else selected_symbols.erase(current_symbol_index); emitGuarded_selectedSymbolsChanged(); } } else if (event->modifiers() & Qt::ShiftModifier) { if (current_symbol_index >= 0) { bool insert = !isSymbolSelected(current_symbol_index); int i = (old_symbol_index >= 0) ? old_symbol_index : current_symbol_index; while (true) { if (insert) selected_symbols.insert(i); else selected_symbols.erase(i); updateSingleIcon(i); if (current_symbol_index > i) ++i; else if (current_symbol_index < i) --i; else break; } emitGuarded_selectedSymbolsChanged(); } } else { if (event->button() == Qt::LeftButton || (event->button() == Qt::RightButton && current_symbol_index >= 0 && !isSymbolSelected(current_symbol_index))) selectSingleSymbol(current_symbol_index); } } else if (event->button() == Qt::LeftButton && current_symbol_index >= 0 && !(event->modifiers() & Qt::ShiftModifier)) { last_click_pos = event->pos(); last_drop_pos = -1; last_drop_row = -1; } } void SymbolRenderWidget::mouseReleaseEvent(QMouseEvent* event) { if (mobile_mode) { if (dragging) { dragging = false; return; } hover(event->pos()); if (tooltip->isVisible()) return; updateSingleIcon(current_symbol_index); current_symbol_index = symbolIndexAt(event->pos()); updateSingleIcon(current_symbol_index); if (current_symbol_index >= 0 && !isSymbolSelected(current_symbol_index)) selectSingleSymbol(current_symbol_index); else emitGuarded_selectedSymbolsChanged(); // HACK, will close the symbol selection screen } } void SymbolRenderWidget::mouseDoubleClickEvent(QMouseEvent* event) { if (mobile_mode) return; int i = symbolIndexAt(event->pos()); if (i < 0) return; updateSingleIcon(current_symbol_index); current_symbol_index = i; updateSingleIcon(current_symbol_index); editSymbol(); } void SymbolRenderWidget::leaveEvent(QEvent* event) { Q_UNUSED(event); updateSingleIcon(hover_symbol_index); hover_symbol_index = -1; tooltip->reset(); } void SymbolRenderWidget::dragEnterEvent(QDragEnterEvent* event) { if (event->mimeData()->hasFormat(MimeType::OpenOrienteeringSymbolIndex())) event->acceptProposedAction(); } void SymbolRenderWidget::dragMoveEvent(QDragMoveEvent* event) { if (event->mimeData()->hasFormat(MimeType::OpenOrienteeringSymbolIndex())) { int row, pos_in_row; if (!dropPosition(event->pos(), row, pos_in_row)) { if (last_drop_pos >= 0 && last_drop_row >= 0) { update(dropIndicatorRect(last_drop_row, last_drop_pos)); last_drop_pos = -1; last_drop_row = -1; } return; } if (row != last_drop_row || pos_in_row != last_drop_pos) { if (last_drop_row >= 0 && last_drop_pos >= 0) update(dropIndicatorRect(last_drop_row, last_drop_pos)); last_drop_row = row; last_drop_pos = pos_in_row; if (last_drop_row >= 0 && last_drop_pos >= 0) update(dropIndicatorRect(last_drop_row, last_drop_pos)); } event->acceptProposedAction(); } } void SymbolRenderWidget::dropEvent(QDropEvent* event) { last_drop_row = -1; last_drop_pos = -1; if (event->source() != this || current_symbol_index < 0) return; if (event->proposedAction() == Qt::MoveAction) { int row, pos_in_row; if (!dropPosition(event->pos(), row, pos_in_row)) return; int pos = row * icons_per_row + pos_in_row; if (pos == current_symbol_index) return; event->acceptProposedAction(); // save selection std::set sel; for (auto symbol_index : selected_symbols) { sel.insert(map->getSymbol(symbol_index)); } map->moveSymbol(current_symbol_index, pos); update(); if (pos > current_symbol_index) --pos; current_symbol_index = pos; selectSingleSymbol(pos); } } void SymbolRenderWidget::selectObjectsExclusively() { emit selectObjectsClicked(true); } void SymbolRenderWidget::selectObjectsAdditionally() { emit selectObjectsClicked(false); } void SymbolRenderWidget::deselectObjects() { emit deselectObjectsClicked(); } void SymbolRenderWidget::newPointSymbol() { newSymbol(new PointSymbol()); } void SymbolRenderWidget::newLineSymbol() { newSymbol(new LineSymbol()); } void SymbolRenderWidget::newAreaSymbol() { newSymbol(new AreaSymbol()); } void SymbolRenderWidget::newTextSymbol() { newSymbol(new TextSymbol()); } void SymbolRenderWidget::newCombinedSymbol() { newSymbol(new CombinedSymbol()); } void SymbolRenderWidget::editSymbol() { Q_ASSERT(current_symbol_index >= 0); Symbol* symbol = map->getSymbol(current_symbol_index); SymbolSettingDialog dialog(symbol, map, this); dialog.setWindowModality(Qt::WindowModal); if (dialog.exec() == QDialog::Accepted) { map->setSymbol(dialog.getNewSymbol().release(), current_symbol_index); } } void SymbolRenderWidget::scaleSymbol() { Q_ASSERT(!selected_symbols.empty()); bool ok; double percent = QInputDialog::getDouble(this, tr("Scale symbols"), tr("Scale to percentage:"), 100, 0, 999999, 6, &ok); if (!ok || percent == 100) return; for (auto symbol_index : selected_symbols) { auto symbol = map->getSymbol(symbol_index); symbol->scale(percent / 100.0); updateSingleIcon(current_symbol_index); map->changeSymbolForAllObjects(symbol, symbol); // update the objects } map->setSymbolsDirty(); } void SymbolRenderWidget::deleteSymbols() { // save selected symbols std::vector saved_selection; saved_selection.reserve(selected_symbols.size()); for (auto symbol_index : selected_symbols) { saved_selection.push_back(map->getSymbol(symbol_index)); } // delete symbols in order for (const auto symbol : saved_selection) { if (map->existsObjectWithSymbol(symbol)) { if (QMessageBox::warning(this, tr("Confirmation"), tr("The map contains objects with the symbol \"%1\". Deleting it will delete those objects and clear the undo history! Do you really want to do that?").arg(symbol->getName()), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) continue; } map->deleteSymbol(map->findSymbolIndex(symbol)); } if (selected_symbols.empty() && map->getFirstSelectedObject()) selectSingleSymbol(map->getFirstSelectedObject()->getSymbol()); } void SymbolRenderWidget::duplicateSymbol() { Q_ASSERT(current_symbol_index >= 0); map->addSymbol(map->getSymbol(current_symbol_index)->duplicate(), current_symbol_index + 1); selectSingleSymbol(current_symbol_index + 1); } void SymbolRenderWidget::copySymbols() { // Create map containing all colors and the selected symbols. // Copying all colors improves preservation of relative order during paste. Map* copy_map = new Map(); copy_map->setScaleDenominator(map->getScaleDenominator()); copy_map->importMap(map, Map::ColorImport, this); std::vector selection(map->getNumSymbols(), false); for (auto symbol_index : selected_symbols) selection[symbol_index] = true; copy_map->importMap(map, Map::MinimalSymbolImport, this, &selection); // Save map to memory QBuffer buffer; if (!copy_map->exportToIODevice(&buffer)) { delete copy_map; QMessageBox::warning(nullptr, tr("Error"), tr("An internal error occurred, sorry!")); return; } delete copy_map; // Put buffer into clipboard QMimeData* mime_data = new QMimeData(); mime_data->setData(MimeType::OpenOrienteeringSymbols(), buffer.data()); QApplication::clipboard()->setMimeData(mime_data); } void SymbolRenderWidget::pasteSymbols() { if (!QApplication::clipboard()->mimeData()->hasFormat(MimeType::OpenOrienteeringSymbols())) { QMessageBox::warning(nullptr, tr("Error"), tr("There are no symbols in clipboard which could be pasted!")); return; } // Get buffer from clipboard QByteArray byte_array = QApplication::clipboard()->mimeData()->data(MimeType::OpenOrienteeringSymbols()); QBuffer buffer(&byte_array); buffer.open(QIODevice::ReadOnly); // Create map from buffer Map* paste_map = new Map(); if (!paste_map->importFromIODevice(&buffer)) { QMessageBox::warning(nullptr, tr("Error"), tr("An internal error occurred, sorry!")); return; } // Ensure that a change in selection is detected selectSingleSymbol(-1); // Import pasted map map->importMap(paste_map, Map::MinimalSymbolImport, this, nullptr, current_symbol_index, false); delete paste_map; selectSingleSymbol(current_symbol_index); } void SymbolRenderWidget::setSelectedSymbolVisibility(bool checked) { bool selection_changed = false; for (auto symbol_index : selected_symbols) { auto symbol = map->getSymbol(symbol_index); if (symbol->isHidden() != checked) { symbol->setHidden(checked); updateSingleIcon(symbol_index); if (checked) selection_changed |= map->removeSymbolFromSelection(symbol, false); } } if (selection_changed) map->emitSelectionChanged(); map->updateAllMapWidgets(); map->setSymbolsDirty(); emitGuarded_selectedSymbolsChanged(); } void SymbolRenderWidget::setSelectedSymbolProtection(bool checked) { bool selection_changed = false; for (auto symbol_index : selected_symbols) { auto symbol = map->getSymbol(symbol_index); if (symbol->isProtected() != checked) { symbol->setProtected(checked); updateSingleIcon(symbol_index); if (checked) selection_changed |= map->removeSymbolFromSelection(symbol, false); } } if (selection_changed) map->emitSelectionChanged(); map->setSymbolsDirty(); emitGuarded_selectedSymbolsChanged(); } void SymbolRenderWidget::selectAll() { for (int i = 0; i < map->getNumSymbols(); ++i) selected_symbols.insert(i); emitGuarded_selectedSymbolsChanged(); update(); } void SymbolRenderWidget::selectUnused() { std::vector symbols_in_use; map->determineSymbolsInUse(symbols_in_use); updateSelectedIcons(); selected_symbols.clear(); for (size_t i = 0, end = symbols_in_use.size(); i < end; ++i) { if (!symbols_in_use[i]) { selected_symbols.insert(i); updateSingleIcon(i); } } emitGuarded_selectedSymbolsChanged(); } void SymbolRenderWidget::invertSelection() { std::set new_set; for (int i = 0; i < map->getNumSymbols(); ++i) { if (!isSymbolSelected(i)) new_set.insert(i); } swap(selected_symbols, new_set); emitGuarded_selectedSymbolsChanged(); update(); } void SymbolRenderWidget::sortByNumber() { sort(Symbol::compareByNumber); } void SymbolRenderWidget::sortByColor() { sort(Symbol::compareByColor(map)); } void SymbolRenderWidget::sortByColorPriority() { sort(Symbol::compareByColorPriority); } void SymbolRenderWidget::showContextMenu(QPoint global_pos) { updateContextMenuState(); context_menu->popup(global_pos); } void SymbolRenderWidget::contextMenuEvent(QContextMenuEvent* event) { showContextMenu(event->globalPos()); event->accept(); } void SymbolRenderWidget::updateContextMenuState() { bool have_selection = selectedSymbolsCount() > 0; bool single_selection = selectedSymbolsCount() == 1 && current_symbol_index >= 0; const Symbol* single_symbol = singleSelectedSymbol(); bool all_symbols_hidden = have_selection; bool all_symbols_protected = have_selection; for (auto symbol_index : selected_symbols) { if (!map->getSymbol(symbol_index)->isHidden()) all_symbols_hidden = false; if (!map->getSymbol(symbol_index)->isProtected()) all_symbols_protected = false; if (!all_symbols_hidden && !all_symbols_protected) break; } bool single_symbol_compatible; bool single_symbol_different; map->getSelectionToSymbolCompatibility(single_symbol, single_symbol_compatible, single_symbol_different); edit_action->setEnabled(single_selection); scale_action->setEnabled(have_selection); copy_action->setEnabled(have_selection); paste_action->setEnabled(QApplication::clipboard()->mimeData()->hasFormat(MimeType::OpenOrienteeringSymbols())); switch_symbol_action->setEnabled(single_symbol_compatible && single_symbol_different); fill_border_action->setEnabled(single_symbol_compatible && single_symbol_different); hide_action->setEnabled(have_selection); hide_action->setChecked(all_symbols_hidden); protect_action->setEnabled(have_selection); protect_action->setChecked(all_symbols_protected); duplicate_action->setEnabled(single_selection); delete_action->setEnabled(have_selection); if (single_selection) { select_objects_action->setText(tr("Select all objects with this symbol")); select_objects_additionally_action->setText(tr("Add all objects with this symbol to selection")); deselect_objects_action->setText(tr("Remove all objects with this symbol from selection")); hide_action->setText(tr("Hide objects with this symbol")); protect_action->setText(tr("Protect objects with this symbol")); } else { select_objects_action->setText(tr("Select all objects with selected symbols")); select_objects_additionally_action->setText(tr("Add all objects with selected symbols to selection")); deselect_objects_action->setText(tr("Remove all objects with selected symbols from selection")); hide_action->setText(tr("Hide objects with selected symbols")); protect_action->setText(tr("Protect objects with selected symbols")); } select_objects_action->setEnabled(have_selection); select_objects_additionally_action->setEnabled(have_selection); deselect_objects_action->setEnabled(have_selection); } bool SymbolRenderWidget::newSymbol(Symbol* prototype) { SymbolSettingDialog dialog(prototype, map, this); dialog.setWindowModality(Qt::WindowModal); delete prototype; if (dialog.exec() == QDialog::Rejected) return false; int pos = (current_symbol_index >= 0) ? current_symbol_index : map->getNumSymbols(); map->addSymbol(dialog.getNewSymbol().release(), pos); // Ensure that a change in selection is detected selectSingleSymbol(-1); selectSingleSymbol(pos); return true; } template void SymbolRenderWidget::sort(T compare) { // save selection std::set saved_selection; for (auto symbol_index : selected_symbols) { saved_selection.insert(map->getSymbol(symbol_index)); } map->sortSymbols(compare); // restore selection selected_symbols.clear(); for (int i = 0; i < map->getNumSymbols(); ++i) { if (saved_selection.find(map->getSymbol(i)) != saved_selection.end()) selected_symbols.insert(i); } update(); } } // namespace OpenOrienteering mapper-0.8.1.1/src/gui/widgets/symbol_render_widget.h000066400000000000000000000240411325266516600225610ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2014 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_SYMBOL_RENDER_WIDGET_H #define OPENORIENTEERING_SYMBOL_RENDER_WIDGET_H #include #include #include #include #include #include #include class QAction; class QContextMenuEvent; class QDragEnterEvent; class QDragMoveEvent; class QDropEvent; class QEvent; class QMenu; class QMouseEvent; class QPaintEvent; class QPainter; class QResizeEvent; namespace OpenOrienteering { class Map; class Symbol; class SymbolIconDecorator; class SymbolToolTip; /** * @brief Shows all symbols from a map in a size-constrained widget. * * SymbolRenderWidget lets the user select symbols and perform actions on them. * It is a plain widget which does not implement scrolling but takes as much * height as neccessary for given width and number of symbols. Scrolling is * realized by SymbolWidget. */ class SymbolRenderWidget : public QWidget { Q_OBJECT public: /** * @brief Constructs a new SymbolRenderWidget. * @param map The map which provides the symbols. Must not be nullptr. * @param mobile_mode If true, enables a special mode for mobile devices. * @param parent The parent QWidget. */ SymbolRenderWidget(Map* map, bool mobile_mode, QWidget* parent = nullptr); /** * @brief Destroys the SymbolRenderWidget. */ ~SymbolRenderWidget() override; /** * @brief Returns the number of selected symbols. */ int selectedSymbolsCount() const; /** * @brief Checks if the symbol is selected. */ bool isSymbolSelected(const Symbol* symbol) const; /** * @brief Checks if the symbol with given index is selected. */ bool isSymbolSelected(int i) const; /** * @brief If exactly one symbol is selected, returns this symbol. * * Otherwise returns nullptr. */ const Symbol* singleSelectedSymbol() const; /** * @brief If exactly one symbol is selected, returns this symbol. * * Otherwise returns nullptr. */ Symbol* singleSelectedSymbol(); /** * @brief Selects the given symbol exclusively. * * Deselects other symbols, if there was a different selection before. */ void selectSingleSymbol(const Symbol* symbol); /** * @brief Selects the given symbol exclusively. * * Selects the symbol with the given number. * Deselects other symbols, if there was a different selection before. */ void selectSingleSymbol(int i); /** * @brief Returns the recommended size for the widget. * * Reimplemented from QWidget::sizeHint(). */ QSize sizeHint() const override; /** * @brief Opens the context menu at the given global position. */ void showContextMenu(QPoint global_pos); public slots: /** * @brief Updates the layout and marks all icons for repainting. */ void updateAll(); /** * @brief Marks the icon with the given index for repainting. */ void updateSingleIcon(int i); /** * Observes settings changes related to symbol display. */ void settingsChanged(); signals: /** * @brief The collection of selected symbols changed. */ void selectedSymbolsChanged(); /** * @brief The user triggered "Switch symbol". * @todo Merge with/Reuse corresponding action in MapEditorController. */ void switchSymbolClicked(); /** * @brief The user triggered "Fill/Create border". * @todo Merge with/Reuse corresponding action in MapEditorController. */ void fillBorderClicked(); /** * @brief The user triggered selecting objects with the active symbol. * @param exclusively If true, an existing selection is replaced, * otherwise it is extend. */ void selectObjectsClicked(bool exclusively); /** * @brief The user triggered deselecting objects with the active symbol. */ void deselectObjectsClicked(); protected slots: /** * @brief Updates icon and selection when a symbol changes. * * @see Map::symbolChanged() */ void symbolChanged(int pos, const Symbol* new_symbol, const Symbol* old_symbol); /** * @brief Updates the widget and the current selection. * * @see Map::symbolDeleted() */ void symbolDeleted(int pos, const Symbol* old_symbol); void newPointSymbol(); void newLineSymbol(); void newAreaSymbol(); void newTextSymbol(); void newCombinedSymbol(); void editSymbol(); void scaleSymbol(); void deleteSymbols(); void duplicateSymbol(); void copySymbols(); void pasteSymbols(); void setSelectedSymbolVisibility(bool checked); void setSelectedSymbolProtection(bool checked); void selectObjectsExclusively(); void selectObjectsAdditionally(); void deselectObjects(); void selectAll(); void selectUnused(); void invertSelection(); void sortByNumber(); void sortByColor(); void sortByColorPriority(); protected: void resizeEvent(QResizeEvent* event) override; /** * @brief Recalculates the layout and size. * * This function reads the icon size from the settings and the widget's * current width and adjusts the number of icons per row, the number of * rows and the widget's height to fit the current number of symbols. */ void adjustLayout(); /** * @brief Returns the top-left coordinates of a symbol's icon. * @param i The index of the symbol. */ QPoint iconPosition(int i) const; /** * @brief Returns the index of the symbol represented at a particular location * @param pos The location */ int symbolIndexAt(QPoint pos) const; void paintEvent(QPaintEvent* event) override; /** * @brief Draws the icon and its decoration (hidden, protected). * * The icon is drawn at (0, 0) with the current icon size. * @param painter The QPainter to be used (must be active). * @param i The index of the symbol. */ void drawIcon(QPainter& painter, int i) const; void mouseMoveEvent(QMouseEvent* event) override; void mousePressEvent(QMouseEvent* event) override; void mouseReleaseEvent(QMouseEvent* event) override; void mouseDoubleClickEvent(QMouseEvent* event) override; void leaveEvent(QEvent* event) override; /** * @brief Handles hovering over the icons, i.e. controlling the tool tip. * @param pos The current location of the pointing device. */ void hover(QPoint pos); void dragEnterEvent(QDragEnterEvent* event) override; void dragMoveEvent(QDragMoveEvent* event) override; void dropEvent(QDropEvent* event) override; /** * @brief Determines the drop location for a given pointing device position. * @param pos The pointing device position. * @param row The icon row where to insert the dropped symbol. * @param pos_in_row The position in the row where to insert the dropped symbol. * @return If there is a valid drop position, returns true, otherwise false. */ bool dropPosition(QPoint pos, int& row, int& pos_in_row); /** * @brief Determines a drop indicator rectangle for a given location in the icons. * @param row The icon row where to insert the dropped symbol. * @param pos_in_row The position in the row where to insert the dropped symbol. */ QRect dropIndicatorRect(int row, int pos_in_row); /** * @brief Takes care of editing and inserting a newly created symbol. * @param prototype The symbol which will be edited and inserted in the map. * @return If editing is cancelled, returns false. Otherwise returns true. */ bool newSymbol(Symbol* prototype); /** * @brief Sorts the map's symbol set by arbitrary criteria. */ template void sort(T compare); /** * @brief Marks the icons of the selected symbols for repainting. */ void updateSelectedIcons(); /** * @brief Emits selectedSymbolsChanged() while temporarily locking the symbol selection against changes. * * An active tool could catch the selectedSymbolsChanged() signal and * finish its editing for that reason. It would than insert a new object to * the map and so trigger another change of selection. Emitting * selectedSymbolsChanged() via this method supresses behavior by setting * the selection_locked flag before emitting the actual signal and resetting * the flag on return. */ void emitGuarded_selectedSymbolsChanged(); /** * @brief Receives context menu events and opens the context menu. */ void contextMenuEvent(QContextMenuEvent* event) override; /** * @brief Updates the state of the actions in the context menu. */ void updateContextMenuState(); private: Map* map; bool mobile_mode; bool selection_locked; bool dragging; int current_symbol_index; int hover_symbol_index; std::set selected_symbols; QPoint last_click_pos; int last_drop_pos; int last_drop_row; int icon_size; int icons_per_row; int num_rows; QSize preferred_size; QMenu* context_menu; QAction* edit_action; QAction* scale_action; QAction* copy_action; QAction* paste_action; QAction* switch_symbol_action; QAction* fill_border_action; QAction* hide_action; QAction* protect_action; QAction* duplicate_action; QAction* delete_action; QAction* select_objects_action; QAction* select_objects_additionally_action; QAction* deselect_objects_action; QAction* sort_manual_action; SymbolToolTip* tooltip; QScopedPointer hidden_symbol_decoration; QScopedPointer protected_symbol_decoration; }; //### SymbolRenderWidget inline code ### inline int SymbolRenderWidget::selectedSymbolsCount() const { return (int)selected_symbols.size(); } } // namespace OpenOrienteering #endif mapper-0.8.1.1/src/gui/widgets/symbol_tooltip.cpp000066400000000000000000000142051325266516600217650ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "symbol_tooltip.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "core/map.h" #include "core/symbols/symbol.h" // IWYU pragma: no_forward_declare QWidget namespace OpenOrienteering { SymbolToolTip::SymbolToolTip(QWidget* parent, QShortcut* shortcut) : QWidget{ parent } , shortcut{ shortcut } , symbol{ nullptr } , description_shown{ false } { setWindowFlags(Qt::ToolTip | Qt::FramelessWindowHint); setAttribute(Qt::WA_OpaquePaintEvent); QPalette text_palette; text_palette.setColor(QPalette::Window, text_palette.color(QPalette::Base)); text_palette.setColor(QPalette::WindowText, text_palette.color(QPalette::Text)); setPalette(text_palette); name_label = new QLabel(); description_label = new QLabel(); description_label->setWordWrap(true); description_label->hide(); auto layout = new QVBoxLayout(); QStyleOption style_option; layout->setContentsMargins( style()->pixelMetric(QStyle::PM_LayoutLeftMargin, &style_option) / 2, style()->pixelMetric(QStyle::PM_LayoutTopMargin, &style_option) / 2, style()->pixelMetric(QStyle::PM_LayoutRightMargin, &style_option) / 2, style()->pixelMetric(QStyle::PM_LayoutBottomMargin, &style_option) / 2 ); layout->addWidget(name_label); layout->addWidget(description_label); setLayout(layout); tooltip_timer.setSingleShot(true); connect(&tooltip_timer, &QTimer::timeout, this, &QWidget::show); if (shortcut) { shortcut->setEnabled(false); connect(shortcut, &QShortcut::activated, this, &SymbolToolTip::showDescription); } } void SymbolToolTip::showDescription() { if (symbol && !description_shown && isVisible()) { description_label->show(); adjustSize(); adjustPosition(false); description_shown = true; } if (shortcut) shortcut->setEnabled(false); } void SymbolToolTip::reset() { symbol = nullptr; tooltip_timer.stop(); hide(); description_label->hide(); description_shown = false; if (shortcut) shortcut->setEnabled(false); } void SymbolToolTip::enterEvent(QEvent* event) { Q_UNUSED(event); hide(); } void SymbolToolTip::paintEvent(QPaintEvent* event) { Q_UNUSED(event); QPainter painter(this); painter.setBrush(palette().color(QPalette::Window)); painter.setPen(palette().color(QPalette::WindowText)); QRect rect(0, 0, width() - 1, height() - 1); painter.drawRect(rect); painter.end(); } void SymbolToolTip::adjustPosition(bool mobile_mode) { auto size = this->size(); auto desktop = QApplication::desktop()->screenGeometry(QCursor::pos()); const int margin = 3; bool has_room_to_left = (icon_rect.left() - size.width() - margin >= desktop.left()); bool has_room_to_right = (icon_rect.right() + size.width() + margin <= desktop.right()); bool has_room_above = (icon_rect.top() - size.height() - margin >= desktop.top()); bool has_room_below = (icon_rect.bottom() + size.height() + margin <= desktop.bottom()); if (!has_room_above && !has_room_below && !has_room_to_left && !has_room_to_right) { return; } if (mobile_mode) { // Change precedence. if (has_room_above) { has_room_below = false; } else if (has_room_to_left) { has_room_below = false; has_room_to_right = false; } else if (has_room_to_right) { has_room_below = false; } } int x = 0; int y = 0; if (has_room_below || has_room_above) { y = has_room_below ? icon_rect.bottom() + margin : icon_rect.top() - size.height() - margin; x = qMin(qMax(desktop.left() + margin, icon_rect.center().x() - size.width() / 2), desktop.right() - size.width() - margin); } else { x = has_room_to_right ? icon_rect.right() + margin : icon_rect.left() - size.width() - margin; y = qMin(qMax(desktop.top() + margin, icon_rect.center().y() - size.height() / 2), desktop.bottom() - size.height() - margin); } move(QPoint(x, y)); } void SymbolToolTip::scheduleShow(const Symbol* symbol, const Map* map, QRect icon_rect, bool mobile_mode) { this->icon_rect = icon_rect; this->symbol = symbol; Q_ASSERT(map); name_label->setText(symbol->getNumberAsString() + QLatin1String(" ") + map->translate(symbol->getName()) + QLatin1String("")); auto help_text = map->translate(symbol->getDescription()); if (help_text.isEmpty()) { help_text = tr("No description!"); } else { help_text.replace(QLatin1Char('\n'), QStringLiteral("
    ")); help_text.remove(QLatin1Char('\r')); } description_label->setText(help_text); description_label->hide(); description_shown = false; shortcut->setEnabled(isVisible()); adjustSize(); adjustPosition(mobile_mode); static const int delay = 150; tooltip_timer.start(delay); } void SymbolToolTip::showEvent(QShowEvent* event) { if (shortcut && !description_shown && !event->spontaneous()) shortcut->setEnabled(true); QWidget::showEvent(event); } void SymbolToolTip::hideEvent(QHideEvent* event) { if (!event->spontaneous()) reset(); QWidget::hideEvent(event); } const Symbol* SymbolToolTip::getSymbol() const { return symbol; } } // namespace OpenOrienteering mapper-0.8.1.1/src/gui/widgets/symbol_tooltip.h000066400000000000000000000106351325266516600214350ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_SYMBOL_TOOLTIP_H #define OPENORIENTEERING_SYMBOL_TOOLTIP_H #include #include #include #include class QEvent; class QHideEvent; class QLabel; class QPaintEvent; class QShortcut; class QShowEvent; // IWYU pragma: no_forward_declare QWidget namespace OpenOrienteering { class Map; class Symbol; /** * @brief A SymbolToolTip displays the name and description of a symbol. * * The tooltip is normally not displayed immediately but scheduled to be * displayed after a short delay, by a call to scheduleShow(). Initially, the * tooltip displays the symbol's number and name only. The description can be * made visible by a call to showDescription(). * * A QShortcut may be set which will trigger the display of the description * while the tooltip is already visible. The shortcut does not need to be * unique: SymbolToolTip enables the shortcut only when the tooltip is visible * and the description is not yet shown. SymbolToolTip does not take ownership * of the shortcut. * * To permanently hide a SymbolToolTip it is not enough to call hide() because * the widget may be hidden but scheduled to show up. Call reset() to hide the * widget and stop the timer if scheduled. * * SymbolToolTip does not use the standard tooltip colors but the text editor * colors. */ class SymbolToolTip : public QWidget { Q_OBJECT public: /** * Constructs a new SymbolToolTip. * The optional shortcut will trigger the description to be shown. */ SymbolToolTip(QWidget* parent = nullptr, QShortcut* shortcut = nullptr); /** * Schedules the tooltip for the symbol to be shown close to * but not covering the region given by rect. * * In mobile mode, tooltip placement relative to the symbol icon is tried * in the order: above, left, right, below. * Otherwise, placement is tried in the order: below, above, right, left. * (desktop mode). */ void scheduleShow(const Symbol* symbol, const Map* map, QRect rect, bool mobile_mode); /** * Resets the tooltip. * It hides the widget, stops the timer and disables the shortcut. */ void reset(); /** * Returns the symbol for which the tooltip is currently shown or * scheduled to be shown, or nullptr. */ const Symbol* getSymbol() const; public slots: /** * Expands the symbol's description in the tooltip. * Disables the shortcut. */ void showDescription(); protected: /** * Hides the tooltip when the mouse enters it. * This is neccessary to let the user select another symbol. */ void enterEvent(QEvent* event) override; /** * Enables the shortcut when the tooltip is shown. */ void showEvent(QShowEvent* event) override; /** * Resets the tooltip's state on hiding the tooltip. * Disables the shortcut. */ void hideEvent(QHideEvent* event) override; /** * Draws the tooltip's background. */ void paintEvent(QPaintEvent* event) override; private: /** * Moves the tooltip so that it is nicely placed close the region given * to scheduleShow(). */ void adjustPosition(bool mobile_mode); QTimer tooltip_timer; /// The timer which triggers the delayed showing. QShortcut* shortcut; /// An optional shortcut for showing the description. const Symbol* symbol; /// The current symbol, or nullptr. QLabel* name_label; /// The label displaying the symbol's name. QLabel* description_label; /// The label displaying the symbol's description. QRect icon_rect; /// The region to be considered when determining position. bool description_shown; /// If true, the full description is visible. }; } // namespace OpenOrienteering #endif mapper-0.8.1.1/src/gui/widgets/symbol_widget.cpp000066400000000000000000000054171325266516600215630ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2014 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "symbol_widget.h" #include #include #include #include "symbol_render_widget.h" // IWYU pragma: no_forward_declare QContextMenuEvent // IWYU pragma: no_forward_declare QWidget namespace OpenOrienteering { SymbolWidget::SymbolWidget(Map* map, bool mobile_mode, QWidget* parent) : QScrollArea(parent) { render_widget = new SymbolRenderWidget(map, mobile_mode, this); setWidget(render_widget); setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); setWidgetResizable(true); setBackgroundRole(QPalette::Base); // Relay render_widget signals connect(render_widget, &SymbolRenderWidget::selectedSymbolsChanged, this, &SymbolWidget::selectedSymbolsChanged); connect(render_widget, &SymbolRenderWidget::fillBorderClicked, this, &SymbolWidget::fillBorderClicked); connect(render_widget, &SymbolRenderWidget::switchSymbolClicked, this, &SymbolWidget::switchSymbolClicked); connect(render_widget, &SymbolRenderWidget::selectObjectsClicked, this, &SymbolWidget::selectObjectsClicked); connect(render_widget, &SymbolRenderWidget::deselectObjectsClicked, this, &SymbolWidget::deselectObjectsClicked); } SymbolWidget::~SymbolWidget() { ; // nothing } const Symbol* SymbolWidget::getSingleSelectedSymbol() const { return static_cast(render_widget)->singleSelectedSymbol(); } Symbol* SymbolWidget::getSingleSelectedSymbol() { return render_widget->singleSelectedSymbol(); } int SymbolWidget::selectedSymbolsCount() const { return render_widget->selectedSymbolsCount(); } bool SymbolWidget::isSymbolSelected(const Symbol* symbol) const { return render_widget->isSymbolSelected(symbol); } void SymbolWidget::selectSingleSymbol(const Symbol* symbol) { render_widget->selectSingleSymbol(symbol); } void SymbolWidget::contextMenuEvent(QContextMenuEvent* event) { render_widget->showContextMenu(event->globalPos()); event->accept(); } } // namespace OpenOrienteering mapper-0.8.1.1/src/gui/widgets/symbol_widget.h000066400000000000000000000067351325266516600212340ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2014 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_SYMBOL_WIDGET_H #define OPENORIENTEERING_SYMBOL_WIDGET_H #include #include class QContextMenuEvent; class QWidget; namespace OpenOrienteering { class Map; class Symbol; class SymbolRenderWidget; /** * @brief Shows all symbols from a map in a scrollable widget. * * SymbolWidget shows all symbols from a map's symbol set. * It lets the user select symbols and perform actions on symbols. * * The implementation is based on SymbolRenderWidget and QScrollArea. * Normally it is used inside a dock widget. */ class SymbolWidget : public QScrollArea { Q_OBJECT public: /** * @brief Constructs a new SymbolWidget. * @param map The map which provides the symbols. Must not be nullptr. * @param mobile_mode If true, enables a special mode for mobile devices. * @param parent The parent QWidget. */ SymbolWidget(Map* map, bool mobile_mode, QWidget* parent = nullptr); /** * @brief Destroys the SymbolWidget. */ ~SymbolWidget() override; /** * @brief If exactly one symbol is selected, returns this symbol. * * Otherwise returns nullptr. */ const Symbol* getSingleSelectedSymbol() const; /** * @brief If exactly one symbol is selected, returns this symbol. * * Otherwise returns nullptr. */ Symbol* getSingleSelectedSymbol(); /** * @brief Returns the number of selected symbols. */ int selectedSymbolsCount() const; /** * @brief Checks if the symbol is selected. */ bool isSymbolSelected(const Symbol* symbol) const; /** * @brief Selects the symbol exclusively, deselecting all other symbols. */ void selectSingleSymbol(const Symbol* symbol); signals: /** * @brief The collection of selected symbols changed. */ void selectedSymbolsChanged(); /** * @brief The user triggered "Switch symbol". * @todo Merge with/Reuse corresponding action in MapEditorController. */ void switchSymbolClicked(); /** * @brief The user triggered "Fill/Create border". * @todo Merge with/Reuse corresponding action in MapEditorController. */ void fillBorderClicked(); /** * @brief The user triggered selecting objects with the active symbol. * @param select_exclusively If true, an existing selection is replaced, * otherwise it is extend. */ void selectObjectsClicked(bool select_exclusively); /** * @brief The user triggered deselecting objects with the active symbol. */ void deselectObjectsClicked(); protected: /** * @brief Receives context menu events and opens the context menu. */ void contextMenuEvent(QContextMenuEvent* event) override; private: SymbolRenderWidget* render_widget; }; } // namespace OpenOrienteering #endif mapper-0.8.1.1/src/gui/widgets/tag_select_widget.cpp000066400000000000000000000244151325266516600223670ustar00rootroot00000000000000/* * Copyright 2016 Mitchell Krome * Copyright 2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "tag_select_widget.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "core/objects/object_query.h" #include "gui/util_gui.h" #include "gui/widgets/segmented_button_layout.h" namespace OpenOrienteering { // ### TagSelectWidget ### TagSelectWidget::TagSelectWidget(QWidget* parent) : QTableWidget{1, 4, parent} { setWhatsThis(Util::makeWhatThis("find_objects.html#query-editor")); setEditTriggers(QAbstractItemView::AllEditTriggers); setSelectionBehavior(QAbstractItemView::SelectRows); setSelectionMode(QAbstractItemView::SingleSelection); setHorizontalHeaderLabels(QStringList() << tr("Relation") << tr("Key") << tr("Comparison") << tr("Value")); verticalHeader()->setVisible(false); auto header_view = horizontalHeader(); header_view->setSectionsClickable(false); header_view->setSectionResizeMode(0, QHeaderView::Fixed); header_view->setSectionResizeMode(1, QHeaderView::Stretch); header_view->setSectionResizeMode(2, QHeaderView::Fixed); header_view->setSectionResizeMode(3, QHeaderView::Stretch); header_view->resizeSections(QHeaderView::ResizeToContents); setMinimumWidth(10 + header_view->sectionSize(0) + header_view->sectionSize(1) + header_view->sectionSize(2) + header_view->sectionSize(3)); addRowItems(0); connect(this, &QTableWidget::cellChanged, this, &TagSelectWidget::onCellChanged); } TagSelectWidget::~TagSelectWidget() = default; QWidget* TagSelectWidget::makeButtons(QWidget* parent) { auto add_button = newToolButton(QIcon(QString::fromLatin1(":/images/plus.png")), tr("Add Row")); auto delete_button = newToolButton(QIcon(QString::fromLatin1(":/images/minus.png")), tr("Remove Row")); auto add_remove_layout = new SegmentedButtonLayout(); add_remove_layout->addWidget(add_button); add_remove_layout->addWidget(delete_button); auto move_up_button = newToolButton(QIcon(QString::fromLatin1(":/images/arrow-up.png")), tr("Move Up")); move_up_button->setAutoRepeat(true); auto move_down_button = newToolButton(QIcon(QString::fromLatin1(":/images/arrow-down.png")), tr("Move Down")); move_down_button->setAutoRepeat(true); auto up_down_layout = new SegmentedButtonLayout(); up_down_layout->addWidget(move_up_button); up_down_layout->addWidget(move_down_button); auto list_buttons_layout = new QHBoxLayout(); list_buttons_layout->setContentsMargins(0,0,0,0); list_buttons_layout->addLayout(add_remove_layout); list_buttons_layout->addLayout(up_down_layout); auto list_buttons_group = new QWidget(parent); list_buttons_group->setLayout(list_buttons_layout); // Connections connect(add_button, &QAbstractButton::clicked, this, &TagSelectWidget::addRow); connect(delete_button, &QAbstractButton::clicked, this, &TagSelectWidget::deleteRow); connect(move_up_button, &QAbstractButton::clicked, this, &TagSelectWidget::moveRowUp); connect(move_down_button, &QAbstractButton::clicked, this, &TagSelectWidget::moveRowDown); return list_buttons_group; } QToolButton* TagSelectWidget::newToolButton(const QIcon& icon, const QString& text) { auto button = new QToolButton(); button->setToolButtonStyle(Qt::ToolButtonIconOnly); button->setToolTip(text); button->setIcon(icon); button->setText(text); button->setWhatsThis(Util::makeWhatThis("find_objects.html#query-editor")); return button; } void TagSelectWidget::showEvent(QShowEvent* event) { if (!event->spontaneous()) { auto last_row = rowCount() - 1; setCurrentCell(last_row, 1); if (!currentItem()->text().isEmpty()) setCurrentCell(last_row, 3); editItem(currentItem()); } } void TagSelectWidget::addRowItems(int row) { auto item = new QTableWidgetItem(); setItem(row, 1, item); item = new QTableWidgetItem(); setItem(row, 3, item); auto compare_op = new QComboBox(); for (auto op : { ObjectQuery::OperatorIs, ObjectQuery::OperatorIsNot, ObjectQuery::OperatorContains }) compare_op->addItem(ObjectQuery::labelFor(op), QVariant::fromValue(op)); setCellWidget(row, 2, compare_op); if (row == 0) { // The first row doesn't use a logical operator item = new QTableWidgetItem(); item->setFlags(Qt::NoItemFlags); item->setBackground(QBrush(QGuiApplication::palette().window())); setItem(row, 0, item); } else { auto logical_op = new QComboBox(); auto and_label = QString { QLatin1String(" ") + ObjectQuery::labelFor(ObjectQuery::OperatorAnd) }; logical_op->addItem(and_label, QVariant::fromValue(ObjectQuery::OperatorAnd)); logical_op->addItem(ObjectQuery::labelFor(ObjectQuery::OperatorOr), QVariant::fromValue(ObjectQuery::OperatorOr)); setCellWidget(row, 0, logical_op); } } void TagSelectWidget::onCellChanged(int row, int column) { switch(column) { case 1: case 3: { auto item = this->item(row, column); item->setText(item->text().trimmed()); break; } default: ; // nothing } } void TagSelectWidget::addRow() { int row = currentRow() + 1; // When nothing is selected, add to the end if (row == 0) row = rowCount(); insertRow(row); addRowItems(row); // Move the selection to the new row int col = currentColumn(); setCurrentCell(row, col); } void TagSelectWidget::deleteRow() { // Always need one row if (rowCount() == 1) return; int row = currentRow(); // When nothing is selected, delete from the end if (row == -1) row = rowCount() - 1; removeRow(row); // If we delete first row, need to fix logical operator if (row == 0) { removeCellWidget(row, 0); auto item = new QTableWidgetItem(); item->setFlags(Qt::NoItemFlags); item->setBackground(QBrush(QGuiApplication::palette().window())); setItem(row, 0, item); } } void TagSelectWidget::moveRow(bool up) { int row = currentRow(); int max_row = rowCount(); // When nothing is selected, move the last row if (row == -1) row = max_row - 1; // Cant move first row up or last row down if ((up && row < 1) || (!up && row == max_row - 1)) return; // Moving the current row down is just moving the row below up.. if (!up) row += 1; // Col 1, 3 are items auto top_item = takeItem(row - 1, 1); auto bottom_item = takeItem(row, 1); setItem(row, 1, top_item); setItem(row - 1, 1, bottom_item); top_item = takeItem(row - 1, 3); bottom_item = takeItem(row, 3); setItem(row, 3, top_item); setItem(row - 1, 3, bottom_item); // Col 2 is comparison combobox auto top_combo = qobject_cast(cellWidget(row - 1, 2)); auto bottom_combo = qobject_cast(cellWidget(row, 2)); auto top_selection = top_combo->currentIndex(); auto bottom_selection = bottom_combo->currentIndex(); top_combo->setCurrentIndex(bottom_selection); bottom_combo->setCurrentIndex(top_selection); // Ignore the swap on the first row because there is nothing to swap if (row - 1 != 0 ) { // Col 0 is relation combobox top_combo = qobject_cast(cellWidget(row - 1, 0)); bottom_combo = qobject_cast(cellWidget(row, 0)); top_selection = top_combo->currentIndex(); bottom_selection = bottom_combo->currentIndex(); top_combo->setCurrentIndex(bottom_selection); bottom_combo->setCurrentIndex(top_selection); } // Keep the moved row selected int col = currentColumn(); // For the down case we already adjusted the row if (up) row -= 1; setCurrentCell(row, col); } void TagSelectWidget::moveRowDown() { moveRow(false); } void TagSelectWidget::moveRowUp() { moveRow(true); } ObjectQuery TagSelectWidget::makeQuery() const { // If there is at least one OR, query will become an OR expression. ObjectQuery query; // AND has precedence over OR, so its terms are collected separately. ObjectQuery and_expression; for (int row = 0; row < rowCount(); ++row) { auto key = item(row, 1)->text(); auto compare_op = qobject_cast(cellWidget(row, 2))->currentData().value(); auto value = item(row, 3)->text(); auto comparison = ObjectQuery(key, compare_op, value); if (row == 0) { // The first term at all and_expression = std::move(comparison); } else { auto logical_op = qobject_cast(cellWidget(row, 0))->currentData().value(); if (logical_op == ObjectQuery::OperatorAnd) { // Add another term to the AND expression and_expression = ObjectQuery(std::move(comparison), ObjectQuery::OperatorAnd, std::move(and_expression)); } else if (logical_op == ObjectQuery::OperatorOr) { if (query) // Add another term to the OR expression query = ObjectQuery(std::move(and_expression), ObjectQuery::OperatorOr, std::move(query)); else // The first term of an OR expression query = std::move(and_expression); // The first term of the next AND expression and_expression = std::move(comparison); } else { Q_UNREACHABLE(); } } // The validness of and_expression is determined by the current line. if (!and_expression) break; } if (query && and_expression) { // Add the last AND expression to the OR expression query = ObjectQuery(std::move(and_expression), ObjectQuery::OperatorOr, std::move(query)); } else { // There was no OR, or the last visited row was not a valid expression. query = std::move(and_expression); } return query; } } // namespace OpenOrienteering mapper-0.8.1.1/src/gui/widgets/tag_select_widget.h000066400000000000000000000037141325266516600220330ustar00rootroot00000000000000/* * Copyright 2016 Mitchell Krome * Copyright 2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_TAG_SELECT_WIDGET_H #define OPENORIENTEERING_TAG_SELECT_WIDGET_H #include #include #include #include #include class QShowEvent; class QToolButton; class QWidget; namespace OpenOrienteering { class ObjectQuery; /** * This widget allows the user to make selections based on an objects tags. */ class TagSelectWidget : public QTableWidget { Q_OBJECT public: TagSelectWidget(QWidget* parent = nullptr); ~TagSelectWidget() override; QWidget* makeButtons(QWidget* parent = nullptr); /** * Builds a query based on the current state of the query table. * * Returns an invalid query on error. */ ObjectQuery makeQuery() const; protected: void showEvent(QShowEvent* event) override; private: /** * Returns a new QToolButton with a unified appearance. */ QToolButton* newToolButton(const QIcon& icon, const QString& text); void addRow(); void deleteRow(); void moveRow(bool up); void moveRowDown(); void moveRowUp(); void addRowItems(int row); void onCellChanged(int row, int column); Q_DISABLE_COPY(TagSelectWidget) }; } // namespace OpenOrienteering #endif mapper-0.8.1.1/src/gui/widgets/tags_widget.cpp000066400000000000000000000164631325266516600212170ustar00rootroot00000000000000/* * Copyright 2013-2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "tags_widget.h" #include #include #include #include #include #include #include "core/map.h" #include "core/objects/object.h" #include "gui/main_window.h" #include "gui/util_gui.h" #include "gui/map/map_editor.h" #include "undo/object_undo.h" namespace OpenOrienteering { TagsWidget::TagsWidget(Map* map, MapView* main_view, MapEditorController* controller, QWidget* parent) : QWidget(parent), map(map), main_view(main_view), controller(controller) { react_to_changes = false; auto layout = new QVBoxLayout(); layout->setMargin(0); tags_table = new QTableWidget(1, 2); tags_table->setEditTriggers(QAbstractItemView::AllEditTriggers); tags_table->setSelectionBehavior(QAbstractItemView::SelectRows); tags_table->setSelectionMode(QAbstractItemView::SingleSelection); tags_table->setHorizontalHeaderLabels(QStringList() << tr("Key") << tr("Value")); tags_table->verticalHeader()->setVisible(false); auto header_view = tags_table->horizontalHeader(); header_view->setSectionResizeMode(0, QHeaderView::Stretch); header_view->setSectionResizeMode(1, QHeaderView::Stretch); header_view->setSectionsClickable(false); layout->addWidget(tags_table); auto help_button = newToolButton(QIcon(QString::fromLatin1(":/images/help.png")), tr("Help")); help_button->setAutoRaise(true); auto all_buttons_layout = new QHBoxLayout(); QStyleOption style_option(QStyleOption::Version, QStyleOption::SO_DockWidget); all_buttons_layout->setContentsMargins( style()->pixelMetric(QStyle::PM_LayoutLeftMargin, &style_option) / 2, style()->pixelMetric(QStyle::PM_LayoutLeftMargin, &style_option) / 2, style()->pixelMetric(QStyle::PM_LayoutRightMargin, &style_option) / 2, style()->pixelMetric(QStyle::PM_LayoutBottomMargin, &style_option) / 2 ); all_buttons_layout->addWidget(new QLabel(QString::fromLatin1(" ")), 1); all_buttons_layout->addWidget(help_button); layout->addLayout(all_buttons_layout); setLayout(layout); connect(tags_table, &QTableWidget::cellChanged, this, &TagsWidget::cellChange); connect(map, &Map::objectSelectionChanged, this, &TagsWidget::objectTagsChanged); connect(map, &Map::selectedObjectEdited, this, &TagsWidget::objectTagsChanged); react_to_changes = true; objectTagsChanged(); } TagsWidget::~TagsWidget() = default; QToolButton* TagsWidget::newToolButton(const QIcon& icon, const QString& text) { auto button = new QToolButton(); button->setToolButtonStyle(Qt::ToolButtonIconOnly); button->setToolTip(text); button->setIcon(icon); button->setText(text); return button; } // slot void TagsWidget::showHelp() { Util::showHelp(controller->getWindow(), "tag_editor.html"); } void TagsWidget::setupLastRow() { const int row = tags_table->rowCount() - 1; tags_table->setItem(row, 0, new QTableWidgetItem()); auto value_item = new QTableWidgetItem(); value_item->setFlags(value_item->flags() & ~Qt::ItemIsEnabled); tags_table->setItem(row, 1, value_item); } void TagsWidget::createUndoStep(Object* object) { Q_ASSERT(object); auto undo_step = new ObjectTagsUndoStep(map); undo_step->addObject(map->getCurrentPart()->findObjectIndex(object)); map->push(undo_step); } // slot void TagsWidget::objectTagsChanged() { if (!react_to_changes) return; react_to_changes = false; int row = 0; const Object* object = map->getFirstSelectedObject(); if (object) { const Object::Tags& tags = object->tags(); tags_table->clearContents(); tags_table->setRowCount(tags.size() + 1); for (Object::Tags::const_iterator tag = tags.constBegin(), end = tags.constEnd(); tag != end; ++tag) { tags_table->setItem(row, 0, new QTableWidgetItem(tag.key())); tags_table->item(row, 0)->setData(Qt::UserRole, tag.key()); tags_table->setItem(row, 1, new QTableWidgetItem(tag.value())); ++row; } tags_table->sortItems(0); setupLastRow(); } else { tags_table->setRowCount(0); } react_to_changes = true; } // slot void TagsWidget::cellChange(int row, int column) { auto object = map->getFirstSelectedObject(); if (!react_to_changes || !object) return; react_to_changes = false; const QString key = tags_table->item(row, 0)->text().trimmed(); const QString value = tags_table->item(row, 1)->text(); if (column == 1 && key.isEmpty()) { // Shall not happen qWarning("Empty key for modified tag value!"); } else if (column == 1) { // Value edited: update the tag createUndoStep(object); object->setTag(key, value); if (row + 1 == tags_table->rowCount()) { // Append new row, jump to key tags_table->setRowCount(row + 2); setupLastRow(); } if (!tags_table->item(row + 1, 1)->flags().testFlag(Qt::ItemIsEnabled)) { // Jump to key tags_table->setCurrentCell(row + 1, 0); } else { // Jump to value tags_table->setCurrentCell(row + 1, 1); } } else if (column == 0) { const QString old_key = tags_table->item(row, 0)->data(Qt::UserRole).toString(); if (old_key == key && !key.isEmpty()) { // Key edited but not changed: do nothing } else if (!old_key.isEmpty() && key.isEmpty()) { // Key deleted: remove tag createUndoStep(object); object->removeTag(old_key); tags_table->removeRow(row); if (row > 0) { // Jump to previous row tags_table->setCurrentCell(row - 1, 1); } else { // Reset current row tags_table->item(row, 1)->setText({}); auto value_item = tags_table->item(row, 1); value_item->setFlags(value_item->flags() & ~Qt::ItemIsEnabled); } } else if (!key.isEmpty()) { // Key edited: update the tags if (object->tags().contains(key)) { QMessageBox::critical(window(), tr("Key exists"), tr("The key \"%1\" already exists and must not be used twice.").arg(key) ); tags_table->item(row, column)->setText(old_key); tags_table->setCurrentCell(row, column); } else { if (value.isEmpty() && old_key.isEmpty()) { // New key, empty value - dont insert yet tags_table->item(row, 0)->setData(Qt::UserRole, QLatin1String("ABOUT TO INSERT NEW VALUE")); tags_table->setCurrentCell(row, 1); } else { // New key with value - update tags createUndoStep(object); object->removeTag(old_key); object->setTag(key, value); } tags_table->item(row, 0)->setData(Qt::UserRole, key); if (tags_table->rowCount() == row + 1) { auto value_item = tags_table->item(row, 1); value_item->setFlags(value_item->flags() | Qt::ItemIsEnabled); } tags_table->setCurrentCell(row, 1); } } } react_to_changes = true; } } // namespace OpenOrienteering mapper-0.8.1.1/src/gui/widgets/tags_widget.h000066400000000000000000000045751325266516600206650ustar00rootroot00000000000000/* * Copyright 2013 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #ifndef OPENORIENTEERING_TAGS_WIDGET_H #define OPENORIENTEERING_TAGS_WIDGET_H #include #include #include class QIcon; class QTableWidget; class QToolButton; namespace OpenOrienteering { class Map; class MapEditorController; class MapView; class Object; /** * This widget allows for display and editing of tags, i.e. key-value pairs. */ class TagsWidget : public QWidget { Q_OBJECT public: /** Constructs a new tags widget for the given map. */ explicit TagsWidget(Map* map, MapView* main_view, MapEditorController* controller, QWidget* parent = nullptr); /** Destroys the widget. */ ~TagsWidget() override; protected slots: /** * Updates the widget from the current object's tags. * * Does nothing if react_to_changes is set to false. */ void objectTagsChanged(); /** * Updates the current object's tags from the widget. * * Does nothing if react_to_changes is set to false. */ void cellChange(int row, int column); /** * Opens the help page for this widget. */ void showHelp(); protected: /** * Returns a new QToolButton with a unified appearance. */ QToolButton* newToolButton(const QIcon& icon, const QString& text); /** * Sets up the last row: blank cells, right one not editable. */ void setupLastRow(); /** * Creates and registers an undo step for the given object. * * @param object must not be null. */ void createUndoStep(Object* object); private: Map* map; MapView* main_view; MapEditorController* controller; bool react_to_changes; QTableWidget* tags_table; }; } // namespace OpenOrienteering #endif // TAGS_WIDGET_H mapper-0.8.1.1/src/gui/widgets/template_list_widget.cpp000066400000000000000000001214231325266516600231200ustar00rootroot00000000000000/* * Copyright 2012, 2013 Thomas Schöps * Copyright 2012-2017 Kai Pastor * * This file is part of OpenOrienteering. * * OpenOrienteering is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenOrienteering is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenOrienteering. If not, see . */ #include "template_list_widget.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "settings.h" #include "core/georeferencing.h" #include "core/map.h" #include "core/objects/object.h" #include "gui/file_dialog.h" #include "gui/main_window.h" #include "gui/util_gui.h" #include "gui/map/map_editor.h" #include "gui/map/map_widget.h" #include "gui/widgets/segmented_button_layout.h" #include "templates/template.h" #include "templates/template_adjust.h" #include "templates/template_map.h" #include "templates/template_tool_move.h" #include "util/item_delegates.h" namespace OpenOrienteering { // ### ApplyTemplateTransform ### /** * A local functor which is used when importing map templates into the current map. */ struct ApplyTemplateTransform { const TemplateTransform& transform; void operator()(Object* object) const { object->rotate(transform.template_rotation); object->scale(transform.template_scale_x, transform.template_scale_y); object->move(transform.template_x, transform.template_y); } }; // ### TemplateListWidget ### // Template grouping implementation is incomplete #define NO_TEMPLATE_GROUP_SUPPORT TemplateListWidget::TemplateListWidget(Map* map, MapView* main_view, MapEditorController* controller, QWidget* parent) : QWidget(parent) , map(map) , main_view(main_view) , controller(controller) , mobile_mode(controller->isInMobileMode()) , name_column(3) { Q_ASSERT(main_view); Q_ASSERT(controller); setWhatsThis(Util::makeWhatThis("templates.html#setup")); QStyleOption style_option(QStyleOption::Version, QStyleOption::SO_DockWidget); // Wrap the checkbox in a widget and layout to force a margin. auto top_bar_widget = new QWidget(); auto top_bar_layout = new QHBoxLayout(top_bar_widget); // Reuse the translation from MapEditorController action. all_hidden_check = new QCheckBox(::OpenOrienteering::MapEditorController::tr("Hide all templates")); top_bar_layout->addWidget(all_hidden_check); if (mobile_mode) { auto close_action = new QAction(QIcon(QString::fromLatin1(":/images/close.png")), ::OpenOrienteering::MainWindow::tr("Close"), this); connect(close_action, &QAction::triggered, this, &TemplateListWidget::closeClicked ); auto close_button = new QToolButton(); close_button->setDefaultAction(close_action); close_button->setAutoRaise(true); top_bar_layout->addWidget(close_button); } top_bar_layout->setContentsMargins( style()->pixelMetric(QStyle::PM_LayoutLeftMargin, &style_option) / 2, style()->pixelMetric(QStyle::PM_LayoutTopMargin, &style_option) / 2, style()->pixelMetric(QStyle::PM_LayoutRightMargin, &style_option) / 2, 0 // Covered by the main layout's spacing. ); // Template table template_table = new QTableWidget(map->getNumTemplates() + 1, 4); QScroller::grabGesture(template_table->viewport(), QScroller::TouchGesture); template_table->installEventFilter(this); template_table->setEditTriggers(QAbstractItemView::AllEditTriggers); template_table->setSelectionBehavior(QAbstractItemView::SelectRows); template_table->setSelectionMode(QAbstractItemView::SingleSelection); template_table->verticalHeader()->setVisible(false); #ifdef NO_TEMPLATE_GROUP_SUPPORT // Template grouping is not yet implemented. template_table->hideColumn(2); #endif auto header_view = template_table->horizontalHeader(); if (mobile_mode) { header_view->setVisible(false); template_table->setShowGrid(false); template_table->hideColumn(3); name_column = 0; } else { template_table->setHorizontalHeaderLabels(QStringList() << QString{} << tr("Opacity") << tr("Group") << tr("Filename")); template_table->horizontalHeaderItem(0)->setData(Qt::ToolTipRole, tr("Show")); header_view->setSectionResizeMode(0, QHeaderView::Fixed); QStyleOptionButton option; auto geometry = style()->subElementRect(QStyle::SE_ItemViewItemCheckIndicator, &option, nullptr); template_table->setColumnWidth(0, geometry.width() * 14 / 10); auto header_check_size = geometry.size(); if (header_check_size.isValid()) { QCheckBox header_check; header_check.setChecked(true); header_check.setEnabled(false); QPixmap pixmap(header_check_size); pixmap.fill(Qt::transparent); QPainter painter(&pixmap); QStyleOptionViewItem option; option.rect = { {0, 0}, geometry.size() }; style()->drawPrimitive(QStyle::PE_IndicatorViewItemCheck, &option, &painter, nullptr); painter.end(); template_table->horizontalHeaderItem(0)->setData(Qt::DecorationRole, pixmap); } } auto percentage_delegate = new PercentageDelegate(this, 5); template_table->setItemDelegateForColumn(1, percentage_delegate); for (int i = 1; i < 3; ++i) header_view->setSectionResizeMode(i, QHeaderView::ResizeToContents); header_view->setSectionResizeMode(name_column, QHeaderView::Stretch); header_view->setSectionsClickable(false); for (int i = 0; i < map->getNumTemplates() + 1; ++i) addRowItems(i); all_templates_layout = new QVBoxLayout(); all_templates_layout->setMargin(0); all_templates_layout->addWidget(top_bar_widget); all_templates_layout->addWidget(template_table, 1); auto new_button_menu = new QMenu(this); if (!mobile_mode) { new_button_menu->addAction(QIcon(QString::fromLatin1(":/images/open.png")), tr("Open..."), this, SLOT(openTemplate())); new_button_menu->addAction(controller->getAction("reopentemplate")); } duplicate_action = new_button_menu->addAction(QIcon(QString::fromLatin1(":/images/tool-duplicate.png")), tr("Duplicate"), this, SLOT(duplicateTemplate())); #if 0 current_action = new_button_menu->addAction(tr("Sketch")); current_action->setDisabled(true); current_action = new_button_menu->addAction(tr("GPS")); current_action->setDisabled(true); #endif auto new_button = newToolButton(QIcon(QString::fromLatin1(":/images/plus.png")), tr("Add template...")); new_button->setPopupMode(QToolButton::InstantPopup); new_button->setMenu(new_button_menu); delete_button = newToolButton(QIcon(QString::fromLatin1(":/images/minus.png")), (tr("Remove"), tr("Close"))); /// \todo Use "Remove instead of "Close" auto add_remove_layout = new SegmentedButtonLayout(); add_remove_layout->addWidget(new_button); add_remove_layout->addWidget(delete_button); move_up_button = newToolButton(QIcon(QString::fromLatin1(":/images/arrow-up.png")), tr("Move Up")); move_up_button->setAutoRepeat(true); move_down_button = newToolButton(QIcon(QString::fromLatin1(":/images/arrow-down.png")), tr("Move Down")); move_down_button->setAutoRepeat(true); auto up_down_layout = new SegmentedButtonLayout(); up_down_layout->addWidget(move_up_button); up_down_layout->addWidget(move_down_button); // TODO: Fix string georef_button = newToolButton(QIcon(QString::fromLatin1(":/images/grid.png")), tr("Georeferenced: %1").remove(QLatin1String(": %1"))); georef_button->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); georef_button->setCheckable(true); georef_button->setChecked(true); georef_button->setEnabled(false); // TODO move_by_hand_action = new QAction(QIcon(QString::fromLatin1(":/images/move.png")), tr("Move by hand"), this); move_by_hand_action->setCheckable(true); move_by_hand_button = newToolButton(move_by_hand_action->icon(), move_by_hand_action->text()); move_by_hand_button->setDefaultAction(move_by_hand_action); adjust_button = newToolButton(QIcon(QString::fromLatin1(":/images/georeferencing.png")), tr("Adjust...")); adjust_button->setCheckable(true); auto edit_menu = new QMenu(this); position_action = edit_menu->addAction(tr("Positioning...")); position_action->setCheckable(true); import_action = edit_menu->addAction(tr("Import and remove"), this, SLOT(importClicked())); edit_button = newToolButton(QIcon(QString::fromLatin1(":/images/settings.png")), ::OpenOrienteering::MapEditorController::tr("&Edit").remove(QLatin1Char('&'))); edit_button->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); edit_button->setPopupMode(QToolButton::InstantPopup); edit_button->setMenu(edit_menu); // The buttons row layout auto list_buttons_layout = new QHBoxLayout(); list_buttons_layout->setContentsMargins(0,0,0,0); list_buttons_layout->addLayout(add_remove_layout); list_buttons_layout->addLayout(up_down_layout); list_buttons_layout->addWidget(adjust_button); list_buttons_layout->addWidget(move_by_hand_button); list_buttons_layout->addWidget(georef_button); list_buttons_layout->addWidget(edit_button); list_buttons_group = new QWidget(); list_buttons_group->setLayout(list_buttons_layout); auto all_buttons_layout = new QHBoxLayout(); all_buttons_layout->setContentsMargins( style()->pixelMetric(QStyle::PM_LayoutLeftMargin, &style_option) / 2, 0, // Covered by the main layout's spacing. style()->pixelMetric(QStyle::PM_LayoutRightMargin, &style_option) / 2, style()->pixelMetric(QStyle::PM_LayoutBottomMargin, &style_option) / 2 ); all_buttons_layout->addWidget(list_buttons_group); all_buttons_layout->addWidget(new QLabel(QString::fromLatin1(" ")), 1); if (!mobile_mode) { auto help_button = newToolButton(QIcon(QString::fromLatin1(":/images/help.png")), tr("Help")); help_button->setAutoRaise(true); all_buttons_layout->addWidget(help_button); connect(help_button, &QAbstractButton::clicked, this, &TemplateListWidget::showHelp); } all_templates_layout->addLayout(all_buttons_layout); setLayout(all_templates_layout); //group_button = new QPushButton(QIcon(QString::fromLatin1(":/images/group.png")), tr("(Un)group")); /*more_button = new QToolButton(); more_button->setText(tr("More...")); more_button->setPopupMode(QToolButton::InstantPopup); more_button->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed)); QMenu* more_button_menu = new QMenu(more_button); more_button_menu->addAction(QIcon(QString::fromLatin1(":/images/window-new.png")), tr("Numeric transformation window")); more_button_menu->addAction(tr("Set transparent color...")); more_button_menu->addAction(tr("Trace lines...")); more_button->setMenu(more_button_menu);*/ updateButtons(); setAllTemplatesHidden(main_view->areAllTemplatesHidden()); // Connections connect(all_hidden_check, &QAbstractButton::toggled, controller, &MapEditorController::hideAllTemplates); connect(template_table, &QTableWidget::cellChanged, this, &TemplateListWidget::cellChange); connect(template_table->selectionModel(), &QItemSelectionModel::selectionChanged, this, &TemplateListWidget::updateButtons); connect(template_table, &QTableWidget::cellClicked, this, &TemplateListWidget::cellClicked, Qt::QueuedConnection); connect(template_table, &QTableWidget::cellDoubleClicked, this, &TemplateListWidget::cellDoubleClicked, Qt::QueuedConnection); connect(delete_button, &QAbstractButton::clicked, this, &TemplateListWidget::deleteTemplate); connect(move_up_button, &QAbstractButton::clicked, this, &TemplateListWidget::moveTemplateUp); connect(move_down_button, &QAbstractButton::clicked, this, &TemplateListWidget::moveTemplateDown); connect(move_by_hand_action, &QAction::triggered, this, &TemplateListWidget::moveByHandClicked); connect(adjust_button, &QAbstractButton::clicked, this, &TemplateListWidget::adjustClicked); connect(position_action, &QAction::triggered, this, &TemplateListWidget::positionClicked); //connect(group_button, SIGNAL(clicked(bool)), this, SLOT(groupClicked())); //connect(more_button_menu, SIGNAL(triggered(QAction*)), this, SLOT(moreActionClicked(QAction*))); connect(main_view, &MapView::visibilityChanged, this, &TemplateListWidget::updateVisibility); connect(controller, &MapEditorController::templatePositionDockWidgetClosed, this, &TemplateListWidget::templatePositionDockWidgetClosed); } TemplateListWidget::~TemplateListWidget() = default; QToolButton* TemplateListWidget::newToolButton(const QIcon& icon, const QString& text) { auto button = new QToolButton(); button->setToolButtonStyle(Qt::ToolButtonIconOnly); button->setToolTip(text); button->setIcon(icon); button->setText(text); button->setWhatsThis(Util::makeWhatThis("templates.html#setup")); return button; } // slot void TemplateListWidget::setAllTemplatesHidden(bool value) { all_hidden_check->setChecked(value); bool enabled = !value; template_table->setEnabled(enabled); list_buttons_group->setEnabled(enabled); updateButtons(); } void TemplateListWidget::addTemplateAt(Template* new_template, int pos) { /*int row; if (pos >= 0) row = template_table->rowCount() - 1 - ((pos >= map->getFirstFrontTemplate()) ? (pos + 1) : pos); else row = template_table->rowCount() - 1 - map->getFirstFrontTemplate();*/ if (pos < map->getFirstFrontTemplate()) map->setFirstFrontTemplate(map->getFirstFrontTemplate() + 1); if (pos < 0) pos = map->getFirstFrontTemplate() - 1; map->addTemplate(new_template, pos); map->setTemplateAreaDirty(pos); map->setTemplatesDirty(); } std::unique_ptr

    ylIuB!V{cj2+* :|G,1wzjN N(MՅPuݝt/Z|o~9);^e|W㣚𵜧DWϫ t`Μ9_[l_F1U&7lv;^?9_;l`(}#-2m5}϶?&?2S=oK<Ȭf?-;;٫`iI*vv]~AfoteeU˜ʆ`,رʇ.hyۡ}/ԕmN?;o4L}NGrO`+Fb293AlK,f։p=i|r˱.#zb^R+,a6#j=+>U zϦN){$}0v|>cfCݤ7ºϭa wo~ؔ1*X&+/\?L> ]/[ YN֯oqG3r۬N?Vq~~o s$ҙQpW;s>̾j7uHiMibAGGYb?' C--WT O ?7RּAkٻv<3i!8oUo|Vo|)T:5Q zUi| s WRZU5!}g̲6*#n];c]q1Sfe>>ƍ/Nwz4Rk(Pї>h?i'QH+CH}1u3g8HFNkQA2,~ޟ]V ([9\))(0SM-e9S-F<,eQ:[ >ai[~op%.cΆu̾G׆jh`}.B oD@^{N@Vmsϙ;ُtپW׏>TMk{T&5«`܌V&"]%Ŧ]$$"ӟ_. mSRM^G?DP94! WK'@HSI^Xi"P]F6y I`^`*8cad=-ktEGrX)D^; ,9ɔ[Ab,HWU9%! F2w |yK߉cc]+"otߗfVކi>Xh|honL V >q?.C)7r~w:IP'6=p)}6ů3You~z{1 a@Pl[,y<`U&DL0A:IaiE_l KlChA H[ J.7@& ƔGϠmSEXOc$@d:sz) f+ ?a1:>:~ԝ~ñ[`J]{?߯WZ>Pegt0sF+OLA?=OB3[J㍟ym0"iwo|FL{jo2Y?/'.'3?xVoό ^2Y_H؆P \#Z^69D~4L齿fXFiT*jեwCQȑ$I(K oKH۶S|G0"y2e$oA8eҫP$T1o;;uF@l{r,ltU ;;v7b+Թ6ݛH`JCߧ +k1< .ϻm[HUkUp(?X^Gyʡ֝%w8x>g4~o~Sؚ"fO!U3CXB^VWTL)_:־xgdsg/M j%{= x?y]O4$+:d(_ˀW ]aMu E N=~n)06Nﻜߝd2$"R7> p`#Ez:*~^[r tr^b"-t]gX}IlOŏx##b>fN*i }xP7 soh g"^o( fc#6V_ Gs΁`.{= ,m`(Q( >$&B ua>x6c;6A"o+&$gt0ZJfuz<(ˋY{:r`Dis(," f+>f5 }'dxS~2~V^`7`rfϙy9jsc;đi'8O=3x(|=t3K  r'`qi rI8f,dFQx^TAdf򳤱"`aGy& Xf\$ )iDq.-2=| s> /͉J-Nヲ~^m !&p`S'$vGWEr885o ^uk]t%`o=rÍ2FX >k< t|ȉE: `Dc>S\lΥP!l+HR=̿w>(6Jjq@#.8Fzg}0 =T;`WNO}7S$oo>I,~.=i3"1P#:A93Rd*,/IُO_OSggHl~̠uX<ǯ!Gs qӊ49gS ФI\0:i'Y`XKSvڣ읍r0?9sSeּ&QdUU`̐->߸KR 8 4Ǎn]ȴjY8*$H(\k@gﶆY{ag[+,oo~ˀ33!Β;?J/1ϡY6&M_/^stk fd5hWd>suB-2Ŀp_hL7?)O@= _^XŚ%"&$c|FWSN ='X`mqۥTGm Su04NQ䌉xHo=#+Int/nujl6}/Kg~pYˮ 0v>r<x6)lt~@el; iw!j7쭀63:mŏaC?>޸T4 `d,uWC :Vh# O=#l`Ͽ G׼x [o2L_zM>/bljLK\a2b7o( Aϯ}8&Dm n)x5 rZm[k}7ά|S|)1mO3ʩ\q3,0}J/,{i7K=_c㙖7sWc }[pxڬ'ZGkG 0?J\EUߊЧ-t7I7fأԵ!Iٵ;_mz n#cvZuO$ԛW -c-k?.>0x;'PtvsI_*חD@Gf;(7(ʐjcM ?_'OgMOBݶ)EW̬ haGAU, pܷ~YhhpN?Mw۾a7tϱSg0u|?W (w(/p;~;ܽ߅ǏT__* Cc׊KYaĿ.}.*ZXqeJ7; 1<탳`c3Bw1>㬵#J_es~m[z_oǟq7j<tB# d e*9 bÈBI[ApbyF`(&^2s"utdz?S{]^?XGANm ][gwK|kӠ< ѷ kp̄#`O_G̎ôs.sWSWgrxp$7[ ~;AV+A}2oNɹ"ݕKB7x}Ӵ}o!ծ=_|4v+&#G?}:::4P>6=lҧYyl `svA0%' Ċn_<t@[*>DIn(2,P/_Բ ;Y*!ꌞ'@laZ0!pȶu*fA{oH~¯ $>jG]]p5 'Ry/j vI[7/:+Zgr|Vx [WG:fK]c`W0{Ҵ_CL?͎p5b5wð֮|S^ɟٳ`UĿ*gO qgI^EWNk8&1:W Ij9d5e$ :~ӫ7[pRv֞00-wJm-L_6ab@WaW0rtr'Tl\W_*pd8-!9u|H8!-c6%U=e{}ȵ^z%toOr>Vl?=Vk̮4EJ۳W`C?2&3ezyK`%/9+V"U\r(Z/`z4%*Xs7oP!2{Qr:ʁ̛πKg}<.`AW^q w4ED3cwB[>f4_R zG*$<1<P2.w%b[~A2;n|ԫ)֨GaI`Zgq`9̹_YQ\*8C}5 _(ς?.K3SНFէ"{5.gh0 KM^v̿ h9dT*Q ~`}_q?|~E/j4nIsz*S7@vBV7z=a=&A8 &t_ 23zQ =r Id_4ݶV`uVN^J|=ñ]iޑB{},<XDCm;nIhpО!ˀM0k`Ϊa%׹t2FB;GEYJz 0`pO#HS2* 0`s x'҃8f?TG XvHRT_) gK~(W%ZF01s͸{XO׋QDb0-7ua ۫` -+MfWR0(R4 iE( *p<Ҁpt4߬x*9+Ch ܃ҬD6A @Xk)/LS;N`i F}l%׉K% pT?{o? {D޿{L ::uϓmRfѾ~H.z=^A\p;o"> J?z|UUmƕ ҹ\qg7͜K[ !6o3s!Z 7ȐwS\WdWk‰r5WX> 9Iatlq|z `B،tA`^ p ? H>m0DÀ U;'o`tpb Hv4C΁tF%"i{1*K~9SicuE\_cm< ]Hƿ(xo~FP4q .K ~+Հzzv{_/,oa}=Zv&-9,& `u sW z&'Qo#kL Pfs-- }<~D8}!,tfx7 |0VQiAMH@'( #k$c~- 0f-H9Py̤?-ң+s%$5gmlqKly%k<voif߳98ypw9g{nV/'r.WWH_?~Bi2u#9?Lry& +g^U~cw)T2K 0~ q lEs `կW\G*C  f*E@A_“ƻ _飇\ *t|>j¢^r:PTGkol?P'CR6=Vp l T\{ 撸$Vj"]0d@1H acЗ[PƜ[:xHCfg7ZqP>?n?!6 3D؏Ds!]-f쓦cخ>fCﱃ3}Y\ nBcKBP3h|߄y¼/+ d5KJ6*IIkW\*orC=X-7oY}:ꀣ:}@ Bi40+uktay)F :yʇГ6zygMЮ+`><7[K +so|fGetѴkkwGon{5c{>T4`=cΤt@2gv;ғgF2}ϩNJa~.'Kz<5ZNF0F&ԑ*xٹ7SIҲ=`~^u0ၺ.Hl~ /}j?߻zmKn=$й/Ʉ/Za0e{Nہ|m "իN/OrR͑*LyE^O}8}"{![#ymQ I_ w~s@mtϽoqH| }0f((~Жg"}摺{?x.C7u |D 乷̠@zIg=GU{HPaĿ7/cS2m:YtolMwx (З *𡪟^_3qr갷_Q9 b AA"mfǀC f ܿ";'<-~/c˜E{h|=m&%/]g%̥+ӟ@ǘqi,ΖFGtc wɦC4[i5dlq#CS'*`8T!  Eù`&%~(QJi`0ǪQpꆶA(֫mzҙbx?Ǿ]geQW 0n\*㇬f;X2<_GaҬ<ߊ7g EpWVM~d>y nB d؅DӀv&/.t/=gإ4@G`|4rc:xB=%0_<~Gk?g:K%eCy多YxezM_W]T(uO]ÄY` y뽧%;tX[K ,"[ ^ՠv:2AwOTJ@ne {R% $c+ts; jk sd&x>7<^[vxO]c`K^K_fM2wf~~?8\|UqRo~S P)|ش y?.'<;5uV-r,hǤ;eQAi ܏.k_32٦h;asO '쀞w'ن9{>Ahkw2z2Y"F!ZDK*3.<f,>&/NAA8e/T-[a͒a.w1MEc62aÄ%V=YJwߤ]e*gUgq̯(ѓ_}+wk"@x08Z,vSroez. U:(~wZP!+hL?9v"{Òߐc-Kj JLyߔy~ ȟ1/GP/}|pu!rݝ>dukKo4Ii\/f٥r`EI`@#tӉ0X"m~K!{ v?w0˗^ R$*{A.%M d0EDGaٟ3Kĩ0yR`L\ؑ A #""l12"s%^V" r,!EDࠉ9_ / *r*}!xS]Տž\8]sw wI|8KE"^E"l.X?S-(ps4Xv~"$9r׵ٰ $¹4 eJ 67{~Y_1kq?4Ҕ:UPh#EA&{Iʐ%1e؟$XI~ko{=;nîgaVĶm#(eߴr>į[AC!D/ $@<?7~SDפ&p!Z 0LL"׳7!m?Z |Iy 愈3-VS~;ۇIlo ~aDl'~4u}90>dg߷޶Z ^Od*q`Kӌ_˹v{m_9Bo j]ip-ĿOn);#I~H ?]XY %V1 3q%j$i a9a O}Y>D؞},JpG ]gBcؒOCw^X*=B2 x&^-蟝cƃ!XSRF Ţ܌T0HLLJp9?1G> EIq`A򦯨/s=~G( 9!_zد} Ӗ\}RwFh򂁶?=O֦|9/-e qg')An?H_F3@y%0,?(wYŚfĿ+_,=M`Ee'(p./;4XaI[}}g)7q>f'voJ8"n煮p*Vl_hJ+z^ ʤ4j v\0AC YRᏛ9J#KHyE[''Q=čl߶ rDUj )8̟Y|&lrP0 @ˁ{}[?dH#gEO2g]"x{ f]կ `+84 $eV\DE_'5ɯv&H+utò]cS ~L`K;0&cG5=KŒ5}Bl_ZJg 9sg[\7~\3q\(2hg[rԫ e~uGhruzZr׾ :M_>W>#+kQ@~\9?,'۟rnUlٗ8?!-7!J[Q=R4f7 m#;6}ۡ$+[Xd[A/syZ9Lť'UEUSK`-sًL?uˊ*H^2wmxv?>8!6Al?sc s_V,kc^n8&=c$ &r>`e-),愠2pk spy=lBEBS< jTq p>G~7?' 2~nt 4Zٲ{a]4m\OaD0@Y>5=xneS):.[0qyʞ2~]:aPb3,ɟG+GN,9/gNliνCAkJ^mGr;q*T/ 2[wc UrEvOE hy%,@]Pz`xM!AoEU߬kzaﳏ#{MNX%t-|>NǕ R j8H',i/=~?k |0.'Z{`ᾼD;"~38scmb/Hc̓rp}`}Uvy`o D+ #R`]/]{"U8 8>@=k@\GkL寷x`fB?@Ay?~b]VIoJ+Cw* S_pV [ #GS?vC<詎jTl7j}1)Q5ZG<ܪ6z4SMx(dwD~UZKA~yOfeۃkkbdИ;"e@!9YE 2'!ؤHמ"C㏀-t)QdmLGSҏ;P7.yaկj ?[u>6G5 A3aO,?@)?Q \]L<Xu: rVHZoc >Y\?iF|wO qۀ"-qc VAJyo^T-Kæ 0j8r6 `p*(5Os}^{V3 l)yv5g/M:V|oI Hx@BطETMGnAX[y<"`=*&Mwg@0g1\;_CR>ť][}L`7oHPcT2eG~et@ctҼ#QˊocB^{H * `PN;_R 7^oD>OIA"c  x= f-J3yZkDOF<(7VD`ZY%R& JH+Zv@yDFV9?::Vi9e*I ڔ;+ʩ2&L~0sFY` {a9eT*Q +,ǎ(c2sKATJH;Xy@ XJAH?|̼Bs툝p~o_Z=<4nuM , {c6 G\R5TwEyߍq ⵀ.-_P]s$ѻw+Eao݉Dve@EE̠H/}@9T:g!nQ&~Ag(B=z{kFq_d `&p $":"E4*7 e|15S ӎ}. +hKrbΛV-o~6p6˫!V`d3,gUo3TQioy:ϭC;mff@A}j@KDĿz.b◧w4TMfZŬ_=0;:`st qŀX^2H_@FzO]?+iK.+yL1jE !xyu֩ {ҩ?-yQ\Kc_mqg9<QFJp?1-PCB>&D[䎛>7=$u\GN_N~WCačbW2N+s |"R#J ]L)V0%uNftu ^1[P%]D1Aً r$O~m({91 @?xɽc:~~#1PWMUa0!SE VП֊VsJ}]sSjcP BEL [M׌QO]7rvTmIBN+V7t@+a5Pg"ے@C&>EA缈_A\%@x :jn_B={]N30;1v<\>b_6ջuU#FI{!l[FҺx0`ίm5{/z}瞄o7TNq,芷}J;Z!5}6*l83 Φo#]ࠀMoYwM}NOJnn5wF;JI´']>/2d=b~k6k@"Z6X(+ې (ƿG@o?Š+ٙh* 0o"xGTyAg4%p|'a)MhU&kV-0vx&_ Ǵϱ/o?Bo8if uB̐I< '|n"U߮;rY7)?j00GRjÜ0?aE0gi vk "YbifM> ڀx,mmۋ@] z}Ѭ~M|+E-kQ@)*m¶8~ ¼dBX'"].* x /X^Z0)A/*3180@uh#2䞒8/?Z:9=bn n}*g$&ir^?nn }G MvV{QDZf8wU7k/tlakR_2(kIF.)ŽůRˇÃ>77Fb?W n]9l^ K,ū_~?/xIz0?3/ro?TkUs7vN +Pދ@Y]5V~`7<39V)-Im.c lgnZ4&@:x0|rmp=q6*5F'^W R/yQ0"!Le!! b; I4Kr0q RU=^T<^ Sl1|~ lv_{i!;Go"iK$(ULޜv@j&سz>Sq>oKܬwK`[sX\-0Svi1f:}RFHS )Jd^;FdٸyMMǻOx`RyY0W<%O r,Bp+ҧ浙?\RE xJN/4g.RʄOD$ 翀\Dg /.#4.,\s8`FهopW¼egoi)yˆp8ҮTAHjP!F!ϲtpvjaԳc]+RceC*R=*\d 0jם qЋyg,|uEv@) ϳ]KvR‚7SQ{& Ƹ*#T9)j;O*<#MWƲ4g΍]T[z* ;%D;zI_-~kԕC*?|Rh?'ka%ڈx(+ i}4@cX3-Wah.p͖r *4J׊4 t,(Ȍ̙g# H2WE="-z1c>*=Sy'lqa\5)#&?9H/ Dx `׵e&'b:0Ei{T&"$?I!rp˚I`2'bcH?^T\MyXpEm!u'ccQR?F6`as~'yW 1a_f? O4g,ڄG˓/:,i9!jff_g4V(--րQMdo`ӭUһ%0 S/Yz^'|1VvMD{ 0i9>3 $d'E♵,8CWOh>Y y3ⲛɱ=|{; 7UO~II%E}Z*;'\ȟd/e& .sXPx3W0Qf?/,yO]XV'-#9:Ǜ&kV,Z`! *٭ ޴Z]:VR 2~M\fKr+XϧoHŎD[KShJGEs߸ {Z4dzv@#((뵮Vr7Q<NټMEy+X}߳ hHlb}}%ڔy._ +&1qDX!"ʺuu2FtpT*< ǏJ=?ML*xYZlו%<su'6!iWwRY2걤ƿ;vY|)IM `޲sS YlzIOOe,Zǿzxqy=,:\֊~tr>:$j(:\q}j/:t_߻{M}j}k_N<0SLK:7_P#uv "=&uJi3q dĘ,( Yi NSV쑼/W]@aOd^D2fӨ!W+ ?#m?2 ?O=tHd=N=b{Tμ^lb=˺Lm"D [~QK:)a0E'&U |m ŦvRM\v:zO/a)ti`[Ļi>Cvt\K_smge:Xj@C[3;}R}4 XH!I+25w7G?AwF,H _J"$@S0}~SlI+ h,7 1ip"1 pt< @L(s@bu|j WW&fKn* }aIQޜ,%:3R록h{.?ִ@Vm<0%8tǾi5Π=Kn/b OgccuEg\Wʟ*&-K)}RzXOL(-%1mu|9)?6 2Ŀ{F{xCS?vC?qi0,gYs [avk cqdtT4tϟHfdיg4mYY а!eqԸS1WrbŸ׀kw)NH'^*jЭNj ϲɛʖer,poC[^3{mVOivqOȠ5,>) u6 1ۿ4|OWb*fo{@`l /{$ ` ހa YO%iȴ6bb@@YAIYtʂTJJ1kG lՀ':ly\4*hF˥%" ڐÓ/qnmU@je=_"],2SmT V'ATZ9f@@|-EL|81 O@O:~?j<տn2Dncz_uG~/՚'.*\Y_tm7AeL hk'0]7t_Oڽoc=>$9+ޡu UbC}=_=grn/Uy~@ Ͻe)FvTQ~ˤFIrݙH#B <|2칝`& |;U`V&]M &CgLAl/D{S .iG%1eh߷ԉ"-Q@QF>`Uc?\yUx\4dxS /zB7/7*6MjT)x`eonY*gqL5",l2 l pVH\m"!W_SP_0waAWNo3 q=e4;z@4HTxh-T#[]A-i묻Ϝ7%}ol-XQB}pZl0`&p1dDaa3/؍Lp{<1vgJ9ei*1>>o^8> p[Ox1ac~R 5䘎Go7atHK9ڮ1Ym:ZGYYlb&?X̣%|qv >|&`n y="ſ51@u|C vqҟE!E$8iU.u&>{?gp VX9rl%əjiepvx$/L3Z'h}rjCƷ38r)րׯG|L{i A!G8 z'ǹSEG]5b{@" -0c赛mi={TL ,Isz̊խi l4ˈa?5Ѻ<2Q.*szT|,aJ,+C#"!Pšl@~}ɐQ?L XU>09bE%O304[ٍ T`J(ySf[|/εQx˟'P::MxXM똭?^-}ĝK~c?Vǒ@O+zV%<άǴӿ$7bff@+ 1_2N'M/ZG^}n $iX>EL|F7FA]=vB@Dﷁs\%RX_LzR+f(#s/:׸Go;콺50&9$s6<&$vy>!>2Itdi|ҲdY0p[_vteGADw ?Θ;Z3=2 rY}nZխ- K+,>hdKMk?=KgњdBqcΔ5ST4 KJEmF[6ܻ?dT_ `! fS{#{HHt0;F#mQDvIR*9I?&IzVbHۑD*Nha <%o2L˙׽{GCwWɛNc8Li<@>224Oʙ ™o}.&ҀAi;&Mh,?&7əI ;rWd\?cl>>~ZG)OנI qH&9V A?AFEB6sg}ȬHd&E5̽$|O;3!Z#2PuɈ`X/#I0\ LP?`S|VtO ͥp=ł|*3+J%#`0EP6-ȉ ξh3{?PPb]e1FpK$c !?G''̷6t0Qp~z:-SO[R*VFu|"ۥW4IK|+Ii0pKξEB-wtZS¡A7Lvtߜv3oL"5'e-rR&NX: DqABL0(6}li 3PٹyPTKH1`WT*F{ȹql{*=Q[L@M| :y"b!/IZq"wVD:P"~7szY9x}.  \[/m_LJ_38-/ތLJ?pٱ+Odզ[ݫ6>`k66^ղm^dկL}_qmp j!2I(ӷY? own;R22aǿ^G %"c#ihX~%QW 2B:&; C$T@&)HLH )i3dϖ%YҌ;! k\K49x7&yVOͽ(QvMzɮr|dF,#plPD}N([uxsW:+$T;.Xsvs*m^j.ii?B,R:."^+򌙰֛ZO_ 1(͜Ac['?(ƾ8\DzVok8!6%`f( |mtw|X6Qӽ{w$&뇤ͯJT So>$Sڐ'`]l]s7zWCxvd!h*a{xl}GI*YMo3NlhKẅ 6p7Z& bϭݧz#""1|lpluR&>Q.hHg&3gQ"c5CкZCU Q~ӐTLdiz}O=[`'aO/t0?? SFC?շߤp5I` o% m {E-#)G;cI󟴶Bi€J!]|Rf?2{&8M} O2883wz:_KKK36sIcc g6ΊYOgd; >):#fiV!wa@\?W12f PVDS :2xp%*nP1>~tn<sً᜿.'_CQq~za%zIؽfuqdN-Y_ OIhTG-Ç_K>7u[+d [7.yrg-M~T77c B,H6zE aFbY1jp}WC Pu-~\ոACSgRsHWzz4j6Re\=nQe>5zw^N ‘rUN `Jˮ_]o_?_'s'~A׶{2{ԋtk6=vG_sc쿝؏egd[L6B&Y 춤%%p~_ՌK5ɏL1=|9Fw%oL!(3x>O u}CӒg4^>l;K_bkF ju]L+FlJdԣY*c+r #VL+ #>I 1q:"ʞ=wX3fYm~%s#bOe[ͥ}=lm~&!ea۟ lAصjeD/N de7|;v_~/?Twwy IfD cnG}>Cnr߬d"lKω?+ "{$pa/[H'0w6t!%'s΃ZFX%F`yk{&3Ǻ&I&[1UAK` ԫ=EB4ֶVOEϰyns!ݚ;Ouhc_ V-p~6؄$qu`Z H?=!gBԧqg]  Vm68R3+=\3SMS:_ G텭O[1_l[W{U:]*3gõgX}ǿѾ ۟__|u6I Dzɶ|ᑎ,OcFٿ\@#s?-0i q Ŀ Coʮ|0\30}x? q2$Rmo\2i<|`8xhh`ؑR=T̜oVB&{4\T5?׮zQyJW ߷rjXWq+oD'<,h3}A̰6G}%@$A >K^|Z%@ˣs&BsKsp!H'Z˓=[6?y A8V?.<=+a?Gv2.'îWۂ.+=?VmXS㹇rxp)\>eeIVL*P-5rQV/.#=lZ]X{f )'Y_/>i" GXC|/.6*."OzT<0ɵg gnZR5{{{t0c,6ͺ$cZ5sVz?T0BC0v Vʾwh?3MŔh{H,9B=l`Ĥ@˛pz=&r/i"6~9Wa[vJMچn]k$ZbA-Q-] H8:"X?NSoYr<7oIm|$dmԋ 0xY:z53!ˑ  ҏKbrdੀS@fMcVJ¡<xIV)/X!f=aƾ׷2Tqvn DLeA^?8%~ݟ*ۆo3[`OP8+p/} ]y6jW K=_|Np/ѫMTU0>Tק׋d꾻z):f@B8 3no>$ϑQ;ݚ6?}> }6rZoбDΣl֣bq L_>rsH>6=TsAD6]f4L3H@;\G#dwڝ e}g3 }fA| dSw1c7O $ݥW'mGfT U PW?1ʚX_'!<{Ń6dCh0T|02\I|@֖6 r಴r zg^/\b>҂ֶ" 8&\YV-k,ϳ]Oo~ܖĩQ* r3Y=ivfV0XddYGWPh$֣K'$z#ͫ(| x<҂{<ۏF/U"_hoÏS``ݏ۞ϔ[a(ߢ:־(S6L?jt;?Ogu>Of?:}7 ,]uCeF/ K량2?ե^;ҲXs"۔eӺ2MU\zK23St|^ pj]=AĿX `|#a3q.dKedz1y/#I+;yd[<;ڱɜ"@<.K+0Bo !eX@X8((N\Ҡ'Ȃ'$4i@X K龙/k2LDzns6QsAi9`%=$D~#Ci孰o 9v1F4Uxdt f?~`p 6 <տWNÍ«Rk^K]_\f\t@ݱ(M=؈`"wKQ5 ;p )sc"DY'lǺ\SoO3]&U|'ќz`֬qZw)yXK6nw6`*I=Й7yXן%xKZ(鲿v EF=`OrOT򳿱?wO[^cEs 5%"S_"__oz//ú{Q>V`uxK[d{"r/'NَхgLgsT2~R~ 7CFIاַ&oBhylMi)+A697sAnJ sO3l\"c+,'ׯ~FL&.-Ʉ* $RP | uT]|mj S BRDl#(7O p@>+_{PkOBVQEye3aṗs.kwaώtpY9fY5ܢAydk.x&c?3/M[+RKg֛(DA% :-s6Y k^/n{FYoLx'3\@~v"aQd776%uKє4gA__&?{ro4-8 DMViy'=%J6?-#N, u~;P3[e}l%Vh1"?E(N+ BAɊiF{^ !l+;jbڛ@) r nu' hL/޴z+Q6()lE$ `n?Dɒ68m/˄'koga7iY7 _~͜SV׿ N-0gR7c `ϸ-/kߕ_2hg\8 O2o{}xsI1V88nVn4[g|QXs[b_uuFߍ+g+]y=9iğl""|6<]M?a@Q[*pks%-t4z0t(ΔSGk.Sfcw?56L8D,۰h̄:0U5߈FՒp=n?"pп(PO e,~ z>1;ؘZlWb4FsignD&8g 6A{bqJg2Rx*X!/4 ?%=30O+ ^K4qుO)UvHTg%`~IPO3q qu~@t`ӪR~7#s~θZ~I%X*_kLv񄢀6cIp.|o'~oxіЬ苣6n^>vl o8ApS.˘|ݨڝ98iimSB`:d0ϟ;;sN @U H-_9}q@曙~bomkf$)7Uk9IՏ59eYl6^.l%@ivc|ƁOWޘ%^d1zoQ SϞ$r*s"jc29ޕuo !ߕLu3ـoĄ̱``V.¼㟶c!P0eā ~$R3cqOH4Rv)Pd O-!tWR:"G^cExlNg~ڎAX3eD\'C:"oCv͏  ( Vc@1{~i\q7P7/V>əVIk1 J2`*W/1eGEaƶ*0oa Z``}@nX<+I-.vŧͅs9":^6`:̞Y+A3ֺx=4i,H]3zˤ0)t$)ZVC!ϗ<x=ZXdzZ\!#J*2[ܢЫl'3 PшQ LNc)ݥ_'nU~ŬwΉpu~~ ZKnK?t6tpyL poǶ>\3kAHܿ<i 9Ҩ:8}΅`}.q녓6~XO8>6`a)~;e붙Wg9@˯hVP ,osC$!\Qͫ ;a?^GR "])zv}z{G[39`] {,0X_AKs,zҁ\x^Lßi}lcoۣȡ7F_pep|N}IJJ_m1 ƻ?)ZO<}CAdnx䇰" X ?? s0[iiБ@qLrk@gm1l1fãsߡ0c~T"YsOR/o2u!߻GFΔ=dd/T>JFAsJL %$:7FD=C*i}Q dϫ/%3<74G#6d, ”e #"DAlpJM s! ]^EiWvEp(Y+*oxs/egO1jLXooзC{ᕇoW 9mϚ#=0ZϾ4cVl`ng DA|__ŇydQZ?\jlٵ9x^ o{f;]vBXB|)kQG8S tx޷&π$-5n($NpgpdOߺg?TY <~D"74c لn?-sT/%sB:{<g':ʄgT1# Hňώց*{|N]V ^p23}Qddz?$bwz/?ο#u7,.O|pLyKj&j{0 ^¦~ш\e߆/#SR%.DBn 0fǖP Dw͞GHS8'|E?w>h$}@ox=L\iTLh[^ʖ(io2`XRb&pdK5ٙ獃^#<~Z&֣zrÑ>3O#te&q_1jൢ3>980}ys&cQ!2KxO`_C[ֵ3~3i/n\c+'zu28yN(|.yaCF%Z<t+p_|τȍZ)حIɠkk&AG@L8A&LV fì'X @<}9:8 {I7pkjkTChq!uf!g>^ 3߷[1^#kqZWE=ylpWH$HK(O{<_q~@Fc@Z _nGq^hb .T"F-7% &3d;.ߛ.UË+RJ*X3j*`EEⶓT172 *T Ӯzܴ^&!Q^w-Y£_S8y \ک]fYiC]UAe~,?۹l(g'Ie8Ha-ߡQ7n5l6W #㛬IkF)c`}}}Ȗeu@GXU(J_ b<_DY&y?} o*e0ۅ^3`gAW acuXsU_d > }^a]Dzʛ/`?767XHOiE O2Ϥ[wv .߇Opƅ4ik~ZrxʬٹBlw)y͙g%cѢ]d?p_~ f/9#<뙴A¤sK;x N惟 k;y߄@eF.K`WᑾѮ}DjY-Pd3ҳ3g\.Fޗ->f[pp9R6>:sSpgƛ2S.a{1;;.}zvC=m Oy܊zCimJkIR8hF,gV# tGk՚Y>!q T<H 7^6a7:έ|"+߄-~"_u&/:~dr\s^M ^Ͽ |j.V+L1_L /s #Ou`+N`1wFi{4]k_?3qrrmj>^7dcB3b7<(a #pDžepQǨI/Aqd<,q+PͰH̏ !6:ne"3hB#Y{z)_@P}4&C/>doMw&ZxJ[3A2 y0Y8' \z[ ^ktO~ }~H{f)ޡ;sU&nO"c7U X0wVsz!{h?M[=qv4 WH?n4GgN`2J;N^@P}{lk|!02 @fqsp{2|ae}J24wb#K}(7 fs| zp&!{k#v|anDAlh6\:rCppUE)9[O3G] ?1. 3\:p©1k*E?Zwv!H[w?P¿}֦+W!hT̼7arE#~"@RaaK {7=z#,9N#+_*cY'Si0B;Œb8 $[ cپcU?#[Ҟ>C&" a%+,"7=^7C}?wX, (Oo ٶVz_Yz/r,nZO {|Y ?7ܻmJx ?~漅N42o[l>o_48Kїl0e7ܫY#~H? |,giFϣs ޚxr3~? +0GL[% "龼 r*>FۿY=>ZSD+JbI$'LpbU"1T0ZI,`1NEkԿ涛51}C;w|_'̚0/q,o*-.x=o/ cY6M8qypƛmqG$7̯zW8 #GAߜyc4i\^+9@yd7eӎ}P5!h5z#pC7ArK3l, ~V9yTj xOR^$}7r5Zf̘l`cQ#h4!Mr=k2Oh!^,ϯ;6!vGlޕh Cé`.oH}W .|oR`~D+O Ǘ2NZx󟄑Ǵ70̋a]K  cm\9&0JO/ls0@ Ƚ.Y#~xz@WqP~| oH7ihr61YG3aAYk_` TlKƢWYF?V5P4Gm[a7ЇX^ 7dpZe3=郣/FoS h  &`e%m _EtIL5>Og߇Ǿg:Y Ϯ YrYLܺN:BPy7xHŬo\m?LYkwh sZ+Wm2/ULv+oAfSx ϽP(i=Dr7kVf bٗ=zm5y'%" =`<9`&@  ky9~/Lkݷ W?=/eYڽ6`E(^"E2nMLkJ?{t(5n4M<4w&-KqKAZ~c*#+Zi{rGTft._\lpdTda`Zo<v?{E$v & iɋ ,le2iR>r~?EX V?<2܀?ow,kZ>$ϕvvEmkyi Vk#5bGE@@M iii9 VF-qYܙ=)u9 hU?|~|=C#Msy/zi3VجY . 32Hb ~a}}x~ '9Vx?O<0G >w?]vE?Nm;IƇNVAi>Sxܬr0.4(2 \Ş [w!$iu!zÖ[V ?Ӳ}~ܕr +^n?\}읷Ӑe,Kw_78Pq$yi V)z};oUow1oxVc ܬ^bma۫]iԏK>W+*_ii}|\$"o__$E^;U9֩2@y**-+pnɌe*㱭cFf=^'~73K%-+rf h+}r>f'=y˹A -w mnV:gasACT}% sfn _@ ȁgFW Yp'7ʰӌ=vzWr80fOqnA.sy+.=xfq.I >ƟmdVW}q77@hޭ(?:Yr>I 2- 1$>5q_m ]gg( -O#ߢoFרAp(;@_u7Alm~nzfLSK#z]@uP+W AA}֬Y{dkpu"|bP*pjH[!Dr1- \s[L^Ajt7iYIH&52nɄy_$"!<h)W<7)Rз'E0J\EgkEtG$4 %J=}''Q+h56C'[\W $;h߶[#~CG>wh=3Eo78=LPY~^@@\-)P|LcuB9/QU)$h6`(ۀ~dm*hLyr3^RH+ Eu}_mT9)Qy"+'ta<["#Z\Z|xmPs9~O_@ OUE k7P lڽ];8<*iJ. (S;\vF]GG2BkNF(@h8}-1X;^ "ٿV` 92vՀ\uGtJ@RK\(ǿkP~Cp;Ll;l-ҮI2<7+l ת\WNXc<Ȃם0UuL) X:rL~,j -ڬ_T s*}3t|AgAl}ꦻ.? N_0A_ꍆ潏m=k$/Tu'cf{2z?m2~p̓ǟǦST-(A(hRu}~hHT_YVCr{1@j.@y3n^U_7@uKSSx9%'Rq'UT d1׿E?`G,'}W%__ޏ s_|Gս}ęe%{6V!.Cԧ>sUy r2|k}{?o@^lV?@"<<0j7Ez/p >r섘&h* }_yv$U`Eo. 0Kx{yI?4oC_P۷myUZp2㆓ۿ xkXJ ]ɬ 0dށ G NcGn[Ai{~R 9k5!L|; <%F2r*NT3%"D|}O|T t01б|YW툆<枀'gɕ!wnWm<((e฾a*cO~ʙy{'PhPP ^9%ÇH 9ꦻ8 i{^C^VVVn?OHP267{CG8dq"*MF6XTwcG|Xݶ6۔=T镨ys0ፎ{O싔;{ef1dX$~jɴ(dR㰍l{JA@u6Uv-8K(}Y?" JxAxIs=Gj'_N_%g92&Q%`$y]؇oVDt\^6I ',V9Y N()gX~"Y'm5׬it0L/]`~:*OXXJ{M,9r+N?;Z7I61Krͥ~~^?@Y?0 PyF#!3wzUJ =@hV/Z=gE*xys*:^S0..mO锬q4P?>?ן9w~eݺu޾}!=zAKN<[~^Jř_.8}w, }\9`H`F*Lcq蒓'"GF-"2L}2B}l@Q|ly=h?NWD|h t0%u1odӠ=L/˄8. .3=k\[vm۶oӦM###}Z_6ovvO G)a0Alz|?5*<zW9lJ$K^7JEpwZ+chӽe:^ &YsO<[߿eFVWGN}.RG?Hd>,Qyu5p `-Yeog s |neV5e:^cҐvr0*A{i.-W%^O_]sck=*ӣ63}ԭ<'e )`GN6,_<\$4]W'YX|<{?LtpIy @ڽGէ?uwRޞ;_xv͆ `=a}|$=| >b$@\%?͞mSȌH%[|N2L/KܱY_G/R4J֝ +Iڊ@.w7{cukjvӠ?L5=5/ܴfY៏Y?q;G /!N˒͛%g(x :>Wt dڇdz>;a8?Tv'Uρbn$Xkߍ͍s\f~p !Ιk+}0KY4VժyDH4'h)s;O-2VTDBW;5y fi !ѶMEW ;FbƱ`h,˜+ p-UǦI;>pL' Q>IDATx}xiWKllccmZ@1IBB7=7p!pI $` `cl{oޥ6sfv+ɶ $_?]I;}s{ _֗jEuu5TUìp8Qs |~~W^Ä _<'C. 7 #''9=Aqg<>fƤy Xޱ^N5^IFK.ׯ{ѣG&K4YRR2>fىDx b,VTE.j4a2`2$|W(tX %UQLFl2;l0;o%hz:FsV5kaE0,f"?۷/0.ƗJc sP&zXpPD rs{D$`ۅfY&z #Oo56!X1&Y#2ݙu #V`Ct41|,,5ģp9]l>йi/b-P1 ;.lCːui0_4 N#Zڐ4ycc%% 9XVF`jt8xG7iX<Abc? 9Hrs`%b0Ql6;?y[dOZkr4KSc` ]^/\gt-_Ӗi:%KExHJެI7j#tz! qtZ?X/lۄ3Xʶ! -`{(R/?m^YBYҥ+ 7<ٓMm DIJYH+FY ]`ޱ9cف|5)dX $$n7|Z!tr|pH#cPJS0'F&~oҙ`LY#283AT ;jPh{w T@"V.?*ۅׇDgP+ Trvˑ ;Pw1lPMX lj_>r" DP4#N\HFF6k[5btojX{lKD#ǁ=W󏵩 ʹr`}~y p?i\gggϟk׮|[3gΜAS2 *2~aW+ 8;"?,ѕʪEsYȚ>Vt pŒq㸟 weK%kDюL'O>}ܹ_wȑ} b2ic (?|WAVlF "tnmONr&Rf_xa7lD+1d#ظ+II[ثx46+\4e/{ä2=q'Ln$HK2"w㽵pzC| Xv?z.G~]F_!%dZ|$.*{Wf΋AFnrAAEݛa%tнaqsg!|ީ6 d`EyYas&i D#iG2U7w(I%=qg{7|maQYBTp n|J$(@SSƐAQs]G8GT`&B|~9lEg>d"!}&3}]~`4[I$G˗aRCqՓ~κly^OJl6"P>,Q!Un-H])D In4FgXr=?n!傝qE?~O}Pd|] ͏x{(x*D54؈C?}J>+i&ҾС'!ee ?['nm\hu֕K.]G &j~>Zeh bLxN-g9e%M<>VvIj[7Qp 6 4iMo8urZ8lF4JGZD2j?](Ϝ{pq酪[xNsGF#"G\׈A pP|v߾3}]طhA%I4ǷxD i!ZjW@dL-%=n"_V6j5KNޚawL/&#qD9LhQsd"eB63K Gf0RBxt4g[0[#AF?sq -e8N53~trK pbƠ<:҈Onh{gǯX]OVM k,0oECqGH nWy)dښSlp^ VHD [vg CҾx 汁Vh\F:մB "sk7;Nߥ{ 0(ω;BKޢsQ4axr8~-jM7: } D0}4 ;al[e J}]#5>mxLk<9`qÐ:ld k,E^ԀpS{N)p[aﭒD5?}/&sPH#$F=:hh8gjjnF6m/DbVf=|p{<: M`Ӿ=*IWD?r*Uk@`8PU\w WMp= ҳ́n%'h?#7F:E GhZK>Jj#o(\#IҠFKÁl0a|Ţ `Q#rgkyM=:fl\a3PE7/R]s-A߸!Zd^jnP \.T`[:.wVJA&<x?9' bd16Kk= fpE~8lCXl =h9;,n n`;V8B&2<1L4gv5XKq 8B^cXX 6y>gbvt [_EƽX&+n2 [ƍdXtg]rG:WߕeΕ"+1(_ Z<3rʔ#{ƌ,(>BJ QNb K?{x$ꆷ\u'Br v ~.:x%iTG1 $k}Fb>p>F~5֛t:D^7oQm0|~lW(yct@Dcm:P ye8;| MlGո_jjՃ!5VqNCd=ǻЙOQbW`;Uk Hw!6.&39 M+( s80P; \|>YE%cQ{4S'I]mp{Pz0u(@J5bfDVaFϝ 5G~av,*T&GQ2dO> 0䅟"o(ƢƏΑE# =ZAK ccg1k@2[:?YVtj؛ ktU˴]@s{G,br\MDT~5@%'v{`7cizm? {sz}}tp3yC9W,m-M;qh+aUcYW9H!o=yy~z-ck7X\1л9߽o,Dbj0X?BV'9,-ęJY% y !2E6wlZTL/IU1Rxnn/ gOBYa?bkv" n;$Lña6ƹȓKI.h>|3O3n}G#J G#e0|9 HP ƞo!lV۬ POrK24b2g;l8i&z İTMk倭8Qc\q#4QV7e^>F8yyAkTz1Y4&tdPW,upX猁])`̡b j3#H1CaT ل܂{ǜs [㈄X4jaS?s3T] ai]r UV$*$\nzY,挶l#Jr%"јxlCD`;pfSFSHJ;&4;|1m;X1z6 ;%L*FPd@BzԧD*m~}Yr#\TsBnL0vGב=Xwyt\knjx$g,ÞQWَ0mt6-LKİ̶DC1 9zGsNBV*946~@d Z1|-pZd ]9 HiIR*Ū. dm+>͵\!i݃SCXFm (6&N\LI6N%޻ N4!"zFWnhh^hDP ]z@$<GKGފ cD3~"_[J,dxvɝ_/ jfSt<DUE9tmF(s 9yP$dLa0Wt(ӇJp\IEvtD+ ]6 Nk[uifD,\wjG2VvkpNӮ%=+Nd I@Glx8=!3s; ":=[Xk("haI"L<ENg̣h!eLrVY烕p<4<Vt?s^"$$,f%NLG~QA"Imyڸ[ o(Ab"0iJ&K[;y9;&&!c O@4"xG|5'5J6}Pml@wc5b|M<0ߖX v_;ҍgMsb_n &d[Q4MF26~qt㇈CzO).5dx;6MSdMҿ62%@y40 3b=Lo o9low:q`nn$?e\1ohؾ]uZ>뙀<N~1ZlAǑwcTI!aGN`RA ߔ˿Ac`qf!{[(E埡fJNzhVMlxC^?^,98ll߾wsYt 0QYշ]2|rQ0rHWoOw ,:"mT1lX 1d0=CP8r F O`ZGǰ9a ą'v۬H¤;l(UO _U-G;yl]( IN$ !;¨w]0"f./nMRz<Ÿd"Ifjޅ5o#R/P$gͱ!' 4< [WΥT^qY(;YZj@w<v6:wN7 2 /EӍ#ro3!P3%$+z ?u#A9܈7ٕ*(-+LR TAS" #Ko {kv @5طvc;+ø |\Ɋg! 5j$; =@nshi'vccHY<.O>Љ3z ڷ1!kV. vBZ~2F(~VM1P‚qgvˊ{˖+׼:{ALxʺ-+0~'t~(a̹נh4Qx$(`k#*n $uZ`szPYh񙶬?tWZ!- Ci^_EZ-d[/ނJrW8sqּyhljkxҤ`w8D> Xϰ%ⅸ+1+$uL]?L>~h(Y]=7+wU˜.piҖWB"ZXD/dxD5iJ!1xP 0П~)rYVk )Q:8m)O $꿗&?%A۩ .GobEmro-$Qp"fWl܃deKjRq"E֭%ۢW)_؈疷ͪ;s5{'34 r^p5F5Q1H߅UU.Yd6&A=itJ'NAy8v|FD]hܻnCQ j6.:U{wnCdV&.lZmNO'l$KGsjŇZE ?p^w`c{H+e.* cs!A#κ;궭Bb 2F |7iA jm8L=Xk(++QNrOpOAm'E3RȈ0ob捿cqfv/uI4@A tABnXy(w:͙=PG#zCXWj|s5TY)p#[4U2vC9@LI_K4Æ=ڍ<0r1-M%BQ{0mt'9v͝rpU5 0qšҶ:^’glߡ\[^j֌ ̎E54+_ S S185e 0#GBy>Ռ1-L|-g\_)1#p%Q{ׯOWg~L)>KLISՎ*r Yi3sDzw i]z7*MWD:# */)ZMظqWƷMz6bWiw08ۂK`\#EAaqLA8i#طyt[G`|{H#-y> 0edkK𣵵wVQYn*x`ʕ9N< x1zPL{4{( =m1:*ө-w Mlg6P8dLR62WӤ3Bb0{.__~˗/3_E/^>f8 É #G͛d W>,tc^ F{͛w&."a@v}EJK91sL\0Nc?䈷;߅-+_=\NJQ2R'n˯%V2jP8y_,a 3 uxWdRH?\,\SNk\}J26f&5Xk7aVӮݍCر} mڴ8a-ʽF|D]W㪫ң%ڈ\k#E;ui}#Aɘ$5{a~Rd"ב0v/0i-I7_2Q3؇g}/"SpbpbHӽ$Eu7(R JuE7SŐ1l\K2::: W,܊[x<-cr5a jBmPN6^bo2 dj8UL_^ds3 I| j%={6na1.EI"C' 3~ fV<ChLܞEui;o%Hhicȷv%MzN]dx[mdzN$$Eo=_zYf`=\1r[DW=%V/*n&9Lb 1BԄ+0>{+@%Xxs(>W[JhB"%4sB#-j7r!(u: c)אrR5ItZusfzQ#-Cg_Y1? O=Ƞ% W\}V̘1Ch:W3+.?Sa.,+A6S`%?BwbϢ|"~oPR DHݦ.dY#PwBm:9v:?"%X Fr.Bx U䲖MIތa&!Z=>'dww19NwWܿ7On2 =+ǯnXx3:w@? =In+D"B71Sa "W._Kj6ϖI G. (uS牋sR=(a,qea xŗpwɲ%f/\P ',߀eNo=uO/E/?y C0I|m8 mz_gי\Py ޛ.EKh!ZMPSuM6{C~WQ~Ynh!}^c }!`鿳A2,mcC7Nccـ6'w TN4ZNXY> gqIJ#d1)};ip]vMn| ^naShd$+o,( fx={Zur!ܬuƴ77c/<=kloYCy%(G̙P{ȵ #ApIE")i&w2KՓg>{!~KQA9Cja:"O*8u>!:~P~EW3jҟ@.[$; )û6&׆eo!Ku=b$&Ağ)6mDX\L$UFcGB鹾_]{`rCjG v̐q-ϴ>pบ}"Q-(7zu#GQÊ1("+? a Wֳ߰FGIK$̞%Άzk2`1k 7^0k"nwX~*y"D% i}lxM$;bӤn&xS̉Xbmܤj 9,΅:@a 7܀H(_w%]G'׭y&iclڱ[w]}Pam ^AA9bt|1@ @8'lI:s e)Ҡ硄Ja!z<*!RzSj2;xSW`L9 cB8(o:^"{PLو5g'T4~BD7 шAJjIapD"l{avEVpP{k2B:}" RvMlq,bo<6ׅmbbgCa_C) g>J+E{ìu*8\92% j/`'QTgnh{\6Izq(9 F!+HrLmboJ?-7w|JAo G*!@?OUӋw}f:#ʧ ?yl +&mq_Z_'%Vpq^n!@"}^rv}L\`G%@,y#OGN`dJG ? q^vm6 = c~@ ck2w=-+V9Qx?/C6 ֵ*Z7>*n`)mG A+q#[SU.^Evr؆ B{Bv:}CQ( b>2}um 45Dl{88hR GO0v”MͲ"wIk٤6 3B|gΤGv}Ov $2ncӈOR8x9 v'/[Ǥt8L~̾NUN_PAi2=yMd-oeW/nv=}KNxslNـG`1 d1NͲkVYU~2y˩ &;H^[Rԩz4<썠,; $u=)û o[ga,L &O>d3|])xj>va{"k!%ͪ1Mƒ 48&x 3?|j ́NT?H>'6ǜ[ t-cӉ‘8 RHܯӫ@maS1 og;AY.OpEӞi3ȾBft06} VJ?DӃ>|&ԇ??~E9',>p;,i&5x% +JBlXhuaa$Ҟ}ў\܃:)jf6mwᒇTȻMgM iL5\39PRqY8Jy+x;D #N1 o"ß!8sP>u"_;^M6`-pQC߱+cN1atbC>UQxwY :D'IkaDO߉5vǟ+CUkmUdqݖ1rZp$P (ػ$ۼw%9rU}77M'5R7y. tCI!)}4@̆#Dv$")ݳ8]`5<=UxnCoΛ.Rp$dIZmpW j7eftً݂!|yPNq+yCvڷE#d޼Ry)g""If/;^{;lS8OϻȎZכÙ]DJk% &hhZ%|ݔ;IֽmI=F^'1A~gC|b^I:K~ѽZ{jsyǬH 8Q$CΌo].༺zۨYjg~ ż`t=9q'}DQRU2)zgTq;aCɿV !xy8UFPMi}$ H{P d@=v[\ז]~BmnʵuYX|(}Jo#$ 3so{]}Wu;.Uɓ*=p[13xsdO{n:/n@IbHĎ 0W8?X4Ju:RY$' &6$= ~5ީiZm7l7.1\yED8;[rEƓK\ /* USM2&li0쌋1"i<*1H#N);妗 Qo J6v"\Ehl؏m;a5GQ\jrp>g\ྼc=Ogۀ.'))\rœ&ma&ROv Jg LJ ;|,y){(6;Y999dg3d8J^NxFпWpJɈ_sMLxO|3ьƁ-ZK/I]_z5a߸maTPćH5$C:}^iȗגcE ufxEn0 ~tCNHF #",Sy͂IcghժUN]k֬YKA%_%P_R'ک7O{<ɠPHDRYQ82;<cF<=5]JV-BRq ~&#x Lv~W貧3Q{*'xݨi) G* }zT}AX^@g^m8?7?9eyI_rڦ_ڲ]Y qCk:wy)&[>ɽ~}ѓ{ WoaӅ or ۻõ-d$gMDt^;tu##OX>VߵWz >Qω!C:|&HNb, 8 P¿0OV2o2x?~>K{'I@ɿ0in$ŎTG] YJIENDB`mapper-0.8.1.1/images/mapper-icon/Mapper-large.psd000077500000000000000000131400531325266516600216740ustar00rootroot000000000000008BPS}8BIMGZ%GZ%GZ%GZ%GZ%GZ%GZ%GZ%G8BIM%~:'Wh8BIM$: Adobe Photoshop CS6 (Windows) 2012-09-17T17:26:25+01:00 2016-05-30T18:55:30+02:00 2016-05-30T18:55:30+02:00 application/vnd.adobe.photoshop 3 sRGB IEC61966-2.1 xmp.did:95DF2A00DA00E211A07B995EF2A73201 xmp.iid:1da69734-97ad-45f1-a083-df46043c7da0 xmp.did:95DF2A00DA00E211A07B995EF2A73201 xmp.did:95DF2A00DA00E211A07B995EF2A73201 created xmp.iid:95DF2A00DA00E211A07B995EF2A73201 2012-09-17T17:26:25+01:00 Adobe Photoshop CS6 (Windows) saved xmp.iid:A07CBA907103E211B602E2A7DEB16909 2012-09-20T23:21:46+01:00 Adobe Photoshop CS6 (Windows) / saved xmp.iid:1da69734-97ad-45f1-a083-df46043c7da0 2016-05-30T18:55:30+02:00 Adobe Photoshop CC 2014 (Macintosh) / 8BIM: printOutputPstSboolInteenumInteClrmprintSixteenBitbool printerNameTEXTprintProofSetupObjc Proof Setup proofSetupBltnenum builtinProof proofCMYK8BIM;-printOutputOptionsCptnboolClbrboolRgsMboolCrnCboolCntCboolLblsboolNgtvboolEmlDboolIntrboolBckgObjcRGBCRd doub@oGrn doub@oBl doub@oBrdTUntF#RltBld UntF#RltRsltUntF#Pxl@R vectorDataboolPgPsenumPgPsPgPCLeftUntF#RltTop UntF#RltScl UntF#Prc@YcropWhenPrintingboolcropRectBottomlong cropRectLeftlong cropRectRightlong cropRectToplong8BIMHH8BIM&?8BIM Transparency8BIM Transparency8BIM5d8BIM8BIM x8BIM8BIM 8BIM' 8BIMH/fflff/ff2Z5-8BIMp8BIM8BIM8BIM08BIM-8BIM@@8BIM8BIMnullbaseNameTEXTUserboundsObjcRct1Top longLeftlongBtomlongRghtlongslicesVlLsObjcslicesliceIDlonggroupIDlongoriginenum ESliceOrigin autoGeneratedTypeenum ESliceTypeImg boundsObjcRct1Top longLeftlongBtomlongRghtlongurlTEXTnullTEXTMsgeTEXTaltTagTEXTcellTextIsHTMLboolcellTextTEXT horzAlignenumESliceHorzAligndefault vertAlignenumESliceVertAligndefault bgColorTypeenumESliceBGColorTypeNone topOutsetlong leftOutsetlong bottomOutsetlong rightOutsetlong8BIM( ?8BIM H HLinomntrRGB XYZ  1acspMSFTIEC sRGB-HP cprtP3desclwtptbkptrXYZgXYZ,bXYZ@dmndTpdmddvuedLview$lumimeas $tech0 rTRC< gTRC< bTRC< textCopyright (c) 1998 Hewlett-Packard CompanydescsRGB IEC61966-2.1sRGB IEC61966-2.1XYZ QXYZ XYZ o8XYZ bXYZ $descIEC http://www.iec.chIEC http://www.iec.chdesc.IEC 61966-2.1 Default RGB colour space - sRGB.IEC 61966-2.1 Default RGB colour space - sRGBdesc,Reference Viewing Condition in IEC61966-2.1,Reference Viewing Condition in IEC61966-2.1view_. \XYZ L VPWmeassig CRT curv #(-27;@EJOTY^chmrw| %+28>ELRY`gnu| &/8AKT]gqz !-8COZfr~ -;HUcq~ +:IXgw'7HYj{+=Oat 2FZn  % : O d y  ' = T j " 9 Q i  * C \ u & @ Z t .Id %A^z &Ca~1Om&Ed#Cc'Ij4Vx&IlAe@e Ek*Qw;c*R{Gp@j>i  A l !!H!u!!!"'"U"""# #8#f###$$M$|$$% %8%h%%%&'&W&&&''I'z''( (?(q(())8)k))**5*h**++6+i++,,9,n,,- -A-v--..L.../$/Z///050l0011J1112*2c223 3F3334+4e4455M555676r667$7`7788P8899B999:6:t::;-;k;;<' >`>>?!?a??@#@d@@A)AjAAB0BrBBC:C}CDDGDDEEUEEF"FgFFG5G{GHHKHHIIcIIJ7J}JK KSKKL*LrLMMJMMN%NnNOOIOOP'PqPQQPQQR1R|RSS_SSTBTTU(UuUVV\VVWDWWX/X}XYYiYZZVZZ[E[[\5\\]']x]^^l^__a_``W``aOaabIbbcCccd@dde=eef=ffg=ggh?hhiCiijHjjkOkklWlmm`mnnknooxop+ppq:qqrKrss]sttptu(uuv>vvwVwxxnxy*yyzFz{{c{|!||}A}~~b~#G k͂0WGrׇ;iΉ3dʋ0cʍ1fΏ6n֑?zM _ɖ4 uL$h՛BdҞ@iءG&vVǥ8nRĩ7u\ЭD-u`ֲK³8%yhYѹJº;.! zpg_XQKFAǿ=ȼ:ɹ8ʷ6˶5̵5͵6ζ7ϸ9к<Ѿ?DINU\dlvۀ܊ݖޢ)߯6DScs 2F[p(@Xr4Pm8Ww)Km8BIM8BIM $m,$Q Adobe_CMAdobed            " ?   3!1AQa"q2B#$Rb34rC%Scs5&DTdE£t6UeuF'Vfv7GWgw5!1AQaq"2B#R3$brCScs4%&5DTdEU6teuFVfv'7GWgw ?^Ic>)I j}^ָe}-]Jr2:mz_У;~yo{-d/&YB޿ךéeO#nbo1N?p~Ksڼۧ}uͥ2yfl9YVUUU[[;^ۦs{-A38nwAoSi+mmL.qʰ\ziWi{?H?_m"i c[oi" ?@ķ eʉBmɂ\#z~쿻$c~g7)0.iWWmļUI6ݺzʩzߧRk[9Wx~}C{Ai9Rm0ծkg,c߬]i;܁{GC =w,bo*6҆9Nqr9=v6]Ay_,cNٹ+ c=n[[~HJ|s`rNP1IЋֱ͓Σ0U#R Z/ז /xmzN3ˍ fͥc{zk}iyuYy_Sb1k6Nd5b);=_՞n4폴7sc]ft] Q0Du=.?@w/ZTgIẃsu;[]HL<ĹC! 5)[GӲ5>;^ãlfwk;=_oS~7Pws:7 87?-? /?J[?uoۘz㰲,[Y;뾧N{:XǛϹBR'f?龏; ?._mx$zn,y9r=Rg8s\LqI9w.ݞxLVr 5r#h]?]55pЍJ[|0C+i>)Gk~;"]mox1iװYɺ{}ge/hC;Z}6;i,slw^Mlҵj'5kXu}s*mL}xC~'п~Dr"湫-ŷ#\ys K.y1w kjV p>:Q';6حyl3~B1+G)GzÕƷ?dY+c1{+}-Uv;FܳcWBޗXul_SJ?_c}céu;~&4׎7f]"б06ZaKóӃ>[쪿&J[_,9@HevQ9'6UWm HcH?W_[mq{M ~ߥr1xW$O-Qz܋ iLN2۷kDZ5pkoJϵuLC!ΰWEp[J:n7ٱY[uN=-f3؞ܬ[ku~9zm/u74Yk!;,iڽ"]p]\7֯d&ephu3jj?OMbq%TџKղmk/cn;E׼ENgMԻ7VO`I.i`r]e+cƭ\ Za#PG湮) :Z> 'js dgqgZW`}7YsQS=* Z5G:q5`ȼ WA?}e?׵#3!ص:k`g~kY5t^sٙc[.#V V'[Uh_`9qG82(Z]u6b]Xg/,);na"󚻛{7Od:՛s[_~ռhcH]-;̏2g$Ĭ_B0їy_VaMo{C_*?Xbb:ܲz8.pV}׿6^R=8jUN^q.?:S~ ;K2ޥ23nd?p h RV]=œ7K1>Ud-ktάj(vr%e_"뾝k:wN RN+ u?HwGIJ?¸N|l˜Rj/h%-pЂ217MpKyufXR }V>3Gį1Cc'j}ob}L q65uz]j_Vnp1*}VZCn675{rKعuzy56ZVZKVL~ SZu;A)x>y~cO`[:[]fUGfwVXƷ赬a pW s;Z\MS[lYzF;7?5[o7Xif[q_[ ?$ <~ivgu>KwW[->˲3vgU2[֝W gLa(K2  ieAGCINV@3Ey'@`>juYZu5odr2e,pkr废ndaˬkq[Cs#WgNjqݜ9n?WwQ[WCC]s8?eK\3nk?M3O=Nڜ*mߪCdk|~l1i 쫮ڪck5` kZ>XZ֦p&0ec 6+ߟcYY.GͩƻYe/n>*ߤ~Ys_fO]HFc$A-WJfQĦ˭}TK}0huOSۃQ#+wέb}wtޟmYYul􎮚=[mfHٓuvgU_3q9%Bc۔?8xD1zp^`Ѡ X+ol澬}g˳uZYՇ9 ٻdz[Inn=&_[ 7XYak!=Z=ʨu糞P8'=?.ZJ.hnIߢ4s~B±cbCݮuMC{w"V2%t-yo6~׮33_FS:!ϧk[+#e~FJpi*ƿwvU_gu{#Pu_OPe#ጲ9}nnOP7}PguƘ[UylagRS2vԹS f][OՏ*2AVY~Z;gYTw8n m_*'R}mV}k@! 8;7u_\5IJsA#~X;a3J:?G)DZ7̜lJ-ʵ[};@ѫ?^oȻ} .{F}2?}a8}ېl{@sFnD/)in?m%)4 {Qq@y7ޱ.X2p'b[j:gF)Ck{>׹6#x$O45ݎ\$IkߏFM.&]K}v49ṮR"I)6#`}&5l|Ks2[ַcų3){ֵ.:\]M87'Vc7#9w>#r?,C.i/kFg],f3 {+ Eto.+X?t0]·\21>:mU5sAs}ۮ{g]CTckwٝ.e{5O^8B\bSnd9WZV'v - BxG_=*{m`cZjA/-v+Of1u1E yxԱrdeXXƴ۬c'9Y_E{GX狞+3;Xz_=h+(j*}әs7SS垮g&KEP=]jӿ_^K p{%g@hI6z(ToR4G4uSNұ]rϰĘ.[~moU5/>[7; ߪȹc 1_^l _HĥwWC~8V:{gs1]oRv muUZiÐ3'VfE@'[6I.Ik}d\Ӽw?"Cw^gՙW}o6^=O_J_A镖+,cm{}\ֿ-l88D?< 1P~Mo:l ,%9w:o zX1Q6Do۟N]m5чF 9X{}X}2~}^FvH5Q^T-7cX}`|һn1~KEM~2us/AFn+䱶"Z-;]j33.10:nX8U8Urmlv9w?#u_7_n%P2_qc/kX饯oں\u|u|9Y PCUt7eihU^ؕNfSϽVϤ+`79<6}|ڙLRί~UZߥlg=W[+*Xd}_6?["˧k; Y9Q-d&tlzYwNswvRzUlXo&uOj?Uf޷֜zTF_'F// Ngg="㨙QJOk}3J:~gaJ]g{ֵs{Zֵs_ Xiu{?Ko~+?^~d,7.2-yt o[?s1?տQN6uΞZi晴Ch>a{m78E{1ͩ76:GD=/X%Z@u׺?NRf._?gVW~j떝$zIvv:ɒg=j.o4z;7t۷eCd>}Tv+T꠿=侺w˵u8ϩ XGFNInF|{\?~u-t)Dn>Xy#x?<A]2RR,Bsˈcyq{*S~!{ReCfVcolp柣cg_yG9NG "Q1}`9;)쯦51 w{?5u\afNQ޳j}zKk"?H[ٶcr8g38koiC..5u/fT=[?ucac~\ UN_{K[tCho?K>O֮K'lJ_<~#u/FӺYmߔ8ɴ o{(ϿQV[F52M͎'_kto'2e#f]\o? 7U4G}rގE5ߖE }_M񉳐LEЉF䵺Z08]62jk=&e]WسFq1,_~G=7 ۘ}?cq?D1bH# 3Î Ijy(mdrOubaaR(ĥ vVO:>T)i2LV//[@9] h?_~?FRM0Eff`'2Kt/y?{YY7}mUϢL֐07U.S 6Ǥ0[[.|.AVy ?;?1+ɓoÿX?oqyc3#NC{G>U~mu,9ϹQH = V,8 pKnxC}8ٍmXÖI7_bF-ޛYԯrm#Y\[+W岦/ph ~wL=?"=sdBޫv&0cxj#sTe9=޻'Iii/mW}rޞUpxԛ.7eV,1¶Ko/.%gT]Lav-SΧ;+%U{[X[,}K kQY1o!6 &D~Q*" @Ck* ?)}tu.e؏6MO{+oYokCMDbz$$:wj랪o_ەYf74~U_LQ LMqFGK^z>ndu$')J@Fx)c8\M]k3t۳X 8tWgXX}cav3cW+Q?-z]?Km)U]C(ZUE\yH;cOI ):R=_lO*%?0Mk~N}Bv;etH#YwR2΀lܸ}Sj~Cl1X?6 :?:!9OPd=Z L<+u;N?9Oo~o5 cJ8Upb -FqK"BV]pD}ߡ{:$d|\KINW`-]da]Tz8BIMluni </Layer group>8BIMlnsrlset8BIMlyid8BIMlsct8BIMlspf8BIMlclr8BIMshmdH8BIMcust4metadata layerTimedoubA 8BIMfxrpg *d)rL  8BIMnorm8(Outer8BIMSoCopnullClr ObjcRGBCRd doub@oGrn doub@oBl doub@o8BIMlfx2|nullScl UntF#Prc@YmasterFXSwitchboolDrShObjcDrSh enabboolMd enumBlnMMltpClr ObjcRGBCRd doubGrn doubBl doubOpctUntF#Prc@*uglgboollaglUntF#Ang@^DstnUntF#Pxl@CkmtUntF#PxlblurUntF#Pxl@HNoseUntF#PrcAntAboolTrnSObjcShpCNm TEXTLinearCrv VlLsObjcCrPtHrzndoubVrtcdoubObjcCrPtHrzndoub@oVrtcdoub@o layerConcealsboolFrFXObjcFrFXenabboolStylenumFStlInsFPntTenumFrFlSClrMd enumBlnMNrmlOpctUntF#Prc@YSz UntF#Pxl?Clr ObjcRGBCRd doub@d Grn doub@d Bl doub@d 8BIMlrFX8BIMcmnS8BIMdsdw31x8BIMmul !8BIMisdw3x8BIMmul 8BIMoglw*8BIMscrnY8BIMiglw+8BIMscrnY8BIMbevlNx8BIMscrn8BIMmul 8BIMsofi"8BIMnorm8BIMvmsk?$?$?$"?$"?$"?$"""8BIMluniOuter8BIMlyid 8BIMclbl8BIMinfx8BIMknko8BIMlspf8BIMlclr8BIMshmdH8BIMcust4metadata layerTimedoubA 8BIMsn2P8BIMfxrp@+|9+@ ?;8BIMlyvr8BIMnorm8 (Inner8BIMSoCopnullClr ObjcRGBCRd doub@oGrn doub@npBl doub@na@8BIMlfx2 nullScl UntF#Prc@YmasterFXSwitchboolGrFlObjcGrFl enabboolMd enumBlnMMltpOpctUntF#Prc@GradObjc GradientGrdnNm TEXTCustomGrdFenumGrdFCstSIntrdoub@ClrsVlLsObjcClrtClr ObjcRGBCRd doub@m Grn doub@m Bl doub@m TypeenumClryUsrSLctnlong Mdpnlong2ObjcClrtClr ObjcRGBCRd doub@mGrn doub@mBl doub@mTypeenumClryUsrSLctnlong3Mdpnlong2ObjcClrtClr ObjcRGBCRd doub@n`Grn doub@n`Bl doub@n`TypeenumClryUsrSLctnlongrMdpnlong2ObjcClrtClr ObjcRGBCRd doub@mGrn doub@mBl doub@mTypeenumClryUsrSLctnlong Mdpnlong2ObjcClrtClr ObjcRGBCRd doub@oGrn doub@oBl doub@oTypeenumClryUsrSLctnlongMdpnlong2TrnsVlLsObjcTrnSOpctUntF#Prc@YLctnlongMdpnlong2ObjcTrnSOpctUntF#Prc@YLctnlongMdpnlong2AnglUntF#Ang@ZTypeenumGrdTLnr RvrsboolDthrboolAlgnboolScl UntF#Prc@bOfstObjcPnt HrznUntF#PrcVrtcUntF#PrcChFXObjcChFX enabboolMd enumBlnMMltpClr ObjcRGBCRd doubGrn doubBl doubAntAboolInvrboolOpctUntF#Prc@laglUntF#AngaDstnUntF#Pxl@^@blurUntF#Pxl@c@MpgSObjcShpCNm TEXT GaussianCrv VlLs ObjcCrPtHrzndoubVrtcdoubObjcCrPtHrzndoub@@Vrtcdoub@ObjcCrPtHrzndoub@PVrtcdoub@CObjcCrPtHrzndoub@XVrtcdoub@Y@ObjcCrPtHrzndoub@`Vrtcdoub@dObjcCrPtHrzndoub@cVrtcdoub@j ObjcCrPtHrzndoub@gVrtcdoub@m`ObjcCrPtHrzndoub@kVrtcdoub@oObjcCrPtHrzndoub@oVrtcdoub@o8BIMlrFX8BIMcmnS8BIMdsdw3x8BIMmul Y8BIMisdw3x8BIMmul 8BIMoglw*8BIMscrnY8BIMiglw+8BIMscrnY8BIMbevlNx8BIMscrn8BIMmul 8BIMsofi"8BIMnorm8BIMvmsk S S S SSS8BIMluniInner8BIMlyid 8BIMclbl8BIMinfx8BIMknko8BIMlspf8BIMlclr8BIMshmdH8BIMcust4metadata layerTimedoubA <8BIMsn2P8BIMfxrp@+|9+@ ?;8BIMlyvrfi$b8BIMnorm(Vector Smart Object8BIMluni,Vector Smart Object8BIMlyidz8BIMclbl8BIMinfx8BIMknko8BIMlspf8BIMlclr8BIMshmdH8BIMcust4metadata layerTimedoubA 8BIMPlLdplcL$bddcfeac-6715-1179-bc96-d795218d25c4+miwH~]-@?Het@#̠@=#+ DŽ@Ewarp warpStyleenum warpStylewarpNone warpValuedoubwarpPerspectivedoubwarpPerspectiveOtherdoub warpRotateenumOrntHrznboundsObjcRctnTop UntF#PxlLeftUntF#PxlBtomUntF#PxlRghtUntF#PxluOrderlongvOrderlong8BIMSoLdsoLDnullIdntTEXT%bddcfeac-6715-1179-bc96-d795218d25c4placedTEXT%bddcc307-6715-1179-bc96-d795218d25c4PgNmlong totalPageslong frameStepObjcnull numeratorlong denominatorlongXdurationObjcnull numeratorlong denominatorlongX frameCountlongAnntlongTypelongTrnfVlLsdoub+miwdoubH~]-doub@?Hdoubetdoub@#̠doub@=#+doub DŽdoub@EnonAffineTransformVlLsdoub+miwdoubH~]-doub@?Hdoubetdoub@#̠doub@=#+doub DŽdoub@EwarpObjcwarp warpStyleenum warpStylewarpNone warpValuedoubwarpPerspectivedoubwarpPerspectiveOtherdoub warpRotateenumOrntHrznboundsObjcRctnTop UntF#PxlLeftUntF#PxlBtomUntF#PxlRghtUntF#PxluOrderlongvOrderlongSz ObjcPnt Wdthdoub@JHghtdoub@E?RsltUntF#Rsl@Rcomplong8BIMfxrpxUU-T@G***8BIMnorm((Text8BIMlfx2 nullScl UntF#Prc@YmasterFXSwitchboolDrShObjcDrSh enabboolMd enumBlnMMltpClr ObjcRGBCRd doubGrn doubBl doubOpctUntF#Prc@BuglgboollaglUntF#Ang@^DstnUntF#PxlCkmtUntF#PxlblurUntF#Pxl@6NoseUntF#PrcAntAboolTrnSObjcShpCNm TEXTLinearCrv VlLsObjcCrPtHrzndoubVrtcdoubObjcCrPtHrzndoub@oVrtcdoub@o layerConcealsboolOrGlObjcOrGl enabboolMd enumBlnMMltpClr ObjcRGBCRd doubGrn doubBl doubOpctUntF#Prc@2GlwTenumBETESfBLCkmtUntF#PxlblurUntF#Pxl@2NoseUntF#PrcShdNUntF#PrcAntAboolTrnSObjcShpCNm TEXTLinearCrv VlLsObjcCrPtHrzndoubVrtcdoubObjcCrPtHrzndoub@oVrtcdoub@oInprUntF#Prc@IGrFlObjcGrFl enabboolMd enumBlnMOvrlOpctUntF#Prc@YGradObjc GradientGrdnNm TEXTCustomGrdFenumGrdFCstSIntrdoub@ClrsVlLsObjcClrtClr ObjcRGBCRd doub@n@Grn doub@n@Bl doub@n@TypeenumClryUsrSLctnlongMdpnlong2ObjcClrtClr ObjcRGBCRd doub@f Grn doub@f Bl doub@f TypeenumClryUsrSLctnlongMdpnlong2ObjcClrtClr ObjcRGBCRd doub@k Grn doub@k Bl doub@k TypeenumClryUsrSLctnlongRMdpnlong2ObjcClrtClr ObjcRGBCRd doub@m`Grn doub@m`Bl doub@m`TypeenumClryUsrSLctnlongMdpnlong2TrnsVlLsObjcTrnSOpctUntF#Prc@YLctnlongMdpnlong2ObjcTrnSOpctUntF#Prc@YLctnlongMdpnlong2AnglUntF#Ang@VTypeenumGrdTLnr RvrsboolDthrboolAlgnboolScl UntF#Prc@YOfstObjcPnt HrznUntF#PrcVrtcUntF#PrcFrFXObjcFrFXenabboolStylenumFStlOutFPntTenumFrFlSClrMd enumBlnMNrmlOpctUntF#Prc@YSz UntF#Pxl@Clr ObjcRGBCRd doub@oGrn doub@oBl doub@o8BIMlrFX8BIMcmnS8BIMdsdw3x8BIMmul ^8BIMisdw3x8BIMmul 8BIMoglw*8BIMmul .8BIMiglw+8BIMscrnY8BIMbevlNx8BIMscrn8BIMmul 8BIMsofi"8BIMnorm8BIMluni Text8BIMlyid8BIMclbl8BIMinfx8BIMknko8BIMlspf8BIMlclr8BIMshmdH8BIMcust4metadata layerTimedoubA "8BIMPlLdplcL$bddcfead-6715-1179-bc96-d795218d25c4@5@=@{@=@{@d @5@d warp warpStyleenum warpStylewarpNone warpValuedoubwarpPerspectivedoubwarpPerspectiveOtherdoub warpRotateenumOrntHrznboundsObjcRctnTop UntF#PxlLeftUntF#PxlBtomUntF#PxlRghtUntF#PxluOrderlongvOrderlong8BIMSoLdsoLDnullIdntTEXT%bddcfead-6715-1179-bc96-d795218d25c4placedTEXT%bddcd279-6715-1179-bc96-d795218d25c4PgNmlong totalPageslong frameStepObjcnull numeratorlong denominatorlongXdurationObjcnull numeratorlong denominatorlongX frameCountlongAnntlongTypelongTrnfVlLsdoub@5doub@=doub@{doub@=doub@{doub@d doub@5doub@d nonAffineTransformVlLsdoub@5doub@=doub@{doub@=doub@{doub@d doub@5doub@d warpObjcwarp warpStyleenum warpStylewarpNone warpValuedoubwarpPerspectivedoubwarpPerspectiveOtherdoub warpRotateenumOrntHrznboundsObjcRctnTop UntF#PxlLeftUntF#PxlBtomUntF#PxlRghtUntF#PxluOrderlongvOrderlongSz ObjcPnt Wdthdoub@0CHghtdoub@RsltUntF#Rsl@Rcomplong8BIMfxrp8BIMnorm( Color Fill 18BIMSoCopnullClr ObjcRGBCRd doub@[Grn doub@+Bl doub@Cl8BIMluni Color Fill 18BIMlnsrcont8BIMlyid8BIMclbl8BIMinfx8BIMknko8BIMlspf8BIMlclr8BIMshmdH8BIMcust4metadata layerTimedoubA @8BIMfxrpt8BIMnorm4( Map + text8BIMluni Map + text8BIMlyid8BIMlsct8BIMpass8BIMlspf8BIMlclr8BIMshmdH8BIMcust4metadata layerTimedoubA Y8BIMfxrpg *d)rL8BIMnorm0(8BIMluni </Layer group>8BIMlnsrlset8BIMlyido8BIMlsct8BIMlspf8BIMlclr8BIMshmdH8BIMcust4metadata layerTimedoubA r8BIMfxrp@9Eb.I<8BIMnorm0(8BIMluni </Layer group>8BIMlnsrlset8BIMlyidp8BIMlsct8BIMlspf8BIMlclr8BIMshmdH8BIMcust4metadata layerTimedoubA 8BIMfxrpSoVS,)Raaa8BIMnorm<(Layer 28BIMluniLayer 28BIMlnsrlayr8BIMlyidq8BIMclbl8BIMinfx8BIMknko8BIMlspf8BIMlclr8BIMshmdH8BIMcust4metadata layerTimedoubA 8BIMfxrp@ubpXj}8888BIMnorm( ( Layer 3 copy8BIMluni Layer 3 copy8BIMlnsrlayr8BIMlyidr8BIMclbl8BIMinfx8BIMknko8BIMlspf8BIMlclr8BIMshmdH8BIMcust4metadata layerTimedoubA )8BIMPlLdplcL$bddcfeae-6715-1179-bc96-d795218d25c4@h¼@h)n@/OZ@eaH@&@~@jxfg@ڵwarp warpStyleenum warpStylewarpNone warpValuedoubwarpPerspectivedoubwarpPerspectiveOtherdoub warpRotateenumOrntHrznboundsObjcRctnTop UntF#PxlLeftUntF#PxlBtomUntF#Pxl@~RghtUntF#Pxl@uOrderlongvOrderlong8BIMSoLd8soLDnullIdntTEXT%bddcfeae-6715-1179-bc96-d795218d25c4placedTEXT%bddcd27e-6715-1179-bc96-d795218d25c4PgNmlong totalPageslong frameStepObjcnull numeratorlong denominatorlongXdurationObjcnull numeratorlong denominatorlongX frameCountlongAnntlongTypelongTrnfVlLsdoub@h¼doub@h)ndoub@/OZdoub@eaHdoub@&doub@~doub@jxfgdoub@ڵnonAffineTransformVlLsdoub@h¼doub@h)ndoub@/OZdoub@eaHdoub@&doub@~doub@jxfgdoub@ڵwarpObjcwarp warpStyleenum warpStylewarpNone warpValuedoubwarpPerspectivedoubwarpPerspectiveOtherdoub warpRotateenumOrntHrznboundsObjcRctnTop UntF#PxlLeftUntF#PxlBtomUntF#Pxl@~RghtUntF#Pxl@uOrderlongvOrderlongSz ObjcPnt Wdthdoub@Hghtdoub@~RsltUntF#Rsl@RfilterFXObjc filterFXStyleenabboolvalidAtPositionboolfilterMaskEnableboolfilterMaskLinkedboolfilterMaskExtendWithWhitebool filterFXListVlLsObjcfilterFXNm TEXTGaussian Blur... blendOptionsObjc blendOptionsOpctUntF#Prc@YMd enumBlnMNrmlenabbool hasoptionsboolFrgCObjcRGBCRd doub@oGrn doub@oBl doub@oBckCObjcRGBCRd doubGrn doubBl doubFltrObjcGaussian BlurGsnBRds UntF#Pxl@DfilterIDlongGsnBcomplong8BIMfxrp@h¼@h)nd`8BIMnorm( (Layer 3 copy 28BIMluni Layer 3 copy 28BIMlnsrlayr8BIMlyids8BIMclbl8BIMinfx8BIMknko8BIMlspf8BIMlclr8BIMshmdH8BIMcust4metadata layerTimedoubA 8BIMPlLdplcL$bddcfeae-6715-1179-bc96-d795218d25c4@h¼@h)n@/OZ@eaH@&@~@jxfg@ڵwarp warpStyleenum warpStylewarpNone warpValuedoubwarpPerspectivedoubwarpPerspectiveOtherdoub warpRotateenumOrntHrznboundsObjcRctnTop UntF#PxlLeftUntF#PxlBtomUntF#Pxl@~RghtUntF#Pxl@uOrderlongvOrderlong8BIMSoLd8soLDnullIdntTEXT%bddcfeae-6715-1179-bc96-d795218d25c4placedTEXT%bddcfea6-6715-1179-bc96-d795218d25c4PgNmlong totalPageslong frameStepObjcnull numeratorlong denominatorlongXdurationObjcnull numeratorlong denominatorlongX frameCountlongAnntlongTypelongTrnfVlLsdoub@h¼doub@h)ndoub@/OZdoub@eaHdoub@&doub@~doub@jxfgdoub@ڵnonAffineTransformVlLsdoub@h¼doub@h)ndoub@/OZdoub@eaHdoub@&doub@~doub@jxfgdoub@ڵwarpObjcwarp warpStyleenum warpStylewarpNone warpValuedoubwarpPerspectivedoubwarpPerspectiveOtherdoub warpRotateenumOrntHrznboundsObjcRctnTop UntF#PxlLeftUntF#PxlBtomUntF#Pxl@~RghtUntF#Pxl@uOrderlongvOrderlongSz ObjcPnt Wdthdoub@Hghtdoub@~RsltUntF#Rsl@RfilterFXObjc filterFXStyleenabboolvalidAtPositionboolfilterMaskEnableboolfilterMaskLinkedboolfilterMaskExtendWithWhitebool filterFXListVlLsObjcfilterFXNm TEXTGaussian Blur... blendOptionsObjc blendOptionsOpctUntF#Prc@YMd enumBlnMNrmlenabbool hasoptionsboolFrgCObjcRGBCRd doub@oGrn doub@oBl doub@oBckCObjcRGBCRd doubGrn doubBl doubFltrObjcGaussian BlurGsnBRds UntF#Pxl@BfilterIDlongGsnBcomplong8BIMfxrp@h¼@h)n8BIMnorm8e3(Shadow8BIMluniShadow8BIMlyidt8BIMlsct8BIMpass8BIMlspf8BIMlclr8BIMshmdH8BIMcust4metadata layerTimedoubA 8BIMfxrpSoVS8BIMlyvrP4 (>IF@8BIMnorm( (Pencil8BIMlfx2lnullScl UntF#Prc@YmasterFXSwitchboolOrGlObjcOrGl enabboolMd enumBlnMNrmlClr ObjcRGBCRd doubGrn doubBl doubOpctUntF#Prc@*GlwTenumBETESfBLCkmtUntF#PxlblurUntF#Pxl@ENoseUntF#PrcShdNUntF#PrcAntAboolTrnSObjcShpCNm TEXTLinearCrv VlLsObjcCrPtHrzndoubVrtcdoubObjcCrPtHrzndoub@oVrtcdoub@oInprUntF#Prc@I8BIMlrFX8BIMcmnS8BIMdsdw3x8BIMmul Y8BIMisdw3x8BIMmul 8BIMoglw**8BIMnorm!8BIMiglw+8BIMscrnY8BIMbevlNx8BIMscrn8BIMmul 8BIMsofi"8BIMnorm8BIMluniPencil8BIMlyidu8BIMclbl8BIMinfx8BIMknko8BIMlspf8BIMlclr8BIMshmdH8BIMcust4metadata layerTimedoubA '8BIMPlLdplcL$bddcfeaf-6715-1179-bc96-d795218d25c4@i !q@~R\m@}kkar@INV@d2QJ@QWf4X@l!@ warp warpStyleenum warpStylewarpNone warpValuedoubwarpPerspectivedoubwarpPerspectiveOtherdoub warpRotateenumOrntHrznboundsObjcRctnTop UntF#PxlLeftUntF#PxlBtomUntF#PxlRghtUntF#PxluOrderlongvOrderlong8BIMSoLdsoLDnullIdntTEXT%bddcfeaf-6715-1179-bc96-d795218d25c4placedTEXT%bddcfeab-6715-1179-bc96-d795218d25c4PgNmlong totalPageslong frameStepObjcnull numeratorlong denominatorlongXdurationObjcnull numeratorlong denominatorlongX frameCountlongAnntlongTypelongTrnfVlLsdoub@i !qdoub@~R\mdoub@}kkardoub@INVdoub@d2QJdoub@QWf4Xdoub@l!doub@ nonAffineTransformVlLsdoub@i !qdoub@~R\mdoub@}kkardoub@INVdoub@d2QJdoub@QWf4Xdoub@l!doub@ warpObjcwarp warpStyleenum warpStylewarpNone warpValuedoubwarpPerspectivedoubwarpPerspectiveOtherdoub warpRotateenumOrntHrznboundsObjcRctnTop UntF#PxlLeftUntF#PxlBtomUntF#PxlRghtUntF#PxluOrderlongvOrderlongSz ObjcPnt Wdthdoub@׏`Hghtdoub@[RsltUntF#Rsl@Rcomplong8BIMfxrpX@9aW*G1$B8BIMnorm((Pencil8BIMluniPencil8BIMlyidv8BIMlsct8BIMpass8BIMlspf8BIMlclr8BIMshmdH8BIMcust4metadata layerTimedoubA E8BIMfxrp@9Eb.I<    .)'%$"! !!!" 51./,(!!! !! !!!!!" !" " !! " "! " "! "!"! ! !""#""#$###"#"########"""""#""""""""""""""!351///--..-..DTWROLMLMJLIJJWZVUTRTRTSTVSRSTWXYYZ[[^ah\LMONPLADFGJRC6556<5()**)*+)+++*,--.../6/.//...-.00360...--+++++,,++++*,,A988??B@@=<>;QHHHEGEFDEEFECEEKA>PQLJIGHGFFFJOSYbhrpljlx|yu{zxzsxwkggigikghrxttompp|~{wumqrpu~wyw{{{|{|xkilgjihkvsosw}y|y~~wrrtstxrwturqqnrqvtqtpusuvusrommqrstrxwz{~mpmrqptssxtjq`aa`b]a___``_c`bc`_bb_cda`db`_`^``^[^^]\[]ZVUQSVZ]^ZZXZX]XZYZY[ZYYYZYZUVWVXYV\YXWXZZYZnplhmsp]Z\[_av|}vvz|wghhhillmoryw]PPRPROPROQOPNKFHIHIIJZ^a_diYTUsmqhhjjhiw]XYyormommrmgc`gcbddckrswrgggcfpstwuwttstsqr{}{}yvutpkic_cegeefcefefgeeoxz~hac_`ghptvvx|jpmikjonop|~}y|}vypwyruqorYXSTTVTTQHIJVFGIJRPLZYSRZcQPMLJIR[]b`ehagbedcdbefdddhghlo\`_dTSSTVUY[[IIJLMLGBCCDCFHR7789689989;?E'-' %)+-7<>>>=>?>>?BACC?>AEFIHHIJHQPRRQQPJKHDFCCDBFEFEFECEGEJNUWWYUWTXYWYUXXVODJLNNOMPOPSTRHIIHLMJJKMWRRPLDCFFFDDFUGFJLJYedU7671*-0./@QVB.+,-+-++*(**+++++*+*+***+*+*(++,+.("!#!"""##$%)#9X{±{T-QÄ?<~Bjs-,kf3}%2mOdoZͣ~`E1& -Kju Ә_+JiY qRGw0r7s msc )o 9  X 7g] .Js#jK'|rE.~N Wp4B~| ,X(@ JxG ;H mWNkFt z aW2-  8A}$\v#:Q m z3J] O#xB>g Q*)B`[6 0dEB]*y (@P``ld`PM@,  &dĞuL" XƏX v!Uh%W1!X<E.o!'[.}#tx yc=տF }g7ٷMN*w|9=Զppt8v ->L[``UPB* 'T3d,;e`~(+^x4p]80i`A J'V%j.J`-t( >@E:1A+z[-o$c(~s -8-U25/$)8$?LBOJ+$&$ .mNk:- [t4K3E= [: h[@ i<k S!0:1i}|p.h ɮoUxy[=՝g5\SxX9"BԍE H+;xE +-N/e1`'1x3z8\NFmly$cC NW8Q KS/Cg1.t 4fy:pN em$z+-x .Eh qFK(` -Kgoh  rF*[em  eg qNYDas  VDD:3x G9(/#X  7Z=h)*lJt$;JC13]N/~=`  xf F lX Ix kZ>3ySQ$W6G- B{ 1x_5؃/?m'A"݌B>d?.f}Psb .LlPCs q%sUd( #6I\rWbZhZ p_6Œc@"2BRbsI5xm[:4K ʺ1]J݅-4 a˰p4}&!47u!׉:lϵX L18*z`G3S "UxXٙe2 I_ XЊB6 ҳwps/lȽܡ^N"sNjRYVbm^.(..%% P޶iB ^JZl*hؽaB"_}( \ ͺ}jU:# `hkAEKlʺyiYF3 , ll [b .0*  I_ymnPl[i@U55&(kS F%XA84KbpdO+|C|?E:|@_] 69 pt $i *$iVnT RC9` 4t8',H PLu ⴈ\5ɯw[?%   Ϻ~jWD-  WӶmCm0 dU ,P8$G?WOHa\=i9#x6n0\=3Pi  %8K^s/%KujU 8N;*T $l ,~U F{vi$CS& 9-!7x  ;nBivN_FS[[P @f̸^4iNx5r},pB#7 Wc x?; Fa7j{ Vr=-=$DB+ #Vj$!B2Y1r-@ eK<=K͡*ș <8  ,0ܪ}X9!!9^ >lWz/Ԛ DH-Y.ocph^WXp YSd7f y*@ !h3F £iP OdjC$x@3  Ut`k/p 9rxBt ETX0dq. U &$kv] F/JT՗9=.,;Yivr[G0qLlEzm4 ^ 6H;@q@VV ? 3<rYl as ` @wb OLR\4@3$Gt -22x1)HN%5 EaPrT\aY u-   p) ˫`+DX?-%5=OtV vy!3@@>)h$93]@`hi; @2)G */x̯|3k!CfE& .1;UY  U:gc-OnLyvm 4 -7Oh( -w M"0 +1ͣ3n`O59PT *=GR`^P@),7 .+%* ?rݧk {A BsT  6Tn/Mb V7GajP9luuc$>\ǛxT9 ) &;SrW`C-i}Y F:>*? 9}1 wcj2 " Dq( -vi&BP)z\$D漰! h3Jn AI -F<s4Y{*] ' m$l0pFG^f6Z`Vt(05 Cp S[D#H#-0BH/iI P8r!Gtsyp X<9x2!rboVgtp)@8gPԽ ZI@$wd9? ѳ * f"_˸,5z_JJkƊQ "xu9G!<WJ:&\~;eu z Abf]s"cW?!c8& 1ݍ=(I ,MP[`ipw z&%PV1>}-%-m)YZyKe KM<n*Oo@S"^Mf n;dXV0 /{պC#"Kd37l|XD}40$yv1$R_{w%Xتt;*7@43B7P7mN..oG N0MN|V&ha&3c?m`K#  R` x0M\°;ga{$*f=H FlHpN7'&S`*04/ $j 2AX &$S}/e_I06$:!7=H:]$%Bh2ii t!}LE\3 T @oy-Of %侖oG p:\r l0( zOTGt   `0c:AkB!x(c0M%Fih$4M h:p02+G- x4]1q0Rwk)+- )&{qbh 0-Xx&7Z.Z -0) #$_  xtT? XF<Չ'>"u 8*%CX4ct xQz X\ yS 6,l FV<3ii{x]JT  h"qTe !?6| = Px9#g-y (E ]ppm]>! 5N(P %5OC?`_yV'z`|he AF5l7}:ln-.4#7*TN~w`p'<ML-$n-*]}Jv']@$8&mFH$XPl,AN7iHh*eZF&N|T5帛'.b@n+ O 0)e O1}P@i= GT`8$eF8tPM[`bN e -(7PyO >Hupڶ7*+}X$BZIm@ 6Hht%X1xlQ11}5ݾcxd-3 Mh92BbT-  ApHʷ*7V3{D% jcJPTIx4$cjxv9.XB*9`'wCE#UttT  ]6pYadb8d*0tҖS aN$@xDuT &:L_``WF$SB n1 E H0K&*@%.Z38aB@B޸Uۻ\;8g 0@sP8P7y0?BEW#!f;-}6@3dĺ?+4IB 3M3du/EC3yMXB:j QOPD3A ~!JQ\ 2GZmzc@[?_ X1VX,okSp ׮R%  N{{Ne vsŒJh3n*n, O~ MX;Bbmr8 (`=,=!dM-! G ^$dX$TX sƻ"qp$,U@&hԘX,^]cVa ?x2U ܝ` RhEx p@DT1@f(mTq 7>#M'~ M|y? m ob8`´ H\ћg2mv" , AΕC`h cӤvK%)Gb}2XjpCzX7P&T]0iݼwpn`mv^g8ZD oJ!;Tl B©lYIZI`+iHk xV;fpF/E&sa;=Wx%12 Q N.Fx f\ S;4h!5x_`97-z(*x;+mM|+Rqf$x(  BD3j1qxE Sok64`x+)H  $ b#XgX ( Nq`U$ Gq_ dWn$ ~I4{#x4@*  %L, 7tA* 3*+IJln 0s-4 ,]L8"D A  p`<XzgrU Y$r {sf  w=y<r;8`H+?v  &RXm `u sLe<qD}Gsp +%I]^/-.p ;hD89<Ux O4FVN~p dYSTHbn7b@ds"<mX|p/u K+`=DV*{*Cpvs.,@TOLdZ8gQE.$ju}V'8Y4i c;AM [`YIuKV,_ ~W<0%02Go',M8X)  yEZB  )GoCYP"9J?{BQ'qei?F,%jg| t|ipny xl  Vaq.I)Y3s=(FI!=s\B ;6lmv[452eZw$PlpMm? $F8  d.hl/^$ns6>nCLh.txnxpӔNFm-{ hJ{ ~k "Avhh[NeFՉ7@RFh&)e<sH=gqJ P"h>eG| W0& hL!.Ql lmY zI.Hmj5mhB A6<(L`hs\k?{  emax vL{}K30+ X|#XPpm? S(9k* 0+>#CF؃-1)=z]Vd 2؃--jKc=!{݇-..4@ii݈3CxIPNfoP3(T݈3V(z- PR0݈3xc ЈUo,t} m37;R  NI8i8N]5[`#n%8Dk9eDZ>,j=?`Rcg 'H x tKi?WN#t61 [EmjX +}@#* U(?jC@QyaV:6=a~"_B82 f85A/JxąU6}0.H-p$\]mt'cCf$dn2aqC$K2(ӌBV((dD,Մ dhL*hnh>- Ј@ pp0lCF5{Sf x3  %1ߐAy i` $M_-'}. pN- $Ix$h 3F-m-}~$p-U!1Q3V 5*7\!Mp -/-5sr3 UXt%D#+D=uPqT'P_A j w(H<jCQ!A.7*P}H=3 R; HC' -V"7R [#6Iq-/-k]m |WP-t24:170ABH0:IB[ PFf^C^R MtB ] 67n b,zx%+L37ZI*b ݲ**$ Ro;.  G&7|Eg A-c!q.t2-F!\OE>S`ZSBHY{?C7[iiA r' +003@@=0, L۰|P9~v km? .KhĬ`={1nxM"UX j@oԱX(e$B{mF*EyIǖe(.CCu]x X  %dp0m>7StIuci=ei!&կɱ|pghppu3MPL`oI׸ي;Es/06<`H_ _2pmMye6mv8\6jS I4M 1XHr XX .  ( ~ <]0P\8M:(wePA m}%v@@0:DF#fS kѭoU9 *@[~J @t w%MHH+͟qC 3dz5ZXu&& bS١j4&_j%-$(~fS+ lԔXXs933-*? Fb'sΛi>.f*$%2"^)ڋ<X辚zX7 -JpY yZ7P07<z*M ǿ 5I{0JH}(HVs? 6RP1A {T((`U1`XI:_^π0_2hM``r4%3Zl ]hc``?&u8 % ՏJ J !~hqiU`^2p~؜`!+<R\G`XD%]ʎRa Y,O5`/b]\ xȕb1z @ `'hmXʡxP'j :F@Xp U]  ;f ɭqT6<( \ @PZ%UΙ`'vx'01@@GB@70,X$/@CPC "BbϱvP%Z]CwH8!) 3Sj@1LB{ wd;=*UBxgt48$`5x {V|1$B1$U+ !"H%1$#D 1 , F a 1$} u<(Ehp7$wt>*!IWphG,L<a;M5N1y2'+[ " a3 ^ehv8I{ :& Xd/@fHV@u S: @ra0:@I^@^:U f8!X-R:2P "E`lpaM*o$[Cz*ALULlz#  8U`$H(5H30hBA!6TxՋBPz @Oiyn NBqd$7 Y~rš{\G1 $5HhLH)FZ&DGa6f3 !65 }{ e9^ZX9Wj{}jR/ =pJT3 )&Nsĕ^)'(f\plIvd$/er*ua&@ h)0?~؆(GR^1jLm/r#n]`5S#*̝$-~x ((sJ[=$,rk$ h3TD*F@4=@ >!D|<1[]z_`hzp ;pڬ}O $@\`"DNAe|ȷwX8OW[`V@nX,7 {?`2,w+!Ny$--W<ۿp]K90#  -7J]q mׂI($!əoI!*^m`H@$VL`3 ך`+g\/~h]p+0UޘR" LiCfanc8XN$v% ,rbƙ;~KLlvv0aQ(h7e<E6/7h'd=/$0 x@$2oo=, #B 9F|b p]z^7DVa-Ût79\/,T<d/&MuF07 q<88 Z؃,mr-# XZ݆-wzK $P} {,w0/ )g]&(A #UO dؐG6-XoNdIy`l  e Lr/Z?B.YD$ Dh*8 9j'"k}8~+ט\xIO P6YT[hH1Nu?uAG3+2}R~ 2IY]P?0>kpLp1  9Q S$cVV7x0iXdM5G(794F  Z)I$(j3J *@N|\_X R. B @euK?~ <X%n]1S^Ԃ,j!X4Fn@vymTfJ];Vz 5sȅD .=*kJn3* n }ix1ЌL  fG6Y[ $ p `N!s  F&t̂8ie$dF>4`Gr]}aS8*M n}C$ ?rYV{7HP q3$#{ i8Bev'7F<y EwH8I<pi\TB h:-"=.\  Aqi!]$||IJڠqVPMregB  Cm " v$qXab/\ |t?l58@<r"_K* 23)(p) 1o :i7$8RTX  KXy5u{z$}u ]?$NrZ $)AV=u p' \L$7~ x1`hvBj@2{u!VQBV2u8O;+p 2N6~*p8y(BeD'L^U]Q39MW,RNo:! JiyK(DGePA. &[*5@,e _ }[Q0_R Ipo!?CXZmx T.}`Gj!Fq~6Sv4F^T I%\{x` N P)Vj jv   +HW A4>hV{R&|$A ,h4GJ?7 w21h dy7M268*{ Z  Ya7-DC>[ S 4 tZ&lfHh[1^=-sH)yoR7Ӵ@xi2g178B P<3x+pzl? /SyT;c9^t{- )L ̳pi`VP\``m~gqBm}O^PD\1p YH:7 D a*hESY-|$Pp"m T0_% 9 ?xl7mbI8xU! -=s{ / Po  4Vu` "H wo( Km@,'Evb e3 AN wq-M )- 6)^4x</;E,IWJ_p!k['!OyswxER}ӳkD2d,d"V9 (>Vkp|kZI9' M Iw;8 gm0[ )7o 9;"JrMd 9Pcu˽E te  !!%Gjw !\ }U ,Zd/$IcL7 9mO*?T7/f  ؞'a;<$Z#^O,a8s)S3z x+X$74t]Kq5e<ihVJ-}KBFxk)E+3fyeaw":K\m~xfP6n 4mUz~~yb~m#tP Fjvi WVxaCIL+ҼHr\<D4KAG/9#yӹ)([^U`2 9 2 'qؽxfTB0&  ,:H-(^4C*$F(nDi hvT2!U 4X8v?P= OĔe;nO%,  &8J\`psqo`WK:(ʵYt?Ih +‹U$"KS*9WsиzZ8 mX;x*hߣg-F -ߟ1!M g>?=׵i!}6KZ3%ND ! Jg)Q7TItP Z?Y~0$ @:3D1lލ>Gxp>n2|h yJ;7*y*8@UxAek%;s\6*$O18H)l4W2 9Fà;"&&;bK=-w! s݈7 sEG0`*<9z} $?t,,Y'Zj {@@CWWHȈH k  ~9k-h-o4$gӣr: F#DR+:pgk, R8`ʯuP)o@H.+` V[EEW8$x 4 .ltP dE@AZwH"ȡ%A* |+)' V1fD8`=:m8z6e vt @3 ?dȸxX3F-XWhK H1  I̘]7 ]85&9PKv R΄.k w` 5KZXvt $wG veA@ [ euC6K \hq0@%`CZTX@ -A=!q0KfuN3X>4KxHA- hNHN"-%00- j(hZ= NΉ@=K+'wسe?3]goao/H 'Sy`: U>㲂Q ¡>`sT5#  a1 GX#<P]M\ ǥyhW\`mN U$ 32 S=;:2 xS/&Bb9I4   #CkxM*r|$kE9-=j-ܤj0)X5` ml$h;<d6JMBI5<CX-E~Zt7 Rb(מf)~O;}-8!)G-#iqxT-tF`w:ߚ[)?l+81\Ndw)}D %IF,,5mGY  7!síĄC<;"3$fe3ֵ+ s.|0C,~!w'> /T{E`_AX{3S=MR UZ ;lh %ys'&&b4'6$u0$+Z֒='ۈ3mr  b7n42 } 8ThzoX9TH݈3?ax Z5 wM p "݆&)w %-\+}#'fe1_8*gx]]@CYE *>Ty-F(5v >wvDfh#7$Ux `$_=dݠ]<.FNf !,^ßzRTLBc'jh] H$#̒Ue/{1 G> r G[Xe.)485szuGeffM1 Dp} \c&(h,c5>15~j0_@ z@] @o!ps;@31Ab'M]XF$> iH:3A;H11q <zPNj]VQ11_k[N@@>@@HYi/03NJIB1*n! m8ASV14$p*F* Lȟb'TaA5"8@e : -t*IApO )<;d7 =\lspkR5  $?<|w9WۡYh' @VtS0R2`w-n HKڨuB u^/WI*#zHnq\>-$($P: $5 -!mI$d7Pn>"2@~rB(Ta%H!a / }: N.7HcH0oK 'A\$tf11&@B!*b1?7Q >J$ {|>C<ym<el$JBxzZK}C?6BxQ-*D+|^B:/2t* w4 7Oww  ?(-v P`\hv=|\)}+ 5TUr[ {C@dMK'4`PRX7:) B)/6~97 8[e G$ B]n\'ZiRaR B (?5 Y])HU- s6"p Wo0\o`4>,%/`&Z lTS _p%:f;J{#y 4YXXcF_!(B "!)?>@0o:&SBm? ($q|7{@;ԑVKKD,8KYh ĢlYPFBPRbs g@w8FC5E1A߷*r!hUh< ] M*\xL߇*1Ⱦu 0h 9iZ (Rf6? H cV OL~q{& db$j ]K_$,P5=< ${N{]a )"P-"4B/!xbFhEJH!iĄCjUeTk ;/jŚk5o3Lux~3DS\6"rz L  S7$#5Lf ~DNq$v*W.AD9q <80G  zjSrS*yl`X<7l]b?@:Lh19m@K_hC@.`Zz@K`GSsH@IW)6*F@&Hp$ @z;`z 6 @/!3Cvj!@/$qlF TC@A "&*p@7U2X T@# xj*sD  x C%}<2 ~*=oXIfҲ2>~9 " "!HZ-!?CNOx+ji2SpB< =V% 0(qCf_hx;  ;"=*s"#KlDB0#$NMwC"k;J,7] Ai~}Phzc<UM0VP()Su`@"iUpkI@O- `pV~eb `vrpNvG':>[\1{gP>C c? P I!{PIriN,CX̏)8+N@U{}@i&,x @/aduQf @Q!(5>A x@ti n\Z @s8  8@,/9R~  xItZ dpS7XP$Ft1 /_T W} HfP`zN):'*"`pnj2h3&oo~[0Z*=^^p`'4y   |`m,( * 0Ph3vv$C\L(I,5/ZD`& 6J(iՊn$a*'  z:a`X[^Q 8$^.] P* `?,n}0a<*NbSg  oBw}=(MFd,[QvC  x9|T  x-F |,}3z$QKN=mF x{[zIׄmh!OP8K -EPP==?.-&  DIr_b W5ZX8=yug[xyb&!'p^N_  )?Gt $IDMpx  b3K/x m23z~<N `r  fh =3-p 73eH g1 dc0 fc' ὗ}iVB2- ?W3]K|@ óuN!}Hu[]0u;BU g3 lD7 S Cz*Xp}Mt>hى8!HT*RF- N4 V@t, !ZxUP #qOJhjr~B$ $~V iV`-WS$&3 Z HIBJ6? (E`u,Nn8*Hsru)o'3(UO?1$urŒb.YM f.˘h> S Z:X辒h>S #Ɋ9"Ÿ}[8 aJ *02@300#``UѮpW<$9Uo%֊;(Sr׹~U+<ֺ yhWG6%  2DVjq%9r՝g0P  Ik"qr))q'6À<-Y[uĆH #H~@ ^)|ZM2+]c%R~h p(R~( uT?aj0PiڿmS\kK-"?e 9 Oar ̻rZF,5M<NjQ"Yy "0)  "ԎD~mJXIm *cVhP#1k%4[-v7 x8^$]8nn2 v_ %;Cpxs( F+< eCjiG3p HJ*Lh&H ,k*711fZn3,U"VC {4y<_TXM1=` _`'mwK%   $,,,,*,,,,,,,,,000000000000..00000000000,,,,,,&((((((($$$"$$  $$$$(((((((((,*,,,,,,,,,,,,,,,,(,,,,,,,,,**..004488888<<<<@DDHHF>@@@DDDD@@>:<<886888866<<<<<<<<@@@@@@@@@@DBBDD@>>@@@@@@@@@<<8888888888;>>;44444688>BB@<<:<:<<<<<8:<<<<<<<<<<8:<<<<<:::<<<<<::<<:<<<<<<<888888888888440000000000000000000000000.0000000000...00000000000000.000000000000000000.000000000000042424400.0004488888844424444444220,,,,,,,,,,,,,,,,,,,,,000000,,,444244442400.044444444:;6000000.00497500044444444444444888888:<;<=?>=<;77<=DDCA;;;?>888888888888882220000000000022:;;=>>@>=>>B>><;:<?@:::<8<<<<<<<<84448688888<<<644<=<<<;5,0444.0,**./**''&&''*)(#"##"&'&&(**+.114224441-./..,,,,,,,,,,,,,,,,,((((($$$$$$$$$  -&  $$$$$$,)'$$$$$$"$'(''''&% "$%&'(+,*(((&(((((((((($$$$$$$$$$$$$$  $-"""""""""" $)($$  ݚؚӚΚ͚͚͚̚̚̚ΚΚ횭횮우뚲ꚴ蚶暹嚻䚽㚿ޚךњ̚ǚšӚ֚ٚޚښٚۚ՚ޚҚϚ̚ʚȚ욽ƚ욹Ě욶Ú횲ޚ횮욖횫Ꚙ효蚚횥暜횢㚟횞ᚡ횜횛횥횚뚧욙ߚ隩욘ߚ皫욗ߚ嚭욖㚰횕ߚ㚳ߚ⚶ޚޚݚߚܚݚܚښߚܚךߚښӚޚٚКޚ֚ҚݚӚԚݚךܚٚܚܚښޚٚᚱ֚䚮蚪욦ÚÚǚȚϚ̚К՚ښݚۚܚ횤ޚ皛ߚ⚠ߚܚښؚ֚ԚҚКϚ͚Ӛޚ⚹䚺暻蚼隽ꚾ뚿ߚݚۚښؚך՚ݚ횿횾횽횼ޚݚۚޚښݚؚۚךښ֚ؚ՚ךӚ՚Қ뚱Ԛњ皳ӚК䚵њК隿ᚶКϚ蚾ߚϚΚ隼ޚ͚͚蚼ݚ̚̚皻ݚ˚̚皹ߚޚʚ˚皸ښޚ͚Ԛ暷֚ߚКؚ暵Қߚњښ嚴͚Қۚ嚲ɚӚݚ䚱ĚԚݚ䚯՚ߚ㚮՚ߚ㚬֚㚪ךᚨҚךᚦךךᚤۚؚ⚢ޚؚ䚟ٚ暝ٚ皛ٚ隙ښ뚗ښ횕ښښښܚܚܚܚݚݚݚޚޚߚߚ蚪皫嚭嚭暬嚿皫嚿蚪嚾隩嚾ꚨ䚾뚧嚽횥嚽䚽횲䚽횭䚼䚼䚻횿䚻㚻䚺㚺㚺㚹⚹⚸Fᚸ /Rv$HoCUߚޚߚݚݚݚݚܚݚܚۚݚۚߚٚٚ2ؚzZ:ךmI&֚u)֚՚՚՚֚ߚךߚٚޚښݚۚܚܚۚݚۚߚښٚؚؚٚךؚךך֚֚֚皶֚՚֚֚՚֚֚՚֚욁֚ךךךךؚߚؚߚٚߚښޚښޚۚޚܚݚܚޚݚݚޚܚߚܚۚКۚњښҚٚԚښ՚ٚךؚٚٚۚךꚿݚךꚾؚ隽ښ隼ݚꚺ隹⚰蚸嚭蚷蚪蚵뚧蚴皳皱暰ߚ暯ܚ暭ښ嚬ך嚪ޚ֚䚩ܚ֚㚧ۚך⚦ٚך⚤ך֚ᚢ֚ךᚡԚך㚟Ӛؚ嚝Ӛؚ皛Қٚ隙Қښ뚗Қښ횕ҚۚԚۚ՚ܚ֚ܚޚښܚٚݚݚ՚ᚡޚњ䚞ޚ͚皛ߚ˚뚗ߚʚߚȚǚᚾƚᚼƚ嚁⚹Ś䚁⚷Ě䚁㚵Ě㚳Ě䚱Ě䚯Ě䚭Ś嚫Ś嚫Ț暬̚皭К皮Ԛ皰ؚ皱ݚ蚲ᚱ蚳嚭蚴ꚨ隴ꚶ욶횷ߚݚۚܚݚޚߚߚޚޚޚܚܚܚښؚ֚ٚٚ՚ӚњϚ͚ʚ뚜ޚߚǚњߚĚǚߚᚣᚤ⚦㚧㚨䚪뚶嚫隸嚭暻ۚȚ暮㚿͚皰Ϛ蚲ޚϚ隴ܚϚꚴꚶܚҚ䚴뚸ۚ՚͚욺ؚ͚ۚ횽ۚߚۚ̚ښݚޚ̚Úۚޚ͚ƚۚߚϚɚܚߚњ͚ݚߚӚњޚ՚՚ߚؚښۚᚡޚ㚟횼횽욿ߚߚޚݚܚۚݚٚߚĚ⚰ߚŚ䚮ݚƚ暬蚪ܚȚ隩ۚۚɚ뚧Ӛښ˚횥͚ٚ̚ɚؚϚǚךњŚךԚĚ֚ךš՚ۚ՚՚횿՚욾՚ꚽ՚՚՚՚՚՚֚ךך{Z9ؚeD"ٚPeF'ښۚܚޚߚ횕뚗Ꚙ隚際障!Bd隞;Yy隟&Yx隠隢隣隤隦隧ꚨ險隫ߚꚬܚꚭۚꚮؚꚰ֚ꚱԚꚲߚҚ뚳ߚК뚴ߚϚ뚵ߚ욶ޚ욷ޚ울ޚ욹ݚ욺ޚޚgޚEޚoߚ?ߚy욿JߚL욾x 뚽i 뚼:Cꚻfꚺ 隹z蚸-皷V暶{ߚp嚵|6ޚ嚿4䚳S_ݚ暽 b㚲)ܚ皻⚰Gۚ蚹Aښ隷gߚٚꚵܚٚꚴٚ뚲sٚ뚱˚ۚ욯̚ݚ욮͚ߚ횬Κᚡ횫Ϛ䚞њ暜Қ隙Ԛ뚗֚ؚۚޚviO蚢(ؚ dGҚ38͚ `yʚ*RƚV'Úzj A?ߚeۚ)#5uؚH n՚ `%ҚXJϚs̚њ0ʚך[̚ܚ̚ޚ/ʚߚšǚ̚Қؚ՚՚֚֚ךؚߚښښܚ֚ߚњ͚ʚƚÚӚҚܚݚeSњ-gК"%|К;ϚQΚޚh͚ǚ&V̚ɚʚ˚̚ΚКҚ֚ښٚޚښ㚶ۚ隲ݚߚKi  Ck 'V(ZrJ% e5oߚ (uޚfݚ8ۚI\aښ,ٚS>ؚE gך S ךOE֚Yj֚X ՚皤՚^՚՚XԚԚ՚՚՚՚֚֚ךؚؚښۚܚݚߚޚ욪욫Ě뚭Ț뚮͚ꚰњꚱך隳ݚ蚵䚽蚶蚸욺皺욹嚽울䚿뚷뚶ꚵ隴隳ߚ蚲ݚ暱ۚ嚰ٚ䚮ך嚭Ӛ暬蚪隩뚧욦Ěǚʚ͚њ՚ܚ ~pdU<"v^暁7Uv0a:|R%` 0g 5 \ O}IxCoؚ֚ٚܚޚߚ욖皛ؚ˚ȚǚƚƚǚǚȚʚ˚ΚҚ֚yhS<(䚼jښSӚ;К"͚ ʚȚuƚ^ÚD-:/DYouۚꚘ횕皁ϚњӚԚךٚۚޚ  $,,,,*,,,,,,,,,000000000000..00000000000,,,,,,&((((((($$$"$$  $$$$(((((((((,*,,,,,,,,,,,,,,,,(,,,,,,,,,**..004488888<<<<@DDHHF>@@@DDDD@@>:<<886888866<<<<<<<<@@@@@@@@@@DBBDD@>>@@@@@@@@@<<8888888888;>>;44444688>BB@<<:<:<<<<<8:<<<<<<<<<<8:<<<<<:::<<<<<::<<:<<<<<<<888888888888440000000000000000000000000.0000000000...00000000000000.000000000000000000.000000000000042424400.0004488888844424444444220,,,,,,,,,,,,,,,,,,,,,000000,,,444244442400.044444444::6000000.00497500044444444444444888888:;;<=?>=<;77;=DDCA;;;?>888888888888882220000000000022:;;=>>@><>>B>><;:<?@:::<8<<<<<<<<84448688888<<<644<=<;<;1,0444.0,**..**''&&'&*)(#"##"&'&&(***.114224441-./..,,,,,,,,,,,,,,,,,((((($$$$$$$$$  -&  $$$$$$,)'$$$$$$"$'(''''&% "$%&'(++*(((&(((((((((($$$$$$$$$$$$$$  $-"""""""""" $)'$$  OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO$DOOOOOOOO*=MOOOOOOOO%9KOOOOOOOO"+OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOM?. OOOOOOOOOOI8%OOOOOOOOOOM<OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOM?. OOOOOOOOD4#OOOOOOO)4$OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO"3DOOOOOOOO.>MOOOOOOO.>LOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO5OOOOOOOOL$OOOOOOOO9 OOOOOOOOL OMOOOOOOO> O&OOOOOOOON'O>OOOOOOOODO KOOOOOOOO6OOOOOOOOOM"O4OOOOOOOOFOOOOOOOO?OOOOOOOOIOOOOOOOO ,OOOOOOOOOL  ?OOOOOOO9OOO@KOOOOOOOIOOO*1OOOOOOO2OOONCOOOOOOODOOOG$MOOOOOOO!MOOOOOOOOO5OOOOOOOOODOOOOOOOOOKOOOOOOOOOO;OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO OOOEOOOOOOOON*OOO,NOOOOOOOOGOOO?OOOOOOOO7OOO!KOOOOOOOOM!OOOO4OOOOOOOOBOOOOCOOOOOOO<OOOO%LOOOOOOO8OOOOO1OOOOOOOGOOOOO-OOOOOOO&NOOOOOOOOOOOO;OOOOOOOOOOOIOOOOOOOOOO/OOOOOOOOOOBOOOOOOOOOOOOMOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO4*MOOOOOOOOOO 5OOOOOOOOO@OOOOOOOOO HOOOOOOOOO*MOOOOOOOOO 5OOOOOOOOO,OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO OOOOOO OOOOO&OOOOO6OOOOEOOOOMOOOO"OOOO7OOOOFOOOOOOOO ,OOOOONCOOOOH .OOO:OOO&OOONOOOGOOO4OOOOOOOOIOOOO9OOOOOOOOOOOOOOOOOOOOOOOO"LOOOOOO9OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOMOO>5* NOOOCOO7OO*OOOOOOOOHOO@@@DDDD@@>:<<886888866<<<<<<<<@@@@@@@@@@DBBDD@>>@@@@@@@@@<<8888888888;>>:44444688>BB@<<:<:<<<<<8:<<<<<<<<<<8:<<<<<:::<<<<<::<<:<<<<<<<888888888888440000000000000000000000000.0000000000...00000000000000.000000000000000000.000000000000042424400.0004488888844424444444220,,,,,,,,,,,,,,,,,,,,,000000,,,444244442400.044444444::6000000.00496500044444444444444888888:;;<=<>=<;77;=DDBA;;;?>888888888888882220000000000022:;;=>>=><>>B>>:;:<?@:::<8<<<<<<<<84448688888<<<644<=;;<;1,0444.0,**..**''&&'&*(("""#"&'&&(***.014224431-./..,,,,,,,,,,,,,,,,,((((($$$$$$$$$  -&  $$$$$$+)'$$$$$$"$'&''''&% "$%$''++*(((&(((((((((($$$$$$$$$$$$$$  $,"""""""""" #)'$$  ӒӔӖӗӘӘәәӚӚӚӛӛӛӛӚӚӚӚәәӘӗӖӕӓ                                                   .0B:B:>>BBB@@BB:**((((((()****++++**********-7:;=?ACEHM[^_^^]\[[YWSD:8643210///////000011000000004;<====>>>>>=><9886777I^cfijimlq+HH\VSPRL3"!l8 ;:?CDGKMQT]`cbefha_]Ynlijmonqqpvzzxwutsrqliibgm|~~~{usdY( ؁؁ؑǁǁǑմѴĽ͠ɠ͊Ɋ}xrpnmoqrtuuuuutrqomnprx}ķyphc\YWXZ\]^_____^]\ZXWY\chpy˽si^UPIFCBBCEFHJKKKKKJHFECBBCFIPU^isŶseYMC=620/1357888887531/026=CMYdsxgWI=2,%! "$&'''''&$" !%,2=IWgx̻n\K=0$%0=K]nɷzeSA1$    $1ASe{ȵu`L9)  *:M`vűoYF2!"2EZqïlUA- .AVm ®iR=) *>Rj ­hP;' ';Ph gO:& ';Ph gO:& ';Ph ­hP;' ';Ph iR=) *=Qi îjS>* +>RiïlU@,  +?SkİlUA- ,@TkİmVB/ -@TkımWC0-@TkıoXD0-@UlűpYE2!.AUlŲpZF3!.AUlƲq\H4# /AVmƳr\H5$ .AUlƳt^J6%  ,?TkǴu_K7&  +>SjǴu_K8'  )CHMYesǴu_K8' & !$'+15:@ELQW]htǴu_K8' (  #'+.27=BHNT[ahnyǴu_K8' + !#&*-169>CINU\bjqyǴu_K8' 0  "%(+-058 !&,16;@EKPTZ^cgjnrw{Ǵu_K8' = $)/7>DIOU[bgmqvz~Ǵu_K8' < %-4;CKSY`hov|Ǵu_K8' < #,5=FOX`hpxǴu_K8' :  *3>HR\fpxǴu_K8'  %/;FQ\hr}Ǵu_K8' ! +7CO\htǴu_K8' #/=IWesǴu_K8' (4CQao}Ǵu_K8'  ,9IXixǴu_K9(   0>O_qǴu_K9(  "3CUfxǴu_K9(  &6FZkǴu_K8'  '9J^qǴu_K8'  )PeyǴu_K8' ,@Rg{Ǵu_K8' -@Ti}dzt^J7& -ASj~Ƴt^J7& -ASj~Ƴs]I6% -ASj~Ƴs]I6% -@Sj~Ʋr\H5$ ,@Rg|Ųr\H4# ,?Rg{ıq[G4#+=OeyűqYF3"*-  '8H\nͽn[I9+  '8H\n²zgVE7*&"  '8H\nɺsbSF82-(!  '8H[o²rbUHA:3+%  '8H[o˽sfXPG?6.$  '8H[oȻvi_TL@7-$  '8H\nƺxnbWJ?4)   '8H\nƻ}obTH;.$  &7GZnȼ{n]PB5*  #5EYmȾxgXH9.! !3DWk~ʿo_O@2%  1AVh|ɺveTD5' /?Sgzĵ~lZI9+  ,=Qex˽q^M=-   +GT_oѺycM:' %  "%'*+.03579:=>@@BCD#DEEDDCBA@?>=<;=CLU`kyкx`J7$ '  "$(+.1469ADGKNRUXZ]`befhjjljhfedba_^`dkr{ηs\E1* "$'),/159<@DHMQTW[_dgkmquwz{}#}{zywusrtw|Ͷr[D1+  #%(*.0369<@DILQUZ^cfjotx{~˴pYB..  "%(+/147:ACGJMPTWZ]adinsx}#ưmV@-    7 #'+049>BFIMORUX[^behlosw| ƯmWB/   ! !"##$%$%&%%&&$%%#! ? !#$%'*,147BFJNTX\aeiloqtwz} Ʊq\H5%   "$&')+.012468:;<=??ABBDEFGGHHGFDCBA@?>=;;9987665443322321232321232123212323232110.-./?134579:<>@BEHJNQUY]agkotx|Ʋu`M<,# !#$&()+.02468<>?ADEGJKMOQQTTVWXZ[Z\\Z[YXVUTRQPNLKJIHFFEDDCBBA@A@?@A@?@A@ABA@?>=<;>@ACDGIKMPSVX[^beimrv|#ȴyfSC4'  !"#$#"!  !""#$#"!  !""##$#"!  !""#$&''('(*+-/1357:?@ACEGILNQTWZ^behloruxz~~}{zxvvutssrrqpqrqpopqpopqpqrsrqpnmmljihi=jlmortvy|ν|l_SHA><:99:<=?@AA@?@ABCCDEFEDCBA@?@ABCDEFEDCBA@?@ABCDEEFGFEDCBA@?@ABCDEFGHIJ$KLNPRTWY\_bfimptx{~~~}|{yxw=xyyz|wj^UNLHFEEFHIKMOOMMLLKL NONNOPQQSST SRQPONONLLKKLMOONOPQSTSQPONONLKLMNNOPQSTUTSRQPONONLKLNONOPQRSSTU&WXYZ[^`begjmptx{~;ƹvkc]ZWTSSTVYYZ\\[[ZZYZ[\]^^_`a`_^]\[ZZYYZ[\\]^__`a`_^]\[[ZZYZ[\]^__`aba`_^]\[[ZZYZ[[\]^__`abcd$effgikmprux{~  ˿{smjgdccdfiijllkkjjijklmnnopqponmlkjjiijkllmnoopqponmlkkjjijklmnoopqrqponmlkkjjijkklmnoopqrst$uvvwy{}Ź|yvsrrsuvxzyxyz{||}~~~}|{zyxxyz{|}~~}|{zyxyz{|}~~}|{zyxyz{|}~'ú 'º%%¾     Ŀ¼ü~{ywutrqopqrtuwy| ƾ{vrnjhfca`^][\^_`bcfilquy~"ú}vqkfa]ZWURPOMKJKMOPSUX\aeintz$ż{tmfa[VQMJFDA?=;:89;<#=?AEHKPUY^djow~(ž|tmf_XSLGC>:7520/-+*+-%/035894.*'$ &"%).2782.)$! '#',06;@FLSY_elt|+ºxphc]WPIC=81,'$ ' "&*/48>DJPU\cjr{,Ⱦwnf^WRLGA<61,(" ( !$).270& ",7BO]l{+û|yusrssuw|ȻsdXL?3'  !*3@O^m~,ztpkhecbbcdhlqv}ufXLA5*  !*6DSctwqkea]ZWUVWZ^chnuǷvfWH>5) *7FWhz,uld^YTPMKHFEFGHKNSW\dnzzjYJ<2*   !.AEINU`kwȺn]K;.&  $2BSevʻ-|n`TJB>:641/-,+,-.036:>EP[ivijwcR@0#  +9J\mŴ pbRF;41-*(&$"#%'+-08BO\kyҿo[H6&  %3BSdvxeVF:.($"  "%,6CQ`pл}gS@.  ,;L\m}ϼo\L;/$!,8GVfwͷw`J8& '5ETeu̹ {hTD3'  #/>M^oʳqZD2  !.=K\k|ʷ wdP?.!  (6GXi| ȱlV@- (6DTctʷwcN=,  #2BSey ǯjS=* $1>L[lz˸ yeP?.   0@Qcw ƮiR<*   -9GUes̺ |hTC2$  /@Qcw ŮiR<*  )4BP_l|νnZJ8+  !1ARdx ƮkT?,  &2>L[hwvbRA4(   %4DUg{ưnXC1   &1=JXetų-~l]L?3*%  +:J[lȳs]J8(     )3>JXcr˻yk[NA92,&   '4CRcs˶yeR@1$ '.7ALYer³,xi\QG@81,'"&1>L[k{ͺn[L=0'"!##$%&&!%$"!  $(/5=GR^guʾ,ymaYQHA;50+&# !&+4>KXguоveVI=3/+'&%&')+-./00. -,**('(('*+.27=DMWblx,}ph_WOHB=73.,*()*.38AKWcqõrdWLC=:7545679;<=>><;:8876679=@DIOW_js~+Ĺyqg_YSMHB><989:>CIQ[fr~ɻrf\TNIFDCDEFHIJKLMMLKIIGGFEFHIKNQV\cjt|*ĺwoic]XSOMIHIJNSYbjuôvmf`[XVUUVWYZ[\]^^!]\ZZXXWVWXXYZ\^aejpw)žzuoiea_\[[]afks{ʽ~wrnkihhiiklmnoonlljijklmoruz){vspnmnorw|Ż~|{{||~}|}~)Ľƾ(Ľý)޼¿( <(20.,--(,*))(*''(&(())(($''#'&:H@>;=8:6865876853512-++,235688:;99799;;<;;=<<:<>>>;=;<?9>@?@@BECGGKOD///.,-&'&&&'&'%')()*)*,+,,..0*< Df ͻfD Be ͼgDRPPŒTHԎEDؐI p߀ vހMHHM`'""''GDCHXXU\Q,UPPX 842:X! b][dH%SKKQxOů 0%)߭*G:>@@![BNNbN[UpHfJ[SZ=NExJ.@591) ޢ h?ɛbnoW+L32 `9f0Z:hjv,9͌Z2 'O}.ѐ]3 $Nyk Tp PX1U6G<$ :@^V^5He/$% BW5NO,o:t|2 !i-u-Xt7Bw/M! PFE 8 xH%!S(`o({!xN@}4p  8={DH~`V? / 9Ki@] nGQ 5x^@jw<0B N@5 fV YI|)x6ͳEHPTP3^%jPq|Pe qePLTFP*A7P9/Pi0 PC0 P,3*P@1P`KAPDf{ZP&jzvP~%X1eP`X=dIP= +P&pDd7 P~PCwPZ%_kP<&P"|YqePxy"-PX^ORBP8&_kP Ak 8wPuq&|2PTVXK%LP5 Pd9pGPr ]0aS8VPP3+P3%1P=1P lT!`PN`75U?.P.ugr}_}Px% h)Pl   x yPGwz o h iP. C:gF7sP\o>CnOs?BkP dDOPH1(P)P ܕYԞfPf:1P?yP+š6@P:jvP=5P^SPvPP$P!$P$P|Pf ] PC<P˲KŲSP$PEz?P, 2P?Ӿl8ξsPicP) ~+P-1Pt" m&PMiJmPmك-h݈3Pbu._y0p(X.hw> ,fy@ +Tt ʸ|\7 *Sr ˹}^9  nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn  ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''%/4788;::;<<;<<<==;;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;==<<<;<<;::;8874/%              %),./011110./.-+*(%#   &-49>ACDEEEED/CBA?=:751,)$!  (26,$  )9MavÔāđīti\RE;0(   ,>Siׁ֖ב׫~rcYJ@4*"  .AWoȾxi]NC7,$  /CZsƾ}l`QE8-%  0D\uönbRF9.%  0E\vƹobSG:.%   0E]wǺpdSG:.&   0E]xȻqdSG:.&   1E^xȻqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xɼqeTG:/&   1E^xȻqeTG:/&   0E]xȻqdSG:.&   0E]wǺpdSG:.&  0E\vƹobSG:.%  0D\uönbRF9.%  /CZsƾ}l`QE8-%  .AWoȾxi]NC7,$  ,>Siׁ֖ב׫~rcYJ@4*"  )9MavÔāđīti\RE;0(   %4EWiz|rg^RJ>6,$  -ACDEEEED/CBA?=:751,)$!    %),./011110./.-+*(%#            8'$070D$$ &6(/,:K,8"'#,8+ ;$$.B((($$(#"!5D? $,(;44,44#8$<;(8( 'O0D6#Wc 3,<8:;'S1@(*(26H 0,8B;R<8<(#$1#@7/,-'.I0P(,,<I:i& 0D,IX6= ,((T(C((70FWR9L>(<$P<H$/(<@,KO\0174VLL<1 $;*XCSS, 4@$U>60,K/`;5*>0<D 80,K3,GD<@R27Gb10?8=F@2@HJV]VNL8GGA@@AXMB_aN_bj]|G]YbgbebXl``}oz~jgrcqi[jqknp{{|x!    *! &6%  $ $'G,,!#  }|wnidUBYORC/%.]QK'83&9`>:3076I_94.,+(044 (( $                                                  "                    #    !    !  '   #%   &# "%  !$$(   '!'%'  !$%"!#'   "!#&% *   !"  $%%'4%    !($"!$0"    !$""$""#(&    #$(/(#3.!!"!&*.)(2(!    !!%!"$$'.-%"+3*!   # ,$!%!#(.0"2-!    "!%(""-$ .1.%)(    #!#$! # *-)**%,1%55!   %"('%(,')A0)*(4< "   "$) ")-(%'&$..&(/,7&    "(%,%)(),)+('-968> #    # "!%#!-*(0-/1//)4A< #    !""*'%.('2,.7/0/;$  ' !!"+$&*&(+$')0;952+;    &'!'))(%(4))2+*(01/302;,  $"$ "##+)"1,*0-092-015:=    *" $%$(%'*(%,'-2/073<6+72:+  %$'#-*')(5,3.14)/;67-65?(*  %#"-*& )264.:0204391+;A2<*)   "$("#%-,!'/-%)3(+034099161CB4>-&   !'%('())',9#'#(0,/9;.0196>98K1/  !)$#( ',1/+3-%3;2':6426E=086A& ! !#"#,)(*(+3531,3@9-0;;448:=C7>) "&% ! &+"'+-)+-3930,/A95D@9=F@?41   ## (&$)# *1$&/)&/1/0668:=<5CA>A>:@8-  !$ !+.2&#)-'*112.97*48@94483?G>46>26    "! #2$!+$(3,+/19-1=<1/53168:9;@69C?;95=F89?=C< 3  "! '##.,12/.,2;1/-020=E86978@669@@989=; ;    "%"#&$($*././211671:?49A5=79BE>8;7:=::C: :  "!%%%.%--$,)'+)+36/;825>43AG::4<>DF=   # %#"!,&%,--0'&41.>:02.7<8:AA<:5=@69>;88@;D6   $' -*'1'05-*13273/67:86<55EA9<@337:?@@?D;;:;::9<;?=?96;88E7   %!"&!$""%2./..2*,.129>;36786C@;9< ;EE9;;><:?E9  ##&$&%&")2(%05/3/=3,11-AH:8:<=CC>@B=5==HGF=    !"$(+!%%+54,.+*432<3381@;:?F8;A5@LD7AD=8AE:8CE6B B  "! &+%' (--&8/-.,*/959969575>>C?@?7>;?I=79=7AF;A>  !# *" $)($"!*,($38257650207;<;3B79?=O:98;G?=B::<7>I?@R&>   # "%'#&##)*.3)*+;3.74?=6/308J<:AA>@;==?9;CE><;=C@GG9;J'C   !%",&$..65'62/0)3-/;;@5976A:@==7B@=?B?=?B>>97A&H   %"$&#%' ')2)'2212/7),3AC86647>@F;><>?9;=AB??B86A9C3  ))'!!&/1&%)%%)/367946?447:@9:5@F9>:DF?>@H;> =A@<@>@=?H2D  $+%%%-2356-&+)8;1;5>@=91;C8>?>?8=?G=DFC<>;<>8:=A>8=@@8=L<<=9?@:=@A<;BA7:=BDCA>A>98M?G   && '% (7.&1+'0-.51-04588;68;7CA:CA>9>DJIF?C>GA><@:9>>I:   /! &%$(%%/)(--1325234565=49A5;A9?H><@>DCF@A>?@GEDL?:F@A?B=@=;H8K     !'$&/+1&(+,84:.13+62665<8:9@@<F@;F;8@GAG<6F;N    !" % %"%'$)("'&)054--<79;A?7>>7>=9A9=@FIC;DI;A>=;?@A=?G?>CHD@=>@>C@>=?D>=B?<:AAEFEE M   $") 4%+9*&/,-,81,:0654A687;>9?CBEFA>EF>=B<@H9?GE:8ADD=@A8A?EB9:MBO   '#&-)+#./#'()2030,15=4:E89>=?H=:8DF@>HID>B>DB;@@@:FIBC@F@;<?>EIABCA=??D?:E:7;<@::>N    !& &$#("#31**,+)+=;9109<>;79C=@ACEG>BAA;AG=CFMB?CHD3CKAB=DJ>AC8?<>DN    $ "#&! +*)*1+")1/><1:.;98JD5:?:??G=;>BF@?A;AJEDHBGA=?>;=F@:@;?B?B<@IH>@B?FR  #" %0&&#%. ,3/66%3>/09>B><;;JQB9@BGIC?@@>CKC@:>C>ECFA9=B;9BI9 $#$! '(2!$)0F5%4-3@;90-:==77>=@BAJDACDIA?B>==BFD=AD8>@AHI?@AEBAF>7=A?CD>A?CKCNFCC@ADCB@??CE>BA=>I?=B@BA@BG>9>CD9<CBDBCCFEC>DAC>BB;8@KF EDDID>;=:F#K !!)' %+1/&-#2/+25,7D20868BLED@=DACDDIBAGF>@@?@@BBDA=EC?E?>?A?C?=?A?IG=;@>9E%Z    !  -#)6%)3(;2,;;,394;62HC=EB>C>@F==>BB<=DLE<=C?9>A=A?<9E+. !" !#%#)$54+24./4<621/8<>=BCC:?CA>LF@HG@GG@KFAE?E=GE==?<B@D:E,S ')#"!))%+'1.16.2(0@5<43B>7JG7@BBFA@FC@IDE@AG=DF@KH<;B??>J@9C@9BGB@=M5Z !#!%0%+)'*(3<'"09164242>>09F99FIF@DBGB?EKFGD?EJDD@CPL>EHF?DI@:;HH@F8DDC@@EBEE.G ##%%&,&/0-<,77341,,1?A?I9GLDBJJCDB@CCKBB@HOG?GHHAHDB>EIBD?LLAD@F>EF;?CA@?A?C?E=\   )% &(%# &'.B2-658/4/2;;<>;DD;KF;DAENG=MFCGEACKFMLDFQH;IF=??@@DPKD@;=LA;OH;?F>C=AE>9D@a    !$&&,%,+),-+(84+10IG99AGB>C>=EGDJC@@ICBOJMK=BEBIKGC?>JB>AA?EOKINDFK;BE?=FC>=DEDCEC=?JE;b    #"%%%)( +4.,,173.>CE7;H<56GG;DKA@GGIJFJSEFGD@E??KAEHDB?A@ME@F>HK9@I:CG9AJCE8CKD;EIB=BD:*  '#"+!'"!&,4(03+4@52/47LA?@RJ>H@CH@?GDIA>?;GGDA E>d     )*&.*0(,2/2-22.:@1B;4=@ILHDCCB@GEA@IGLII>ECHB;>>@E9;?E?c    #$%.5*)*6+3>701DA51@F19;EULF=HD?HKK@DFAADBCCHC@??FJ=DK>CGECIG:F@=>>ABEF?HIJ=@D?<;@K?EDd  %%! #$(%$,5/+.8./9B3.969@B9B@AIFDCBABIGEJNSC@JB>EILNBRG?ACGHC@FJC@A>;AHC=?EFEEIB=;A>:>ID>G@  #"(C$&.$!,1/(.;'-8@931@@=BNF?EDEBDKEDPN?JMNMGKGMHHIFLLFDFGBFOJH@CAF?DHFNGDE?C?ABE G>@A@C?BEACDG@BC:IJ@=D6  "!+''(,1&):/*431/2B68I<;>@F?-HIMK@EDGDLJFMJDERECMDECEIKDBHG>E@CIGBEFD@A@>AGCFIE?>>?FCC=B?FE8BKf  "'! (#&24/..34.2=01;9?H<9KGDG>HN>BHDCFIGG@MMGBFPKFIKCEJBGEJJABIGCFDFCFD@BD@DFD?CG??B=DIJ<=HA@EJUP   ++#%%')#).3-0C42<3:;><@B@GKFJCEKBAFJDIGFIJINDDGEKHJKJIE?KEBMCGPJ@IEEIFQK@?GM?=B@DIA=LJ=?>@IH@J!d  !# + /0&$.)(00<146=::?>87;ADENHCHJENWGHCBINGHCNIMJBBDLG>FGKE>KIGGE?JTJIGC@CHRF<@BCD??DIC=HH=GF:>BAKm   "+)!+#,1&+4-./1D55<7:37EDFCCLJHNTFGFDKKLCAHIFGDCBHNDCFBDKF?DHKBHLODCGFAEIDIC?FII=AG=@D?@IMO'    $S#$('!$(*/($,,(.45:->DC<>B?HAEGBGOLOD>GKGGPMFF\JAKRFHCFNIMEAGFBKEBPFERGGBDDBLBDKIEF=?BBCID=>>==>FE>BA9N)f    !!%&*(+($&4.',134A476:=B>::AJHGAEHKGLFBNOIIQEFGICDFMLDGMHLNEACIBBJONHIHEJEFDAAE?JH@HCBLHBNI@CC?CD@:73LD:F?JICTGEDJKKLKONMDOLDFINOKDNJCJCDGDGB@CONDH?BCFCIAFJFF>?=HCIN>BAEGCCFGAEB<=FJ+o   %#$ )&),1(%:9-)-645AD;3GA8ELH@CFCEOPJPSNRPGLNLMPDMOAGHAGKAEHFAGC?FIEGCAGH@GMMDG@EOO?=MD:?>EM@HC=;CKAA@EL@EL?CF=>>DG=CBF5'  %-2#-+#22*,I7:515?>>A@BDC=DC@@GCEHC=HC?G?ALCBHKD=IH7<    "$!$##!"$'(/,3=.69/9;3@A9A>:@A@IHJKQ5HOQFTTIPLHMLMAFTMEDLGDKRKJKGILACQLGCHHELLDGNJADBDMFBEC@IPJHEEJ?AFFKHHFCGKHOUPGLOLNKKQTMGPOMPJIJPFNPGHELLHDQSDKONLIGDEMEEPG?GPECF@EMGDADFDGEFE=BDEEO>w  ,./5,140-1,/3A>:>;=;;>?@?FB>OCy   !$,253/').7=4-4@;7>>3?LFADFCGIHEKMUZQHRPNKOLIHJHEFKKDNPEDMLHJLJLJLKJLGKCCFEGKHHDHHIDBKDAKAACDE>DLACKF?=IG@DF;ICx  ""$"#'$$!-4'+*,8?:717<<7=?>?KG:?JJEHFHJKQVTJHRQQTQHKKSIDXPCJONBQLOLHHIAENFGIGCEIGE@AROFHJMK@IHFCABGEDIE@>BEICHDBI>@OB N    +$#!(/#%5/)0.9<935CA9;B;BEBBENMHFGPMIGPSMNKQJQWQGJQRFQ&FGPUMDLPGGPTNFCCJUKDHDFDBDMJ?EKGNJDAGK?FGCDHH?>DB@CFDEF=>GJv     #!)#),.-'.*:;-99;858><>ED@APLEMHLSLOUTKRVJMSMJKRSSTNGNVJKKPMKOEPIHKQLLIGKHJUNFHMJHQGCFGECJK=?NRLHOCCKCBDEC@BCECDBD>Cx #$ !%*+))'+).;4,8>2@;1@?5;K9=?>AKOAHQHPHNPMKOMNNLGT[HKNGRPJJIKMHFJGCHLJJGENPJDJOJCLKBBGKABNMHDEEFEVN?EBECEDBDCING=BME=E=AN>     '+""#*,))4//104-3EG99>;BF9BGAKHTCGUMPLKPPIN5ROEJKHPMONJOIIEOUMHGNOQMONELJCGQRLMBFOICCLOGENNEKJGLGFB>DNDKC@CJLA?IFHF@EL` !$&+$$*!()/&+1%>=624=CB;9AB;LMHI>ERADUTJLRJJTMNOQRQLJNHKPLOJIGQNN[OPMJRSMGLJHGHJGHCOOEHGKG=?OMFHJCGC?IDINIBDCGGF?>@BMKKH"   " %*,$&('/+.1A6;>3<DNG?=K-  $,&$+%-61/8255,8C>D?<>=DKHGFUGHQOOUPHQYXSKTRJ[SLKX[HSUSOMU^PHKOKGROCLQMJEOKHGIOINXZMCNRFLNGH@DMKIMWMKPHI@HFBFFDMFMJLFDUD=HEJ$ k#'$$.'!/8(29@62749;7<>CDCOMEGIOPLHMJNVYRULVVQTRONNLOOPOLTTOHOKLUFJMNUPMGNSLGSONEQLDLXO?FMMEMJFJDOIHIIJCFBDEAACBDAAFOHEF@DEC?IM'   ". %,%-1)+3:7749;==:D88APCGUNPSTFKTQIKPSZXONJVTKTNQOKOOLMIQRMHLLQPLPLLNFIJJGMLKHGDINKFCGFHREFOPMLFGMICGFEECFGJHFEFJGB?@?@G@HJF@Q-   !!$(# (5/)66/870/=E??E9:IB?KDPQGKNIX\PTNKQRTSSKOUNLQLLNXTJPOSWLJIUZOLNIKQLQTONHKJNEMKCK[LBTJFIGJQJEBLVJFKCILLCBDPEBOIDHEBGBECJDJCJ4 # %$"#$'20)55122;46C9CA>BNN@IMNYJGJHRQTWKNTRQUPOMKTOMSUMWWJVPJPPJHVTSSLLMWYPQPQIJLGIVPGIMNEOQDPLOJBHCJKAGFGJNLCEFJEBOH>KSLQI?DIC>AJ5 '   ",%#"+."(51.172@A??=AFDERQEMQVTQMKQOTMRZTRRVMRTQPMVOSRPSRSIPLUQKQRMOOUYMJHNOFLKLFKUJLNDDSMKJJIJ LIELHCCLLKMKJCGIIJHCEGMRLEBFNCBT3M (+%&*-/'.,1.12756A>;@@IFCGCMSFLONSRQUQRKTZROORORVVNNPKUVLPVNKU.SRRPGITWRMJGJKJJIPLMNHCKOENMIJDBEGJMHDKIIDJJEHLJFIHGKNNH?FGJIDCQJ?Q6  $* $+))1.04),=C>FOEHCGZLMTMMNRNSQSNR\SJ\VUYLSTIL[OMOJSMNZRSQIQLQTPPHLRSMGDKUFJRGHJNOKNHOTGGHCNONODFLHJHAFEMHBGCEIGD=EGFOFHEBT@ = #"##&&(')*366.29;89AC=CFCOA   $"-($**+4/2784,==1CEHDIBDGHDELINQKYYXRQXLNZXPUXQNSNRXNKUXOMONULPQNLMTTQ[RKIJQQLNTWRENQRSMSNNKDDPIMWQFMPNLJEHICJLJOJLO>KJ@IFBG?DLIEGDBDIHLH    $(%+,3- 2D08<34;>HBB:IMAFRGCXUOUHMWWIQWVZXXYUWX\TQ`XY\^VUYOSLQVVPOMXSMOVQO[VKTTLTLPGTQMOPQGQRMJGHTQLOVY@MYQIELKHHIOQJMFPDBLOOKHKIJCCEALMBBEG -*!'+'1--27563H?9BE<=IGOHGOLGKWVURFVaWMPX[aTNXWULV\XRWHX`WXSOPSMP[YWMRYLMQVVSSVZYYMVTLSKHITTOPHFQMDOLKFTOLQMJKNMJOPOJLRPGKEIOGIJQJDOLMHDMFLMCENN    %!(*#'..2p4:25;5:8HJDBIF=DMHVZNNXaSRY][Y_W`\RXSOWMZcTUP]SSTQUSUS^WR\UQUUXQOYVRPUYWJKIPWQFNTNLKILRONOJHKKHGLUPFJIIMLHIDPPDBCFHDEIGBFH@ENEFBAHGIHI   &$%&!,-43+273244179>@BC?IJPLJLSSLQWVUYPRa[PRZ]URYX_^ZVUVYQO]V]SPVVTRW[PXSOUMKOMNKOQKJRKNVOIULJLHLKNOMQNPJHRIJIMOLGDHMDCFMIJHHGBCCKHDJFBOL?BGFG@IHJ   }#$'%!0.-.*4;8348>B8?>>JPMGPTLRWKTVPWTUYT[ZP[XUW\c[U[RMO\XQRW[VUZQTVUSQTPQWOOUQMVWRQNKUMONHXRLNLRVNNMNQVVNJTOGELKJLJMLHIRKCMKECELHHIGKFJQDCGDOEBGEI  '$&"(-&,8-(8:5687?G=@HCCKHLUQSPLNSXWXWXUZWR]^RT[V__UXVUU_UPRY[SX_ZQUSTUWUZQK^VVUPKLKVPJUXJNLJTKQVRJNQLFIOOMFJTRSHFHNUFNPMLLJDFKIJHCDEGCMWK?CLCBLFCIS' #%'&%(',/#+3)0812:?:=FJBCDLKROPRTQR`[YUPRX\]QT``_TV]\W\ZUWWSSPUUT]Z^][ZVX\QUUWKW\UNSRNKOOWTPTLQPNJLZRJKHKOJNMIMIMITPFHRONKLKHNPNMAJHABIKBCHHNRHIEHCDABHR' !+-8+'*0524-:<37::96:I?DBDHOFL^SKKT[_Z`SRXZcaZ[_RVTVY`YZ\PZ[RWCOMQW\^XX]RPW\WKP]PNMMQUNPJO^QQVKNOFYSEMRPJMOMDMLEMPNMEGMIIOEAKNFFLLEIKGHKEQK>GKHFGHC>BCEY+  '""'-G(-2))5@8,97>?>@JIKIITGFLTPV]QVSPT[dc^XcfVQZWXZZVX\YUZS]\VWTMSVTQQSUMTU4[NNVXPOPPRZLRWRUJNQQRJHWQRNKNOLMXQPPHRFJNHEGEKQNJEBJNEJKIGGIELNAIFCEHHJAFW*    %$.,12,%+01133/;C?ABF@CCEIHUQZVWOV_XX\V[[ceXVWZ]]YW\WVXR\cUX_Z\YTYRR^QR^NNYWWPZ`NSQ[WRONNYQHLULTQMTTSMJPRLIMMPKOKTTPQGTMHWMPOKRTGDLMFBHHIFDESTGFLA?FPM@CQ2)%!!$2(&0.,.753  '!'1"#,1)53,:;9  #"! "##+82622/"A414/AH<87JLHKIKLPMK\[TJMTUaaY]W^W][YZ[_a_`haQV^]WfZUaZXSTTc_XVV\WSZWP`VPWMLPS`PMXRLWZNQNKVRMRPRPOVOORLS\VWLFIIHERRJMPGLLDNNGKKLJPOEMPEERLEMGLIGSHEG9  (.6)*244,.-0A89I?>B<=IOA=LKCBISWbWSYRR[f`ZWXY\c^_h[da`^ZYW\VTW\X`X[aR[TQUYVQWSOUXMS]VQPMSVW[NLMP^RJ[_QLQSMQYSLQRQNMPWSSMGGHIMOHVOLLOQPZPIKHUKEGNNHKNQDMHDGEFJB@H?  (!$),$*--3*4;5;1=3BKE;DNNHDLMORVMOVNSZ\[gg[`a[\b]^fd_Za`Z[^_ZU^_XY\^[\X[Y^WZ_XYVZZTVUTZRP[aSQTSORUXZVPNPTUUWS[UJPOUZXNKQRMJPLURIIKOQQHEJRIDJJCOMHLSLHJMKOHISWO@KO?GMKLBNP    $%(4)-.)../+4;946BGDCGJLNVINWSJOVZVP^a_m`W[WX^_`ae\\d[WZ^^XXa_cVX[VZWb[PYaTQVRWYVRVWY][WUSRPRUVRRZ]VQRYSV[^SQVSLNQVUNMVTRMTUKLOPOPRNOKOJINNKFNHFOJBBNRKNHNIMKGEDKQCOMMG"  ")'%&,04-+)530>D@85GJGAGMUOPTSLGMOQU]amfcZViaX\]icY_\[f`_d`]]ab[^^[ZS[YZe[Y\WPU\TTYX[]YY]YPRVVZV^bSPUURQOQNYYVLTZKLMMPZPJNMOLTLOSNUPOIR`PLMQKJLOTONJEHFKKGOQFDLJGUFEGEFJO'  %( '-/67,+;56@<:A=DFAFPIHFNRKLPVRZZ\Y[`^g`aa`aal__bY_]Y_ece`Z][Z]d`W^ZYXYa]UUWWSVVUdcWVTWXPX^VRRYYQRMRORUUSPPSTXRNTWVOQQJOPONJJRKONLKMRYJLMGLLNIRPGLQNCEIKJPPIEDFRGDJ@GLL&  #"!!(14+5821800@AE<@UMESFPTMUZPUY[gg\Za_bd^ch`Ybf]df^_egb[Zghabe][Xb]\[Y]Z`^^\]``[T[Y\WQS[YQWTRSXZPVXXSVTSSQQRUQSNMZNJORRNJRTOUOVMVPLVMJMJKIEFGPMHRNIMIGLJJQEDAJTMCBQKIGAFN-  !#(*%-,+4+;D/8B99NGAELDJIUGNXP[VL_dX^^\\g^bbeic^hiabW_ic``cbf_\\deadSa^X`Ve\[Y[\iZT\TVYcXPPUcZNX]POX\TXYOOVQPMQWJSZNLMTWXTQPQ[UVQKPOKQQNWSMHKSSJKGEHJGENKEGGEHOFHGNIHFHKCBPHG *  &)"1,)*2+)42:<8:?;CJFNJC1GOQSOPYWVZ]ae\^dce``hgljcfcjn_\e`^[_al^XbiYY\Z_]a^ZJTS^`YY[QTUTa\[RXdYNV[NUR]^NTVQRWTPUOO\QWYLZRQ[QHQURTNHKOLYNQXKLRIPYYSEKOEFJLXJGJDNNLSGJIGMJGEJNMQ*/ !#*3$,6-/.-/26>>9C@?CELQIUPLL^_S[Xu]h\Xinj]eifefjeXcece\^benhb[a`^bfc`d_`eX_WVV\dZQ^dW`bSS^bWZQSQUS[fT]\VNPR[[UYQQYVRQW\RQMVXRMGMRNMRSGMZIMSJONIKMJOOEJOKGSLGJORDKGCFKAALKFIBKT(.  !$&%+**'.4/@:.@A9KN5CNNVRTM\bYWWahfgcXhi__g\ltd^c^e`__ejaY`a^]`]bZ]YW]Y_^[XZf]Y\]b_]a^VQVac]S]bYX[VYTUV[VX\^]_VXTJWVLLMTVSTPPS^XZQMJUXTPFNYHKVKYXHMJMORFH RWMGSMEJDIRMHGILKJR05  % $&,&+<102.3=7AA48ERF@KJFILLZWMPV\\]UTejgZakf^_d_hc`d_nhaZahkg]fe^`^b``]b`W`c_VYb]Z`a^`YS\^XWY\]g^TVRVZTSZ[TKY_SPU`]TMRVMONPQJPVOMUJQSHIMWRNRKMQSMINLHDKRHKPN[SPKOKJLLMNRECMFINKCBM41  ))"%&$*=8/043;A?=F;?FLJNNKIFMSST_`^We^Zkjfi^^acbq`Zgge``ffbjbjfenb__Zbga`ag\Zbf]]^[dU\b]^S[c_UVWUb`QTRZ^TMY]ROSRVTNWSY]JPPQQVQOOTVTQLNQPQMJLPLSSKWTJHIINRNMOQQOIPMGMSNFTNGLKJNLO@ET40  % &/*&01/B>148;<7CDKKLWVDUUGY\XY[^i__grj^iffjaboracsb^g_ejfga`^efY[Wah\]Y_adfZ^a`[aeZ\fV_WV[WRV^]QS_`SX]_XWYPS[XZWQMVXWVPWOPTW\MOWXUUNPQLRPJLMPQJFRVRVTLKQMPNNMTOIAMMENOIMMKLHDAADT91  "&*$+!(/-0/:6.96:DHAMQ7  " ,/0-5.)4:B?5@ILAB>JEGYZUTPS\]gd ZWdkclukffdokmjnagumcblmfjh\fahmd` ad`]h^]aZW\a3Yahc]W[]b[deZZXZ[ZSZb[fZPU\cVYZ\\VZRYXRTUSQVOINUYUURNOUORJJLNSUOKPMIFKQNNLPMGOHJTILPHGDAEKFGIH@CIR?3  (*,-.7219;E:8CFEBGNDFLQYV]\U\ZYbc]bebekmfefdeoicdimmjilg`hkif[`kbc^_jh_`\]fl_\cde]X]d\]_T[`Q\g\^_X\^ZcY[bQVWZ_YWcaWV[S_\WRQRQSPPRWYRQQPURPVQTORTPJTSHPWTIO\IPPTTJPOKCJLSQILIBDEHCF;3  %#!&.&$*6<5B58=<8?IJOGXPLLKEWY_aXc]]aZ]_bbaokdjkflmdohagmfacc^`egfddc^_kfagb\b_oh\ee[ecXYW_gaZYZb^[Y[U^\Z`[ZTXQR^RMV[`_V^ZSTXOVa[TSWVVNRTOSYNOSMJV[KKLQOFSRNUSOOQNPRNJNRJKKSJCNSH>AIIC@B;8KK :  $$ .,2,(71-5=:6DK ? #*#"+)+$383-6@9>DDGLKGDHLTNRWZ[[gcfg_kda8snjprjlmlpfcpnnk`ghijb_fhggaddf`ahid\`e^a^d]_d]beb_hb_a_^aC[\]aaS^\a]Y[TbeRORVURZVYZYYOW`]SRROPXYONUROTNP`QMPRQRLJNXPMJJGNNMOOJGEJLKGQPNK?= 7 ' ),+),,158;458;CHMHIGFVTJPZUY\]efhd`hga jqliigordooakp/`nrogbke^cajj``b``ebac_cXdb`ffb`a^ff\XUb`d]UXWb`\<`aVUYSU][SWVW]WXUQZXZY[\STRTRRUJW]YPQPRPMQLPVRPPONPPOQJOPHJLDKTI:EI>LNECJPJ?ELBHDFA9?B;8GB<;37:?<:    )5(%+-245@?3<==AEHKFNQRQYZW]gR]hbdje[_eqotqjumrnjjslgwoelqimdfnnlbgmcel_ckfdlrgigcdZ`]dbbfbajh^`c]a`\^f]^\Z_bUVWa`RTb[[UYWPU[X`TLX[O\cSQTaVOTPNOSUTYW]PTWMPU[MGRPHOTHMHISWF?HREAF@?F>:13D>78B6849>  ##(*%04(/2-:<=A6MOFIQKROSSVZV[\Vjh^d^Ycjiusmoikrqelihqsvyjdkikeenmga^fnnh^gcalhc`Ykf[cfjjge`]dj_\^ah`VY\h^^e_eU[WYXU[\ZVZ`TV`WW\SR\UNOUSYSXUOKX]PW[QYVTXUYUNLUSRXHHJOOMASJEJIIEJFCD<89@6374>?6=48B> -("-+#'874;92485;LOGJTSPNLSRMQYWUnuTW``dtvljon]iszzgfkkghkroqqijilebnjbmfmoeb]^]ch]efa`eY_c_a\eafb]gd`ZZ^\VYcb\ciXZaaWWc\[\YSWXS\QUXXYZVUONQZRVR^^SQRUVQNPPYTOTMJSPJIHMPHGME>K=?@7 ),'*(,<66>74M?1=KD@GFRTBU\^[\W_]`dhgwskggshiwmr|wmnhjvqcsxjoqcejjlpg`hi]a`^aa_ffdflm]i`]a[W`ecib^ajabYWX[dXc\^aT_`Y\c\d]UXV\[XPR[PSbWW\UXRNSWUSQSO[QLRSUQNQWMSTNILKOHMFCGIIHGCEB8FD989<6..6<D  /#(,)50--1C7=6=B@BK?GOHOTRWWeckc\fb[rho|xihjoqmnlipykflngcnxtsidhdjhopnjbfblihngodee[ac_kfie\k[`ea_[]Zc^W\heZe_\US^]VZZWaXUR]g]X]X[_TZYRXU[_^SWXRQWVTXVMUTXNOIINPHMRMJJRNCBCH?@GJI?>@B@>CG=-4=;844*.8BD  *!#!.*!1/28;D::FB?GEWKPPOQYaXT[ZYg_aenh]^ryouittq.tomlmosxuifqiljoqmldfssqn_ikjggbfjabehi\dg`jcfnaC`ebXbY_a`a[_b]`_R`^[XY\Sc^PZXQNZ[U[X\UQSU[V`[QYVYYTTZUNRVTMJIKRWIMVJIJMPF=?AF:;G;>5JT?HHDOZ\UU\bWZY_efkgjpuqlkruxrvutawttmhmpusplhmpdnulrooqqlaeff^hghojkcfmidgaafklf``b[hb\_Yecg^]ZTWXbc_ZcaYY[X^\WR[\UZTSZ[SSXVUUOYQVWPTYTKT[KJOSQILKJUTKGK>>KP24746.//94/1*601D  '%"()0)015A@9:>A>KJHOQKTPP[`X`fda`bdkcnjhxtvpljrwx'nqjtqgurjntlklsjgqklm`botmk`gffd^gfgnmgbe`"Zgb`bcbdb`eaZ]]VabYZ^]W_ZUXY][`aZ\SV,RU^VQYRbYT[NUWVRWPMQSNKTWMHCOW@BFDKI=>9.6:67-.:531+/0,,F % !$)0'0597AD@DHKNISOS[[^\`k_Ydggllntpqprv|vgswowqruotnmkiuvnotomrpmllkdjrtnfeioidbgjgigj`e]_nafc[gkd[\fcj`Z_[eh`\Y`]W[\U_]_kYXaY]T[^_\VTV\VRT[PR^WO[YQRNJMYJGNGPHD@ABKJD:BI764=<>?=9487-/963-16.<6&/0#)>   &!'&.<*/@9@;;3;@EBMKNFJUK_RVVZb]bjcqndrxmjmv}pxxqny}vo|vA=8AEFRAERXMQXXge`cg`ekklfsynw|~xo|zutzquxuytqAuqjrwnpurppysqruljnoofmpnkromcigeeijegghaeg\gc`k]Xfg`d_c`ZefYbiaXX\:a_Y`Z[bZW^^WR^\T\U]TLUUWX\THJTHNOBYQFLV?BMAFJA@=?@:493;C4-:<95,*43((+/)%+#"%!%'% #K  #!'%*,.6:.@A8<@CGFSJGMMK]Ygkbgdgdimnrnguuly{|{trwyqn}wuywsxwlrvvtsrmkoonlikqkhnrjedevlhnoeheljgfbhjf^fhhohgbZia_cfcYaj`bi_[`XWfXU\[c_]]_a[TX]_WXT[aUYYZXRMFPRNOMKRMKPH:B509:2:1).-/0$*60)*0#1/%'$"'$$%H   %#(&$!)07;@B>=>E95=:;841.50-/,'%-)+&&*2%!*)%"6  !+"#+('2477FC1?IJBFTPJIW]VSbjRWhhltomtzw|xz||wyztqwrqyvtvzr{ptuiywyvmomnnmjjheorulhiikedhf`iruk[kkeim_adlj\^laZab^aa[U`aZ^`W][ea[`\]]Z[[TX[PQ[^V[PMUVDLbHENLIJE@HKIL>A53@B6><5=@6/542D6,-69(*%//%,'"$(""$M  !)!,#%+/165:<4<?BC=88=7B>B211;;8*0+/.($,/'" $''%!! "%   @'**3++5.1@6D>6NOIHJKVQTRT\a]`qajonmeqsmt{{x~sz}yzw}tzoq~}st>wuwz{toyxunqruxw{nntidijjssqwoebggfhfipjcdcdbjecdjecdchffe\`Y\[\=[g[Wd`RYcXYZ\WRY]VNRYRKKQ^J@GCNBAE?GB@G77?43DB3/<63118824''+)/%$1(4''$ "!R  '*6416B;47=B>@@K]VNVPY\V^\_[dnrjgixvwwlz}rwyu||y{svww~xss}}wtrrkytop{wqtuwmkpulgmosdrrenktkjnaqpeehn`ejfcakg_if`cde[d`gdT]c_[^iZ\\a[VVMYX^fWMMYVSVMJNOFDDHB9=HGDCC16G6155618412509//&)0!)"$((#(&!#!M "!'$+-.712AFAFC@IPKESQI[WZ[e_X_kkeifpozqy~{}y~xv}|xuuv~vyxu"yztm{ql|sl{nvuqirmnugrorhlpripgmockYebtjdlghiba^ahakd]el]dl^^c\`g[ZaZ[]`\ZUZYPU^QSPS^YPQF>OG=EGE=GHGE]RL[_ZXZUaibovsmqutms~z~x|}p{uyxw~uy{pztuqtqqzuopoopnnpmqnnmnllohdenheghphcmpqhaajm]ei]gg]]dbdacdb]_ic`\_VWc[RRYWXLPWWLOMNTK>BIKC:=C@J@8E=<@<486/??2184.)'(-*0)'&!*#!&!R 0$$(%',9517>;:A@OLEFQXVPbWZbcdiplec|sks{}v{{zv}y{}x{}z}yws|vnqwx{ptstuuyujpknjlkrujtpjjstmhmkChlqgfagosoaflf_d`mhaa]kg[gcb]_g\W]g[^a[\XYOS\VJVQJL>GPD>=LI@<9C?C>AH9/>925410*00"'2+''*-3%#"" "$ W "&&#.(-,.=9?KCLIFLKVSJTe\^ajh]^qpiniwwp{z}q~x{vx{}}{~|~tt{skuxozysty{uvpoknmsrnptlonegnwrlslifdjndgqk`dc_gpkffje\^me`f\]_fa]Z[Z\g^VVXRS[RQSbUIHDCKO@?LHH>CIC<;55=<;2:54930)-1,)/((-&&! '*"+#&  V  "#,,,*3-FHA;:4B?42386423-)*%/0+(!%!")("+ S  %!(+076288DA?@BJ@FXSRWOc^P`]bgjfqwuzqr~|~vx||~zyy~{x~uxxw~xvy~}{zs{vprpstrru|rgloqtnokkgiomkfksmlpflehfdcieaja^ahhkqdZb^]\XbVa]Vb^PZ_VRT]XPPDIZTMMTG=KI?F?CH<9ABGDSPU\VPX[Xa\[cpkehogozr}xyx{}|~~{}y{{xxw|wxsptuxmryphupnqpnqsqhhgktnjmmcdmojfipqiafjegiea_cbee_if^Y^dXX^V[YVTXPS[\MEKCHPJGMLA:?A7:AED647A764-147*+.+:0++-,*("#%!$&    W ! %!'+,349A9<<@BCCLXUNX_][Yicadklspvtxyy|{~}yz{x|x{yvu|xrs{xwutsotzmlrqvtnmohkrvolqdehlkbqujgmfqmnoiflm`bkcdj`ckbcYbb[YeQRdWITVTMJJFIRHGIJCBBA9B?71+2736>9..2+')+3)(%)1 "$    S  #$#"%/823??;?JGWOELPXWcb_^^eei`huuqxwsuv}|s}{s~yy}q{uz|vvzpnwtuzloqnwuwqkpouysjmcknksntpejegmsrda_cd^hiedl^`h]``V^VZb`XXWVORYHLGKQNAGMIED?::7??=>754.52107:4&'+0,!+'! $""%##     Z  !+( '0/,2<;5?CBNVEQUQP[\X`j^^bqpunrtpz|||{|}u}|z}}qu{ts{~ytp~vxyltusplwutqnuqoggpoohlmfelehpffheeje\`ibkiZ`a_^edS_RUV]UKR\UGOONNQJ=GKI@@=7;<2:A816?84514,(03++2)&($""%'!(     T #!%0''-6521@DBH>9PIAYcTUXTafcZ\eqvsfpqvv{~}~}~uuxzvwwxwq{|suwutpkjjtljppjgdmwposoph^givj_ejfnl`_ejcfacbYaa^^[ZMRWLNLILQNQLIHCEKEB@<>8B<:D:30;==/,411/)!((#-3$!-&#&      +  0*,4*120=@5:IA?DQWWK_WM]iahhgmpsnph|t}v}~yz~y}|w|~yxy}s{wkowmk{pwsmsurloqsontvwuvodfgrvmulabnpgbaefb^h]\[b_U[[SPQPNOKNIOKFGGED63?<7.79+-01+))+.'&&($"* %!!    `  $*'82:A66>OH=EMOWQM\`^\gg[neoy{op{{u~uz~|t{x{x{zwsomokq|pplrpkpjotovtunsnegpmkmmjii`ejlaab\bc]Y_WZ[QMPNZYOFLNOMIBGJGJ4ED=;49:6594713605*%(**(%(,%*$"'"   ^ !##-5*,42DL=9GJTQP]W\cT`khbfdaouwtl{zw}~~}|}}|zW|~z{uyprw{vtuku|vlgrvporspvpldihjjeijl`]gmb^fbaX_c^_WX[Q]PPRGMPRJ=AI@>E==F916=11=4421/0.*%!',2+&(%      _  $ (871158<548BE59?3/048&/, 0-#&(%)*)$$       `  *#,)/6/875E>@D=<894:=?2486,1-40(02!"(0)#*&!    ^  "'!/015685=FMBLSHJ^VYeaaXbjdhifls||sy||y}yr{}{|ywtzzuux{||}upuqplmqxuxrmrtsnrkpdfmhqb^kjeW`adabZ_MOaXQHNJDGIGETE?RA:C?35H4044129'++ (4(!'(1'  b   #(/134@:9E=HE?7985;932*,53-+.1'&2+&)%("! "       c  !2.0/.*/772KHHRKMPMZ^\cabipnkqyyy||||L~~|wxzwuurryvvxjw~rfkunsqkmqe_ihbYSbbUXWUSSZPSSRPIEKHGD=KA;AGCB;6968D73//*).0-*'##)$%-      d "%((/-7=5A<=KLOKWQ]UZ[Z_^`pikrs~y|yt~x~~}x~}z{y{~}|yzurysrp{volmrnbhnjkggfccaU[]T\X[WPSPEJXJK=BQ?FGLDAFG>:98><:;70(0,%-+,,',-"&&%$$#!   e  *)2./2-9DDJEDGJXSV\e^_[S`ejsfltxwrx~|~zyx}}w}wu~|}}|wuzy|ok{yplkkpm^qr^]jgjf[[ZWZ[aVRKSQHKPJEPELO66;8342-+()/.*)&##("#)""   f  %#(.0,378:=JBFMMRQYXb_Z\bccormvs{z|~}|v|zwu{wz|{{tztu{vsrvityfim_cd\Zn\TcTQXX`[RFRJETN>BMGIA8>LLA:93.:538--675-/#+%!&#*!#"!     g $#&1*,76;7AF=?OUSWSbf\\jgmspwq|znv}~|||4|yu}u|z~zqssxxju~roklvtjog]cdng[]SPe\OVUVZRWPKPNGBGD::@?:HH8>G041E<8>41--4&)2#%-!$#&))(     a#'##,'+9>4;QOOIGLKZeYZkkdbklxq}uzx~{~u|p|~~uuvqsppyrvqmf``hh`Ybk`YZ[SYPUVKKNECKNB>>BD>928:87A<6:841011)(0(&)%"%"$ "      5  ''/2(227;>>FJCQY\QYc^]ptlieq{zu|*{{{|ww{}{}{os~qqmqwlfpdcbee[\_\bcWNKVYJMFGEIEGRFC:8A@E?/2?<152101+.4-)++1+!!$#    m  ()&14)6?L>C\HIFQ]_aQO`eehqw{mv{w}{|v~z|yrtxsjnphahrrnkf]U_`\RSbZUMPMJIQOXQEIQOIKA;ED>C5-6:0/1+.,,0")$ #' #   n   ",.3398?HDJHGLYQUXabbgmmekuqqzz}{{~~ixxtyxytskplnkhrkgo\ibZd_g[PU[QUROH??NYQKDI@?A=8@==@<51.,3-+201/!%*&"!'#   n #+%5>?:=CD20@6+/,12.*'1(# )&"$   q$&&-'71-5=9?EHT[PVaee`k^fsiuyry~y||bw~mrtxfhrhndd`Z\_kX[[XRJUUQ]RIORB?C@;=6:G6CD10714+%02-)$%*$)%&  n !")8-(>86?BIHOUSRXc]cji`otypsx}vtv~wmzsmnvghfhsqh`bPTXSTQXeOBOORMMLG;:;D@?CBIPHSHYd]gibgq}ty|}qvw{veoVpj[in`cj_W]ZWP`WJUNGBPNID?I;D>/8D;+.:9/+,2))*&%'&$&   D %-).67@77QQFROQ\\mcaplet{xz0~x{zrvmy}omsXidc[TZa\YUZTPWQUWHB@MG@LCB>9<=>8A>1*(84/,'!*%$!!(*"   R 1./8;86AFDQVSaZdccdar|mu%z{myvxyvcgqvSmig`V`\\TWWKUPKPPCLBJBD<6:><44?2-4+10+'-0"$%((#) "     {  (( *,/5AA58MRHLU[ff[mov|ouzzxvyi{nrvidf`hZia[a\YY\TYSQTIMSWKAAGGE:007<1,:49//57.,'("$&*#    | !'3/23C<;?HMPZWYTXenw~{|pvy~~xwztrkgtrkfc\\_Ke\L]\GQYWROOJSHA@BH>2=5761-536@2-#)+*%#$$     y  !""+*.875A=:DSIC\[YZU]tuvhu||<~wx}wilt~{ehikfdeX WbUNRQS[PABHH>6HI;=9E2332-0/$*.($&"%%"  6 )0/*:84>LLNTUXQagepnrwv~Eòv|}t{zqtqxvumbfc[Zji^XQZUMSHFKNLHIDKB=MD=78995?8;9,',/3%)+%!"#!    J !+&+>=5=MSSV^a`\immuoqz~1stowwvxlppgf\c\W\aWZVHVQOXLKO?AJJGD8@<9;=67&2?0*-(-.*!,-!!    C %+&--.:>:GEDKUSe_V\djptmqxƷ~{{usyvspnuic^c`X[aZf[ENNQR,PRFHC?BEBD<834174.9//)&&*+$(.$!   z !1(%:DE>>PRMYVX`\arv|zuy~ȿ¾tzyvourmmqimf\_VXY^]TIF@A>ELFG=AB:<IQ[XVaghrww~~¾vpzzdq{rk`bkeRW_TQSQUUFEDG1;?319/31,+(+($&!  +x104:A:68<-0.00//)$$&%$!#   )50(296160.6$!*.% )     "$,09@>FOLKO`ldiiyyu}ĽɾŽzwrwsjcjnco`QZS]f]TXNJOHDAB<>BB;8344E?061,&,%)!"$ #!     2+/34FHO\`cduehz}xżƿxy{wv|ztrbbjdd]][Q\^[TROKIGRR?@C@?C587;;2--,%)+'$"'.## "    #&"1=?AGPSSbbait||{~Ǽɻ}wuszxj^hkg[VV`XPVWUJRMLH=B==B=:7G:476,0-(+,,%*$     !*.-9B=EN]_Xae_qy}xqt~uoja[fgbW[]XQKP[REFFRFEC=?49=66?120.(/*&"%"%#!    &35:8>JZRP\`^losz||ɿ¿{vtrrhlrndVYgSbUOYRLLA@LLI>KB9CEC:23<0)'/&.'3""'    -,69EADWVOV]jnwr}}úx{}xorsabirffgVVUZRLTVFBJHFR?9@@:/E8)234-"$(%&%#$!"    "),8:=@GKRVXhnjr|xļ¸z|whwtjndmkdW^eVRZSLXYM@GSECF@E6/32,)/)$3-""$   #$3/.JF=QU'Q^doeltz~Sɻ{}t~mirknqjb]Y_]_[NTPNLJCEMH=:B>@:,27.+1'&&%,!     *&->BJHJQ_Rbqmtvv}¾û|tnk{oiidrkXOW^[VPRTNIMNGFBC78C3)844-%6+#%)"!"  ##08=@EM`W^`]pw}~ýſ³}vrxlgwcikaa[\X\XTFKVIGFJKK;9=<<:.'03-&#-%#$"    &)/<<>KONXb^eqy{}ʻ~x~rrqrplfne`[VWXSRPIHJEGBI>=;5775=2&(+.+&$"   !.09@>LYKU^`d?ryzª<~vovsypmifk`\l\\TKILLHFBDC??FE017-0.+),1%(*!   $4=IH>F5>BB>73*37*%%-$$#"   !-1:<=MMUVaiqxux¾ijyttqrpug\bcX\V_YWRKRND>CK@<@EW_W`rlp~ɼ}|rxljnmoma\bXXR\TNOMIA>??<6>658+,.*.))) !    %473=INXZeysvux~{x}{yjmvehb]dY\cWTSLLNGBIB?@234-2;2+6#%$ !   '*2;>HNO\bjlx®xwtrosd`id_^VV`PVSYSRE>@E=;;2.0,92)(+.$%  $*2:ARWW^cbluǹ~u~xvpbhqcV`ZXbd_ZKCIN@BED7CPLZpinzĶ{v}ltsdnpaY^\PU]KHIGBCDDA?53<6,5)%/(&#(#"    ,9>;CQ\frnr*ǽAtptfecpd[jOT]VQUHG@AM=7HBD9*53-+'* !%!    #3CCQZeiuvPrviotiiW]\TSYTROE@A?CB;@?7/4*-,"+ %!    &/AEPUepwPɿ{vogmh_[ZWYTPPJNF<A2@@4.)2+)'""%   ),:VTPlk{Yżts~xiio`abT]TRNOHHM59A5+32*++'*"    &65@OO^ltUûpxjnzk^c`Z]VLKQMK>CG:29@462*+($#    &*;QM[nrrKƹ|yryuljdWY^S^XEJMDIHF>/1//,(,+#  & ,;PS]rzKż|xojh]_]QOISVNID<<973,0+',1"  ' (>FNSfvFǼ}q|wlphmbUWORNIEABF;>45+*(!#%!   '@INXctHŸº|{}ipmhl\XX\]RUK;?D;7/:7+.0$ #   (BDSgnEŵ}{z{e]_]_TQNQFIEC<63&//-'$ ")BY\ly>tgmqgcZUXOMHC>A<@/--+,&&(! # &.9H\mv;żs|yuibb^Z]PORPKKGA7/,/1+*,     (4HRZqI~zsuteil`XQTGIWPGE;86+()"'  "1CM]p~Iɷ{}ln{s]c`X[MMOFEA;;2,1,0&  &GW[jwB½~smpphfVTTEIO@<975*.+"*#    #;I[luzHƹxyuu~tkn_[[PTRBCLA/643) (!  1HBZuH|w|lhnidc]ZNGMC>=15-,5/%    "<.,-*'   "1;Xpy;ʾ|uyiigXZUPBDA>48>83*-"  $>Mc~=üyynn_eTNWGMBB6/83-+$    $3LZw 4ûv~yungaQZTNLHA9;:0*&##!   )Eck ;ʽú~kpqih^SQNVL8J>0-*0"&   9_e{˶6wvwkg^ZQIOG>=:78+)$&   'Cds} ´.}jvton\ZUJMOHA@98+/#"   %E[iɼ0qxwtkjaZXXMD=CA0//)#!  &4[eȿ1suhpx_cWTKEF?426&('#  (Bbw¹-ssyrnfa[[^ZHFGF@74*+.%   &=[k¾u$qwsfheb]WXURNMIIC95)-)-,  '4Twļtt(mej`bXSOS\HFMNIDC7(0&    +Irǵxo|uol\^PTRKFFE>=>D750)-(  $9dü}pbagd]X"PHPRB?B=?8:65.$(#%( 0 $,PԼzznjb]QUXSQD!CGD>84285*.*,*! 3 /@by{rzu]`_YQNVC=C<640.2-/-"  2 '3Tɺrld_gaQKMNBD<44/0*%$'$#%   )0Gk!Թywka]^XVQEL@6=3/%((#( " 4  /=\ſµtwec^LOD;C7564/$'   5)1FįuejhRHGI740-- !   5 .C_ƹuumeOOKG52.)!!!  5 *<Űys`R]F5,/, $    8"Sܻ~ia\EC891'"" / Dʙ~q`GA@D95/'"   ) ؔdP=>?;=9../&    ;d>$%,572*&*  !$*,,'         " *$%$& )*01-220+58405=<8:<>;=HEBIDFHHIPRNRQTUVTYWX^_]\[^j`acegbhloosnrwsuvtvtu||tyuy||}z{~}~x}wuw}lw}{rvobj_]hb\SSXTSYTVWIPXXNITJNYMNPMJTVXYQ_a`[hkdVcbja[^gmfdm`]\djU]TRFTVPOMRQEOSSSHCLH??C?HB:<=6;.A:1.-461//*23..,5/.62+05)5/0.'%               #               )     &   !   %5   <   $8$ *  C %  F  /     !(< ;     . $!   "  )   , !2     *        6  +,                3    %                                  "                                                                                                                               " *$%$& )*01-220+58405=<8:<>;=HEBIDFHHIPRNRQTUVTYWX^_]\[^j`acegbhloosnrwsuvtvtu||tyuy||}z{~}~x}wuw}lw}{rvobj_]hb\SSXTSYTVWIPXXNITJNYMNPMJTVXYQ_a`[hkdVcbja[^gmfdm`]\djU]TRFTVPOMRQEOSSSHCLH??C?HB:<=6;.A:1.-461//*23..,5/.62+05)5/0.'%               #               )     &   !   %5   <   $8$ *  C %  F  /     !(< ;     . $!   "  )   , !2     *        6  +,                3    %                                  "                                                                                                                               " *$%$& )*01-220+58405=<8:<>;=HEBIDFHHIPRNRQTUVTYWX^_]\[^j`acegbhloosnrwsuvtvtu||tyuy||}z{~}~x}wuw}lw}{rvobj_]hb\SSXTSYTVWIPXXNITJNYMNPMJTVXYQ_a`[hkdVcbja[^gmfdm`]\djU]TRFTVPOMRQEOSSSHCLH??C?HB:<=6;.A:1.-461//*23..,5/.62+05)5/0.'%               #               )     &   !   %5   <   $8$ *  C %  F  /     !(< ;     . $!   "  )   , !2     *        6  +,                3    %                                  "                                                                                                                                $""&$(&*.,0.0648::<>>BDFJHLNPRVVX\\^dfjlnrrtz|~      #"$%&)')+)---//.214163775897889:;;9==<<;;??@=>@?>@?BB?@BABC@B@BC@DB=A>@?@>@?@=A=??;?>@?@>?>??=????>=><>=;==8986976667776743324.-./../,.)(***(&&'& "##                                                    !   !"!   !"#"!!   !"#$#""!   !"#$%$##"!   !"#$%&%$$#"!   !"#$%&'&%%$#"!   !"#$%&'('&&%$#"!   !"#$%&'()('&%$#"!   !"#$%&'()*)('&%$#"!   !"#$%&'()*+*)(('&%$#"!   !"#$%&'()*+,+*))('&%$#"!   !"#$%&'()*+,-,+**)('&%$#"!   !"#$%&'()*+,-.-,++*)('&%$#"!   !!""##$$%%&'()*+,-./.-,,+*)('&%$#"!  !!""##$$%%&&'()*+,-./0/.-,+*)('&%$#"!  !!"##$$%%&&''()*+,-./010/.-,+*)('&%$##""  !!""#$$%%&&''(()*+,-./01210/.-,+*)('&%$#"  !!""##$%%&&''(())**+,-./0123210//.-,+*) (''&&%%$##  !""##$$%&&''(())**++,-./0123432100/.-,+*)('&&%%$# % !!"##$$%%&''(())**++,,--./0123454321100/.-,+ *))(('&&%%$ # !!""##$%%&&'(())**++,,--..//0123456543221100//..--,,++**)(('&&%$ ( !""##$$%&&''())**++,,--..//0012345676543210/. -,,++**)(('&&% ( !!"##$$%%&''(()**++,,--..//00112234567876543210/.- ,++**)(''&% - !""##$%%&&'(())*++,,--..//001122334567898765443210/..--,++*))(''&  / !!"##$$%&&''())**+,,--..//011223456789:9876554433221100//.--,++*))('& - !""#$$%%&''(()**++,--..//01122334456789:;:9876655443321100//.--,+**)(('  4 !!"##$%%&&'(())*++,--..//0112233445566789:;<;:98765432100/..-,,+**)(' 0 !""#$$%&&''())*++,,-..//01122334455667789:;<=<;:9876543322100/..-,,+*))(  7 !!"##$%%&''(()**+,,--.//0012233445566778899:;<=>=<;:98877665544322100/.--,++*)( . !""#$$%&&'(())*++,--../0012233445567789:;<=>?>=<;:9988776554432210//.--,+**) 7 !!"##$%%&''())**+,,-../001123344556778899::;<=>?@?>=<;:9876554332110/..-,++*) < !""#$$%&&'(()**++,--.//01122344556778899::;;<=>?@A@?>=<;:988776554332100/.--,+** : !!"##$%%&''())*++,--../00122344556778899::;;<<==>?@ABA@?>=<;;::99877655432210//.-,,+* = !""#$$%&&'(()**+,,-../00112334556678899::;;<<==>>?@ABCBA@?>=<;:9876654432110/..-,+* 6 !!"##$%%&''())*++,--.//0112334556678899::;<<==>?@ABCDCBA@?>=<;::98876654332100/.-,,+ = !""#$$%&&'(()**+,,-../0012234456677899::;<<==>>??@ABCDEDCBA@?>==<<;::9877655432210/..-,+ B !!""#$$%&''())*++,--.//011233455677899::;<<==>>??@@ABCDEFEDCBA@?>=<;;:99877654432100/.-,, @ !!"##$%%&'(()**+,,-../001223445667889::;;<==>>??@@AABBCDEFGFEDCBA@?>==<;;:9887665432210/..-, N !""#$$%&&'())*++,--.//01223445667889::;;<==>>??@@AABBCCDEFGHGFEDCBA@?>=<<;::9877654432100/.-, E !!"##$%%&''()**+,,-../001233455677899:;;<<=>>??@@ABBCDEFGHIHGFEDCBA@?>>=<<;:9987665432210/.-, ? !""#$$%&&'(()*++,--.//01223445667889::;<<=>>??@AABBCCDEFGHIJIHGFEDCBAA@?>==<;;:988765443210/..- K !!"##$%%&''())*+,,-../00123345567889::;<<==>??@@ABBCCDDEEFGHIJIHGFEDCBBAA@??>==<;::987665432100/.- K !!"##$%&&'(()**+,--.//01223445677899:;;<==>??@@ABBCCDDEEFFGHIJKJIHGFEDCBBA@?>>=<;;:987765432210/.- G !""#$$%&''())*++,-../00123345567889::;<<=>>?@@AABCCDDEEFFGGHIJKLKJIHGFEDCCBBA@@?>==<;:998765443210/.. E !!"##$%%&'(()**+,,-.//01223445677899:;;<==>??@AABCCDDEEFFGGHHIJKLMLKJIHGFEDCCBA@??>=<;;:98766543210//. K !""#$$%&&'())*++,-../00123345567889::;<==>??@AABBCDDEEFFGGHHIIJKLMNMLKJIHGFEDDCBBA@@?>==<;:988765432110/. K !!"##$%%&''()**+,,-.//01123445677899:;<<=>>?@@ABBCCDEEFFGGHHIIJJKLMNONMLKJIHGFEDDCBAA@?>>=<;::98765433210/. I !!"##$%&&'(()*++,--./00123345567889:;;<==>??@AABCCDEEFFGGHHIIJJKKLMNONMLKJIHGFEEDCCBA@@?>=<;;:98765543210/. G !""#$$%&''())*+,,-../01123445677899:;<<=>>?@@ABBCDDEFFGGHHIIJJKKLMNOPONMLKJIHGFFEDDCBAA@?>==<;:98776543210/. E !!"##$%%&'(()**+,--.//0122345567889:;;<==>?@@ABBCDDEEFGGHHIIJJKKLLMNOPQPONMLKJIHGGFEEDCBBA@?>>=<;:98876543210/. I !""#$$%&&'())*++,-../01123445667899:;<<=>??@AABCCDEEFFGHHIIJJKKLLMMNOPQPONMLK JIIHHGFFEDCBA@@?>=<;::98765432100/ G !""#$%%&''()**+,,-.//0122345567889:;;<==>?@@ABBCDDEFFGHHIIJJKKLLMMNOPQRQPONML KJJIIHGGFEDDCBAA@?>=<;;:98765432110/  G !!"##$%%&'(()*++,--./00123345667899:;<<=>??@AABCCDEEFGGHIIJJKKLLMMNNOPQRSRQPONMLKJIIHHGFEEDCBBA@?>=<<;:98765432110/ E !""#$$%&&'())*+,,-../0112344567789::;<==>?@@ABBCDEEFFGHHIJJKKLLMMNNOPQRSRQPONML KJJIHHGFFEDCCBA@?>==<;:98765432210/  C !!"##$%%&''()**+,--.//0123345667899:;<<=>??@AABCDDEFFGHHIIJKKLLMMNNOPQRSTSRQPON MLLKKJIIHGFFEDCCBA@?>>=<;:98765432210/  !?"#$$%&&'(()*++,-../0112344567789::;<==>?@@ABCCDEEFGGHIIJJKKLMMNNOPQRSTUTSRQPONM LKKJJIHGGFEDCBA@?>>=<;:98765432210/   !"=#$$%&''())*+,,-.//0122345567889:;;<=>>?@AABCDDEFFGHHIJJKKLLMNNOPQRSTUTSRQPO NMMLLKJJIHHGFEDCBA@??>=<;:98765432110/  !!"#B$%%&'(()**+,--./0012334566789::;<==>?@@ABBCDEEFGGHIIJKKLLMMNNOOPPQQRSTUVUTSRQPON MLLKKJIHHGFEEDCBA@??>=<;:98765432100/  !""#$@%&&'())*++,-../0112344567889:;;<=>>?@AABCDDEFFGHHIJJKKLMMNNOOPPQQRSTUVUTSRQPONNMMLKKJIIHGFEEDCBA@??>=<;:9876543210/.  !""#$%>&''()**+,,-.//0123345667899:;<<=>??@ABBCDEEFGGHIIJKKLLMNNOOPPQQRSTUVWVUTSRQPO NMMLKKJIIHGFEEDCBA@?>>=<;:9876543210/.  !!"##$%&:'(()*++,-../0112344567789::;<=>>?@AABCDDEFFGHHIJJKLLMMNNOPPQRSTUVWVUTSRQPOONNMLLKJIIHGFEEDCBA@?>>=<;:9876543210/.  !""#$$%&'?())*+,,-.//0122345567889:;<<=>??@ABBCDEEFGGHIIJKKLMMNNOOPPQQRRSSTUVWXWVUTSRQP ONNMLLKJIIHGFEEDCBA@?>=<<;:9876543210/.  !!"##$%%&'?'()**+,--./0012334566789::;<==>?@@ABCCDEFFGHHIJJKLLMMNOOPPQQRRSSTUVWXWVUTSRQP ONNMLLKJIIHGFEEDCBA@?>=<;::9876543210/.  !!"#$$%&&'(;()*++,-../0112344567889:;;<=>>?@AABCDDEFGGHIIJKKLMMNNOPPQQRRSTUVWXYXWVUTSRQPPOONMLLKJIIHGFEDCBA@?>=<;:98766543210/.  !""#$$%&''()@)*+,,-.//0122345667899:;<<=>?@@ABCCDEFFGHHIJJKLLMNNOOPPQQRRSSTTUUVWXYXWVUTSRQPPOONMLLKJIHHGFEDCCBA@?>=<;:9876543210//.  !!"##$%%&'(()*<*+,--./0012344567789::;<=>>?@AABCDDEFGGHIIJKKLMMNNOPPQQRRSSTTUVWXYZYXWVUTSRQPPONNMLLKJIHHGFEDCBAA@?>=<;:9876543210/.-  !""#$$%&&'())*+8+,-../0122345567889:;<<=>??@ABBCDEEFGHHIJJKLLMNNOOPPQRRSSTUVWXYZYXWVUTSRQPPONNMLKKJIHGGFEDCBA@??>=<;:9876543210/.-  !"##$%%&''()**+,-;./0012334566789::;<==>?@@ABCDDEFFGHIIJKKLMMNNOPPQQRRSSTTUUVVWXYZ[ZYXWVUTSRQPPONNMLKKJIHGFFEDCBA@?>=<;::9876543210/.- # !!"##$%&&'(()*++,-.7/0112344567889:;;<=>>?@ABBCDEEFGGHIJJKLLMNNOOPQQRRSSTTUUVWXYZ[ZYXWVUTSRQQPOONMMLKJJIHGFEDDCBA@?>=<;:9876543210/..-  !""#$$%&''())*+,,-./:0122345567899:;<<=>?@@ABCCDEFFGHHIJKKLMMNNOPPQQRRSSTTUUVVWWXYZ[ZYXWVUTSRQQPOONMMLKJIIHGFEDCBAA@?>=<;:9876543210/.-, " !!"##$%%&'(()**+,--./0612334567789::;<=>>?@AABCDDEFGGHIIJKLLMMNOOPPQRRSSTTUUVVWXYZ[\[ZYXWVUTSRRQQPONNMLLKJIHGGFEDCBA@?>==<;:9876543210/.-,   !""#$$%&&'())*++,-../0102345567889:;<<=>??@ABBCDEEFGHHIJJKLLMNNOPPQQRRSTTUVWXYZ[\[ZYXWVUTSRRQPPONNMLKJJIHGFEDDCBA@?>=<;:98765432110/.-, # !""#$%%&''()**+,,-.//0125345667899:;<==>?@@ABCDDEFFGHIIJKKLMMNOOPPQRRSSTTUUVVWWXYZ[\[ZYXWVUTSSRQQPPONMMLKJIHHGFEDCBAA@?>=<;:9876543210/.-,+ ( !!"##$%%&'(()*++,--./00123344567789:;;<=>>?@ABBCDEEFGHHIJJKLLMNNOPPQQRRSTTUUVVWXYZ[\]\[ZYXWVUTTSSRQQPOONMLKKJIHGFFEDCBA@?>=<;;:9876543210/.-,+ $ !""#$$%&&'())*+,,-../01223645567889:;<<=>??@ABCCDEFFGHIIJKKLMMNOOPPQRRSSTTUUVVWWXXYZ[\]\[ZYXWVUTSRRQPPONNMLKJIIHGFEDCCBA@?>=<;:9876543210/.-,,+ ' !!"##$%%&''()**+,--./00123342566789::;<==>?@AABCDDEFGGHIJJKLLMNNOOPQQRRSSTUUVVWWXYZ[\]\[ZYXWVUTSSRRQPOONMLLKJIHGGFEDCBA@?>==<;:9876543210/.-,+* . !!"##$%&&'(()*++,-../01123445367889:;;<=>??@ABBCDEEFGHHIJJKLMMNNOPPQRRSSTTUUVVWWXXYZ[\]\[ZYXWVUUTTSSRQQPONNMLKJJIHGFEDDCBA@?>=<;:98765432100/.-,+* ( !""#$$%&''())*+,,-.//012234566/7899:;<==>?@@ABCCDEFFGHIIJKKLMMNOOPQQRRSSTUUVVWWXYZ[\]^]\[ZYXWVUTSRRQPOONMLLKJIHGGFEDCBA@?>>=<;:9876543210/.-,+*) - !!"##$%%&'(()**+,--./00123345677289::;<=>>?@AABCDEEFGGHIJJKLLMNNOPPQQRSSTTUUVVWWXXYYZ[\]^]\[ZYXWVUTTSSRQQPONNMLKJJIHGFEDDCBA@?>=<;:98765433210/.-,+*) + !""#$$%&&'())*++,-../011234556788,9:;<<=>??@ABCCDEFFGHHIJKKLMMNOOPPQRRSSTTUVVWWXYZ[\]^]\[ZYXWVUTSRRQPOONMLLKJIHGGFEDCBA@?>>=<;:9876543210/.-,+*)( . !""#$%%&''()**+,,-.//0123345667899/:;<==>?@@ABCDDEFGGHIIJKLLMNNOOPQQRRSTTUUVVWWXXYYZ[\]^]\[ZYXWVUTTSSRQPPONMMLKJIIHGFEDCCBA@?>=<;:98765432210/.-,+*)( 3 !!"##$%%&'(()*++,--./0012344567789:;0;<=>>?@ABBCDEEFGHHIJJKLLMNNOPPQRRSSTTUUVVWWXXYYZZ[\]^]\[ZYXWVUTSRQQPONNMLKKJIHGFFEDCBA@?>=<<;:9876543210/.-,+*)(( / !""#$$%&&'())*+,,-.//0122345567899:;<,<=>?@@ABCCDEFFGHIIJKKLMMNOOPQQRRSTTUUVVWWXXYYZ[\]^_^]\[ZYXWVUTTSRRQPOONMLLKJIHHGFEDCBAA@?>=<;:9876543210//.-,+*)(' 2 !!"##$%%&''()**+,--./0012334566789::;<=-=>?@AABCDDEFGGHIJJKLLMNNOPPQQRSSTTUUVVWWXXYYZZ[\]^_^]\[ZYXWVUTSSRQPPONMMLKJJIHGFEDDCBA@?>=<;:98876543210/.-,+*)('' 9 !!"##$%&&'(()*++,-../0112344567889:;;<=>?'@ABBCDEEFGHHIJKKLMMNOOPPQRRSSTUUVVWWXXYYZ[\]^_^]\[ZYXWVUUTSSRQQPONNMLKKJIHGGFEDCBA@?>>=<;:9876543210/.-,,+*)('& 3 !""#$$%&''())*+,,-.//0122345667899:;<==>?@(ABCCDEFGGHIIJKKLMNNOOPQQRRSTTUUVVWWXXYYZZ[\]^_^]\[ZYXWVUTTSRRQPOONMLLKJIHHGFEDCBBA@?>=<;:98765433210/.-,+*)('&% 8 !!"##$%%&'(()**+,--./0012334567789::;<=>>?@A$BCDEEFGHHIJJKLLMNNOPPQRRSSTTUVVWWXXYYZ[\]^_^]\[ZYXWVUTTSRRQPPONMMLKJIIHGFEDDCBA@?>=<;:99876543210/.-,+**)('&% 6 !""#$$%&&'())*++,-../0112345567889:;<<=>??@A'BCCDEFFGHIIJKKLMMNOOPQQRRSTTUUVVWWXXYYZZ[\]^_^]\[ZYX#WVVUUTSSRQQPONNMLKJJIHGFFEDCBA@?>==<;:9876543210/..-,+*)('&%$ 9 !""#$%%&''()**+,,-.//012334566789::;<==>?@AAB&CDDEFGGHIJJKLLMNNOPPQQRSSTTUUVVWWXXYYZZ[\]^_^]\[ZYXW"VUUTSSRQQPOONMLKKJIHGGFEDCBA@@?>=<;:98765433210/.-,+*)(''&%$ > !!"##$%%&'(()*++,--./0112344567789:;;<=>>?@ABBC"DEEFGHHIJJKLMMNOOPPQRRSSTUUVVWWXXYYZ[\]^_^]\[ZYXW#VUUTTSRRQPOONMLLKJIHHGFEDCBBA@?>=<;:98776543210/.-,+**)('&%$# : !""#$$%&&'())*+,,-.//0122345567899:;<<=>?@@ABCCD#EFFGHIIJKKLMMNOOPQQRRSTTUUVVWWXXYYZZ[\]^_^]\[ZYXWV"UTTSRRQPPONMMLKJIIHGFEDCCBA@?>=<;::9876543210/..-,+*)('&%%$# = !!"##$%%&''()**+,--./0012334566789::;<=>>?@AABCDDEFGGHIJJKLLMNNOPPQQRSSTTUVVWWXXYYZ[\]^_`_^]\[ZYXWV#UTTSRRQPPONMMLKJJIHGFEDDCBA@?>==<;:98765432110/.-,+*)(''&%$#" D !!"##$%&&'(()*++,-../0112344567889:;;<=>??@ABBCDEEF GHHIJKKLMMNOOPQQRRSSTUUVVWWXXYYZZ[\]^_`_^]\[ZYXWV$UTTSSRQPPONNMLKJJIHGFEEDCBA@?>>=<;:98765443210/.-,+*))('&%$#"" > !""#$$%&''())*+,,-.//0122345667899:;<==>?@@ABCCDEFGGHIIJKLLMNNOPPQQRSSTTUUVVWWXXYYZZ[\]^_`_^]\[ZYXWVU#TSSRQQPONNMLKKJIHGFFEDCBA@@?>=<;:9876543210/.-,++*)('&%$$#"! C !!"##$%%&'(()**+,--./0012334567789::;<=>>?@AABCDEEFGHHIJJKLLMNNOPPQRRSSTTUVVWWXXYYZZ[\]^_`_^]\[ZYXWVU$TSSRQQPONNMLKKJIHGGFEDCBAA@?>=<;:98876543210/.--,+*)('&&%$#"!  A !""#$$%&&'())*++,-../0112345567889:;<<=>??@ABCCDEFFGHIIJKKLMMNOOPQQRRSTTUUVVWWXXYYZZ[\]^_`_^]\[ZYXWVU%TSSRQQPOONMLKKJIHGGFEDCBBA@?>=<;:99876543210//.-,+*)(''&%$#""!  D !""#$%%&''()**+,,-.//012334566789::;<==>?@AABCDDEFGGHIJJKLLMNNOPPQQRSSTTUUVWWXXYYZ[\]^_`_^]\[ZYXWVU&TSSRQQPOONMLLKJIHHGFEDCBBA@?>=<;::98765432110/.-,+*))('&%$##"!  I !!"##$%%&'(()*++,--./0112344567789:;;<=>>?@ABBCDEEFGHHIJJKLMMNOOPPQRRSSTUUVVWWXXYYZZ[\]^_`_^]\[ZYXWV)UTTSSRQQPOONMLLKJIHHGFEDCCBA@?>=<;;:98765432210/.-,+**)('&%$$#"!  E !""#$$%&&'())*+,,-.//0122345567899:;<<=>?@@ABCCDEFFGHIIJKKLMNOOPQQRRSTTUUVVWWXXYYZZ[\]^_`_^]\[ZYXWV*UTTSSRQQPONNMLLKJIHHGFEDCCBA@?>=<<;:98765433210/.-,++*)('&%%$#"!!  H !!"##$%%&''()**+,--./0012334566789::;<=>>?@AABCDDEFGGHIJJKLLMNOPPQRRSSTTUVVWWXXYYZZ[\]^_`_^]\[ZYXWV+UTTSSRQQPONNMLKKJIHHGFEDCCBA@?>==<;:98765443210/.-,,+*)('&&%$#"!!  O !!"##$%&&'(()*++,-../0112344567889:;;<=>??@ABBCDEEFGHHIJKKLMMNOPQQRRSTTUUVVWWXXYYZZ[\]^_`_^]\[ZYXW.VUUTTSRRQPPONNMLKKJIHHGFEDCCBA@?>==<;:98765543210/.--,+*)(''&%$#""!  I !""#$$%&''())*+,,-.//0122345667899:;<==>?@@ABCCDEFGGHIIJKLLMNNOPQ RSSTTUUVVWXXYZ[\]^_`_^]\[ZYXW/VUUTTSRRQPPONNMLKKJIHGGFEDCCBA@?>==<;:98766543210/..-,+*)(('&%$##"!  N !!"##$%%&'(()**+,--./0012334567789::;<=>>?@AABCDEEFGHHIJJKLLMNNOPPQRRSSTUUVVWWXXYYZZ[\]^_`_^]\[ZYX2WVVUUTTSRRQPPONMMLKKJIHGGFEDCBBA@?>==<;:98766543210/.-,+*)(('&%$$#"!  L !""#$$%&&'())*++,-../0112345567889:;<<=>??@ABCCDEFFGHIIJKKLMMNOOPQQRRSTTUUVVWWXXYYZZ[\]^_`_^]\[ZYXWVU.TSSRRQPPONMMLKJJIHGGFEDCBBA@?>==<;:98766543210//.-,+*))('&%$$#"!  O !""#$%%&''()**+,,-.//0123345667899:;<==>?@AABCDDEFGGHIJJKLLMNNOPPQQ RSSTTUUVWWXXYYZ[\]^_`_^]\[ZYXWVU/TSSRQQPOONMMLKJJIHGFFEDCBBA@?>=<<;:98766543210//.-,+**)('&%%$#"!!  T !!"##$%%&'(()*++,--./0112344567789:;;<=>>?@ABBCDEEFGHHIJJKLMMNOOPPQRRS TUUVVWWXXYYZZ[\]^_`_^]\[ZYXWV2UTTSSRQQPOONMLLKJIIHGFFEDCBAA@?>=<<;:987665432100/.-,+**)('&%%$#"!!  P !""#$$%&&'())*+,,-.//0122345567899:;<<=>?@@ABCCDEFFGHIIJKKLMMNOOPQQRRSTUVWXYZ[\]^_`_^]\[ZYXW5VUUTTSRRQPPONNMLLKJIIHGFEEDCBAA@?>=<;;:987655432100/.-,+**)('&%%$#""!  S !"##$%%&''()**+,--./0012334566789::;<==>?@AABCDDEFGGHIJJKLLMNNOPPQRRSST TUVVWWXXYYZZ[\]^_`_^]\[ZYXWVUT1SRRQPPONNMLKKJIHHGFEEDCBA@@?>=<;;:98765543210//.-,+**)('&&%$#""!  Z !!"##$%&&'(()*++,-../0112344567889:;;<=>??@ABBCDEEFGHHIJKKLMMNOOPQQRRSTTU UVVWWXXYYZZ[\]^_`_^]\[ZYXWVU4TSSRQQPOONMMLKKJIHHGFEDDCBA@@?>=<;::98765443210//.-,+*)('&&%$#""!  T !""#$$%&''())*+,,-.//0122345667899:;<==>?@@ABCCDEFGGHIIJKLLMNNOPPQQRSSTTUUVWXXYZ[\]^_`_^]\[ZYXWV7UTTSSRQQPOONMMLKJJIHGGFEDCCBA@??>=<;::98765443210//.-,+**)('&&%$#""!  Y !!"##$%%&'(()**+,--./0012334567789::;<=>>?@AABCDEEFGHHIJJKLLMNNOPPQRRSSTUUVVWXYZ[\]^_`_^]\[ZYX>=<;:998765433210/..-,+**)('&&%$#""!  U !""#$$%&&'())*++,-../0112345567889:;<<=>??@ABCCDEFFGHIIJKKLMMNOOPQQRRSTTUUVVWWXXYYZZ[\]^_`_^]\[ZYXWVU8TSSRRQPPONNMLLKJIIHGFFEDCBBA@?>==<;:988765433210/..-,+*))('&&%$#""!  Z !""#$%%&''()**+,,-.//0123345667899:;<==>?@AABCDDEFGGHIJJKLLMNNOPPQQRSSTTUUVWWXXYYZ[\]^_`_^]\[ZYXWV;UTTSSRQQPOONMMLKKJIHHGFEEDCBAA@?>==<;:987765432210/.--,+*))('&%%$#""!  ] !!"##$%%&'(()*++,--./0012344567789:;;<=>>?@ABBCDEEFGHHIJJKLMMNOOPPQRRSSTUUVVWWXXYZ[\]^_`_^]\[ZYXW>VUUTTSRRQQPOONMMLKJJIHHGFEDDCBA@@?>=<<;:987765432210/.--,+*))('&%%$#""!  [ !""#$$%&&'())*+,,-../0122345567899:;<<=>?@@ABCCDEFFGHIIJKKLMMNOOPQQRRSTTUUVVWWXXYYZZ[\]^_`_^]\[ZYXWVUT:SRRQPPONNMLLKJJIHGGFEDCCBA@??>=<;;:987665432110/.-,,+*)(('&%$#""!  ^ !""#$%%&''()**+,--./0012334566789::;<==>?@AABCDDEFGGHIJJKLLMNNOPPQQRSSTTUVVWWXXYYZZ[\]^_`_^]\[ZYXWVU=TSSRQQPPONNMLLKJIIHGFFEDCCBA@?>>=<;::987655432100/.-,,+*)(('&%%$#""!  c !!"##$%&&'(()*++,-../0112344567889:;;<=>??@ABBCDEEFGHHIJKKLMMNOOPPQRRSSTUUVVWWXXYYZZ[\]^_`_^]\[ZYXWBVUUTTSSRQQPOONMMLKKJIHHGFEEDCBBA@?>==<;:998765443210//.-,++*)(''&%$$#"!!  Y !""#$$%&''())*+,,-.//0122345567899:;<==>?@@ABCCDEFFGHIIJKKLMNNOOPQQRSSTTUUVVWXXYZ[[\]^_`_^]\[ZYXWVUT>SRRQPPOONMMLKJJIHGGFEDDCBAA@?>=<<;:988765433210//.-,++*)(''&%$$#"!!  a !!"##$%%&'(()**+,--./0012334567789::;<=>>?@AABCDEEFGHHIJJKLLMNNOPPQRRSSTTUVVWWXXYYZZ[\]^_`_^]\[ZYXWVUATSSRRQPPONNMLLKJIIHGGFEDCCBA@??>=<;;:987765432210/..-,+**)('&&%$##"!!   \ !!"#$$%&&'(()*++,-../0112345567889:;<<=>??@ABCCDEFFGHIIJKKLMMNOOPQQRRSTTUUVVWWXXYYZZ[\\]^_`_^]\[ZYXWFVUUTTSSRQQPOONMMLKKJIIHGFFEDCBBA@?>>=<;::987665432110/.--,+*))('&&%$##"!  ] !""#$$%&''())*+,,-.//0122345667899:;<==>?@@ABCDDEFGGHIIJKLLMNNOPPQQRSSTTUUVWWXXYYZ[\]^_`_^]\[ZYXWVUTBSRRQQPOONMMLKJJIHHGFEEDCBAA@?>==<;:9987655432100/.-,,+*))('&%%$##"!   a !!"##$%%&'(()**+,--./0012344567789:;;<=>>?@ABBCDEEFGHHIJJKLMMNOOPPQRRSSTUUVVWWXXYYZZ[\]]^_`_^]\[ZYXWVUETSSRRQPPONNMLLKJJIHGGFEDDCBA@@?>=<<;:988765443210//.-,,+*)(('&%%$#""!   ^ !""#$$%&&'())*++,-../0122345567889:;<<=>?@@ABCCDEFFGHIIJKKLMMNOOPQQRRSTTUUVVWWXXYYZZ[\]]^_`_^]\[ZYXWJVUUTTSSRQQPOONMMLKKJIIHGFFEDCCBA@??>=<;;:987765433210//.-,++*)(''&%$$#""!  ] !""#$%%&''()**+,--./0012334566789::;<==>?@AABCDDEFGGHIJJKLLMNNOPPQQRSSTTUVVWWXXYYZ[\]^^_`_^]\[ZYXWVUTFSRRQPPOONMMLKJJIHHGFEEDCBAA@?>>=<;::987665432210/..-,+**)(''&%$$#"!!  c !!"##$%&&'(()*++,-../0112344567889:;;<=>??@ABBCDEEFGHHIJKKLMMNOOPPQRRSSTUUVVWWXXYYZZ[\]^^_`_^]\[ZYXWVKUTTSSRRQPPONNMLLKJJIHGGFEDDCBA@@?>=<<;:9887654432110/.--,+*))('&&%$##"!!  ^ !""#$$%&&'())*+,,-.//0122345567899:;<<=>?@@ABCCDEFFGHIIJKKLMMNOOPQQRSSTTUUVVWWXXYYZZ[\]^_`_^]\[ZYXWVUTSGRQQPOONMMLKKJIIHGFFEDCCBA@??>=<;;:9877654332100/.-,,+*)(('&%%$##"!  a !!"##$%%&''()**+,--./0012334567789::;<=>>?@AABCDDEFGGHIJJKLLMNNOPPQRRSSTTUVVWWXXYYZZ[\]^__`_^]\[ZYXWVULTSSRRQPPOONMMLKJJIHHGFEEDCBAA@?>>=<;::987665432210//.-,++*)(('&%%$#""!  e !!"#$$%&&'(()*++,-../0112344567889:;<<=>??@ABBCDEFFGHHIJKKLMMNOOPQQRRSTTUUVVWWXXYYZZ[\]^__`_^]\[ZYXWVOUTTSSRQQPPONNMLLKJIIHGGFEDDCBA@@?>==<;:9987655432110/..-,+**)(''&%$$#""!  l !""#$$%&''())*+,,-.//0122345667899:;<==>?@@ABCDDEFGGHIIJKLLMNNOPPQQRSSTTUUVWWXXYYZ[\]^__`_^]\[ZYXWVUTMSRRQQPOONMMLKKJIIHGFFEDCCBA@??>=<;;:9877654432100/.--,+*))('&&%$$#"!!   a !!"##$%%&'(()**+,--./0012344567789:;;<=>>?@ABBCDEEFGHHIJJKLLMNNOPPQRRSSTUUVVWWXXYYZZ[\]^_`_^]\[ZYXWVUPTSSRRQPPOONMLLKJJIHHGFEEDCBAA@?>>=<;::987665433210//.-,,+*)(('&&%$##"!!    ^ !""#$$%&&'())*++,-../0112345567889:;<<=>??@ABCCDEFFGHIIJKKLMMNOOPQQRRSTTUUVVWWXXYYZZ[\]^_``_^]\[ZYXWUVUUTTSSRQQPPONNMLLKJIIHGGFEDCCBA@@?>=<<;:9987655432110/..-,++*)(('&%%$#""!   h !""#$%%&''()**+,,-.//012334566789::;<==>?@AABCDDEFGGHIJJKLLMNNOPPQQRSSTTUUVWWXXYYZ[\]^_``_^]\[ZYXWVUTQSRRQQPOONMMLKKJIHHGFFEDCBBA@??>=<;;:9887654432100/.--,+**)(''&%$$#""!   c !!"##$%%&'(()*++,--./0112344567789:;;<=>??@ABBCDEEFGHHIJJKLMMNOOPPQRRSSTUUVVWWXXYYZZ[\]^_``_^]\[ZYXWVVUTTSSRRQPPONNMLLKJJIHGGFEDDCBAA@?>>=<;::987665433210//.-,,+*))('&&%$##"!!   ^ !""#$$%&&'())*+,,-.//0122345567899:;<<=>?@@ABCCDEFFGHIIJKKLMMNOOPQQRRSTTUUVVWWXXYYZZ[\]^_``_^]\[ZYXWVUTSRRQQPOONNMLKKJIIHGFFEDCCBA@@?>=<<;:9987655432110/..-,++*)(('&%%$##"!    d !!"##$%%&''()**+,--./0012334567789::;<=>>?@AABCDDEFGGHIJJKLLMNNOPPQRRSSTTUVVWWXXYYZ[\]^_``_^]\[ZYXWVUWTSSRRQPPOONMMLKKJIHHGFEEDCBBA@??>=<;;:9877654432100/.--,+**)(''&%%$#""!    e !!"##$%&&'(()*++,-../0112344567889:;;<=>??@ABBCDEEFGHHIJKKLMMNOOPQQRRSSTUUVVWWXXYYZZ[\]^_``_^]\[ZYXWVZUTTSSRQQPPONNMLLKJJIHGGFEDDCBAA@?>==<;::987665432210//.-,,+*))('&&%$$#"!!   Y !""#$$%&''())*+,,-.//0122345667899:;<==>?@@ABCCDEFGGHIIJKLLMNNOOPQQRSSTTUUVVWXXYZ[\]^_``_^]\[ZYXWVUTXSRRQQPOONMMLKKJIIHGFFEDCCBA@@?>=<<;:9887655432110/..-,++*)(('&&%$##"!!    b !!"##$%%&'(()**+,--./0012334567789::;<=>>?@AABCDEEFGHHIJJKLLMNNOPPQRRSSTTUVVWWXXYYZZ[\]^_``_^]\[ZYXWVU[TSSRRQPPOONMMLKJJIHHGFEEDCBBA@?>>=<;;:9877654332100/.--,+**)(''&%%$#""!   a !!"#$$%&&'())*++,-../0112345567889:;<<=>??@ABCCDEFFGHIIJKKLMMNOOPQQRRSTTUUVVWWXXYYZZ[\]^_``_^]\[ZYXbWVVUUTTSSRQQPPONNMLLKJIIHGGFEDDCBA@@?>==<;:9987665432210//.-,,+*))('&&%$$#""!   ] !""#$$%&''()**+,,-.//0123345667899:;<==>?@AABCDDEFGGHIIJKLLMNNOPPQQRSSTTUUVWWXXYYZ[\]^_``_^]\[ZYXWVUT\SRRQQPOONMMLKKJIIHGFFEDCCBA@??>=<<;:9887654432110/..-,++*)(('&&%$##"!!   \ !!"##$%%&'(()*++,--./0012344567789:;;<=>>?@ABBCDEEFGHHIJJKLMMNOOPPQRRSSTUUVVWWXXYYZZ[\]^_``_^]\[ZYXWVaUTTSSRRQPPONNMLLKJJIHHGFEEDCBAA@?>>=<;::9877654332100/.--,+**)(''&%%$#""!   Y !""#$$%&&'())*+,,-../0122345567899:;<<=>?@@ABCCDEFFGHIIJKKLMMNOOPQQRRSTTUUVVWWXXYYZZ[\]^_`_^]\[ZYXWVUTS]RQQPOONNMLKKJIIHGFFEDCCBA@@?>==<;:9987655432210//.-,,+*))('&&%$$#""!   Y !""#$%%&''()**+,--./0012334566789::;<==>?@AABCDDEFGGHIJJKLLMNNOPPQQRSSTTUVVWWXXYYZ[\]^_`__^]\[ZYXWVUbTSSRRQPPOONMMLKKJIHHGFEEDCBBA@??>=<;;:9887654432110/..-,++*)(('&%%$##"!!   Z !!"##$%&&'(()*++,-../0112344567889:;;<=>??@ABBCDEEFGHHIJKKLMMNOOPPQRRSSTUUVVWWXXYYZZ[\]^_`__^]\[ZYXWgVUUTTSSRQQPPONNMLLKJJIHGGFEDDCBAA@?>==<;::9876654332100/.--,+**)(''&%%$#""!   Y !""#$$%&&'())*+,,-.//0122345567899:;<<=>?@@ABCCDEFFGHIIJKKLMMNOOPQQRSSTTUUVVWWXXYYZZ[\]^_`__^]\[ZYXWVUT]SRRQQPOONMMLKKJIIHGFFEDCCBA@@?>=<<;:9887655432210/..-,++*))('&&%$$#""!    U !!"##$%%&''()**+,--./0012334567789::;<=>>?@AABCDDEFGGHIJJKLLMNNOPPQRRSSTTUVVWWXXYYZ[\]^_`_^]\[ZYXWVcUTTSSRRQPPONNMLLKJJIHHGFEEDCBBA@?>>=<;;:9877654432100/.--,+**)(('&%%$##"!!   T !!"#$$%&&'(()*++,-../0112344567889:;<<=>??@ABBCDEFFGHHIJKKLMMNOOPQQRRSTTUUVVWWXXYYZZ[\]^_`_^^]\[ZYXWVUTS`RQQPPONNMLLKJIIHGGFEDDCBA@@?>==<;:9987665432210//.-,,+*))(''&%$$#""!    N!""#$$%&''())*+,,-.//0122345667899:;<==>?@@ABCDDEFGGHIIJKLLMNNOOPQQRSSTTUUVVWXXYZ[\]^_`_^^]\[ZYXWVU]TSSRRQQPOONMMLKKJIHHGFFEDCBBA@??>=<<;:9887655432110/..-,++*)(('&&%$$#"!!     !O"##$%%&'(()**+,--./0012344567789:;;<=>>?@ABBCDEEFGHHIJJKLLMNNOPPQRRSSTUUVVWWXXYYZ[\]^_`_^]]\[ZYXWVcUTTSSRQQPPONNMLLKJJIHGGFEDDCBAA@?>>=<;::9877654332100/.--,+**)(('&%%$##"!!     !"P#$$%&&'())*++,-../0112345567889:;<<=>??@ABCCDEFFGHIIJKKLMMNOOPQQRRSTTUUVVWWXXYYZZ[\]^_`_^]]\[ZYXWVUTbSRRQQPOONMMLKKJIIHGFFEDCCBA@@?>=<<;:9987655432210//.-,,+*))(''&%$$#""!    !"N"#$%%&''()**+,,-.//012334566789::;<==>?@AABCDDEFGGHIIJKLLMNNOPPQQRSSTTUUVWWXXYYZ[\]^_`_^]\[ZYXWVU]TSSRRQPPOONMMLKJJIHHGFEEDCBBA@??>=<;;:9887654432110/..-,++*)(('&&%$##"!!    !!"#O#$%%&'(()*++,--./0112344567789:;;<=>>?@ABBCDEEFGHHIJJKLMMNNOPPQRRSSTUUVVWWXXYYZZ[\]^_`_^]\\[ZYXWVUTS^RQQPPONNMLLKJJIHGGFEDDCBAA@?>==<;::9876654332100/.--,+**)(''&%%$#""!    !""#$N$%&&'())*+,,-.//0122345567899:;<<=>?@@ABCCDEFFGHIIJKKLMMNOOPQQRRSTTUUVVWWXXYYZZ[\]^_`_^]\[ZYXWVUTbSRRQQPOONMMLKKJIIHGFFEDCCBA@??>=<<;:9887655432210/..-,,+*))('&&%$$#""!    !"##$%J%&''()**+,--./0012334566789::;<=>>?@AABCDDEFGGHIJJKLLMNNOPPQQRSSTTUUVWWXXYYZ[\]^_`_^]\[[ZYXWV_UTTSSRRQPPONNMLLKJJIHHGFEEDCBBA@?>>=<;;:9877654432100/.--,++*)(('&%%$##"!!    !!"##$%&K&'(()*++,-../0112344567889:;;<=>??@ABBCDEEFGHHIJKKLMMNOOPPQRRSSTUUVVWWXXYYZZ[\]^_`_^]\[ZYXWVUT`SRRQQPOONNMLKKJIIHGGFEDDCBA@@?>==<;:9987665432210//.-,,+**)(''&%%$#""!    !""#$$%&'J'())*+,,-.//0122345567899:;<==>?@@ABCCDEFFGHIIJKKLMMNOOPQQRRSTTUUVVWWXXYYZZ[\]^_`_^]\[ZoZYYXXWWVVUUTSSRRQPPOONMMLKKJIHHGFEEDCBBA@??>=<;;:9887654432110/..-,++*))('&&%$$#""!    !!"##$%%&'(F()**+,--./0012334567789::;<=>>?@AABCDDEFGGHIJJKLLMNNOPPQQRSSTTUVVWWXXYYZ[\]^_`_^]\[ZYYXeWVVUUTTSSRQQPPONNMLLKJJIHGGFEDDCBAA@?>>=<;::9877654332100/.--,+**)(('&%%$##"!!    !!"#$$%&&'((G)*++,-../0112345567889:;<<=>??@ABBCDEFFGHHIJKKLMMNOOPPQRRSSTUUVVWWXXYYZZ[\]^_`_^]\[ZYXWVUT`SRRQQPOONMMLKKJIIHGFFEDCCBA@@?>=<<;:9987655432210//.-,,+*))(''&%$$#""!    !""#$$%&''())F*+,,-.//0122345667899:;<==>?@@ABCDDEFGGHIIJKLLMNNOOPQQRSSTTUUVVWWXXYYZZ[\]^_`_^]\[ZYXbXWWVVUTTSSRRQPPONNMLLKJJIHHGFEEDCBBA@?>>=<;;:9877654432110/..-,++*)(('&&%$##"!!    !!"##$%%&'(()**B+,--./0012344567789:;;<=>>?@ABBCDEEFGHHIJJKLLMNNOPPQRRSSTTUVVWWXXYYZ[\]^_`_^]\[ZYXWeWVVUUTTSSRQQPOONNMLLKJIIHGGFEDDCBA@@?>==<;::987665433210//.--,+**)(''&%%$##"!    !""#$$%&&'())*++C,-../0122345567889:;<<=>??@ABCCDEFFGHHIJKKLMMNOOPQQRRSTTUUVVWWXXYYZZ[\]^_`_^]\[ZYXWVUbTSSRRQPPOONMMLKKJIHHGFFEDCBBA@??>=<<;:9887655432110/..-,++*))('&&%$$#""!    !""#$%%&''()**+,,B-.//012334566789::;<==>?@AABCDDEFGGHIIJKLLMNNOOPQQRSSTTUUVVWWXXYYZZ[\]^_`_^]\[ZYXWVUTSZRQQPPONNMLLKJJIHGGFEDDCBAA@?>>=<;::9877654332100/.--,+**)(('&%%$##"!!   " !!"##$%&&'(()*++,-.>./0112344567789:;;<=>>?@ABBCDEEFGHHIJJKLLMNNOPPQRRSSTTUVVWWXXYYZ[\]^_`_^]\[ZYXWVUaUTTSRRQQPOONMMLKKJIIHGFFEDCCBA@@?>==<;:9987665432210//.-,,+*))(''&%$$#""!    !""#$$%&&'())*+,,-./?/0122345567899:;<<=>?@@ABCCDEFFGHIIJKKLMMNOOPQQRRSTTUUVVWWXXYYZZ[\]^_`_^]\[ZYXWVUTdTSSRRQPPOONMMLKJJIHHGFEEDCBBA@??>=<;;:9887654432110/..-,++*)(('&&%$$#"!!   ! !"##$%%&''()**+,--./09012334566789::;<==>?@AABCDDEFGGHIJJKLLMNNOPPQQRSSTTUUVVWXXYZ[\]^_`_^]\[ZYXWVUTS\RQQPPONNMLLKJJIHGGFEDDCBAA@?>==<;::9876654332100/.--,+**)(''&%%$##"!!   & !!"##$%&&'(()*++,-../01:12344567889:;;<=>??@ABBCDEEFGHHIJJKLMMNNOPPQRRSSTUUVVWWXXYYZ[\]^_`_^]\[ZYXWVUTSSRQ[POONMMLKKJIIHGFFEDCCBA@??>=<<;:9887655432210//.-,,+*))('&&%$$#""!   " !""#$$%&''())*+,,-.//012;2345567899:;<<=>?@@ABCCDEFFGHIIJKKLMMNOOPQQRRSTTUUVVWWXXYYZZ[\]^_`_^]\[ZYXWVUUTTSSRQPSONNMLLKJJIHHGFEEDCBBA@?>>=<;;:9877654432100/.--,++*)(('&&%$##"!!   ' !!"##$%%&'(()**+,--./00123534567789::;<=>>?@AABCDDEFGGHIJJKLLMNNOPPQQRSSTTUUVWWXXYZ[\]^_`_^]\[ZYXWVUTSRRQZQPOONMMLKKJIIHGGFEDCCBA@@?>==<;:9987665432210//.-,,+**)(''&%%$#""!   , !!"#$$%&&'(()*++,-../0112345467889:;<<=>??@ABBCDEEFGHHIJKKLMMNOOPPQRRSSTUUVVWWXXYYZ[\]^_`_^]\[ZYXWVUTTSSRRQP[POONMMLKKJIHHGFEEDCBBA@??>=<;;:9887654432110/..-,++*))('&&%$$#""!   ( !""#$$%&''())*+,,-.//0122345657899:;<==>?@@ABCDDEFGGHIIJKKLMMNOOPQQRRSTTUUVVWWXXYYZZ[\]^_`_^]\[ZYXWVUTSRQQPPUONNMLLKJJIHGGFEDDCBAA@?>>=<;::9877654332100/.--,+**)(('&%%$##"!!   + !!"##$%%&'(()**+,--./0012344567189:;;<=>>?@AABCDEEFGGHIJJKLLMNNOPPQQRSSTTUUVWWXXYYZ[\]^_`_^]\[ZYXWVU TSSRRQQPOOXNMMLKKJIIHGFFEDCCBA@@?>=<<;:9987655432210//.-,,+*))(''&%$$#""!   ) !""#$$%&&'())*++,-../0122345567809:;<<=>??@ABCCDEFFGHHIJKKLMMNOOPPQRRSSTUUVVWWXXYYZ[\]^_`_^]\[ZYXWVUUTTSSRQQPPONNYMLLKJJIHHGFEEDCBBA@?>>=<;;:9877654432110/..-,++*)(('&&%$$#"!!   , !""#$%%&''()**+,,-.//0123345667839::;<==>?@AABCDDEFGGHIIJKLLMNNOOPQQRRSTTUUVVWWXXYYZZ[\]^_`_^]\[ZYXWVUT SRRQQPOONNMSLKKJIIHGGFEDDCBA@@?>==<;::987665433210//.--,+**)(''&%%$##"!!   1 !!"##$%%&'(()*++,--./0112344567789/:;;<=>>?@ABBCDEEFGHHIJJKLLMNNOPPQRRSSTTUVVWWXXYYZ[\]^_`_^]\[ZYXWVUTTSSRRQPPOONMMLKRJIHHGFFEDCBBA@??>=<<;:9887655432110/..-,,+*))('&&%$$#""!   - !""#$$%&&'())*+,,-.//0122345567899:.;<<=>?@@ABCCDEFFGHIIJKKLMMNOOPPQRRSSTUUVVWWXXYYZ[\]^_`_^]\[ZYXWVUTS RQQPPONNMLLKJJIHGGFEDDCBAA@?>>=<;::9877654332100/.--,++*)(('&%%$##"!!   0 !"##$%%&''()**+,--./0012334566789::;/<==>?@AABCDDEFGGHIIJKLLMNNOOPQQRSSTTUUVVWWXXYYZZ[\]^_`_^]\[ZYXWVUTSSRRQQPOONMMLKKJIMHGFFEDCCBA@@?>=<<;:9987665432210//.-,,+**)(''&%%$#""!   5 !!"##$%&&'(()*++,-../0112344567889:;;<+=>??@ABBCDEEFGHHIJJKLLMNNOPPQRRSSTTUVVWWXXYYZ[\]^_`_^]\[ZYXWVUUTTSSRRQPPONNMLLKJJIHNGFEEDCBBA@??>=<;;:9887654432110/..-,++*))('&&%$$#""!   1 !""#$$%&''())*+,,-.//0122345567899:;<<=*>?@@ABCCDEFFGHIIJKKLMMNOOPQQRRSTTUUVVWWXXYYZ[\]^_`_^]\[ZYXWVUTSRRQQPOONNMLLKJIIHGHFEDDCBAA@?>==<;::9876654332100/.--,+**)(('&%%$##"!!   6 !!"##$%%&''()**+,--./0012334567789::;<=>>+?@AABCDDEFGGHIJJKLLMNNOOPQQRSSTTUUVVWWXXYYZZ[\]^_`_^]\[ZYXWVUTSSRRQPPOONMMLKKJIHHGFKEDCCBA@??>=<<;:9887655432210//.-,,+*))(''&%$$#""!   ; !!"##$%&&'(()*++,-../0112344567889:;;<=>??'@ABBCDEEFGHHIJJKLMMNNOPPQRRSSTTUVVWWXXYYZ[\]^_`_^]\[ZYXWVUTSRQQPPONNMLLKJJIHGGFEADCBAA@?>>=<;;:9877654432100/.--,++*)(('&&%$##"!!   5 !""#$$%&''())*+,,-.//0122345667899:;<==>?@@&ABCCDEFFGHIIJKKLMMNOOPQQRRSTTUUVVWWXXYYZ[\]^_`_^]\[ZYXWVUTSRRQQPOONMMLKKJIIHGFFEHDCCBA@@?>==<;:9987665432210//.-,,+**)(''&%%$#""!   : !!"##$%%&'(()**+,--./0012334567789::;<=>>?@AA'BCDDEFGGHIJJKLLMNNOPPQQRSSTTUUVVWWXXYYZZ[\]^_`_^]\[ZYXWVUUTTSSRRQPPONNMMLKJJIHHGFEEDICBBA@??>=<;;:9887654432110/..-,++*))('&&%$$#""!   A !!"#$$%&&'())*++,-../0112345567889:;<<=>??@ABB#CDEFFGHHIJKKLMMNOOPPQRRSSTTUVVWWXXYYZ[\]^_`_^]\[ZYXWVUTSRRQQPOONNMLLKJJIHGGFEDDCCBAA@?>==<;::9877654332100/.--,+**)(('&%%$##"!!   ; !""#$$%&''()**+,,-.//0123345667899:;<==>?@@ABCD"DEFFGHIIJKKLMMNOOPQQRRSTTUUVVWWXXYYZ[\]^_`_^]\[ZYXWVUTSSRRQPPOONMMLKKJIIHGFFEDCCBDA@@?>=<<;:9987655432210//.-,,+*))(''&%$$#""!   > !!"##$%%&'(()*++,--./0012344567789:;;<=>>?@AABCDE#EFGGHIJJKLLMNNOPPQQRSSTTUUVVWWXXYYZZ[\]^_`_^]\[ZYXWVUTSRQQPPONNMLLKJJIHHGFEEDCBBAE@?>>=<;;:9877654432110/..-,++*)(('&&%$$#"!!   < !""#$$%&&'())*+,,-../0122345567889:;<<=>??@ABCCDEFFGHHIJKKLMMNOOPPQRRSSTTUVVWWXXYYZ[\]^_`_^]\[ZYXWVUTSRRQQPOONMMLKKJIIHGGFEDDCBA@@??>==<;:9987665433210//.--,+**)(''&%%$##"!!   ? !""#$%%&''()**+,--./0012334566789::;<==>?@AABCDDEFGGHIIJKKLMNNOOPQQRRSTTUUVVWWXXYYZ[\]^_`_^]\[ZYXW#VUUTTSSRRQPPONNMMLKJJIHHGFEEDCBBA@??B>=<<;:9887655432110/..-,,+*))('&&%$$#""!   D !!"##$%&&'(()*++,-../0112344567789:;;<=>>?@ABBCDEEFGHHIJJKLLMNNOPPQQRSSTTUUVVWWXXYYZZ[\]^_`_^]\[ZYXWVUTSRRQQPOONNMLLKJJIHGGFEDDCBAA@?>>:=<;::9877654332100/.--,++*)(('&%%$##"!!   @ !""#$$%&&'())*+,,-.//0122345567899:;<<=>?@@ABCCDEFFGHHIJKKLMMNOOPPQRRSSTUUVVWWXXYYZ[\]^_`_^]\[ZYXWV$UTTSSRRQPPOONMMLKKJIIHGFFEDCCBA@@?>=<=<;:9987655432210//.-,,+**)(''&%%$#""!   C !"##$%%&''()**+,--./0012334566789::;<==>?@AABCDDEFGGHIIJKLLMNNOOPQQRRSTTUUVVWWXXYYZ[\]^_`_^]\[ZYXWVUTS RQQPPONNMLLKJJIHHGFEEDCBBA@?>>=<;>;:9887654432110/..-,++*))('&&%$$#""!   H !!"##$%&&'(()*++,-../0112344567889:;;<=>??@ABBCDEEFGHHIJJKLLMNNOPPQQRSSTTUUVVWWXXYYZZ[\]^_`_^]\[ZYXWVU%TSSRRQQPOONMMLKKJIIHGGFEDDCBA@@?>==<;:8:9876654332100/.--,+**)(('&%%$##"!!   D !""#$$%&''())*+,,-.//0122345567899:;<<=>?@@ABCCDEFFGHIIJKKLMMNOOPPQRRSSTUUVVWWXXYYZ[\]^_`_^]\[ZYXW*VUUTTSSRRQPPONNMMLKKJIHHGFFEDCBBA@??>=<<;:9897655432210//.-,,+*))(''&%$$#""!   G !!"##$%%&''()**+,--./0012334567789::;<=>>?@AABCDDEFGGHIIJKLLMNNOOPQQRRSTTUUVVWWXXYYZ[\]^_`_^]\[ZYXWVUT&SRRQQPPONNMLLKJJIHGGFEDDCBAA@?>>=<;::987/654432100/.--,++*)(('&&%$##"!!   L !!"##$%&&'(()*++,-../0112344567889:;;<=>??@ABBCDEEFGHHIJJKLLMNOPPQRRSSTTUUVWWXXYZ[\]^_^]\[ZYXWV+UTTSSRRQPPOONMMLKKJIIHGFFEDCCBA@@?>=<<;:9987645432210//.-,,+**)(''&%%$#""!   H !""#$$%&''())*+,,-.//0122345667899:;<==>?@@ABCCDEFFGHIIJKKLMMNOPQRRSSTUUVVWWXXYYZ[\]^_^]\[ZYXWVUTS'RQQPPONNMLLKJJIHHGFEEDCBBA@??>=<;;:98876754432110/..-,++*))('&&%$$#""!   K !!"##$%%&'(()**+,--./0012334567789::;<=>>?@AABCDDEFGGHIJJKLLMNNOOPQQRSSTTUUVVWWXXYYZ[\]^_^]\[ZYXWVU,TSSRRQQPOONMMLKKJIIHGGFEDDCBAA@?>==<;::98766514332100/.--,+**)(('&%%$##"!!   P !!"##$%&&'(()*++,-../0112345567889:;<<=>??@ABBCDEEFGHHIJJKLLMNNOPPQRRSSTTUVVWWXXYZ[\]^_^]\[ZYXWVUTSR(QPPONNMMLKKJIHHGFFEDCCBA@??>=<<;:98876554232210//.-,,+*))(''&%$$#""!   L !""#$$%&''())*+,,-.//0123345667899:;<==>?@@ABCCDEFFGHIIJKKLMMNOOPQQRRSSTUUVVWWXXYYZ[\]^_^]\[ZYXWVUT-SRRQQPPONNMLLKJJIHGGFEDDCBAA@?>>=<;;:98776544332110/..-,++*)(('&&%$##"!!   O !!"##$%%&'(()**+,--./0012344567789:;;<=>>?@AABCDEEFGGHIJJKLLMNNOPPQQRSSTTUUVVWWXXYYZ[\]^_^]\[ZYXWV2UTTSSRRQPPOONMMLKKJIIHGFFEDCCBA@@?>==<;:99876654332-10//.--,+**)(''&%%$##"!!   T !!"##$%&&'(()*++,-../0112345567889:;<<=>??@ABBCDEEFGHHIJJKLMMNNOPPQRRSTUVVWWXXYZ[\]^_^]\[ZYXWVUTS.RQQPPONNMLLKJJIHHGFEEDCBBA@??>=<;;:98876554321100/..-,++*))('&&%$$#""!   P !""#$$%&''())*+,,-.//0123345667899:;<==>?@@ABCDDEFFGHIIJKKLMMNOOPQQRRS STUUVVWWXXYYZ[\]^_^]\[ZYXWVU3TSSRRQQPOONMMLKKJIIHGGFEDDCBAA@?>==<;::9877654332100(/.--,+**)(('&%%$##"!!   S !!"##$%%&'(()**+,--./0012344567789:;;<=>>?@AABCDEEFGGHIJJKLLMNNOPPQQRSST TUUVVWWXXYYZ[\]^_^]\[ZYXWVUTSR/QPPONNMMLKKJIHHGFFEDCCBA@??>=<<;:9987655432210//+.-,,+*))(''&%$$#""!   X !!"##$%&&'())*++,-../0122345567889:;<<=>??@ABCCDEFFGHHIJKKLMMNNOPPQRRSSTTUVVWWXXYZ[\]^_^]\[ZYXWVUT4SRRQQPOONNMLLKJJIHGGFEEDCBAA@?>>=<;;:9877654432110/..,-,++*)(('&&%$$#""!   T !""#$$%&''())*+,,-.//012334566789::;<==>?@@ABCDDEFFGHIIJKKLMMNOOPQQRRSSTUUVWXYZ[\]^_^]\[ZYXWV9UTTSSRRQPPOONMMLKKJIIHGFFEDCCBA@@?>==<;:99876654332100/.--&,+**)(''&%%$##"!!   W !!"##$%%&'(()**+,--./0012344567789:;;<=>>?@ABBCDEEFGGHIJJKLLMNNOPPQQRSSTTUUVVWWXXYYZ[\]^_^]\[ZYXWVUTS5RQQPPONNMLLKJJIHHGFEEDCBBA@??>=<;;:9887655432110/..-,,)+*))('&&%$$#""!   Z !!"##$%&&'())*++,-../0122345567889:;<<=>??@ABCCDEFFGHHIJKKLMMNNOPPQRRSSTTUVVWWXXYZ[\]^_^]\[ZYXWVU:TSSRRQQPOONMMLKKJIIHGGFEDDCBAA@?>>=<;::9877654332100/.--,++*)(('&&%$##"!!   _ !""#$$%&''())*+,,-.//012334566789::;<==>?@@ABCDDEFFGHIIJKKLMMNOOPQQRRSTTUUVVWWXYZ[\]^_^]\[ZYXWVUTSR6QPPONNMMLKKJIHHGFFEDCCBA@@?>=<<;:9987655432210//.-,,+**$)(''&%%$#""!   [ !"##$%%&'(()**+,--./0012344567789:;;<=>>?@ABBCDEEFGGHIJJKLLMNNOPPQQRSSTTUUVVWWXXYYZ[\]^_^]\[ZYXWVUT;SRRQQPOONNMLLKJJIHGGFEEDCBAA@?>>=<;;:9887654432110/..-,++*))%('&&%$$#""!   h !!"##$%&&'(()*++,-../0122345567889:;<<=>??@ABCCDEFFGHHIJKKLMMNNOPPQRRSSTTUVVWWXYZ[\]^_^]\[ZYXWBVUUTTSSRRQPPOONMMLKKJIIHGFFEDCCBA@@?>==<;::9876654332100/.--,+**)(('&%%$##"!!   a !""#$$%&''())*+,,-.//012334566789::;<==>?@AABCDDEFFGHIIJKKLMMNOOPQQRRSTTUUVVWWXXYYYZZ[\]^_^]\[ZYXWVUTS=<<;:9887655432210//.-,,+*))('' &%$$#""!   f !""#$%%&''()**+,--./0012344567789:;;<=>>?@ABBCDEEFGGHIJJKLLMNNOPPQQRSSTTUUVVWWXXYYZZ[\]^_^]\[ZYXWVUATSSRRQQPOONMMLKKJIIHGGFEDDCBAA@?>>=<;::9877654432100/.--,++*)(('&&!%$##"!!   d !!"##$%&&'(()*++,-../0112345567889:;<<=>??@ABCCDEFFGHHIJKKLMMNNOPPQRRSSTTUUVWWXYZZ[[\]^_^]\[ZYXWVUTSR=QPPONNMMLKKJIHHGFFEDCCBA@@?>=<<;:9987665432210//.-,,+**)(''&%%$##"!!   ] !!"#$$%&&'())*+,,-.//012334566789::;<==>?@AABCDDEFFGHIIJKKLMMNOOPQQRRSSTUUVVWWXXYZ[[\]^_^]\[ZYXWVUTBSRRQQPOONNMLLKJJIHGGFEEDCBBA@?>>=<;;:9887654432110/..-,++*))('&&%$$#""!   f !""#$$%&''()**+,--./0012344567789:;;<=>>?@ABBCDEEFGGHIJJKLLMNNOPPQQRSSTTUUVVWWXXYYZ[[\]^_^]\[ZYXWVUTSR@QPPOONMMLKKJIIHGFFEDCCBA@@?>==<;::9876654332100/.--,+**)(('&%%$##"!!   ` !"##$%%&'(()*++,-../0112345567889:;<<=>??@ABCCDEFFGHHIJKKLMMNNOPPQRRSSTTUUVWWXYZ[\\]^_^]\[ZYXWVUTSCRQQPPONNMLLKJJIHHGFEEDCBBA@??>=<<;:9887655432210//.-,,+*))(''&%$$#""!   [ !!"##$%&&'())*+,,-.//012234566789::;<==>?@AABCDDEFFGHIIJKKLMMNOOPQQRRSSTUUVVWWXXYZ[\\]^_^]\[ZYXWVJUTTSSRRQQPOONMMLKKJIIHGGFEDDCBAA@?>>=<;::9877654432110/..-,++*)(('&&%$$#"!!   b !!"#$$%&''())*+,--./0012334567789:;;<=>>?@ABBCDEEFGGHIJJKLLMNNOPPQQRSSTTUUVVWWXXYYZ[\]^_^]\[ZYXWVUTSFRQQPPONNMMLKKJIHHGFFEDCCBA@@?>=<<;:9987665433210//.--,+**)(''&%%$##"!!   a !""#$%%&''()**+,--./0112345567889:;<<=>??@ABCCDEEFGHHIJJKLMMNNOPPQRRSSTTUUVVWWXXYYZ[\]]^_^]\[ZYXWVUKTSSRRQQPOONNMLLKJJIHGGFEEDCBBA@?>>=<;;:9887654432110/..-,++*))('&&%$$#""!   Y !"##$%%&'(()*++,-../0122345667899:;<==>?@@ABCDDEFFGHIIJKKLMMNOOPQQRRSSTUUVVWWXXYZ[\]]^_^]\[ZYXWVUTSRGQPPOONMMLKKJIIHGFFEDCCBA@@?>==<;::9876654332100/.--,+**)(('&%%$##"!!    \ !!"##$%&&'())*+,,-.//012334567789::;<=>>?@ABBCDDEFGGHIJJKLLMNNOOPQQRSSTTUUVVWWXXYZ[\]^_^]\[ZYXWVUTLSRRQQPPONNMLLKJJIHHGFEEDCBBA@??>=<<;:9987655432210//.-,,+*))(''&%%$#""!   ] !!"#$$%&''()**+,--./0012344567889:;<<=>??@ABBCDEEFGHHIJJKLLMNNOPPQRRSSTTUUVVWWXXYYZ[\]^^_^]\[ZYXWVQUTTSSRRQQPOONMMLKKJIIHGGFEDDCBAA@?>>=<;;:9877654432110/..-,++*)(('&&%$$#""!   W !""#$$%&''()**+,-../0112345567899:;<==>?@@ABCCDEFFGHIIJKKLMMNOOPPQRRSSTUUVVWWXXYZ[\]^^_^]\[ZYXWVUTSMRQQPPONNMMLKJJIHHGFFEDCCBA@@?>==<;:99876654332100/.--,+**)(''&%%$##"!!    T !""#$%%&'(()*++,-../012234566789::;<=>>?@AABCDDEFGGHIIJKLLMNNOOPQQRRSTTUUVVWWXXYZ[\]^^_^]\[ZYXWVURTSSRRQQPOONNMLLKJJIHGGFEEDCBBA@?>>=<;;:9887655432110/..-,,+*))(''&%$$#""!    Y !!"##$%&&'())*+,,-.//012334567789:;;<=>??@ABBCDEEFGHHIJJKLLMNNOPPQQRSSTTUUVVWWXXYYZ[\]^^_^]\[ZYXWVUTSRNQPPOONMMLKKJIIHGFFEDCCBA@@?>==<;::9877654332100/.--,++*)(('&&%$##"!!    U !!"##$%&&'())*+,--./0012344567889:;<<=>?@@ABCCDEFFGHHIJKKLMMNOOPPQRRSSTUUVVWWXXYZ[\]^^_^]\[ZYXWVUTSSRRQQPPONNMLLKJJIHHGFEEDCBBA@??>=<<;:9987655432210//.-,,+**)(''&%%$#""!     R !!"#$$%&''()**+,-../0112345567899:;<==>?@AABCDDEFGGHIIJKKLMMNOOPQQRRSTTUUVVWWXXYZ[\]^_^]\[ZYXWVUTSRQQPPOONMMLKKJIIHGGFEDDCBAA@?>>=<;;:9877654432110/..-,++*))('&&%$$#""!     S !""#$$%&''()*++,-../012234566789::;<=>>?@AABCDEEFGGHIJJKLLMNNOPPQQRSSTTUUVVWWXXYYZ[\]^__^]\[ZYXWVUTSTRQQPPONNMMLKJJIHHGFFEDCCBA@@?>==<;:99876654332100/.--,+**)(('&%%$##"!!   O !""#$%%&'(()*++,-.//012334567789:;;<=>??@ABBCDEEFGHHIJJKLMMNNOPPQRRSSTTUVVWWXYZ[\]^__^]\[ZYXWV[UTTSSRRQQPOONNMLLKJJIHGGFEEDCBBA@??>=<;;:9887655432110/..-,,+*))(''&%$$#""!   P !"##$%%&'())*+,,-./0012344567889:;<<=>?@@ABCCDEFFGHIIJKKLMMNOOPQQRRSSTUUVVWWXXYZ[\]^__^]\[ZYXWVUTSRUQPPOONMMLKKJIIHGFFEDDCBA@@?>==<;::9877654332100/.--,++*)(('&&%$##"!!   O !!"##$%&&'())*+,--./0112345567899:;<==>?@AABCDDEFGGHIIJKLLMNNOOPQQRSSTTUUVVWWXXYZ[\]^__^]\[ZYXWVUTZSRRQQPOONNMLLKJJIHHGFEEDCBBA@??>=<<;:9987655432210//.-,,+**)(''&%%$##"!!    !N"#$$%&&'()**+,--./011234566789::;<=>>?@ABBCDEEFGGHIJJKLLMNNOPPQQRSSTTUUVVWWXXYYZ[\]^_^]\[ZYXWVUTSRXQPPOONMMLKKJIIHGGFEDDCBAA@?>>=<;;:9877654432110/..-,++*))('&&%$$#""!    !J!"#$$%&''()**+,-../012234566789:;;<=>??@ABBCDEEFGHHIJKKLMMNOOPPQRRSSTUUVVWWXYZ[\]^_^^]\[ZYXWVUTS[RQQPPONNMLLKJJIHHGFFEDCCBA@@?>==<;:99876654332100/.--,+**)(('&%%$##"!!    !"K"#$$%&''()*++,-.//012334567789:;<<=>??@ABCCDEFFGHIIJKKLMMNOOPQQRRSTTUUVVWWXXYZ[\]^_^^]\[ZYXWVUTSRQYPOONMMLLKJJIHGGFEEDCBBA@??>=<;;:9887655432210//.-,,+*))(''&%$$#""!    !""J#$%%&'(()*++,-.//012344567889:;<<=>?@@ABCDDEFGGHIIJKLLMNNOOPQQRSSTTUUVVWWXXYZ[\]^^]\[ZYXWVUTS^RQQPPOONMMLKKJIIHGFFEDCCBA@@?>==<;::9877654332100/.--,++*)(('&&%$$#"!!    !""#K$%%&'(()*+,,-./0012344567899:;<==>?@AABCDDEFGGHIJJKLLMNNOPPQRRSSTTUUVVWWXXYYZ[\]^^]\[ZYXWVU_TSSRRQQPOONNMLLKJJIHHGFEEDCBBA@??>=<<;:9987655432210//.-,,+**)(''&%%$##"!!    !""#$%C&'())*+,,-./011234556789::;<=>>?@ABBCDEEFGHHIJJKLMMNOOPPQRRSSTUUVVWWXYZ[\]^^]\[ZYXWVUTSR_QPPOONMMLKKJIIHGGFEDDCBAA@?>>=<;;:9877654432110/..-,++*))('&&%$$#""!    !"##$%F%&'())*+,--./011234566789::;<=>>?@ABBCDEEFGHHIJKKLMMNOOPQQRRSTTUUVVWWXXYZ[\]^]]\[ZYXWVUTYSRRQQPPONNMLLKJJIHHGFFEDCCBA@@?>==<;:99876654332100/.--,+**)(('&%%$##"!!    !"##$%&E&'())*+,--./012234566789:;;<=>??@ABCCDEFFGHIIJKKLMNNOOPQQRSSTTUUVVWWXXYZ[\]^]]\[ZYXWVUTSRQZPOONMMLLKJJIHGGFEEDCBBA@??>=<;;:9887655432210//.-,,+*))(''&%$$#""!    !"##$%&&F'()**+,-../012234567789:;<<=>?@@ABCCDEFGGHIIJKLLMNNOPPQQRSSTTUUVVWWXXYYZ[\]^]]\[ZYXWVUTS`RQQPPONNMMLKKJIIHGFFEDCCBA@@?>==<;::9877654432100/..-,++*)(('&&%$$#"!!    !"##$%&&'@()**+,-../012334567789:;<<=>?@@ABCDDEFGGHIJJKLLMNNOPPQRRSSTTUVVWWXYZ[\]^]\[ZYXWVUTSRQXPOONNMLLKJJIHHGFEEDCBBA@??>=<<;:9987655432210//.-,,+**)(''&%%$##"!!    !"##$%&&'(?)**+,-../012334567889:;<==>?@AABCDDEFGHHIJJKLMMNOOPPQRRSSTUUVVWWXYZ[\]^]\\[ZYXWVUTSR\QPPOONMMLKKJIIHGGFEDDCBAA@?>>=<;;:9877654432110/..-,++*))('&&%$$#""!    !"##$%&&'()*>+,-.//012334567889:;<==>?@AABCDEEFGHHIJKKLMMNOOPQQRRSTTUUVVWWXXYZ[\]^]\[ZYXWVUdTSSRRQQPPONNMLLKJJIHHGFFEDCCBA@@?>==<;:99876654332100/.--,+**)(('&%%$##"!!   & !"##$%&&'()*?*+,-.//012344567899:;<=>>?@ABBCDEEFGHHIJKKLMMNOOPQQRSSTTUUVVWWXXYZ[\]^]\[[ZYXWVUTSRZQPPOONMMLLKJIIHGGFEEDCBBA@??>=<;;:9887655432210//.-,,+*))(''&%$$#""!   % !"##$%&&'()**>+,-.//012344567899:;<=>>?@ABBCDEFFGHIIJKKLMNNOPPQQRSSTTUUVVWWXXYZ[\]^]\[[ZYXWVUTbSRRQQPPONNMMLKKJIIHGFFEDCCBA@@?>==<;::9877654432100/..-,++*)(('&&%$$#""!   $ !""#$%&&'()**+?,-.//01234456789::;<=>>?@ABCCDEFFGHIIJKLLMNNOPPQRRSSTTUUVVWWXXYYZ[\]^]\[ZZYXWVUTSRQVPOONNMLLKJJIHHGFEEDCBBA@??>=<<;:9987655432210//.--,+**)(''&%%$##"!!   # !""#$%&&'()**+,7-.//01234456789::;<=>??@ABCCDEFFGHIIJKLLMNNOPPQRRSSTTUVVWXYZ[\]^]\[ZZYXWVUTS^RQQPPOONMMLKKJIIHGGFEDDCBAA@?>>=<;;:9877654432110/..-,++*))('&&%$$#""!   " !""#$%%&'()**+,-6.//01234456789::;<=>??@ABCCDEFGGHIJJKLLMNNOPPQRRSSTUUVVWXYZ[\]^]\[ZYYXWVUTSRQ]POONNMLLKJJIHHGFFEDCCBA@@?>==<;:99876654332100/.--,+**)(('&&%$##"!!   * !!"#$%%&'())*+,-.7./01234456789::;<=>??@ABCCDEFGGHIJJKLLMNOOPPQRRSSTUUVVWWXYZ[\]^]\[ZYiYXXWWVVUUTTSSRRQPPOONMMLKKJIIHGGFEEDCBBA@??>=<;;:9887655432210//.-,,+*))(''&%%$#""!   ' !!"#$$%&'())*+,-..6/01234456789::;<=>??@ABCDDEFGGHIJJKLMMNOOPQQRRSTTUUVVWWXYZ[\]^]\[ZYXXWVUdTSSRRQQPPONNMLLKKJIIHGFFEDCCBA@@?>==<;::9877654432110/..-,++*)(('&&%$$#""!   & !"#$$%&'(()*+,--./501233456789::;<=>??@ABCDDEFGGHIJJKLMMNOOPQQRRSTTUUVVWWXYZ[\]^]\[ZYXWVUTSRXQPPOONMMLLKJJIHHGFEEDCBBA@??>=<<;:9987655432210//.--,+**)(('&%%$##"!!   % !"##$%&''()*+,--./0412334567899:;<=>??@ABCDDEFGGHIJJKLMMNOOPQQRRSTTUUVVWWXYZ[\]^]\[ZYXWgWVVUUTTSRRQQPPONNMMLKKJIIHGGFEDDCBAA@?>>=<;;:9877654432110/..-,,+*))(''&%$$#""!   - !"##$%&''()*+,,-./012334567899:;<=>??@ABCCDEFGGHIJJKLMMNOOPQQRRSTTUUVVWWXXYZ[\]^]\[ZYXWVhVUUTTSSRRQQPOONNMLLKJJIHHGFFEDCCBA@@?>==<;:99876654332100/.--,++*)(('&&%$##"!!   * !""#$%&&'()*++,-./0112234567889:;<=>>?@ABCCDEFGGHIJJKLMMNOOPQQRRSTTUUVVWWXYZ[\]\[ZYXWVUTS\RQQPPOONMMLKKJIIHGGFEDDCBBA@?>>=<;;:9887655432210//.-,,+**)(''&%%$##"!!   ) !!"#$%%&'()**+,-./0012134567789:;<=>>?@ABCCDEFGGHIJJKLMMNOOPQQRRSTTUUVVWWXYZ[\]\[ZYXWVUTSRQ]POONNMLLKJJIHHGFFEDCCBA@@?>==<;::9877654432110/..-,++*))('&&%$$#""!   / !"#$$%&'())*+,-.//012304566789:;<==>?@ABBCDEFGGHIJJKLMMNOOPQQRRSTTUUVVWWXYZ[\]\[ZYXWVUT[TSSRRQPPOONMMLLKJJIHGGFEEDCBBA@??>=<<;:9987655432210//.--,+**)(('&%%$##"!!   , !"##$%&'(()*+,-../012345-6789:;<<=>?@ABBCDEFFGHIJJKLLMNOOPPQRRSSTUUVVWWXYZ[\]\[ZYXWVUTS`SRRQQPPONNMMLKKJIIHGFFEDDCBAA@?>>=<;;:9877654432110/..-,,+*))(''&%$$#""!   + !""#$%&''()*+,--./012344,56789:;;<=>?@AABCDEFFGHIIJKLLMNNOPPQRRSSTUUVVWXYZ[\]\[ZYXWVUTSR_QPPOONNMLLKJJIHHGFEEDCCBA@@?>==<;:99876654332100/.--,++*)(('&&%$##"!!   1 !!"#$%&&'()*++,-./012334506789::;<=>?@@ABCDEEFGHIIJKLLMNNOPPQRRSSTTUUVVWWXXYZ[\]\[ZYXWVUTSRQPWONNMMLKKJIIHGGFEDDCBBA@?>>=<;;:9887655432210//.-,,+**)(''&%%$##"!!   . !"#$$%&'()**+,-./01123456/7899:;<=>??@ABCDDEFGHHIJKKLMNNOPPQQRSSTTUUVVWWXXYZ[\]\[ZYXWVUTSRQ\QPOONNMLLKJJIHHGFFEDCCBA@@?>==<;::9877654432110/..-,++*))('&&%$$#""!   4 !"##$%&'(()*+,-.//01234567,789:;<=>>?@ABCCDEFGHHIJKKLMMNOOPQQRSSTTUUVVWWXYZ[\]\[ZYXWVUTSRRQQP]POONMMLKKJIIHGGFEEDCBBA@??>=<<;:9987655432210//.--,+**)(('&%%$##"!!   1 !!"#$%&&'()*+,--./012345567+89:;<==>?@ABBCDEFGGHIJJKLMMNOOPQQRRSTTUUVVWWXYZ[\]\[ZYXWVUTSRQPOYONNMLLKKJIHHGFFEDDCBAA@?>>=<;;:9877654432110/..-,,+*))(''&%$$#""!   7 !"#$%%&'()*++,-./0123345678&9:;;<=>?@AABCDEFFGHIIJKLLMNOOPPQRRSTTUUVWXYZ[\[ZYXWVUTSRQQPPOOZNMMLLKJJIHHGFEEDCCBA@@?>==<;:99876654332100/.--,++*)(('&&%$##"!!   4 !"##$%&'())*+,-./01123456789*9:;<=>?@@ABCDEEFGHIIJKLLMNNOPPQRRSSTTUUVVWWXYZ[\[ZYXWVUTSRQPONNMPLKKJIIHGGFEDDCBAA@?>>=<;;:9887655432210//.-,,+**)(''&%%$##"!!   1 !!"#$%&''()*+,-../01234567789':;<=>>?@ABCDDEFGHHIJKKLMMNOPPQQRSSTTUUVVWXYZ[\[ZYXWVUTS RQQPPOONMMLSKJJIHHGFFEDCCBA@@?>==<;::9877654432110/..-,++*))('&&%$$#""!   5 !"#$%%&'()*++,-./01234456789:$;<==>?@ABBCDEFGGHIJJKLMMNOOPQQRSSTTUUVWXYZ[\[ZYXWVUTSRQPONNMMLKTJIIHGGFEEDCBBA@??>=<<;:9987655432210//.--,+**)(('&%%$##"!!   2 !"##$%&'())*+,-./01123456789::(;<=>?@AABCDEFFGHIIJKLLMNNOPPQRRSSTTUUVVWWXYZ[\[ZYXWVUTSR QPPOONNMLLKJNIHHGFFEDCCBAA@?>>=<;;:9877654432110/..-,,+*))(''&%$$#""!   6 !!"#$%&&'()*+,-../01234567789:;%<=>??@ABCDDEFGHHIJKKLMNNOPPQRRSSTTUUVVWXYZ[\[ZYXWVUTSRQPONNMMLKKJIQHGGFEEDCBBA@??>=<<;:99876654332100/.--,++*)(('&&%$##"!!   5 !"#$$%&'()**+,-./01234456789:;<=%>?@ABCCDEFGGHIJJKLMMNOOPQQRRSSTTUUVVWWXYZ[ZYXWVUTSR QPPOONNMLLKJJIKIHGFFEDDCBAA@?>>=<;;:9887655432210//.-,,+**)(''&%%$##"!!   7 !!"#$%&''()*+,-./00123456789::;<$=>?@AABCDEEFGHIIJKLLMNNOPPQRRSSTTUUVVWXYZ[ZYXWVUTSRQPO NMMLKKJJIHNHGFEEDCCBA@@?>==<;::9877654432110/..-,++*))('&&%$$#""!   4 !"#$$%&'()*+,,-./01234566789:;<=>$?@ABCDDEFGHHIJKKLMNNOPPQQRRSSTTUUVVWWXYZ[ZYXWVUTSRQPPOONNMLLKKJIIHGOGFEDDCBBA@??>=<<;:9987655432210//.--,+**)(('&%%$##"!!   8 !""#$%&'(()*+,-./01223456789:;;<=!>?@ABBCDEFFGHIJJKLLMNOOPPQRRSSTTUUVWXYZ[ZYXWVUTSRQPO NMMLLKJJIHHGFIFEDCCBA@@?>==<;::9877654432110/..-,,+*))(''&%$$#""!   < !"#$%%&'()*+,--./01234567789:;<=>?!@ABCDDEFGHHIJKKLMNNOPPQQRRSSTTUUVVWXYZYXWVUTSRQPPOONNMLLKKJIIHGGFELEDCBBA@??>=<<;:99876654332100/.--,++*)(('&&%$$#"!!   9 !""#$%&'())*+,-./01223456789:;<<=> ?@ABBCDEFFGHIJJKLMMNOOPPQQRSSTTUUVWXYZYXWVUTSRQPONMMLLKJJIHHGFFEDDDCBAA@?>>=<;;:9887655432210//.-,,+**)(''&%%$##"!!   ; !"#$%%&'()*+,--./01234567889:;<=>?@ABCDEEFGHHIJKLLMNNOOPQQRRSSTTUUVWXYZYXWVUTSRQPONM LKKJIIHGGFEEDCBEA@@?>==<;::9877654432110/..-,++*))('&&%$$#""!   8 !""#$%&'())*+,-./01233456789:;<<=>?@ABBCDEFGGHIJJKLMMNNOPPQQRRSTUVWXYZYXWVUTSRQPONMMLLKJJIHHGFFEDDCBAF@?>>=<;;:9887655432210//.--,+**)(('&%%$##"!!   < !"#$$%&'()*+,--./01234567889:;<=>??@ABCDDEFGHHIJKKLMMNOOPPQRRSSTTUVWXYXWVUTSRQPONMLLKKJIIHGGFEEDCCBA@@?>==<;::9877654432110/..-,,+*))(''&%$$#""!   9 !!"#$%&'(()*+,-./01223456789:;<<=>?@ABBCDEFFGHIJJKLLMNNOOPQQRRSSTTUUVWXYXWVUTSRQPONNMMLLKJJIHHGFFEDDCBBA@?C>=<<;:99876654332100/.--,++*)(('&&%$$#"!!   ; !"#$$%&'()*+,,-./01234567789:;<=>??@ABCDDEFGHHIJKKLMMNNOPPQQRRSSTTUUVWXYXWVUTSRQPONMLLKKJIIHHGFEEDCCBA@@?>;=<;;:9887655432210//.-,,+**)(''&%%$##"!!   ? !"#$%&''()*+,-./00123456789::;<=>?@AABCDEFFGHIIJKKLMMNOOPPQRRSSTUVWXWVUTSRQPONMLKJJIHHGGFEDDCBBA@??>@=<<;:99876654332100/..-,++*))('&&%$$#""!   : !""#$%&'()**+,-./01234556789:;<==>?@ABCCDEFGGHIJJKLLMNNOOPQQRRSSTUVWXWVUTSRQPONMLLKKJIIHHGFFEDCCBAA@?>>=A<;;:9887655432210//.--,+**)(('&%%$##"!!   < !"#$%%&'()*+,-../01234567889:;<=>??@ABCDEEFGHHIJJKLLMNNOPPQQRRSSTUVWXWVUTSRQPONMLKJJIHHGGFEDDCBBA@@?>==<;;::9877654432110/..-,,+*))(''&%$$#""!   9 !!"#$%&'(()*+,-./01223456789:;;<=>?@AABCDEFFGHIIJKKLMMNNOPPQQRRSTUVWVUTSRQPONMMLLKKJIIHHGFFEDCCBAA@?>>=<<;<:99876654332100/.--,++*)(('&&%$$#"!!   ; !"##$%&'()*++,-./01234556789:;<==>?@ABCDEFGGHIIJKKLMMNOOPPQQRRSTUVWVUTSRQPONMLKKJJIHHGGFEEDCBBA@@?>==<;::69877654432110//.-,,+**)(''&%%$##"!!   = !"#$%%&'()*+,-../01234567889:;<=>??@ABCDDEFGGHIJJKLLMMNOOPPQQRSTUVWVUTSRQPONMLKJIIHHGFFEDCCBAA@??>=<<;:999876654332100/..-,++*))('&&%$$#""!   A !!"#$%&'(()*+,-./01123456789::;<=>?@AABCDEEFGHHIJJKLLMNNOOPPQQRSTUVUTSRQPON MLLKKJJIHHGGFEEDCBBA@@?>==<;;:988:7655432210//.--,+**)(('&%%$##"!!   < !"##$%&'()**+,-./01234456789:;<<=>?@ABBCDEEFGHHIJJKLLMNNOOPPQRSTUVUTSRQPONMLKJJIIHGGFFEDCCBAA@??>=<<;:998764654432110/..-,,+*))(''&%$$#""!   > !"#$%%&'()*+,--./01234567789:;<=>>?@ABBCDEFFGHHIJJKLLMMNOPQRSTUTSRQPONMLKJIIHHGFFEEDCBBA@@?>>=<;;:988765554322100/.--,++*)(('&&%$$#"!!   @ !!"#$%&''()*+,-.//01234567899:;<=>??@ABCCDEFFGHHIJJKLLMMNNOOPPQRSTUTSRQPONMLKJIHGGFEEDCCBAA@??>=<<;::9877654-432110//.-,,+**)(''&%%$##"!!   ; !""#$%&'(()*+,-./01223456789::;<=>??@ABCCDEFFGHIIJJKLLMMNNOOPQRSTSRQPONML#KJJIIHHGFFEDDCBBA@@?>>=<;;:988766543232100/.--,++*))('&&%$$#""!   = !"#$$%&'()**+,-./01234456789:;;<=>?@@ABCDEFFGHHIJJKKLMMNNOPQRSTSRQPONMLKJ!IHHGGFEEDCCBAA@??>=<<;::98776544323210//.-,,+**)(('&%%$##"!!   ? !"#$%%&'()*+,,-./01234556789:;<<=>?@@ABCDEFFGHHIJJKKLLMMNNOOPQRSRQPONMLKJI!HGGFFEDDCBBA@@?>>=<;;:9987665433210+/..-,++*))(''&%$$#""!   A !!"#$%&&'()*+,-../01234567789:;<<=>?@AABCDEFFGHHIIJKKLLMMNNOPQRSRQPONMLKJIH!GFFEEDCCBAA@??>==<;::9877655432210/,.--,+**)(('&&%$##"!!   < !""#$%&'(()*+,-./001234567789:;<==>?@AABCCDEFFGHHIIJJKKLLMMNNOPQRQPONMLKJIHG!FEEDDCBBA@@?>>=<;;:99876654332110/.$-,,+*))(''&%%$##"!!   > !"##$%&'())*+,-./011234567889:;<==>?@AABCCDEEFGGHIIJJKKLLMMNOPQRQPONMLKJI&HGGFFEDDCCBAA@??>=<<;::98876554322100/.-),++*)(('&&%$$#""!   > !"##$%&'()**+,-./011234567889:;<==>?@@ABCCDEEFGGHHIIJJKKLLMMNOPQPONMLKJIH&GFFEEDCCBBA@@?>>=<;;:99876654432110//.-,*+**)(''&%%$##"!!   @ !"#$$%&'()**+,-./012234567889:;<==>?@@ABBCDEEFFGHHIIJJKKLLMNOPONMLKJIHG&FEEDDCBBA@@?>>=<<;::98876554322100/.--,+"*))('&&%$$#""!   B !"#$%%&'()*++,-./012234567889:;<<=>??@ABBCDDEFFGGHHIIJJKKLLMNOPONMLKJIHGF&EDDCCBAA@??>==<;;:99876654432110//.-,,+*%)(('&%%$##"!!   = !"#$%%&'()*++,-./012234567889:;<<=>??@AABCDDEEFGGHHIIJJKKLMNONMLKJIHGFE&DCCBBA@@?>>=<<;::98876554332100/..-,++*)(''&%%$##"!!   = !"#$%&&'()*+,,-./012234567789:;;<=>>?@AABC DEEFFGGHHIIJJKLMNONMLKJIHGFED&CBBAA@??>==<;;:99876654432110//.--,+**)( '&&%$$#""!   ? !!"#$%&&'()*+,,-./012234567789:;;<=>>?@@AB BCDDEFFGGHHIIJKLMNMLKJIHGFEDC&BAA@@?>>=<<;::98776554332100/..-,,+*))('#&%%$##"!!   ? !!"#$%&&'()*+,,-./012234566789::;<==>?@@AB BCCDEEFFGGHHIIJKLMLKJIHGFEDCB&A@@?>>==<;;:98876654432210//.--,++*)(('&%$$#""!   A !!"#$%&''()*+,,-./011234566789::;<==>??@AA BCCDDEEFFGGHHIJKLKJIHGFEDCB(A@@??>==<;;:998776554332100/..-,,+**)(''&%$##"!!   A !""#$%&&'()*++,-./0112345567899:;<<=>>?@@ABCDEEFFGHIJKLKJIHGFEDCBA(@??>>=<<;::98876654432210//.--,++*))('&&%$#""!   A !""#$%&&'()*++,-./0012345567889:;;<=>>?@@A ABCCDDEEFFGHIJKJIHGFEDCBA@(?>>==<;;:998776554332110/..-,,+**)(('&%%$#"!   C !""#$%&&'()*++,-./0012344567889:;;<==>??@@ABBCCDDEEFGHIJIHGFEDCBA@*?>>==<;;::98876654432210//.--,++*))(''&%%$##"!!   C !!"#$%%&'()**+,-.//012334567789::;<<=>>?@@ABCDEFGHIJIHGFEDCBA@?*>==<<;::988776554332100/..-,,+**)(('&&%$$#""!   E !!"#$%%&'()**+,-../0122345667899:;;<==>??@@AABBCCDDEFGHIHGFEDCBA@?>*=<<;;:998776554332110//.--,++*))(''&%%$##"!!   E !!"#$%%&'())*+,-../0112345567889::;<<=>>??@ABCDEFGHGFEDCBA@?>,=<<;;:9988766544322100/..-,,+**)(('&&%$$#""!    > !"#$$%&'(()*+,--./01123445677899:;;<==>>??@ABCDEFGFEDCBA@?>=,<;;::988776554332110//.--,++*))(''&%%$##"!!   > !"#$$%&'(()*+,,-./00123345667889::;<<==>>?@@ABCDEFEDCBA@?>=.<;;::9987765544322100/..-,,+**)(('&&%$$#""!   < !"##$%&''()*++,-.//01223445677899:;;<<==>?@ABCDEFEDCBA@?>=0<;;::9987766544322110//.--,++*))(''&%%$##"!!   < !""#$%&&'()**+,--./011233456678899:;;<<==>?@ABCDEDCBA@?>=2<;;::9988766554332110//..-,,+**)(('&&%$$#""!!   E !""#$%%&'())*+,,-.//01223445667889::;;<<==>>??@ABCDCBA@?>=<;:987)65543322100/..--,++*))(''&%%$##""!   C !!"#$%%&'(()*++,-../011233455677899::;;<<=>?@ABCBA@?>=<;:987+65544322110//.--,++**)(('&&%$$#""!!   A !!"#$$%&''()**+,--./0012234456677899::;;<<==>?@ABA@?>=<;:9876+5443321100/..-,,+**))(''&%%$##""!   A !"##$%&&'())*+,,-../0112334556678899::;;<=>?@A@?>=<;:9876-5443322100/..--,++*))(('&&%$$##"!!    ? !""#$%%&'(()*++,--.//0122334556678899::;;<=>?@?>=<;:98765-43322100//.--,,+**)((''&%%$##""!   ? !!"#$$%&''()**+,,-../00122344556778899:::;;<=>?@?>=<;:98765/43322110//..-,,++*))(''&&%$$#""!!   8 !!"##$%&&'(()*++,--.//011233445567789:;<=>?>=<;:987651433221100/..--,++**)(('&&%%$##""!   ; !""#$%%&''()**+,,-../0011233445566778899:;<=>=<;:9876543210*/..--,,+**))(''&%%$$#""!!   9 !!"#$$%&&'(()*++,--../00122334455667788899:;<=<;:9876543210/*.--,,++*))(('&&%$$##"!!   ? !!"##$%%&''())*++,--.//0012233445566789:;<;:9876543210/.&-,,++*))((''&%%$$#""!!   8 !""#$$%&&'(()**+,,--.//001223344567789:;:9876543210/.,-,,++**)((''&&%$$##"!!   < !!"##$%%&''())*++,,-..//001122334455666789:9876543210/.-,,++**))(''&&%%$##""!   3 !""#$$%&&'(())*++,,-..//00112233456789876543210/.-*,++**))(('&&%%$$#""!!   0 !!"##$%%&&'(()**++,--..//00112234567876543210/.-0,++**))((''&&%$$##"!!   4 !""#$$%%&''(()**++,--..//00123445676543210/.-+,++**))((''&&%%$##""!!   4 !!""#$$%&&''())**++,,--..//0011233456543210/.-,+*)('&%$#""!!   - !!"##$%%&&''())**++,,--..//01234543210/.-,+*)('&%$#"!!   ( !""##$%%&&'(())**++,,--./012343210/.-,+*)('&%$#"!   * !!""#$$%%&&'(())**++,,-./01123210/.-,+*)('&%$#"!   # !!"##$$%%&''(())*+,-./001210/.-,+*)('&%$#"!   . !""##$$%%&&''(())**++,-.//01210/.-,+*)('&%$#"!   % !!""##$$%%&&''(())*+,-../010/.-,+*)('&%$#"!     !!""##$$%%&&''(()*+,-./0/.-,+*)('&%$#"!     !!""##$$%%&&'()*+,--./.-,+*)('&%$#"!    !!""##$$%&'()*+,,-.-,+*)('&%$#"!    !!""#$%&'()*++,-,+*)('&%$#"!     !!"#$%&'()**+,+*)('&%$#"!     !"#$%&'())*+*)('&%$#"!    !"#$%&'()*)('&%$#"!    !"#$%&'(()('&%$#"!    !"#$%&''('&%$#"!    !"#$%&&'&%$#"!    !"#$%%&%$#"!                    $"&$(**,0.228::<@@BHJLPRTXZ\^`dhlnqxzx~    ###%$%''')*)+,,/./.1-/./10230244344224445445676737766767778681340323151322422123123223244414423431233./-/,,/+.*--+,)*+,+*))*#&$$"##$#"                                            !   !"!!   !"#$#"!   !"#$%$#"!   !"#$%&%%$#"!   !"#$%&'&&%$#"!   !"#$%&'(''&%$#"!   !!""##$%&'()*)(('&%$#"!   !!""##$$%%&'()*+*))('&%$#"!   !""##$$%%&&'()*+,+**)('&%$#"!   !""##$$%%&&''(()*+,-.-,+*)('&%$#""!!  $ !""##$%%&&''(())*+,-./.--,+*)('&%$##"!!  $ !""##$%%&&''(())**++,-./0/..-,+*)('&%%$$#""! ( !""##$%%&&'(())**++,,-./010//.-,+*)('&&%$$#"" * !""##$%%&&'(())**++,,--..//01232100/.-,+* )((''&%%$#" . !!"##$%%&&'(()**++,,--..//0012343210/.--,,++*))(''&%%$# 2 !!"##$%%&&'(()**++,,-..//001234543210/.--,,+**)(('&%%$ + !""#$$%&&'(()**+,,--..//012345676543210/..-,,+**)(('&%% 6 !!"#$$%&&'(()**+,,--.//001122334456787655443322110//.--,++*)(('&% 3 !!"##$%%&''()**+,,-..//011223344556789876655443322100/..-,++*)(('& 5 !""#$$%&''())*+,,-../0011233445566789:;:9877665544332110/..-,++*)((' 2 !!"#$$%&&'())*++,-../0012233445667789:;<;:98876554432210//.-,,+*)(( 6 !"##$%%&'(()*++,--./0012234455667789:;<=<;:9876655432210//.-,,+*)( ? !!"#$$%&''()**+,--./00122344566778899::;;<=>?>=<;:9876654332100/.-,++*) : !"##$%&&'())*+,,-.//012234456678899::;;<<=>?@?>=<;:9877654432100/.-,+** @ !!"#$$%&'(()*++,-../01123445667889::;;<<==>>?@A@?>=<;:987765443210//.-,+* D !"##$%&&'()**+,--./00123445667889::;<<==>>??@ABCBA@?>=<;:988765443210/..-,+ ? !!"#$%%&'(()*+,,-.//0123345667889::;<<=>>??@@ABCDCBA@?>=<;;:988765443210/.-,, A !"##$%&''()**+,-../0122345667889:;;<==>>?@@AABCDEDCBA@@?>>=<<;:998765433210/.-, C !!"#$$%&'())*+,--./0112345567889:;;<==>??@@AABBCCDDEFGFEDCBAA@??>==<;:998765432110/.- E !""#$%&&'()*++,-./0012344567889:;;<==>??@AABBCCDDEEFGHGFEDCBBA@@?>==<;:98876543210/.- I !"#$$%&'(()*+,-../012334567789::;<==>??@AABCCDDEEFFGHIHGFEDCCBA@@?>==<;:98776543210/. D !""#$%&&'()*++,-./011234566789::;<==>?@@ABBCDDEEFFGGHIJKJIHGFEDCBAA@?>==<;:9876543210/. H !"#$$%&'(()*+,-../012344567899:;<==>?@@ABBCDDEFFGGHHIJKLKJIHGFEDCBAA@?>=<<;:9876543210/ C !!"#$%&&'()*++,-./011234567789:;<<=>?@@ABBCDDEFFGGHIJKLMLKJIHGFEDDCBAA@?>=<;::987654310/  J !"##$%&'(()*+,-../01234456789:;;<=>??@ABBCDEEFGGHHIIJJKKLMNMLKJIHGGFEEDCBAA@?>=<;:9876543210 N !!"#$%%&'()**+,-./011234567889:;<=>>?@ABBCDEEFGGHIIJJKKLLMNONMLKJIHHGFEEDCBA@??>=<;:987654210 X  !""#$%&''()*+,--./01234456789:;<<=>?@ABBCDEEFGGHIIJJKLLMNOPQPONMLKJJIHHGFEDDCBA@?>=<;:987654321 P !"#$$%&'())*+,-./001234567899:;<=>?@AABCDDEFGGHIIJKKLLMMNNOOPQRQPONMLKKJIHGFEDCCBA@?>=<;:98764321 T !""#$%&&'()*+,,-./01234456789:;<=>>?@ABCDDEFGGHIJJKKLMMNNOOPPQRSRQPONMLKKJIHHGFEDCBA@?>=<;:98765431 X !"##$%&'(()*+,-.//0123456789::;<=>?@ABBCDEFGGHIJJKLLMMNOOPPQRSTSRQPONMLKKJIHGGFEDCBA@?>=<;:9765432 R !!"#$%%&'()*++,-./01234456789:;<=>??@ABCDEFFGHIIJKLLMNNOOPPQQRRSTUTSRQPONNMLKKJIHGFEDCCA@?>=<;:9876432 O !""#$%&''()*+,-../0123456789::;<=>?@ABCDDEFGHIIJKLLMNNOPPQQRRSSTUVUTS RQQPPONNMLKJJIHGFEDCBA@?>=;:9876542 V !"#$$%&'()**+,-./01223456789:;<=>?@AABCDEFGHHIJKLLMNNOPPQQRSSTUVWVUTSRQPPONMMLKJIHGFEDCBA@?>=<;:876543 V !!"#$%&&'()*+,--./0123456789::;<=>?@ABCDEEFGHIJKKLMNNOPPQRRSSTTUUVWXWVUTSRRQPPONMLKJIHGFEDCBA?>=<;:986543 O !"##$%&'(()*+,-./00123456789:;<=>?@AABCDEFGHIIJKLMMNOPPQRRSSTUUVWXYXWVUTSRRQPOONMLKJIHGFEDCBA@?><;:987543 W !"#$%%&'()*++,-./01234567899:;<=>?@ABCDEFGGHIJKLLMNOPPQRRSTTUUVVWWXYZYXW VUUTTSRRQPONMLKJIHGFECBA@?>=<:987643 W !""#$%&''()*+,-../0123456789:;<=>?@AABCDEFGHIJKKLMNOOPQRRSTTUUVVWWXXYZ[ZYXW VUUTTSRQPPONMLKJIHGFEDCB@?>=<;987653 T !"##$%&'()**+,-./01234556789:;<=>?@ABCDEFGHHIJKLMNNOPQQRSTTUUVWWXXYZ[\[ZYXW VUUTSSRQPONMLLKIHGFEDCBA?>=<;:87654 T !!"#$%%&'()*+,--./0123456789:;<=>?@AABCDEFGHIJKLLMNOPPQRSSTUVVWWXXYYZZ[\]\[Z YXXWWVUUTSRQPPONMLKJIHFEDCBA@>=<;:87654 O !""#$%&'(()*+,-./01123456789:;<=>?@ABCDEFGHHIJKLMNOOPQRSSTUUVWWXXYZ[\]^]\[Z YXXWVVUTSSRQPONMLKJIHGFDCBA@?=<;:97654 S !"#$$%&'()*++,-./0123456789:;<=>??@ABCDEFGHIJKLMMNOPQRRSTUUVWWXYYZZ[[\]^_^]\[Z YXXWVUUTSRQPONMLKJIHGFECBA@?=<;:97654 U !""#$%&&'()*+,-../0123456789:;<=>?@ABCDEFGHHIJKLMNOPQQRSTTUVWWXYYZZ[[\\]]^_`_^]\[ ZYYXWWVUTSRRQPONMKJIHGFEDBA@?><;:97654 U !"##$%&'())*+,-./01234566789:;<=>?@ABCDEFGHIJKLMMNOPQRSSTUVVWXXYZZ[[\\]]^^_`_^]\[ ZYXXWVUUTSRQPONMLKIHGFEDBA@?><;:97654  P !!"#$%%&'()*+,,-./0123456789:;<=>?@ABCDEFGGHIJKLMNOPQQRSTUUVWXXYZZ[\\]]^_`a`_^]\ [ZZYXWVVUTSRQPONMLKJHGFEDCA@?><;:97654 P !""#$%&''()*+,-./00123456789:;<=>?@ABCDEFGHIJKLMMNOPQRSTTUVWWXYZZ[[\]]^^_`aba`_^]\\[[ZYXXWVUTSRQPONMLKJHGFEDCA@?><;:97653  P !"#$$%&'()**+,-./0123456789:;<=>?@ABCDEEFGHIJKLMNOPQQRSTUVVWXYYZ[[\]]^^__`abcba`_^] \[[ZYYXWVUTSRQPONMLKJHGFEDBA@?=<;:87653 P !!"#$%&&'()*+,--./0123456789:;<=>?@ABCDEFGHIJKLMMNOPQRSTTUVWXXYZZ[\\]^^__``abcba`_^]\\[ZZYXWVUTSRQPONMLKJHGFEDBA@?=<;:87653  N !"##$%&'(()*+,-./01234556789:;<=>?@ABCDEFGHIJKLMNOPQQRSTUVVWXYYZ[\\]^^__``abcdcba`_^^]]\[ZZYXWVUTSRQPONMLKJHGFECBA@>=<;987643 N !"#$%%&'()*++,-./0123456789:;<=>?@ABCDEFGHIJKKLMNOPQRSTTUVWXXYZ[[\]]^__``aabcdedcba`_^]]\[[ZYXWVUTSRQPONMLKIHGFECBA@>=<:987543 N !""#$%&''()*+,-../0123456789:;<=>?@ABCDEFGHIJKLMNOPPQRSTUVWWXYZZ[\\]^^_``aabbcdedcba`_^^]\[[ZYXWVUTSRQPONMLJIHGFDCBA?>=<:987543 L !"##$%&'())*+,-./0123456789:;<=>?@ABCDEFGHHIJKLMNOPQRSTTUVWXYYZ[\\]^^_``aabbcdefedcba`_^^]\[[ZYXWVUTSRQPONMKJIHGEDCB@?>=;:986542  !J!"#$%%&'()*+,,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPPQRSTUVWWXYZZ[\]]^__``abbcdefedcba``__^]\[[ZYXWVUTSRQPONLKJIHFEDCA@?><;:976532  !"H"#$%&''()*+,-./01223456789:;<=>?@ABCDEFGHIJKLMNOPQRSTTUVWXYYZ[\\]^^_``aabcdefgfedcba``__^]\[[ZYXWVUTSRQPNMLKJHGFEDBA@?=<;9876432  !"#$I%&'()**+,-./0123456789:;<=>?@ABCDEFGHIJKLMMNOPQRSTUVWWXYZZ[\]]^_``aabbccddefgfedcba`_^^]\[ZZYXWVUTSRPONMLKIHGFDCBA@>=<:9875432  !!"#$I%&&'()*+,--./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRRSTUVWXYYZ[\\]^__`aabbccddefghgfedcbbaa`_^^]\[ZYXXWVUSRQPONMKJIHGEDCB@?>=;:9865421  !"##$%G&'(()*+,-./0123456789:;<=>?@ABCDEFGHIIJKLMNOPQRSTUVVWXYZ[[\]]^_``aabccddefghgfedcbba``_^]]\[ZYXWVUTSRQPOMLKJIGFEDCA@?=<;:8765321  !"#$%%&J'()*++,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQQRSTUVWXXYZ[\\]^__`aabbccddeeffghihgfedcbba``_^]\\[ZYXWVUTSRPONMLKJHGFECBA@>=<;98754320   !""#$%&''H()*+,-.//0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUUVWXYZZ[\]^^_``abbccddeeffghihgfedcbba`__^]\[ZZYXWVUSRQPONMKJIHGEDCB@?>=;:98654310  !"##$%&'()*B+,-./0123456789:;<=>?@ABCDEFGHIJKLMNNOPQRSTUVWXXYZ[\\]^__`aabbcddeefghihgfedccbaa`_^^]\[ZYXWVUTSRQPNMLKJIGFEDCA@?=<;:87653210  !!"#$%%&'()*@+,--./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSSTUVWXYZZ[\]]^_``abbccdefghijihgfedccba``_^]\\[ZYXWVUTSQPONMLKIHGFDCBA?>=<:98754320/  !""#$%&'(()*+E,-./01234456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVVWXYZ[[\]^__`aabccddeeffgghijihgfedcbba`__^]\[ZYXWVUTSRQPOMLKJIHFEDCA@?><;:97654210/   !"#$$%&'()*++,A-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQQRSTUVWXYYZ[\]]^_``abbccdeeffghijihgfedccbaa`_^]]\[ZYXWVUTSRPONMLKIHGFECBA@>=<:98754321/. % !!"#$%&&'()*+,-.B./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUUVWXYZ[[\]^^_`aabbcddeeffgghijkjihgfeddcbba``_^]\[ZYXWVUTSRQPONLKJIHFEDCA@?><;:97654210/. ! !"##$%&'())*+,-./@0123456789:;<=>?@ABCDEFGHIJKLMMNOPQRSTUVWXXYZ[\\]^_``abbccdeeffgghijkjihgfedcbaa`_^]]\[ZYXWVUTSQPONMLKIHGFDCBA@>=<;98754321/.- & !"#$%%&'()*+,,-./0<123456789:;<=>?@ABCDEFGHIJKLMNOPQRSSTUVWXYZZ[\]^^_`aabbcddeefghijkjihgfeddccba`__^]\[ZYXWVUTSRQPOMLKJIGFEDCA@?><;:97654210/., + !""#$%&''()*+,-./001=23456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVVWXYZ[\\]^__`abbccdeeffgghijkjihgfedccbaa`_^]\\[ZYXWVUSRQPONMLJIHGFDCBA?>=<:98754321/.-, ' !"##$%&'()**+,-./01239456789:;<=>?@ABCDEFGHIJKLMNOPPQRSTUVWXYYZ[\]]^_``abbcddeefghijklkjihgfeddcbba`_^^]\[ZYXWVUTSRQONMLKJHGFEDBA@?><;:97653210/.,+ * !!"#$%%&'()*+,--./01234:56789:;<=>?@ABCDEFGHIJKLMNOPQRSTTUVWXYZ[[\]^__`aabccdeeffgghijklkjihgfedccba``_^]\[ZYXWVUTSRQPONLKJIHGEDCB@?>=<:98754320/.-,+ ( !""#$%&'(()*+,-./0123455;6789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWWXYZ[\\]^_``abbcddeeffgghhijklkjihg feedccbaa`_^]\\[ZYXWVUTRQPONMLKIHGFECBA@>=<;:87653210/-,+* + !"#$$%&'()*++,-./01234567789:;<=>?@ABCDEFGHIJKLMNOPQRRSTUVWXYZZ[\]^^_`aabccddeffgghijklkjihgfeddcbba`_^^]\[ZYXWVUTSRQONMLKJIGFEDCA@?><;:98654310/.-,*) 0 !!"#$%&&'()*+,-../01234567889:;<=>?@ABCDEFGHIJKLMNOPQRSTUVVWXYZ[\\]^__`abbccdeeffgghhijklkjihgf eddcbba`__^]\[ZYXWVUTSRQPONLKJIHGEDCB@?>=<:987643210.-,+*) , !"##$%&'())*+,-./0123456789:6;<=>?@ABCDEFGHIJKLMMNOPQRSTUVWXYYZ[\]]^_``abbcddeffgghhijklkjih$gffeedccba``_^]\[ZYXWVUTSRQPONMLJIHGFDCBA@>=<;:87654210/.-+*)( 1 !"#$%%&'()*+,,-./0123456789:;7<=>?@ABCDEFGHIJKLMNOPQRSSTUVWXYZ[[\]^__`aabccdeeffgghhiijklkjihg#feedccba``_^]\[ZZYXWVUTRQPONMLKIHGFEDBA@?><;:98654320/.-,+*(' 6 !""#$%&''()*+,-./00123456789:;<1=>?@ABCDEFGHIJKLMNOPQRSTUVWWXYZ[\\]^_``abbcddeefgghijklkjihgf"eddcbaa`_^]\\[ZYXWVUTSRPONMLKJIGFEDCA@?>=;:987543210.-,+*)(' 2 !"##$%&'()**+,-./0123456789:;<=>2?@ABCDEFGHIJKLMNOPQQRSTUVWXYYZ[\]^^_`aabccddeffgghhijklmlkjihgf#eddcbaa`_^]]\[ZYXWVUTSRQPNMLKJIHFEDCBA?>=<;987653210/.,+*)('& 5 !!"#$%%&'()*+,--./0123456789:;<=>?3@ABCDEFGHIJKLMNOPQRSTUUVWXYZ[[\]^__`abbccdeeffgghhiijklmlkjihgf$eddcbaa`_^^]\[ZYXWVUTSRQPONLKJIHGFDCBA@>=<;:87654310/.-,+)('&% 3 !""#$%&'(()*+,-./01234556789:;<=>?A/BCDEFGHIIJKLMNOPQRSTUVWXXYZ[\]]^_``abbcddeffgghhijklmlkjihgf%eddcbba`_^^]\[ZYXWVUTSRQPONMKJIHGFECBA@?=<;:97654321/.-,+*)(&%$ 6 !"#$$%&'()*++,-./0123456789:;<=>?@AB0CDEFGHIJKLMNOPQRRSTUVWXYZZ[\]^^_`aabccdeeffgghhiijklmlkjihgf&eddcbba`_^^]\[ZYXWVUTSRQPONMLJIHGFEDBA@?>=;:987543210/-,+*)('&%$ ; !!"#$%&&'()*+,-../0123456789:;<=>?@ABC,DEFGHIJKLMNOPQRSTUVVWXYZ[\\]^_``abbcddeefgghhijklmlkjihgf'eddcbaa`_^^]\[ZYXWVUTSRQPONMLJIHGFEDBA@?>=<:987643210/.-+*)('&%$# 7 !"##$%&'())*+,-./0123456789:;<=>?@ABCDE-FGHIJKLMNNOPQRSTUVWXYYZ[\]]^_`aabccddeffgghhiijklmlkjihgf(eddcbaa`_^]]\[ZYXWVUTSRQPONMLKIHGFEDCA@?>=<;987653210/.-,+)('&%$#" < !"#$%%&'()*+,,-./0123456789:;<=>?@ABCDEF'GHIJKLMNOPQRSTTUVWXYZ[[\]^__`abbccdeeffghijklmlkjihg+feeddcbaa`_^]]\[ZYXWVUTSRQPONMLKIHGFEDCB@?>=<;987654310/.-,+*)(&%$#"! A !""#$%&''()*+,-./00123456789:;<=>?@ABCDEFG(HIJKLMNOPQRSTUVWWXYZ[\\]^_``abbcddeffgghhijklmlkjihg,feedccbaa`_^]]\[ZYXWVUTSRQPONMLKJHGFEDCB@?>=<;:87654320/.-,+*)('&%$#"! = !"##$%&'()**+,-./0123456789:;<=>?@ABCDEFGHI)JKLMNOPQQRSTUVWXYYZ[\]^^_`aabccdeeffgghhiijklmlkjih/gffeedccbaa`_^]\\[ZYXWVUTSRQPONMLKJHGFEDCB@?>=<;:976543210.-,+*)('&%$#"!  @ !!"#$%%&'()*+,--./0123456789:;<=>?@ABCDEFGHIJ%KLMNOPQRSTUUVWXYZ[[\]^__`abbcddeefgghhijklmlkjihgfe+dcbba``_^]\[[ZYXWVUTSRQPONMLKIHGFEDCBA?>=<;:976543210/.,+*)('&%$#"!  > !""#$%&'(()*+,-./01234556789:;<=>@ABCDEFGHIJKK&LMNOPQRSTUVWXXYZ[\]]^_``abbcddeffgghhiijklmlkjihgf.eddcbba``_^]\[ZZYXWVUTSRQPONMLKIHGFEDCBA?>=<;:976543210/.-,*)('&%$#"!  A !"#$$%&'()*++,-./0123456789:;<=>?@ABCDEFGHIJKLM NOPQRSSTUVWXYZZ[\]^^_`aabccdeeffghijklmlkjihg1feeddcbba`__^]\[ZYYXWVUTSRQPONMKJIHGFEDCB@?>=<;:986543210/.-,+*('&%$#"!  F !!"#$%&&'()*+,-../0123456789:;<=>?@ABCDEFGHIJKLMN!OPQRSTUVVWXYZ[\\]^_``abbcddeefgghhijklmlkjih4gffeedccbaa`_^^]\[ZYXWWVUTSRQPOMLKJIHGFEDCB@?>=<;:986543210/.-,+*)('%$#"!!  B !"##$%&'())*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOO"PQRSTUVWXYYZ[\]]^_`aabccddeffgghhiijklmlkjihgfe0dccbaa`_^]]\[ZYXWVUTTSQPONMLKJIHGFEDCA@?>=<;:976543210/.-,+*)('&%$#"!  G !"#$%%&'()*+,,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTTUVWXYZ[[\]^__`abbccdeeffghijklmlkjihgf3eddcbba``_^]\\[ZYXWVUTSRQPONMLKJIHGFEDBA@?>=<;:976543210/.-,+*)('&%$#"!  L !""#$%&''()*+,-./00123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWWXYZ[\\]^_``abbcddeffgghhijklmlkjihg6feeddcbba`__^]\[[ZYXWVUTSRQPONMLKJIHGFDCBA@?>=<;:876543210/.-,+*)('&%$#"!  H !"##$%&'()**+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQQRSTUVWXYYZ[\]^^_`aabccdeeffgghhiijklmlkjih9gffeedccbaa`_^^]\[ZYYXWVUTSRQPONMLKJIHFEDCBA@?>=<;9876543210/.-,+*)('&%$#"!  K !!"#$%%&'()*+,--./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUUVWXYZ[[\]^__`abbcddeefgghhijklmlkjihgfe5dccbaa`_^]]\[ZYXWVVUTSRQPONMLKJHGFEDCBA@?>=;:9876543210/.-,+*)('&%$#"!  I !""#$%&'(()*+,-./01234556789:;<=>@ABCDEFGHIJKKLMNOPQRSTUVWXXYZ[\]]^_``abbcddeffgghhiijklmlkjihgf8eddcbba``_^]\\[ZYXWVUTSRRQPONLKJIHGFEDCBA@?=<;:9876543210/.-,+*)('&%$#"!  L !"#$$%&'()*++,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSSTUVWXYZZ[\]^^_`aabccdeeffghijklmlkjihg;feedccbaa`__^]\[ZZYXWVUTSRQPONMLKJIHGFEDCBA?>=<;:9876543210/.-,+*)('&%$#"!  Q !!"#$%&&'()*+,-../0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVVWXYZ[\\]^_``abbcddeefgghhijklmlkjihgfe9dccbaa`_^^]\[ZYXXWVUTSRQPONMLKJIHGFEDCA@?>=<;:987654321/.-,++*)('&%$#"!  M !"##$%&'())*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOOPQRSTUVWXYYZ[\]]^_`aabccddeffgghhiijklmlkjihgf=<;:987643210/.-,+*)(('&%$#"!  R !"#$%%&'()*+,,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTTUVWXYZ[[\]^__`abbccdeeffghijklmlkjihg?feedccbba`__^]\[[ZYXWVUTSRQQPONMKJIHGFEDCBA@?>=<;:876543210/.-,+*)('&&%$#"!  W !""#$%&''()*+,-./00123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWWXYZ[\\]^_``abbcddeffgghhijklmlkjihgfe=dccbaa`_^^]\[ZYYXWVUTSRQPONMLKJIHGFEDCBA@?>=;:9876543210/.-,+*)('&%%$#"!  S !"##$%&'()**+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQQRSTUVWXYYZ[\]^_`aabccdeeffgghhiijklmlkjihgf@eddcbba``_^]\\[ZYXWVVUTSRQPONMLKJIHGFEDCBA?>=<;:9876543210/.-,+*)('&%$##"!  V !!"#$%%&'()*+,--./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUUVWXYZ[[\]^_ `abbcddeefgghhijklmlkjihgCfeedccbba`__^]\[[ZYXWVUTSSRQPONMLKJIHGEDCBA@?>=<;:9876543210/.-,+*)('&%$#""!  T !""#$%&'(()*+,-./01234556789:;<=>?ABCDEFGHIJKKLMNOPQRSTUVWXXYZ[\]]^_`abbcddeffgghhiijklmlkjihgfeAdccbaa`_^^]\[ZYYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#"!!  W !"#$$%&'()*++,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSSTUVWXYZZ[\]^^_`abccdeeffghijklmlkjihgfDeddcbba``_^]\\[ZYXWVVUTSRQPONMLKJIHGFEDCBA@?>=<;9876543210/.-,+**)('&%$#"!  \ !!"#$%&&'()*+,-../0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVVWXYZ[\\]^_``ab cddeefgghhijklmlkjihIgffeedccbba`__^]\[[ZYXWVUTSSRQPONMLKJIHGFEDBA@?>=<;:9876543210/.-,+*)(''&%$#"!  X !"##$%&'())*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNNOPQRSTUVWXYYZ[\]]^_`aabcdeffgghhiijklmlkjihgfGeddccbaa`_^]]\[ZYYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%%$#"!  ] !"#$%%&'()*+,,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSSTUVWXYZ[[\]^__`aabccdeeffghijklmlkjihgJfeeddcbba``_^]\\[ZYXWVVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$##"!  b !""#$%&''()*+,-./00123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWWXYZ[\\]^_``abbcddeffgghhijklmlkjihMgffeedccbaa`__^]\[ZZYXWVUTSRRQPONMLKJIHGFEDCB@?>=<;:9876543210/.-,++*)('&%$#""!  ^ !"##$%&'()**+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQQRSTUVWXYYZ[\]^^_`aabccdeeffgghhiijklmlkjihgfKeddcbbaa`_^]]\[ZYXXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)(('&%$#"!!  a !!"#$%%&'()*+,--./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUUVWXYZ[[\]^__`abbccdeefghijklmlkjihgNfeeddcbba``_^]\\[ZYXWVUUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&&%$#"!  _ !""#$%&'(()*+,-./01234556789:;<=>?@ABDEFGHIJJKLMNOPQRSTUVWXXYZ[\]]^_``abbcddeffghijklmlkjihgfeLdccbaa`_^^]\[ZZYXWVUTSRQQPONMLKJIHGFEDCBA@>=<;:9876543210//.-,+*)('&%$$#"!  b !"#$$%&'()*++,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRRSTUVWXYZZ[\]^^_`aabccdeeffgghhiijklmlkjihgfOeddcbba``_^]]\[ZYXWWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,++*)('&%$#""!  g !!"#$%&&'()*+,-../0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVVWXYZ[\\]^_``abbcddeefgghhijklmlkjihTgffeedccbba`__^]\[[ZYXWVUTTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)(('&%$#"!!   c !"##$%&'())*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNNOPQRSTUVWXYYZ[\]]^_`aabbcddeffgghhijklmlkjihgfReddccbaa`_^^]\[ZYYXWVUTSRQPOONMLKJIHGFEDCBA@>=<;:9876543210//.-,+*)('&&%$#"!   b !"#$%%&'()*+,,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSSTUVWXYZ[[\]^__`aabccdeeffghiijklmlkjihgUfeeddcbba``_^]\\[ZYXWWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,++*)('&%$$#"!   h !""#$%&''()*+,-./00123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWWXYZ[\\]^_``abbcddeefgghhijklmlkjihXgffeedccbaa`__^]\[ZZYXWVUTSSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)(('&%$#""!   i !"##$%&'()**+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPPQRSTUVWXYYZ[\]^^_`aabccddeffgghhiijjjklmlkjihgfVeddcbba``_^]]\[ZYXXWVUTSRQPONMLLKJIHGFEDCB@?>=<;:9876543210//.-,+*)('&&%$#"!   d !!"#$%%&'()*+,--./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTTUVWXYZ[[\]^__`abbccdeeffghijklmlkjihgYfeeddcbba`__^]\\[ZYXWVUUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,,+*)('&%$$#"!   j !""#$%&''()*+,-./01234556789:;<=>?@ABCDFGHHIJKLMNOPQRSTUVWWXYZ[\]]^_``abbcddeffgghhijkklmlkjihgfeWdccbaa`_^^]\[ZZYXWVUTSRQQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*))('&%$#""!    g !"#$$%&'()**+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRRSTUVWXYZZ[\]^^_`aabccdeeffgghhiijklmlkjihgfZeddcbba``_^]\\[ZYXWWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/..-,+*)('&&%$#"!!   f !!"#$%&&'()*+,--./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVVWXYZ[\\]^_``abbcddeefgghhijkllmlkjih_gffeedccbba`__^]\[[ZYXWVUTTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,++*)('&%$$#"!   v  !"##$%&'(()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMMNOPQRSTUVWXYYZ[\]]^_`aabbcddeffgghhiijkllmlkjihgf]eddccbaa`_^]]\[ZYYXWVUTSRQPOONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)(('&%$#""!    g !"#$%%&'()*++,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSSTUVWXYZ[[\]^__`aabccdeeffgghhiijklmlkjihg`feeddcbba``_^]\\[ZYXWVVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/..-,+*)('&&%$#"!!   h !""#$%&''()*+,-.//0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVVWXYZ[\\]^_``abbcddeefgghhijklmmlkjihgfe^dccbaa`_^^]\[ZZYXWVUTSSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,++*)('&%$$#"!   r !"##$%&'())*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPPQRSTUVWXYYZ[\]^^_`aabccddeffgghhiijklmmlkjihgfaeddcbba``_^]]\[ZYXXWVUTSRQPONMLLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)(('&%$#""!   d !!"#$%%&'()*+,,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTTUVWXYZ[[\]^__`abbccdeeffghijklmmlkjihfgffeedccbba`__^]\[[ZYXWVUUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.--,+*)('&%%$#"!!   m !""#$%&''()*+,-./01223456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWWXYZ[\\]^_``abbcddeffgghhijklmmlkjihgfdeddccbaa`_^^]\[ZYYXWVUTSRQQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+**)('&%$##"!   n !"#$$%&'()**+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQQRSTUVWXYYZ[\]^^_`aabccdeeffgghhiijklmmlkjihggfeeddcbba``_^]\\[ZYXWWVUTSRQPONMLKJIHGFEDCBA@?>=<;:98765432110/.-,+*)(''&%$#""!   d !!"#$%&&'()*+,--./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUUVWXYZ[[\]^__`abbccdeefgghijklmmlkjihgfeedccbaa`__^]\[[ZYXWVUTSSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,,+*)('&%%$#"!   c !"##$%&'(()*+,-./0123456789:;<=>?@ABCDEFGHIJKLLMNOPQRSTUVWXXYZ[\]]^_``abbcddeffgghhijklmmlkjihgfheddcbba``_^]]\[ZYYXWVUTSRQPONNMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*))('&%$##"!   j !"#$%%&'()*++,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSSTUVWXYZZ[\]^^_`aabccdeeffgghhiijklmmlkjihmgffeedccbba`__^]\\[ZYXWVVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/..-,+*)(''&%$#""!   d !""#$%&''()*+,-../0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVVWXYZ[\\]^_``abbcddeefgghijklmmlkjihgfkeddccbaa`_^^]\[ZZYXWVUTSRRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,++*)('&%%$#"!   c !"##$%&'())*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOOPQRSTUVWXYYZ[\]]^_`aabbcddeffgghhijklmlkjihgjfeeddcbba``_^]\\[ZYXWWVUTSRQPONMLKJIIHGFEDCBA@>=<;:98766543210/.-,+*)(('&%$##"!    d !"#$%%&'()*+,,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTTUVWXYZ[[\]^__`aabccdeeffgghhiijklmllkjihgfeidccbaa`__^]\[[ZYXWVUTTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.--,+*)('&&%$#"!!   ^ !""#$%&''()*+,-./01123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWWXYZ[\\]^_``abbcddeefgghhijklmllkjihgfoeddcbbaa`_^]]\[ZYYXWVUTSRQPPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+**)('&%$$#"!    c !"##$%&'()**+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQQRSTUVWXYYZ[\]^^_`aabccddeffgghhiijklmlkjihgjfeeddcbba``_^]\\[ZYXWVVUTSRQPONMLKJIHGFEDCBA@?>=<;:98765432110/.-,+*)(''&%$#""!   b !!"#$%%&'()*+,--./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUUVWXYZ[[\]^__`abbccdeeffgghhiijklmlkkjihgfeidccbaa`_^^]\[ZZYXWVUTSSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,,+*)('&%%$#"!!     ^ !""#$%&'(()*+,-./01234566789:;<=>?@ACDEFGHIJKKLMNOPQRSTUVWXXYZ[\]]^_``abbcddeffgghhijklmlkkjjihgofeeddcbba``_^]]\[ZYXXWVUTSRQPONMMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*))('&%$##"!   _ !"#$$%&'()*++,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRRSTUVWXYZZ[\]^^_`aabccdeeffgghhiijklmlkjjihgfegdccbba`__^]\[[ZYXWVUUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/..-,+*)('&&%$#""!    Y !!"#$%&&'()*+,-../0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVVWXYZ[\\]^__`abbccdeefgghijklmlkjihgfkeddcbbaa`_^^]\[ZYYXWVUTSRQQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,++*)('&%$$#"!   Z !"##$%&'())*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNNOPQRSTUVWXXYZ[\]]^_``abbcddeffgghhijklmlkjiihgofeeddcbba``_^]\\[ZYXWWVUTSRQPONMLKJIHGFEDCBA@?>=<;:98765543210/.-,+*)(('&%$#""!    [ !"#$%%&'()*+,,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSSTUVWXYZZ[\]^__`aabccdeeffgghhiijklmlkjihgfegdccbaa`_^^]\[[ZYXWVUTSSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.--,+*)('&&%$#"!!   U !""#$%&''()*+,-.//0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVVWXYZ[\\]^_``abbcddeefgghijklmlkjihphggffeddcbba``_^]]\[ZYYXWVUTSRQPONNMLKJIHGFEDCBA@?>=<;:9876543210/.-,+**)('&%$$#"!    V !"##$%&'()**+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPPQRSTUVWXYYZ[\]]^_`aabbcddeffgghhijklmlkjihglgffeedccbba`__^]\[[ZYXWVVUTSRQPONMLKJIHGFEDCBA@?>=<;:98765432100/.-,+*)(''&%$#""!   W !!"#$%%&'()*+,--./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTTUVWXYZ[[\]^__`aabccdeeffgghhiijklmlkjihgfkeddccbaa`_^^]\[ZYYXWVUTSRRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,,+*)('&%%$#"!   Q !""#$%&''()*+,-./01233456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWWXYZ[\\]^_``abbcddeefgghijklmlkjihgfedjcbba``_^]\\[ZYXWWVUTSRQPONMLKJIIHGFEDCBA@?>=<;:9876543210/.-,+*))('&%$##"!    R!"#$$%&'()**+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQQRSTUVWXYYZ[\]^^_`aabccddeffgghhijklmlkjihgfehedccbaa`__^]\[[ZYXWVUTTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/..-,+*)('&&%$#"!!    !!S"#$%&&'()*+,--./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUUVWXYZ[[\]^__`aabccdeeffgghhiijklmlkjihgfedidcbba``_^]]\[ZYYXWVUTSRQPPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,++*)('&%$$#"!    !"#M#$%&'(()*+,-./0123456789:;<=>?@ABCDEFGHIJKLLMNOPQRSTUVWXXYZ[\]]^_``abbcddeffgghijklmlkjihgfedcjcbba`__^]\\[ZYXWVVUTSRQPONMLKJIHGFEDCBA@?>=<;:98765432110/.-,+*)(('&%$#""!    !"#$%L&'()*++,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRRSTUVWXYZZ[\]^^_`aabccdeeffgghhijklmlkjihgfeddccdbaa`_^^]\[ZZYXWVUTSSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,,+*)('&%%$#"!!    !""#$%O&''()*+,-../0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVVWXYZ[\\]^__`abbccdeeffgghhiijklmlkjihgfeeddcbbea``_^]\\[ZYXXWVUTSRQPONMLLKJIHGFEDCBA@?>=<;:9876543210/.-,+*))('&%$##"!    !"##$%&I'())*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOOPQRSTUVWXXYZ[\]]^_``abbcddeffgghijklmlkjihgfedccbaaa`__^]\[[ZYXWVUUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/..-,+*)(''&%$#""!    !"#$%%&'J()*+,,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSSTUVWXYZZ[\]^__`aabccdeeffgghhijklmlkjihgfeddcbbaa`b_^]]\[ZYYXWVUTSRQQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,++*)('&%%$#"!    !""#$%&''(F)*+,-./01123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVVWXYZ[\\]^_``abbcddeefgghijklmlkjih gffeedccbba``_c^]\\[ZYXWVVUTSRQPONMLKJIHGFEDCBA@?>=<;:98765543210/.-,+*)(('&%$##"!    !"##$%&'()*G*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPPQRSTUVWXYYZ[\]]^_`aabbcddeffgghhijklmlkjihgf eddccbaa`_^^]]\[ZZYXWVUTSSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.--,+*)('&&%$#"!!     !!"#$%%&'()*+H,--./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTTUVWXYZ[[\]^__`aabccdeeffgghhiijklmlkjihgfeeddcbba``_^]]^\[ZYXXWVUTSRQPONNMLKJIHGFEDCBA@?>=<;:9876543210/.-,+**)('&%$$#"!   % !""#$%&'(()*+,B-./01234566789:;<=>?@ABCEFGHIIJKLMNOPQRSTUVWWXYZ[\\]^_``abbcddeefgghijklmlkjihgfe dccbaa`__^]\[_[ZYXWVUUTSRQPONMLKJIHGFEDCBA@?>=<;:98765432100/.-,+*)(''&%$#""!   ! !"#$$%&'()*++,-C./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRRSTUVWXYYZ[\]^^_`aabccddeffgghhijklmlkjihgfeddcbbaa`_^^]\[ZYWXWVUTSRQQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,,+*)('&%%$#"!!   $ !!"#$%&&'()*+,-..D/0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUUVWXYZ[[\]^__`aabccdeeffgghhiijklmlkjihgfeeddcbba``_^]\\[ZYZXWWVUTSRQPONMLKJIIHGFEDCBA@?>=<;:9876543210/.-,+*))('&%$##"!   " !"##$%&'(()*+,-./0>123456789:;<=>?@ABCDEFGHIJKLMMNOPQRSTUVWXXYZ[\]]^_``abbcddeefgghijklmlkjihgfedccbaa`_^^]\[[ZYXVWVUTTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/..-,+*)('&&%$#""!   % !"#$%%&'()*++,-./01?23456789:;<=>?@ABCDEFGHIJKLMNOPQRRSTUVWXYZZ[\]^^_`aabccddeffgghhijklmlkjihgfeddcbba``_^]]\[ZYYXWWVUTSRQPOONMLKJIHGFEDCBA@?>=<;:9876543210/.-,++*)('&%$$#"!   * !""#$%&''()*+,-../012@3456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVVWXYZ[[\]^__`aabccdeeffgghhiijklmlkjihgfedccbba`__^]\[[ZYXWVVXUTSRQPONMLKJIHGFEDCBA@?>=<;:98765432210/.-,+*)(('&%$#""!   & !"##$%&'())*+,-./01234:56789:;<=>?@ABCDEFGHIJKLMNOOPQRSTUVWXXYZ[\]]^_``abbcddeffgghijklmlkjihgfeddccbaa`_^^]\[ZYYXWVUTRSRRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.--,+*)('&%%$#"!!   + !!"#$%%&'()*+,,-./012345;6789:;<=>?@ABCDEFGHIJKLMNOPQRSSTUVWXYZZ[\]^^_`aabccdeeffgghhijklmlkjihgfeeddcbba``_^]\\[ZYXWWVUTSSRQPONMLLKJIHGFEDCBA@?>=<;:9876543210/.-,+**)('&%$##"!   0 !""#$%&''()*+,-./01123456<789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVVWXYZ[\\]^__`abbccdeeffgghhiijklmlkjihgfedccbaa`__^]\[[ZYXWVUTTSRTQPONMLKJIHGFEDCBA@?>=<;:9876543210//.-,+*)(''&%$#""!   , !"#$$%&'()**+,-./01234567869:;<=>?@ABCDEFGHIJKLMNOPPQRSTUVWXYYZ[\]]^_``abbcddeffgghijklmlkjihgfeddcbba``_^]]\[ZYYXWVUTSRQPPPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,++*)('&%%$#"!   / !!"#$%&&'()*+,--./01234567897:;<=>?@ABCDEFGHIJKLMNOPQRSTTUVWXYZ[[\]^__`aabccdeeffgghhijklmlkjihgfedccbba`__^]\\[ZYXWVVUTSRQPOQNMLKJIHGFEDCBA@?>=<;:98765543210/.-,+*)(('&%$##"!   4 !""#$%&'(()*+,-./01234566789:8;<=>?@ABDEFGHIIJKLMNOPQRSTUVWWXYZ[\\]^_``abbcddeeffgghhiijklmlkjihgfeddccbaa`_^^]\[ZZYXWVUTSSRQPONKMLKJIHGFEDCBA@?>=<;:9876543210/.--,+*)('&&%$#"!!   0 !"#$$%&'()*++,-./0123456789:;<2=>?@ABCDEFGHIJKLMNOPQQRSTUVWXYYZ[\]^^_``abbcddeffgghijklmlkjihgfedcbba``_^]\\[ZYXXWVUTSRQPONMMLLKJIHGFEDCBA@?>=<;:9876543210/.-,+**)('&%$$#"!   3 !!"#$%&&'()*+,-../0123456789:;<=3>?@ABCDEFGHIJKLMNOPQRSTUUVWXYZ[[\]^__`aabccdeeffgghhijklmlkjihgfedccbaa`__^]\[[ZYXWVUUTSRQPONMLKMJIHGFEDCBA@?>=<;:98765432100/.-,+*)(''&%$#""!   1 !"##$%&'())*+,-./0123456789:;<=>?4@ABCDEFGHIJKLLMNOPQRSTUVWXXYZ[\\]^_``abbcddeeffgghhiijklmlkjihgf!eddcbbaa`_^]]\[ZYYXWVUTSRQQPONMLKJGIHGFEDCBA@?>=<;:9876543210/.-,,+*)('&%%$#"!!   4 !"#$%%&'()*+,,-./0123456789:;<=>?@.ABCDEFGHIJKLMNOPQRRSTUVWXYZZ[\]^^_`aabbcddeffgghijklmlkjihgfe!dccbba``_^]\\[ZYXWVVUTSRQPONMLKJIHHHGFEDCBA@?>=<;:9876543210/.-,+*))('&%$##"!   9 !""#$%&''()*+,-.//0123456789:;<=>?@A/BCDEFGHIJKLMNOPQRSTUUVWXYZ[[\]^__`aabccdeeffgghhijklmlkjihgf$eddccbaa`_^^]\[ZZYXWVUTSSRQPONMLKJIHGDFEDCBA@?>=<;:9876543210/..-,+*)('&&%$#""!   5 !"##$%&'())*+,-./0123456789:;<=>?@ABC0DEFGHIJKLMNNOPQRSTUVWXXYZ[\]]^_``abbcddeeffgghhiijklmlkjihgfed"cbba``_^]]\[ZYXXWVUTSRQPONNMLKJIHGFEEDCBA@?>=<;:9876543210/.-,++*)('&%$$#"!   : !!"#$%%&'()*+,,-./0123456789:;<=>?@ABCD*EFGHIJKLMNOPQRSSTUVWXYZZ[\]^^_`aabccddeffgghijklmlkjihgfe%dccbaa`__^]\[[ZYXWVUUTSRQPONMLKJIHGFEDFCBA@?>=<;:98765432210/.-,+*)(('&%$#""!   ? !""#$%&''()*+,-./01123456789:;<=>?@ABCDE+FGHIJKLMNOPQRSTUVVWXYZ[[\]^__`aabccdeeffgghhijklmlkjihgf(eddcbbaa`_^]]\[ZYYXWVUTSRQQPONMLKJIHGFEDC@BA@?>=<;:9876543210/.--,+*)('&%%$#"!!   ; !"#$$%&'()**+,-./0123456789:;<=>?@ABCDEFG%HIJKLMNOOPQRSTUVWXXYZ[\]]^_``abbcddeefghijklmlkjihgfe(dccbba``_^]\\[ZYXWWVUTSRQPONMLKJIIHGFEDCBAA@?>=<;:9876543210/.-,+**)('&%$##"!   > !!"#$%&&'()*+,--./0123456789:;<=>?@ABCDEFGH&IJKLMNOPQRSSTUVWXYZZ[\]^^_`aabbcddeffgghijklmlkjihgf+eddccbaa`_^^]\[ZZYXWVUTTSRQPONMLKJIHGFEDCBA@B?>=<;:9876543210/..-,+*)(''&%$#""!   C !""#$%&'(()*+,-./01234556789:;<=>?@ABCEFFGHI'JKLMNOPQRSTUVVWXYZ[\\]^__`aabccdeeffgghhijklmlkjihgfed)cbba``_^]]\[ZYXXWVUTSRQPOONMLKJIHGFEDCBA@?>>=<;:9876543210/.-,++*)('&%%$#"!   ? !"#$$%&'()*++,-./0123456789:;<=>?@ABCDEFGHIJK(LMNOPPQRSTUVWXXYZ[\]]^_``abbcddeeffgghhiijklmlkjihgfe,dccbaa`__^]\[[ZYXWVVUTSRQPONMLKJIHGFEDCBA@?>=?<;:98765543210/.-,+*)(('&%$##"!   B !!"#$%&&'()*+,-../0123456789:;<=>?@ABCDEFGHIJKL"MNOPQRSTTUVWXYZZ[\]^^_`aabccddeffgghijklmlkjihgf/eddcbbaa`_^]]\[ZYYXWVUTSRRQPONMLKJIHGFEDCBA@?>=<9;:9876543210/.--,+*)('&&%$#"!!   @ !"##$%&'())*+,-./0123456789:;<=>?@ABCDEFGHIJKKLM#NOPQRSTUVWWXYZ[\\]^__`aabccdeeffgghhijklmlkjihgfe/dccbba``_^]\\[ZYXWWVUTSRQPONMLLKJIHGFEDCBA@?>=<;::9876543210/.-,+**)('&%$$#"!   C !"#$%%&'()*+,,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQQRSTUVWXYYZ[\]]^_``abbcddeefghijklkjihgf2eddccbaa`_^^]\[ZZYXWVUTTSRQPONMLKJIHGFEDCBA@?>=<;:9;8765432100/.-,+*)(''&%$#""!   H !""#$%&''()*+,-.//0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUUVWXYZ[[\]^__`aabccddeffgghijklkjihgfed0cbba``_^]]\[ZYXXWVUTSRQPPONMLKJIHGFEDCBA@?>=<;:98576543210/.-,,+*)('&%%$#"!!   D !"##$%&'())*+,-./0123456789:;<=>?@ABCDEFGHIJKLMMNOPQRSTUVWWXYZ[\\]^_``aabccdeeffgghhijklkjihgfe3dccbaa`__^]\[[ZYXWVVUTSRQPONMLKJIHGFFECBA@?>=<;:987766543210/.-,+*))('&%$##"!   G !"#$%%&'()*+,,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRRSTUVWXYYZ[\]^^_``abbcddeefghijklkjihgf6eddcbbaa`_^]]\[ZYYXWVUTSRRQPONMLKJIHGFEDCBA@?>=<;:98765243210/..-,+*)('&&%$#""!   L !""#$%&''()*+,-./00123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[[\]^__`aabccddeffgghijklkjihgfe6dccbba``_^]\\[ZYXWWVUTSRQPONMMLKJIHGFEDCBA@?>=<;:98765433210/.-,++*)('&%$$#"!   H !"#$$%&'()**+,-./0123456789:;<=>?@ABCDEFGHIJKLMNNOPQRSTUVWXXYZ[\\]^_``abbccdeeffgghhijklkjihgf9eddccbaa`_^^]\[ZZYXWVUTTSRQPONMLKJIHGFEDCBA@?>=<;:98765432120/.-,+*)(('&%$#""!   M !!"#$%&&'()*+,--./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRRSTUVWXYZZ[\]^^_``abbcddeefghijklkjihgfed7cbba``_^]]\[ZYXXWVUTSRQPPONMLKJIHGFEDCBA@?>=<;:987654321.0/.-,,+*)('&%%$#"!!   R !""#$%&'(()*+,-./01233456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVVWXYZ[[\]^__`aabccdeeffgghijklkjihgf=<;:9876543210//.-,+*))('&%$##"!   N !"#$$%&'()*++,-./0123456789:;<=>?@ABCDEFGHIJKLMNOOPQRSTUVWXXYZ[\]]^_``abbcddeeffgghhijklkjihgfed:cbbaa`_^]]\[ZZYXWVUTSSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.0.-,+*)(''&%$#""!   Q !!"#$%&&'()*+,-../0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSSTUVWXYZZ[\]^^_``abbcddeefghijklkjihgfe=dccbba``_^]\\[ZYXXWVUTSRQPONMMLKJIHGFEDCBA@?>=<;:9876543210/.-,,++*)('&%%$#"!    V !"##$%&'(()*+,-./01234567899:;<=>?@ACDEFGHHIJKLMNOPQRSTUVVWXYZ[\]^__`aabccdeeffgghijklkjihgf@eddcbbaa`_^^]\[ZZYXWVUUTSRQPONMLKJIHGFEDCBA@?>=<;:98765443210/.-,-+*)(('&%$##"!   R !"#$$%&'()*++,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQQRSTUVWXXYZ[\]^_``abbcddeeffgghhijklkjihgfe@dccbba``_^]]\[ZYXXWVUTSRQQPONMLKJIHGFEDCBA@?>=<;:9876543210/.--,+'*)('&&%$#"!!   `  !!"#$%&&'()*+,-../0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTTUVWXYZZ[\]^ _`aabbcddeefghijklkjihgfCeddccbaa`__^]\[[ZYXWVVUTSRQPONMLKJIHHGFEDCBA@?>=<;:9876543210/.-,+**()('&%$$#"!   Z !"##$%&'())*+,-./0123456789:;<=>?@ABCDEFGHIJKLLMNOPQRSTUVWWXYZ[\\]^_ `aabccdeeffgghijklkjihgfedAcbba``_^]]\[ZZYXWVUTSSRQPONMLKJIHGFEDCBA@?>=<;:98765432100/.-,+*)(''&%$#""!   V !"#$%%&'()*+,,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQQRSTUVWXYYZ[\]]^_` abbcddeeffgghhijklkjihgfeDdccbaa`__^]\\[ZYXXWVUTSRQPONNMLKJIHGFEDCBA@?>=<;:9876543210/.-,,+*)('#&%%$#"!!   ` !""#$%&''()*+,-.//0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUUVWXYZZ[\]^^_`abcddeefghijklkjihgIfeeddcbbaa`_^^]\[ZZYXWVUUTSRQPONMLKJIHGFEDCBA@?>=<;:98766543210/.-,+*))('&$%$##"!   ^ !"##$%&'())*+,-./0123456789:;<=>?@ABCDEFGHIJKLMMNOPQRSTUVWWXYZ[\\]^__`a abccdeeffgghijklkjihgfeGdccbba``_^]]\[ZYXXWVUTSRQQPONMLKJIHGFEDCBA@?>=<;:9876543210/.--,+*)('&&% $#""!   Z !"#$%%&'()*+,,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQQRSTUVWXYYZ[\]]^_``ab bcddeeffgghhijklkjihgfJeddccbaa`__^]\[[ZYXWVVUTSRQPONMLKJIIHGFEDCBA@?>=<;:9876543210/.-,+**)('&%$$!#"!   ` !""#$%&''()*+,-./00123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUUVWXYZ[[\]^^_`aabbcddeefghijklkjihgfedHcbba``_^]]\[ZZYXWVUTSSRQPONMLKJIHGFEDCBA@?>=<;:98765432110/.-,+*)(''&%$#"""!   ` !"##$%&'()**+,-./0123456789:;<=>?@ABCDEFGHIJKLMNNOPQRSTUVWWXYZ[\\]^__`aabccdeeffgghijklkjihgfeKdccbaa`__^]\\[ZYXXWVUTSRQPOONMLKJIHGFEDCBA@?>=<;:9876543210/.-,,+*)('&%%$#"!!   ^ !"#$%%&'()*+,--./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRRSTUVWXYYZ[\]^^_``abbcddefghijklkjihgfedKcbbaa`_^^]\[ZZYXWVUUTSRQPONMLKJIHGFEDCBA@?>=<;:98876543210/.-,+*))('&%$##"!   \ !""#$%&''()*+,-./01123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUUVWXYZ[[\]^__`aabccddeefghijklkjihgfeNdccbba``_^]]\[ZYXXWVUTSRQQPONMLKJIHGFEDCBA@?>=<;:9876543210/..-,+*)(''&%$#""!   ` !"##$%&'()**+,-./0123456789:;<=>?@ABCDEFGHIJKLMNNOPQRSTUVWWXYZ[\\]^__`aabccdeeffgghijklkjihgfQeddccbaa`__^]\[[ZYXWVVUTSRQPONMLKKJIHGFEDCBA@?>=<;:9876543210/.-,++*)('&%%$#"!   ` !"#$%%&'()*+,--./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRRSTUVWXYYZ[\]^^_``abbcddeeffghijklkjihgfedOcbba``_^]]\[ZZYXWVUTSSRQPONMLKJIHGFEDCBA@?>=<;:98765432210/.-,+*)(('&%$##"!   ` !""#$%&''()*+,-./01123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUUVWXYZ[[\]^__`aabccddeeffgghhijklkjihgfTeddccbaa`__^]\\[ZYXXWVUTSRQPOONMLKJIHGFEDCBA@?>=<;:9876543210/.--,+*)('&&%$#"!!   [ !"##$%&'()**+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOOPQRSTUVWXXYZ[\\]^__`aabccdeeffghijklkjihgfedRcbbaa`_^^]\[ZZYXWVUUTSRQPONMLKJIHGFEDDCA@?>=<;;:9876543210/.-,+**)('&%$$#"!    ^ !"#$%%&'()*+,--./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSSTUVWXYZZ[\]^^_``abbcddeeffgghhhiijklkjihgfeUdccbaa``_^]]\[ZYXXWVUTSRQQPONMLKJIHGFEDCBA@?>=<;:9876543210/..-,+*)(''&%$#""!   ` !!"#$%&''()*+,-./01223456789:;<=>?@ACDEFGGHIJKLMNOPQRSTUVVWXYZ[[\]^__`aabccddeeffgghhiijklkjihgfedUcbbaa`__^]\[[ZYXWVVUTSRQPONMLKKJIHGFEDCBA@?>=<;:9876543210/.-,++*)('&%%$#"!!    Y !"##$%&'()**+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPPQRSTUVWXXYZ[\]]^__`aabccdeeffghiijklkjihgfeXdccbba``_^]]\[ZZYXWVUTSSRQPONMLKJIHGFEDCBA@?>=<;:98765433210/.-,+*)(('&%$##"!    Z !"#$%%&'()*+,--./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTTUVWXYZZ[\]^^_``abbcddeeffgghijklkjihgf[eddccbaa`__^]\\[ZYXXWVUTSRQPOONMLKJIHGFEDCBA@?>=<;:9876543210/.--,+*)('&&%$#"!!   [ !!"#$%&''()*+,-./01223456789:;<=>?ABCDEFGHIJJKLMNOPQRSTUVVWXYZ[[\]^__`aabccddeeffgghhijjklkjihgfedYcbba``_^^]\[ZZYXWVUUTSRQPONMLKJIHGFFEDCBA@?>=<;:9876543210/.-,+**)('&%$$#"!    U !"##$%&'())*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPPQRSTUVWXXYZ[\]]^__`aabccdeeffghijjklkjihgfe\dccbaa``_^]]\[ZYXXWVUTSRQQPONMLKJIHGFEDCBA@?>=<;:9876543210//.-,+*)(''&%$#""!   V !"#$$%&'()*+,,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTTUVWXYZZ[\]^^_``abbcddeeffgghijklkjihgfed\cbbaa`__^]\[[ZYXWVVUTSRQPONMLKKJIHGFEDCBA@?>=<;:9876543210/.-,,+*)('&%%$#"!!   W !"#$%&&'()*+,-./01123456789:;<=>?@BCDEFGHIJKKLMNOPQRSTUVVWXYZ[[\]^__`aabccddeeffgghhijkklkjihgfe_dccbba``_^]]\[ZZYXWVUTSSRQPONMLKJIHGFEDCBA@?>=<;:98765543210/.-,+*))('&%$##"!   Q !""#$%&'(()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPPQRSTUVWXXYZ[\]]^__`aabccdeeffghijkklkjihgfedc]baa`__^]\\[ZYXXWVUTSRQPOONMLKJIHGFEDCBA@?>=<;:9876543210/.--,+*)('&&%$#""!    R !"##$%&'()*++,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTTUVWXYZZ[\]^^_``abbcddeeffgghijkklkjihgfed`cbba``_^^]\[ZZYXWVUUTSRQPONMLKJIHGFFEDCBA@?>=<;:9876543210/.-,+**)('&%$$#"!    S!"#$%%&'()*+,-./00123456789:;<=>?@ABCEFGHIJKKLMNOPQRSTUVVWXYZ[[\]^__`aabccddeeffgghhijklkjihgfeeddccbaa`__^]\\[ZYXXWVUTSRQQPONMLKJIHGFEDCBA@?>=<;:98765432100/.-,+*)(''&%$#""!    !!M"#$%&''()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPPQRSTUVWXXYZ[\]]^__`aabccddeffghijkllkjihgfedccbbaa`_^^]\[[ZYXWVVUTSRQPONMLKKJIHGFEDCBA@?>=<;:9876543210/.-,,+*)('&%%$#"!!    !""N#$%&'()**+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTTUVWXYZZ[\]^^_``abbccdeeffgghijkllkjihgfefdccbaa``_^]]\[ZYYXWVUTSSRQPONMLKJIHGFEDCBA@?>=<;:98766543210/.-,+*))('&%$##"!    !"##M$%&'()*+,--./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVVWXYZ[[\]^^_`aabbcddeeffgghijkllkjihgfedfcbbaa`__^]\\[ZYXWWVUTSRQPOONMLKJIHGFEDCBA@?>=<;:9876543210/..-,+*)(''&%$#""!    !"#$%N%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPPQRSTUVWXXYZ[\]]^__`aabccddeeffgghhijkkjihgfeidccbba``_^^]\[ZZYXWVUUTSRQPONMLKJIHGFFEDCBA@?>=<;:9876543210/.-,++*)('&%%$#"!    !"#$%&'F()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSSTUVWXYZZ[\]^^_``abbccdeeffghijkkjihgfedcgbaa`__^]\\[ZYXXWVUTSRQQPONMLKJIHGFEDCBA@?>=<;:98765432110/.-,+*)(('&%$##"!    !!"#$%&'I())*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVVWXYZ[[\]^^_``abbcddeeffgghijkkjihgfedgcbba``_^^]\[[ZYXWVVUTSRQPONMLLKJIHGFEDCBA@?>=<;:9876543210/.--,+*)('&&%$#"!!    !""#$%&'(J)*+,,-./0123456789:;<>?@ABCDEFGHIJKLMNOOPQRSTUVWXXYZ[\\]^__`aabccddeeffgghhijkkjihgfemdccbaa``_^]]\[ZYYXWVUTSSRQPONMLKJIHGFEDCBA@?>=<;:98776543210/.-,+**)('&%$$#"!    ! !"#$$%&'()D*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSSTUVWXYYZ[\]]^_``abbccdeeffghijkjihgfedmcbbaa`__^]\\[ZYXWWVUTSRQPOONMLKJIHGFEDCBA@?>=<;:9876543210/..-,+*)(''&%$#""!   " !"#$%&&'()*E+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVVWXYZ[[\]^^_``abbcddeeffgghijkjjihgfeidccbba``_^^]\[ZZYXWVUUTSRQPONMLKJIHGGFEDCBA@?>=<;:9876543210/.-,++*)('&%%$#"!!   % !"#$%&'(()*+D,-./0123456789:;<=>?@ABCDEFHIJKKLMNOPQRSTUVWXXYZ[\\]^__`aabccddeeffgghijkjjihgfedchbaa`__^]\\[ZYXXWVUTSRQQPONMLKJIHGFEDCBA@?>=<;:98765432110/.-,+*)(('&%$##"!   & !"#$%&'()**+,>-./0123456789:;<=?@ABCDEFGHIJKLMNOPQQRSTUVWXYYZ[\]]^__`aabccddefghijkjiihgfeidccbba``_^^]\[[ZYXWVVUTSRQPONMLLKJIHGFEDCBA@?>=<;:9876543210/.--,+*)('&&%$#"!!   ' !!"#$%&'()*+,--?./012456789:;<=>?@ABCDEFGHIJKLMNOPQRSTTUVWXYZ[[\]^^_``abbccdeeffghijkjiihgfedchbaa``_^]]\[ZYYXWVUTSSRQPONMLKJIHGFEDCBA@?>=<;:98876543210/.-,+**)('&%$$#"!   ( !""#$%&'()*+,-./@0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVVWXYZ[\\]^^_``abbcddeeffgghijkjihgfedlcbbaa`__^]\[[ZYXWWVUTSRQPOONMLKJIHGFEDCBA@?>=<;:9876543210/..-,+*)(''&%$#""!   ) !"##$%&'()*+,-./0?123456789:;<=>?@ABCDEFHIJKLMMNOPQRSTUVWXXYZ[\]]^__`aabccddeeffgghijkjihhggfkeddccbaa``_^]]\[ZZYXWVUUTSRQPONMLKJIHGGFEDCBA@?>=<;:9876543210/.-,++*)('&%%$#"!!   * !"#$%%&'()*+,-./01;23456789:;<=>?@BCDEFGHIJKLMNOPQQRSTUVWXYZZ[\]]^_``abbccddeffghijkjihggfedjcbbaa`__^]\\[ZYXXWVUTSRQQPONMLKJIHGFEDCBA@?>=<;:98765432210/.-,+*)(('&%$##"!   + !"#$%&&'()*+,-./012<3456789:;<>?@ABCDEFGHIJKLMNOPQRSTTUVWXYZ[[\]^^_``abbccdeeffgghijkjihgfendccbba``_^^]\[ZZYXWVVUTSRQPONMLLKJIHGFEDCBA@?>=<;:9876543210/.--,+*)('&&%$#""!   * !"#$%&'(()*+,-./0123;45678:;<=>?@ABCDEFGHIJKLMNOPQRSTUVVWXYZ[\\]^__`aabbcddeeffgghijkjihgfofeeddccbaa`__^]\\[ZYYXWVUTSSRQPONMLKJIHGFEDCBA@?>=<;:99876543210/.-,+**)('&%$$#"!   + !"#$%&'()**+,-./01235:6789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXXYZ[\]]^__`aabccddeeffgghijkjihgfendccbba``_^^]\[[ZYXWWVUTSRQPOONMLKJIHGFEDCBA@?>=<;:9876543210//.-,+*)(''&%$#""!   , !"#$%&'()*+,-./01234564789:;<=>?@ABCDEGHIJKLMNOPPQRSTUVWXYZZ[\]]^_``abbccddefghijkjihgfedcfbaa``_^]]\[ZYYXWVUTTSRQPONMLKJIHGFFEDCBA@?>=<;:9876543210/.-,,+*)('&%%$#"!!   + !"#$%&'()*+,-./01234567589:;<=>?@ABDEFGHIJKLMNOPQRSSTUVWXYZ[[\]^^_``abbcddeeffghijkjihgfedkdcbba``__^]\\[ZYXWWVUTSRQQPONMLKJIHGFEDCBA@?>=<;:98765433210/.-,+*))('&%$##"!   , !"#$%&'()*+,-./01234567869:;<=>?@BCDEFGHIJKLMNOPQRSTUUVWXYZ[[\]^__`aabbcddeeffgghijkjihgfedclcbaa``_^^]\[ZZYXWVUUTSRQPONMLKKJIHGFEDCBA@?>=<;:9876543210/.--,+*)('&&%$#""!   - !"#$%&'()*+,-./01234567895:;<=>@ABCDEFGHIJKLMNOPQRSTUVWWXYZ[\\]^__`aabccddeeffgghijkjihgfedcbfbaa`__^]\\[ZYYXWVUTSRRQPONMLKJIHGFEDCBA@?>=<;::9876543210/.-,+**)('&%$$#"!   . !"#$%&'()*+,-./0123456789:/;<>?@ABCDEFGHIJKLMNOPQRSTUVWXXYZ[\]]^__`aabccddefghijkjihgfedccbbga``_^^]\[[ZYXWWVUTSRQPONNMLKJIHGFEDCBA@?>=<;:9876543210//.-,+*)(''&%$#""!   4 !"#$%&'()*+,-./0123456789:<0=>?@ABCDEFGHIJKLMNOPQRSTUVWXYYZ[\]]^_``abbccdeeffghijkjihgfedcbaac`__^]]\[ZYYXWVUTTSRQPONMLKJIHGFFEDCBA@?>=<;:9876543210/.-,,+*)('&%%$#"!!   3 !"#$%&'()*+,-./0123456789;<=/>?@ABCDEFGHIJLMNOPQRRSTUVWXYZZ[\]^^_``abbcddeeffghijkjihgfedccbba``d_^^]\[[ZYXWWVUTSRQPPONMLKJIHGFEDCBA@?>=<;:98765433210/.-,+*))('&%$##"!   4 !"#$%&'()*+,-./0123456789;<=>0?@ABCDEFGHJKLMNOPQRSTTUVWXYZ[[\]^^_``abbcddeeffgghijkjihgfedcbaa``_e^]]\[ZZYXWVUUTSRQPONMLKKJIHGFEDCBA@?>=<;:9876543210/..-,+*)('&&%$#""!   3 !"#$%&'()*+,-./012345678:;<=>?/@ABCDEFHIJKLMNOPQRSTUUVWXYZ[[\]^^_`aabccddeeffgghijihgfedcbba``__^_]\\[ZYXXWVUTSRRQPONMLKJIHGFEDCBA@?>=<;;:9876543210/.-,++*)('&%$$#"!   4 !"#$%&'()*+,-./012345678:;<=>?@.ABCDEGHIJKLMNOPQRSTUVVWXYZ[\\]^__`aabccddeeffgghijihgfedcbaa``_^^]`\[ZZYXWVVUTSRQPONNMLKJIHGFEDCBA@?>=<;:9876543210//.-,+*)(('&%$##"!   8 !"#$%&'()*+,-./0123456789;<=>?@A-BCDFGHIJKLMNOPQRSTUVWWXYZ[\\]^__`aabccddeeffgghijihgfed cbbaa`__^]\\\[ZYYXWVUTTSRQPONMLKJIHGFFEDCBA@?>=<;:9876543210/.-,,+*)('&&%$#"!!   7 !!"#$%&'()+,-./0123456789;<=>?@AB'CDFGHIJKLMNOPQRSTUVWXXYZ[\\]^__`aabccdeefghijihgfedcb a``_^^]\[[]ZYXWWVUTSRQPPONMLKJIHGFEDCBA@?>=<;:98765443210/.-,+*))('&%$$#"!   6 !"#$%&'()*+-./0123456789:<=>?@ABC&DFGHIJKLMNOPQRSTUVWXXYZ[\]]^__`abbccdeefghijihgfedc baa`__^]]\[ZY^YXWVUUTSRQPONMLKKJIHGFEDCBA@?>=<;:9876543210/..-,+*)(''&%$#""!   <  !"#$%&'()*+,-/0123456789:<=>?@ABCD%FGHIJKLMNOPQRSTUVWXYYZ[\]]^_``abbccdeefghijihgfedcb a``_^^]\[[ZYXXXWVUTSRRQPONMLKJIHGFEDCBA@?>=<;;:9876543210/.-,++*)('&%%$#"!!   ; !"#$%&'()*+,-./123456789:;=>?@ABCDE$GHIJKLMNOPQRSTUVWXYYZ[\]]^_``abbccdeefghijihgfedcbaa``_^]]\[ZZYXWVWUTSRQPONNMLKJIHGFEDCBA@?>=<;:98765432100/.-,+*)(('&%$##"!   : !"#$%&'()*+,-./013456789:;<>?@ABCDEG#HIJKLMNOPQRSTUVWXXYZ[\]]^_``abbccdeefghijihgfedcba``__^]\\[ZYXXWVZUTSSRQPONMLKJIHGFEEDCBA@?>=<;:9876543210/.-,,+*)('&&%$#"!!   >  !"#$%&'()*+,-./012356789:;<=?@ABCDEFH%IJKLMNOPQRSTUVWXXYZ[\]]^_``abbccddeeffghijihgfedcbaa``_^^]\[[ZYXWVVUVTSRQPOONMLKJIHGFEDCBA@?>=<;:98765443210/.-,+*))('&%$$#"!   = !"#$%&'()+,-./012345789:;<=>@ABCDEFGH$JKLMNOPQRSTUVWWXYZ[\\]^__`abbccddeeffghijihgfedcba``__^]\\[ZYYXWVUTTWSRQPONMLKJJIHGFEDCBA@?>=<;:9876543210/..-,+*)(''&%$#""!   < !"#$%&'()*+,./012345689:;<=>?ABCDEFGHI#KLMNOPQRSTUVWWXYZ[\\]^__`aabccddeeffghihgfedcbaa``_^^]\[[ZYXWWVUTSRQOPONMLKJIHGFEDCBA@?>=<;;:9876543210/.-,++*)('&%%$#"!!   @  !"#$%&'()*+,-./12345678:;<=>?@BCDEFGHIJ KMNOPQRSTUUVWXYZ[[\]^__`aabccddeefghihgfedcba`__^]]\[ZYYXWVUUTSRQRPONMMLKJIHGFEDCBA@?>=<;:98765432100/.-,+*)(('&%$##"!   ? !"#$%&'()*+,-./013456789:<=>?@ABDEFGHIJKLMNPQRSSTUVWXYZ[[\]^^_`aabbcddefghihgfedcba``_^^]\[[ZYXXWVUTSSRQPSONMLKJIHGFEDDCBA@?>=<;:9876543210/.--,+*)('&&%$#"!!   > !"#$%&'()+,-./012356789:;=>?@ABCEFGHIJKL!MNOPQRSTUVWXYZZ[\]^^_``abbccddeeffghihgfedcba`__^]]\[ZZYXWVVUTSRQPOOMNMLKJIHGFEDCBA@?>=<;:98765443210/.-,+**)('&%$$#"!   @ !"#$%&'()*+,./012345789:;<=?@ABCDEGHIJKLMNOPQRSTUVWXYZZ[\]]^_``abbccddeefghihgfedcba``__^]\\[ZYXXWVUTSSRQPONMNLKJIIHGFEDCBA@?>=<;:9876543210/..-,+*)(''&%$#""!   ? !"#$%&'()*+,-./12345679:;<=>?ABCDEFGIJKLMNOPQRSTUVWXXYZ[\]]^__`aabccddefghihgfedcba`__^^]\[[ZYXWWVUTSRQPPONMLJKJIHGFEDCBA@?>=<;;:9876543210/.-,++*)('&%%$#"!!   A !"#$%&'(*+,-./012456789;<=>?@ACDEFGHIJLMNOPQRSTUVVWXYZ[\\]^__`aabbcdefghgfedcba``__^]\\[ZYYXWVUUTSRQPONMLLKKJIHGFEDCBA@?>=<;:98765432100/.-,+*)(('&%$##"!   @ !"#$%&'()*+-./012346789:;=>?@ABCEFGHIJKLMNPQRSTTUVWXYZ[[\]^^_``abbccddeefghgfedcba`__^^]\[[ZYXWWVUTSRRQPONMLKJILHGFEDDCBA@?>=<;:9876543210/.--,+*)('&&%$#""!   B !"#$%&'()*+,-.012345679:;<=>@ABCDEFHIJKLMNOPQRSTUVWXYYZ[\]^^_``aabccddefghgfedc!baa``__^]\\[ZYYXWVUUTSRQPONNMLKJIHFGFEDCBA@?>=<;:98765543210/.-,+**)('&%$$#"!   A !"#$%&'()+,-./013456789;<=>?@BCDEFGHIJLMNOPQRSTUVWWXYZ[\]]^__`aabbccddefghgfedcba`__^^]\[[ZYXXWVUTSSRQPONMLKJIHHGGFEDCBA@?>=<;:9876543210/..-,+*)(''&%$#""!   E  !"#$%&'()*+,./012346789:;<>?@ABCEFGHIJKLMNPQRSTTUVWXYZ[[\]^^_``abbccdefghgfedcba`_^]]\[ZZYXWVVUTSRQPOONMLKJIHGFECDCBA@?>=<;::9876543210/.-,++*)('&%%$#"!!   B !"#$%&')*+,-./02345679:;<=>@ABCDEFHIJKLMNOPQRSTUVWXYYZ[\]]^__`aabbccddefgfedcba"`__^^]\[[ZYXXWVUTSSRQPONMLKKJIHGFEDDCBA@?>=<;:98765432100/.-,+*)(('&%$##"!   F  !"#$%&'()*+-./012356789:<=>?@ACDEFGHIJLMNOPQRSTUVVWXYZ[\\]^__``abbcdefgfedcba`"_^^]]\[ZZYXWVVUTSRQPPONMLKJIHGFEDCBEBA@?>=<;:9876543210/.--,+*)('&&%$#""!   C !"#$%'()*+,-./12345689:;<=?@ABCDFGHIJKLMNOPQRSTUVWXYZZ[\]]^_``aabbccdefgfedcb'a``__^^]\[[ZYXXWVUTTSRQPONMLLKJIHGFEDCBA?@?>=<;:98765443210/.-,+**)('&%$$#"!   E !"#$%&'()*+-./012456789;<=>?@BCDEFGHJKLMNOPQRSTUVWWXYZ[\\]^^_``aabbccdefedcba`%_^^]]\[ZZYXWWVUTSRRQPONMLKJIHGFFEDCBA@@?>=<;:9876543210/..-,+*)(''&%$#""!   D !"#$%'()*+,-./12345789:;<>?@ABCDFGHIJKLMNOQRRSTUVWXYZZ[\]]^__`aabbcdefedcba`_%^]]\[[ZYXXWVUUTSRQPONNMLKJIHGFEDCBA@?><=<;:99876543210/.-,++*)('&%%$#"!!   F !"#$%&'()*,-./012456789;<=>?@BCDEFGIJKLMNOPQRSTUVWWXYZ[[\]^^_``aabbcdefedcba`_^%]\\[ZZYXWWVUTSRRQPONMLKJIIHGFEDCBA@?>==<;:98765432100/.-,+*)(('&%$##"!   J  !"#$%'()*+,-./12345689:;<>?@ABCEFGHIJKLMNOPRRSTUVWXYYZ[\\]^^_``aabbcdedcba`*_^^]]\[[ZYXXWVUUTSRQPONNMLKJIHGFEDCBA@??>=<>;:9876543210/.--,+*)('&&%$#""!   G !"#$%&'()*+-./012356789;<=>?@BCDEFGHJKLMNOPQRSTUUVWXYZZ[\]]^__``aabbcdedcba`_*^]]\\[ZZYXWWVUTSRRQPONMLKJJIHGFEDCBA@?>=<;:898765433210/.-,+**)('&%$$#"!   I  !"#$%&()*+,-./12345689:;<=?@ABCDFGHIJKLMNOPQRSTUVWXYZ[[\]^^__``aabcdedcba`_^*]\\[ZZYXXWVUUTSRQPOONMLKJIHGFEDCCBA@?>=<;:99876543210/..-,+*)(''&%$#""!   H !"#$%&'()*+,./012356789:<=>?@ACDEFGHIJLMNOPQRSSTUVWXYYZ[\\]^^__``aabcdcba`_^]*\[[ZYYXWVVUTSSRQPONMLKKJIHGFEDCBA@?>=<;:988:76543210/.-,++*)('&%%$#"!!   J  !"#$%&'(*+,-./02345679:;<=>@ABCDEFHIJKLMNOPQRSTUUVWXYZZ[\\]^^__``abcdcba`_/^]]\\[ZZYXXWVUTTSRQPPONMLKJIHGFFEDCBA@?>=<;:987665432100/.-,+*)(('&%$##"!   L  !"#%&'()*+,-.012345789:;=>?@ABCEFGHIJKLMNOPQRSTUVWWXYZZ[\\]^^__``abcba`_^/]\\[[ZYYXWVVUTSRRQPONMLLKJIHGFEDCBA@?>==<;:98765743210/.--,+*)('&&%$#""!   I !"#$%&'()*,-./012456789;<=>?@BCDEFGHIKLMNOPQRRSTUVWX YZ[[\\]^^__`abcba`_^]/\[[ZYYXWWVUTTSRQPPONMLKJIHGGFEDCBA@?>=<;:987654313210/.-,+**)('&%$$#"!   K  !"#$%&')*+,-./12345689:;<=?@ABCDEFHIJKLMNOPQRSTTUVWXYZ[[\\]^^_`aba`_^]\/[ZZYXXWVVUTSRRQPONMLLKJIHGFEDCBA@@?>=<;:98765432210/..-,+*)(''&%$#""!   M  !"#$%&'()*+,-/012346789:;=>?@ABCEFGHIJKLMNOPQRSTUUVW XYYZ[[\\]]^^__`aba`_^]\[/ZYYXWWVUTTSRQPPONMLKJIHHGFEDCBA@?>=<;:987665432130/.-,++*)('&%%$#"!!   J !"#$%&'()*,-./012456789;<=>?@BCDEFGHIJKLMNOPQRSTUUVW XYYZZ[\\]]^^_`a`_^]\[1ZYYXWWVUUTSRRQPONMLLKJIHGFEDCBBA@?>=<;:98765432100//.-,+*)(('&%$##"!   J !"#$%&'(*+,-./12345679:;<=>@ABCDEFGHIJLMNNOPQRSTUVVWXYZZ[[\]^_`_^]\[Z1YXXWVVUTSSRQPOONMLKJIHHGFEDCBA@?>=<;:99876543210/.-.,+*)('&&%$#""!   L  !"#$&'()*+,-/012345789:;<>?@ABCDEFGIJKLMNOOPQRSTUVVWXYZZ[[\\]]^_`_^]\[ZY1XWWVUTTSRQQPONMLLKJIHGFEDCCBA@?>=<;:98765432110/.-*,+**)('&%$$#"!   N  !"#$%&'()*+-./012356789:;=>?@ABCDFGHIJKLMNOPPQRSTUUVWWXYYZ[[\]^_^]\[ZY3XWWVUUTSSRQPOONMLKJIHHGFEDCBA@?>=<<;:9876543210/..-,++*)(''&%$#""!   K !"#$%&'()+,-./012456789;<=>?@ABDEFGHIJKLMNOPPQRSTUUVWWXYYZZ[[\]^_^]\[ZY5XWWVVUTSSRQQPONMLLKJIHGFEDCCBA@?>=<;:98765443210/.-,++,*)('&%%$#"!!   K !"#$%&')*+,-./02345678:;<=>?@ACDEFGHIJKLMNOPPQRSTTUVVWXXYYZZ[[\]^]\[ZYX5WVVUTTSRRQPONNMLKJIHHGFEDCBA@?>==<;:9876543210//.-,+*)($'&%$##"!   M  !"#$%'()*+,-./12345689:;<=>?ABCDEFGHIJKLMNOOPQRSTTUVVWXYYZ[\]\[ZYXWV4UTTSRRQPPONMLLKJIHGFEDCCBA@?>=<;:98766543210/.-,,+*)(''&&%$#""!   O  !"#$%&'()*+,-/012345789:;<=>@ABCDEFGHIJKLMNOOPQRSSTUUVWXYZ[\]\[ZYXWVU4TSSRQPPONMMLKJIHHGFEDCBA@?>>=<;:98765432100/.-,+*))('#&%$$#"!   L !"#$%&'()*+,./012346789:;<=?@ABCDEFGHIJKLMNNOPQRRSTTUVVWWXXYYZ[\[ZYXWVU6TSSRQQPONNMLKKJIHGFEDCCBA@?>=<;:98776543210/.--,+*)(''&$%$#""!   L !"#$%&'()*,-./012356789:;<=?@ABCDEFGHIJKLLMNOPQQRSTTUUVWXYZ[ZYXWVU8TSSRQQPOONMLLKJIHGGFEDCBA@?>>=<;:98765432110/.-,+**)('&%%%$#"!!   N  !"#$%&'(*+,-./012356789:;<=?@ABCDEFGHIJKKLMNOOPQRRSTTUVWXYZYXWV=<;:98876543210/..-,+*)(('&%$##"!   N  !"#$%&()*+,-./012456789:;<=?@ABCDEFGHIIJKLMNNOPQQRSSTUUVVWXYXWVUTSR7QPPONNMLKJJIHGFFEDCBA@?>>=<;:98765433210/.-,++*)('&&%$#" "!   P  !"#$&'()*+,-./012456789:;<=>@ABCDEFFGHIJKLMMNOPPQRRSTTUVWXWVUTSR9QPPONNMLKKJIHHGFEDCCBA@?>=<;:98876543210//.-,+*)(('&%$$#"!   R  !"#$%&'()*+,-./012456789:;<=>?ABCCDEFGHIJKKLMNOOPQQRRSTTUVWXWVUTS=RQQPPONNMLLKJIIHGFEEDCBA@?>>=<;:98765433210/.-,,+*)('&&%$#""!   K !"#$%&'()*+,-./012456789:;<=>?@ABCDEFGHIJJKLMMNOOPQQRRSSTTUVWVUTSRQP:ONNMLLKJJIHGFFEDCBAA@?>=<;:988765432100/.-,+*))('&%$$#"!!   K !"#$%&'()*+,-./012456789:;<=>?@ABCDEFGHHIJKLLMNNOPPQQRRSTUVUTSRQP>=<;:98765443210/.--,+*)(''&%$##"!   M !"#$%&'()*+,-./012356789:;<=>?@ABCDEEFGHIJJKLMMNNOPPQQRRSTUTSRQPONM9LKKJIHHGFEEDCBA@@?>=<;:988765432110/.-,+**)('&%%$#"!!   M !"#$%&'()*+,-./012345789:;<=>?@ABBCDEFGHHIJKKLMMNNOPPQQQRSTSRQPON=MLLKKJIHHGFFEDCBBA@?>=<<;:98765443210/..-,+*)(('&%$##"!   M !"#$%&'()*+,-./012345679:;<=>??@ABCDEFFGHIJJKLLMMNNOOPPQRSRQPONMLK:JIIHGFFEDCCBA@?>>=<;:988765432110/.-,++*)('&&%$#""!   M !"#$%&'()*+,-./0123456789:;<=>?@ABCCDEFGHHIJJKLLMMNNOOPPQRQPONML>KJJIIHGGFEDDCBA@@?>=<;;:98765443210/..-,+*)(('&%$$#"!   I !"#$%&'()*+,-./0123456789:;<=>?@@ABCDEFFGHHIJJKKLMMNOOPQPONMLKJ=IHHGGFEDDCBAA@?>==<;:987765432110/.-,++*)('&&%$#""!   O  !"#$%&'()*+,-./0123456789:;<<=>?@ABCCDEFGGHHIJJKKLLMMNNNOPONMLKAJIIHHGFFEDDCBBA@?>>=<;:998765443210/..-,+*))('&%$$#"!!   J  !"##$%&'()*+,-./1234567789:;<=>?@@ABCDDEFGGHHIJJKKLMMNONMLKJIH>GFFEDDCBBA@??>=<;;:987665432110/.-,++*)(''&%$##"!   L  !!"#$%&'()*+,-./0123456789:;<==>?@ABBCDEEFGGHHIIJJKKLMNMLKJIHGF=EDDCBBA@@?>==<;:988765433210/..-,+*))('&%%$#"!!   I  !"#$%&'()*+,-./01234567899:;<=>??@ABCCDEEFFGHHIIJKLMLKJIHGAFEEDDCBBA@@?>==<;::987655432100/.-,++*)(''&%$##"!   I  !"#$%&'()*+,-./01234456789:;<<=>?@@ABCCDEEFFGGHHIJKLKJIHGFED>CBBA@@?>==<;;:987765432210/.--,+*))('&%%$#"!!   K  !"#$%&'()*+,,-./01234567899:;<=>>?@AABCCDDEEFFGGHHIJJKJIHGFEDCB=A@@?>>=<;;:988765443210//.-,++*)(''&%$##"!   D !"#$$%&'()*+,-./01234556789:;;<=>>?@AABBCDDEEFGHIIHGFEDCCBBAA@@?>>=<;;:9987655432110/.-,,+*))('&%%$#""!   F !""#$%&'()*+,-.//01234567889:;<<=>>?@AABBCCDDEEFGHGFEDCBA@@??>==<;;:9987665432210/..-,+**)(''&%$##"!   A !"#$%&'()**+,-./012344567899:;<<=>>?@@AABCDEFGFEDCBA@??>==<;;:9987765433210//.-,,+*)(('&%%$#""!   C !"#$%&&'()*+,-./001234566789::;<<=>>?@@AABBCDEEFEDCBAE@??>>==<;;:99877654432100/.--,+**)('&&%$##"!   A !"##$%&'()*++,-./012334567789::;<<=>>??@@AABCDDEDCBA@?>B=<<;;:99877655432110/..-,++*)(('&%$$#""!   A !!"#$%&'(()*+,-.//012344567789::;<<==>>??@@ABCCDCBA@?>=<;?:99877655432210//.-,,+*))('&&%$##"!   < !"#$%%&'()*++,-./01123455677899:;;<<=>?@ABBA@?>=<;:9>8776554332100/.--,+**)(''&%$$#"!!   : !""#$%&'(()*+,-../01223455677899:;;<<=>?@AA@?>=<;D:9988776554332100/..-,++*)(('&%%$##"!   < !"#$%%&'()*++,-.//01223455677899::;;<<=>?@@?>=<;:98A766554332110/..-,,+*))('&&%$$#"!!   A  !"##$%&'(()*+,,-./0012234556778899::;;<=>??>=<;:9876@544332110//.-,,+**)(''&%%$#""!   ? !!"#$%%&'())*+,-../00122344566778899::;<==>=<;:9876549322110//.--,+**)(('&&%$##"!!   6 !"##$%&''()*++,-../00122344556789:;<<;:9876@5443322110//.--,++*)(('&&%$$#""!   6 !!"#$%%&'(()*+,,-../001223345566789:;;:9876543921100//.--,++*))(''&%%$##"!   6 !"##$%&&'())*+,,-../001123344556789::98765432107/..--,++*))(''&%%$##"!!   9 !!"#$$%&''())*+,,-..//01122334456789987654329100//..--,++*))(''&&%$$#""!   7 !""#$%%&''())*++,--.//00112233456776543210/.5-,,++*))(('&&%$$#""!   5 !"##$%%&''())*++,--..//00112234566543210/.-,+,*))(('&&%$$##"!!   1 !!"##$%%&''())*++,,--..//00112345543210/.-,1+**))(('&&%%$##"!!   1 !!"##$%%&''())**++,--../0123443210/.-,+*)*(''&&%%$##"!!   - !!"##$%%&''(()**++,,--./012210/.-,+*/)((''&&%%$##""!   + !!"##$%%&&'(())**++,,--./0110/.-,+*)('&&%$$##""!   . !!"##$%%&&''(())**++,,-./00/.-,+*)('+&%%$$##""!   ! !!"##$$%%&&'()*+,-.//.-,+*)('&%$#""!!   $ !!"##$$%%&&''()*+,-.-,+*)('&%$'#""!!   $ !!""##$$%%&&''()*+,,+*)('&%$#"!    !!""##$%&'()*++*)('&%$#"!    !!""##$%&'()**)('&%$#"!    !"#$%&'())('&%$#"!    !"#$%&'('&%$#"!         #,:.*//#196(;@>8'%%1$$!!!!!!!  #'(--,((#-!$#*#&9")/!,! & ~wnh_WAmzoe[QI>::;;;:2g¿ ~}|{zyxwutuuvsld[RH<3(9shZL=0# 2 ~|{|{zslc[RG?5,?zpbUH<0# : ~~}|}|{zyzzrkc[TJB80;zpcWM@3( >  ~}|{zzyxwvuohaYRH?7/4wl`TH=1% >~}|zywvvutsrqmf]VNF=5-.|si]RF;0% >  ~{zyxvutsrrqponmhaYSLC=4-)xofZPE:/$ > }|zywvusrrqonnmlkjid^XPIA:2+%~umcWNC8.# >#~}}{zyyxwvttssrqqonnmmllklkjihgfgfea[UNG@80*!wqh`VLB6+">~|{zyywusrqonmljjiihgffedcba]WQKE>6/*qnf^SI?4+!> ~}|{xwvutq onmllkkjhhffeecbba`_^][TNIB:4-'h}kc[PF<3* >'~}}{{yxvuuttsrqnmljhgfeccbb`_^]\[ZYZWRLF@82,'`zg^WNE;0( >  #|{{zyywwutrqqpponnkjhgdcbba__^^\[ZYXWVUVRMHA=60*%Wuc[TKB8.& > ~}|zywusqpnmmllkjigffecb` _^]][ZZYYXXWWVVUTSRQROID@:4/)$NqaYQH@6.% >  ~{{zyxxvvsrpoomlkiihhgfdcbb`_]\\[[ZYXVVUUTSRQQPONMLJFA=82,&! Fm\UNF=4,# > ~}}{yxwvspomlljihfedca_]\\YXXWWVUSRQQPPONNMLKJIGC>:5/*% @iYRKC<3+" >~{wvvs qpmlmmjiihfec ba`^\Z[ZYXUTRPONLKJJIHHIHGFGGFEEFEDED@;73-($ :fVOI@81(! > ~}|{yyxwusrqomjiheedba_ ^]\[YWWVUTQPONNLJIHGFFEDCBA@<83/,&  5aRKE=6/'  > 1}|zzyvvutsqpommljjgfecba`^\[ZYXWT SRNNMMLJJIGFDDCDCCBA@A@??>?>=>=<841,($ .\PIC;4,& >  -}|{zyvwtssrqpnlkijigfdba^^][YXWUSQONOKIHGFDDBA@?>?>>=<=<;:;;::9:98830-*%!(XLF@81*# >~}{zyywutsqponnljhgfeedca_]\[ZXWTSRPPMMLJIIFEEDCCB@?=<;::998765431.*'# #SIB<5.(" > ~{zywvvsrqpollkjhfedbaa__\[YWWVTSPPOONMLJHGGFDB@?>=<:877665543210/-*'#!ME>93,&  > ~|ywvutsqonnliihgfdb`_\ZXVTTSQPMLKJHGDCA?=<;:8766532210/.-,*'$!FC;60+% > 1}}|{yvtsrqpomkkifgfeca_^\ZYWUSQQPNMJIHGEDA@?;::98765332200//./-,-,,+**+*)*)(&$!A@84.(" > }|{yvsrqnmmljggecdca_][\X WUTRRQOMMLJIFEDCA@==<;:76554320//.-+**)('&%$#" ;=51+%  >~}{zywwvtq.onlkkigedb``^^][YXVUUSRRPONMKJIHFDCCBA@><::8854210/-+*('&&$%$#$#"! !   6:1-(# > }{zxwvtqn)ljihhfdbba^]]\ZWVTSRRPONLLKJHGFECA@?>>=;865 31//-,-,*(&&%%$"" !    26.*%  >  ~}}{xwvusrplkkjhfca__][ZZYWSSQPNOLLJHHGFCA?<;;::88512210,+**)('&##""!  -4*'# > |zzyvutqqnlhfdbb_]\[ZXWVUTPOOLKKIHHDB@??>;976442/..,+)%&&%$#" '0'$  >n~}{zyvtsqopmlljfefecb``\\ZYWUTUTQOMLJIHHEEB@B?=<<;87544320/-++*('$$#"!  $,%" >(|{zxwvtqpomlijjgdcba`^^\[YWVUSQPONLIHF)EBB@>==:997630110/-+)((&%#   )! >U~}|zyxvttqommjifggea`_^\[ZYXVSSRPNMLKHFECAB@>>;;:975542/,--,+)'$##!!    $ >9}{zzwvutrqnlkjhfd%c_\]\[YWWUSPPOMKJIHECB@>>=;:67753210.+) ('%#     > "}~}zyyxuttqnnkiihdca`\ZZYXVTTRPMMKIHGFEB@?=;;:87420/.-+(&%$"    >2~|{{ywvvsrqnlljgffba_%]YWXVUSQQPMJJIGEDDB?=<:99754200/,+*)(%"!    >&~}zywt qpoljihecc`^\WTUTRPNNMJHGECAA@>=9864 20.,,+)'&%$"     > }|{zwur pnmjggecaa]\ZURRQONLLJGEDCA>?>;96542 /.,++)'&%%#!   > |zzyxvuurppomkkheec`__[ZXW SPPOMLKJIFDCB@>;9665221//,++*(&&%#!   >&~~|zxvussqonnkjjgddc_^^ZYWSP NMJJIFDCCA><9664221//,++*(&&%#! >& ~}|zxvtssqonmkjjgeec_^][[XWSP NLKJHFDCCA> <9764221//,+(&&%#! >$~}|{yxxvtssqonnmjjhedc_^^[ZXWSPNMKJIFECCA?>><:75520/-+(&#! > ~}|{yxwvtssqonnmjjhedc_^^[ZXWSPNMKJIFECCA?>><:75520/-+(&#! > ~}|{yxwvtssqonnmjjhedc_^^[ZXWSPNMKJIFECCA?>><:75520/-+(&#! > ~}|{yxwvtssqonnljjhedc_^^[ZXWSPNMKJIFECCA?>><:75520/-+(&#! > ~}|{yxwvtssqonnljjgedc_^^[ZXWTPNMKJIFECCA?>><:75520/-+(&#! > ~}|{yxwvtssronnljjgedc_^^\ZYWTPNMKJIFECCA?>><:7553220/-+('&&#! > ~}|{yxwvtsonnmjjhedca^^\ZYWUPNMKJIGECCA?>><:8553220/.+(&$" >  ~}}{yxwwtspnnmjjheddb^^\ZYWUPOMKJJHECCB@>>=;8553220/.+)&$" > ~}{yxxwtspnnmjjhedda^^\ZYW UQPPOMKJJGEC@> ;8653220/.+ *'&&$" >~}|yxxwtspnnmjjifeca^^\ZYW UQPPOMLKIFEC @>><;8653220/.+ *'&&$#  > ~}|zxxwtsonj ifdda^^]ZZWVPOMLKIGFC @>><;8654220/.+ *'&&%#  >}}|zxxwtspnnmjjifddb^^]ZZWVQPPOMLKJHFDCB?>><;9654220/.+ *'&&$#  >}}|zxxwtspnnmjjifddb^^]ZZWURPPOMLKJHFDCB?>><;9654220/.+ *'&&$#   >}}|zxxwtspnnmjjifddb^^]ZZWUQPPOMLKJHFDCB?>><;9654220/.+ *'&&$#   >}}|zxxwtso nmjjifddb^^]ZZWUQPPOMLKJHFDCC@>><;9654220/.+ *'&&$#   > }}|zxxwtspnnmjjifddb^^][ZWVRPPOMKJJHFDCC@>><;9753220/.+ *'&&$#   >}}|zxxwvsGpnnmkjjfdcb_^\ZZXWWVRPPOMLKJHFDCB?>>;;9653221//,++*'&&$#   > }}|zxxwvsGpnnmkjjfdcb_^^[ZXWWVRPPOMLKJHFDCB?>>;;9653221//,++*'&&$#   >}}|zxxwvspnkjjfddb^[ZXWWVRP/MLKJHFDCB@>>;;9653221//,++*(&&$#!  >~}}zxxwvspnkjjgddb^[ZXWWVRPNLKJHFDCB@>>=;9764221//,+(%&%$! >}}{ywwusqonnkjjfddb_^^[ZXWWVRPNLKJIGDCB?>>=;9864321//,+(%&%$  > ~}}{ywwvsqonnkjjgedc_^^[ZXWSPNLKJHGECBA?>=;:864322//-+(&&%$! > ~}}{ywsqn kjjhedc_^^[ZXWSPNLKJHFECBA?>=;:864322//-+(&&%$" > ~}}{ywsqn kjjhedc_^^[ZXWSPNLKJIFDCBA?>=;:864322//-+(&&%$" > ~}}{ywsqn kjjhedc_^^[ZXWSPNLKJIFDCBA?>=;:864322//-+(&&%$" > ~}}{ywwvsqn kjjhedc_^^[ZXWTPNLKJIFDCBA?>=;:864322/.-+(&&%$" >  }}zxxwvtssqonnljjhedc`^^[ZXWTP"NLKJJFCCBA?>=;:864322/..,+*)&&%$" >}}{ywwvtssronnljjhedc`^^[ZXWTP"NLKJJGCCBA?>=;:8643220..++*)&&%$# > }}{ywwvtssronnljjhedd`^^[ZYWTPOLKJJGCCBA?>=;:8643220..++*)&$"  > }}{xxwwtssrnljjhda_^[ZZWTPOMKJJGECBA?>><:8753220/.++*)&$"  >~}{xvtssqonnljjhddc`^^[ZZWUPOMLJIGECBA?>><:8753220/-++*)&$" > ~}{xvtssqonnmjjhedc`^^\ZXWWXUPOMLJIGECCA?>><:8753220/-++*)&$"  > ~}|yxxvtsonnmkjiedda_^\ZYWTP#OMLJJGDCCA?>><:8753221/-++*)'&&%#  > ~}|yxxvtsonnmkjiedda_^\ZYWTP#OMLJJGDCCA?>><:8753221/-++*)'&&%#  >~}{yxxvtsonnmkjiedda_^\ZYWTP#OMLJIGECCA?>><:8753220/-++*)'&&%#  >~}{yxxvtsonnmkjiedda_^\ZYWTP(OMLJIGECCA?>><:8753220/-++*)'&&%#  >~}{yxxvtspn kjifddb^^]ZYWVQPPOMLJJGECBA?>><:8753221/-+*&%#  >~}|zxxvtspn kjifddb^^]ZZWVQPPOMLJJGFDBA?>><:9754221/-+*'&&%#  > ~}|zxxvtspn kjifddb^^]ZZWVQPPONLJJGFDCB?>><:9763221/-++*)&#  >(~}|zxxwvspn kjjfddb_^]ZZW VROPONMJJGFDCB@>>=;9774221//-+*)'&#!  >%(~|zxxwvspn kjjfddb_^]ZZWVQPPONMKJGFDCB@?>=;9774221//,++*&#!   >%(~|zxxwvspn ljifddb_^]ZZWVQPPONMKJHFDCB@?>=;977420/,++*(&#! >%(~}|zxxwvspn ljjfddb_^^[ZWVQPNMKJHFDCB@?>=;977420/,++*(&#! >% ~}|zxxwvspnljjfddb`^^[ZXWWVRPNMKJHFDCB@?>=;977520/-++*(&$! >% ~}|zxxwvspnljjfddb`^^[ZXWWVRPNMKJHFDCB@?>=;977520/-++*(&$! >% ~}|zxxwvspnljjfddb`^^[ZXWWVRPNMKJHFDCB@>>=;976520/-++*(&#! >%(~}|zxxwvspnljjfddb`^^[ZXWWVSPNMKJHFDCB@>>=;976520/-++*(&#! >%  ~}|zxxwvsqn ljjhddc`^^\ZXWTPNMKJIFDCB@>>=;:76520/-++*(&$! > ~}|zxxwvtssrn ljjhddc`^^\ZYWTPNMKJIGFDBA?>=;:76420.,++*(&$! >  ~}|zxxwwtssrn ljjgddc`^^ZZYWTPONKJIGFDCB?>><:76420/.++*(&$! >  ~}|zxxwwtssqonnljjgedc`^^[ZXWTPNMLJIGFDCA?>><:76420/.++*(&$! >}}{xxwwtssqonnmkjgedc`^^]ZWTPNMLJIGFDCA?>><:87520/.+(&%"  >2}}{yxwwtssqonnmkjgedc`^^\ZYWTPNMLJJGFDCA?>><:87520/.+)'&&%"  >(}}{yxwwussqonnmjjhedca^^\ZYWTPNMLJJGFDCA?>><;86520//,++)'&&%"   >(}}{yxwwussronnmjjhedca^^\ZYWUPNMLJJGEDCA?>><;86520//,++)'&&%"   >(}}{yxwwussronnmjjhedca^^\ZYWUPNMLJJGEDCA?>><;86520//,++)'&&%"   >(}}{yxwwussronnmjjhedca^^\ZYWUPNMLJIGFDCA?>><;86520//,++)&%"   >}}|zwuspnnmkjiedcb_^]ZYWVQPPNNLJIGFDCA@>><;9653220/.,++)&"   >'}}|zwwvvspnnmkjiedcb_^]ZYWVQPPNNLJJGEDCB@>><;9653220/.-++)&"!  > }}|zwvspn kjiedcb_^]ZYW VQPPONLKJGEC@>><<9653220//-++)&%#!  > ~}|yxwwvspn kjhfedc_^]ZYWVQPMLKJGEC@>><<9763221//,++)'&&%"   > ~}}zxwwuspn kjhfedc_^]ZYWVQPNLKJGEC@>><<97620.+)(&&%"!  > ~}}{xwwuspn kjhfedc_^^ZYWVQPNLKJGECCB@>><<97620.+)(&&%$!  > )~}}{xwwvspnljjfedc`^^ZZXWWVQPNLKJHFDCC@>><<:7632/ -++)(&&%$!  > )~}}zxwwvspn ljjfddc`^^[ZXWRP"NLKJHFDCC@>><<:763221//-++)(&&%$! >  )~}}zxwwvspnljjfddc`^^[ZXWWVRP"NLKJHFDCC@>><;9763221//-++)(&&%$! >  )~}}zxwwvspnljjfddc_^^[ZXWWVRP"NLKJHFDCC@>><;9763221//-++)(&&%$! >  )~}}zxwwvspnljjfddc_^^[ZXWWVRP"NLKJHFDCC@>><;9763221//-++)(&&%$! > ~}}zxwwvtssqn ljjgddc`^^[ZXWSP"NLKJIFECB@>><;:763221//-++)(&&%$! >~}}zxwsqn ljjgddc`^][YWSPNLKJIFECA@>><;:7632/ -++)(&&%$! > ~}}|xwsqnmjjgd`^^\ZXWSPNLKJIFECC@>>=;:7632/-+(&$! >~}|ywsqonnljjgedd`^^\YXWSPOMKJIFECC@>>=<:875322//-+(&%!  > ~}{xxwwsqonnljjgedd`^^\YXWSPOMKJIFDCBA>>=<;875322//.+(&%!  > ~}{xxwwussronnmkjgedda^^]ZXWTQPPOMKJJGDCBA>>=<;8753220/-++*)&%" > ~}{xxwvussronnmkjgedda^^]ZXWTQPPOMKJJGDCBA>>=<;8753220..++*)&%" > ~}{xxwvussronnmkjgedda^^\YXWTQPPOMKJJGDCBA>>=<;8753220..++*)&%" > ~}{xxwvussronnmkjgedda^^\YXWTQPPOMKJJGDCBA>>=<;8753220..++*)&%"  > ~}|ywwvussronnmkjgedda^^\YXWTPOMKJJGDCBA>>=<;8753220..++*)&%"  > ~}{xxwvtsonnmkjgedda^^\YXWUQPPOMKJJGDCBA?>=<;8753220.-++*)&$# > ~|{ywwvtssronj hedda^^]ZXW,UQPPOMKJJGDCBA>>=;:9753221.-++*)'&&$#   >~}|zwtssronj hedda^^]ZYWUQPMKJJGDCCB><:9753221/.++*)'&&%"   >~}}yxwwussronj ieddb^^]ZYWUQPNKJJGFDCB?>>=;9762 1//,+*)'&&%"   >~}yxwwussrpnj ieddb^^]ZYWUQPNLJJHFDCB@>>=;9762 1//,+*)'&&%"   >~}yxwwussrpn ljieddb^^]ZYWUQPNLJJHEDCB@>>=;9762 1//,+*)'&&%"    >~|zxwwusqn kjieddc_^^[YWUQPNLJJHEDCB@>>=;9763221//,++)(&#  >}{zxwwusqn kjieddc_^^[YW VQOPPNLJJHFDCB@>>=;9763221//,++)(&#  >}{zxwwusqn kjieddc_^^[YW VQOPPNLJJGFDCB@>>=;9763221//,++)(&#  >}{zxwwuspn kjieddc_^^[YW VQPPONLJJGFDCB@>>=;9763221//,++)'&#  >}{zxwwuspn ljifddc_^^[YWSPPONLJJHFDCB@>>=;976320/,+*)(&#  >}|{ywwusqn mjigddc`^^[ZXWTPNLJJHFECB@>>=;:85420/,+*)(&$! > }|{ywwvsqn mjjhddc`^^[ZXWSPNLJJHFECC@>>=;:86420/,++)(&$   > ,}|{yxwwtssqonnkjjgcdb_^^[ZXWWVSPOMJJHFECCA?>><:8752/-++)(&$!   > ,~}|{yxwvtssqonnkjjgcdb_^^[ZXWWVSPOMJJHFECCA?>><:87520/-++)(&$!   >@}}{yxwwtssqonnmjjgddc`^^\ZXWWVSPNKJIGECCA?>><:87520/-++*)&$!  >#}}{yxwwtssqonnmjjgddca^^\ZXWSPNKJIGECCA?>><:86520/.++*)&%   >*}}{yxwwtssqn mjjgddca^^\ZXWTPNKJJGECCA?>><:86520/.++*)&$   >*}|{yxwwtssqn mjjgddca^^\ZXWTPNKJJGECCA?>><:86520/.++*)&$   >*}|{yxwwtssqn mjjgddca^^\ZXWTPNKJJGECCA?>><:87520/.++*)&$   >*}|{yxwwtssqn mjjgddc`^^\ZXWTPNMKJJGECCA?>><:87520/.++*)&$   >}|{yxwwtssqnmjjgd`]^\ZXWUQPPNMKJJGECCA?>><:8752 1/.++*)'&&$   > (}|{ywwvtssronnmjjiedda^^\ZXWUQPPNNKJJGFDCA?>><;76621/-++*)'&&%"  >}}{ywtssronnmjjiedda^^\ZXWTPNLJJGFDCA?>><;76621/.++*)'&&%"  >(}}{yxwwtssronnmjjhedda^^\ZXWUQPPNNLJJGFDCB?>>=;7652 1/.++*)'&&%"   >(~}{yxwwtssronnmjjhedda^]\ZXWUQPMLKJGECCB?>>=;87621/.+)'&&%!    > ~}{yxwwtspnnmjjheddb^^]ZYWWXUQPMLKJHFDCB?>>=;9653221/.+)(&&%!   >~}{yxwwuspnkjheddb^^]ZYWWXVQP-MLKJHFDCC@>>=<:653221//-++)(&&%" >~}{yxwwuspn kjheddb^^]ZYWVQP-MLKJHFDCC@>>=<:653221//-++)'&&%" >~}{yxwwuspn kjheddb^^]ZYWVQP'MLKJGFDCC@>>=<:653221//-++)'&&%" >~}{yxwwuspn kjheddb^^]ZYWVQP'MLKJGFDCC@>>=<:653221//-++)'&&%" >~}|zxwwuspn kjieddb_^]ZZWROPPMLKJHFDCC@>>=<9554221//-++)'&#   >~}|zxwwuspn kjjfddb`^]ZZW+ROPONMKJGFDCB@>==<9554221//,++)'&&%$!  >~}}zxwwvsqn kjjfddb`^]ZZWROPONLKJGFDCBA>><<:5542/ -++)'&&%$! >~}}zxwwvsponnkjifddc`^]ZZWROPPMLKJGFDCC@>><<966420/-++*'&#! > ~}}zxwwvsponnkjifddc`^]ZZWROPPNLKJGFDCC@>><<986420/-++*(&#! >  }|zxxwusponnkjifddc`^^[ZXWWVRPOLKJHFECBA>>=<;8532/ -++*)'&&#! > }|zxwsqonnljjgedda^][ZYWSPNLKJIFECBA>>=<;8532 0/-++*)'&&#! >}|zxwsponnljjgedc`^][ZYWSPNLKJIFECBA>>=<;8532 0/-++*)'&&#! >}|zxwsponnljjgedc`^][ZYWSPNLKJIFECBA>>=<;8532 0/-++*)'&&#! >}|zxwsponnljjgedc`^][ZXWSPNLKJIFECBA>>=<;8532 0/-++*)'&&#! > }|zxwsponnljjhddc`^][ZXWSPNLKJIFECBA>>=<;85320/-++*(&#! > }|zxw tssqonnljjgda^][ZXWTPNLKJIGECBA?>=<:75320/.++*(&#! > }}{xwtssqonnljjgedda^^\ZXWTPNLLJJGDCBA?>><:75321/.+)'&&$"  > $}}{yxwwtssqoonljjgedda^^\ZYWTPLKJIGECBA?>><:8653221/.+*'&&%"  > ,~}{yxwwtssqoonljjgedda^^\ZYXWWTPMKJJFEDCA> <;8653221/.+*'&&%"  > ,~}{yxwwtssqoonljjhedda^^\ZYXWWTPNKJJGEDCA> =;9653221/.+*'&&%"  >(~}{yxwwussrpnnmjjhfddb_^\ZYXWWTQPMLJJHEDCA@>>=;8653221/.+)'&&%"  >~}{yxwwussrpnjhfdda^^\ZYXWWUQPNLJJHEDCA@>>=;8653221/.+)'&&%"  >~}{yxwwussrpnj hedda^^\ZYXWWVPNKJJHEDCA@>>=;8653221/.+)'&&%"  >~}{yxwwussrpnj hedda^^\ZYXWWVPNKJJHEDCA@>>=;87521/.+)'&&$"   >~}{yxwwussrpnjhedda^^\ZYXWWUQPNKJJHEDCA@>>=;87521/.+)'&&$"  > ~}{yxwwuspn kjifddb_^\ZYWVQPNKJJHFDCA?>>=;87521/.+*'&&$"   >  ~}{yxwwuspnkjifddb_^]ZZXWWVQPNKJJIEDCA?>><;965320-+*(&&%#   >  ~}|zxwwtspn kjifddb_^]ZZXWQPPOMKJJIEDCA?>>=;87632/-+*(&&%#   >! ~}|zxxwvspnkjjfddc_^]ZZXWWVROPPOMJJHFDCB@><97632/,++*(&&%#   > }{zxxwvspnkjjfddc_^]ZZXWWVROPPOLJJHFECB@><9763221//,++*(&&%#   > }{zxxwvs!qonnkjjfddc_^]ZZXWWVROPPOLJJHFDCB@><97632/,++*(&&%"   >  }|{yxwvs!qonnljigddc`^][ZXWWVRPPONLJJHFDCB@><:8642/,++*(&&%#!  >  }|{yxwvs!qonnljigddc`^][ZXWWVRPPONLJJHFDCB@><:8642/,++*(&&%#!  >  }|{yxwvsqnljigddc`^][ZXWWVRPPONLJJHFDCB@><:8642/,++*(&&%#!  >  }|{yxwvsqnljigddb`^][ZXWWVSPPONLJJHFDCB@><:8642/,++*(&&%#!  >  }|{yxwvs!qonnljifddb`^][ZXWWVSPPONLJJIFDCB@><:8642/,++*(&&%#"  >  ~}|{yxwvsqn mjigddc`^^\ZYWSP OLJJIFECB@><:76520/,++*)&&%$!  >  }}|{ywwvtssqonnljihddc`^^\ZYWSPOLJGECB@><:75520/,++*)& %!  > /}}|{yxxwtssqonnljjhddc`^^\ZYWSPNKJJGECCA>=;76520/.++*(& $"  >(}|{yxxwtssqonnmkjhedca^^\ZYWSPNKJJGECCA>=:66520/.+)&$!  >}|zxwtssronnmkjhedca^^\ZYWSPNKJJGECCA>=:76521/.+)'&&$!  > ~}|{yxxwussronnmjjiedda^^\ZYWTPONKJJGECCA?>>=;76521/-+)'&&$"  > ~}|{yxxwussronnmjjiedda^^\ZYWTQPPONKJJGECCA?>>=;76521/-+)'&&$"  > ~}|{yxxwussronnmjjiedda^^\ZYWTQPPNNKJJGECCA?>>=;76521/-+)'&&$"  > ~}|{ywussronnmjjiedda^^\ZYWTQPPNNKJJGECCA?>>=;76521/-+)'&&$"  >)~}|{ywuspnnmjjiedda^^\ZYWUPNMKJJHECCA?>>=;75521/-+)'&&$"  >-~}|{zxwwtspnnmjjifddb^^]ZYWUQPPNMKJJHEDCA?>>=;9553221/-+)(&&%!   >-~}|{zxwwuspnAkjieddb^^]ZYWWXUQPPNNKJJIFDBA@>>=;9553221/.,++)(&&%"   >}}{zxwwvspnAkjiedcb_^]ZYWWXUQPPOMLJJIECCB@>>=;9553221/.,++*(&&%"   > ~|{zxwwvs!ponnkjifddc_^]ZYWWXUQPPOMLJJIECCB@> <9653221/.,++*(&&%"   >~}|zxwwvsponnkjhfedc_^][YWVRP MLKJIECCB@><876320.,++*(&&%"   >~}|zxwwvsqonnkjifddc`^^[ZWVRPPONLJJHFCCB@><965420.,++*(&#   > }}|zxwwvsqonnkjjfddb_^^[ZW ROPPNLKJIFCCB@><:6542/.,++*(&#! >  }}|zxwwvsqonmkjjfddb_^^[ZWROPPMLKJIFCCB@>>=<:6542!/.,++*(&&%#! >  }}|zxwwvsqonmkjjfddb_^^[ZWROPPMLKJIFCCB@>>=<:6542!/.,++*(&&%#! > }}|zxwwvsqonmkjjfddb_^^[ZWROPPMLKJIFCCB@>>=<:6542!0.,++*(&&%#! > }}|{ywwvsqonmkjjfddb_^^[ZXWROPPMLKJIFDCB@>>=<:6552!0.,++*(&&%$! >}}|zxwwvsqonnkjjgddb_^^[ZXWRPMLKJIFDCBA?>=<;7542/-++*(&&%#" >}}|yxwwvsqonnkjjgddca^^[ZXWTPMLKJIFDCCA>>=<;6532!0/-++*(&&%#" >~}}{yxwvsqoonljjhda^^[ZXWSP OLJJIFDCB@>;977421/-+)'&&$"  >}}{yxwvsqoonljjhd`^^[ZXWSP NLJJIFDCB@><98742/-+*'&&$"  > ~}}{yxwvtssqoonljjhd`^^[ZYWSPNLJJIGECBA?>>;:7742/-+)&$"  > ~}}{yxwwussroonmjkhda^^[ZYWSPNLKJIGECBA?>>;:77420/-+)&$" >(~}}{yxwwussroonmjjiedda^^\ZYWTPNLKJIGECBA?>>;:77421/-+)'&&$"  >  $~}}{yxwwussroonljjiedda^^\ZYWTPNLKJIGECBA?>>;:77421/-+)'&&$"  > $~}}{yxwwussroonljjhedda^^\ZYWTPNLKJIGECBA?>>;:77421/-+)'&&$"  >  $~}}{yxwwtssroonljjhedda^^\ZYWTPNLKJIGECBA?>>;;86421/-+)'&&$"  > 2~}}{yxwwtssroonmjjiedda^^\ZYWUQPPNLKJIGECBA@>>;;96420/.+)'&&$" >  ~}}{yxwvtsponnjjheddc^^][YWUQPPOLKJJHECCA@>>;;96421..+*'&&$#  >  ~}}{yxwvtsponnjjheddc^^\ZYWUQPLKJJHECCA@>>;;86421..+*'&&$#  > ~}|zxxwtsponnjjheddb^^\ZZWUQPNLJJHFDBA@>><:9764221/.+ *'&&%#  > ~}|zxxvusqonmjjifdda_^][ZWUQP MKJJIFDCB@>(:9853221/.,++*'&&%#  > }}|zxxvusqonmjjifddb_^][ZWQP5MKJJIFDCBA>>=;9753221/.,++*'&&%# > .}}|zxxvuspnj ifddc_^][ZWVRPMKJJIFDCB@>>=;876420.,++*(&&%#  >  .}}|zxxvuspnj ifddc^^][ZWVRPMKJJIFDCB@>>=;876420.,++*(&&%#  >  }}|zxxvuspnj ifddc^^][ZWVRPMKJJIFDCB@>>=;876420.,++*(&&%#   >  }}|zxxvuspnj ifddc^^][ZWVRPMKJJIFDCB@>>=;876420.,++*(&&%#  > ~}|zxxvusqn ljjgdcc_^][ZWVRPMKJJIFDCB@>>=;876420.,++*(&&%#! > ~}|{yxvusstqnljjgd _^^[ZXWWVRP MKJJIFDCB@>:975320.,++*(&&%$! >  ~}|{ywwvsstqnljjgd_^^[ZXWSPNKJJHFDCB@>>=::86420.,++*(&$! >   }|{ywwvsstqn ljjgddc`^^[ZXWSPNMKJHGDCCA?>><987520/-++*(&$!  >  ~{{ywwvtssronnljjgddc`^^[ZXWSPNMKJHFDCCA?>><977520/-+)& $!  >  2~|{ywwvtssronnljjgddc`^^\ZXWSPNLKJHFDCCA?>><977520/-++*(& $!  > 2}}{ywwvtssronnmjjhddc`^^[ZYWSOPPNLKJIFECCA?>><:8653220/-++*(& $!  >2}}{ywwvtssronnmjjhddc`^^[ZYWTPNLKJJGECCA?>><:8653220/-++*(&$! > -}}{ywwvtssronnmjjgddc`^^[ZYWTPNLKJJGECCA?>><:8653220/-++*(&$! > }}{ywwvsronnljjgddc`^^[ZYWTPNLKJJGECCA?>><:8653220/-++*(&$! >}}{ywwvsronnljjhddc`^^[ZYW,UQPPNLKJJGECCA?>><:8653221/-++*)'&&%# >1~}}{ywwvtssronnmjjhddc`_^\ZYWWXUP(NLKJJGFCBA?>><:8653221/-++*)(&&%" >~}}{ywtssronnmjjhddca_^]ZYWWXUP"OMKJJGFCCB@>><;8664221/.++*)(&&%"  > ~}}{yxxwtssronnmjjhddca_^]ZYWWXUP.ONLJJGFCCB@>>=;8664221/.++*)(&&$"  >6~|{yxxwtssronnmkjhedca^^\[ZWWXUPOMLJIGFCCB@>>=;86521/.+*'&&$"  >+}}{yxxwtssronkjhfdca^^]ZYWWXUPOMLJIGFCCB@>>=:87521/.+*'&&%"  > $}}{yxxwtssronnmkjifddb_^]ZYWVQPPOMLJJGECCB@>>=;8664221/.+*'&&%#   >  }}{yxxwuspnnmkjifddb_^]ZYW2VROPOMLJJGFDCB@>>=;8654221/.,++*'&&%#   >" }}{ywuspnnmkjhfddb_^]ZYW2VQPPOMLJJGFDCB@>>=;8654220/.,++*'&&%#   >#}}{ywuspnnmkjhfddb_^]ZYW2VQPPOMLJJGFDCB@>>=;8654220/.,++*'&&%#   >}}{ywuspnnmkjhfddb_^]ZYW3QOPOMLJJGFDCB@>>=;8654220/.,++*'&&%#   > }}{zxwwuspn/kjifdca_^]ZZXWWVROPOMLJJHFDCB@>><;9654220/.,++*'&#! > }}{{xwwutsspn kjiedca_^]ZZXWROPOMLJJHFCCB@?><;9654220/.,++*'&#!  > }}|zxwwvtsspn kjieddb`^]ZZXW ROPOMLKJIFCA?>=;9654221//-++*'&#!   > }}|zxwwvtsspn kjieddb_^]ZZXW ROPOMLKJHFCA?>><9654221//-++*'&#!   > ~}|zxwwvtssqonnkjigedb`^^[YWSPMLKJGFCA?>=<97642210/-+('&&#!  > ~}|zxwtssqonnljjgedc`^^[YXWSPNMKJHFDCCA?>=<:7552210/.+('&&$! > ~}|zxwtssqonnljjgddc`^^[ZYWTPNMKJHFECCA?>=<:7552210/.+('&&$" >! 2~}|zxwwvtssqonnljjgddc`^^[ZYWSPMLKJHFECCA?>=<:7552210/-+('&&$" > C~}|zxwwvtssqonnljjgddc`^^[ZYWSPMLKJHFECCA?>=<:7552210/-+('&&$" > :~}|zxwwvtssqonnljjgedc`^^[ZYWSPMLKJHFECCA?>=<:7552210/-+('&&$" > :~}|zxwwvtssqonnljjgedb`^^[ZYWTPMLKJHFECCA?>=<:755221//-+('&&$" > #~}|zxwtssqonnmjjhedca_^\ZYWTPMLKJIGECCA>>=<:75420/.+('&&$# > #~}}{xwtssqonj hedda^^\ZYWTPNLKJIGECCB>>=;:75420/.+)&#" > $~}|yxwwtssqoonnjjiedda^^\ZYWTPMKJIGECCA?>><:8763220/.+*& %# > $~}|zxwwtssqoonnjjiedd`_^\ZYWTPMKJIGECCA?>><:8763220/.++,*& %" > :~}|zxwwtssqoonmjjiedca_^\ZYWTPOMKJIGECCA?>><;8653220/.++,)& %"  > :~}|zxwwussroonmjjhfdcb_^][YWTPOMLJJGEDCB?>><;8653220//,++*& %#  >'~}|zxwwusonj hfdcb_^][YWUQPPOMLJJGEDCB?>><;8653220//,++*& %#   >~}|zxwwusonj hfdcb_^][YWUQPPOMLJJGEDCB?>><;8653220//,++*& %#   > ~}|zxwwusonj hfdcb_^][YWUQPPOMLJJGECCB?>><;8653220//,++*& %#   > ~}|zxwwusonj hfdcb_^][YW,VQPPOMLJJGECCB?>><;8653220/.,++)'&&%#   >  }}|zxwvvsponnkjifdcb_^][YW3ROPOMKJJHECCB@>>;;9653221/.,++)'&&%$!  >  }}|zxwvvsponnkjifdcc_^][YW4VRPPOMKJJIECCB@>>;;9653221/.,++*(&&%$!  >  ~}}zxxwvspn kjjgddb_^][ZWVRPNLJJHFDCB@>>=;9764221//,++*(&$!  >  }}zxxwvspn kjjgddb_^][ZW VROPPNLJJHFDCB@>>=;9864221//,++*(&$!  > ~}}zxxwvtssponnkjjgddb_^][ZWRP"NLKJHFDCCA>>=<9774221//-++*(&&%$!  >  ~}}zxxwvtssqn ljjgddc_^^[YWRPNLKJIFDCCA>>=<:773221//-+'&&%#!  >   ~}}{xxwvtssqn ljjgddc`^^[YWRPNLKJIFDCCA>>=<:8742/.-+(&#!  > ~}}{xxwvtssqn ljjgddc`^^[YWRPNLKJIFDCCA>>=<:8742/.-+(&#!  > '~}}{xxwvtssqn ljjgddc`^^[YWRPNLKJHFDCCA>>=<:874221//-+(&#!  >  '~}|{xxwvtssqn ljjgddc`^^[YWRPNLKJHFDCCA>>=<:874221//-+(&#!  > 4~}|{xxwvtssqn ljjgddc`^^[YWRPNLKJHFDCCA>>=<:874221//-+(& #!  > ;}}{ywwvtssronnmjigd`^^\ZXWSPNLKJIFECCA>>=<97642/.-++*)& $"  > $-~|{ywwvtssronnmjihedd`^^\ZYWWXUPPONMKJIGECCA>;:8642/-+(&$"  > U~|{yxwwussronnmjjiedda_^\ZYWWXUPPONMKJIGECCB>=;8762/.+(& $"  > U~|{yxwwussronnmjjiedca_^\ZYWWXUPPONMKJIGECCB>=;8762/.+(& $"  > ]~|{yxwwussronnmkjhedca_^\ZYWWXUPPONMLJIGECCB?>>=;87620/.,+*)'&&$"  >>~|{zxwwussronnmkjhedda_^]ZYWWXUPOMLJIFECCB?>>=;9753220/.++*)&%"  > 2~|{zxwwussronnmjjiedda_^]ZYWTQPPOMLJJGECCB?>>=;9753220/.++*)&%"  > (~|{zxwwussronnmjjiedda_^]ZYW TQPPOMLJJGECCB>=;8763220/.++*)&%"  > (~}}{yxwwussronnmjjiedda_^]ZYW TQPPOMLJJGECCB>=;8763220/.++*)&$!  >}|{zxwwuson kjhedda^^]ZYW UQPPOMLJJHECCB>&=;9664221/.++*)'&&%"  >}|{zxwwuson kjheddb_^]ZYW7UQPPOMLJJHFDCB?>>=;9664221/.,+*)'&&%"  >}|{zxwwvspn kjjfddb_^]ZZW4VRPPONMJJHFDCC?>><;9664221//,+*)'&&%"   >}|{zxxwvspn kjjfdda^^]ZZW4VRPPONMJJHFDCC?>>=;9664221//,+*)'&&%"   >~|{zxxwvsponnkjjfddb_^]ZYWVRPPONMJJHFDCC@><9664221//,+*)'&&%"   > '~}|zxwwvsponmlkifddb_^][YWVRPPOMLKJHFDCC@><9753221//,++*'&&%#    > ~}|zxwwvsponnkjifddc`^^[ZWVROPONLJJHFECC@><:75320/-++*(&#!  > ~}|zxwwvsqonnkjjfddc_^^[ZWVROPONLJJIFECC@><:753221//-++*(&#!  > ~}|zxwwvsqonnkjjfddc_^^[ZWVROPONLJJIFECC@><:753221//-++*(&#!  >  ~}|zxwwvsqonnkjjfddb_^^[ZWVROPONLKJHFDCC@><:753221//-++*(&#!  >  ~}|zxwwvsqonnkjjfddb_^^[ZW ROPONLKJHFDCC@><:753221//-++*(&#!   > ~}{{xwwvsqn ljjfddb_^^[ZXW SPPONLKJHFECC@><:755221//-++*(&#!   >  ~}{{xwwvussqn ljjgddb`^^[ZXWSPPONMKJHGECC@?>>;:855221//-++*(&$!   > ~}{{xwtssqn ljjgddca^^[ZXWSPPONMJJIFECCA?>><;855221//-++*(&$"   >  ~}zxxwwsqoonljjhedda^^[ZXWTPNMLJIFECCB?>=<;76421/.+ )'&&$"  >  ~}zxxwwtssqoonljjhedda^^[ZXWTPNMLJIFECCB>>=<;86421/-+ )'&&$"  > }|{zxwwussroonljjida^^[ZYWTPNMLJIGFCCB>>=<<96521/.+ )'&&$"  > }|{zxwwtssroonmjjida^^\ZYWTPNMLJIGFCCB>>=<<96521/.+ )'&&$"  > }}zyxwwtssroonmjjida^^\ZYWUQPPNMKJIGFCCB>>=<<96420/.+ )'&&$"   > }}zyxwwtssroonmjjida^^\ZYW TQPPNMKJIGFCCB>;96420/.+ )'&&$"   > }}zyxwwtssroonmjjida^^\ZYW TQPPNMKJIGFCCB>;:86420/.+ )'&&$"   > ~}zyxwwussroonmjihedca^^\ZZWUPNMKJIGFDCA?>>;:86420//,++)'&&$"   >  }}zyxwwvspn kjjedda^]\ZYWUQPPNMLJJHFDCA?>>;;96421/.,++)'&&$"   >  }}{yxwwvspnjeddb^^][YWUQPPOMLJJHFDCA?>>;;96421/.,++)'&&$#   >  }}{yxwwvspnnmjjieddb^^][YWUQPPOMLJJHFDCA?>>;;97521/.,++)'&&$#   > ~}|yxxwvso mjjheddb^^]ZYWUQPPOMLJJHFDCA?>><;98621/.,++*'&&%#   > ~}|zxwwvssro7mjihfddb`^]ZZXWWUQPPONLJJHFDCC?>><:9763221/.,++*'&&%#   >  ~}|zxwwvssro7mjjifddb`^]ZZXWWURPPONLKJHFDCA@>><;:763221/.,++*'&&%#    >  ~}|zxxwusonj ifddc_^]ZZXWRPPONLJJHFDCA@>><;:7632/ -++*(&&%#    > ~}|zxxwusonj ifddc_^]ZZXWRPPONLJJHFDCA@>><;:7632 /.,++*(&&%#    >  ~}|yxxwusonj ifddc_^]ZZXWRPPONLJJHFDCA@>><;:7632 /.,++*(&&%#   > ~}|yxxwusonj ifddb`^]ZZXWRPPONLJJHFDCAA>><;:7632 /.,++)(&&%#   >  ~}|zxxwvtsspn ljifedc_^^[ZXWSPPONLJJIFDCB@>><;:7632 0.-++*)&&%#!   > ~}|zxxwuspn ljifedc_^^[ZXWSPPONLJJIFDCB?>><::7542 0.,++*)&&%#!   > ~}}zxvtsspnljigd`^^[ZXWSP OMKJIFECB@>;976420/-++*)&$!   >  ~}}zxxwwtsspnljigd`^^[ZXWSP OMKJIFECCB><:77420/.++*)&$!   > }|zxxwwtssqonnljigd`^^[ZXWSP OMKJIGEDCA><:77420/.++*)&#!   >  }|{xxwwtssrn mjjhedca^^\ZYWSP OMKJIGEDCA><;8742 0/.++*)'&&#!   >  }|{xxwwtssrnmjjhedca^^\ZYWWXTOPPOMKJIGEDCA><;87420/.++*)'&&#"  >  }|{xxwwtssrnmjjhedca^^\ZYWWXUP OMKJIGECCA><;87420/.++*)'&&#"  >  }|{xxwwtssrnmjjhedca^^\ZYWWXUP OMKJIGECCA><;87420/.++*)'&&#"  >  }|{xxwwtssrnmjjhedca^^\ZYWWXUP NMKJIFECCA><;87420/.++*)&#!  > }|{xxwwtssronnmjigedca^^\ZYWTP NMKJJGECCA><;87420/.++*)&#"  >  }|{yxwwtssronkigedda^^\ZYWWVUP OMKJJGFDCA>$<:8743220/.++*)'&&$"  >  ~|{yxwwtssron kihfdda^^\ZYW UQPPOMKJJGFDCA><:8653220/.,+*)'&%&#  > ~|{yxxwussron kjheddb_^\ZYW UQPPONLJJGFDCB?>>=<8753220//-+*)'&#   > }|{yxxwussronnmjjhedca_^\ZYW UQPPONLJJGFDCB?>>=<8753220//-++*'&#   > }|{yxxwussron kjhfddb_^\[YWUQPPONLJJGFDCB?>>=<8753221//+*'&#   >  }}{zxxwussrpn kjhfddb`^]ZYWVQPPONLJJHFDCB?>>=<9653221//+)(&#    > }}{zxxwussrpn kjhfddb`^]ZYW VQPPONLJJHFDCB?>>=<9653221//-++)(&#    > }|{yxxwussrpn kjhfddb`^]ZYW VQPPONLJJHFDCB?>>=<9653221//-++)(&#    > }|{yxxwussrpn kjhfddb_^]ZYW VQPPONLJJHFDCB?>>=<9653221//-++)(&#    >  }|{yxxwuspn kjhfddb_^]ZYW VRPPONLJJIFDCB?>>=<9653221//-++)(&#    > }|{zxxwuspn liifddb_^]ZYWRPPONLJJIFECB?>>=;966420.,++)(&#   > }|{zxxwvsqn liigddb_^]ZYWRPPONLJJIFDCBA?>=;96642/-++)(&#!  >}}{zxwsqn kjifddb_^]ZYW ROPONLKJIFDCCA>;96532/-++*(&#!   >~}{zxxwwsqonnkjifddc`^][YW RPPONLKJIFDCCA>=:7532/-++*(&#!   >~}|zyxwwsqn kjigedc`^]\ZXWRP NLKJJFDCCA>=:75420/-+)'&&$!  > ~}|{yxwwsqn kjigedc`^^\ZXWSP NMKJJFDCCA>=:75420/-++*)'&&$!  > ~}|{yxwwtssronnmjigedca^^\ZXWSP NMKJIGDCCA><:75420/-++*)'&&$!  > ~}|{yxwwtssronnmjigedca^^\ZXWSP NMKJIGDCCA><:75420/-+)'&&$!  > ~}|{yxwwtssronnmjigedca^^\ZXWSP NMKJIFDCCA><:75420/-+)'&&$!  > ~}|{yxwwtssronnmjigedca^^\ZXWSP NMKJIFDCCA><:75420/-+)'&&$!  >  ~}|{yxwwtssronnljigedca^^\ZXWTPNMKJIFDCCA>>=<:75420/-+)'%%$!   >  ~}|{yxwwusonnmkjhedb`^^\ZXWTPNMKJJGECCB?>><:85520/.,++)'%%$"  >  ~}|{yxwwtsonj heddb^^\ZXWTPNMLJJGEC?>=<<85520/.+ )'%%$"  > }|{yxxwtssronj heddb^^\ZXWTPOMKJJGEC?>=<;86521/-+ *'&&$"   > ~}{yxxwtssronj hfedb^^\ZXWTPOMKJJGEC?>>=;86521/-+ *(&&$"   > }}{zxxwtssronj heddb_^\ZYW UQPPOMKJJHFCCB> <;9653220/-+*(&&$"  >  }}{zxwwuspnj ifddb`^]ZYWURPPOMKJJHECCB?>><:9653220/.+ *(&&$"  > }}{zxwwusonj ifddb_^]ZYWURPPOMKJJHECCB?>><:9653221/-+ *(&&$"  > }}{zxwwusonj ifddb_^]ZYWURPPOMKJJHECCA?>><:9653221/-+ *(&&$"  > }}{zxwwusonj ifddb_^]ZYWURPPOMKJJHECCA?>><:9653221/-+ *(&&$"  > }}{zxwwusonj ifddb_^]ZYWVRPPOMKJJHFDBA@>><;9653221/-+*'&&$"  > }}{zxwwuspnj ifddb`^]ZYW,VRPPOMLKJHGDBA@>><;9653221/.,++*'&&$"  > }}{zxwwvspn kjigedc_^][YW,VRPPOMLKJHFDBA@>><;9653221//,++*(&&$#! > }|zxvsponnkjigedc_^^[ZWVRPNLJJHFCCB@>>=;:764221//,+(&&%#! >  }|{xvsponnkjigedc_^^[ZWVRPNLJJHFDCB@>>=;:764221//,+(&&%#! > ~}|{yxxvsponnljjgedc`^^ZZXWWVRP NLJJIGDCB@> ;:764221//-+(&&%#!  >  ~}|zxxwvtssqonnljjgedca^^ZZXWWVSP NLKJJGDCB@>;:764221//-++*(&&%#!  > ~}|{xvtssqn ljjgedca^^ZZXWTPNLJGDCB@>;:7642 0/-++*(&&%#!  >~}|{xvtssqn ljjgedca^^ZZXWTPNLJGDCB@>;:7642 0/-++*(&&%#!  >~}|{xvtssqn ljjgedc`^^ZZXWTPNLJGDCB@>;:7642 0.-++*(&&%#"  >~}|{xvtssqn ljjgedc_^^ZZWUPNLJGDCB@>;:7642 0.-++*(&&%#"  > ~}|{xvtssrn ljjgddc_^^[ZWUPNLJHDCBA>;:7642 0.-++*(&&%#"  > }}{yxwvsrnmjjgd a^^\ZXWWVSPNLJGDCC@>;986521/,+*'&&$"  > }}{yxxvsrnmjjgda^^\ZXWUPNLJGDCC@>;:86521/,+*'&&%" >}}|yxxvtssrn mjjhedda^^\ZXWUPOMKJIGECCB?>><;86520/-+ *'&&%" >,~|{yxxwtssrpnnmjjhedda^^\ZXWUPOMKJIHFDCB?>><;86520/.,++*'&&%" > ,~}{yxxwtssrpnnmjjifdda^^\[XWUPOMKJJHECCB?>><;86521/.,++*'&&$"  >~}{zxxwtspnnmjjieddb^^]ZYWUP/OMKJIFECCB?>>=;7753221/.,++)(&&$"  > ~}{zxxwtspnnmjjheddb^^]ZYW3VQPPOMKJIFECCB?>>=;7753221/.,++*'&&$"  >2~}{zxxwtssrpnnmjjheddb^^]ZYW3VQPPOMKJIFECCB?>>=;7753221/.,++*'&&$"  > 2~}{zxxwtssrpnnmjjheddb^^]ZYW3VQPPOMKJIFECCB?>>=;7753221/.,++*'&&$"  >~}{zxxwtsonnmkjheddb^^]ZYWVROPOMKJJGECCB?>>=;77532/.,++)(&&$"  >~}|{yxwtson%kjheddb_^]ZZXWWVRPPOMKJJGFDCB?>>=;86532 /.,++)(&&%"   >~}|{yxwus(pnnmjjieddb`^]ZZXWWVRPPONLJJIFDCB@>><<95421..+**)'%%$" >~}|{yxwus)pnnmjjieedb_^]ZZXWWVRPPOMLJJHFCBB@>><;86421.-*)'%%$" >}}{zxxwusonnmjjigedb^^]YYWVRO MKIIGFCBB@=;86531.+*'%%$" > ~}|zxxwusonnmjjifccb^^\ZYWVRO MLJIHFCBB?=;86531.,* '%%$"  >~}|zxwwurpnmmkiifccb_^][YWVRO MLJIIFCBB?=;86531.,**)'% "  >~}|zxwwurpnmmkiifccb_^][YWVRO MLJIIFCBB?=;86531.,**)'% "  > ~}|zxwwurpnmmkiifccb_^][YWVRO MLJIIFCBB?=;86531.,**)'% "  >~}|zxwwurpnmmkiifccb_^][YWVRO MLJIGECBB?=;86531.,**)'% "  > ~}|zxwwurpnmmkiifccb_^][YWVRO MLJIGECBB?=;86531/.,**)'% "  >~}|zxwwvrqnmmkiigdcb`^][YWVSO MLJIHECBB?=;865310.,**)(&%$#   >~}|zxxwvrpnmmliigdcc`^][YXV TPOOMLJIHECBB?=;97542110.+**)(&%%#   >~}}{xxwvsrrpnmmliihdcc`^][YXV$TPOONMKIHEDBBA?==<96652110.,**)(&%%#   >}}{ywwvtrrpommliihdcc`]][YXV$TPOONMKJHEDBBA?==<96652110.-+**)&%%#   > }}{ywwvtrrqommkjihdcb`]][YXVTPOONMKJHEDBB@>==<96641 0.-+**)&%%$!  > }}{ywwvtrrqnmmljihecc`]][YXVVWTO.NMKJIFDBB@>==<97652110.-+*)('%%$!  > }}{ywwvtrrqnmmljihecc`^][YYV2UPOONMKIIFDBB@>==<97652110.-**)('%%$!  >}}{ywwvtrrqnmmljihdcc`^][YYV2UPOONMKIIFDBBA?==<97552110.-**)('%%$!  >}}{ywwvtrrqnmmljihdcc`^][YYV2UPOONMKIIFDBBA?==<97552110.-**)('%%$!  > }}{xxwvtrrqnmmljihdcc`]][YXV2UPOONMKIIFDBBA?==<97552110.-**)('%%$!  > ~}}{xxwvtrrqnmmljihdcc`]][YXV,UPOONMKIIGECB@>==<97552110.-**)('%%$!  >  ~}}|ywwvtrommljihecca^][YYV UPOOMMKIIGECB@>==<97552110.-+*)('%"  >  ~}}|ywwvtrommljiiecca^][YYVUPOONMKIIGEBBA?==<:8552110..+**(%"  >  ~~}|ywwvusrrommljiiecca^][YYV QNONMLJIGEB@=:7552110..+**)&%"  > 2~~}|ywwvusrrommlkihecca^]\ZYV QNONMLJIGEB@=:8652110/.+**)&%%$" > 2~}{ywwvusrrpmmlkihecca_]][XV QNONMLJIFEB@= :8653110/-*)&%%$" > ~}{zxwvusrrpm kiifccb^]]YYWVRNONMLJIFECBB@>==;8654110/.+**)&%#  > "~}{zxwvusrrpm kiifccb^]]YYWVRNONMLJIGECBB@>==;8654110/.+**)&%#  > *}}{zxwvusrrpm kiheccb^]]YYWVQNONMLJIGECBB@>==;8654110/.+**)&%#  > *}}{zxwvusrrpm kiheccb^]]YYWV QNONMLJIGECBB@=;8653110/.+**)&%%$"  >:}}{zxwvusrrpm kiheccb_]]YYWV RNONMLJIGECBB@=;8653110/.+**)&%%$"  > }}{{xwvusrrpm liifccb`]]ZYWV&ROONMLJIGEDBB@>=<;9654110/.,**)&%%$"   >  }}{{xwvusrrpm liifccb`]]ZYWV&ROONMLJIGEDBA@==<;9654110/.,**)&%%$"!  >}}|zxwwusrrpnmmliifdcc`]]ZYWVSOMLKJGEDBB@==<;97641/.,*(%#   >}}|zxwwusrrpnmmliifdcc`]]ZYWVSOMLKJGEDBB@==<;97641/.,*(%#   > ~}|zxwwusrrpnmmljifdcc`]]\YWVRO NLKJGEDBB@= ;97642110.,*(%#  > ~}|{ywwusrrpnmmljifdcb`]][YYVSONLKJHEECBA>==;97642110.,*(%#  >  ~}|{ywwvtrnmmljigdcca]][YYVTONLKJIFECBB?=<;9764211/.-*(% #!  > ~}|{ywwvtrnmmljigdcb`]][YYVTONLKJIFECBB?=<;9764211/.-*(% #!  > ~}|{ywwvtrrqnmmljigdcb`]]\ZYVTONLKJIFECBB?=<;9764211/.-*(% #!  > ~}|zxwwvtrrqnmmljigdcb`]]\YXVVWTONLKJIFECAA?=<;9764211/.-*(% #"  > ~}|zxwwvtrnmmliihecca]][XWVUQOONLKJIGDBAA?=<;:754211/..+**(% #" > ~}|zxwwvtrommljihecca]]\YWVUQOONLKJIGCBAA?=<;:7542110..* )&%%#"  > ~}}{xwwvtromi gecca]]\ZXVUQOONLKJIGDBAA?==<:7542110..*&%%$"  >~}}|yxwvtromi gfdca]]\ZYVUQOMKJIGECBA?==<:7653110.-*&%%$"  > ~}|yxwvtromi hedca_^\ZYVUQOMKJIGECBA?==;:8753110..,*&%%$"  >~}|yxwvtrom jihfdca_]\ZYVUQOMLJIGECBA?==;:8653110..+*&%%$"  >~}|yxwwurpm jihfdcb^]\ZYV&QNOOMKJIHDCBA?==;:8653110..+**)&%%$#   > ~}|yxwwurpm jihfdcb^]\ZYV&QNOOMKJIHDCBA?==;:8753110..,**)&%%$#  >~}|yxwwurpm jihfdcb^]\ZYV&QNOOMKJIHDCBA?==;:8753110..,**)&%%$"  >~}|yxwwurpm jihfdcb^]\ZYV&QNOOMKJIHDCBA?==;:8753110..,**)&%%$"  >~}|yxwwurpnmmjihfdcb^]\ZYVROMKJIGECBA?==;:8753110..,*&%%$"   >,}}|yxwvvsrrqnmmkiifdcb_]]ZXVRO"MKJIHDCBA?==;:8753110..,**)&%%$"   > }}|yxwwurqnmmkiifc^]]ZXVRO"MKJIHDCBA?==;:8753110..,**)&%%$#   > }zxxwurqnmmkiigccb^]]ZYXVRONLJIGFCBBA>=<;9664110..+**)&%"   > }}zxxwvrqnmmkiigccb_^]ZYXVRONLJIGFEBBA>=<;9664110..,**)&%"   > ~}}zxxwvsrrqnmmliigdcc`_]ZYXVRONLKIHEDBB@>=<;96641/.-+*)'%"   >  ~}}zxxwvsrnmmljigdcc`^][YXVRONLKIHEDBB@>=<;9764211..-+*)(% #!  >  ~}}zxxwvsrnmmljigdcc`^][YXVRONLKIHEDBB@>=<;9764211..-+*)(% #!  >  ~}}zxxwvsrnmmljigdcc`^][YXVRONLKIHEDBB@>=<;9764211..-+*)(% #!  > ~}}zxxwvsrrqnmmljigdcc`^][YXVRONLKIHFDBB@>=<;9664211..-+*)(% "!  >,}}zxxwvtrrqnmmljigecca]]\YXVRO(NLKIIGDBB@>=<;9664211/.-+*)(&%%#!  >}}zxxwvsrnm jigecda]]\YXVSO(NLKIIGDBB@>=<;9765211/.-+*)(&%%$!  > }}zxxwvsrnm jhhecc`^]\YXVTPOONLKIIGDBBA>==;9865211/.-++*)&#"  > }}zxxwvsrnmmljihecc`^]\YXVTPOMKJJFDCCA?>=;:8642/.+)&#"  >$ }}|yxwwtssrn mkjhedd`^^\ZYWTPOMLKIGECCB?>=<;8653220/.+)&%"  >~}{yxwwtssronnmkjhedda^^\ZYWTPOMLJIGEC ?>=<;8753221/.+)'&&%"  > ~}|zxwwtsonnmjjhfddb_^\ZYWWXUPONLJIGEC ?>><;9763221/.+)'&&%"  > ~}|zxwwusonj ifddb_^\ZYW VQPPOMLJJHEC@>><;9763221//,++)'&&%"  >8~}|zxwwuspnnmkjifddb_^]ZYW VQPPOMLJJHEC @>=<;9763221/.+)'&&%#   >8~}|zxwwusonnmkjifddb_^]ZYW VQPPOMLJJHEC @>=<;9763221/.+)'&&%#   >8~}|zxwwusonnmkjifddb_^]ZYW VQPPOMLJJHEC ?>=<;9763221/.+)'&&%#   >  ~}{yxwwus.onnmkjieddb_^^[ZXWWVQPPOMLJJHFDCC@>=<;9764221/.+)'&&%#!  > ~}{yxwwvs:onnmkjjgddb_^^[ZXWWVRPPOMLJJHFDCC@>=<;9763221//,++)'&&%#!  >  ~}{yxwwvspnljjgddc_^^[ZXWWVRP"MLJJHFDCCA>><;9763221//,++*'&&%#!  >  }|zxxwvsponnkjjgddc_^^[ZXWWVRPNLJJHFDCCA>><;9875221//,++*(&$"  >  ~}zyxwvsponnkjjgddc`^^[YXWWVRPOMJJHFDCC@>>=;:875221//,++*(&$"  > ~}zyxwwtssqonnkjjgd `^^[YXWWVRPNMKJIFECC@>>=;:875221//-++*(&$!  > ~}zyxwwtssqonnkjjgd `^^[YXWWVSPNMKJIFECC@>>=;:87520/-++*(&$"  > ~}zyxwwtssqonnkjjgd `^^[YXWWVSPNMKJIFECC@>>=;:87520/-++*(&$"  > ~}zyxwwtssqonnkjjgd `^^[YXWWVSPNMKJIFDCC@>>=;:87520/-++*(&$"  >~}zyxwwtssqonnkjjgd `^^[YXWWVSPNMKJIFDCC@>>=;:87520/-++*(&$"  >~}zyxwwtssqonnkjjgd `^^[YXWWVTPNMKJHFDCC@?>=;:87520/-++*(&$"  > ~}zyxwvtsn mjjhedc`^^[YXWUPNMKJIGDCCA@>=;;86520.-++*('&&#"  > ~}zyxwvtsn mjjhfdda^^\YXWUPNLKJJGDCCA@>=;;86520.-++*)'&&#"  > }}{yxwvtsnmjjhda^^[ZYWUPOLJ#GDCCA?>=;;8653220.-++*)'&&$"  > }}|yxxwtsn mjjhddc`^^[ZYWUPNJ#GEDBA?>><:8763220/.++*)'&&%"  >(~}|yxxwussronnmjjhddca_^[ZYWUP'NKJJGECCB?>><;9863220//,+*)'&&$!  >(~}|yxxwussronnmjjhedda^^\ZYWUPNKJJGECCB?>><;87620//,++)'&&$"   >(~}|yxxwussronnmjjieddb^^\ZYWUP'NKJJGECCB?>><;8763221//,++)'&&%"   >2~}|yxxwussronnmjjieddb^^\ZYWUP'NKJJGECCB?>><;8763221//,++)'&&%#   > 2~}{yxxwussronnmjjieddb^^\ZYWUP'NKJJGECCB?>><;8763220//,++)'&&%#   >L~}{yxxwussronnmjjieddb^^\ZYWVQP'NKJJGECCB?>><;8763220//,++)'&&%#   >9}|yxxwuspnkjifddb^[YWVQP*NKJJGEDCB?>><;9863221//,++)(&&$#   > 9~}|yxxwuspnkjifddb^ZXWWVQP*NKJJGFDCB?>><;:753221//,++)(&&%#   > -~}|yxxwuspnkjifddb^ZXWRPNKJJGFDCB?>><;9763221//,++)'&#   > ~}|yxvspn kjifddc_^^ZZXWRPPONLJJGFDCC@>>=<9774221//-++)'&#  >}{zxxwwtsspn kjifdca_^^ZZXWRPPONMKJHFDCCA>>=<9774221//-++)'&#   > ~|{xxwwtsspn kjigedc_^^[ZXWQPOMKJHFDCCA>>=<97742/-++)(&#  >  ~|{xxwwtsspn ljigedc`^^[ZXWRPOMKJHFECCA>>=<:7732/-++)(&$! >  ~}}{xxwwtsspn ljigedc`^^[ZXWSPOMKJHFECCA>>=<:7732/-++)(&$! > ~}}{xvspn kjigedc`^^[ZXWSPOMKJHFECCA>>=<:7732/-++)(&$! >~}}{xvspn kjigedc`^^[ZXWSPOMKJHFECCA>>=<97732/-++)(&$! >~}}{xvspn kjigedc`^^[ZXWSPOMKJHFECC@>>=<97732/-++)(&$! > ~}|zxxwwtssqn ljigddc`_^[ZYWTPOMKJIFECCA>>=<:76520/-++))&$! > ~}|zxxwwtssqn ljjgddca_^[ZYWTPOMJJIFECBA>>=<:7653220/.++))&#! > ~}}{xxwwsqonnljjgd`^^[ZYWTPNLKJIFECCB?>>=;76520/.++*)&#!  > ~|{xxwwsqonnljjgeed`^^\ZXWTPNLLJIFECCB?>>=;77320/.++*)&#!  >}|{yxwwsronnmjjhfdd`^^\ZYWTP!OLLJJGECCB?>>=;8743221/.++*)'&&$"  >  }|{yxwwtssronnmjjhfdda^^\ZYWWXTP OMLJJGFDCB>=;8653221//,+*)'&&%"  >  }|{yxwwussronnmjjhfdda^^\ZYW UQPPOMLJJGFDCB>=;8653221//,+*)'&&%"  > !}|{yxwwussronnmjjhfdda^^\ZYW UQPPOMLJJGECCB>=;8653221//,+*)'&&%"  > !}|{yxwwussronnmjjhfdda^^\ZYW UQPPOMLJJGECCB>=;8653221//,+*)'&&%"  >  }|{yxwwussron kjhfdda^^\ZYW UQPPOMLJJGECCB>=;8653221//,+*)'&&%"  >  }|{zxwwussron kjhfddb_^]ZYW UQPPOMLJJGFDCB>=;9653221//,+*)'&&%"   >  }|{zxwwvssrpn kjhfddb_^]ZYW VQPPONKJJGFDCB@>>=<9653221//,+*)'& #  >  }}|yxwwvssrpn kjhfddb_^]ZYW.VQOPPMLKJGFDCB@>>=;9653221//,++)'&%$#   > ~}|yxwwvssrponnkjhfddc_^]ZYW%VQOPPLLKJGFDCC@>>=<9753221//,++*'&&%"   >  5~}|zxwwussrponnkjigedc_^]ZYXWWVQOPPMLKJGEDCC@>>=<97542 0/,++*(&&%"   > ~}|zxwwus)ponnkjigedc_^]ZZXWWVROPPNLKJGFECC@>>=<97642 0/,++*(&&%$   > ~}|zxwwvsqonnljigedc`^]ZZXWSPNLKJHFECC@>>=<:7642 0/,++*(&&%#  > .~}|zxwwvsqonnljifddc`^]ZZXWSPNLKJHFECC@>>=<:7642 0/,++*(&&%#  >.~}|zxwwvsqonnljifddc`^^[ZXWSP NLKJHFECC@>>=<:754221//,++*(&&%#  >.~}|zxwwvssrponnljifddc`^^[ZXWSP NLKJHFECC@>>=<9754221//,++*(&&%#  >.~}|zxwsponnljifddca^][ZXWSP)NLKJIGECC@>>=;:754221//,++*(&&%#  > ~}|zxwtssqonnmjjgddca^]\YXWWVTPMLKJIGECB@>>=;:7542 0/-++*(&&%#!  >~}|zxwtssqonnmjjgddc`^^\ZXWWVSPLKJIGECB@>>=;:7542 0/-++*(&&%$"  >$}}{yxwwtssqoonljigddc`^^\ZXWWVSP OMJJIGECB@><:87420/-+)&$"  >0}}{yxwwtssqoonljigedc`^^\ZYWWVSPLJJIGECC@> <:8753220/-+)&$"  >,}}{yxwwtssqoonljjhddc`^^\ZYWTPOMKJIGECBA?>><:8653220/.+)&$"  >(}}{yxwwtssronnmjjhedda^^\ZYWTPOMKJJGECBA?>><:8653220/.+)&$"  >(}}{yxwwtssronnmjjhedda^^\ZYWUQPPOMKJJGECBA?>><:8653220/.+)& $"  >(}}{yxwwtssronnmjjhedda^^\ZYWUQPPOMKJJGECBA?>><:8653220/.+)& $"  >#}}{yxwvtssronnmjjhedda^^\ZYWUQPLJGECBA?>><:8653220..,+*)& $"  >#}}{yxwvussronnmjjhedda^^\ZYWUQPLJGECBA?>><;9653220..,+*)& $"  >#}}{yxwvussrpnnmjjheddb_]\ZYWURPLJHECBA?>><;9653220.-,++)& $"  > ~|{yxwwtsonnmjjhedda^^\ZYWUQPLJHECBA?>><;9653221.,+)'&&$#   > ~}{yxwwusonnmjjhedda^^\ZZWUQPPOMJHECCA?>><;9653221/.+)'&&%#   >~}|yxxwvsonnmjjiedda_^\ZZWUQPNKJJHFDCA?>>=<9762 1/.,++)'&&%"   >~}|yxxwusponmjjiedda_^\ZZWVQPNKJJHFECA@>>=<97621/.+)'&&%"   > ~}|yxxwusqn kjjfddb_^^[YWUQPNKJJIFDCA@>>=<9762 /-,++*'&&%"  > ~}|zxxwusqn kjjfddb_^^[YWVRPNKJJIFDCA@>>=<:7632 /-,++*(&&%#! > ~}{zxxwusqn kjjfddb_^^[YWVRPNKJJHFDCA@>>=<:763221/.+*(&&%#! > ~}{zxxwusqn kjjfddb_^^[YWVRPNKJJHGDCA@>>=<:763221/.+*(&&%#! > ~}{zxxwusqn kjjfddb_^^[YWVSPPONKJJHFDCA@>>=<:763221/.+*(&&%#  >  ~}{zxxwusqn ljifddc_^][YWVSOPONKJJIFDCA@>>=<:7632 /-,++*(&&%#! >~}|{ywwusqn ljifddc`^^[ZWVSOPPNKJJHFECA@>>=<:7642 /-,++*(&&%$! >~|{ywwvsqn ljigddc`^^[ZW TPPONLJJIFDCB@>;97642/-++*(&$!  >~|{xxwvsqonnkjjfddb`_^[ZWVSPPONMJJIFDCBA><:7742/-++*(&$!  > +~}|{xxwwtssqonnlkjgedb`^^[ZWVSPPONMKJIFDCBA?>>=:7742/-++*(&$!  >  +~}|{xxwwtssqonnmkjgedc`^^\ZWSPOMKJIFDCBA?>>=:77420/-++*(&$!  >  ~}|{yxwvsqonnmjjhddc`^^]ZXWTPOMKJIFECBA?>><;87520/-++*(& $"  >~}|{yxwvsqonnmjjhddc`^^]ZXW TQPPOMKJIFECBA>=:76520/-++*(&$"  >~}|{xxwvsqonnmjjhddc`^^]ZXW TQPONMKJIFECBA>=:76520/-++*(&$!  >~}|{xxwvsqonnmjjhddc`^^]ZXW TQPONMKJIFECBA>=:76520/-++*(&$!  >~}|{xxwvsqonnmjjhddc`^^]ZXW UQPONMKJJGECBA>=;76520/,++*(&$! > #~}|{yxwvtssqonnmjjhedda^^]ZYW,UQPONMKJJGFCBA?>><;8663221.-,+*)'&&%" >#~}|{yxwwussrpnnmjjiedda^^]ZXWURPPONKJJHECCA?>><;8663221//+ )'&&$"  >#~}|{yxwwussrponmjjieddb^^]ZXWURPPOMKJJHECCB?>>=<8663221//+ )'&&$"  >(~}|yxwwussrponmjjheddb^^\[XWUP NLLJJHECCB?> ;8653221//+ *'&&$"  >(~}|yxwwussroonnjjhfddb^^\[XWUP NLLJJGFCCB?>;87521//+ *'&&%"  > ~}{zxwwusponnkjifdda_^][YWVQPPOMLJJHFECB@> <8753221/.+ *(&&%#  >  ~}{zxwwusponnkjifdda_^][YWVRPPOMLJJHEDCB@> <9653221/.+ *(&&%#  >  ~}{zxwwusponnkjifdda_^][YWVRPPOMLJJHECCB@> <9653221/.+ *'&&%#  >  ~}{zxwwusponnkjifdda_^][YWVRPPOMLJJHECCB@> <9653221/.+ *'&&%#  >  ~}{zxwwusponmjjifdda_^]ZYWVRPPOMLJJHECCB@> <9653221/.+ *'&&%#  >0}}{zxwwuspnnmj fddb`^]ZZXW SPPONMJJHFECB@> <8754221/.+*'&#   >0}}{zxwwusqnnmkjifddb`^][ZXW SPPONMJJHFECB@><8754221/.,++*'&$  ><~}|zxxwvsqonnljifddc_^][ZXWSPNLKJHFDCCA>>=<:6542 0/-++*(&&%$  > <~}}zxxwvspn ljjgddc_^][ZXWSPNLLJHFDCCA>><<:65420/,+)&&%#! > ~}}zxxwvspn ljjgddc_^][ZXWSPMLKJHFDCC@>>=<:76420/,+(&&%$! >  ~}|{yxwvspn ljjgddc`^^[ZXWSQPPNLJJIFDCC@>>=;:86520/,+(&&%$! >~}|zyxwvsqn ljjhddca^^\ZXWSPMLKJHFDCCA>>=;:86520/-+(&&%$! >~}|zyxwvsqn ljjhddc`^]\ZXWTQPPMLKJHFDCBA>>=;:86520/-+(&&%$! >~}|zyxwvspn ljjhddc`^][ZXWTQPPMLKJHFDCBA>>=;:86520/-+(&&%$! > ~}|zyxwvspn ljjhddc`^][ZXWTPMLKJHFECBA>>=;:86520/-+)&&%$! > ~}|zyxwvsqonnljjhddca^][ZXWTPMLKJIFECBA?>=;:86520/-+)&&%$! > 2~}|zyxwwtssronnmkjhdcba^]\ZXWTPMLLJJGFDAA?>=;:8652 1/.++*)'&%$" >B~}}zxxwvtssronnljihedda^^\ZXWTPNLLJJGFDBA?>=;:86521/.+)'&&$" > $}}|yxwvtssronnljigedda^^\ZYWTPOMKJJGFDBA?>><98763221/.+*'&&%"  >$}}|zwwvtssronnljihedda^^\ZYWTPONKJJGFDBA?>>=98764221/.+*'&&%"  > U~}|yxwvtssronnmkjiedda_^\ZYWWXUQPPONKJJHECCA?>>=:9764221/-,++*'&&%"  >X~}|ywwvussronnmkjifddb_^]ZYWWXUQPPOMLKJHECCA@>>=;9764221/.,++*(&&%#  >]~}|ywwvussronnmjjifddb_^\ZYWWXUQPPONKJJHECCA@>>=;9764221/.,++*(&&%"  > ]~}|ywwvussronnmjjifdda_^\ZYWWXUQPPONKJJHECCA@>>=;9764221/.,++)(&&%"  > ]~}|ywwvussronnmjjifdda_^\ZYWWXUQPPONKJJHECCA@>>=;9764221/-,++)(&&%"  > 2}}|ywwvtssronnmjjiedda_^\ZYW&ROPONKJJHECCA@>>=;9763221/-,++)(&&$"!  > )}}|ywwvtspn kjifddb_^]ZYW&ROPONKJJIECCA?>>=:8763221/-,++)(&&$"!  >1}}|zxwvtspn kjifddb_^^[ZWVRPPONKJJIECCA?>>=:97542 0-,++*(&&%$  >~}}zxwwuspn kjifddb_^^[ZWQOPPNLJJIFDCA?>>=:976420.,++*(&#   >~}{zxwwuspn kjjfddb_^^[ZW ROPOOMJJHGECB@>;8774221/.,++*(&#   >,~{zxwwutsspn ljjfddb_^^[ZW*ROPONLJJIGDCBA?>>;8774221/.-++*(&&%#! > !}|zxwwutsspn ljjfddb_^^[ZWROPONLJJIGDCBA?>>;877420.-++*(&&%#! > !}|{xwwutssqn ljjgddc`^^ZZXWROPONLJJIFDCBA?>>;977420.-++*(&$! > }|{xwwutssqn ljjgddc`^^ZZXWROPONLJJIFDCBA?>>;977420.-++*(&$! >}|{xwwutssqn ljjgddc`^^ZZXWROPONLJJIFDCBA?>>;977420.-++*(&$! > }|{xwwutssqn ljjgddc`^^ZZXWROPONLJJIFDCBA?>>;977420.-++*(&$! >  }}{xwwutssrn mjjhedd`^^[ZXWSPOLJJIFDCBA?>>;977420.-++*(&$! >  }}{ywwutssrn mjjhedca_^[ZYWTPOLJJIFECBA?>>;:8653220.-++*(&%! >  ~}}{ywwvsqn ljjgddca_^[ZYWTPNMKJIFECCA?>><:8653220/-++*(&%! > }{yxwwtssqonnljjgddca_^[ZYWTPNLJIFECCB?>>=:77520/.++*(&%"  > 2~~}{yxwvtssqonnlkjgddca_^[ZYWTPNKJIFECCB@>>=:77520/-+(&%! > #~}}{yxwvtssronkjgda_^[ZYWTPNKJJGECCB?>>=:86521/-+)'&&%!  > #~}}{yxwvtssronj iedda_^\ZYWTPONKJJHECCB?>>=:7663221/-+ )(&&%"  >#~}}{yxwwtssronj iedda_^\ZYWUPONKJJHECCB?>>=:7663221/.+ )(&&%"  > #~}}{yxwwtssronj iedda_^[ZYWUPNKJJHECCB?>>=:76521/.+ )(&&%"  > #~}}{yxwwtssronj iedda_^[ZYWUPNKJJHECCB?>>=:76521/.+ )(&&%"  > ~}}{ywtspnj iedda_^[ZYWVQPPNMLJJHECCB?>>=:76621/.+ )(&&%"  > }}{zwtspnj ifddb^^\ZYWVQPPNMLJJHECCB@>>=;8663221/.+ )(&&%#  >  }}{zwvspnj7ieddb^^\ZYWWXVQPPOMLKJHFDCBA>>=;9553221//,++*'&&%"   >  }}|zwvspnj7ieddb^^\ZYWWXVQPPOMLKJHFDCB@>>=;9553220//,++*'&&%"   > ~}}zxwwvspnj heddb_^][YWVQPMLLJHEC@>;87632/,++*'&#   > ~}|{xwwvspn kjheddb`^][YWVQOPPMLLJHEDCC@>;8764221//,++*(&#   >~}|{xwwvspn kjifddb`^][ZWRPPONMKJIFDCCA>>=<9764221//,++*(&$!  >  ~}|{xwwvsqonnljifddb`^][ZWSPPONMKJIFDCCA>>=<9764221//,++*(&$! >  ~}|{xwwvsqonnljifddb`^][ZWSPPONMKJHFDCCA>>=<9764221//,++*(&$! >  ~}|{xwwvsqonnljifddb`^][ZW)SPPONMKJHFDCCA>>=<9764221//,++*(&&%$! > ~}|{xwwvsqonnljifddb`^][ZW)SPPONMKJHFDCCA>>=<9764221//,++*(&&%$! >(}}|{ywwvsronnljjgdcb`^^[ZXW)SPPONMKJIFDCCA>>=<:754221//,++*(&&%$!  > (}}|{ywsronnljjgdcba^^[ZXWSP"MLLJIFDCCA>>=<;754221//-++*(&&%$! > (}}|{ywsqonnmjjgddca^^[ZXWTPMLLJIFDCCB>>=<;7642/-+(&$! > }}|ywsronnmjjgedda^^[ZXWSPOMKJIFDCCB>>=;:8752/-+)'&&%" > (}}|ywsronnmjjgedda^^[ZXWRPNLKJIFDCCA>>=<9875322//-+)'&&%"  >  }}|ywtssqonnmjjgedda_^[ZYWSPNLKJIGECCA?>=<:875322//-+)'&&%"  >  }}|ywtssronnmjjhedca_^\ZYWSPNLKJJGECCA?>=<:8753220/.+)'&&%#  >}}|ywtssronnmjjhedca_^\ZYWTPNLKJJGECCA?>=<:8753220/.+)'&&%"  >}}|ywtssronnmjjhedca_^\ZXWTPNLKJJGECCA?>=<:8753220/.+)'&&%"  > }}|ywtssronnmjjgedca_^\ZXWTPNLKJJGECCA?>=<:8753220/.+)'&&%"  > }}|ywtssronnmjjhedca_^\ZXWWXUPNLKJJGEDBA@>=<;8763220/.+)'&&%#  >+ }}|yw2ussronnmjjhedca_^\ZXWWXUQPPNLLJJHDCBA@>=<;8763221//+)'&&%#   > M}}{xxwwussronnmjjhfddc_]][YWWXUQPPOLLJJHDCCB@>><;8763221//++**(&&$#   >-~}|zxwwussroonmjjhfddc_^]ZYWWXUQPNLJJHECCB@>><;9874221/.+ *'&&%#   >-~}}zxwwussroonmjjhfddc_^\ZZXWXUQPNLJJHEDCB@>>=;9874221/.++*)(&#   >-~}}zxwwutsroonmkjifddb`^\ZZXWWVQPNLJJHFDCB@>>=;9874221/.,+*)(&#  >~}|zxwwutssponnkjifddc`^]ZYXW(ROPPNLJJIFDCB@>>=;9874221/.,++)(&&%#  >~}|zxwwutssponnkjifddc`^]ZYXW(ROPPNLJJIFDCB@>>=;:874221/.,++)(&&%#  > '}|zxwwutssqn kjifddc_^]ZYXW(ROPPNLJJIFDCB@>>=;:874221/.,++)(&&%#  > '}|zxwwutssqn kjifddc_^]ZYW(ROPPNLJJIFDCB@>>=;9774221/.,++)(&&%#  > '}|zxwwutssqn kjifddc_^]ZYW(ROPPNLJJIFDCB@>>=;9774221/.,++)(&&%#   > }}zxwwutssqn kjjgddb_^]ZYW(SOPPNLJJHFDCB@>>=;97742210.,++)(&&%"! >  ~}|zywwutssqn ljjgddc`^^[ZXWSPNLJJIFDCB@>>=;:86420.,++*)'&&#! >~|{ywwvsqn ljjgddc`^^[ZXWSP$OLJJIFDCC@>>=;:775221//,++*)'&&#!  >~|{xxwvtssqnljjhd`^^[ZXWSP$ONKJIGDCCA?>><:775221//-++*)'&&#!  >1|{xxwvussronnljjhd`^^[ZXWSPN"KJHGDCCB?>><:775221//.++*)'&&#!  >1~|zxxwvussronnljjhd`^^[ZXWSPONKJIFDCCB?>><:77520/.+)'&&$!  >:~|{yxwvussronnljjhedda^^\ZYWTP$ONKJIFECCB?>><;8763220/.++*)'&&%"  >:~|{yxwvussronnljjgedda^^\ZYWTP$ONKJIFECCB?>><;8763220/.++*)'&&%"  > 1~|{yxwvussronnljjgda^^\ZYWTP$ONKJIFECCB?>><;8763220/.++*)'&&%"  >1~|{yxwvtssronnljjgda^^\ZYWTP$ONKJJGECCB?>><;8763220/.++*)'&&$"  >#~|{yxwvtssronnljjgedda^^\ZYWUPONKJJHECCA?>><;88620/-++*)'&&$"  >#}}|zxwwussronnmjihedca^^]ZZW(UQPPOMKJIGFDCB?>><:9753220/.++*)'&&%"  >}}|zxwwtssronnljjiedca^^]ZZWUQPPONKJIGFDCB?>>;:9663220/.+ )'&&%"  >.~}{yxxwtssroonljjiedca^^]ZZW UQPPONMJIGFC @>>=;8663220/.+ )'&&$" >~|{xwtssroonmkjiedca^^]ZZW UQPPONMJIGFC @>>=;7663220/.+ )'&&$" > ~}{yxxwtson kjhedca^^]ZZW UQPPOMLKIGEC@>>=;876433100-,,+*(('%"!!   >!}}|zxwwusonj5ifdcb__][ZXWXUQPQQNLLKIHFEEC@A?>=:88665430/0/-++*(&$$# $#!   >  }}|zxxwvstsqpoolljgffeaa_]][Z[YUT#RPONLLKHHGDDCB@>=:9:9775442200/-+))')(&%"    >  |{zzyvsronnkiihdcb`_]]^]XWUTRROOMLLJHHGFDAA>=;:86431/--,*)& $"     > ~||{zyuttsp mkkifedbb`_`_ZYWVUTRQPONMJJIHFDCB@ ><:;;987763100.-,)'$#!   > ~~}z)wvwvssrpnmlihgdecbdb]\\[ZYWWUTRQQOMMLKIGFDCA@>=;::9853321/-,+*&$#"!    >(~}zyxxvusqppokjjggea_]\ZZXWUTTRPQOOMKIGEBA?=><;97764310 /.+*+**)(&%$#$#$#"  > |z{{xwwtrrqnmmkjhcbba`_^][ZYWVVTSRQPNMLJJIHGFDB@A@>=:;::9874321/.,+*)(''(('('('(('&#  > ~}~|zzxuutqppmmkfdeddb``^]\ZZYWVVUSPPOMKJIGECDDB@=>==<<:875210.--,,+*+*)&#! >~}|ywxwtsroonmifggfecba_^]]\ZYYXWUSRPONLKKJIGFDABAA@@><;85454211010././.-*&# >|zzyvuusrqp lkjjiggfecc`]\[[YVVTSRPNLJJIGEEDDCB@@>??>;::9987654343430.*&" >#}||zwwuttrrsomkjihgeec`_][YYWU SRRQPONNLKIIHHGFCBBC@A>=<==<;:9978767767667640.)%  > |zzywvusp#onlkkhhgfecbcaa_][ZZ[YXXUVUTSRRPOMMLLKJHFGGEDBA@>==<<;;<<;<;851,(# >$~}|{zyxvs rpoonlkjjifedb`_^]\ZYXVTSQQPPONLK JIGFFEEDDBA@?@?<94/*%  >  ~|{zxutsrrpnmligedba`_]\[YWVTSRQONMLJIIH GFEEDDCCDDCDC?<72,)# >  ~}{xwvutsqpo mlkkjhgdcddbb`_^\ZYWVUTRQPMLMLLKKIHGHGHGC?:50+% > ~zz{zxwusrrqoonnljjgfggeedbbaa_]\ZY/XWVVTTSSQPQOONNLMMKKJJKLKJJKJKKLMLFB>82,&  >  ~}~|{zzywvuvurqnlkj higgeecdcb`_^^]^][YXWVTSRQQPPOPOPOJGA;5/(" >  }}||zzx vuttsqonmmnmkkihfcbaa`a`^]\ZYWWXWWVVTRSTSSTTSSTSSTRNJC>80*# >"~}|{zzxvtsrrqrpoollmmjijjgefedc`a`][\Z[XXYYXXWWXWWXXWYWSNGB:4-% > ~}{|{zxwvutrqonljjihgedba`_]\]\[\\[[\\[\\][VQKE=6.'  >   ~}|zyxwvutsrqqommlkjihgfdcbaa`_`_`_YTNHA81)" > }|{z{zyyxwwvvuttrqppponmlkigfedcdcdc]XQJB;3+" >  ~}~}||zyxwutsssrqpoponnlklkihihgghghgfhg a[VME=4,$ > ~}|{yxwwwvtsrqonmlklklje_XQH@7/% > ~|{zzzywwvursrqppopopoib[SKB90' > }~}~}{zyyzyxwvwvutststsrmf^VLC;1(  >~~~|{"zyzyxwwxwxxwxxwxxwwxwqjaYOF=2) >¿ ~~}|}|{|{|~{ume\RI?4+!> ~xqh`UKA6-# > |ulcXMC8.$ > xof[PE:/$ > |qi\RF;0% > ul_TH=2& > xnbVJ>2' >  {qdYM@3( >  th\OB6+  > xj^QD7+ >  {maSE8+  > ~ocUG9-! >reVH:/! > tgXJ=/" >wiZM>0# >(zk\N?1$ >}o`PA2% >~o`PA1# < n]K8'"ȿyncWKE?=>>>=2k B>Ue"|P 0A 2 =|>WJ"Dw))n 6\ BMR]QRb9 t2)' 3X AHQ: b/{t%)k 3X AHQ9 c/{t%)k 4X BHS9 c.ۿ{t%ͼ)k 4X BHS9 c.ۮ{t%ͫ)k 4X BHS9 c.۝{t%͚)k 4XBHS9 e.ی{v%͉)k 4XBHS9 e.zv$)j 4XBHS9 e.zw$)j 4XDHT9 f-yw$)h 4XDHT9 f-yw$)h 4XDHT9 f-yw$)h 4XDHT9 f-ؾ;y"$ͻh^ oX|H$ֲ80M=,BQqXC0_! ow^I|6$$ ~<eN:BQ, i~ aF,!#&(*+.0011233445666656677777777777777753201.,*'&# " ! " ! "  " ! "  " ! " ! "  " !  " ! " ! "  " !  " ! " ! "  " ! ! ! " ! " !    " ! "  " !! !! !  " ! !! " ! " ! "  " !  " ! " ! "  " !  " ! " ! "  " !  " ! " ! "  " ! $&$$$$+-'(#('&$#%"!#$#!###" !  !"!#"Ł$%&&''())տ*ȶt*Ͽ~Ŵ,˻ݟ,±ݵ-¸Ϭ+ҨƼp+hzd+ƶdĺsB¼+ӟ~Ⱦ{k[Jr,+Ɵuz٥·raTKC;6G+нxhXNF>81?)Ęst}~n^RIA:4/+$9 +弔˱ؑ\mtdULD=61-)&$x+xjǸȥV0WPG?93.*'%$+Ɵχfˬp^0-B;50,(&$+ͽDZpºrFqr8/2-*'%=+ó|ۥɫh}K]D?aC#*(%$+ʹŲ|]dDLA+ ᯬ Ȯ 𯬪 ޯ Į  ٯ 믬 կ 诬 Ѯ 寬 ͮ 󯬪 ᯬ Ȯ 𯬪 ޯ Į  ٯ 믬 կ 诬 Ѯ 寬 ͮ 󯬪 ᯬ Ȯ 𯬪 ޯ Į  ٯ 믬 կ 诬 ¾ Ѯ 寬 ǻ ͮ 󯬪 ᯬ ͸ Ȯ 𯬪ݷ ޯ е Į  ٯ ٲ 믬 կ ׯ  诬 ­ Ю   寬 Ǫ ͮ 󯬪 ᯬ ͧ Ȯ 𯬪ݦ ޯ Ф Į   ٯ ١ 믬  կ מ 诬 œ Ю 寬 Ǚ ͮ 󯬪 ᯬ ͖ Ȯ 𯬪ݕ ޯ Г Į  ٯ ِ 믬 կ ׍   ‹Ю 寬Ljͮ󯬪ᯬͅȮ 𯬪݄  ޯ Ђ Į  ٯ 믬 կ 诬 Ю 寬 ͮ 󯬪  Ȯ  ޯĮٯ믬  կ  诬 Ю 寬 ͮ 󯬪 ᯬ Ȯ 𯬪 ޯ Į  ٯ 믬 կ 诬 Ю 䯬 ̮ 󯬪 ௬ Ȯ 𯬪 ޯ Į  ٯ 믬 կ 诬 Ю 䯬 ̮ 󯬪 ௬ Ȯ 𯬪 ޯ Į  ٯ 믬 կ 诬 Ю 䯬 ̮ 󯬪 ௬ Ȯ 𯬪 ޯ Į  ٯ 믬 կ ۿ  诬 Ľ Ю   䯬 Ⱥ ̮ 󯬪 ௬ͷȮ𯬪۶ ̩ ޲Ĝ Ųдľ  ʲ ɰ ǯ Ȯ ƭ Ȭ ˫ è ç Ŧ ¥ Ĥ £ ȡ 濽ƽͼ׼ ~Kt⻹ LKKNڋKJ;WOK=87TڙK@77K WKKC77R ڥKKG76Y`KI86b ײKJ;6llK>7sǽKB9zsE=IFoN^!#&(*+.00112334456666566777777777777777777776520.+)()(((()(((()((((((()(((()(((()(((()(((()((((((()(((()(((()(((()(((()((((((()(((()(((()(((()(((()((((((()(('''&''&''('&'''''(''''''&'''(()(((()(((((&&''(&''''&&&%&&&'&&&&'%%&'(''''(&&''((()(((()((((('&&'(''''('''''''('''''&'(()(((()(((()((((((()(((()(((()(((()(((()((((((()(((()(((()(((()(((()((((((()(((()(((()(((()(((()((((((()(((()()'00-+*+*((''%$'&&%&%%$$###"! !   !"!#"Ł$%&&''&))³(ʷx*Áƶ,̼ޣ,ò߷-ùү+ժȾt+k}·f+ǷÿgƻtD¼+¤դ|m\Kr,+ǡwکĹscTKB:6F+ѾĪyiYNE=6/:)śuwśo_RH@82,(!3+澖Ͷڕ`nveVLC;4.*&#!w+|mɺʨY0XPG>71+'$! +ǤЌiͮva1-A:3-)%" +ξʶsļtKst9-/*&#!:+ŴܪˮkL^EA`B"'$" +˻ɶ}^eEL@+;K'!i+Ԭ~z`ZHE77-43%ǷӾuiHa5J)9 -# 1%ʾǺp9\UN@;2/(&!!5&ܖ|?'G.8#,$ %½źTuG%)0.%!o&ůwɿ{kT;aV*'%$8ϰøqaSI48N0! кűǼwgWMD<4!3<Fﶿ}n]QH?81,#0*ϲƔtdUKB:4.)%"wńzzjYOF=60+'#!&ϰC^SI@92-(%" и \6D<5/*&#!S m.21,'$! ϲ zywM(%" { ywusqW&!,ΰ zxvtromu8з ywurpnkif yxvsqnligebϲ zywurpmkhebѿ ywuspnkigdg zxvtromjgebж ~ywurpnkifc} yxvsqnligdaϱ zywurpmjhebо ywuspnkigcj zxvtromjgebе |ywurpnkhec ywvsqnligdaϱ zywtrpmjhebм ywuspnkifcn zxvtqoligebг {ywurpnkheb ywvsqnligdaϰ zxwtromjhebк ywurpnkifcq yxvtqoligebϲ zywurpnkheb ywusqnkigdcϰ zxwtromjgebи ywurpnkifcu yxvtqoligebϲ zywurpnkheb ywusqnkigdeΰ zxvtromjgebз ywurpnkifcy yxvsqnligebϲ zywurpmkhebѿ ywuspnkigdg zxvtromjgebж ~ywurpnkifc} yxvsqnligdaϱ zywurpmjhebо ywuspnkigcj zxvtromjgebе |ywurpnkhec ywvsqnligdaϱ zywtrpmjhebм ywuspnkifcn zxvtqoligebг {ywurpnkheb ywvsqnligdaϰ zxwtromjhebк ywurpnkifcq yxvtqoligebϲ zywurpnkheb ywusqnkigdcϰ zxwtromjgebи ywurpnkifcu yxvtqoligebϲ zywurpnkheb ywusqnkigdeΰ zxvtromjgebз ywurpnkifcy yxvsqnligebϲ zywurpmkhebѿ ywuspnkigdg zxvtromjgebж ~ywurpnkifc} yxvsqnligdaϱ zywurpmjhebо ywuspnkigck zxvtromjgebе |ywurpnkhec ywvsqnligdaϱ zywtrpmjhebм ywuspnkifcn zxvtqoligebг {ywurpnkheb ywvsqnligdaϰ zxwtromjhebк ywurpnkifcq yxvtqoligebϲ zywurpnkheb ywusqnkigdcϰ zxwtromjgebи ywurpnkifcu yxvtqoligebϲ zywurpnkheb ywusqnkigdeΰ zxvtromjgebз ywurpnkifcy yxvsqnligebϲ zywurpmkhebѿ ywuspnkigdg zxvtromjgebж ~ywurpnkifc} yxvsqnligdaϱ zywurpmjhebо ywuspnkigck zxvtromjgebе |ywurpnkhec ywvsqnligdaϱ zxwtrpmjhebм ywuspnkifcn zxvtqoligebг {ywurpnkheb ywvsqnligdaϰ zxwtromjhebк ywurpnkifcq yxvtqoligebϲ zywurpnkheb ywusqnkigdcϰ zxwtromjgebи ywurpnkifcu yxvtqoligebϲ zywurpnkheb ywusqnkigdeΰ zxvtromjgebз ywurpnkifcy yxvsqnligebϲ zywurpmkhebп ywuspnkigdg zxvtromjgebж ~ywurpnkifc} yxvsqnligdaϱ zywurpmjhebо ywuspnkigck zxvtromjgebе |ywurpnkhec ywvsqnligdaϱ zxwtrpmjheb ywuspnkifcn zxvtqoligebг {ywurpnkhebů ywvsqnligdaϰ zxwtromjheb к ywurpnkifcq yxvtqoligebϲ zywurpnkheb ywusqnkigdcϰ zxwtromjgebи ywurpnkifcu yxvtqoligebϲ zywurpnkheb ywusqnkigdeΰ zxvtromjgebз ywurpnkifcy yxvsqnligebϱ zywurpmkhebп ywuspnkigdg zxvtromjgebж ~ywurpnkifc} yxvsqnligdaϱ zywurpmjhebо ywuspnkigck zxvtromjgebе |ywurpnkhec ywvsqnligdaϱ zxwtrpmjhebл ywuspnkifcn zxvtqoligebг {ywurpnkheb ywvsqnligdaϰ zxwtromjhebк ywurpnkifcqy xvtqoligebϲzy wurpnkhebyw usqnkigdcϰzxwtromjgebиywurpnkifcuyxvtqoligebzywurpnkhebywusqnkigdeΰzxvtromjgebзywurpnkifcy ɯyxvsqnligeb ϱzywurpmkhebпywuspnkigdg zxvtromjgebж ~ywurpnkifc} yxvsqnligdaϱ zywurpmjhebо ywuspnkigck zxvtromjgebе |ywurpnkhec ywvsqnligdaϱzxwtrpmjhebл ywuspnkifcn zxvtqoligebг {ywurpnkheb ywvsqnligdaϰ zxwtromjhebк ywurpnkifcq yxvtqoligebϲ zywurpnkheb ywusqnkigdcϰ zxwtromjgebи ywurpnkifcu yxvtqoligebϲ zywurpnkheb ywusqnkigdeΰ zxvtromjgebз ywurpnkifcy yxvsqnligebϱz ywurpmkhebпy wuspnkigdgzx vtromjgebж~ywurpnkifc}yxvsqnligdaϱzywurpmjhebоywuspnkigckzxvtromjgebе|ywurpnkhecywvsqnligdaϱzxwtrpmjhebлywuspnkifcnzxvtqoligebг {ywurpnkheb ywvsqnligdaϰ zxwtromjhebк ywurpnkifcq yxvtqoligebϲ zywurpnkheb ywusqnkigdcϰ zxwtromjgebи ywurpnkifcuyxvtqoligebϲ zywurpnkheb ywusqnkigdeΰ zxvtromjgebз ywurpnkifcy yxvsqnligebϱ zywurpmkhebп ywuspnkigdg zxvtromjgebж ~ywurpnkifc} yxvsqnligdaϱ zywurpmjhebо ywuspnkigck zxvtromjgebе |ywurpnkhec ywvsqnligdaϱ zxwtrpmjhebл ywuspnkifco zxvtqoligebг {ywurpnkheb ywvsqnligdaϰ zxwtromjhebк ywurpnkifcq yxvtqoligebϲ zywurpnkheb ywusqnkigdcϰ zxwtromjgebи ywurpnkifcu yxvtqoligebϲ zywurpnkheb ywusqnkigdeΰ zxvtromjgebз ywurpnkifcy yxvsqnligdbϱ zywurpmkhebп ywuspnkigdg zxvtromjgebж ~ywurpnkifc} yxvsqnligdaϱ zywurpmjhebо ywuspnkigck zxvtromjgebе |ywurpnkhec ywvsqnligdaϱ zxwtrpmjhebл ywuspnkifco zxvtqoligebг {ywurpnkheb ywvsqnligdaϰ zxwtromjhebк ywurpnkifcq yxvtqoligebϲ zywurpnkheb ywusqnkigdcϰ zxwtromjgebи ywurpnkifcu yxvtqoligebϲ zywurpnkheb ywusqnkigdeΰ zxvtromjgebз ywurpnkifcy yxvsqnligdbϱ zywurpmkhebп ywuspnkigdg zxvtromjgebж ~ywurpnkifc} yxvsqnligdaϱ zywurpmjhebо ywuspnkigck zxvtromjgebе |ywurpnkhec ywvsqnligdaϰ zxwtrpmjhebл ywuspnkifco zxvtqoligebг {ywurpnkheb ywvsqnligdaϰ zxwtromjhebк ywurpnkifcq yxvtqoligebϲ zywurpnkheb ywusqnkigdcϰ zxwtromjgebи ywurpnkifcu yxvtqoligebϲ zywurpnkheb ywusqnkigdeΰ zxvtromjgebз ywurpnkifcy yxvsqnligdbϱ zywurpmkhebп ywuspnkigdg zxvtromjgebж ~ywurpnkifc} yxvsqnligdaϱ zywurpmjhebо ywuspnkigck zxvtromjgebе |ywurpnkhec ywvsqnligdaϰ zxwtrpmjhebл ywuspnkifco zxvtqoligebг {ywurpnkheb ywvsqnligdaϰ zxwtromjheb к ywurpnkifcq yxvtqoligeb ϲ zywurpnkheb ywusqnkigdc ϰ zxwtromjgeb и ywurpnkifcu  yxvtqoligebղ zywurpnkheb ܾı ywusqnkigde zxvtromjgeb ywurpnkifcy yxvsqnligdb zywurpmkheb ywuspnkigdg zxvtvomjgeb ~y~zifc} }nda b k æ    å ī   Ū ¤ ħ     å  áĤǢ ~KhŴ LKKMڋKJ;NOK=77KڙK@77Es WKKC76Jv ڥKKG76Pv`KI86Wv ֲKJ;6^lK>7cKB8isE;mIBpoIsUr}z!#&(*+.00112334456666566777777777777777777777520.,)((((&%%&'&%%&'%%%&&%%%&%%%'&%%&'&%%&'&%%&'%%%&&%%%&%%%'&%%&'&%%&'&%%&'%%%&&%%%&%%%'&%%&'&%%&'&%%&'%%%&&%%%%$##&%$$%&%$$%&%$$%&$$$%%#$$&%%%'&%%&'&%$$%%$$%&$$$%$$""$""#%%$$###%$$'%$#$%$$%&&%%%&%%%'&%%%%$$%$%%%%&'$#%%$#%$$###&&%%&'&%%&'&%%&'%%%&&%%%&%%%'&%%&'&%%&'&%%&'%%%&&%%%&%%%'&%%&'&%%&'&%%&'%%%&&%%%&%%%'&%%&'&%%&'&%%&'%%%&%%%%&%%%'%-,,)(('&(''%$'&&%&%%$$###"! !   !"!#"Ł$%&&''&))ƴ*˹~*²Ƅȸ,ξߦ,ĵ-ƻӳ+׭ɿv+qøg+ȹ‰jǽvH¼+Ħ֪~n^Mu-+ɣ{ۭźudVLC<9G+Ƭ{k[PG>70>+Ǟy{ǞqaTJB:3.)"5+θۙbpwgXNE=60+'$"x+繀qʼˬ\2ZRH@82-(%"!+ɧґnΰwf3.C;4/*&#!+˹xŽuMww;/1,'$";+ƶݯ̯lN`GDbF$(%#!+/̽˷_gGNA,=M*"i+ó֮|b\IG88/ 65+IɹylIc7L*;!.$"2+Ȼu;^VOB=40('""6+u|ޛ·ĂA(I/9$-%!+3vƻUyJ&*1/ '"p+zW)||mV>dX,(&%9 P~sNĹscUK5;Q2"! x8ǵȽyiYOF=5"5=!G }{k.o_SIA93-%2, ,u"XɕvfWMD<5/*&#x yRƆ}|l[QG?81,(%"' W}s3E`UKB:4.)&#! x3 ba7E=60+'$"T {h p042-(%#! 8u 6xQ)&#!| yMeX)"- ]|s: .w/ <{ {c  >u  xG  d|s  )v+  z_  Bt  x@  n|q  /v&  z[  I~s  x<  u|n  3u%  zW  P~s  x8  }{k  ,u"  yR  W}s  x3  {h  8u  yM  ]|s  .w/  {c  >u  xG  d|s  )v+  z^  Bt  x@  n|q  /v&  z[  I~s  x<  u|n  3u%  zW  N~s  x8  }{k  ,u"  yR  W}s  x3  {h  8u  yM  ]|s  .w/  {c  =u  xG  d|s  )v+  z^  At  x@  m|q  /v&  z[  H~s  x<  v|m  3u%  zW  N~s  x8  }{k  ,u"  yR  W}s  x3  {h  8u  yM  ]|s  .w/  {c  =u  xG  d|s  )v+  z^  At  x@  m|q  /v&  z[  H~s  x<  v|m  3u%  zW  N~s x8  }{k  ,u"  yR W }s  x3 {h 8u  yM ] |s .w/ {c =u xG d|s )v+ z^ At  x@  m|p  /v&  z[  H~s  x<  v|m  3u%  zW N~s x8  }{k  ,u"  yR  W}s  x3  {h  8u  yM  ]|s  .w/  {c =u  xG  d|s ' v+  z^ A t x@ m |p / v& zZ L~s x< v|m 3u$ zU R~s x8 ~{k  ,u"  yR  W}s  x3  {h  8u  yM  ]|s .w/ {c =u  xG  d|s  'v+   z^   At  x@  m|p  /v&  zZ  L~s  x<  v|m  3u$  zU  R~s  x8  ~{k  ,u"  yR  W}s  x3  {h  8u  yM  ]|s  .w/  {c  =u  xG  d|r  'v+  z^  At  x@  m|p  /v&  zZ  L~s  x<  v|m  3u$  zU  R~s   x8  ~{k  ,u"  yR  V}s  x3  {h  8u  yM  ]|s  .w/  {c  =u  xG  d|r  'v+  z^  Dt  x@  m|p  -v&  zZ  J~s  x<  u|m  1u$  zU  Q~s  x8  }{k  ,u"  yR  V}s  x3  {h  8u  yM  ]|s  .w/  {c  =u  xG  d|r  'v+  z^  Dt  x@  m|p  -v&  zZ  J~s  x<  u|m  1u$  zU  Q~s  x8  }{k  ,u"  yR  V}s  x3  {h  8u  yM  ]|s  .w/  {c  =u  xG  d|r  'v+  z^  Ds  x@  m|p  -v&  zZ  J~s  x<  u|m  1u$  zU  Q~s  x8  }{j  ,u"  yQ  V}s  x3  {h  8u  yL  ]|s  .w.  {c  =u  xG  d|r  'v*  z^  Ds  x@  m|p  -v&  zZ  J~s  x<  u|m  1u$  zU  Q~s  x6  Ƥ{j  zĔu"  ҎyQ  Ս}s  ԍx3  ӎh3^ys; Ӟ$XS x!  ѺB  ЧQ  R  ӫK  @  ԯ :  ?Y}x'  ֵtUz  S  غu ԥ, ڿf ب~]~z ڬ~~ ~~ݲ ~ ~}޸ ~} ~} ~|ܥ ~}| ~}|ߩ ~}| ~}|| ~}|| ~}|} ~|{~ ~}|{~ ~}|zv~}{z~}|{z~}|{z~}|zz~|{zz~}|{zz}|zy{}{zy{~|{zx|pʼn|{yx~ ~K`|zyw} LKKLr|zyw~ڋKJ;HswOK=87FڙK@77Bf WKKC76Fg ڥKKG76Jg`KI86Pg ղKJ;6UwlK>7XKB7]sE9`I?doEePdrk .9AHGLOUVY[Z]]baaaaccbbddfffdddbddbdb``^^^^^^^^ZZYXWVTTRQPNMKIHFFFFJGGHHIJJKLMNNNOPSRRSTUUUVVVWWXXXXZZZZZ[[[\\\]]^^^^^^^^^^^^________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________aacegqu|~35><A@4903:%x            !!!!    !"#$%%&&'(((('&%$#"!    !#%'()*+,-../011110/.-,+*)('%#!   #%(*,.0124567789::::987654210.,*(%#    #&),/1468:;=>?@AABCDDDDCBA@?>=;:8641/,)&#  " !%(,0369BEHKMOQSTUVWXYZZZZYXWVUTSQOMKHEB>;73.*&"  # "&+059>CGKNRUWZ\^_abccddeffffedcba_^\ZWURNKGC>950+&"  % !&+06;AFKOTX\_begikmnooppqrrrrqponmkigeb_\XTOKFA;60+&! % %*06FMU]dkrx~~xrkd]UMF>70 )# ) %,3;CKS[ckszzskc[SKC;3 ,% , !(/6>GOXajrzzrjaXOG>6 /(! , #*19BKT]fpx®ÁÑÞxpf]TKB9 1*# ) %,4HS^iuƾui^SH >5,$( %-6?IT_kwwk_TI ?6-$ & &.7@JUamxúxmaUJ @7.%& &.7AKVbnzżznbVK A7.% ' '/8ALWco{ƽ{ocWL A8.& &  '/8BLXco{ȿ{ocXL B8.& &  '/8BMXdp||pdXM B8.% '  '/9BMXdp||pdXM B9.% (  '09CMYeq}}qeYM C9/% '  (09CNYeq}}qeYN C9/% &  (09CNYeq}¸}qeYN C9/% ( !(09CNYeq~¸~qeYN C9/% % !(09CNYeq~ù~qeYN C9/% ' !(09CNYer~ù~reYN C9/& ' !(09CNZfr~ùrfZOD:1(! ' !(09CNZfr~ùrfZOD:1(! ' !(09CNZfr~ùrfZOD:1(! ' !(09DNZfr~ùrfZOD:1(! ' !(0:DOZfr~ùrfZOD:1(! ' !(1:DOZfr~ùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrźrfZOD:1(! ' !(1:DOZfrɽrfZOD:1(! ' !(1:DOZfrøsgZOD:1(! ' !(1:DOZfrvh[OD:1(! ' !(1:DOZfr ܿ|m]PE:1)! ' !(1:DOZfr îueUH;2)! ' !(1:DOZfr 䮛r_M?3)! ' !(1:DOZfrĜoZF6," ' !(1:DOZfrnT@0$ ' !(1:DOZfrlP:( ' !(1:DOZfrhH0 ' !(1:DOZfrZ:# ' !(1:DOZfrqE( ' !(1:DOZfrR- ' !(1:DOZfrW2 ' !(1:DOZfrY2 ' !(1:DOZfrV0 ' !(1:DOZfrL* ' !(1:DOZfrlA$ ' !(1:DOZfrZ6! ' !(1:DOZfrqI, ' !(1:DOZfr\;& ' !(1:DOZfrrM2# ' !(1:DOZfr^@-! ' !(1:DOZfrqP8+! ' !(1:DOZfr_D4(! ' !(1:DOZfrqS=2)! ' !(1:DOZfrɂbI<1)! ' !(1:DOZfrqWG;1(! ' !(1:DOZfreRD:1)! ' !(1:DOZfrv^PD:1(! ' !(1:DOZfrm\OD:1(! ' !(1:DOZfr͕}hZOD:1(! ' !(1:DOZfr vfZOD:1(! ' !(1:DOZfr sfZOD:1(! ' !(1:DOZfr ﭔrfZOD:1(! ' !(1:DOZfr 񼡎rfZOD:1(! ' !(1:DOZfr װrfZOD:1(! ' !(1:DOZfr rfZOD:1(! ' !(1:DOZfr ϵrfZOD:1(! ' !(1:DOZfr ırfZOD:1(! ' !(1:DOZfrнrfZOD:1(! ' !(1:DOZfrʺrfZOD:1(! ' !(1:DOZfrźrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfrùrfZOD:1(! ' !(1:DOZfr~ù~rfZOD:1(! ' !(0:DOZfr~ù~rfZOD:0(! ' !(09DNZfr~ù~rfZND90(! ' !(09CNZfr~ù~rfZNC90(! ' !(09CNZfr~ù~rfZNC90(! ' !(09CNZfr~ù~rfZNC90(! ' !(09CNYer~ù~reYNC90(! % !(09CNYeq~ù~qeYNC90(! & !(09CNYeq~¸~qeYNC90(! &  (09CNYeq}¸}qeYNC90(  '  (09CNYeq}}qeYNC90(  &  '09CMYeq}}qeYMC90'  '  '/9BMXdp||pdXMB9/'  &  '/8BMXdp||pdXMB8/'  &  '/8BLXco{ȿ{ocXLB8/'  ' '/8ALWco{ƽ{ocWLA8/' ' &.7AKVbnzżznbVKA7.& ' &.7@JUamxúxmaUJ@7.& ) %-6?IT_kw wk_TI?6-% ' %,5>HS^iu ƾui^SH>5,% ' $+4=GQ\gr~º~rg\QG=4+$ ' #*2;EOZep{ ž{peZOE;2*# ) ")1:CMWbmx  ſxmbWMC:1)" + !(08AKU_it~۳ܯ  ſ~ti_UKA80(! )  '.6?HR\fpzӮԯ ¾zpf\RH?6.'  ) %,4GOXajrz zrjaXOG>6/(! ' %,3;CKS[ckszzskc[SKC;3,% ' #)07>FMU]dkrx~~xrkd]UMF>70)# %  &,39AHOV]cjpuz~~zupjc]VOHA93,&  % #(.5;BHOU[afkptx{~ ~{xtpkfa[UOHB;5.(# & %*06CGKNRUWZ\^_abccdd^effglox$$ ~}|{zyxxywxwvutsrqponmlkjhgfedcba_^\ZWURNKGC>950+&"  ! "&*.37;>BEHKMOQSTUVWXRYZZ[\^dly;~}|{zzyxxwvutstsrsrqonmlmllkkjihihfgffdcbba`_^]\[ZYXWVUTSQOMKHEB>;73.*&"  % !%(,0369?@AAB;CDDCDCDCD HJVl}O|{zzxwvttsrqqonllkiiggffeedcaba``^\[ZYXVUSRQPONMLKLKJIHGFEDCBA@?>=;:8641/,)&#    #%(*,.012456778919::989 ;=EQc|i~}||zyvuutsqqoomlkihhffdbba`_^]\[\ZYXWUTSRSQPOPMKLKJIGHFEDEEDCBA@?>=?>=<;:987654210.,*(%#    !#%'()*+,-../'0110/.0./15=Mb|[~||{zxwvttrroonnljihgfecba_^]][ZYYXUSQPNNLKJIIHHFFEEDCBA@?>>=<==<=<:;: 9889887887764543210/.-,+*)('%#!   !"#$%%&&'((&'&''&'&'&&'/&%'&)1=Lf ~|{xwvutrqqpmlkjhgfeb%`^^][ZYXWVSTRQPONMLKJJHFGFCDCAA@@?>=;=;:998767665355332231010010/..-.-,-,+*)*)('&%$#"!     !! - !"+7Ki|1{ywvustsqppnmllkiggeccba`_][ZZYVVTSRRPPNMMKIHHGFCCA?><;:;9 765533232121/,-,+)+*+*)('&%&$#"!   - )7Poľ~{||{zyxwwvuturqnonllihfedaa``_^]\ZXWWVUTSSQPNMKJIHFEDBA@=>=;;9897653321/00/.--,+*)('&%$#"! !     /&7Or}{yyyxwuuqonnmnmljkjjhhggedcba`a_^_]\ZZWWVURQPONNMKJIHGFFECBB@==<<;987853 20/0..-++*(&'&&$$#"#"! !    -$5Oqķ{xwtsqmjjhhggec`ab`_] \]][^Y\ZYYXWXWV$WVUTRQOPPMNMKLIHHFFEDCBAA@@>=>;9877553 10/..-,)**((&$#" !     -#7Tq}zwtrpmkhfeb`_[[YVVWSSTQONMLNLNLIKKJMJJKKJHIJKJGEGDDCCBB@><;87 65542320/.-,+**)(('$%$#$"!"   ( &8Phxsmjfca^[ZWURPOLJIGGFDB@A?>?>=>?=;=<=<==<<;<=;;::9:;8776524212100//../-+()((&%$#     4 '7J[gmmjidgdgdda]ZUSOMKIGEC@><;98674332322010/../ ././0./00/.0/0/./.-+*)*))(%# "#""!    # (5=DKJCC@@AADHHCCAA>:876431/-,+*('&&%""$$$#"#"#"#$#$#&% &%#"##"#!!"   . %*+(''&''+--14030/+*'&%%#"    1  "!                          28BIMPatt8BIMlnk2IUliFD$bddcfeac-6715-1179-bc96-d795218d25c4Vector Smart Object.ai ART5U/%PDF-1.5 % 1 0 obj <>/OCGs[5 0 R 21 0 R 38 0 R 55 0 R 72 0 R 89 0 R 106 0 R 123 0 R 140 0 R 157 0 R 174 0 R 191 0 R 208 0 R]>>/Pages 3 0 R/Type/Catalog>> endobj 2 0 obj <>stream proof:pdf uuid:7dbfed74-e54d-418c-9195-d77e398c62d8 uuid:cb1caa52-2224-4500-b8f6-91bc24e79a46 2012-09-17T16:52:56+01:00 Adobe Illustrator CS6 (Windows) 2012-11-19T22:48:31Z 2012-11-19T22:48:31Z 256 204 JPEG /9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgAzAEAAwER AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE 1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp 0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo +DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A9U4q7FXYq7FXYq7FXYq7 FXYq7FXYqhdU0+HUtNurCZ3jiuonieSJuMihxTkjb0YdRtkZx4gR3soS4SD3MCn/ACM8sJC0mmXl 7ZamvxQX4mqwk6hmAC1360ocwj2fDoSD3ucO0Z36gCO5Mfy280apfJf6Br5H+INEk9K4f/f0R+xK Olfc/I98s0uUm4y+qLXq8MY1OH0yZtmW4bsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsV dirsVdirsVdirsVdirsVdirsVdirsVdirzrz7peraL5lsvPWiWzXZgT6trVlF9qW3/noOvEdfCin oDmDqIShMZIi+92GmnGcDika7veyryt5y0DzRZm60m49QpT17dxxmiJ6B0/iKg9jmRhzxyC4uLmw TxmpBO8uaXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FUv 1zzBo2hWRvdWu0tLcbBnO7HrxRRVmPsoyGTJGAuRpsx4pTNRFsNH55eSC4qt6tuW4/Wjbn0gK05b Nyp/sa5i/wAoY/Ny/wCTsnl82fW1zBdW0VzbuJYJ0WSGRdwyOOSsPYg5mAgiw4JBBoqmFDz/AM5+ SL21vj5t8nj6tr9v8VzaIP3V5H1dWQU+I/j/AK1DmFn05B48f1fe52n1AI8PJ9P3J/5J856d5q0r 63bgwXcJ9O+snPxwy9welVP7J/iCMuwZxkjY5tGo05xSo8uhZDl7Q7FXYq7FXYq7FXYq7FXYq7FX Yq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYqk3m3zXpXljR5dS1BxsCtvADR5pKbIv8T265VmzR xxst2DDLJKgw7yt5L1LzFqI82ed4xLcPvpmjOD6VtF1Uuh/a/wAk/Nt+mLhwGZ48nwHc5ebUDGPD xfE96YfnBqMtp5O/RlnGGudZni063TYAeoan26Jx+nJ62VY6H8WzXoYXk4jyjuyzQtMTStFsNMRu a2VvHBz/AJvTQLX6aZk44cMQO5xck+KRl3lHZNg7FXn3nHyXrFnrI84eTqJrK/8AHQ0/pFdx/tbV A5Gm479R8XXCz4JCXiY/q6jvc/BqImPh5Pp6HuTLyr+Z2g65N+j7kNpOtoeEumXfwPz7qjMF5/LZ vbJ4dXGex2l3FrzaOUNx6o94ZfmU4jC/On5jQaPcpoujQHVvM1z8MFjF8Sxk9GmI6UG/Hw60G+Ym fVCJ4Y7zczT6UzHFL0w70rt/yz8xa0n1vzb5kvfrcnxCy0+QQwQ16KKqykjuQo+Z65WNLOe85G/J sOrhDbHEV5tS+V/zO8tVn8v62des0FW0zU6mQgdkkJ/42XE4s2PeMuIdxUZsOTaceE94TXyx+aOj 6rdfovU4n0TXUISSwu/g5P4RuwWtewND4VyzFq4yPCfTLuLXm0cojij6o94ZpmW4bsVdirTuqKXc hUUEsxNAAOpJxVyOrqHQhkYAqwNQQehBxVvFXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FWN+c/Pmj+V rZfrFbnUZ9rPTYt5ZWJoNhXite/3VOY+fURxjfn3ORp9NLIdth3sd8teSta1vV4/Nfnej3afFpmj f7qtlrVS6/ze30nfYU4sEpy48nPoO5yM2ojCPBi5dT3vRsznXsc8++UR5o0L6lHN9VvreVbmwud/ gmjqBWm9CCRt8+2UajD4ka69HI02fw5XzHVjUHnb8yNFjFv5g8rS6iYxQ6hprcw4H7RjQP17/Z+W Y4z5YbSjfmHIOnwz3hOvIp35a/M/yzrt7+jgZtP1ToLC+T0pC3cLuyk+1a+2XYtXCZrke4tOXRzg L5x7wy3MlxXYqkHmnyL5a8zQ8dUtA06ikd5F8E6fJx1+TVHtlObTwycw34dTPH9JYwn5a+craP6j Y+dbuLST8IjkiDzon8qy8wR7Up8sx/yuQbCZ4XJ/N4zuYDiZJ5R8h6D5Xic2MbTXs/8AvTqE55zy Emp+L9kV3oPprl+HTxx8ufe4+fUyyc+XcyLL3HdiqSeZ/Jnl3zNbehq1qsrqKRXK/DNH/qON/oO3 tlOXBHIPUG7DqJ4zcSw6PTvzQ8mfBprr5q0JPsWs7cLuJB+yrHrtsKcv9UZi8ObFy9cftcszw5uf ol9iJX85LSIcL/y3rVtcj7cQtlYD6WeM/wDC5L88BzjL5MfyBPKUa97m/OKKf4NN8sazdz9o2twg r23Rpf1Y/nb5Rkfgv5Cuc4j4oSfSvzI88H0dZUeWfLj/AN7ZxMHupl/ldvA+4X/VORMMub6vRH7W Ynhw7x9c/seiaVplnpenW2nWalLW1jEUKkliFUdydzmdCAiAByDr5zMiSeZRWSYuxV2KuxV2KuxV 2KuxV2KuxV2KuxVhXnfz7Pp13F5e8vQfpHzPebRwDdLdWH95L26b0Pbc7dcTUagxPDHeZczT6YSH HPaA+1vyX+XUWlXB1vW5jqvme4+Ka9k+JYieqwg9KDbl91Btjg03CeKW811Gq4hwx9MGaZluG7FX Yq7FWNeefI+m+adMaKRRDqcI5WF+opJFIN1HIblCeo/jmPqNOMg8+hcjT6g45eXUIP8ALHzTe61o 89nqtV1zR5Taakp+0WWoVzT+biQfcHI6TMZxqX1R2LPWYRCVx+mW4ZjmU4jsVdirsVdirsVdirsV dirsVdirsVdirsVdirsVdirsVdirsVdirsVdirBvO/ni+ivl8reVo/rnme6X4mFDHaRkbySE/CGp uAenU9gcPUagg8EN5/c5un04I457QH2pl5H8j2vlq1kllkN7rd6eepak9S8jk1KqTuEB+/qcs0+n GMd8jzLXqNQch7ojkGT5kOM7FUHq+sabo+ny6hqVwttaQiryP+AAG5J7AbnITmIizyZwxmZoc3iP mL84vNnmPUP0V5Pt5baKQlY2jUPdyjxNOQjHy6fzZqcutnkPDjH63cYtBjxjiyG/uQj+RfzstYDq a3N21wo5vFHfM85pv0DkN1OwJyP5fUDez82f5nTE8ND5Mw/Kb81L7Wrw+X9fodUCsbW54hDL6YJe ORRQB1AJqBuAe/XJ0esMzwy5uJrdEIDjh9KO4jRPzrXh8Nt5lsSXXov1iAE1+fGL/hsn9Go/rhh9 em84F6Pme692KuxV2KsI83fmTHp96NC8vW/6Z8yynilrH8UcJ8ZmBHTutfmVzEzarhPDH1TczBpO IcUzwwS6D8t/Nurr9b8z+abyO7k+IWenP6MMXfiKfC1P9X6T1ysaWct5yN+TYdXjjtCArzV/yl1D VJT5g025vpdSstKvjbWF5OeUjKvIMC5qSBxBHz8Mlo5H1AmwDsjWxiOEgUZDd6Dma4CS+bvMR8v6 M+oi3NywdYxHXiAW7s1DQZkaXB4s+G6cHtDWfl8XHXEifLusfpnRbXU/RNv9ZUsYmNSCGKnegqDS o9shnxeHMxu6bdHqPGxRyVXEmOVOS7FXYq7FXYq7FXYq7FXYq7FXYq7FWPfmBr91oHk/UtVtF5XM CKsNRUK8rrGHI/yefLKNTkMMZkG/S4hPIInklf5VaBpGneX1vbe6j1HVNRAn1O/RxKxkf4vT5Ak0 SvfqanK9HjjGNg2TzLbrcspToiojkGa5luG7FXYq+fPzG1bUvPH5gw+WNOkrZ2s31WECvD1R/fzs O/ChH+qNuuaTVTObLwDkPwXe6WAw4uM8zv8AqD2Xyh5K0Pyrp4tdNh/esB9Yu3AM0p/ym8PBRsM2 uHBHGKDqc+ollNlPsuaHz5ciKT8/wNN+yNRjMnH+ZYwbnp/lB65pD/jO3f8A2u9H+K7/AM3+x6D+ Yv7rzv5EuU2k+uzQmm1Vk9NTv9+Zup2yQPm4Ol3x5B5PQsznAUbu8s7K3e5vJ47a3jFZJpWCIo92 YgDBKQAspjEk0GC3/wCcejvctY+W7C68w3w6LaoyxA+8hBanuEp75hy1sbqAMi5sdBKrmREIVtI/ NnzT8Oq3kXljSn+3aWZ53TL4M4JpUbH4x7rkeDPk+o8A8ubPjwY/pHHLz5Mt8qeSvL/le1MOlwUl k/v7uQ855f8AXeg29hQZk4cEcY2cXNqJ5Dcky1eS7j0m9ks1LXaQStbqBUmQISgA/wBbLJk8Jrm1 QA4hfJ5H5A8y+ZNC8twaRpnk+/vL0ySS3lzMGt4mlkbY8mQ1ogUbkdM1mmyzhDhECS7XU4YTnxSm APmzLy1+Yd7fa/8A4e13RZdG1Z4Wnt0LiVJVWpPFgF7A+I2O+ZWLUmUuCQ4S4mbSiMOOMuKKI8m+ dG8zz39rcaf6EdvQgE8wVYkcHBA+LbNzqtJ4IBBu3m+zu0vzRlExqvxugH/M250/W4tP1ny7daXp 01z9StdSdg0bSVotQFChT2KsdvlmlOrIlUokC+b0o0YMLhIEgXTPMzHCUL6+srCynvr6dLaztkaW 4uJWCIiIKszMdgAMVSryb508u+ctCj1zy/c/WtOkkkiWQo0bB4mKsCjgMPH5YqnmKuxVpHR15IwZ TWjKajY0PTFabxV2KuxV2KuxV2Kqdxb29zBJb3EazQSqUlikAZWU7EMp2IOAgEUUgkGw8kbSdH0f 82tJsfJgaGbjI3mC2idngSHY0apbiafs9AeNN81nBGOcDH8Xaccp4Ccn+a9fzaOqUru6trS2lurq VYbeBTJLK5oqqoqSScBIAspjEk0HinnL8872+kfS/KUDoJT6S37KTM9dv3EY+yT2JqfYHNTn7QJ9 MPm7jB2cB6sny/Wjvy38pQ+SNMuvOPmw/Vbgx8YIXq0kSOd6j/fshoAOoHXqaT0uHwgck9mGrznN IY8e7KvIv5q6Z5v1e7062sprV4IjcRPKytzjV1RqhfsmrrtU5kafWDLIgCnG1OiliiCTaM/Mjzzb eU9CedSr6ncgx6fAd6vTeRh/KnU+Ow75PVagY4316MNJpjllXQc2AfkR5Tu7i+ufOGo8m5epHZO+ 7SSSH99NU/Ste9T4Zhdn4SSchc7tLOABjDItfnHmH82NC0u0/eQeXVkvdRkXdUkcDgh7VBVPvPhl +Q8eeMR/DuXHxDw8EpH+PYMq85+c9K8q6Uby8PqTyVSzs0P7yaTwHgB+03b50GZOfPHHGy42n08s sqDEdK8ia15uuE1zz5K/ok+pY+Xo2aOKJT09Wm/Kh6fa8T+zmLDTyyniy/6Vyp6mOIcOL4yeiafp un6dbLa2FtFa2yfZihQIvzooG+Z0YiIoCnAlMyNk2ickxdirsVSHzd520PytZCfUZS08lRbWcfxT St4KvYeLHbKc2eOMWW/Bp5ZDQeZWGt65YecIPNPmbTnN9qcXp6dZBiv1W1Z+OykEmQ7/AAmnXf7W 13Z3Z8s/FmmaPQOB2x2zDSyhghHiB3J/V3s885+b5/LuueXbVFgjstXuZIr+4nBCoq+nQhwVCsef 7VRmNqM5hKI6F2Wn04nGR6hPo5fLuv254NaarbwS9vTuESaPof2gGWu2WgxmOhaSJwPWKNa4t0mS B5UWeUExxFgHYL9oqvU0rvljW8P86+WfzD/NbzzceWNTtrjy5+Wmhzj67LULLq0i0dPSI2MZBBHZ erfGAqqsi8m2smqebjDoj/ovyX5PP1LT7C1+GKaYKUfl/MNySTvuO7E5g45SyZSb9EftdhkjHFiA I9c/sD1LM517AvzH8z6mLm18oeXW/wBz+r7STL/x7W5rykJH2SQDv2AJ60zD1WU2McPql9jnaTDG jkn9MftLJfKflmz8taFb6TaO8iQ1aSVySXkbd2p+yCewy/DiGOPCHGz5jkkZFOMtanYq7FXYq7FX YqwDzl541KbVR5R8oKLjXpgRd3fWKzTozMdxyWv0e52zCz6gmXBj+r7nO0+nAj4mT6fvTryP5H0/ ytYMiMbrU7k89Q1B95JXO53NSFBOw+k75bp9OMY8+padRqDkPcByDJcyHHeJfnv5qu7rUbXyhpxZ q8JLyNOskshHoxfR9qneo8M1PaGYkiAdx2bhAByFnv5efl1pflTTYmaNJtZkWt3ekAsCw3jjJ+yg 6e/U5mabTDGP6TharVSyn+i8/wD+citRvvrukaaOS2XpvcbV4vKW4b+6KP8Ahswu05GwOjndlQFE 9U1/JnylJ5b0i/8ANOtg2jTwH00kHFo7VP3ju4O45lQQPAe+WaHD4cTOW36mrX5/EkIR3/WwzTbT VPzU8/y3F0Wi0uH4paf7ptVb4IlPTm/66t2zFgDqMtnl+hzJyjpsVD6v0vcfMeoW3lXydeXdnCsc Wm23G0gUfAGoEiFPDkRXNvlkMeMkdA6bFE5cgB6ljPke307yd5Ek8x6zNW81JRqGoXLbySNKOUUQ 8W+Lp/MTmPpwMWPjlzO5cjUE5cvBHkNgoeRtAvvMurf478yx1eX/AI4WnNulvAD8ElD+0eq/8F3F Bp8ZyS8SfwDLUZRjj4UPie96Vme65I/N/nfyr5P0v9KeZNRi06zLcEeTkzO9C3GONAzu1B0UHFXl D/8AOQXnHzVI0H5W+RrzV4alRrep/wCi2VQd6fEqsO+8qt/k4qjNM8n/APOSer6ha3vmLznp+h2c csc0ml6VaiaqowYxO7rG3xAcT+9YYqnv526HPeaVZ6pH501Hyh+jRMFGnM3+lvNw4o8Qki9Vk9M8 BXbkTsMry5YwFlsxYpZJVF5L5b0b/nIvTbq288S6faeeEaPhbQXzCC+jhU1WWNCY0Vj+yfjO9ab1 ynFCMyMhFFvzTljBxg3HyejeXvzw1XVde03SNb/LjXtIu7qdIUuprYyW0LO3EyGd0i+Bf5gMywSH CMQeYRf5xap5zlu7bQNC/L9fNsEsK3P6QuLkW8EExZ09Pf0/i4qDUSjY5XkxRmKkLbseWUDcTTD9 D/Lj/nIi5tGtI9V0byFpk5Bmg0mJ7q6O3VpZTK5I8RcYMeKMBURS5c0pm5G2aeQvyC8veV/MCeaL /VtT8xeaUDhdU1C4c8fUUo/FFO4KsRSRnyxrZX+YXmqDy35XvL0yBbyRDDYJ+007ii0Hfj9o+wyj U5hjgT16ORpcJyTA6dVP8tfLj6B5OsbKZOF5KpubwH7XqzfEQ3uq0X6MGlxcGMDqnV5ePIT0TbzH r9joGi3WrXrUhtk5BK0Z3Oyovux2yzLkEImRasWIzkIhiX5W6DfOl35w1ta61rx9RARvDa/7rRfD kAD/AKoXMbSYzvkl9UvucrWZRtjj9MfvZ/ma4LsVdirsVdirsVYd+ZPnC50TT4NO0lfW8xau31fT YVoSpYhTKQf5a/DXv7A5i6rOYCo/VLk5ekwCZuX0R5ozyH5LtfK+k+kSLjVLk+rqV8al5ZTufiO/ FSdvv6nJafAMcfPqw1OoOSX9EcgyXMhx3Yq+ffKkY8wfnjc3c37yK3u7m4Wu/wANvySD/gSE+7NJ hHHqCfMu9zHg0wA7h9vN9BZu3RKctvbysjSxJI0Z5RllDFT4rXocBAKQSHmX59+ZvqHlyHRYXpca o9ZQOogiIY/8E/EfKua/tHLUOHvdj2bh4p8X81N/ya8sDRPJsE0qcbzVKXc5PUIw/cr9Cb/MnLdD i4Mfmd2rX5uPJXSOzMNV0y01TTbnTrxOdrdxtFKo2PFhTY9iOxzKnASBB5FxITMSCOYYNZ/kxpKz 2w1PVb/VtOsiDZ6bdS1gSmwBUdgOy0+7bMOOhjYsmQHRzZa+VHhAiT1Cd+d/zK8j+RbBbjzHqcVl yWttZr8dxKBtSKFKuR2rTiO5GZzgPLI/Ov55/mkB/grTh5J8py9PMGpqHvZo/wCaCKhAqP5QR4SD FXrkfk+wvPLml6R5pWHzJJpqws11ewIwmuYYzGLhomMihzyJ79cVT5ESNFjjUIiAKiKKAAbAADFW IebPzN0PQ2+o2ldW1yQ8INMtDzfn0AkKhuHy+17Zi5tXGGw3l3OXg0kp7n0x7ylXl/yFq2tamnmX z263F6u9joy729svUclqQze2/uSelePTymePJz7u5ty6mMI8GLl1PUvRcznXuxV2KoLVNc0bSYvV 1O9gs46VBmkVK/6oJqfoyE8kY8zTOGOUvpFsJvfzftbyZrHyfplzr990Eio0duhO1XZgGoPcAe+Y ktaDtjBkXMjoSBeQiI+1d5f/AC/1i/1iLzL54uVvNTh+Ky02On1a23qNujMP17kscOPTSlLjyGz3 dAuXUxjHgxCh1PUs8vLy1srWW7u5VgtoFLyyuaKqjckk5mSkALLgxiSaHN5fapdfmd5hjvp43h8k aTLW2hcFTezrtyI/lHfwG3UmmuF6iVn+7j9rspVpoUP7yX2PVQABQbAdBmydY7FXYq7FXYq7FXYq 828mQ/4g/MXzD5luvjj0mU6XpanonCqyOvgT1/2ZzAwDjyymemwdhqD4eGMB/FuXpOZ7r3Yq7FXz 7+Wk8ek/nBe2l6fSkmkvLRC2w9QyclG/83Cg8ajNJpTw5yD5h3urHFpwR5F9BZu3RJZ5h8y6L5fs HvtVuUgiUEohI9SQj9mNOrNleTLGAuRbMWGWQ1EPBdMg1L80vzEa7uY2TS4SrTrWqw2iH4IeX88m /wBJJ6DNNAHUZbPL9DvJkabDQ+r9L6MVVVQqgKqiiqNgAOwzevPqGo6jYabYz3+oXEdpZWyGS4uZ mCRoi7lmZqADFWFeQfzV0T8x31yDQLe/g02xpBb648RiiuGkDBmt2YGjR0Bo2+4quKpR5H/5x48o aDqLa9r00vm7zTI3qS6vqv7wB/5o4GMiqR2LFmHYjFXqM80UEMk8ziOGJS8kjGgVVFSSfADATSQL 2ebL5s8++c55R5Oji0rQ4nMY1m8Xk8pU0JiQhxT/AGJ9yDtmB42TKf3e0e8uw8DFhH7z1S7gr/8A Kp9Tv/8AlIfNupX6sPjggb6vF8uJMq/hkvyZl9U5Fj+djH6IRDJ/LXkbyv5bU/omxSKZhR7l6yTM O45tUgewoMyMWnhj+kOPm1E8n1FPsuaGIeYfzV8maLIbdrs316DxFnZD1n5dOJYEIDXsWrmLk1mO G12fJy8WiyT3qh5pQPOX5n61T9BeV10+2YfDd6o5U0P7Xp/um/Bsr8fNP6Y1723wMMPqnfub/wAD /mVqu+ueb2tYz1t9Mj9MUPUeoPRb7wcfy+WX1Tr3L+Ywx+mF+9G6X+Tfkmzm+sXcM2q3Vama+lMl T7ovBG/2QOShocY3O582E9fkOw9I8mXxxaZpdoEiSCxs4+iqEhiX6BxUZlACI7g4hMpHvLFde/Nr ydpf7m3uv0rfseMVnYfvmZugHNfgG/vX2zHyazHHkbPk5OPRZJcxwjzSOPyz5x8+XMd15tDaR5cR hJBoMTESy0NR67bEfTv4KvXKRiyZjc/THubzmx4RWP1T/nfqelWdna2VrFaWkSwW0ChIokFFVRsA AMz4xAFB10pEmzzVcKHYq7FXYq7FXYq7FXldpqh/Lvzhqlvq8bjy3r1ybyy1JVLJDNISXjkoCe9P GgB7mmujPwMhEvokbt2coePjBj9cRVPULW6trq3juLWVJ7eUco5o2Dow8VYVBzYAgiw60gg0VTCh 2KvLfzM/KCfXtROuaFKkGpvT6zBISiSMoAV0YA8XoN+x9u+u1Wi4zxR5uy0muEBwy5MR/wAH/n3x Ft9avfRG3P8ASKUpTpX1uftmN4Gp5Wfn+1y/H0vOh/pf2InTPyG806jdi58xamkKsf3hV3ubhh4V aij58j8sMOzpyNzP6WM+0oRFQH6HsPlnyvo3lvTV0/SofSiB5SSNvJI/88jdz/mM2mLDHGKi6nNm lkNyTG7u7WztZru7lSC1t0aWeeQhUREHJmZjsAAKnLWp88Oda/5yD8yvHHJNp35QaNcBXYcoptWu I6GniEH/AAo/yz8Kr6C0rStN0nTrfTdMto7OwtUEdvbQqEjRR2VRiqKxVKPN2lXOreWNU022bhcX dtJHESaAsV2BPg3Q5VmgZQIHUNuCYjME9Cxf8q/NmlzaRb+WbhBp2uaSn1afT5fgZzHsZEB+0W6s OtfbfMfR5gY8B2kOjk63BISMxvGXVn+ZrgqN7eW1laTXl1IIra3RpJpW2CqoqScEpACymMSTQ5vM If8AFX5mSySieXQ/JQYpGke1zeAGhqf5f+FHSjEE5rhx6j+jj+0uyPBp/wClk+wM58v+TvK/lqD/ AHGWUVuyL+8u3+KUgDctK3xU+mmZmPBDGNg4WXPPIfUWNah+bC3N9Jpvk/Sp/MN3GeMlxH8Fqh6V MlDUV77A9jlEtZZrGOI/Y5MdFQvIeEfapi0/O7VPjlvdN0GNv91RJ68gH+yEy/8ADYK1EuoivFpo 9JSXf8q0823Zrqnne/kVvtR2q/Vh8vhcj/hcfyszzmV/N4x9OMfHdfD+SHk4yiXUJb7VJOrG6uCa /TGI2/HCNBj62fip7RydKHuDK9F8qeW9EWmladBaNShlRAZCPAyNVz9JzJx4YQ+kU4uTNOf1G01y xqdirsVdirsVdirsVdirsVUL6wstQtJLS9gS5tZRxkhlUMpHuDkZREhR5MoyMTY5vMLjTpvy682a Qmj3TyeX9fuhazaPKxcxO7KokhqeRpy+fY1qKa8x8CY4T6ZHk7ES/MYzxD1RF29WzZOsUru7tbO2 kubuZLe2iHKWaRgiKPEsaAYDIAWUxiSaDA7j84LO6uXtfK+j3vmGSP7csCNHCPm5VmHzKjMM60E1 AGTnDQkC5yEVOT8yvOdohn1DyPeR2q7vJDN6zKviUEY6d9xg/NZBzgaUaTGdhkFsv8seaNI8y6Um paXIXgYlHRxxkjcdUdd6HfMnFljkjcXFzYZY5VJJPOH5wflv5QhkfW9dtY546/6DC4nuiR2EMXJx 82AHvlrUkNnceXPz4/LCUzQanpGjXl2yJ8QgllS0l2YEc45I3pRhuAwI6rXFXoejaNpei6Va6TpV slpp1lGIra3jFFVF/WT1JO5O5xVGYq7FXYqxvzX+X/l3zLxmvImh1CIfuNRtz6c6U6fENmp25A07 ZRm00MnPn3uRh1U8fLl3MTm1L8wPIDLJq0p8yeVlYLJfAUu7dSaAvUkt/siQf5lzFM8uH6vVD7XK EMWf6fRPu6FFfm9qq3vkWwXT5w1prt5awCde8MoaUHx6xjbJ62d4xXKRDHQwrKb5xBZ/Y2VtY2cF laoIra2RYoYx0CoKAZmxiAKDgykZGywj85bu7Hl+x0m2lMA1u/hsZ5h1ET1LD6SBX22zD10jwiI/ iNOZoIjjMj/CLZhomh6Xommw6dpsCwWsIoFHVj3Zz+0x7k5lY8YgKDiZMkpmzzR2TYOxVINd8++U NCLJqWqQxTL1t0JllB8DHGGYfSMpyaiEOZb8emyT+kMbH506Pcb6bouragn+/YbYFCPEHmT+GY/5 6J5Rkfg5H8nyHOUR8Vaz/OrydJcC31BbzSJj2voCor84zJT5mmGOux3RuPvRLs/JVipe5m1lfWV9 bJc2U8dzbybpNCwdD8mUkZmRkCLDhyiYmjsr4WLsVdirsVdirsVdiqC1vUf0Zot/qXD1PqNtNc+n 05elGXp9PHIZJcMSe4M8cOKQHeWBflz5Wl1lrbz35juGvtWug0ljA39zbR8iF4LvvTceFf5t8w9L h46yT3kfsc7VZuC8UBURz83pWZ7rnz5+dX5oeVbbz5F5f12a4vNO02JJP0Hpo9Se7vJaFI3oVCfC 46moFeO5zCy4pZclH6B9pc/Dljix2P7yX2BFab5i/wCcifMNokHlDyfpnkXQyP8AR5tWYmYDx9FV DKT1+KD6cy4xERQcKUjI2dyz/wAj2Xnnyxo+pX35jeaLfWGYpMk8cCW0VsqqQ6AqsYcMacfgBr41 xlIRFnksYmRoc3j/AOXX/OOum+atNvdV17zBr8unXV1J9TgN0E+sRA0Ms/JJORJ2JBG4zH0s+IGV ULcjVw4JCN8Rrd6v5Y/5x/8Ayi8uUex8u289wBQ3N9yu5K/zL6xdUP8AqAZlOKxH/nG65k8uah5u /Ky/el55bv3udNDHeSwuSCrKPAHi5/4yDFXr+oeZvLum3CW2oana2lw9OMM0yI+/Q8WINPfK5ZYx NEgNkcM5CwCUxR0dFdGDIwDKymoIO4IIyxrbxV2KvIf+cgNY8wWNvpcFlNLbadOZfrEsLMnOVePF HZabUqQK7/Rms7RnIAAcna9mY4Ekncpb+X3mDW5Pyw82PqjyXdjbQPHYyTEuS0sTK8fJqnipKH2r lemyS8GfFuGzVYo+NDh2J5p3J5UvNV/JHTLW1Pq6hawx6jZcNyXBaQItP2vTkKj3y44TLTgDmN2k ZxHUknkdma+RvN9l5o0GC+hYC7RQl/b/ALUUwHxAj+UndT4ZlafMMkb69XD1GA45V06LPP8A5SPm jy89hFL9XvYZFubCc1os8dQvKm9CGI9uuOpw+JGuvROmz+HO+nVjNr+ZvmPRo1tPNvlq+F1GOJvb CMTQzEftDdUBPcBz9HTMcaucdpxN+TkHSQnvjkK7iqP+a+q3/wC68u+U9Su5j8PO6QW8SMf5mHqL 97DD+clL6ISKPyUY/XOI926mfKX5meZt/MmtLo1g/wBrTNM+2R3V5K/8bOPbB4ObJ9cuEdwT4+HH 9EeI95ZD5f8Ayz8l6GFa002Oa4HW6uR68pPiC9Qp/wBUDL8elxw5Boy6vJPmWUAACg2A6DMhxkPf 6bp+o27W1/bRXVu32opkV1+5gcjKIkKItlGZibBpgt3+T9nbXLXnlXVbvy9csaskLNJA3sULKafN iPbMOWiANwJi5sdcSKmBILT5W/OCUfVpfN1ulsdjPHaoJqfQi0+YauPg5+XH9i+NpxvwG/ezzT7e 4trC3t7ic3U8MaJLcsOJkZVALkAmhbrmZEEAA7uFIgkkbIjJMXis/wCeP5nX00kXlz8qdXmTkwiu tSc2KkDo3GSKm4/y8Va/xN/zlhdHlB5P8v6cm/7u8uTO3gByguQPfFWa/l3qX5qywanJ+Y1hpen+ h6bae+ltIyunFjMZPUlmI40WnTviq/yB+b/kHz4sieX9SV72Gpm06dTDdKAacvSfdl/ylqB33xVl 91bQ3VtLbTqHgnRo5UPRkccWH0g4CLFFIJBsPLfLmvf8q41KTyr5nuFh0F2ebRNYmISFY2NWjlck KlCd69D7EHMDBI4peHLkfpLsc8Rmj4keY+oLPLn543XnPz9Do3kjRX1TyrZM6695lmJhhQ8TwFvy py+Kmx+JhWigfFmwdajtb/JfTovM83nHyc8Wi+abiR5rqd19SKd5BRyVbn6Zbq3FaMdyK75j58U5 UYyoj5OTgywjYnGwfmiPS/PQj0vW0VT09ektf9anE/8AEcprU/0W69N/SWwfldqusXUd3541yTVx E3OPTYB6NqD7heNfoVT74jSSkbyS4vLok6yMBWKPD59XoUMMMEKQwosUMShI40AVVVRQAAbAAZmg U4BN7lfhQ8A86aNqHm785dN1n8urz6nqOmW0lj5h1ld7d7d6hVUj7TqGYBvGhX7Fcw5ajjJhD59z mx0/hgTn8upZXrPkHyJ5b8k6tql2o1a+9GVG1K4f1He6cmIcACVUiU/MdztlWTTY8eMk7nvbceqy ZMgA2F8h3Mw/Lazu7PyLosF2CJ1twxVuoVyXRTXwVgMydLEjHEHucXVyByyI72SZkOO7FVK5tbW6 haC6hSeBvtRSqHQ033VgRgIB2KRIg2FGfSdNm0yXS2t0WwmiaF7dFCJwcEMAFpTr2wGAI4ejITIl xXu8zV/NX5YFUl5az5I9Sgk/4+bMO3f2qf8AVJ/lJzX+vT/0sf2h2NQ1P9HJ9hRuveVL23vF88eQ Jl+tXCCa7sE3gvYm+MlV/mbw7ncUbrPJhIPiYufd3sMWYEeFl5d/cyryV5z0/wA1aV9bt1MF1CfT vrJz+8hk8D0qpp8Jpv8AMEZkYM4yRsc3F1GnOKVHl0LIMvaHYq4kAEk0A3JOKsK8z/mr5e0omy0x v01rUnwW9hZ/vayHoHdKgfIVb2zEy6yMdh6pdwczDopy3l6Y95V/y68v67p9pe6n5gmZ9Z1iX6xc W4YmOBRXhGoqVB33p7Dth02OUQTL6pMdVljIiMPpiy7MpxXYq7FXYq7FXYq7FXYq81/Mf8h/KPnG f9L2pfy/5sib1bbzBp/7uYSjo0yqU9X51D+DDFWEL+cP5l/larab+aujS6zpsaldP816WoZJiB8E dwG4Krsdqni3+S32sVQ/l/8ALbzj+dCW3m38zL+Sx8sXK/WNB8qWDmNRFIP3U0zkGpZTyB3Y1/ZH w4CEgs30XyZ+ZPkmzXSvKlxp1/oURP1W0u4zE8fIkn+74V69ef0ZheHnh9JEh5uf4mnn9QMT5I39 BfnLqhH17X7PR4TSsdjD6r/e4B+6THw88uchH3I8TTx5RMveuH5W+YGHKbzxrDSHqY5XjX6FEhpj +Ul/Pkv5yHTHFTfyf+aOjj1tF80/pULubPU468gP2fUJkNT81+eDwM0fplfvSM+Cf1Q4fcmXlT8x xqGpnQPMFk2i+Y06WshrFN7wv38aVPsTlmHVcR4ZDhk15tJwx44HiggfzC1rV9V1i28ieXpDFeXq erq16v8Ax72p6io7sOv0D9rIanJKUhjjzPP3M9LjjGJyz5Dl5llvl7yvpGg6JHpFjFS2VSJWP25W YUZ3YUqzf7WZWLEIRoOLmyyyS4i+ZfzI8qecfymsb+xsBPq/5W6ncLdRgkyTaVcF6lTXf03rSp2O 1aN9uvU4TkgQObPS5hjmJHk9q/Ln85tK84rYQW+n3KS3SsPrcSK9mGiQu37zkGX7NOJXY7ZHFqSZ CMokS+xnl0oETKMgY/azLXfNGhaD9WOrXS2q3knpQO4YqW/ymAIUCvVqZdkyxhXEatpx4ZTvhF0m aOrqHQhkYAqwNQQehByxqbxV2KvM5Pyz1fzRf6lfecb6eENI0emWVnMvoxwU+FqFWBJ+QrSpzX/l ZZCTkPup2I1ccYAxj3ku/Jy51PTJdW8l6tUXWjyLLbAkH9xN8Xw0J+GpDf7LHRExvHLnFdeIyrJH lJfqVsug/nFpNzY/u4fMkE0WoQLsrSQqW9TwqTx/HxwyHBnBH8XNEDx6cg/wcnpOZ7r2A69+YWrX OtzeW/Jlguo6pb/De30ppa2xrQhiKcmHz67fFuMwsmpkZcGMWfsDnY9LER48hqPQdShv+VW67rZE vnLzJcXync6fZ/ubcH7qN8+APvkfykp/3kr8gy/ORh/dxA8zzZf5f8n+WvL0ZXSLCK2ZhR5gC0rD waRyzke1cyseCEPpDi5c88n1G04y1pdirsVdirsVdirsVdirsVdiqye3guIXguI1mglUrJFIoZGU 7EMpqCDirFfzC/LbSfO2gW2jXN5eaVHYzJc2U+mSLA8UsSNGlPhYcQrnYU9qYq81k0f/AJyM/Lk+ vpepJ+Y/l2LeTTrwGLU1T/IkJd3YAU+2/wDqYqzb8tvzv8l+emNjbyPpfmKGq3WgX49K6Vl+3wBo JAtDXjuP2gMVeg4q7FWLfmB5Jt/M2kn0qQ6zZ/vdLvFPF0lX4gpYb8WI+jr2zG1OAZI/0hycnS6g 45f0TzeW+SfPN/pmv6n5j1vTbi6jv1js766tk5vBcWyKjK6Hjx50DUqPatCBr8GoMZGcgTe3ydnq NMJQEIkCtx7izR/zK8064/o+TfLk0q/tahqQ9GBfkAwDf8HX2zK/NTn/AHcfiXD/ACkIf3kvgFHU fNH5k6DbvN5q0Sz1TQXBS9ksOTMkbCjc0kJDLTrVaeLYnNmx7zAMfJMcGHJtCREvNi+mflRPp+u2 XnP8mNbi0zS9RnjGuaDccpLFoiw9UrFWqSRgn93UEfsso2zMx5BMWOTg5McoGpc3qnmo+W9X0jWd JvJbe6lsbYz3lrzRpbflG7RSMoPKMniSpNMjniJQILLBOUZgjnaX/lBPczfl1o73BLMFlRCSSfTj ndEG/gqgD2yrREnELbtcAM0q/GzMcynEUb17lLOd7VBJdLG5gjb7LSBSVB6bE4JXW3NMavfk8/0f 86vLjeWvr+tSrbaxEZEuNLiVzJzViFVA38y03JoD1zChrocFy+ruc/J2fPjqO8e9AeR9YfXPzW1D Vfqctjz0aJbi2mpySRniZQadaoKrXenYZDTz48xlVen9TPUY+DAI3fq/Wjlb9P8A5z8k3s/K1oVZ uqm5uAQR90n3pkvr1HlAfaw+jT+cz9j0WT1BGxjAMlDwB6VptXM8uvDz78i4of8ABH1oCt1d3U0l 1KftO4IAqe+2YPZ4/d33lz+0T+8roA9DzOcB2KuxV2KuxVIvMPnzyV5cB/Tuu2OmuBX0ri4jSQ9/ hjJ5t9AxVhc//OTn5GQSGN/M6Fh1MdpfSL/wSQMv44qzXyh528secNF/Tfl29+u6WZHi+sGOWEc4 6cxxmSNtq9aYqnmKuxV2KuxV2KuxV2Ksa1f8t/JGr+ZdO8z3+kwS67pUgltL8DjJyUEJ6nGnqcCe Scq8SKjFWF+bfzr1XyN56ksPOWhPaeSbwxpo/me25TKH4AuLlRXjVuVAKMAOjDcKvUrDULHUbKC+ sLiO6srlBJb3ELB43RhUMrLUEHFVfFXmtoR5W/N24tT8GmebIfWhPRReR1LD5k8v+DGYEf3eeuk/ vdjL95gB6w+5U/Mj83F8p6nFpdpZLeXbRiadpHKIisSFUUBJY0r7YdVrfDNAWUaTQ+KOImgyXyR5 ts/N3l5dTjg9GrNBdWzEOFkUAstaDkCrA9O+X4MwyxtxtRgOKfCmC2lloejzrpdgqxWySzRWNsoU u9C/FQB9pztllCEfSGHEZy9R+L5Z/OVvMlhr035jaN5f1Dy7b3EI0/zeJQViuYZmWMNxZUqeikjq eJ23ri+JLKDExMbHNyvDjhIkJCdHk+lPImpeVLjy9Y2flzUYb+0tLeIK0TqXKlQQ7qN1L1r065fh 4QOGJ+lx8wmTxSFcTI8uaXYq82/Nzzf+X35d6RJ5n1LTLO416YldLt/SiFzc3G2/PiWCpWrydh7k A1nFC7oW2DNOqs0wn/nFLzb5j802/mXV9Z04+rc3Su+ug8Y5nNW+rpER/urmSSppQgUwY8IgSRzk yyZ5TAB5RRnlPzlf6Q+u2Vhpc99511XUpXeFoyIolr8JlaoNFZnPYU7jNZhzmPEALyEu0z4BPhJN Y4xZcut/nRp1FvNBstXRhtJaTCJgfBubb/Qv05leJqI84iXucXw9PLlIx96bflX5b1Py/wCUo7TU 1Ed7NNJcSwgqwj9QgBar8PRa7ZZpMRhCjzatblGTJceTL8ynEdiriQBU7AdTiryTzl/zkToFjqje XPJdhP5080mqiz06rW8TDYmadQ4+HvwBA/aK4qyP8v4/zM1TQtTH5j29lZy6gzLaWOnO3OC3kj4P HI4LDlX4gyyN17bDFUl8uf8AOM/5PaJSRtF/S931e61SRrlnJ6lozxgr/wA88VZxbeSPJdrEIbbQ NNghX7McVpAijtsFQDFURceW9Dn0i40c2UUWmXQIubWBRCkitTmrCPjUOBRvEbHFUyxV2KuxV2Ku xV2KuxV2KoTVtI0zWNOuNM1S1jvbC6Qx3FtModHU9iDirwG/0rzZ+QOpyatoaz65+VF1IX1HRy3O 4015G/vImbfgNviJo3R96Pir3rQNd0vX9Gs9Z0qcXOnX8Sz20wBHJG8QaEEdCD0xVKPP/k5fM+jr FBL9W1WycXGmXe44TL0BI3CtTenTY9sx9Tg8SPmOTk6bP4ct/pPN5VrUGi+aNVgsvOk0nlfzXaoL ea5dA1rdIpPBwSVVD135cT49hrpiOSVZPRMfIuzxmWON4/XA/MPX/JnlnS/LmgwadpshngqZWuCQ TK79Xqu3YAU7Zs8GKOONB1OozSySsq3mTzTonlzT3vdUuFiQA+nFUGWVh+zGnVif9vDlzRgLkUYs MshqIebDyRq35o2t1qXm9pLLRLmKSPRtIQkcVkQqs79DUV5A9W9loDiYoSyyE57DoHMzZIYonHDc nmWCfkV5V1HU/wAuUv8AQpzp3nXytfXWmXBDfu7gRMJBHID8OyShAaUNPi8RZn01njhtP72vBqqH Bk3h9z1vQPze0aSth5nVtB1u3+G5guFYRMw/aR6GgPWjfQT1wY9bHlP0yTl0MucPVFfrH5x+Vral to3qa7qkm0FnZo5DMelXIp/wPI+2GeugNo+o+SMegmd5emPm8z81f846eY/zL16z81ebNV/R0zyR pc6TEDJ6dilT6cTFmWKQntuNyx+LbLNP4hFz6tWo8MECHTr3vedC0LSNA0i10fR7VLLTbJBHb28Y oqqPxJJ3JO5O53zIcdHgAEkDc9TirsVdirsVSvWPNHl3RbixttV1G3srnU5lttPhmcK80rGgVF6n cgYq86/Mf8vPzM89+Z30ebXk0T8txFG00dhyF9eMwpLDKTtxqD/kUK/CxrRVnHkn8v8Ayj5K0pdN 8uadHZQ0HrSgcppmA+3NKfic/M0HagxVkOKuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KrJ4ILiCSCeN ZYJVKSxOAyOjCjKynYgjYg4q84/Nnzp5u/L+20nWdG0SHUfJljWPzDbW4K3UEFAsTwKKIscff6B8 I+IKs08q+atB81aFa65oV0t5p12vKOReqn9pHXqrqdmU9MVVta8v6Jrdr9V1azivId+IkXdSe6MK Mp91OQyY4zFSFtmPLKBuJphjfkvpFu7No2s6ppCsSfRt7g+mK+Gwb72zE/IxH0mUfi5f8oSP1RjL 4I3Rvyi8q2F8uoXjXGs36GqTahJ6oBHQhQFB/wBlXJw0UImz6j5sMmunIUKiPJm2ZbhsR8j+YfI9 /qvmXSfLNlHZXWj3xj1xYrZLZZLuQsGkJQD1Wb0zVzvirIdS0TRtUVV1Kwt71V+wLiJJeNf5eQNP oyE8cZcxbOGSUeRIdp2iaNpgYabYW9kG+19XiSKvz4AVxjjjHkAFnklLmSUbk2DsVdirsVdirx3z 3+fTnWD5O/LOxHmnzhISkkkXxWNnQ8WeaUFVbievxBR+01fhxVU8gfkMltq6+cfzDvv8VedpCsgk m+KztGG6rbxEKp4H7LcQB+yq9cVevYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FVskccsbR yKHjcFXRgCrKRQgg9QcVeD+Y/wAsvOX5Za7decvymj+taTct6uu+SWr6UgFSz2ijoQPsqvxL0Xkv wYq9N/LP8ytE/MDy+dX0uG4tmglNrf2dzGyPBcooZ4uVOL05DdT86HbFUu886Z5q0nVv8Y+XJpLt o41j1PRpGZo5YI96xL+yV3O3zHcHC1EJxl4kN+8Odp5wlHw57dxZL5U816T5n0mPUdOkqp+GeBqe pFJTdHH+dcyMOaOSNhx82GWOVFOEdHBKMGAJUkGu6mhG3gctaafMXk/zFrOh+a/OWr6bIkdnY+Z3 0e+tHAL3irJLxkdqD4wEahHj9B1uWMsJEhIkE8i7PDKOcGJiAQOYfT2bJ1jsVdiqT+a9N13UNGkt 9D1H9GaiGWSK4K8geBrwbrRWPU0PyOVZoylGomi24ZxjK5CwxTR/zL1DTdRi0Pz1Zfou/kPG31JP 95J+1eW4X51p48cxoaoxPDkFHv6OVk0gkOLEeId3VkXnX8wPKPkrSTqfmPUY7KAg+hGTymmYCvCG IfE5+Ww70GZzgMZ8peZH/NvyTraahomoeX9D1Hna6ddPIIp7m0kjFLiPjuhr7Mh8WHLFWSeRfy88 peRtHXSvLlitrCaGec/FPO4/bmlPxOfwHYAYqyPFXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FX Yq7FXYq7FUo8x6DLqnl7U9L069fRbrUopE/SVoqiaOSQcTKOlWptWoPgQaHFXjQ86/nj+VzmHzpp beePKkWyeYtMUC8iQd7iIdaD+cDf/dhxVG6FqnlbzdqTeYPyn8zWun61dqf0jot1WItXq7W7KzAq a7qjLXcEd8LJpZCXFjPCT8nOx6qJjw5RxAcu96L5S8vWnknytcNf3YlkT1b/AFa+YkKWC8pG+Lfi qr3+ffLtPh8ONcz1adTn8SV8h0fO/wCSkL6pe6Nd6uPq2n+ZNe1HXY+eyyspEcUfgSJoWX/ZUzH1 O+WETy5uTpdsM5DnyfWWZ7rnYq7FVC41Cwt57e3uLmKG4u2KWsMjqrysFLFY1JBYhQTQdsVeQfnP rf5ha3fXPkTyt5P+tqYY7ibzNqJCWUAcGkkBBFZEoR15VB+BlO9eXHGcalybMWSUJXE7pL+R/wCS +gapp9p5184zTeZvMFTFb/X2MlrbrbtwjEUTfa4025bDsoyGnyCcAQKDPU4jCZBNl9AAACg2A6DL 2h2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV5x53/5x9/LHzdOb2404 6XqxPIappbC1n5A15MAGidv8pkJ98VTD8xYfzQttJ08fl4mnXMtqxW/tNWZ29eEIFRFbapJqWLOv zxV5Vrfmzz2+kwaL5u/Jq8MNqWa2n8vSiVYix39GO3jmC8j1HMg+G2VZcMcgqTdhzyxm4sUuvzQ1 jRVhk0ix86adJ6qRwQ6pBW1LE/YbdVOw/wB99umYc9OcYuMyPe5uPUxyGpQB9z3vz/5x/MXQ7bSI vK/k8+ZL/UIpDdsLhbeK0kjEZAk5KVIcyNT94v2c2Aut3Wyq9nn+pv8A85JavDy8wa/oP5dabJsw iZZrungrO0qE/wCpKpwSnGPM0yhjlLkLVvIn5a/k/p3mODX9U82y+afNlvIskWpalelQsynZok5D l8ndxlQ1OM/xBsOlyAWYl6d+ZOpapY+U7tdLtJrq/vaWcIhUsYzP8HqNTpSux/mpg1UyIHhFk7Mt JCJyDiNAbo7yX5eHl7yvp+kVDSW0f79h0MrkvIR7c2NPbJ4MfBARYajL4kzJOstaXYq7FXYq7FXY q7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq8s/M/UbC08+eVX192i8vWwluS wRnVrhPs8lQFjxIj7Hr7nNdq5AZI8X0uz0cCcU+H6z9yvP5+80ebXNj5FsHgtWPGbzBeLxiQd/SU 1BP3n/J74TqZ5NsY27yxGmhi3ynf+aEfpP5O+Xo5fruvyzeYNUfeW4u3bhX/ACYwensxbJw0Uecv UfNhPXz5Q9MfJPJfy88jyenXQ7NDEysjRRLE1VNRVk4k/Tlx02P+aGgarL/OLIcvaHYq7FXYq7FX Yq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYqh7/APR31f8A3Iej9XqP 96OPDl2+3tXIyqt2UbvZWi9L019Lj6VBw4U407UpthCCuwodirsVdirsVdirsVdirsVdirsVdirs Vf/Z Adobe PDF library 10.01 application/pdf 1 False False 1000.000000 1000.000000 Pixels Cyan Magenta Yellow Black Default Swatch Group 0 Document endstream endobj 3 0 obj <> endobj 7 0 obj <>/Resources<>/ExtGState<>/Properties<>>>/Thumb 214 0 R/Type/Page>> endobj 210 0 obj <>stream HWnf;Oq^ gm$$$@h,"]Xק$ OwuU˟^/xmo~z=~~km95rߏ/_jw캾wx>~?vnWӃ/~zƧkby^c͞x rչcqe'jΑnJLkhb93Ġ3мAm{o.f/hdu3 n=[5iw`icriUn|FMi"aS#7+$8dT8 ϓ읂PHjA JIx:;cFsy9DQnPa%f8Jw:2NwpO"R<ўpT"WIh4K;?jkh{epl!D6v 1ýb0'±qdx[eswAyqYBҗ3Xœ;Qw.4ڒ)#у yMbax%V\RIA/0.և7 X2Sv ]gGS u3qAfEvTflc┆P[j_!MD!)Cv4z|I鋚N"΂-޷ tL;d@*KyBo('W}g whұw$,<*V>W`t쏄wXg8΁Pm%!-vH7jmycKo]*>$x}|UuQ.Fg ٲF, 'C> t(30=WAZ${vʯDsՕQ1j du L +̌24FkZ/8ZOgԒZ`ɛDí 5hpj/$VTVu|yTF b{7yVGPR'R HYH)d.ջc,Z`ꇖaY:)ˈ yO/sO΢-Aزe̓'3e@yq`YV% U4^5Tև T0Yy ɪɎBdO)[ekt`WM<̪^N.:Hvr7t˓5g{F0)S6ZV7$z #{u{4ȘW]lS[Bb<"NԸ',GXϻ_O8@2<18U^wSA26 QA[4m*AMyHWd<7*E/) I)t+y-04ÄԠ#6mKEMtL\ehMf`YEf*X>mn۰߳~U3yry}d=dL>pc%ӑO6c1&h=`|۽m^3p1/fruJrTlL0ORUG z7*Bj4G2'Su endstream endobj 214 0 obj <>stream 8;Xoa>Zk$/HlYb63<0@.A7,C7f,6%U(`35S$in\<$^q~> endstream endobj 215 0 obj [/Indexed/DeviceRGB 255 216 0 R] endobj 216 0 obj <>stream 8;X]O>EqN@%''O_@%e@?J;%+8(9e>X=MR6S?i^YgA3=].HDXF.R$lIL@"pJ+EP(%0 b]6ajmNZn*!='OQZeQ^Y*,=]?C.B+\Ulg9dhD*"iC[;*=3`oP1[!S^)?1)IZ4dup` E1r!/,*0[*9.aFIR2&b-C#soRZ7Dl%MLY\.?d>Mn 6%Q2oYfNRF$$+ON<+]RUJmC0InDZ4OTs0S!saG>GGKUlQ*Q?45:CI&4J'_2j$XKrcYp0n+Xl_nU*O( l[$6Nn+Z_Nq0]s7hs]`XX1nZ8&94a\~> endstream endobj 208 0 obj <> endobj 217 0 obj [/View/Design] endobj 218 0 obj <>>> endobj 213 0 obj <> endobj 212 0 obj [/ICCBased 219 0 R] endobj 219 0 obj <>stream HyTSwoɞc [5laQIBHADED2mtFOE.c}08׎8GNg9w߽'0 ֠Jb  2y.-;!KZ ^i"L0- @8(r;q7Ly&Qq4j|9 V)gB0iW8#8wթ8_٥ʨQQj@&A)/g>'Kt;\ ӥ$պFZUn(4T%)뫔0C&Zi8bxEB;Pӓ̹A om?W= x-[0}y)7ta>jT7@tܛ`q2ʀ&6ZLĄ?_yxg)˔zçLU*uSkSeO4?׸c. R ߁-25 S>ӣVd`rn~Y&+`;A4 A9=-tl`;~p Gp| [`L`< "A YA+Cb(R,*T2B- ꇆnQt}MA0alSx k&^>0|>_',G!"F$H:R!zFQd?r 9\A&G rQ hE]a4zBgE#H *B=0HIpp0MxJ$D1D, VĭKĻYdE"EI2EBGt4MzNr!YK ?%_&#(0J:EAiQ(()ӔWT6U@P+!~mD eԴ!hӦh/']B/ҏӿ?a0nhF!X8܌kc&5S6lIa2cKMA!E#ƒdV(kel }}Cq9 N')].uJr  wG xR^[oƜchg`>b$*~ :Eb~,m,-ݖ,Y¬*6X[ݱF=3뭷Y~dó ti zf6~`{v.Ng#{}}jc1X6fm;'_9 r:8q:˜O:ϸ8uJqnv=MmR 4 n3ܣkGݯz=[==<=GTB(/S,]6*-W:#7*e^YDY}UjAyT`#D="b{ų+ʯ:!kJ4Gmt}uC%K7YVfFY .=b?SƕƩȺy چ k5%4m7lqlioZlG+Zz͹mzy]?uuw|"űNwW&e֥ﺱ*|j5kyݭǯg^ykEklD_p߶7Dmo꿻1ml{Mś nLl<9O[$h՛BdҞ@iءG&vVǥ8nRĩ7u\ЭD-u`ֲK³8%yhYѹJº;.! zpg_XQKFAǿ=ȼ:ɹ8ʷ6˶5̵5͵6ζ7ϸ9к<Ѿ?DINU\dlvۀ܊ݖޢ)߯6DScs 2F[p(@Xr4Pm8Ww)Km endstream endobj 211 0 obj <> endobj 220 0 obj <> endobj 221 0 obj <>stream %!PS-Adobe-3.0 %%Creator: Adobe Illustrator(R) 16.0 %%AI8_CreatorVersion: 16.0.0 %%For: (Anders) () %%Title: (Vector Smart Object.ai) %%CreationDate: 11/19/2012 10:48 PM %%Canvassize: 16383 %%BoundingBox: 503 534 557 577 %%HiResBoundingBox: 503.0342 534.4507 556.8789 576.6245 %%DocumentProcessColors: Cyan Magenta Yellow Black %AI5_FileFormat 12.0 %AI12_BuildNumber: 682 %AI3_ColorUsage: Color %AI7_ImageSettings: 0 %%RGBProcessColor: 0 0 0 ([Registration]) %AI3_Cropmarks: 0 0 1000 1000 %AI3_TemplateBox: 499.5 500.5 499.5 500.5 %AI3_TileBox: 202.3999 79.1299 797.4199 920.9902 %AI3_DocumentPreview: None %AI5_ArtSize: 14400 14400 %AI5_RulerUnits: 6 %AI9_ColorModel: 1 %AI5_ArtFlags: 0 0 0 1 0 0 1 0 0 %AI5_TargetResolution: 800 %AI5_NumLayers: 1 %AI9_OpenToView: 516.75 571.625 32 1554 900 26 0 0 83 122 0 0 0 1 1 0 1 1 0 1 %AI5_OpenViewLayers: 7 %%PageOrigin:0 0 %AI7_GridSettings: 72 8 72 8 1 0 0.8 0.8 0.8 0.9 0.9 0.9 %AI9_Flatten: 1 %AI12_CMSettings: 00.MS %%EndComments endstream endobj 222 0 obj <>stream %%BoundingBox: 503 534 557 577 %%HiResBoundingBox: 503.0342 534.4507 556.8789 576.6245 %AI7_Thumbnail: 128 100 8 %%BeginData: 9595 Hex Bytes %0000330000660000990000CC0033000033330033660033990033CC0033FF %0066000066330066660066990066CC0066FF009900009933009966009999 %0099CC0099FF00CC0000CC3300CC6600CC9900CCCC00CCFF00FF3300FF66 %00FF9900FFCC3300003300333300663300993300CC3300FF333300333333 %3333663333993333CC3333FF3366003366333366663366993366CC3366FF %3399003399333399663399993399CC3399FF33CC0033CC3333CC6633CC99 %33CCCC33CCFF33FF0033FF3333FF6633FF9933FFCC33FFFF660000660033 %6600666600996600CC6600FF6633006633336633666633996633CC6633FF %6666006666336666666666996666CC6666FF669900669933669966669999 %6699CC6699FF66CC0066CC3366CC6666CC9966CCCC66CCFF66FF0066FF33 %66FF6666FF9966FFCC66FFFF9900009900339900669900999900CC9900FF %9933009933339933669933999933CC9933FF996600996633996666996699 %9966CC9966FF9999009999339999669999999999CC9999FF99CC0099CC33 %99CC6699CC9999CCCC99CCFF99FF0099FF3399FF6699FF9999FFCC99FFFF %CC0000CC0033CC0066CC0099CC00CCCC00FFCC3300CC3333CC3366CC3399 %CC33CCCC33FFCC6600CC6633CC6666CC6699CC66CCCC66FFCC9900CC9933 %CC9966CC9999CC99CCCC99FFCCCC00CCCC33CCCC66CCCC99CCCCCCCCCCFF %CCFF00CCFF33CCFF66CCFF99CCFFCCCCFFFFFF0033FF0066FF0099FF00CC %FF3300FF3333FF3366FF3399FF33CCFF33FFFF6600FF6633FF6666FF6699 %FF66CCFF66FFFF9900FF9933FF9966FF9999FF99CCFF99FFFFCC00FFCC33 %FFCC66FFCC99FFCCCCFFCCFFFFFF33FFFF66FFFF99FFFFCC110000001100 %000011111111220000002200000022222222440000004400000044444444 %550000005500000055555555770000007700000077777777880000008800 %000088888888AA000000AA000000AAAAAAAABB000000BB000000BBBBBBBB %DD000000DD000000DDDDDDDDEE000000EE000000EEEEEEEE0000000000FF %00FF0000FFFFFF0000FF00FFFFFF00FFFFFF %524C45FD2FFFA0756E7575FD7BFFCAA1CAA7756EFD7FFF7575FD7EFFA86E %A7FD7EFF75A0FD7EFFCA4ACAFD7EFFA16EA7FD7EFFA14A75A8FD7EFF7575 %CAFD7EFF7575A8FD6EFFCAA176A0A1FD0BFF7575CAFD6CFF766E75A07574 %A0FD0BFF7575A8FD0FFFA8FD5AFF7575A7FFFFFF75A0FD0CFF7599FD0DFF %CF76997599A0FD56FF7575A8FFFFFFA875A7FD0DFF6F75A8FD0AFFA16E9A %A1CAA1754A99A7FD52FF7575CFFD04FFA175FD0FFF7575A0FD08FF996ECA %FD05FFCF767575FD50FF756EA8FD05FF75A0FD10FFA1756EA1A8FFFFFFA1 %756FCFFD08FFA7754AA0A8FD0EFFA8A1FD3CFF7575CAFD06FFA76EFD12FF %A76E75A0A77575A0FD0CFFCA7575A1FD0CFFA16EA1FD3AFFCA6E75CAFD08 %FF9A4AA0A7CACAFD0FFFA1A075A0A8FD0FFFA0756EA1A8FD05FFCACAA075 %6EA7FD3AFFCA6EA0FD0BFFA775756E7576FD23FFCFA775757599FD04756E %75A0FD3BFFA04AA1FD0EFFA8CFA1754AA7FD24FFA8A7A1A7A1A7A7FD3DFF %9A6ECAFD13FFC96ECAFD65FFA8756FFD16FFA16EFD64FFA87475FD18FF99 %A0FD62FFA76E76FD11FFA8CAA8FD05FFA174A8FD17FFA8CAA8FD46FFA775 %A0FD10FFCA76996E7575A7FD04FF75A7FD15FFC975756E75A1FD44FFA16E %7CFD0FFFA8A06E6E4A756E6E4A9AFFFFFFA075FD13FFA8756EA1A8FF7C75 %FD08FFAF6184FD38FFA174A1FD0FFFA7756E7575A7A8CFA0756EA0FFFFA1 %75FD12FFA17575FD04FFA775FD08FFAF143CFD37FFA16EA1FD0FFFA06E4A %75A1FD05FF7C6E6ECFFFCA6ECFFD0FFFCF756EA0FD05FF75A0FD08FFA861 %84FD36FFA86EA1FD0FFFA7756E9ACAFD07FF6E75A7FFFF99A8FD0DFFA8A0 %6E9AA8FD05FFCA75CAFD41FF7576FD10FF754A99FD09FF754AA7FFFF6FA7 %FD0AFFA8A16F6E75CAFD07FFA76EA87CA075A1CAFF6060A9FFA7A0A1FD34 %FF6ECAFD0FFFCA6E75A7FD08FFCF6E75A8FFFF75A1FD07FFCAA7757575A7 %CAFD09FFA775759AA0A16FA0AF3D14AFFFA77575A0FD07FFA852A8FD29FF %A04A75A7FD0DFF756E75FD09FF766E6FFFFFFF6FA7FD06FF7C6E6EA1A8FD %0DFFA8FD04FFA16EFF8485FD04FFA1A0FD06FFA8F8F8FFA8FD09FF75CAFD %1FFFA075A8FD0BFF76756ECAFD08FFA8756EA1FFFFFF75A7FD04FFCF7575 %A7FD15FF6EC9FD0FFF27F8FFA774A8FD07FF7599FD21FF6EA7FD08FFCFA1 %6E6E4AA1FFFFA1A1CAFD04FF754A75CAFFFFCA6EFD04FFA74AA0FD15FFCA %6E763C85FFFF6EA7FD08FFA8F8F8FFCA74A0FD07FF7675A8FD20FF7675FD %08FFCA756E746FA7FFFFA06E4AA7FFFFFFA16E6EA1FFFFFF75A0FFFFFFC9 %6EA1FD15FFA76EA0851436FFFF75A1CFA8FD06FFA827FFFFA175FD07FFA1 %74A8FD20FF766EA7FD07FFA8754A6E76FFFFFFA06E6E6EA0FFFFA74A6E76 %FFFFFFA06EA8FFFFA74AA1FD12FF7DA8FFA16EA7FFFF60AFFFFF7C756EA1 %FFFF7D27F8FD05FF6EA1FD06FFCA7576FD20FF7674A8FD08FFA06E75A7FF %FFFFCA996E756EFFFFFF6E7575FFFFFFCA75A1FFFFCF6EA7FD0CFFA87D27 %2752FFFF27F827F87DFD08FFA8A15252F8F827A8FD04FFA775CAFD06FFA0 %6FFD20FF7575A8FD08FFA84A6EA1FD04FFA74A756EA0FFFF756E6EFD04FF %A075FFFFFF6EA0FD0AFFA87DF8F8F82752A8FFFFA9A82027F8F8275252A8 %FF7DF8F8F827277DA8FD06FFA075FD06FFCA6EA8FD1FFF7575CAFD09FF75 %6E75FD05FFA76E6EA0FFFFA7756EA7FD04FF6FC9FFFF7599FD09FFA827F8 %F852A8FD07FF75A0FFFF7D5227277DFFA87D7DFD0AFFCA6ECAFD06FF75A0 %FD1FFF7575A8FD09FFA8754AA7FD06FFA1A8FFFFFF754A9AFD05FF7675CA %756EA8FD07FFA8FF52277DFD08FFCA6E75A8FD07FFA8FD0CFFA84AA0FD06 %FF7674A8FD1EFFA075CAFD0AFFCA6E75CAFD0AFFCA6E75A7FD05FFA89A6E %A0CAFD06FF7DF87DFD0BFFA76EA1FD15FFA16EA1FD06FFA16EA7FD1EFFA7 %6EA1FD0BFF7C6E75FD0BFF766E75FD0FFF52F8F87DA8FD09FFA8754AA8FD %13FFCF7C6E6ECAFD06FFA16EA1FD1EFFCF75A0FD0CFFA04AA0FD0AFFA875 %6EA1FD0DFF7DF8F87DFD0BFFA07575FD12FFCFA0756E9AA7FD07FFA76EA0 %FD1FFFA06EFD0DFF756E76FD0AFFA04A75A8FD0BFF52F8F87DFD08FFA8A7 %7C9A6E75A1FD0FFFA7A775754A75A0CAFD08FFA06E7CFD20FF6ECAFD0DFF %A06EA1FD09FFCF6E75A0FD0BFFA8F852FD07FFCAA175756FA0A0CAFD0DFF %CAA09A6F7575A0A1FD0BFF7575A7FD20FFA175FD0EFF766E75FD09FF756E %6EFD09FFA852A8FD09FFA76E75A7A8FD0DFFA8A775756E99A0A7A8FD0BFF %A8A06E75A8FD21FFA076FD0EFFA76E75A8FD06FFCA75756ECAFD08FF7DF8 %27FD09FFA875A0FD0EFFCA76756EA0A7FD0DFFA8A7757575CAFD23FF75A0 %FD0EFFA8754A75A0A8A1A176756E6E4AA7FD08FF52F852FD0AFF9A75FD0D %FFA8754AA0A7FD09FFA8CAA1A075756E756EA0A7FD25FFC96EFD0FFFA775 %6E746E746E746E7575CFFD08FF7DF852FD0BFF75A1FD0CFFA06E76FD07FF %A8A776996E99759975A1A1CACAFD29FF7575FD0FFFA8A075756E7575A1A7 %FD09FF7DF852FD0CFFA74AA8FD0AFF756EA1FD06FFA7756E6EA0A1A8A8FD %30FFCA996EA7FD1EFF7DF852FD0EFFA16EA1FD08FFA16EA8FD06FF9A6EA1 %CAFD37FFA14A75A8FD1BFF7DF852FD10FFA76E75A0FD05FFA875A0FD06FF %756ECFFD3BFF7599FD0EFFCAFD0DFF7DFD13FFA1756EA7FFFFFFCA75FD06 %FFCA6EFD3EFF6EA1FD08FFCAA16F756E756E75A1FD06FFA8277DFD17FFA1 %4AA0FFFF7C99FD06FF75A0FD0FFFCFA1A0759A75A0A7FD27FF75A0FD07FF %A07575A1A8FFCFCAA099A7FFA8A85227F8F8A8FD19FF6EA1FFA175FD06FF %A076FD0CFFCFA0756E9975A0A0A075756EA1CAFD22FFA8756EFD07FF756E %A1FFFFFFA8A8FFFF2020F8F8F827527DF8A8FD14FFA17575CAFFA16EFFA1 %75FD06FFA075CFFD09FFA1754A75A1CAFD06FFA8A76F6E75A1A8FFA8A7A8 %FD1AFFCA9975FD07FFA199CFFF7D27F8F827FFFF52267D7DFD04FF2752FD %13FFCA756E7575FFA199FFFF6ECAFD06FF75A0CAFD05FFA7A06E75A0FD0D %FFCAA0FD04756ECAFD1AFFA16EFD08FFA0512EF8F8277DA8FFFFFF6EA1FD %06FF52F8FD13FFCA6E746E75FFA076FFFFA06FFD06FFCF757575A0759A6E %7575CAFD12FFA8CAA8FD1CFFA09AFD07FFA8F8F827A8FD06FF7575FD08FF %F87DFFFFA09A76CFFD0DFFA16E75A1FF75A0FFFFCF7575FD07FFA8A1A0A0 %A0CAFD0AFFCFFD29FFCA6ECAFD06FF7D204ACAFD04FFA8A04A75A8FD08FF %52F8A76E757C9A75FD0EFFCFA8FFFFA74AA7FFFFA86E75FD15FFA175A8FD %16FFA8A87D7D2752272727FD0AFFA075FD04FF7DA8FFFFA06E75759A7575 %75CAFD0AFFA8F84AC9FFFFA775FD13FFA16EA0FFFFCA9975FD13FFA875A0 %FD16FFF8F8F82727FD0552FFFF52A8FD07FF7575FF84F8F8FD05FFA1A7A1 %CAA8FD0CFF9A4ACAFFFFFFA075FD14FFCA6E99FFFFCA996EFD11FF7C6E76 %FD17FFA8A8FD09FFA827F852FD07FF7526F82EFD16FF9A6EFFFFA8FFFF6E %CAFD15FFCA6EA1FFFFFF9A75FD0DFFA7A06E75A1FD25FF7DF8F87DFD04FF %7DF8F844CAFFFFFFCFA1A0FD0575A0A1FD08FF756EFFFFFFF852A775FD14 %FFA8A06E754ACAFFFFA8757CFD09FFA7A06E6E75A7FD29FF52F827FFFF52 %F87DFFC96E7575996E7575A1A1A7A1A176756EA1FD05FFA06EFD05FFF827 %6FC9FD13FFA075FFA87576FFFFFFA175FD07FFCA757575CACFFD2CFFA827 %F827F8A8FD04FFA1A1A1CACFFD07FFCFA76E75A8FFFFA76ECAFD05FFA8F8 %F84A9975FFFFFFA7CAFD0CFFA1CAFFFFA075A8FFFFCF6ECAFD05FFA06EA1 %A8FD31FFA8F87DFFFFA8FD11FF7674A8FF6FA1FD08FF27F84BCAFFFFFFA0 %6ECAFD10FF6EC9FFFFA875CAFD04FFA16EFD38FF52F8F87DA8FD0FFF7675 %A175A8FD05FFA8FFFFFF52F827FD04FFA06FFD10FF757CFFFFA76EFD05FF %6EA7FD39FFA852F8F852FD0FFF75756ECAFFFFCAA175756FA0CAFFA82727 %FFFFFFCA75CAFD09FFA8A0FD04FF75A1FFFF7CA0FD04FFCA75A8FD3CFF7D %27F852FD0EFF6E756E7575756FA7A8A8756EA1FFA8A8FFFFFFCA6EFD0AFF %CA6EA7A89A7575A0FFCF75A1FD05FF6ECAFD19FFA87D7D527D52A8FD1EFF %7DF8F8A8FD0CFFA075FFA1A7A8FD05FFA075A8FFFFFF27A9A199FD0BFFA1 %6E7575CA6ECAFFCA6EFD06FF75A7FD15FFA87D2727F8F8F827F8277DFF7D %FD1EFF27A8FD0CFFA76EA8FD09FFA8FD04FF28F87576FD0DFFA8FF7699FF %FF759AFFFFA8FFFFFF6EA7FD12FFA8A8FFA8F82752A8A8FD06FF7DFD21FF %27A8FD0AFF75A1FFFFFFA176CAFD06FFA8CAA1F820FD0FFF7DF827FFFF99 %A1FF757575A07675A8FD11FF52F827FD2EFFA852F852FD09FFA76EFFFFA1 %4A6E4ACAFD05FFA76E9951F8A1FD0DFF7DF8F8F8A8FF75756E9AA8CA7CA0 %A1FD0FFFA852F8F852FD31FFA8F827A8FD08FF75A1FF75756E6EA1FD08FF %C9F8266EA7FD0AFF7DF8F8F8A8FFFFCFA0CAFD14FF7DF8F87DFD35FFF8F8 %7DFD07FFA16EA8CA4A6E6ECFFD07FFA06E51F8A14AA0FD08FFA826F8F8A8 %FD18FFA827F852FD38FF27F87DFD07FFA075A8FFA7FD08FFA06EFFA9F8A8 %FF6EA0FD06FFA175757D7DFD19FFF827A8FD3AFF52F852FD07FF7C6E76FD %07FFA89A6ECAFFFFF852FFFF756E7CCAA7A16E75A0FD19FFA8A8FFA8A8FD %3CFF527DFD08FFA8996EA0A1CAA8CAA07575FD04FF5252FFFFFFA1996E75 %75A1CAFD19FF52F852FD42FFA8FD08FFCA76756E756E75A0FD0DFFA8FD1A %FFA8F8F852FD42FFA8F852FD0BFFCAFD2AFF7DF827A8FD44FF7DF827FD13 %FF7D27FD1EFFA827F87DFD47FF7DF827FD07FFA1FD0AFF7C20FD1DFF7DF8 %F8A8FD49FFA8F8F8FD06FF9A6ECFFD08FF766E6FCFFD0EFFA175A1FD0AFF %5252FD4CFFA827F8A8FD05FFA06ECFFD07FF7D27A075FD0FFFA07475FD06 %FF7D52FD51FF52F852FD05FFA06ECAFD06FF5227A175FD10FFA8754AA7FF %FFA827F87DFD52FFA8A8FFA8FD04FFA06ECAFFA0A1FFFF5227A075FD12FF %A76EA752F827FD57FF27F87DFFFFFFA06E9A4ACAFFFF275275A7FD13FF51 %F8057DFD58FFA87DF8F852FFFFCA75CAFFFFFF274B99FD12FF7DF8F84BCA %FD5CFF52F8F852A8FD04FF7DF8A16ECAFD0EFFA8FFFF5252CA6ECAFD5EFF %7DF8F827FFFFFFA87DFFA06EA0A1CAFD09FF7D27F87DFFFFFFCA75CAFD60 %FF7D52A8FF527DA8FFFFC975996E99A8FFFFFF7D52F8F8F8527DFD04FFA0 %75FD65FF7DF8F8F8272E7D52524ACAFF27F8F8057DA8FD06FFCA6ECAFD67 %FFA87D52522727F84BCAFF52A8A9FFCAC9A0A0A0CAFFFFA1A0FD6EFF9AA0 %FD04FFCA757575A0A07575FD71FFCA6ECFFFFFA7754AA1A8FD76FFA175FF %CF7674A0FD7AFF6E756EA0A8FD7CFFA7FDB7FFFF %%EndData endstream endobj 223 0 obj <>stream %AI12_CompressedDataxg(|??`hrɹ9@0fo%ـmh=g^`J**Jc=1 )ہkKOy! YSX. 25aD!0`[v)z?߬C4 7%cȬ1oaI_iH W97QPiZ:]8x+&>w Ln|=Mnxqh^y 9p[:<^4 9|.ތ+j}ӛߧ6 iRkM'Z.7$hI|I"ȃp!$ k<Ηq5}|=@P;{@Aa~jQ4@H%}>t& g>A7[@ŞiG8}ܦV% 6'tx]p5s.; jAB|pG0Fu!ǜWHSݬ)> bf]G㒢;ه~ 2lq]/ۢMS&~<X$?)8Mu x''x5n^G}u̬댛e1@p :=סS?=_f?h,?ˀ>pXQiA8Yأ8e hO/_3e~5_UY`P_9n L`81 @i0h֬4S. ռ&`1^0fP͖ k^Izx}IIZa'$ 7U`lKL#U /j=ޟ3_g~TjY3@/*{wumt>gfi>L2}` 97C}r>l>/H]U\4BTtFv ω29ėlֿhː&9N Ӵuj !MkFnϰe; vEw3h\R@S9KͿ&i;pӠqa0?vmB*uݽ@nIGnЛu zjz>I Zn}4cS!KR_}>h~x ~zI{4S܄ּ%Mk]+O?TS|[J5'Р ǰD/AׄCg:bBZ&R g@\@HC~Pz AG2h ^L1uTj]I!=~KgH8;S#1qܷ4p.9np@|;=hp%̇k!:?zzh4?As||]^t1Ls?"4o$ xq*A_>.$8VC`Xٗ21'ƫKpaԇ-ܬDx RNYޜqM,2Kg29 vq&#7+g=*fLI<kL^-נ<xAM`n4/4f[Hu~ Rca' = >xn %f۱w\<)T`(U?0g"2QjOM ?>>x]||1:bn]a7q%3UUû~&͑Q(Lj)?HzIެ)5TZnF jL bQ.'fgZԡFb :09 16/Bb{0<o ,ړ0T_jn@n{hE1t&xwgtc6bjpZp?LO{. f>.PV;d=aI%g'pCc n||G-'h~ 8{Z7M1ӓf?F]Π7UGi6VSD BP%W%  6!O 4oIO?5Sz>b4.Fm4ufZvyntSŶ>(6UΜLmMB.29Ԟ4mJ4 5WKEsHhB0Aes/~bt8 i@pбq+a{<(qݿ2aC}y6>;[r</do7a;r9?\۹bsJ_dXIFut@)W,vD(29vSnkY=oY2}bj[rt ɦfC&=u8T\~8s\K*usI }U\43\&܋49 -kJGj[tr(I*|4ӛ|I?\k r_Kc^jHU*XCfP9,d{?(z * o-6o`mL8s,,L]Qܗ̓ h E☘*d^˓Fg˕ӓzp}~ŞXR!)nUQCHj=8KzCfj dʁ3=.p!BTZVDJ LcH^[7ȉ.1jŶj1x|q L[Nwð  |s`B.G[?~~MOtr^Ο)2wk :9r-#}g. Y~1Z/tfd]U aRm<6V$>pǥԘTQh}鉊ܞ/$"hKO6kYq*u j)7{`BW͚W*/襵%1B%hw[Q[ cAKA36q1}mBS1?B!29'bvi(4*P+ B;A@/y[U<):2훹Igw9DD4N3=A#ͯvK 1υÌmCS{np^S:EOso# D¥I49Ckd0o,ר1LيnC͞ӹ,4]c,f3Ssұu.bKeȟ͸*=p|IOms n+l7lV; aOU@pa!i̺B202n!UGud<՗-`JiEyPi {r= 7Dh!D`Lv~UO`&d0/Az] R)E|EΨsN4 10Fb^&*x6.u!粣W+ZA'D/kbt,YLƨ`g0^I]EOXOssYMm¼1$FBdɄ]êhZrs:!4 -cb FocF4',"g M_0'f c ?nVDs2-D[Zg/2s@DZX }$;8-0^Ǭ[c)(n;o`Mhtr6^<M`SmI^v.J4׭Nu(2ֲƃh/%jdY6յ:㏕cãN3*6E- kl:En_h&,@9ήC޵+qr- 4ӣJIcrR1vi?%IW zKFcD-y?>gxOk!Yb~s_&m-{PYh8v }fLͬ)< 'UYwd/2iyn2gy)c%t<.7t5J)05=ӹwxy*B %aXN˜7ғ5v623RD,T AF#g=.Y]*pbdy_U@]hT[l<>=ƀa/ۋ'- N>pnR/,d`b}rY!'(vypjCRES(ѨY][G%w' m<\zsMv;X M(^f#`XO}f%p\ޥ6<90x+ϲZev3ܴ}Z\h*_ɸ˗09=x :k/ٴe, UD8p˜e,fpnb1hPgqYm6NDn3O;IsF!Ilr(+N_^]U-.4#UOvtr77ᨰ A=vxu7Oi|P=܀@cclɇ=?[$iH.PD6Wv7OxҕD]3nG+x('}/\꾜FI#g0j֓Wj: (5_WD:|sbzhH#inbAmBm }FdHfroṕ -0BWs5Is0i14h e^ ge6Fɧ_\eȘmm97Hd 9 z+0$.Rf>mOkw_Hl 5nA%C1yv/QXYBh*pO27P?a #x"X5k~wb-U*hxЯ-ïYFZ$X\>X#rE1T@j."NƆs5ZlDS{WPm/MĒ>֛.K(Z̀:&t.J؎l? įYb#ہ/]`z| p-&3i+ltgMU"8zo=@μRӸ;,J:.vL[TG8ʐA/*^lEcJv2e !}I+.UN̓( XCǺjk%dBp8_4}^0i|(g{>A[Ȇ+iGm0ogd-fXG=4Kg.+vc*^\ٰM{9ʷIT3Xp;aJO;Mowy7ͫ;BÍZ=:v\ K  tq~WAi@W2.dh(3 چ^.h+ϜƱI^o`<4+T0ȼeB0oYqK%=$pCqQ)ĩ#L¸qHM,O-0;O%BS`(`'E*bזŌ[¥uc}D2:s"¹x<\D,Nf˨wߵ>p*J%9xz| tś)=1luC'ͼo[w¹qn`$xȄhג30z-7iӥbo"}ed $ɗ j_g=7RmV؜:q,r@+3l'^Sa.dg!v_g͈,vZ2h:`L>#l߆*MvDE2ww|L sH5iB]E&vFMrH1Dyg6}L0Ctv6'ም5[ @ص%,І65gaMfJO9mֱfI47o_lg^pkH1RzI Z]B^@!b;K1=|` 2ybs{V`]%Q!UWt\uc'X[VOwhEls4n@"/CƐӲ:=($f%2ѱ'J`J13:jZmpv`놄9jg6UβFu `%6ҽ}"٭\Hs6d9}6:Mz}vVzyxΌ1JKxu?8hEx1?ۏv?8N;$w ){]h޴q2 qcY!W^R!mpe0{9[+Dj}ằZF4l_#R +-p: Col?.eKY6mnv’.Nrwax} >Vu׋ M(nMnGɈuq߀53Z)!#_o  sǫY=h7% ɏ;j>)zMؗ^H.14{M9fAvZKdY-^vZ.7RK۫_(?$$:n<4j=I**>%jqJBsmK]sBМ8&ŝtnf>>Qbtt=a~8kB\邻J?./_m®bӜ?hv&Z̏4N im"/<)4/#I6Z/WADsbHf:1"1l,;m< 7Cal$[|_ q1QzM|X`7Qfry'}{phEI̸̚&A#Sf˗;Tb‰eg/a:3at ӎ?~SRQ}uX¾^66[mMƱ26ېla*65Gۍ^wkyɊ֐[GkMZ^+dX&*+F Ѥ _ژhN5[lzikG\6Ρ־WbZ@;\ԓpwnnV=;>E? <-_^G:mV^Otχ.]-tՕ_Q]ѵvݻl[VWѭnoHLɳі>[3.<{95=J[9&yw\z}=9g2|TJ=z,|ez[_zJCªT 1}Ycf}ߵ7}?sg]o'q1nkÆP4 $k%Cm6 ᣑH|1L)a~uvѰp#eL6c_ܴ5q{WxX_44Ƣ1 |A_ LlT2^ /rde2LdIk6&ٙr9f~.7z5oykC42;aY|,,y3ei6}C-%eY{fi5=+XyК'֪-Vh+?z64%mlj7>;F{WwFm5\ڵnǬvȞ'W{i9ػ/b_83q;ϊg.qWGyvNޱ,Nmqڛ͡30ycſtnGwv:w7-/ŋNӯkLe f'mgD+DgwC"!zץ.Whu6꿻, ~׮3)w]'^9)Ok z Uy<<޳w?օ \*v5wdޝY,_IgnҴct&D_zwcLo,*J5緁R* | jqWE 62Hz+?,F(b@|Bli&\ZnžT.Np;+ǯEc@ܤF⩟y>"dM>T4fUr6ϥulXd)|kxSXԶ _-;k9g8 796ٍQ Mcb&gon\U;Ŗ2tZ9gs6H2dرN|/~VfJwb-@N:2o|f5ߦO:ާ+GhV~hBU\$&{g=ؗ9:¾Rpa=<NUmKCt1dfopNw0" /%}%?y,p^v @G=9ڔ g#fSĽT=psFqS]Zt`3wc՝l7:Z鏯>*] N-dVڷUlV+[\MA:c$#'æwnfXdkm5nXckҘAqGK,ެyWٗdOQha֊rqXYco;tNkn}i' ~iI0Q2=c.kafw>ٝFXЅYc?m6Ͼ'c=ZxŅ;&ߋ ޵ޥw̝ ]N< [x&lj1^ c|Ց|xOdh":", E~mphhfuhqo6{1|̇ք'_ 4X0s@FzƴӦ} zkn @{2PC 7wax@Ĩ]&,q8Zc9p ճiRXto0k GE;}ԥuˡCk$6X(Yj < >Q "4&}5QOyŰ(/g)2\wfwW=\ͫqb֍FAN:g+a?t/ݭCk<G.uuRXGt?@cf,X`>^Mf_\kCn /;UQ, pܩ-5X}:7}>v j}F{Ԭ#5t%jcv^3_,oh!bpiBBb}c{ Vi~$ZQ@kmI%4%nkӊʻ8֒_f_0Q¼p 熏U񵿥.c-u MKk[)t ahXrRV1Ӵ|WmDX7ԋi*giqpvۡQJU:td VR{]"#;CĶ+iϼ(-<ͻźZXOah\wM9T(]%ğz"k5mH5Y#U4e4YH4n[}dVq2_T$:4B΃1mߑD7mızߤjc+TkgxlH`Fsm^!~`ك|[`l0.X}Ⱥ?cuY9|š[(5qzZ1}&XI`'+ız?7GK+r-$Bb| GCLIc ToX"Ia)6V'C퐰aU|յ3יvPn0AǏ'qyg]d3 CFΜ{Yq!l?a 6.ohrbwa;-V+ WG 5Cܣ4. a M:uՠgo Ɵi"ؘ ;fNc=V0b s -_о<-ĭWP#inŗB͕zmy!f$8w'ěv |(T+BWW6i GC:N 8@І{IhJv*F-QF 2:8XœhLf]cmSĬ?l5!-iB}bN߬}')ǧ0k 12,]M"@+=y)Sk`\o{)M%jMM "Y|Mz9|6UUۢ bJM}B;+,wlIz>eU(@ųSӨ`0Hc Fc\ymbܽ=h -ÜL#@L46vb;wJ%rͬw+9[W\gN^^qoQ i}=-Yb60~wH3dr9 4p7ȌFRB0UQғ!%y6pMIz0 u!Qb#=Q(<=u< 31|˦_cHS"P[>tjE j# ESP/ GxFGs3ᕬ!z6EgS@X?p?sqNuR xD$GAH]bBPP^7.v+qRkž*r?ZU٠ê( ^rF0r )a3*_Q;@+MwsZx xA".։$fK~ )ki_ötS"l!ÆB+YJ/^% | z 9Y"P댜M])~3GY}ᇷ/w7W(5P&FE@_$4\<Ľa|8sfN2 MaGײ ]Ewy]9Pg,b|k߽=63)Rurx1\Y o!m(o`pdI7JDIAh7I7  wKoyN4E@jĉ<&:F(w)#}cx*&2iV=%vq^ ~Qy%"0g f2TetFJ{g=aMY^bJB6BL\@}+t0" I_@{ݛU@943S$# E9rFsV~$e>8o .jZbQ*r +>Dct6b/*w''HGxvʀI(ń ߮ecȄ1dɐH4L|qԬKj< ]VDM<+Sj 4E׹d"?I^>|ŕuv؟b۰\M6-''[cL̍s%5%HPt7548! o3aEƏLRIm6啛 4^!Jk DG.CZh~rb%t4YNs#q or5;n/V]r֍l9϶c hSg{-Uк{cpO9|-ӥtr>VZ>n]||O{k||O{k/瓯c||Q'_ǎr>~L9~pS2v*&@ +^[lZBZ 40v o_)LR թ:ʦ@SUŊ"># tA|*'N~L]2} l[n;}xGA>u=9M>$p\|☺nQq {7N[޸Cg1O'lTzۣ7qU $2NeI(Twoù9A%UDouX+(QUtl n*Mp SBi*C$stKd0uFWQ̪س%KW2Xj2B &Cc,SP⧦Uv32q:5i'IM -@`7i&)o]2Oଢ଼]'vI*W] $8Ϝܴkt׋5Dg ^(oj>1of) ̖n‹[6fi'|S6Ma;~V9E7 nO)zLE՝8Ҽyýb_vg>7~\].D.էCYO~};+D~P.dkě+O~ޙ%jJ˕Mq'=t~2Xsxou kE$VKHQ&[ ֎OXUdK5֢ q6G}1Q/"L+sV6n놕0?_UqsV2N%`+.ztFtq5g(Lzbӧ6lW7j)?ʷrev^"lwz#|R O5#|; UXQKx'nX7RE*nTVS>1֘>1/A(" ~daXUZ7-c -KOHG)fB>OlGb>2U}2+-}taL# Ԟuga'\'zWarb"P-wA}bU}Gy1Qҏ*la|χ]_͵ʯ tڙ7,=%)>96tAo Ć99I^ݺGdB!SZ+T;u⧀|D+E!09q[b"WHx0U eytgEͤկxܕ{Jw:evKVէVȐwt2.ub0f&{ e%,g^Wy߉(*kqD]q4ԗJI\wk!X/u}Ձb" 5ɤ\xlK9Mqw5QI>pX'xkv7o *If_%(^'<ا@.I Rǯ%ퟘM4, i>vӆꫮ/K]oIBcRs,+5IAPx tN~?nnB>$QjtUC)F/@Tݏ,b$hU$Z#j)9.s`a*嗅1^yA o@wsq/F1.PGoc @@@2Hł"RBqf1ɮ#M7_'½w}+T޿.yߍ??ƿq]lnOM?AXW>= NP_KhE]3CVKho_^<iNP,<_<RRBEPyvq=oQu ϲR]g2F ـˬ ^ZJGXNSh39B^byh SE!r{hy?PR!C&cRb@dknƭ B1$x./nSTPxy/SvϩZD{ee+Wr7JerU' _*{eO,㘷ENByOӮb wXj؆u(3tDc?j=/XCӺBAʼ YGM5g\M'rZC 7BI%@#)c9f&[k%UvϫXyfN=8.Ix6W%t͕/Õ)fi9wW2g@$!lJ;1L +@"{6oj ?N91^i?2_]lueq%G<-'ԕ$ !UɒD#H#[Sj29`iTzshɏZ!nPQOj+usA]^29OLῺ"ޫ#IJ)O̱9uv 8㜼IwCA[(P'OTz b)]g'} Sg@]EIoK̡`WR|յ\͡d"@+  d [rDS&h:@d*㮪ſ,ITk6ro~Q_O"Pt=>K#$㶋n(I{Wb %ʪF*ᦒD ^# [KUUn\RGkqB^VujwYRA^BL]VȆT{1 ae`r3`(kG]tGT+m4{/<鄜ˆ0MF(RWm!/0AEik.P}¹ԮS,I(ArҀJt9;b\Ƙ6]'iLe5`0j$.h|3'l \#h˅/&Sfx¼w KۏW{ׄ:f7~m~3S錓mXw8F 65|-_ No@x[akAgդ[OP#SQ"vm=o4-v[?Scs}#*NعA%=,p-c仇sA&{b()"$ZwwozX=7kmd)g'"UW{]7Ŵ0X''Bgw(/#Uߦ%JE]l׀#Ufw 8=NfoEVa-p>x Uec%wE]V(D K49$pe3J|jm t;΅ց^w?W79+&eo ‰qgeM2W;aaypȌ jS'J= A1 pϩY~yJ~t^N `M5&Mĝs)ۥ=u4Yn@:9M}ut0 XV`SI$X\ ZT[9aDچt1Ap-9ngpwۀ> |/۹D>dl$OF\➴q9xyb q#NQ{'6ni5 y9бLNzC'L6!l5q?|Éa`Fs3$9bk>ӧ;ٚQ١GGsΎz̨_T@tqurw]:UY w݁c;<ݥN|wܯOj'cK˰݃K&Fڟ:=VA?֙ߓ#!+[ (ZQFdގ\,"iolNɈBcM 5ȸfu4z[_vf9F¸V7{M uŽ Q&F*bugj{Sj :3`hr`}e7ABh^Ȇpr-Nޏ<4YDpBWNʛLJڭ,kr/ +!{]1,$i/fV_Nc*hܕ7orlԪ_ܐA3;+<be_c;bYS;#Yq99ͼPC`HycN,ïn_q MNΌlzri{+ w͊~J6j56Wk94F檇 OͷmmQ})={|}z= L*RVcsW<]SC3 _S*Frhl:}t5vCF%JpfڧfŦNMަ*g `S7!؞]*םX\UKAտWǓˏ3 ۻʁfJdvM[jߓy}8h&DًU* /@$GwUm?ϩaue BhcWp |y#( I3iQF4x<έj9-oN,?`Fٚ%_I( ?qlo󦷯JKӋ0SwI-cz0md4yxsRO;E6j TQ2H١u'+Й?Ta8/}yѭu>񬽵u*nxtFtol7`?laTW2O )vzIj41sF{ɫڤuN~DޮO. E_wx&k`=joߤ#O.5MZSǴ^VJ/Sh>oMlUN܄1q&~ՅփU=<pqnB~;pjV2n,f5MȦ =KdfwlZ/'%=K9jh+=K9ug*mI8;~sP՝rYVjٜގut{y8:_~eZ欷' 6 {I~NԷzIsFdJ3M*P~hgq~$MDkߙh7vA42}c6G"*5.'C?D5siלO=ӲCVZWL;utѫCeəMy-#1 KS]?,on{ܑhD[ryiK_ݏgvFT^ma[1K:K>G'i={a޿9_}(yf˳2Q/OSkJaOSo>M8%wn}͉e`=% c֛ |8_0UxKz)GZ- ̗_) {o-uf1@U`Oc B>'Ǐ?P >RZ968TA"}ɂ<UrⓌD9ܶwOZ#(!SFGl&/VN>ѯ87㥝O E==8 lc87~`$Tϱ]b0\aMo$j/h67 ~d E?{Ǭje&T^78|8L`5n3rތ4yȄ `qкkCKRSdi|4g>ȭjo0A?̇Ȧ'0M0rxEBIsaJ:Z@3_iXN3UVQնu{h2DKv%|#?ֽ7W0RccRG3/$~au0#+5ͮ&!V^|ZA7?tF6yz:4Vq<~oQO?kO$qSVI+.ĽVJ(`Xi$!iE@>7@KZQ 91N?| f'fFՁX@3_͈3bQ" ҫ(:ϵىj/ݕ<'QѯZ,Ĕ99zhI2O7(Zѳ[|@*<:OxtNKOq"gRA5TGWthTMMVǥkm.%/ϣ//ezw0x~‡jYD%T[(+žcgEP/2 apTr,*D@4p籖_tUz/9GWERs'"lZ%j>;.M8V@/n.x Ko;]}ӚɬTqu*5pg앲wtTFx >&y {Iԧ~찐d\:~Kc>rQ;;1OT8\~ɬI[ScO~YpuSn$sӳF_yb4;mposxyrei#N,]5+$\WaGEVeSgfFjC9Jo*y&k Kx4tq!;MIB|4%Wƙt ݒ”0&kzv WL^qsJ^Up#-@ҏ]# KcVlw YkN,SLg,e6;㜠dB_Pe~9Fa`^O+?\G0- 54ˌpC}3P+΅ Xђŕ`dxn2>)_5V׷|? [O{<]?\=e&2T..ĂIɛRv U~ wo7ӺpLקq?zonop۠'7p_?Cnw#S"s>ONkOގIKϧuƯOX}{8fm<}Ag?|6 lM!llGLk06?J>Z~{gClF=!G_0bxV>زZ'U%@oE-(>Y`nxMZwȧS7%. |X)xI>׺H"%]29C2d었I>ڱ~ Н$*f&J}p/~POh eJ Eaa- x?c/uU_W⾮cb^r蠂/m tE1irhub᾿:>1T۱\uk{O] ͇:{36k'*bZ3vh߉d-pq֎v| w)/ɕ?3+Yk콇Qw´Hz͸{'Hn:]JI,iJ͟C'ZvVP{;nuyD-?>=fbr2slO 4m6YJHooڇ1*3(}EE4m5r(e{A$ ǒ ][1Ǽ$/Sc. :+a8pZ}!PT,Y$")R#YVMjr!AVKH)Pthan`P(6g;(@}R*N>^(@lRI:QLў\E(.LC08)&KjORӠ[@99@B4od_kؓ`*M^@MA ȇ=J5M Jf=R"j96& %*G)FE`uG¶Ql\BNH\46)e([8ȃdQ!pPyalȊ8Q( HGf"LW0^蔀hL$Mf#CM 5$q_26Q8 $TP@>[W[ ?<ScD1R&8*@fPЊJ3VJi,mQo *2R`@4@qiʵR$H9(JAf[Ԣ'P9z엏;&䍃qt#2Luy4{!ܮGbvD6vC$*۱qm]yQR@lט `&@*fD%D_:r`@I`MX36dpbqp@qqVPaP5sј큗Y|L7U0rPЌ"eZbMFB68@}h)A˹by8U1 ;=R8 ĕa)`)kZ QE/%}`GorIҧ!7M.Dk-8cڨ6J?%R ='0JFb22MThQG%ELX ]GxEMjD+@ P'BWD'{VHM=cUv5t3(=&4^O'`NFVy6rDLg'?Al+)o2B}␊|IKAƎZs\'< q^.;e;;v&KvҰ BE2Cl @ڡ" [ 7/<Өw9T3q "~f6Q*"+[14'|a)1Y϶VB &Z4L`xD XH P>J[pU<0g$'J2$lib+C0˘ W#0%3Y@~3=9Dw]%:1zYnt]y(}X\@\@tfU` VmЖzmJ J<)׬znC0(m7~=G$(G6˞4 > FOn DE4a0mA$Is3#ILyd0a`\J QTws`N 3ٔoVlfuU 2ōu&;7WxXz;×7kP&IA[)OqAJJضI˄ʉ8!ŵIU6/JPąH6a{ $90G94Pf[=C 0 %]rȓdkmXN f"@ C͎=-] cbÆb4r0l0U "µj#L40{>f ?200c,簭 B] 7DhTQ(Ơ!9E:r0PL, פ5GP>51|xO!w| L=K6ѹ "!tqwR8 ,Zr9_F(YB}4qHCc3*-{&ߌYH%Vr̊mBS6sc E@9Tv O hLj=gsBy3RB45m`L*5 'A@L;ƹ'OrCoR)oW(&ݾ;vd%s W PptXėd"d$^M%ð A]€Pz(ĭ0?ˆX*p4*^b@}A\VdqQ_1gӖOAX DN큔^!GK}܌'3?21n͛T cæWWBw_cNkHKSJ0.&9A;qoN& SGD$0P(ώ0q"\.pI.~pSix#,F'@^BxhsM1p&P:܄pBz Al/f P"[!jK{(P#ࢷ&h"aQ0 (b$1Ö!mΞ1 &avZИr0OpNlh@3@\ӬO91]opQ8\1#A'9p3xE}q`R j"y<>Nj8-< )yʸ|^s\*)!Me b^Q0\uYca<Űw0isf$<)ZS #WQJP~ǵ],CI~Em1!~4&vLfh:V{-`tDTL6D4*:yϵcJ@^5I)"ie%/ %ZT񶛸ckZ7sFS;"9Iy\fe )azɷ4fɚj$A 1fTiD{N"u+z-zTSÑ2p\IAL:JmTi%y\,8+i]d, Ѫ`AؼJ{CqɓvRܡͲI y@p*NC9sZxI xhxjqv{'_*s5\=WS*z`pu(By endstream endobj 5 0 obj <> endobj 21 0 obj <> endobj 38 0 obj <> endobj 55 0 obj <> endobj 72 0 obj <> endobj 89 0 obj <> endobj 106 0 obj <> endobj 123 0 obj <> endobj 140 0 obj <> endobj 157 0 obj <> endobj 174 0 obj <> endobj 191 0 obj <> endobj 200 0 obj [/View/Design] endobj 201 0 obj <>>> endobj 183 0 obj [/View/Design] endobj 184 0 obj <>>> endobj 166 0 obj [/View/Design] endobj 167 0 obj <>>> endobj 149 0 obj [/View/Design] endobj 150 0 obj <>>> endobj 132 0 obj [/View/Design] endobj 133 0 obj <>>> endobj 115 0 obj [/View/Design] endobj 116 0 obj <>>> endobj 98 0 obj [/View/Design] endobj 99 0 obj <>>> endobj 81 0 obj [/View/Design] endobj 82 0 obj <>>> endobj 64 0 obj [/View/Design] endobj 65 0 obj <>>> endobj 47 0 obj [/View/Design] endobj 48 0 obj <>>> endobj 30 0 obj [/View/Design] endobj 31 0 obj <>>> endobj 12 0 obj [/View/Design] endobj 13 0 obj <>>> endobj 209 0 obj [208 0 R] endobj 224 0 obj <> endobj xref 0 225 0000000004 65535 f 0000000016 00000 n 0000000328 00000 n 0000029421 00000 n 0000000006 00000 f 0000080220 00000 n 0000000008 00000 f 0000029472 00000 n 0000000009 00000 f 0000000010 00000 f 0000000011 00000 f 0000000014 00000 f 0000082377 00000 n 0000082408 00000 n 0000000015 00000 f 0000000016 00000 f 0000000017 00000 f 0000000018 00000 f 0000000019 00000 f 0000000020 00000 f 0000000022 00000 f 0000080290 00000 n 0000000023 00000 f 0000000024 00000 f 0000000025 00000 f 0000000026 00000 f 0000000027 00000 f 0000000028 00000 f 0000000029 00000 f 0000000032 00000 f 0000082261 00000 n 0000082292 00000 n 0000000033 00000 f 0000000034 00000 f 0000000035 00000 f 0000000036 00000 f 0000000037 00000 f 0000000039 00000 f 0000080361 00000 n 0000000040 00000 f 0000000041 00000 f 0000000042 00000 f 0000000043 00000 f 0000000044 00000 f 0000000045 00000 f 0000000046 00000 f 0000000049 00000 f 0000082145 00000 n 0000082176 00000 n 0000000050 00000 f 0000000051 00000 f 0000000052 00000 f 0000000053 00000 f 0000000054 00000 f 0000000056 00000 f 0000080432 00000 n 0000000057 00000 f 0000000058 00000 f 0000000059 00000 f 0000000060 00000 f 0000000061 00000 f 0000000062 00000 f 0000000063 00000 f 0000000066 00000 f 0000082029 00000 n 0000082060 00000 n 0000000067 00000 f 0000000068 00000 f 0000000069 00000 f 0000000070 00000 f 0000000071 00000 f 0000000073 00000 f 0000080503 00000 n 0000000074 00000 f 0000000075 00000 f 0000000076 00000 f 0000000077 00000 f 0000000078 00000 f 0000000079 00000 f 0000000080 00000 f 0000000083 00000 f 0000081913 00000 n 0000081944 00000 n 0000000084 00000 f 0000000085 00000 f 0000000086 00000 f 0000000087 00000 f 0000000088 00000 f 0000000090 00000 f 0000080574 00000 n 0000000091 00000 f 0000000092 00000 f 0000000093 00000 f 0000000094 00000 f 0000000095 00000 f 0000000096 00000 f 0000000097 00000 f 0000000100 00000 f 0000081797 00000 n 0000081828 00000 n 0000000101 00000 f 0000000102 00000 f 0000000103 00000 f 0000000104 00000 f 0000000105 00000 f 0000000107 00000 f 0000080645 00000 n 0000000108 00000 f 0000000109 00000 f 0000000110 00000 f 0000000111 00000 f 0000000112 00000 f 0000000113 00000 f 0000000114 00000 f 0000000117 00000 f 0000081679 00000 n 0000081711 00000 n 0000000118 00000 f 0000000119 00000 f 0000000120 00000 f 0000000121 00000 f 0000000122 00000 f 0000000124 00000 f 0000080719 00000 n 0000000125 00000 f 0000000126 00000 f 0000000127 00000 f 0000000128 00000 f 0000000129 00000 f 0000000130 00000 f 0000000131 00000 f 0000000134 00000 f 0000081561 00000 n 0000081593 00000 n 0000000135 00000 f 0000000136 00000 f 0000000137 00000 f 0000000138 00000 f 0000000139 00000 f 0000000141 00000 f 0000080793 00000 n 0000000142 00000 f 0000000143 00000 f 0000000144 00000 f 0000000145 00000 f 0000000146 00000 f 0000000147 00000 f 0000000148 00000 f 0000000151 00000 f 0000081443 00000 n 0000081475 00000 n 0000000152 00000 f 0000000153 00000 f 0000000154 00000 f 0000000155 00000 f 0000000156 00000 f 0000000158 00000 f 0000080867 00000 n 0000000159 00000 f 0000000160 00000 f 0000000161 00000 f 0000000162 00000 f 0000000163 00000 f 0000000164 00000 f 0000000165 00000 f 0000000168 00000 f 0000081325 00000 n 0000081357 00000 n 0000000169 00000 f 0000000170 00000 f 0000000171 00000 f 0000000172 00000 f 0000000173 00000 f 0000000175 00000 f 0000080941 00000 n 0000000176 00000 f 0000000177 00000 f 0000000178 00000 f 0000000179 00000 f 0000000180 00000 f 0000000181 00000 f 0000000182 00000 f 0000000185 00000 f 0000081207 00000 n 0000081239 00000 n 0000000186 00000 f 0000000187 00000 f 0000000188 00000 f 0000000189 00000 f 0000000190 00000 f 0000000000 00000 f 0000081015 00000 n 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000081089 00000 n 0000081121 00000 n 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000033616 00000 n 0000082493 00000 n 0000029778 00000 n 0000036609 00000 n 0000033922 00000 n 0000033808 00000 n 0000032865 00000 n 0000033051 00000 n 0000033101 00000 n 0000033690 00000 n 0000033722 00000 n 0000033959 00000 n 0000036685 00000 n 0000036863 00000 n 0000037927 00000 n 0000047731 00000 n 0000082520 00000 n trailer <<7BAC5CE523C4874981926FC40A79D0CC>]>> startxref 82684 %%EOF *uuid:7dbfed74-e54d-418c-9195-d77e398c62d8liFD$bddcfead-6715-1179-bc96-d795218d25c4Vector Smart Object.aiPDF ART5%PDF-1.5 % 1 0 obj <>/OCGs[5 0 R]>>/Pages 3 0 R/Type/Catalog>> endobj 2 0 obj <>stream proof:pdf uuid:edd6ddd4-6cef-9c4c-ae00-326be6ce1b5e uuid:4f484ae1-9794-6c4d-bb19-3c0236c19106 2016-05-30T18:48:19+02:00 Adobe Illustrator CC 2014 (Macintosh) 2016-05-30T18:48:19+02:00 2016-05-30T18:48:19+02:00 Adobe PDF library 11.00 application/pdf endstream endobj 3 0 obj <> endobj 7 0 obj <>/Resources<>/ExtGState<>/Properties<>>>/Type/Page>> endobj 8 0 obj <>stream HVˎ! Wۋ1`|$ir8$6R =<QV?~ǧKN_R%I߻0џۗ{{|rKu{IF${i>eB]cQ2 ak% can B0 rAz=Z{L%<@YMly} W9 r]eVaȧrJRg\'R 4>07b`0uF<p{ u0ȡp#܃( 5#~xf%bSn z8R<ޠ4 9?G#)筂ȗl̺lYroY>X)sb2tt?R*D-}`n̽F[p8 * TeZjMpN/'|cpF^jIU{mA+ Pu YN:10eNOM ƖK2&,sV-ֲ-k<9Ҽ @S<#3T cJr,e O!B;RN!ۡ0Diީm{~B[轅[轅[P[+~Pkfrjpfo`? RxM>!@TI~D `#_* endstream endobj 5 0 obj <> endobj 12 0 obj [/View/Design] endobj 13 0 obj <>>> endobj 11 0 obj <> endobj 10 0 obj [/ICCBased 14 0 R] endobj 14 0 obj <>stream HyTSwoɞc [5laQIBHADED2mtFOE.c}08׎8GNg9w߽'0 ֠Jb  2y.-;!KZ ^i"L0- @8(r;q7Ly&Qq4j|9 V)gB0iW8#8wթ8_٥ʨQQj@&A)/g>'Kt;\ ӥ$պFZUn(4T%)뫔0C&Zi8bxEB;Pӓ̹A om?W= x-[0}y)7ta>jT7@tܛ`q2ʀ&6ZLĄ?_yxg)˔zçLU*uSkSeO4?׸c. R ߁-25 S>ӣVd`rn~Y&+`;A4 A9=-tl`;~p Gp| [`L`< "A YA+Cb(R,*T2B- ꇆnQt}MA0alSx k&^>0|>_',G!"F$H:R!zFQd?r 9\A&G rQ hE]a4zBgE#H *B=0HIpp0MxJ$D1D, VĭKĻYdE"EI2EBGt4MzNr!YK ?%_&#(0J:EAiQ(()ӔWT6U@P+!~mD eԴ!hӦh/']B/ҏӿ?a0nhF!X8܌kc&5S6lIa2cKMA!E#ƒdV(kel }}Cq9 N')].uJr  wG xR^[oƜchg`>b$*~ :Eb~,m,-ݖ,Y¬*6X[ݱF=3뭷Y~dó ti zf6~`{v.Ng#{}}jc1X6fm;'_9 r:8q:˜O:ϸ8uJqnv=MmR 4 n3ܣkGݯz=[==<=GTB(/S,]6*-W:#7*e^YDY}UjAyT`#D="b{ų+ʯ:!kJ4Gmt}uC%K7YVfFY .=b?SƕƩȺy چ k5%4m7lqlioZlG+Zz͹mzy]?uuw|"űNwW&e֥ﺱ*|j5kyݭǯg^ykEklD_p߶7Dmo꿻1ml{Mś nLl<9O[$h՛BdҞ@iءG&vVǥ8nRĩ7u\ЭD-u`ֲK³8%yhYѹJº;.! zpg_XQKFAǿ=ȼ:ɹ8ʷ6˶5̵5͵6ζ7ϸ9к<Ѿ?DINU\dlvۀ܊ݖޢ)߯6DScs 2F[p(@Xr4Pm8Ww)Km endstream endobj 9 0 obj <> endobj 15 0 obj <> endobj 16 0 obj <>stream %!PS-Adobe-3.0 %%Creator: Adobe Illustrator(R) 17.0 %%AI8_CreatorVersion: 18.0.0 %%For: (Anders Gressli) () %%Title: () %%CreationDate: 30/05/2016 18:48 %%Canvassize: 16383 %%BoundingBox: 531 565 548 571 %%HiResBoundingBox: 531.12109375 565.224609375 547.7353515625 570.4658203125 %%DocumentProcessColors: Cyan Magenta Yellow Black %AI5_FileFormat 13.0 %AI12_BuildNumber: 18 %AI3_ColorUsage: Color %AI7_ImageSettings: 0 %AI3_Cropmarks: 0 0 1000 1000 %AI3_TemplateBox: 499.5 500.5 499.5 500.5 %AI3_TileBox: 220.5 120 779.5 903 %AI3_DocumentPreview: None %AI5_ArtSize: 14400 14400 %AI5_RulerUnits: 6 %AI9_ColorModel: 1 %AI5_ArtFlags: 0 0 0 1 0 0 1 0 0 %AI5_TargetResolution: 800 %AI5_NumLayers: 1 %%PageOrigin:0 0 %AI7_GridSettings: 72 8 72 8 1 0 0.800000011920929 0.800000011920929 0.800000011920929 0.899999976158142 0.899999976158142 0.899999976158142 %AI9_Flatten: 1 %AI12_CMSettings: 00.MS %%EndComments endstream endobj 17 0 obj <>stream %%BoundingBox: 531 565 548 571 %%HiResBoundingBox: 531.12109375 565.224609375 547.7353515625 570.4658203125 %AI7_Thumbnail: 128 40 8 %%BeginData: 4810 Hex Bytes %0000330000660000990000CC0033000033330033660033990033CC0033FF %0066000066330066660066990066CC0066FF009900009933009966009999 %0099CC0099FF00CC0000CC3300CC6600CC9900CCCC00CCFF00FF3300FF66 %00FF9900FFCC3300003300333300663300993300CC3300FF333300333333 %3333663333993333CC3333FF3366003366333366663366993366CC3366FF %3399003399333399663399993399CC3399FF33CC0033CC3333CC6633CC99 %33CCCC33CCFF33FF0033FF3333FF6633FF9933FFCC33FFFF660000660033 %6600666600996600CC6600FF6633006633336633666633996633CC6633FF %6666006666336666666666996666CC6666FF669900669933669966669999 %6699CC6699FF66CC0066CC3366CC6666CC9966CCCC66CCFF66FF0066FF33 %66FF6666FF9966FFCC66FFFF9900009900339900669900999900CC9900FF %9933009933339933669933999933CC9933FF996600996633996666996699 %9966CC9966FF9999009999339999669999999999CC9999FF99CC0099CC33 %99CC6699CC9999CCCC99CCFF99FF0099FF3399FF6699FF9999FFCC99FFFF %CC0000CC0033CC0066CC0099CC00CCCC00FFCC3300CC3333CC3366CC3399 %CC33CCCC33FFCC6600CC6633CC6666CC6699CC66CCCC66FFCC9900CC9933 %CC9966CC9999CC99CCCC99FFCCCC00CCCC33CCCC66CCCC99CCCCCCCCCCFF %CCFF00CCFF33CCFF66CCFF99CCFFCCCCFFFFFF0033FF0066FF0099FF00CC %FF3300FF3333FF3366FF3399FF33CCFF33FFFF6600FF6633FF6666FF6699 %FF66CCFF66FFFF9900FF9933FF9966FF9999FF99CCFF99FFFFCC00FFCC33 %FFCC66FFCC99FFCCCCFFCCFFFFFF33FFFF66FFFF99FFFFCC110000001100 %000011111111220000002200000022222222440000004400000044444444 %550000005500000055555555770000007700000077777777880000008800 %000088888888AA000000AA000000AAAAAAAABB000000BB000000BBBBBBBB %DD000000DD000000DDDDDDDDEE000000EE000000EEEEEEEE0000000000FF %00FF0000FFFFFF0000FF00FFFFFF00FFFFFF %524C45FD0DFFA87D2727FD06F827527DFD1FFFA87D2727FD06F827527DFD %13FFFD0BA8FD11FFFD0BA8FD0BFF7D27FD0EF8277DFD19FFA87DFD0FF852 %A8FD0FFF7DFD0BF87DFD0FFF52FD0BF8FD09FF7D27FD12F827A8FD16FF7D %27FD12F827FD0EFFA8FD0BF827FD0FFF27FD0BF8FD08FF27FD16F852FD13 %FFA827FD16F87DFD0CFF7DFD0CF87DFD0DFF52FD0CF8FD07FF27FD18F827 %FD11FFA8FD19F852FD0BFFA8FD0DF8FD0DFFFD0DF8FD05FFA8FD1BF827A8 %FD0EFF7DFD1BF827FD0AFF7DFD0DF852FD0BFF27FD0DF8FD04FFA8FD1DF8 %27A8FD0CFF7DFD1DF852FD09FFA8FD0EF8A8FD09FF7DFD0EF8FD04FFFD1F %F827FD0BFFA8FD1FF87DFD08FF7DFD0EF827FD08FFA827FD0EF8FFFFFF27 %FD20F87DFD0AFF27FD20F8A8FD07FFA8FD0FF8A8FD07FF7DFD0FF8FFFF7D %FD10F82727FD10F8FD09FF52FD10F827FD11F8FD07FF7DFD10F8FD06FFA8 %FD10F8FFFF27FD0DF87DA8FD04FFA852FD0DF852FD07FFA827FD0CF8277D %A8FFFFFFA8A827FD0DF87DFD06FFA8FD10F852FD05FF27FD10F8FF7DFD0C %F827FD09FFA827FD0CF8A8FD06FF7DFD0CF852FD09FF7DFD0CF827FD06FF %7DFD11F8A8FFFFFF7DFD11F8FF27FD0BF852FD0CFF27FD0BF87DFD06FFFD %0CF87DFD0BFFA827FD0BF8A8FD05FFA8FD11F852FFFFFF27FD11F8A8FD0B %F827FD0DFFA827FD0BF8FD05FF7DFD0BF87DFD0DFF7DFD0BF827FD05FF7D %FD12F8A8FF7DFD12F87DFD0BF8FD0FFF7DFD0BF8A8FD04FF52FD0AF827FD %0FFF52FD0AF827FD05FFA8FD12F827FFFD13F827FD0AF87DFD10FFFD0BF8 %7DFD04FFFD0BF87DFD0FFFA8FD0BF8A8FD04FF7DFD27F827FD0AF8A8FD10 %FF7DFD0AF852FD04FFFD0AF827FD11FF52FD0AF87DFD04FFA8FD32F8FD11 %FFA8FD0AF827FFFFFFA8FD0AF852FD11FF52FD0AF87DFD04FF7DFD31F852 %FD12FFFD0AF827FFFFFFA8FD0AF87DFD11FFA8FD0AF852FD04FFA8FD0AF8 %27FD11F852FD14F852FD12FFFD0AF827FFFFFFA8FD0AF87DFD11FFA8FD0A %F827FD04FF7DFD0AF8FFFD10F827A8FD14F87DFD12FFFD0AF827FFFFFFA8 %FD0AF8A8FD12FFFD0AF827FD04FFA8FD0AF8FF7DFD0FF8FFA8FD14F827FD %12FFFD0AF827FFFFFFA8FD0AF852FD11FF7DFD0AF852FD04FF7DFD0AF8FF %FF27FD0DF87DFFA8FD14F827FD12FFFD0AF827FFFFFF7DFD0AF852FD11FF %A8FD0AF852FD04FFA8FD0AF8FFFFA8FD0CF827FFFFA8FD15F8FD11FF7DFD %0AF852FFFFFFA8FD0BF8FD11FF52FD0AF87DFD04FF7DFD0AF8FFFFFF52FD %0BF8A8FFFFA8FD0AF852FD0AF87DFD10FF52FD0AF852FD04FFFD0BF8A8FD %10FF27FD0AF8A8FD04FFA8FD0AF8FD04FFFD0AF852FFFFFFA8FD0AF852FD %0AF827FD0FFFA8FD0BF8A8FD04FF27FD0AF852FD0FFF7DFD0BF8FD05FF7D %FD0AF8FD04FF7DFD09F8FD04FFA8FD0AF8A8FD0BF87DFD0EFF27FD0AF827 %FD05FF7DFD0BF8A8FD0EFF27FD0AF852FD05FFA8FD0AF8FD05FF27FD07F8 %A8FD04FF7DFD0AF8FF27FD0BF87DFD0CFF52FD0BF827FD05FFA8FD0CF8A8 %FD0CFF27FD0BF87DFD05FF7DFD0AF8FD05FFA8FD06F827FD05FFA8FD0AF8 %FF52FD0CF8A8FD0AFF52FD0CF8A8FD06FF27FD0CF8FD0BFF52FD0CF8FD06 %FFA8FD0AF8FD06FF52FD04F827A8FD05FFA8FD0AF8FFFFFD0DF852A8FD06 %FFA827FD0CF827FD07FFA8FD0DF852A8FD06FF7DFD0DF87DFD06FF7DFD0A %F8FD07FFFD04F852FD06FFA8FD0AF8FFFF7DFD0EF82752527D5227FD0EF8 %A8FD08FF27FD0EF8277D527D5227FD0DF827FD07FFA8FD0AF8FD07FFA8F8 %F827FD07FFA8FD0AF8FFFFA827FD20F827FD09FFA8FD21F852FD07FF7DFD %0AF8FD08FF52F8A8FD07FFA8FD0AF8FFFFFFA8FD20F8FD0BFF52FD1FF827 %FD08FFA8FD0AF8FD09FF7DFD08FFA8FD0AF8FD04FF52FD1EF8A8FD0CFF27 %FD1EF8FD09FF7DFD0AF8FD12FFA8FD0AF8FD05FF52FD1CF8A8FD0EFF27FD %1CF8A8FD09FFA8FD0AF8FD12FF7DFD0AF8FD06FF27FD1AF8A8FD10FF27FD %19F827A8FD0AFF7DFD0AF8FD12FFA8FD0AF8FD07FF7D27FD16F827A8FD12 %FF52FD17F827FD0CFFA8FD0AF8FD12FFA8FD0AF8FD08FFA827FD14F852FD %15FF7D27FD14F852FD0DFF7DFD0AF8FD12FFA8FD0AF8FD0AFFA827FD10F8 %52A8FD18FF7DFD11F87DFD0FFFA8FD0AF8FD12FFA8FD0AF8FD0CFFA852FD %0BF82752FD1DFF7D52FD0BF8527DFD11FFA8FD0A27FD12FFA8FD092727 %%EndData endstream endobj 18 0 obj <>stream HWmo6?@4$[~ͮ{ɮAAKÆoHZ,W[8 }X^NC&Fg>Ȝ9cTB?^ ǷlMU|&BR_#glنxϧqE)@@zoX+vF z8a)x45FcG"q{2= \w8ʿx#>}ka_%o},R`g`W}9d'% ilN&c[;G~庚6}؃ QM(qdMZf7<_c@>d=0w os@@A,U& {#:: pwA4ιAqُIL\{NL?xcg9C"qn;C-+> n@1h߆i9| 0 Ac-웡W i27wsߒ THŞ~IMl떷`8R=} T5NI4kFП4K X7%6W|<`g3AADLXP D˘h OjS-H \nc (Bah&R8g2z9;X q(Kg%UbvK(3瀦c*y?718yk괏.ZsFeTeod#˝TOc9[IV=]Lg k ZTC:hhb,(RWpG$E;9-vhrl1+Y GTRhQ~>HY赘 q"<ׯX`gVW76_4Л{*%FW {Vt,Ts{~S]tkE t+c+"L`h"S72ͦݑ!A9(~K%X Ѱ~Nd @>'3SXd;l&8eRθm_A-bjo=v2LfD~ $ U8]AGz#˝ysn8T%):=.<-#cn W0%LGaBR/2`T0(3GT4R^2A@?`cטV{BvdYy:p>^/ 0~G4t.q~77b.B[W!ii[C-ۿޭmL3=c&g NWef"f)/z3T6G&eI&kVQXC p#+;LE#`2]88# v[Hh\՝b x۟<5+- /-a^ 60bЭ7(;"=T;I`6cdu&|6-VPTZqk2$fV9>4?{gALبq?}Qg̥WFE^#WڨsyzPw<4y!8&ӝ~[WAUG҃z4tW@\]iҊUYmv 0Vݵ4->[.mf`ٯn\v}΂p7\% ȟP:ț[6D1ۑQ |H˿rj*D1P9Q7_[ln(;~f|oCl_ k>B\맦>b_NKn 2GWz\- j .}y]\:,}unGE_%QHGV^M \KQ8-G+EqLFK9}XAU;C)`54pBK hJXbSލ6Ħʢj3Z}Pd 4`l.3رDFH@FѐXMt[E'B\f1JWN[^GÖSϬe|`9Qx [;GeTȖm2ۘa1 d%o1GMlNpJ_Zk(,7ןSKNq\i(B+eir,~NM褩/c|˝g|"»aғ Xut0{wBMG4ȓkZ`4'Bئ pk,X^J~zY6hʷ`E b`:C3Ԅ0]0C6+T*.=d `.?0Dd{AzTѴ /r= pU>1n!./9$,T#ctp-#c6 $G1U 钉Z>3}.RP\τqRI߮~#<,f=v8"_sc  @A|:E dR~,ȼac :rCmsAY&Ơ]NrhLxJt|U^lje{c;L5,f2X 6v5O0݁|&T )`$tHgR>U8dCXFc!LR L̬-{/4<,`mV 涱ۆ3[` ';".A v6Z="]LaL02aL+`~vB$Kcl`/n_lX -_+6 >l:`Ea1.|a@iD`ff$a-)wfxtvxt8>ɒLOg M{Xzu\aoRZkfC1DdZ$UyOp&O/U2IEȯ+RJVؓ&́X?ꄶH0$5H`XFDy["+͞lOH٧ZkFՇS~\U7]YS&BJ&ӳ\9s?W関h}|wTT\QWPYCVVԙ8@č7XXBp"a^ bZǛmg3E[xx{?[L&~jcOن4GM2'4Z&^Ne5J%=7n4o.mgkWMOMۑHu:ȍ#ykQ]]V6E0]VÈ .e'|J%lpqɣ" |ه1X/ͱA||0>>xew\<4*,QtNPB(]tf8dž+d hp0dcr04Ag7KpԆլHplq8OYu1~B@0A 5l!1q@>-b:tsP9f egf8 Q#w,2j `7^P-;pn$ab1Ńl'Y>| {hNjN#8|wUar& QKpe+Q{JS9$'18zćL+h"/Gsxik Z~Oz*M8Q+B8NYхP Ц @`x3#ӽS/+?k ^<jA"H Jhj)ȶU{+_Q9&+;٦#C/x,0[a [&_ |QDa3:oP7L(u@i#/ Ȣ`\p ᢢʫ .]e8{󞨉#QʄOdX{)aSQc"Z# E.BZiNfh5uJkV\޳LY?wd =?&mH(iWڋ깗+y2/I,wA!NtS׳lc<;/z4J736~Szn+}ՋI:}j 7_vf;kd:ދǎc.v2p/O;r,JMx6V:c4Jks9sbf3gaEh4 .U5>*,n/|v(LJ}~DJ[9/ 't  ]߶K6d0Vk'YבxWT",fZeK՛CsQt*+a'Yׯh_ހ..BUFv*DCSU,961ү>OLndjF*eԲ?կJ}W:8*7QJbg} #eۖtp)@M P}@KLORZ܋Zcrl8P^r:8mн *4 ueϨ-yF_Y-U?) _ui^}kX 㬞򡪧y 6pQrU]m+Qۆ=}aӴ'dKO: GUcGvzyr?ƉIPv?Jq;VGݯL+EbB]͒('Þ7h{B]O'l?yjyz$ /@kXYw*:掵f>qqh5 r^{qmz6aݠ ):/G&Xѡ'h/!O}A; ԘbS-μܠ˳so/h梁{qX[[zq7xZ(hώ+> ~΢&%Ao~.^[, RO&1_>ƪg:g"$.i L2̳!>Sʿ﨏|V0 T ZT(&|f&1 gQٶi4ֶsm!EʃERJ zh0 hTc-m1T5ZPQ !2 dKMz=/`6Ǻ.q=gl;׷6 N1vqR,&7HvT)UYA HI#S +ߛs\zTgw+d3Kgw ;_%?ԯo"B.~\Pp3Q5Q*}ݍkRŗťb1ٳLIyaRsƆY|{]J?6U _wо8JtGYoy}-VZ)UH| =cW|2R>%*>lFڛ&-~aRNxVW4,(0IZx5KrGRv 饤:δzpp%xh#G>1k^.wJyP ZFZ*hF7׾j@"OITp):_Hб_{G[8WNf_ɿmSߋyͦT`{QN+9tb6.ޛNDogӎn4;ْ*Zb4ԧ%FA`b;hz}Yt;PaF6QB6~}#QcEjKMy/ +=O~z}-\w_Wz8˓w^AԯW_0;_&促@Lj;[D*Z`|g/e\ 2IE@"hZj0՟%u+gӲ?eF T7l.wEKw}vTί)wŜBsA@l' @r3SxNےR!Q zF'v&t[᣹^"-%!>އ~W :׏cԡN22Ţ*S2fy8U}]Ob,g$+Y|4LzBpb-mjT^ksXR(C{EO/";eA~ǀm*$ZJ^BKZn6E-;Ww1Q+H<{&t"0$ze2oej mrl{Gq*D˱BRcaSf}叮S n+W"E=&b ;sA Qz1Pcvem)g mW:Ђȟnq s(I^IyqtJVͼl,VaFjV-&f4Zqھ@rԶРێ捓KWS:tL9)8~ ~ڼ8$zut3*_Tg;XY1^-ce̊h3mnvnDҶv\r v(8Vx<ۂǦ[ܽAOG1po+捧ؼD%ܳk~*p{NA5wP`q1 X\rs{#T5 U-F +Shj5&6Mv|ڋ`7^s O0Dǃ)}@].i{Px X^>BRng ~tšRyTNYǒEpu{Lmĩ͠ qSg: |ĉG7RWCiHw..&rUQհ7-JS::4a[!-^#u-QqM@vröU1L'ź\c!)6` %)CvLP0m*}CTZ1Ͷyo(TQi¼0. 0E*t$`XM1"<(sh6mkNnx39'; ]@0mՑeAnl\61 Dz)Ûv1] x[v0{ؾ;|`^6vhФ -7Lw.nK`'= $iљA<4*{|DeLÝ]PiP}&PV / ~NP"GEMLÉXn*KX@>nO(u m>C6D|F+(&Q\9sgsш{LWc >.=!mA~рh=8&="rY\ j kʼnc-F,@n:d;Q"+oiTup?$fCZ1L<Ε[gd,Pszp@* ,Q\b'Fy䍎0?>lʓ0Sr\} 1U;Emei|_ize>Q=ATD@~UMrŐ/Ő=B݅0ou4]h^m8X۷!a:B St|䀞>nಠĜJ 0P:hMw;ӵS0, 34%dyPI;0|>:撖1)Ǝ J(,T]t%Ec-tۍ2zJR%Su+R[W,({n}MYzI+h޸&.Μ+j9mZX:ёC*`r x!,Z/ 7NX|t{^h#SĖOM&S5X8 ]4EX?%_漖zm%D/G0$3m *@( (-6DoUJ*ƗZ.W89{|v:9|d3'fsTc_5NP"kӃPn6b;-)5nBGupݏ9wJʢ~fou=6|ز! },QD ϭ:j 6e 1>Nj#QF4gK8Ս*PL$(vHB!$@ %tMm/+' A}]ӶJ6S\j\'T%X1~snc|fOݟΧt2闸g^P@EA!YtF_oͣJm6cU禫)l0)ΤtO^xi >8MTfj"v?5rNi?A%vjvB{@ϩsOɕ n¦V0JצFƁ{(x#*CIUWm>4ʼ0V{$b_͹yx֠kdJ׍D]}vr|8Wymd::_/L@N:I1+j1pިMt#(/Mrh%ypJJXs`~אJAjXd{Yҩݺ$tqBqش-=s)r\$T1Ǚd9i}WKbE$S֩K{[pUT\Km\v&3kQ"S1'n.E=rI5vЅNwBf'8*7l8+*xYٺ[=+pk GZiEZFEwIvJgGį ؙ\>igX뎧:*¨,=#@BIR^4$8cASH_@Pok==0AWnRj疃9hE?cBc9&811E{ML8Tz!\N+F$;-Oq}iGWQԌgڅqԸ]Yٗ߾ DV{@&$ɬnY ;Nώ4  Һ _I6\:1FB3K6MidY ^.ruVmRh`MdoLDqCdZ9;5DwʄV|̙P`^άݦTe s PUd^=F"jO2# ]7$_{9IT3+?i5%3c!@:)v>g VԚ_15cv_p-\61 Ͼv7eg ,bܐxtc$ՌIFI܃CiS{.*0qߙAcG"'2Q1J (dk1pFuvp#˸ƈ6/_,YzɈ!^q} 1/I7"}j %d5]v쾠p\̴ gZ۹ ad7>ۍ?N?@_2I5L1pZCj %Yh Q!hB1)oNJ \θ}1ҌƢ|cըv@c1cC(RB+A'vl}aThTP'p5S{԰vbr:QC @"r6( q SR|Yd&i (n$Bb; bCt]aJ90ٲ@k07/(b"mo6?ͦr5kǍNJGUO3v[9lkƽ9:=ZnVo(`^WB|l[Gn9҅c!i 𨠺=.wIoJfWlbp?fq8vZNK-rnk!3TqT s(!t-'U`vqd9m F%w[ #S.u8>Z8NB9NAyӬq{u*tε8^^NQ9B8^8^XWӽ9eF_ Y,>\|'X'HAEvo4('O$;*&ޡ~UTYx w I5r\bd~x* ,46Jy^ufc=ԩn$ S'!ID=>51\exV2F01+$)$tbѩKs)R>SobJ[c"$/lSt%Ǯ0b'i 3qDe/׾Z*rq%nZIXU+>;^@?ۦOoSEJ]|P o%c![nHM ͳfu0jxLwvC'6źNk,3HU^^@CJ Z5NZLy,W1Ե؝w$h,H2hUmߗ&a P$8ܹ B!y#(!H^z$ /#R@!hFc44A#j$ Hw|̙9g)JHGǝ_=AFT8 &?g'DPh?DAbJOhYC&sY4; xqo+6TIxWaUwfxE}=O|ׄYQyD΃F dIVcσ+!L iTe8Xդ WCTeci + +x#0@8 ߹˱LДTx`l<tP!8Vˢ8sG@+WNo,&8,xԿSNZoE!(සݵ؏-BR_l I,XBft mzhpҊ(L2(C`u)dEil2=` endstream endobj 19 0 obj <>stream HWVJ}<@!@ H,RW 303?\m{}NJ>oboW:9תV?NɫZ~<YW J5Fx|E`woʙm~T*>V' {Fu˕U7$`kF\ |nI@.;Z5evHFRFҚДC3:& G MO>#ݐ${qI !jk/=BCՒq^6YYW2U;--[bJr}=KEi)ԮQtߕ)r#uqʼnuk RJ,}\ XΪ ΏiUF1|X+NhY n*$h\뛒ϕ^qָx;]ܗt|32_-~fɔiԎ5WۨҌJ22ϳzZqߏTh*wR>$q,ʗGz򬦪7F-h9Ҍ9 |j,5F'wV┘2ּՏ_3~mɗug,Q팘 ΛIV57|Hs`4y}S;ܘ';.K:||$z@j$DgFx7oRb7Ìy9•-P z0&K)K5%[Y7_/i3/097iy U뒓yZK7-CP'#O7omK4>cHHKLI\۟4/GCw&s YAӕ9I2<%W}ŒN{e?&]Fv> ܬqC) a$vphHA+4ou9mh"Q=I$99{R[ҏ*5(Q̊s iqؒP'7C #BfMpY<(349:Cr'+(I2 qsS\" >y: ;4_A -˾q26m,qEwhoFQHB 3Ir-0:iPkE6vuq?XQ .0juʭdyrVv}f,55N8g*ʠtyq*Hp~7T?7c %+86%qggPً?9PV=ɧe쩔AN]̌]B\ abݨ8-f fXu8PO ;Z)}nC#E7?fV.?aGKI]~@'}oM)rtK#}PZ $Ĝ $[ 3E:1C/7֢c1rAz+_WZeqve.sW3n`.`EcU}A(zV_gd D(!b^c;7T6q2\P`T@dRs&!YoiRC}Af'Gw@BvLc:JV]-"ZS{q+B 9LfMp'.Ɍ-T4(p~:Q݈%doCg^{J>TN LTqJSeyFFq!&z4jP5]WƘ+ع>gO#LI{sےMUt 0eei]MVK`0/°u "f{w`Axawuq3%A;gs'] WH])11c'M9}c+{ ,yBQJ)륭|gg{fz e89Kilo!+d*k$:|rͼ}irwb螽inւ2V$+8zwW;h[0azdp]< =:=`b ,%89t¦lyOKْC(d9J0-3rp>btYs%d\yR!|0C:Sq0avgzɅ°{RB໡RF{To?3C9aFQ:f7Fv]M)\k;0TD Czr_ ߚjpݢv (<84M/փEJi!Xdo?&XH=4[>˄4[؄E-{qİ< ]Fi}i7Anǚ($;[]0M6)xۼu G7esS3{A43FWV^Ӝț\. u7\^3^t}LI*{,wV~,ǐh&)xW[ |Z*4 gJ9Lk" Ӧ5٪|&NYJda3kQ׼e3m:L!D0ô֦Y;S/:*,o2U[wk.h[|$ _4/L!S;N>7S\cÏuxoR%f "pV Ţؗ[ƾz1=e evl-x ᩵ Vmm)] Voif!Ҷ[QKp ^͏|hr+En{4cQl;3 W7&}7NQ>6T_ʕpx7&xH7mGW#n2kY-{WY)h_ZJ|SN5ޒ klAb KnxܻaZ^6 ,Q4[yoO"Nȅɞ Iߥs]y\1Næ ,@TEPr%QpOc"y YFYdjkwGw6r<7z5 ɐ3:_k,0QR-,fKYP-oF+Vn1ut]-}:#dFS>|Y-1;4B% _fXN&W9 s?9CR_8<p(織Fo(W֫22淰y;Hcdڛ,Gx#gyr]o}Frc ,C د,>p,"/efiiYf7jYyfF=*na~۴yhV`ddV:geNVȬa˚6FPgӺRGli/bw,ڔegjZelkgoNPC!W:Z /n\H_n$/#ί{b60施a9r/׶x_/yKp޷l$S6HӽSIm}!6\$x\F''EV&`sG^' AesQ6.A@~8C^cbj,{T4P G'$0 Lh c}fI&JP3?P^ !̈́5ŀ`Uc6+x}܁~y tW)&"RYrHb9hL}Jƿ#MG ? 4[1 24rʽN^:qB>bF'lipM{t ӅIµeW\2SW7z[Ppfnþ%Wbr$ϻh }tik[flx2mjB3_CqoDf9m mLyYt1.A*/unj&Dq./Ņ6J̝z.#%ٶ3E<=JArɾp{w%]!WH5Dr~}%]p $ ]_w]Gd$ |n5J6Kؙ#̀1äj37U}oOa T7>6"9,x|ī&s_&88lj^/'^88_6o&^I`~'I9:F H]6 3Д[HDZH_zUƮN ]Lz4CE`sxElՅnŒfRUՋEx:{Y&G5Y&_;=p@(nzbqς|/M۝/reV9պx(j6$RJʡ:v&T٣PV@g5>dT #N AvuCdaөl h;S Yȑ&RRR!(:/ Q8X*87}^*7pV[|; S"X<"eKslP% c"kCҒ{[mGbި rS9C%IN=*>]{"t4&bOƉqyʭ.۰J {*KFG"*AMOåPUh |B!mc'11a0FkVQi:-Wc1nXwc'*6БIG{W]nGB\ʣ6i+q)v;gі֑ڑ5iCRMB6ɾE2«ďR2D7>g\BxKϾ+ ,GAū+65x' C؀qptWlLЪPtlO07e5e## ;6 ByP&>p{wFNgD0څ'׏ԋhѮDGPԋ]"iB=BޥX_»[()Hb" tP5$K @=u{Y#{Xt> ~iYa&l]G!H|\4\!gL[L̝@)vOJjo~\/C} iGdi`5ӁfIR|xJ2&"{@ەubۗ|`k?.4@)mW@fܡh\wF4#r+tv߼i=kY3J*K6}p &wNP^0&325SH GfJ!A~ %!V&#rjK.{)~sx b.vò׈sM۔kf\c Z0`ՠٯHGb#_SXҭԕ&`BB! 2 X=!^xJRIuuծݻ,t. GU/a_6g;Lfec UaߔDvX ֔*?Gas꨺ ]&\6',&f#G5*ǫW魀 SDy۷CafC0|'a0isʞN|E{d.vvxYl|T*1u1M#X!9s )$ C̽ e/u(Hy #cie/"WFWgFVWP}a!=asg;m [I#F31?&ݠEJ73Bn /1!o7hG[P >P- 6U^:^COtVOUytWiV yyCkx;Gp5cE5|͐b=F. XBhsY\A~ 8Ӛgƍhˇs띕U_:rd.lR5$hG8_ 267bLvi2r/'F',osy Їxj$oUB@5-U{;܌u1;#<2[p ߻Nv@9g\0 -0A(9\r,43[2`a/$z1w\<dUyM@6Am/B=Bh$IX#(P{ `QzӑCj֬#GQY$ {Xiv~fo]aX߭&Qw}R YMK[:.~ *VOk.gn{rv6Re}Wz߽jWX!8?;)@\o9|cм b֝'`R7{iM8fxCSW;P*OT82c!ihXA*$2D 3gs #iz24Eud!\PƸ}'`ln7~,#do?$aZx½[W ,N P "> >̱\DI]8(^8cn o߆y&`dGcH^[RW1{/ZOM]` $ ׭ UűD.p~?| Hh pATLwW==Uv'Q8f&Y~ct{zI4?~uuOwv_ yhL~u39_3w:!TjHۿowc~?,}uJf{gY?8M.oW>t"1&[+O^>8\0 ΃:Vmǀ?W}n[ປ&=tsɽA ekݽzg̈́oH9X<^P6{['r_:}韷.wPD^y,a/WV:lNG*DO`5@v A' 7_w vZ yg'pNJs-5ikC2H5|8\|?VoǼ=P~jRLPδ&zסJBP~%YBk@W~Pi2ҨVq/6>ZDX0o&>sl-XXTZJgKcD|=`^]l(5quL(3CBԎ;磆阛F հg|ژ:i\  xiudcrCt5r)%!R'W+:>҂;bN"[ DO,*hu\z.(O}+c,y $mIJ֩E<*RW|2R7+šNTf ֞\Y@5+-8ެۧ[q[M}Խ>]{sm}6*<ɼ RF~PoRJ̆>˚oY`簒>5s*" Y>d,@۽/Τ -,Jr>^PqZkP,Q-(2}$dM*gT& .-H dt&{F ")`KWpWr5zvd6"IF/u*$Az'=FSlEHs$J,<dbtS> @cR.`=!̴瑇5Z(>Z߄ԯUTtgH4U&KQoP,Dl.s$&BZq< K8J 04t{)SL*C%rFi{@PSDjMY)'P:񥜗 H,+p4I'x*zds2DljwA,ȼu.ԻH:25-{NiR^881_\kt_B,~!P~ZX{g6/u]A ! YGTΪ#ZPu@7u!Tu@-m]jsD]T6k0џڮ(%XjJ^u*%R[Zu,TQiסȮcEIfHx Rv)+SۮR,i|e17v x ]w@-C/YPZ@{Ve֝2;K \R_w,1)Ҹce! 0I\Xa`$сPaKΏ;«yXU\y!!-̻wI j!ڼsTXE̻S00+{}Ue4UTy/46UBj0*;$˱y ȼJT-;LB̸kV=ךϥEY2YT=O&&&ߜXE s7m %>mɂQpEWwCx{xvu=m&dloe~}~3]9n?˫l1H{~=w#qdbWP1/ endstream endobj 6 0 obj [5 0 R] endobj 20 0 obj <> endobj xref 0 21 0000000000 65535 f 0000000016 00000 n 0000000144 00000 n 0000003469 00000 n 0000000000 00000 f 0000004594 00000 n 0000042756 00000 n 0000003520 00000 n 0000003804 00000 n 0000007576 00000 n 0000004893 00000 n 0000004780 00000 n 0000004664 00000 n 0000004695 00000 n 0000004928 00000 n 0000007649 00000 n 0000007832 00000 n 0000008790 00000 n 0000013822 00000 n 0000029619 00000 n 0000042779 00000 n trailer <<12DE95F903FC43C895A4121E1E15237B>]>> startxref 42954 %%EOF *uuid:edd6ddd4-6cef-9c4c-ae00-326be6ce1b5ejliFD$bddcfeae-6715-1179-bc96-d795218d25c4Layer 3 copy.psb8BPB8BIM8BPSW8BIMZ%G8BIM%}Ǿ pvN8BIM$7 Adobe Photoshop CS6 (Windows) 2012-09-17T17:26:25+01:00 2012-09-17T19:04:02+01:00 2012-09-17T19:04:02+01:00 application/vnd.adobe.photoshop 3 sRGB IEC61966-2.1 xmp.iid:95DF2A00DA00E211A07B995EF2A73201 xmp.did:95DF2A00DA00E211A07B995EF2A73201 xmp.did:95DF2A00DA00E211A07B995EF2A73201 created xmp.iid:95DF2A00DA00E211A07B995EF2A73201 2012-09-17T17:26:25+01:00 Adobe Photoshop CS6 (Windows) 8BIM: printOutputPstSboolInteenumInteClrmprintSixteenBitbool printerNameTEXTprintProofSetupObjc Proof Setup proofSetupBltnenum builtinProof proofCMYK8BIM;-printOutputOptionsCptnboolClbrboolRgsMboolCrnCboolCntCboolLblsboolNgtvboolEmlDboolIntrboolBckgObjcRGBCRd doub@oGrn doub@oBl doub@oBrdTUntF#RltBld UntF#RltRsltUntF#Pxl@R vectorDataboolPgPsenumPgPsPgPCLeftUntF#RltTop UntF#RltScl UntF#Prc@YcropWhenPrintingboolcropRectBottomlong cropRectLeftlong cropRectRightlong cropRectToplong8BIMHH8BIM&?8BIM 8BIM x8BIM8BIM 8BIM' 8BIMH/fflff/ff2Z5-8BIMp8BIM8BIM8BIM08BIM-8BIM@@8BIM8BIM5nullboundsObjcRct1Top longLeftlongBtomlongRghtlongslicesVlLsObjcslicesliceIDlonggroupIDlongoriginenum ESliceOrigin autoGeneratedTypeenum ESliceTypeImg boundsObjcRct1Top longLeftlongBtomlongRghtlongurlTEXTnullTEXTMsgeTEXTaltTagTEXTcellTextIsHTMLboolcellTextTEXT horzAlignenumESliceHorzAligndefault vertAlignenumESliceVertAligndefault bgColorTypeenumESliceBGColorTypeNone topOutsetlong leftOutsetlong bottomOutsetlong rightOutsetlong8BIM( ?8BIM H HLinomntrRGB XYZ  1acspMSFTIEC sRGB-HP cprtP3desclwtptbkptrXYZgXYZ,bXYZ@dmndTpdmddvuedLview$lumimeas $tech0 rTRC< gTRC< bTRC< textCopyright (c) 1998 Hewlett-Packard CompanydescsRGB IEC61966-2.1sRGB IEC61966-2.1XYZ QXYZ XYZ o8XYZ bXYZ $descIEC http://www.iec.chIEC http://www.iec.chdesc.IEC 61966-2.1 Default RGB colour space - sRGB.IEC 61966-2.1 Default RGB colour space - sRGBdesc,Reference Viewing Condition in IEC61966-2.1,Reference Viewing Condition in IEC61966-2.1view_. \XYZ L VPWmeassig CRT curv #(-27;@EJOTY^chmrw| %+28>ELRY`gnu| &/8AKT]gqz !-8COZfr~ -;HUcq~ +:IXgw'7HYj{+=Oat 2FZn  % : O d y  ' = T j " 9 Q i  * C \ u & @ Z t .Id %A^z &Ca~1Om&Ed#Cc'Ij4Vx&IlAe@e Ek*Qw;c*R{Gp@j>i  A l !!H!u!!!"'"U"""# #8#f###$$M$|$$% %8%h%%%&'&W&&&''I'z''( (?(q(())8)k))**5*h**++6+i++,,9,n,,- -A-v--..L.../$/Z///050l0011J1112*2c223 3F3334+4e4455M555676r667$7`7788P8899B999:6:t::;-;k;;<' >`>>?!?a??@#@d@@A)AjAAB0BrBBC:C}CDDGDDEEUEEF"FgFFG5G{GHHKHHIIcIIJ7J}JK KSKKL*LrLMMJMMN%NnNOOIOOP'PqPQQPQQR1R|RSS_SSTBTTU(UuUVV\VVWDWWX/X}XYYiYZZVZZ[E[[\5\\]']x]^^l^__a_``W``aOaabIbbcCccd@dde=eef=ffg=ggh?hhiCiijHjjkOkklWlmm`mnnknooxop+ppq:qqrKrss]sttptu(uuv>vvwVwxxnxy*yyzFz{{c{|!||}A}~~b~#G k͂0WGrׇ;iΉ3dʋ0cʍ1fΏ6n֑?zM _ɖ4 uL$h՛BdҞ@iءG&vVǥ8nRĩ7u\ЭD-u`ֲK³8%yhYѹJº;.! zpg_XQKFAǿ=ȼ:ɹ8ʷ6˶5̵5͵6ζ7ϸ9к<Ѿ?DINU\dlvۀ܊ݖޢ)߯6DScs 2F[p(@Xr4Pm8Ww)Km8BIMB8BIM!UAdobe PhotoshopAdobe Photoshop CS68BIM".MM*bj(1r2i ' 'Adobe Photoshop CS6 (Windows)2012:09:17 19:04:02&(.HH8BIMLmoptdTargetSettingsClrTObjc ColorTableClrsVlLsisExactboolMttCObjc NativeQuadBl longGrn longRd longTrnsbool autoReduceboolcolorTableControlObjcColorTableControl lockedColorsVlLs shiftEntriesVlLsditherAlgorithmenumDitherAlgorithmDfsn ditherPercentlongd fileFormatenum FileFormatPNG8 interlacedbool noMatteColorbool numColorslongreductionAlgorithmenumReductionAlgorithmSelerolloverMasterPalettebooltransparencyDitherAlgorithmenumDitherAlgorithmNonetransparencyDitherAmountlongdwebShiftPercentlong zonedDitherObjc ZonedInfo channelIDlong emphasizeTextboolemphasizeVectorsboolfloorlongzonedHistogramWeightObjc ZonedInfo channelIDlong emphasizeTextboolemphasizeVectorsboolfloorlong8BIM-msetnullVersionlong8BIMms4w8BIMmaniIRFR8BIMAnDsnullAFStlongFrInVlLsObjcnullFrIDlongL6FStsVlLsObjcnullFsIDlongAFrmlongFsFrVlLslongL6LCntlong8BIMRoll8BIMmfrip˸???8BIMnormL( Layer 3 copy8BIMluni Layer 3 copy8BIMlnsrlayr8BIMlyidB8BIMclbl8BIMinfx8BIMknko8BIMlspf8BIMlclr8BIMshmdH8BIMcust4metadata layerTimedoubAs YS8BIMfxrpzz$+379;@CEHJLNQSUVXY[[^]a_a_ca`_^^]\\[ZZYWWXWVUVUVUVUVUVTVUWY[\^_abdegijlmoprsvvxxzxyxyxxyxzxzzzzzzzzzzzz{z{z{z|z|z|z|z|z{z{z{zzzzxzyyyyyyywzwyxyxyxyxyxyxwxwxwxwxvxwyxyxyxxwywzyzyzyzyzyzyyyyyyyyyyxyxyxyxyxyxyxywwwwwuwuvvvvvvvtttsuttttttstssssssssssrtqtrtrtrtstsrrrrqrqrqrqrqqqqqpqpqoqoqooonpopoonononolmllmmkmkmklklkkkkkjkjkjjjijhjhkikghgfhfhfhfefeeeddeddcccbbbaa_b_cacaa`babbab`a``````_`_`^^^^]\\ZZYYWWVUUUTSSRSRTSSRSRSSRPQPQRQPQOPPOOOOPPOOOPQSSVUZ[^^`bceghhiiffca__[XTPNJGD?=953/)"           !#%&( &%#!  !#&*,013 10,*&#  !%*.158<>?A ?>:850,(#  !&*058>AFJKMO MKHEA<71,&!   %*05:?FKPTY\]_ ]ZWTOHC<70*#   #(.5:AHMTZ_dhknp mkgb]WPJA:3, % ( !&,38?FOU]djpty}|wsme]UME<5 ,&  %*07>EMU]emsz |tmdZPH> 5.& + #(.3:CJR\dkt}|qh]TJ ?5.%  %*18?HPYajs| vk_TJ?5,% . #(.53*# . %*18?HPZdmvľyk_TF<1(! 1 #(.5EMW_js} ƺsdUH<1( * !%,18AJR\eny }m_PE8.% + #(05>EMWajs} whZM?5*# ) !&,18AJR\eny ǼsdUH<1( , #(05>EMWajtĸ}m_PE8.%+ !&,3:AJR\enywhZM?5,#, #(05>EMWajtȼsdUH<1(!* !&,3:AJR\enyŸ}n_RE:0&, #(05>EMWajtwhZMA7.%* !&,3:AJR\enyȼsdWJ>5,%, #(07>FOWajtĸ}naTF<3,* !&,3:AJR\epyyj]PE<3, #(07>FOWajtǼtgZOE<, !&,3:AJT\epzĸqdYME, #*07>FOWakt|nbWM, !&,3:CJT]gpzǼymbY. %*07>FOYbkvŸwmb, !&,3:CKT]gpzwm. %*07?FOYbkvȽy, !&.3:CKT]gqzź+ %*07?FPYbkv¸, !&.3EMW_hs}ǿ* !%,18AJRZenyǿ ) #(05>EMWajs}ǿ '!&,18AJR\enyǾ &#(05>EMWajt}ż $!&,3:AJR\enyúz ##(05>EMWajt}tn !&!,3:AJR\enyǾypha  #(0 7>EOWajt ûtkbZT  !&,3:AJR\epy zpg]UOH #(07>FOWajt ļvkbYPHA< !&,3:AJT\epz |qg]TKE>71 #(07>FOWaktwmbYPH?81.( !&,3:AJT\epz|qh]UKC<5.(%! #*07>FOYaktļwmdZPH?81*%! !&,3:CJT]gpz}sh_UKC<5.(# %*07>FOYbkvĻwmdZPH?81*% !&.3:CKT]gpzǿ|qh_UKC<5.(#  %*07?FOYbkvûwmdZPH?81*%  !&.370*% . #(.570*# , #(.570(# , #(05>EMW_js}.ƽzpe\TJA:3,&! * !&,18AJR\eny,tjaWOF>70(# , #(05>EMWajs}.Žype\RJA:3,&! * !&,1:AJR\eny,tjaWOE>70(# , #(05>EMWajt.Žyne\RJA:3,&! * !&,3:AJR\eny,tjaWME>50(# , #(05>EMWajt.Žyne\RJA:3,&! * !&,3:AJR\eny,}tjaWME>50(# , #(07>EOWajt.żyne\RJA81,&! * !&,3:AJR\epy,}sjaWME>50(# , #(07>FOWajt.ļyndZRJA81,%! , !&,3:AJT\epz,}sh_UME>5.(# , #(07>FOWakt-ļwndZRHA81,%! , !&,3:CJT\gpz,}sh_UME<5.(# . #*07>FOYbkv-ļwmdZPH?81,% , !&,3:CKT]gpz,}sh_UME<5.(# . %*07?FOYbkv-ĻwmdZPH?81*% , !&.3:CKT]gpz,ǿ|sh_UME<5.(# + %*07?FOYbkv-ûwmdZPH?81*% , !&.370*# , #(.570(# , #(.570(# , #(.570(# , #(.550(# , #(05>EMWajs}/Žyne\RJA:3,&! * !&,18AJR\eny-tjaWME>50(# , #(05>EMWajt,Žyne\RJA:1,&! * !&,3:AJR\eny*}sjaWME>50(# , #(07>EMWajt)żyne\RJA81,&! * !&,3:AJR\eny'}sj_WME>50(# , #(07>FOWajt&ļyndZRJA81,%! * !&,3:AJR\epz$}sh_UME<5.(# , #(07>FOWajt #ļwndZRHA81,%! * !&,3:AJT\epz !}sh_UME<5.(# - #(07>FOWakt ļwmdZPH?81,% + !&,3:AJT\gpz }sh_UME<5.(# * #(07>FOWakvwmdZPH?81*%  (!&,3:AJT\gpzǿ|qh_UME<5.(#  '#(07>FOYbkvûwmdZPH?81*%  %!&,3:AJT]gpzǿ|qh_UKC<5.(#  $#(07>FOYbkvûwmbZPH?81*%  !"&,3:CJT]gqzǿ|qh]UKC<5.&!   #(!07>FOYbkvûvmbYPH?81*%   !&,3:CKT]gq|ǿ|qg]TKC<3.&!   #(07>FOYbkvºvkbYPH?71*%   !&,3:CKT]gq|ƾzqg]TKC:3.&!  #(07>FOYbkvºvkbYOF?7 0*%  !&,3:CKT]gq|ƾzpg]TKC:3. &!  #(07>FOYbmv!ºvkbYOF>70*%   !&,3:CKT]gq|$ƾzpg]TJC:3,&!   #*07>FPYbmw$ºtkaWOF>70*#  !&,3:CKT]gq|'ƾzpe\TJA:3,&!  #*07?FPYbmw'¹tkaWOF>70(#   !&,3:CKT]hq|*ƽzpe\RJA:3,&!  %*07?HPYdmw*tjaWOF>70(#  !&.370(# ! !&.350(# $ !&.550(# ' !&.550(# ( !&.55.(# + !&.570*# + %*18?HR\epz,ƾzpg\TJC:3,&! ) !&.570(# + %*18?HR\epz,ƾzpe\TJA:3,&! ) !&.570(# + %*18?HR\epz,ƽype\RJA:3,&! ) !&.570(# * %*18?HR\epz,ƽyne\RJA:3,&! ) !&.550(# * %*18?JR\epz,Žyne\RJA:1,&! ) !&.550(# * %*18?JR\epz,ŽyndZRHA81,%! ) !&.570*# + !&.570(# ) !&.570(# ) !&.570(# ) !&.550(# ) !&,370*#   #(07?JR]gs}|qg]TKC:3,&!   &,3<EMWbmwżvkbYOF>70(#   #(07?HR]gs}|qg]TJA:3,&!  &,370 (#  #(07?HR]gs}!zpg\TJA:3,& !  &,350(#   #(07?HR]gs}$zpe\RJA81,%  %,370(# % %,3:EMWbmy+ú}sh]TKC:3,&! ( !(07?HR]hs*ǾwmbYOF>70(# + %,3:EMWbmy+º|qg]TJA:3,&! ' !(07?HR]hs*ǾwmbYOF>50(# ) %,3:CMWbmy+¹|qg]TJA81,% ' !(07?HR]hs*ƾvkbWOE>5.(# * %*3:CMWbmy*¹|pg\RJA81*% ( !(.7?HR]hs*ƽvkaWME<5.&! ' %*3:CMWbmy*¹zpe\RJA81*% % !(.7?HR]hs*ƽtjaWME<5.&!  #%*1:CMWbny*¹zpeZRH?81*%  "!&.7?HR]hs*ƽtj_UMC<3.&!   %*1:CMWbny*zneZPH?70*#  !&.7?HR]ht*ƽtj_UKC:3,&!   %*1:CMWbny*yndZPH?70(#   !&.7?HR]ht*żsh_UKC:3,&!   %*1:CMWbnz*yndZPF>70(#   !&.7?HR]ht*żsh]TKC:3,%  %*1:CMWbnz*ymdYOF>70(#  !&.7?HR]ht)ż}sh]TJA:1,%  %*1:CMWbnz*wmbYOF>5.(#  !&.7?HR]ht)ż}qh]TJA81*%  %*1:CMYbnz*wmbYOE>5.(!  !&.7?HR]ht)Ļ}qg]TJA81*%  %*1:CMYdnz*wkbWOE<5.&!  !&.7?HR]ht )Ļ|qg\RJA81*%  %*1:CMYdnz *ȿvkaWME<5.&!  !&.7?HR]ht )Ļ|pe\RH?80*#  #*1:CMYdnz*ǿvkaWMC<3,&!   !&.5?HR]ht)ĻzpeZPH?70(#   #*1:CMYdnz*ǿtj_UKC:3,&! # !&.5>HR]ht)úzpeZPH>70(# # #*1:CMWdnz*ǿtj_UKC:3,% & !&.5>FR\gs)úzndZPF>70(# & #*18AKWbmy*Ǿtj_UKA:1*% ' &.35.(! % #(08AKWbmz*Ǿsh]TJA81*% % %,35.&! % !(08AKWbmz*Ǿsh]TJA81*% % %,370(# $ #*35.(! $ #*1:EOZgs%tj_UKA:1*% % &.5?JTamw#ƼzndZOF>5.&! $ #*1:EOZeq"tj_TJA81*% % &,5?JU_kw żzndYOE<5.&! $ #*1:EOYeqth]TJA81*# # &,5?JT_ky żynbYOE<3,&! % #(1:EMYeq sh]TJ?80(# # %,5?HT_kyymbWME<3,% ' !(1:CMYeq}sg\RH?70(# # %,5>HT_jwŻwmbWMC:3,% ' !(1:CMYdp}}qg\RH?70(# # %,5>HT_jwĻwmaWMC:3,% ' !(18CMYdq}ȿ}qg\RH>7.(! # %,3>HR]jwĺwkaUKC:1*% $ !(08CMWdq}ȿ}qeZPF>5.&! # %,3>HR]jvĺvkaUKC:1*% $ !(.8AKWdp}ǿ|peZPF>5 .&! # %*3>FR]hvúvj_UKA81* # $ !(.8AKWbp}ǿ|peZOF<5.&!  # %*35.&! " &,7?JUbmz'ǽymaWKC:1*% ! !*1:EO\gt&¸qg\PF>5.&! # %,5?HUamz'ǽwkaUKA81*# ! !(18EOZgs&}qeZPF>5.&!   %,5?HTamy&ƼwkaUKA80(# ! !(1:COZes%}qeZOF<3,&   %,5>HT_ky#Ƽwk_TJA80(# # !(0:CMZeq"}peZOE<3,%   #,5>HT_kw Ǽvj_TJ?70(! # (08CMYdq|pdYOE<3,%   #,3>FR]kw ǻvj_TJ?70(!  (07AMYeq} zndYOE:3*% " #*3>FR]jw ƺsh\TH?7.(!  &07AKWdp} ymbWMC:1*% % #*15.&!  &07AKWdp}ymaUKA:1*# " #*15.&!    !(1:EOZgsƸznbYMC:1(#   #.5>JU_mz˾th\RH>5,%  !(1:EOZesŵznbUMC80(!  #,5>HTaky"˽tg\PH>5,%  !(18COYeq "ŵzmaUJA80(!  %,5>HT_kw %˽sgZPE<3,%  !&08CMYdq} %ŵwmaUJ?80(!  %*55.&  #*35.&  #*3:FP\ht%̺znbWKC80*# ! &.7?KWbnz#ısg\PF<3*% " !*1HT_kw$î}peYOC81*# ! !(0:COZeq}$͵wj_RH>7.%  %,5>HT_kw$í|pbYKC81(!  !(08CMYep}"ʹvh]RF>3,%   %,5>HR_jv$|nbWKC81(!  !(08CMWdnz"̳th\PF<3,%   %,3>HR]ht$zmbUK?7.(!  !(08AKWbnz"ͲsgZPE<3,#   #,3>FP\ht#ymaTJ?7.&  !&.7AKWbnz"ͱqeZOE:1*#   #*35,%  &.7?JUakw#̯|nbWMA81(#  #*3:EOZeq}#ӽvh\PF>3,%  %,7?JTajv#̭znbWJA8.&!  (08CMYep|#Իsg\PF<3,#  !*1JUbmy#պsgZOE:1*#  #,5AKYdq}"̩wk_TJ?5.&  #,7AMZgs#չqeYOC:1*#  !,7AO\hv"̨wj_TH>5,& !,5AO\jw!ն|pdWMC81(!  *5?M\jw"˨th]RF<3,%  (3>KZhw ֵznbWKA80&!  &1>JYhv"ʦth\PF<3,#  %0:HWet ִzmaUJA7.&!  #,:FTbq"ɤse\OE:1*# !,7CRan ײwmaTJ?5.&  (5AO_m|ɢ}qeYME:1*!  (1>MZjzװwj_TH>5,%  &05,%  #.8ERbp!ǞznbWKA80&! !*7CP]mخth]PF>3,%  (3?O\jy!ƝymaUKA7.&!  &1>KYhw٪sgZPE:3*#  %0:JWet!Ŝym_UJ?7.&  #.:FTbq٩}peZOE:1*!  #*7CPan!™vj_RH>5.% *5AO]k|٦|pdYMC80(!  (3>KZjwth]RH>3,%  &0KYgv߼|peYOE:1(!  &05,&  (3>KZgwӒymbYM A80(!  &1JYgv΋th]TJ?5.&   &0:HUdq䢃ymbWMC:1*#  %08FTbn̊|qg]RH>5.%  #,8EP_m!㝀vkaUKC80(!  !*5AO]j$DŽyndZOF>3,%  *3?MZg#ߔzpg]TJA80(!  &1>JWew$δyph_UME:1*#  &05.&!  %.:FRan)ڿ|vnjbZTME>70(!  #,8CP]k*ӵytnhb\UPJC<70(#  !*5AO\j*ʪ|vpkga\UOJE?830(#  *3?MYg,ysnhd_YTOJC?:51*&#  &1>JWev/յ|vpkea\WRMFC>:50,&#  &0:50.(%!  #,8CP]k2ֶwsmhd_YTOKE?<73.,&#  !*5AO\j2ͫvpkga\WRMHC>:71.(%#  (3?KYg4ĝzsnjd_ZUOJFA<830,&%!  &1:71.*&#  %.:FTan.ϫsnje_ZUPKFC>851,(%!  #,8CP]k2Şwqmhb]YTOJE?<730*&#  !*5AMYg.ٹtpjea\UPKFC>851,*%#  &1830,(%! C %.8EP]үqkgb]WTOJE?:730*&#  (3>JU'ȡwnjdaZWPKHC>:51.(&! > #,5AMܼqmhd]YTOJEA<830,(%! : %.8Cjүpkeb\WRMHC?:71.*&# 6 %.8Pƞvmhd_ZUPKFA>851,(%# 2 %.<Գkhea]YTOJFA<83.,&%! 1 %,qa_]\YUPMHC?:51.*&# . #Ħ}UPRTRROMJFA<851,(%#   ]W8:73.,&%! % !&,0378::88530,*&#   %&*,.,*&%!  ##%%##                     " " "  " """"""""""""""""""" """ " $ $ $"$""""""""" " " "" ""$"" " "" " " $ $ $ " " !! " "     " " " " " " "" """" $"$" #"$" #"$" #"$ !""!!$ " """  " """"             " " "  " """"""""""""""""""" """ " $ $ $"$""""""""" " " "" ""$"" " "" " " $ $ $ " " !! " "     " " " " " " "" """" $"$" #"$" #"$" #"$ !""!!$ " """  " """"             " " "  " """"""""""""""""""" """ " $ $ $"$""""""""" " " "" ""$"" " "" " " $ $ $ " " !! " "     " " " " " " "" """" $"$" #"$" #"$" #"$ !""!!$ " """  " """"  8BIMPatt8BIMAnno8B64FMsk 2 FTA耻Rl 8IMF6#,/ ,{IIQHHHLnrIIUIHHHHdgn,kII\=/JHHlHIUT&{cIIb2YHHUIII}>G[HHj$HHpHHHTrfUHHs*HHvOHHH|P 0QHH|;HH~rHHH\e1MHHݦPHHOHHPC OOO~OOOrOOOO`OOOOOSOOOOOOOOOOOOOObOOOOOOOOOOOOOOOOOOOO]OOOOOOOOOOOOqOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOPOOOOOOOOOOOOOOOOOQOOOOOOOOOOOOOOOOOOOֆPOOOOOOOOOOOOOOOOOOOOsOOOOOOOOOOOOOOOOOOOOO\_OOOOOOOOOOOOOOOOOOOOOO}ȃQOOOOOOOOOOOOOOOOOOOOOOW_OOOOOOOOOOOOOOOOOOOOOOOOoOOOOOOOOOOOOOOOOOOOOOOOOOaPOOOOOOOOOOOOOOOOOOOOOOOOOTˆUOOOOOOOOOOOOOOOOOOOOOOOOOOPˆUOOOOOOOOOOOOOOOOOOOOOOOOOOOOËVOOOOOOOOOOOOOOOOOOOOOOOOOOOOOUOOOOOOOOOOOOOOOOOOOOOOOOOOOOOQSOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOW}POOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOf}POOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOQ}POOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOcƆSOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOVӔYOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOPzcOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOsPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOmbOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOmROOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOmӀOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOrzOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOuxOOOOOOOOOOOOOOOOOOOOOOOOOOOOPxOOOOOOOOOOOOOOOOOOOOOOOOOOOPxROOOOOOOOOOOOOOOOOOOOOOOOOOuaOOOOOOOOOOOOOOOOOOOOOOOOOiOOOOOOOOOOOOOOOOOOOOOOOO][OOOOOOOOOOOOOOOOOOOOOOPOOOOOOOOOOOOOOOOOOOOOObmOOOOOOOOOOOOOOOOOOOOOyYOOOOOOOOOOOOOOOOOOORQOOOOOOOOOOOOOOOOOOUOOOOOOOOOOOOOOOOOOROOOOOOOOOOOOOOOOOPOOOOOOOOOOOOOOOOOyOOOOOOOOOOOOOOOO]OOOOOOOOOOOOOOOOOOOOOOOOOOOOWOOOOOOOOOOOOoOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOwOOOOWOOOOOOOlOOOOOkOOORO|OORoG !-#b ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! " ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! !                         !#%'()+,-./0123445556666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666  !#%&')*+,-../0112223333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333343333333333333333333333333333333333333333333333333333333333333333333333333333333 !"#%&'()*+,--..///0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000  !"#%&'(()**++,,,-----------------------------------------------------------------------------------------------------.-------------------.--------------------------------------------------------------------------------  !"#$%&&''(()))**********************************************************************************************************************************************************************************************************  ""##$%%%&&&''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''('''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''   !!""####$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$  !!!!!!!!!!!!!!"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!               [g"ysb *g8ܙ{un 7Yh}wp: Ue?'ysb 4t3ْ{un reʸRh}wp: [0ؤmC ysb  '~g"{un EyOh}wp: ^Hhyj!ysb }0{un l}wp: ^Yu!ys` Y %q{un 1|l|wp8 <^!xs`  r!{tn 7!MC(T{|vp8 !'aհxr` ags!P԰{tn  Qg֍|vp8 @IUְxr` ]3'm԰{tn %*2 g֐|vp8 *Y1ְxr_ y) 9gHҰ{tn \M$".666ACՐ|vp5 d"* խxr^ 4[0Ұ{tn =7h/vՐ|vp5 iivĿB }޼Jխxr^ ЊZV dyѝe-0Ұ{tn Ϧ@jMՐ|vp5 k/7լxr^ .~'Ѱ{tn *ÿŽNLJՑ|vp5 k9ȿǼž#+{N#cլxr^ Bǿ¿<ںtg^QPS^RNѰ{tn EǾ¿ſ¾`|!=Ց|vp2 kEƽJ8ժxr\ ?ƽþ#u@Ѱ{tn 9ÿ¾' gXՑ|vp2 o.ýLpjժxr\ #ûy?Ѱ{tn 4¹JՑ|vp2 qάGýž*&ժxr\ ϏXRe2=Ѱztn rl½¼78Փ|vp2 qOƾſý0gըxr\ ,ſĽ5"Ѱzsn ͽļcjՓ|vp0 p˒1@'դxrZ hUƿӸyskw$vPаzsn <zĽüT&>g?-Փ|vo0 t5zn"դxrZ ʏ¾ÿ6 lFаzsm ]Fż!n#NfՓ|vo0 w&nrF~~դxrZ ʝ%r1ϰzsm i = ||vo0 v2N`xrZ īvG3ϰzsm si)SMԖ|vo. v8A]z%ԟxrX ­uS+_кwJk ΰzsm jIr,QsԖ|vo. x17HPBZԟxrX £q?T8Dΰzsm ļe?666666666666666777777777777777777777777777777899:::::::::;;<=>=>>??@@ACCCCDDEFFHIIJJKLLOOPPPQQSTVVXXZZZ[]^```a¿~~~}y}vyztqpqlsnmkhnikbdgbc`]Y^ZZUY[XSUTQTRQPNMNLKLJJJJHGHGFGFFFDDCCCBBBBB@A@@???>????=======<<<<<<<<::::99::::::9:999998333333333333334444444444445555555555555555555677888888888999::::;<<==>@@@@AABBBDFFGGHHILLNOOOPQRSTUUXXYZZ[]]_}}~{{|w|q|qqopmkmlinhdde`jaa_\\\XZYVVRVWQRRMOQLMMJJKIIJGFEEEDDDCCCCB@AA@@???>>>><<<<;;<<;;:::9::9999998:8777776777777766665550000000000000011111111111122222222222222222223445555555556678888999::;==>>????@ACCEEFGGHHJKMNNNOPQSSTUUWXXYZ[þz|yuvvurvqpkmkghlhfebc_`^^[`XW][UWPQUORULMMIKHJHGFFEEDCDCBBA@A??@>>>=====<=;;;:9999888988777667666666644444444433344333332333--------------............./................./00111111111223444566677799:=====?@@@BBCEEFFHHJKKKMNOQRRRRUWWWYZ~~~{{{y|ursqkpjijkgggb`babd\][[VZTTUUQRMMLNKKKHFEFEECECCBAA@@@@====<=<<<;;;:997888777766555544444432223333211000111100000000/////.**************++++++++++++,,,,,,,,,,,,,,,,,,--../////////00111123334567789999:<>>>@@@ABBBCEGHHIJJMMOOPPRTUVWs¾~~z|}~|txrqtpliidjffddad[\\]\bZXTVTRRNOMMNIIJIGFHECEDBB@AA???>==<;;::::9998876655554444333322212221000////////...-----...---------,,,'''''''(''''''(((((((((((())))))))))))))))))**++,----....///////000123445777789;;;==>?AAAABCEFGIIJJMMOOOPQRT½|{wxq|wsuunpnileebde__a\ZY\UVV[TPWPOQOIKJIGGDEDBCAA@A??==<=<;;:88878887665554332222111110000///.///----,,,,+,,++++++****+************)$$$$$$$%$$$$$$%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&'''())))***++++++,----.//011445577888:;<=>>@@@@CCDFGGHJJLNNNNPQ~zx{s|tputkmmngcdkb`d]ZZ]X[TWQQRRQRMMJILFHIDFDDA?A@>>==;<;;999777655555433322200////./....----,,,,+,,*******)))((()((((((('''''''''&&&&&'!!!!!!!!!!!!!""""""""""""###################$%%&&'''''''(()((()+++++,-.//0011446667889;;==>>?@@BCEFGGGJJMMM\||ywvturumpllijfece_a`_]^YWWTSVUPQQLJILHHGFCDB@@>>><==:::9787756644232222110//....-,,,++,,++****)*)(((''''''''&&&%%%%&&%%%%%$$$$$$$$$######" !""##$$$$%%%&&('''())(())*+,-..//11444456798:;====>@AACEEEHHIKK~zzu{tospvmfkhgegbc^abZW]ZU^SUTPLPMMKHFICADBAA???==;;98877665533311100000/.---+,+++****)))))(''''&&&&%%$$###$$$####""""""""""""""!!!!! !!!!  !!!""""##$$$$%&&&&'''()*,---0012223457799;;<<==?@BCDDEFHIJÿ{}{ywvxwunrmrnhdcgbaa]YW]XXWWQPMNONHIMFGDFFD@A?>=<;;78876655424211111/...,-,,++******))((''''&%%$$$$$$###"""""""!!!!   """"#$$$$%%%&'('))),-./001123356789:;;<=?@BACCDFJ½}yu{rrrtplmggeig_]b^]]^TSXTRQOPMIIHHDIC?B@??@=;;:;77564533121000//---,++***)))('''''%&%%$$$$##"""!!!!   """"####$%&&'''*,,-..00111245788:::;=?@AABBC¿~}yzs{rvnnklifgge__^][\\\UWTRQNLPIJJEECA@F>=?<;;:9786744432210/.-/,,,+****)(((''''&&&%%$$##"""!!  !!!""#%%%())*++..///012455789::<=>@ABB¿~~w~xutoqnlmhgfcf`d_\X\XUUW[TMPKIMHKEADA?@C=><=;978743322100/---,,+*+*)()'&&&&&&%%$###""!!  !!###%''(((++,.../0122456899::=>ABŽ{}~vxrwsnqiikkifd__cb\^\V\XNSNUNIIIDJEGAB>A<:>79767665420/....-,,,++)(((''%%%$######"!!! !!!#%%&''))+,,-...1113468998:;=p¾zz}ywpqwnurkghd_c`\`Z^][WTSWNOFHJDCFFB@A<<==97968314142/0..--,**))('&&&$$$##""""! "$$%&&'())*+,--//0223678889;z~~szwtokuqfiidgad[[^YaSXSUPOJMNHDFHD>>A;;;<999564130///..,,++))(()&'%%%$#""""!   ##$%%&')))*+,,-./122557788<{}xyoptomlijegdaa`c\W^VQUSPOKIMEIFC@D=>;;:89885534300//,-,,+*))'''&%%###!!   !""#%''(((**,,,-/0124477Uÿ}|}vwwommnlqlcghi\aY[ZVXQPNQJINIEJCDB?<=<;867441311///---,,*)()('&&%$""""!!   !#$%%'(()++,,.//12356}zuwtqmlmoeijh_]bc[[UVSQVMLNHHDNCADA;=:;9:745331.0/.,,*+))'''&&####!!    !#$$&&()**+,,..02246¿~~{vzsnnkhgdjac_`]\[[ZUSRPKPLIJJDCA=;==996:45330100.-,,**((('&%$%##"!    !""$$&&))**+,-.013A¿~}}wqnpmiifkdgd__^X]ZTZVQNNMGFEM@CA>;:86974313/..--++,)((&&%%%###!    !!"#%%(()**+,.012~yxtwprmoghdbfac`]ZbSZVOORLIIGGDCC:=>998654411/0,+,+)))('&&%$$$#!!   !!!"$$&'((***..02ÿ~~|tyrrpnmmigb`\\^`TYRVRNQKIQGDAACA?:8976672111/0,-++)*''&&%%%#$!!!!    ""#%&&))*,,.2ÿ~xx}~tovroijbc`d]]XZVaRROMSJMFGJDD>?;9:8664432.2...-**))''&&%%$"#"!   !!"#%%'))*,.}~{xuvnrunsseece`_[^X[STOZLLOLJHHCADA@88664432/1-..-***)''&&&%$$!"!!   !"#$%'()++/|||u|vyuqkvmhkjhldbd^c_^_SWPQLNLQJJDD@G?==95469231../--**))(''&%%$#!!!!   !""$&')*,.Ŀ~}~x~vxvrpojrhljmgh`f_ca\\XTYNQPUHQPIB?B?=E=877442312.--+*)))'&&&&###""    !""$&')+-f}wvzxrttkohpelqgcacfbcd_]``ZPSQLKHNFJND?A>:<87657322//--,+*))'&&&%%#"#!!  !!#$')+/¿}}~y}yurssqmjrjfeibeia_eYab_X`W\SULQMLEHCDB>?;:8:9552400..,-+)))(&&%%$$"!    "$%'+-1ÿ|{wz~{twvqnjnkkidjgce``]_ZY^WY]ZTYPTOMILLHDF@A=::7777423/00-,+*+))&'&%%%$#"!    !#&&*.N¾}~}}t{qsupuunmmifdfgl`_a\eW^]U^SXWQUPOMOMKIOHD@D@?=7=5573322-.--++*))&&&&%%$#""    "#&)-/þwzusrskmnrhjjfdeccca^^YYWZV\]S\USRRRKHKLEIGJ@>?::=988474130.--,-+*(('&&&%%$#"!!!!   "$'-.>þ{}{xxvzyqkollkifhfjh]]_Y\_YXWXUTPSRUOSONHIGIIIHHECE=@;;:994564321/--,+++)((''%&&##"!!!!   #&(-1þwyvwwonoorkkfcedi^`_^[WXWWTQSPQOROSKOMJOOMFICCFBA@?@=;:97:5611200/-,,++*)))'&&&&$#"!!!!   #')-4yxtuumslnjili`bi_b[``V\XQQYTQTMYLLPKKMJFGEEKJGDAA;87=5555323013/-/,++)()''&&&%$#"""!   !$',2xxv{qinkgemab^d[VZXTTTWXRUPMONIIILIHGGGFFDB?@D>B><<@<<;;<;<757435432000/./,+**)()'''&%%%#"""!   "%)/3촼|}}{ulofshg^\_XaSXTTVPXSINNJMJIJJIGGGEDBDCABBE>??<<>9<9;:896876553544211./.--,,,)('''&&%%%$#"""!   !%)/4ܶo{wwjvfbdd`^`YRXROTMMPLMIRHGGFECACDBDCAAA??@@=@<=:<=<9=88776766663342331///--.,,+*(''&'&%%%##"""!    !$*0l̰}xuvt`hglZ^XQTUZMNMMSKKKGDKEFGCCEAA@??A@?>>><=>><:998987987876776634330010/./..--,,+*'('''%%%$##"""   "%).r}kwqdc^\Y^WZOOMMLGJKEGEIED@DCCAAB??=<>?=???<<<>;99:8977777665555433332110////...,,,,+))(&&'&%%$##"""   #%'.說wiiig`a_\TYZRMQLKGGEDFE>DC?A>>>>>>>@<<<<<;:;:<:9799987876764555333221111111////.-.,+,*+*))(''&%%%###"!!   !%&K؉{pdid_]`UUTR[QTPOILFEFABB??>?;@=<==::;:9;:99698979766666854553432212211111101000.-..-++****)))))&&%$####!!!   #&dxc]ZVVVXWYSSQTMKLJJJABA@>?;;:;::8::78886687778655655554344442223221111111111100//.--,,+++***)))(''($$$$##"!!!!   "$tKDbDAC<@>:8694654565866544435553554553333222232222210100000/....--.-,+*+*))))))'''(%%$####!!!!    #&(>H><9A@DDGFFDA@==A;6;6:886634505200222212232201112222111112111101100////.../,--,-,+,+**)))))(''''%%%$$##""!!   #$&-3.65<<<=<=<<9:86773132300000/00//0/./.00/0//000/0////0//01///////-..----,,,,+,++++))))(((''''&%%%%$$#"!!   "%''*/0444665444431111-//-.-+...-*,.--...,-,...../.-../....---------------++++******(((('''&&&&&&%$$#"!!!    "$&'')-.0-0...-,+,,*+**)*)+)**+****+,+,-+++++,,,--------,,,,,,,,,,,,,,,,******)))((('''''&&&&$$$$#!!!   ##$$')')((()'''(('''()''))('())'))))****++++*****++++*+++,,,,,+++++++*))))))))((''''&&&%%$$$$#""!   """$$%$##%%#%%$$$%&%'&'%&&&&'&''''')())())))*****))))))))*********))))((((((''&&%%%$##$$""""   !""!!!!!"!!"""""#"##%%%%%%&&'&&&&&&&''''(()))((((((()))))(((((('''''''&&&%%%$#####"""""!!    !"""!"###$$$$%%&&%%%%%%%&&'''&&&&(((((((''''''''&&&&&&%%%%$$#######""""!!!!     !!"!"""#$####$%%%%%%%%%$$$%&&&&&''''''''''''&&&&&%%%%%$$$######!!!!!!!     !!!"######%%%$###$$$$$$$$$%%%%%%%%%%$$$$$%$%%%$###"""!!!!!!    !!!""###################$$$$$$$$##$#########""!!!!!!!     !!""""#####################""""""""""""!!!!!!    !!""""""#""#######""##"""""""""""""!    "!!!!!!!!""""""!!!""!!!!!!!!!     !!!!!!!                                                              PMIFB@=9740-,)''#" PNJEB@<9731-,)'&#" OMJEB?<8731-,*'&"" OMJEB@<8720-+*&&"!  PLIEB@<9620-+)&&"! PLIFC?<8621-,)'%"! OLHEB?;7630,,)&$"!  NKHDB><7620,,)%$"  NKHEB>;7520,,'&%!   OKHEB><7520,+(&$!  OKHDA><751/++'&$!  NLIDA>;851/++'%$"   MLGDA=;741/,*'$#"  NKGCA>:641.+*'$#! NJHC@>:641.++'%$!   MKHC?=:740.,)'%$!  MKGC@=:640/,*'$$   MKGCA<:541/*)&##    MKFC@=:541/+)'$" MJGB@<9540.+)&%# MIFB?<9530-+)'$"  LJGB><9630-*)&#"  LIGA?;:62/-))%#" LHEA@<953/.)(%$" KHEB?;844/-*(&#"  KHEB?;843.,))%#! KHFA>;843/-('$#" KIF@=;843/,)'%#! KHD@><832/,)'&"!  KGD@?<832/,)&$"   JGD@=;842.,(&$!  JHE@=9742.-''$"   JGE@<:741.+'($"  JHD@=;630-*''#! JGD?>:621.*)'#  KGC?<:721.*''"!  KHC@<9630-*(&#!  JGC@<:630,+(%"   IEC>=:611,+'&#  HFB>=9610-*&%#  HFB?<:61/,)&%!  HFC?;952/+)&$   HFC?:742/+(&#   GEB?:7510+)%#"  GFA>:840.*)%$"    FDA=:630/)(%$!  FCB>;53/.*($#  GC@=;54/-+)$" GD@;9730,)(%"! FC?;9730,)'$" EC?;8520,*&$"   FA?;842/+)(#!  DB=;752-+((""  DC>:731-,)%"   DA?9941-+($"  CA>9851-*'%$  CB?8730-+'$"   F@?;63/++'$   E?<953/+*'$!  C@<863/,)&%  B@=852/*)&%  A?>751.*+%%   @>=9510+)$#  @>;761.+)%$  @>;751,,($#  A>;630,*'$#  A><640-)(#"  A<:630-*)$" A<:520,*&%!  >=;520,*'#! ><:520,)&$! >;942.,)$"   >;842.,($#  >;741/+'&#  >;750-*(%"  >;85/,*&$# >:640.*%$$ =:721-*&$" <9620-)%$" <9620-*%$! ;:63/,*&$   ;861.,)&$   :950.+(%#  <951/+(%" :851-+($#! <850.*'#"  <74/-+'#! 9741,*&$" <850-)&#! :73/-)(#  863/+)&#! :52/,)&"! ;52/+(&"! 862/,(%!  852.-(%! 732-,'%" 841-,'%! 742.*'$! 632.*&$! 631-*'$! 731-*&$  62/,*&$ 62/,*'$ 62/+*(#  520-)'#  520+)&#  62.+)&"  52.,(%# 520+(%# 51/+(%" 41.+(%" 41.*(%" 41.*(%" 41.*(%" 41.*(%" 41.*(%" 41.*'%" 41.*'%" 41.*'%" 41.*'%" 41.*'%" 41.*'$" 41.*'$" 41.*'$" 41.*'$" 31.*'$" 31.*'$" 31.*'$" 30.*'$" 30.*'$" 30.*'$" 30.*'$" 30.*'$" 30.*'$" 30.*'$" 30.*'$" 30.*'$" 30.*'$" 30.*'$" 30.*'$" 30.*'$" 30.*'$" 30.*'$" 30.*'$! 30.*'$! 30-*'$! 30-*'$! 30-*'$! 30-*'$! 30-*'$! 30-*'$! 30-*'$! 30-*'$! 30-*'$! 30-*'$! 30-*'$! 30-*'$! 30-*'$! 30-*'$! 30-*'$! 30-*'$! 30-*'$! 30-*'#! 2/,)&#  2/,)&#  2/,)&#  1.+(%"  1.+(%" 0-*'%! /-*'$! .,)&#  .+(&#  -*(%" ,)'$! +(&#  *'%" )&#! '%"  &#! %"  #! ! 9998887888888878888887888777777776776666777666676666776666676666666666666666666666666666666666555443210/.-,+)('%#!  5444555444455554444555444454444334333334333334433344443333333333333333333333333333333333333333222110/..-,+*)'&%#!  2222112222211112222112211111111110111110001110001111000000000000000000000000000000000000000000///..--,+*)('&%#"! //////...//////./////.......-----.------...----.------.---------------------------------------,,,++**)(('&%#"!  ,,,,,,,,,+++++,,++++++++++**+++++***+++++****++*****++****************************************)))((''&&%$#"!  ))))(())))))))()))((((((((((''''''((((('''((('''''((''''''''''''''''''''''''''''''''''''''''''&&&%%%$##"!  &&&&&&%%%&&&&&&%%%%%%%%%%$$$$$%%%%$$$$$$%%%$$$$%%%%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$####""!!  """"#####"""""""""""""!!!"""""!!!!""""""!!!!!"""!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!                      N        "  %  (  )  )  )  )  )  )  )  )  )  )  )  )  )  )  )  *  *  * +   +   ,!  ,!  ,!  -"  .$  /%  1&1  1( ;k  3)"+  4+#k  6-% F*  7/'""i  90)$  ;2+S  <42  >6  @b  GH      J    &  P    %  O    $  O    "  L    "  K    #  N  "  +   V'  .%  :,#  e6+"  A5)   @3(  >1&  =0%  ;.#  9-"  8+   7*  6)  4(  3'  2%  1$  0#  /"  ." -" -! ,! ,   +   + + *  *  * * + *  +   +!  ,!  ,!  -"  .#  .#  /$  0%  0&   2'  3(  4)  5*   5*!  6,"  7-#  9.$  :.%  ;0&  <0'   =2(   >2)!  >3)"  @4*"  @5+#  A5+$  B6,%  B7-%  D8.&  D8.&  E9/&  E9/'   E9/'   F:0'   F:0'   F:0'   G:0'   F;0'   G;0'   G:0'   G:/'  F:/&  F9/&  F9.%  F8.%  E8-%  D7-$  C7,#  B6+#  B5+"  A5*!  @4)   ?3(  ?2'  >2'  >0%  <;:876543110////..------,,,,,,,,,,,,,,,,,*'$    !!!"""""#%&()*,036:=@DH\ÿ~ytmga]WRLID@<:7531/.,+*)('&%$$$###""""!!! !    "#%)-038;@½ztmga[WOJC@;841/,*('%$"   "%),/48þwqld]VQKC>962/,)&#"   #&+.;wlh`ZSLF>940-)&$!  !%*rÿytmd\UOHB:41-*&$!  ")ztohea\TLG>93/+(%"   !,þysmib_YWQOHC<82.+(%#   #bùwohc^YTPMIFE@<951.*(%#   $qg\VQKIECB?=;:7632/,*'%#!  "reYQKD><;99876543210.,*)&$"   *399621/..///////.-,+)('%#!   "##$$&&''())))*))('&$#"    ""$$$$%%%$$#"!!    !!"!!!!                           !#%'()+,-./0123445556666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666  !#%&')*+,-../0112223333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333343333333333333333333333333333333333333333333333333333333333333333333333333333333 !"#%&'()*+,--..///0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000  !"#%&'(()**++,,,-----------------------------------------------------------------------------------------------------.-------------------.--------------------------------------------------------------------------------  !"#$%&&''(()))**********************************************************************************************************************************************************************************************************  ""##$%%%&&&''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''('''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''   !!""####$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$  !!!!!!!!!!!!!!"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!                )LPRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQN R Z_ a a a a a a a a b b cc e  g#P( i&4g k* ( m.3 oNX s 7 ~  =   < ~  <  $ L  0 q- o* m' k$i"hf e d c c b b b b cde f! h# i% j& l) n* p, q. r0 s1! u3" v4# v4$ w5$ w5$ w6$ x5# w4# w3! v2! t1 t0 r. q- p* n( l% j#h!fe c b a a a a a a ` ] mI!%&&&&&&&&&&&&&&&&&&&&&&&&&&''(()+.17=DKƽxnbXPG@;741/-+*))(''&&&&&&&&&#  $+2:º}pbVJ?70*&#  &Ażzi[N@5-'!  }~rg]UH<1*$  Ѱzh[RLE@<6/)$   dRE<654320-*'#   !$%&'''%#!          s!"4Mp# }_$"v%#p&^%m'&Qt(()**D,R,.U),L'&+E&)>>$>(="&= %=P/$="@B!Cn FjBK!Q W\kdNkaJr]hybp 3 u / o n  7g 4.3L;D]LW d^ 9sJ Q' o   -(Vn:WtH F Ty  Z  5!W T_# t% & (K * -! + 1}*m |/) ' !&s { n%" 8$7 # !7 f  h\I YQCUcfd-!TZ](AZnBAV/f( ( C:c? 'nQ}Ho@[MI< 9 *b  G  7  + { k .(   O       7     o  Z E 1    /  Z   H   L w m cd \ U M LG C ? ; 8 s Bk4x5 # c (  2tM G ]7x s  ueR Z[(.Gl fNYCD[jxpG 6# NGJlGgCoaD:fEsEGJMPUMZ_dJ$k |q w$ Q}s  -%  X E*  IQ  g 5   " x  S  `  *, ^ ?9  TE mT c G q      / }V {  ! 7 P TBf k}    7 !  R    a 'B+f~JHw%e\ao8p2# yKTe2 d A k   ~b. f3 f3 G _ { J    gs .e RW YvI 9 + q  FL z "  %  Z 1   P  e  6  C  Y ?e ,b_&\ZYWUS>QPO6NvMhMF\MPMHN~@O@7P.Q(S!UUVXZ ]_Qa*dgik09[8  b -* x5 B N0 w\j j ;z / r o  R L = @H  .f DT Z u/[  }uJx 2nOpbF4$Uq},r G  %k  6  "  P + ~ =  ]_   ,_  v!| :%+=05b <&2@xM]im{ez  4  { ( .) H8 cG|}VDev4U%?J'u( ~^v4? v#llDH<$1 o2 : [  p VY <% "S   `8 2;o]od]od~~^XPI/A_7, !$<> wXu> p5[(H 8'o ~ ~ M} })   f    M     O    qw ?q k d ] X mT EO J F C ? = q< Y: B9 .8  gXrgXrwv\ gXrgXrgXrgXr0Jf!W><|fBD"+so7F!U }dU s L  # a  @ z  / -  A ^( 3 _ ` P a c x e|  g iO l n q9@ twozN}bC6Th(yVD+6h7%w*H#1>P`q      )4 2 ;Y F O ,]  z < A k Ps 4S  E  | K* ~r-aNx;QY(, PcE^'% uhuhd`\XSLCDd-=<6M/^'r*U R &A ^L{#m%Ogvi4xL"wpj_d<^YUPLGDsAY=?:%75321100 %D0Le(:M`u%Gn.<Ije @x/R1un<)$:N<p=)?AtC_BE GIKoM8^O5Q SVtXHZ#[d]E_(a dgfDh$ikmro\qIs}5uf"vRxTVZ]`eiHnpsx }"$(&W(*-0*3e69=,@pDGLPOSW9\a.gu0 ]${Z9zC xj]rQBDl9S/?%)uO+bC(  1EYo!?b~)y:tOo=akeugc^Z&,! lWB. (Yp_{PpBf6[*Nfx  TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR !$'*-/2579;=?BCEFHIJKLMNOOPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ%OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO-xNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN "%(*-/2468:<>?ACDEFGHIJKKKLLLLLLLLLLLLLLLLLLLLLLLLLLLLL7xJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJBtIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII "%'*,.02468:;=>?@BCCDEEFFGGGGGGGGGLi89;<=>?@BBCCDDEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEViCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC !#&(*,./13467]i  "%')*,./12456789:;<<===>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>ii;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ii999  !#%'()+,-./01234455566666666666666666666666666666666666666666666666666666666666666666666666666666666666666ni3333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333xv00000000000000000000000  !"#%&'(()**++,,,------------------------------------------------------------------xx*******************************************************************************************************************************************'''''''''''''''''''''''''''''''''''''''''''  !!""####$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!     x" p. iB _ZRwF; 0$4V !!!"!!!!  pJT!!  9 3  l g8?|    o=%  IG :q                      OOOOOR4TBJ4DI2|X,0i+Nf'r-'-'{--/.e/<5Fb=I^ESQS.+aSurPKIODkEq9)eأrCj%H6/O s\+@A+az%X8=S)x+f>L[:~, #-C\n{_P&B48C1U.m+%"!"%"."8"?%E*K-Q2\8\?\H\H\HTEM?E9=ѥyT4)Dc77i011Z-+z*++]%+J!+=1=3C7S?lE&H=PkY+[Ye'kdr2|D()>/OCGs[5 0 R]>>/Pages 3 0 R/Type/Catalog>> endobj 2 0 obj <>stream proof:pdf uuid:b0ba8869-68b7-4741-8ede-f715b8dcd23e uuid:d4b6a6bb-2e79-46bd-8463-c73047cceb49 2012-09-20T20:47:50+01:00 Adobe Illustrator CS6 (Windows) 2012-09-20T20:47:50+01:00 2012-09-20T20:47:50+01:00 Adobe PDF library 10.01 application/pdf endstream endobj 3 0 obj <> endobj 7 0 obj <>/Resources<>/ExtGState<>/Properties<>/Shading<>/XObject<>>>/Type/Page>> endobj 8 0 obj <>stream HVn0+DkrhF~%Y;Z.wfVtv_{3i8 L"9Ӹ ? _?3$Ӈ@G)KB>ױ0}K1Ѱ1ggW}Չs.gk%Ř BKNm-VO6*lp4V%* d .\,Đ'%4UUO٪ꯗj\sŭ}_{J)]T.JV2fW:+^GCVof C]蔍 YEILjx(N Iqfk=<-N(#<ͫ9R)Qk KVjܨ4^,#oicBkM*W:&:B,PBeL.I1:6 pCrnb XjMI#-A; 8ꕢ9QDPF% ~H#Bv\_6<RCS 4x}c#LfUR(\f8,Knߡd=CO $>_e$L8ι^h|d:[J[;Fij?~PN׶ /~^Z=0S=t@1#1#1#1#1#1#1#1#k[b2n endstream endobj 9 0 obj <> endobj 23 0 obj <>/ExtGState<>/Shading<>>>/Subtype/Form>>stream q 1845.888 21.71 m 1845.888 9.805 1834.694 0.152 1820.888 0.152 c 1757.554 0.152 l 1743.747 0.152 1732.554 9.805 1732.554 21.71 c 1732.554 90.377 l 1732.554 102.283 1743.747 111.935 1757.554 111.935 c 1820.888 111.935 l 1834.694 111.935 1845.888 102.283 1845.888 90.377 c h W n q 0 g /GS0 gs -0.0000049 111.7827148 111.7827148 0.0000049 1789.2207031 0.1523438 cm BX /Sh0 sh EX Q Q endstream endobj 24 0 obj <>/ExtGState<>/Shading<>>>/Subtype/Form>>stream q 1657.258 78.312 -1440.688 33.533 re W n q 0 g /GS0 gs -0.0000015 33.5332031 33.5332031 0.0000015 936.9145508 78.3115234 cm BX /Sh0 sh EX Q Q endstream endobj 25 0 obj <>/ExtGState<>/Shading<>>>/Subtype/Form>>stream q 1663.144 0.071 -5.886 111.765 re W n q 0 g /GS0 gs 5.8857422 0 0 -5.8857422 1657.2578125 55.9536133 cm BX /Sh0 sh EX Q Q endstream endobj 26 0 obj <>/ExtGState<>/Shading<>>>/Subtype/Form>>stream q 1718.459 109.178 -18.227 2.586 re W n q 0 g /GS0 gs 0 2.5869141 2.5869141 0 1709.3457031 109.1777344 cm BX /Sh0 sh EX Q Q endstream endobj 27 0 obj <>/ExtGState<>/Shading<>>>/Subtype/Form>>stream q 1718.459 104.004 -18.227 2.587 re W n q 0 g /GS0 gs 0 2.5869141 2.5869141 0 1709.3457031 104.0039062 cm BX /Sh0 sh EX Q Q endstream endobj 28 0 obj <>/ExtGState<>/Shading<>>>/Subtype/Form>>stream q 1718.459 98.831 -18.227 2.586 re W n q 0 g /GS0 gs 0 2.5859375 2.5859375 0 1709.3457031 98.8310547 cm BX /Sh0 sh EX Q Q endstream endobj 29 0 obj <>/ExtGState<>/Shading<>>>/Subtype/Form>>stream q 1718.459 93.657 -18.227 2.587 re W n q 0 g /GS0 gs 0 2.5869141 2.5869141 0 1709.3457031 93.6572266 cm BX /Sh0 sh EX Q Q endstream endobj 30 0 obj <>/ExtGState<>/Shading<>>>/Subtype/Form>>stream q 1718.459 88.483 -18.227 2.587 re W n q 0 g /GS0 gs 0 2.5869141 2.5869141 0 1709.3457031 88.4833984 cm BX /Sh0 sh EX Q Q endstream endobj 31 0 obj <>/ExtGState<>/Shading<>>>/Subtype/Form>>stream q 1718.459 83.31 -18.227 2.587 re W n q 0 g /GS0 gs 0 2.5869141 2.5869141 0 1709.3457031 83.3095703 cm BX /Sh0 sh EX Q Q endstream endobj 32 0 obj <>/ExtGState<>/Shading<>>>/Subtype/Form>>stream q 1718.459 80.723 -18.227 2.587 re W n q 0 g /GS0 gs 0 2.5869141 2.5869141 0 1709.3457031 80.7226562 cm BX /Sh0 sh EX Q Q endstream endobj 33 0 obj <>/ExtGState<>/Shading<>>>/Subtype/Form>>stream q 1718.459 84.215 -18.227 2.587 re W n q 0 g /GS0 gs 0 2.5859375 2.5859375 0 1709.3457031 84.2158203 cm BX /Sh0 sh EX Q Q endstream endobj 34 0 obj <>/ExtGState<>/Shading<>>>/Subtype/Form>>stream q 1718.459 76.455 -18.227 2.587 re W n q 0 g /GS0 gs 0 2.5869141 2.5869141 0 1709.3457031 76.4550781 cm BX /Sh0 sh EX Q Q endstream endobj 35 0 obj <>/ExtGState<>/Shading<>>>/Subtype/Form>>stream q 1657.258 0.062 -1440.741 30.821 re W n q 0 g /GS0 gs -0.0000013 30.8212891 30.8212891 0.0000013 936.8876953 0.0620117 cm BX /Sh0 sh EX Q Q endstream endobj 36 0 obj <>/ExtGState<>/Shading<>>>/Subtype/Form>>stream q 1718.459 71.281 -18.227 2.587 re W n q 0 g /GS0 gs 0 2.5878906 2.5878906 0 1709.3457031 71.2802734 cm BX /Sh0 sh EX Q Q endstream endobj 37 0 obj <>/ExtGState<>/Shading<>>>/Subtype/Form>>stream q 1718.459 66.108 -18.227 2.586 re W n q 0 g /GS0 gs 0 2.5859375 2.5859375 0 1709.3457031 66.1083984 cm BX /Sh0 sh EX Q Q endstream endobj 38 0 obj <>/ExtGState<>/Shading<>>>/Subtype/Form>>stream q 1718.459 60.934 -18.227 2.587 re W n q 0 g /GS0 gs 0 2.5869141 2.5869141 0 1709.3457031 60.9345703 cm BX /Sh0 sh EX Q Q endstream endobj 39 0 obj <>/ExtGState<>/Shading<>>>/Subtype/Form>>stream q 1718.459 55.761 -18.227 2.587 re W n q 0 g /GS0 gs 0 2.5869141 2.5869141 0 1709.3457031 55.7607422 cm BX /Sh0 sh EX Q Q endstream endobj 40 0 obj <>/ExtGState<>/Shading<>>>/Subtype/Form>>stream q 1718.459 50.646 -18.227 2.587 re W n q 0 g /GS0 gs 0 2.5869141 2.5869141 0 1709.3457031 50.6455078 cm BX /Sh0 sh EX Q Q endstream endobj 41 0 obj <>/ExtGState<>/Shading<>>>/Subtype/Form>>stream q 1718.459 45.472 -18.227 2.586 re W n q 0 g /GS0 gs 0 2.5869141 2.5869141 0 1709.3457031 45.4716797 cm BX /Sh0 sh EX Q Q endstream endobj 42 0 obj <>/ExtGState<>/Shading<>>>/Subtype/Form>>stream q 1718.459 40.298 -18.227 2.587 re W n q 0 g /GS0 gs 0 2.5878906 2.5878906 0 1709.3457031 40.2978516 cm BX /Sh0 sh EX Q Q endstream endobj 43 0 obj <>/ExtGState<>/Shading<>>>/Subtype/Form>>stream q 1718.459 35.124 -18.227 2.587 re W n q 0 g /GS0 gs 0 2.5869141 2.5869141 0 1709.3457031 35.1240234 cm BX /Sh0 sh EX Q Q endstream endobj 44 0 obj <>/ExtGState<>/Shading<>>>/Subtype/Form>>stream q 1718.459 29.951 -18.227 2.587 re W n q 0 g /GS0 gs 0 2.5869141 2.5869141 0 1709.3457031 29.9511719 cm BX /Sh0 sh EX Q Q endstream endobj 45 0 obj <>/ExtGState<>/Shading<>>>/Subtype/Form>>stream q 1718.459 24.778 -18.227 2.586 re W n q 0 g /GS0 gs 0 2.5859375 2.5859375 0 1709.3457031 24.7783203 cm BX /Sh0 sh EX Q Q endstream endobj 46 0 obj <>/ExtGState<>>>/Subtype/Form>>stream /CS0 cs 1 1 1 scn /GS0 gs 1657.258 78.312 -1440.688 33.533 re f endstream endobj 47 0 obj <>/ExtGState<>/Shading<>>>/Subtype/Form>>stream q 1718.459 19.604 -18.227 2.587 re W n q 0 g /GS0 gs 0 2.5859375 2.5859375 0 1709.3457031 19.6044922 cm BX /Sh0 sh EX Q Q endstream endobj 48 0 obj <>/ExtGState<>/Shading<>>>/Subtype/Form>>stream q 1718.459 14.43 -18.227 2.587 re W n q 0 g /GS0 gs 0 2.5859375 2.5859375 0 1709.3457031 14.4306641 cm BX /Sh0 sh EX Q Q endstream endobj 49 0 obj <>/ExtGState<>/Shading<>>>/Subtype/Form>>stream q 1718.459 9.257 -18.227 2.587 re W n q 0 g /GS0 gs 0 2.5869141 2.5869141 0 1709.3457031 9.2568359 cm BX /Sh0 sh EX Q Q endstream endobj 50 0 obj <>/ExtGState<>/Shading<>>>/Subtype/Form>>stream q 1718.459 4.084 -18.227 2.586 re W n q 0 g /GS0 gs 0 2.5859375 2.5859375 0 1709.3457031 4.0839844 cm BX /Sh0 sh EX Q Q endstream endobj 51 0 obj <>/ExtGState<>/Shading<>>>/Subtype/Form>>stream q 1700.232 0.161 -11.752 111.765 re W n q 0 g /GS0 gs 11.7519531 0 0 -11.7519531 1688.4804687 56.043457 cm BX /Sh0 sh EX Q Q endstream endobj 52 0 obj <>/ExtGState<>/Shading<>>>/Subtype/Form>>stream q 1730.211 0 -11.752 111.765 re W n q 0 g /GS0 gs 11.7519531 0 0 -11.7519531 1718.4589844 55.8823242 cm BX /Sh0 sh EX Q Q endstream endobj 53 0 obj <>/ExtGState<>/Shading<>>>/Subtype/Form>>stream q 1758.034 0.08 -5.886 111.765 re W n q 0 g /GS0 gs 5.8857422 0 0 -5.8857422 1752.1484375 55.9624023 cm BX /Sh0 sh EX Q Q endstream endobj 54 0 obj <>/ExtGState<>>>/Subtype/Form>>stream /CS0 cs 0 0 0 scn /GS0 gs 1657.258 0.062 -1440.741 30.821 re f endstream endobj 55 0 obj <>/ExtGState<>>>/Subtype/Form>>stream /CS0 cs 1 1 1 scn /GS0 gs q 1 0 0 1 236.0518 95.0776 cm 0 0 m 0 7.975 -10.367 14.694 -16.382 16.767 c -19.535 16.767 l -22.959 15.544 l -236.052 -39.125 l -16.957 -16.955 l -19.256 -11.925 0 -8.235 0 0 c f Q endstream endobj 56 0 obj <>/ExtGState<>/Shading<>>>/Subtype/Form>>stream q 236.052 95.078 m 236.052 103.053 225.685 109.771 219.669 111.845 c 216.517 111.845 l 213.093 110.622 l 0 55.953 l 219.095 78.123 l 216.795 83.152 236.052 86.843 236.052 95.078 c W n q 0 g /GS0 gs 12.5297241 -40.9828911 -40.9828911 -12.5297241 113.6064453 90.6855469 cm BX /Sh0 sh EX Q Q endstream endobj 57 0 obj <>/ExtGState<>>>/Subtype/Form>>stream /CS0 cs 0.008 0.008 0.008 scn /GS0 gs q 1 0 0 1 210.6714 2.1484 cm 0 0 m 5.846 -2.086 l 15.736 -2.086 l 20.657 1.111 25.38 5.921 25.38 11.303 c 25.38 20.93 5.744 22.692 5.947 28.735 c -210.671 53.805 l -0 0.001 l h f Q endstream endobj 58 0 obj <>/ExtGState<>/Shading<>>>/Subtype/Form>>stream q 210.671 2.148 m 216.517 0.062 l 226.408 0.062 l 231.328 3.26 236.052 8.069 236.052 13.452 c 236.052 23.079 216.415 24.84 216.618 30.883 c 0 55.953 l 210.671 2.149 l h W n q 0 g /GS0 gs -13.3173065 -40.9864464 -40.9864464 13.3173065 126.5410156 60.1508789 cm BX /Sh0 sh EX Q Q endstream endobj 59 0 obj <>/ExtGState<>/Shading<>>>/Subtype/Form>>stream q 1757.718 0.062 -5.886 111.764 re W n q 0 g /GS0 gs 5.8857422 0 0 -5.8857422 1751.8320312 55.9443359 cm BX /Sh0 sh EX Q Q endstream endobj 103 0 obj <> endobj 66 0 obj <> endobj 11 0 obj [/ICCBased 105 0 R] endobj 104 0 obj <> endobj 106 0 obj <> endobj 107 0 obj <> endobj 108 0 obj <> endobj 109 0 obj <> endobj 105 0 obj <>stream HyTSwoɞc [5laQIBHADED2mtFOE.c}08׎8GNg9w߽'0 ֠Jb  2y.-;!KZ ^i"L0- @8(r;q7Ly&Qq4j|9 V)gB0iW8#8wթ8_٥ʨQQj@&A)/g>'Kt;\ ӥ$պFZUn(4T%)뫔0C&Zi8bxEB;Pӓ̹A om?W= x-[0}y)7ta>jT7@tܛ`q2ʀ&6ZLĄ?_yxg)˔zçLU*uSkSeO4?׸c. R ߁-25 S>ӣVd`rn~Y&+`;A4 A9=-tl`;~p Gp| [`L`< "A YA+Cb(R,*T2B- ꇆnQt}MA0alSx k&^>0|>_',G!"F$H:R!zFQd?r 9\A&G rQ hE]a4zBgE#H *B=0HIpp0MxJ$D1D, VĭKĻYdE"EI2EBGt4MzNr!YK ?%_&#(0J:EAiQ(()ӔWT6U@P+!~mD eԴ!hӦh/']B/ҏӿ?a0nhF!X8܌kc&5S6lIa2cKMA!E#ƒdV(kel }}Cq9 N')].uJr  wG xR^[oƜchg`>b$*~ :Eb~,m,-ݖ,Y¬*6X[ݱF=3뭷Y~dó ti zf6~`{v.Ng#{}}jc1X6fm;'_9 r:8q:˜O:ϸ8uJqnv=MmR 4 n3ܣkGݯz=[==<=GTB(/S,]6*-W:#7*e^YDY}UjAyT`#D="b{ų+ʯ:!kJ4Gmt}uC%K7YVfFY .=b?SƕƩȺy چ k5%4m7lqlioZlG+Zz͹mzy]?uuw|"űNwW&e֥ﺱ*|j5kyݭǯg^ykEklD_p߶7Dmo꿻1ml{Mś nLl<9O[$h՛BdҞ@iءG&vVǥ8nRĩ7u\ЭD-u`ֲK³8%yhYѹJº;.! zpg_XQKFAǿ=ȼ:ɹ8ʷ6˶5̵5͵6ζ7ϸ9к<Ѿ?DINU\dlvۀ܊ݖޢ)߯6DScs 2F[p(@Xr4Pm8Ww)Km endstream endobj 12 0 obj <> endobj 101 0 obj <> endobj 102 0 obj <> endobj 110 0 obj <> endobj 111 0 obj <> endobj 112 0 obj <> endobj 100 0 obj <> endobj 98 0 obj <> endobj 99 0 obj <> endobj 113 0 obj <> endobj 114 0 obj <> endobj 115 0 obj <> endobj 97 0 obj <> endobj 96 0 obj <> endobj 95 0 obj <> endobj 94 0 obj <> endobj 93 0 obj <> endobj 92 0 obj <> endobj 91 0 obj <> endobj 90 0 obj <> endobj 89 0 obj <> endobj 88 0 obj <> endobj 87 0 obj <> endobj 86 0 obj <> endobj 85 0 obj <> endobj 84 0 obj <> endobj 83 0 obj <> endobj 82 0 obj <> endobj 81 0 obj <> endobj 80 0 obj <> endobj 79 0 obj <> endobj 78 0 obj <> endobj 76 0 obj <> endobj 77 0 obj <> endobj 116 0 obj <> endobj 117 0 obj <> endobj 75 0 obj <> endobj 74 0 obj <> endobj 73 0 obj <> endobj 72 0 obj <> endobj 71 0 obj <> endobj 70 0 obj <> endobj 69 0 obj <> endobj 68 0 obj <> endobj 67 0 obj <> endobj 65 0 obj <> endobj 63 0 obj <> endobj 64 0 obj <> endobj 118 0 obj <> endobj 119 0 obj <> endobj 120 0 obj <> endobj 121 0 obj <> endobj 61 0 obj <> endobj 62 0 obj <> endobj 122 0 obj <> endobj 123 0 obj <> endobj 22 0 obj <> endobj 124 0 obj <> endobj 125 0 obj <> endobj 126 0 obj <> endobj 127 0 obj <> endobj 128 0 obj <> endobj 129 0 obj <> endobj 130 0 obj <> endobj 131 0 obj <> endobj 5 0 obj <> endobj 132 0 obj [/View/Design] endobj 133 0 obj <>>> endobj 13 0 obj <> endobj 14 0 obj <> endobj 15 0 obj <> endobj 16 0 obj <> endobj 17 0 obj <> endobj 18 0 obj <> endobj 19 0 obj <> endobj 20 0 obj <> endobj 21 0 obj <> endobj 10 0 obj <> endobj 134 0 obj <> endobj 135 0 obj <>stream %!PS-Adobe-3.0 %%Creator: Adobe Illustrator(R) 16.0 %%AI8_CreatorVersion: 16.0.0 %%For: (Anders) () %%Title: () %%CreationDate: 9/20/2012 8:47 PM %%Canvassize: 16383 %%BoundingBox: 207 2447 2054 2560 %%HiResBoundingBox: 207.3994 2447.3936 2053.2871 2559.3286 %%DocumentProcessColors: Cyan Magenta Yellow Black %AI5_FileFormat 12.0 %AI12_BuildNumber: 682 %AI3_ColorUsage: Color %AI7_ImageSettings: 0 %AI3_Cropmarks: 143 1749 2089 2885.0308 %AI3_TemplateBox: 1500.5 1499.5 1500.5 1499.5 %AI3_TileBox: 695.0098 2019.415 1536.8701 2614.4351 %AI3_DocumentPreview: None %AI5_ArtSize: 14400 14400 %AI5_RulerUnits: 2 %AI9_ColorModel: 1 %AI5_ArtFlags: 0 0 0 1 0 0 1 0 0 %AI5_TargetResolution: 800 %AI5_NumLayers: 1 %%PageOrigin:0 0 %AI7_GridSettings: 72 8 72 8 1 0 0.8 0.8 0.8 0.9 0.9 0.9 %AI9_Flatten: 1 %AI12_CMSettings: 00.MS %%EndComments endstream endobj 136 0 obj <>stream %%BoundingBox: 207 2447 2054 2560 %%HiResBoundingBox: 207.3994 2447.3936 2053.2871 2559.3286 %AI7_Thumbnail: 128 8 8 %%BeginData: 3677 Hex Bytes %0000330000660000990000CC0033000033330033660033990033CC0033FF %0066000066330066660066990066CC0066FF009900009933009966009999 %0099CC0099FF00CC0000CC3300CC6600CC9900CCCC00CCFF00FF3300FF66 %00FF9900FFCC3300003300333300663300993300CC3300FF333300333333 %3333663333993333CC3333FF3366003366333366663366993366CC3366FF %3399003399333399663399993399CC3399FF33CC0033CC3333CC6633CC99 %33CCCC33CCFF33FF0033FF3333FF6633FF9933FFCC33FFFF660000660033 %6600666600996600CC6600FF6633006633336633666633996633CC6633FF %6666006666336666666666996666CC6666FF669900669933669966669999 %6699CC6699FF66CC0066CC3366CC6666CC9966CCCC66CCFF66FF0066FF33 %66FF6666FF9966FFCC66FFFF9900009900339900669900999900CC9900FF %9933009933339933669933999933CC9933FF996600996633996666996699 %9966CC9966FF9999009999339999669999999999CC9999FF99CC0099CC33 %99CC6699CC9999CCCC99CCFF99FF0099FF3399FF6699FF9999FFCC99FFFF %CC0000CC0033CC0066CC0099CC00CCCC00FFCC3300CC3333CC3366CC3399 %CC33CCCC33FFCC6600CC6633CC6666CC6699CC66CCCC66FFCC9900CC9933 %CC9966CC9999CC99CCCC99FFCCCC00CCCC33CCCC66CCCC99CCCCCCCCCCFF %CCFF00CCFF33CCFF66CCFF99CCFFCCCCFFFFFF0033FF0066FF0099FF00CC %FF3300FF3333FF3366FF3399FF33CCFF33FFFF6600FF6633FF6666FF6699 %FF66CCFF66FFFF9900FF9933FF9966FF9999FF99CCFF99FFFFCC00FFCC33 %FFCC66FFCC99FFCCCCFFCCFFFFFF33FFFF66FFFF99FFFFCC110000001100 %000011111111220000002200000022222222440000004400000044444444 %550000005500000055555555770000007700000077777777880000008800 %000088888888AA000000AA000000AAAAAAAABB000000BB000000BBBBBBBB %DD000000DD000000DDDDDDDDEE000000EE000000EEEEEEEE0000000000FF %00FF0000FFFFFF0000FF00FFFFFF00FFFFFF %524C45FD0DFFCFFFCFCFC9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CF %C9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CF %C9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CF %C9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CFA8FF7DA87DFFA8FD0EFFCAFF %CAFFCAFFCACFC9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CF %C9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CF %C9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CF %C9CFC9CFC9CFC9CFC9CFC9CFC9CFA8FFA8FFA8FFA8FFFFFFA8FD06FFA8FF %CAFFFFFFCFFFCACFCACFC9C8C7C8C7C8C7C8C7C8C7C8C7C8C7C8C7C8C7C8 %C7C8C7C8C7C8C7C8C7C8C7C8C7C8C7C8C7C8C7C8C7C8C7C8C7C8C7C8C7C8 %C7C8C7C8C7C8C7C8C7C8C7C8C7C8C7C8C7C8C7C8C7C8C7C8C7C8C7C8C7C8 %C7C8C7C8C7C8C7C8C7C8C7C8C7C8C7C8C7C8C7C9A8A87D7D7DA87DFFA8FF %A8FFA87DA87DA8A1CAA7CAC9C9A1C9A7C9A1C9C2C19EC7C0C19EC7C0C19E %C7C0C19EC7C0C19EC7C0C19EC7C0C19EC7C0C19EC7C0C19EC7C0C19EC7C0 %C19EC7C0C19EC7C0C19EC7C0C19EC7C0C19EC7C0C19EC7C0C19EC7C0C19E %C7C0C19EC7C0C19EC7C0C19EC7C0C19EC7C0C19EC7C0C19EC7C0C19F7D7D %2727277D52A8FFA8A8A8FFA87C76277CA1C9A1C9C9CAC9CAC9CAC9C9C0C7 %C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7 %C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7 %C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7 %C0C7C0C7C0C8A8A97DA87DA984FFA8FFA8FFA8FD04FFA8A8A1A17CA17CA1 %A0A1A1C89E9E9E9F9E9E9E9F9E9E9E9F9E9E9E9F9E9E9E9F9E9E9E9F9E9E %9E9F9E9E9E9F9E9E9E9F9E9E9E9F9E9E9E9F9E9E9E9F9E9E9E9F9E9E9E9F %9E9E9E9F9E9E9E9F9E9E9E9F9E9E9E9F9E9E9E9F9E9E9E9F9E9E9E9F9E9E %9E9F9E9E9E9F9E9E9E9F9E9E757D7D2727277D52FD06A8FD09FFA8A7A0A1 %A0A1A0A0749F749F749F749F749F749F749F749F749F749F749F749F749F %749F749F749F749F749F749F749F749F749F749F749F749F749F749F749F %749F749F749F749F749F749F749F749F749F749F749F749F749F749F749F %749F749F749F749F749F74752752F827F85227A87DFD04A8FD0CFFA8A8A1 %A0FD04745074747450747474507474745074747450747474507474745074 %747450747474507474745074747450747474507474745074747450747474 %507474745074747450747474507474745074747450747474507474745074 %74745074747450744B27F8F821F8F8277DA87DA87DFF %%EndData endstream endobj 137 0 obj <>stream Hԗ[ۺ ;UԢ(idMNQa{B~gH]^R@1uȑѳ?_̖ۏKٳk;:z^v{ U]nݜ=ϳ^D_P~ݞ NeG]g'dt&KۭݒgUf\?"' c#i,?ownJS͍q,P n&](,-Wv?WnWlnwߚMƝ&G^oF}W^[f dc 5\.3s}rU~CyiQ%DS9UBz&=Ҕӫ9^nHZPse.z(Pe~=l7mP!~cmaOxR/]^7ĸ-7uoׇ=S熧@_o-9|v=vw\ҭ ;~MJڗxo.^O+^Ve!vLl烧:/\n?Khzޭ6x>{7鶇WO칟W~/\`1J\m#?6wї/qDǧ/O _[mPlߛ)ͦ"1uwH6zB$m<[OzTn)./|ܮWf8nv_;>G(on}!CS͟yѬ׫뮹:A}EW'}9=Drwϴ~fctHiE9OtqXq7D[\b./_gIo /]\a}A2ainCNc3Q9nLv08e.w\M%S)2TLj4|>}ZeZq8\$tf%EG,gK9a_ʬ 5Hu .U(ުnyԮ?B{=Y\񱸳^#UX|6cMM!MՂxM)"3u* E FLy<['|_-{!%LY6s y+AZ -hȈO*ĝcR&q#z+dؘ!E$QL>b#sap==g6e.XOaf T,qWq-bAJVX0dBZRH)?H( fUJ)(\Zhas]RWx!1%d)k4;^=>((qSH0"9- \停zXa-PsuJń׻צ^ QBD+'9~+a5Y !)f3JLOvrn~ުx0I6a/*5TeNz~xI.{BDړ)J^2M!PtQ IE$GrX,-&˄MͯTBڜ'aJ 5iab]* TeP@X¤MujXE.1Rf[ rB0 3,@u,kϩ%i)-< Vz-zu  &jD c/9R`,‚_ dqCha{ٲ`! 3ߛ fӽqPG&Nڽ[f! =(4<=L4$W1OA;,%%X%S1M`鈤;b&bIIPI"(}GDa@(P3&g 8Aa1q`8s8RO]Fi\B#9#Sc$1 qT*(>"y@QO=F E=C郑hdhC:H=;}QI>W\i/h3Zsϝ*=OAYYϥK%!8*RNexK@Y6ɫh$An2o"|NB ߞe%WIRpZz&Ccƚ`] ͠0L_ ga{{K *9zvPZ3OXBePR 쒒nJDOjG{)I^oϔ-Gh%6QLBF+jّ7검}i)Qdlb,9822IQG,^X)̈|Dl kf(A78"v|Y W#g3<xVGÂ\ HG"q$]8FQDY[*%|%@늦y>g' "܈k5Ñ kNIQ\|XMXsfΫG>O'XӳӷsCo+_ jUսQsCoW&46Mw( 7lVV6f(PUY7Z:3eEI SOtQύvYe2K[E}={55jXo|PV'$Lh7R&fcC2uBʎ3MuD?gW+O茨7G"Fš93^;xSG$/m'sA=(žrdP|zDX=:!y9{.5l:6ӧUehv0[O^o)\Kml@e~څZѪ L'`X4253݇U1_cDTq[]7¥9l1E!~,5@44P]4zx'ƫ32:3qQ՘uZ;{R=q ;pÁ8:' eF-o#;AB_X1v΃IGF]&a*-H(=ՀD C'ZQ7P j^JP+TʡP|4-$r6PI+ҎCC hHUY[3tHWLj"t=HfHydcKcpKD)#,Ԋ.F.RTLO)E,!R/JU4WUj]lë1^嶰[}nnwXy\% %:V2lqxjw,a', ;yvyxgaZ.sbZy~|b` K3YvM Z#y,^nI'JZc*'ZZx>[o+rSջ7I~xѷO~㛛_2~;vX~ܾ}?o?O?۷g ?=;o CGGq(jc#bū6|C[0HK@@BOlKݫݯ㭦'3"IAY<(ΫC)ʵhע^7 ziT@yPfdSgZ] OS.;ܿhIOedX_wLneZ4Q3ӽ(7) @{EbCn5 @ERIыZ) @!*!>ՎgݱMv}߳_h;bV(8 $.C-qBCsnJs\ƫ @1' #ϗ;8u_1't܌>Mօ>1c[fb\qL+)@ca-LB*n&3r*0]%m%E]ƮK= G[>Gfd;Nnp~\;"Q;AjDcD%q)&S$*bBStN5Tl|bDlفR|WɒySe=MxA(X!۹66(mUrFfW*WӂzbA33g]aCwLJ)7eE0 j#l3[ (垥scr"/9Ie4]8N8d tͤ O>яgtel,`Cޙ`(_,$=z9Z*1>?c-X GڸxUCNlPs|?>Dg8B|2*>"P#=>wGӠ#}t!QHObBi<ćtB(P3O3С#oD'^P&+6Х{=Ϝr]]СK !)l@%D*zB!+r\ $A^du8N[YjS9E/_Bb8Pbh*H<,"3 َ:CX0m}Ne Fq}g؈!)dhnlħe[:0Z!FUEX& `*yyY*wWF~ :ۊޙG#v<lG]k-wN 4\BqA T` éF*F;$IS&ӗTb SwU}WZaR.H !1, }kKGy'_ #DQ\Afէ}ΗEff(k$VaQBZN8!bLbx9+n3B~6%*2dY8uwiUVRKrH ?x+=yRw.ΐ'N?8|<y•*Te 8*{}ŒQqJesc764ž$Ve%])dR*!sLw{Uupz ب|ԉu*Aw 0G^4V9Βhg<rZx.[XasH. ȭ@ !tӺàbAt# `*A٣S\r*uTC\q 5qS˪* ݎP/} }_A]#NwǬp}“8,o_ F8,/YwbMY&::KVվ׺`蠻I q cbjAl] Bk|Ψ$h2|:Oc d>sq>w3|f9Y|r z;.< ֪bj~ 5>7'gs?ٟIkS61>'L'}>l<)J}RżBzАڿ'OaE\Czv_{N< kYvOvjXŲwqwR/VKUgZ'6mWsKEvGd Wܲy/ JI<ѶW)xjٸx>td|L[tOX!l6W Ӕ׆s˦YoT]2kft׻vw%`¬SpUqlՃO0WiyqN>q.3&;9NؓOOcwh8-kB>b%$*OiqrC1`Q f3]IP c~/+*"E*UuSŔt?5geP3*s,\YY\$~ X`= pwّC7v9✀ c##g>819΅B gb.ǧQ/YX_puqa"3h<0!~ ;('' nEVLW'+e"9zSsa *3٤\6|*mvo=e6k(B)9Mt5טd0+絤a*Y j&'@ˀ5WgςL'lEݽmχw{1x@^A=Lryݲ!s;Z}-@`{Ow8_GJVf#^3a5)@Z ^3ѫ&z~ =C]xm^*aϝ׃*GD"zde/-x>}_ڤI~>{/>T0>ޔnGҏ_2}}ѻ;6mS"E l|mLAsJUicvdȪr]:{ ;E=J(KLbT:ɻoPN߆ƥSS*IUGt:56V^Qݗz):V!aMYy+W2_}W]fmyT "(oU^oo {H}ӬC`T‡kzBByPgI#!n6y)3ԎY&PPOy'˯{jTROuTY\eRPdM?Ge>ei<2U)E&ojQ"M=,=c{1*O՘]Swijq>jєE)ESMJ4- צ[Ho'U 4 E>*QJ}Rܟ8Mg׼S'4n |G<(tmJ攥9e/e~MPh\*SU"!&4MMJ叿'rL1 6.nd&ތ97%^%_k?pG!OcNQ7Z'LyKe>i8'm[PA=~|/|֎wY(FuiF~mTPd n7|`q0n8[G=֣pe+"[}cLcV>gqkv3qoK~cQx/@6M@Ū`6Ed, 2 /E:s!YI~dg0.$;]Qs羚>O_??;\||~xj[ p}\m)~JRsT~f:.Eϸ푃Wٝ]}uչW^=3;O^Ձk2 ,[5{kvT%fsq^)ikc_6٭7޸sEopŝ'^g:YGLzc5 h=Uϋ܄Zpq_Gyo/oۄ]}Wwy2뗮2C/I4x ugt9Idw"s:tJ:үt6]D5$,Y(Ӏy922Tlk^S~\uM؛9šӰDu"nu&Vf.]\Vwd`. u6;77Ū 2xI!QT!sn.NXD[\ qмavrs7Y1!X&@pOEhBƣp6p;̆,ޡ+#dq } C n*3E6$3p=O]i ,'yr<_rOVS%mg^XӜg=S\oOx}Wso5^- Fǩ8nT NNrĉz쌙M878wS̹CFtã+3zp2 ¢o33:p,0ekG]ڡA" rكN`6`vMhX 1Sx@= CcxLLʴV,Do] P8$zfl*7B<O"  xAeG诇HR j$-eDoX{d| gy. `pLƐ` zni^{p; U ܀t vD1D|c&x 1= ү5\\tݽΕU54ųo,o+>9iԧr0Fabͨtߕ?ɨ5/]M_}<|8=?w<?7O<冯(nS6h7//j*K)\1pDP MeX˲1 )EOa5 2RYM1-#+k!(ȑRǥ,9$$DDq2%B%RJjv%%B%p[d @)\.De "eB&Ŗi.G"h4 % Ve- [*oUDz.gU|\* ^˕+Wor8QȔ˪r^cܢW^6WE+_ j \!3+KD&~'W^e2+ۑ\˵;؝erB Bo r ;{Q[n/VD. Bh!O ϕbuϕsd`8G( +cGѓc$K)1$FG%H %,$-,SX^#S,$YYvRh֛Scdp.fL4hḵ%[/8[yU*oMTxᡋ\yLd+4J]m9mtC$R*ѝJ*LL+^#̞}ՑHqeV.[r.j髎T.Ie$j㬆TZKAvW&g %N+O!SVVV,C۶, nQ_p]q\]|Jd:xyAP*f}-+5p&7.>bPYHQW|kOԭV\SLVȪa[ [><-Ek%Qa&IΏ%L00#%QI@Q/nCמ-xۑ4P@*-D_z6Z5S֜ Ww._ԴqSC.\qHn~)T)Ֆҥ?Kg蟥-R,#7,ˢMDLh{byP%EOb̥KlD:b|^L%XGI Q5,T$$z(& Cx. ,IĜ XK1z#)k),X]"~aܔ$9 yASYRy1?I^2F(492Cp35{:ZǝR\Na#+-TC5 I [/H͆.%tOLKVa"ψC02;H*$+I$$=tcj0 H9tchqN&*/M&I{ʟd2So29B0Yn}e2,L^鮈$3r " RoAe%rl+lI&2,D$R#*ǎ< DFHD"%)i;yx4GG$ʅG2=g3Lcqqfjrq+7i\Y䶂 +G^TW?{*rOP99eS'\n\>|]|2->>)\''*3}.Ur_l.cPri7\W쮸 KPw׹T%욻{x'|Uh?9eƏǃ.o;v\D~_nj͇o橼pm\4{l1&qy.XG_K\" `E"8&4EGhCkG؃,}Z!`00&#cpfPaԷn`z 0CE0bh0=f3x=n@K ſM碧r~͐(Y^{7NN]-k~f *2pd7Y29rA6Pʌ0h$dl4E"  W,X_lXTlX,B}%0٦6j`{`5h b.}b xiuC8k&Π}N3ctCŇ -fNߨx7h_P^ܼwf9{|S%ϣ[gsWTO3 S+$B<3 2$dF}<L8 П1z ``0e 0qptL͉  W|dFՌ.;YRRL;I:$ŧ)bpfRGPen&P(f`OJ}t& ON O-a!PlTp46cOD':Έn0NC#|/W-jO|$?hmOO__)_^{t?sys۸Y^y䛾nFwX6]zx~B_͵o{Zb5V/}"uTƒXaغMQ=@[P ÀlC–G+h 52+CX6DQMch˼DJb tq1Yx|% Wk#~_5M37= \؅8v#Z4͈\k 6PJ%5P{dx( 4"- _'H(uBյwa ja0$ܚ0SߞE!@VG Ww+;hBDOy9gڼ' |18cgpz'%G V')5Gcah|Oݜ쒛\t7488sҡ uN6`G;XoV(ۣ# dn0Xc6HY'=鈠W{-g5WNYeVޖ-.QsÒQ LnÍ-*̎ev䈝`)òv= %"@p, LN&2K}C40̓iǓ; &B#Hb!S,3rJD+Sdpݓݲͺ\h3ߪ_5 (0e^)s/UI*h;ޑ.{Si)@t${(Z*5 )@-!. |߸.gwN +=:;`2l'Ug#k'Ꝕ~o~}'^~?K}?^^>}.out6~g_%W O&i5lmΌ#|XGJ2K5v5j1s5HlesT!S.l+FGأ1d逳hG\Gp>l@&dnLH0[XbFr`,K/溵xlmm<[?=5F6FMS{Y@uD2+;_BvkV;Eg 9Y&jy(ULUv*ki=+Ad%ZV ̩Ͷ4[Ӏgi/UaYj;y/JC2i^ԍR2K>ZJȺ'/NTV'G G;.Oy}˓YwqeZn@4kW^BzKGJ'F@2lݲe8=_^Ikv_Wfj;k%u|ѻqջܽ_ut5o_G-e?Zi?G}gtyXGU#ZviZj狡s!fhWfC)NHLM"I*QF'RL`JiN3J4#)9cMꃺݖ:(JyZJ^/vOi|/y5hȫ^uҮ{}BdQfآBCMߪ}>nUGz.GUGCX>:V}ɥEꃆaч>G]W^uEi;>/6@_Hbٲ-Ovܴ^u2y ZiQ )O_(Ѷn݇ s?>~gd&3 sOīkߦoQ]H'3-?ZP1ͻB"׺|E+,Wڸ=Ƞw"8QmBD/6[,s&Q!ChI=82felbQs1A9\q_WTet3+5@ۻůc}`5Sxin+Բ:{Smp"Ui͸R|oכg|I녃ȁM1R*YT*wDV(Yh Xyooo ڱiÞ\9=kR曆ۄ13 _@t0|fD@Kb.-Qfzm3Q6S!',|QPE6l(>f (-"a*@DyPiq UJ5"^K8 hˌc7FQL|ϻ@:.XD녺K^[ VAІ`HXΡQ w~="MnI$zÜtdcI"/TҌ횛Raۓwԓؓ}P5GUet{K-"  xS vQZA Y+\$Ps1w>{Ud IX~Wn| \u_;eQXD3w.*oHw [rV}!Ohnא]i_|6Y"1aZdxqžP Qp A_Շ}jC}x$[Ebnc.ޠَvs֚Y f@-~N^2}>(VJM]1'0gs}!i44}kQ*愙ʴ6.Bq,nWvW$B\T%=EkJrL)NN2,XυZ|%z6`wC*[fj2A[Wo?)+(Ax4h}DsB4E]HpzT{2eA^qײB"6liym:S$Pֶ="a} &w&ݮ-ePJ\59 Y`ٸ_ Or%rW.~6\Ҷ)]u1¤~ZOe02P]sm[1wGlr ]q$°gIudWKφ,] Ue9a&#gaQZf]N-0Zz%W5)luW^GheItiImIUgm"儬e"fXlx85N&KUْJ6c"e׌ <-5mp`|v#M(62G`m;k OU@U*`2ykwMYCa6ߊ#E1X*$"dSSD{dVE|Z0 BND(dU6I4LQĪ (&6Ӻ3v3r'<mXE=_l &"8`uCrZuR<45x4A1Áib6Eҽ\f 銅#QW(J A Ӽ'j)=Cߢo!Ux JY7mw^pIj[E(T< i}=їoH ~jE.BxZy}(i<4{Cg2%e|J S3|Y$EE-+S=ܱ7]Wٍ ˜F7@}߇ww,*4LeJ z"*m>1DV ٞjХxO+ZA扠Rfs cD "G#Z81QqhM֬X L~0 cg^~_«m1QzAz!rQkE)VR 7nq}9I83 ɧCp?Y5[[(DKr\mZlzȎϿeOwecӂff-?upA>Qk+i!mI@vlUDlXW,G],Ъ>" jOgB q0&œs T!zX.p ,T<_p6*1hphxV4p%-p *#ff,O#N/B|SGє=^+9tl$b6َzz NCFP >zPrP]a[(Mf6e!×k>0(}FF“|m :zK $0ҔȺ 뺋K sg0$RY1HH0Iu=,WoHǾ rc1xܐWҫL``&>"K0AxGk$.b͈x2/$j$ d )[-JcNv6 i `f^Ms?h‡&C7(4ᨽ\ݾy$S%"uzZYp,ؿs7S` ڼR&'%)V)jJ(ui~] LxG\a endstream endobj 138 0 obj <>stream HTWӂ@ l+bWbEA?xIvf=MԖ ?X;Y)yY==jͮ}ʣӘjQ%~3Q~j*Lxk![ëU5R\{dd:+6_nu+ӝ!Gs0ϝI8+Ē+V2d>wMo VnU䘊MlC-$h(~TNc>; S&:Yq~yNaC`3b8z`Y.MK+Ѿ5e BO㕳IN\ܛF7|:ׯo@8|2SB,y/.˽D;Q&3OGuvsMJ9Ր?槤W ~ɽj8%=1mE#MƖo˅avTfznTyyQ=>SӖS;p1iGShy04Z]:ܐ#!zv?m,輜zPids5Sl-l#a c.^gp\#5;lvfΖ=m*{F>o0grfsn|se!qq?Ok*J4;!}~.||ܺؒJn0fr{D؟*-߸ʤdV I$Hc$JPZ"LN/l"R5v!@]ˊ[ѾFog3 &kܫqֵWaem(gxTHK=jIu3[UծW"V&r#rtLӻɬ*>HgBPNC?HAF (9BB338VeZza= A)锞0~HB 7{Gx~F bvtl4Fq1".3#,/SGVGP:D< MYzzΖg0xA[{OCpk*U!j1rvNQ84ՐiPlvgb.=&~n3ڽ!}NZ>/Q )?G(WB+/wFćkf%MDCBBKe+S>7DNA8'r<(b֑AA`Y(B #ȑU؀ X &0{`[RAY1 kAk@Ld6Ww˧hX?i_d9G6!4 nכpdYsOߺ&4$Iks+"&KTcÇKhuU]:V$P1Q`!JY},[AS,hQX! 9$ :\%wUGh:_&P5b0cO -Q!!RG oZh˖=`:1>, )6˟U v.TKD5AT'ndY9B}&UHfo7;ҧx>h  mUw{- D^ t Є#Uctr85 25"2aAGi8B|Ul`I!XGE ꦆš6ҪԴ)̺Ik`ܷ'7$0>2fԿh{fcgdU}Rp[ ٲG.AaJc_ ]2$ RVl}d@U[˳%a0$@Р* -?-%Dm~6l6˲S"VƁ1wA3ZTc6 /3׋}\˓l$'hI=+pgÈ:zz͖f*М3w1pV_eˀ %X\c*%_7Jj ; 8fDUDSW_HVSvkCE7swC־`.kd}S.CN"xi!b$}?]/-?ȖC}Fz7#[9k `Y^YJfJYǓTݺ>QG&LhTc]MD䖕Sկ4h۔\8c볐i.L%=Acmۍܲ%oCF%p`B0hw$~Px -Mhs6( 4!Oh2ф8z \ 4pC wh27(|@kDn@p#>@PA>p4 D B$kH<]H! k@HlxT&FJ=,S6$_ Oap<|0/aTʣaalV( a=4)'w CXm5Dc*E/+%Q:>(bDhz;5Ƌ; 5{Buŕ~ zEڧ<覓?mVrICG8 vD?ULK[NU0ƣq}r⥭iv v">Xײ+H6lDĢ5嵝Yk!PM.`:l PB!!8;;Y=~L/Nr۬+B^u>鮱|ܦbI(Fƭ$Ry=ɣ,K_%mΚa{* `x88bQ9ThiS^R:e-忕Ky^Ŕ OsRo.AoE!a'Ir7m ~ŻMb?MeJ(KvE 9OcoוFK~c}5Bqp+:lc.J_:冼Wkl/ [Hɠ/[w5n[܆>/Bv|fJxrk$2N«"H` xB±EZTv&3!<':D6(AP@8=*|(Z̑:L1y梣( Rk] 31I/c)~psHFL_F8]!^Egrj,3D?A.*Avq7,۾)E16'yyP oIXu$&"8Pȕ{TDqez>(TB͡˓bۄb&) SGujNu%OZ蹦S!F>22!T`!6PN˱Y u8Td\OgK->Vwiq_S8|sGߘ[}\J6vRD^16TiFsGyPhǙW-X*<>{s1 :ϳ=V!KRmEؘZ;Qq{j5g54}ĚZlC'*XS,ѭ1ԗ(,e۾'\ KĬZsZTJYq NUAF]>Kq?OԅTҩʓ#E$3]6)?AjZ< iШqz;^*Pabm%D zrNV[Hf|@>Ϛ:}(jɻNډ<xHb}ۓ^Nd4::6 Qyky-@ʹ*vA#R㛽fp 2h euKǺ20/b:-e9q'OAfcCe-TB>*'.esVOuvckY`}M][gx^`):x8 %-;(O C-*RdOeGA-vT W*mref&/aHe:<RP {*3clr*抵NE֘"[<=RNm݄֙h|囖f&%R꡾NuuSy֞qAE;< >sq^H \?Ɨh sڃѾA^G!#r)(ϖiM#4'*RP.Ǭe&^5n+*~r,)l8twi:H.YaCXtaffAc]U0g CsidfqUǟxC`,lycmlbߵb8;+cv|udjig35m 5IgD2` ioғKKh|~׈cz-]|/s I7]h8WȐazDgakAE"!0baުӓ[w9s hĶ܌Lգ>ç1W}8{=ˈQ\)պQ(Μ ^Qy@\tZqZltrɭK3bclf%jGF!iD wQF@V%q l AmFE\roBլ#/p\C0. L`;Гƨ~ٕ[`~vC"T--qy{rgcɮ2Yu7<@  m4u?Wl7cL=@?:Gish|0 yeIx2mP^: i9mTiiC naVύم+$xKze:+Ok'p\t3?g\eΰh<xpYBQ0qى~eXy\v08Y~L`!IӾySp 7j)_J4 DnH)b*.S=!AEfLUsJ]N\cyhu5&F4āQ5w$spBo`o(#k0HvgQK Q.;[fgJݐ?Fl6[>lHhKb 颭2e-D eDv]B|b%ըH8Z{ xPs2!?ͅ "OK2/ a5?jll_}'F(XD򇢽-ҎPQ/{rB>!ЙtpP' w6G:WH[h#di#,lpN 혳:ـhevČZ Lw]K'f{SKb7𳼂 g=lEB1 ͆$v-<@%'Yh<; @1EIЉ8 xRay:Ufĵ\D 6ӕ rhQmʺE;v5. K+3ß ##[2I&g\45Rec̔zmﶅlƟRO'(硁HbOs:nrq&di/軎cRua^4 m@(F:Y64еzk5\s%ŠBMѿV58vב;EJS%uVPh34|_lh+u`;FsD\طf~ [熕͉4i@yƝl`)ǴP~ [eK؈QuT-\z4rk`iˏa`XR-أAl)*ZAec.CYDoA[Y Xj9T%2(޴+[aP}=U4Œkstr2vuE4T[ -0xr +W<&uzTn :Ao>*RatplहYQ x%w5FvTcOcNu(Esϸfr=7q&IFe0*M|.@՘:_aML5ihceTQd:@<`m H[I#mȔz)ݞ8lv?'(R$9gN*^!rg\{N,qx+la{TSrH;#mLU_6j7-Й`^.=l&mߝ]VM YK"H==oⲺc/YT†?LѰ++֯OnA%VibI'LX 1~AF4|!Oe`x>WP+ -} 7ޕfNJkxp; !WS>p]y!iN*-ӄ_1P;JuBc4@8H%qHoϼt; $TbW?>;8%7J?K| Ȍ@|z> QۿA ҘiZ :n]BFb&<w8q P@>ZK#M@O+ʽ=զGj3 B^QM&og F*l}G ckhqr͛Uܜ_;N-g2)c 7(|\rm(4PF1q)$UI8= %J ` UO ~.տYEbj#2:ZpG.092:æB{ZC|d֘eNgUfuJp)E& =o*MUU. P@lҎDXO|)F[uBK1+~SL[[4/ Qd:}MX P'"WzSdph`M򞽐I?5Plj7IgvAѰj:Cv un'~`[P W4ꯖ&l洷Y.$҅ScQdLQV2c9 ,\MgVq=X%!bXh5z;Z^VGFp25ZXGaY-d؆5fZ&Z|?oSV_q÷zp_'-9 \ɹj߇F/d9"hVi;$V:p?h520:P-*=p\i1oS>U;cBi4 {2Q4V۱%XmJLm+ ٧?eY$V|HH #9h#"赦,Z'R /}I6V V|C>S;U-RՊgOT74J /)  ҍБ|To:B,9[0}*%N|x})REn'9Kzʹo巗^~Dׇoƿ.8W[ЗH'jM|&Q+N7n`8^n: ^$.BU'AAȐ.I#eJKuV,4r&Ʀ' _jUr~⠨ Fs.W83^87¨/q~QÙ[H\èNb[h؍k^"3% v;-ƅ}<&t[E0n̶XXǹ74D4RmZgIHOk?Vm"|!'.zDVP:ˉߙ[(}>nqvK-sW:[C7|uS;4TQ(Ug4 ܗ&-uI+L`vH}o>^@;hTQ{g{m0оo=&u-1.4Z`{PK <)yT{gq&乫!UbͰ'52y.S ,JQ3nߞZpYUs8xqirfస_߷#p.`\n4cJǡ~o!Q:u\$d8@7n/ĭVn5jNQt]J CV+Zk"ͧ*oZx.N_k#\M^oc:MMV*rPڔUU-S68[@taey0'.|!|Hg\>8iCw䷧&YesOoIngX9&19ވ}q\kċ(hZ\\PE_{voȔ(vtPΥ,촳z/y'ұQ)+jM ;i pڈR Qx.Svw\KGcMGQ~h6Z}C}74)(Lƀ3 cnR<71kȡ G4V̰qDgA3'ŠQF!fq<l^ LC/'W,A OKGV tOQt 4Q_ug8F ԟޞҋk,ۼrN纓qz[56TbW?>;8EcVG$<[I'9 U8'B0'鷟B~3fBT'TY~CjBTj~P_Hu'wQ&}1 :t00\ Ͻ\(hH>M QS|Bz ``tNVZ{B>EI5#BT۹-h=?ջK+n~Q,6cs)յ-YWŢNBBUi\W2V Tc*n(tM-rbwsl rq7.O`/G7ukN6* j2ǾV4iՇآJns\:ob]l} =5Jrh1]e氊G[SF% ݞT*@PvT( m 2ͱذ&OybT>=c}>pIQaeB*ŀ "n]BBf7C'_}O3VB*0>Ik$כ{npP1L Ҿ>,`\v8`M+/u=d % RKSy%°!ɔꔁS]֙g3f-#2fNQmϱm6k4B /#z G;#Vޭnm[ҏ9?0U]<6cl95xج_Wub̟ zJ>O;MiH-F '[_~ )ّDwKΆ̺|Bo p뽿nhb\F]J҂(cy=2Lf#jJ}`|XnyѦhVlT=:o_LH`}Qͪ$(FΚ1zD!&-?5~>JimQ[^ 0$p endstream endobj 139 0 obj <>stream HWg{>~\!_.uϺWۺ? aEl훈 gs9?TB_n,y# z`/T]oE{y/{ ph$;-,HMdu'"Ȓ*5HJZӺ \N)Jw? [sRUQE&ܑ <J# dF;IZ!(փ?c@ ҂d,l B ?yMF1xIH`KS !ڮ7Szm,ZXYv;yi0rNg(C8dH85~j>k-N(VMaiۉ:0PeRp-O4 feӘPIߘ:Q|3+ ҡkиDtk^"H$3:)yCȽE`gPa nB,^iy2-\Z4usPZr#8$ 1 BLC>ʹr$mΛˀQ8j.[ c=2AEٮBä% A C -뒹? 4Ac==s\r/qއxTzT+. cO3Gfq`Bnz[>fV}ku`=x6ianПJ`0h*Hl@W6mex翚Pf?NFعoL)S Σ~8.wkGa"}何x}6yQ{v/pXnѥuϦpHZf]r߮oپ<4cHt?E{:iq쌝rG~˹$){Xw4)b+R̳(oLRl<hx}GR~D'ۘih8OmpRPOϾH ͇?QȸsךQ&ÐEoO0`?ϋup,}.X2l+5tws< F=s&:):l1b3VOr?WkϾ!|:o=Ś"{?=={@{s"[v~ۼ*a'Pa+MVE pQsl=AӮo{0ƗWuNC @"l#L#b_J.ܝEr\HdsIp溜O|{)^Jv Bï4g OHk~ƎԁbbDJ~؞cz%O'}[* Ⱥ*\'ÍUf<[Gbft/'j+/-JےeoAM@\JiS335qދʒ)H_؜g̙3$]KxQsTV LCOٿXh`4NG0E FD1j]4mU'gV[wCJN (?{@h״|o$R1,p8r+qi Gi(A"S%Dgjn'0__3Q#ZH@2N-WTwI(~OLWR4jrv.` ą$"zO0WU^[AMyY5嫼^--j@9 fhQP$p/,77FsʼXwZVFȉտ4YDZK<Ȼe( Y{0;ջTX=-2b$>VU}UoO$ ]gv -0~exR툐Us̮eQX"#7~KNHxԠQ)M& H`((^w L7pdGtS(\Ī;Nw+hƸlzʿvBٕJoU6ЫEY8U+Ii ؛EՎC7.rn7jc͑eo:"*nC'lQz_ar?{|pR"{`j~%BשͱTKim~O 9+ 8oO)kC1 M'odcDZ\cUQԐ˖QAA/g^(_@^Bnv+WdNH7γ5R6&CnXqIi#`#ّQ7ЍHg6l摾绀A7ӗ0߱md$7Cv*X}9tv XZ-yEq*8ioO>Ψi$ oۤQةP/QZ/҂Pkˆn1 P`2* _'/z6uYMn2@3Cb#]Tc@7X `  bnE.8i&daڨHT~ 7[nń5Yqۈ毝 ol$z-=пM;oΊ_/." pv"~sǢĀ4.6 68!0Ҹbw ƣCh3o%g"dTgmWގEb[4Z%,\ PMea(j*J\kw: d`P@1 bL AldZ>}ɩ$Tk]H0k#I UٸQ\V*JqO:&Ky xN[L+8aNDVBC$pqĭ4Ǹai1`w[M3x({Uy:H)C!  '?cy^蕵3GB50y\ڭ&;NKK$?Od=Zxy2/le6C]~(-ryJٖ!?;ff`γ!H|3Yk@pF6 cw^Hn7wOMDIؼl<˳37c> [ pB6»(Qi/PF wii/s%b5*gqu HXÄD=R֨`~ڶSs]Vd-紷P_ Sg&Վ|EyBiry0Z_" `OD%Z9آU263hψ3% o|F09q~"P{i-Vk%//O[pW]Ц7U"@XSylyű,)L#ӳ Y[?̳wtt|*|~|ÿWR~ G𑑽:3R"r?e &= K89=)?bxmS(9TAXη"aPa }8FAj;PD ,8FA@CE1 DEy1^jb2 V+v j׶Mp漉0md,D&p :ȩ<+Tη>"F4:uusZ]RCWXg|XhRi;jMBge)&)0HwUZ~0v `*A|`j$Gpۂ}0Hޞw/x -)!u?`osíU{oW{p7d k`PD{R{j&7`;a֕6qlor*eHp':~  Ɗk0MYY}ǂ*աG}_"38hEo "|o2" hE:f::E؋KĕUPEZuQ|]n%Y6n00dU.::t~r+@}]YWw@,;5D78}{#vum"Bs/ܡ> ^?FwGD|1x=l,;o>_A]ܚ>;s|glOd&iM/zq ˁ^v;0ԵW.ݩ7;[Vw>{XH;(E ?Hd-YH~C:X>8O{vc1E@tHwf/X~8X;K!W}jE81k/ '_kp٩3KskApKZwq"]&J:Ԝ$Oe\ h yfZ #뽀 Hr HNUySշꣁQ*1/&UEO_lwT\ n#I7[<8hoRqeSx)#X']Kh3P-”^̬.g].~6$D?`ucK X^Csp8feZB-dF@DGw2+ˬaX~^;mc6jgG dfk&d{iv=~]ȮmBH} 8nz }amLoocBa%V2ohF5+'y {4n]V#zf|3~љ[\5^5.!|!q{h5@JA/hrQ`w%\^r!}kU[kWգ7v|S}ĺ9_>0lX s~>Ffg[2eߣX<8ћ)U[?́z܂#~osO.;> ,wpm.=)ED޼[&/ smם͉27#ٷ[în<\Dldm5ڝ|/#֪X3t(O#TK5<O8^3na郃?n뷋kKSvbǝ2'~H47Oj6^ 7$[;nm˵VayMr?kVY㨈tp(dyqQ'Ϡm蓥[wV?dI#<3</9ϵ >Bm\qE/{|K^wrzֻ̈$ >Ð}P^.ON/jIL-7oOoN?__KGYiOr?fT߃=z2Ó=WfO+|FF}LF_;x ɻ<9ɍb;Hm\bXlDr- '1ML|].k`5 Fm A|in(˝F97:듂%cSۤT_֢I?m_\JRQ;WzLحH9XX NSX^"8rL{-HBkLXc3JE)'Wr-J=p3nHÀwWHq291D3 "gy*R:]VʔeˤlC؃Oyv5%ݡJ/u4ˁaB"̧8cꙗ ÜY"T ځ̜ p,(Gӂ\BwZȑ{=.Kf89 eكɖ:qHj9N2g$XY,V NNs' X;Ny=%ڹXZ+ҼBQnEqq %=݉Sق$96䅷Z6`tGOh_ )Ģ$PH]bAm<amԅ]4PGD*8eQw 5 -rIC 48 @*YZb e@\' ,qh p#uUAsH9p|VHQZӢEL[Y mFbp2@$)н;TkI7yUcsj1BEϱp@72N &b)cK:'B4WS$Qr'TDFF Z>F„* Ӝ 8f!Xd@);L %u]RMJ#1-v衒]:Vm"h{Dpe @R?HP>F5 u$#PxIE 5 Mo:q B0X:@j(8`(Dӵ8MgZ/_]J 8r&dɓ)x:N5kV #ff0\<(^0S8O#ɂ%az9;~<΂ Fy!Y'$[(k0#ġpTs8n`RHGU`eq.-M6Q\FPqcL7ôVZ(荐z`uEi 1:‰iq$v\󦛔 j==ü7Ng)^&EHMS<ʇ#b$`)v|`5ooFuXjvr5j&Y0C.;c1s0, ]xU!NcqA !&JR),ZL9˜ ~5r( C୉uD|LQb*c͋&utDinrKp ޚP{Vp }Nq VE7:w4 )¬2LM0`A{kFb3mpP@moq|I(wG pÑi4Ww ^L0֝xyd&8ud܂ u@Z:Dj::eAώ`Z $pDvR[OZƖ4sDbd|"+8ZpItJQ[0 SDR$&2rƝ:IbIgh{7I 3I !Y(0w`d%b_nJ_gb?""XӴ3TDB.B)Hz!JYBd܀ k@(1hUB<#$0zG6E' F*uR7:V?[˜S6]`d9"yJ1.H 7La" sVcbZW0 SL(>A|F ^tWyL oI.<ܖHnھV'灒f0- P_l Y px>D+qaA/9KBi`g|YbMY\E$6ϒ s 6LVbx&0.Ŕ9b%% (UяcOΘ(\B0Jt?:nyIkNIZvˋo6FV24ָA֏5G %@5| T-8EJLB}7BVBc֢n*Q0SBRJkHϲD0lb.g.E]B ֊%!Ğe}"$a{a '(TfJ*-0Y g SdI6c[(&1Ǟ1QT0 U%& %&n>gbHlo:* -cjd&Va]a0 &1 ΧkX0Bab T0Pm]`n:HpO`V+Uc_`D$N GX69(Y0`d%&e* s s2$ϋ ֵ &L3L o^ĶK.[ÑeI3 o}Q`F:JC!Қ8WK9Ǿ %C6A"Q˜$Xpіp5\0u7oEDh4#JX!l">A3#3dFB>H8NH6 DR|d%%\{1DiSZd?[IL0R"4gJb쮭Nm%pvTDaPR&+1jQ-]쟪aAdIL0a¼9,f:f `=Da3#oD"W ~dVxsbH\BN1,|0\1Rʄ_HLwH%_e8ٞ6 7m xOz;!{KwƲe D/‰JcGe Gi gy~yfho|]L䅞;xCwxԂ2n\*yj,QY\+"~h4ty?2At 0A~eL]`?}l:F`o6}F-SG>QJ9Mě6R=eR9qDHCm퍹q> endobj xref 0 141 0000000000 65535 f 0000000016 00000 n 0000000144 00000 n 0000003650 00000 n 0000000000 00000 f 0000028723 00000 n 0000080620 00000 n 0000003701 00000 n 0000004566 00000 n 0000005372 00000 n 0000030033 00000 n 0000019446 00000 n 0000022737 00000 n 0000028917 00000 n 0000029042 00000 n 0000029165 00000 n 0000029290 00000 n 0000029413 00000 n 0000029536 00000 n 0000029659 00000 n 0000029784 00000 n 0000029909 00000 n 0000027516 00000 n 0000005433 00000 n 0000006050 00000 n 0000006427 00000 n 0000006787 00000 n 0000007146 00000 n 0000007505 00000 n 0000007862 00000 n 0000008219 00000 n 0000008576 00000 n 0000008932 00000 n 0000009289 00000 n 0000009646 00000 n 0000010002 00000 n 0000010380 00000 n 0000010737 00000 n 0000011094 00000 n 0000011451 00000 n 0000011808 00000 n 0000012165 00000 n 0000012522 00000 n 0000012879 00000 n 0000013235 00000 n 0000013592 00000 n 0000013949 00000 n 0000014224 00000 n 0000014580 00000 n 0000014936 00000 n 0000015291 00000 n 0000015646 00000 n 0000016007 00000 n 0000016360 00000 n 0000016719 00000 n 0000016996 00000 n 0000017413 00000 n 0000017933 00000 n 0000018364 00000 n 0000018877 00000 n 0000080584 00000 n 0000027118 00000 n 0000027181 00000 n 0000026373 00000 n 0000026436 00000 n 0000026310 00000 n 0000019302 00000 n 0000026247 00000 n 0000026184 00000 n 0000026121 00000 n 0000026058 00000 n 0000025995 00000 n 0000025932 00000 n 0000025869 00000 n 0000025806 00000 n 0000025743 00000 n 0000025305 00000 n 0000025368 00000 n 0000025242 00000 n 0000025179 00000 n 0000025116 00000 n 0000025053 00000 n 0000024990 00000 n 0000024927 00000 n 0000024864 00000 n 0000024801 00000 n 0000024738 00000 n 0000024675 00000 n 0000024612 00000 n 0000024549 00000 n 0000024486 00000 n 0000024423 00000 n 0000024360 00000 n 0000024297 00000 n 0000024234 00000 n 0000024171 00000 n 0000024108 00000 n 0000024045 00000 n 0000023472 00000 n 0000023535 00000 n 0000023408 00000 n 0000022850 00000 n 0000022914 00000 n 0000019238 00000 n 0000019482 00000 n 0000020087 00000 n 0000019653 00000 n 0000019760 00000 n 0000019868 00000 n 0000019977 00000 n 0000023059 00000 n 0000023180 00000 n 0000023289 00000 n 0000023679 00000 n 0000023825 00000 n 0000023930 00000 n 0000025512 00000 n 0000025634 00000 n 0000026580 00000 n 0000026776 00000 n 0000026891 00000 n 0000026997 00000 n 0000027325 00000 n 0000027422 00000 n 0000027660 00000 n 0000027896 00000 n 0000028020 00000 n 0000028143 00000 n 0000028266 00000 n 0000028386 00000 n 0000028490 00000 n 0000028600 00000 n 0000028799 00000 n 0000028831 00000 n 0000030108 00000 n 0000030322 00000 n 0000031238 00000 n 0000035128 00000 n 0000053339 00000 n 0000067619 00000 n 0000080643 00000 n trailer <]>> startxref 80813 %%EOF 8BIMlnkE8BIMFEidVH$bddcd27e-6715-1179-bc96-d795218d25c4sHׁR8EQIh,90 O=U;&BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBg߀-B"{Q!D)BB"{Q!D)BB"{Q!D)BB"{Q!G:Bv9J$DkXk#D+sjg=eCܶr Q!1 w5ۙ.~89._m;};tb_(uZ"!J].Ļ.b-r/DBXmBR}ޝ] !J](4 Q:!p/,(uO[{(uLR_>{(u/a~J$D+X~o DCۢ{JB6Jn qQc̷C,)ҷ)ԡ!nh!Jb؁!N}/{:| 3k١CB:*8l<$DBwp<Qk=rrqDqh3 B: IJF/.{Xdt:ԋCuHZ <^byS{V~/)C Qu!am-7(e QE!:ke&ĒB)]qQ!qsB|%ŶZn|lIF3] !J=b+vHZOXRt:;[s!>;$DgBk9rCROXRxݧa3-$bΡg4lQ*b[˱b!9;<$DP%=L!!jTRtΔ!! _˳t lꉵ$o)7(5TXd;a"DpqQa528L(˴9a"D_aM%ԏ?3]OW!'Ӯtq]S;BzXn/)L ia"DG!ʨ6E'o)irCR C[y}oEd+Oo6C02ZnQnm:laZ2GokSZnx '])OB<_|-q<t{ ϶v< u6**T!qbp>WVzyB,ї¼Txo4r:\o󰦵!ÚsZn{9BkhY J0%8L_S"-et Q,ۋUr9m8:\x&BjmGö-h!Я|yHBOZWUqpKp Qd_w@(aü_kn 1r!!*y#RW33 uO;Bq#cTɨa3cyNX^:;Xicy˶\ΩX^"S**j-bbcy3q7TLqRĺS =`ډ(RꌩX."~ljZ5ʩàCc*b8L J5qTs,e;ErE~9+N) X=,Wxg)ePw5oa)Q[ {X:+Y_r(Z1k= E|Z*L mPt83\ECCQzh"θbyQ(bexSJ=1qjmYCe*@.-#.c;"U|[<<4kHXR}.3"9jQ\M?>pkX&bek; eBDѾ8RV@D?R|J!,nCX" sJ*|%B"zU,sb02=l `5l& ˍWDg:0wD4s2d+ID 1Cv6%*n>/ҩn(fQe]щ+z?Sez_z)p?zQpGzq\yLٙE!ODä!cy_ѕCI5RB] .Xa=</ĒJe%e/K"JI0vS"+t% r9X>Dtۍ;NGѓy>a$E5^FG"x27(`i. `?=B Etú4'= TrykAD?_ث_3{(u/a~J$D+X~o DCۢ{JB6Jn qQc̷C,)ҷ)ԡ!nh!Jb؁!N}/{:| 3k١CB:*8l<$DBwp<Qk=rrqDqh3 B: IJF/.{Xdt:ԋCuHZ <^byS{V~/)C Qu!am-7(e QE!:ke&ĒB)]qQ!qsB|%ŶZn|lIF3] !J=b+vHZOXRt:;[s!>;$DgBk9rCROXRxݧa3-$bΡg4lQ*b[˱b!9;<$DP%=L!!jTRtΔ!! _˳t lꉵ$o)7(5TXd;a"DpqQa528L(˴9a"D_aM%ԏ?3]OW!'Ӯtq]S;BzXn/)L ia"DG!ʨ6E'o)irCR C[y}oEd+Oo6C02ZnQnm:laZ2GokSZnx '])OB<_|-q<t{ ϶v< u6**T!qbp>WVzyB,ї¼Txo4r:\o󰦵!ÚsZn{9BkhY J0%8L_S"-et Q,ۋUr9m8:\x&BjmGö-h!Я|yHBOZWUqpKp Qd_w@(aü_kn 1r!!*y#RW33 uO;Bq#cTɨa3cyNX^:;Xicy˶\ΩX^"S**j-bbcy3q7TLqRĺS =`ډ(RꌩX."~ljZ5ʩàCc*b8L J5qTs,e;ErE~9+N) X=,Wxg)ePw5oa)Q[ {X:+Y_r(Z1k= E|Z*L mPt83\ECCQzh"θbyQ(bexSJ=1qjmYCe*@.-#.c;"U|[<<4kHXR}.3"9jQ\M?>pkX&bek; eBDѾ8RV@D?R|J!,nCX" sJ*|%B"zU,sb02=l `5l& ˍWDg:0wD4s2d+ID 1Cv6%*n>/ҩn(fQe]щ+z?Sez_z)p?zQpGzq\yLٙE!ODä!cy_ѕCI5RB] .Xa=</ĒJe%e/K"JI0vS"+t% r9X>Dtۍ;NGѓy>a$E5^FG"x27(`i. `?=B Etú4'= TrykAD?_ث_3{(u/a~J$D+X~o DCۢ{JB6Jn qQc̷C,)ҷ)ԡ!nh!Jb؁!N}/{:| 3k١CB:*8l<$DBwp<Qk=rrqDqh3 B: IJF/.{Xdt:ԋCuHZ <^byS{V~/)C Qu!am-7(e QE!:ke&ĒB)]qQ!qsB|%ŶZn|lIF3] !J=b+vHZOXRt:;[s!>;$DgBk9rCROXRxݧa3-$bΡg4lQ*b[˱b!9;<$DP%=L!!jTRtΔ!! _˳t lꉵ$o)7(5TXd;a"DpqQa528L(˴9a"D_aM%ԏ?3]OW!'Ӯtq]S;BzXn/)L ia"DG!ʨ6E'o)irCR C[y}oEd+Oo6C02ZnQnm:laZ2GokSZnx '])OB<_|-q<t{ ϶v< u6**T!qbp>WVzyB,ї¼Txo4r:\o󰦵!ÚsZn{9BkhY J0%8L_S"-et Q,ۋUr9m8:\x&BjmGö-h!Я|yHBOZWUqpKp Qd_w@(aü_kn 1r!!*y#RW33 uO;Bq#cTɨa3cyNX^:;Xicy˶\ΩX^"S**j-bbcy3q7TLqRĺS =`ډ(RꌩX."~ljZ5ʩàCc*b8L J5qTs,e;ErE~9+N) X=,Wxg)ePw5oa)Q[ {X:+Y_r(Z1k= E|Z*L mPt83\ECCQzh"θbyQ(bexSJ=1qjmYCe*@.-#.c;"U|[<<4kHXR}.3"9jQ\M?>pkX&bek; eBDѾ8RV@D?R|J!,nCX" sJ*|%B"zU,sb02=l `5l& ˍWDg:0wD4s2d+ID 1Cv6%*n>/ҩn(fQe]щ+z?Sez_z)p?zQpGzq\yLٙE!ODä!cy_ѕCI5RB] .Xa=</ĒJe%e/K"JI0vS"+t% r9X>Dtۍ;NGѓy>a$E5^FG"x27(`i. `?=B Etú4'= TrykAD?_ث_3('^o~pQrr܉?t~n=%]wfeldI~Nbߟ]۵}wq-B)DohisK;JkYj'0txjNӱ=ƝH=PE:$Qa?sSϸ4w !uFc "u+SUv+ ;Ur%&gx%z};lvj[n{D\Jęɬwi4nPͺl;y> \"͍-aНM\Ot]G9B|ORhyhc &FlvvX-VM3.=S@ HbfrX+6y_Wz|r]f8w䏽,PQe6KcBCOU].x*wSt'c}0JJ iD?ZmxXnm[ό R 1y>ϳ,"D{7msoWOZxn=3"e=RrSmxg8 T=|nua9&YzlLXm LQYġZKCӾڼaZ׷S#< 8f(l*qzc5Itwaܖ[)ס/UQ;wA$ r{?᫯rICȭ.q6oc6wL<<=.j[΍i}wJILbgf6{i4 PZ~ѵY.VOᖓcJ6V:rcYI%AHeľ?7uZ>o)u$,733wϔ, s]Vb~UbfLew ܽg[}\o|n}^o04&ƭ;_H0ݽɭM$y>Vz.k?"H4/^"M8ޙCa8O]ޖ?õOJ*,c1BvlD|, }!$67"(B~`/О~_\zjL͘C\ 69$u>:tjw11"TyfMagfqƱ?C\r6?;9oE_(Ƴl"[2OUzSR.2,5=hX!ιf<纄"s9 hѦ5Gx;^& jgEuQZvpkpFCQ!,ꟵTgEJ!pG{i&(d%?+YR Nq\跏| ڗ0ZH "V0RDX) Ja}87մ[qfRR:aX)m[yNyysø1%4<0R eqӔJGk !, r0!)̔ضsC߅8F>N!RZRղE#Ci)۶-7ƺaCZ>;"_ބaJ4Ϟ.e*)J)!ze,tn_`~ ha.`aQhƳU](Y%qС4Ѷպqo V| 2܇4i3E>*s!D88~Ǿ7o-xhk~a0(8r3 8,yϲEv 57B3{FF xHE)D|W14<u4m^L vB-;a.0RPf˼*U\s +kxڶm Hy1 `^L] S(%l2F0AG=L!ik\{ψ|qBqsk%+U*U4%= 3Ck!nosqD>4L '%UU2<<i8 F-tӰx[9r$A'h,e Y pP~uV;ki^!p=F"w !DRɟO Qoey+7vp}(e8 c\$luHt|zkheX)i0лҐP.D-a},/et+R0v>Y3 j6->l{``\Uq|H!xʒ $8|JxCˤ0 u!K@C)_?l8 ;!,;JQtCc/Hɲ4Drk!e /+JB=^^]٨#EbY]b&/9%E 9(E'aӵ 7oyַFŬu>\LI<J91W8 NʊSr~@yq6p*kfʺANxLLiUEtm.KBR bp՗un%yx2rAJPCk gk)~me<%rGfC+nF6 ceEI2Hð9iq҇p74L<#RԬw]4>f !ޭv6_Beqq8L<F 2!Z6m+k]W!uxøR!,d"e[=]'@^L(Vݵ Q˧ry/f1ڨAC]C0,zV 7)RJpJ y'`6aC2[5ZN<%ˆdeNφ7KV-G}93C=)ݷX}a`0ÌyI- Jx3ŭ.f:H6%=Ǚr: b:ARN~^G6>aa9QGE?֜R'+9c]؉^^;|_"m(M*؆mhUd#h:zLx^t2i&P4* 6{8Ǖ\Yqn>y)nbZOVASRmY $P+ȡQJJtq4, f˿!YVV;2JYFLR#yA}ߋ~Z0(+Ī.qWJu} fv{w2 ,-sZyB9}5֘I-pFpJ! ;,7wQAkTZC-~ > !"8+9eA $/)G5PtnJOhe : Q7 (AI:9OLC}A-k,l|xVA󒲏͇+-ПO~.e'ŷzj9G)1NqZЂ2qGs>y | 9|?t%>%J4/ Fy9-4#IaB-փ6uVӒRΚ{C"O1AлyJ |Ԥ'B (}HpJ^5mPuxC7d'?oJZ0I\5iAU$Qr:u+eYt7 )z_qYaa`4+杵ۙ2Ju1\RA<[ځ"8ú> (_JNYk%N)8YPˁCgyYSBa‘rx>rZiewZbd`Pˁ lBuGM<oO2m5fbP8-~$)I0ÊU`ğ2e<z4*!E ègC)CF$Ͳ3mxfMCWa.jeZ͓5;Z>,7aS3h_﬙e?yRjuC ;@ $E)sNyÚ%Fpl<;2\MJ)Xap$1NIה_`)JjR1TjjY(0C xQ״SLǑr$ JqzR~-9cQH;2kʙ6Hq@zBUb9G8ͲZ"yXaE$ )V=gB"vby6b{){R[EArE B-[/UQ#娌2jh9da٭&UYB->ġJ5 )QJvXA[v*4#406ӟ˿\02rEU|yixNCg9ҎL^6<2ȳvJu8 ]ix/F9,eqeQRH^(AC|ZCVrcpCfmZN: [*E^pnq蜏CcRӖ򆂌thgih9jq4->kI4I#J,Z{ yYY:٠#0/ QWYAax9?R.rߟ-RN+HuZ Ȳ,)?qHUA칻JCG6 9TZckr!V({]q{!vKj{5'ec7jЁFEe]^?HFyYky5jkn:YӶS)JՖ nDIV> s)л2ZNpG=.me!J(VJuQ"M`V^rSJNR˶RlZ5ĮfiQTU}(8}kyF9}?@b=qdfa!v^Y)y(9Msi^^m-[8o%!KJYVYyaNFX)VC!\0K޾AVEkqyh7?X‘(&$e1y|? 4#o%,8:|aQヒzvXrIRT?%ɒf —)0YʖS@b"/Aê'qA8\Z:J[3.iv05HUF{孕mfS['(jjh9!dheQqFiH3o3kں|m(/rHvy -4ICG</0ToUAʪK: jYj(y V䇏YXᡕrƦq.nvX`C@-HǶA~Joyl6$VME9\#EMUuEaLih)bb)̿V5maheR$; qq†iځp;9[@ö&u^4if\H %{!S=NF.5ASsqlF oq.KXi8B[-cl~pJ8K}Dz M=P˛jyhfJӴ2 ChP;0S8L>eȃj'[ҦʛR:3l!{Y>ip80SU@}ݒ)iKidB@Z˭NI&/cbij4ds5u#kqd LCTUӐ) 4Nӻ5 )y m*JRt+e#E-5ߝ,/dbYeUa^fJ%{P< ӲOewg&hDdҶ/yA4vJ$[C۴=/ ! C 4iW<qVBh v/,>_2B4TlHV7mߏ|e-K<4Sx<޻67P-]mia"]hazIqo-h#iyȗyȟKRF 2 u(2-09?0ƚ~hC6O=IqZTUGqʡ%?JAx~&i_ z:bO*~/2D +ŴN&%\CzJI AR0f!x4D{haoyѹCyX{(%/~IR*C!$8^;}CA-5J)XO$Ȳ,P'jQ'v, 7#cܺ ˒ý"\RJW2 FZx2y!mge^ПGiI*+ 4E2Ɩd2[jh(%Ma~\PZh2I#op= ihJ_4tq2Ƅ^ 2t-cbp˛~zյl^)J)JQ,V jhi[`L6jC$cEz"ZyVRi@CtYޱHPVI)d%gJ>GVd˶eayђ>׵<Ҭ`B1MA5ܼ[jhVZ9/c?2am"h4\&u-+Y( N1 vM$̖j_"J"e\y2N)C[,Dzָ4{E$q^ Fq{ޙͭ;#`Ժ._*-J&.ehaB:Ƕ)-5y5/(Z =8蝂\wm=+%TsR2}~8L4+K*t-Hv3U8nR}~Dɲ(R!xibbwz>n>Tt ~&YA8TZIl7abU2+Ϟ$ ӢJX d`՟JeBEr ]ʐz&@jZ:iIZdTR8D !x||m:T:}~)i^R,8D3 7ݏ5m(+C).t/,+K9h$$No:m|pf=8e HaBJ8㝍B :iry_sJIf'llh0Wи< QlzKgbF6ơuuB0;ӟ{yoƉǁ1XY߅"1(n;XxYVkGۤ, 2kRg%LGwRkQu=rS a%xqhv6FΎh T2 CB'#B4S>h( eYh JF@}4Zӫ3a`U(UqaspDGv]Y*8%1+W},ZCLUUJFBdnPJYڋ)as߹R >-D x$kx*-o7T%lC9] dPY6|.8^Ig!2w Y"GK1X8NF& cDR($#>-BcdXaq)$O M#~!rlj%2A<ce!V2rPJ÷'!B$`rVA91DB#+ !rbPxU`,3i Hr`@7zm&2k2h`JqUHq D8RJ=S &J 7`uÅmrWvhIԷ%czRb8Z|&o<\^X5^~Xˣ;J{^h\S$X~۶_Owɷy[R8gц2Oԙ6Ǖa:ߛ+m8^T&ODH]}?*Iy1#e) SdF _ݿBʓ[ Pw7y#t ~/i)܎@0N}ܸ ֔W"J^a=(wcCowhF8;Q<t5%Ք3tlTJ 㥖jx#E֞S_L +m>`gNn$~;Nc]c?_朣GߔƏ~Ora  xշ93s4 ڤ=jk9?O m0Xg(ҷٽIk dKp: hR 3\dM1!_eD>I[|:4ʇ^1Ooa/g,T3SsIc&݈YWyq͑q=`qhcj\A6ԕ>u+``}"$Wm\Fy3#poǮQӲ:О]5T$>W{iG"J'Й\Z=`x8F|G9)g8a|xlqHz 5R$wӾx! cm>Px9oG cv7o-< 4 $WgzlXїP|OP>Zx,RZg9ɕ 8w||1dr}XcwD2Iғz`{Lo=^u̇QnpzMY7wUv oKײg=3/߿=܉AiФYMm xX7:>>Gvەdx}9eoS;@F2}ơQG=S~t`#snX 4fwID1?PZ&heuW8syq-;# q}CX:5t?vI vC_Z# xl{WD}K'T$+s#uj"yJPm-7\(:07ۀ/z`oxw|ӳ̭;!x*7poߍ_Mc(No?J1bja{U;"=7Ͼ}vz-?oyJ%]ESˬ=v p른U) #:h6÷;ڻ" +;8 <[m4rn 8%]~ƾQ,'Z3ֱGt+vC tZ {⛞{bX|Sk4/jE4{ iS&ķ.}ַtK~Z#вZ8&6 }]._>|iHIS2$BrZ-nKX[K5FvSȕ+?hyit3/ /zC[@n,exfӆ`?Jz67x|Չ0R/W4BR.7 U~H|{760#0eB l̾8 \qgj c3v7 H{ z |>D\}}wpuMi̭#ʺ'ty [n]=6խ6 mF #<+q[&h۸H}67&; ް 4PMPw3Vofȴ&*c7H {3SeNRY}"^}P׎I*Kg }o>}#FD"=74U(sE #Ȕo[VX=b]>'=|o,)Z3,66[6fcu#72v@aYz3Xn;Ux\7 #{S-JtN kUfl*e{քV&\0NR_s-\h6  =oc]`=[J9GlمS`ơwo=zb*amDr 6Φ%aMO{9}!w)R?#d7Ϡd=;>n~lΧL%Rf~_XLqDo>}?=>r p}Z|IV+2fY+@{vXzpU~=CJdإnI0gµyL?.z>ك2bixkBKM>+\ U^g<-7Of95̬,Zt7??|~r|>^}REXȥJ.L|p)_mO9 1MZ^4V}'Ti{9 gIL3OwZM,kfM!.E(qZQ2޲a=nU Qv,0tusӏF^{N oH#,z1O+|!:Y9nшtZ5/i!w}~m`p7isN0{$|7PnF ֦@R%xTSi|:ǁOO9eiۜ~xL2X8‚r!Cy+x*_8ٲn,2vc|ǵ&f3&ܘ4.f+7Sߵi_ }+'O|>Zw2eyx=/wؤ4&;wJx/uX>R T[f+q8${b~Tˈ޸ܖ:](:r#w0='[#߹C߰f;k7Q̛" վC3u Fy8lT u$;փH*+d[-h@3Nxzm->دrXMR% isϰϒtz3F7qv墳i!* f@ː/CmkDܹ_zL.Ry3=[/v 4sqKzpqZdOߏq+.30U[ {Mcf{[A5, O6bNy؝YuR[!68%]~ƹe5 ڴấ@ZM/I@TC}B۶b=w>zrde.6O8| r@_>?>|jM-c~[ 3ޙkBW,Y-U-",eu]Mmf}Hd.*&*?go !Y4!5q I{7F8h<}FF55soK_߫x ]b?1ZJ&`j' Ru|p?Uinjzj>1^|_Dj0ωŒ@ `ܴmNrjuoYsVCtp|33L3=cRڱ@Ŭç_$HH3/ƹk8jkOG|qnY;ߛ3&r[Jh]V%>aOڍ7~{[eLP jV7?m=*?g?|zp߳LC)ck<+Lg}VeX//6]́6IZi!O|Z5u+hŜoļwwT3vh&w1*oUATݹsub?'||.4YvtM\B?=3{@}?/O?f5:ކAC=##upÜqy:դ!q|ǞOކ}z^|_+l }/1e~Ro: I%p }xblap=/b;OM3nFJ~bRQ 3"3 ֒0hoL!<LJ @_}B;Q|7=ݪv p'7z_d[/1=xѵ|Bg!̎/}q>}>NjZ`\ V(UaUL:6*ߠ/^ bǒ e?=zs]޼{:o$#vrc'ZX#BpCQAjFj3pOݠHX;y3.Oϋ_jP+)aj8Mh3wwߵں8|\ݮ1%w85ۡjX.f5?na|{Åc`IEnY`nTSAjB*ωZm|WXf}3nsՄ|[~x G,Y.sp& aP ȫkfN/-2C+ߋ-c|MߖZzrҮP}\߭e. 4uNhŷ-uJɠwKKNjO LPo&qkrhXw{{bYV{~θOvSL|qf49f84 7kQ}#;>Q}L\)3.d>d=;2U~ԅ}|x6 jbҨ}Rr@nz]oۛ_\NW_y}kMM :CҼz;0.)X"\nW9Ikc}׵8/1Є-O`ܲ*eMTη>};^}K x{>h:"o#3lh x#&p+NS}<7W+5V|Ca&4^$xmܧO5I\엩1RHڽ1.Hf08P~x-u,z!=#x@lP|Z5_C5\Mwo,ci?Y>~r6[2@wԒO'Ļo6-mspvNVP zI{/h9njsV{kok4pn&r 8~k[͖x>U1ZJ7Y|~v]i'G}cHr;~ځ34Wxmo|9Mz {Gqy-զՐ/~f pQ)ff]vP_cZCfy3~-<=i!RpЖoڀ~3_s { sOk_ݷT?>74:ߺtIK7Ǥ/9Q|$;#/%WLNNpu71d81+\~ nqܷ,+eXoab%ַ>mj(o_W-&k-N!w3ˌ Fo*=3~%\}<|߮4m֯~kv?#^}cm]˥;!iЪ P.|3|'^Kҥ/-'Vÿz$DHo^3]ǃzfgπ4r%:D_ebc6{($ig/U*mwSpf}z'p&p43v)65DɃco_wjkr"0nŠwi9KK5;b"jG4|*rpvyyݱHߩ:&:K|kbP~6(aY9jB7I‡ﯴOU\\zl:F.jƼpݡibxUu[]wp8FZ/L[*X6~uV%;+x.R7"oPWZ}ƽESn֐9<lZy1=oo ' / 0ؿ[3G[U(~jɷjg63Q:# ZW3z\mbw/p|X󫨕sۇ+B/ж*yU%S\F߲Eur H8Fs_ \34O!| E?VCmR`_5@sOr p-j>X{v?(K*Lu|s6y{ÊV avk_yI8wW;X}ϡI6bt9jK%W ,1QfvE. [Tz?w7|{sxk]߅wv<?~񭀗6>AnBAM.Q7*8 qYX$߯ڃxp7rt ~>9)FQl; Ԁ q2d$+Z[aȚo;Qw^4Kn.'p4Z#/-k{7n;o:e6V!CڑN]bω@ӕz*0y% qi70];X^^LĄ}p|Y<46^O̬/+&9 I  ͖gf\m͎k 8jH߷7jg^0}Մt"of@if!"|U\r~>F![Gj̼ܞ xn7Oӝ[}=5 Ԝ-@sxZ s;pszF|π` ne`ᒥᖬx S रr)ߠWƎob=U Br'f<!M8; tw,=_篃7gTmq;[d 8@^G=cwji0s. (6/ VohjX&m{MBZ KR5!$&znoMsm?a>c7h;l4s|{^D@zj|/wۢ7.]MH_N5BT4HiU̔,`aVFpg:/_7jC6eˊ  )!sm]Fp3 4kuv5tޕk/8 0ҺgB.,ebĶŊEOVF!׀/ݵ6yqM#M5}:/nQڭ npmy9v˄oZy05-pDS]#C~Yf?&l;7^Cqn|gh;.AGP wt,>nx=CmܔoѨҎEb xCy$s}. K;wpkķ>􃲑h̶%o1=̌8 yPghڠ 5/n[W|#߂QD̵՞GbK'&!xXW Z=.yLfuz=M7yF+.yޞ?sDf+ 8]34P;IҐ1>39 ^8EeL3/ZR]N#s!.6/Ƕ|W Gߚ{ S@A Az3Q/zABGϷ_c,&!39Xv\"Z/q ݓ?[Fkx/ݰ/QƯ4sזGǨ0c [vy9oҨ3K"{^`oXrCG5 07;iVsH-$xQ4{[o(;NDr9=(ylo͒I Eܦ~a@5߯XϷh+ɞh #9/Z̦F?,"`wp^!d (LI8/;rY; _>- "񾶭7N,%NmkˋZd!1C2B!ל&" Z&lrla )Unqo@/|U|Qsoj8[-ߚ(r&xڨy z*]s6 M'ǖMB(:tˣm'=1^qcjaf[0_Ӄyv*.9Pױ|F&ڻfpw7%=Mzv \F!I6I&[黴B50?PZeϱ'g5HP| ci;P +͑ȟB]_dO\hޜ=GIErs f[۠ie!ř4{Q*b[Qa/S5JWE\i.˓۵ŧxC֔{]v۞^9#]-}W$My='6˷Ohoarnߞoki|b  1vWHoN \|+p>O@ZA%7NjG;q5w[o۫q kvC7Q9ao#'`o':_VGn~(lxSfODՅf@́[ng!e}dvShη^>L'sۏ|+<|7'0w\ěP1ƺ <c͌o]{)E:HkYj_)u45 ^2]gG 2c]Sߔ8Nj٠s ɡnFr~ͼ7)>"xm?8O/_|#̗ܽu%)Lfmon{Jޞ-7^[MC'f i}A1,b(WWX|F9k nA^ wYtPD":"s5@߷+`<{o42}/~+ߤFzphUC;v ~շFG47E7^ 0m3v7Ɋm 7kwIqOƽdW}نjx:`! 4M5Ln #.AW)×2ھO|K ÷?:|ֵ7 Zb|{xf%!.l?i?]s8jh8+[\~wkx+eGeߕo)̷aK\>zBy S_A!<뾻lS^AfO?+Tu]&ni!AWJ|{70d9S-=^M)Y~[ŠŃy( c}o?-)5F6\]+B)E?'ԞHPI }/jD8r'+dH 'Hs/w XCsHԽ _DO[|G?;W|poJ|{Y_H21_q|~[%E\}JgV۰e_auWpk{$.5۪e6ֻ0L] z}r e ~o[}c"e?#mt2K"OQX1 {ao9 龡6^Vq"ǛwQu̷/GGИy{ݷpVihL՞hm uŭ}N`^'aEqԲP"{q(;8 G#!ogX:˾t5a[E*Zۍx m5Rַ0򯤅7Vm^kZ" % :pX_77"F풠̘m/#bU.~l"@2͞"3{>^l?|$]vEޙ4k}46뾹Z/@8`Ho偱yay] o{oȭ&}Կ eQ>87F'i_nmFC,mWAf9A9nY: z+gIqwM]>R >+{)i^![W6OAߔ}?no\ӕoֿ4iWC/W[W9q eߘaq4GTҩJOi Vej#AƑmDq Oxp^aL̪<֯8?:;Z^mo| p돜?sGS4A7x}[_w=}蠸>=EB> tN ;^Iа- co<(Elg4`/y.ۛ}UC" U 3uhj"W=K&ay?25OlȐqղNKPgF9/_7i0r(oDA&g\m'Df;tb;_~c{V{4ĺ |675ۂ7vڽ3%"柞X6EPלxPs(g_]Z ?Y>|_}M |oK۷&qXGiP4|$<݊ە0SoNk2VoyM)@pt3HX빜O3ߙ1ϰ|a;mpra7Oi<xmov9`qe 5#VVm0~Ѕ}9tezd!zcj0C[&Xi9m[Rjq]w/]` *(і]2 5%rk7%J@b"HĔ__q?Dfߒ2Ea/]M&>m#} 䰨.DzSs %1Rbcy2^WoA퍾~(Ǎ@jK Rc{dG3dM!!N9|<<".7ǚ/>GJsiey!IwFz=Mf Y[//.s߾(mvÄO}jřkƐh:";h}ZZWhO6ðT_ކ~.$ūF"npoojM;O绘P^nɿ'[:8ilMnyhS|Jr MXaY ~FY1e Mk-I*HrDvPo~76[f ,Xop%{w&}58bZFa]gيɳhBnvJP 0D~Dko,'bb8BXbErQػzPřo#pafl}&2KN_|y} gVoĬgmo&,1yq3[A4X]nݞ{ܳ* DSSs֊'!k'\o'} ˌ:\8o5w+}(+Fd{Gk}oy4 l_|rݼZBG@b7hB͊iǃk-6$o_h'47LkT/03~}-ɟb_sʷ0lߐ>^n wV ӛ]m'}W|AٞԆ|c-nqFTw\>QonOMՅU.3 >W`P#p# )Z۶2O{/>ZWF"5ي7u󽜠htӞOuÈ#Mvj=BSeZ{fKAnߐO6Sq`@\4c]eƥ oߟo][+1{<2ͺ!(&mu'!mOy\W|>"!392ޅF{7iV)-5Nc2O[͠Ta {o{B7, \oK|+{y2e P{Of&=3.[KD0+3[.f9?n[3)R{'ƃ~''+215RW| enb}MLk]؞E٤xu%u'wyu137%pao. DOwErF2jhyOw&<9,%8hhn?Zs~~Ŭ&`:2ɿ/Źg{oJ TK@&31½vZv _&-Ir@ƤϚ0C|qe]&p. 6sZ-Kډp at]l<;f7 OfSvvoLgۮإ>[v`[blc" p|Ct5_[tM|b<{e^ఢK;!0X]֮Ȑg73ntxg0SfA}۪^ xw>ߘaJ6 jVho(n'i&/Ʌb|7wkiGV4O+Iַ׺ܫ]jh}-3Ǒn/:X҃X.  n:o;LF-Ġo^} ojaqFg_ب1h:ԟ鬾Qߝ74S~k^WQ֙oaj=|$vx~|}{ ծmª;QcQr~ NjGӘ#ߓ Go.MtRpWqqc4(=7Z_x]oPJ">3]Oķ FbߚHS~~14n6nyBoqz%gLO0o jߚV:i6f}MZnug2}x̷%!Uy0g*pX|7&ʹxq9PGSگ=/_13x,p}7s7Y5]j|tߩ]蛀GjGWeR  –}u) wˠ=Ә&*XH7Xo2}(9||Ӽb$}> oq.Y=5xF[|2.f7QN(bGPoWRߘ6e{"v4$7M|~9Aoߗ|j.:7/upRR[|sިp̫]]L|mbߔ6T#d"@Z dZ|[":z |?ۋ1(}s6]uP=oko Y˥?;#wL0fCB ޹6$Hm{)ڮuE @E8;I,6wK-&rߘ.Y8a ׿x,Mtӧ |;3e=)R-A疗}abWcy ta_#wj73PC)pvR.Z|GWVōVfX|weП?zu d4luۏF:aCoՖ'ds؝q=ݣoLgk&B5}sy+xNwIqൺ `jwo^&ߡ&>Qpqd>3uEjf nȘ IfI5 "v=I\?7!M)X~(ɍ*Aoc-*3o!)vfP hQu!|Nb/(}l|7꾁nJN y le6R uj^7>5>x QKe@AⳮW"ߎ}hqfoa화jWOݢ\kYٻ1uvIV__,~9MrU՜@sKlP˰M#?`=|7ջE7`Ie|ekߒH^& wCԾ "0"ߏV57'}[F3|~2Z҄m0vD0t]֌%by*)~}H!}!ӚB>#l^l{<}}o:(<~ OW`Q!MtKۋ]B7ҽ ye}k"MuwxݙO遼^$5\o;~u v]^ߟ\ΧpmĶ΃M]6;uYQ b>|OۅXTR}}wI7w׃KdaU5Eiq Q(W)GZF]hmB<1zz"k3cg.J$eOը8(ds!}i?Iime}j-P3UfWGZsV\Doo8||²oi~76igˌxqK ׌η~J5=zcZ0?A-?x{{d9dה[]w51k0 ;eyk#9z)tɷEx +,Fos_a_J m+W!&O180-Oi}_Xw|/YG}sEċM9l}?("z S3`$R&ruv\nۀ/+n J{{q3|`7*ƒƼ &6Dסo%1_|[fly)AtR?zƥ YV#f~ۭ?-?:~X\=C=x_a/QcBD3/k&n'}p0]o־~3߷7lm|c'(x4 ]"z p?Hy#ჾkCJI&ѬH[KMKcJv_߿ibj!pc@9Vy(8c<|g;ɺ|^-vuy51l YñZJO#Lijቋ?j5 ' lq=ѷ%ҏF{>z'nE8 aaMfa3cf?2wiXMfg(Ҏ*4l;K&ߙ6]I;c"{qBt8&q `JEwMՈ3i@Wk[L' 97m[ӅܾnDb5X-/&qj er~JkNܷ' }uD8〒u ȈǤkN8SnG oz7-]?7?#viákh5B?n 7,E?| a!}%01dp!#s 9 R݀DZ4I>~PXYAGky/j9YKG#xk}kZ`׀[oȭ&yV# 1s|fottD_s"|`]pؐ?iz70p1&nG33bS+"-oLr]kor둾$K EH ͑Q hE]"[oȭjhdQd- L{!g}qϢ3c\ࢃB{x5D-\k L4lnEt|H oj06w\_$t/הE`^˞;|I7roOr7?ooJ|;mylg54~?/Bَ.~Zo?-?,VQ5Gӝ{g, n#~?z|@êhhn2;@[ܢK|}|Aq#=1q420]X(/^&ߑa?1ok /}g鲖 ) !y3'X5b_ fv`Qt}:P 4l OB{ >+A- |m;N޷ϧ0Bb^>lOh+՘ut@ד}S"=#DVh81`~UiHɷO,ߛo"|ri2Ce[]ߝ6h'vQ"xsTkmf2E]]$LMKրA36|Њ<^^.>+e-sGո PP1l2Y .dHƟK&9c{dپ/X?mm&7S:3>S9rd ȹnuy䵃o;(ϛy!fxtd= MIu zP$$bzZNWgdVϛ<ڥ>eD'/ Q-nsFQ} o|=ͥ]-&=SM ʸ)^sڍ7NozLv&*:wM[Oa2X@ƓA*>-ETr]LLRFskmtޅ nqGP{UVk "1}=e³ ]E2X=B8x+[2|Xi3LS-M7jXC8~7z\aX452<߃WwLg>./eo7|ՃC)^h`\"e%gvJ=ZH& b^7?~5bL}$gFehA5e!+0o÷<ڦd{-1ZnHkdpG 9C*pR0c2 LdrM2#,Nc䖻1 e$1#4|#&hr,y6ï[UΛ|k 6ߏY0m ʊC/vc;&@wNQ(|?dS>`=Ӵ1aZHJZ]av{7 A3,\P6JWabL3Z3!C]^xNv.]Gx|I͞)b+fI{sӱ.f\fg }*p;2Cߏ1n9l6/3r߾3h87fʹ} rO&UeaHvsV~~raY*fJ(0s$0nR,d.8/3=bǦy|}rf8n*l q 8|d7?\oDQz/_Š_m {~UkR)\ pK4r"Ωnf\r ًL^w^>)ƒV7wٔOUnW@ze`|x9$hs}'MDD SmN3_on:%@ Q&QU#bF})nxd2 kȠzS'65_Sݙ:Hy|W^D_zDR{#Rf&=n\W9upBj$fЫ.kf_Vłm)j@ 8c܋+w>W *|is|'Sa{'ui}2YP/y8|j TxDFL XnD})ï[U)] ^Qwn)\^C˯#+vRV8ֹh{\fMmZRYOa pPis]-L,7߷ڔOАj8p7dL$#̂9شm>6~|W?V3m4~s\?_&=/s^UWׯkuA{k6Wި\H&3+0h.cMqn-{m'IY&4Wג?#:]D׿O]e&yH+|[ԛz6?kS>p'-ml*'vGüIsa=N4EEnsϑD5'4>px}I7,›Z TY2C/d {o%|nS>pJCpˉR`hi(8 N%jHN3jXzF?m|$03xp R x}Mm ^nVw| Yn9{z۾O)'Ą/wz|?Ϧ|*TI?ҹ xנc 7p܃_]=nHJ|;ocOJ4niQ6Wf-hx2ob9SFyoODwKy4],#z{)T']h LIg܌IIZٸ DX›#2M>DYg Ɩ_$kj1|Hcs|$ݺ8$LyeĄ%$&7.+"zcndz ؋h0jqw$ - islʧQ\Vyz V)r#fx;06.V8NGzAIcoy۸ ~%ï`&PQm'=:-<Y/k4LЊF~-E~{'@ )P2+.Nqٔo?j$c8\į88NRFx&|gUOw@}6rD5ƌ!-]/l;p䕏RD_'Ut%nLLk2CӸ8|? =EZ_9r@@Iίs \nַՍ@<њΣ&+>$X bq+S+nP}3 "դ3ʂ@Xӳ*C"IRƲߗVi',WDžpXFYv6ȕC;m? C_JxWߗtfn6|3pݲW*B 3šQWęRj_ʕYpa.%WGAW@?z٘"ve6zyͷ%m65|ewZ$ d&\i/U]  ߗo?a0F#z] @uOR"{ji(t }z 8iD0}`R!c%j: 1U(s 6Lȳxaf6!jd3=_wo#o 7CF9f/Ckl2o@)oB)m:{#9х7F_-NtB}>U*p=2ߞK^)-7w<o@$TgQɟ>AJoBM7ZIGoJ x`b&:}ߒoŞOulF 0<*4w?}ߓTM9pBeu\LQH T&Mh+I [|k[|3_rMB׾؇y~Bx }_η8. 82XVku-˳#}GYX+|˵mroψS_ϷDG'P]K{k!;Đޤ-JFm[ͅ=D-ᨵlKxwk#p?/00 a6l%~o=n|=!d?m}`<0(緧W^mk7~j̵ s0}Ծ}Wdy$bddcfea6-6715-1179-bc96-d795218d25c4sHׁR8EQIh,90 O=U;&BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBg߀-B"{Q!D)BB"{Q!D)BB"{Q!D)BB"{Q!G:Bv9J$DkXk#D+sjg=eCܶr Q!1 w5ۙ.~89._m;};tb_(uZ"!J].Ļ.b-r/DBXmBR}ޝ] !J](4 Q:!p/,(uO[{(uLR_>{(u/a~J$D+X~o DCۢ{JB6Jn qQc̷C,)ҷ)ԡ!nh!Jb؁!N}/{:| 3k١CB:*8l<$DBwp<Qk=rrqDqh3 B: IJF/.{Xdt:ԋCuHZ <^byS{V~/)C Qu!am-7(e QE!:ke&ĒB)]qQ!qsB|%ŶZn|lIF3] !J=b+vHZOXRt:;[s!>;$DgBk9rCROXRxݧa3-$bΡg4lQ*b[˱b!9;<$DP%=L!!jTRtΔ!! _˳t lꉵ$o)7(5TXd;a"DpqQa528L(˴9a"D_aM%ԏ?3]OW!'Ӯtq]S;BzXn/)L ia"DG!ʨ6E'o)irCR C[y}oEd+Oo6C02ZnQnm:laZ2GokSZnx '])OB<_|-q<t{ ϶v< u6**T!qbp>WVzyB,ї¼Txo4r:\o󰦵!ÚsZn{9BkhY J0%8L_S"-et Q,ۋUr9m8:\x&BjmGö-h!Я|yHBOZWUqpKp Qd_w@(aü_kn 1r!!*y#RW33 uO;Bq#cTɨa3cyNX^:;Xicy˶\ΩX^"S**j-bbcy3q7TLqRĺS =`ډ(RꌩX."~ljZ5ʩàCc*b8L J5qTs,e;ErE~9+N) X=,Wxg)ePw5oa)Q[ {X:+Y_r(Z1k= E|Z*L mPt83\ECCQzh"θbyQ(bexSJ=1qjmYCe*@.-#.c;"U|[<<4kHXR}.3"9jQ\M?>pkX&bek; eBDѾ8RV@D?R|J!,nCX" sJ*|%B"zU,sb02=l `5l& ˍWDg:0wD4s2d+ID 1Cv6%*n>/ҩn(fQe]щ+z?Sez_z)p?zQpGzq\yLٙE!ODä!cy_ѕCI5RB] .Xa=</ĒJe%e/K"JI0vS"+t% r9X>Dtۍ;NGѓy>a$E5^FG"x27(`i. `?=B Etú4'= TrykAD?_ث_3{(u/a~J$D+X~o DCۢ{JB6Jn qQc̷C,)ҷ)ԡ!nh!Jb؁!N}/{:| 3k١CB:*8l<$DBwp<Qk=rrqDqh3 B: IJF/.{Xdt:ԋCuHZ <^byS{V~/)C Qu!am-7(e QE!:ke&ĒB)]qQ!qsB|%ŶZn|lIF3] !J=b+vHZOXRt:;[s!>;$DgBk9rCROXRxݧa3-$bΡg4lQ*b[˱b!9;<$DP%=L!!jTRtΔ!! _˳t lꉵ$o)7(5TXd;a"DpqQa528L(˴9a"D_aM%ԏ?3]OW!'Ӯtq]S;BzXn/)L ia"DG!ʨ6E'o)irCR C[y}oEd+Oo6C02ZnQnm:laZ2GokSZnx '])OB<_|-q<t{ ϶v< u6**T!qbp>WVzyB,ї¼Txo4r:\o󰦵!ÚsZn{9BkhY J0%8L_S"-et Q,ۋUr9m8:\x&BjmGö-h!Я|yHBOZWUqpKp Qd_w@(aü_kn 1r!!*y#RW33 uO;Bq#cTɨa3cyNX^:;Xicy˶\ΩX^"S**j-bbcy3q7TLqRĺS =`ډ(RꌩX."~ljZ5ʩàCc*b8L J5qTs,e;ErE~9+N) X=,Wxg)ePw5oa)Q[ {X:+Y_r(Z1k= E|Z*L mPt83\ECCQzh"θbyQ(bexSJ=1qjmYCe*@.-#.c;"U|[<<4kHXR}.3"9jQ\M?>pkX&bek; eBDѾ8RV@D?R|J!,nCX" sJ*|%B"zU,sb02=l `5l& ˍWDg:0wD4s2d+ID 1Cv6%*n>/ҩn(fQe]щ+z?Sez_z)p?zQpGzq\yLٙE!ODä!cy_ѕCI5RB] .Xa=</ĒJe%e/K"JI0vS"+t% r9X>Dtۍ;NGѓy>a$E5^FG"x27(`i. `?=B Etú4'= TrykAD?_ث_3{(u/a~J$D+X~o DCۢ{JB6Jn qQc̷C,)ҷ)ԡ!nh!Jb؁!N}/{:| 3k١CB:*8l<$DBwp<Qk=rrqDqh3 B: IJF/.{Xdt:ԋCuHZ <^byS{V~/)C Qu!am-7(e QE!:ke&ĒB)]qQ!qsB|%ŶZn|lIF3] !J=b+vHZOXRt:;[s!>;$DgBk9rCROXRxݧa3-$bΡg4lQ*b[˱b!9;<$DP%=L!!jTRtΔ!! _˳t lꉵ$o)7(5TXd;a"DpqQa528L(˴9a"D_aM%ԏ?3]OW!'Ӯtq]S;BzXn/)L ia"DG!ʨ6E'o)irCR C[y}oEd+Oo6C02ZnQnm:laZ2GokSZnx '])OB<_|-q<t{ ϶v< u6**T!qbp>WVzyB,ї¼Txo4r:\o󰦵!ÚsZn{9BkhY J0%8L_S"-et Q,ۋUr9m8:\x&BjmGö-h!Я|yHBOZWUqpKp Qd_w@(aü_kn 1r!!*y#RW33 uO;Bq#cTɨa3cyNX^:;Xicy˶\ΩX^"S**j-bbcy3q7TLqRĺS =`ډ(RꌩX."~ljZ5ʩàCc*b8L J5qTs,e;ErE~9+N) X=,Wxg)ePw5oa)Q[ {X:+Y_r(Z1k= E|Z*L mPt83\ECCQzh"θbyQ(bexSJ=1qjmYCe*@.-#.c;"U|[<<4kHXR}.3"9jQ\M?>pkX&bek; eBDѾ8RV@D?R|J!,nCX" sJ*|%B"zU,sb02=l `5l& ˍWDg:0wD4s2d+ID 1Cv6%*n>/ҩn(fQe]щ+z?Sez_z)p?zQpGzq\yLٙE!ODä!cy_ѕCI5RB] .Xa=</ĒJe%e/K"JI0vS"+t% r9X>Dtۍ;NGѓy>a$E5^FG"x27(`i. `?=B Etú4'= TrykAD?_ث_3('^o~pQrr܉?t~n=%]wfeldI~Nbߟ]۵}wq-B)DohisK;JkYj'0txjNӱ=ƝH=PE:$Qa?sSϸ4w !uFc "u+SUv+ ;Ur%&gx%z};lvj[n{D\Jęɬwi4nPͺl;y> \"͍-aНM\Ot]G9B|ORhyhc &FlvvX-VM3.=S@ HbfrX+6y_Wz|r]f8w䏽,PQe6KcBCOU].x*wSt'c}0JJ iD?ZmxXnm[ό R 1y>ϳ,"D{7msoWOZxn=3"e=RrSmxg8 T=|nua9&YzlLXm LQYġZKCӾڼaZ׷S#< 8f(l*qzc5Itwaܖ[)ס/UQ;wA$ r{?᫯rICȭ.q6oc6wL<<=.j[΍i}wJILbgf6{i4 PZ~ѵY.VOᖓcJ6V:rcYI%AHeľ?7uZ>o)u$,733wϔ, s]Vb~UbfLew ܽg[}\o|n}^o04&ƭ;_H0ݽɭM$y>Vz.k?"H4/^"M8ޙCa8O]ޖ?õOJ*,c1BvlD|, }!$67"(B~`/О~_\zjL͘C\ 69$u>:tjw11"TyfMagfqƱ?C\r6?;9oE_(Ƴl"[2OUzSR.2,5=hX!ιf<纄"s9 hѦ5Gx;^& jgEuQZvpkpFCQ!,ꟵTgEJ!pG{i&(d%?+YR Nq\跏| ڗ0ZH "V0RDX) Ja}87մ[qfRR:aX)m[yNyysø1%4<0R eqӔJGk !, r0!)̔ضsC߅8F>N!RZRղE#Ci)۶-7ƺaCZ>;"_ބaJ4Ϟ.e*)J)!ze,tn_`~ ha.`aQhƳU](Y%qС4Ѷպqo V| 2܇4i3E>*s!D88~Ǿ7o-xhk~a0(8r3 8,yϲEv 57B3{FF xHE)D|W14<u4m^L vB-;a.0RPf˼*U\s +kxڶm Hy1 `^L] S(%l2F0AG=L!ik\{ψ|qBqsk%+U*U4%= 3Ck!nosqD>4L '%UU2<<i8 F-tӰx[9r$A'h,e Y pP~uV;ki^!p=F"w !DRɟO Qoey+7vp}(e8 c\$luHt|zkheX)i0лҐP.D-a},/et+R0v>Y3 j6->l{``\Uq|H!xʒ $8|JxCˤ0 u!K@C)_?l8 ;!,;JQtCc/Hɲ4Drk!e /+JB=^^]٨#EbY]b&/9%E 9(E'aӵ 7oyַFŬu>\LI<J91W8 NʊSr~@yq6p*kfʺANxLLiUEtm.KBR bp՗un%yx2rAJPCk gk)~me<%rGfC+nF6 ceEI2Hð9iq҇p74L<#RԬw]4>f !ޭv6_Beqq8L<F 2!Z6m+k]W!uxøR!,d"e[=]'@^L(Vݵ Q˧ry/f1ڨAC]C0,zV 7)RJpJ y'`6aC2[5ZN<%ˆdeNφ7KV-G}93C=)ݷX}a`0ÌyI- Jx3ŭ.f:H6%=Ǚr: b:ARN~^G6>aa9QGE?֜R'+9c]؉^^;|_"m(M*؆mhUd#h:zLx^t2i&P4* 6{8Ǖ\Yqn>y)nbZOVASRmY $P+ȡQJJtq4, f˿!YVV;2JYFLR#yA}ߋ~Z0(+Ī.qWJu} fv{w2 ,-sZyB9}5֘I-pFpJ! ;,7wQAkTZC-~ > !"8+9eA $/)G5PtnJOhe : Q7 (AI:9OLC}A-k,l|xVA󒲏͇+-ПO~.e'ŷzj9G)1NqZЂ2qGs>y | 9|?t%>%J4/ Fy9-4#IaB-փ6uVӒRΚ{C"O1AлyJ |Ԥ'B (}HpJ^5mPuxC7d'?oJZ0I\5iAU$Qr:u+eYt7 )z_qYaa`4+杵ۙ2Ju1\RA<[ځ"8ú> (_JNYk%N)8YPˁCgyYSBa‘rx>rZiewZbd`Pˁ lBuGM<oO2m5fbP8-~$)I0ÊU`ğ2e<z4*!E ègC)CF$Ͳ3mxfMCWa.jeZ͓5;Z>,7aS3h_﬙e?yRjuC ;@ $E)sNyÚ%Fpl<;2\MJ)Xap$1NIה_`)JjR1TjjY(0C xQ״SLǑr$ JqzR~-9cQH;2kʙ6Hq@zBUb9G8ͲZ"yXaE$ )V=gB"vby6b{){R[EArE B-[/UQ#娌2jh9da٭&UYB->ġJ5 )QJvXA[v*4#406ӟ˿\02rEU|yixNCg9ҎL^6<2ȳvJu8 ]ix/F9,eqeQRH^(AC|ZCVrcpCfmZN: [*E^pnq蜏CcRӖ򆂌thgih9jq4->kI4I#J,Z{ yYY:٠#0/ QWYAax9?R.rߟ-RN+HuZ Ȳ,)?qHUA칻JCG6 9TZckr!V({]q{!vKj{5'ec7jЁFEe]^?HFyYky5jkn:YӶS)JՖ nDIV> s)л2ZNpG=.me!J(VJuQ"M`V^rSJNR˶RlZ5ĮfiQTU}(8}kyF9}?@b=qdfa!v^Y)y(9Msi^^m-[8o%!KJYVYyaNFX)VC!\0K޾AVEkqyh7?X‘(&$e1y|? 4#o%,8:|aQヒzvXrIRT?%ɒf —)0YʖS@b"/Aê'qA8\Z:J[3.iv05HUF{孕mfS['(jjh9!dheQqFiH3o3kں|m(/rHvy -4ICG</0ToUAʪK: jYj(y V䇏YXᡕrƦq.nvX`C@-HǶA~Joyl6$VME9\#EMUuEaLih)bb)̿V5maheR$; qq†iځp;9[@ö&u^4if\H %{!S=NF.5ASsqlF oq.KXi8B[-cl~pJ8K}Dz M=P˛jyhfJӴ2 ChP;0S8L>eȃj'[ҦʛR:3l!{Y>ip80SU@}ݒ)iKidB@Z˭NI&/cbij4ds5u#kqd LCTUӐ) 4Nӻ5 )y m*JRt+e#E-5ߝ,/dbYeUa^fJ%{P< ӲOewg&hDdҶ/yA4vJ$[C۴=/ ! C 4iW<qVBh v/,>_2B4TlHV7mߏ|e-K<4Sx<޻67P-]mia"]hazIqo-h#iyȗyȟKRF 2 u(2-09?0ƚ~hC6O=IqZTUGqʡ%?JAx~&i_ z:bO*~/2D +ŴN&%\CzJI AR0f!x4D{haoyѹCyX{(%/~IR*C!$8^;}CA-5J)XO$Ȳ,P'jQ'v, 7#cܺ ˒ý"\RJW2 FZx2y!mge^ПGiI*+ 4E2Ɩd2[jh(%Ma~\PZh2I#op= ihJ_4tq2Ƅ^ 2t-cbp˛~zյl^)J)JQ,V jhi[`L6jC$cEz"ZyVRi@CtYޱHPVI)d%gJ>GVd˶eayђ>׵<Ҭ`B1MA5ܼ[jhVZ9/c?2am"h4\&u-+Y( N1 vM$̖j_"J"e\y2N)C[,Dzָ4{E$q^ Fq{ޙͭ;#`Ժ._*-J&.ehaB:Ƕ)-5y5/(Z =8蝂\wm=+%TsR2}~8L4+K*t-Hv3U8nR}~Dɲ(R!xibbwz>n>Tt ~&YA8TZIl7abU2+Ϟ$ ӢJX d`՟JeBEr ]ʐz&@jZ:iIZdTR8D !x||m:T:}~)i^R,8D3 7ݏ5m(+C).t/,+K9h$$No:m|pf=8e HaBJ8㝍B :iry_sJIf'llh0Wи< QlzKgbF6ơuuB0;ӟ{yoƉǁ1XY߅"1(n;XxYVkGۤ, 2kRg%LGwRkQu=rS a%xqhv6FΎh T2 CB'#B4S>h( eYh JF@}4Zӫ3a`U(UqaspDGv]Y*8%1+W},ZCLUUJFBdnPJYڋ)as߹R >-D x$kx*-o7T%lC9] dPY6|.8^Ig!2w Y"GK1X8NF& cDR($#>-BcdXaq)$O M#~!rlj%2A<ce!V2rPJ÷'!B$`rVA91DB#+ !rbPxU`,3`H1 0 'j(F#;1" F$$#`DHI0" F$$#`DHI0" F$$#`DHI0" F$$#`DHI0" F$$#`DHI0" F$$#`DHI0" F$$#`DHI0" F$$#`DHI0" F$$#`DHI0" F$$#`DHI0" F$$#`DHI0" F$$#`DHI0" F$$#`DHI0" F$$#`DHI0" F$$#`DHI0" F$$#`DHI0" F$$#`DHI0" F$$#`DHI0" F$$#`DHI0" F$$#`DHI0" F$$#`DHI0" F$$#`DHI0" F$$#`DHI0" F$$#`DHI0" F$$#`DHI0" F$$#`DHI0" F$$#`DHI0" F$$#`DHI0" F$$#`DHI0" F$$#`DHI0" F$$#`DHI0" F$$#`DHI0" F$$#`DHI0" F$$#`DHI0" F$$#`DHI0" F$$#`DHI0" F$$#`DHI0" F$$#`DHI0" F$$#`DHI0" F$$#`DHI0" F$$#`DHI0" F$$#`DHI0" F$$#`DHI0" F$$#`DHI0" F$$#`DHI0" F$$#`DHI0" F$$#`DHI0" F$$#`DHI0" F$$#`DHI0" F$$#`DHI0" F$$#`DHI0" F$$#`DHI0" F$$#`DHI0" F$$#`DHI0" F$$#`DHI0" F$$#`DHI0" F$$#`DHI0" F$$#`DHI0" F$$#`DHI0" F$$#`DHI0" F$$#`DHI0" F$$#`DHI0" F$$#`DHI0" F$$#`DNu8BIMFMsk 2 "../%?G`p D[ceed\U.0&/$"+##   .!)),*./40#1.("%(%-*3'44"8+),%647/+/8:32-!47=5-;520     ##%&('+5778=T[Snjpynf^M=)"       ! &"422#$*!-%3/1()/05/+,3?0*/<6(,6-9:2/5=<;6FD:890:?53H-5=./6E63753<69@5=;5@666.10,$%,$."#!$  )zscbXQK.6/  "../%?G`p E[hcfiYU*0$(              ""#('*)$*#    #(,23;;@LUTskt}sk^J:)'       %!# / "   #!$*,#1#-1496++74804@:/1=89=73>A@F<JGBA?;AB85F59?567G@8:74:2-:.74.5222%)$%'     )zscbXQK.6/  "../%?G`p &F\gcaiZU*0$(                  "( *%'#%%)))(#)%     #)-.67<FPOnejysf`^G6#     *   " *-1))2.%/11294/7<<?8EE=?89:>60>185226D936200/.1(0/,2-/* %!"   )zscbXQK.6/  "..11234456442223345665678<>??ABACFGIJHFHIKKKMLJIGFECB?><;964200//2----*++,-.///014334555566677<;;<><===>=<:8899::;<<=;<<<=>?@?@AABABBBCABDCDCCBBA@@@@@??>>=<<<;;99:::99788887777777566665555555555555555868787;7;7777677;88;88;8888:888;;9::;::::::::::::::::;;;;;;;?;>A?ABD;?@?>>=>==========>>>>>>>>>>==>==??===>>?????????????????=?@@@@???@@@??@???>>==?==>=>>@????????>????>=>>>>A?>>=><<<<<=<;=:99::::>>:888888877777777777775555555565555666665555555555555555555544444444444557:ARn*zscbXQK.6/ ¿           "   )#% #$ /&/ ' ! þ("     ѿ Ѿ  ÿ¿ ÿȿ ÿɿɾ ֿ׿ÿп- Ŀ ݽĿϿ ڿſۿþ*ܾ $¾ Ͽ ſſ&ӿſ "ռ׿Ŀؿſ վ"þĿп$ܿĿƾ Ŀ#ݿý Ŀʽݿ½;þſ ڿ¼ǭſ Խþ $ƾǽ  ɧƿ¼̾ ľľ"»ӥſն ¼½ Ƽ ¼žĿ0 ¼ ʣ&ſ  ˾ þ  Ŀ2ľϻ3ų  ¾  ڹ ¾󱶺 ǹ ¾7򱵹𵹽0򰵹 ˵3񯵹ø3񯴸կ4𮳷 Ӷþշ  ſ5﮲ ڼ  Ŀ; Ǵ Ŀ ľ ȳ Ŀ6ÿ  Ŀ 7ߴ þ$㲷ÿٿÿ=쫱޴þ ᳸ÿ8쫰ᴹ ÿľ쳸 ÿ 陙9뫯Ŀ׾¾ھ¾?ꪯ3¸Ŀþ·¾ ¾:ꩯɷþ ʶ@驮ý˼¾о;設ƿݺ¸ſ<稬տշſ︼ٶþB稬ȶtý=槬Ͽ~طſŸܷžʽÿ妫Ŵýӷټ¿B䦪˻ݟſƿ߶߿ﵼž份¾ǿܿ@䦩±ݵ͸û촷޿и¾ݿ<㥩¸Ϭ߾ſ߾پ޾G⤩ҨƼp߾߾ﶼپBhzd߾׸ü۷ Ǽƾ߾ϽCƶdĺsB¼߽ȺľƲ߽˺ ü߽ϻ߽ϼCӟ~Ⱦ{k[Jr,߽ƿ̲߽ ž߽Ժ?Ɵuz٥·raTKC;6HϿҰ߼ ſٺJнxhXNF?81@ οἼر ߼ݺϺEĘst}~n^RIA;50-'=(ο츽ü鼼۰ ⹽ϺF弔˱ؑ\mtdULD=72.,*,ο績üݰ츾 ߻㸽ιFxjǸȥV0WPG?94/,*,0ο举ľﷷ௱踿 ¼渽̵CƟχfˬp^0-B;61.+,-Ϳ߰ⷽľ௱帿 ¼踽EͽDZpºrFqr803/-+-KͿߧⷽľ௱ߧ縿 ¼ߧ踽Gó|ۥɫh}K]D?aC$,++.Ϳߠ渾ľ௱ ¼ߠ丽CʹŲ|]dDLA+ݧܵĿ¾ݧ쵺ĿĿɳ'뱵 ݧ͵ſݧжſݧ Ŀɶ!ᯬžܨƶžþܨȷſܨԿĿʷε"ܩĶÿܩƷĿ ˸ﵹ"Ȯ;ŷÿܪ ̸#Ǹ󺡠#𯬪ž#ƵΪ߫ɶ¾ݫſ̺'ַ󻢡#뭭ݭϳ¾ Ҵÿݭ ͻ'ÿ򻣢$ޯþܭ  ڭ  ͼÿ ˻򻣢%ſݮʳ ̳ÿ μþ$ÿ񻣢%Į dz ߱ ȴμ¾'ƾþ𻣣&žװϵװѶ߰ ¼μ'ҽ𻤤&Źٵƺڷ߲ü ξ ・'ٯž޺ ý ξ#ヲ(ԳҷԳӸĽ Ͼֳ'%žܵλ ϼľϾ'&믬¾ ޶¿ ľϾ#'ſݷ"ַ ľϾ(»콨*կĹ۲ľϿ 뾩+$ųľ Ͽ '뾩+ľֺ"򻽺 ľ Ͽ*ɹ꽩'诬߰ӻ «ߴ ž Ͽ 齩(߰ϼͽ#žŽϿſ龫(Ѯľþêξ žϿ $ſ辫)ŬпžϿ.ž羬+ɭžϿ Ŀ翬)寬ߧ+ҮſϿĿ澭,ſ۫1ᴥ}$ŵþ徭+ͮľ{ _ý忭,󯬪þۨ|x!̪ý俯,ݫワ~{aþ㿯-ᯬ}{%ý㿮.̙ $Ӭý⿯+Ȯ쳗-ӏý'𯬪þᢗ~ý+ĿӚՀþ+ޯĿ։þ,밖+פľ+Įþ־妖 ,Ŀޞ1Mſ+¾ԙטſ+ٯĿ˗?#,ÿ ֬", ¾)믬 . 驕Ŏ" +կ¿˄{y~  ,ʾ п 㡕 Ȁ}}{zw  #+¿Ϳ ߜغ}|{zw ,诬#ۚ ܬ~}|{zy ,+&¿֘)}}:+Ѯ$"Ж)⎋!, "ɕ ю.!–뼐.寬!!  /  +ͮ쭗ו   ,󯬪  / 2ᯬ ؛3˘.Ȯ/𯬪/ߠ3ޯ˙4 4ĮܢϺ5ϑfKGJcϪ2JDǞ 3ٯƚZSڣ45/41+5믬آ G;6 ͽyޜ6կ/ Ú*" 7R ѢǬ:~ ۩ֹU=)诬 в%" ʭ!Ѯ) 默ŧޙѫ!O Թۙҙқ  տ 寬 鿚'֔! 轙X4 ﭓ ͮ0 乘ؙ| 󯬪Y ޳ ½-锓  ٬יl ᯬ ҦۙҠL2 뫓  ʡޙ5$ Ȯ2 8'¿ 𯬪Y ᷘ_C  خJ8 ޯ ϥ ϏaG9CZt ƞĮ$? 彚? ߵ X ׬ٯw ϥsǟG Ü믬 %羚  幗կ  帖  r 帗诬 U渗; 뾘Ю~- Ɯo Ϡߙץ 寬  a 콗ͮB ϝ 󯬪$  t9 C ᯬ ! ڣÙQg3 "*!ȮiEᛙx!𯬪!  *!ޯ 󯙙Fg$! Įc#!B(A ❚ ٯ$ ۝ 1|՜  Ь 믬 Y i "/  կ7ߟUi ' ՛ 6@ޙ 诬{ ^ niv Ю S VW ! 8 bjĘ ,-/󸗗 寬&  ?A T " ޛٯд ͮ &  IJ 󯬪.(9 "ᯬN 2 !Ȯ #𯬪  ٱr" ۮg"ޯްhAjAĮ Ʊyj\NA5./==EXnc/o 'O|ݟW 迖 ;p ψDٯ圕  /Rvs+Qœ$HoѐM !嚓 ԗCߡe'$믬 ғU5*di-# ܔ a"_կ 橐 |5*Yc7ɏbDJ) ܖ PE^-ac 姎 Fx+Y ϲ~dH  俎 E!AWo|me\J7&Ю& ѐ P;TfzȲ]:}# J ܘbU湎H\\]   |"ښzZ:= 3t寬⮍@mI& V"iuoC  @}ͮ  4UIW9w󯬪ǎkNȍQ  ɏ ?5{ДYᯬ  ˎ Nޛ؜b(zǎ a dxBZȮ Í?kҟm>: 𯬪! 'jޱgB  #5Jd %޳{adz|} ޯ$۪fM ՟ V3 Į ΔEl * 9A  ٭1u ٯ Μ():" ( e.Ҧ!'믬 Ñ!.?ԫ.\Śկ,Ô .vΦ 4! ֻ 7/诬 Ȝ H?ҪXLЮֹhT  x ] 4ǝ'c寬ʣc+˩"[ ͮ ̪[ 󯬪̪ 3[ ̪  T ᯬ5̧ HQ ʧ  @Ȯ  ]1𯬪н# #5Ͷ} p%ޯ̫   ž"~~ sĮи}~}~~ʮ~~ ~~~~~  }~~~~~~~}}~~}~ . ٯͲ~ }~}}|}}~} \[ٜɨ~|~~~~}|}|}}{}} Қ~~~}~}~}{}~| }||{|{,4 믬(~}~}}|}~|}~|}||}|{||Wͻ~|}~}|}}||}|{|}|{   կ ū~~|}~}|}|}~|{{|zz| -n ̵~~~}~}~}|{|}|{|{{|` ̾}~~}&~|~~|}{y}|{|}yz|8D՜诬 ƪ~~|}}~|}|}}{|z|{}{9 ǰ~}{~}~||~}z}}{|.}|{{z{|y~ÿ p/$ЮǴ~~}~~~}~{|}~|{~}|}}~}{|xz|zy|Vȷ|}}}~|}{}~{{|}|{x y{¿¿K*ܟƸ|}~}~~~}~}{|{}|{||y{|{yz|½ ɚ!寬{|{||}}} ~}}~~|}||zz||3{}|xyz{zz¿ X!z{|}|}{}}|}}}{z|}|{|{y{}{xz½"!." ͮz{z|}{}|}|}}|y{|zz{|y||{4 ͛ 󯬪z{|{{||{| {yy{z{|zx{yzzz|¿ Vz{yz{|{z{||{{z{z{zz{xyW¿%ڡ)ᯬ|zyzz{{z{z{z{|{y|y|{zyz{xy# } yz{z{zz}{z{yzzxyz|(Gޥ Ȯyzy|zz{zyzxz x  𯬪yyzzyzxy{ywyyxyyx0$n٣ yxyzyxyxx}½ >3,ޯ{xyyzyxzzyz{wwy|   Ûyxyzzxxywvy( O΢Įxxyxywy Ҧ$~wyxywxwvD nЦxwywxw 3ơ  ٯx-޸0w0 ƥ (Я믬 Ϩ ѲǪ Φ ΰα2կ  *ǠƪԺ' ٿ׻ʪ!гʰո%诬 #Ť׼Ʀ5/ϴ˱ӳЮѽۿğ6 §г׳6 éͯǣ&䯬  ؘ̭޺ 8Ƚղ٘ӯ̮IJ$߻٘ ǡ9󯬪;||}~̧ ⼙' z{|~ܘ۳~*௬  zy{z{~ 丘Ӭ~~;/yvwwvxyz|{{|}~~ Υ~~;Ȯ{sstuuwwvyxyz{|}~繘ɟ ~<𯬪$wqqrssttuuwxyz{{|~~Úǝ< sppqr sttuuvwxwxyyz{|~Ԣě=ޯ(rmnonoppqprrsstuvwwxyyz|{~*ě>%xlkmlmmnonnopqrqssrssuuvxwwyyz{{|}~ќƜĮ,ujiijjklnopqqrqsuvwxyyz{|}-Ȟ}ogghihhikjkllmlnnopnqrs utuuvwxxyyz|{|} Ϡ wideghihhijkjlmlmnonoopqqrsrtuvwwxz {||}~К֦ ٯreb dedefigghijiiklmmnoq prsrtstutvvwyz{||#ޭ5{mcbaabe fhfghihgjjlmnononpqpqrrtstuuvxyz{$ug_```babdedegfhighkjkklmnpqppssrstuuwxwyĘ 믬}od]_^_a^_a`abdbccdefgghhiijkmlmmnpqqpprrtrstuuzҠ yi_]`_]_`]^` _ababcdccdfghijiijjklnoqqprrqrtxᬖ կ"uf_`^]\^]^_`_`bbabbcddceffhijkjklkmmlnooqqru  5~pb`]Z^_\ ^[\\]\]^]]__^``acdfefghihjjkjklnow 񵖖Ԡ :~na_]\__[^][^]\]Z\]_]^_a`abcBeffeefghhijijillkx )诬rc^\]\]^[Z\\[\]Z[]^]^_^`acbdegefghihhkjjx ˚ /sgb_a`__`^\[^\[]YY[[Z\[Z\[[\[[\^^__`abedceffggimz4 0Юxgdaab`__^^]]^\YZYZ[ZX]\ZZ[\\^_`_``acd efkz ș .}l^ba``_]`__^^\[\[YZXY[ZY\]\^]^S_^__`a`bbcdkz!xfaa__`_]^ ]\[Z[ZXYYXYYX[Z Y[[YZZ[Z[\^^\^(`^_``a`ky@͚5䯬paa_a]`_``__^\^]\]ZZYZ[ZVVXYYXZ\[[]\\_^`l{'~ka]__^\^^`a_]\[^\[ZZ\ZYYXWVXYVWXZYZZXZ[Z[[\\[`ny̮֞n_a`_` ^_]^_^^]][[\ZXZXYWVXWWUVXWVWZWWYWX YZ[[cr| %󯬪oaa``ba_^_\[^^[\Z YZXY[YWVVXVUVXWWYWVW X[eq/sa`a`a_^]a]__\]^^\\[][WZ[XXZWWTUVUVUVUWXUVW[gt' םE௬yfbaa`aa]^_^^`^_^[\]]\\XZ[Z[XWYWTUWTTSSTVVUV\it bab`a`]__^_^"`]]\[Y[\[YYXWZXWXVWUSTRSSRU\gt ) 'ڗ8Ȯbca^^a_[\\^_^^\Y]\YY\\ZYVYVSWVTWVUTSQR\iv 𯬪ibaa`_^`][[]\Z[[XWXZ[XV%TURQUTTWer|< ֜^b_]\_]W^]^\Z^_ZYWWUXVUURUaq~  ! ȗƘޯ_^]^]\]^]Z\[\Z,YWXYXVWWTUTZjx%C~[]\_^[_ZY]\YX[WVWUTVXes~~~HĮ^Z]]^]\[]ZY\ZY-ZVZWVUTZhw~~  g]\]\]Z\[]ZWVVYXWVT\n|~~~~~D"\\_][YXZX WTXVV_k} ~}~}{}~||}|}~< !ٯ`^\]\XZYWVWXU`q~}}{{~|}~~}~(ߝ";x][\Z[YXXZXVWWZl~}|}||{||{}z|~~}{}}~-  ݛ(Z\[XY\ZXXWUUfz}||~}z {xx{}xz{z|}~c믬cYXZWX0^r|}zz|{zz{zwyxuz|y{}}~}}~ ''[[X!WXXgy}y{{y|ywxyxxzvw{yxxz}/-կ]XY[YX[q}}zy{xxyyvxyxxwv|ywyxxzz|{{}&6pZXWY_x|}{|x{|wxwuyvwwyyvvstxwuxwxz| @CVVZa{|~~xvx{{vvwtwvsutxvtutvuwxx|zz{|}}~ D 诬]Yaz |~zzxuyxyvtwuurssrtturtuwwxzzxy||~D$?a|}{||xuvuwxxvwsqvusqrtqsqrprwuuxyyxy||}~   NЮ{|z{z|xxy{totvstqqrpquqotrotssuwuwxwzzy~|{~}~+ U~}{{xxwvrvvqnqrppopuspqpooqrrsswxvwvzxy|z~}~&   ¿|{vwwxtqtsqspqonor kprloonporstvu vy{zxxy}~ S ¿䯬|~|zywupstoponqprpqqtolklmnppmssruutvxy|{~{|)   )}}z|{svurppqonsplmpoprlmn mlmpnnopqsuwzwvx|{}4  ,̮{|zvwwxswqorpspmnonlnjlnmkjjknompq#uvtuwxxy|xz}~6 ܗ$󯬪~zxuzvtqrrlpnlolknrlhjkhlnmkkmnlorpqsuvwxz ~|{  (~|zx{yuqqoimnkjkmnmilihhkkj gmkijnmoqpr"suuvuxzxz}||~   ¿2௬zwsuvupnqnmmjijjhljfglhhihjmifjjiklm&npqrsutuz{xxy||}} - ¿(~|}zwvrrqqolmoplgjikjheffhghgihghiihjllmmorrqstv{x y|}~~  Ȯ}}|xyvusooljlljmghighgdeghfgf hfgihglloomoqtwxwxzyz}~9  𯬪yzvvwrsnllfimghjgighefecedegefhiil'mpnoqrstuxxzy||~ ¿2ywurwqpnkmhfhidgffekhefhgccdccdhedfefhilklornotrsvwwxx~~  ޯw{wqqonniHfefcfieccecbegcacaaebfeccegfgjlmlkmrpqtqtvwxwzz~~& ܘ!0tstrokijihdedbdggaca`a`eecaa__a_ceccf,giljmnmnoqtssvvy||y{~  2Įrqkmqmkjfeiedgca_adb_cc``ca^_a`^`ccacdeg*ijommnprstvywwz}4 !¿!{rlpkfhfhhgfg`ed]`aba^` ]^^`a`^_ccabdeg'jmljpqqprry{{z}}   "jmoieefcdfidc_`b`b_\__]^ ]^`a_`_acbcdfgljmopprrtwyz~}~' $¿bٯpmjgebefcddaa`_ad__b_\`]Z][\[Z^^]^_`a`chefhgkmkomnqrtwy|}}|  $'kmgagab`_a^__]][]Z]^\^]\XY[Y\]]\_ba`bgegigjmo%tstyxy{} Ϻ #'mjadgda`^_b_\^Y[ZX[][^^WXYXZY Z]^]\`ccddegjmoposrxzvz|'϶ ֛ 7믬nfiedeaa`\[\\Y\_Z^ZXZVZZXUVVWYZX\]\]]`baffegjn"qtutxwy}~ؾ $edcfa\c`ZZ[Y[X[\WZ\ZXVUVWVWXX\]3_cdfdfgklnqnpuvvxz~{ ϳ  ;կe_``]\\_[[YZY[YXW[XWXXVVUTTVWTUXX[][\]adfeeiijnot&qwxxy{}  ʫ șxda`d]X[VY[YXYTYXQUVT?UTUVWWV[_[\__cdgliklnotutvxy|}| ˫ #%c_`__\\YTVXVVZYWWUTQTUSRQQTWXVYZ\\_`afefijmooqqsvw}$ѯ￘诬e^]\[[YYZXUXVUXWSSUSOSQRPQRSUVXYYZZ_bbeffhj,orrqv{xwx۸ަZZYYWZXUUWVVWTTSRQSQNOR@PQSUUXXYY_adbefknklnpstv{z{y}ŢŚЮYZZVVWUUVUTTVSRPSQNPOMPOQT/UXX[^aabdfhpolnrusuvzx}}~  ز尗ﰕjWXVYWWUUSTRQQORPRONLMO@PSVUUY[\^`afgiiknnoquuty|{$ʣף% VTVSUTPNQOOMONQNMNMLNO PRUXX[^^\`e3kjjsrpuutv}{{} ߺ ǝᕕ䯬]VSSRPRRQONOKMLKKJLLKMMPPRTUY]\]a`bdhkmrrsuwzw{}~!Ѫ 뿙UUTPQOMMLKKLOMLLKJJKJJLCMPRUWXZ[\abceghkmmnnquv{zz} ȟ ̮SRPPTNOQNKKIILIJKJJKJHKJKLNPPSUXZ[\^_dfghfmpopsxwuyz~忚 󯬪dRPNKOLKONKGHFGGIHIILLPQRUWYX[aa_bhkhlonu.w{}|ߵ ߲'OPNPMLKJILJHGFHFGIHJJKPPMRXVXY[\aedbgimqopsrvw}+ ح ᷙ#"௬TQMOMLLKGGJHHFDEEFEGGHHJMONQTWZZXagehihmoorruy{}}%ҩ hzMMJJIJIEGGFHGFEEDFFEGHHJJKNMPVXUZ[^_dcciijlkqsquy{|~Х ЭȮMJHHFCFEDEBDCEMGHIJMMOOSWZX\]^abdeghnrnqwvv{͢ ᾠ¿𯬪WJGGDEFDCCDBCDDEMGKJKMMOOTW[Z^`addhjjlpqqstx{~¿ʟ Ӱo̩HGGDDBCECBDB@CCBBDDFEFGLMMLPSSUX^a_addglkknptvuvxʞ ǧ¿MĜIDDEECB@ABAA@BBCBBDEGEHILNPSSTUW\_aadacgjoqootuw}~ɞ ޼ ŲlCBABAA?>@ABCE4GIJNNOSTUUX[^``achlnnopty{ ǝ ӯľDB@B=>@ABCEEGFHJLPSQRUY\\[^aceiklopssx~ ǜ Ƥ OCAA=>?@ABTCFFEHILNORUUY^]^aafhfhknopwz||~ǜ ޺ =>>=>?@7ABABBCCDEFIKNOQTXUW[\^cefhhkootuxʝ Ѯ@=>?@ABVCDDEGIKNOPSVYWZ[^b`bfilmorw}{̝  ]==<==>?\@AABBDCFFEGKMPNQVWZ^^aadffinonpv|ˠ ҫW0;=<==>>?P@AABBCDFFELNOPQTWY[^^aedhnnprtx| Ρ น" M$+;<=>?A??@@AABCCEGFGMQSPWF[\_beielmlstx{~Ϣ  A$%'<==<=>?@ACVDHIKPPSTWZ]^^ceggipqovz{ѤǞʕ 7$$%%7=>=>?@=ABCDFGHKMOQSWUZ]`bbdeiijqsy ѦƜ /%$&''1=>A@A#BCDFILMPQTUYZ^^dgglimqouz| Ө *%$$%+==>?@X?ABCDFIKNSRSSZ^^_dggilrruw}Ө  %$%&:>?@[CFHJKLQVSWXZ^^_fjhkpruu½Ө ڣ z$$%$%'2>>?A @FGIJMMTVVZ^Z^bdfklrxsx~( Ѩ l&$$%$%%&')=?ARBCGIJNPPSXZZ``befhmprxy~¾ݠ! e$$%$%&'4?@BCFHHMRWUWW[`aiegmmpw}{ϧ _8$$%%&&%&O')=??@@ACCEIKMPWZZ]\`affnqqw~~̤̊W<;=?>??@AACGDEGHILQRWW[_`cegkonr{ ̊P;<<=>==>?E@ABCCECEGIIMPQVYZ\ccfokouw{̤ J<==?@BBCCEGILLMQRQY[_aggjpmo{ B<;=<==>??@?ACCDGGILLPRSUX\adcglmos| A<<;<=>?0ABDEFJKLMOSVXYZ_acgirsu}==<<;<<==>>>$?@?A@CHHJINPUXVWY]_bfgkotx~"½  |><=<=>==>>?@ ACCDHJKMOQY]*^efknjs}Φp?<<==<>=>??AKFHHINQSVXZ^`bacjijstϦ c<=>?>??@ ABEFFIKKOQUZ]`abfhnqr|" ¼K)*+,+,2.//12688><=ACACEIKMNPVWW^]geehjnrttru}w|  濽N:99:;<=@AAABCHKMLQ[XZY_^aeinsppzF;:99;::;;<=>?CF9DILQRRQX]^_`cfkqpsy|{ƽB;9::;;<=<@ADDJO2PSSTZabbjoqs{{~zͼ?<;:<=>@>DHJKPSTTX_bhmsw||}׼??<=<=ABCHHMPTTVW\ahlisw{x~݃Pw⻹}=>?=<==>@ADGIMOTWZ]*]adkqs{ WQMOr= >BDFJLNUWZ^^e,ilr|| XSOJ?>?CDILNTTW^bfgnz}'{bUQO?87T]>?>@AEILOSWY_ehrxw&_XQC98KU>?@ABCGPOT^^`iquw}+q^WH97RN>>@?ABCHMSZZ_hluzy +g]P<8XD>??@?BDDKQT[bejuz*gY?8`FAA@AAEFJSXYfnn{*paH:gBCB@AEGPS`dfo~*nS@jGCCABDIOV_u *z`LrGEDCHLP^fr~ !lUqLJEDMZds &}jwOJIOZn+~Z`k!ᢦx¿         "   )#(  #" /&Ŀ/½'Ŀ ! ½ +ѿ  £mWG7'# #'8GWn ĤnXH8'$ #'6GVl  鵃Q))Rп 뷄S*'Qн  ~>A¿B=~ߌm''n ¿q'&mȾ6܀+/ ¿/+Ⱦ6.W\\VȽ6Z ~BEտGA־6?D¿E?Ͼ67SJMOI-6 þ lpÿriξ6 !پĿ1(+۾¾,'*۾6aV\]U6 $½*--*6 ;ξ ľZ{Ŀ}&ӿſ6 "NX׾ÿTRؿĿ6 #ս"ý71:ٿÿ75$ܿþ6hž Ŀ&,ܿÿ('#ݿ¼6 !"ۿÿܿ¼6AͽþbؿĿ ٿ»5ſ 5&Ӽý; !$5ožĿ&!5 # #4'.5E̾ ľg0Iþ7!B5JpſSf5(¼@ z 5vžľ0  5& '&Dſ- = 5L ʽ ýp U _{ 5ľ2þ5-Ϻ G0'"I-A5~ i.9CRG<1 u-8CQH=2 5 ڸ * 󰵹FܸV +ſB}߹Y!&5SƸ v2򰵸LH`UDdw5𴸼0򯴸`}ſ[   5 1ʴL2񮴸>O·ovKJto52񮳷)Bſ&G  5Ԯ-0𭲶ES Ӷ}}¾MLն  r Ŀ5Y2ﭲ~ ڼ !$v 쿴 $Ŀ52^ Ǵ þ* þi dz+ ľ56T2¾* 8 ſ+ / þ5 2ߴ! ֿ ¾$㱶¿&ٿþ5 23쪰2v޴½ k ;j᳷`þ5a2쪯Kᴸ ¾ þC쳸ÿ  þ5 阘2몯v#þM׾Vپ½5;Y2ꩮ,3·þg¾3"^½52ꨮKɶ½%Bɵ5#62騭¼3˼>Ͻ5g2訬ſE^ݺPRĿ~὿42秬³շľ:M︼ص¾CC4?a2秫ʷxT¼I42榫Áطž#ĸܷľ)ɽ¾4%9奪ƶU0¼wӷ`(ٻ¿42䥩̼ޣſ*zžxߵ5pľl亽¾42䥩ò߷͸º+Q볶иſ5D¾42㤩ùү&ľs*!} 42⣨ժȾt\Qﶻ42k}·f׸»۷ ƻ4uD\3ǷÿgƻtD¼ϿlȺý/űw˺ »:κ4i_2¤դ|m\Kr,ϿUſ^̱_ ĽgԹ4&}_2ǡwکĹscTKB:6GϾC{ѯM žٹ42_2ѾĪyiYNE>6/;ξ2;ذ=0 ſܹ4N_2śuwśo_RH@93-*$7'ξ&Q뷽»}ۯ.I o⸼4c_2澖Ͷڕ`nveVLC;5/+)')ξ`績»yܯ&Q츽 n㸼4%_2|mɺʨY0XPG>72,)'(,ξf举ýmர%]績 e帼39^3RǤЌiͮva1-A:4.+((*;mⷽýmரd帾 ^縼2I]4ξʶsļtKst9.0,)')I;lⷽý lர]績 ]縼1k\5ŴܪˮkL^EA`B#)'(*;_渾ým߮U긿 i丼1&\5˻ɶ}^eEL@+;L()ξX跾»zۯ"N츾 l㸽14\5Ԭ~z`ZHE77. 65ξI»}گ%= rṽ1Q[H0ǷӾuiHa5J)9 .$!#7ξ)-հ5"ſݺ1 i[1ʾǺp9\UN@;2/)($%BϾ:аE žغ1  %Ô[2ܖ|?'G.8#-&&ϾJſf˲Uº ĽpӺ1 :[!2źTuG%)0. &&Ͽ`͸Ľ=²kѸüH̻1  N[3ůwɿ{kT;aV*(&&!=Ͽ|zݷpḽ ¼ļ1 p[Uϰ(øqaSI48N0$$Hſ< 1( [к'űǼwgWMD<4!3=!Tľ2º)1  :Z%ﶿ}n]QH?81,#2.Է¼=[紷ٸGP캽¾1U 衢Z%ϲ#ƔtdUKB:4.*&$ÿ^󴺿Ŀܶ%Rſy༽¿1q Z"ńzzjYOF=60+($$+ľCĸ¼ηNɸ½Ӽ¿1*ȦZcϰ C^SI@92-(&$#ſtſ1ﵻſ:żÿ1¨>Zи\6D<5/*&$$d0ĸ¼)(Ǹ½ 1ĬV Zm.21,'$##ľO]踼Ž\S1ŭuӬZ-ϲzywM(%#"þ/1ȶػ:+ʷ۽1ư ,YywusqX&"/ſo½Gƽ¾z½Rʾ1Dz?ưYqΰ zxvtromw8!ܴþ1촹þ)þ1Ȳ[갴Yз ywurpnkifY̴ľ(~ÿMеľ/t þ1ɵxY!yxvsqnligecľ[ŵĽiѿ½eȶľtӾþ1ɶ/ʹY4ϲ"zywurpmkhebõ¾.ƶþ& Ŀ2ʷCﴸZѿ"ywuspnkigdm0õ%+Ŷ¾  z Ŀ2˸_ƷY#zxvtromjgebľn>#ƴĿ0ſx9ɵ5ž2̹ÿ{"ֶYж#~ywurpnkifcGγ= O >Ҵ¾DCſ2̺ÿ0󹻾ÿY$yxvsqnligda¾?> < ſI9 > 2 ̻þ E˺ Y>ϱ%zywurpmjhebſ-ɳ¿/2* ̳¾3 * 2 ͼ¾`¿Xо%ywuspnkigcr* Ƴ 2 ȴ2ͼ"ž¾X&zxvtromjgebľMδ['FѶc $ 2μ4ѽĿXе&|ywurpnkhec(Ĺ!1yƺ%»3 ξG ÿY'ywvsqnligdaľ&,+$/ & ¼3 ξg˿ĿYJϱ(zywtrpmjheb2 nw$ 9kz'ü3 ξ&Xм%ywuspnkifcyľ&c›f*? &_Þj-6ý3ξ3X&zxvtqoligebH 1ϱzxwtrpmjhebOR%9KOOSЏLлywuspnkifcy4[OQ Ӗ"iߡe'(zxvtqoligeb IOQђT5*di-г{ywurpnkheb  RIOQ a"_ywvsqnligda  yIIOQ |4*XLϰ@zxwtromjhebIOQbDJ)к ywurpnkifc~UIOQ OE^-acyxvtqoligeb vIOQ Fw+X α~dHϲzy#wurpnkhebIOQ E!AVn{le\J7&yw usqnkigddKHINQ O;Sfyȱ]:}# Iϰzxwtromjgeb ZIHHIOQbT渍1\\]иywurpnkifc mHIHOQ |"OOM?. = 3tyxvtqoligebHIHOQ@cOI8% Vϲzywur"pnkhebHIOQiOM: zxvtromjg!eb GHGHN'juOݰgB  #4Jdж ~ywurpnkif cݓFGHGGHGH{azODz{} yxvsqnligda$ـFFGHufMO]ϱ zywurpmjhebmFGHGi U2Oо ywuspnkigcs[FGHbElO zxvtromjgeb%ڪMFEFGFG ]9AOrе |ywurpnkhec։EFFEFG]0tO^ ywvsqnligdaiFFEFG FGG\):OSϱzxwtrpmjheb"ثOEEFFEEFFEFG`( eOPлywuspnkifcx+}DEFEF#GFGg'Ozxvtqoligeb ױWEDEDDE(FEFFGGFm-?Oг{ywurpnkheb;҇EDEEDEF{-\Oywvsqnligda(ղ\DEFEFF-uOϰzxwtromjhebEDEDEEDEEFEFI 4!Oкywurpnkifc~3եQDEDEFER7.Oyxvtqoligeb  nDDCDCDEDEe H?Oϲzywurpnkheb$͈HDCDDCDEDXLOywusqnkigddԠREDCDCDEKhSOϰzxwtromjgeb  ԲcDDEDEDCDCDCD] w ]O иywurpnkifc#ҽpDDCDCBCDCDCD'cOyxvtqoligeb|ECDCDCDScOϲzywurpnkheb*ŇICCDCDCDDCCBBCBC Dv"[Oywusqnkigdh%ƊLCDBCCDCDCDBCBCBN[Oΰzxvtromjgeb NJLCBCDCBBCCBCt  2[Ozз ywurpnkifc"NJNCCDCBBCBCBT SOz yxvsqnligeb ŅKCBDCABCCBBCBCBCE HPOz"ϱzywurpmkheb ‚FBCCBCCBABCBCBCCBAa @OzпywuspnkigdlѶwFCCBCCBABCBBCBBAO  ]0OzzxvtromjgebϭiCBCBCB%ABBCBABBF #Ozж~ywurpnkifcʡ`BBCBCCBCB@ABAABCBAn o|Oz&yxvsqnligdaǎPAABABCBCAABA BABBAAa uO{ϱzywurpmjhebѸ{HBACBCBABBAABABBAABBAAB V rmOоywuspnkigcr ϦkB ABBCBABBCA@ABNfOzxvtromjgeb ŐUBA @AABAABAABA NaOе|ywurpnkhec ϵ{HBA@ABA@A @AL - YOywvsqnligdaʞfCBABBABAABA@AA@A@K- \[UOϱzxwtrpmjheb ÊQAABA@A@A@&?A@N POлywuspnkifcxЭqDB@A@A?@B@?@AA?@?Q ,3O zxvtqoligeb XBBAA@ABBA@?@A@@A@@?@?@@ZVOг!{ywurpnkhebͫuFCBABA@@BBA@?@@?@@?AB@?@?d  |Oywvsqnligda˾Y@BAA@@AABA@@??@A@?@@AA@?Aq  -mmOPϰzxwtromjhebʡlDAAB@A@?@?@??@@?@?G} `^ORк ywurpnkifc} ˱}J@AABA@@A@A@"?@??@@?@@>?@S *DTOUyxvtqoligeb̿YABB@A@A@?A?@?@@?@?@?@?b9O\ ϲzywurpnkhebŚjBABABBA@?@@A@?@A?>A@?@?>Evÿþ¿ o.Oa ywusqnkigddƢtHA B@AA??@A??A@@?@? >?S UkOkϰzxwtromjgebǨyM@A@AA@@A@A?>@@?>?@?@>??> ?dÿ(K*ZOwиywurpnkifcŮQ@A@A@?>>?>??>? >?Duÿ  PO"yxvtqoligebW@??@A@A @AA??>?>>??3?@?==>?>?T XO#ϲ zywurpnkheb_?@A@A @A@>>@?>??>>@>=@n@-bOP$ywusqnkigdd?@@??@A@A@@A@>>?>>??>?2?>?>R ROWΰzxvtromjgebI?@??@@?@@A@A@?@>=>=>>2>BoUOcз ywurpnkifcj?@? @?@@?AA@?@?>?>??==>>V:%]Os yxvsqnligebA?A??@@>?>?>Iy= }Oϱ zywurpmkheb[>?@?A??A?>? >??Bh*GeOPпywuspnkigde>??>?>?A??A?>=? >[ OZ! zxvtromjgebF>?>??>?>? N~03maOmж ~ywurpnkifci>?>?>=Gt¼ >2}O yxvsqnligda?>?>??>?@==>Dk"  QNNOSϱzywurpmjhebU=>>??>??>=@c* N^NOdо ywuspnkigcg=>?>?>>=?] eNOzxvtromjgebC=>>?>=Z¿ meNOTе$|ywurpnkhecg==>=>=X+2\NOm/ywvsqnligda>>=>SʇQNOϱ0zxwtrpmjhebQ=R&  ݢcNNNObл0ywuspnkifck}&vNNOP!zxvtqoligeb  {P }QNNOgѾaг2{ywurpnkhebzHNP yPNNQǘcCBywvsqnligda ۵qHNPҡoNN{ѥoFCBBuϰzxwtromjheb   بdGGHNP꿎_NNbټODBDкywurpnkifc| ʓXGFGHNP֩yQNNW ϛbEDCB4yxvtqoligeb#շ{IFGHNP`NNS۲xJEDCCBBPϲ5zywurpnkheb ʙaEFGHGHNPܬ{SNNU ʐVGFFEDCCB ywusqnkigdd 0 ЫwIDEFGHLPƗfNN[ ۬lHHGFFEDCCBϰzxwtromjgeb  зTDDEFGFGHMP沁VNNjˍTHGFEDCBEи6ywurpnkifc ̻[CCDEFGFGHNPxONNRoIIHGFEDCB7yxvtqoligeb_BCDEFGHGNPuONr֘YIHGFEDCBUϲzywurpnkheb  dz[BCDEDDEFGHLPQN_MJJIHGFEDDCB8ywusqnkigdgâzRABCDCCDEFGFGHMPϐXN_ hJIHGFEDCBΰ9zxvtromjgeb.kF@@AB CDDCDDEDEEFGHNQlNa ڙYKJJIHGFEEDCBGз'ywurpnkifc ( zV?@??@@ABCDEDEEFGHJXMMNOqΆOKJJIHGFEDCB%yxvsqnligdb  bC>>?@ABCDEFGO؋PMNVzKJJIHGEDCB[ϱ;zywurpmkheb+gF<=>?@??@ABCDEFG^؅NMNjlKJJIHGFEDCBп;ywuspnkigdkgH;;<=>?@?@ABCDEFGtߍOMNW`KJKJJHGFEDCBD,zxvtromjgeb &dF::;;<=>?@?@ABCDEFWRMNl]KJKJHGFFEDC[ж-~ywurpnkifc }`B:;<= >==?>?@@??@ABCDCDDEEIy`MNT[KJKJIHFEDHz=yxvsqnligda tY@89:99:;<=>?@?@ABCDFgMN_YKJKJIHHGFFED`ϱ zywurpmjheb*mR:789:;:;<;;<=>==?@?@ABCD^VMNcYLKJKJIHHFE J{о ywuspnkigcp,fK7667678789:;:;<<;<<==>=>>??@AB-CB[MNe\LLKJKJIHHGFFEEYzxvtromjgebw_D5566566789:9:;<=<=>?@AB^aMNY_LKJIHHGFFGqе |ywurpnkhec oV=345656789:;<=>?@ADfRMN~kMMLLKJIHGFN(ywvsqnligdaiQ833234335656789:;<=>?@ LsMXxMLKJIHGZϱ+zxwtrpmjhebxaI532324345456578789:;<=>?A_MjޅMKJIHkл>ywuspnkifcvoY@22323 4345544665678789:;<=>=>OsxMqSMKJIHx zxvtqoligebhP:1212243434565667878899:;<GjqMk`MKJIMг {ywurpnkhebx`G3121212323456789:;:;323321010/0/./.0/01123 4345>Ys gMSMLKM ϰ&zxwtromjgebfI233212010/././0/0./0010112>324433A[tMqMLK и!ywurpnkifcw[<2321210100//./././0//00101212!C]w@MSLMLKq yxvtqoligebpP4213210./..0.-./0114H`{@ ]MLKLK` ϲzywurpnkhebhH323121101 3010010/../.-.-.-././06MBc}M[LKJKRywusqnkigdgkI1221122012110/./..-,.-.-,..--.-./>Uk1 %OMLKJΰzxvtromjgebnL323212001100//0./../.--,..,-.-.- .1DZr wMwsLKJdзywurpnkifcxQ3212112002200/0/-/0-./--,-.--,-,-.,,.5K`v ' M_YLKJPyxvsqnligdb^:3202211211001100/./#0.-.,+,.-,,+,-,,-8Pcy  vMNLKJzϱzywurpmkheb_22322112112211210 /00/./.-/.--,+,+,:Of{$ 'M~LKWп+ywuspnkigdd2230021/0012110.00..00/.-/-+.-+-,+Yv} ~|z}~{{|{|~  qMZMMLp ywvsqnligda10/ -./.,--.,<\y~|}{{}||}~|}|~LSMZMML"ϰRzxwtrpmjhebF/0/./.--..,.-4Ux~}|}||{{|{~}z{}}|{||~C M_VMLQ%лywuspnkifcg/0/-./.- ,-Jm~|}}yzxw{|xz~{z|}~~}> M}XML]'zxvtqoligeb3.-.A-,--<_~||zz{{zz{ywyxtz{yz||}|}~ RM]MLp'¿г({ywurpnkheb]//,--.-.Jn~|yzzy{yvxzvwzywwyz|}|}JMdML#ywvsqnligda0../--4[~~||zxzxvwyxwvuuv|ywyxxyz|{{|# MmML%ϰ@zxwtromjheb@/.-.:g{}{{xz{wxwuxvwwxxvusswvuxwxyyz{||6MMLM!кHywurpnkifcg,,.@l{~}wvx{zuuwtwvsutwvsutuuvwx{yy{{}}~ 4MoMLR!yxvtqoligeb1.An |~zywtxxyus vuuqsrqsstrstwy wx{{~~};MPMLU&ϲWzywurpnkhebY@q|z~{|wtvuwxxuvsqvtrqrspsqqorwttxyxwy{{}}~~ _MVMLU!9ywusqnkigdcm{y{y{wwxztosursqqrpqtqosqotssuvuwxwy}{{}}~~+  MyqMLY!ϰzxwtromjgeb}}zzw:vruupnprppnotroqpnnprrsrwxvwvyxx|y}~}~* MMLU*иywurpnkifcz{{vwvxtpsrpsoponoqkprlnpoqrtvtuuvy{ywxy}~~:+MyMLP!yxvtqoligeb{}|yyvuprsopomqpqpqqsokklmnpplsrrt vwxwy{z}{|  cMbMLMϲ)zywurpnkheb~|}y{{rvuqooqonrokmonpqklmkmpnpqrttuwywvx|{}|}}~ ) MWMLywusqnkigdf{|zvwsvpnqprolnomlniknlkijknmpooqqtustvwwy{xy|~~/ OMoNML!ΰ>zxvtromjgeb~~yxuzvsqqrlonlokjnrkhghjkgknmjjlnlnqpqrrsruuwwy }{{# MlMLoз5ywurpnkifcz~|yxzxupqnilnkjjmnmikhghjkjjiglkhjmloqor sttvuwzxz}{|~   OMMLW!yxvsqnligdb~ywstvupmqmlmji hkifglhhigilhfi+jklmlmpqrrtsuyzxwy|{}| < M\ML"(zywurpmkheb~||yvurrppnklnplfihjjgdeehghfhgihjlnrqprtv{xwxx{}~~}  TMML!¿п ywuspnkigdh||{wxuusnnkjkkjlfgighgdeggfgfhegihgllnnmopqqsswxvxy|~~9 MwRML`!ί3zxvtromjgebxzvuwrrnlkeilghigfghghdedbeccddeggeehiil'mpnnqrsttwwzy{{}  zMeML!ж%~ywurpnkifc}xwtrwqomjlhehidfefejheeggbcgeceh kllkloqnnsrsvx}}7 [MSTML! ȯyxvsqnligdavzvpponmhfefcfiebbebaegcaca`dbfebcegfgil'klqppsqsuwxvzz}}& PMMLV#ÿ ϱ0zywurpmjheb{srsrojhjihddcadffab`_a`edca`^^`^bebbe giljlnlmnpsuvy|{x{} MaVML"о`ywuspnkigclqpkmpmkifdhedfb`_`db_cc__ba^_`_^`bbaccefggijnllnpqssuxwvz|# MMLc"zxvtromjgebvqlokfgfghgfg`dd]`a^`_8]^]`a`^_bcabdefggilljoqqpqqx{zz|}~ M_fML"е"|ywurpnkhec{jmoieefcdfhcb_`b_b_\^_]^]`^__acbcdefgglljlnporrtwxz}~}~~' MyMLf%ywvsqnligdapmjfebefccd` _`c__a^\`]Y][Z]^]^__a`cheehfklkomnqqswx||  MPML%ϰzxwtrpmjhebvjmfafaa`_a^ ]\[]Z]]\]\\XY[Y\6]\_aa_bfefigjlnnotssxwxz}~ ۱m NM^RMLc%л'ywuspnkifcoljacfd``]^b^\^XZYW[]Z]^WWYXZY7\^\\`bbcdefggjlnpnrrwzuz{~&ް]NNTV TMiMLzxvtqoligebkehecda``\[Y\_Y]ZWZVZZWUVVWYZX\$]_baffefjiimnpsuswvy~|~”hNTV bMuxMLSг%{ywurpnkhebvddcfa\b`ZZ[Y[X[\VZ\YXVUUWVWXW[]\ _bcfcfgkkmpmpu#wz~{ 䳀UNTV ~MNMML!ÿFywvsqnligdac^``]\\_[[XZX[YXVZWWXXVVUTTVVTUWX[][[]acfeehhjnnstsqwxwy{|.uONTV PM|ML ¿ϰ!zxwtromjhebmc`_d\X[VY[YXYXXYTYWQTU%TSUTUVWWV[^[\^^bdfkhkknnstsuwy{|{uONTV gMsMLc+к3ywurpnkifcob^__^[[YSVXVVZXVVUSQTTSRQPSTSSWWVXZ[[^`aehjmnopqsuw}-|PMNTWNM]XMML)¿3yxvtqoligebb]\\Z[YYZXUWVUXVSSURSSNSQQPQQRTVWYYZZ^aaegj(orrquzwwxȊVMNUWgMOML+ϲzywurpnkhebpZZYXWYXTUV&WTTRQPQPSQNNRQQPQRTUWXYY^`cadejmjlnortvz%x}~&ߣeMNOUWRM]MMLaÿywusqnkigdcXYZUVWUTPUSROSQPPMNPNLOOPQQSTUWWZ]a`bdfgonlnrtstuyw|}~~ ŁPMNOUW|Ms~L+ϰ"zxwtromjgeb`WXVYVWTTSTQQPOQORONNMLMO?RVTUY[\^``fgihkmnoquuty|{~$hMNOUWdMRYLLKL1и&ywurpnkifcoVTSTVVRTTPNQNOMONPNMNMLKKNNORUWX[^]\`e3kjjrrotutv|zz}ӐTMNOUW WM_LLKLM*¿yxvtqoligeb\VRSRPRRQOONOKLKHJKLKLMOPRSTY]\]``adghhkmqqsuwywz|}!tMNOUW QMsLLKl.ղkzywurpnkhebkUTTPOPQOLMKJKKOMKLKJJKJJKKLMOQTWWZZ[`acdghjmlnmquvzzy}aLMNj ݈NMRLLK)ܾıywusqnkigddSROPSMOQMKKIIKIIJJIKJ!KJKKNOPRTXZZ[]_cfghflpnorwvtxz~#!ݘXLMNY |M_LK)zxvtromjgeb\QONKOLKOMKGHFGGI8GIIKLOPRUWYW[``_bgjglomttutw{|{ ҉PLLMNf ΀MN{fLK2ÿlywurpnkifcoOONPMKKIILJHGFHFFEFFIIHJJKOOMQWVWYZ\`ecagimqnorrvw|}| zLMNk ҈PM]QLKL-¿yxvsqnligdbQQLOLKGFJHHFCEDEEFGHGJ3MONPTWYYW``afdgigmnorrtxz|}sLMNj YNMPLKc!¿zywurpmkhebeMLJJIJIEGFFGGFDFEGHHJJKNMPUXUZ[]_ci1jljprqux{|}mLMN]  uQQPPONNMsLKy'ywuspnkigdfMJGHFCFFEEDCCDEBDCEMFHHJMMNOSVZX\]^`bcdghnrnqwuvzfLMNQ Ӗ]TRRPNNMaLK'zxvtvomjgebRIGGDEEDCBDDCBCDDE FKJKMMNOTV[Z]``cdhjjkpqqstxz~ aLMNi }TRPONM`LK&h~y~zifcmHGGDDACDBBCB@CCBBDDFEFGKMMLPRRUX]`^`cdfkjkmpsvuuxÿaLMNQ ަjRPM`K) }ndaID`ECB@ABAA@BBCBBDEGEHIKNPRSTUV\_``dacgjopoosuv~}}aLMN] ̑XMeK&bYCA@A?=?@ABCEOFIJMNNRTUUX[]`_achkmmoptx{ aKLMNt |NMvK& hCA@A=>=?@ABC EGFHJKPRQRUY[^`cdikkoprsx~`KLMNP eMOK&JC@A==>==>?@@AABTCEFEHIKNNRUUY^]]`afgfhkmopvz|{~`KLMX ̏VMMaK(æ =>>=>=>9?@ABABBCCDEFIJNNQTXUVZ\]bdfhgjoosux dJKLMa xMMK%?<=<=>==>?@AB&CDDEGIJNOPSUYWZZ]a`bfhkmoqw|{!dJJKLMn ڙ[MMgK N=<=>L?AABBDCEFEGJMPNQUWZ^]aadffimnnpv|iJJKLMu sMMRK% ?);<<=>?O@ABBCDFFEKNOPQTWY[^]addhnmorsw| kJKLM{ ЊPMMK*6;<=<==>A?V@ABCCEGEGMQRPVWW[[_adhekmlrtw{~mJKLMN VMMK'å-;<=>>?AC?DHIJPPRTWY\^^cegfhppnuz{qJKLMOZMMsK$ ī$3=>==>?=ABCDFGHKMNQSVUY]_aadehijprx uJKLMOWMMqK$ ~)=>A@$ABCDFILMPQSUYY]^dgglimpntz{%wJKLMOQMMqK# u<=>?@ ?@BCDFIKMSRKY]^_dgghkrqtw|xIIJKLMO ܄MM|Km# j8=?@[CEHJKKQVSWWY^^_ejhkortu~wHIJKLMO cMMLKX% ¤](==>?AL@EGIIMMTVVZ]Z^bdeklrwrx} wHIJKLMOOMMM]K$ ħQ;>>?A]BCGHIMPPRWZZ`_aeegmpqxy~wHIKLO_MMTtK( T,>>??@CFHHMQVUWVZ`aiegllow}{)wHHIJKLNMnKU7'<>?@@ACCDIKMPWZZ]\_afemppv}~ rHHIJKLM̊P<;=?>??@AACGDEGHILQRWW[_`cegkonr{ ̊K;<<=>==>?E@ABCCECEGIIMPQVYZ\ccfokouw{̤ åG<==?@BBCCEGILLMQRQY[_aggjpmo{ A<;=<==>??@?ACCDGGILLPRSUX\adcglmos| z@<<;<=>?0ABDEFJKLMOSVXYZ_acgirsu}s==<<;<<==>>>$?@?A@CHHJINPUXVWY]_bfgkotx~"½  k><=<=>==>>?@ ACCDHJKMOQY]*^efknjs}Φáb?<<==<>=>??AKFHHINQSVXZ^`bacjijstϦ Y<=>?>??@ ABEFFIKKOQUZ]`abfhnqr|" ¼C)*+,+,2.//12688><=ACACEIKMNPVWW^]geehjnrttru}w|  H:99:;<=@AAABCHKMLQ[XZY_^aeinsppzC;:99;::;;<=>?CF9DILQRRQX]^_`cfkqpsy|{@;9::;;<=<@ADDJO2PSSTZabbjoqs{{~zĤy><;:<=>@>DHJKPSTTX_bhmsw||}ʥūu??<=<=ABCHHMPTTVW\ahlisw{x~݃PkŴj=>?=<==>@ADGIMOTWZ]*]adkqs{ WQMNc= >BDFJLNUWZ^^e,ilr|| XSOJ?>?CDILNTTW^bfgnz}'{bUQO?77KU>?>@AEILOSWY_ehrxw&_XQC98EO>?@ABCGPOT^^`iquw}+q^WH96JJ>>@?ABCHMSZZ_hluzy +g]P<8PC>??@?BDDKQT[bejuz*gY?8WDAA@AAEFJSXYfnn{*paH:\BCB@AEGPS`dfo~*nS@_GCCABDIOV_u *z`LhGEDCHLP^fr~ !lTiLJEDMZds &}fpOJIOZn+|yZ`k!ޜx¿                () %/&þ/ 'þ !  'ҿϾ  ζyl_SOH OS`ly¿ зzm`SPH OS^lxվ  ĜtTHTtϾ ƝvUHStϻ  ˘eHg͙hHdܿߣȊRHR ǿˎRHQƽ^HHVHY ȿYHVƽ^HLXHyHI|¿|IHHxƼ^H{ HÿhHjվĿlHHgּ^HJHGeHjϿjHHeν^H_uHGnHqĿrHHm-Ǿ^H ½HG HI¾IHHͽ^H Nؽþ[HGſSHVھVHHS*ۼ^HȾľHGxH}Ŀ~HHwȾ^H $LHGUHWÿWHHU̾ſ^H cν ý{HGHþHH&Ѿľ^H "ĿHGqGyֽ¾vGGt׾ý]G NԻ"^GFZGGaؾ¾^GG]$ھ¼]Gż ýGFPGGU۾¾SGGR#ܾ]G ĿMGFIGGMھ¾KGGK۾]Gf̻GFſHGGL־þKGGK ؾ ]GľGFGGKľHGGH Կž]GQҺbGF HGGMĿJGG$KοĿ]GļýGFKGGQſKGGMĿ]G ſOGF OGG]RGGX]Gjʼ ¼GFEſZGGm½_GG!h]G ľGFEnGGľuGGӿ]GSfGFE GG ſGG]Gļ½GFE0FFLȴFFJ \FGĿPFED RFF&iľVFFcԿĿ\Fo ɻ FED wFF ſFF Ŀ\FýFED2ſFFH¼FFGĿ\FV͸ þkFED0RFF"lVFFfϿĿ\F ÿFEDFW_htkbZIFF FV_hslcZJFF Ŀ\FH ٷ ÿTFEDC󯳹FjƤwLFFUľGFgƧzMFFPѿĿ\Ftŷ ¿FEDC2򯳸oFlHFFwFiłJFFĿ\FF𲶺GFEDC0򮲸FKFFJľF{LFF HԾ ľ\F FZʲoFEDC2񭲸dFr¶GFFnFm˿HFFľ\FFFEDC2񭱷ERۿfEEIпľEOۿjEE GӾ ľ\EEIԭUEDCB3A𬰶iEt ӴEEoEoԴEE Ľ\EEyEDCBA2אּE ں LEENĿE 콲 NEEJ̾ý\EEGEDCBA2ſ}E Ƴ SEE ¼E ű¿TEE ý\EE \tEDCBA2FESEE ^ ľHEÿTEEE W ½\EE EDCBA2ſE߳¿LEE վ E⯴OEEؽ\EEKYEDCBA2쪮¿YEݳEE ÿaE౵ÿFE\EEEDCBA2쪭Em᳸ EE K½Eg챶þEH¾\EE 阘IEDCBA3@멭ſEMoEEֽĿEJ𾵺wEټ \EEayEDCBA@2ꨬ¿TEIEEZELE|\EEEDCBA@2ꧬEmȵEEN¿EgȳĿEJÿ\EEM\EDCBA@2駫ſEZEEʺEĿcEλþ\EEEDCBA@2觪ľiD|ĿDDܸqDsýD໽¾[DDJDCBA@?2禪ƴEDӵ¾`DDoHD׳gDg[DDdDCBA@?3>禩˹~DtDDJDlĿDE[DDDCBA@?>2楩²ƄſD׵þMDDöD۵ýRDȻ[DDN^DCBA@?>奨ȸſuDXDDѵDQĿDع[DDCBA@?>2䤨ξߦþSDľDD޳[DüD㸼[DDCBA@?>2䤧ĵD̷SDDr벴Dηľ[Dh𷼾[DDCBA@?>3=㣧ƻӳCO¼CCRCJCJZCCBA@?>=3<⢦׭ɿvCzžCCCqﴺCZCDCCBFBA@?>=<2qøgCַCCCڵ CŹZCCCBgzBA@?>=<2ȹ‰jǽvH¼ϾCǸUCCįCɸ _C͸ZCąCCD|BA@?>=<2Ħ֪~n^Mu-ϾtCĽ|CC˯}C ¼CӷZCOCCB|BA@?>=<2ɣ{ۭźudVLC<9HϾfCHžCCЭnCD þCطZCCCW|BA@?>=<3;Ƭ{k[PG?70? νXC`žCC׮aCW ľCܷZCoCCB|BA@?>=<;2Ǟy{ǞqaTJB;4/+%9'νNCq뵻CCڭUCk ĿC᷺ZCGCCB|BA@?>=<;2θۙbpwgXNE=71,*(*νCB}涽BBۭMBq붼 ĿBⶺYBBBK|A@?>=<;:2繀qʼˬ\2ZRH@83.*()-νA㶽AA߭LAz緾 A嶺XA]AA@{@?>=<;:93kɧґnΰwf3.C;50,))+ͽ>ᵼ>>߭@>~䷾ >z綺U>>>hy=<;:98748˹xŽuMww;02.*(*Jͽ;ᵼ;; ߭;x淾 ;x綺S;;;>v:9876544ƶݯ̯lN`GDbF%*()+ͽ9x嶽99߭B9p鸾 9㷺Q9H99t87654324̽˷_gGNA,=N+*ν8r絼88ڭD8i뷽 8㷻P888Rt76543215ó֮|b\IG880!87!νA8d88٭F8Z Ŀ8᷻P8l88t765432169ɹylIc7L*;!/%"%8νJ8Mž88ԮT8Dľ8ܸP8 =88t76543216Ȼu;^VOB=40))%&CϽX89ž88Ϯa8 þ8׸P8 88 E“t776543217cޛ·ĂA(I/9$.''Ͼf8Ľ}88ɰo8¹ ¼8ѸP8 X88t76543217vƻUyJ&*1/!('Ͼx8˷»Z888иd8ʹP888is76543218zW.||mV>dX,)''">Ͼ8ܵſ:888෼ =8úP8 88=s654321 =~s(\ĹscUK5;Q2%%9d񲹿ľ99;9Z 9Q9J99s765432 x8'ǵȽyiYOF=5"5>#U9;½99S99KQ9 99 Xs765432 n{k%6o_SIA93-%409ӵ[99u粵9طſd9k츽Q9 o99 蟡s765432 1u"#iɕvfWMD<5/+'%½?9w򲸾þ99۴F9mľ9ߺQ9@99 r765432 yR"Ɔ}|l[QG?81,)&%,ý`9>ö99Ͷi9;Ǹ9ӺQ999Jǥr765432 C}s =E`UKB:4.)'%$ľ:ľQ:::ľY:ݾR:]::s8876543 2x3ra7E=60+'%%eſ:Pö::K:JŸ:CR:ê;::qs8876543 x{hp042-(%%$:¾k::w縻:𴹿ýu:oR:ì::@ӫs876543 4u?Q)&$#¼P:QƴĿ::׺Y:MɶĿ:ۼR:Ů M:::r76543 yMtY)#0ľ;e;;ż;n;ȽS;ư;;;^Ưs87654 J|s:ſ;E۲;;S;B쳸;L½S;DZu;;=꯲s87654 w/ I@;t˲¾K;;C;jδ¾Q; ½S;dzC;s87654 {c üv<)ij½<<о<ǵ¾<ҽ½T<ȴ<PͲr98765 6u ſ<B³<<Pþ<@ĵ<J þT<ɵa<r998765 xG D<R³A<<H<NõD< þT<ʶ><yĵr98765 Q|s ý=^IJþS==þ=ZǴ W==ѿýU=ʷ=Fյs:9876 4v+ Rſ=fͱ^== l =^ѳc===bþU=˸S=󸹼r9876 z_ _=^ 쾳 ]== ޿ þh=Z ^== ĿU= ˹= dʸ ÿr9876 8t ľ>Q ȱR>>Uſ>O ʲV>>OĿU> ̺{>@췼s:987 6x@ O>C ű C>>U>A dzF>> ĿU>̺H> ļ r::9876\|q ý>kͲx>>Lſ>eϴ~>> I U>ͺĿ>Vмþr::9876 v& ^ſM?D·G??U?CĹJ??V? ͼg?¾r;:98 7z[ ý?KP??Pſ?JS?? K V? ͼÿA?ʾþr:987=~s ſV@GԏJ@@\@F֒M@@W@ ν@Mٿ¿r;:98 x< %ý@M~ͭP@@ a @L{ίR@@YW@νW@r;;:98 e|n iAHW`hh`WHAAqAHV_hi`WHAA XAνAn¿s<<;:98u% kľCAA"EAA| XAνAEr<;:9 zW AANAAJܿXAξ LA ÿr;:9 B~s Ŀ\BB$bBBYB ξ B^ÿs<;: 9x8 .¼CBB"żEBBz YB ξ+mBBþr<;: p{k ذBBW ҮYtBB S ¼YB ξ Fþr<<;: 8u" |߰ĿpCCGԞjWSMJwCC D¼ZCξ½r=<; yR ½TCCҔ_URNIEA=XCC ¼ZCξ ½q<; H}s HDD؏XSPLHEC>:8YKDD!ؿ¼[Dξ½r=< :x3 9ſDDvSNLIFB?=:730.`·DDo¼[Dξr==< y{h ĿEE+eRHEDB?=:7630.+)'wEE_½\Eξr>= ;u þEE1_]?>=;:86431/-+(&%"!EE$Y»\Eξr= yM ½EEY643210.-,**'%$" EEV ­\Eξr= M|s FF_A..--,**)'&$#"! FFZ!ɿ\FϿ(q>>={c !FFp?RFFjʪ\F&p==u "HGG!([KGG|$ũ]G#q>xG UGH^ !bĿXGG*ª]G%pU|s mGS7 5qGGPѿ]G"lv+ I¾Hs%>IHHo^H#z^ ¾[HL۾6^HHJݿí^H"෱t ĿHpпZ&IHHl+Ȯ^H"ްx@ hITAnĿlIIRϰ_I!Γ|q ¾VIKӿ/5YIIJ.տҸ_I"½ðv& SQIJ#vÿTIIŮ_I ¾z[ ¾TIJп1UIIJж_I "Ŀ~s _JQӾ Y`JJPԿ`J "Ŀx< !!ÿȀKJm ؿ k ¿ʄMJi`J!ſ|n ¿uKJe ؿ W0¿wKJb`J"u% "\¿UJNyӾ KLIKWJNw"տ  äzW ߶jNKaȾ@r*#}ාkOK`ʿ "ǖ~s  ĭsg[RK OWdp}Ͼ8л= įtg[SK OWdp|п  "İx8 +˾.'Ք5wͿ &ʌ{k #̿(5 nS %̶u" j+&$FEz¿&ͩyR $!W .Η}s ""j  &ʰx3 #3(~ J  Џ{h "!!pK z !ѷu #z `  +ѬyM M#!  !Ҕ|s  ?({Q!˰w/ $@ 2-C ӓ{c & %6!Ӵu '?'&ҰxG #rJw^!Ӑ|s #JW?!ɰv+ $M0e Әz^ 'x-!Ӭt (Xyeȼ Ұx@ )3 Fٚi=/T%Ӎ|q )~%F2~!ưv& ']A3() k Ӟz[ ' FV5$!ӥ~s (B_m{N"Ѱx< )${KV2"Ӎ|m )4&( X,!ðu% *h Z;-꾓oK+9g(4ӤzW *. !aQwBD2Iӟ~s +Q 2"qÆG$v>аx8 /+} D@g+#N!ӎ{k  YzݚW =ahԾu" u d5ьI"v1U^l\=ԩyR ( nm~<-N~ԛ}s N y!6f* z(2ϰx3 7~ y%wڧtA*Ԑ{h  w#C<1.Ժu  s &hUXӬyM / ke;ԕ|s X ^A %C]nzzoX0\ Ͱw/ C O-1!M׎;;Ԕ{c  @!~bL,SԴu  4f-mVVZӰxG 1 |'Y9}U(ũ}yplcԐ|s X gLbG ʰv+ N SAS@ Ԙz^  ?>K/mi ӭt s .>"RsӰx@ #> t#>R=d^ԍ|q > aDmr{5ǰv& ]W MK!Ԟz[ v =WEԥ~s q/dLqҰx< $F ߃("rԎ|m  $x$+sŰu% h߰  n:#D դzW ߴ  kLzՠ~s ߷ k` Ұx8 +ޮ p k"s_Z Վ{k ޥ Sk- u" uޕ; x;3F3Su yR |, (Mr#C՛ }s m 1`F~а x3 7ݤ?"r5Ր{h ߅ T(aYmջu ` w/XԬ yM A+8"C#Օ |s # L>886Ͱw/ C }>W1)Ք{c ! 8>v)3յu 2 m>:$6԰xG hD:! )4Ր|s /,4)˰v+ N '=\<ՙz^   yN4G+ծt aS"_԰x@ ɿCMz Ս|p }B)@xI%Ȱv&  m4R<7՞z[ # =&;%*vH{զ~s {*] 8CӰx<   Fo M]"Վ|m XP 1Vm{{lL#SŰu%  @7_(դzW 7t)b~s  + K'x8  " {k z}(d xu" lu1, |yR R uD}s  8 xX+аx3  +xgdՐ{h % ZKջu  !   (b+"_ԬyM  % -6Ōb>t!Կu" s%ncWKi ԩyR  Ҏ) gSi!Ԛ}s   ҨA 9 v \h-ϰx3 5 дR'ib^ ԑ{h `+b^!Թu 'oY"Z^&ҮyM %t# %ZZ!Ӕ|s v#W  2ZS!̰w/ Au%. SS ӓ{c  n!i GPwS!Ӵu ϼi@ ?rS&ҰxG [) \0iS!ӏ|r  ΢K k  #`SȰv+ KȐ> P  nWS Ҙz^ y+ @ MU!Ҭt ϱa 2qB^&Ѱx@  ̘J(:^'Ҍ|p  |/'{3^(ưv& Y ά`&{ - )a ҞzZ D  #u [Z$i!Ҧ~s  νt++*y  r&аx< % ϢT/ +3v!ҍ|m 4$8Ul!u$ e ˟YG   W ФzU ɺ{5U! ,lB!Н~s ǓM!f_0 *Ͱx8 *ʦe$0|¿ C#$ Ѝ{k  ʹy6E"9,!Ϻu" o‹K%^½ n.c4 ϪyR ÖY! /w TA@!ϖ}s Şb&"F½ K)*O!ʰx3 4æi- _2  e-ϐ{h p44w%¿ W].϶u T '-5/ήyM ./qÿ   &%ϒ|s UTZ6*Ȱw/ 7:x$.K&Γ{c %c<}uh"ΰu M F8#ͰxG  <{9r*$Ύ|r  *hl4B$İv+ !\ 52We#Θz^ Q     !%Ψt H N07&̰x@ >y7 :`'͋|p ;s l:""Ϳv& 7o02ވ.C#̞zZ !2l#  e }"̡~s "0k* ԇ75#˰x< %h ܻNv$́|m  ۺ\$ W;"Ϸv4u$ `ٹ\!$ ښS #ÅBzU "  ٧O!$džFUϗQ .~s  ՗?!$o16زk& x8 *~0!$͐R ( ̇> j{k ((ӫ^!$s4#{٥Y u" )k  Ň>!$ӕW!%z v- yR *" ͞Z $y;-ؙH 4}s  #ίo. $^%@s( x3 2ʵx7!$ۚS!eߞL v{h  y>!$ޖNI, u tũr9 $]1` yM -  b. $p(1w ? <|s -O!$C4| ~+ w/ ;c5c})Hh {c . tG"nk&^U u /}!wM$7d@}D xG 0uM&Um&_6\|r 0 pK#!0y"C07{v+ D*mG!\4#]- `Șz^ !c?E]3s*<Ȥt 2}Y7:|&8~+`ưx@ 3sR/ 7rd:/3lj|p  kI';x5)s4Sǻv& N a@  H~![@#mǞzZ 4wX8%Yz){Q4ǜ~s )lP0 @q a@fGİx< 3"cE%-YQJ$ZƉ|m wY:#O{3IB4 jƶu$ XnO0 "KuI0M#uŤzU 'bC$$Lu S}} (|Ɩ~s '}[:(Ms(bG3-°x8 'xX61.Tx}Y)Ŋ{k |[8 5Y} 4%&IJu" _^<[w $ (}Z)xG %A]x b<=_|r  (y'yL z{ytv+quompprojnnlkmhknjjiikmllnlnonppttrsuwvw{wx{}!  Fh*|s +r~}ywtytrppqknmkojjnpjgfgijfjmliiklkmqppqrtvwxxy|zz~&  acDH"w. -~~zywxwupqnhlljiilmkhkgfgiji fkkhhlknpnppqrtsusvywy{zz|~ 0 q)+{c ~ ~xvssutompljgiigkhffkgghfhjhehijl%opqqsrsxywwx{{||% ns/,¿u [}{{wutqroomjkmokeggiigcddfggfg/fghhfijklkmqppqsuzwuwwz{}||  $fq'xG {{zvwttrnmkjkeghggfgfcdfgdgddehdfhffjklnoruwuwx{}}|2  P"4(|r 'rwzutvqqmkjehlfgigefgfhddcbdcbdcdefddfhhk'lommoqrssvvywzz|~  U:w(¿v* S3wvspupomikfdfhcedeeiheeffabcabcfccddeghjkkjjnpmnrqqtuuww||~ .#$j'z^ $tyuoonmlhggedecdhebbda`dfcaa``caedbcdfik'jlpoorortvvuxy}|}~: g~(0s TXrqrrnjhihgccbacfe_a_^``ccba`^^_^acbbddeggkjllklmorqrttxzzxy|? 5&.x@ 5ppkkokiiecgdceb`__ca^bb^_aa]^`^]_ba`ccdffghjmllmoprruvx{~# r8&|p lokojegef _cc\_`a_]`^;]^]_`_^^bb`accegghkjjnpoopqwzxx||}  s3='¿v& 6imnhedebcfgca^^a_a_\^^]\^_^_^`babcdfgglkjkmonqqsvxy|~  rS<'¿zZ mkifdadfb_``^_b^^`][`]X\Z[ZY]^2`_bgddgejkjnlmporvx{~||{ }}c1¿~s Yilfae``_^_]^^\[Z\Z]\[]\ZWXZX8Z[\[^``_aedeggillnnsqrxwvy~|| ҜnC 1"8'x< kjabec__\]a][]XZYW[[Y\]VVYX8Y[][[`bbccdfggillnmqquxtxz|~1֛e/#& $@-ÿ|m dehcbc_[=Z[YZ]X\XWXVYYVTUUWXYW[\Z\\^a`eedfihhlmprtrvux}|| ſw=#& 6NS$'u$ 8dcaea[a_YYZYZWZ[VZ[XWVTUWVU;VXW[\[[\^bcecefijlplostuvx}z~~ ޞ_$#& []q-zU b^__][[_[ZXYWZXXVYWUWWVUST UTUWWY\ZZ\`cd.hgilnssqpuxwxzz ٓO#& V,~s Tb_^c\X[VYZYVXVWWTWWQSUUTTSTUVY^Y\^^bcekgjkmmqsqtwxz  ܖN#& =Kc8+x6 a^[OXSUWVVZXVUTSQSSRQPPSTSRVWUXYZZ^^`ddehilmnppquvvu|Y#&x/).Ƥ{j \][[ZZYXYWTVUTWVSRTQQRNSPPOQ SUWXXYY^a`eghhinq(tzvvw~l&#&=\+Ĕu" 8ZZWWVXXSTU&VSSQPOPPROMNQPPOPQSUWWXX^_badeimikmoqsuy%x|~~ي;#&!06 ҎyQ 6WYZUTWUTUTSSTRQOSQPPMNOMLNNPQPRSUWWZ]``abegomkmpsrstxv|} %a#&VK[v.Ս}s LVWVXVUSQPPMPOQNLKLMLMNNOQUTUX[[]__eehhjm)putrx{z~~>#&9"f+"ԍx3 TSSTTURSRONPMNLNMONKNKMN ORUWW[^\[^ekiiqqnttsu{yz|~ s%#& (2-ӎh3^ys; XURSQPRRQOOMJLK IJKJLMOPQRTX[_7`bgghjkppruvyvy{|#O#& { KD.Ӟ$XS :TPOPPOKLKJ NMJKJIIKIIK2MOPTUWXZ[`abdehikkmlpuuyyx| 6Ay g"fu*x! RQNPRMNQLJJHHKIIJJIJIHJIJKMNOQTWXZ[\^ceegekommruuswy}~)*d V2`)ѺB MPOMKNKKNMJGHFFGGHGIIKLOPQTVXW[_`^afifknltvz k :} ]V=2ЧQ "ONMOLJJHHLIHEEFEFEFFHI b y0$"!5k$ԯ: EIFFCDDCBBCDBABCDMFIIJLLNOSV[Y]__cdgiiknqpssvz} 7? X$"4&?Y}x' #HFFCCACDAACA@BCPEDEGJLLKPQRTX]_^`ccdkikmnsuttv~7c ֍@!4)ֵmUz GCDCA@@A@BRCDGDHIJLPQRSTU[^_`babehnonnrsu||}7/ s):%{S 6BA@A@@?=?@ABCD0FIILMNRSTTX[]^^`bhjmknoswy} 7N VOy%غ uCA?A=>=?@ABC DGEGIJPQPPTX[]`bdhjjnnqrw|~17m ي:zn&ԥ@B??==>==>?@@AABTCEFDGHKMNQTTX^[]_aegegjlnntx{z|7) p&5s(ڿ `=>>=>=>?@ABABBC"DFHJMNPSWTVZ[]bcegfinnrtw}<5 Qqw&ب ~><=<<>==>?@ABC#DGHJMOPRTWWXZ\`_aehjlnou{z},<E }-=w$ ~?=<;<<=>\?AABBCCEFDGIMPMPUWX^]``ceehlnmou{~}AP K"w%ڬ~y/#:<=>?@@ABBCCFFDKNOOPSWXZ^\`dbgmlnprv{~  FX jx}*~v':<=<<=>A?(@ABCCDGEFMPQPVWW[[^adhejlkpsvz~'Ha }&\'ݲ~s :<<;=>>?AC>GHIOOQTVX[^^bdfegoomtyyMi+K! ~}n0=><=>? ABCCEGGKLMPSUTX\^aabdghioqw~ #Rk(Iz!޸ ~}g"=>@$ABCCFGLLOPSTXY\^cffkhmomsy{}%Wj} I`" ~}_ <=>?@?@BCCEHKMRPRRY\^^bfDjqpsvz~Wj bVF# ~|U5=?@KCDGJKKPTSVWX]^^difinpst} Wj 8s)%ܥ ~}|J!==>?AK@DFGILMTUTY]Z^acejlpwpw|߸Wi}2 ~}{? ;>>?AB FGIMOPRWXY__`cdglopww|߸Wi3$M(ߩ ~}zG&>>??@BFGGLQUTVUZ_`gdfklov|{) ޴Wc\Fs ~}|yN6N :>?@@ABBDIJMPWYY\[^_fcloot|~ޮPc̊~}|vL<;=?>??@AACGDEGHILQRWW[_`cegkonr{ ̊~}|uH;<<=>==>?E@ABCCECEGIIMPQVYZ\ccfokouw{̤ ~|{rE<==?@BBCCEGILLMQRQY[_aggjpmo{ ~}|{n@<;=<==>??@?ACCDGGILLPRSUX\adcglmos| ~}|zk@<<;<=>?0ABDEFJKLMOSVXYZ_acgirsu}~}{ze==<<;<<==>>>$?@?A@CHHJINPUXVWY]_bfgkotx~"½  ~}|{z_><=<=>==>>?@ ACCDHJKMOQY]*^efknjs}Φ~}|{yY?<<==<>=>??AKFHHINQSVXZ^`bacjijstϦ ~}|zwQ<=>?>??@ ABEFFIKKOQUZ]`abfhnqr|" ¼~|{zt>)*+,+,2.//12688><=ACACEIKMNPVWW^]geehjnrttru}w|  ~}|{ztE:99:;<=@AAABCHKMLQ[XZY_^aeinsppz}|zyqA;:99;::;;<=>?CF9DILQRRQX]^_`cfkqpsy|{٤}{zyn?;9::;;<=<@ADDJO2PSSTZabbjoqs{{~z 췤|{zxi><;:<=>@>DHJKPSTTX_bhmsw||}Ȍ|{yxf??<=<=ABCHHMPTTVW\ahlisw{x~݃Pc}{zx^=>?=<==>@ADGIMOTWZ]*]adkqs{ WQMMr|zyvY= >BDFJLNUWZ^^e,ilr|| XSOJ?>?CDILNTTW^bfgnz}'{bUQO?87FP>?>@AEILOSWY_ehrxw&_XQC98BL>?@ABCGPOT^^`iquw}+q^WH96FH>>@?ABCHMSZZ_hluzy +g]P<8JA>??@?BDDKQT[bejuz*gY?8PDAA@AAEFJSXYfnn{*paH:UACB@AEGPS`dfo~*nS@WGCCABDIOV_u *z`K`GEDCHLP^fr~ !ޠlRcLJEDMZds &}dlOJIOZn+yvZ`k!ܚx  vÔāđī ׁ֖ב׫  Ⱦ  ƾ ö ƹ Ǻ Ȼ Ȼ ɼ ɼ ɼ ɼ  ɼ  ɼ  ɼ  ɼ ɼ ɽ ɽ  ɽ  ɽ  ʽ  ʽ  ʽ  ʽ  ʽ  ʽ  ʽ  ʾ  ʾ  ʾ  ʾ ˾ ˾ ˾ ˿ ˿  ˿ ̿ ̿   'QL d  o  9,    Y  (#  ¶  ¶ G  ·    3  o 1 D  5  $    a    #       V    !      N    (      ^    )      [    '      L    &      I    $      B    #  n   ! 6! !  $  a"  #! 2#  #"  &"! W$"  %#!  2$"  '$" )&#! (&"   '%"  &$! ๫&#  ĸ%#  ĸ$"  ĸ$! ĸ#!  ķ"   ÷!  ÷! !÷  !÷ "÷  ¶ ¶  ¶ ¶ ¶ µ             ̿ ̿ ̿ ̿ ̿ ˿ ˿ ˿ ˾ ˾ ˾ ˾ ˾ ˾ ʾ ʾ ʾ ʾ ʽ ʽ ʽ ʽ ʽ ʽ ʽ ʽ ʽ ʽ ʽ ʽ ʽ ɽ ɽ ɽ ɽ ɽ ɼ ɼ ɼ ɼ ɼ ɼ ɼ ɼ ɼ ɼ ʼ ɼ ɼ ʼ ʼ ʼ ʼ ɼ ɼ ɼ ɼ ɼ ʽ ʽ ɽ ʽ ʽ ʼ ʼ ʽ ʽ ʽ ʽ ʽ ʽ ʽ ʽ ʽ ʽ ʽ ʽ ʽ ʽ ʾ ʾ ʽ ʽ ʽ ʽ ʾ ʾ ʽ ʽ ʾ ʾ ˾ ʾ ˾ ˾ ˾ ˾ ʾ ʾ ˾ ˾ ˾ ˾ ˾ ˾ ˾ ˾ ˾ ˾ ˾ ˾ ˿ ˿  ̿ ̿ ̿ ̿ ̿  ̿ ̿ ̿                                                                                                   ̿ ̿  ̿ ̿ ̿ ̿ ̿ ˿ ˿ ˿ ˿ ̿ ̿ ˿ ̿ ̾ ̾ ̾ ̾ ̾ ̾ ˾ ˾ ˾ ˿ ˾ ˾ ˾ ˾ ˾ ˾ ˾ ˾ ˾ ʾ ʽ ʽ ʽ ʽ ʽ ʽ ʽ ʽ ʽ ʽ ʽ ʽ ʼ ʼ ʼ ʼ ɼ ʼ ɼ ɼ ɼ ɼ ɼ ɼ ɼ ɼ ɼ ɼ ɼ ɼ ɼ ɼ ɼ ɼ ɼ ɼ ɼ ɼ ɽ ɽ ɽ ɽ ɽ ɽ ɼ ɼ ɼ  ɼ  ɼ  ɼ  ɼ  ɼ  ɼ ɼ ɼ ɼ ɼ ɼ ɼ ɼ ɼ  ɼ  ɼ  ɼ  ɼ ɼ ɼ Ȼ Ȼ Ǻ  ƹ ö  ƾ  Ⱦ  !"%%&'&((*,.0237A{zyrpjhc_a^YXWTSMLJGJGFA=<=;::76754533110/.-,+*+))(''&%%$$##""     !"##$&&'(+,.13 ½9~vyvtqigd__a\YWTSLNHEGEB<;:;986543110/-,+**)(&%$"!     !"$$%')*-/M2xyxwpoifed]YVWXONNKFFE@?=;986765341/.-,,++**))(('('&&%$$##"!    !!#%((*üww|nrtgga`[XXTNFGEE??@;6754220//.,+)('&%$"!       "%'(|ttwrokjedcaWVROMGGID?:>:753541/.,+)('&%$"!   !$'*wtv{rljiec``^ZXWUUQLIDEDDB98663120- ,+**'('&''&&%$#!   !L þ,uqmiked_]Z[_UQUTRONHAD@;;96201.,.-**)'& %$$##""!   ½'}vqvrlkb`aZ[YURRQLKLMGEC?B?:861/-.,+)('&%$#!"!     zw&tnfdfc^\XRWWNLLJKFHDC@<<9:;221.-+((%&&$#"!!   }wts+ljd`YZ[XVNMNLIECBEB<=;<:7421..,*)''%$#""! !!  ) Essoqna a^\VUVMJKHC ?>@==;854421/,* (%%##""!   + v¾}|vtlhdab_TQRRJJEBA?>:877632561.,-)+(&'$"!!    ~tole_\[WUQKMFAD?<799473/0/.*+,*))($$##""!  * wxpjia]YPPKEIB?A>;663/2-,*.)&'(%#$#"!  * A繽ysoe^`\QMJI??<99011-- *))%&&%#"#""!   * kչreb^YMMIF>;960/..,)(%%&#""!  ! ɴwse`TLOD;775../,'&%"!   & ζvl_UOCA983.,+(&&$#!  + EyjcYIB?>72/*'%"!  ! `jhTKBBA<;6/+*%"   V1=2,,034/*'&   #$$!      mapper-0.8.1.1/images/mapper-icon/Mapper-small.psd000077500000000000000000052276451325266516600217300ustar00rootroot000000000000008BPS00`B8BIM?Z%GZ%GZ%GZ%GZ%GZ%GZ%G8BIM%iꮁs$(X8BIM$: Adobe Photoshop CS6 (Windows) 2012-09-17T17:26:25+01:00 2016-05-30T18:55:22+02:00 2016-05-30T18:55:22+02:00 application/vnd.adobe.photoshop 3 sRGB IEC61966-2.1 xmp.did:95DF2A00DA00E211A07B995EF2A73201 xmp.iid:e20e3a5d-70dd-4fc5-a68d-31d49f41fd9c xmp.did:95DF2A00DA00E211A07B995EF2A73201 xmp.did:95DF2A00DA00E211A07B995EF2A73201 created xmp.iid:95DF2A00DA00E211A07B995EF2A73201 2012-09-17T17:26:25+01:00 Adobe Photoshop CS6 (Windows) saved xmp.iid:A07CBA907103E211B602E2A7DEB16909 2012-09-20T23:21:46+01:00 Adobe Photoshop CS6 (Windows) / saved xmp.iid:e20e3a5d-70dd-4fc5-a68d-31d49f41fd9c 2016-05-30T18:55:22+02:00 Adobe Photoshop CC 2014 (Macintosh) / 8BIM: printOutputPstSboolInteenumInteClrmprintSixteenBitbool printerNameTEXTprintProofSetupObjc Proof Setup proofSetupBltnenum builtinProof proofCMYK8BIM;-printOutputOptionsCptnboolClbrboolRgsMboolCrnCboolCntCboolLblsboolNgtvboolEmlDboolIntrboolBckgObjcRGBCRd doub@oGrn doub@oBl doub@oBrdTUntF#RltBld UntF#RltRsltUntF#Pxl@R vectorDataboolPgPsenumPgPsPgPCLeftUntF#RltTop UntF#RltScl UntF#Prc@YcropWhenPrintingboolcropRectBottomlong cropRectLeftlong cropRectRightlong cropRectToplong8BIMHH8BIM&?8BIM Transparency8BIM Transparency8BIM5d8BIM8BIM 8BIM x8BIM8BIM 8BIM' 8BIMH/fflff/ff2Z5-8BIMp8BIM8BIM 8BIM08BIM-8BIM@@8BIM8BIMnullbaseNameTEXTUserboundsObjcRct1Top longLeftlongBtomlong0Rghtlong0slicesVlLsObjcslicesliceIDlonggroupIDlongoriginenum ESliceOrigin autoGeneratedTypeenum ESliceTypeImg boundsObjcRct1Top longLeftlongBtomlong0Rghtlong0urlTEXTnullTEXTMsgeTEXTaltTagTEXTcellTextIsHTMLboolcellTextTEXT horzAlignenumESliceHorzAligndefault vertAlignenumESliceVertAligndefault bgColorTypeenumESliceBGColorTypeNone topOutsetlong leftOutsetlong bottomOutsetlong rightOutsetlong8BIM( ?8BIM H HLinomntrRGB XYZ  1acspMSFTIEC sRGB-HP cprtP3desclwtptbkptrXYZgXYZ,bXYZ@dmndTpdmddvuedLview$lumimeas $tech0 rTRC< gTRC< bTRC< textCopyright (c) 1998 Hewlett-Packard CompanydescsRGB IEC61966-2.1sRGB IEC61966-2.1XYZ QXYZ XYZ o8XYZ bXYZ $descIEC http://www.iec.chIEC http://www.iec.chdesc.IEC 61966-2.1 Default RGB colour space - sRGB.IEC 61966-2.1 Default RGB colour space - sRGBdesc,Reference Viewing Condition in IEC61966-2.1,Reference Viewing Condition in IEC61966-2.1view_. \XYZ L VPWmeassig CRT curv #(-27;@EJOTY^chmrw| %+28>ELRY`gnu| &/8AKT]gqz !-8COZfr~ -;HUcq~ +:IXgw'7HYj{+=Oat 2FZn  % : O d y  ' = T j " 9 Q i  * C \ u & @ Z t .Id %A^z &Ca~1Om&Ed#Cc'Ij4Vx&IlAe@e Ek*Qw;c*R{Gp@j>i  A l !!H!u!!!"'"U"""# #8#f###$$M$|$$% %8%h%%%&'&W&&&''I'z''( (?(q(())8)k))**5*h**++6+i++,,9,n,,- -A-v--..L.../$/Z///050l0011J1112*2c223 3F3334+4e4455M555676r667$7`7788P8899B999:6:t::;-;k;;<' >`>>?!?a??@#@d@@A)AjAAB0BrBBC:C}CDDGDDEEUEEF"FgFFG5G{GHHKHHIIcIIJ7J}JK KSKKL*LrLMMJMMN%NnNOOIOOP'PqPQQPQQR1R|RSS_SSTBTTU(UuUVV\VVWDWWX/X}XYYiYZZVZZ[E[[\5\\]']x]^^l^__a_``W``aOaabIbbcCccd@dde=eef=ffg=ggh?hhiCiijHjjkOkklWlmm`mnnknooxop+ppq:qqrKrss]sttptu(uuv>vvwVwxxnxy*yyzFz{{c{|!||}A}~~b~#G k͂0WGrׇ;iΉ3dʋ0cʍ1fΏ6n֑?zM _ɖ4 uL$h՛BdҞ@iءG&vVǥ8nRĩ7u\ЭD-u`ֲK³8%yhYѹJº;.! zpg_XQKFAǿ=ȼ:ɹ8ʷ6˶5̵5͵6ζ7ϸ9к<Ѿ?DINU\dlvۀ܊ݖޢ)߯6DScs 2F[p(@Xr4Pm8Ww)Km8BIM8BIM 00 Adobe_CMAdobed            00"?   3!1AQa"q2B#$Rb34rC%Scs5&DTdE£t6UeuF'Vfv7GWgw5!1AQaq"2B#R3$brCScs4%&5DTdEU6teuFVfv'7GWgw ?>cKr@v|*x?Zz]J[nOİYMm?ZgRWt}Pkf`mg޲,WfكFӎn[fuu~J0DŽᏦHee_G֮~7BfC1ۭ6uߤl3>gE u.}ŏIGFٕтEmf_^[o/M+Dc/&}3%\˟nJkaŋۘ2J>'G؟k}ytO>G=Utl썬vFV5+&0U-5(O{ 7CYxVKk79i,}GGVd`ϳܦCT.z6-\Ჺ{]:g9ǵ;z] vE;Z`'9c?uudzLCvˋ7W<*܌L^_>H2&:COﺦNOڨsj\k+ ;f_I,̹X!3Ws}Y k{V ju :é}_k7/1ﯧm ͑ۏWw:¡՗}2缀KH=Mn;ޜ+7eu4˝YX}^S,ߞ}&[Hp}ь$HN"?ɏ(=75cMiϥcV%ZOFm8?[Wp]ư s?SGb&j8BIM!]Adobe PhotoshopAdobe Photoshop CC 20148BIM"6MM*bj(1$r2i ' 'Adobe Photoshop CC 2014 (Macintosh)2016:05:30 18:55:2200&.(6HH8BIMmopt4TargetSettingsMttCObjc NativeQuadBl longGrn longRd longTrnsbool fileFormatenum FileFormatPNG24 interlacedbool noMatteColorbooltransparencyDitherAlgorithmenumDitherAlgorithmNonetransparencyDitherAmountlong8BIM-msetnullVersionlong8BIMms4w8BIMmaniIRFR8BIMAnDsnullAFStlongFrInVlLsObjcnullFrIDlongL6FStsVlLsObjcnullFsIDlongAFrmlongFsFrVlLslongL6LCntlong8BIMRoll8BIMDmfrinullAFStlong@ 8BIMnorm0(8BIMluni </Layer group>8BIMlnsrlset8BIMlyid8BIMlsct8BIMlspf8BIMlclr8BIMshmdH8BIMcust4metadata layerTimedoubAb8BIMfxrp2LXW/24//.rr8BIMnorm8/.(Outer8BIMSoCopnullClr ObjcRGBCRd doub@oGrn doub@oBl doub@o8BIMlfx2|nullScl UntF#Prc@YmasterFXSwitchboolDrShObjcDrSh enabboolMd enumBlnMMltpClr ObjcRGBCRd doubGrn doubBl doubOpctUntF#Prc@*uglgboollaglUntF#Ang@^DstnUntF#PxlCkmtUntF#PxlblurUntF#Pxl@NoseUntF#PrcAntAboolTrnSObjcShpCNm TEXTLinearCrv VlLsObjcCrPtHrzndoubVrtcdoubObjcCrPtHrzndoub@oVrtcdoub@o layerConcealsboolFrFXObjcFrFXenabboolStylenumFStlInsFPntTenumFrFlSClrMd enumBlnMNrmlOpctUntF#Prc@?Sz UntF#Pxl?Clr ObjcRGBCRd doub@d Grn doub@d Bl doub@d 8BIMlrFX8BIMcmnS8BIMdsdw3x8BIMmul !8BIMisdw3x8BIMmul 8BIMoglw*8BIMscrnY8BIMiglw+8BIMscrnY8BIMbevlNx8BIMscrn8BIMmul 8BIMsofi"8BIMnorm8BIMvmskD5TD5TD5TD5:D5:D5::::TTT8BIMluniOuter8BIMlyid 8BIMclbl8BIMinfx8BIMknko8BIMlspf8BIMlclr8BIMshmdH8BIMcust4metadata layerTimedoubA8BIMsn2P8BIMfxrp?o\`? Ɇ 8BIMlyvr-,RR8BIMnorm8 -,(Inner8BIMSoCopnullClr ObjcRGBCRd doub@oGrn doub@npBl doub@na@8BIMlfx2 nullScl UntF#Prc@YmasterFXSwitchboolGrFlObjcGrFl enabboolMd enumBlnMMltpOpctUntF#Prc@GradObjc GradientGrdnNm TEXTCustomGrdFenumGrdFCstSIntrdoub@ClrsVlLsObjcClrtClr ObjcRGBCRd doub@m Grn doub@m Bl doub@m TypeenumClryUsrSLctnlong Mdpnlong2ObjcClrtClr ObjcRGBCRd doub@mGrn doub@mBl doub@mTypeenumClryUsrSLctnlong3Mdpnlong2ObjcClrtClr ObjcRGBCRd doub@n`Grn doub@n`Bl doub@n`TypeenumClryUsrSLctnlongrMdpnlong2ObjcClrtClr ObjcRGBCRd doub@mGrn doub@mBl doub@mTypeenumClryUsrSLctnlong Mdpnlong2ObjcClrtClr ObjcRGBCRd doub@oGrn doub@oBl doub@oTypeenumClryUsrSLctnlongMdpnlong2TrnsVlLsObjcTrnSOpctUntF#Prc@YLctnlongMdpnlong2ObjcTrnSOpctUntF#Prc@YLctnlongMdpnlong2AnglUntF#Ang@ZTypeenumGrdTLnr RvrsboolDthrboolAlgnboolScl UntF#Prc@bOfstObjcPnt HrznUntF#PrcVrtcUntF#PrcChFXObjcChFX enabboolMd enumBlnMMltpClr ObjcRGBCRd doubGrn doubBl doubAntAboolInvrboolOpctUntF#Prc@laglUntF#AngaDstnUntF#Pxl@(blurUntF#Pxl@.MpgSObjcShpCNm TEXT GaussianCrv VlLs ObjcCrPtHrzndoubVrtcdoubObjcCrPtHrzndoub@@Vrtcdoub@ObjcCrPtHrzndoub@PVrtcdoub@CObjcCrPtHrzndoub@XVrtcdoub@Y@ObjcCrPtHrzndoub@`Vrtcdoub@dObjcCrPtHrzndoub@cVrtcdoub@j ObjcCrPtHrzndoub@gVrtcdoub@m`ObjcCrPtHrzndoub@kVrtcdoub@oObjcCrPtHrzndoub@oVrtcdoub@o8BIMlrFX8BIMcmnS8BIMdsdw3x8BIMmul Y8BIMisdw3x8BIMmul 8BIMoglw*8BIMscrnY8BIMiglw+8BIMscrnY8BIMbevlNx8BIMscrn8BIMmul 8BIMsofi"8BIMnorm8BIMvmskHVHVHVHq Hq Hq q q q VVV8BIMluniInner8BIMlyid 8BIMclbl8BIMinfx8BIMknko8BIMlspf8BIMlclr8BIMshmdH8BIMcust4metadata layerTimedoubA8BIMsn2P8BIMfxrp@x7Q0? Ɇ 8BIMlyvrZC G C >8BIMnorm01(Map8BIMluni Map8BIMlyid8BIMclbl8BIMinfx8BIMknko8BIMlspf8BIMlclr8BIMshmdH8BIMcust4metadata layerTimedoubA8BIMPlLdplcL$b96baa42-6715-1179-bc96-d795218d25c4Q`wOrž-@Mkl0I hP@QZx/@U.jZ`A-MtzE@Xʇwarp warpStyleenum warpStylewarpNone warpValuedoubwarpPerspectivedoubwarpPerspectiveOtherdoub warpRotateenumOrntHrznboundsObjcRctnTop UntF#PxlLeftUntF#PxlBtomUntF#PxlRghtUntF#PxluOrderlongvOrderlong8BIMSoLdsoLDnullIdntTEXT%b96baa42-6715-1179-bc96-d795218d25c4placedTEXT%b96b84eb-6715-1179-bc96-d795218d25c4PgNmlong totalPageslong frameStepObjcnull numeratorlong denominatorlongXdurationObjcnull numeratorlong denominatorlongX frameCountlongAnntlongTypelongTrnfVlLsdoubQ`wOdoubrž-doub@Mkldoub0I hPdoub@QZx/doub@U.jZ`A-doubMtzEdoub@XʇnonAffineTransformVlLsdoubQ`wOdoubrž-doub@Mkldoub0I hPdoub@QZx/doub@U.jZ`A-doubMtzEdoub@XʇwarpObjcwarp warpStyleenum warpStylewarpNone warpValuedoubwarpPerspectivedoubwarpPerspectiveOtherdoub warpRotateenumOrntHrznboundsObjcRctnTop UntF#PxlLeftUntF#PxlBtomUntF#PxlRghtUntF#PxluOrderlongvOrderlongSz ObjcPnt Wdthdoub@JHghtdoub@E?RsltUntF#Rsl@Rcomplong8BIMfxrpB@yU *2228BIMnorm(P(Text8BIMlfx2nullScl UntF#Prc@YmasterFXSwitchboolDrShObjcDrSh enabboolMd enumBlnMMltpClr ObjcRGBCRd doubGrn doubBl doubOpctUntF#Prc@BuglgboollaglUntF#Ang@^DstnUntF#PxlCkmtUntF#PxlblurUntF#Pxl@NoseUntF#PrcAntAboolTrnSObjcShpCNm TEXTLinearCrv VlLsObjcCrPtHrzndoubVrtcdoubObjcCrPtHrzndoub@oVrtcdoub@o layerConcealsboolIrShObjcIrSh enabboolMd enumBlnMMltpClr ObjcRGBCRd doubGrn doubBl doubOpctUntF#Prc@>uglgboollaglUntF#Ang@^DstnUntF#PxlCkmtUntF#PxlblurUntF#Pxl@NoseUntF#PrcAntAboolTrnSObjcShpCNm TEXTLinearCrv VlLsObjcCrPtHrzndoubVrtcdoubObjcCrPtHrzndoub@oVrtcdoub@oOrGlObjcOrGl enabboolMd enumBlnMMltpClr ObjcRGBCRd doubGrn doubBl doubOpctUntF#Prc@2GlwTenumBETESfBLCkmtUntF#PxlblurUntF#Pxl@NoseUntF#PrcShdNUntF#PrcAntAboolTrnSObjcShpCNm TEXTLinearCrv VlLsObjcCrPtHrzndoubVrtcdoubObjcCrPtHrzndoub@oVrtcdoub@oInprUntF#Prc@IGrFlObjcGrFl enabboolMd enumBlnMOvrlOpctUntF#Prc@YGradObjc GradientGrdnNm TEXTCustomGrdFenumGrdFCstSIntrdoub@ClrsVlLsObjcClrtClr ObjcRGBCRd doub@n@Grn doub@n@Bl doub@n@TypeenumClryUsrSLctnlongMdpnlong2ObjcClrtClr ObjcRGBCRd doub@f Grn doub@f Bl doub@f TypeenumClryUsrSLctnlongMdpnlong2ObjcClrtClr ObjcRGBCRd doub@k Grn doub@k Bl doub@k TypeenumClryUsrSLctnlongRMdpnlong2ObjcClrtClr ObjcRGBCRd doub@m`Grn doub@m`Bl doub@m`TypeenumClryUsrSLctnlongMdpnlong2TrnsVlLsObjcTrnSOpctUntF#Prc@YLctnlongMdpnlong2ObjcTrnSOpctUntF#Prc@YLctnlongMdpnlong2AnglUntF#Ang@VTypeenumGrdTLnr RvrsboolDthrboolAlgnboolScl UntF#Prc@YOfstObjcPnt HrznUntF#PrcVrtcUntF#PrcFrFXObjcFrFXenabboolStylenumFStlOutFPntTenumFrFlSClrMd enumBlnMNrmlOpctUntF#Prc@YSz UntF#Pxl?Clr ObjcRGBCRd doub@oGrn doub@oBl doub@oIrGlObjcIrGl enabboolMd enumBlnMSftLClr ObjcRGBCRd doub@m@Grn doub@b@ Bl doub@POpctUntF#Prc@RGlwTenumBETESfBLCkmtUntF#PxlblurUntF#Pxl@ShdNUntF#PrcNoseUntF#PrcAntAboolglwSenumIGSrSrcETrnSObjcShpCNm TEXTLinearCrv VlLsObjcCrPtHrzndoubVrtcdoubObjcCrPtHrzndoub@oVrtcdoub@oInprUntF#Prc@I8BIMlrFX8BIMcmnS8BIMdsdw3x8BIMmul ^8BIMisdw3x8BIMmul M8BIMoglw*8BIMmul .8BIMiglw+閏D,8BIMsLit閏D,8BIMbevlNx8BIMscrn8BIMmul 8BIMsofi"8BIMnorm8BIMluni Text8BIMlyid8BIMclbl8BIMinfx8BIMknko8BIMlspf8BIMlclr8BIMshmdH8BIMcust4metadata layerTimedoubAU8BIMPlLdplcL$b96baa43-6715-1179-bc96-d795218d25c4@@@E@@E@.@@.warp warpStyleenum warpStylewarpNone warpValuedoubwarpPerspectivedoubwarpPerspectiveOtherdoub warpRotateenumOrntHrznboundsObjcRctnTop UntF#PxlLeftUntF#PxlBtomUntF#PxlRghtUntF#PxluOrderlongvOrderlong8BIMSoLdsoLDnullIdntTEXT%b96baa43-6715-1179-bc96-d795218d25c4placedTEXT%b96b84f0-6715-1179-bc96-d795218d25c4PgNmlong totalPageslong frameStepObjcnull numeratorlong denominatorlongXdurationObjcnull numeratorlong denominatorlongX frameCountlongAnntlongTypelongTrnfVlLsdoub@doub@doub@Edoub@doub@Edoub@.doub@doub@.nonAffineTransformVlLsdoub@doub@doub@Edoub@doub@Edoub@.doub@doub@.warpObjcwarp warpStyleenum warpStylewarpNone warpValuedoubwarpPerspectivedoubwarpPerspectiveOtherdoub warpRotateenumOrntHrznboundsObjcRctnTop UntF#PxlLeftUntF#PxlBtomUntF#PxlRghtUntF#PxluOrderlongvOrderlongSz ObjcPnt Wdthdoub@0CHghtdoub@RsltUntF#Rsl@Rcomplong8BIMfxrp39008BIMnormbb( Color Fill 18BIMSoCopnullClr ObjcRGBCRd doub@[Grn doub@+Bl doub@Cl8BIMluni Color Fill 18BIMlnsrcont8BIMlyid8BIMclbl8BIMinfx8BIMknko8BIMlspf8BIMlclr8BIMshmdH8BIMcust4metadata layerTimedoubAs8BIMfxrpcX8BIMnorm400( Map + text8BIMluni Map + text8BIMlyid8BIMlsct8BIMpass8BIMlspf8BIMlclr8BIMshmdH8BIMcust4metadata layerTimedoubA8BIMfxrp2LXW/24/8BIMnorm0(8BIMluni </Layer group>8BIMlnsrlset8BIMlyidw8BIMlsct8BIMlspf8BIMlclr8BIMshmdH8BIMcust4metadata layerTimedoubA8BIMfxrp@W9 ȿc|@8BIMnorm0(8BIMluni </Layer group>8BIMlnsrlset8BIMlyidx8BIMlsct8BIMlspf8BIMlclr8BIMshmdH8BIMcust4metadata layerTimedoubA8BIMfxrp%]&ukf06w8BIMnorm<(Layer 28BIMluniLayer 28BIMlnsrlayr8BIMlyidy8BIMclbl8BIMinfx8BIMknko8BIMlspf8BIMlclr8BIMshmdH8BIMcust4metadata layerTimedoubA8BIMfxrp@@]@@c6׸OM0B8BIMnorm( ( Layer 3 copy8BIMluni Layer 3 copy8BIMlnsrlayr8BIMlyidz8BIMclbl8BIMinfx8BIMknko8BIMlspf8BIMlclr8BIMshmdH8BIMcust4metadata layerTimedoubA8BIMPlLdplcL$b96baa44-6715-1179-bc96-d795218d25c4@24 @2Ֆ@PE9v@.%w@PmO@F8V@5 vw@Gcwarp warpStyleenum warpStylewarpNone warpValuedoubwarpPerspectivedoubwarpPerspectiveOtherdoub warpRotateenumOrntHrznboundsObjcRctnTop UntF#PxlLeftUntF#PxlBtomUntF#Pxl@~RghtUntF#Pxl@uOrderlongvOrderlong8BIMSoLd8soLDnullIdntTEXT%b96baa44-6715-1179-bc96-d795218d25c4placedTEXT%b96b9278-6715-1179-bc96-d795218d25c4PgNmlong totalPageslong frameStepObjcnull numeratorlong denominatorlongXdurationObjcnull numeratorlong denominatorlongX frameCountlongAnntlongTypelongTrnfVlLsdoub@24 doub@2Ֆdoub@PE9vdoub@.%wdoub@PmOdoub@F8Vdoub@5 vwdoub@GcnonAffineTransformVlLsdoub@24 doub@2Ֆdoub@PE9vdoub@.%wdoub@PmOdoub@F8Vdoub@5 vwdoub@GcwarpObjcwarp warpStyleenum warpStylewarpNone warpValuedoubwarpPerspectivedoubwarpPerspectiveOtherdoub warpRotateenumOrntHrznboundsObjcRctnTop UntF#PxlLeftUntF#PxlBtomUntF#Pxl@~RghtUntF#Pxl@uOrderlongvOrderlongSz ObjcPnt Wdthdoub@Hghtdoub@~RsltUntF#Rsl@RfilterFXObjc filterFXStyleenabboolvalidAtPositionboolfilterMaskEnableboolfilterMaskLinkedboolfilterMaskExtendWithWhitebool filterFXListVlLsObjcfilterFXNm TEXTGaussian Blur... blendOptionsObjc blendOptionsOpctUntF#Prc@YMd enumBlnMNrmlenabbool hasoptionsboolFrgCObjcRGBCRd doub@oGrn doub@oBl doub@oBckCObjcRGBCRd doubGrn doubBl doubFltrObjcGaussian BlurGsnBRds UntF#Pxl@DfilterIDlongGsnBcomplong8BIMfxrp@24 @2Ֆ0B8BIMnorm( (Layer 3 copy 28BIMluni Layer 3 copy 28BIMlnsrlayr8BIMlyid{8BIMclbl8BIMinfx8BIMknko8BIMlspf8BIMlclr8BIMshmdH8BIMcust4metadata layerTimedoubA8BIMPlLdplcL$b96baa44-6715-1179-bc96-d795218d25c4@24 @2Ֆ@PE9v@.%w@PmO@F8V@5 vw@Gcwarp warpStyleenum warpStylewarpNone warpValuedoubwarpPerspectivedoubwarpPerspectiveOtherdoub warpRotateenumOrntHrznboundsObjcRctnTop UntF#PxlLeftUntF#PxlBtomUntF#Pxl@~RghtUntF#Pxl@uOrderlongvOrderlong8BIMSoLd8soLDnullIdntTEXT%b96baa44-6715-1179-bc96-d795218d25c4placedTEXT%b96b927d-6715-1179-bc96-d795218d25c4PgNmlong totalPageslong frameStepObjcnull numeratorlong denominatorlongXdurationObjcnull numeratorlong denominatorlongX frameCountlongAnntlongTypelongTrnfVlLsdoub@24 doub@2Ֆdoub@PE9vdoub@.%wdoub@PmOdoub@F8Vdoub@5 vwdoub@GcnonAffineTransformVlLsdoub@24 doub@2Ֆdoub@PE9vdoub@.%wdoub@PmOdoub@F8Vdoub@5 vwdoub@GcwarpObjcwarp warpStyleenum warpStylewarpNone warpValuedoubwarpPerspectivedoubwarpPerspectiveOtherdoub warpRotateenumOrntHrznboundsObjcRctnTop UntF#PxlLeftUntF#PxlBtomUntF#Pxl@~RghtUntF#Pxl@uOrderlongvOrderlongSz ObjcPnt Wdthdoub@Hghtdoub@~RsltUntF#Rsl@RfilterFXObjc filterFXStyleenabboolvalidAtPositionboolfilterMaskEnableboolfilterMaskLinkedboolfilterMaskExtendWithWhitebool filterFXListVlLsObjcfilterFXNm TEXTGaussian Blur... blendOptionsObjc blendOptionsOpctUntF#Prc@YMd enumBlnMNrmlenabbool hasoptionsboolFrgCObjcRGBCRd doub@oGrn doub@oBl doub@oBckCObjcRGBCRd doubGrn doubBl doubFltrObjcGaussian BlurGsnBRds UntF#Pxl@BfilterIDlongGsnBcomplong8BIMfxrp@24 @2Ֆ w8BIMnormL66(Group 28BIMluniGroup 28BIMlnsrlset8BIMlyid|8BIMlsct8BIMpass8BIMlspf8BIMlclr8BIMshmdH8BIMcust4metadata layerTimedoubA8BIMfxrp%]&ukf8BIMlyvrP.28BIMnormP67(Layer 38BIMluniLayer 38BIMlnsrlayr8BIMlyid8BIMclbl8BIMinfx8BIMknko8BIMlspf8BIMlclr8BIMshmdH8BIMcust4metadata layerTimedoubA8BIMfxrpJqB_? 002228BIMnorm( (Vector Smart Object8BIMlfx2lnullScl UntF#Prc@YmasterFXSwitchboolOrGlObjcOrGl enabboolMd enumBlnMNrmlClr ObjcRGBCRd doubGrn doubBl doubOpctUntF#Prc@*GlwTenumBETESfBLCkmtUntF#PxlblurUntF#Pxl@6NoseUntF#PrcShdNUntF#PrcAntAboolTrnSObjcShpCNm TEXTLinearCrv VlLsObjcCrPtHrzndoubVrtcdoubObjcCrPtHrzndoub@oVrtcdoub@oInprUntF#Prc@I8BIMlrFX8BIMcmnS8BIMdsdw3x8BIMmul Y8BIMisdw3x8BIMmul 8BIMoglw*8BIMnorm!8BIMiglw+8BIMscrnY8BIMbevlNx8BIMscrn8BIMmul 8BIMsofi"8BIMnorm8BIMluni,Vector Smart Object8BIMlyid}8BIMclbl8BIMinfx8BIMknko8BIMlspf8BIMlclr8BIMshmdH8BIMcust4metadata layerTimedoubA8BIMPlLdplcL$b96baa45-6715-1179-bc96-d795218d25c4@3_@G%RD@EŹ@1 qoB@H$@2~@8&pX F;@H^E93zwarp warpStyleenum warpStylewarpNone warpValuedoubwarpPerspectivedoubwarpPerspectiveOtherdoub warpRotateenumOrntHrznboundsObjcRctnTop UntF#PxlLeftUntF#PxlBtomUntF#PxlRghtUntF#PxluOrderlongvOrderlong8BIMSoLdsoLDnullIdntTEXT%b96baa45-6715-1179-bc96-d795218d25c4placedTEXT%b96baa41-6715-1179-bc96-d795218d25c4PgNmlong totalPageslong frameStepObjcnull numeratorlong denominatorlongXdurationObjcnull numeratorlong denominatorlongX frameCountlongAnntlongTypelongTrnfVlLsdoub@3_doub@G%RDdoub@EŹdoub@1 qoBdoub@H$doub@2~doub@8&pX F;doub@H^E93znonAffineTransformVlLsdoub@3_doub@G%RDdoub@EŹdoub@1 qoBdoub@H$doub@2~doub@8&pX F;doub@H^E93zwarpObjcwarp warpStyleenum warpStylewarpNone warpValuedoubwarpPerspectivedoubwarpPerspectiveOtherdoub warpRotateenumOrntHrznboundsObjcRctnTop UntF#PxlLeftUntF#PxlBtomUntF#PxlRghtUntF#PxluOrderlongvOrderlongSz ObjcPnt Wdthdoub@׏`Hghtdoub@q RsltUntF#Rsl@Rcomplong8BIMfxrp#^wyȔx#8BIMnorm(00(Pencil8BIMluniPencil8BIMlyid~8BIMlsct8BIMpass8BIMlspf8BIMlclr8BIMshmdH8BIMcust4metadata layerTimedoubA8BIMfxrp@W9 ȿc|@  !,-21&"#,<GIDAADEBPQJHFEF@<=<9623@DF;'.=>9;==9<;@>Z`RKFD9-0242/(%""#$#'*%! GWP"H ᗳO7: #;Rx` Xn;,A [:~'+  _Vsw."|JP-h fh YK#0F2" $3;>sҚv w2`w/yqM CbDA*Y$޹m $  5Th=$  F- %G"B- ְӕU-A1Y 7@ jGƣ(8 eZ 5v <Th R Fc(@q.*O }[ɪ7 aJ*+'kxA{w ?Z6';yUbyNfnh.$+7=:%'+n`٩ply!  t d~h(@16,$ 3,6~*Rj\a9O$;U<.|tO A1B hK$,.~cA89܈3Ҟϩ J BAx 71@DV k׎ zQ'v3:X07 72jh S }s N u&޳eGf m[RAGp Ntw Ct jY13"N70>hw3j <(b1#4(PTw"#z؁'  iuj2(az"3pP=HS"8Mu $g" B^Tă3'<cP FD 89tXbGO~y72fuL=O~ Q a;Enkr/^N -8 1_[#q }sFuu=n?M%LpEӮZ0Me\ iRzXW c(DJ`L._H< X$7\m_+T/i0`@0\8H YsE"< m  54"k~"1E %v\K= -岠Hc-;G&-@<ke|= >В H F  dd(v>b |4 9+@$ 3M hߘ "̟ *}]@o3&v[(j|W kzqZ=5!} Ar|I_NW+; Z L O U ] ">rOJ z3=.][#c{lHPf*NhojUR`T$,Q``#Mlp0gO!Z^n1<U1tm8 @~ʢ_ e un;=Xԕ^+NWp44_ҥ|gh~ B3 'c " ]'&QH xEAD`wae(eN _ șDPQڢ^he)d %tgˀ J2` Τ&yz(_.݂^в T4 A,Sɉ= Ljf"[ YZJvHBds<%G 8 9I"1f ::h85'࿌zu#f6QoϮ K=/RD d1 hI-D9ՈE{DP@!+G7V#eTl =j_i/~`o $ҥKx;F{P!Q:=Ɓ<;*Ij#<[p &P 1 <5G?F t][R dWZ+یa ;Ez<".M cb́Ӱ#p_3ad.7йDfj dS\. Gkw3Ag~?]>[jpm\İ^b@K? U> A0Fe #  $$*,(,,,&(*((($$  ! !')#$*+&    "E rP8*o,qzS $&3- ' Ek$*!?aYwyKJf  $$*,(,,,&(*((($$  ! !')#$)*&    OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOTOOOOOOOOOOOOOOOOOOOOOOOOOOODOOOOOOOOOOOOOOOOOOJ:OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO)OOOOO OOOOOOOOOOOOOMOOOOOO9 O NOOOOOOOO:OO?+NOOOOONOOOOOOOOOOOOOOOONOOOOOOD OO DOOOOOFOOHOOOOOODOOOOOOOOOOOOOOOOOOOODOOOO OOOOOLOOOODOOOO$7OOOOOOOI OOO! OOOOOOOOOOOOO2.OOO=HOOOOOO>OOOOOO'NOOOOOOOOOOOOLKO&O4EOOOOOOO  $$*,(,,,&()((($$  ! !')"$))&    %      ) 0["3Vٽ%WVPVZPYXP[VPZ]s©d0)T~NMs E571u@Q!$#"#$&'%" z܉O7u&:},# a}ma'/[N },IE.pG,oBB } (G uu ~x^).  ^ 32 *5w }چS3nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn''''''''''''''''''''''''''''''''''''''''''''''''ӀӀ        "    *& $69  %0CH  '4AIM )6DKHR  *7GNLIT'  *9GPPLKV1 .OYVRRPLSD 0BR\[WVTQORM"3BWa`[YWURQRT&8HYfea]\XWTRQZ&9L]ljea`^[XWUR`(';Oeqojgeb_]YXXUY+ +?Umywqmigfb``[QJE'/G[u~xuqlkjf`WJ=4,0Of}|xvtpf[L>3' 7Plyn_O@4(;YrydRC5(AdnZG7) GpĴs\K<' Q~ٹ}bN9'eڰiT;$ wҜ{Y<% SōeC  /qFJD"Z                   ,00133232444444433333344443334444444444441/,"  $'&%$#"!  )6COX_^]\[ZYXWUTQOLIFB>:5/+%  /EZoztng`YOH>4*"%@]zÿwlaUE:,",Lnº~rbQC4&,Ln¹~raPB3%,Ln}p`PB2&,Ln}p_OA1%,Ln|o^O@1$ ,Ln{n]O@0$ ,Lnǿzm\M>/" ,LnǾzm[L>/#,LnǾykYK<.",LnƾyjXK<.!,Ln#ƾxiXK<.!,Ln#ǾwhXJ;-!,Lm ƾvhWJ:,,Lm ŽvgVI:+,Lm ŽufUH9*,Lm żteUH9* ,Ln ĻtdTG7) ,Ln ĻtcSF6(  ,Lm ûtcRE6(  ,Lm ĺscTD4&  ,Lm ùscTE4&  ,Ln rbQD4' ,Lm ¹}p`PC3& ,Lm |o^OB3& ,Lm |n]O@1$ ,Lm }n]N?0# ,Lm |n^N?/# ,Lm {m\N>.! ,Lm zk[M?/  ,Lm ǾyjZL=/" ,Lm ǾwiZM<.! ,Lm ǾwjYL=- ,Lm ƽwiZK<- ,Lm ƽvhYK<.  ,Lmżxj[M;)  ,KmǾwgS>) *,LmĹv`I2  ,LmøubL5" ,KmȾzkXC/",LnƽugWE2 )FdŽxneYM@3&  3I>1# &Ifpn\5 "DUbS EefV|K 3h|iJsr/ VYBvT 8omEO}q> ^}V?j{U' 4xmCMxj; GtvR>`qU- !Th[=JmhG)9Z\B7ZtdB-# .HOB.:atdI5,%IR?%)DbogS>6!2) CTM@H\baSJOPGA6-'  1MP@>P_c[LMRKB=1-'  GVJ?JYbbQJQMD@7.,$:Z]HFT^cXLOND@;1-#  L`XKPYb\MMOE@>1-+3V`VNT\`QMOF?A8-,"EZ\SPSZXOQH?@>3-&%KXOGPQSVULA?=3-%4INADQOOUOB@?3' !?LE?NQNLIDA@4& .CI@GRKH=8A?0" 3?<6BGB;//6, #15(".3//.+  )83&# +)  '7, #    222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222 !!!!!!!!!!""#$%&&'(()**+,,..//01222222101222       %)-02 1.*&!   &.6=DKPT SOHB90'    *5ALV`ipv pg^RE:-$  (5CQ_lx xjZK;."  -, -ELRY`gnu| &/8AKT]gqz !-8COZfr~ -;HUcq~ +:IXgw'7HYj{+=Oat 2FZn  % : O d y  ' = T j " 9 Q i  * C \ u & @ Z t .Id %A^z &Ca~1Om&Ed#Cc'Ij4Vx&IlAe@e Ek*Qw;c*R{Gp@j>i  A l !!H!u!!!"'"U"""# #8#f###$$M$|$$% %8%h%%%&'&W&&&''I'z''( (?(q(())8)k))**5*h**++6+i++,,9,n,,- -A-v--..L.../$/Z///050l0011J1112*2c223 3F3334+4e4455M555676r667$7`7788P8899B999:6:t::;-;k;;<' >`>>?!?a??@#@d@@A)AjAAB0BrBBC:C}CDDGDDEEUEEF"FgFFG5G{GHHKHHIIcIIJ7J}JK KSKKL*LrLMMJMMN%NnNOOIOOP'PqPQQPQQR1R|RSS_SSTBTTU(UuUVV\VVWDWWX/X}XYYiYZZVZZ[E[[\5\\]']x]^^l^__a_``W``aOaabIbbcCccd@dde=eef=ffg=ggh?hhiCiijHjjkOkklWlmm`mnnknooxop+ppq:qqrKrss]sttptu(uuv>vvwVwxxnxy*yyzFz{{c{|!||}A}~~b~#G k͂0WGrׇ;iΉ3dʋ0cʍ1fΏ6n֑?zM _ɖ4 uL$h՛BdҞ@iءG&vVǥ8nRĩ7u\ЭD-u`ֲK³8%yhYѹJº;.! zpg_XQKFAǿ=ȼ:ɹ8ʷ6˶5̵5͵6ζ7ϸ9к<Ѿ?DINU\dlvۀ܊ݖޢ)߯6DScs 2F[p(@Xr4Pm8Ww)Km8BIMB8BIM!UAdobe PhotoshopAdobe Photoshop CS68BIM".MM*bj(1r2i ' 'Adobe Photoshop CS6 (Windows)2012:09:17 19:04:02&(.HH8BIMLmoptdTargetSettingsClrTObjc ColorTableClrsVlLsisExactboolMttCObjc NativeQuadBl longGrn longRd longTrnsbool autoReduceboolcolorTableControlObjcColorTableControl lockedColorsVlLs shiftEntriesVlLsditherAlgorithmenumDitherAlgorithmDfsn ditherPercentlongd fileFormatenum FileFormatPNG8 interlacedbool noMatteColorbool numColorslongreductionAlgorithmenumReductionAlgorithmSelerolloverMasterPalettebooltransparencyDitherAlgorithmenumDitherAlgorithmNonetransparencyDitherAmountlongdwebShiftPercentlong zonedDitherObjc ZonedInfo channelIDlong emphasizeTextboolemphasizeVectorsboolfloorlongzonedHistogramWeightObjc ZonedInfo channelIDlong emphasizeTextboolemphasizeVectorsboolfloorlong8BIM-msetnullVersionlong8BIMms4w8BIMmaniIRFR8BIMAnDsnullAFStlongFrInVlLsObjcnullFrIDlongL6FStsVlLsObjcnullFsIDlongAFrmlongFsFrVlLslongL6LCntlong8BIMRoll8BIMmfrip˸???8BIMnormL( Layer 3 copy8BIMluni Layer 3 copy8BIMlnsrlayr8BIMlyidB8BIMclbl8BIMinfx8BIMknko8BIMlspf8BIMlclr8BIMshmdH8BIMcust4metadata layerTimedoubAs YS8BIMfxrpzz$+379;@CEHJLNQSUVXY[[^]a_a_ca`_^^]\\[ZZYWWXWVUVUVUVUVUVTVUWY[\^_abdegijlmoprsvvxxzxyxyxxyxzxzzzzzzzzzzzz{z{z{z|z|z|z|z|z{z{z{zzzzxzyyyyyyywzwyxyxyxyxyxyxwxwxwxwxvxwyxyxyxxwywzyzyzyzyzyzyyyyyyyyyyxyxyxyxyxyxyxywwwwwuwuvvvvvvvtttsuttttttstssssssssssrtqtrtrtrtstsrrrrqrqrqrqrqqqqqpqpqoqoqooonpopoonononolmllmmkmkmklklkkkkkjkjkjjjijhjhkikghgfhfhfhfefeeeddeddcccbbbaa_b_cacaa`babbab`a``````_`_`^^^^]\\ZZYYWWVUUUTSSRSRTSSRSRSSRPQPQRQPQOPPOOOOPPOOOPQSSVUZ[^^`bceghhiiffca__[XTPNJGD?=953/)"           !#%&( &%#!  !#&*,013 10,*&#  !%*.158<>?A ?>:850,(#  !&*058>AFJKMO MKHEA<71,&!   %*05:?FKPTY\]_ ]ZWTOHC<70*#   #(.5:AHMTZ_dhknp mkgb]WPJA:3, % ( !&,38?FOU]djpty}|wsme]UME<5 ,&  %*07>EMU]emsz |tmdZPH> 5.& + #(.3:CJR\dkt}|qh]TJ ?5.%  %*18?HPYajs| vk_TJ?5,% . #(.53*# . %*18?HPZdmvľyk_TF<1(! 1 #(.5EMW_js} ƺsdUH<1( * !%,18AJR\eny }m_PE8.% + #(05>EMWajs} whZM?5*# ) !&,18AJR\eny ǼsdUH<1( , #(05>EMWajtĸ}m_PE8.%+ !&,3:AJR\enywhZM?5,#, #(05>EMWajtȼsdUH<1(!* !&,3:AJR\enyŸ}n_RE:0&, #(05>EMWajtwhZMA7.%* !&,3:AJR\enyȼsdWJ>5,%, #(07>FOWajtĸ}naTF<3,* !&,3:AJR\epyyj]PE<3, #(07>FOWajtǼtgZOE<, !&,3:AJT\epzĸqdYME, #*07>FOWakt|nbWM, !&,3:CJT]gpzǼymbY. %*07>FOYbkvŸwmb, !&,3:CKT]gpzwm. %*07?FOYbkvȽy, !&.3:CKT]gqzź+ %*07?FPYbkv¸, !&.3EMW_hs}ǿ* !%,18AJRZenyǿ ) #(05>EMWajs}ǿ '!&,18AJR\enyǾ &#(05>EMWajt}ż $!&,3:AJR\enyúz ##(05>EMWajt}tn !&!,3:AJR\enyǾypha  #(0 7>EOWajt ûtkbZT  !&,3:AJR\epy zpg]UOH #(07>FOWajt ļvkbYPHA< !&,3:AJT\epz |qg]TKE>71 #(07>FOWaktwmbYPH?81.( !&,3:AJT\epz|qh]UKC<5.(%! #*07>FOYaktļwmdZPH?81*%! !&,3:CJT]gpz}sh_UKC<5.(# %*07>FOYbkvĻwmdZPH?81*% !&.3:CKT]gpzǿ|qh_UKC<5.(#  %*07?FOYbkvûwmdZPH?81*%  !&.370*% . #(.570*# , #(.570(# , #(05>EMW_js}.ƽzpe\TJA:3,&! * !&,18AJR\eny,tjaWOF>70(# , #(05>EMWajs}.Žype\RJA:3,&! * !&,1:AJR\eny,tjaWOE>70(# , #(05>EMWajt.Žyne\RJA:3,&! * !&,3:AJR\eny,tjaWME>50(# , #(05>EMWajt.Žyne\RJA:3,&! * !&,3:AJR\eny,}tjaWME>50(# , #(07>EOWajt.żyne\RJA81,&! * !&,3:AJR\epy,}sjaWME>50(# , #(07>FOWajt.ļyndZRJA81,%! , !&,3:AJT\epz,}sh_UME>5.(# , #(07>FOWakt-ļwndZRHA81,%! , !&,3:CJT\gpz,}sh_UME<5.(# . #*07>FOYbkv-ļwmdZPH?81,% , !&,3:CKT]gpz,}sh_UME<5.(# . %*07?FOYbkv-ĻwmdZPH?81*% , !&.3:CKT]gpz,ǿ|sh_UME<5.(# + %*07?FOYbkv-ûwmdZPH?81*% , !&.370*# , #(.570(# , #(.570(# , #(.570(# , #(.550(# , #(05>EMWajs}/Žyne\RJA:3,&! * !&,18AJR\eny-tjaWME>50(# , #(05>EMWajt,Žyne\RJA:1,&! * !&,3:AJR\eny*}sjaWME>50(# , #(07>EMWajt)żyne\RJA81,&! * !&,3:AJR\eny'}sj_WME>50(# , #(07>FOWajt&ļyndZRJA81,%! * !&,3:AJR\epz$}sh_UME<5.(# , #(07>FOWajt #ļwndZRHA81,%! * !&,3:AJT\epz !}sh_UME<5.(# - #(07>FOWakt ļwmdZPH?81,% + !&,3:AJT\gpz }sh_UME<5.(# * #(07>FOWakvwmdZPH?81*%  (!&,3:AJT\gpzǿ|qh_UME<5.(#  '#(07>FOYbkvûwmdZPH?81*%  %!&,3:AJT]gpzǿ|qh_UKC<5.(#  $#(07>FOYbkvûwmbZPH?81*%  !"&,3:CJT]gqzǿ|qh]UKC<5.&!   #(!07>FOYbkvûvmbYPH?81*%   !&,3:CKT]gq|ǿ|qg]TKC<3.&!   #(07>FOYbkvºvkbYPH?71*%   !&,3:CKT]gq|ƾzqg]TKC:3.&!  #(07>FOYbkvºvkbYOF?7 0*%  !&,3:CKT]gq|ƾzpg]TKC:3. &!  #(07>FOYbmv!ºvkbYOF>70*%   !&,3:CKT]gq|$ƾzpg]TJC:3,&!   #*07>FPYbmw$ºtkaWOF>70*#  !&,3:CKT]gq|'ƾzpe\TJA:3,&!  #*07?FPYbmw'¹tkaWOF>70(#   !&,3:CKT]hq|*ƽzpe\RJA:3,&!  %*07?HPYdmw*tjaWOF>70(#  !&.370(# ! !&.350(# $ !&.550(# ' !&.550(# ( !&.55.(# + !&.570*# + %*18?HR\epz,ƾzpg\TJC:3,&! ) !&.570(# + %*18?HR\epz,ƾzpe\TJA:3,&! ) !&.570(# + %*18?HR\epz,ƽype\RJA:3,&! ) !&.570(# * %*18?HR\epz,ƽyne\RJA:3,&! ) !&.550(# * %*18?JR\epz,Žyne\RJA:1,&! ) !&.550(# * %*18?JR\epz,ŽyndZRHA81,%! ) !&.570*# + !&.570(# ) !&.570(# ) !&.570(# ) !&.550(# ) !&,370*#   #(07?JR]gs}|qg]TKC:3,&!   &,3<EMWbmwżvkbYOF>70(#   #(07?HR]gs}|qg]TJA:3,&!  &,370 (#  #(07?HR]gs}!zpg\TJA:3,& !  &,350(#   #(07?HR]gs}$zpe\RJA81,%  %,370(# % %,3:EMWbmy+ú}sh]TKC:3,&! ( !(07?HR]hs*ǾwmbYOF>70(# + %,3:EMWbmy+º|qg]TJA:3,&! ' !(07?HR]hs*ǾwmbYOF>50(# ) %,3:CMWbmy+¹|qg]TJA81,% ' !(07?HR]hs*ƾvkbWOE>5.(# * %*3:CMWbmy*¹|pg\RJA81*% ( !(.7?HR]hs*ƽvkaWME<5.&! ' %*3:CMWbmy*¹zpe\RJA81*% % !(.7?HR]hs*ƽtjaWME<5.&!  #%*1:CMWbny*¹zpeZRH?81*%  "!&.7?HR]hs*ƽtj_UMC<3.&!   %*1:CMWbny*zneZPH?70*#  !&.7?HR]ht*ƽtj_UKC:3,&!   %*1:CMWbny*yndZPH?70(#   !&.7?HR]ht*żsh_UKC:3,&!   %*1:CMWbnz*yndZPF>70(#   !&.7?HR]ht*żsh]TKC:3,%  %*1:CMWbnz*ymdYOF>70(#  !&.7?HR]ht)ż}sh]TJA:1,%  %*1:CMWbnz*wmbYOF>5.(#  !&.7?HR]ht)ż}qh]TJA81*%  %*1:CMYbnz*wmbYOE>5.(!  !&.7?HR]ht)Ļ}qg]TJA81*%  %*1:CMYdnz*wkbWOE<5.&!  !&.7?HR]ht )Ļ|qg\RJA81*%  %*1:CMYdnz *ȿvkaWME<5.&!  !&.7?HR]ht )Ļ|pe\RH?80*#  #*1:CMYdnz*ǿvkaWMC<3,&!   !&.5?HR]ht)ĻzpeZPH?70(#   #*1:CMYdnz*ǿtj_UKC:3,&! # !&.5>HR]ht)úzpeZPH>70(# # #*1:CMWdnz*ǿtj_UKC:3,% & !&.5>FR\gs)úzndZPF>70(# & #*18AKWbmy*Ǿtj_UKA:1*% ' &.35.(! % #(08AKWbmz*Ǿsh]TJA81*% % %,35.&! % !(08AKWbmz*Ǿsh]TJA81*% % %,370(# $ #*35.(! $ #*1:EOZgs%tj_UKA:1*% % &.5?JTamw#ƼzndZOF>5.&! $ #*1:EOZeq"tj_TJA81*% % &,5?JU_kw żzndYOE<5.&! $ #*1:EOYeqth]TJA81*# # &,5?JT_ky żynbYOE<3,&! % #(1:EMYeq sh]TJ?80(# # %,5?HT_kyymbWME<3,% ' !(1:CMYeq}sg\RH?70(# # %,5>HT_jwŻwmbWMC:3,% ' !(1:CMYdp}}qg\RH?70(# # %,5>HT_jwĻwmaWMC:3,% ' !(18CMYdq}ȿ}qg\RH>7.(! # %,3>HR]jwĺwkaUKC:1*% $ !(08CMWdq}ȿ}qeZPF>5.&! # %,3>HR]jvĺvkaUKC:1*% $ !(.8AKWdp}ǿ|peZPF>5 .&! # %*3>FR]hvúvj_UKA81* # $ !(.8AKWbp}ǿ|peZOF<5.&!  # %*35.&! " &,7?JUbmz'ǽymaWKC:1*% ! !*1:EO\gt&¸qg\PF>5.&! # %,5?HUamz'ǽwkaUKA81*# ! !(18EOZgs&}qeZPF>5.&!   %,5?HTamy&ƼwkaUKA80(# ! !(1:COZes%}qeZOF<3,&   %,5>HT_ky#Ƽwk_TJA80(# # !(0:CMZeq"}peZOE<3,%   #,5>HT_kw Ǽvj_TJ?70(! # (08CMYdq|pdYOE<3,%   #,3>FR]kw ǻvj_TJ?70(!  (07AMYeq} zndYOE:3*% " #*3>FR]jw ƺsh\TH?7.(!  &07AKWdp} ymbWMC:1*% % #*15.&!  &07AKWdp}ymaUKA:1*# " #*15.&!    !(1:EOZgsƸznbYMC:1(#   #.5>JU_mz˾th\RH>5,%  !(1:EOZesŵznbUMC80(!  #,5>HTaky"˽tg\PH>5,%  !(18COYeq "ŵzmaUJA80(!  %,5>HT_kw %˽sgZPE<3,%  !&08CMYdq} %ŵwmaUJ?80(!  %*55.&  #*35.&  #*3:FP\ht%̺znbWKC80*# ! &.7?KWbnz#ısg\PF<3*% " !*1HT_kw$î}peYOC81*# ! !(0:COZeq}$͵wj_RH>7.%  %,5>HT_kw$í|pbYKC81(!  !(08CMYep}"ʹvh]RF>3,%   %,5>HR_jv$|nbWKC81(!  !(08CMWdnz"̳th\PF<3,%   %,3>HR]ht$zmbUK?7.(!  !(08AKWbnz"ͲsgZPE<3,#   #,3>FP\ht#ymaTJ?7.&  !&.7AKWbnz"ͱqeZOE:1*#   #*35,%  &.7?JUakw#̯|nbWMA81(#  #*3:EOZeq}#ӽvh\PF>3,%  %,7?JTajv#̭znbWJA8.&!  (08CMYep|#Իsg\PF<3,#  !*1JUbmy#պsgZOE:1*#  #,5AKYdq}"̩wk_TJ?5.&  #,7AMZgs#չqeYOC:1*#  !,7AO\hv"̨wj_TH>5,& !,5AO\jw!ն|pdWMC81(!  *5?M\jw"˨th]RF<3,%  (3>KZhw ֵznbWKA80&!  &1>JYhv"ʦth\PF<3,#  %0:HWet ִzmaUJA7.&!  #,:FTbq"ɤse\OE:1*# !,7CRan ײwmaTJ?5.&  (5AO_m|ɢ}qeYME:1*!  (1>MZjzװwj_TH>5,%  &05,%  #.8ERbp!ǞznbWKA80&! !*7CP]mخth]PF>3,%  (3?O\jy!ƝymaUKA7.&!  &1>KYhw٪sgZPE:3*#  %0:JWet!Ŝym_UJ?7.&  #.:FTbq٩}peZOE:1*!  #*7CPan!™vj_RH>5.% *5AO]k|٦|pdYMC80(!  (3>KZjwth]RH>3,%  &0KYgv߼|peYOE:1(!  &05,&  (3>KZgwӒymbYM A80(!  &1JYgv΋th]TJ?5.&   &0:HUdq䢃ymbWMC:1*#  %08FTbn̊|qg]RH>5.%  #,8EP_m!㝀vkaUKC80(!  !*5AO]j$DŽyndZOF>3,%  *3?MZg#ߔzpg]TJA80(!  &1>JWew$δyph_UME:1*#  &05.&!  %.:FRan)ڿ|vnjbZTME>70(!  #,8CP]k*ӵytnhb\UPJC<70(#  !*5AO\j*ʪ|vpkga\UOJE?830(#  *3?MYg,ysnhd_YTOJC?:51*&#  &1>JWev/յ|vpkea\WRMFC>:50,&#  &0:50.(%!  #,8CP]k2ֶwsmhd_YTOKE?<73.,&#  !*5AO\j2ͫvpkga\WRMHC>:71.(%#  (3?KYg4ĝzsnjd_ZUOJFA<830,&%!  &1:71.*&#  %.:FTan.ϫsnje_ZUPKFC>851,(%!  #,8CP]k2Şwqmhb]YTOJE?<730*&#  !*5AMYg.ٹtpjea\UPKFC>851,*%#  &1830,(%! C %.8EP]үqkgb]WTOJE?:730*&#  (3>JU'ȡwnjdaZWPKHC>:51.(&! > #,5AMܼqmhd]YTOJEA<830,(%! : %.8Cjүpkeb\WRMHC?:71.*&# 6 %.8Pƞvmhd_ZUPKFA>851,(%# 2 %.<Գkhea]YTOJFA<83.,&%! 1 %,qa_]\YUPMHC?:51.*&# . #Ħ}UPRTRROMJFA<851,(%#   ]W8:73.,&%! % !&,0378::88530,*&#   %&*,.,*&%!  ##%%##                     " " "  " """"""""""""""""""" """ " $ $ $"$""""""""" " " "" ""$"" " "" " " $ $ $ " " !! " "     " " " " " " "" """" $"$" #"$" #"$" #"$ !""!!$ " """  " """"             " " "  " """"""""""""""""""" """ " $ $ $"$""""""""" " " "" ""$"" " "" " " $ $ $ " " !! " "     " " " " " " "" """" $"$" #"$" #"$" #"$ !""!!$ " """  " """"             " " "  " """"""""""""""""""" """ " $ $ $"$""""""""" " " "" ""$"" " "" " " $ $ $ " " !! " "     " " " " " " "" """" $"$" #"$" #"$" #"$ !""!!$ " """  " """"  8BIMPatt8BIMAnno8B64FMsk 2 FTA耻Rl 8IMF6#,/ ,{IIQHHHLnrIIUIHHHHdgn,kII\=/JHHlHIUT&{cIIb2YHHUIII}>G[HHj$HHpHHHTrfUHHs*HHvOHHH|P 0QHH|;HH~rHHH\e1MHHݦPHHOHHPC OOO~OOOrOOOO`OOOOOSOOOOOOOOOOOOOObOOOOOOOOOOOOOOOOOOOO]OOOOOOOOOOOOqOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOPOOOOOOOOOOOOOOOOOQOOOOOOOOOOOOOOOOOOOֆPOOOOOOOOOOOOOOOOOOOOsOOOOOOOOOOOOOOOOOOOOO\_OOOOOOOOOOOOOOOOOOOOOO}ȃQOOOOOOOOOOOOOOOOOOOOOOW_OOOOOOOOOOOOOOOOOOOOOOOOoOOOOOOOOOOOOOOOOOOOOOOOOOaPOOOOOOOOOOOOOOOOOOOOOOOOOTˆUOOOOOOOOOOOOOOOOOOOOOOOOOOPˆUOOOOOOOOOOOOOOOOOOOOOOOOOOOOËVOOOOOOOOOOOOOOOOOOOOOOOOOOOOOUOOOOOOOOOOOOOOOOOOOOOOOOOOOOOQSOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOW}POOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOf}POOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOQ}POOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOcƆSOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOVӔYOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOPzcOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOsPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOmbOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOmROOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOmӀOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOrzOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOuxOOOOOOOOOOOOOOOOOOOOOOOOOOOOPxOOOOOOOOOOOOOOOOOOOOOOOOOOOPxROOOOOOOOOOOOOOOOOOOOOOOOOOuaOOOOOOOOOOOOOOOOOOOOOOOOOiOOOOOOOOOOOOOOOOOOOOOOOO][OOOOOOOOOOOOOOOOOOOOOOPOOOOOOOOOOOOOOOOOOOOOObmOOOOOOOOOOOOOOOOOOOOOyYOOOOOOOOOOOOOOOOOOORQOOOOOOOOOOOOOOOOOOUOOOOOOOOOOOOOOOOOOROOOOOOOOOOOOOOOOOPOOOOOOOOOOOOOOOOOyOOOOOOOOOOOOOOOO]OOOOOOOOOOOOOOOOOOOOOOOOOOOOWOOOOOOOOOOOOoOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOwOOOOWOOOOOOOlOOOOOkOOORO|OORoG !-#b ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! " ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! !                         !#%'()+,-./0123445556666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666  !#%&')*+,-../0112223333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333343333333333333333333333333333333333333333333333333333333333333333333333333333333 !"#%&'()*+,--..///0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000  !"#%&'(()**++,,,-----------------------------------------------------------------------------------------------------.-------------------.--------------------------------------------------------------------------------  !"#$%&&''(()))**********************************************************************************************************************************************************************************************************  ""##$%%%&&&''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''('''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''   !!""####$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$  !!!!!!!!!!!!!!"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!               [g"ysb *g8ܙ{un 7Yh}wp: Ue?'ysb 4t3ْ{un reʸRh}wp: [0ؤmC ysb  '~g"{un EyOh}wp: ^Hhyj!ysb }0{un l}wp: ^Yu!ys` Y %q{un 1|l|wp8 <^!xs`  r!{tn 7!MC(T{|vp8 !'aհxr` ags!P԰{tn  Qg֍|vp8 @IUְxr` ]3'm԰{tn %*2 g֐|vp8 *Y1ְxr_ y) 9gHҰ{tn \M$".666ACՐ|vp5 d"* խxr^ 4[0Ұ{tn =7h/vՐ|vp5 iivĿB }޼Jխxr^ ЊZV dyѝe-0Ұ{tn Ϧ@jMՐ|vp5 k/7լxr^ .~'Ѱ{tn *ÿŽNLJՑ|vp5 k9ȿǼž#+{N#cլxr^ Bǿ¿<ںtg^QPS^RNѰ{tn EǾ¿ſ¾`|!=Ց|vp2 kEƽJ8ժxr\ ?ƽþ#u@Ѱ{tn 9ÿ¾' gXՑ|vp2 o.ýLpjժxr\ #ûy?Ѱ{tn 4¹JՑ|vp2 qάGýž*&ժxr\ ϏXRe2=Ѱztn rl½¼78Փ|vp2 qOƾſý0gըxr\ ,ſĽ5"Ѱzsn ͽļcjՓ|vp0 p˒1@'դxrZ hUƿӸyskw$vPаzsn <zĽüT&>g?-Փ|vo0 t5zn"դxrZ ʏ¾ÿ6 lFаzsm ]Fż!n#NfՓ|vo0 w&nrF~~դxrZ ʝ%r1ϰzsm i = ||vo0 v2N`xrZ īvG3ϰzsm si)SMԖ|vo. v8A]z%ԟxrX ­uS+_кwJk ΰzsm jIr,QsԖ|vo. x17HPBZԟxrX £q?T8Dΰzsm ļe?666666666666666777777777777777777777777777777899:::::::::;;<=>=>>??@@ACCCCDDEFFHIIJJKLLOOPPPQQSTVVXXZZZ[]^```a¿~~~}y}vyztqpqlsnmkhnikbdgbc`]Y^ZZUY[XSUTQTRQPNMNLKLJJJJHGHGFGFFFDDCCCBBBBB@A@@???>????=======<<<<<<<<::::99::::::9:999998333333333333334444444444445555555555555555555677888888888999::::;<<==>@@@@AABBBDFFGGHHILLNOOOPQRSTUUXXYZZ[]]_}}~{{|w|q|qqopmkmlinhdde`jaa_\\\XZYVVRVWQRRMOQLMMJJKIIJGFEEEDDDCCCCB@AA@@???>>>><<<<;;<<;;:::9::9999998:8777776777777766665550000000000000011111111111122222222222222222223445555555556678888999::;==>>????@ACCEEFGGHHJKMNNNOPQSSTUUWXXYZ[þz|yuvvurvqpkmkghlhfebc_`^^[`XW][UWPQUORULMMIKHJHGFFEEDCDCBBA@A??@>>>=====<=;;;:9999888988777667666666644444444433344333332333--------------............./................./00111111111223444566677799:=====?@@@BBCEEFFHHJKKKMNOQRRRRUWWWYZ~~~{{{y|ursqkpjijkgggb`babd\][[VZTTUUQRMMLNKKKHFEFEECECCBAA@@@@====<=<<<;;;:997888777766555544444432223333211000111100000000/////.**************++++++++++++,,,,,,,,,,,,,,,,,,--../////////00111123334567789999:<>>>@@@ABBBCEGHHIJJMMOOPPRTUVWs¾~~z|}~|txrqtpliidjffddad[\\]\bZXTVTRRNOMMNIIJIGFHECEDBB@AA???>==<;;::::9998876655554444333322212221000////////...-----...---------,,,'''''''(''''''(((((((((((())))))))))))))))))**++,----....///////000123445777789;;;==>?AAAABCEFGIIJJMMOOOPQRT½|{wxq|wsuunpnileebde__a\ZY\UVV[TPWPOQOIKJIGGDEDBCAA@A??==<=<;;:88878887665554332222111110000///.///----,,,,+,,++++++****+************)$$$$$$$%$$$$$$%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&'''())))***++++++,----.//011445577888:;<=>>@@@@CCDFGGHJJLNNNNPQ~zx{s|tputkmmngcdkb`d]ZZ]X[TWQQRRQRMMJILFHIDFDDA?A@>>==;<;;999777655555433322200////./....----,,,,+,,*******)))((()((((((('''''''''&&&&&'!!!!!!!!!!!!!""""""""""""###################$%%&&'''''''(()((()+++++,-.//0011446667889;;==>>?@@BCEFGGGJJMMM\||ywvturumpllijfece_a`_]^YWWTSVUPQQLJILHHGFCDB@@>>><==:::9787756644232222110//....-,,,++,,++****)*)(((''''''''&&&%%%%&&%%%%%$$$$$$$$$######" !""##$$$$%%%&&('''())(())*+,-..//11444456798:;====>@AACEEEHHIKK~zzu{tospvmfkhgegbc^abZW]ZU^SUTPLPMMKHFICADBAA???==;;98877665533311100000/.---+,+++****)))))(''''&&&&%%$$###$$$####""""""""""""""!!!!! !!!!  !!!""""##$$$$%&&&&'''()*,---0012223457799;;<<==?@BCDDEFHIJÿ{}{ywvxwunrmrnhdcgbaa]YW]XXWWQPMNONHIMFGDFFD@A?>=<;;78876655424211111/...,-,,++******))((''''&%%$$$$$$###"""""""!!!!   """"#$$$$%%%&'('))),-./001123356789:;;<=?@BACCDFJ½}yu{rrrtplmggeig_]b^]]^TSXTRQOPMIIHHDIC?B@??@=;;:;77564533121000//---,++***)))('''''%&%%$$$$##"""!!!!   """"####$%&&'''*,,-..00111245788:::;=?@AABBC¿~}yzs{rvnnklifgge__^][\\\UWTRQNLPIJJEECA@F>=?<;;:9786744432210/.-/,,,+****)(((''''&&&%%$$##"""!!  !!!""#%%%())*++..///012455789::<=>@ABB¿~~w~xutoqnlmhgfcf`d_\X\XUUW[TMPKIMHKEADA?@C=><=;978743322100/---,,+*+*)()'&&&&&&%%$###""!!  !!###%''(((++,.../0122456899::=>ABŽ{}~vxrwsnqiikkifd__cb\^\V\XNSNUNIIIDJEGAB>A<:>79767665420/....-,,,++)(((''%%%$######"!!! !!!#%%&''))+,,-...1113468998:;=p¾zz}ywpqwnurkghd_c`\`Z^][WTSWNOFHJDCFFB@A<<==97968314142/0..--,**))('&&&$$$##""""! "$$%&&'())*+,--//0223678889;z~~szwtokuqfiidgad[[^YaSXSUPOJMNHDFHD>>A;;;<999564130///..,,++))(()&'%%%$#""""!   ##$%%&')))*+,,-./122557788<{}xyoptomlijegdaa`c\W^VQUSPOKIMEIFC@D=>;;:89885534300//,-,,+*))'''&%%###!!   !""#%''(((**,,,-/0124477Uÿ}|}vwwommnlqlcghi\aY[ZVXQPNQJINIEJCDB?<=<;867441311///---,,*)()('&&%$""""!!   !#$%%'(()++,,.//12356}zuwtqmlmoeijh_]bc[[UVSQVMLNHHDNCADA;=:;9:745331.0/.,,*+))'''&&####!!    !#$$&&()**+,,..02246¿~~{vzsnnkhgdjac_`]\[[ZUSRPKPLIJJDCA=;==996:45330100.-,,**((('&%$%##"!    !""$$&&))**+,-.013A¿~}}wqnpmiifkdgd__^X]ZTZVQNNMGFEM@CA>;:86974313/..--++,)((&&%%%###!    !!"#%%(()**+,.012~yxtwprmoghdbfac`]ZbSZVOORLIIGGDCC:=>998654411/0,+,+)))('&&%$$$#!!   !!!"$$&'((***..02ÿ~~|tyrrpnmmigb`\\^`TYRVRNQKIQGDAACA?:8976672111/0,-++)*''&&%%%#$!!!!    ""#%&&))*,,.2ÿ~xx}~tovroijbc`d]]XZVaRROMSJMFGJDD>?;9:8664432.2...-**))''&&%%$"#"!   !!"#%%'))*,.}~{xuvnrunsseece`_[^X[STOZLLOLJHHCADA@88664432/1-..-***)''&&&%$$!"!!   !"#$%'()++/|||u|vyuqkvmhkjhldbd^c_^_SWPQLNLQJJDD@G?==95469231../--**))(''&%%$#!!!!   !""$&')*,.Ŀ~}~x~vxvrpojrhljmgh`f_ca\\XTYNQPUHQPIB?B?=E=877442312.--+*)))'&&&&###""    !""$&')+-f}wvzxrttkohpelqgcacfbcd_]``ZPSQLKHNFJND?A>:<87657322//--,+*))'&&&%%#"#!!  !!#$')+/¿}}~y}yurssqmjrjfeibeia_eYab_X`W\SULQMLEHCDB>?;:8:9552400..,-+)))(&&%%$$"!    "$%'+-1ÿ|{wz~{twvqnjnkkidjgce``]_ZY^WY]ZTYPTOMILLHDF@A=::7777423/00-,+*+))&'&%%%$#"!    !#&&*.N¾}~}}t{qsupuunmmifdfgl`_a\eW^]U^SXWQUPOMOMKIOHD@D@?=7=5573322-.--++*))&&&&%%$#""    "#&)-/þwzusrskmnrhjjfdeccca^^YYWZV\]S\USRRRKHKLEIGJ@>?::=988474130.--,-+*(('&&&%%$#"!!!!   "$'-.>þ{}{xxvzyqkollkifhfjh]]_Y\_YXWXUTPSRUOSONHIGIIIHHECE=@;;:994564321/--,+++)((''%&&##"!!!!   #&(-1þwyvwwonoorkkfcedi^`_^[WXWWTQSPQOROSKOMJOOMFICCFBA@?@=;:97:5611200/-,,++*)))'&&&&$#"!!!!   #')-4yxtuumslnjili`bi_b[``V\XQQYTQTMYLLPKKMJFGEEKJGDAA;87=5555323013/-/,++)()''&&&%$#"""!   !$',2xxv{qinkgemab^d[VZXTTTWXRUPMONIIILIHGGGFFDB?@D>B><<@<<;;<;<757435432000/./,+**)()'''&%%%#"""!   "%)/3촼|}}{ulofshg^\_XaSXTTVPXSINNJMJIJJIGGGEDBDCABBE>??<<>9<9;:896876553544211./.--,,,)('''&&%%%$#"""!   !%)/4ܶo{wwjvfbdd`^`YRXROTMMPLMIRHGGFECACDBDCAAA??@@=@<=:<=<9=88776766663342331///--.,,+*(''&'&%%%##"""!    !$*0l̰}xuvt`hglZ^XQTUZMNMMSKKKGDKEFGCCEAA@??A@?>>><=>><:998987987876776634330010/./..--,,+*'('''%%%$##"""   "%).r}kwqdc^\Y^WZOOMMLGJKEGEIED@DCCAAB??=<>?=???<<<>;99:8977777665555433332110////...,,,,+))(&&'&%%$##"""   #%'.說wiiig`a_\TYZRMQLKGGEDFE>DC?A>>>>>>>@<<<<<;:;:<:9799987876764555333221111111////.-.,+,*+*))(''&%%%###"!!   !%&K؉{pdid_]`UUTR[QTPOILFEFABB??>?;@=<==::;:9;:99698979766666854553432212211111101000.-..-++****)))))&&%$####!!!   #&dxc]ZVVVXWYSSQTMKLJJJABA@>?;;:;::8::78886687778655655554344442223221111111111100//.--,,+++***)))(''($$$$##"!!!!   "$tKDbDAC<@>:8694654565866544435553554553333222232222210100000/....--.-,+*+*))))))'''(%%$####!!!!    #&(>H><9A@DDGFFDA@==A;6;6:886634505200222212232201112222111112111101100////.../,--,-,+,+**)))))(''''%%%$$##""!!   #$&-3.65<<<=<=<<9:86773132300000/00//0/./.00/0//000/0////0//01///////-..----,,,,+,++++))))(((''''&%%%%$$#"!!   "%''*/0444665444431111-//-.-+...-*,.--...,-,...../.-../....---------------++++******(((('''&&&&&&%$$#"!!!    "$&'')-.0-0...-,+,,*+**)*)+)**+****+,+,-+++++,,,--------,,,,,,,,,,,,,,,,******)))((('''''&&&&$$$$#!!!   ##$$')')((()'''(('''()''))('())'))))****++++*****++++*+++,,,,,+++++++*))))))))((''''&&&%%$$$$#""!   """$$%$##%%#%%$$$%&%'&'%&&&&'&''''')())())))*****))))))))*********))))((((((''&&%%%$##$$""""   !""!!!!!"!!"""""#"##%%%%%%&&'&&&&&&&''''(()))((((((()))))(((((('''''''&&&%%%$#####"""""!!    !"""!"###$$$$%%&&%%%%%%%&&'''&&&&(((((((''''''''&&&&&&%%%%$$#######""""!!!!     !!"!"""#$####$%%%%%%%%%$$$%&&&&&''''''''''''&&&&&%%%%%$$$######!!!!!!!     !!!"######%%%$###$$$$$$$$$%%%%%%%%%%$$$$$%$%%%$###"""!!!!!!    !!!""###################$$$$$$$$##$#########""!!!!!!!     !!""""#####################""""""""""""!!!!!!    !!""""""#""#######""##"""""""""""""!    "!!!!!!!!""""""!!!""!!!!!!!!!     !!!!!!!                                                              PMIFB@=9740-,)''#" PNJEB@<9731-,)'&#" OMJEB?<8731-,*'&"" OMJEB@<8720-+*&&"!  PLIEB@<9620-+)&&"! PLIFC?<8621-,)'%"! OLHEB?;7630,,)&$"!  NKHDB><7620,,)%$"  NKHEB>;7520,,'&%!   OKHEB><7520,+(&$!  OKHDA><751/++'&$!  NLIDA>;851/++'%$"   MLGDA=;741/,*'$#"  NKGCA>:641.+*'$#! NJHC@>:641.++'%$!   MKHC?=:740.,)'%$!  MKGC@=:640/,*'$$   MKGCA<:541/*)&##    MKFC@=:541/+)'$" MJGB@<9540.+)&%# MIFB?<9530-+)'$"  LJGB><9630-*)&#"  LIGA?;:62/-))%#" LHEA@<953/.)(%$" KHEB?;844/-*(&#"  KHEB?;843.,))%#! KHFA>;843/-('$#" KIF@=;843/,)'%#! KHD@><832/,)'&"!  KGD@?<832/,)&$"   JGD@=;842.,(&$!  JHE@=9742.-''$"   JGE@<:741.+'($"  JHD@=;630-*''#! JGD?>:621.*)'#  KGC?<:721.*''"!  KHC@<9630-*(&#!  JGC@<:630,+(%"   IEC>=:611,+'&#  HFB>=9610-*&%#  HFB?<:61/,)&%!  HFC?;952/+)&$   HFC?:742/+(&#   GEB?:7510+)%#"  GFA>:840.*)%$"    FDA=:630/)(%$!  FCB>;53/.*($#  GC@=;54/-+)$" GD@;9730,)(%"! FC?;9730,)'$" EC?;8520,*&$"   FA?;842/+)(#!  DB=;752-+((""  DC>:731-,)%"   DA?9941-+($"  CA>9851-*'%$  CB?8730-+'$"   F@?;63/++'$   E?<953/+*'$!  C@<863/,)&%  B@=852/*)&%  A?>751.*+%%   @>=9510+)$#  @>;761.+)%$  @>;751,,($#  A>;630,*'$#  A><640-)(#"  A<:630-*)$" A<:520,*&%!  >=;520,*'#! ><:520,)&$! >;942.,)$"   >;842.,($#  >;741/+'&#  >;750-*(%"  >;85/,*&$# >:640.*%$$ =:721-*&$" <9620-)%$" <9620-*%$! ;:63/,*&$   ;861.,)&$   :950.+(%#  <951/+(%" :851-+($#! <850.*'#"  <74/-+'#! 9741,*&$" <850-)&#! :73/-)(#  863/+)&#! :52/,)&"! ;52/+(&"! 862/,(%!  852.-(%! 732-,'%" 841-,'%! 742.*'$! 632.*&$! 631-*'$! 731-*&$  62/,*&$ 62/,*'$ 62/+*(#  520-)'#  520+)&#  62.+)&"  52.,(%# 520+(%# 51/+(%" 41.+(%" 41.*(%" 41.*(%" 41.*(%" 41.*(%" 41.*(%" 41.*'%" 41.*'%" 41.*'%" 41.*'%" 41.*'%" 41.*'$" 41.*'$" 41.*'$" 41.*'$" 31.*'$" 31.*'$" 31.*'$" 30.*'$" 30.*'$" 30.*'$" 30.*'$" 30.*'$" 30.*'$" 30.*'$" 30.*'$" 30.*'$" 30.*'$" 30.*'$" 30.*'$" 30.*'$" 30.*'$" 30.*'$" 30.*'$! 30.*'$! 30-*'$! 30-*'$! 30-*'$! 30-*'$! 30-*'$! 30-*'$! 30-*'$! 30-*'$! 30-*'$! 30-*'$! 30-*'$! 30-*'$! 30-*'$! 30-*'$! 30-*'$! 30-*'$! 30-*'$! 30-*'#! 2/,)&#  2/,)&#  2/,)&#  1.+(%"  1.+(%" 0-*'%! /-*'$! .,)&#  .+(&#  -*(%" ,)'$! +(&#  *'%" )&#! '%"  &#! %"  #! ! 9998887888888878888887888777777776776666777666676666776666676666666666666666666666666666666666555443210/.-,+)('%#!  5444555444455554444555444454444334333334333334433344443333333333333333333333333333333333333333222110/..-,+*)'&%#!  2222112222211112222112211111111110111110001110001111000000000000000000000000000000000000000000///..--,+*)('&%#"! //////...//////./////.......-----.------...----.------.---------------------------------------,,,++**)(('&%#"!  ,,,,,,,,,+++++,,++++++++++**+++++***+++++****++*****++****************************************)))((''&&%$#"!  ))))(())))))))()))((((((((((''''''((((('''((('''''((''''''''''''''''''''''''''''''''''''''''''&&&%%%$##"!  &&&&&&%%%&&&&&&%%%%%%%%%%$$$$$%%%%$$$$$$%%%$$$$%%%%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$####""!!  """"#####"""""""""""""!!!"""""!!!!""""""!!!!!"""!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!                      N        "  %  (  )  )  )  )  )  )  )  )  )  )  )  )  )  )  )  *  *  * +   +   ,!  ,!  ,!  -"  .$  /%  1&1  1( ;k  3)"+  4+#k  6-% F*  7/'""i  90)$  ;2+S  <42  >6  @b  GH      J    &  P    %  O    $  O    "  L    "  K    #  N  "  +   V'  .%  :,#  e6+"  A5)   @3(  >1&  =0%  ;.#  9-"  8+   7*  6)  4(  3'  2%  1$  0#  /"  ." -" -! ,! ,   +   + + *  *  * * + *  +   +!  ,!  ,!  -"  .#  .#  /$  0%  0&   2'  3(  4)  5*   5*!  6,"  7-#  9.$  :.%  ;0&  <0'   =2(   >2)!  >3)"  @4*"  @5+#  A5+$  B6,%  B7-%  D8.&  D8.&  E9/&  E9/'   E9/'   F:0'   F:0'   F:0'   G:0'   F;0'   G;0'   G:0'   G:/'  F:/&  F9/&  F9.%  F8.%  E8-%  D7-$  C7,#  B6+#  B5+"  A5*!  @4)   ?3(  ?2'  >2'  >0%  <;:876543110////..------,,,,,,,,,,,,,,,,,*'$    !!!"""""#%&()*,036:=@DH\ÿ~ytmga]WRLID@<:7531/.,+*)('&%$$$###""""!!! !    "#%)-038;@½ztmga[WOJC@;841/,*('%$"   "%),/48þwqld]VQKC>962/,)&#"   #&+.;wlh`ZSLF>940-)&$!  !%*rÿytmd\UOHB:41-*&$!  ")ztohea\TLG>93/+(%"   !,þysmib_YWQOHC<82.+(%#   #bùwohc^YTPMIFE@<951.*(%#   $qg\VQKIECB?=;:7632/,*'%#!  "reYQKD><;99876543210.,*)&$"   *399621/..///////.-,+)('%#!   "##$$&&''())))*))('&$#"    ""$$$$%%%$$#"!!    !!"!!!!                           !#%'()+,-./0123445556666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666  !#%&')*+,-../0112223333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333343333333333333333333333333333333333333333333333333333333333333333333333333333333 !"#%&'()*+,--..///0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000  !"#%&'(()**++,,,-----------------------------------------------------------------------------------------------------.-------------------.--------------------------------------------------------------------------------  !"#$%&&''(()))**********************************************************************************************************************************************************************************************************  ""##$%%%&&&''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''('''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''   !!""####$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$  !!!!!!!!!!!!!!"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!                )LPRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQN R Z_ a a a a a a a a b b cc e  g#P( i&4g k* ( m.3 oNX s 7 ~  =   < ~  <  $ L  0 q- o* m' k$i"hf e d c c b b b b cde f! h# i% j& l) n* p, q. r0 s1! u3" v4# v4$ w5$ w5$ w6$ x5# w4# w3! v2! t1 t0 r. q- p* n( l% j#h!fe c b a a a a a a ` ] mI!%&&&&&&&&&&&&&&&&&&&&&&&&&&''(()+.17=DKƽxnbXPG@;741/-+*))(''&&&&&&&&&#  $+2:º}pbVJ?70*&#  &Ażzi[N@5-'!  }~rg]UH<1*$  Ѱzh[RLE@<6/)$   dRE<654320-*'#   !$%&'''%#!          s!"4Mp# }_$"v%#p&^%m'&Qt(()**D,R,.U),L'&+E&)>>$>(="&= %=P/$="@B!Cn FjBK!Q W\kdNkaJr]hybp 3 u / o n  7g 4.3L;D]LW d^ 9sJ Q' o   -(Vn:WtH F Ty  Z  5!W T_# t% & (K * -! + 1}*m |/) ' !&s { n%" 8$7 # !7 f  h\I YQCUcfd-!TZ](AZnBAV/f( ( C:c? 'nQ}Ho@[MI< 9 *b  G  7  + { k .(   O       7     o  Z E 1    /  Z   H   L w m cd \ U M LG C ? ; 8 s Bk4x5 # c (  2tM G ]7x s  ueR Z[(.Gl fNYCD[jxpG 6# NGJlGgCoaD:fEsEGJMPUMZ_dJ$k |q w$ Q}s  -%  X E*  IQ  g 5   " x  S  `  *, ^ ?9  TE mT c G q      / }V {  ! 7 P TBf k}    7 !  R    a 'B+f~JHw%e\ao8p2# yKTe2 d A k   ~b. f3 f3 G _ { J    gs .e RW YvI 9 + q  FL z "  %  Z 1   P  e  6  C  Y ?e ,b_&\ZYWUS>QPO6NvMhMF\MPMHN~@O@7P.Q(S!UUVXZ ]_Qa*dgik09[8  b -* x5 B N0 w\j j ;z / r o  R L = @H  .f DT Z u/[  }uJx 2nOpbF4$Uq},r G  %k  6  "  P + ~ =  ]_   ,_  v!| :%+=05b <&2@xM]im{ez  4  { ( .) H8 cG|}VDev4U%?J'u( ~^v4? v#llDH<$1 o2 : [  p VY <% "S   `8 2;o]od]od~~^XPI/A_7, !$<> wXu> p5[(H 8'o ~ ~ M} })   f    M     O    qw ?q k d ] X mT EO J F C ? = q< Y: B9 .8  gXrgXrwv\ gXrgXrgXrgXr0Jf!W><|fBD"+so7F!U }dU s L  # a  @ z  / -  A ^( 3 _ ` P a c x e|  g iO l n q9@ twozN}bC6Th(yVD+6h7%w*H#1>P`q      )4 2 ;Y F O ,]  z < A k Ps 4S  E  | K* ~r-aNx;QY(, PcE^'% uhuhd`\XSLCDd-=<6M/^'r*U R &A ^L{#m%Ogvi4xL"wpj_d<^YUPLGDsAY=?:%75321100 %D0Le(:M`u%Gn.<Ije @x/R1un<)$:N<p=)?AtC_BE GIKoM8^O5Q SVtXHZ#[d]E_(a dgfDh$ikmro\qIs}5uf"vRxTVZ]`eiHnpsx }"$(&W(*-0*3e69=,@pDGLPOSW9\a.gu0 ]${Z9zC xj]rQBDl9S/?%)uO+bC(  1EYo!?b~)y:tOo=akeugc^Z&,! lWB. (Yp_{PpBf6[*Nfx  TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR !$'*-/2579;=?BCEFHIJKLMNOOPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ%OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO-xNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN "%(*-/2468:<>?ACDEFGHIJKKKLLLLLLLLLLLLLLLLLLLLLLLLLLLLL7xJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJBtIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII "%'*,.02468:;=>?@BCCDEEFFGGGGGGGGGLi89;<=>?@BBCCDDEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEViCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC !#&(*,./13467]i  "%')*,./12456789:;<<===>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>ii;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ii999  !#%'()+,-./01234455566666666666666666666666666666666666666666666666666666666666666666666666666666666666666ni3333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333xv00000000000000000000000  !"#%&'(()**++,,,------------------------------------------------------------------xx*******************************************************************************************************************************************'''''''''''''''''''''''''''''''''''''''''''  !!""####$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!     x" p. iB _ZRwF; 0$4V !!!"!!!!  pJT!!  9 3  l g8?|    o=%  IG :q                      OOOOOR4TBJ4DI2|X,0i+Nf'r-'-'{--/.e/<5Fb=I^ESQS.+aSurPKIODkEq9)eأrCj%H6/O s\+@A+az%X8=S)x+f>L[:~, #-C\n{_P&B48C1U.m+%"!"%"."8"?%E*K-Q2\8\?\H\H\HTEM?E9=ѥyT4)Dc77i011Z-+z*++]%+J!+=1=3C7S?lE&H=PkY+[Ye'kdr2|D()>/OCGs[5 0 R]>>/Pages 3 0 R/Type/Catalog>> endobj 2 0 obj <>stream proof:pdf uuid:edd6ddd4-6cef-9c4c-ae00-326be6ce1b5e uuid:4f484ae1-9794-6c4d-bb19-3c0236c19106 2016-05-30T18:48:19+02:00 Adobe Illustrator CC 2014 (Macintosh) 2016-05-30T18:48:19+02:00 2016-05-30T18:48:19+02:00 Adobe PDF library 11.00 application/pdf endstream endobj 3 0 obj <> endobj 7 0 obj <>/Resources<>/ExtGState<>/Properties<>>>/Type/Page>> endobj 8 0 obj <>stream HVˎ! Wۋ1`|$ir8$6R =<QV?~ǧKN_R%I߻0џۗ{{|rKu{IF${i>eB]cQ2 ak% can B0 rAz=Z{L%<@YMly} W9 r]eVaȧrJRg\'R 4>07b`0uF<p{ u0ȡp#܃( 5#~xf%bSn z8R<ޠ4 9?G#)筂ȗl̺lYroY>X)sb2tt?R*D-}`n̽F[p8 * TeZjMpN/'|cpF^jIU{mA+ Pu YN:10eNOM ƖK2&,sV-ֲ-k<9Ҽ @S<#3T cJr,e O!B;RN!ۡ0Diީm{~B[轅[轅[P[+~Pkfrjpfo`? RxM>!@TI~D `#_* endstream endobj 5 0 obj <> endobj 12 0 obj [/View/Design] endobj 13 0 obj <>>> endobj 11 0 obj <> endobj 10 0 obj [/ICCBased 14 0 R] endobj 14 0 obj <>stream HyTSwoɞc [5laQIBHADED2mtFOE.c}08׎8GNg9w߽'0 ֠Jb  2y.-;!KZ ^i"L0- @8(r;q7Ly&Qq4j|9 V)gB0iW8#8wթ8_٥ʨQQj@&A)/g>'Kt;\ ӥ$պFZUn(4T%)뫔0C&Zi8bxEB;Pӓ̹A om?W= x-[0}y)7ta>jT7@tܛ`q2ʀ&6ZLĄ?_yxg)˔zçLU*uSkSeO4?׸c. R ߁-25 S>ӣVd`rn~Y&+`;A4 A9=-tl`;~p Gp| [`L`< "A YA+Cb(R,*T2B- ꇆnQt}MA0alSx k&^>0|>_',G!"F$H:R!zFQd?r 9\A&G rQ hE]a4zBgE#H *B=0HIpp0MxJ$D1D, VĭKĻYdE"EI2EBGt4MzNr!YK ?%_&#(0J:EAiQ(()ӔWT6U@P+!~mD eԴ!hӦh/']B/ҏӿ?a0nhF!X8܌kc&5S6lIa2cKMA!E#ƒdV(kel }}Cq9 N')].uJr  wG xR^[oƜchg`>b$*~ :Eb~,m,-ݖ,Y¬*6X[ݱF=3뭷Y~dó ti zf6~`{v.Ng#{}}jc1X6fm;'_9 r:8q:˜O:ϸ8uJqnv=MmR 4 n3ܣkGݯz=[==<=GTB(/S,]6*-W:#7*e^YDY}UjAyT`#D="b{ų+ʯ:!kJ4Gmt}uC%K7YVfFY .=b?SƕƩȺy چ k5%4m7lqlioZlG+Zz͹mzy]?uuw|"űNwW&e֥ﺱ*|j5kyݭǯg^ykEklD_p߶7Dmo꿻1ml{Mś nLl<9O[$h՛BdҞ@iءG&vVǥ8nRĩ7u\ЭD-u`ֲK³8%yhYѹJº;.! zpg_XQKFAǿ=ȼ:ɹ8ʷ6˶5̵5͵6ζ7ϸ9к<Ѿ?DINU\dlvۀ܊ݖޢ)߯6DScs 2F[p(@Xr4Pm8Ww)Km endstream endobj 9 0 obj <> endobj 15 0 obj <> endobj 16 0 obj <>stream %!PS-Adobe-3.0 %%Creator: Adobe Illustrator(R) 17.0 %%AI8_CreatorVersion: 18.0.0 %%For: (Anders Gressli) () %%Title: () %%CreationDate: 30/05/2016 18:48 %%Canvassize: 16383 %%BoundingBox: 531 565 548 571 %%HiResBoundingBox: 531.12109375 565.224609375 547.7353515625 570.4658203125 %%DocumentProcessColors: Cyan Magenta Yellow Black %AI5_FileFormat 13.0 %AI12_BuildNumber: 18 %AI3_ColorUsage: Color %AI7_ImageSettings: 0 %AI3_Cropmarks: 0 0 1000 1000 %AI3_TemplateBox: 499.5 500.5 499.5 500.5 %AI3_TileBox: 220.5 120 779.5 903 %AI3_DocumentPreview: None %AI5_ArtSize: 14400 14400 %AI5_RulerUnits: 6 %AI9_ColorModel: 1 %AI5_ArtFlags: 0 0 0 1 0 0 1 0 0 %AI5_TargetResolution: 800 %AI5_NumLayers: 1 %%PageOrigin:0 0 %AI7_GridSettings: 72 8 72 8 1 0 0.800000011920929 0.800000011920929 0.800000011920929 0.899999976158142 0.899999976158142 0.899999976158142 %AI9_Flatten: 1 %AI12_CMSettings: 00.MS %%EndComments endstream endobj 17 0 obj <>stream %%BoundingBox: 531 565 548 571 %%HiResBoundingBox: 531.12109375 565.224609375 547.7353515625 570.4658203125 %AI7_Thumbnail: 128 40 8 %%BeginData: 4810 Hex Bytes %0000330000660000990000CC0033000033330033660033990033CC0033FF %0066000066330066660066990066CC0066FF009900009933009966009999 %0099CC0099FF00CC0000CC3300CC6600CC9900CCCC00CCFF00FF3300FF66 %00FF9900FFCC3300003300333300663300993300CC3300FF333300333333 %3333663333993333CC3333FF3366003366333366663366993366CC3366FF %3399003399333399663399993399CC3399FF33CC0033CC3333CC6633CC99 %33CCCC33CCFF33FF0033FF3333FF6633FF9933FFCC33FFFF660000660033 %6600666600996600CC6600FF6633006633336633666633996633CC6633FF %6666006666336666666666996666CC6666FF669900669933669966669999 %6699CC6699FF66CC0066CC3366CC6666CC9966CCCC66CCFF66FF0066FF33 %66FF6666FF9966FFCC66FFFF9900009900339900669900999900CC9900FF %9933009933339933669933999933CC9933FF996600996633996666996699 %9966CC9966FF9999009999339999669999999999CC9999FF99CC0099CC33 %99CC6699CC9999CCCC99CCFF99FF0099FF3399FF6699FF9999FFCC99FFFF %CC0000CC0033CC0066CC0099CC00CCCC00FFCC3300CC3333CC3366CC3399 %CC33CCCC33FFCC6600CC6633CC6666CC6699CC66CCCC66FFCC9900CC9933 %CC9966CC9999CC99CCCC99FFCCCC00CCCC33CCCC66CCCC99CCCCCCCCCCFF %CCFF00CCFF33CCFF66CCFF99CCFFCCCCFFFFFF0033FF0066FF0099FF00CC %FF3300FF3333FF3366FF3399FF33CCFF33FFFF6600FF6633FF6666FF6699 %FF66CCFF66FFFF9900FF9933FF9966FF9999FF99CCFF99FFFFCC00FFCC33 %FFCC66FFCC99FFCCCCFFCCFFFFFF33FFFF66FFFF99FFFFCC110000001100 %000011111111220000002200000022222222440000004400000044444444 %550000005500000055555555770000007700000077777777880000008800 %000088888888AA000000AA000000AAAAAAAABB000000BB000000BBBBBBBB %DD000000DD000000DDDDDDDDEE000000EE000000EEEEEEEE0000000000FF %00FF0000FFFFFF0000FF00FFFFFF00FFFFFF %524C45FD0DFFA87D2727FD06F827527DFD1FFFA87D2727FD06F827527DFD %13FFFD0BA8FD11FFFD0BA8FD0BFF7D27FD0EF8277DFD19FFA87DFD0FF852 %A8FD0FFF7DFD0BF87DFD0FFF52FD0BF8FD09FF7D27FD12F827A8FD16FF7D %27FD12F827FD0EFFA8FD0BF827FD0FFF27FD0BF8FD08FF27FD16F852FD13 %FFA827FD16F87DFD0CFF7DFD0CF87DFD0DFF52FD0CF8FD07FF27FD18F827 %FD11FFA8FD19F852FD0BFFA8FD0DF8FD0DFFFD0DF8FD05FFA8FD1BF827A8 %FD0EFF7DFD1BF827FD0AFF7DFD0DF852FD0BFF27FD0DF8FD04FFA8FD1DF8 %27A8FD0CFF7DFD1DF852FD09FFA8FD0EF8A8FD09FF7DFD0EF8FD04FFFD1F %F827FD0BFFA8FD1FF87DFD08FF7DFD0EF827FD08FFA827FD0EF8FFFFFF27 %FD20F87DFD0AFF27FD20F8A8FD07FFA8FD0FF8A8FD07FF7DFD0FF8FFFF7D %FD10F82727FD10F8FD09FF52FD10F827FD11F8FD07FF7DFD10F8FD06FFA8 %FD10F8FFFF27FD0DF87DA8FD04FFA852FD0DF852FD07FFA827FD0CF8277D %A8FFFFFFA8A827FD0DF87DFD06FFA8FD10F852FD05FF27FD10F8FF7DFD0C %F827FD09FFA827FD0CF8A8FD06FF7DFD0CF852FD09FF7DFD0CF827FD06FF %7DFD11F8A8FFFFFF7DFD11F8FF27FD0BF852FD0CFF27FD0BF87DFD06FFFD %0CF87DFD0BFFA827FD0BF8A8FD05FFA8FD11F852FFFFFF27FD11F8A8FD0B %F827FD0DFFA827FD0BF8FD05FF7DFD0BF87DFD0DFF7DFD0BF827FD05FF7D %FD12F8A8FF7DFD12F87DFD0BF8FD0FFF7DFD0BF8A8FD04FF52FD0AF827FD %0FFF52FD0AF827FD05FFA8FD12F827FFFD13F827FD0AF87DFD10FFFD0BF8 %7DFD04FFFD0BF87DFD0FFFA8FD0BF8A8FD04FF7DFD27F827FD0AF8A8FD10 %FF7DFD0AF852FD04FFFD0AF827FD11FF52FD0AF87DFD04FFA8FD32F8FD11 %FFA8FD0AF827FFFFFFA8FD0AF852FD11FF52FD0AF87DFD04FF7DFD31F852 %FD12FFFD0AF827FFFFFFA8FD0AF87DFD11FFA8FD0AF852FD04FFA8FD0AF8 %27FD11F852FD14F852FD12FFFD0AF827FFFFFFA8FD0AF87DFD11FFA8FD0A %F827FD04FF7DFD0AF8FFFD10F827A8FD14F87DFD12FFFD0AF827FFFFFFA8 %FD0AF8A8FD12FFFD0AF827FD04FFA8FD0AF8FF7DFD0FF8FFA8FD14F827FD %12FFFD0AF827FFFFFFA8FD0AF852FD11FF7DFD0AF852FD04FF7DFD0AF8FF %FF27FD0DF87DFFA8FD14F827FD12FFFD0AF827FFFFFF7DFD0AF852FD11FF %A8FD0AF852FD04FFA8FD0AF8FFFFA8FD0CF827FFFFA8FD15F8FD11FF7DFD %0AF852FFFFFFA8FD0BF8FD11FF52FD0AF87DFD04FF7DFD0AF8FFFFFF52FD %0BF8A8FFFFA8FD0AF852FD0AF87DFD10FF52FD0AF852FD04FFFD0BF8A8FD %10FF27FD0AF8A8FD04FFA8FD0AF8FD04FFFD0AF852FFFFFFA8FD0AF852FD %0AF827FD0FFFA8FD0BF8A8FD04FF27FD0AF852FD0FFF7DFD0BF8FD05FF7D %FD0AF8FD04FF7DFD09F8FD04FFA8FD0AF8A8FD0BF87DFD0EFF27FD0AF827 %FD05FF7DFD0BF8A8FD0EFF27FD0AF852FD05FFA8FD0AF8FD05FF27FD07F8 %A8FD04FF7DFD0AF8FF27FD0BF87DFD0CFF52FD0BF827FD05FFA8FD0CF8A8 %FD0CFF27FD0BF87DFD05FF7DFD0AF8FD05FFA8FD06F827FD05FFA8FD0AF8 %FF52FD0CF8A8FD0AFF52FD0CF8A8FD06FF27FD0CF8FD0BFF52FD0CF8FD06 %FFA8FD0AF8FD06FF52FD04F827A8FD05FFA8FD0AF8FFFFFD0DF852A8FD06 %FFA827FD0CF827FD07FFA8FD0DF852A8FD06FF7DFD0DF87DFD06FF7DFD0A %F8FD07FFFD04F852FD06FFA8FD0AF8FFFF7DFD0EF82752527D5227FD0EF8 %A8FD08FF27FD0EF8277D527D5227FD0DF827FD07FFA8FD0AF8FD07FFA8F8 %F827FD07FFA8FD0AF8FFFFA827FD20F827FD09FFA8FD21F852FD07FF7DFD %0AF8FD08FF52F8A8FD07FFA8FD0AF8FFFFFFA8FD20F8FD0BFF52FD1FF827 %FD08FFA8FD0AF8FD09FF7DFD08FFA8FD0AF8FD04FF52FD1EF8A8FD0CFF27 %FD1EF8FD09FF7DFD0AF8FD12FFA8FD0AF8FD05FF52FD1CF8A8FD0EFF27FD %1CF8A8FD09FFA8FD0AF8FD12FF7DFD0AF8FD06FF27FD1AF8A8FD10FF27FD %19F827A8FD0AFF7DFD0AF8FD12FFA8FD0AF8FD07FF7D27FD16F827A8FD12 %FF52FD17F827FD0CFFA8FD0AF8FD12FFA8FD0AF8FD08FFA827FD14F852FD %15FF7D27FD14F852FD0DFF7DFD0AF8FD12FFA8FD0AF8FD0AFFA827FD10F8 %52A8FD18FF7DFD11F87DFD0FFFA8FD0AF8FD12FFA8FD0AF8FD0CFFA852FD %0BF82752FD1DFF7D52FD0BF8527DFD11FFA8FD0A27FD12FFA8FD092727 %%EndData endstream endobj 18 0 obj <>stream HWmo6?@4$[~ͮ{ɮAAKÆoHZ,W[8 }X^NC&Fg>Ȝ9cTB?^ ǷlMU|&BR_#glنxϧqE)@@zoX+vF z8a)x45FcG"q{2= \w8ʿx#>}ka_%o},R`g`W}9d'% ilN&c[;G~庚6}؃ QM(qdMZf7<_c@>d=0w os@@A,U& {#:: pwA4ιAqُIL\{NL?xcg9C"qn;C-+> n@1h߆i9| 0 Ac-웡W i27wsߒ THŞ~IMl떷`8R=} T5NI4kFП4K X7%6W|<`g3AADLXP D˘h OjS-H \nc (Bah&R8g2z9;X q(Kg%UbvK(3瀦c*y?718yk괏.ZsFeTeod#˝TOc9[IV=]Lg k ZTC:hhb,(RWpG$E;9-vhrl1+Y GTRhQ~>HY赘 q"<ׯX`gVW76_4Л{*%FW {Vt,Ts{~S]tkE t+c+"L`h"S72ͦݑ!A9(~K%X Ѱ~Nd @>'3SXd;l&8eRθm_A-bjo=v2LfD~ $ U8]AGz#˝ysn8T%):=.<-#cn W0%LGaBR/2`T0(3GT4R^2A@?`cטV{BvdYy:p>^/ 0~G4t.q~77b.B[W!ii[C-ۿޭmL3=c&g NWef"f)/z3T6G&eI&kVQXC p#+;LE#`2]88# v[Hh\՝b x۟<5+- /-a^ 60bЭ7(;"=T;I`6cdu&|6-VPTZqk2$fV9>4?{gALبq?}Qg̥WFE^#WڨsyzPw<4y!8&ӝ~[WAUG҃z4tW@\]iҊUYmv 0Vݵ4->[.mf`ٯn\v}΂p7\% ȟP:ț[6D1ۑQ |H˿rj*D1P9Q7_[ln(;~f|oCl_ k>B\맦>b_NKn 2GWz\- j .}y]\:,}unGE_%QHGV^M \KQ8-G+EqLFK9}XAU;C)`54pBK hJXbSލ6Ħʢj3Z}Pd 4`l.3رDFH@FѐXMt[E'B\f1JWN[^GÖSϬe|`9Qx [;GeTȖm2ۘa1 d%o1GMlNpJ_Zk(,7ןSKNq\i(B+eir,~NM褩/c|˝g|"»aғ Xut0{wBMG4ȓkZ`4'Bئ pk,X^J~zY6hʷ`E b`:C3Ԅ0]0C6+T*.=d `.?0Dd{AzTѴ /r= pU>1n!./9$,T#ctp-#c6 $G1U 钉Z>3}.RP\τqRI߮~#<,f=v8"_sc  @A|:E dR~,ȼac :rCmsAY&Ơ]NrhLxJt|U^lje{c;L5,f2X 6v5O0݁|&T )`$tHgR>U8dCXFc!LR L̬-{/4<,`mV 涱ۆ3[` ';".A v6Z="]LaL02aL+`~vB$Kcl`/n_lX -_+6 >l:`Ea1.|a@iD`ff$a-)wfxtvxt8>ɒLOg M{Xzu\aoRZkfC1DdZ$UyOp&O/U2IEȯ+RJVؓ&́X?ꄶH0$5H`XFDy["+͞lOH٧ZkFՇS~\U7]YS&BJ&ӳ\9s?W関h}|wTT\QWPYCVVԙ8@č7XXBp"a^ bZǛmg3E[xx{?[L&~jcOن4GM2'4Z&^Ne5J%=7n4o.mgkWMOMۑHu:ȍ#ykQ]]V6E0]VÈ .e'|J%lpqɣ" |ه1X/ͱA||0>>xew\<4*,QtNPB(]tf8dž+d hp0dcr04Ag7KpԆլHplq8OYu1~B@0A 5l!1q@>-b:tsP9f egf8 Q#w,2j `7^P-;pn$ab1Ńl'Y>| {hNjN#8|wUar& QKpe+Q{JS9$'18zćL+h"/Gsxik Z~Oz*M8Q+B8NYхP Ц @`x3#ӽS/+?k ^<jA"H Jhj)ȶU{+_Q9&+;٦#C/x,0[a [&_ |QDa3:oP7L(u@i#/ Ȣ`\p ᢢʫ .]e8{󞨉#QʄOdX{)aSQc"Z# E.BZiNfh5uJkV\޳LY?wd =?&mH(iWڋ깗+y2/I,wA!NtS׳lc<;/z4J736~Szn+}ՋI:}j 7_vf;kd:ދǎc.v2p/O;r,JMx6V:c4Jks9sbf3gaEh4 .U5>*,n/|v(LJ}~DJ[9/ 't  ]߶K6d0Vk'YבxWT",fZeK՛CsQt*+a'Yׯh_ހ..BUFv*DCSU,961ү>OLndjF*eԲ?կJ}W:8*7QJbg} #eۖtp)@M P}@KLORZ܋Zcrl8P^r:8mн *4 ueϨ-yF_Y-U?) _ui^}kX 㬞򡪧y 6pQrU]m+Qۆ=}aӴ'dKO: GUcGvzyr?ƉIPv?Jq;VGݯL+EbB]͒('Þ7h{B]O'l?yjyz$ /@kXYw*:掵f>qqh5 r^{qmz6aݠ ):/G&Xѡ'h/!O}A; ԘbS-μܠ˳so/h梁{qX[[zq7xZ(hώ+> ~΢&%Ao~.^[, RO&1_>ƪg:g"$.i L2̳!>Sʿ﨏|V0 T ZT(&|f&1 gQٶi4ֶsm!EʃERJ zh0 hTc-m1T5ZPQ !2 dKMz=/`6Ǻ.q=gl;׷6 N1vqR,&7HvT)UYA HI#S +ߛs\zTgw+d3Kgw ;_%?ԯo"B.~\Pp3Q5Q*}ݍkRŗťb1ٳLIyaRsƆY|{]J?6U _wо8JtGYoy}-VZ)UH| =cW|2R>%*>lFڛ&-~aRNxVW4,(0IZx5KrGRv 饤:δzpp%xh#G>1k^.wJyP ZFZ*hF7׾j@"OITp):_Hб_{G[8WNf_ɿmSߋyͦT`{QN+9tb6.ޛNDogӎn4;ْ*Zb4ԧ%FA`b;hz}Yt;PaF6QB6~}#QcEjKMy/ +=O~z}-\w_Wz8˓w^AԯW_0;_&促@Lj;[D*Z`|g/e\ 2IE@"hZj0՟%u+gӲ?eF T7l.wEKw}vTί)wŜBsA@l' @r3SxNےR!Q zF'v&t[᣹^"-%!>އ~W :׏cԡN22Ţ*S2fy8U}]Ob,g$+Y|4LzBpb-mjT^ksXR(C{EO/";eA~ǀm*$ZJ^BKZn6E-;Ww1Q+H<{&t"0$ze2oej mrl{Gq*D˱BRcaSf}叮S n+W"E=&b ;sA Qz1Pcvem)g mW:Ђȟnq s(I^IyqtJVͼl,VaFjV-&f4Zqھ@rԶРێ捓KWS:tL9)8~ ~ڼ8$zut3*_Tg;XY1^-ce̊h3mnvnDҶv\r v(8Vx<ۂǦ[ܽAOG1po+捧ؼD%ܳk~*p{NA5wP`q1 X\rs{#T5 U-F +Shj5&6Mv|ڋ`7^s O0Dǃ)}@].i{Px X^>BRng ~tšRyTNYǒEpu{Lmĩ͠ qSg: |ĉG7RWCiHw..&rUQհ7-JS::4a[!-^#u-QqM@vröU1L'ź\c!)6` %)CvLP0m*}CTZ1Ͷyo(TQi¼0. 0E*t$`XM1"<(sh6mkNnx39'; ]@0mՑeAnl\61 Dz)Ûv1] x[v0{ؾ;|`^6vhФ -7Lw.nK`'= $iљA<4*{|DeLÝ]PiP}&PV / ~NP"GEMLÉXn*KX@>nO(u m>C6D|F+(&Q\9sgsш{LWc >.=!mA~рh=8&="rY\ j kʼnc-F,@n:d;Q"+oiTup?$fCZ1L<Ε[gd,Pszp@* ,Q\b'Fy䍎0?>lʓ0Sr\} 1U;Emei|_ize>Q=ATD@~UMrŐ/Ő=B݅0ou4]h^m8X۷!a:B St|䀞>nಠĜJ 0P:hMw;ӵS0, 34%dyPI;0|>:撖1)Ǝ J(,T]t%Ec-tۍ2zJR%Su+R[W,({n}MYzI+h޸&.Μ+j9mZX:ёC*`r x!,Z/ 7NX|t{^h#SĖOM&S5X8 ]4EX?%_漖zm%D/G0$3m *@( (-6DoUJ*ƗZ.W89{|v:9|d3'fsTc_5NP"kӃPn6b;-)5nBGupݏ9wJʢ~fou=6|ز! },QD ϭ:j 6e 1>Nj#QF4gK8Ս*PL$(vHB!$@ %tMm/+' A}]ӶJ6S\j\'T%X1~snc|fOݟΧt2闸g^P@EA!YtF_oͣJm6cU禫)l0)ΤtO^xi >8MTfj"v?5rNi?A%vjvB{@ϩsOɕ n¦V0JצFƁ{(x#*CIUWm>4ʼ0V{$b_͹yx֠kdJ׍D]}vr|8Wymd::_/L@N:I1+j1pިMt#(/Mrh%ypJJXs`~אJAjXd{Yҩݺ$tqBqش-=s)r\$T1Ǚd9i}WKbE$S֩K{[pUT\Km\v&3kQ"S1'n.E=rI5vЅNwBf'8*7l8+*xYٺ[=+pk GZiEZFEwIvJgGį ؙ\>igX뎧:*¨,=#@BIR^4$8cASH_@Pok==0AWnRj疃9hE?cBc9&811E{ML8Tz!\N+F$;-Oq}iGWQԌgڅqԸ]Yٗ߾ DV{@&$ɬnY ;Nώ4  Һ _I6\:1FB3K6MidY ^.ruVmRh`MdoLDqCdZ9;5DwʄV|̙P`^άݦTe s PUd^=F"jO2# ]7$_{9IT3+?i5%3c!@:)v>g VԚ_15cv_p-\61 Ͼv7eg ,bܐxtc$ՌIFI܃CiS{.*0qߙAcG"'2Q1J (dk1pFuvp#˸ƈ6/_,YzɈ!^q} 1/I7"}j %d5]v쾠p\̴ gZ۹ ad7>ۍ?N?@_2I5L1pZCj %Yh Q!hB1)oNJ \θ}1ҌƢ|cըv@c1cC(RB+A'vl}aThTP'p5S{԰vbr:QC @"r6( q SR|Yd&i (n$Bb; bCt]aJ90ٲ@k07/(b"mo6?ͦr5kǍNJGUO3v[9lkƽ9:=ZnVo(`^WB|l[Gn9҅c!i 𨠺=.wIoJfWlbp?fq8vZNK-rnk!3TqT s(!t-'U`vqd9m F%w[ #S.u8>Z8NB9NAyӬq{u*tε8^^NQ9B8^8^XWӽ9eF_ Y,>\|'X'HAEvo4('O$;*&ޡ~UTYx w I5r\bd~x* ,46Jy^ufc=ԩn$ S'!ID=>51\exV2F01+$)$tbѩKs)R>SobJ[c"$/lSt%Ǯ0b'i 3qDe/׾Z*rq%nZIXU+>;^@?ۦOoSEJ]|P o%c![nHM ͳfu0jxLwvC'6źNk,3HU^^@CJ Z5NZLy,W1Ե؝w$h,H2hUmߗ&a P$8ܹ B!y#(!H^z$ /#R@!hFc44A#j$ Hw|̙9g)JHGǝ_=AFT8 &?g'DPh?DAbJOhYC&sY4; xqo+6TIxWaUwfxE}=O|ׄYQyD΃F dIVcσ+!L iTe8Xդ WCTeci + +x#0@8 ߹˱LДTx`l<tP!8Vˢ8sG@+WNo,&8,xԿSNZoE!(සݵ؏-BR_l I,XBft mzhpҊ(L2(C`u)dEil2=` endstream endobj 19 0 obj <>stream HWVJ}<@!@ H,RW 303?\m{}NJ>oboW:9תV?NɫZ~<YW J5Fx|E`woʙm~T*>V' {Fu˕U7$`kF\ |nI@.;Z5evHFRFҚДC3:& G MO>#ݐ${qI !jk/=BCՒq^6YYW2U;--[bJr}=KEi)ԮQtߕ)r#uqʼnuk RJ,}\ XΪ ΏiUF1|X+NhY n*$h\뛒ϕ^qָx;]ܗt|32_-~fɔiԎ5WۨҌJ22ϳzZqߏTh*wR>$q,ʗGz򬦪7F-h9Ҍ9 |j,5F'wV┘2ּՏ_3~mɗug,Q팘 ΛIV57|Hs`4y}S;ܘ';.K:||$z@j$DgFx7oRb7Ìy9•-P z0&K)K5%[Y7_/i3/097iy U뒓yZK7-CP'#O7omK4>cHHKLI\۟4/GCw&s YAӕ9I2<%W}ŒN{e?&]Fv> ܬqC) a$vphHA+4ou9mh"Q=I$99{R[ҏ*5(Q̊s iqؒP'7C #BfMpY<(349:Cr'+(I2 qsS\" >y: ;4_A -˾q26m,qEwhoFQHB 3Ir-0:iPkE6vuq?XQ .0juʭdyrVv}f,55N8g*ʠtyq*Hp~7T?7c %+86%qggPً?9PV=ɧe쩔AN]̌]B\ abݨ8-f fXu8PO ;Z)}nC#E7?fV.?aGKI]~@'}oM)rtK#}PZ $Ĝ $[ 3E:1C/7֢c1rAz+_WZeqve.sW3n`.`EcU}A(zV_gd D(!b^c;7T6q2\P`T@dRs&!YoiRC}Af'Gw@BvLc:JV]-"ZS{q+B 9LfMp'.Ɍ-T4(p~:Q݈%doCg^{J>TN LTqJSeyFFq!&z4jP5]WƘ+ع>gO#LI{sےMUt 0eei]MVK`0/°u "f{w`Axawuq3%A;gs'] WH])11c'M9}c+{ ,yBQJ)륭|gg{fz e89Kilo!+d*k$:|rͼ}irwb螽inւ2V$+8zwW;h[0azdp]< =:=`b ,%89t¦lyOKْC(d9J0-3rp>btYs%d\yR!|0C:Sq0avgzɅ°{RB໡RF{To?3C9aFQ:f7Fv]M)\k;0TD Czr_ ߚjpݢv (<84M/փEJi!Xdo?&XH=4[>˄4[؄E-{qİ< ]Fi}i7Anǚ($;[]0M6)xۼu G7esS3{A43FWV^Ӝț\. u7\^3^t}LI*{,wV~,ǐh&)xW[ |Z*4 gJ9Lk" Ӧ5٪|&NYJda3kQ׼e3m:L!D0ô֦Y;S/:*,o2U[wk.h[|$ _4/L!S;N>7S\cÏuxoR%f "pV Ţؗ[ƾz1=e evl-x ᩵ Vmm)] Voif!Ҷ[QKp ^͏|hr+En{4cQl;3 W7&}7NQ>6T_ʕpx7&xH7mGW#n2kY-{WY)h_ZJ|SN5ޒ klAb KnxܻaZ^6 ,Q4[yoO"Nȅɞ Iߥs]y\1Næ ,@TEPr%QpOc"y YFYdjkwGw6r<7z5 ɐ3:_k,0QR-,fKYP-oF+Vn1ut]-}:#dFS>|Y-1;4B% _fXN&W9 s?9CR_8<p(織Fo(W֫22淰y;Hcdڛ,Gx#gyr]o}Frc ,C د,>p,"/efiiYf7jYyfF=*na~۴yhV`ddV:geNVȬa˚6FPgӺRGli/bw,ڔegjZelkgoNPC!W:Z /n\H_n$/#ί{b60施a9r/׶x_/yKp޷l$S6HӽSIm}!6\$x\F''EV&`sG^' AesQ6.A@~8C^cbj,{T4P G'$0 Lh c}fI&JP3?P^ !̈́5ŀ`Uc6+x}܁~y tW)&"RYrHb9hL}Jƿ#MG ? 4[1 24rʽN^:qB>bF'lipM{t ӅIµeW\2SW7z[Ppfnþ%Wbr$ϻh }tik[flx2mjB3_CqoDf9m mLyYt1.A*/unj&Dq./Ņ6J̝z.#%ٶ3E<=JArɾp{w%]!WH5Dr~}%]p $ ]_w]Gd$ |n5J6Kؙ#̀1äj37U}oOa T7>6"9,x|ī&s_&88lj^/'^88_6o&^I`~'I9:F H]6 3Д[HDZH_zUƮN ]Lz4CE`sxElՅnŒfRUՋEx:{Y&G5Y&_;=p@(nzbqς|/M۝/reV9պx(j6$RJʡ:v&T٣PV@g5>dT #N AvuCdaөl h;S Yȑ&RRR!(:/ Q8X*87}^*7pV[|; S"X<"eKslP% c"kCҒ{[mGbި rS9C%IN=*>]{"t4&bOƉqyʭ.۰J {*KFG"*AMOåPUh |B!mc'11a0FkVQi:-Wc1nXwc'*6БIG{W]nGB\ʣ6i+q)v;gі֑ڑ5iCRMB6ɾE2«ďR2D7>g\BxKϾ+ ,GAū+65x' C؀qptWlLЪPtlO07e5e## ;6 ByP&>p{wFNgD0څ'׏ԋhѮDGPԋ]"iB=BޥX_»[()Hb" tP5$K @=u{Y#{Xt> ~iYa&l]G!H|\4\!gL[L̝@)vOJjo~\/C} iGdi`5ӁfIR|xJ2&"{@ەubۗ|`k?.4@)mW@fܡh\wF4#r+tv߼i=kY3J*K6}p &wNP^0&325SH GfJ!A~ %!V&#rjK.{)~sx b.vò׈sM۔kf\c Z0`ՠٯHGb#_SXҭԕ&`BB! 2 X=!^xJRIuuծݻ,t. GU/a_6g;Lfec UaߔDvX ֔*?Gas꨺ ]&\6',&f#G5*ǫW魀 SDy۷CafC0|'a0isʞN|E{d.vvxYl|T*1u1M#X!9s )$ C̽ e/u(Hy #cie/"WFWgFVWP}a!=asg;m [I#F31?&ݠEJ73Bn /1!o7hG[P >P- 6U^:^COtVOUytWiV yyCkx;Gp5cE5|͐b=F. XBhsY\A~ 8Ӛgƍhˇs띕U_:rd.lR5$hG8_ 267bLvi2r/'F',osy Їxj$oUB@5-U{;܌u1;#<2[p ߻Nv@9g\0 -0A(9\r,43[2`a/$z1w\<dUyM@6Am/B=Bh$IX#(P{ `QzӑCj֬#GQY$ {Xiv~fo]aX߭&Qw}R YMK[:.~ *VOk.gn{rv6Re}Wz߽jWX!8?;)@\o9|cм b֝'`R7{iM8fxCSW;P*OT82c!ihXA*$2D 3gs #iz24Eud!\PƸ}'`ln7~,#do?$aZx½[W ,N P "> >̱\DI]8(^8cn o߆y&`dGcH^[RW1{/ZOM]` $ ׭ UűD.p~?| Hh pATLwW==Uv'Q8f&Y~ct{zI4?~uuOwv_ yhL~u39_3w:!TjHۿowc~?,}uJf{gY?8M.oW>t"1&[+O^>8\0 ΃:Vmǀ?W}n[ປ&=tsɽA ekݽzg̈́oH9X<^P6{['r_:}韷.wPD^y,a/WV:lNG*DO`5@v A' 7_w vZ yg'pNJs-5ikC2H5|8\|?VoǼ=P~jRLPδ&zסJBP~%YBk@W~Pi2ҨVq/6>ZDX0o&>sl-XXTZJgKcD|=`^]l(5quL(3CBԎ;磆阛F հg|ژ:i\  xiudcrCt5r)%!R'W+:>҂;bN"[ DO,*hu\z.(O}+c,y $mIJ֩E<*RW|2R7+šNTf ֞\Y@5+-8ެۧ[q[M}Խ>]{sm}6*<ɼ RF~PoRJ̆>˚oY`簒>5s*" Y>d,@۽/Τ -,Jr>^PqZkP,Q-(2}$dM*gT& .-H dt&{F ")`KWpWr5zvd6"IF/u*$Az'=FSlEHs$J,<dbtS> @cR.`=!̴瑇5Z(>Z߄ԯUTtgH4U&KQoP,Dl.s$&BZq< K8J 04t{)SL*C%rFi{@PSDjMY)'P:񥜗 H,+p4I'x*zds2DljwA,ȼu.ԻH:25-{NiR^881_\kt_B,~!P~ZX{g6/u]A ! YGTΪ#ZPu@7u!Tu@-m]jsD]T6k0џڮ(%XjJ^u*%R[Zu,TQiסȮcEIfHx Rv)+SۮR,i|e17v x ]w@-C/YPZ@{Ve֝2;K \R_w,1)Ҹce! 0I\Xa`$сPaKΏ;«yXU\y!!-̻wI j!ڼsTXE̻S00+{}Ue4UTy/46UBj0*;$˱y ȼJT-;LB̸kV=ךϥEY2YT=O&&&ߜXE s7m %>mɂQpEWwCx{xvu=m&dloe~}~3]9n?˫l1H{~=w#qdbWP1/ endstream endobj 6 0 obj [5 0 R] endobj 20 0 obj <> endobj xref 0 21 0000000000 65535 f 0000000016 00000 n 0000000144 00000 n 0000003469 00000 n 0000000000 00000 f 0000004594 00000 n 0000042756 00000 n 0000003520 00000 n 0000003804 00000 n 0000007576 00000 n 0000004893 00000 n 0000004780 00000 n 0000004664 00000 n 0000004695 00000 n 0000004928 00000 n 0000007649 00000 n 0000007832 00000 n 0000008790 00000 n 0000013822 00000 n 0000029619 00000 n 0000042779 00000 n trailer <<12DE95F903FC43C895A4121E1E15237B>]>> startxref 42954 %%EOF *uuid:edd6ddd4-6cef-9c4c-ae00-326be6ce1b5edliFD$b96baa45-6715-1179-bc96-d795218d25c4Vector Smart Object.ai ART5%PDF-1.5 % 1 0 obj <>/OCGs[5 0 R 141 0 R 277 0 R 413 0 R 549 0 R 685 0 R 821 0 R 957 0 R 1093 0 R]>>/Pages 3 0 R/Type/Catalog>> endobj 2 0 obj <>stream proof:pdf uuid:b0ba8869-68b7-4741-8ede-f715b8dcd23e uuid:83f94084-d1b2-45d6-aba1-035343a77b29 2012-09-20T20:47:50+01:00 Adobe Illustrator CS6 (Windows) 2012-12-03T11:08:04Z 2012-12-03T11:08:04Z 256 40 JPEG /9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgAKAEAAwER AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE 1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp 0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo +DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A9U4q7FXYq4kAVJoPHFVN rhB03P4ZEyZCKi9w578R7ZEyZiCGuHQxtyNe9T7Zj56MDbbAG0B6sXjmo8SLlcJd6sXjj4kV4S71 YvHHxIrwl3qxeOPiRXhLvVi8cfEivCXerF44+JFeEu9WLxx8SK8Jd6sXjj4kV4S71YvHHxIrwl3q xeOPiRXhLvVi8cfEivCXerF44+JFeEu9WLxx8SK8Jd6sXjj4kV4S71YvHHxIrwl3qxeOPiRXhLvV i8cfEivCXerF44+JFeEu9WLxx8SK8JXR6fDdlpajaib+2/8AHNr2fIGBrv8A0BwtSPUPc0fL1sXi bknNA4HStGLnr/s8z3HpcdGhiubW4DLW3l5jcVqUZP8AjbFU1+te+BLvrXvira3Qrv0xVE9MVQFx rlhFsjGdvCKhH/BEhdvnXFW4dQ9aBZQAnIbivKh770H6srlKmyMLUZdQt1PxyrX3YVyqWUd7dHFL uQsmsWo6PX5A5UdRFtGnkhpNaj/ZVj86DKzqA2jTFL9S1e4NuTEgopqwr2zVdq6jIMJMBy5+5ytP p48W6T/p24/kH3nOU/lSfc7H8pF36duP5B95x/lSfcv5SLv07cfyD7zj/Kk+5fykXfp24/kH3nH+ VJ9y/lIu/Ttx/IPvOP8AKk+5fykXfp24/kH3nH+VJ9y/lIu/Ttx/IPvOP8qT7l/KRd+nbj+Qfecf 5Un3L+Ui79O3H8g+84/ypPuX8pF36duP5B95x/lSfcv5SLv07cfyD7zj/Kk+5fykXfp24/kH3nH+ VJ9y/lIu/Ttx/IPvOP8AKk+5fykXfp24/kH3nH+VJ9y/lIu/Ttx/IPvOP8qT7l/KRd+nbj+Qfecf 5Un3L+Ui79O3H8g+84/ypPuX8pF36duP5B95x/lSfcv5SLv07cfyD7zj/Kk+5fykUuuvN9/b3jxI tF4I2x7ksP4Z2ns5mOXBKR/nn7ouh7VxiGQAfzf0lL288636unuISYZVvA8vFiAYnugBzrT/AHUO 2dBTrUVD5z1KbX9CtW2iub0xy7/si1nf/iSDAr0X6174Eu+te+Ku+te+KsR89eXvzkkDzeX9Vsr6 IbravEIJgRWnH1TNAxp+0eGBLwnzT5m/NLTLj6v5jl1LTpGJC1BgR6bHg8YRHH+qSMKHpf5U62r+ Uomv7x5JpZpXQzFmPHlT7Rr3BzWarebtNJtBnEd1ay/3cqP8mBzGpy7VcCuxVa5UIxb7NDy+WQyG IiTLlW6Y3ezHM85d07FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYqtittNkmka5aktF AG32d6fjXPQfZT/Fpf1z90Xmu2f70f1f0lDNpnl4z2EnrLyVbsItYuRq91y2P7z9pum2dO6hUntt Gj1fRJI5D6yXpMQ23b6rOP8AiNcCWY/WvfAl31r3xV31r3xVnGKqN5Z2d7bPa3kEdzbSikkEyLIj DwZWBBxVjd7+WnliWFY7KD9G8BSMW3wxgDoPTNVp8qZTPBGTdDPKLFdT/LrzDZ1e0KX8Q6cDwkp/ qMafcxzHlppDlu5UNTE89mPtc6hYymGYS20q9Y35IfuNMxzGubkRnfJFQ69fL/u7kPBgDkeEMuIq eseZNSWxLxpG4Q1kG4JXx2PbNX2rp5ZMVA7dXIwZeGTHP8aXf++Yv+G/rnM/kI95c38wXf40u/8A fMX/AA39cfyEe8r+YLv8aXf++Yv+G/rj+Qj3lfzBd/jS7/3zF/w39cfyEe8r+YLv8aXf++Yv+G/r j+Qj3lfzBd/jS7/3zF/w39cfyEe8r+YLv8aXf++Yv+G/rj+Qj3lfzBd/jS7/AN8xf8N/XH8hHvK/ mC7/ABpd/wC+Yv8Ahv64/kI95X8wXf40u/8AfMX/AA39cfyEe8r+YLv8aXf++Yv+G/rj+Qj3lfzB d/jS7/3zF/w39cfyEe8r+YLv8aXf++Yv+G/rj+Qj3lfzBd/jS7/3zF/w39cfyEe8r+YLv8aXf++Y v+G/rj+Qj3lfzBd/jS7/AN8xf8N/XH8hHvK/mC7/ABpd/wC+Yv8Ahv64/kI95X8wXf40u/8AfMX/ AA39cfyEe8r+YLv8aXf++Yv+G/rj+Qj3lfzBYp5i8+3UWsMDxj/cx0Va0+0+++dr7O4Rj05A/nn7 g6HtSfFkB/o/pLHv8dXPrafL65/drfKY+UtPjkvCCR/d/wC7Pnm+dbSd6B5ym1Dzh5dgZ9vrxY7/ APLrOv8Axtil7l9a98Cu+te+Ku+te+KvUMVdirsVdiqheWFjexeleW8dxHv8MihgK+FemAxB5pEi OTE9V/K/SLir6fNJYyHon97F9zHkP+CyiWnB5ORHUyHPdh2s/l35zit54LS2S+MiMqPFLGg+IU39 Ux+OYefTTMSALsOTDUw67MQ/5VP+Zf8A1Z/+nm1/6q5pv5Kz/wA37R+tyPzmPv8Avd/yqf8AMv8A 6s//AE82v/VXH+Ss/wDN+0frX85j7/vd/wAqn/Mv/qz/APTza/8AVXH+Ss/837R+tfzmPv8Avd/y qf8AMv8A6s//AE82v/VXH+Ss/wDN+0frX85j7/vd/wAqn/Mv/qz/APTza/8AVXH+Ss/837R+tfzm Pv8Avd/yqf8AMv8A6s//AE82v/VXH+Ss/wDN+0frX85j7/vd/wAqn/Mv/qz/APTza/8AVXH+Ss/8 37R+tfzmPv8Avd/yqf8AMv8A6s//AE82v/VXH+Ss/wDN+0frX85j7/vd/wAqn/Mv/qz/APTza/8A VXH+Ss/837R+tfzmPv8Avd/yqf8AMv8A6s//AE82v/VXH+Ss/wDN+0frX85j7/vd/wAqn/Mv/qz/ APTza/8AVXH+Ss/837R+tfzmPv8Avd/yqf8AMv8A6s//AE82v/VXH+Ss/wDN+0frX85j7/vd/wAq n/Mv/qz/APTza/8AVXH+Ss/837R+tfzmPv8Avd/yqf8AMv8A6s//AE82v/VXH+Ss/wDN+0frX85j 7/vd/wAqn/Mv/qz/APTza/8AVXH+Ss/837R+tfzmPv8Avd/yqf8AMv8A6s//AE82v/VXH+Ss/wDN +0frX85j7/vd/wAqn/Mv/qz/APTza/8AVXH+Ss/837R+tfzmPv8Avd/yqf8AMv8A6s//AE82v/VX H+Ss/wDN+0frX85j7/vd/wAqn/Mv/qz/APTza/8AVXH+Ss/837R+tfzmPv8AvYn5k/If839Q1Yz2 2ij0RABza7tBVk5HjT1a1NaDt75vezcEsWPhkKNuv1WWM5WO5Iv+hdPzp5RP/h740EgP+l2HRzIR v6/+XmwcZPPJv5E/m/Yeb9Hv77Q/Rs7W49SeU3dm3FfTda8UmZju3YYqXvP+D/Mn/LOv/IxP64od /g/zJ/yzr/yMT+uKp1oXkt4ZUudSZWZCGS3TcVH857/IYq//2Q== Adobe PDF library 10.01 application/pdf 1 True False 1946.000000 1136.030762 Points Cyan Magenta Yellow Black Default Swatch Group 0 Document endstream endobj 3 0 obj <> endobj 7 0 obj <>/Resources<>/ExtGState<>/Properties<>/Shading<>/XObject<>>>/Thumb 1147 0 R/Type/Page>> endobj 1095 0 obj <>stream HMn0:/1g8iWAd썶Y8-\z>J$lEc?y3Dxh`D!j89O)%36|gkNW\O@\ ŇJ>fszʫ=J%Ί_![O)(+DdB6`K6kr eA ))Dom@f݈]be:|~)Rû7k[J.TG>-{dMtQً%qzPkI:HQ˒ۄ6m|bђsnyy>4o!J1݋91"VJ p:4XޏeY|y-Tv>1mnNK61g6͟m܃҃ڃ}=z0wIOIOIOIOIOiOiOiOVEWi%T3 endstream endobj 1096 0 obj <> endobj 1147 0 obj <>stream 8;XF30lhn#&4P.F0%l8;\C1(Ca'[5)FnEqWnV7I0di NX.FD0,N=<4:X<(_:7,SF<=.D(5hZe>,H)o.6Xg!UHG+9+Z(I`qOS$)fp/P,4!aV? jaDVE:t56h;!I3f$$5B,#cm6=HZ!&Q?S*.Zc_'5dRpWXZJ-g*XFZL_b!6OSqAhr9: L$K3*V=>-g#!n#/RC6XN>c!dWPW*gtE^'X+2kO+]~> endstream endobj 1149 0 obj [/Indexed/DeviceRGB 255 1150 0 R] endobj 1150 0 obj <>stream 8;X]O>EqN@%''O_@%e@?J;%+8(9e>X=MR6S?i^YgA3=].HDXF.R$lIL@"pJ+EP(%0 b]6ajmNZn*!='OQZeQ^Y*,=]?C.B+\Ulg9dhD*"iC[;*=3`oP1[!S^)?1)IZ4dup` E1r!/,*0[*9.aFIR2&b-C#soRZ7Dl%MLY\.?d>Mn 6%Q2oYfNRF$$+ON<+]RUJmC0InDZ4OTs0S!saG>GGKUlQ*Q?45:CI&4J'_2j$XKrcYp0n+Xl_nU*O( l[$6Nn+Z_Nq0]s7hs]`XX1nZ8&94a\~> endstream endobj 1110 0 obj <>/ExtGState<>/Shading<>>>/Subtype/Form>>stream q 1845.888 52.858 m 1845.888 23.872 1795.694 0.371 1781.888 0.371 c 1658.554 0.371 l 1644.747 0.371 1623.554 23.872 1623.554 52.858 c 1623.554 220.045 l 1623.554 249.034 1644.747 272.533 1658.554 272.533 c 1781.888 272.533 l 1795.694 272.533 1845.888 249.034 1845.888 220.045 c h W n q 0 g /GS0 gs -0.0000119 272.1621094 272.1621094 0.0000119 1734.7207031 0.3710937 cm BX /Sh0 sh EX Q Q endstream endobj 1111 0 obj <>/ExtGState<>/Shading<>>>/Subtype/Form>>stream q 1520.258 190.668 -1143.688 81.645 re W n q 0 g /GS0 gs -0.0000036 81.6450195 81.6450195 0.0000036 948.4145508 190.668457 cm BX /Sh0 sh EX Q Q endstream endobj 1112 0 obj <>/ExtGState<>/Shading<>>>/Subtype/Form>>stream q 1526.144 0.173 -5.886 272.119 re W n q 0 g /GS0 gs 5.8857422 0 0 -5.8857422 1520.2578125 136.2324219 cm BX /Sh0 sh EX Q Q endstream endobj 1113 0 obj <>/ExtGState<>/Shading<>>>/Subtype/Form>>stream q 1596.459 265.82 -18.227 6.296 re W n q 0 g /GS0 gs 0 6.2958984 6.2958984 0 1587.3457031 265.8203125 cm BX /Sh0 sh EX Q Q endstream endobj 1114 0 obj <>/ExtGState<>/Shading<>>>/Subtype/Form>>stream q 1596.459 253.223 -18.227 6.299 re W n q 0 g /GS0 gs 0 6.2988281 6.2988281 0 1587.3457031 253.2226563 cm BX /Sh0 sh EX Q Q endstream endobj 1115 0 obj <>/ExtGState<>/Shading<>>>/Subtype/Form>>stream q 1596.459 240.626 -18.227 6.299 re W n q 0 g /GS0 gs 0 6.2988281 6.2988281 0 1587.3457031 240.6259766 cm BX /Sh0 sh EX Q Q endstream endobj 1116 0 obj <>/ExtGState<>/Shading<>>>/Subtype/Form>>stream q 1596.459 228.031 -18.227 6.299 re W n q 0 g /GS0 gs 0 6.2988281 6.2988281 0 1587.3457031 228.03125 cm BX /Sh0 sh EX Q Q endstream endobj 1117 0 obj <>/ExtGState<>/Shading<>>>/Subtype/Form>>stream q 1596.459 215.435 -18.227 6.299 re W n q 0 g /GS0 gs 0 6.2988281 6.2988281 0 1587.3457031 215.4345703 cm BX /Sh0 sh EX Q Q endstream endobj 1118 0 obj <>/ExtGState<>/Shading<>>>/Subtype/Form>>stream q 1596.459 202.837 -18.227 6.299 re W n q 0 g /GS0 gs 0 6.2988281 6.2988281 0 1587.3457031 202.8369141 cm BX /Sh0 sh EX Q Q endstream endobj 1119 0 obj <>/ExtGState<>/Shading<>>>/Subtype/Form>>stream q 1596.459 196.54 -18.227 6.297 re W n q 0 g /GS0 gs 0 6.296875 6.296875 0 1587.3457031 196.5400391 cm BX /Sh0 sh EX Q Q endstream endobj 1120 0 obj <>/ExtGState<>/Shading<>>>/Subtype/Form>>stream q 1596.459 205.042 -18.227 6.299 re W n q 0 g /GS0 gs 0 6.2988281 6.2988281 0 1587.3457031 205.0419922 cm BX /Sh0 sh EX Q Q endstream endobj 1121 0 obj <>/ExtGState<>/Shading<>>>/Subtype/Form>>stream q 1596.459 186.148 -18.227 6.299 re W n q 0 g /GS0 gs 0 6.2988281 6.2988281 0 1587.3457031 186.1484375 cm BX /Sh0 sh EX Q Q endstream endobj 1122 0 obj <>/ExtGState<>/Shading<>>>/Subtype/Form>>stream q 1520.258 0.151 -1143.741 75.042 re W n q 0 g /GS0 gs -0.0000033 75.0419922 75.0419922 0.0000033 948.3876953 0.1513672 cm BX /Sh0 sh EX Q Q endstream endobj 1123 0 obj <>/ExtGState<>/Shading<>>>/Subtype/Form>>stream q 1596.459 173.55 -18.227 6.299 re W n q 0 g /GS0 gs 0 6.2988281 6.2988281 0 1587.3457031 173.5498047 cm BX /Sh0 sh EX Q Q endstream endobj 1124 0 obj <>/ExtGState<>/Shading<>>>/Subtype/Form>>stream q 1596.459 160.957 -18.227 6.296 re W n q 0 g /GS0 gs 0 6.296875 6.296875 0 1587.3457031 160.9560547 cm BX /Sh0 sh EX Q Q endstream endobj 1125 0 obj <>/ExtGState<>/Shading<>>>/Subtype/Form>>stream q 1596.459 148.359 -18.227 6.3 re W n q 0 g /GS0 gs 0 6.2998047 6.2998047 0 1587.3457031 148.359375 cm BX /Sh0 sh EX Q Q endstream endobj 1126 0 obj <>/ExtGState<>/Shading<>>>/Subtype/Form>>stream q 1596.459 135.763 -18.227 6.299 re W n q 0 g /GS0 gs 0 6.2988281 6.2988281 0 1587.3457031 135.7626953 cm BX /Sh0 sh EX Q Q endstream endobj 1127 0 obj <>/ExtGState<>/Shading<>>>/Subtype/Form>>stream q 1596.459 123.309 -18.227 6.299 re W n q 0 g /GS0 gs 0 6.2988281 6.2988281 0 1587.3457031 123.3095703 cm BX /Sh0 sh EX Q Q endstream endobj 1128 0 obj <>/ExtGState<>/Shading<>>>/Subtype/Form>>stream q 1596.459 110.712 -18.227 6.297 re W n q 0 g /GS0 gs 0 6.2978516 6.2978516 0 1587.3457031 110.7119141 cm BX /Sh0 sh EX Q Q endstream endobj 1129 0 obj <>/ExtGState<>/Shading<>>>/Subtype/Form>>stream q 1596.459 98.114 -18.227 6.301 re W n q 0 g /GS0 gs 0 6.3007812 6.3007812 0 1587.3457031 98.1142578 cm BX /Sh0 sh EX Q Q endstream endobj 1130 0 obj <>/ExtGState<>/Shading<>>>/Subtype/Form>>stream q 1596.459 85.519 -18.227 6.297 re W n q 0 g /GS0 gs 0 6.296875 6.296875 0 1587.3457031 85.5185547 cm BX /Sh0 sh EX Q Q endstream endobj 1131 0 obj <>/ExtGState<>/Shading<>>>/Subtype/Form>>stream q 1596.459 72.923 -18.227 6.299 re W n q 0 g /GS0 gs 0 6.2988281 6.2988281 0 1587.3457031 72.9228516 cm BX /Sh0 sh EX Q Q endstream endobj 1132 0 obj <>/ExtGState<>/Shading<>>>/Subtype/Form>>stream q 1596.459 60.328 -18.227 6.296 re W n q 0 g /GS0 gs 0 6.2958984 6.2958984 0 1587.3457031 60.328125 cm BX /Sh0 sh EX Q Q endstream endobj 1133 0 obj <>/ExtGState<>>>/Subtype/Form>>stream /CS0 cs 1 1 1 scn /GS0 gs 1520.258 190.668 -1143.688 81.645 re f endstream endobj 1134 0 obj <>/ExtGState<>/Shading<>>>/Subtype/Form>>stream q 1596.459 47.73 -18.227 6.298 re W n q 0 g /GS0 gs 0 6.2978516 6.2978516 0 1587.3457031 47.7304687 cm BX /Sh0 sh EX Q Q endstream endobj 1135 0 obj <>/ExtGState<>/Shading<>>>/Subtype/Form>>stream q 1596.459 35.133 -18.227 6.299 re W n q 0 g /GS0 gs 0 6.2988281 6.2988281 0 1587.3457031 35.1328125 cm BX /Sh0 sh EX Q Q endstream endobj 1136 0 obj <>/ExtGState<>/Shading<>>>/Subtype/Form>>stream q 1596.459 22.538 -18.227 6.299 re W n q 0 g /GS0 gs 0 6.2988281 6.2988281 0 1587.3457031 22.5380859 cm BX /Sh0 sh EX Q Q endstream endobj 1137 0 obj <>/ExtGState<>/Shading<>>>/Subtype/Form>>stream q 1596.459 9.943 -18.227 6.296 re W n q 0 g /GS0 gs 0 6.296875 6.296875 0 1587.3457031 9.9423828 cm BX /Sh0 sh EX Q Q endstream endobj 1138 0 obj <>/ExtGState<>/Shading<>>>/Subtype/Form>>stream q 1578.232 0.393 -11.752 272.118 re W n q 0 g /GS0 gs 11.7519531 0 0 -11.7519531 1566.4804687 136.4516602 cm BX /Sh0 sh EX Q Q endstream endobj 1139 0 obj <>/ExtGState<>/Shading<>>>/Subtype/Form>>stream q 1608.211 0 -11.752 272.119 re W n q 0 g /GS0 gs 11.7519531 0 0 -11.7519531 1596.4589844 136.0595703 cm BX /Sh0 sh EX Q Q endstream endobj 1140 0 obj <>/ExtGState<>/Shading<>>>/Subtype/Form>>stream q 1659.034 0.194 -5.886 272.119 re W n q 0 g /GS0 gs 5.8857422 0 0 -5.8857422 1653.1484375 136.2539063 cm BX /Sh0 sh EX Q Q endstream endobj 1141 0 obj <>/ExtGState<>>>/Subtype/Form>>stream /CS0 cs 0 0 0 scn /GS0 gs 1520.258 0.151 -1143.741 75.042 re f endstream endobj 1142 0 obj <>/ExtGState<>>>/Subtype/Form>>stream /CS0 cs 1 1 1 scn /GS0 gs q 1 0 0 1 396.0518 231.4893 cm 0 0 m 0 19.418 -10.367 35.777 -16.382 40.824 c -19.535 40.824 l -19.481 40.824 l -396.052 -95.257 l -16.957 -41.279 l -19.256 -29.034 0 -20.049 0 0 c f Q endstream endobj 1143 0 obj <>/ExtGState<>/Shading<>>>/Subtype/Form>>stream q 396.052 231.489 m 396.052 250.907 385.685 267.267 379.669 272.313 c 376.517 272.313 l 376.57 272.313 l 0 136.232 l 379.095 190.21 l 376.795 202.455 396.052 211.44 396.052 231.489 c W n q 0 g /GS0 gs 23.1758785 -75.8049011 -75.8049011 -23.1758785 189.7539063 215.2138672 cm BX /Sh0 sh EX Q Q endstream endobj 1144 0 obj <>/ExtGState<>>>/Subtype/Form>>stream /CS0 cs 0.008 0.008 0.008 scn /GS0 gs q 1 0 0 1 376.5176 0.1494 cm 0 0 m -0 0.002 l 9.89 0.002 l 14.811 7.786 19.534 19.497 19.534 32.602 c 19.534 56.041 -0.103 60.33 0.101 75.044 c -376.518 136.083 l -0 0.002 l h f Q endstream endobj 1145 0 obj <>/ExtGState<>/Shading<>>>/Subtype/Form>>stream q 376.518 0.149 m 376.517 0.151 l 386.408 0.151 l 391.328 7.936 396.052 19.646 396.052 32.751 c 396.052 56.19 376.415 60.479 376.618 75.193 c 0 136.232 l 376.517 0.151 l h W n q 0 g /GS0 gs -22.0643902 -67.907196 -67.907196 22.0643902 213.6894531 128.1318359 cm BX /Sh0 sh EX Q Q endstream endobj 1146 0 obj <>/ExtGState<>/Shading<>>>/Subtype/Form>>stream q 1658.718 0.151 -5.886 272.117 re W n q 0 g /GS0 gs 5.8857422 0 0 -5.8857422 1652.8320312 136.2099609 cm BX /Sh0 sh EX Q Q endstream endobj 1193 0 obj <> endobj 1156 0 obj <> endobj 1098 0 obj [/ICCBased 1195 0 R] endobj 1194 0 obj <> endobj 1196 0 obj <> endobj 1197 0 obj <> endobj 1198 0 obj <> endobj 1199 0 obj <> endobj 1195 0 obj <>stream HyTSwoɞc [5laQIBHADED2mtFOE.c}08׎8GNg9w߽'0 ֠Jb  2y.-;!KZ ^i"L0- @8(r;q7Ly&Qq4j|9 V)gB0iW8#8wթ8_٥ʨQQj@&A)/g>'Kt;\ ӥ$պFZUn(4T%)뫔0C&Zi8bxEB;Pӓ̹A om?W= x-[0}y)7ta>jT7@tܛ`q2ʀ&6ZLĄ?_yxg)˔zçLU*uSkSeO4?׸c. R ߁-25 S>ӣVd`rn~Y&+`;A4 A9=-tl`;~p Gp| [`L`< "A YA+Cb(R,*T2B- ꇆnQt}MA0alSx k&^>0|>_',G!"F$H:R!zFQd?r 9\A&G rQ hE]a4zBgE#H *B=0HIpp0MxJ$D1D, VĭKĻYdE"EI2EBGt4MzNr!YK ?%_&#(0J:EAiQ(()ӔWT6U@P+!~mD eԴ!hӦh/']B/ҏӿ?a0nhF!X8܌kc&5S6lIa2cKMA!E#ƒdV(kel }}Cq9 N')].uJr  wG xR^[oƜchg`>b$*~ :Eb~,m,-ݖ,Y¬*6X[ݱF=3뭷Y~dó ti zf6~`{v.Ng#{}}jc1X6fm;'_9 r:8q:˜O:ϸ8uJqnv=MmR 4 n3ܣkGݯz=[==<=GTB(/S,]6*-W:#7*e^YDY}UjAyT`#D="b{ų+ʯ:!kJ4Gmt}uC%K7YVfFY .=b?SƕƩȺy چ k5%4m7lqlioZlG+Zz͹mzy]?uuw|"űNwW&e֥ﺱ*|j5kyݭǯg^ykEklD_p߶7Dmo꿻1ml{Mś nLl<9O[$h՛BdҞ@iءG&vVǥ8nRĩ7u\ЭD-u`ֲK³8%yhYѹJº;.! zpg_XQKFAǿ=ȼ:ɹ8ʷ6˶5̵5͵6ζ7ϸ9к<Ѿ?DINU\dlvۀ܊ݖޢ)߯6DScs 2F[p(@Xr4Pm8Ww)Km endstream endobj 1099 0 obj <> endobj 1191 0 obj <> endobj 1192 0 obj <> endobj 1200 0 obj <> endobj 1201 0 obj <> endobj 1202 0 obj <> endobj 1190 0 obj <> endobj 1188 0 obj <> endobj 1189 0 obj <> endobj 1203 0 obj <> endobj 1204 0 obj <> endobj 1205 0 obj <> endobj 1187 0 obj <> endobj 1186 0 obj <> endobj 1185 0 obj <> endobj 1184 0 obj <> endobj 1183 0 obj <> endobj 1182 0 obj <> endobj 1181 0 obj <> endobj 1180 0 obj <> endobj 1179 0 obj <> endobj 1178 0 obj <> endobj 1177 0 obj <> endobj 1176 0 obj <> endobj 1175 0 obj <> endobj 1174 0 obj <> endobj 1173 0 obj <> endobj 1172 0 obj <> endobj 1171 0 obj <> endobj 1170 0 obj <> endobj 1169 0 obj <> endobj 1168 0 obj <> endobj 1166 0 obj <> endobj 1167 0 obj <> endobj 1206 0 obj <> endobj 1207 0 obj <> endobj 1165 0 obj <> endobj 1164 0 obj <> endobj 1163 0 obj <> endobj 1162 0 obj <> endobj 1161 0 obj <> endobj 1160 0 obj <> endobj 1159 0 obj <> endobj 1158 0 obj <> endobj 1157 0 obj <> endobj 1155 0 obj <> endobj 1153 0 obj <> endobj 1154 0 obj <> endobj 1208 0 obj <> endobj 1209 0 obj <> endobj 1210 0 obj <> endobj 1211 0 obj <> endobj 1151 0 obj <> endobj 1152 0 obj <> endobj 1212 0 obj <> endobj 1213 0 obj <> endobj 1109 0 obj <> endobj 1214 0 obj <> endobj 1215 0 obj <> endobj 1216 0 obj <> endobj 1217 0 obj <> endobj 1218 0 obj <> endobj 1219 0 obj <> endobj 1220 0 obj <> endobj 1221 0 obj <> endobj 1093 0 obj <> endobj 1222 0 obj [/View/Design] endobj 1223 0 obj <>>> endobj 1100 0 obj <> endobj 1101 0 obj <> endobj 1102 0 obj <> endobj 1103 0 obj <> endobj 1104 0 obj <> endobj 1105 0 obj <> endobj 1106 0 obj <> endobj 1107 0 obj <> endobj 1108 0 obj <> endobj 1097 0 obj <> endobj 1224 0 obj <> endobj 1225 0 obj <>stream %!PS-Adobe-3.0 %%Creator: Adobe Illustrator(R) 16.0 %%AI8_CreatorVersion: 16.0.0 %%For: (Anders) () %%Title: (Vector Smart Object.ai) %%CreationDate: 12/3/2012 11:08 AM %%Canvassize: 16383 %%BoundingBox: 207 2367 2054 2640 %%HiResBoundingBox: 207.3994 2367.0947 2053.2871 2639.6279 %%DocumentProcessColors: Cyan Magenta Yellow Black %AI5_FileFormat 12.0 %AI12_BuildNumber: 682 %AI3_ColorUsage: Color %AI7_ImageSettings: 0 %%RGBProcessColor: 0 0 0 ([Registration]) %AI3_Cropmarks: 143 1749 2089 2885.0308 %AI3_TemplateBox: 1500.5 1499.5 1500.5 1499.5 %AI3_TileBox: 695.0098 2019.415 1536.8701 2614.4351 %AI3_DocumentPreview: None %AI5_ArtSize: 14400 14400 %AI5_RulerUnits: 2 %AI9_ColorModel: 1 %AI5_ArtFlags: 0 0 0 1 0 0 1 0 0 %AI5_TargetResolution: 800 %AI5_NumLayers: 1 %AI9_OpenToView: -287 3427 0.5 1554 900 26 1 0 83 122 0 0 0 1 1 0 1 1 0 1 %AI5_OpenViewLayers: 7 %%PageOrigin:0 0 %AI7_GridSettings: 72 8 72 8 1 0 0.8 0.8 0.8 0.9 0.9 0.9 %AI9_Flatten: 1 %AI12_CMSettings: 00.MS %%EndComments endstream endobj 1226 0 obj <>stream %%BoundingBox: 207 2367 2054 2640 %%HiResBoundingBox: 207.3994 2367.0947 2053.2871 2639.6279 %AI7_Thumbnail: 128 20 8 %%BeginData: 5994 Hex Bytes %0000330000660000990000CC0033000033330033660033990033CC0033FF %0066000066330066660066990066CC0066FF009900009933009966009999 %0099CC0099FF00CC0000CC3300CC6600CC9900CCCC00CCFF00FF3300FF66 %00FF9900FFCC3300003300333300663300993300CC3300FF333300333333 %3333663333993333CC3333FF3366003366333366663366993366CC3366FF %3399003399333399663399993399CC3399FF33CC0033CC3333CC6633CC99 %33CCCC33CCFF33FF0033FF3333FF6633FF9933FFCC33FFFF660000660033 %6600666600996600CC6600FF6633006633336633666633996633CC6633FF %6666006666336666666666996666CC6666FF669900669933669966669999 %6699CC6699FF66CC0066CC3366CC6666CC9966CCCC66CCFF66FF0066FF33 %66FF6666FF9966FFCC66FFFF9900009900339900669900999900CC9900FF %9933009933339933669933999933CC9933FF996600996633996666996699 %9966CC9966FF9999009999339999669999999999CC9999FF99CC0099CC33 %99CC6699CC9999CCCC99CCFF99FF0099FF3399FF6699FF9999FFCC99FFFF %CC0000CC0033CC0066CC0099CC00CCCC00FFCC3300CC3333CC3366CC3399 %CC33CCCC33FFCC6600CC6633CC6666CC6699CC66CCCC66FFCC9900CC9933 %CC9966CC9999CC99CCCC99FFCCCC00CCCC33CCCC66CCCC99CCCCCCCCCCFF %CCFF00CCFF33CCFF66CCFF99CCFFCCCCFFFFFF0033FF0066FF0099FF00CC %FF3300FF3333FF3366FF3399FF33CCFF33FFFF6600FF6633FF6666FF6699 %FF66CCFF66FFFF9900FF9933FF9966FF9999FF99CCFF99FFFFCC00FFCC33 %FFCC66FFCC99FFCCCCFFCCFFFFFF33FFFF66FFFF99FFFFCC110000001100 %000011111111220000002200000022222222440000004400000044444444 %550000005500000055555555770000007700000077777777880000008800 %000088888888AA000000AA000000AAAAAAAABB000000BB000000BBBBBBBB %DD000000DD000000DDDDDDDDEE000000EE000000EEEEEEEE0000000000FF %00FF0000FFFFFF0000FF00FFFFFF00FFFFFF %524C45FD1BFFC9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CF %C9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CF %C9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CFA8A9A8AFA8A8 %A8FFA8A8A8FD20FFCFFFCAFFCAFFCFCFC9CFC9CFC9CFC9CFC9CFC9CFC9CF %C9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CF %C9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CF %C9CFC9CFA8FFA8FFA8FFA8FFA8FD22FFCAFFFFFFCAFFCFCFC9CFC9CFC9CF %C9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CF %C9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CFC9CF %C9CFC9CFC9CFC9CFC9CFCAFD24FFCFFFCAFFA8FFCAFFA8FFCACFA8CFFD4C %C9C8CFAFFFFFFFA8FFFFFFA8FFA8FFA8FFA8FFA8FFA8FFA8FD12FFCFFFFF %FFCAFFCFFFCAFFC9CEC9CEC9CEC9CEC9CEC9CEC9CEC9CEC9CEC9CEC9CEC9 %CEC9CEC9CEC9CEC9CEC9CEC9CEC9CEC9CEC9CEC9CEC9CEC9CEC9CEC9CEC9 %CEC9CEC9CEC9CEC9CEC9CEC9CEC9CEC9CEC9CEC9CEC9CEC9CEA7FFA8FFA8 %FFA8FFA8A8A8FFA8FFFFFFA8FFFFFFA8FD0CFFCAFFCAFFCAFFCAFFCAFFCA %CFCACFCACAC8C8C7C8C1C8C7C8C1C8C7C8C1C8C7C8C1C8C7C8C1C8C7C8C1 %C8C7C8C1C8C7C8C1C8C7C8C1C8C7C8C1C8C7C8C1C8C7C8C1C8C7C8C1C8C7 %C8C1C8C7C8C1C8C7C8C1C8C7C8C1C8C7C8C1C8C7C8C1C8C7A77DA87DA87D %A87DA852A8A8FFA8FFA8FFA8FFA8FFA8FD08FFA8FFFFFFCFFFFFFFCAFFCA %CFCACFCACAC9CAC9C8C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0 %C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0 %C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C79F7E7D %7D7D527D7D7D527DFFA8FFA8FFA8FFA8FFA8FFA8FD04FFA8FFA8A8A8FFCA %CFA8CAC9CAA7C9A7C9A1C9A1C9A1C9A7C79EC7C0C09EC7C0C09EC7C0C09E %C7C0C09EC7C0C09EC7C0C09EC7C0C09EC7C0C09EC7C0C09EC7C0C09EC7C0 %C09EC7C0C09EC7C0C09EC7C0C09EC7C0C09EC7C0C09EC7C0C09EC7C0C09E %C7C0C0C0A0597D527D527D537D52A8A8FFA8A8A8FFA8A8A8FFA8A8FFFFFF %A8A8FD047DA1CAC9CAC9C9C9CAC9CAC9CAC9CAC9CAC9CAC8C7C0C7C0C7C0 %C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0 %C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0 %C7C0C7C0C7C0C7C0C7C0C7A07EFD057DA87D597DFFA8FFA8FFA8FFA8FFA8 %FFA87D7C27522752272727A1C9C9A1C9C9C9A1C9C9C9A1C9C9C9A1C9C9C9 %C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7 %C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7 %C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0A67EA87DA87DA87DA87DFD0DA8 %FFA87D522727522752A0CAC9CAC9CAC9C9C9CAC9C9C9CAC9C9C9CAC9C7C0 %C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0 %C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0 %C7C0C7C0C7C0C7C0C7C0C7C0C7C0C6A0A9FD0BA8FFA8A8A8FFA8A8A8FFA8 %FD04FFA87D272727A07CA1A0A7A1C9A1C9A1C9C9C9A1C9A7C9A1C8C0C09E %C7C0C19EC7C0C19EC7C0C19EC7C0C19EC7C0C19EC7C0C19EC7C0C19EC7C0 %C19EC7C0C19EC7C0C19EC7C0C19EC7C0C19EC7C0C19EC7C0C19EC7C0C19E %C7C0C19EC7C0C19EC7C0C19EC7C0A07DA87DA87DA87DA852FD0DA8FD07FF %7D7DA0A17CA1A0A1A1A7A1C9A1C9C9CAC9CAC9C9C0C7C0C7C0C7C0C7C0C7 %C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7 %C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7C0C7 %C0C7C0C7C0C7C0C79F7E7D7D7D527D7D7D527DFD0CA8FD0AFFA8A77CA076 %A17CA17CA1A0A1A0A7A1A79FC09E9F9EC19E9E9EC19E9E9EC19E9E9EC19E %9E9EC19E9E9EC19E9E9EC19E9E9EC19E9E9EC19E9E9EC19E9E9EC19E9E9E %C19E9E9EC19E9E9EC19E9E9EC19E9E9EC19E9E9EC19E9E9EC19E9E9EC19E %752E7D2E5227525252277DA8A87DA8A8A87DA8A8A87DA8FD0DFFA8A77CA1 %7CA1A0A1A0A1A0A1A1A0749F749F749F749F749F749F749F749F749F749F %749F749F749F749F749F749F749F749F749F749F749F749F749F749F749F %749F749F749F749F749F749F749F749F749F749F749F749F749F749F4B52 %27525227275252277DFD0CA8FD10FFA1A1767C76A17CA17CA1A07BFD4D74 %5127272127F8272727F87D7DA87DA87DA87DA87DA87DA8FD13FFA1A17CA1 %7CA1A0A1757B747B749F747B749F747B749F747B749F747B749F747B749F %747B749F747B749F747B749F747B749F747B749F747B749F747B749F747B %749F747B749F747B749F747B749F747B749F747B749F4AFD09277DA8A8A8 %7DA8A8A87DFD04A8FD14FFCACAA1A076A07C754A74747450747474507474 %745074747450747474507474745074747450747474507474745074747450 %747474507474745074747450747474507474745074747450747474507474 %7450FD04744BF827F827F827F827F87D7DA87DA87DA87DA87DA8A8FD19FF %CAA1A1757574757475747574757475747574757475747574757475747574 %757475747574757475747574757475747574757475747574757475747574 %75747574757475747574757475747574757475747526FD09277DA87DA8A8 %A87DFD04A8FD1CFFA8FFCAFFA8FFCAFFA8FFCAFFA8FFCAFFA8FFCAFFA8FF %CAFFA8FFCAFFA8FFCAFFA8FFCAFFA8FFCAFFA8FFCAFFA8FFCAFFA8FFCAFF %A8FFCAFFA8FFCAFFA8FFCAFFA8FFCAFFA8FFCAFFA8FFCAFFA8FFCAFFA8FF %A8FFA8FFA8FFA8FD07FFA8FD04FFFF %%EndData endstream endobj 1227 0 obj <>stream %AI12_CompressedDataxUw#ɲ0z{b0lo$VV{9\e)+!2228NY(/9).j٫zn}4w>j9{ y]nw*|F ͣ.Z>.E=fN o._]CS~ 5eeE]Q$Yde#\'-T@5 Rчn(Un߫E23j/I2!V˝djl ?˔1igfW/7fxZYx+|ܕk2n-_g=%z sqdUk^P ?W_pWhZF^>(w0nO|2p^~(>ͳ@55# 3e9I,Z\o<.I9mGU᪆ HvNQuY)k9MePoV3Fiw/j$^ܾjT0 1@Y*ǀE~k Ьo- a2Z\vr*7.xˀUS e8w`h+Gi)DKy {ҮVlNZT2瑳^t0n`trGנQumQ+6MZtXv|gzNk5*!ֶD6|bp a$V!˄,[7/B_y30v˜ோi 70-em3DuLAjÔᝅga0+>.cc{)KE 1R/-bĊEC?):;>04(:!^T$IH,GUdgT`8F<ه**?t`@ UIUEUUU@XbWJįifZftMu$o0ǔᵆ{PJ jHtXa0Em&+bNOXvM|V\4yg>ˀՒbТ僅Ǭblt] uE?`@C($2PsE<2f1"&0tX@ GBE qK;aR\\Z(%D|R Zt^4V0PĊZ0]=BplBa|'MP\dR\(.MK1L20I˜`L8&!LHbLB4 aƣ1HX᐀E ` ơ8Y15_àcaAǜC.1LJRIHQ0/(QQLJIp~ũ1kxG?",R8_"C?aD,b8dDq:M)Q ҨИ~qۄAϓQ_O"!J"D8@Fe<*ZaIx'Tw?a e#\)pVR8/eRn0)"Jg X|Z:x+L&;xT>ʞ2#si(SXF`jrd S‡]1TxI8S›Fi $Llp8;΄_AZ&FxN 9 M q-0W̱Il2Eah$yx A=hWbI~2޽* O ȠblU0@P48-WG aRXRHw$-{x -se"&Q;.`u]sZC$3*1NbSpЌ{xg3˄WFg(gapX:xa"-$J1i:">,ᵡ+dk&E4ND*J1I$l||ƣ֍HDE֍h\rX5,ut0wbqH@| :5Dy$B(sQek kmQL,[]%& [+kQN$O!ݻؠbdK^a*"[ }O`5iT Rdz>E/9Xk߯*,Q g 1`[@9hm&ՔȘg f-bq'k15HC̡`;-G;\w|TO0[Z& ylN0Q_n5zDɏ(ʎIqr[n`Y#{1^` w 65jX` ĊB(&t93"=s>̦bcjSaV [ULjUa1vؒ"cK #P  눌9gabV1a)p!Vbu gjDl "DȄ 6!1_a+k`]%^Yl5fm)vūqo^YbYan7-gk5ehul'K[eX*x3Zi[i+mVzZ'wr6;x,=L<:lw4j-_21ɰҽLVNdU&:J'wW+_gWZkQ)/2E,d*S2r_ʞ' {]9XudoQ>_X:%xAwK3xTdy Xܧ4J< qߘfH!2 K}Cn:Qjr+A!z\T:}6ɥf4^DEM(D^'x\ |b)>/dҩW 1Qw؜^C;H`F\fpݐ̵ !]v+o-("\Ќ9j9$%b TȄ=s5ƢrP{NtւG/? ?JI&Zb$d]F(Ƴ'Oӎ>Ǟl}шOfƼ3=N5O}$y^EfxhW _t{*B?nf/6+ؔ`IMh$QåGDV\Ɍl5  l|Y0e7GQ6¸i:A3>G#o8nM*]1(U4 3s q)>EOI/D2F}.r"11}`J>MLjL0>{wuj.``8X1z)25sDƪN20T qRlIqyC`R!Tƕ"/H(Uxǝ3̈́P1pD,Svz@IO3 䎀pI*p HiD"v1;P=bTΦbE9Qmb7f, ؤ2(%\03DH&򘪂'ҹSEbs6 8d%K `I5l?oM7x1c%TLnD k@Dj)OP!wy5m:"94x$0(dЊ e FE2Y`g͑5QtH3t")(r}Ό~&< I6Uq5:`?b !4(NvxIX9`fh W.Z̥3ݡ ԡ3БM "pJ(@Ìg@?RF"|r8bNF 'Jq8K4 )1x %` -ɓ l#43i3h,ʥnjMDNvZx2!X<+$W*LA=*]>18*B_CHkp=.qLMt0hB(lj]"D R#$Hi&9ЪDFN)BON1hMkeY@5D1|D\2̃BqY+ Z!r̳qc`C` /a̹`b,Eh})IuaJtLaSVS\ؔ/1iIj~$yП#>d 7&8it[a]AoDAnb*&Q= #gg$%𓚏/GEA7̵+rIs-#:v wFw#m@u/.Ƶ싥_;+KL`rGxā$ABZu0EOlù^hQ3$ڡNWR1E}s-9ԍ\vK,%h;oS4)3:E l3fImb퍏?a>}A M,>]*xD2=8U\cLqܥ }.)R&حp܎& dp iT,IMZ>J 9f -!rzRt#N"|eO%VkVοq즃1ᙸc.L-X8 seI”4Z<& aUj<8vP >h !$ xwMJӒ*MI`'pbQk*ai#] +ګŏ1Cdȑ/%VĔK!]Sb L֥I8Vd!N jRIp":JiȲiM)gj0!Ui?D$3T$i麌[6M鄁AsU͖pȪng9ՒĘ_6mB < y"joLuuP N`#D5&dYE-ӱ"Z"^nq ̪`۰5Gn(D[M{?K9"xI <^\2fq˛Zt ztG'wtVGg437ӳr@%`I3Ll "KKwـB#MVI881O1%7TSC3}$"ipRvybXԞ#%K:Xrt|jTɔ("OWPA/|OE?ˠX@a dĤɋ\~^uW}ўusD}xПbNns?dT~=EK>q 0 "_Ŵ2'SG~tߨk.bפ8g4$ <.foHh3QF0̀~ /u1JŎx1vޓ'OJ,W7C^6M+$a? `ؗ: kP`S ,&K)UKɒi8 1h:,(hF,C-gzUNHn7Zq vdoيn ~#hQHɄ3r+>.[LzWͳ)FXR>3$,N3VD'h<%7XP;B DxbH+cV# änIo!en2uX5;G$tU2gI3Ƅ3'ѭοE;HL7.^6c+_yf%w>v-A`"U 7b꫘[_+FiR+iߕ$~FzΠo?&R&B9e:?OM<;SaG(E*鮔e24!+5ڗ/%4h}I[پ$w*\%w;4LKz7p{nt'X\9!mSܙؖ*Dԩ{ UjPu]v?'Ut8דL vׅs׊ԑ-:R;9 \C3إT>߹~J+QrJ4M܅(nTe].60U \Goe %NtF') I(&@%h:{c@{,Nkα&cv;G$Hx.yr'C4yhQ2z⡢;BCS5Zl~$Ik\r:MTQ~hzӤL/ Iv?SIfY_~Sn6v7c4~gtGeH:2'WHe !0>O/n4L |yOөψN'3ț0;﬘W41s3RtLwww_ xW&drWܓxWCw$kؓtGўSd4v$O#/HYܑh?yOHK +Hl9و4w:MР%\jD'PcG#wЌHov!8]I<#1/RmsTv0/"Iv2+ݯ]wRwwە%J=)?'})(]`txNF717ᜤo?wNJ9ٗp#ܗ ϑ}Iw%8 ٗZ߾Ⱦ]?%A4¾I4]űu[l9a1Q+dǦeEg =j聏qU0 `FKz(Mp OYɢ%dj@@QWN*H~LBk>c1JU'}NFM>K%GώT=OhJcC@e_Pu@Hcc-ǎ6A @P`9pEjrN41yPAV{vP՚&vc/V%t'c>Ը#/<}xזtՓǨ/]i B#h$\JoJ¥DK^2 -\jЩ:?BW#8FG5 j+55qƔ'+}z53?@ #. +HL "$Ab#HdD,.BOƾGq#BQ/82 <AbDlCHlH_dp;/AA`0_3?1 $3AD y!d迵MNӍz+)nNfﱒFXxZQ#۪ )CnZcEs^C/uɶ 1tUc#y"&d 3 o}Yu3D3#5@/!wvB3a-x|vQFl.Ҳ3fg4]3 yݾ&Nll X݅"|4[5v'-D|x?z4[N4Ws(Cr,WeUl M%-r_ "jrƬӀ#xEԎS;F?Uw+q2bFgZ ޣ!>mc<:RZX_1ڿΉS5ZeyC2 ,͖dېSnʙ5 RcA ̙$4gRj PriV }J4HUɴqx°t6>Je$L?{LLpM5cI,!$!ؙD,]'2Sq~,5 a5|)br81Q86 $b::ez:_@Zn6% $p$:$L'8eKݒ1#Gl\hA$6Q MT>2ؠP.^&`J% m!̡@a˻XO/3p/2U$t}z\cp+MӅ;_Gq.4J|dc[ xMlW }*3dVr2w_o881_Gq,{Ęt=qO@㒗2w Adc,|p'cEV^"f>by'!M%bX2(b`jfo߿% ꟩[PRZ{%9,ц,wB^S\B9~!9G{' =c Đ' c&0FiGbg',c^/Ǹ4"5?/][.P c< b (\B?a%kQ)]r)[/iV߿7#wN{VW5r%߫u3+z9u.V+I$nݢt ;o޽gf2ORƒo{u^?#_a AV<ۣf<ܿTXwj-wNBeGEoZ]nZJfe@ſF빙F|zhxeEg@Eh:ǃlǁRY(`(q& 8sQٌ1QD<(ぢ/cqV({n,FV0/EN[ }8>' 6][Sx;lNZCeKser6doc-< ARFN\}}).=zv5&sR$3ڶ6rZMm6ͧGuXtOUlǚ^CSˋf],^*[^Kn?=ՙsBmĶ[+K%::l9rށ[9aZư$=gs2wڬbEKfW+cXϺV-Qf贻f] Q-¬LSfS9u9=lj0ryN&9mvhkŲJIaç4`u0H#&=;ʷLZհ0˨Jج5 ʆ*tF+ymWKl,^m l-d Z.s Y͵$\U081(7 ^[sKG3r, JnPUl)ʥj9/wOQzL-7`q݌:߶h6G[rY1rh4Mk3nXJK/-˝Z[Z'jsħO#F۝VS/|,`u/3nQ3!N7oZ1 EٖjY7 ܓh=yjnU1 sXKq\wa?6aоtPmcu/u͈夜$Йg/jl'_D`-` ݍ8!T8i7 |yTQ*ƬULn~pnXQ 8A#䬗Gd:sX\K݊.fӘ䬁32Zenc!-+Ʒb(ǫ[%!5,bc1JSf 2^V@a5Ձ nfcW2()H 4;s+Lf9G `dSxpj P^$_$㨝ZQ&{%-!)63hkNpVL#1BdQ*;;|\l6z:ZC/^Z|/=n׬}Ԙ=}Qs}Е2:یZ(#2FT:ZLNhVnKĨX@JA 5g#&H3˿A^S`9jݬTkiUz\JJaRQUiLfZCPlKR [Y9nvWet#e RJ1 )X+B mL>YDZ9)ah7s`4bYꨖGT|T[jy7 {J3#KcTz>قbk#W@Y䈆1awꧭt^ &w bC6 e 8QӭJI|'ؑ9V_㏫Uy=VPEudUՌY)ӑ jU@?r58ƫmaJIj!ZwPR{oA,aS'RLpp#5EIqHsz}a7<);׆H`aD@-oC51?6N| z,0sshA)wMU1fEvJjv)270ufXfH!ZE9;~ GFZ9 r~Znyk#+6bbVL>6Y y~od|k pЊ&r)3hTe\uan<@@rTl7[ N ݨzm!`d{*۝aKFFE]q^*l01jG#8UitsH*ڕfc(9D:ߥjju`1uZBq4ʯyAP'RcDk5EMGN!< ZV _RNNLT3Nrq ^ZvpMiU_䈴liU$U+`Ll(FT Ϗ`NڥvgWa8C*Q%Ǜ~(hpv:W)p"59q3{gȶa,+ӭ'P Il:پXc[9f OѺGkvGĜ@IufNi8M4L#h5p-n.6׾1%9c!M|"Z34D^]۳th6=t}mnɮEhP];f7RvF=^=lv.NdsЬ'D?MVnfţ>_֎]!#yCyx3; ~ѿk_Wֶп'}q[:֙ڂ7su:} %t3sTk|gXwKv.Wпx;_ez' Co]06CGBqoނ¥4='n-sٙwAx=2;g 5 O+LfO=RUǵ|3aW_{%.+oE&8-U.=Pv Й@^|}3|Qe'V7{рM4Ŗ>9/m͸ʤG4{|^7?6Žhp?VKK۟wUWoeO|މabpt(on0<6ԩ^?V>ݶI_u.^f-3AeQ3UbgMԬjիsvXD˔ݘ1'٭vC^̺ ~{|3lxݝn=fNZك垓=Od糧3^T{GZJ^'ͥ {y2gK})[>Ȗn[SJi0w3nzmvślwIٝ{-hbƝ{X (6GufB_ݟg&Vv~1>h{O{ݓґ6q^;kL\u/'$hѩML\d_& u躲LTdiujnWE{jrFzn&/IY&uqҪ;?*N|lL>i˓wOvkb2oO_/;t38&;3Vejv{Z|yާU}~|ʯN>LUަ>~tzSNn:ۀngn{q1mtiW\L=MlO_黉ss^5_~N*Lff?Jg3zkF3'fVO凙33G˙L^j3eafz73v];[{uEiqsk:ך}Vlc{ Q͍\b:nupvhebf1_+jdu݃Y\_ONn~̟>Jf`yY´y\0υ]caW*/4 wB\>=,t/O|Y\۝(,?_[9m/':ӋGVn;qnB7Ks///7NX]:)v&Nե5X>n'ӛ겳,*kQ]xs;[KE(gj&wp=w>\vsFmeb_]_Y>?/XI}>vՕ@G} 9z5!mPNJׯT8Su{sЍ,ϯo^^:"d)7vOϪ(f3)rQ@x4Yu޿h6CWOԶ~ji7'ttEmcݜ'ۅVhu/~,tuƜw8ݙ}?SՅj8򵵳zW~kV/7P^䵩ZsMv5ߟ]Okٵn^Y&mコ~V[[럕Bi~8Vx:hA{s/?'pg1{'O{/Y;ى=7 ݜUS|K}7ϯJybgp|2ߺN<.^Z[뗧s'Jjx=y>^_l]/> m2̻tsVn鉛ve35voOn_?w?;pX|rvuڼrp>? 7]|,6OixtuvqԼZޘ|>WK*xٛ͗ymk?^1}}>N k秧iP8/Zkxё̓|Xz}/v~0_%|qkyE,VNnn{ʶ*G}Sko+G]תgW!bLߍbaJ׾c~t[zpfzr>q[߬o_Jߘg(H%3m7ͷzKN&[/?Z>ti?o+sOs3?]&lv?y7gH4' GzjT㟤$Ո`da`WKP~QϷ{'Ztq̤GTN7L)gʒf'˟=fw4#%Y]̧0jJrCwb i(iOտ2 ojNTצ!'㑬iHj!%fLV1Xt2. !UFEun־V]Zuk1j#h4AP΍rw!bU629%#J | a5LP JF- Glw% É2>&(_%(_HT PA*_CK QAr2H !c,M!/!Hl װA6H_K űsza1~Fb%lb0m- ȠKŐչ( IM&IF7 5ԏJ:yX\٘].3'v{٥Yu\ݾ<ҶVg۽ʑ5 "Ȥ$uwuI^{-l/:۝e^;̶Y{v/ff_:,AN;]z]t7^kS7{m+~!)ӮgΥܫXoi{#|^t/)U Iϯ<ݘ-vu7^Anj_?׷foHC0܄w3ȵ&.[Sd 6_A7b1(gsޛL3SszZ@懲At-K+y]Z}/c9D^CXӶ(7VF5߳*&~ gΖU*V<,|1Tvkipz[ l{5ׯKJa ٘)m hY㬁@~AK4yg}uc/q+ t#&4fN߭ )w`𭴸,)mfc}~W;qu r!sVYßYFnXqoK}ѯ|kwMuNGΥ"p̧Ixtq(aGԚU˵w٢5EP v󋛓k7dPM"/^~n]iG7[x,6`ݦf|"⢦ō\dWW׎T% mew¹XMloYr>sU' ޹]fZޛq~n/IӍ5>e_8#D[ {yJȪZ= ʾ%RwÎ[36nƍƽ{9t(;elI՛fxnӚ(v^Y񪝪Ié2?k:S=}>hqc~4Mov/sc_n̅\5_/a`B7R! J8c9z'dzahƬs^k-@ g5m!]Eg^/͙R! 3~ř} a2}( o Gx|՝ftY@Z0T-֬䲨ʥp<uA_mO})!ڨU]ж +ѹ\xrԙTO={wmR%/y[3d?Dп.~k\*g 8C)g}94e\yo:Ш>}quoc?1A@C )3%l uHB :W/j"POd+&FGx];@ ݬVpsnABCd+7Żމt3 . 4~Ezc|Kslk h֨I\[`H̔e5V|8^,6+[h x[yqe 'eˬ!r2BP&İ naCC%o|3=5dkB{<7,y#Kj׹ wp+psy}eٙysΦ×t] owٶ<%z@8MA9pY [aSXJOiЍܗ`y[؍RvWEw,ſtxFz{tN<s6`b*s8sxu~{GZܜ!jcsRnSp(q"7\.W`5Ӽz*JX#̸L< JЍ(-E&G[$o^껱VO X6yFMý=EM!<8_Hg`Nf;oJ+؟ #i4qNw@$2ܧ8*d1I=5$xDA\6jn0eNO yKjL 9|'K|1r #A!t8Z[ }^Yܼ{Zor m×7 R6~ϝq ?.Qm"rYWn׶Hg+o:ӛ [?DZ9ܓz> FE8"CeLM~Z۫7յnu{6"v)tgƅV@a r6>Sˣvx 'T"5ʥRCP<'(|jOkpG{se眞\֍l9GɎW8lyc˸-61׫x~j7Jo'pHg֡v)L2v`L9uI3W[9BaQkr BrߊdlE29s3\| ߻EN7( (nphQ}p./g1RōCBٿdw2'v0.uf$?1W[a4 :H_n,]r 뭏24Fƣ6;\)ke.;$hnqNV'A6Ve+{ߝv;$qp\Ku{)f?x1!oe탐_k~a:mhN1u A( ʣpBx8b9 \OUrMטn^6GjsDXsRt%vR.ܖ/B]XAνB -KhW]'yi>3t„Gx2O.tZ))w" bsൻO՚AJGmܫ`SxB4ENK,E|7CW\!`}4NDjؖυ¡/vww,@P{> lOa:1mluy7f0dwM]6˵]va noܨysԹ])cnX3G`du)d3Fò+Š]Qr)؛p3™1MIoa@5a0X݅O`!{{6ʮsA\q4mtBdO#'c)eYȼ1mO-9g /pfH{]iag=-lm)^?zg0M{6ʮpO 1-}և7tFo5pqWx@i ߅ܒ/F~e-^+G5 <JCQŵūq$կd8|_+9Ѐ{R@)/IHY'#H}>XO},J, oL|-][: J›L# r5gR}auNZ%[=|ƒѝбkd=/ݽ+vء~Yq컿ЃEehX.[k`,يT/ZNK2m)9҅QHˇv.۲XZ>"`s@l߬(US1l8G6h{*="+wږ5X1[KB\=i/y쨹\Y*ƧF788ix| O{H͢ p\&lk -{y{O898pUAO.+h +VkQ Wuy~%S%溼:ݺ\@ڃ~qpҿ|7Ēz՛*lGYB|)U[X_YOy_,a;s}FYX=]霎u)}1>9{z/ϯk4{uU>9XX=;U}5|5g>=VxbbճfOpճq}FkXuUFulcݛGX1{iIay/E~F JRqO ;4 }ڔ:t;w 7\ăj*Jtj,~CŚit1[UV}zV3Uֳ::Za/ pXbmo\XϊX M=%{^FlݛT+@%|U/a痎uKn7i*֣ +\?m膅.k8}]XazyIfaOUFtV&85k]e`ūۛ< +[yv@N}bao:Š+=2 kS)䭅fVdr,_&BąٟFg `]5O`c}Xg/2+LI~Yq 6v.!\):6 p*`F %ti=?m ̔k[~/;ki>Y>ktkZ>#hmB`_ˍ7uw0ͯ_H߉T~]੧߹M\Yz}@/h,6 #|Yl)2z:*WzZ[:z 3F_SyCܽ8`=튇a)a\Ó4 AOmtƪ6Žx7jiU/viW痖x}]k1vp4–x6+Y76ݴTnJ[MI;o t5G]-. ϙ&Mdkem{PI 4({zI|?Us()?^/-~OkŅ)m;p~ v$[Y%e΀Db*ͮѪqycImbVkji+>;?}Q0B2M_xBm~?! j\v|igǞR%.._G"ם:½7B=G9 }${;j ȍ]@!A.c" P^0%4~N_qͦ/Qʜq~#[~rsТHzgm/)tVapbi0NPn&= CfQӇBk\ٓtĞepVcpxmWVsKi^߃7779<.:_74|Hh4K 3N(tr_ߚ >JU/Mbnl Gl r ;@Thw&4&(RIPa;c4{GMY<0 x큗oomS)v𞱪}%0B<*TO gM| aӬ3oI/3,of?\K!Z XjMMIo"_Sq/út;30CmQ| rA깵Ikl=ôLL1aXC$nǓ&^{'4PxmbC+莑sOgrac9{>I HؓfGœp'5S-glEO-u:wwZ2[fRق{0[qc5`0EYj'hrOPW7)!b,Ux=qDɃȞ-NWN<-Lz :?:k2Q4m:n #2r`88 ~ ߲|HnlS-_Ӽ 3{XnĞ(!HꗧĻks#<˄ e#ZaR䟯PfУ}tQk>f3;ÍE)3L+t qGd&(I80"%]FT>6Փm(3֏$bKM+Z܆|KA"s 0b;NAOC~3{7 l:0n&d=[bl6 Z,="V_/7˹$ {+9$~EH  ϐf1EIi[#p Q&&`Decp/B/!4KQ~< ~@[#z1w7q}Q<0$Qph\'!HIprNa4Xݡ#@l*rtN ׅ@XĹܨ9J=A-~`|F;|yv(GI7htmenmD 08ca H7;"K1 (VK0 )0PH@<$ ·^$)1}ZQ-d@?%pG9sX:E 1Ƴ\]5liuN/E-%*#3FkR1IkR^br>+LTNT^˗"K ||)t1I9_x-__8~9_x-֒!D9_x-=x|<7~nE|:>Arŷ-w;-;7$X!;u@jrF|FS|NYX=`hu0ZTc#+E|# t%nx*ҭ@WH6tbl8; /F >Ɨ8w9='.n~ -.bGGT8ݨ;sCI1KA[(Ɠr' aNEbɯ;72]zfZ:"m0l)}FkFpQY$c:<Fqj c 2Z "b\'hfGeޯe5L5Q5ԤY0dQ1Ic38{LZDOIS@Nja$*\Qe涂bJђ|{!rM?\O-?H˹'] 0|V".7kZJDGYGۀ@3U7 h +!JݫӐ6)oa2(XiFE vYHrv=w@Tq--ʼn׍x 6.ߓk|}JR+z'd1 ޤ/ztqa0zˢDoJLQf6^f ;.8zfCRF87sP=\ ,S yIo"k ײ@(40SNWDi,!̹Hũ,G,$sRV bVc϶,]f1rTitBL*cX%~%~< 1Ctv ?%:֍u>GŸ = L.p 1klv2px\\(&?Dɘ Ꮰ:2R&G`[lD㯃:`;-=h鄊d/&P$+e)&S$ %/P&R$ 2˕O灲ba0[uyshEy>Ɠ+&wO @Qv])UO(N(L\B'^NnQظ%_?"{{e'}$=i?-؁gر!Dz9 3br:=`/!bFW]8OduxgW$ك#]x~=bxz7( Sx!x(pT=j-]jbfi_+HWvWə g+fewٻ7f +.2XiTb]ݥ56VxUS.[J_\+v@` \oN1煵ӅoV)YXݓSxb;NXXoCem|ڜfbM_NǬ.es|[D/(^ jfw9}#OLT/01 ذst~q7a*.J;q*z},jvF(3jt0z>'umxv{$řD}ۊ/w0nza71G /h]u G\?oǶz5Ud}+N}čpѼz.QaiC_G"IѪLF> S =¾LyI 늫B6V-cN/be}gt%,#ݪ>j>rDn}OVDam ]\a Y<>ZUb%aaؓ/j&TG;i}+Aa&Md'UGlD "]1 hkX\%gg&LߌY=32fa1b[GR89en%q@y0R>GajN'^7HQW_Eʍ1[L BjrMU $R}>:$Ћwoꗞv^]>n06W/W؄LC0]&ez+]͠41|cyaff yCiK?刺oG'B/b\79 dRF<7F P5N3)7xnr5[9My?q5Vx~pYGp >-A[Lkؙ!qƾ:$58;A3䲣4, i>kFRAWXzT+L& MdYbN`'A4G Xq s|Ҟ>$`k/;Rڋލ/@{qݏL(I`#s6?lr lmN+n4e,䣌Ys<'wqb\)p˸gQ.$6%<}4 cw÷߿BFP$,6(?6J 81fWɏ u?Tg^q_\w{(j_ql]}_ @Z;ֽǷ_$/$7} յt :^w<=QY{_]Pj_kվgۭ!~$kAeځedـчcBګb5z-UX;gS=s${{hoQHA.|n?=JB2&#-`t+*x7nh@$rI]1nk; !iIN]y7rQMM08hNTnoba>2]wIEh/B{CXe nZgYKMXbXCuҶf LW#H}+"htl+2sʼhM|yj:ʭnyXo;LsfY# ̝!1\`Ǫ3[+?3Xy90GL*6-qU9iX8ܐLsnNX{YtK0y&x%M Bĵoi{#W;y'l?׍n$H٦>[qJ%,#o\Ǒi;D]CgF gSuo^Ok!kAbE="2|$nE-0xqLT b.#L-6y8Fv_GEvݝ<\N9_6 O~.o0L OZ$<ņw1miIB 6N6+I !UfIb|ukQ^M#4/OEF7Qs =;%óUmBn< WN0' "0ow^`La9uvc6bCa=ܤQfI*I]Q ^x Nv(Sg@DI%kؕ/][y2~d7EGlDИ%)8b~ؤEVŚI1w@ǚʸ*ff%f#Y3FI=)1J`#I,IGtGbw mo-FI"C#%Ѫ$1#*nI"{^)VRD="ۓ=k I)/+$ݨ]V~e" .+ ʴ?tYRb@ ݰݟQ̼֟[=굣Ȯ݉:ՊMKzCf1/<T1.&qax73rCV1@#>)Qr 0HrKnGb6^x%ż0yqaRcmK0<6/x<9 /Wf(f'Yko|a?y7R??۪ ȃaKچXj*[Wbn.ysk!_/6 Phgٱ>OˏxPxvH-jã_ EwYm%۞vDtO2'^zG@v-/4DLt }%I]OdY3_>|Sӣz*1iGF#fLQ~?mڢx2C=9Y+J9C?%E&YoH@~A%]?ߋkFk4C0k乀.ےGvl7Ks2@{t>SN i|QY=T߅c>HѹIH;͋Tߍ*s~⾧|Th*f=s_98oN^B'c4ֱݫ'?> :#]~^>ۨ]tW_iu{{= Q8Z낰tͷ87f+_KEHKPm~Q4y㕪t}&R}f=Mtԫ+sc^ܚ\[娮ĞxYI>IJ j:[Ibi̦e5X4k'󝟵V-+OKm0 Lqpru}oGwb*mciO' 7&iCW*t SdRqT]ozJzu} IՆZ}ѿ._[q[|?A>@)eDV^c~''spҢǜXa7*jrA ͻB'8BY4魝l-ou8J7ngy6Xs.k˟w ,bU(֙H sn2x{9uBˀbhd}ۉaGl7"Þ^~v7Qh`r!.HC&132P?<ͻLEL~O6M~?5n:wh ' wSf\Qd/6'rꛤ7ϟ Ћm -5as2S8 @Pu%Pǹih\߁I҆lӌD 1)΄?lƇY61lL&&)XdZc$٘;ql̡gL c VDqZ=۩ŵN6+|9ǣFnwqU:1~_}u4V3/XA0Ii4 =IO8L9cMyOYض6Ɉmd:pr' 3|0r;=FZp g܆>6,RBq{Ҳ1_$Ag}q,H:R_G^{NjP[Vɸ{U'[0jϹK##_Ymxo -YY]wدrp3dRFWqad}+dqf_r<% 0W26|(6h@] 0rऎwA ^otF~~wfgr ;k1-qݹ誺H~:krba\>vv;$A>I=+!z _{?{S/jœP` Kr~/vy!.E_s&,P;v&A(~<,̠yY\<`c/hDσ2c0V}'A8 <錄J <م7ٻK1D.oPZ(\8h NH5 "XlЄqFE?{CE URaDHKE#Qs?lCǃ1aS(0`HH'Gd?0QYn0YHJZ qqaHtld> U*-x|*{5| 0(-l\1PJTBA%* KR Ai%1@JrN`J+)lRky )N7a!vW㬈,p@"2Cb.q|%2x Ē\?7Vp^i;&^jjVK'rWԸ./ٕCO@V[j\c6LOY=Πڠy ?QAB𮵵"ՠj潪:z)Ef+!-;EN\Z{ ϯWMxp{)z]@r$5zY@[и +-H^σnWШDχO jg돳ՎZ0JBi?k!Fӫ {n~ f6*WN~`ݭËY&OK^%kRw 7h1v|9QПW '}L=c x\NIȶKIgord5 yz/*څO˥җI7 FB 8\3PZ+vFŋ3`|hv$^>\3 zW9%gSEJhˏ6\neJ Й RZX/ &U%4! qZIL[y,iE$1!4c|^\ ߄@ ^|VD%CYIL֍"DzĄ yML Ğxg-Bt Tޥ :dﻖm-,[oAmq]mWE,!-FIgq@mx~YǵJrazZD?PUGH=tLZ4\ .s"Mp;٢5etiLĽxNE4r,iPFr hN}=;7. ]٢ qJY:M˛Rpw[GNv[[s-yl[ ;2;y4[=|oEװ$ٜ5 C?{-7ՆJX|}3UFmF}<|?Zjq* 'vgP^wSS߰Co{~wnadu ~ĩ+q!EMdӐdUK_bʔ, Z kdV3u R6ldA"!`"N!nf(an.X8&]] umP!m8hfbV\LD۩}^GrS۩.CC0Կ}x &кXS꟢(<%`` ʺ( O6sp>U ci1ۢ.릠k2Uo56^o!G(DY,Ad3 &j*xU ¿s̺ScIpǚ:Qt E B, d%K,UL$)}f*`p\ YЌV&^ vD M φ΢:eJjSHFvmJ]&Xh`J*Sdз)hN(U7ñ 떟 /g THJe}I*r&X"8d` LaP1I,t.x*L! ZP @\2Ue mJb R߂<" 7v(Qn*(ppN'o[zn WU\TKqf"zKWI1(xNtD59 țt a l%Z,!kՀ2Lioeι `&K2("@uJ w`i*':2-54SDm%S@?.)S!A{$Qc):U[e V^(XQj0F5U0S4`)"A d(g VC ihP$R'< 6S JM9 TSD 6%P(? Qb+VDn2X iXz0.@{ƂΌ fCtLPȪ"bFBָ,֢ u$lA6پI*-N8њ:Zi (hW"W%h $)hfaN3抮(n| a4SlM\B M G1aC&{D 4/E HBZI「d\} Eh:uGo7 S^6x5Fr9q/4 `#L\-B%ɀ(l*Ѓ@T ސpc|0 $>.ꀁKQu/ʃj4jh>E!TYDPU CT&+UBD3z | eD!QV{hLy=VDc8&t%2{w r"FW)6:oG"!RɖdV M/rAEt҉2:cG`dZu>2:teyEeEԂ,[V# fH_d$Qapce|!6:oH2q>"puj&8ge[@cActhEdFIӁQJQ,}t<"2;\YH$ $6G`z]rf $GK8tD G\zRUN=ʴ4.= xz$lted,ԫ`,K^"YՌaEpޚ8yCSa> zs/$U,Uf" 0ѦCOu7  @MtD`e(<@%[WGBêaC6fXia~!&8yTep!0WBy I1 ̱֔`Ή&iR6ęb?GN,32>jDa`;QA>-"K,v,`I+>"*ߒcl 2 =_C+)#4`WT` نѦB}u{6f1OؾW12.i*.sx%ABN_H OR "xK* fH_4:,bʂ'6퟇E ˠ*pD\W=txd<fL`)V MŲ'0cJb3OX,4G,KSر,Y]ӫ;d !VC0Q d5e$Z /tL=^ nx 50+AS/{Wo*7xh7~{S-#n3jH endstream endobj 1148 0 obj [/ICCBased 1195 0 R] endobj 5 0 obj <> endobj 141 0 obj <> endobj 277 0 obj <> endobj 413 0 obj <> endobj 549 0 obj <> endobj 685 0 obj <> endobj 821 0 obj <> endobj 957 0 obj <> endobj 1086 0 obj [/View/Design] endobj 1087 0 obj <>>> endobj 950 0 obj [/View/Design] endobj 951 0 obj <>>> endobj 814 0 obj [/View/Design] endobj 815 0 obj <>>> endobj 678 0 obj [/View/Design] endobj 679 0 obj <>>> endobj 542 0 obj [/View/Design] endobj 543 0 obj <>>> endobj 406 0 obj [/View/Design] endobj 407 0 obj <>>> endobj 270 0 obj [/View/Design] endobj 271 0 obj <>>> endobj 132 0 obj [/View/Design] endobj 133 0 obj <>>> endobj 1094 0 obj [1093 0 R] endobj 1228 0 obj <> endobj xref 0 1229 0000000004 65535 f 0000000016 00000 n 0000000277 00000 n 0000010218 00000 n 0000000006 00000 f 0000089956 00000 n 0000000008 00000 f 0000010269 00000 n 0000000009 00000 f 0000000010 00000 f 0000000011 00000 f 0000000012 00000 f 0000000013 00000 f 0000000014 00000 f 0000000015 00000 f 0000000016 00000 f 0000000017 00000 f 0000000018 00000 f 0000000019 00000 f 0000000020 00000 f 0000000021 00000 f 0000000022 00000 f 0000000023 00000 f 0000000024 00000 f 0000000025 00000 f 0000000026 00000 f 0000000027 00000 f 0000000028 00000 f 0000000029 00000 f 0000000030 00000 f 0000000031 00000 f 0000000032 00000 f 0000000033 00000 f 0000000034 00000 f 0000000035 00000 f 0000000036 00000 f 0000000037 00000 f 0000000038 00000 f 0000000039 00000 f 0000000040 00000 f 0000000041 00000 f 0000000042 00000 f 0000000043 00000 f 0000000044 00000 f 0000000045 00000 f 0000000046 00000 f 0000000047 00000 f 0000000048 00000 f 0000000049 00000 f 0000000050 00000 f 0000000051 00000 f 0000000052 00000 f 0000000053 00000 f 0000000054 00000 f 0000000055 00000 f 0000000056 00000 f 0000000057 00000 f 0000000058 00000 f 0000000059 00000 f 0000000060 00000 f 0000000061 00000 f 0000000062 00000 f 0000000063 00000 f 0000000064 00000 f 0000000065 00000 f 0000000066 00000 f 0000000067 00000 f 0000000068 00000 f 0000000069 00000 f 0000000070 00000 f 0000000071 00000 f 0000000072 00000 f 0000000073 00000 f 0000000074 00000 f 0000000075 00000 f 0000000076 00000 f 0000000077 00000 f 0000000078 00000 f 0000000079 00000 f 0000000080 00000 f 0000000081 00000 f 0000000082 00000 f 0000000083 00000 f 0000000084 00000 f 0000000085 00000 f 0000000086 00000 f 0000000087 00000 f 0000000088 00000 f 0000000089 00000 f 0000000090 00000 f 0000000091 00000 f 0000000092 00000 f 0000000093 00000 f 0000000094 00000 f 0000000095 00000 f 0000000096 00000 f 0000000097 00000 f 0000000098 00000 f 0000000099 00000 f 0000000100 00000 f 0000000101 00000 f 0000000102 00000 f 0000000103 00000 f 0000000104 00000 f 0000000105 00000 f 0000000106 00000 f 0000000107 00000 f 0000000108 00000 f 0000000109 00000 f 0000000110 00000 f 0000000111 00000 f 0000000112 00000 f 0000000113 00000 f 0000000114 00000 f 0000000115 00000 f 0000000116 00000 f 0000000117 00000 f 0000000118 00000 f 0000000119 00000 f 0000000120 00000 f 0000000121 00000 f 0000000122 00000 f 0000000123 00000 f 0000000124 00000 f 0000000125 00000 f 0000000126 00000 f 0000000127 00000 f 0000000128 00000 f 0000000129 00000 f 0000000130 00000 f 0000000131 00000 f 0000000134 00000 f 0000091408 00000 n 0000091440 00000 n 0000000135 00000 f 0000000136 00000 f 0000000137 00000 f 0000000138 00000 f 0000000139 00000 f 0000000140 00000 f 0000000142 00000 f 0000090032 00000 n 0000000143 00000 f 0000000144 00000 f 0000000145 00000 f 0000000146 00000 f 0000000147 00000 f 0000000148 00000 f 0000000149 00000 f 0000000150 00000 f 0000000151 00000 f 0000000152 00000 f 0000000153 00000 f 0000000154 00000 f 0000000155 00000 f 0000000156 00000 f 0000000157 00000 f 0000000158 00000 f 0000000159 00000 f 0000000160 00000 f 0000000161 00000 f 0000000162 00000 f 0000000163 00000 f 0000000164 00000 f 0000000165 00000 f 0000000166 00000 f 0000000167 00000 f 0000000168 00000 f 0000000169 00000 f 0000000170 00000 f 0000000171 00000 f 0000000172 00000 f 0000000173 00000 f 0000000174 00000 f 0000000175 00000 f 0000000176 00000 f 0000000177 00000 f 0000000178 00000 f 0000000179 00000 f 0000000180 00000 f 0000000181 00000 f 0000000182 00000 f 0000000183 00000 f 0000000184 00000 f 0000000185 00000 f 0000000186 00000 f 0000000187 00000 f 0000000188 00000 f 0000000189 00000 f 0000000190 00000 f 0000000191 00000 f 0000000192 00000 f 0000000193 00000 f 0000000194 00000 f 0000000195 00000 f 0000000196 00000 f 0000000197 00000 f 0000000198 00000 f 0000000199 00000 f 0000000200 00000 f 0000000201 00000 f 0000000202 00000 f 0000000203 00000 f 0000000204 00000 f 0000000205 00000 f 0000000206 00000 f 0000000207 00000 f 0000000208 00000 f 0000000209 00000 f 0000000210 00000 f 0000000211 00000 f 0000000212 00000 f 0000000213 00000 f 0000000214 00000 f 0000000215 00000 f 0000000216 00000 f 0000000217 00000 f 0000000218 00000 f 0000000219 00000 f 0000000220 00000 f 0000000221 00000 f 0000000222 00000 f 0000000223 00000 f 0000000224 00000 f 0000000225 00000 f 0000000226 00000 f 0000000227 00000 f 0000000228 00000 f 0000000229 00000 f 0000000230 00000 f 0000000231 00000 f 0000000232 00000 f 0000000233 00000 f 0000000234 00000 f 0000000235 00000 f 0000000236 00000 f 0000000237 00000 f 0000000238 00000 f 0000000239 00000 f 0000000240 00000 f 0000000241 00000 f 0000000242 00000 f 0000000243 00000 f 0000000244 00000 f 0000000245 00000 f 0000000246 00000 f 0000000247 00000 f 0000000248 00000 f 0000000249 00000 f 0000000250 00000 f 0000000251 00000 f 0000000252 00000 f 0000000253 00000 f 0000000254 00000 f 0000000255 00000 f 0000000256 00000 f 0000000257 00000 f 0000000258 00000 f 0000000259 00000 f 0000000260 00000 f 0000000261 00000 f 0000000262 00000 f 0000000263 00000 f 0000000264 00000 f 0000000265 00000 f 0000000266 00000 f 0000000267 00000 f 0000000268 00000 f 0000000269 00000 f 0000000272 00000 f 0000091290 00000 n 0000091322 00000 n 0000000273 00000 f 0000000274 00000 f 0000000275 00000 f 0000000276 00000 f 0000000278 00000 f 0000090110 00000 n 0000000279 00000 f 0000000280 00000 f 0000000281 00000 f 0000000282 00000 f 0000000283 00000 f 0000000284 00000 f 0000000285 00000 f 0000000286 00000 f 0000000287 00000 f 0000000288 00000 f 0000000289 00000 f 0000000290 00000 f 0000000291 00000 f 0000000292 00000 f 0000000293 00000 f 0000000294 00000 f 0000000295 00000 f 0000000296 00000 f 0000000297 00000 f 0000000298 00000 f 0000000299 00000 f 0000000300 00000 f 0000000301 00000 f 0000000302 00000 f 0000000303 00000 f 0000000304 00000 f 0000000305 00000 f 0000000306 00000 f 0000000307 00000 f 0000000308 00000 f 0000000309 00000 f 0000000310 00000 f 0000000311 00000 f 0000000312 00000 f 0000000313 00000 f 0000000314 00000 f 0000000315 00000 f 0000000316 00000 f 0000000317 00000 f 0000000318 00000 f 0000000319 00000 f 0000000320 00000 f 0000000321 00000 f 0000000322 00000 f 0000000323 00000 f 0000000324 00000 f 0000000325 00000 f 0000000326 00000 f 0000000327 00000 f 0000000328 00000 f 0000000329 00000 f 0000000330 00000 f 0000000331 00000 f 0000000332 00000 f 0000000333 00000 f 0000000334 00000 f 0000000335 00000 f 0000000336 00000 f 0000000337 00000 f 0000000338 00000 f 0000000339 00000 f 0000000340 00000 f 0000000341 00000 f 0000000342 00000 f 0000000343 00000 f 0000000344 00000 f 0000000345 00000 f 0000000346 00000 f 0000000347 00000 f 0000000348 00000 f 0000000349 00000 f 0000000350 00000 f 0000000351 00000 f 0000000352 00000 f 0000000353 00000 f 0000000354 00000 f 0000000355 00000 f 0000000356 00000 f 0000000357 00000 f 0000000358 00000 f 0000000359 00000 f 0000000360 00000 f 0000000361 00000 f 0000000362 00000 f 0000000363 00000 f 0000000364 00000 f 0000000365 00000 f 0000000366 00000 f 0000000367 00000 f 0000000368 00000 f 0000000369 00000 f 0000000370 00000 f 0000000371 00000 f 0000000372 00000 f 0000000373 00000 f 0000000374 00000 f 0000000375 00000 f 0000000376 00000 f 0000000377 00000 f 0000000378 00000 f 0000000379 00000 f 0000000380 00000 f 0000000381 00000 f 0000000382 00000 f 0000000383 00000 f 0000000384 00000 f 0000000385 00000 f 0000000386 00000 f 0000000387 00000 f 0000000388 00000 f 0000000389 00000 f 0000000390 00000 f 0000000391 00000 f 0000000392 00000 f 0000000393 00000 f 0000000394 00000 f 0000000395 00000 f 0000000396 00000 f 0000000397 00000 f 0000000398 00000 f 0000000399 00000 f 0000000400 00000 f 0000000401 00000 f 0000000402 00000 f 0000000403 00000 f 0000000404 00000 f 0000000405 00000 f 0000000408 00000 f 0000091172 00000 n 0000091204 00000 n 0000000409 00000 f 0000000410 00000 f 0000000411 00000 f 0000000412 00000 f 0000000414 00000 f 0000090188 00000 n 0000000415 00000 f 0000000416 00000 f 0000000417 00000 f 0000000418 00000 f 0000000419 00000 f 0000000420 00000 f 0000000421 00000 f 0000000422 00000 f 0000000423 00000 f 0000000424 00000 f 0000000425 00000 f 0000000426 00000 f 0000000427 00000 f 0000000428 00000 f 0000000429 00000 f 0000000430 00000 f 0000000431 00000 f 0000000432 00000 f 0000000433 00000 f 0000000434 00000 f 0000000435 00000 f 0000000436 00000 f 0000000437 00000 f 0000000438 00000 f 0000000439 00000 f 0000000440 00000 f 0000000441 00000 f 0000000442 00000 f 0000000443 00000 f 0000000444 00000 f 0000000445 00000 f 0000000446 00000 f 0000000447 00000 f 0000000448 00000 f 0000000449 00000 f 0000000450 00000 f 0000000451 00000 f 0000000452 00000 f 0000000453 00000 f 0000000454 00000 f 0000000455 00000 f 0000000456 00000 f 0000000457 00000 f 0000000458 00000 f 0000000459 00000 f 0000000460 00000 f 0000000461 00000 f 0000000462 00000 f 0000000463 00000 f 0000000464 00000 f 0000000465 00000 f 0000000466 00000 f 0000000467 00000 f 0000000468 00000 f 0000000469 00000 f 0000000470 00000 f 0000000471 00000 f 0000000472 00000 f 0000000473 00000 f 0000000474 00000 f 0000000475 00000 f 0000000476 00000 f 0000000477 00000 f 0000000478 00000 f 0000000479 00000 f 0000000480 00000 f 0000000481 00000 f 0000000482 00000 f 0000000483 00000 f 0000000484 00000 f 0000000485 00000 f 0000000486 00000 f 0000000487 00000 f 0000000488 00000 f 0000000489 00000 f 0000000490 00000 f 0000000491 00000 f 0000000492 00000 f 0000000493 00000 f 0000000494 00000 f 0000000495 00000 f 0000000496 00000 f 0000000497 00000 f 0000000498 00000 f 0000000499 00000 f 0000000500 00000 f 0000000501 00000 f 0000000502 00000 f 0000000503 00000 f 0000000504 00000 f 0000000505 00000 f 0000000506 00000 f 0000000507 00000 f 0000000508 00000 f 0000000509 00000 f 0000000510 00000 f 0000000511 00000 f 0000000512 00000 f 0000000513 00000 f 0000000514 00000 f 0000000515 00000 f 0000000516 00000 f 0000000517 00000 f 0000000518 00000 f 0000000519 00000 f 0000000520 00000 f 0000000521 00000 f 0000000522 00000 f 0000000523 00000 f 0000000524 00000 f 0000000525 00000 f 0000000526 00000 f 0000000527 00000 f 0000000528 00000 f 0000000529 00000 f 0000000530 00000 f 0000000531 00000 f 0000000532 00000 f 0000000533 00000 f 0000000534 00000 f 0000000535 00000 f 0000000536 00000 f 0000000537 00000 f 0000000538 00000 f 0000000539 00000 f 0000000540 00000 f 0000000541 00000 f 0000000544 00000 f 0000091054 00000 n 0000091086 00000 n 0000000545 00000 f 0000000546 00000 f 0000000547 00000 f 0000000548 00000 f 0000000550 00000 f 0000090266 00000 n 0000000551 00000 f 0000000552 00000 f 0000000553 00000 f 0000000554 00000 f 0000000555 00000 f 0000000556 00000 f 0000000557 00000 f 0000000558 00000 f 0000000559 00000 f 0000000560 00000 f 0000000561 00000 f 0000000562 00000 f 0000000563 00000 f 0000000564 00000 f 0000000565 00000 f 0000000566 00000 f 0000000567 00000 f 0000000568 00000 f 0000000569 00000 f 0000000570 00000 f 0000000571 00000 f 0000000572 00000 f 0000000573 00000 f 0000000574 00000 f 0000000575 00000 f 0000000576 00000 f 0000000577 00000 f 0000000578 00000 f 0000000579 00000 f 0000000580 00000 f 0000000581 00000 f 0000000582 00000 f 0000000583 00000 f 0000000584 00000 f 0000000585 00000 f 0000000586 00000 f 0000000587 00000 f 0000000588 00000 f 0000000589 00000 f 0000000590 00000 f 0000000591 00000 f 0000000592 00000 f 0000000593 00000 f 0000000594 00000 f 0000000595 00000 f 0000000596 00000 f 0000000597 00000 f 0000000598 00000 f 0000000599 00000 f 0000000600 00000 f 0000000601 00000 f 0000000602 00000 f 0000000603 00000 f 0000000604 00000 f 0000000605 00000 f 0000000606 00000 f 0000000607 00000 f 0000000608 00000 f 0000000609 00000 f 0000000610 00000 f 0000000611 00000 f 0000000612 00000 f 0000000613 00000 f 0000000614 00000 f 0000000615 00000 f 0000000616 00000 f 0000000617 00000 f 0000000618 00000 f 0000000619 00000 f 0000000620 00000 f 0000000621 00000 f 0000000622 00000 f 0000000623 00000 f 0000000624 00000 f 0000000625 00000 f 0000000626 00000 f 0000000627 00000 f 0000000628 00000 f 0000000629 00000 f 0000000630 00000 f 0000000631 00000 f 0000000632 00000 f 0000000633 00000 f 0000000634 00000 f 0000000635 00000 f 0000000636 00000 f 0000000637 00000 f 0000000638 00000 f 0000000639 00000 f 0000000640 00000 f 0000000641 00000 f 0000000642 00000 f 0000000643 00000 f 0000000644 00000 f 0000000645 00000 f 0000000646 00000 f 0000000647 00000 f 0000000648 00000 f 0000000649 00000 f 0000000650 00000 f 0000000651 00000 f 0000000652 00000 f 0000000653 00000 f 0000000654 00000 f 0000000655 00000 f 0000000656 00000 f 0000000657 00000 f 0000000658 00000 f 0000000659 00000 f 0000000660 00000 f 0000000661 00000 f 0000000662 00000 f 0000000663 00000 f 0000000664 00000 f 0000000665 00000 f 0000000666 00000 f 0000000667 00000 f 0000000668 00000 f 0000000669 00000 f 0000000670 00000 f 0000000671 00000 f 0000000672 00000 f 0000000673 00000 f 0000000674 00000 f 0000000675 00000 f 0000000676 00000 f 0000000677 00000 f 0000000680 00000 f 0000090936 00000 n 0000090968 00000 n 0000000681 00000 f 0000000682 00000 f 0000000683 00000 f 0000000684 00000 f 0000000686 00000 f 0000090344 00000 n 0000000687 00000 f 0000000688 00000 f 0000000689 00000 f 0000000690 00000 f 0000000691 00000 f 0000000692 00000 f 0000000693 00000 f 0000000694 00000 f 0000000695 00000 f 0000000696 00000 f 0000000697 00000 f 0000000698 00000 f 0000000699 00000 f 0000000700 00000 f 0000000701 00000 f 0000000702 00000 f 0000000703 00000 f 0000000704 00000 f 0000000705 00000 f 0000000706 00000 f 0000000707 00000 f 0000000708 00000 f 0000000709 00000 f 0000000710 00000 f 0000000711 00000 f 0000000712 00000 f 0000000713 00000 f 0000000714 00000 f 0000000715 00000 f 0000000716 00000 f 0000000717 00000 f 0000000718 00000 f 0000000719 00000 f 0000000720 00000 f 0000000721 00000 f 0000000722 00000 f 0000000723 00000 f 0000000724 00000 f 0000000725 00000 f 0000000726 00000 f 0000000727 00000 f 0000000728 00000 f 0000000729 00000 f 0000000730 00000 f 0000000731 00000 f 0000000732 00000 f 0000000733 00000 f 0000000734 00000 f 0000000735 00000 f 0000000736 00000 f 0000000737 00000 f 0000000738 00000 f 0000000739 00000 f 0000000740 00000 f 0000000741 00000 f 0000000742 00000 f 0000000743 00000 f 0000000744 00000 f 0000000745 00000 f 0000000746 00000 f 0000000747 00000 f 0000000748 00000 f 0000000749 00000 f 0000000750 00000 f 0000000751 00000 f 0000000752 00000 f 0000000753 00000 f 0000000754 00000 f 0000000755 00000 f 0000000756 00000 f 0000000757 00000 f 0000000758 00000 f 0000000759 00000 f 0000000760 00000 f 0000000761 00000 f 0000000762 00000 f 0000000763 00000 f 0000000764 00000 f 0000000765 00000 f 0000000766 00000 f 0000000767 00000 f 0000000768 00000 f 0000000769 00000 f 0000000770 00000 f 0000000771 00000 f 0000000772 00000 f 0000000773 00000 f 0000000774 00000 f 0000000775 00000 f 0000000776 00000 f 0000000777 00000 f 0000000778 00000 f 0000000779 00000 f 0000000780 00000 f 0000000781 00000 f 0000000782 00000 f 0000000783 00000 f 0000000784 00000 f 0000000785 00000 f 0000000786 00000 f 0000000787 00000 f 0000000788 00000 f 0000000789 00000 f 0000000790 00000 f 0000000791 00000 f 0000000792 00000 f 0000000793 00000 f 0000000794 00000 f 0000000795 00000 f 0000000796 00000 f 0000000797 00000 f 0000000798 00000 f 0000000799 00000 f 0000000800 00000 f 0000000801 00000 f 0000000802 00000 f 0000000803 00000 f 0000000804 00000 f 0000000805 00000 f 0000000806 00000 f 0000000807 00000 f 0000000808 00000 f 0000000809 00000 f 0000000810 00000 f 0000000811 00000 f 0000000812 00000 f 0000000813 00000 f 0000000816 00000 f 0000090818 00000 n 0000090850 00000 n 0000000817 00000 f 0000000818 00000 f 0000000819 00000 f 0000000820 00000 f 0000000822 00000 f 0000090422 00000 n 0000000823 00000 f 0000000824 00000 f 0000000825 00000 f 0000000826 00000 f 0000000827 00000 f 0000000828 00000 f 0000000829 00000 f 0000000830 00000 f 0000000831 00000 f 0000000832 00000 f 0000000833 00000 f 0000000834 00000 f 0000000835 00000 f 0000000836 00000 f 0000000837 00000 f 0000000838 00000 f 0000000839 00000 f 0000000840 00000 f 0000000841 00000 f 0000000842 00000 f 0000000843 00000 f 0000000844 00000 f 0000000845 00000 f 0000000846 00000 f 0000000847 00000 f 0000000848 00000 f 0000000849 00000 f 0000000850 00000 f 0000000851 00000 f 0000000852 00000 f 0000000853 00000 f 0000000854 00000 f 0000000855 00000 f 0000000856 00000 f 0000000857 00000 f 0000000858 00000 f 0000000859 00000 f 0000000860 00000 f 0000000861 00000 f 0000000862 00000 f 0000000863 00000 f 0000000864 00000 f 0000000865 00000 f 0000000866 00000 f 0000000867 00000 f 0000000868 00000 f 0000000869 00000 f 0000000870 00000 f 0000000871 00000 f 0000000872 00000 f 0000000873 00000 f 0000000874 00000 f 0000000875 00000 f 0000000876 00000 f 0000000877 00000 f 0000000878 00000 f 0000000879 00000 f 0000000880 00000 f 0000000881 00000 f 0000000882 00000 f 0000000883 00000 f 0000000884 00000 f 0000000885 00000 f 0000000886 00000 f 0000000887 00000 f 0000000888 00000 f 0000000889 00000 f 0000000890 00000 f 0000000891 00000 f 0000000892 00000 f 0000000893 00000 f 0000000894 00000 f 0000000895 00000 f 0000000896 00000 f 0000000897 00000 f 0000000898 00000 f 0000000899 00000 f 0000000900 00000 f 0000000901 00000 f 0000000902 00000 f 0000000903 00000 f 0000000904 00000 f 0000000905 00000 f 0000000906 00000 f 0000000907 00000 f 0000000908 00000 f 0000000909 00000 f 0000000910 00000 f 0000000911 00000 f 0000000912 00000 f 0000000913 00000 f 0000000914 00000 f 0000000915 00000 f 0000000916 00000 f 0000000917 00000 f 0000000918 00000 f 0000000919 00000 f 0000000920 00000 f 0000000921 00000 f 0000000922 00000 f 0000000923 00000 f 0000000924 00000 f 0000000925 00000 f 0000000926 00000 f 0000000927 00000 f 0000000928 00000 f 0000000929 00000 f 0000000930 00000 f 0000000931 00000 f 0000000932 00000 f 0000000933 00000 f 0000000934 00000 f 0000000935 00000 f 0000000936 00000 f 0000000937 00000 f 0000000938 00000 f 0000000939 00000 f 0000000940 00000 f 0000000941 00000 f 0000000942 00000 f 0000000943 00000 f 0000000944 00000 f 0000000945 00000 f 0000000946 00000 f 0000000947 00000 f 0000000948 00000 f 0000000949 00000 f 0000000952 00000 f 0000090700 00000 n 0000090732 00000 n 0000000953 00000 f 0000000954 00000 f 0000000955 00000 f 0000000956 00000 f 0000000000 00000 f 0000090500 00000 n 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000090580 00000 n 0000090613 00000 n 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000037067 00000 n 0000091526 00000 n 0000011258 00000 n 0000012078 00000 n 0000038402 00000 n 0000027634 00000 n 0000030938 00000 n 0000037268 00000 n 0000037395 00000 n 0000037520 00000 n 0000037647 00000 n 0000037772 00000 n 0000037897 00000 n 0000038022 00000 n 0000038149 00000 n 0000038276 00000 n 0000035840 00000 n 0000013231 00000 n 0000013864 00000 n 0000014252 00000 n 0000014622 00000 n 0000014989 00000 n 0000015358 00000 n 0000015727 00000 n 0000016093 00000 n 0000016462 00000 n 0000016831 00000 n 0000017196 00000 n 0000017565 00000 n 0000017934 00000 n 0000018321 00000 n 0000018688 00000 n 0000019055 00000 n 0000019421 00000 n 0000019790 00000 n 0000020159 00000 n 0000020527 00000 n 0000020894 00000 n 0000021259 00000 n 0000021626 00000 n 0000021992 00000 n 0000022276 00000 n 0000022642 00000 n 0000023009 00000 n 0000023376 00000 n 0000023739 00000 n 0000024112 00000 n 0000024476 00000 n 0000024846 00000 n 0000025130 00000 n 0000025558 00000 n 0000026092 00000 n 0000026528 00000 n 0000027050 00000 n 0000012144 00000 n 0000089917 00000 n 0000012663 00000 n 0000012715 00000 n 0000035432 00000 n 0000035497 00000 n 0000034671 00000 n 0000034736 00000 n 0000034606 00000 n 0000027485 00000 n 0000034541 00000 n 0000034476 00000 n 0000034411 00000 n 0000034346 00000 n 0000034281 00000 n 0000034216 00000 n 0000034151 00000 n 0000034086 00000 n 0000034021 00000 n 0000033572 00000 n 0000033637 00000 n 0000033507 00000 n 0000033442 00000 n 0000033377 00000 n 0000033312 00000 n 0000033247 00000 n 0000033182 00000 n 0000033117 00000 n 0000033052 00000 n 0000032987 00000 n 0000032922 00000 n 0000032857 00000 n 0000032792 00000 n 0000032727 00000 n 0000032662 00000 n 0000032597 00000 n 0000032532 00000 n 0000032467 00000 n 0000032402 00000 n 0000032337 00000 n 0000032272 00000 n 0000031686 00000 n 0000031751 00000 n 0000031621 00000 n 0000031053 00000 n 0000031118 00000 n 0000027420 00000 n 0000027673 00000 n 0000028287 00000 n 0000027849 00000 n 0000027957 00000 n 0000028066 00000 n 0000028176 00000 n 0000031267 00000 n 0000031391 00000 n 0000031501 00000 n 0000031900 00000 n 0000032050 00000 n 0000032156 00000 n 0000033786 00000 n 0000033911 00000 n 0000034885 00000 n 0000035087 00000 n 0000035203 00000 n 0000035310 00000 n 0000035646 00000 n 0000035745 00000 n 0000035989 00000 n 0000036233 00000 n 0000036358 00000 n 0000036482 00000 n 0000036606 00000 n 0000036727 00000 n 0000036832 00000 n 0000036943 00000 n 0000037148 00000 n 0000037181 00000 n 0000038480 00000 n 0000038662 00000 n 0000039745 00000 n 0000045954 00000 n 0000091555 00000 n trailer <<307F5F360BAAFD48A91B55B5AE4CA0A6>]>> startxref 91720 %%EOF UliFD$b96baa42-6715-1179-bc96-d795218d25c4Vector Smart Object.ai ART5U/%PDF-1.5 % 1 0 obj <>/OCGs[5 0 R 21 0 R 38 0 R 55 0 R 72 0 R 89 0 R 106 0 R 123 0 R 140 0 R 157 0 R 174 0 R 191 0 R 208 0 R]>>/Pages 3 0 R/Type/Catalog>> endobj 2 0 obj <>stream proof:pdf uuid:7dbfed74-e54d-418c-9195-d77e398c62d8 uuid:cb1caa52-2224-4500-b8f6-91bc24e79a46 2012-09-17T16:52:56+01:00 Adobe Illustrator CS6 (Windows) 2012-11-19T22:48:31Z 2012-11-19T22:48:31Z 256 204 JPEG /9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgAzAEAAwER AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE 1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp 0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo +DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A9U4q7FXYq7FXYq7FXYq7 FXYq7FXYqhdU0+HUtNurCZ3jiuonieSJuMihxTkjb0YdRtkZx4gR3soS4SD3MCn/ACM8sJC0mmXl 7ZamvxQX4mqwk6hmAC1360ocwj2fDoSD3ucO0Z36gCO5Mfy280apfJf6Br5H+INEk9K4f/f0R+xK Olfc/I98s0uUm4y+qLXq8MY1OH0yZtmW4bsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsV dirsVdirsVdirsVdirsVdirsVdirsVdirzrz7peraL5lsvPWiWzXZgT6trVlF9qW3/noOvEdfCin oDmDqIShMZIi+92GmnGcDika7veyryt5y0DzRZm60m49QpT17dxxmiJ6B0/iKg9jmRhzxyC4uLmw TxmpBO8uaXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FUv 1zzBo2hWRvdWu0tLcbBnO7HrxRRVmPsoyGTJGAuRpsx4pTNRFsNH55eSC4qt6tuW4/Wjbn0gK05b Nyp/sa5i/wAoY/Ny/wCTsnl82fW1zBdW0VzbuJYJ0WSGRdwyOOSsPYg5mAgiw4JBBoqmFDz/AM5+ SL21vj5t8nj6tr9v8VzaIP3V5H1dWQU+I/j/AK1DmFn05B48f1fe52n1AI8PJ9P3J/5J856d5q0r 63bgwXcJ9O+snPxwy9welVP7J/iCMuwZxkjY5tGo05xSo8uhZDl7Q7FXYq7FXYq7FXYq7FXYq7FX Yq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYqk3m3zXpXljR5dS1BxsCtvADR5pKbIv8T265VmzR xxst2DDLJKgw7yt5L1LzFqI82ed4xLcPvpmjOD6VtF1Uuh/a/wAk/Nt+mLhwGZ48nwHc5ebUDGPD xfE96YfnBqMtp5O/RlnGGudZni063TYAeoan26Jx+nJ62VY6H8WzXoYXk4jyjuyzQtMTStFsNMRu a2VvHBz/AJvTQLX6aZk44cMQO5xck+KRl3lHZNg7FXn3nHyXrFnrI84eTqJrK/8AHQ0/pFdx/tbV A5Gm479R8XXCz4JCXiY/q6jvc/BqImPh5Pp6HuTLyr+Z2g65N+j7kNpOtoeEumXfwPz7qjMF5/LZ vbJ4dXGex2l3FrzaOUNx6o94ZfmU4jC/On5jQaPcpoujQHVvM1z8MFjF8Sxk9GmI6UG/Hw60G+Ym fVCJ4Y7zczT6UzHFL0w70rt/yz8xa0n1vzb5kvfrcnxCy0+QQwQ16KKqykjuQo+Z65WNLOe85G/J sOrhDbHEV5tS+V/zO8tVn8v62des0FW0zU6mQgdkkJ/42XE4s2PeMuIdxUZsOTaceE94TXyx+aOj 6rdfovU4n0TXUISSwu/g5P4RuwWtewND4VyzFq4yPCfTLuLXm0cojij6o94ZpmW4bsVdirTuqKXc hUUEsxNAAOpJxVyOrqHQhkYAqwNQQehBxVvFXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FWN+c/Pmj+V rZfrFbnUZ9rPTYt5ZWJoNhXite/3VOY+fURxjfn3ORp9NLIdth3sd8teSta1vV4/Nfnej3afFpmj f7qtlrVS6/ze30nfYU4sEpy48nPoO5yM2ojCPBi5dT3vRsznXsc8++UR5o0L6lHN9VvreVbmwud/ gmjqBWm9CCRt8+2UajD4ka69HI02fw5XzHVjUHnb8yNFjFv5g8rS6iYxQ6hprcw4H7RjQP17/Z+W Y4z5YbSjfmHIOnwz3hOvIp35a/M/yzrt7+jgZtP1ToLC+T0pC3cLuyk+1a+2XYtXCZrke4tOXRzg L5x7wy3MlxXYqkHmnyL5a8zQ8dUtA06ikd5F8E6fJx1+TVHtlObTwycw34dTPH9JYwn5a+craP6j Y+dbuLST8IjkiDzon8qy8wR7Up8sx/yuQbCZ4XJ/N4zuYDiZJ5R8h6D5Xic2MbTXs/8AvTqE55zy Emp+L9kV3oPprl+HTxx8ufe4+fUyyc+XcyLL3HdiqSeZ/Jnl3zNbehq1qsrqKRXK/DNH/qON/oO3 tlOXBHIPUG7DqJ4zcSw6PTvzQ8mfBprr5q0JPsWs7cLuJB+yrHrtsKcv9UZi8ObFy9cftcszw5uf ol9iJX85LSIcL/y3rVtcj7cQtlYD6WeM/wDC5L88BzjL5MfyBPKUa97m/OKKf4NN8sazdz9o2twg r23Rpf1Y/nb5Rkfgv5Cuc4j4oSfSvzI88H0dZUeWfLj/AN7ZxMHupl/ldvA+4X/VORMMub6vRH7W Ynhw7x9c/seiaVplnpenW2nWalLW1jEUKkliFUdydzmdCAiAByDr5zMiSeZRWSYuxV2KuxV2KuxV 2KuxV2KuxV2KuxVhXnfz7Pp13F5e8vQfpHzPebRwDdLdWH95L26b0Pbc7dcTUagxPDHeZczT6YSH HPaA+1vyX+XUWlXB1vW5jqvme4+Ka9k+JYieqwg9KDbl91Btjg03CeKW811Gq4hwx9MGaZluG7FX Yq7FWNeefI+m+adMaKRRDqcI5WF+opJFIN1HIblCeo/jmPqNOMg8+hcjT6g45eXUIP8ALHzTe61o 89nqtV1zR5Taakp+0WWoVzT+biQfcHI6TMZxqX1R2LPWYRCVx+mW4ZjmU4jsVdirsVdirsVdirsV dirsVdirsVdirsVdirsVdirsVdirsVdirsVdirBvO/ni+ivl8reVo/rnme6X4mFDHaRkbySE/CGp uAenU9gcPUagg8EN5/c5un04I457QH2pl5H8j2vlq1kllkN7rd6eepak9S8jk1KqTuEB+/qcs0+n GMd8jzLXqNQch7ojkGT5kOM7FUHq+sabo+ny6hqVwttaQiryP+AAG5J7AbnITmIizyZwxmZoc3iP mL84vNnmPUP0V5Pt5baKQlY2jUPdyjxNOQjHy6fzZqcutnkPDjH63cYtBjxjiyG/uQj+RfzstYDq a3N21wo5vFHfM85pv0DkN1OwJyP5fUDez82f5nTE8ND5Mw/Kb81L7Wrw+X9fodUCsbW54hDL6YJe ORRQB1AJqBuAe/XJ0esMzwy5uJrdEIDjh9KO4jRPzrXh8Nt5lsSXXov1iAE1+fGL/hsn9Go/rhh9 em84F6Pme692KuxV2KsI83fmTHp96NC8vW/6Z8yynilrH8UcJ8ZmBHTutfmVzEzarhPDH1TczBpO IcUzwwS6D8t/Nurr9b8z+abyO7k+IWenP6MMXfiKfC1P9X6T1ysaWct5yN+TYdXjjtCArzV/yl1D VJT5g025vpdSstKvjbWF5OeUjKvIMC5qSBxBHz8Mlo5H1AmwDsjWxiOEgUZDd6Dma4CS+bvMR8v6 M+oi3NywdYxHXiAW7s1DQZkaXB4s+G6cHtDWfl8XHXEifLusfpnRbXU/RNv9ZUsYmNSCGKnegqDS o9shnxeHMxu6bdHqPGxRyVXEmOVOS7FXYq7FXYq7FXYq7FXYq7FXYq7FWPfmBr91oHk/UtVtF5XM CKsNRUK8rrGHI/yefLKNTkMMZkG/S4hPIInklf5VaBpGneX1vbe6j1HVNRAn1O/RxKxkf4vT5Ak0 SvfqanK9HjjGNg2TzLbrcspToiojkGa5luG7FXYq+fPzG1bUvPH5gw+WNOkrZ2s31WECvD1R/fzs O/ChH+qNuuaTVTObLwDkPwXe6WAw4uM8zv8AqD2Xyh5K0Pyrp4tdNh/esB9Yu3AM0p/ym8PBRsM2 uHBHGKDqc+ollNlPsuaHz5ciKT8/wNN+yNRjMnH+ZYwbnp/lB65pD/jO3f8A2u9H+K7/AM3+x6D+ Yv7rzv5EuU2k+uzQmm1Vk9NTv9+Zup2yQPm4Ol3x5B5PQsznAUbu8s7K3e5vJ47a3jFZJpWCIo92 YgDBKQAspjEk0GC3/wCcejvctY+W7C68w3w6LaoyxA+8hBanuEp75hy1sbqAMi5sdBKrmREIVtI/ NnzT8Oq3kXljSn+3aWZ53TL4M4JpUbH4x7rkeDPk+o8A8ubPjwY/pHHLz5Mt8qeSvL/le1MOlwUl k/v7uQ855f8AXeg29hQZk4cEcY2cXNqJ5Dcky1eS7j0m9ks1LXaQStbqBUmQISgA/wBbLJk8Jrm1 QA4hfJ5H5A8y+ZNC8twaRpnk+/vL0ySS3lzMGt4mlkbY8mQ1ogUbkdM1mmyzhDhECS7XU4YTnxSm APmzLy1+Yd7fa/8A4e13RZdG1Z4Wnt0LiVJVWpPFgF7A+I2O+ZWLUmUuCQ4S4mbSiMOOMuKKI8m+ dG8zz39rcaf6EdvQgE8wVYkcHBA+LbNzqtJ4IBBu3m+zu0vzRlExqvxugH/M250/W4tP1ny7daXp 01z9StdSdg0bSVotQFChT2KsdvlmlOrIlUokC+b0o0YMLhIEgXTPMzHCUL6+srCynvr6dLaztkaW 4uJWCIiIKszMdgAMVSryb508u+ctCj1zy/c/WtOkkkiWQo0bB4mKsCjgMPH5YqnmKuxVpHR15IwZ TWjKajY0PTFabxV2KuxV2KuxV2Kqdxb29zBJb3EazQSqUlikAZWU7EMp2IOAgEUUgkGw8kbSdH0f 82tJsfJgaGbjI3mC2idngSHY0apbiafs9AeNN81nBGOcDH8Xaccp4Ccn+a9fzaOqUru6trS2lurq VYbeBTJLK5oqqoqSScBIAspjEk0HinnL8872+kfS/KUDoJT6S37KTM9dv3EY+yT2JqfYHNTn7QJ9 MPm7jB2cB6sny/Wjvy38pQ+SNMuvOPmw/Vbgx8YIXq0kSOd6j/fshoAOoHXqaT0uHwgck9mGrznN IY8e7KvIv5q6Z5v1e7062sprV4IjcRPKytzjV1RqhfsmrrtU5kafWDLIgCnG1OiliiCTaM/Mjzzb eU9CedSr6ncgx6fAd6vTeRh/KnU+Ow75PVagY4316MNJpjllXQc2AfkR5Tu7i+ufOGo8m5epHZO+ 7SSSH99NU/Ste9T4Zhdn4SSchc7tLOABjDItfnHmH82NC0u0/eQeXVkvdRkXdUkcDgh7VBVPvPhl +Q8eeMR/DuXHxDw8EpH+PYMq85+c9K8q6Uby8PqTyVSzs0P7yaTwHgB+03b50GZOfPHHGy42n08s sqDEdK8ia15uuE1zz5K/ok+pY+Xo2aOKJT09Wm/Kh6fa8T+zmLDTyyniy/6Vyp6mOIcOL4yeiafp un6dbLa2FtFa2yfZihQIvzooG+Z0YiIoCnAlMyNk2ickxdirsVSHzd520PytZCfUZS08lRbWcfxT St4KvYeLHbKc2eOMWW/Bp5ZDQeZWGt65YecIPNPmbTnN9qcXp6dZBiv1W1Z+OykEmQ7/AAmnXf7W 13Z3Z8s/FmmaPQOB2x2zDSyhghHiB3J/V3s885+b5/LuueXbVFgjstXuZIr+4nBCoq+nQhwVCsef 7VRmNqM5hKI6F2Wn04nGR6hPo5fLuv254NaarbwS9vTuESaPof2gGWu2WgxmOhaSJwPWKNa4t0mS B5UWeUExxFgHYL9oqvU0rvljW8P86+WfzD/NbzzceWNTtrjy5+Wmhzj67LULLq0i0dPSI2MZBBHZ erfGAqqsi8m2smqebjDoj/ovyX5PP1LT7C1+GKaYKUfl/MNySTvuO7E5g45SyZSb9EftdhkjHFiA I9c/sD1LM517AvzH8z6mLm18oeXW/wBz+r7STL/x7W5rykJH2SQDv2AJ60zD1WU2McPql9jnaTDG jkn9MftLJfKflmz8taFb6TaO8iQ1aSVySXkbd2p+yCewy/DiGOPCHGz5jkkZFOMtanYq7FXYq7FX YqwDzl541KbVR5R8oKLjXpgRd3fWKzTozMdxyWv0e52zCz6gmXBj+r7nO0+nAj4mT6fvTryP5H0/ ytYMiMbrU7k89Q1B95JXO53NSFBOw+k75bp9OMY8+padRqDkPcByDJcyHHeJfnv5qu7rUbXyhpxZ q8JLyNOskshHoxfR9qneo8M1PaGYkiAdx2bhAByFnv5efl1pflTTYmaNJtZkWt3ekAsCw3jjJ+yg 6e/U5mabTDGP6TharVSyn+i8/wD+citRvvrukaaOS2XpvcbV4vKW4b+6KP8Ahswu05GwOjndlQFE 9U1/JnylJ5b0i/8ANOtg2jTwH00kHFo7VP3ju4O45lQQPAe+WaHD4cTOW36mrX5/EkIR3/WwzTbT VPzU8/y3F0Wi0uH4paf7ptVb4IlPTm/66t2zFgDqMtnl+hzJyjpsVD6v0vcfMeoW3lXydeXdnCsc Wm23G0gUfAGoEiFPDkRXNvlkMeMkdA6bFE5cgB6ljPke307yd5Ek8x6zNW81JRqGoXLbySNKOUUQ 8W+Lp/MTmPpwMWPjlzO5cjUE5cvBHkNgoeRtAvvMurf478yx1eX/AI4WnNulvAD8ElD+0eq/8F3F Bp8ZyS8SfwDLUZRjj4UPie96Vme65I/N/nfyr5P0v9KeZNRi06zLcEeTkzO9C3GONAzu1B0UHFXl D/8AOQXnHzVI0H5W+RrzV4alRrep/wCi2VQd6fEqsO+8qt/k4qjNM8n/APOSer6ha3vmLznp+h2c csc0ml6VaiaqowYxO7rG3xAcT+9YYqnv526HPeaVZ6pH501Hyh+jRMFGnM3+lvNw4o8Qki9Vk9M8 BXbkTsMry5YwFlsxYpZJVF5L5b0b/nIvTbq288S6faeeEaPhbQXzCC+jhU1WWNCY0Vj+yfjO9ab1 ynFCMyMhFFvzTljBxg3HyejeXvzw1XVde03SNb/LjXtIu7qdIUuprYyW0LO3EyGd0i+Bf5gMywSH CMQeYRf5xap5zlu7bQNC/L9fNsEsK3P6QuLkW8EExZ09Pf0/i4qDUSjY5XkxRmKkLbseWUDcTTD9 D/Lj/nIi5tGtI9V0byFpk5Bmg0mJ7q6O3VpZTK5I8RcYMeKMBURS5c0pm5G2aeQvyC8veV/MCeaL /VtT8xeaUDhdU1C4c8fUUo/FFO4KsRSRnyxrZX+YXmqDy35XvL0yBbyRDDYJ+007ii0Hfj9o+wyj U5hjgT16ORpcJyTA6dVP8tfLj6B5OsbKZOF5KpubwH7XqzfEQ3uq0X6MGlxcGMDqnV5ePIT0TbzH r9joGi3WrXrUhtk5BK0Z3Oyovux2yzLkEImRasWIzkIhiX5W6DfOl35w1ta61rx9RARvDa/7rRfD kAD/AKoXMbSYzvkl9UvucrWZRtjj9MfvZ/ma4LsVdirsVdirsVYd+ZPnC50TT4NO0lfW8xau31fT YVoSpYhTKQf5a/DXv7A5i6rOYCo/VLk5ekwCZuX0R5ozyH5LtfK+k+kSLjVLk+rqV8al5ZTufiO/ FSdvv6nJafAMcfPqw1OoOSX9EcgyXMhx3Yq+ffKkY8wfnjc3c37yK3u7m4Wu/wANvySD/gSE+7NJ hHHqCfMu9zHg0wA7h9vN9BZu3RKctvbysjSxJI0Z5RllDFT4rXocBAKQSHmX59+ZvqHlyHRYXpca o9ZQOogiIY/8E/EfKua/tHLUOHvdj2bh4p8X81N/ya8sDRPJsE0qcbzVKXc5PUIw/cr9Cb/MnLdD i4Mfmd2rX5uPJXSOzMNV0y01TTbnTrxOdrdxtFKo2PFhTY9iOxzKnASBB5FxITMSCOYYNZ/kxpKz 2w1PVb/VtOsiDZ6bdS1gSmwBUdgOy0+7bMOOhjYsmQHRzZa+VHhAiT1Cd+d/zK8j+RbBbjzHqcVl yWttZr8dxKBtSKFKuR2rTiO5GZzgPLI/Ov55/mkB/grTh5J8py9PMGpqHvZo/wCaCKhAqP5QR4SD FXrkfk+wvPLml6R5pWHzJJpqws11ewIwmuYYzGLhomMihzyJ79cVT5ESNFjjUIiAKiKKAAbAADFW IebPzN0PQ2+o2ldW1yQ8INMtDzfn0AkKhuHy+17Zi5tXGGw3l3OXg0kp7n0x7ylXl/yFq2tamnmX z263F6u9joy729svUclqQze2/uSelePTymePJz7u5ty6mMI8GLl1PUvRcznXuxV2KoLVNc0bSYvV 1O9gs46VBmkVK/6oJqfoyE8kY8zTOGOUvpFsJvfzftbyZrHyfplzr990Eio0duhO1XZgGoPcAe+Y ktaDtjBkXMjoSBeQiI+1d5f/AC/1i/1iLzL54uVvNTh+Ky02On1a23qNujMP17kscOPTSlLjyGz3 dAuXUxjHgxCh1PUs8vLy1srWW7u5VgtoFLyyuaKqjckk5mSkALLgxiSaHN5fapdfmd5hjvp43h8k aTLW2hcFTezrtyI/lHfwG3UmmuF6iVn+7j9rspVpoUP7yX2PVQABQbAdBmydY7FXYq7FXYq7FXYq 828mQ/4g/MXzD5luvjj0mU6XpanonCqyOvgT1/2ZzAwDjyymemwdhqD4eGMB/FuXpOZ7r3Yq7FXz 7+Wk8ek/nBe2l6fSkmkvLRC2w9QyclG/83Cg8ajNJpTw5yD5h3urHFpwR5F9BZu3RJZ5h8y6L5fs HvtVuUgiUEohI9SQj9mNOrNleTLGAuRbMWGWQ1EPBdMg1L80vzEa7uY2TS4SrTrWqw2iH4IeX88m /wBJJ6DNNAHUZbPL9DvJkabDQ+r9L6MVVVQqgKqiiqNgAOwzevPqGo6jYabYz3+oXEdpZWyGS4uZ mCRoi7lmZqADFWFeQfzV0T8x31yDQLe/g02xpBb648RiiuGkDBmt2YGjR0Bo2+4quKpR5H/5x48o aDqLa9r00vm7zTI3qS6vqv7wB/5o4GMiqR2LFmHYjFXqM80UEMk8ziOGJS8kjGgVVFSSfADATSQL 2ebL5s8++c55R5Oji0rQ4nMY1m8Xk8pU0JiQhxT/AGJ9yDtmB42TKf3e0e8uw8DFhH7z1S7gr/8A Kp9Tv/8AlIfNupX6sPjggb6vF8uJMq/hkvyZl9U5Fj+djH6IRDJ/LXkbyv5bU/omxSKZhR7l6yTM O45tUgewoMyMWnhj+kOPm1E8n1FPsuaGIeYfzV8maLIbdrs316DxFnZD1n5dOJYEIDXsWrmLk1mO G12fJy8WiyT3qh5pQPOX5n61T9BeV10+2YfDd6o5U0P7Xp/um/Bsr8fNP6Y1723wMMPqnfub/wAD /mVqu+ueb2tYz1t9Mj9MUPUeoPRb7wcfy+WX1Tr3L+Ywx+mF+9G6X+Tfkmzm+sXcM2q3Vama+lMl T7ovBG/2QOShocY3O582E9fkOw9I8mXxxaZpdoEiSCxs4+iqEhiX6BxUZlACI7g4hMpHvLFde/Nr ydpf7m3uv0rfseMVnYfvmZugHNfgG/vX2zHyazHHkbPk5OPRZJcxwjzSOPyz5x8+XMd15tDaR5cR hJBoMTESy0NR67bEfTv4KvXKRiyZjc/THubzmx4RWP1T/nfqelWdna2VrFaWkSwW0ChIokFFVRsA AMz4xAFB10pEmzzVcKHYq7FXYq7FXYq7FXldpqh/Lvzhqlvq8bjy3r1ybyy1JVLJDNISXjkoCe9P GgB7mmujPwMhEvokbt2coePjBj9cRVPULW6trq3juLWVJ7eUco5o2Dow8VYVBzYAgiw60gg0VTCh 2KvLfzM/KCfXtROuaFKkGpvT6zBISiSMoAV0YA8XoN+x9u+u1Wi4zxR5uy0muEBwy5MR/wAH/n3x Ft9avfRG3P8ASKUpTpX1uftmN4Gp5Wfn+1y/H0vOh/pf2InTPyG806jdi58xamkKsf3hV3ubhh4V aij58j8sMOzpyNzP6WM+0oRFQH6HsPlnyvo3lvTV0/SofSiB5SSNvJI/88jdz/mM2mLDHGKi6nNm lkNyTG7u7WztZru7lSC1t0aWeeQhUREHJmZjsAAKnLWp88Oda/5yD8yvHHJNp35QaNcBXYcoptWu I6GniEH/AAo/yz8Kr6C0rStN0nTrfTdMto7OwtUEdvbQqEjRR2VRiqKxVKPN2lXOreWNU022bhcX dtJHESaAsV2BPg3Q5VmgZQIHUNuCYjME9Cxf8q/NmlzaRb+WbhBp2uaSn1afT5fgZzHsZEB+0W6s OtfbfMfR5gY8B2kOjk63BISMxvGXVn+ZrgqN7eW1laTXl1IIra3RpJpW2CqoqScEpACymMSTQ5vM If8AFX5mSySieXQ/JQYpGke1zeAGhqf5f+FHSjEE5rhx6j+jj+0uyPBp/wClk+wM58v+TvK/lqD/ AHGWUVuyL+8u3+KUgDctK3xU+mmZmPBDGNg4WXPPIfUWNah+bC3N9Jpvk/Sp/MN3GeMlxH8Fqh6V MlDUV77A9jlEtZZrGOI/Y5MdFQvIeEfapi0/O7VPjlvdN0GNv91RJ68gH+yEy/8ADYK1EuoivFpo 9JSXf8q0823Zrqnne/kVvtR2q/Vh8vhcj/hcfyszzmV/N4x9OMfHdfD+SHk4yiXUJb7VJOrG6uCa /TGI2/HCNBj62fip7RydKHuDK9F8qeW9EWmladBaNShlRAZCPAyNVz9JzJx4YQ+kU4uTNOf1G01y xqdirsVdirsVdirsVdirsVUL6wstQtJLS9gS5tZRxkhlUMpHuDkZREhR5MoyMTY5vMLjTpvy682a Qmj3TyeX9fuhazaPKxcxO7KokhqeRpy+fY1qKa8x8CY4T6ZHk7ES/MYzxD1RF29WzZOsUru7tbO2 kubuZLe2iHKWaRgiKPEsaAYDIAWUxiSaDA7j84LO6uXtfK+j3vmGSP7csCNHCPm5VmHzKjMM60E1 AGTnDQkC5yEVOT8yvOdohn1DyPeR2q7vJDN6zKviUEY6d9xg/NZBzgaUaTGdhkFsv8seaNI8y6Um paXIXgYlHRxxkjcdUdd6HfMnFljkjcXFzYZY5VJJPOH5wflv5QhkfW9dtY546/6DC4nuiR2EMXJx 82AHvlrUkNnceXPz4/LCUzQanpGjXl2yJ8QgllS0l2YEc45I3pRhuAwI6rXFXoejaNpei6Va6TpV slpp1lGIra3jFFVF/WT1JO5O5xVGYq7FXYqxvzX+X/l3zLxmvImh1CIfuNRtz6c6U6fENmp25A07 ZRm00MnPn3uRh1U8fLl3MTm1L8wPIDLJq0p8yeVlYLJfAUu7dSaAvUkt/siQf5lzFM8uH6vVD7XK EMWf6fRPu6FFfm9qq3vkWwXT5w1prt5awCde8MoaUHx6xjbJ62d4xXKRDHQwrKb5xBZ/Y2VtY2cF laoIra2RYoYx0CoKAZmxiAKDgykZGywj85bu7Hl+x0m2lMA1u/hsZ5h1ET1LD6SBX22zD10jwiI/ iNOZoIjjMj/CLZhomh6Xommw6dpsCwWsIoFHVj3Zz+0x7k5lY8YgKDiZMkpmzzR2TYOxVINd8++U NCLJqWqQxTL1t0JllB8DHGGYfSMpyaiEOZb8emyT+kMbH506Pcb6bouragn+/YbYFCPEHmT+GY/5 6J5Rkfg5H8nyHOUR8Vaz/OrydJcC31BbzSJj2voCor84zJT5mmGOux3RuPvRLs/JVipe5m1lfWV9 bJc2U8dzbybpNCwdD8mUkZmRkCLDhyiYmjsr4WLsVdirsVdirsVdiqC1vUf0Zot/qXD1PqNtNc+n 05elGXp9PHIZJcMSe4M8cOKQHeWBflz5Wl1lrbz35juGvtWug0ljA39zbR8iF4LvvTceFf5t8w9L h46yT3kfsc7VZuC8UBURz83pWZ7rnz5+dX5oeVbbz5F5f12a4vNO02JJP0Hpo9Se7vJaFI3oVCfC 46moFeO5zCy4pZclH6B9pc/Dljix2P7yX2BFab5i/wCcifMNokHlDyfpnkXQyP8AR5tWYmYDx9FV DKT1+KD6cy4xERQcKUjI2dyz/wAj2Xnnyxo+pX35jeaLfWGYpMk8cCW0VsqqQ6AqsYcMacfgBr41 xlIRFnksYmRoc3j/AOXX/OOum+atNvdV17zBr8unXV1J9TgN0E+sRA0Ms/JJORJ2JBG4zH0s+IGV ULcjVw4JCN8Rrd6v5Y/5x/8Ayi8uUex8u289wBQ3N9yu5K/zL6xdUP8AqAZlOKxH/nG65k8uah5u /Ky/el55bv3udNDHeSwuSCrKPAHi5/4yDFXr+oeZvLum3CW2oana2lw9OMM0yI+/Q8WINPfK5ZYx NEgNkcM5CwCUxR0dFdGDIwDKymoIO4IIyxrbxV2KvIf+cgNY8wWNvpcFlNLbadOZfrEsLMnOVePF HZabUqQK7/Rms7RnIAAcna9mY4Ekncpb+X3mDW5Pyw82PqjyXdjbQPHYyTEuS0sTK8fJqnipKH2r lemyS8GfFuGzVYo+NDh2J5p3J5UvNV/JHTLW1Pq6hawx6jZcNyXBaQItP2vTkKj3y44TLTgDmN2k ZxHUknkdma+RvN9l5o0GC+hYC7RQl/b/ALUUwHxAj+UndT4ZlafMMkb69XD1GA45V06LPP8A5SPm jy89hFL9XvYZFubCc1os8dQvKm9CGI9uuOpw+JGuvROmz+HO+nVjNr+ZvmPRo1tPNvlq+F1GOJvb CMTQzEftDdUBPcBz9HTMcaucdpxN+TkHSQnvjkK7iqP+a+q3/wC68u+U9Su5j8PO6QW8SMf5mHqL 97DD+clL6ISKPyUY/XOI926mfKX5meZt/MmtLo1g/wBrTNM+2R3V5K/8bOPbB4ObJ9cuEdwT4+HH 9EeI95ZD5f8Ayz8l6GFa002Oa4HW6uR68pPiC9Qp/wBUDL8elxw5Boy6vJPmWUAACg2A6DMhxkPf 6bp+o27W1/bRXVu32opkV1+5gcjKIkKItlGZibBpgt3+T9nbXLXnlXVbvy9csaskLNJA3sULKafN iPbMOWiANwJi5sdcSKmBILT5W/OCUfVpfN1ulsdjPHaoJqfQi0+YauPg5+XH9i+NpxvwG/ezzT7e 4trC3t7ic3U8MaJLcsOJkZVALkAmhbrmZEEAA7uFIgkkbIjJMXis/wCeP5nX00kXlz8qdXmTkwiu tSc2KkDo3GSKm4/y8Va/xN/zlhdHlB5P8v6cm/7u8uTO3gByguQPfFWa/l3qX5qywanJ+Y1hpen+ h6bae+ltIyunFjMZPUlmI40WnTviq/yB+b/kHz4sieX9SV72Gpm06dTDdKAacvSfdl/ylqB33xVl 91bQ3VtLbTqHgnRo5UPRkccWH0g4CLFFIJBsPLfLmvf8q41KTyr5nuFh0F2ebRNYmISFY2NWjlck KlCd69D7EHMDBI4peHLkfpLsc8Rmj4keY+oLPLn543XnPz9Do3kjRX1TyrZM6695lmJhhQ8TwFvy py+Kmx+JhWigfFmwdajtb/JfTovM83nHyc8Wi+abiR5rqd19SKd5BRyVbn6Zbq3FaMdyK75j58U5 UYyoj5OTgywjYnGwfmiPS/PQj0vW0VT09ektf9anE/8AEcprU/0W69N/SWwfldqusXUd3541yTVx E3OPTYB6NqD7heNfoVT74jSSkbyS4vLok6yMBWKPD59XoUMMMEKQwosUMShI40AVVVRQAAbAAZmg U4BN7lfhQ8A86aNqHm785dN1n8urz6nqOmW0lj5h1ld7d7d6hVUj7TqGYBvGhX7Fcw5ajjJhD59z mx0/hgTn8upZXrPkHyJ5b8k6tql2o1a+9GVG1K4f1He6cmIcACVUiU/MdztlWTTY8eMk7nvbceqy ZMgA2F8h3Mw/Lazu7PyLosF2CJ1twxVuoVyXRTXwVgMydLEjHEHucXVyByyI72SZkOO7FVK5tbW6 haC6hSeBvtRSqHQ033VgRgIB2KRIg2FGfSdNm0yXS2t0WwmiaF7dFCJwcEMAFpTr2wGAI4ejITIl xXu8zV/NX5YFUl5az5I9Sgk/4+bMO3f2qf8AVJ/lJzX+vT/0sf2h2NQ1P9HJ9hRuveVL23vF88eQ Jl+tXCCa7sE3gvYm+MlV/mbw7ncUbrPJhIPiYufd3sMWYEeFl5d/cyryV5z0/wA1aV9bt1MF1CfT vrJz+8hk8D0qpp8Jpv8AMEZkYM4yRsc3F1GnOKVHl0LIMvaHYq4kAEk0A3JOKsK8z/mr5e0omy0x v01rUnwW9hZ/vayHoHdKgfIVb2zEy6yMdh6pdwczDopy3l6Y95V/y68v67p9pe6n5gmZ9Z1iX6xc W4YmOBRXhGoqVB33p7Dth02OUQTL6pMdVljIiMPpiy7MpxXYq7FXYq7FXYq7FXYq81/Mf8h/KPnG f9L2pfy/5sib1bbzBp/7uYSjo0yqU9X51D+DDFWEL+cP5l/larab+aujS6zpsaldP816WoZJiB8E dwG4Krsdqni3+S32sVQ/l/8ALbzj+dCW3m38zL+Sx8sXK/WNB8qWDmNRFIP3U0zkGpZTyB3Y1/ZH w4CEgs30XyZ+ZPkmzXSvKlxp1/oURP1W0u4zE8fIkn+74V69ef0ZheHnh9JEh5uf4mnn9QMT5I39 BfnLqhH17X7PR4TSsdjD6r/e4B+6THw88uchH3I8TTx5RMveuH5W+YGHKbzxrDSHqY5XjX6FEhpj +Ul/Pkv5yHTHFTfyf+aOjj1tF80/pULubPU468gP2fUJkNT81+eDwM0fplfvSM+Cf1Q4fcmXlT8x xqGpnQPMFk2i+Y06WshrFN7wv38aVPsTlmHVcR4ZDhk15tJwx44HiggfzC1rV9V1i28ieXpDFeXq erq16v8Ax72p6io7sOv0D9rIanJKUhjjzPP3M9LjjGJyz5Dl5llvl7yvpGg6JHpFjFS2VSJWP25W YUZ3YUqzf7WZWLEIRoOLmyyyS4i+ZfzI8qecfymsb+xsBPq/5W6ncLdRgkyTaVcF6lTXf03rSp2O 1aN9uvU4TkgQObPS5hjmJHk9q/Ln85tK84rYQW+n3KS3SsPrcSK9mGiQu37zkGX7NOJXY7ZHFqSZ CMokS+xnl0oETKMgY/azLXfNGhaD9WOrXS2q3knpQO4YqW/ymAIUCvVqZdkyxhXEatpx4ZTvhF0m aOrqHQhkYAqwNQQehByxqbxV2KvM5Pyz1fzRf6lfecb6eENI0emWVnMvoxwU+FqFWBJ+QrSpzX/l ZZCTkPup2I1ccYAxj3ku/Jy51PTJdW8l6tUXWjyLLbAkH9xN8Xw0J+GpDf7LHRExvHLnFdeIyrJH lJfqVsug/nFpNzY/u4fMkE0WoQLsrSQqW9TwqTx/HxwyHBnBH8XNEDx6cg/wcnpOZ7r2A69+YWrX OtzeW/Jlguo6pb/De30ppa2xrQhiKcmHz67fFuMwsmpkZcGMWfsDnY9LER48hqPQdShv+VW67rZE vnLzJcXync6fZ/ubcH7qN8+APvkfykp/3kr8gy/ORh/dxA8zzZf5f8n+WvL0ZXSLCK2ZhR5gC0rD waRyzke1cyseCEPpDi5c88n1G04y1pdirsVdirsVdirsVdirsVdiqye3guIXguI1mglUrJFIoZGU 7EMpqCDirFfzC/LbSfO2gW2jXN5eaVHYzJc2U+mSLA8UsSNGlPhYcQrnYU9qYq81k0f/AJyM/Lk+ vpepJ+Y/l2LeTTrwGLU1T/IkJd3YAU+2/wDqYqzb8tvzv8l+emNjbyPpfmKGq3WgX49K6Vl+3wBo JAtDXjuP2gMVeg4q7FWLfmB5Jt/M2kn0qQ6zZ/vdLvFPF0lX4gpYb8WI+jr2zG1OAZI/0hycnS6g 45f0TzeW+SfPN/pmv6n5j1vTbi6jv1js766tk5vBcWyKjK6Hjx50DUqPatCBr8GoMZGcgTe3ydnq NMJQEIkCtx7izR/zK8064/o+TfLk0q/tahqQ9GBfkAwDf8HX2zK/NTn/AHcfiXD/ACkIf3kvgFHU fNH5k6DbvN5q0Sz1TQXBS9ksOTMkbCjc0kJDLTrVaeLYnNmx7zAMfJMcGHJtCREvNi+mflRPp+u2 XnP8mNbi0zS9RnjGuaDccpLFoiw9UrFWqSRgn93UEfsso2zMx5BMWOTg5McoGpc3qnmo+W9X0jWd JvJbe6lsbYz3lrzRpbflG7RSMoPKMniSpNMjniJQILLBOUZgjnaX/lBPczfl1o73BLMFlRCSSfTj ndEG/gqgD2yrREnELbtcAM0q/GzMcynEUb17lLOd7VBJdLG5gjb7LSBSVB6bE4JXW3NMavfk8/0f 86vLjeWvr+tSrbaxEZEuNLiVzJzViFVA38y03JoD1zChrocFy+ruc/J2fPjqO8e9AeR9YfXPzW1D Vfqctjz0aJbi2mpySRniZQadaoKrXenYZDTz48xlVen9TPUY+DAI3fq/Wjlb9P8A5z8k3s/K1oVZ uqm5uAQR90n3pkvr1HlAfaw+jT+cz9j0WT1BGxjAMlDwB6VptXM8uvDz78i4of8ABH1oCt1d3U0l 1KftO4IAqe+2YPZ4/d33lz+0T+8roA9DzOcB2KuxV2KuxVIvMPnzyV5cB/Tuu2OmuBX0ri4jSQ9/ hjJ5t9AxVhc//OTn5GQSGN/M6Fh1MdpfSL/wSQMv44qzXyh528secNF/Tfl29+u6WZHi+sGOWEc4 6cxxmSNtq9aYqnmKuxV2KuxV2KuxV2Ksa1f8t/JGr+ZdO8z3+kwS67pUgltL8DjJyUEJ6nGnqcCe Scq8SKjFWF+bfzr1XyN56ksPOWhPaeSbwxpo/me25TKH4AuLlRXjVuVAKMAOjDcKvUrDULHUbKC+ sLiO6srlBJb3ELB43RhUMrLUEHFVfFXmtoR5W/N24tT8GmebIfWhPRReR1LD5k8v+DGYEf3eeuk/ vdjL95gB6w+5U/Mj83F8p6nFpdpZLeXbRiadpHKIisSFUUBJY0r7YdVrfDNAWUaTQ+KOImgyXyR5 ts/N3l5dTjg9GrNBdWzEOFkUAstaDkCrA9O+X4MwyxtxtRgOKfCmC2lloejzrpdgqxWySzRWNsoU u9C/FQB9pztllCEfSGHEZy9R+L5Z/OVvMlhr035jaN5f1Dy7b3EI0/zeJQViuYZmWMNxZUqeikjq eJ23ri+JLKDExMbHNyvDjhIkJCdHk+lPImpeVLjy9Y2flzUYb+0tLeIK0TqXKlQQ7qN1L1r065fh 4QOGJ+lx8wmTxSFcTI8uaXYq82/Nzzf+X35d6RJ5n1LTLO416YldLt/SiFzc3G2/PiWCpWrydh7k A1nFC7oW2DNOqs0wn/nFLzb5j802/mXV9Z04+rc3Su+ug8Y5nNW+rpER/urmSSppQgUwY8IgSRzk yyZ5TAB5RRnlPzlf6Q+u2Vhpc99511XUpXeFoyIolr8JlaoNFZnPYU7jNZhzmPEALyEu0z4BPhJN Y4xZcut/nRp1FvNBstXRhtJaTCJgfBubb/Qv05leJqI84iXucXw9PLlIx96bflX5b1Py/wCUo7TU 1Ed7NNJcSwgqwj9QgBar8PRa7ZZpMRhCjzatblGTJceTL8ynEdiriQBU7AdTiryTzl/zkToFjqje XPJdhP5080mqiz06rW8TDYmadQ4+HvwBA/aK4qyP8v4/zM1TQtTH5j29lZy6gzLaWOnO3OC3kj4P HI4LDlX4gyyN17bDFUl8uf8AOM/5PaJSRtF/S931e61SRrlnJ6lozxgr/wA88VZxbeSPJdrEIbbQ NNghX7McVpAijtsFQDFURceW9Dn0i40c2UUWmXQIubWBRCkitTmrCPjUOBRvEbHFUyxV2KuxV2Ku xV2KuxV2KoTVtI0zWNOuNM1S1jvbC6Qx3FtModHU9iDirwG/0rzZ+QOpyatoaz65+VF1IX1HRy3O 4015G/vImbfgNviJo3R96Pir3rQNd0vX9Gs9Z0qcXOnX8Sz20wBHJG8QaEEdCD0xVKPP/k5fM+jr FBL9W1WycXGmXe44TL0BI3CtTenTY9sx9Tg8SPmOTk6bP4ct/pPN5VrUGi+aNVgsvOk0nlfzXaoL ea5dA1rdIpPBwSVVD135cT49hrpiOSVZPRMfIuzxmWON4/XA/MPX/JnlnS/LmgwadpshngqZWuCQ TK79Xqu3YAU7Zs8GKOONB1OozSySsq3mTzTonlzT3vdUuFiQA+nFUGWVh+zGnVif9vDlzRgLkUYs MshqIebDyRq35o2t1qXm9pLLRLmKSPRtIQkcVkQqs79DUV5A9W9loDiYoSyyE57DoHMzZIYonHDc nmWCfkV5V1HU/wAuUv8AQpzp3nXytfXWmXBDfu7gRMJBHID8OyShAaUNPi8RZn01njhtP72vBqqH Bk3h9z1vQPze0aSth5nVtB1u3+G5guFYRMw/aR6GgPWjfQT1wY9bHlP0yTl0MucPVFfrH5x+Vral to3qa7qkm0FnZo5DMelXIp/wPI+2GeugNo+o+SMegmd5emPm8z81f846eY/zL16z81ebNV/R0zyR pc6TEDJ6dilT6cTFmWKQntuNyx+LbLNP4hFz6tWo8MECHTr3vedC0LSNA0i10fR7VLLTbJBHb28Y oqqPxJJ3JO5O53zIcdHgAEkDc9TirsVdirsVSvWPNHl3RbixttV1G3srnU5lttPhmcK80rGgVF6n cgYq86/Mf8vPzM89+Z30ebXk0T8txFG00dhyF9eMwpLDKTtxqD/kUK/CxrRVnHkn8v8Ayj5K0pdN 8uadHZQ0HrSgcppmA+3NKfic/M0HagxVkOKuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KrJ4ILiCSCeN ZYJVKSxOAyOjCjKynYgjYg4q84/Nnzp5u/L+20nWdG0SHUfJljWPzDbW4K3UEFAsTwKKIscff6B8 I+IKs08q+atB81aFa65oV0t5p12vKOReqn9pHXqrqdmU9MVVta8v6Jrdr9V1azivId+IkXdSe6MK Mp91OQyY4zFSFtmPLKBuJphjfkvpFu7No2s6ppCsSfRt7g+mK+Gwb72zE/IxH0mUfi5f8oSP1RjL 4I3Rvyi8q2F8uoXjXGs36GqTahJ6oBHQhQFB/wBlXJw0UImz6j5sMmunIUKiPJm2ZbhsR8j+YfI9 /qvmXSfLNlHZXWj3xj1xYrZLZZLuQsGkJQD1Wb0zVzvirIdS0TRtUVV1Kwt71V+wLiJJeNf5eQNP oyE8cZcxbOGSUeRIdp2iaNpgYabYW9kG+19XiSKvz4AVxjjjHkAFnklLmSUbk2DsVdirsVdirx3z 3+fTnWD5O/LOxHmnzhISkkkXxWNnQ8WeaUFVbievxBR+01fhxVU8gfkMltq6+cfzDvv8VedpCsgk m+KztGG6rbxEKp4H7LcQB+yq9cVevYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FVskccsbR yKHjcFXRgCrKRQgg9QcVeD+Y/wAsvOX5Za7decvymj+taTct6uu+SWr6UgFSz2ijoQPsqvxL0Xkv wYq9N/LP8ytE/MDy+dX0uG4tmglNrf2dzGyPBcooZ4uVOL05DdT86HbFUu886Z5q0nVv8Y+XJpLt o41j1PRpGZo5YI96xL+yV3O3zHcHC1EJxl4kN+8Odp5wlHw57dxZL5U816T5n0mPUdOkqp+GeBqe pFJTdHH+dcyMOaOSNhx82GWOVFOEdHBKMGAJUkGu6mhG3gctaafMXk/zFrOh+a/OWr6bIkdnY+Z3 0e+tHAL3irJLxkdqD4wEahHj9B1uWMsJEhIkE8i7PDKOcGJiAQOYfT2bJ1jsVdiqT+a9N13UNGkt 9D1H9GaiGWSK4K8geBrwbrRWPU0PyOVZoylGomi24ZxjK5CwxTR/zL1DTdRi0Pz1Zfou/kPG31JP 95J+1eW4X51p48cxoaoxPDkFHv6OVk0gkOLEeId3VkXnX8wPKPkrSTqfmPUY7KAg+hGTymmYCvCG IfE5+Ww70GZzgMZ8peZH/NvyTraahomoeX9D1Hna6ddPIIp7m0kjFLiPjuhr7Mh8WHLFWSeRfy88 peRtHXSvLlitrCaGec/FPO4/bmlPxOfwHYAYqyPFXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FX Yq7FXYq7FUo8x6DLqnl7U9L069fRbrUopE/SVoqiaOSQcTKOlWptWoPgQaHFXjQ86/nj+VzmHzpp beePKkWyeYtMUC8iQd7iIdaD+cDf/dhxVG6FqnlbzdqTeYPyn8zWun61dqf0jot1WItXq7W7KzAq a7qjLXcEd8LJpZCXFjPCT8nOx6qJjw5RxAcu96L5S8vWnknytcNf3YlkT1b/AFa+YkKWC8pG+Lfi qr3+ffLtPh8ONcz1adTn8SV8h0fO/wCSkL6pe6Nd6uPq2n+ZNe1HXY+eyyspEcUfgSJoWX/ZUzH1 O+WETy5uTpdsM5DnyfWWZ7rnYq7FVC41Cwt57e3uLmKG4u2KWsMjqrysFLFY1JBYhQTQdsVeQfnP rf5ha3fXPkTyt5P+tqYY7ibzNqJCWUAcGkkBBFZEoR15VB+BlO9eXHGcalybMWSUJXE7pL+R/wCS +gapp9p5184zTeZvMFTFb/X2MlrbrbtwjEUTfa4025bDsoyGnyCcAQKDPU4jCZBNl9AAACg2A6DL 2h2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV5x53/5x9/LHzdOb2404 6XqxPIappbC1n5A15MAGidv8pkJ98VTD8xYfzQttJ08fl4mnXMtqxW/tNWZ29eEIFRFbapJqWLOv zxV5Vrfmzz2+kwaL5u/Jq8MNqWa2n8vSiVYix39GO3jmC8j1HMg+G2VZcMcgqTdhzyxm4sUuvzQ1 jRVhk0ix86adJ6qRwQ6pBW1LE/YbdVOw/wB99umYc9OcYuMyPe5uPUxyGpQB9z3vz/5x/MXQ7bSI vK/k8+ZL/UIpDdsLhbeK0kjEZAk5KVIcyNT94v2c2Aut3Wyq9nn+pv8A85JavDy8wa/oP5dabJsw iZZrungrO0qE/wCpKpwSnGPM0yhjlLkLVvIn5a/k/p3mODX9U82y+afNlvIskWpalelQsynZok5D l8ndxlQ1OM/xBsOlyAWYl6d+ZOpapY+U7tdLtJrq/vaWcIhUsYzP8HqNTpSux/mpg1UyIHhFk7Mt JCJyDiNAbo7yX5eHl7yvp+kVDSW0f79h0MrkvIR7c2NPbJ4MfBARYajL4kzJOstaXYq7FXYq7FXY q7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq8s/M/UbC08+eVX192i8vWwluS wRnVrhPs8lQFjxIj7Hr7nNdq5AZI8X0uz0cCcU+H6z9yvP5+80ebXNj5FsHgtWPGbzBeLxiQd/SU 1BP3n/J74TqZ5NsY27yxGmhi3ynf+aEfpP5O+Xo5fruvyzeYNUfeW4u3bhX/ACYwensxbJw0Uecv UfNhPXz5Q9MfJPJfy88jyenXQ7NDEysjRRLE1VNRVk4k/Tlx02P+aGgarL/OLIcvaHYq7FXYq7FX Yq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYqh7/APR31f8A3Iej9XqP 96OPDl2+3tXIyqt2UbvZWi9L019Lj6VBw4U407UpthCCuwodirsVdirsVdirsVdirsVdirsVdirs Vf/Z Adobe PDF library 10.01 application/pdf 1 False False 1000.000000 1000.000000 Pixels Cyan Magenta Yellow Black Default Swatch Group 0 Document endstream endobj 3 0 obj <> endobj 7 0 obj <>/Resources<>/ExtGState<>/Properties<>>>/Thumb 214 0 R/Type/Page>> endobj 210 0 obj <>stream HWnf;Oq^ gm$$$@h,"]Xק$ OwuU˟^/xmo~z=~~km95rߏ/_jw캾wx>~?vnWӃ/~zƧkby^c͞x rչcqe'jΑnJLkhb93Ġ3мAm{o.f/hdu3 n=[5iw`icriUn|FMi"aS#7+$8dT8 ϓ읂PHjA JIx:;cFsy9DQnPa%f8Jw:2NwpO"R<ўpT"WIh4K;?jkh{epl!D6v 1ýb0'±qdx[eswAyqYBҗ3Xœ;Qw.4ڒ)#у yMbax%V\RIA/0.և7 X2Sv ]gGS u3qAfEvTflc┆P[j_!MD!)Cv4z|I鋚N"΂-޷ tL;d@*KyBo('W}g whұw$,<*V>W`t쏄wXg8΁Pm%!-vH7jmycKo]*>$x}|UuQ.Fg ٲF, 'C> t(30=WAZ${vʯDsՕQ1j du L +̌24FkZ/8ZOgԒZ`ɛDí 5hpj/$VTVu|yTF b{7yVGPR'R HYH)d.ջc,Z`ꇖaY:)ˈ yO/sO΢-Aزe̓'3e@yq`YV% U4^5Tև T0Yy ɪɎBdO)[ekt`WM<̪^N.:Hvr7t˓5g{F0)S6ZV7$z #{u{4ȘW]lS[Bb<"NԸ',GXϻ_O8@2<18U^wSA26 QA[4m*AMyHWd<7*E/) I)t+y-04ÄԠ#6mKEMtL\ehMf`YEf*X>mn۰߳~U3yry}d=dL>pc%ӑO6c1&h=`|۽m^3p1/fruJrTlL0ORUG z7*Bj4G2'Su endstream endobj 214 0 obj <>stream 8;Xoa>Zk$/HlYb63<0@.A7,C7f,6%U(`35S$in\<$^q~> endstream endobj 215 0 obj [/Indexed/DeviceRGB 255 216 0 R] endobj 216 0 obj <>stream 8;X]O>EqN@%''O_@%e@?J;%+8(9e>X=MR6S?i^YgA3=].HDXF.R$lIL@"pJ+EP(%0 b]6ajmNZn*!='OQZeQ^Y*,=]?C.B+\Ulg9dhD*"iC[;*=3`oP1[!S^)?1)IZ4dup` E1r!/,*0[*9.aFIR2&b-C#soRZ7Dl%MLY\.?d>Mn 6%Q2oYfNRF$$+ON<+]RUJmC0InDZ4OTs0S!saG>GGKUlQ*Q?45:CI&4J'_2j$XKrcYp0n+Xl_nU*O( l[$6Nn+Z_Nq0]s7hs]`XX1nZ8&94a\~> endstream endobj 208 0 obj <> endobj 217 0 obj [/View/Design] endobj 218 0 obj <>>> endobj 213 0 obj <> endobj 212 0 obj [/ICCBased 219 0 R] endobj 219 0 obj <>stream HyTSwoɞc [5laQIBHADED2mtFOE.c}08׎8GNg9w߽'0 ֠Jb  2y.-;!KZ ^i"L0- @8(r;q7Ly&Qq4j|9 V)gB0iW8#8wթ8_٥ʨQQj@&A)/g>'Kt;\ ӥ$պFZUn(4T%)뫔0C&Zi8bxEB;Pӓ̹A om?W= x-[0}y)7ta>jT7@tܛ`q2ʀ&6ZLĄ?_yxg)˔zçLU*uSkSeO4?׸c. R ߁-25 S>ӣVd`rn~Y&+`;A4 A9=-tl`;~p Gp| [`L`< "A YA+Cb(R,*T2B- ꇆnQt}MA0alSx k&^>0|>_',G!"F$H:R!zFQd?r 9\A&G rQ hE]a4zBgE#H *B=0HIpp0MxJ$D1D, VĭKĻYdE"EI2EBGt4MzNr!YK ?%_&#(0J:EAiQ(()ӔWT6U@P+!~mD eԴ!hӦh/']B/ҏӿ?a0nhF!X8܌kc&5S6lIa2cKMA!E#ƒdV(kel }}Cq9 N')].uJr  wG xR^[oƜchg`>b$*~ :Eb~,m,-ݖ,Y¬*6X[ݱF=3뭷Y~dó ti zf6~`{v.Ng#{}}jc1X6fm;'_9 r:8q:˜O:ϸ8uJqnv=MmR 4 n3ܣkGݯz=[==<=GTB(/S,]6*-W:#7*e^YDY}UjAyT`#D="b{ų+ʯ:!kJ4Gmt}uC%K7YVfFY .=b?SƕƩȺy چ k5%4m7lqlioZlG+Zz͹mzy]?uuw|"űNwW&e֥ﺱ*|j5kyݭǯg^ykEklD_p߶7Dmo꿻1ml{Mś nLl<9O[$h՛BdҞ@iءG&vVǥ8nRĩ7u\ЭD-u`ֲK³8%yhYѹJº;.! zpg_XQKFAǿ=ȼ:ɹ8ʷ6˶5̵5͵6ζ7ϸ9к<Ѿ?DINU\dlvۀ܊ݖޢ)߯6DScs 2F[p(@Xr4Pm8Ww)Km endstream endobj 211 0 obj <> endobj 220 0 obj <> endobj 221 0 obj <>stream %!PS-Adobe-3.0 %%Creator: Adobe Illustrator(R) 16.0 %%AI8_CreatorVersion: 16.0.0 %%For: (Anders) () %%Title: (Vector Smart Object.ai) %%CreationDate: 11/19/2012 10:48 PM %%Canvassize: 16383 %%BoundingBox: 503 534 557 577 %%HiResBoundingBox: 503.0342 534.4507 556.8789 576.6245 %%DocumentProcessColors: Cyan Magenta Yellow Black %AI5_FileFormat 12.0 %AI12_BuildNumber: 682 %AI3_ColorUsage: Color %AI7_ImageSettings: 0 %%RGBProcessColor: 0 0 0 ([Registration]) %AI3_Cropmarks: 0 0 1000 1000 %AI3_TemplateBox: 499.5 500.5 499.5 500.5 %AI3_TileBox: 202.3999 79.1299 797.4199 920.9902 %AI3_DocumentPreview: None %AI5_ArtSize: 14400 14400 %AI5_RulerUnits: 6 %AI9_ColorModel: 1 %AI5_ArtFlags: 0 0 0 1 0 0 1 0 0 %AI5_TargetResolution: 800 %AI5_NumLayers: 1 %AI9_OpenToView: 516.75 571.625 32 1554 900 26 0 0 83 122 0 0 0 1 1 0 1 1 0 1 %AI5_OpenViewLayers: 7 %%PageOrigin:0 0 %AI7_GridSettings: 72 8 72 8 1 0 0.8 0.8 0.8 0.9 0.9 0.9 %AI9_Flatten: 1 %AI12_CMSettings: 00.MS %%EndComments endstream endobj 222 0 obj <>stream %%BoundingBox: 503 534 557 577 %%HiResBoundingBox: 503.0342 534.4507 556.8789 576.6245 %AI7_Thumbnail: 128 100 8 %%BeginData: 9595 Hex Bytes %0000330000660000990000CC0033000033330033660033990033CC0033FF %0066000066330066660066990066CC0066FF009900009933009966009999 %0099CC0099FF00CC0000CC3300CC6600CC9900CCCC00CCFF00FF3300FF66 %00FF9900FFCC3300003300333300663300993300CC3300FF333300333333 %3333663333993333CC3333FF3366003366333366663366993366CC3366FF %3399003399333399663399993399CC3399FF33CC0033CC3333CC6633CC99 %33CCCC33CCFF33FF0033FF3333FF6633FF9933FFCC33FFFF660000660033 %6600666600996600CC6600FF6633006633336633666633996633CC6633FF %6666006666336666666666996666CC6666FF669900669933669966669999 %6699CC6699FF66CC0066CC3366CC6666CC9966CCCC66CCFF66FF0066FF33 %66FF6666FF9966FFCC66FFFF9900009900339900669900999900CC9900FF %9933009933339933669933999933CC9933FF996600996633996666996699 %9966CC9966FF9999009999339999669999999999CC9999FF99CC0099CC33 %99CC6699CC9999CCCC99CCFF99FF0099FF3399FF6699FF9999FFCC99FFFF %CC0000CC0033CC0066CC0099CC00CCCC00FFCC3300CC3333CC3366CC3399 %CC33CCCC33FFCC6600CC6633CC6666CC6699CC66CCCC66FFCC9900CC9933 %CC9966CC9999CC99CCCC99FFCCCC00CCCC33CCCC66CCCC99CCCCCCCCCCFF %CCFF00CCFF33CCFF66CCFF99CCFFCCCCFFFFFF0033FF0066FF0099FF00CC %FF3300FF3333FF3366FF3399FF33CCFF33FFFF6600FF6633FF6666FF6699 %FF66CCFF66FFFF9900FF9933FF9966FF9999FF99CCFF99FFFFCC00FFCC33 %FFCC66FFCC99FFCCCCFFCCFFFFFF33FFFF66FFFF99FFFFCC110000001100 %000011111111220000002200000022222222440000004400000044444444 %550000005500000055555555770000007700000077777777880000008800 %000088888888AA000000AA000000AAAAAAAABB000000BB000000BBBBBBBB %DD000000DD000000DDDDDDDDEE000000EE000000EEEEEEEE0000000000FF %00FF0000FFFFFF0000FF00FFFFFF00FFFFFF %524C45FD2FFFA0756E7575FD7BFFCAA1CAA7756EFD7FFF7575FD7EFFA86E %A7FD7EFF75A0FD7EFFCA4ACAFD7EFFA16EA7FD7EFFA14A75A8FD7EFF7575 %CAFD7EFF7575A8FD6EFFCAA176A0A1FD0BFF7575CAFD6CFF766E75A07574 %A0FD0BFF7575A8FD0FFFA8FD5AFF7575A7FFFFFF75A0FD0CFF7599FD0DFF %CF76997599A0FD56FF7575A8FFFFFFA875A7FD0DFF6F75A8FD0AFFA16E9A %A1CAA1754A99A7FD52FF7575CFFD04FFA175FD0FFF7575A0FD08FF996ECA %FD05FFCF767575FD50FF756EA8FD05FF75A0FD10FFA1756EA1A8FFFFFFA1 %756FCFFD08FFA7754AA0A8FD0EFFA8A1FD3CFF7575CAFD06FFA76EFD12FF %A76E75A0A77575A0FD0CFFCA7575A1FD0CFFA16EA1FD3AFFCA6E75CAFD08 %FF9A4AA0A7CACAFD0FFFA1A075A0A8FD0FFFA0756EA1A8FD05FFCACAA075 %6EA7FD3AFFCA6EA0FD0BFFA775756E7576FD23FFCFA775757599FD04756E %75A0FD3BFFA04AA1FD0EFFA8CFA1754AA7FD24FFA8A7A1A7A1A7A7FD3DFF %9A6ECAFD13FFC96ECAFD65FFA8756FFD16FFA16EFD64FFA87475FD18FF99 %A0FD62FFA76E76FD11FFA8CAA8FD05FFA174A8FD17FFA8CAA8FD46FFA775 %A0FD10FFCA76996E7575A7FD04FF75A7FD15FFC975756E75A1FD44FFA16E %7CFD0FFFA8A06E6E4A756E6E4A9AFFFFFFA075FD13FFA8756EA1A8FF7C75 %FD08FFAF6184FD38FFA174A1FD0FFFA7756E7575A7A8CFA0756EA0FFFFA1 %75FD12FFA17575FD04FFA775FD08FFAF143CFD37FFA16EA1FD0FFFA06E4A %75A1FD05FF7C6E6ECFFFCA6ECFFD0FFFCF756EA0FD05FF75A0FD08FFA861 %84FD36FFA86EA1FD0FFFA7756E9ACAFD07FF6E75A7FFFF99A8FD0DFFA8A0 %6E9AA8FD05FFCA75CAFD41FF7576FD10FF754A99FD09FF754AA7FFFF6FA7 %FD0AFFA8A16F6E75CAFD07FFA76EA87CA075A1CAFF6060A9FFA7A0A1FD34 %FF6ECAFD0FFFCA6E75A7FD08FFCF6E75A8FFFF75A1FD07FFCAA7757575A7 %CAFD09FFA775759AA0A16FA0AF3D14AFFFA77575A0FD07FFA852A8FD29FF %A04A75A7FD0DFF756E75FD09FF766E6FFFFFFF6FA7FD06FF7C6E6EA1A8FD %0DFFA8FD04FFA16EFF8485FD04FFA1A0FD06FFA8F8F8FFA8FD09FF75CAFD %1FFFA075A8FD0BFF76756ECAFD08FFA8756EA1FFFFFF75A7FD04FFCF7575 %A7FD15FF6EC9FD0FFF27F8FFA774A8FD07FF7599FD21FF6EA7FD08FFCFA1 %6E6E4AA1FFFFA1A1CAFD04FF754A75CAFFFFCA6EFD04FFA74AA0FD15FFCA %6E763C85FFFF6EA7FD08FFA8F8F8FFCA74A0FD07FF7675A8FD20FF7675FD %08FFCA756E746FA7FFFFA06E4AA7FFFFFFA16E6EA1FFFFFF75A0FFFFFFC9 %6EA1FD15FFA76EA0851436FFFF75A1CFA8FD06FFA827FFFFA175FD07FFA1 %74A8FD20FF766EA7FD07FFA8754A6E76FFFFFFA06E6E6EA0FFFFA74A6E76 %FFFFFFA06EA8FFFFA74AA1FD12FF7DA8FFA16EA7FFFF60AFFFFF7C756EA1 %FFFF7D27F8FD05FF6EA1FD06FFCA7576FD20FF7674A8FD08FFA06E75A7FF %FFFFCA996E756EFFFFFF6E7575FFFFFFCA75A1FFFFCF6EA7FD0CFFA87D27 %2752FFFF27F827F87DFD08FFA8A15252F8F827A8FD04FFA775CAFD06FFA0 %6FFD20FF7575A8FD08FFA84A6EA1FD04FFA74A756EA0FFFF756E6EFD04FF %A075FFFFFF6EA0FD0AFFA87DF8F8F82752A8FFFFA9A82027F8F8275252A8 %FF7DF8F8F827277DA8FD06FFA075FD06FFCA6EA8FD1FFF7575CAFD09FF75 %6E75FD05FFA76E6EA0FFFFA7756EA7FD04FF6FC9FFFF7599FD09FFA827F8 %F852A8FD07FF75A0FFFF7D5227277DFFA87D7DFD0AFFCA6ECAFD06FF75A0 %FD1FFF7575A8FD09FFA8754AA7FD06FFA1A8FFFFFF754A9AFD05FF7675CA %756EA8FD07FFA8FF52277DFD08FFCA6E75A8FD07FFA8FD0CFFA84AA0FD06 %FF7674A8FD1EFFA075CAFD0AFFCA6E75CAFD0AFFCA6E75A7FD05FFA89A6E %A0CAFD06FF7DF87DFD0BFFA76EA1FD15FFA16EA1FD06FFA16EA7FD1EFFA7 %6EA1FD0BFF7C6E75FD0BFF766E75FD0FFF52F8F87DA8FD09FFA8754AA8FD %13FFCF7C6E6ECAFD06FFA16EA1FD1EFFCF75A0FD0CFFA04AA0FD0AFFA875 %6EA1FD0DFF7DF8F87DFD0BFFA07575FD12FFCFA0756E9AA7FD07FFA76EA0 %FD1FFFA06EFD0DFF756E76FD0AFFA04A75A8FD0BFF52F8F87DFD08FFA8A7 %7C9A6E75A1FD0FFFA7A775754A75A0CAFD08FFA06E7CFD20FF6ECAFD0DFF %A06EA1FD09FFCF6E75A0FD0BFFA8F852FD07FFCAA175756FA0A0CAFD0DFF %CAA09A6F7575A0A1FD0BFF7575A7FD20FFA175FD0EFF766E75FD09FF756E %6EFD09FFA852A8FD09FFA76E75A7A8FD0DFFA8A775756E99A0A7A8FD0BFF %A8A06E75A8FD21FFA076FD0EFFA76E75A8FD06FFCA75756ECAFD08FF7DF8 %27FD09FFA875A0FD0EFFCA76756EA0A7FD0DFFA8A7757575CAFD23FF75A0 %FD0EFFA8754A75A0A8A1A176756E6E4AA7FD08FF52F852FD0AFF9A75FD0D %FFA8754AA0A7FD09FFA8CAA1A075756E756EA0A7FD25FFC96EFD0FFFA775 %6E746E746E746E7575CFFD08FF7DF852FD0BFF75A1FD0CFFA06E76FD07FF %A8A776996E99759975A1A1CACAFD29FF7575FD0FFFA8A075756E7575A1A7 %FD09FF7DF852FD0CFFA74AA8FD0AFF756EA1FD06FFA7756E6EA0A1A8A8FD %30FFCA996EA7FD1EFF7DF852FD0EFFA16EA1FD08FFA16EA8FD06FF9A6EA1 %CAFD37FFA14A75A8FD1BFF7DF852FD10FFA76E75A0FD05FFA875A0FD06FF %756ECFFD3BFF7599FD0EFFCAFD0DFF7DFD13FFA1756EA7FFFFFFCA75FD06 %FFCA6EFD3EFF6EA1FD08FFCAA16F756E756E75A1FD06FFA8277DFD17FFA1 %4AA0FFFF7C99FD06FF75A0FD0FFFCFA1A0759A75A0A7FD27FF75A0FD07FF %A07575A1A8FFCFCAA099A7FFA8A85227F8F8A8FD19FF6EA1FFA175FD06FF %A076FD0CFFCFA0756E9975A0A0A075756EA1CAFD22FFA8756EFD07FF756E %A1FFFFFFA8A8FFFF2020F8F8F827527DF8A8FD14FFA17575CAFFA16EFFA1 %75FD06FFA075CFFD09FFA1754A75A1CAFD06FFA8A76F6E75A1A8FFA8A7A8 %FD1AFFCA9975FD07FFA199CFFF7D27F8F827FFFF52267D7DFD04FF2752FD %13FFCA756E7575FFA199FFFF6ECAFD06FF75A0CAFD05FFA7A06E75A0FD0D %FFCAA0FD04756ECAFD1AFFA16EFD08FFA0512EF8F8277DA8FFFFFF6EA1FD %06FF52F8FD13FFCA6E746E75FFA076FFFFA06FFD06FFCF757575A0759A6E %7575CAFD12FFA8CAA8FD1CFFA09AFD07FFA8F8F827A8FD06FF7575FD08FF %F87DFFFFA09A76CFFD0DFFA16E75A1FF75A0FFFFCF7575FD07FFA8A1A0A0 %A0CAFD0AFFCFFD29FFCA6ECAFD06FF7D204ACAFD04FFA8A04A75A8FD08FF %52F8A76E757C9A75FD0EFFCFA8FFFFA74AA7FFFFA86E75FD15FFA175A8FD %16FFA8A87D7D2752272727FD0AFFA075FD04FF7DA8FFFFA06E75759A7575 %75CAFD0AFFA8F84AC9FFFFA775FD13FFA16EA0FFFFCA9975FD13FFA875A0 %FD16FFF8F8F82727FD0552FFFF52A8FD07FF7575FF84F8F8FD05FFA1A7A1 %CAA8FD0CFF9A4ACAFFFFFFA075FD14FFCA6E99FFFFCA996EFD11FF7C6E76 %FD17FFA8A8FD09FFA827F852FD07FF7526F82EFD16FF9A6EFFFFA8FFFF6E %CAFD15FFCA6EA1FFFFFF9A75FD0DFFA7A06E75A1FD25FF7DF8F87DFD04FF %7DF8F844CAFFFFFFCFA1A0FD0575A0A1FD08FF756EFFFFFFF852A775FD14 %FFA8A06E754ACAFFFFA8757CFD09FFA7A06E6E75A7FD29FF52F827FFFF52 %F87DFFC96E7575996E7575A1A1A7A1A176756EA1FD05FFA06EFD05FFF827 %6FC9FD13FFA075FFA87576FFFFFFA175FD07FFCA757575CACFFD2CFFA827 %F827F8A8FD04FFA1A1A1CACFFD07FFCFA76E75A8FFFFA76ECAFD05FFA8F8 %F84A9975FFFFFFA7CAFD0CFFA1CAFFFFA075A8FFFFCF6ECAFD05FFA06EA1 %A8FD31FFA8F87DFFFFA8FD11FF7674A8FF6FA1FD08FF27F84BCAFFFFFFA0 %6ECAFD10FF6EC9FFFFA875CAFD04FFA16EFD38FF52F8F87DA8FD0FFF7675 %A175A8FD05FFA8FFFFFF52F827FD04FFA06FFD10FF757CFFFFA76EFD05FF %6EA7FD39FFA852F8F852FD0FFF75756ECAFFFFCAA175756FA0CAFFA82727 %FFFFFFCA75CAFD09FFA8A0FD04FF75A1FFFF7CA0FD04FFCA75A8FD3CFF7D %27F852FD0EFF6E756E7575756FA7A8A8756EA1FFA8A8FFFFFFCA6EFD0AFF %CA6EA7A89A7575A0FFCF75A1FD05FF6ECAFD19FFA87D7D527D52A8FD1EFF %7DF8F8A8FD0CFFA075FFA1A7A8FD05FFA075A8FFFFFF27A9A199FD0BFFA1 %6E7575CA6ECAFFCA6EFD06FF75A7FD15FFA87D2727F8F8F827F8277DFF7D %FD1EFF27A8FD0CFFA76EA8FD09FFA8FD04FF28F87576FD0DFFA8FF7699FF %FF759AFFFFA8FFFFFF6EA7FD12FFA8A8FFA8F82752A8A8FD06FF7DFD21FF %27A8FD0AFF75A1FFFFFFA176CAFD06FFA8CAA1F820FD0FFF7DF827FFFF99 %A1FF757575A07675A8FD11FF52F827FD2EFFA852F852FD09FFA76EFFFFA1 %4A6E4ACAFD05FFA76E9951F8A1FD0DFF7DF8F8F8A8FF75756E9AA8CA7CA0 %A1FD0FFFA852F8F852FD31FFA8F827A8FD08FF75A1FF75756E6EA1FD08FF %C9F8266EA7FD0AFF7DF8F8F8A8FFFFCFA0CAFD14FF7DF8F87DFD35FFF8F8 %7DFD07FFA16EA8CA4A6E6ECFFD07FFA06E51F8A14AA0FD08FFA826F8F8A8 %FD18FFA827F852FD38FF27F87DFD07FFA075A8FFA7FD08FFA06EFFA9F8A8 %FF6EA0FD06FFA175757D7DFD19FFF827A8FD3AFF52F852FD07FF7C6E76FD %07FFA89A6ECAFFFFF852FFFF756E7CCAA7A16E75A0FD19FFA8A8FFA8A8FD %3CFF527DFD08FFA8996EA0A1CAA8CAA07575FD04FF5252FFFFFFA1996E75 %75A1CAFD19FF52F852FD42FFA8FD08FFCA76756E756E75A0FD0DFFA8FD1A %FFA8F8F852FD42FFA8F852FD0BFFCAFD2AFF7DF827A8FD44FF7DF827FD13 %FF7D27FD1EFFA827F87DFD47FF7DF827FD07FFA1FD0AFF7C20FD1DFF7DF8 %F8A8FD49FFA8F8F8FD06FF9A6ECFFD08FF766E6FCFFD0EFFA175A1FD0AFF %5252FD4CFFA827F8A8FD05FFA06ECFFD07FF7D27A075FD0FFFA07475FD06 %FF7D52FD51FF52F852FD05FFA06ECAFD06FF5227A175FD10FFA8754AA7FF %FFA827F87DFD52FFA8A8FFA8FD04FFA06ECAFFA0A1FFFF5227A075FD12FF %A76EA752F827FD57FF27F87DFFFFFFA06E9A4ACAFFFF275275A7FD13FF51 %F8057DFD58FFA87DF8F852FFFFCA75CAFFFFFF274B99FD12FF7DF8F84BCA %FD5CFF52F8F852A8FD04FF7DF8A16ECAFD0EFFA8FFFF5252CA6ECAFD5EFF %7DF8F827FFFFFFA87DFFA06EA0A1CAFD09FF7D27F87DFFFFFFCA75CAFD60 %FF7D52A8FF527DA8FFFFC975996E99A8FFFFFF7D52F8F8F8527DFD04FFA0 %75FD65FF7DF8F8F8272E7D52524ACAFF27F8F8057DA8FD06FFCA6ECAFD67 %FFA87D52522727F84BCAFF52A8A9FFCAC9A0A0A0CAFFFFA1A0FD6EFF9AA0 %FD04FFCA757575A0A07575FD71FFCA6ECFFFFFA7754AA1A8FD76FFA175FF %CF7674A0FD7AFF6E756EA0A8FD7CFFA7FDB7FFFF %%EndData endstream endobj 223 0 obj <>stream %AI12_CompressedDataxg(|??`hrɹ9@0fo%ـmh=g^`J**Jc=1 )ہkKOy! YSX. 25aD!0`[v)z?߬C4 7%cȬ1oaI_iH W97QPiZ:]8x+&>w Ln|=Mnxqh^y 9p[:<^4 9|.ތ+j}ӛߧ6 iRkM'Z.7$hI|I"ȃp!$ k<Ηq5}|=@P;{@Aa~jQ4@H%}>t& g>A7[@ŞiG8}ܦV% 6'tx]p5s.; jAB|pG0Fu!ǜWHSݬ)> bf]G㒢;ه~ 2lq]/ۢMS&~<X$?)8Mu x''x5n^G}u̬댛e1@p :=סS?=_f?h,?ˀ>pXQiA8Yأ8e hO/_3e~5_UY`P_9n L`81 @i0h֬4S. ռ&`1^0fP͖ k^Izx}IIZa'$ 7U`lKL#U /j=ޟ3_g~TjY3@/*{wumt>gfi>L2}` 97C}r>l>/H]U\4BTtFv ω29ėlֿhː&9N Ӵuj !MkFnϰe; vEw3h\R@S9KͿ&i;pӠqa0?vmB*uݽ@nIGnЛu zjz>I Zn}4cS!KR_}>h~x ~zI{4S܄ּ%Mk]+O?TS|[J5'Р ǰD/AׄCg:bBZ&R g@\@HC~Pz AG2h ^L1uTj]I!=~KgH8;S#1qܷ4p.9np@|;=hp%̇k!:?zzh4?As||]^t1Ls?"4o$ xq*A_>.$8VC`Xٗ21'ƫKpaԇ-ܬDx RNYޜqM,2Kg29 vq&#7+g=*fLI<kL^-נ<xAM`n4/4f[Hu~ Rca' = >xn %f۱w\<)T`(U?0g"2QjOM ?>>x]||1:bn]a7q%3UUû~&͑Q(Lj)?HzIެ)5TZnF jL bQ.'fgZԡFb :09 16/Bb{0<o ,ړ0T_jn@n{hE1t&xwgtc6bjpZp?LO{. f>.PV;d=aI%g'pCc n||G-'h~ 8{Z7M1ӓf?F]Π7UGi6VSD BP%W%  6!O 4oIO?5Sz>b4.Fm4ufZvyntSŶ>(6UΜLmMB.29Ԟ4mJ4 5WKEsHhB0Aes/~bt8 i@pбq+a{<(qݿ2aC}y6>;[r</do7a;r9?\۹bsJ_dXIFut@)W,vD(29vSnkY=oY2}bj[rt ɦfC&=u8T\~8s\K*usI }U\43\&܋49 -kJGj[tr(I*|4ӛ|I?\k r_Kc^jHU*XCfP9,d{?(z * o-6o`mL8s,,L]Qܗ̓ h E☘*d^˓Fg˕ӓzp}~ŞXR!)nUQCHj=8KzCfj dʁ3=.p!BTZVDJ LcH^[7ȉ.1jŶj1x|q L[Nwð  |s`B.G[?~~MOtr^Ο)2wk :9r-#}g. Y~1Z/tfd]U aRm<6V$>pǥԘTQh}鉊ܞ/$"hKO6kYq*u j)7{`BW͚W*/襵%1B%hw[Q[ cAKA36q1}mBS1?B!29'bvi(4*P+ B;A@/y[U<):2훹Igw9DD4N3=A#ͯvK 1υÌmCS{np^S:EOso# D¥I49Ckd0o,ר1LيnC͞ӹ,4]c,f3Ssұu.bKeȟ͸*=p|IOms n+l7lV; aOU@pa!i̺B202n!UGud<՗-`JiEyPi {r= 7Dh!D`Lv~UO`&d0/Az] R)E|EΨsN4 10Fb^&*x6.u!粣W+ZA'D/kbt,YLƨ`g0^I]EOXOssYMm¼1$FBdɄ]êhZrs:!4 -cb FocF4',"g M_0'f c ?nVDs2-D[Zg/2s@DZX }$;8-0^Ǭ[c)(n;o`Mhtr6^<M`SmI^v.J4׭Nu(2ֲƃh/%jdY6յ:㏕cãN3*6E- kl:En_h&,@9ήC޵+qr- 4ӣJIcrR1vi?%IW zKFcD-y?>gxOk!Yb~s_&m-{PYh8v }fLͬ)< 'UYwd/2iyn2gy)c%t<.7t5J)05=ӹwxy*B %aXN˜7ғ5v623RD,T AF#g=.Y]*pbdy_U@]hT[l<>=ƀa/ۋ'- N>pnR/,d`b}rY!'(vypjCRES(ѨY][G%w' m<\zsMv;X M(^f#`XO}f%p\ޥ6<90x+ϲZev3ܴ}Z\h*_ɸ˗09=x :k/ٴe, UD8p˜e,fpnb1hPgqYm6NDn3O;IsF!Ilr(+N_^]U-.4#UOvtr77ᨰ A=vxu7Oi|P=܀@cclɇ=?[$iH.PD6Wv7OxҕD]3nG+x('}/\꾜FI#g0j֓Wj: (5_WD:|sbzhH#inbAmBm }FdHfroṕ -0BWs5Is0i14h e^ ge6Fɧ_\eȘmm97Hd 9 z+0$.Rf>mOkw_Hl 5nA%C1yv/QXYBh*pO27P?a #x"X5k~wb-U*hxЯ-ïYFZ$X\>X#rE1T@j."NƆs5ZlDS{WPm/MĒ>֛.K(Z̀:&t.J؎l? įYb#ہ/]`z| p-&3i+ltgMU"8zo=@μRӸ;,J:.vL[TG8ʐA/*^lEcJv2e !}I+.UN̓( XCǺjk%dBp8_4}^0i|(g{>A[Ȇ+iGm0ogd-fXG=4Kg.+vc*^\ٰM{9ʷIT3Xp;aJO;Mowy7ͫ;BÍZ=:v\ K  tq~WAi@W2.dh(3 چ^.h+ϜƱI^o`<4+T0ȼeB0oYqK%=$pCqQ)ĩ#L¸qHM,O-0;O%BS`(`'E*bזŌ[¥uc}D2:s"¹x<\D,Nf˨wߵ>p*J%9xz| tś)=1luC'ͼo[w¹qn`$xȄhג30z-7iӥbo"}ed $ɗ j_g=7RmV؜:q,r@+3l'^Sa.dg!v_g͈,vZ2h:`L>#l߆*MvDE2ww|L sH5iB]E&vFMrH1Dyg6}L0Ctv6'ም5[ @ص%,І65gaMfJO9mֱfI47o_lg^pkH1RzI Z]B^@!b;K1=|` 2ybs{V`]%Q!UWt\uc'X[VOwhEls4n@"/CƐӲ:=($f%2ѱ'J`J13:jZmpv`놄9jg6UβFu `%6ҽ}"٭\Hs6d9}6:Mz}vVzyxΌ1JKxu?8hEx1?ۏv?8N;$w ){]h޴q2 qcY!W^R!mpe0{9[+Dj}ằZF4l_#R +-p: Col?.eKY6mnv’.Nrwax} >Vu׋ M(nMnGɈuq߀53Z)!#_o  sǫY=h7% ɏ;j>)zMؗ^H.14{M9fAvZKdY-^vZ.7RK۫_(?$$:n<4j=I**>%jqJBsmK]sBМ8&ŝtnf>>Qbtt=a~8kB\邻J?./_m®bӜ?hv&Z̏4N im"/<)4/#I6Z/WADsbHf:1"1l,;m< 7Cal$[|_ q1QzM|X`7Qfry'}{phEI̸̚&A#Sf˗;Tb‰eg/a:3at ӎ?~SRQ}uX¾^66[mMƱ26ېla*65Gۍ^wkyɊ֐[GkMZ^+dX&*+F Ѥ _ژhN5[lzikG\6Ρ־WbZ@;\ԓpwnnV=;>E? <-_^G:mV^Otχ.]-tՕ_Q]ѵvݻl[VWѭnoHLɳі>[3.<{95=J[9&yw\z}=9g2|TJ=z,|ez[_zJCªT 1}Ycf}ߵ7}?sg]o'q1nkÆP4 $k%Cm6 ᣑH|1L)a~uvѰp#eL6c_ܴ5q{WxX_44Ƣ1 |A_ LlT2^ /rde2LdIk6&ٙr9f~.7z5oykC42;aY|,,y3ei6}C-%eY{fi5=+XyК'֪-Vh+?z64%mlj7>;F{WwFm5\ڵnǬvȞ'W{i9ػ/b_83q;ϊg.qWGyvNޱ,Nmqڛ͡30ycſtnGwv:w7-/ŋNӯkLe f'mgD+DgwC"!zץ.Whu6꿻, ~׮3)w]'^9)Ok z Uy<<޳w?օ \*v5wdޝY,_IgnҴct&D_zwcLo,*J5緁R* | jqWE 62Hz+?,F(b@|Bli&\ZnžT.Np;+ǯEc@ܤF⩟y>"dM>T4fUr6ϥulXd)|kxSXԶ _-;k9g8 796ٍQ Mcb&gon\U;Ŗ2tZ9gs6H2dرN|/~VfJwb-@N:2o|f5ߦO:ާ+GhV~hBU\$&{g=ؗ9:¾Rpa=<NUmKCt1dfopNw0" /%}%?y,p^v @G=9ڔ g#fSĽT=psFqS]Zt`3wc՝l7:Z鏯>*] N-dVڷUlV+[\MA:c$#'æwnfXdkm5nXckҘAqGK,ެyWٗdOQha֊rqXYco;tNkn}i' ~iI0Q2=c.kafw>ٝFXЅYc?m6Ͼ'c=ZxŅ;&ߋ ޵ޥw̝ ]N< [x&lj1^ c|Ց|xOdh":", E~mphhfuhqo6{1|̇ք'_ 4X0s@FzƴӦ} zkn @{2PC 7wax@Ĩ]&,q8Zc9p ճiRXto0k GE;}ԥuˡCk$6X(Yj < >Q "4&}5QOyŰ(/g)2\wfwW=\ͫqb֍FAN:g+a?t/ݭCk<G.uuRXGt?@cf,X`>^Mf_\kCn /;UQ, pܩ-5X}:7}>v j}F{Ԭ#5t%jcv^3_,oh!bpiBBb}c{ Vi~$ZQ@kmI%4%nkӊʻ8֒_f_0Q¼p 熏U񵿥.c-u MKk[)t ahXrRV1Ӵ|WmDX7ԋi*giqpvۡQJU:td VR{]"#;CĶ+iϼ(-<ͻźZXOah\wM9T(]%ğz"k5mH5Y#U4e4YH4n[}dVq2_T$:4B΃1mߑD7mızߤjc+TkgxlH`Fsm^!~`ك|[`l0.X}Ⱥ?cuY9|š[(5qzZ1}&XI`'+ız?7GK+r-$Bb| GCLIc ToX"Ia)6V'C퐰aU|յ3יvPn0AǏ'qyg]d3 CFΜ{Yq!l?a 6.ohrbwa;-V+ WG 5Cܣ4. a M:uՠgo Ɵi"ؘ ;fNc=V0b s -_о<-ĭWP#inŗB͕zmy!f$8w'ěv |(T+BWW6i GC:N 8@І{IhJv*F-QF 2:8XœhLf]cmSĬ?l5!-iB}bN߬}')ǧ0k 12,]M"@+=y)Sk`\o{)M%jMM "Y|Mz9|6UUۢ bJM}B;+,wlIz>eU(@ųSӨ`0Hc Fc\ymbܽ=h -ÜL#@L46vb;wJ%rͬw+9[W\gN^^qoQ i}=-Yb60~wH3dr9 4p7ȌFRB0UQғ!%y6pMIz0 u!Qb#=Q(<=u< 31|˦_cHS"P[>tjE j# ESP/ GxFGs3ᕬ!z6EgS@X?p?sqNuR xD$GAH]bBPP^7.v+qRkž*r?ZU٠ê( ^rF0r )a3*_Q;@+MwsZx xA".։$fK~ )ki_ötS"l!ÆB+YJ/^% | z 9Y"P댜M])~3GY}ᇷ/w7W(5P&FE@_$4\<Ľa|8sfN2 MaGײ ]Ewy]9Pg,b|k߽=63)Rurx1\Y o!m(o`pdI7JDIAh7I7  wKoyN4E@jĉ<&:F(w)#}cx*&2iV=%vq^ ~Qy%"0g f2TetFJ{g=aMY^bJB6BL\@}+t0" I_@{ݛU@943S$# E9rFsV~$e>8o .jZbQ*r +>Dct6b/*w''HGxvʀI(ń ߮ecȄ1dɐH4L|qԬKj< ]VDM<+Sj 4E׹d"?I^>|ŕuv؟b۰\M6-''[cL̍s%5%HPt7548! o3aEƏLRIm6啛 4^!Jk DG.CZh~rb%t4YNs#q or5;n/V]r֍l9϶c hSg{-Uк{cpO9|-ӥtr>VZ>n]||O{k||O{k/瓯c||Q'_ǎr>~L9~pS2v*&@ +^[lZBZ 40v o_)LR թ:ʦ@SUŊ"># tA|*'N~L]2} l[n;}xGA>u=9M>$p\|☺nQq {7N[޸Cg1O'lTzۣ7qU $2NeI(Twoù9A%UDouX+(QUtl n*Mp SBi*C$stKd0uFWQ̪س%KW2Xj2B &Cc,SP⧦Uv32q:5i'IM -@`7i&)o]2Oଢ଼]'vI*W] $8Ϝܴkt׋5Dg ^(oj>1of) ̖n‹[6fi'|S6Ma;~V9E7 nO)zLE՝8Ҽyýb_vg>7~\].D.էCYO~};+D~P.dkě+O~ޙ%jJ˕Mq'=t~2Xsxou kE$VKHQ&[ ֎OXUdK5֢ q6G}1Q/"L+sV6n놕0?_UqsV2N%`+.ztFtq5g(Lzbӧ6lW7j)?ʷrev^"lwz#|R O5#|; UXQKx'nX7RE*nTVS>1֘>1/A(" ~daXUZ7-c -KOHG)fB>OlGb>2U}2+-}taL# Ԟuga'\'zWarb"P-wA}bU}Gy1Qҏ*la|χ]_͵ʯ tڙ7,=%)>96tAo Ć99I^ݺGdB!SZ+T;u⧀|D+E!09q[b"WHx0U eytgEͤկxܕ{Jw:evKVէVȐwt2.ub0f&{ e%,g^Wy߉(*kqD]q4ԗJI\wk!X/u}Ձb" 5ɤ\xlK9Mqw5QI>pX'xkv7o *If_%(^'<ا@.I Rǯ%ퟘM4, i>vӆꫮ/K]oIBcRs,+5IAPx tN~?nnB>$QjtUC)F/@Tݏ,b$hU$Z#j)9.s`a*嗅1^yA o@wsq/F1.PGoc @@@2Hł"RBqf1ɮ#M7_'½w}+T޿.yߍ??ƿq]lnOM?AXW>= NP_KhE]3CVKho_^<iNP,<_<RRBEPyvq=oQu ϲR]g2F ـˬ ^ZJGXNSh39B^byh SE!r{hy?PR!C&cRb@dknƭ B1$x./nSTPxy/SvϩZD{ee+Wr7JerU' _*{eO,㘷ENByOӮb wXj؆u(3tDc?j=/XCӺBAʼ YGM5g\M'rZC 7BI%@#)c9f&[k%UvϫXyfN=8.Ix6W%t͕/Õ)fi9wW2g@$!lJ;1L +@"{6oj ?N91^i?2_]lueq%G<-'ԕ$ !UɒD#H#[Sj29`iTzshɏZ!nPQOj+usA]^29OLῺ"ޫ#IJ)O̱9uv 8㜼IwCA[(P'OTz b)]g'} Sg@]EIoK̡`WR|յ\͡d"@+  d [rDS&h:@d*㮪ſ,ITk6ro~Q_O"Pt=>K#$㶋n(I{Wb %ʪF*ᦒD ^# [KUUn\RGkqB^VujwYRA^BL]VȆT{1 ae`r3`(kG]tGT+m4{/<鄜ˆ0MF(RWm!/0AEik.P}¹ԮS,I(ArҀJt9;b\Ƙ6]'iLe5`0j$.h|3'l \#h˅/&Sfx¼w KۏW{ׄ:f7~m~3S錓mXw8F 65|-_ No@x[akAgդ[OP#SQ"vm=o4-v[?Scs}#*NعA%=,p-c仇sA&{b()"$ZwwozX=7kmd)g'"UW{]7Ŵ0X''Bgw(/#Uߦ%JE]l׀#Ufw 8=NfoEVa-p>x Uec%wE]V(D K49$pe3J|jm t;΅ց^w?W79+&eo ‰qgeM2W;aaypȌ jS'J= A1 pϩY~yJ~t^N `M5&Mĝs)ۥ=u4Yn@:9M}ut0 XV`SI$X\ ZT[9aDچt1Ap-9ngpwۀ> |/۹D>dl$OF\➴q9xyb q#NQ{'6ni5 y9бLNzC'L6!l5q?|Éa`Fs3$9bk>ӧ;ٚQ١GGsΎz̨_T@tqurw]:UY w݁c;<ݥN|wܯOj'cK˰݃K&Fڟ:=VA?֙ߓ#!+[ (ZQFdގ\,"iolNɈBcM 5ȸfu4z[_vf9F¸V7{M uŽ Q&F*bugj{Sj :3`hr`}e7ABh^Ȇpr-Nޏ<4YDpBWNʛLJڭ,kr/ +!{]1,$i/fV_Nc*hܕ7orlԪ_ܐA3;+<be_c;bYS;#Yq99ͼPC`HycN,ïn_q MNΌlzri{+ w͊~J6j56Wk94F檇 OͷmmQ})={|}z= L*RVcsW<]SC3 _S*Frhl:}t5vCF%JpfڧfŦNMަ*g `S7!؞]*םX\UKAտWǓˏ3 ۻʁfJdvM[jߓy}8h&DًU* /@$GwUm?ϩaue BhcWp |y#( I3iQF4x<έj9-oN,?`Fٚ%_I( ?qlo󦷯JKӋ0SwI-cz0md4yxsRO;E6j TQ2H١u'+Й?Ta8/}yѭu>񬽵u*nxtFtol7`?laTW2O )vzIj41sF{ɫڤuN~DޮO. E_wx&k`=joߤ#O.5MZSǴ^VJ/Sh>oMlUN܄1q&~ՅփU=<pqnB~;pjV2n,f5MȦ =KdfwlZ/'%=K9jh+=K9ug*mI8;~sP՝rYVjٜގut{y8:_~eZ欷' 6 {I~NԷzIsFdJ3M*P~hgq~$MDkߙh7vA42}c6G"*5.'C?D5siלO=ӲCVZWL;utѫCeəMy-#1 KS]?,on{ܑhD[ryiK_ݏgvFT^ma[1K:K>G'i={a޿9_}(yf˳2Q/OSkJaOSo>M8%wn}͉e`=% c֛ |8_0UxKz)GZ- ̗_) {o-uf1@U`Oc B>'Ǐ?P >RZ968TA"}ɂ<UrⓌD9ܶwOZ#(!SFGl&/VN>ѯ87㥝O E==8 lc87~`$Tϱ]b0\aMo$j/h67 ~d E?{Ǭje&T^78|8L`5n3rތ4yȄ `qкkCKRSdi|4g>ȭjo0A?̇Ȧ'0M0rxEBIsaJ:Z@3_iXN3UVQնu{h2DKv%|#?ֽ7W0RccRG3/$~au0#+5ͮ&!V^|ZA7?tF6yz:4Vq<~oQO?kO$qSVI+.ĽVJ(`Xi$!iE@>7@KZQ 91N?| f'fFՁX@3_͈3bQ" ҫ(:ϵىj/ݕ<'QѯZ,Ĕ99zhI2O7(Zѳ[|@*<:OxtNKOq"gRA5TGWthTMMVǥkm.%/ϣ//ezw0x~‡jYD%T[(+žcgEP/2 apTr,*D@4p籖_tUz/9GWERs'"lZ%j>;.M8V@/n.x Ko;]}ӚɬTqu*5pg앲wtTFx >&y {Iԧ~찐d\:~Kc>rQ;;1OT8\~ɬI[ScO~YpuSn$sӳF_yb4;mposxyrei#N,]5+$\WaGEVeSgfFjC9Jo*y&k Kx4tq!;MIB|4%Wƙt ݒ”0&kzv WL^qsJ^Up#-@ҏ]# KcVlw YkN,SLg,e6;㜠dB_Pe~9Fa`^O+?\G0- 54ˌpC}3P+΅ Xђŕ`dxn2>)_5V׷|? [O{<]?\=e&2T..ĂIɛRv U~ wo7ӺpLקq?zonop۠'7p_?Cnw#S"s>ONkOގIKϧuƯOX}{8fm<}Ag?|6 lM!llGLk06?J>Z~{gClF=!G_0bxV>زZ'U%@oE-(>Y`nxMZwȧS7%. |X)xI>׺H"%]29C2d었I>ڱ~ Н$*f&J}p/~POh eJ Eaa- x?c/uU_W⾮cb^r蠂/m tE1irhub᾿:>1T۱\uk{O] ͇:{36k'*bZ3vh߉d-pq֎v| w)/ɕ?3+Yk콇Qw´Hz͸{'Hn:]JI,iJ͟C'ZvVP{;nuyD-?>=fbr2slO 4m6YJHooڇ1*3(}EE4m5r(e{A$ ǒ ][1Ǽ$/Sc. :+a8pZ}!PT,Y$")R#YVMjr!AVKH)Pthan`P(6g;(@}R*N>^(@lRI:QLў\E(.LC08)&KjORӠ[@99@B4od_kؓ`*M^@MA ȇ=J5M Jf=R"j96& %*G)FE`uG¶Ql\BNH\46)e([8ȃdQ!pPyalȊ8Q( HGf"LW0^蔀hL$Mf#CM 5$q_26Q8 $TP@>[W[ ?<ScD1R&8*@fPЊJ3VJi,mQo *2R`@4@qiʵR$H9(JAf[Ԣ'P9z엏;&䍃qt#2Luy4{!ܮGbvD6vC$*۱qm]yQR@lט `&@*fD%D_:r`@I`MX36dpbqp@qqVPaP5sј큗Y|L7U0rPЌ"eZbMFB68@}h)A˹by8U1 ;=R8 ĕa)`)kZ QE/%}`GorIҧ!7M.Dk-8cڨ6J?%R ='0JFb22MThQG%ELX ]GxEMjD+@ P'BWD'{VHM=cUv5t3(=&4^O'`NFVy6rDLg'?Al+)o2B}␊|IKAƎZs\'< q^.;e;;v&KvҰ BE2Cl @ڡ" [ 7/<Өw9T3q "~f6Q*"+[14'|a)1Y϶VB &Z4L`xD XH P>J[pU<0g$'J2$lib+C0˘ W#0%3Y@~3=9Dw]%:1zYnt]y(}X\@\@tfU` VmЖzmJ J<)׬znC0(m7~=G$(G6˞4 > FOn DE4a0mA$Is3#ILyd0a`\J QTws`N 3ٔoVlfuU 2ōu&;7WxXz;×7kP&IA[)OqAJJضI˄ʉ8!ŵIU6/JPąH6a{ $90G94Pf[=C 0 %]rȓdkmXN f"@ C͎=-] cbÆb4r0l0U "µj#L40{>f ?200c,簭 B] 7DhTQ(Ơ!9E:r0PL, פ5GP>51|xO!w| L=K6ѹ "!tqwR8 ,Zr9_F(YB}4qHCc3*-{&ߌYH%Vr̊mBS6sc E@9Tv O hLj=gsBy3RB45m`L*5 'A@L;ƹ'OrCoR)oW(&ݾ;vd%s W PptXėd"d$^M%ð A]€Pz(ĭ0?ˆX*p4*^b@}A\VdqQ_1gӖOAX DN큔^!GK}܌'3?21n͛T cæWWBw_cNkHKSJ0.&9A;qoN& SGD$0P(ώ0q"\.pI.~pSix#,F'@^BxhsM1p&P:܄pBz Al/f P"[!jK{(P#ࢷ&h"aQ0 (b$1Ö!mΞ1 &avZИr0OpNlh@3@\ӬO91]opQ8\1#A'9p3xE}q`R j"y<>Nj8-< )yʸ|^s\*)!Me b^Q0\uYca<Űw0isf$<)ZS #WQJP~ǵ],CI~Em1!~4&vLfh:V{-`tDTL6D4*:yϵcJ@^5I)"ie%/ %ZT񶛸ckZ7sFS;"9Iy\fe )azɷ4fɚj$A 1fTiD{N"u+z-zTSÑ2p\IAL:JmTi%y\,8+i]d, Ѫ`AؼJ{CqɓvRܡͲI y@p*NC9sZxI xhxjqv{'_*s5\=WS*z`pu(By endstream endobj 5 0 obj <> endobj 21 0 obj <> endobj 38 0 obj <> endobj 55 0 obj <> endobj 72 0 obj <> endobj 89 0 obj <> endobj 106 0 obj <> endobj 123 0 obj <> endobj 140 0 obj <> endobj 157 0 obj <> endobj 174 0 obj <> endobj 191 0 obj <> endobj 200 0 obj [/View/Design] endobj 201 0 obj <>>> endobj 183 0 obj [/View/Design] endobj 184 0 obj <>>> endobj 166 0 obj [/View/Design] endobj 167 0 obj <>>> endobj 149 0 obj [/View/Design] endobj 150 0 obj <>>> endobj 132 0 obj [/View/Design] endobj 133 0 obj <>>> endobj 115 0 obj [/View/Design] endobj 116 0 obj <>>> endobj 98 0 obj [/View/Design] endobj 99 0 obj <>>> endobj 81 0 obj [/View/Design] endobj 82 0 obj <>>> endobj 64 0 obj [/View/Design] endobj 65 0 obj <>>> endobj 47 0 obj [/View/Design] endobj 48 0 obj <>>> endobj 30 0 obj [/View/Design] endobj 31 0 obj <>>> endobj 12 0 obj [/View/Design] endobj 13 0 obj <>>> endobj 209 0 obj [208 0 R] endobj 224 0 obj <> endobj xref 0 225 0000000004 65535 f 0000000016 00000 n 0000000328 00000 n 0000029421 00000 n 0000000006 00000 f 0000080220 00000 n 0000000008 00000 f 0000029472 00000 n 0000000009 00000 f 0000000010 00000 f 0000000011 00000 f 0000000014 00000 f 0000082377 00000 n 0000082408 00000 n 0000000015 00000 f 0000000016 00000 f 0000000017 00000 f 0000000018 00000 f 0000000019 00000 f 0000000020 00000 f 0000000022 00000 f 0000080290 00000 n 0000000023 00000 f 0000000024 00000 f 0000000025 00000 f 0000000026 00000 f 0000000027 00000 f 0000000028 00000 f 0000000029 00000 f 0000000032 00000 f 0000082261 00000 n 0000082292 00000 n 0000000033 00000 f 0000000034 00000 f 0000000035 00000 f 0000000036 00000 f 0000000037 00000 f 0000000039 00000 f 0000080361 00000 n 0000000040 00000 f 0000000041 00000 f 0000000042 00000 f 0000000043 00000 f 0000000044 00000 f 0000000045 00000 f 0000000046 00000 f 0000000049 00000 f 0000082145 00000 n 0000082176 00000 n 0000000050 00000 f 0000000051 00000 f 0000000052 00000 f 0000000053 00000 f 0000000054 00000 f 0000000056 00000 f 0000080432 00000 n 0000000057 00000 f 0000000058 00000 f 0000000059 00000 f 0000000060 00000 f 0000000061 00000 f 0000000062 00000 f 0000000063 00000 f 0000000066 00000 f 0000082029 00000 n 0000082060 00000 n 0000000067 00000 f 0000000068 00000 f 0000000069 00000 f 0000000070 00000 f 0000000071 00000 f 0000000073 00000 f 0000080503 00000 n 0000000074 00000 f 0000000075 00000 f 0000000076 00000 f 0000000077 00000 f 0000000078 00000 f 0000000079 00000 f 0000000080 00000 f 0000000083 00000 f 0000081913 00000 n 0000081944 00000 n 0000000084 00000 f 0000000085 00000 f 0000000086 00000 f 0000000087 00000 f 0000000088 00000 f 0000000090 00000 f 0000080574 00000 n 0000000091 00000 f 0000000092 00000 f 0000000093 00000 f 0000000094 00000 f 0000000095 00000 f 0000000096 00000 f 0000000097 00000 f 0000000100 00000 f 0000081797 00000 n 0000081828 00000 n 0000000101 00000 f 0000000102 00000 f 0000000103 00000 f 0000000104 00000 f 0000000105 00000 f 0000000107 00000 f 0000080645 00000 n 0000000108 00000 f 0000000109 00000 f 0000000110 00000 f 0000000111 00000 f 0000000112 00000 f 0000000113 00000 f 0000000114 00000 f 0000000117 00000 f 0000081679 00000 n 0000081711 00000 n 0000000118 00000 f 0000000119 00000 f 0000000120 00000 f 0000000121 00000 f 0000000122 00000 f 0000000124 00000 f 0000080719 00000 n 0000000125 00000 f 0000000126 00000 f 0000000127 00000 f 0000000128 00000 f 0000000129 00000 f 0000000130 00000 f 0000000131 00000 f 0000000134 00000 f 0000081561 00000 n 0000081593 00000 n 0000000135 00000 f 0000000136 00000 f 0000000137 00000 f 0000000138 00000 f 0000000139 00000 f 0000000141 00000 f 0000080793 00000 n 0000000142 00000 f 0000000143 00000 f 0000000144 00000 f 0000000145 00000 f 0000000146 00000 f 0000000147 00000 f 0000000148 00000 f 0000000151 00000 f 0000081443 00000 n 0000081475 00000 n 0000000152 00000 f 0000000153 00000 f 0000000154 00000 f 0000000155 00000 f 0000000156 00000 f 0000000158 00000 f 0000080867 00000 n 0000000159 00000 f 0000000160 00000 f 0000000161 00000 f 0000000162 00000 f 0000000163 00000 f 0000000164 00000 f 0000000165 00000 f 0000000168 00000 f 0000081325 00000 n 0000081357 00000 n 0000000169 00000 f 0000000170 00000 f 0000000171 00000 f 0000000172 00000 f 0000000173 00000 f 0000000175 00000 f 0000080941 00000 n 0000000176 00000 f 0000000177 00000 f 0000000178 00000 f 0000000179 00000 f 0000000180 00000 f 0000000181 00000 f 0000000182 00000 f 0000000185 00000 f 0000081207 00000 n 0000081239 00000 n 0000000186 00000 f 0000000187 00000 f 0000000188 00000 f 0000000189 00000 f 0000000190 00000 f 0000000000 00000 f 0000081015 00000 n 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000081089 00000 n 0000081121 00000 n 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000033616 00000 n 0000082493 00000 n 0000029778 00000 n 0000036609 00000 n 0000033922 00000 n 0000033808 00000 n 0000032865 00000 n 0000033051 00000 n 0000033101 00000 n 0000033690 00000 n 0000033722 00000 n 0000033959 00000 n 0000036685 00000 n 0000036863 00000 n 0000037927 00000 n 0000047731 00000 n 0000082520 00000 n trailer <<7BAC5CE523C4874981926FC40A79D0CC>]>> startxref 82684 %%EOF 8BIMlnkE8BIMFEid p$b96b9278-6715-1179-bc96-d795218d25c4(0BHK Pg(J˴$n20Vma [KO! BoJi ؄vp+`[-<kkCM`ƀB7 P @J*0B̄R{@@Qy(  pB@8AqaHK Pg(J˴$n20Vma [KO! BoJi ؄vp+`[-<kkCM`ƀB7 P @J*0B̄R{@@Qy(  pB@8AqaHK Pg(J˴$n20Vma [KO! BoJi ؄vp+`[-<kkCM`ƀB7 P @J*0B̄R{@@Qy(  pB@8AqaHb``Z )WP̷|Ln ?R6@0._H% j-l@RzF6wqg(V&^L ߶_ K?pM:'z5#ypjJϒ bi LJLXn,5@dg4a+wl*@㊎dg[M뿑ŷMDK$ryԃ?3?O~Z. iyQD,$, R@ՔΖ /3yMsHu>pЀ_i@,u2 p"Wf%S?a`$>*_A~˪.}|y y p*AbMs1DKHے kI"p_bpaC~+R~5=܋|YhFGJb<%X `%WJ).\@"9( [b2epg16:LP (@7y .cwM2룺% S2⎰dXb. Sʖd2E5̶W<')zd2[ ̺w2>QXdcG24AQ%33'31m.M226SP׽u;cW+/eՄB/1wӝܶdrAtUvSZz9 vpa$b96b927d-6715-1179-bc96-d795218d25c4(0BHK Pg(J˴$n20Vma [KO! BoJi ؄vp+`[-<kkCM`ƀB7 P @J*0B̄R{@@Qy(  pB@8AqaHK Pg(J˴$n20Vma [KO! BoJi ؄vp+`[-<kkCM`ƀB7 P @J*0B̄R{@@Qy(  pB@8AqaHK Pg(J˴$n20Vma [KO! BoJi ؄vp+`[-<kkCM`ƀB7 P @J*0B̄R{@@Qy(  pB@8AqaHb``Z )WP̷|Ln ?R6@0._H% j-l@RzF6wqg(V&^L ߶_ K?pM:'z5#ypjJϒ bi LJLXn,5@dg4a+wl*@㊎dg[M뿑ŷMDK$ryԃ?3?O~Z. iyQD,$, R@ՔΖ /3yMsHu>pЀ_i@,u2 p"Wf%S?a`$>*_A~˪.}|y y p*AbMs0B*H1 7%[`0 `0/8BIMFMsk 2*11111111111011110.-10-/111010111101..01110/.(*11/10101111.11110.-10-/1110101/0101.-01110/.(*11101111111111110.-10-011101/1/0101..00110/.(#  -.(// þý»%¿ӿǾ¾޽¾ ฽/սռ빽/츼ʺコ/꣥ޡؖ˞ʝ蜜ɘ׋/ѧ٨ꥧ粤͢`/ʯջ ԑɸ&˖Ѿ¾/娝ҭ/՞Ơ/&4蟏 Û 񵜘'䥖 彚ٿ췭ȷ Qꫛex x笙騥 ꧜vԠ 𸞶mܸ$z=VY$O0'k||G$阧 ʼ̟/uhユ󭯷¾ { B뭨|8촖szF䩛ޮ~Ø ȯ̼/ԫܵ  ⴒ{nhn/Șsgdl| 紒֖魗߲#Öӕǖ瘾̷to|/՟Гӽrbevش ȥ߸_Z`|Ҧikt Ξnoz [}/C ;Y',gDCDD;.j!9!*|&OYr`?+@y(3.5!gq:< <en9-/:q%*׍/sV>*A@Aa/4+u%Ǹ䭤6/L~cڿᯯ@ =TU%,eCտ䳯/w_Àկ/]lbּ氯{/N›ߥgJPǯ Y\ [TMoRڣwm篯z ܔT]Ǖ|仯 OcU⯯yQe%xg_䳯xzWģyr{دz p\k]汯{ ﷞Xmɯ$z=VY$B篯z&/'j}L{G$蘦仯v/thNν㯯ygN {N䵯K[BNدzOJ8Pտ氯|rFK{FqYʯyZ Tkծ簯zڬ}PXܣbἯ/tKXҊPR㯯ysVCY MۉLd䵯|cK:>Z/UUaگyN<CA=B=AB;7EE@ai?DE7/>C>?B?~9B?pS?@B6DDB:8ACC6/UB??A@=AJ8B86CB@?KQ?@BA;/8C::A7t>>P>94@>X;>>;W.9.MMBcF<>:%8==?~8>:5=5;<22=2Ƹ2h/K=AAB@=Dϸ^q:?@B@>63?4ٽ2n/U<;Q'f>7DsQWQӼ%/U5aˊ4B9ѱͶ/_hf粁k>#\; އ+0 e.%G`$YR x$/|lYѿ:T q9(ȏP;%T@4ӿ%wV)UM~c I1C7j w*lb1$yk/v')7܌3"9W{gȳ6]yZ)~K&##lw)饠so{2+}Уp`ctc#?L>ᩓ]Y_zAYٝdkt ~ioz P~o()*+)()*+)*,*,+*(T (W (d% ( )*+y++)((((\(](\([(\([(Z(Z([(\(\(\(\ ([ (\ ([ ([ ([ (Y (X (X (U (T (T (R (Q (Q(Q(Q (PQRQTSTUVUW𹫡sf[VTSRSRQP   vV='  i) mapper-0.8.1.1/images/mapper-icon/Mapper.icns000066400000000000000000010371361325266516600207530ustar00rootroot00000000000000icns>^is32خvͫNƽĵص鶾ʸʨؾ8)˿x0-۫ԍŖѓ,𥇼ݺż槲-ɔ,׿ܛ,yӜ+w+fɒ&ORQN7V$(PSIZܮu`G@_NS_DQJC*O DD]hbSYEΩ:>oT;REʇ:ۢ鷪ָt-ЬڲÉz0ǭӤͧ-ڪքhʆӑ-磊ԙĻĠ.ӡնŔ,쿧ǥ-ꩯвym͜+伽x,ݲ׹bͰ'PUSN9O (PVIZڮuvb]uiRu_jbbKg7[]qzsil[ըU\wjWg^vR>ޛ⨨@2ƕԣK1ܶД-ة~Xt-Lُ-͂ºt/Ɖ$<•,ꮑT .墳蔠#Ciɝ+檬vvٜ,ݥ֮۔aȞ(PVTN:J (QWJYs8mkIJJJJJKJJKJJL-JKK{JJJJJJJJJLCin*il32 wneUľмֽ[yϼֺŹл⾾ٸʸĽἾ̷ˠǟܱȫҚީٴ͚ȗ٫çk̷ԪŭߕXɺԪ´Ȫ]Űa^綰ɿc^ŪǧEW3̧h^Kp^ʺݛ}عnh^۩.⧣ɹe^᩵覊鴾c^̈́Fc72VgtѾ:Ŵd^^:kޯʭఽf^6Хb^ң˥֏b^Ҩ輪񯏦c^һ[ϩe^Ū߹봢h^ĥrp}h^賫l^Ǎn^īݥהkqs^޹ҽ]r۸u^4Olx^: +*)4T  *,'"TfUVQaUTX^wnӆhrs[[lU# 8nb] [y"$m R KF d `.̅OF;d ""=dg(!!b~%YN]ɜk1 [ Qahȩj6 Mf4_^aޢԿXQ#{̢:6Yٰk]翴Ѫ߰ҾY^~ήf^rm~~x3wٸi^rدʰƫe^^:kwîׯºlj^6k׷_c^ңlȰj]ob^Ҩsӵyc^⽓r[ڜƭg^s֓mʴxjcik^m޿fTSbh^uzӱssl^qغn^qlɭlqu^ʉ۶_rĆx^پqQmw^: +**4G  *-' "TfUVQVUTX^wnՔ}qpkUE-(4S}8*+8sr)?4&[yDF~3i1c_/x98sK?/˒+h5^>m|l^IyfYn^LGبopw^h[xÖu`qc\y^7ӣcRmw^:  +**4>  *-' "TfUVQOUTX^l8mk5ffgghgfffffghhgggghhhggghih(kAfNg?g^h(gf@ffR fL fM fM fL fL fN fN fN fM fMfLfLfJfHfFfEfDiDWC'zZ8$& it32H׀ހ=MLMLMLMLMLOPŇLLL$K̓ ̃͂͂Kʄ ʄ̄ȂJ ɊNj̅ ǃI ȎȎ ̅ Ȃ H ǐđ ˆ ł Eē Ó ʆ Ƃ ?uPŅŅ ÆĆ ɇ Â$Ä ń Ą ÅȇĂ޽j ƒ „ Ń LJ㮱Âۺب7 пƒ ƿƉٸȴÄҾ޾‚澃ʽѿĉֶ½㽃ɿ꾹ؽÊؽԳŽY׼༂ó˾ӽ‚Ѱ騘ͼ鄾Ȱ½⺄ýͼơ3ȼļ̰迃缃ἈṺהsAWe缱忰ϲ䄲泃赱ϭ嫮򴋪P1&媜䫜ϳ䡝礜ӖʗǞ\:+殛䤝̲⠝ۚ䡝יԕɗF)'㺜ݝƳ䩞̛壟㹜Ŭ֖Δ[9,˞Ş⸠豠妢⫼椢姹֘L)!ߣ秥Ρ٢橥丳Сñ՚W鲨䨄޾ꮨ筩춧޺՝ҠCЩ ª鿫 趫 籭䫮՟s괱 ۹ ᮄ ӳ 赱 ͮԢŸ ٲź ʳ· 麶ӤटU ȸ 뿺 龂Ӧ: ľʩ쿿 ÂңРD ժzÏ ɂǾϱA ō뷕ō ЂƾCˈ॒ˇ~ԂǻݣD΀7כӂȽELΗ͟Fʔv ꨗG ˁœ 򭉂ρ *H𠒎 ۢH 򶒘▖+I 娒̔ ʟJ ΛhFg᳝ 訕J䰓.-+虐K 뾘񢗗Ŭ~xҘ آK Ĝ۾c\Ó򯘢K 󣗙   Ȟ;L 㼛ޢQI˧ 妕bL۳ٔĔuALӫ ա Lͥ񭘤 ḶÔ'ŞLӥ♘񢜤㥕*K߭ 󧕖(띗 K Ҕ t"<雗 ҠK !ʃ ,읗K :&󤗔 K 񲔚7࣢KJ먕y %㗖J줕d1ǑС"FJ׼ d@IIퟜgd ݣI] 9dnw!j*I~kU>In͟I ` pDwѹ ꨖ 괋Ia Ӳìn0# q0iƊHل,+db-Pzۢ ˎH] ?եo@3SȎHD4$ĕ·ʟހཊH6cƕ!觖ثH2(ȕ܀ƕH56ɕآڀѨH@>ɕ !򯙡ӳH< ȕȞցѳ~}G4ƕ"妕Ͱ}~}GL”5Ƨ{}{}H #աһ{}}y|H Q򳓙 $񭘡ǭ}z||x}Gcꪔ6Şʵz{}zwG/* ݠ%㥘{{|{wyGY  Ș"}xz|ywþG`M 簓ҡyxv|GT ƛ uy¾GǢ=ž¿Gв)खʨHǮ=ڼϻHֻ Ӯw(РǹGݻɣ)쪘~}Gج/Ü~zvw|G賓Û*ݣzrnnrvxyyH؝@ȝyohefjmoppt~H Җԣ+͟~pe^]_aefggktH ٖ粒+ꨙob[XXZ]^_`dmzH鞔"͚te]ZXWX]erH촑,ۣpb[Z[YVSSYam{IߖD࢐e[[\ZXTW]gtIԙ-ʠkY[XW[eo{I 󩔒̓ 觝XW[gu~~J G͒^_r{yxyz}J ٕ آzu}xvt!vy|K鞓0򮝐|vsqqooqux{~KΑȾ ȟ|vqnmk#mptxz~K𦓕ٓ1妛yqljiffggkorwzKޘMƼvlhdccbacfjnrw}L ֘ޕɾ2ա|dca_^\\^aejou|L ݠ3񭞈c^[ZYWVX\bgmtzγL 붓N𥓐ɿşhXWVTRQSX^flsxаL֦N㗔㥝xQSPOMLOU\ckrxḶNԒTLKHHILSZcjqxڴLѮLɐɾӦdCFEDFJPZaipy֮LäFſ̋=?@BDIPX`hqzӫLضC˾T8?ACGOW`gp}ӪL ĞBÏɶI2??AFNW`grӫLO뿖Ȉȫ<3@?DMV_ju˥LAώ⸸ǧ?&/4ADNYbn~ͱLǧ2ɜC8ABFKS]jyM=ˌ838:?HQYcsJ l  ^67*[f[׀ހ=MLMLMLMLMLOPņL܀ۀLL̪y0£|z ΀K΂? "N n3 *] -5Kp   + U > pL1J (  Cd 3I ]  : M03H S  1 ~ 2E  j   A 0 2?uP   D    v Z  1$P  Hn*  ' Z^  ¿ױ1Ծj ߳ .y +  KV  W [讴:1Ҽب7U6 * ^οn  я 0кȴҾ Nئ ;M ;"0͸½ߛ  }4 ùg G p .˴ŽYk  ˴;=W$.Ȳ骛IE Ѵ# xٙ "'1-Òȣ37 h- ն ϼ2B,וıt@Ud1 q7 s׷  &k  ´ $󶎬Q."0l3x׸ %#a VϚʠ]9(8  S ԶҰ 'ϝ!ġױG'$S &Ҵ ε' R&ӤM ͳ ­Ě\7)z iƷG 74'խ H 7бK'ذ 75|h f'׺ɯǡ ڻqH*pB yԾ ѿ  !'5z湼ʠ ^ɮpp/v  tN  B +  4!'ݳ 0̟մwqP  ?ΐ)  b   Yw  (c ̝ oi  &AJ<  N  .FH5   @(X˜ >ϰrn;@   Ѻ )ɚڹ~i~8 %oyV  )ȚgȭodF $ gnWWN8 *ƒմveB ; ILMDOu!  *ƊljCw  8hDNHMVR*ƿ΀rcDNf"  ;WGODlX  N  ȼٹ|iuENӞhC0%"*7RNJMFİ\<-##-<[urtq}ƭodFJK1R*ԲveGGMJP}A;u*ݾklH߀FNHU_IC̀ qcH ENG\LLQ*ط{g{I lEOEdEIl!ŭneJ RGODsFEĹ"~WMMYӲudJxEKLEEkIEű찦PBLLKIAWܽkoK OFMES _KK͜qUIEJi̦(NFVSFKCd̮qbK WDLIDx[KNÐeKEILKIHIMHFç"®ߝJKA׶zfK ߛYCKKC]bKOQHEJMIEJ[pzlMKLMӥ u;9ĭmg;LՐUDKLCUFNMKGEOn_LC󼥦qlѱtcbLȁNDKKCS EIN]{EFۼjqALsIELJCW FE$ʮpc LhEGMHD_ZLE&ֵyf LdCGLFFjELW(­mgLgCHLEHuNMD]Rcбsc*KvEGLDJ} gHJ](TIJۻit K JDLFH} ENHѬ!QLSɮogK AIDr2EODԥULNմwi|K9Z FOEv򷩀&cJEpdKz zDOEg(DKZ{EDϰrq4JhEODkʤLLJCBCH?Aڹ}pXJbFOD|ê)CCDCA?YȭpkFJmFNE}vմvmFBICOFݿlk IPNEh\΀ rcI] 8fP9Gl߭j*ٹ|hyI~jU3HaƭnecI _ pDvйԲvd AIa Ҳ®^ # p0iݾkmBIH؄,+d sNGcb-Oỳqc JDHH] ?ENJԤo@3Rط{g|KCDMHC40EOCŭne݀GCDCH5cENEm'Ӳudׅ?CH2(FNGcܽknۀ_D;UH@>FNG_0׶zg~јR;B?iH ENG`5ʮpcѪqE8=?9=fH Q~DOFe5ֵygտ\=9>=8BlGcmEOEl6­mmȡpH8;?;8KzG/* [GODy7бsix}P;:==7;XĿGY  MJME"ۻnbA8=?:8GnſG`M xEMJJ!ɮpkL<;8A`üGT RGOFZ մwmV5>YľGݡ^DLKEz ne]~G߳}RDJLDV2ݵϰre»޻{HΣuTEFLJCO#ԡg?ڹ}is̥mDAH辍dKDGKKEF\;„Q=2ȭoeĩ~T>?EEGʍ]GDILIDG[=nF>C=MմvgsR>;@C??aGtIDJLGDNi>`BCFA;AݿljtXC98=@>;FmG߀GELHDRxYAFHA@[΀*reiQ?537;=:9B^HXCLGFh@[AGHAGpٹ|jss\F800378648GcH KHKCi5fBGIBLƭofiQ>1,-/23104@WuɀH JIIJ@FGJBLԲvg}hM9-)*-//--1?TpHYGIPBTCLEGݾlmsU<.**,-+**0=RlHBLJCHJAì,qj}wN5++..*((0>SjIKLDDaBLDOط{o^:+,/.*(.,.**4H_tI mGCIHHIEӲtoQ))3Kg{}~J _HGGFJGLܽo_/;_{{ywxz}J bHEHJIGS̮qjVf~wutssvx{K yEEK[GHQƿ׶zkm|urqpooquxz~KBMH%BKHȾĭnh|~vpmlk#lpswz~KhEJMLGKBѱsjp{qkihfefgknrvzK NHJHMwDFeżۼmirmgccbb`bejmrw|L NFLCiMJKEȾʮpikgca^^\[^aejot{L ]CLGHNBGfĽֵxlfd^[ZYWVX\afmsyݱ\KL FGLDL~NhGBɿ¬nh_ZWVTRQSW^ekrx}TEFHMLgCHKEFcNOIIձsl`TRPNMLNT\ckrxך`EEKKECVLbDEKHCMsNDG[ǿ~neRMJGHIKRZbjpwˆQCILFF[LwOCHKFCYLBEnȽvkTFEDDEJPZahox|JCKJCO{L՜dGDKJCQFAC{Žȸl>?@BDIPW_gpzyICKHCYL†SCIKBYFACM:>ACGOW_gp|yGCKHDaL ޞZCKHEBACzŦ30??AFMV_gq{HCKGCcLOMAA87;bɿǬ&*@?DMV^itݹr?8@BBbLKϏԉȲ5#+ADMYbn}久Lǧ/ƹA;BCFKS]jyM=ȿs848:?HQYcsJ æV  K58%ZdX׀ހ=MLMLMLMLMLOPLڀـLLӺ0̴K՛eI?= @Nq ɋ\E>=>=CU}MIKGXJIKG]KJDGHE?>dJEGKFHFDݴ=HC[I >BG>K}:EGF;aKEG >pXBGBZH ;EGBDt:G >YKEG <;GFBZE <;>>;=DF 9JDF :yFz}H=F EAJDFDGӿ=FE@Xռj \׾>DE9RAE ;|ʾȾ:E DCICE=ͤ9EDC?UѸȴGDECGм:E=pն:E?`ȿo=E9ICE>`LBEDCC>Tε½ۭ9D9Z@DBK:D=CCD>iHBD99EDCBA=S̲ŽY:DBFܡ9D<Dz`?D?a׿wC9T@C8ҲDBC9?C<οGAC@W@C=d;BAA@?:MٙdzvBVdU;?4Z:?4Դ=>?5@>?5ѿC=?73>=<;:5E򹒯S0#P38-R38,ӵ778-778.<685FD48,y,66543*X̢ٝ^;*W28/mB58-г:78,л0873<685G˫,84B,654414ǿI(%m095HϾ19.˳I590mݔ-97<=796Hբh/9-ʰ-6654*s¡L H_8*.:0/:99öd3:8=W4:5S>8:7Iث98:1e姾-765/ExQK-ջ2;5W<9;5V⿾ޑ/;0ޫ1;0~?9;8Iٷ.;99Ʈį.765.9 - M9<=1a4=<=07<5ϾD9<2@: =4k3> <;b6>;;̀O7> 8VB<> ;Lٿ6=8Sɭ095:& B=?4`؀ԣN6@? @4}ݵ4? @<7v̏@9@? @3C=?N@vȫ25Nu c9CB?<;=@B 6ͭI>CB><<>AB C?BF@B?OƩ26515 N=ED7X1>AD B;GBDAQŨ,g^L N=FEFD8E()" ?AFEFA=ICEBSä:$OB b:EGA?f UM=G=KKEGDTÞxH CF>FHC<_=qx?@GHG@?tLFHETqnDхO?@DFG8EB>Cc'A~L?ADFGGFDA>HrC=?2*&ѷ)@ L8@}&پO ߟL=O x.6˶xg*KPY ;-"ֹ9% K Z ^!!÷bK j K2w t#з&;Kp, oNр6ݽK  KkU S;р(j,yXpuJ<A5 J4V 4^  EJBqz\Sc$ FIv ޻H  Il"hLquI\ 8f<?ݫi)2 .I}iT,HIĦ[  :I _ oCuη!R qI` аS o0iD  H׃,+cFAa-NxԽmzށށ H] >kӣn?3RԸ/4 HC3%} pV H5bD:̶~XjH1'6׺@ ـ; H4532ƶjk .H?>00ҷ,8΅+ TH; .پS Հ̊5  %G4.6ʶ{]ł4^GKy/չ<" ׵o* KH k25öfНT GH PX85Ϸ)=ֺ~:  QGbBBּO# ɓR  &bG.* ,R7Ƕx>i+  7zľGY ~ iѸ9$  "UƾG`L Q9b  B}ûGT !*:ʶ& 9qŽGՇ1T׹K* 4kGמX!&;ݭt_ް[H‰O#lΎE5&&ȕLHm8/vf& O§^'d.Gn/,a=H $DY. :GM?uފ5 yžH( _;  KG]"R?- 5yqctT5  ;vH)>.Nһ2))fH,  !EvH ?<`θ[* xZ<#  :`H 7]g!CzX6   ;] H+&]ƵD* g?   !;\~Hd CaBɺm So6  &@]zID7 /  &?[uIv8!FŵV, 5ToI DItǺ~ 8^x|}J 3@- %Rx{xvwy{J 6H$õj '`}vtssruxz~K T`K.#ź,'|trponnptwy}KcƿS iuollj#korvy}K>$}õ{<pkige$fjmpuy~K |R=Ļ<0 lngcbaa_bdjmpv{L @Mƿf Ilb`]][Z]`dinszL 0 ]Ny=ö) $e^ZYXVUW[`flrx֜^-L bZN?ǾԧK N]VUSQPRW]dkqwY$L>7}N"@' .YRONLKMT[bjqw3&L6LN/ȿ߫t MNJGHHKQYaiowf ,dLQ+wLHƼիy 6KDCCEJOY`howZVLˁ9 sFYü g39"YcVt8mk@0KOOOPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPNIA?@AACDFH J L GmQ ɏ&o4f;!l  F   ( k  E  X S T R  P N M KJHGFEEDDDCCCCCDDDEEFFFGGHHHIIIJJJKKKLKL L L L L L L L L L L L L K KKJJIHHGFEEDDDCCCCCCBBBAAAB>'**+,,---......................../////011224679;>AC@ypg_XSPMKIGEDB@>=;:9865543221110///...--,,+'"  {nc[QE;2,'$!  /{j\QHB<5/(#! L޾r\KA70+'%" shG1(! '% ic09` jP ftypjp2 jp2 Ojp2hihdrcolr"cdefjp2cOQ2d#Creator: JasPer Version 1.900.1R \@@HHPHHPHHPHHPHHP]@@HHPHHPHHPHHPHHP]@@HHPHHPHHPHHPHHP]@@HHPHHPHHPHHPHHP _߆k>LRXُ_g|+0CYIfa*l ^1q&.5%Æ$R5omv}?!G f]~a8;Li¥[3&Rk#tm|Rut~ vjiO_2[3rR+Ti~('ᮄ3R =XҌتy ngsV%2-x2I}i:6uRt\.֟XOi$[NprcoܹQ^¹^i'U^Xa{˜^`B| (I  ̚pGFذ[q|| ~v|AS1*IAB:(ym܇3+;x*m(kqHj/Q&d?ZRd9|Be<p |WN?ߞߞOC0`[Ol&=3 >BpBS?`蔊ll:ݠ`g!DfTA/V?9]0&6vOy ]SFxv31gL>wb *lD27ZFFxiO>4Ќyb 9BfV-?xI XI}v۪>/5jvQz4Mt/Fp+g¬ʲv"\@#bcc">Ҧٵ&3)1l]ЉО;>G(8M,UUnR#飥T Bһ`o]rЊ 4*u*i&Qo-m#@&3͌+ e?,ʴ?}*Q]\߄@%Cig\Xx}h -p!R~Ul<LJ$(G ?P".5=5HR>1@3lFDj ت+ y&?V#gS Jem*(Św&NDvl<{R *ȇ "@oDRDb 1 [@]2hP#;MyeEڡm =ȽҹRK@~2twfG:NI($:%KPT+y~0yy.o=!7XT/徼~u{INMH !ָVs*Dzl'GbsP:CQ*߬nPqi:y蝦۝| :RCI6' ,]KQOp.K@1ZD-Țht "sJ!j\폰a(ZǶ ~FwdO|!Z_6Gp߀{DYrYu8v1"ha-x,e\UNjEt@Wc/ĞeG]h{)LKk9a(w: xl}]Bn1ҐϽ/f/fJcwdmcǎEly i)%$WWS\~rPxEr̻7RaOg &*7Φ9oi*Frm:gVũ x$1bgsJU%vOHJ`EiNNÞvN3өW8JoIU0+y#B|-}"r[Z"0ѪkڃdKo.5c>@e=a@A`֓ꕬO?ףOҖ֥ں쳗G,,tR6JDx|ZuF{Z?ͬ+W)S>\>;_d.Ad 6y 7uϊKrg<˱T>gG;W >DuEs,S֡512ʻ[z# /Gkjaa )2@cCmb2E<< ؄f.?u/W'}sPA=U1b[,Ճ4ʢZNd:qTW.citGs(.)L>}*ENneKVӗ no%@䞍{t Cn\\4b\zPHجLkc!/kXP= }8C=ڂüUިcܺ$@KC*-_ŚGmu1 i70h#CxCMT1!SŵGycŒ'Nb-qE1pz~J.%[g t2 r%+IWJ Uț ƵfpD ! 1EuzOY9Ar?V'ei)ĸQ%R p jK'nIB.D2f!:Xo9#$X6-}%+ C &u;l5Q^NhVg%5)?1`Bn!HS|!)d00K[/rN} C+}u|,.sORtaUAٟrPZj:wtkxy;7|msk~v4{jQeYXgC8\{ۋò:b%*\)!FG(w XZ!Ăl%RcQN `8 ?kSs v,Ed#ONfpR RcGΈg)3)ٙ)k9z9>DSKGP!q I!53vίe \zUapD5dޱ;&WLM1C{_'pms oZC2%}n+eC 2{kc*ZDfD/nWz@\'/BGXq,k/ ԵGeE12;)OvGK qv@oV['xEԓ΢l/lkK+f*TH @[Ø*"Z Nh%>jnG;9ґCok7ó?@/`O{.=+wҘ$m=$(0bswѬx+(,؍byZUDgX`?jF[Lx#QE+R:al6"d#4ݳ%pn(H?v|{a4Ew;_=YsĴވ-p-q}>ڵHR&߅t7E iՂ//ܰ3AMtD#> {i?'-Ci]pPJ `[c3}-3 'pb!Ac.h8=`b#wtUkr >Vq:DsstuRdAĿ\IJR# 1ilф!HrMxFR.R9u"q;J0Sov'M\㺔be>%hye܏+hH_n"WM5M>#3?]vut]QtSr?ܼλ\gÀI;JwKZ|3Jy]6DI/:Om 4 G"eTD]ϬVcRٙF(J+G`YsSWkFXR2 $ztXQKY>wk- I-C!xP`=`AW  p1`0N,n`&{S@uГ ”k3ضZ(:+s WskRel^Q/̲k3AGףtQ=!LQ+g&sTȮa+*G[9;&<srw713HL_׃ds%B%TZE|N>,9 k <yVB#H.7wxP9SH&`R.5Γj)l:#d&EP~[B&ɏc' [2+DtKc<`&Wҕ6zIgazQwŴHڸR"T*PQ%]CgH =yF/Χq P]{'xK=C`}p@0NzLkC SƙudFE1ɔ~FuIj?/aO'1C^.K8m jZ RdWMk::Ѫjr s/#u9UCV2ActutӲTl q39I.4 ZSu*f#zN"i b3C(H]!ɈK<-i'Cr0?f4 …fyJAg_P)FC#S;T+D]rSȸXn-д@+8 up"< qGv@x&*!$ivzu ⨷1L_~eo79A*MbQւ9S 6 $}=ėS8M&7"=p5mF1)3f4HV NZ"6A^Y hJWLYKjr9fsC쨌4w Q)=FdpGpP3 OM]YoHdbq6)AHPX둵BLTHu)I}H9o rQdl[#^T}I;Ba`q >3=!:F#',~qA(̳OMJ:w.3J+OE(& ף^> % 8IO#[J\MUGlir䁼/5[ '77G<3$HU^Ƨ5T1{~ hM]JbߕR\(І<]v dpB޻ƻOT۶сf fyxݭ,7<5'h ,zֳ8-k^fuG䏌>U9]w&) Xng^QW;tmD­FT12`EvE| ^EB]rP;~['G Q ֔)8=BO |7CBb Zu([,vQ߈(C y> J. >yR&{gtH5yTSM<67gߎ1M:$~OK? d-gvT[_lN]2)4ҏ*5ORan!4'"-`ć9e~7($T %LR: h})}Q:f Ax<"yrgٚ*T|}28շ@GI3ƢVU>KS S/=\֐c%ź!ZYS 4~rubvZlkP>B/!]f-wb^[Gg,&k- FtA{;RߞYua!æwS]J&ZO0NP[Go rJހq).*y~m,ʝ#mZ5 Y&4ur߱7Rl I: 4/dg3&TV'aqښG6=T~*ͺ mZR_@ةKDL1W~,0//:eFy8ο~LYB CYzV aC M=c/H{]Y5-Ld{ &f)wQ;ep: K$=>o 5 7bTzWf | ԤP5r-\r|F#6NXkBh3 1S=9W9(@AYJkr d@CRTz0M;J'Zh!Qǥa/g !|'/cMזB-#HP'V[=+rA<2JhS8K['iY ЖuޢV0ju#G}sI '"j;o199쪛")I3/Iw1-2h:Q)*;Tn,ۙIkmPqaplY>U^VL C)Sp t;|8lNx/*Z,a739tC[xsDgyPXҌS=Z7,fH/#O^4XRB?=H[EZa1ƶd B,9АR96O%#IЏϥm|Q8GBaN@gCiyo x;JUCY}-kVx9 my/_cA9sbМMŠ'?5}-[ 6'7 $GW)?v&KyW1Tq@*PmdӲ#}sb4uHQșJGŁ4U"FsZFx0`*g$PjlՑcA? !x>ʛ{<$XXB y$/x5omMF.6i573b-IJhƁ%*,59(~-;9W DL![A㙈#'1|%3>8N8L;QDkTi5Xu[ c&>p!'er ]he{N1 n)1dYMyZ(;+:nń\iIđU*z5m6Α3%Ophj7r0lL=HpN!'Y58?q[}Pf mZ-C$s{$sm3[=< >gMn\-pB55~__SF}lR~)2z\ū~]JYFpz*S'~Ζe!oj2]MkMK.9k*V.&YKXS,m%S%]e$27 SlZ_0[`PaOoTL.7uXb#ڄ,6~k'QJA;u&!ϪI'ӕ}Fk3JG!4I9VB=Đ{aɣX}3'4\(|1^LP-QpXj yDW ŢG#0HZH]_HٳmG *;[-<~NmVpNH4ɢ@:_9ul}HXt9d}N0#QC~ Lp0 0PcTSF:BZ>_D22kG&ڜQ}j>_L‘CX=ud]N0gG 'WHvqQ \O$#-6Ϊ]{)fI/o W2fH 1h5P̆A<#A84^]rzءDF3B!@] oAXƁ5wV穭$˼BnŤŽPYN a :YWy}h{]¤v~To&+@15y{V:ş|#V8& VJ)z ʑ@M`Q6),)~?2HC$s;(Ў_qjD$BN]ѻ."}K%מ]"{mv,Ғǜ3pX-(iTxxVSj2GC[s:DDÙ.]#?\OҨqk1=싋gF &kA]c~-k:*^D>#V!vnk uO7vS;ZOo}_kZ!(`˒飴dx3 PIEPΗ|iV/ġA`EYߧ>ɹV6q9Aa)u1M,iۑ_ syKUzMxvZnj[O-V|ھhCdtc(; a9Dcg㠩ꨖ C5P3_oh/?:c)!f98ɝnCj{"{6qaacq[:iFp 2vlR AϨ 04s,<UUkľ=;:H"6t?:FY5{S[hmMxZb w@}|w]ޙg={[+{oQ,AaK8"/:zN$dM<ղ.z]FA1iʷ2uq(ޮz]D4g'5Fd-VIVP82^ yMZ\ v2N\C{cy>0Q-&rկ@>:xcLC0(:(^x% e?uhgykNmi@a%;cAϧYOf[X4IL5ŀ<6c>Y-xYcïbC!ּ*TN$AEoW>H`tz> s0w﹆cZԵ8ZWk&jāl- )xE"NHBqzڸJXGoe&*¿ ]cLh؀Ӟ˿sQn _@{ը*NyHtj hW8:$4Xᝈ ʎTOPel#A~mΈsMK)z)6+r\9>k犤-[;'BRvP9\Zns|!o/Al4KVkGꞎJ*֔%'݄)~zjEOf6.­p2N?H4AFsك!˃[(".3:CO{B0+Qr$oA{j;>HG$y-o0V}x{2<)߆xtibQ"DaAwNG >ˠwi#DMàe.Hdxhb>QIT9xdXiVYNχ&A5c'=v bCs2Oe͓ZI=?c[q-+90D Pu߿]Y2f!nŕ:,;TU$qHYFo-EńnOQ6.F f׸EU&k=Znj2Ű(?)')("p[XSF3RoS];籸$#kH>gf|&v kq(Y3O_}?3Tn(@.tv[?Ϫ5ҝu6^Yv98?NuZHF/ j$Nj-1*Z0P /ŘK9b Y~F(fK;`̣|FC ޲SZ9 b}nC~ Bqr(E"F3)R[AijVlpZ”}ﻛm;(1 1G.)('"-@̾#^ƔI4o_RW}2DZ$mנEb9ANf ^ycN6PG}K:e/~4-ptlr,fs[Ќ]Ӄ3drǃzye_y~򴏬Ir1%t#dC jxHRyE8`˨! ?va8B`|['דO-L'wh*W/g3k2+&b{U*=Trj+{#PF^US<,FpZKkwj+FeI7Sr #θu8#2>OMm܈SM 7@'}0c칤mSƻOhQLot J_딂ZCBe@X]y;k* HdʿDL:;ou:ٽy"rC^8^҇s_\;'@soE y Lآ {L>-"֕6E)gSʤ @a#ncn$!rj ̓PR&󞰥SԈ\XmĄ ^ժQ_mء-,40zVc4 AnNW02 _(߿ 8i ('>&jX> uHpԧ."Bm?IۻzoD*{Z|Iy2.VNvC.>l]m 4A!cV9akW NjfY>-C@e:Tr7t<+tb ,e!hY-FgQƞ-{\Tң~*&M6 NG(=fΉf Ě2?+⠑Lx$]_rK~EV>~Sq( <ʬj{rCί'BZ2Bi'9QY9$ (,(9_3[Y,19F'|TwMLqOħb3z6i/Uo. ggARE#{z5l6 &n!' Y꨼4(:]&19I'%@uxVA%)»lwg="@ێҠ=࿄^ӣT٩A)3A(xp 2nj\&2cϕb`n׽ZnٟQ7OYQ.,f/iO>C&-Ŀ+$ ڷ+,Niʨ9%S:Y Є5z118EDO>:?_(th=9a^"BaԯLa xques.uZ@SxZW87.j>?4-o&:ݟ3sw//PLQ+.a?50}oP0ȷv5~}Y𻡂HM]6 U= @?YH<# Ȣ frAwhe7XH2uy-g %uYtstLdA4r?ʛ4}!(K pW..N7X RFfOne,bL1-򎙺p.Nbݰ zu<ݵi)2).E=QX#Ȓ}ѿZa@|6$-TTא5[NQA3|s*x5KapyB}nk\k,jxYV^x^8|^ݷkc@˾!"t1~2\V[5H0R<2q:,Y}{s gS#Dḧ́Kb%NV˂Qdgܰ7m0hWrF%(XGU /.Nf)e-P*{%1^{l [w1T5RQ+nxT7ΔE1'\1z< j`kCN<ֻݿ/=j8Lө?p[Hˆ|>q IFiL<5 2 |`H,9!f.6r䞓e t)f{jh`VlFl&~nA2yBڍ(/sJ58R8~UB[E}#=e'qshM RF ,Vw/5#rqՌtfH v"%[#˨B-he2CĤa53 F\Dh%cG+rTώYNn=lg杼o^bDVZhMXloӊܸF$5@<^ܷSl~;Ox"Cm `twbWpzf'T*NZZ {߷VȅH{5K镫6=6?4zvAs+b3.O0IP+hɒ::d;U ?h@sbÁ1,DS:nCą6խ8\LKΉh)^H'cQYO*Tq>up)wc7QiHbwM%9+MeH!üLj~>p!3usY9?9슈0 ȶA{$ l)g0Ѭ||j;U>PGNU1c[ߍ\w%[o#6쬓4M-=5#Ӌku$'//A-F*W\Z+TRApSRa^(ce?}VߑİTMBaStf*O2c(327MJzt6_o1_b({j;~hewfFKGk^evcDN_ͣ@/> KdKpoCr]ӣ6;[-vms2e I"Y|mnLª+uu-+CvO.Mm}Q"܏W1#PlGmd=`q2MReZy9ϑ2liI/T6y =N`,{ X<,nh" 5 gTdp6yfWG(I%^?kbĜ \73T5rYǸbF2  q3_V%1a?du1fȮ0Lax9S{v@#vYmT]Y DC4>~QUf!9}x3L"dOKSKvKǕabC٤0HX؋ᵠ-q-m6DRV_8^s I2m0QQ8JX=-uĂ%@8tt XdZz :n k7NJ;T*N0 2YZE.w jUY{7 k@7hP! YۉD)nR+q|=nh=AR;$0fkBYkSw.aQH p~_ѳT%TC7bJG-@(cA̍\ F-~^"; cHʪo4oZI'ق|Ork靼^SnJ"(8@ HEjA#O327[t{`0uGav"HX #+M'1~0ωZZe5fYGh>yx|qn4)JпӪ ANèG}[Q}s)r !%} j0rL'޺PF%hF:_۟uZN@lNxUÇ afZ.tNmMϬ9bT"ڱ7J4 V4ۦ^2;ش7޳ arx)Xhy@Z;{b4~$e7f.3~='l)j#ZG;7x.#u8>fW +TE[=$D* mh ?pm&vdT&@*&B_5?6AU&^c5E)۪X=Vru~% \]$پ(2abv{m/SѭIM[W6WT<=\"KÃAڸzi{]"X6%l_僗IM wLEЀou: k͓< Wv|F?yYJ4Rb@20omৼr}QUw)_Ĩ9X1dB<ސfGV>h|=cM#p>GwC u 딑QB=ɿ fMRfGuOT hzEp!>$b(,2 z0R|˥&k-ʈ:vDd&=;sg`m6G%,"ΣDfokQ&Yfe)EDO(kAwAZ~Y"gC)(*q;r HY,<5XZHB:sde eVN)?F.y_-{M #kEX0|W~}¥0^T q%s82nv W5AAp۠CBÊ*= T}qG.*e9/ -/ _.rFhT[%O(iaLB}-ĕ6 "}pBG.ՐH(iu ozI x6ό;9Bԛ(qQA (BfZ>ƀ2 _O:1+-OA _ nfZNCvF cע~;% _&/H}##DbSŐv ;[x yTUlPȭcF]/91PhN@2;Bep)[Sݲ:^QƳ[; :D.e jY7Grb 4 R(Cy8DJgCOkg:>=8F`sh7܋Ix^u,%iխA9yؓSPL+Z]-Zm!Ê}s3ۍ3ݩ&`8qzVة#^8ғݽ6nLFFjO3s1-H_Vz&F( w` ;sT(b$]ak`l<QqY BRp>}䨭J[~耹KBdՔG=YSAe\# F*؂B%N<~UFIT4d<=ɯxp~kU \>#Hj訣|5.,/պWŗ_vX9;w~ߔatkH%TAēv_baS՛0% x=K#QP3\mN, ]_/N$zPVYg6T+X3sf^@g@7KgzoȑMr?Q vq?&R{.cFu]*!dHX6+Y֛hqС:l & 4H35ŠbҚqtdBG6lgI(֫n,TR`KfL} .k98%ɩx *q^ u|8yu%*yy(9Q3aK-d >6\ju}0yQEXٚdLJ=?P9gV>ro,3@K{]c#w:!<j'ʟb^Vh0-WJf=;.eŖ~)˱R׬玢a̋1 c;,WDU #s2$WPUoeU,oI(i 5\MSj}f V2ɸRmnYRO#hGڕc#{`j 04MED}LSQ0ڿgbK&Y,JúB!6~kJj,؀:B\ 9Ql#y.8T{[֔|,d#1c5CZO Zfq@ k5iYUmh@ ;#R?m(W( 7 ǯŒ9k'!Ey8Aٺ5/8QW* b,|oT^ x]X{ŧF{yֲ0aݘZa{(AS*i$r u"6VUGKD N(O sxzKҀ#r +08"+*m&7#?#^l=8w'Ń3-پ &SߵnRM*3EQKQ;؁Wm?k̞-m %spdř "nߘyGo: 8d_`c]`D I8ؒWLɽV~i *$  ݎ:"jf"\׭eV^.Ь=9=m +f 0loR4{&CՏ&9y5EK2P -Hu O*qw*" (g4zR-_ CeKA[y80/z\ `w@ɏ( Kxxsu@ tKYNO'`WoM˯*~PL<8E!a .Hl}tW,A)F͔EWgYMw5y6(\Zcĩ([RC\& =SM}o [s  ?c'?F5c2vOmu~bk@,"!65B]}6L_ũ途 &_MYB\͕%_ lOOyVlje% D_O +kЧ{o)$TEaF[_t0|KɣKթxk!jjhw 7Fܱ6iզO rg9Q@`GWi*}|ǀzTr dOȵj?<>ݗU}j=ۆڇ.bP)3Dq$@&cA>d+%H*w/oøy@ ANSw[aN5:+Bs@"Jc mKvpVѐߣdue)"KiVPG,0ڻ3 wE[[3M7]mDRF<`5geZU 6S`'8-C]H!BC[lh*Ma/^֍X^ {B0%*rlSqT*'UCP _-\FۼeH -[P]Epv(ӶYd.YJ_;_;m3RGT^3gɌ#hJmvhߤMb?938qJ3]\\]L_IC&`6Ic)H(֒ݐ/hv U4>ޛSX_OtL|vܣ?̣#aseEu%vo6̣FɎnpY G`Nl4oko W>>..vSURk̰I\cܑl62zAqQԃ˷pBbO40yʇ:z#4x^\^ůQn#c:N*k鰢n :/ ^$()$qL"Y!FTQ5jEhQ?*V.8C3e J h+,wFT0!T~M%E=(pfYN}j%s/F#|ǀ٤X /-SeBLքؾ!V b8g|^# JQ0ݵb"qV`:#8$5e/Y>țEZӅqChS-Ay]8vf洗.9Aː%cL:a+(DCBh@A>TDJa+&OW0vuJKGo wNqˆCVslI.tmHh&핻4H2z|x3mx#mB\ʉf1J(1 }%iMm%Pj`jIǵ]%PM4@Y9~q8ٱ ).wDң|fNR.(vs\Wl8^`B)"w!kR*1{@/8Xm<$N efxny5ll#Ou(?eJ>6ѡ%40Q˴4@w_lB݉>WAk+Es7Pe4-B_h¹ 74IHW=H>x^{޳` Oӂ>5cu1ss# FԶy^PHK_|SWbDƃE"L z5]bA3-kӹ\F׳LL5FcOмmA#J="\Jvӳ rฦ{OpÕv'lAV[~zƋxvu-J,K *4c _װGv-]/ᑴ\%[]2 'ICa!švlbS+#7+Ckٹo:ܤ{*ivxK RC3)2 /J:9L_6BfzkdcjfOcn1?~VQ'Jmw D~U^p &kϝm?g( :Ң fMPnz"Ijj;@Z{(ԿFo2k9-ݕv+e?K)xQ$biZ MEmXsQw]1<d͌$!^v9Utbo&G2?Jeeҩ)m\ؠ;vCOUmgUB]$R,0( ZJU 'k.Q K'pUWI\]P Q^/fVlt%0djV p 2f ՠwgw_-@<B RQَ(y 2a?; E2nȼڡǶkFj'gXzϩg{>3B|juڧj*3Zlzم7"ȭiAq?Neё6OXE/I8'|{Jz8aE |#>m< R扬i=[/ߢ"NKPjm5 &xY9 Dk>Z&>fhmb/=r-H>cuVL:[Ф1$}rj,MʀE ;sK~<6p񢗉wH#ZfwA|xh E:T]+ +4 yDu"b¦6 }wrS)Zqxlܠ֙+>-'?wyws-D[РI)cnb[nB*!K}o)ɿy ?/?"A;񳸁mv?T%/a+ a\XCBM/ g74H_dFnyk@~T=!a樯bpfV0^Cb =|aKPwO xoŚ BբL&KŞk(DИ7zn ~O\q`R2dV.:#Wms;iӶCv'f}fw|nс*"Dy!'F&(]#1гTWv;ׇpa:F)0JQ10꽏Ȗlb)X.5'PTѬm*AyM]~D01i~QjkPٶ!Gj!7 !!9y{]p&#B7 %ıҁ/HmiIGaьJfP{tۤS cog- bn!-ypDmɫZy8WY%,P&wH$H{kjg&m`͙kedY pu\/vJp R%lD$f,B\<1#I-fuoh)8 w3s .hg2j{ڱP?H#7;ת(*w*nONFq.KrGM,JA@Zvb3T.>_HGۂҬ)P!H>pcx$>PCDթ'E5 =EwIQ]n| -z*@sVBj⍬8iM 5ijf}' V.O}@H(*0q?~y=tj *zUwsx92:)%P$eZ1t]J+`&4mwpĬV0^bλKumet2%P_8;_P8{+j+ziPzٓ6P[c XcX,'j|1 9fw;IU+ J;IIfq -amFUVGVmrL ^i)Rq#dWP-Oc738CK!eX9[.{-ӧ"+FP1 -V4;EAU /8XˌTYr]j ?P4 (3] ebxGwgyű w(Y@#h%0Z3S\nDSק\`ClTޙ%+:(_c?}r_2s a _ Y;oJQK@ph3)Qón9ډPۊ\̉j0{L||MˤZO+ݤc"Ykf!U'fU1Չ P%S-XPy;]Я qHG%(]%m[Y}׷zP/zBch&[2wmNuF3z߳0j;C>L+oaFeEX_-Tː.L!4&gF4`]Cd&dy3 DBurI+pH~I^q&(B 7g@Շƾ΍ޥcSvʕl|ƦYm5?US. mPʖ*ϑ6RC JW#c@`<Df,n:$'3_rѪ3BxmVG[SL6ͩ~U.T:R J|9 =GuH@~k =>.NSw!c~zrm@06 NiRBVc*$ E-OvGʈ z uzc;\瀛."d*;q*CJ njl*ӑݟw+`8Qhd9՚t+ d3bPN\<pK,]Jݐw>` #7>Zd^[V+Ls?:0J$['-Y6HTNwYJ$LSqXx̬]l6VgO,njųJyZQ0S?7u,"g>9bt++"eײ) rG4x]\~<~,$Ӳ09:li ! ,`rۆ5/jգ%7(=CwԘ:>UHYP3=2*&>#-!n|+tݢxv`iźM~r]tֳ;,jK R{J\i+8ϲQ#ؾ/ a~[.ʗV4U':5ml i Yo18%i@{7AmeLpD^?#)8Ķ#e`œojP) F]&?_,WQzd]ƍA&NWƕ^ 4OYgur:uTDxQN.uIz:]u5~fGDdB ?o&&Ӄ o:-g~1;'NGTz^i v.Dv"}%PNY̬e)f51NºgLK3%ԷlwpA !&4QrwϓНL(=UdIf^X>`XÉh<0ߖ]МnXV훕PiWS⩪QrD{bwe{%n;$1%^䜆}9 N9S(}?%G(*d2ELC~aN!x62*[sK' R\\g5] R2Y. |jN?B?v5φs/Of4){sĎќNfk-,\xO{|d,qizF8DM M| #4]ge4#V$te_u,Ē%7"_*TBppQxM7?+ Ң+_QX(xV6jX<#e)"ƙWyhӯtQᬄ`IK&ucQC6bbB'kneNNEQj-cfO x2+R IsQ++wBP0 duyyuvUhwx+EFp6uor&8^`'ŝ48JOI.*\dD1ͣ8I*6U߅IĮQ P!bZ,!Sh4hֵt(\=6G8VE"Q{qߥs-# Vh;VԝK S*$1$8:%>^+/[ŖU5':ڨRULvk_Ւ`)ZڿY#36on3o0*'Qi){5Щ 26RԖ<{9/Uoy~p2㕪Ja7_AE8R "Qi|LBi]eMͥu4%Tb{qn̒ "Dh` [=F|-9 llQU"㿡nR+q`bXbXf蠽wH\.N'=d)Nh1eFh} H;`FN?, XW-qrUPa~HDդ XZ ]O);'qk6#>nxq96 垱b<{H"o[zvs3]aնUY 2 n@%UfZ$Q6c$4%%Ęe.q&^0URh.'VPlh6g(?']_R8G"7>74 kOw`mޚ=*03`|՘Լ8}%oB~,m'N@wO {բzg`iz1 EhFVKcz==/R@ҝ,\v |(-C ^HaNFmaINV@Gfy}`P~X[қ&vtkt+qϷUͨ7FNy49v1<9qM鳾pj̞{oK_`i_?/]*U{[[gP>Zudzs]4T;q`` Zg D1NBxQw^ Jlqȵ Y3 LO]&忑fŤkRSqSGCxH'ۨK##(zCQ07JcйV$=VniB<7IkCGcU肛N =Î$3Q-j( %Jp?R1Q_~"B@$vHr@ț^S13V!PT Kqm0:}ۋԬSAztY[巋Jm)C{ϗ9[\;c#yWr |zG(ZCgOãz'a"Wz ꍦ񿝛GU{l_E֏tGa. iHY/%4Mq܆mC=|`<2uHSٳRXJ 8Fe9ov0/P#۲K -K;1WzFz]tVfN+G`R&yNҰ+gI)c.Gnn6R kr=5l1`z1a^]{COZ*PAܷK8^w9vj Cr<#3PhtZJW:@!ܞΠ}8f#\U G{Ȓf} :UQ ?$j Bt\_tk`Hy}wdtQ<9%JXT`XzPT, wzۡOPq5LR\;f#˨4` {z8R .ωƝUQ!6_y{Ƃ\}j `^9sіeg$#0O6L KKhșR洂D٩)!a}H$qx[in;fs cKf'{24";394LU`$B$]]V9;IQjGy@v .c~Z#hClt"d4W~?#Dptt]~VC81mV7-5YCmh&f4@fۜu1ZW TKTrTvb^T-֞?lM8Zl Q7xX.'{[X^uGqkzG\Awȯĺp&"X >7H3'oi0FMi۬d-tjI`Mд003^ΧaЌFh~E&іx G.od3!MEɔeŽhp@>Rׅ{W_sU>Ecʈ,*MوLxvIZ5heѠ!hT9(Ω~-iZL6܁ăNV\"9QwН@?dב9j%5u[I(ա~6!j@k[:pTQʅ~Tvbt:N hufHWnjK4껪*蝚}Ao2O D۫dB&M;ؖ|&8'U+9 BRŰ6{aD1x>:hCûZbhwkdXˮji89bhPhKGdTR(9мee+sQ-XnƠ]_8K+)1Dht O7l8N]dM@>͛#\nLK[K0Ns߸m@VDN'M >Q߉AάdPDJuS FPpQ%N"F񈍧8V⼨޻Uyk+Dyy4| g.#=)[Ęfۖĺϱ! Q$ûp ^NM)*ļ,̦='>`ƢPv*0,-n1%eEEDN֑DdW ,) 1hII5}A,$+JKghtBU.uhC|aL`٩Q6EDCSA|ߘd>vйvcmW=k0iu*a`j fgjTL}h/FݼA3OnM@~>Yӓ$ }K󸓻9utEv(|+j1+MYJXI-,f[k|;4l7Jx@PoH9XqBM`. y-yHJ/[,Sm,x+%c<T=Uؒ=ҳV,DWN'lH>HS}9;"ls(d)Ƈj*f)z e W0XvU$(cAҠ>`gVJ 7ME8f[ќLB{rsSV r ?i~E73_fvb?w (;&)t)~ulEP\oĦcJlŪ-&Ш2IůG3``Y/S$~,,5C&G,OUķL}?MF C -_ mYp bb*C+]t~ 9!N9 :~׸CW}T֞\d7?~]Gݜo^5^Ý(3x.zHBqz:VStFlrU.յ9a2ҫM2:(WaH>T–{,HXýЗw ۄovqv|O#nрw;z*x>+1 jFz07"(?iqۺWu. WTVڋc椺+ޟw{rمֆ,sQ)]ڢ.T+ mݏpfp'[G1[aN;Rd唣l=ֻXD Cv/]%tG։H7cxuv=d{ًN/tV#Vb;=fMY8tIw6aLI3׷ZNGHC^[v?ԯ:(@|Ghn^8y^v}Cuͬ[qS]U֌@82xAkt}Lvrh iT !\w ZNQ+6C_1A[%& nBr4Zpo[2I ؗpk'FoNպ11䡚nA'EGEe |[" UؼoAl0k3K d 6tkmhjZ#zع6B 9n RڗQ'| s ZIARd?p[0%{5 :kAUnjXAz-Dv4 |$N!ji">f[Xja[X5wMw!b'Gy{(}V}CcB;yRHV+C pr[ofr-W}ȾN!y? p2ykH ʬ}#ۊ`P=(3߄3tnq` :P EHl৸jod).M dz9 ^qyfQ@pxNQS]HdHޡmQ; w*JgUm9КlJ[fcUpnRkTZOr8<'?>ZT7kY#'}PE+2O][TWޑfm\OKQW^ I ZnG c5̯GɷIZ9EXnUZ?j eKZ^E*]3Y7vV6(5CʑĿJ..8p?Wu3Tbl]g^`fEG!N⠃ɺD{䟒2&ƻMڏ^yA9-6[\1(|z+@±z$V9E:f_3 G}0lDLΌM2Zl\%5w}ot}Q6`lHa :Nգ]yՠQ~rJ/ W<#M1PB Lhj 5].m|Ҽ0~2_z( ZBL;xrSZLwVN7^j&Z#+6{;^Ők[(A!,(3[ D-5d>vg7b[p7S]kM$#ͰEv'̴E[yyH\ | Xf;" ة pʶ_++&*|&V/ $k>$yig6KW{v0d>u[jt="Z&xA98KdFq~ڇ kr &j LlE*#'ЅY& d(/3`ou8u@9_նGJ.*:܏.KqϓkɍfMƸ]T$6%L|VXIh3q0WWTs0Dq4%Hث+jdT!Z(`b@qwNu|FƢ^z+,e{oߐ^?g\h^4I$5p怋U@kw5#__a4u\/\\1H 2w{ukrwX :Z "TQ<;LWl[ 2 )<.B pTU l -B_Udsd܂Dhzfӳ .E۝ yܻea*/g ?a`c{__ :*ӝ@#l3ۭҾ<])B! xb%i&9CÙv92,|FdLLPlw[npz ZTLǤ#rw2P֔\}i zUi+aqG?n0uO=wq\'zj"[C6G캝yԄ9VФK\κGش;1<_(.x>k~~q}%%.Nl:@-:ݠ|dSNRm;:WÞX6EDB?J#82!+Eމ!.6:q4&r0LyrBs\[y}|+ESnݻS=wyXTJoHMkԭ*SЃ}fEQv|@ˎ\᳿[i7}1thy}qYue9߷cP4n6LraߎM 1w~j5u =W!C@^u)0nުcl\<ӑҏ Ҡa (LODn\k-_ aH36̰@ e5v=ۨpR^ lYW(,U_eSA/:6OH_&cS}}ٯz2$:;ޮvKE<ݿ4HuE:2v~ؙr\y?T"u0 cNᲙv}gbuc>8kkT<՘#/Ei7UяX9軾c~فi0`xw$!Ր5[qB?)'GkZ ˜G {$L#Ig/1%HKK1' W53bϫv<"}U c*CK?0TܩEyCwu,|Xojr#9,s0/7PMu&ln5d@]=6 uaMgɹXi"l;y< -༤xQ?)+]j}F"m!&^ru$߷!:0KV>>szjw//O ]G U O%jOpJu2/[3OB%/w+ )h1po!<`Iej1~mſ7l!*brk_c Z/Ty >WoG龕3bʨO>@גD^$-( #)`k/gr X*&-۞FJ@&nlvL$ZGUNJ ^e~_.xqO|ޯ L|ww`G?GZ|x>_Vo0%+[+FPRy>ݢ7] 5,10λ+||4rAv{@m+i`9yz A@&!ꍛ[IL|9?b[ktd$Ea(F3O66<"ji4 ee14PmSJ/W91IڡMCCbҮe)Xrw?|IŶbtEVj.1K.S]'2}mX)2Ԝ쟑 +d[y]RUhTU$FI| >F{7-gq1ַ B2aWZ,%䗆88POj} :n1<߫#zR1 5Y7s83m=F?ph[nqzJ/7辐eGnGH܀ oKb~QG|;Aݱ&ڑ\U͠#Ɛɢd8(frEץ6;l_g˂ANAEIGLk J$Ds)'RVg*3@tl[90{@`(ڤewrL z<?}RQƧv!GF=9ڥuHmڌW5?Èñp1o*j!dJ7 +(qD4#JrZۣ<"Hm %B ,Y%3;=c W YU9L&FO&g&Ly-nvsջx\oۤs}BADE1 LjTy+p_'#V!=؁mp?юoqKg,Q$,}\ y=>?:AV/v$|gjq98KMz9.ϯ"{Ck)6 m pF0H`8ʥj WtVݖS {V<޽AZ4L*V)KqG4P[U&V0Z WTnme:VqRiGXNꃋjOA y8,S~\nu߅_#A\n䴼6ԗ`paX!`ZDY0ZK5W-N].g 5{08ۣ[~c4Y'1t˰bHgZiQN]=t*0Nl=LZd9-3X|lgێj!| 0uQ,|>Wk޶hu<4K  9sp͟gV6K}Cl^8% _|L'#ùTdo/1#Iz?2n\תO6Up+=:4y1t fh /}-rXhvAx^mwSTi#ph. LyK v\ ک1OKQ_ym62\SCE ne,i1| Xzэ}c ~?Pɋ1:áײ2C (fXM˨Ѡ\R@d }Y) >oSG[a9c#8+e^yxQ?髚p .S!{v0#e2$b7:qqDž7a%X3 T|C Rf5 M~ VYΈQ _# VqŎh7ZɈR(^}p6`TpGqP\<9pw/_OVL%>Q ,yt@ #owE(@eS{;ǯ^ȁ|Y=q].<c5* 3"'m&nC`TlYUӜXĄղ5b9u?Ĝ"*1p6WI~h/Od#uk˰4i ր?AU72sĵǴ4%1i :rbnP`u%*s=^U ZP-7RAsAl'U$>m _ Pr}5J=Pa~7kڪ~k?~+uK{%pD+2Jsca尨4d)U4GT_wzOMZ# t=o 5JQBT/'R.@0;BB6zdj?OV{I@ϿrljYTX],Pë":Y5]_T׫}wOIhȘi vn 8(/"",\J//;Hn-C͒|ԭ*˜]S#8ˑv O^˧g oa2& Zvu^rڐ6V jBhV–ӱv:Hg592뗊Pe7qG!0oK[JiƌAsM8t=:[GaxGYwX|p V) m̴`&$f꼵\y¬К@Ed C&hs18MXK/kf5[F1p?gUM\𑙭c8,"sEx>t~G$Ǹc6 = r}|٩mc_%Zj_E z. Nm}CUp4ݫb(jc3^MHپ@E$.t ǽ^/3j5?NbaaFE璬3_$-H$;D7<>&4>XQ勀S sݱJ-Jִa "WNIT.Hu) ꆎB?Z5(j*ifw?nƒ5j0 M2"5;>LdVe9Ɋ ,官\5 M"[K QPCO= K݃ZB'2¯رأT>:~Uo 04 ׏-XF+lBTk;0C$74`+ߤeCzc>6:Z)U_V@|cWtn~QXU8MSTUTV;CW:[+mz..j{BsoI7^ROHJ;A01NyU>Ӑ_: MICLkAee!W_Э13VZa^yhaReaU9FhN| Db5.Jϗ5 ?@&wfDX]}5Aev'-٬b ]՝9ʘ?S&hx& ru=ʼ¼(q0fʪO"6?[").xM Ո= Qj$!NVlG}Ku"BDߟ\;?xUK}k{#60ӧ)7$X"{fRf?Hb&[<K))}by׏R;(fՅk?5ժ?stFVs 'GhD.3RAkCZN)`͡M3w)uMIF}nm,mOଣ3~M%lGDj魑1WƢj~[d Cxm:OcLjW tNM&6h(DŽ8 <P156@(w du۳Au{3 7Kn@7~5?zR4f:DՄRr)Qóڡʷ jF+ԦOMPxn Q!^c ]"InxBh wQe蹹>7z5]X߇(YD?LLIy`LbĚ`ٝJ@NZ "y3  D$YRlWZq!Јf ,j}JkW-t+"'A:mHUc!jHYd^ڐ^jmVz\/xApN ;::0 dkL-ܰgcy~$[Ɇ՚$9ŔcR`wd\~Q`R+1= "XQS2v5T0"^+P(.F pR] ,(tM3`\0'SF)ˀ p+-ᗰNTi`pSaWO4619Q39u)yNX2^ǔ%u;2ɞ[>B ؘH鎴҈G8 AgWV4H4ܒ^0`]-r=/+.8^eo> 9Ti׶~}Y*j$w.#HއCAU 7"Ӯ.Gƥdtuђ-!u;4TBRVu g5X8Ru, O'ׂ mm f;i?^8IK۝mTow&pؿ`%}a AJe.~kBnJ4ׁa$~-]oŦbԱYS@ LBwT6zk)+Mzp/"k,G+_/}X}$ U.؎j2lڅs`8׻AQ˾YO.5`1@y 6E\hP{'W]0fhS}f;e2+:UPeG8';Dr޲,[W~Y`H7L d/D߁:FDT|vDľW>bT"@Vy}9fU?6爊bc|Im@=Dl VY{ f '18)6 j]&}x}9«Rn:eE+Z>PP\9Uqk_G≈` ۪tx77ЦVSMMv$ G?~6CZ1f141 p+vq& 5aN 0eUrߨmg>9/3=4j'BkWg0"\S^#v-tEyŃ>rPsdeP7M[*]ֿw^c{QK';̶ަlLa2g;I0}GZ-EUҫKϥ"ZT&S-f9Km#<@\%h=E߰_(I?Vd@R eɉ\qMzͼlx{I Rc"&gKoWnlv;c_-T 2tYx, jI7"s$pߍ4&=L ;ڱ_Iڜ>|>S>Ysݽ|pM(=ZD\qX<ӿ$jAa< K͟ZL7bOxj P7s-ǾTk ЇJaTFeٲPxCW4>tw]eX&co. r[, KMT͚nC]/?e)x2(IӢSQ,8gRJQ5澮`aY8KK75mcj   k6JkK2#I{{ {idŝ;99b_-BWrpߞ`k-k|+[U#U-~ea㉇+ vuǝ(߃-\R0\Kb*I>N+mJDZ4뉬UV6#ʹΏn>tP;儵ǺW+= Uت 7]_ޥ.x(PLc"_f^R/c2umk)CHJBoZPqruV?>C2k,dg=&ƄpW%9N %󃍃,BsP_z) WX) @mӦIX HT? /On5} qRԱWn3iUY)Ya9>z>1J3`NUw,v; i7JxM"MIsiG%UM~$V{oQpyȉArJ ~#~a[WJޥpebL:OДE' Ʋ}mi23gs~1]V9w'_;`t$"|/Vd/-vQΟ ,꒢Qv?iXKQ(QqlY̏B6lW{R@z#v8霮uv%oV"=J0Mx%PD3T1̼PBd@zvk>fmx3!޵Vx תh&LlS_cˍAq4_2_=|,V7CęOߩ\roJ{`~(@jcr1>U (P&:,"+!aK,bWiJW eVshMå 뫳A T@ Yq믰Dit9B% -u].-䖢F;.c}ymbgS4/)L E;~4 7MzRbN@+D%FI)ɫdmN-<}R ~obG9vn#"%CøO-2IH0B&Wc-Hfxq9|+>դXIAF :^Gk^cOJ_d oKk{SRzZqƳו ]jF q) Z/B}Ff6=#~Z3aH7Wl-1 a/o=`*,:LjõM5ūYGH#jr!5:0(L&q"e)!$79!VCp,K),\vq,A:Bԍu3'ޝٕl?^bCi1 ZLsS;΀?ك;ᎅ±mW!V[5!~Ŗn|gh:$nhh@=D68+:yS `0C;Gh d(8![7bw S\1&t#wƚr%@f7bb-Jx28#EuEJz:WVQqJ 1k8d*mX J )cpĞD)%Tƈ; boG}E1Fbyj!ΔZNJ&ȂY~$<7+i2 "q8:xg3@W[W9Ltb'\<{T􌕫570>5NQ):v*ȋ,^khp-KSw14(ޤ::x a*]4Mi0̓y䯐i* u~@-$;Z5ȿ܏J}eZZNB,FPS;m]gG`(HU 4d,WÕW1$B9^BeѸ=;AH [~Zr+?9pw#vqZcǴVdI^ b:lnҔ rd#eޣ朗paJ5Ծ 0-+ X{8/z0_%I9O@x/ybvtL-8vf9iaà\0f,<~a[׻V F-ټG֬RTDq/OLPYxxO UMb C< &3@iOIdD ̓]uf VAX->P]L~WG?#QQ#pmvdso*U 42A0^$Mqp&7(1]PA L87}>Qb- I6Vy] G'8t b=u w~XGnXە&h ʎFpjDW}I+8O+;61Icr;ǤuCNw[gcq-l9@Lb\^W2arYn|nsG2Bd8}7,8p^''K &]oRXJz9"6z 382[+^X3`Ac=wL7PJS ,BUE=̱&\춆1/eu7JZۭǩ;;4 sM#> gx62?9~l> c \ml[APzϔ3тoFkSހcW@5 _BY?' x/oP(WVdb7!& Oú,cr4_Ϟ'E|> *,pn}N>LЮmA3Lh*`YZH;~U߶ݺo "X"uxgĦ.I$ ldCZH @%gGޟp RF`ȢZQ1.kk:BqM,WX 4e+Y+F)rC?ƺc4'SAӔ0` n+4Zj$~A⪸~"!<W"iuq'l-v%chˬTc~R2A3pϓc+p+/j|treZM׵Ȍ=,\5R,@j:mxkD2^:`F^;RYPn>׆VxVi,F&13ij9B"F%Q1$ Ywm^0x4]%o=Z/L/>&6)]זahV` *w6$aRȊ2yX32"eaK>ks>푩9RUĹ ze(# Pmd@>-Ad\.<ʘpzb3mm/Vr}~5}Utp;e\kCYϧ oB=zHJJeN!=5|`2)0nLL߮Ԑ20BU o3 3W,Y_"#66WIЌ1`Djj{ Xz_JܽV0l^'$ "] rB(.¿,#'ul (G"&|!D 2IATmw4xpTjOR^A\Gڨ"J{ \(#||>m%~6.&~E|eNb4c'"Y 5")nqg'V5wZ"[sΆLWz9GW9{rpճOA:c{5]rgHD9<'7+r7ˏ[reѴ q=HߡpCa"P;+_B:@╸ZTu]8 ϫsRs 5p :Ux DCW35㝁q"K1=s ڴc[ 7d/-~ZQ6yAP}<~M(BpT4|Q}DAX'pm51V8ycXh'LvkSS;D]Pv&Q>G? ($)Ki <0[p7S]9u-؎c$蔗6Khop`b=Kf*3 T*HLv8Hrۋ+msuPϸՆUiq8B476#W3u~}ɏ!MF#ݶ쿎hoTˍ Y."֪;z@c/2u>m;fAGd(/3`ou8u@9_նGJ.*:܏.KqϓkɍfMƹ5N1U}Aj`i Zg=o!@ vgt3M(}!v24tzM.3 B%H&~F^Hed%.j_wVQ'l~ml $o N.(i Z Vz2#m)-xގ;-XH%P[Ykrakz,A J ޙi^8ݳpSfDWvQm㎨ki_LGLQ-&£F nZ)ݔsT/Qykj8} u-rPSW~?rDBFv=ӧHɧ?dx 5KV%S׶Ui| (2 #i_BY𳐬=1gy2+5pWn.@G!gnN?jtLmi6RGl{#kۇJzn21oBFNީ.p/|a_K:?6{2ڝĶٙm6]Uvs:d |S XDg28G #95}ڎu{PMŇѿXjEC~1tֹd o( 71 +tu_Wn@Ř5g`3l00fJ}h(Qao>5D}կY:y!),{jb,@>.V\k-_ aH36 8jjaY3n*5l+# 1>$.íUջ/ ʊ\/SCS}ͨiR}\qoA)6"' N (oQt2@bO8)5]^|}$[7 lltpfqw3N 1xcATlN -ڳ ǁۃ /#L:i$@8ºf_ &# RM!i0]!:~9K!`G/bM,/:b>h=3]xPJ( >÷D%3l>aQ'"D-F]ԿVN Al S˗Q\_cFTJW٢g'r o94ddYPz9!)UD[^)l?;K _}$li9~ꧼḍAu6:z^A*MpW: W5슽Y!mw7u4V`]ZsPWw?dΝd|k 7SQ*"QW4U&6*&ď C~ZLD\V7Vo`mҙq^tf~H*2VfZh,=X2"1>EORb&L8Iei@/ٝ,ze8I,ּET`r4%4 }*6(ϷDDLQdJuċ`hy<$IZ=Nr鈧7A<G_6TdΓ7^qsZ%q!Agd _*ko,xtyWRը{-[߄݅\%3cOt2K6t!jv}Pq5IHij~-2b6rW1-\8֣q,@ efG>!x%KI9-EQ n7IYUb)Y`}kI'V65At܅A? `_x登*/G+:\ BF4[ќJm4BL4vчx7Y3ո:јK퀀!H8׷~e*QW:m#<.ʊk-E@DN9q:cloƠ2D=sZګNmaRN!h)ׁ', ~Xf SԣFb7֐R7U1S`s3.$Ϯ\h2L^=“lǔr)fOe!;d*)EaSРg2,-T[Ӳn&f ŗ%.~x&uA^ MgM{yR1y&e`$:WJ൬^#0S=f:SU]*՗(ES"ouW~z1p|XmGArg?1dJ%pJj[|Z+]^zHX#5 `2X wLSuRQS ]̦$N1މ>5u_ ͞ CM0?.ÆHp篹VqB+x*&!r,6G> ض<4;[@3Hqo'1ڝz?[Ry* Z!nDrdsWҜt#Tlى]z+u;Z\s!we7ťkJB_-B:1 @.ZϢ;3q }4)w^ FT߷_=uש_(8dOt>jE _C%CƫZ+ 'Ol Av:oI' =$dGD,I %[Z8\1}VF51F|Bz xS'ՈwET& jA/Dž_宻;Mzb%VBA\0Zhw*_2hؘ5_ӈffv>: Km C#/&h~TdDTn˛10}YU9Yb>PH4wpkO8ԉ5(H=keBN Q?}* ~Q.G> ';|k5`RZ JɊUDsf?Lũ5[(cID@^bLsL8=:}*GyW#[uZUnvf3-Ov0CR,^I+ట)P/)ʻ*Fέ`M͒ǘw"ltnpKY2ըrKn[+|k ݂ @CsiCm`{O/w`"Mʮq=:F uc[5)Ƹ[)sEVeP~Rg:Qx_AŲE:[=Qz[oV0 wn9dC?ٜF 7O jc&h{t/Ix170rH#zC3,4(':|)(ܨ uvOz2A''MCQ;;λ:9B|v-WPc 4q]@J"Y)x*ogE=q͸DZ 57 aeἤ}]^)5BiDϵ;EX^8`*[:XY,Ա!֨K-8>7+BW(0s[| 5Rln2QT$<s$ʈ~_^a&ʜ]{oh$οeGI6 MA. 6e۵'^l|Sl\H,q7W4 _40T\ɗp[VvэpR⸇ꐻWC f4OZ9{0H=./2so9>V~<h!"7>,s)NBRɕ>XUU0,AP˜4ϖ=eOcMv%J:KSlG}FчHh2*|=f5LH>0@<=f^͕s3⦣ VpR荼q-@E`k]: .FwlBCoU=삯;PٳWşG~0qZ<}{4}M9^@~=0z\%\wP$^eH,_QF׫-.r^\"TP#\t *uW%˒K}$U0,o½f-6]n9cCuOU^f\,,O4 #%%Rq=LU1]B}jp)sB3p m$Yե:_|ML HIK5 WOO0eCUT7HsH#c&-G')?aX8_5i8lpeS4&jC;|l riiz8tp.<4rRfD=;ț(gyC0rGJ&3ʷiŻpSGYrgkW~U=_+oRCDˢ3\c{i8^z~0Dlo5XJP3O`ߢ\mo(`:T?|oCr!rCətzE} F%U2:7sLۋ fl}KBRmpƯx w~;؍×;U`ͧJTq8Y 6 7C]w\Mb-T7ARm}EP]jdTqm8!VLqk6q>a@6t- ; ?-gfAaK_$.f;!Vd/Blէ<ad'/lW2σ%M0YAύ%S2xZm z(B!`Ň dM; (m8xs˼{FؔN+9%I^4gmoƑ@1 MQÁLV+US0"d\)qR[gOJť. ^aX"d-6iUat DqƗ>ܠ=}QF\>&M| Λ=`w%).f Pu-#>ۏ%aҊ$uik؂]SETI Wz@z!$7xqST"55ޜ' VApUR^5Ίm6 xt'/h qYQI?9D}p;>D!ޘ1ȚO&POp?X$22wF8*0fKq!|a=?/Fᯐmnb6.IR1^8ܟNJ Jv .75RQ __sM0_܃)\)P,{p)n'X{rV6}&7}ȌALzw _S(AfUsQZP}ZԳ<Їx~e-'Lj4V-| a0FKi&ɩQ9\@æ|PYoN>h+|OO Ů/ɕ0L~'7@0|/eu}zh̎ҿ.5k,&*#۩{!&qiŁ]%쒕zR /G:,xRmvEZ"ex[fVɤlYESjKR]KjX,ckd)) Z;3p}ׇfOoIv>ݶ紨xdVk1qJL:ն\e8Ft0} ;=Og:U;$qηi4ٌ3̵Qoc>F2rC($* K9=Z7`7!xaHP+T_~'ʓ8JAХy{&[ Gb s4)܎7.3l1=m`|z6+\cWi)/{-ԓ{/zaO =nY}|369Єs:l۹(+Ӽ4 Ak֐fKE,*}i"D.&m [G΅ L#'HbaKZ&c5:stX9옢L']D4ղšY-0ġD[cr+qHZݖnXB꘾#:?4 &Lm(9Hu4'2a1PDīOu(kw<BK0wnq^Kk4`LD~ܤ'++09~Cvu}5^Q'$YؒnOY֫9.v)2UP➢$ie7n_]: wu*6x*RksoE{g-8\Ӑ8DŽ?,nw]xPd' ?^M|5f*R^v\oV+9?)~M-bT'mdx~5?$ ߦ քi׾UDv QFޜ1V4'{/ SၬaRofgoqv~ӜY@o"@X<9T0zNY0`2 Q"iJa\x+0d4!;Py\>?V$ʱ2x$4ilu}A&HDVB#xdzQVP`]8V?97!qא\ϥL4qԿBrgb{ĸg  P(UKnQ_ NÛFU Z)#93k-ROt0dZ^Cp"u"|-b%M̰NiUS;q7c x&g)ܔp7;j|0P9VČ*AfLj0ASF? M#Է1&$;Fvoo3[) $&7rmb} V.s zlCK3-q\kF~hZ%߀'~(M>i+~,Qn7%aiȬ@(3).ģ%R^z-1N'|̼ /E{VQez?OʐʗVg&wMSkbLTc>om̺,DjWP*{;BI K<Bϙ-`ZyMK|vg)r9 &{!s5Et0.[K Y$;?6KE?q$2d+]~8+HUSGݤj" fKSz\X^)p[C7WtƦ(BxQգ)BQ75R2ioS.}5N/Gjle -8"轸]ʌjFּ*OصOi%'7(?Aɇya:vcb9\#"a3t rr &:׀mz=f3L(kڗDCm$#WL>{LC\eX7^eKR*h܃҄@~Z]ϧ2&"*edsQX[[)ZK$m;7Q@rK MӻS\zEk AC'4Q) eTwF/͡ ) Y{Ds/izBΊc?#n4{ W Ǫbhg88{K]͏8#vgL,㟿`OuՄN ~:! g8ϕɓ?hOeCXmK'c31O \ ",a)WR[đ1Od6k8V\lb0V fzSdؑj $`(Uv&:9f`]B-XPo7RapgE%s[oT0|\/>-闬%X|&eoTTĎǟx3URBwͧNlRX7\F'@hSԿr(N҇*ª*{k֊Uܷaȑٖ9*Sv@76}יuȐ =q ;uD}'֒-2KR2|a\bEFkcErn!|lH 5V?J__EN`᪾l>1S4h_y1X_LrZ= 7;4ѬP݈/rHx6d5Kٜn_4.kSPë.Z$ @|A_E 6$;Za)MӫYR.G;~l&S |soL9fbMU+^W@rc$`q{ZQ4q6o.F#(x W<9°+vuee9~oɖ9 eudM'C A6^g!D~@Kdž)>a>ɰ]@4XLn,Zscp 6[]cp)4$ yAu*y)ˋTџcѩ7c36o60VӬ.CJ([`fn ̉IMn+wanBw"S\oV7aֱž^:BAFtKL *y\4[5_E%ץ VN+| lamЗ nI(҃otY#kG=@;J 豳 i~ҴE{>9qĽ,׾H<3:3yBI/Au^=a*-D`i +Ft%1q/o"xkI`UBo$&#&pP>wCX~,uroK+6c*Lơ8$.-cyC9䱰v(dÑ\W]k <1k1Gџ&l<ƅg[Wy$_sv]d-{>Ux}9)lSN {©?%*ʑ7 XhAiȈV#ϣNhaٻ 5}2bμ kVƅ(5kci7Th2s,jv%3 1O(SƇdKNSH(o-Җv7TAx# 5*NaRm;h%qa:!ɮReiMӎ6~r ˓p }E㼄EL{)[Jp$Hj檷̄}|orΫ~VjOjWi#EGV' =\ >_]EIUtV7*tt`?Wo,oB`ـD9)DpEF\G$=C,ަ MNp~tP0NZ7W:U%q]6ܨQ@ܕ*:}kOO?$ƔRȶsN9捐vpjǨ0 CCF{0J ^!(Й&8vk ~s pEBܒzX+ S:N08U%^U̟$Ļ@gTI^k 1b"(ڹrI!b X < 41* f+g ZA;X`0g?aƛ g^@T .-LS3.VAr=XQ)?=(q$.UKR:B-g;ԅhczO<[VYP> U֦l*xt.Ds^ #aMt 寶>8H[?Xeݮ ĭ%ME,; +^lV(hni}LlJZHU5OJ_PbsFb XҴi=2fOP}^dB-t=KZ%i 2)MD0%bq]ҨX$oR%HS8{qRbi:X8_4 7  1.jA-|%E:E6L N,> $a,0ʈh Ţ Ec4KB3ȦOIv/ KZk7-/#<3%S\.Ol"_uC+i K@z쵫#?fm| =z="R%^|T:#sMR4,ߪ/>|X>1r]Ä.7Ӕ-4hW}Y2)w?NvU(Z17o[=_?#5iZ9( 01-rg2JLsqW&ZӣFf!k6˼̹i2 oOąn;Hc"& B =<:Rث1?6%SsZȹntYd9?e}߰!kjyY@q4½K♕U_c"3ذ6,bt/W`jm|*o&鸁7{~F{~0@ڤyȿBT۽+Ԛ:-oSߒo,y9݃g) 7$7lIIx>Ԏ#UCDᇭtptRz3с7,ga~ 4<f 95Gl!+# ,1iBn.qd{`:^[s}u8 -L"5aƲ(J\6"s > s>:cosa⬪M!NA&~Nb(cxjO8q~jDwVQߛqW%'}yBG0/3i[iq 1g𫪉FatLR&7ک\ $e8;ZؽVO3TWOa(:'3ߡDLv49kvuqL6_Ɂz,ѷ'&RjpO0]@ۨ،7 9ma[<Ȍ8# x_ʠn]lQCԉWz,knecMT Z.7BtA:70FMNjwcTkTo1=NTE,bv)l(ދ,)@X[ǿŘ Ce:sgA<@O[G{PaSߨ-ãc U g$s O0EceiM?؃!q8 [.hd8uVʹ}Ȭfn։cLfFGw  9ŽQ'b7>"ce DH2T]0>Y-]8y`+ͫ`Ag= ő%h1ozsfNU7fW6ZqkS㼕 fo%4c3BOGHQm }?-:1PT 6_:Zd[mY}ؑV8OrB1jT0[TFYJϑ XuLyci&zOC9x,G22HoBZWmT^EE C/MY]>JZ .`{V2EA75>._ ;Wq#Ɲ1Go(f3<%\ \"dTgjLB⠈@OSfw!*UyYN+Rl|鰏?/${_B>FS"sC(z{Wy j$*%v;~ J:Q^cQըla-.h9Y/^jza_f٭Fo0>`s>6R~/~/sͳ6X, ᐊWaΈ Ao:^/R)p`p_/?Eؓ׹Mnu9t5}^xj(7Kp.lm$´˙xTˇlL-=\'xxIܱP']MC4ƿR y$֬[S> E,Z2Xvޖx)M?=W;,1!e߽~<5A{P7uzP,߫U"mT~C}զ|]TF1ͭՕ!Ww^;#BHOk#'MzIA4hTjK+Ҧk㡠yiPڣ>EM\p8 4OC(TA.%c'ǁt.SuPƒ_ti4IT(?T%[KfJxMk 5\Ó7DXM`$2Rtdb*iajH٣u*P0 W Ow@[9#j?wjS:G}m4Hx+iٻ!ɚIusG?q4/\Ju 8d')c۝W os|W;*v/  |>3!ĵ5p{tJb`p v5v-Dr} GXshHwxFMUlV^zY>h.lAz 2oj |(W3LADU/e@ ڠi(v; %jb9Hh#|#/mZXZ/ _UB+6I 0a[Y1YIn䞎0 p D%IJ$k>cR8\i<źi]>}Xk풻rk/!/.HձUYBDd!oT{c`M{7 [g34tf/z%/,Bw3`(s<"TO$%<_摧yhXNqH4BЂ:i&S"6@tv}qAX7c*LM3߰`'r``дCֺ ~ ?j-ˁAgd4i oH"`Ck4wtծ=Lj6>4[ v:º[4{aW2(+[2Z$Wڍ~d*ԕ~*m)m~2Ұ^_`q|)>S )cMuzK-5[W2B66yҸ׆!WNPepaNln"*iIo+/_3~24cHBA=g)[sWu)+- I3wBכ'2JU;tP/ !{ SSBN[zQg:}Oݘ״Ӑb6dc&@$_C@J/u++"qERՅyTxoW3CqS61!r  \]t[k㥷&@ٽmU F#ڮɯ%S bU3Gg\ tה(SkP$ +$<4>}#G}GM) ~s:GJ7C˥\XS Pi=ITKl@^Φʩș^2Y/~EzKk\k-Vos4$m">YwK5 ܏JOq8,zXka@.w;$?r6?ghcIksnx,h Ö&DdWݕ3)9 $caT/> mQt-fBC; X.wߊ!mI2o32rѳ{h86wz J1j9GicW)O* ٠z/[Iɵʱm](4&D9\2dueq=2>uH<#TBsua]hH]e YPШYmU8۝jgAn,<Ĥ#m{K̏ ؆dy0X@6߾BI"Vc)g(5{Gw%u *(\Vię o ϣ;h^|9Str}1{>®O2ȳ)2E]*|.jSP9J/~ՠkHV06 ᶹi,{IA5Ϫu UQ2 {FCdzk`ӆ撣;ǀ`z})*;?c v =7!n;.F \gpBBgSv Qyx` `O7({Vkyh%TޒC]a_yɮN.//¬ܽ 9e _ x'dHĀ$t|jIL<g"ˈVfr!Me39dUll )ˀy/2`K٘^qٯVnM`ո1$q?Mij!Ӽ5K+蕝,n8j1? ѫ dQ;9ޖ9qXFv=C. dV@ HQK/ /5ԜD-RZB2;cw}ZFjߦWv\ h6R%~:x.h$6T3hDžn|'$ 3E0C+ 3~\03d!A?`e- #QE2rD/&X102m /R>%.5X> DLõhEFq4A:dQI?m!̧jZn7ݭ&7Z_806͛MC?/Z2wB QB3̘ޡ_H٤ҳR4+uE[(r~+Ͼܨ)_/1s;"Hyvef |f T " ̏z4G^#U mIv*bgyB>}9D|[ V="@~6]4k=N8HĨ\+b }5mxzc8}Kk)jf6TC(jj8%} Bp@oH\{x,F7ʓkl5~{un>d+zH+DIwuG+A]%ɼ>4(خfp& zx6&(m ,2X>j*u5)0]QLӉ W-F jq?F6 ! !ˇ`e50\OQ]~,j9dXVy9?d^!,no 7HA.vFnlc_]6 آrä:XF,髐M d!Cx]˿wydA ,^**Sp]P+?$ڨdjfeeV*nӗmNKPOUB8jLF6%.x8ST484;fձXS'{qÝA/d2).l ? 6J>wܒXhVeK&"'Hmb}=WG1sŏ>1@B =f;=DRy1_n> @x;ꂱS۶bLJ,R:f{ں6HeeH$[uZ*߳501tx :?|%KT%CcF ^!=.XzHĔR;,[#0I yҸ T"⣺ﺇ+6tH-J{ԁ]ңϥ1Y3~9"dt!>YWx/ńY-w|FX:<gx=]nvhXa@.'LrBZBL sEs1VdIxs/8餇TZ7)1,/RCwv$}yٶJgPxd^ c}mzE_+Y/G$T *<M|S aRגFAᅃ*K( Y2`I9=mAwMdjR>,&fMt*y, WkqByjidAN ҅3Y:(L2׸0=`fO=])C;0:iG)x$$8qOmAHGwe-Gjq[ڴ?9w=lR8nfc{ kFm up.Aiqb2]crシFzͷp Sh4S7(.GI;zc%qHQw.a,cqD4\m TsBaidcdvFxiw70]_^|K"$/XkҐU9u2I1e?IwM5*nj۵L/Ok#7.kfuw e3аJ&:%J$+K o[%(U!+)Q4&9 "XU%+0s**QjAg MCwqt9<q!GA{wfrN&T~ʉP/AP]?}{orq Repm~d!՜eLRuRx"{-jP!Ql^(ǁkɴd/xh5_ $6) y0VIiG9}r7M?za 8)G9bE"h{^Ig _W8^ R6Pد*\N5 ְTz Qб cqϋi`f$ƳJw鰹Z-N.l9 Sۅ3,ˎ-{G"^(]@ "c砭>xk1,*HR/*3n1fuV\ ZU S MN%x^Nj(^3Mn‘=APGu])+"6;atv@j.*eQZOm֎Y0ft*` vUȐu4l׻uiMNXeRN5iIrV ޮY=#XҴf.GP=K5 T3{4 T~>H߭qRcf k/SIkMڇCichkR #٪:/sgf< \ T64"[@S-KuZgpbh[hLmMRVe?43.l1ndvkCR͆.skqz kk}\mSf Un܀e2OjA[ LM-w3[\Ngw?]? |Syiְc4} I:O?kkO}pv`Wze a?}2wKZԗ <0d`tȆS6Tހ?T&3cwť#QnĞc\lծB˙!M7 ]Q[S̊]1^*> ":IQ;oć. Gl69~,&}LĊWÛ3ޥsz! #W Zcg?ؐiyaQk1 D><6 Brײ[?6зI0K_XG-wz5sS?R) 5d"!VfнMUy{Ʌ{PCH:s-cXRʘbM`,c!4criY25}5 nf&å W!;oO'4=0,ln)K'zlrtkWT#Y6WGUUs(D-BȢٵ{]~._;r3BuȹJ9_ I[|/R,DG^J:F*o`S~!S/L#p;hSxQ\*f*uv *} 8+k\"}`#C?G|mЃ#ivÀYt#dt&Ըw/I(+MDMEQ/vr“1MJ#}ʰ3Jý7%+[fI'8dGTF$W(5evߚ8M9&vXkͨx: ܈Yu 8M8G""~|] q`ߘSO1e&̴8k ؓs>(AgQ*7 Lq&yo۽{]UE{PE~+ dr$D<aG22SҹxrJON"~\ש2qWl 2]ՍoFv5L)c;#W"˿64yj/H_S{#j. y(饭,AQ\_˺#ODM҉1Ek!?zAtB>ICo"i-ŴIn's1tޠ1|FvFA"RPpN_޿k~ ûki?k۬  tKxx퍧zlsT` 3+[=Ki=DP?+޴gIQZ=FK|JLx⏝ =Xkpe{ajJƂJI;v3U&ɏqKC=R^(%u źoخ0?]}_-pw+o1$W,V;%Zk}`m{1Ńe/03"CE\@Uc !j[!L./e9L<I 1)za-;wR[ |aS)R iM,fɵ|#fl2߹)/t"u1 ͆qrgʾK;A>؂k u\椌Mi$-2\VF?8$;{Vzz9>o94n\o3I]L -p^{'cєʖ3|F}y3&^ƒ~-$ƱT4`ByXTb [o! qcS OZX*$~Fu>1ˎ X#NHb%LGMr6}2S݄1\2l^ ^BDTq}Կ2У3V:U-7NJ5968whBtPI}]C"> {pDNǽ &%B^D*&{waY,~_Ǩ%I@2M e6+9%72"p}CC{܊1@=Ya4>_0zK1}d֛:mu\\q.§k͉Mʎ#—Dq~F%>v mꅐF_mi}_j'𿯶WU9Yl"VECooo;K`HzX?it{~KB0I2 v ZtQ;E/a~~:\W]uWQd ٌ M"p2WH _lOdj cI_?:]ITb3^KV?$rEs9.F4P{S̥3| ˩*h)=eLҜL(LlDUjOD@oD& ~~w㬡'w<) cc7p>C,~>ۂ,?7@@nMNyg,+XB9]fFYe$b(!,*+5SŠ <1~_7|7|c'[.I`\!I&ݧ?G!?C{Yyu+^/////d̙;UMs %COMj>1OӢ7>HQn%:gaTʺWXe#;O *U2A(8S#1$ F{Ym r5tZjeH"zo?HuI +򑰿tF^`zcAEKvi ԪfѯH%YL9.6ݭAlU"eYҌi*_/|BH_#1Mt,|iLSO#xwV82=rib./3"mwUcImmmmmgf$8 f?dӛ=o҅x|\?ĸN%EDik<7#Nb)[ϼ 6JQt(HQA$Mh\BK=>phG8PArŶ&-)S `x9 /*9uC?dQ'M mA$I$I$I$I$,Dڽ/KP;̄c1Ʉ.\V nthQlQ ^pfABJ$%&iAI;Lq<'E7% s).FՔGۃ삞h29vNxŸ %e\P9aG=~{۔slOj<_PI Cҍ6hM G@Ǎ`ˈ>n 5D!MMYw3]]cGx-6+eғUнZx7s!fHAk0*_Up6 [S䧌"JQ3({|B\]fpըq-wzxF}d}r|v)&XٚfjFgYaSIݤ N.<ڭW",uFt$*e_r7@bJ p+B| hRUݒHZ@l* 쵐Zq&dIbKzoz 3߾kicJG|^m(8 x0;OQh'XR.Q+榙CEy9염:rٸ񖹤ێH<ӧkCE'6%g՗":iyc]/`:H灃x2CH_/W!֠=PnJcG# ߭/+ȚW$^MHIνJCBDjd*c/qЄbhLvB{|fk"=OH1tw:vec ANr2)H Oɿϴ(C,,>` h5 ,<ݤ򇮷`%WVa2ub Ui6| TyZ!(gql+z'WsH!Wl=?oXG<au7o;_Wof|o oE?M}K﫿۳_/zm~ǿWl?WZ;Q߷[&{oH3?Р, O%x[&,vj h8j|(a_y &l ?;Pr(R"JIޔp 9q#bof'?kdܫm9iaL~K%W0$ 鄆! ogPWEIa;bN2="]`-6gF)duy(`)sG6RBpK=NsṼ[pKMDo JOri%! Ѓz\-!CX/ҼYFxg#d8Vj]#W VH˫!P$_k#~\3vh>JNr]AvÛ˛QUk}h ̣lo4ƚAB-pӕ$Y%$Hɧk34TDXP1>_PRcW!Нg! o{1Gs2Ŕj, yRf2ۂLT?lAY[Lp60ΌKHuき?ck?3ܝ9oxn1OSHq3צ@n{ŴU^8)\E|E&6eTYUXGvF7{ϻB`KXu+!3D xi*X;jZb''r!)Q4+N&̨d5@|*D89U,zi&0!E&=`1M6ѩ\fF\Aנ) F5_ojܧ`PʿK(}C;=~#a)U]qUX)QxD]sWU^v4k Z5'N=B0f:m\m7=,|%Y.r)V2m | 42k0@g>?4ۮKob)ʀ nԧLiEف@ 0#5`PajRz_ c :*Wg#~y, .-B9M 89pDyGu=*/H\8;Խr7weFiH`.7 N5)wn/NVųv]2ANR鏖1T8z8uAu'h[%\t&HED_Q9f6Ӊ}=n\դ\y}/ŗ֢9׶ jWݵakVy7T*ℊlS?h#5+N aER|G $=.u)'S m3b:EO܊QtVv+EZY g}X3_A(YB& FͫFk^K= _ܷCQiP]_T"XwY3N؞<\^'qdG7E[ =j$Lq֙`I p3ntsD)*nMQl{ݔOv6# QʽSeT-`"~Hfk"EW#l\ʱ)ew/*R\t79:%dwߨܰ2P/1BsnjVP7]e`kިCI:Շ;AGߠ( '^~ݯ*LO}MtgAAOrN' rP1EJ@X悿so-<%aHZfN[Uef}|pDֆ VM˯79; cJkDS S8K z47kC~Z:OL~xs ^K8*YFŒF^9WH5L}b dm)={A|۾:q5@zK3 &!Lq!^ 0e~vgECu ` %P CG[;8ϧYʃPmW"nvjطr$JN ,~UێZgc NkiVg]6vv2X@y5ȒvOh6 OJ΂~$m vO*W6!{'^4a֣ !NT5:YcVƁʝNzm#3rI_J_R-m ñco'.rOZ B'3Pjl0cZOC-PV0ՈGv:,`U 3kfvH?!nا@BI_]AЀ%(.5X`- ;cp$YIB@%0oqՑ/MB(WBSqdsӲcM|bķ$ wH O/k4e1qᒒL6b^Ο=m ;4O \ŝ'7ϗ:o>^hB s Yu?Nb5@ -h˖u#uK҄,2SJF6,pԴYF*/m W8}fJ=?~{& ye/`1Z外C;Ne@y=aiuU(,aD¹ r0,*q1!QZۄ;B(nI06Cj *J-***/VȀ7|K#DnƆZ޼\~̜&cՊ,(izNRcm^^yΞsd(P2_#+cJX7 J\oZ{#3dBP\x"bx [B`rsY FTĘ؂"7VX(r9+Mƒsz Fh!0åf}-'?=3gnڣ ~\)ߐ VL-efق2菡uX_sW1=P]I44^XkPK/FW[? /2=ױ*Q'XJG DƢ3|&n>T탴(Zm"TMMY %{'ED`mA⠨CU(~ 5R#tV2F &lT 8G9}cuobZ =OqڊW֤UE' ٨z*y1lmXYKZ;:OLJ =?`8zi}f쩝g_aij:(ȋP[mثG;to(fS5r*n+Aeߔ*&dσ RuY(C 3hdkڪT4ULCh&D  ԮH$ /..&f=3e%JS mnRl#| py/}mAiuJsfSڞԧ~s5/3oz6Y{Q9qѰ]@*$bV;)QLFk Yܰuɑ<ĪXS.IM{ur/L Vs0nOm$]ni/NPX;u甴r)=a5Hxwձ)cZT>Xlk]s_ 2ۙЪ>KY[66F^8B|Y20ҋ'bW,2,2j]1D O.@UqeEU ^NF.o\ $?ˁh8jxw-ѻI䝏ўp uj"E<[m#u[mک[ XEv`x]Ăz;tc5IT6@!naB u$GTĂ^ӯLp2bX_o*c4MXV-[8\'T}rITYR &)oB° A iI=W^ j 6_X:$gm% dX\b32 |, s4L͖gR@DYc1ǣ1|BVC9)]ٜis2o+˱Eȕn𞶔^Ѐ#-<7'Gl68߄n]x' !]HHWCl3O0 ιl 'KJ3"sv>ȤI|)簧d zȌQP/jtIc` ;hg,tϏ@:yysQ^B #XHcD I>ͳgLg}ѫB9]["?'Z{ :uh6t4ˤ5Oi4aHfg4쨄9]v3֠g/3GzG[VX%Yg[yٳNXE120^\TaV#ߠTV*=N+-THGhx{[u\*_CJV3Td5YH{LXF.QA"no͕{@얶9<Q{pZmt,C}/n̽r:gk`4y:?4Lg_wr hxH'*4K vK J֪#NOlp.d _Fhp@nR|;70n͈y0R%QK8z-#2rgfCY||3gp:}sOhnkNЛ!A)WQMrKm|,9yI WK>Ƹ8Hbc @EIxErLI%i{ < Ts%Q{45RՓT~cm1P-tGt2ҭ6yꥵ{ %$:1eKy_8 @["M;yqrM;+]EA q:UTXՈمS a|C +x&"QeN@Yg?x%t|xk(>/ٓ] a0 #\Kڏ5 xϣ^Vam(NU{g|S7Ayiu`TZcb>ĬDd}/]3]/,fiA} .$gG'8֙sw,ѳ5;! 8GN\>y@Yn8~t-Nl.^4)<,"N(:}*j `kD( s۾S "q(FI$S2?~ڃȰϹ>OgZ:x':euAA޾>ZcRW=p4 zHzS3̶.&]_2U%0 hPs6:"rM;W׺PKk3Փ:sPS*m+#MWI_S`^6F*i,1*b7Lp"M W6=ȓg{&0=k-w$uFۢ'o}BpE[:>iI>|t@v7- =+=Zbm~g o)MpyXbt375ksqlj/٥Y ܻܐ;ዾ9{(ݗe 0(QPwt]J.ma*0C^(KcΗ#5{AnT3v rsPͅBPؠ/ ^Es#*P‰Tʱtkܛ'Z|`t-ZtIpĦ;ȕ,h@yJgPʠ ƎkPGyR(]ea^w4>_4Q8}FQdxPZn"x0nv_!n7OS ”О!ze&f9cIg*-,3VZŧk 釢IWD~R# yyyx!Zd {hm[h7Rufl Im칡(JB$h}N?`ʬ=#vY̺ #\is&=;WzK-D"v֥v ?;g \k`u$-9yC|wЕx ?ryƤ.G 0: DQQZbU%j *Q ]6H엣Uf9Kvd؎+*B@&kpesd|D T:-'B/Vz>h14hx(g0M3GgJe3;`S"{`nZWbKFb3K8ת-n,Jס~?9N n_@lxaX8{S%`X@?Xn,Cpj{=٭[Z…-? 셔lcCUiK)d&ӈ6UrZ{q \,↫HS;88ȋ9Cg1a[ՖSvKOGZ~%i_[2AR;5k?uܒ%ȀDjήbw8Eoy:? 6笧{Bj:hhfxa`8ŜY$c &-!-JQ`TVxSUn͙2 :!C p/hud-֓*!]> g>z $Lw;²D+Q sn8uh]&hhoB_ 9C:γ<Uҡh\H;Qet]Er5on- fTlR AWyzrC : sTnbϱ==]{! X}#̨jlP;wx JZuBgK< WNgDEO\yS ;6/HdpNY3AHRuTP_4膻DGqQ 3maJ7I`A&@o40#cc>Fd(3(H#n,F|Q~#~ a@ T[uv7]w>_k2L~kvzij֎Gܐ(O@6 f|El ХU~uf+΁9:ҿM2ŞC*/ 1jɅz~@5zUT$XK* d! ֚bʿʱOd[?zOu܌ !G,V0ū//- H1N+O HDaLc1:!WB[>?Oԍ>,y.p_ę1R}C,؅Qix>kY$oZ$!<7"=WPbkjyMBvsR7N~0Ha0]q}tvR)N=߷[%<._Y+(U:]د%D. JW&^qwa{Ozz n1{j mr~^\R4)#s^좀Nm2H=;ï+U̾%Ӏe=+F1q;*q&c/:Dm:*ANw1{-V)n%^y@-jۖo\Y #G> &[8 @~h&w8m#r;߻?A f/5UI#4_BCLlK V: 3,=I'a՛]q}1c>{ŮGS r1*x0OS?#v];Tc"fT-TROaSbHBS}p )G Kt/@}s5As3s%$ "UX>^FNF<5%ߐ;Gusaa߉g8nF}˳J#9IH;~^{15I I øLB)>dW<%Ҧ[7x@ګͤ|} )zHSc\ Y{@Sv.E|OOy9Zfmҁdjsj6m:Qi7pr/cb1Q ?(=vIY|̋Ӯ}]|LYLxEfB5⏫?5-M{H C" Iosy{$&9*JD?[eJz-H >Bk3EῄxXŋTޔa%))#ZqIó&9K@b)}ch 1/NdATNcNifU 1QQ`}+I/on $=%E 櫶&sI^<2;L䙢㟥 (k Q_;}o`d4"dk0sxcJ 2U_kICRIؑgA;$ Lo4Vh@Uc#q~@tĀi+{F&09H{b>721| ђ(wϼ/}"{i^]Z)Ց:xcܣLa&=yM'RG$ΦbĶ5*~:ȘVDU{E8 k_qW?Ҩdsn^p5-ũN]2_V JItJ;>UK4K;ƼFLK]G'Plk 6hNC}˽ vĖxc͡>v)x X~JwIt#t}hBN& wt5e `n^`oP~ղʦrlp߮@ؾ/5=41\=r'+tQ39OMG7bC|tBfSe_G29Rv,ݑt_ 3E^$xhύnm}f'D~ԇ /{a_舲]㍁W?; <ZO@wCyG[ )P4|.L#u!Do}?P;qRC!\#Z)r̻z|[0_Rc/,IX 8cի1 睧+ݿ?Ob9xP(lZ=z(tK㿱FayrwI70LA``NJZD2J'8^}v@H']a9EXU8KwxeL]Ddg/Y&Zp9O|UO9CH[hUK/ṽ5:6׏5f"clDg@aAl"Ne3/WNYHǁJ9mAّ1D҉Ռ5rB3?x} 0Y-DƐ*cF@ n+DkV4K{!ޅH oG.i3*r S^Ι8Ӹc-5|N~ r %cQ: B݌jfP- Z*> e=:_4ӪIKc wk_-t?5r 1Stdh ;+MF&V&ޒؿw,kv;T(Tj]QxY$t )eގ0 r_H,](+7 F #6S:ᮮ?>=QbV#LH{Sիhp`~q)@̫`}Yu}vWH84`# ,]R0?!C© q#~myV  2[f-ʄcDo( =P${z^RM9K5^z&@m#V \p ^o:ǢP=(V$u^3ph nyiFVa{t|2C{Jy/H^_:[) [$9kK;z䁟  f6b]uvoR+7GmoױLQ+@-sJIo "σU/ !<c `=#W~lI47s~];뽧u]̳;T`I47$#*IQdVHԭo:2F@=]FݧvRtxU;=U@5̹x1Z', ͓qx> (br-/ۆ %w\%LʯY_<\!|wBeBX^/w8D3`j>V.?i 52O5RN*H+? j#5h/_5@-70'o)Yry >Pz } xSgY֯_ h1kMc|. RC9xVcR -? AQj1iQMT7ڈzmC p\N.OHAC<, 75BS ZoXe5l$S8q05d CDvW ikcŸo/ ]BOwf9kyWKыȹhPkTMw\m`>h4ABtU@4.1dJAЙ)$fZg .1hrnOn^hKIr>`lܻT>UΑYu5YV+۫zBBp2"{"}$cBdL 1w@ ǃ}26~nv\XKCD 7}[4.V=/7)'1Xdi;)%wvO Y)DRx %ur5>ď&JW@%Lqxr # M!cS=F _3'+2<*wQM<~p8xs+qq3HnVVpQb؞`R$qͫy{O!?k_z_uGӷA#"V(c* ʓYxancweEz$m, [WY; E~Cm'_W {{ wl}G}臄[e?=_9b3MS 5aD sZJe>=D)EqW<-GӘth Y+أW>V&p;5e ~D?~n\\W8@d[JVH8(UM۷1́cBS&_3-D1Yls=$taP!>Q"Ht4zm3sńE8[ <>mU (W ,OBUi\,Pq(MnDz* 5As,ZFVh{eQ*_&ү{'[eMM>+b䞥Xl<Ƒr!fݸttqķtaFM#;f6$ g9Xt*hafzl'#U$ʕˢ: П\ʡ 6W-zRD7DK"jCUd~rQ٥x.rYIQ@2 хf̐f ~y..yzQX\xEP 9^J z |{R:ݽ i'ݡ;뇹hlb/hf;0mgP&,]WzcB?Cj^"bCtZژй{c:dWТ毬H7xNFh/=I~u@kr^=4I[̐mOFAҢr7"! ._u1r*n ރxEzh-W i-A !ϧy:y^F.b^uD|>ƒDkD'p> 1`eoX߮ }m:K]5?5%Sa(E*:CYO?$-tz=9I0RRqX/ۢ(k 񡣣nKk-_B/dޮj-0vܽMa93q%~׆ jw@IUmUt~l.! rıu!+kq[†<'{ 8&lW8~W?$45\ڞL`V\t&ėKvf CE]r6a"Mzv#4{%,{]΋<~ƾ)Ҝƺ\ O8 omisW➲`Zv\ws둟K&dW'{uB2UmCg |)Omop?ndkϰ{l[#ht_hZN,sMI;͕`}q#h[PRE[|f`7cn{ C)PDl r)JK5*#D)^nV[T! #7u[~2۪B_BlpU/Uqij9>v2/U| Mt?ѧZ\ALK/m(ǃz +uYd{m |PKRC߱E(aW? &0BlXPw#ΙɦP {\>oӌV]KwmTxjVO859}$GXQ~ xJ !~ڂb ,% 嘔VHXuR"Ho^aLYҾ \X^.:6hk9?]r-@?;?:;;3Ye'vDRxO@dW9 R8/7)05 ؠ)K5ː=z"Y 70.>K^cԋnI{hT7蒂2k˪5ÍY+=T)ie[-m@R,岥k6)Q/< EP“*bq%̟HzmU2A4I}chM*oI,+§ޅl~>r3R@?CDeC)Ǹ;GT`_$M8k;W]aboPhQrV+!]d"i2{ϑA~'uɧ:n8V$NVeA:EĵQV"dMT9&HCCzXpTwL.0r1ejXУ 0ZAmC>ucbI<.y߀M%m`< 6 xOCyg~uZxsĤ $ZX$b?3Fiw{o ReaN7-fn7y@c3gP$7Dq9!.<"CEy|$Š ix: @1H&+SUBC4Q`V!D 6b#Yx>j3@sqqIPC0!b~MM#p{M8zCu+&Y'c33Hpol@nTnu"arX?8wm%NAaILdMNRAm٥V sjj~[b~ {ϔ]i:[r(/Zz.GjAJwE!C'2+7]tc;T]؃S:1Oϓ#=lsZ딅}XC'30AYqu xX2qN!6n-cʍr)0P'!ƐY{1/V:/T-Z--oiMTwĸDz%v4a'wbvlAp=G 5"=b+;|6g,H}£}h+,SY{q _䵤 Ɋ%OQ K~ XsJ7e :?AeTSĜbiFŶh_9ؕ,9 $Z露?+F} m0[tQ^T„")]FVY^ Y4Jgf_~@r@ l w]rwqԅo K #z;vt1} )#=]B\(vpW~|x MTJ* WZS'SԱEa*l,۷ Wj˒Ro`=4||Q|<#("K;hIM!Cm1|Z#(,"&M[P_6*Phk֤y\pCWw)%;z9 6}p*c0Z:.gi_7oQ\1TB[)pEt@Qvbpoe,gƌr GѱY@~'iû.L}aBmZXP/V7M̪8?{e*Jux+JA)*8 cfsĽ$^k_n%KEOWWsEbj:u=Js?dM^R[}KH4;T,8Z\/|ΞlT66UG}cv#`k?ZFȞ6V$\pjQ2p2Ov/y^i~~ru%Hz/Ȓ[6)6u>yD e!U%_-\ WurQ⁝F>x(<+LH[}:(feؔ#ٕ0^j6NIܕ SM <6JWl/Op=S$C#+ h[7qSǨn2G(!WqpF”%l<^*2%&a<ڦb2Өў;͐YBwV"oY\ l0?jv3 q, ;Y! ћsL, o! Pb!&mHWNœ/_0^rJ}̀_`$tCp M(tQ7 'c{|wΓVСW]DOi%LVw;&@Ö y{^GP,0|R O["(_ >J/SE[;˛Ažba4g.V`= n"yyX~;;V˩/uF`Ղ$ ~qŷvne34l\ɥ +]tn"9'UoVnzgJR-"oC,+ͧ¹-neӝ[ `8Z:/jxȚ!KIhgHɺߢ#wRhyͯ$hofRxډzCXW4Nbe #r)U0>=iYL<>E@@Sj( !i>ث~_!iQ@/5oiTc9\~ R4_OÎZ%,J=ϣVOҕeLNl6azabO*BrP PwcK۞?7 4ȏ^GJ¢ѯ~F ,Xs_uG$2$E_(PrCAҳkzkXJ\ [1) &U M/ 7Z S,zq{Σ ̾}F|fo6vxc3D!755 Fm3`娐Dg1 "bH##7PJkgFTeayqzJ/@ِPBSU}w/|*Y{;=*h7|zAڦW]YHzSI3hR.vpFmQSVBX` FDuWC}46iL@=O: GER t93m"_-FsKcP/6 ~S{Ex# vGf}Cq^Sj<(l]3TK.b<^k};ˋE8yWg4.24<;8D;xU'9nv*cC tJᮾunpȇ`ǻ67NEDܒ)BكY"jHNAw(]Բu<2?i46i@ZD 6L>r8>FmrPB I1cZ>/.go!,^<Ԕ!0ݳTq*VQtvu#E}lŮleMZ[ohSK*?ſC"ܓpJfOB.h^57gG˙\ Ĭ0 X@[=np{"@|єb3%mF܀6o1 Sd8e$ k+4#=> sg磪8a3h-W96~8 )c`$﬜]dO`0x-{{^8auRӍ(u&ꩌBO/{P,"g+N!>RȮcEʷiI2Uf+cqp^H>\Mkv~V~|Bʨ w%/$Sm?Lף7Eɀ0v*I{܍l{n.SKƙ?7ab\2VKb~̹' -;I߲ !H>BPfVZY)USZ6>{o“~2 z%.qb#\ (Aӳ-!ܔkDM,~oX#01Vjh(bç>_!}A[(t>42aR5`a_h!b4ѭ@e%wDJ$tZU/-!jnQx~t"=5ڻ>ʝPX"l2*!AH#}>H%ՓZ- IeE9v>ݑ1Zr:Y G+@v r*:rԛJ Bs c@߫)q(܂_&' Q ~J8bBLV9 *bBLb<2nsb@_S<63(viqu `G .B_987hTke a&4)b7`^tQTzz؅_xpm /~ a=B) @cBp'sj d= T4:Z~1A&&+;惡hIZF̟*wC%08Q`VQv|nV`Bo^!?@Qk,cWڈkYEM/^T,rڧ1\W lOTiuHY4|┍}{;\ww@\-RƛX"8(hw4tĴY3h~tlq8X G8%jo|>t16xF0 m9yܨ=a9Q} A.ـ${ZQBg9]QyIw } x¢6 Ȩ^:9$48lx%AQIt|TZW ӂv= ofJ"8LJ&[ ߻ %Q`#ک>mhBWۦގx,[5m s}>>tbٸMU<_|, /Z~ҳJ<&o`A%tB 1D(@2z?B0bAFUFs4;gя0g%6ʨ[:A`b.ZCFS1277N"x+۪>~E dN6jxZPx +gzWI,/H KH~7RGd01wN X+B,VEW؅bNXGe Gc wb[+b- A~eL-u L6\h3t 2C煰(T!;x:r,i,g#%c6̐\>k)cj杘v+M~3ހZF%…u;Vm#"\l8ѕ6: \Ĥ{jD`\uCDQt0RAu=/?s+E> ع}g'SW{m(Iv}N$hQo]qS%eJB鼇Fk7+V 4Z3ç*Wj/?j?aB".HixGv}5D4F|ي[Y'd# UNN]H啒zsl;zmSϭ9}XZlS\Bt̏1@ Jwz0eWj0leDGtORO"Ɍnp|O,bW⮉/Z{Y]C~G8ȮVs7b}v!6j]!Х!8̴FphX'yNqZRODbH,g~5z/ `B3E*A_0un59 /덣=<*,i'lK-զ e+&ָz|W:UQ mB߱64ZW52^[uѺ{^+侞JH,ԭAҌ9(l~#elCDu6L$A4APMkTHrg04JvQ3R82=v3ڭ:9DqiMKk/D0'PEh%f<ޚK頲Yu"[H2$վ5k8Vd_m v!DJ> 1q,0DzeCIM388\6YЬcdԜ|+XR cZ(K8WI0Ϝ ZDZhAtbq_@Y]DV@t&\jvbTB3K H$-1N$<R.LES,fޗ+/q9YGh,:FJύ-Т96-!u\v(jI֖[\1HvVaB ѺC $(>) ٙG/zϚdN @lBOXnPOjνJq8<90\3{z[ 1h.JN#3\BT*@tZ[$O۹TRuثNHt0莴z{؇xصwBOYYRTuqh%Z 6u'.{Vq!Ryo`` ɱ'F6.'JXק_k ծDtY';p46xƹĕDy8`xUw=0{\׬g^6eތ KݧM:^pm$s<.ժ&qx/Ul @Aa0P q݀6W[\`/#wo?@pz\}dcJŃp DͿ. @ Z{-Н@ӝa͝s\ϛ6pF$"|F'%~kPgM.Z1?[)坮ipruU;CD Am4,N3F>=܋g]]_"Tb;Ȧ k?,νfcg~\pPo(ikO;_pldU.^ U{M!KV ih)$Nx5DԞ=\&oط']em]Hڧ.^"s@G8XmwoAc+6vѪM #85㨕qSb@ݑcGd]U(Dvt M,Q(dazGR_mQ =oT}h_by"KY*ETݥ F' = `flC0 n[ ,dx0.>&+.-kqMp/ԖD ݇GCk|Go4 6!"l4*.ѿRduzb~iފ6է}xCCG(&&5] KYžoӹA3lIXhF":˅?T[$M֑%YmIyDjPo,8 1g"bkA|/sX)Mm!'nfiC󬓥 ՙ3Czi13maPlIyF 2.]ɲ!ES?_*+M gex*w<F~:YʂEЧ VO YCeF8`Pآ|imS3UW$4G82k(vҘXX40YR_k6Q5&Ɲ>6pޝZ4XD^GxgMڙwU+mz3si؈ND#u n|:Y+oclf?+I|$0`'nA^Y !+mZ 8=жl$#6ىl)~8=yNr2BT#4[^pKz|9Pw9J` =(ESIי?,/3F8/inԯݥYJO{$Yq4^`K&`p"O 8;+I}8ܒqy28 i~wGb W=ۆtSksBUٔO8(fQvIFb\ YOYGENRq]&fKdnVh WE=;}#DugㅓoGc[[Fh˚2ozNYK*=$"F+tGݙkRm8ϕ;c|~nB0g%f^q) #G$sDɴ/|. awnD`o'O1j0 ).7s>It8w kvohs.L.7zt`HfGXT^KIORk>8D8I6swK0* E TL LУ[7)=<_PB!i_ V{oHk{<*38ļПFU#lw]z(16R4!:c4/ä6Xm8Q+0CdUG֍>cL[[V]Bn #6 < XXY$t9QT Mw#rr%H~QU_L"d1;[de>dKˀN/kR̎ ; (RUbcysg0f.V|YH*`%Nݱ^s?>^IKB (uNpQ+h=Gz NH.<(Y~7@1oiwYvWVj5B( ^at ߬zg7@ ? C1s1Vńo;IUM϶e-DVpO+&ѤM'Fs W8j.g`$1=zJ),MNE1NL5bM7VRu|%ba_ aiX ziй|RµS$X B!#z\E3Sg5-k7V [9mӬ|SQxU&;t]=nDn7͙&>OHʍ):4]p95pjv15w`W9oEǴFi#~.eՋtu7U7*7 Ȟ,(ƞ!8d#Ϻ%Y)!-we*>eWVAҮ<,J.,gcPy9.ܦC?>Xp1l,n<90Cܻmo!#l 6aYr?"<7p=h>Q`/-. 4\^G"_WhC1l-y1'_,i#]Z\=5(G6C y÷'诜2ӻo LiQ<۾k-zhG ahIǼ[_&lGӑMq*o ~V֚(;h$`/[MTb._HP1 }S.J?R׮4:Y^aC.mTXuޘO>6Q8ǐw +TH{ St-O,:jbHD,(^+vaj5/hIG9<":MP?q~-qi'[f(eYYkL{FUkz0ڔ V8= Wk\gPXloZauA!ڤŨ8 ](]O/V6p7JVI.n!2wevK&*?h^TdpU #^X]+xɦ8eƭDHUzH#Qv(=E smF'}TV.p(2;-dYW^M֒em6tv.\ Bhb{,iiv+?;c *!ܤ3ܣ V: XHoBfvzpdocd7Y:`AKYs2@- ؉Dg3e j%О^PođYG*|g}0, sܥJ,bOvKQgKQ`D1E~MdJ^2AZC8zT#uE~잿އF||>ɧ jpNxu1?eݏ,]ye1HAPN_N<= Eao&Lk$Clh }J{ٹ'D]}ezg/jRQvD42[Y!蝙dWImqQd^O c36bűZtNߤ߶5 gɑ\mPT7zA`JQzrS!g/:ttHխž3|HƋIdEYO",-]cGG勥'ܙ.cF26աPCLKk_=Ъ)+xL`G/bga)ṑ̙ ?'}ك)#3\1るP-#[?Wi*uqK B_uꢡB5{ҒC//6NGk~yeczmB :~tȻ*r.JL(;S &mTbAD}9mEfU顆lեq{E-U5I e~d4úz(ޔ$_13;)LAKNڭﲽ|@*ɺgvG(1`FMX0_`(DPrs` _8ʿb˯GXv: xU9)ZrHe_3<`qk\` >*CDa IEd%`) oQEkZ>V|y $7dJJ´;vKQѣS3< d.TpuͰb1ݢjnr*=%jz47l:gJ4 CD>hKN͞C_g@M{@!Rﲻ0Mk{2{fwT1̀9pA? q6jl))M|tQS_ɵ}Urb뭰nGސDv \ `@%5[votJVx+XNx%; [z)ۅ/kQ@@'i<ѓC+yfo!#a }Շ09{),?B1_{%BIXH pl~C뻩蘱+̈́yc.E R8!֓^OG#tj6  n+%Tv26ڲ(>^S8Z7mXUU:cYIG,$y=XMaZ^ 5ۊM+A0=#TV5q&$b &1$as+P8ţ[2 eeXq4#>6GFo˱~ׁP[кl`OQģS8#h*/Of\IJNky)RwaRe .!tb-Ӄe\uoɡűu>.ٯ+J@MFDh9vd_MǯدҳU{Թ hی5b0+N O65chǪJ] vJ8^;dMz x2HPL5T:S$p`lT|!p󣺠]sF3S{cĊ|_KhBd"='lzHOCUsl(bF啺YYʕ.Mk䞳['rO!J/姟 SS"1.kM&%ӘnFU|sœ^,4u$P"s@"'@FO6SPv ~8.U&;[wuVDj&$qo~Өy6ג+1jϑ૬[TYs*Hȼ*ogdzo=N&En/@&8rے|ޝɲ[y:E`6WXe"}Chj?8aM&NL`jɶT."-Kvv3tU>j0DTE] je&' vfXʕ#ASV28o!/Yo@qYz@哠#s'cM@n[o3=7.0Gi9n,CsQMQ)"N$؞U1s"!22 EF+ jtRZ*GïXmT-39oy>3ixOL8$\ ̷L-[|ᄒvߏhUgVN:0|/m&5gz#,:9ȗp#'kXLlA<$H3{*6T#HwE%b+;5M"|ᗿEؿR#^u\`q*]8 E2mG[^-V9#dQf hV::O X] 239Ylx]5l'5-8KnoBI3Ɇ~] B7.Yr^pZ-NI%VYf_M,-Qg0{m(zY Grd [ yM.g, ]p#v@?5(&_XAʊq-dt 7{7GSb](L74rp_JWawᘰLo$4-F=W}II:C C.^c=,:G"0\. .D$6Yi޲՘>BGIO K:&ehd\=9Cj+hM8 7}r .mĉJ*O6*񜆰9sy,O7K K6Cy a 2Tbq1ZelMJCݵ#`F A8>&g=hiUe#{ABb勀׃_ӢA`x  ^er}f^[w}\rC 34w;=F(1!lf_rL&>dP/s#g|Sv泳%k njPGu\N?#v=aSQ]lKR3>VE { 73;_ؐU_<*::FC3T47J^WUyL=[A '(߲laN:ϾC|M\He)=8^3Ĺbc/`J)JLhsv S(rC_51` s7m@ov^Bq*ZQuBd~S(MiW= 6&P$NW$X՜XOUo5yɔFzOT,ۜ}6vl Q)*2a֥ąuKBzj%(!+ BlKpMU-B䢣EO6vQV_g~Lvv4~ -]](|Cxo09: |I忑|Jz%J2IIz93 Wd 0Ϭg<5^lQ s~oY ~`q`qI4tu¸ZcquG](%yס̍!h=ϵ̉VpY濪gmB7bfuTmQ~ )/_V+PTcQ.2IK$KQ*b䗦cH)W?ybܑ%I=uRF" c:ن΢tӅ [F%b9TluV\>ѵ1, 6b"Z< q;ZOC;6 æ{\8&8Bth-^yEQKC-wvg/<'R$FpJ ]Ϻ*y *zFg!j-'|[w"Ax~7z?PoYF <8_/WojÿOև z&_ot_{߷֛ ~ݽRm{﷬ۏ[}޺?U}dooWI3=$"O hBőn !Ư?BeRC#r^CsAF'x7}]xD[=b'j5{': ߈JDXk +8tK(ʮgF,-ֆibE`qUzCd9-myU-= Z/dnhJB; 0n&fVvvn,m-zʼn)|.o[/#jthՃe[i Omȣ fF UB>^ynA i5ЩG[c.nnj%Lw@}o`Y2..X%vwP:E $۵)gUxP B667.NM@2)4%ԕmi5$EWNc{aICNPL [b1Z?(2s{x㺳BK?g:.ev-WzIZe"t hϾ&a6h5CD{TD FQ@5$*ѤfRyOg}.#4kW}}BD%)vȄX Cn/4 -wQ1%sF=}g,(AZzRwcDG9~,\JI/DP$ײҗٞ8R0=Ƌ!-|ΥG'WC plQ"WvKX硱к].t1 Wu ]Nn\CP $>Z$9ACQѯCY<k: <)Yyw@'B;z:;JSR(0*FICe/0v2Z/inkY}ptnk䍞'6f 8B!)`= Rhr~Q~}} n~Xiz.hucѱݿJ\(-&6,Џ1U.8:@*Viˇ "0>+7wۙG.ZJOUy47EAy+gYA꛻-xdAjo+Ê0(f8~+rT`eγz ;X 1ҕ>F' qh4~:-H::g`+ n1^ḾYSPq)$3#<'Ass:앸ς`; @ yviu57O۩SGެ`֥n~rk4vIӜZR1nS3=%Ņ[]f p#S5IzlL̓hA (q~vqXRn[ooyǁJd3vZ?TcZ聶6ݶXoGjJ5֩7DCa=5%U/O{<hNk[MK 磕!KmB !xհ@!d3{ #W !Haҧ_=MZoGJOy3-\羂wJr:R8D;/u_ ۍlX$ݤ iv_E-O^%*K3w֓>YxǓ`bw' v;5\Ӌ8Jf #A H"R7~}E:c MF'U4T>1b!09~HA.fc` l@͝b1{ ļ%X('+1wxMR?fՉH_M$pŹ^Hĸ%u{{6[aӓ`n?]A>υ^ZѮ\`C ?t:)ܟ 3Tz`,d1%,Ï5E)%Ib?k_w4<*[S ^2k\rGt##`r_8#4"!~ݔhޞ'j)?eHsV^C7fx#UdT@Ya^èISYR)o(Z`vr{ J6 d'iQI4VfXkeSzxC-)luvo'c>9)sۢr0IwBX4l%.ɾ'{:qry6bKЩn>$;KRPyFS<2-5y8-n<( v"X 'H:Irb : ޟ,~Y!MoN L]2Eg3VRFrc'D:̊3AjSnb1E]$ZD9S)1K@vh!s'^Zn ǫ3<ǸsL8\r0#? ;7n]cfRd-- ]S~b>EZ'k/YzH> 1vg2+fB\龖^kuDPޥ-ZzA [F;yR9&r׶tsA>7r3a= 4:%Ea}_ONq&&*$EX;!_fH?ڗި‘:U(N^C4Eq{,hmpbcUr]Zמ]/7 $ АE~qx̪،k HnP*ҸyDis+oΒzH0ηfaKy:CHٵS GOK7/eji(i7gy +ݐfաN548f-,+-nv7o5ȰeLK_wM$i!m{"J}&^t č^) e$ j*Tt_WUK(VTQ.b}kehSMi#eWϴ"4}VCX{v$pQmc9L B/5U\\â-KO,# AٌP-Gr K1™ W,0_!g꘾kdbEG!h^HoP9- ^߯KTsyj !VkT:6Bz!Z1U; GfM;0[<6vD]'~.{ʓ(V|5[2~#sE9PP' |Ece,Q:5 SH3=NU^Kw2[`QA_~'tD DB} 2l?1~MP( Kf:.[Yl\ۻOMuci$ˤp 彪5I77dok(ȚԊ1Ys1^4$c,%״"CH^hw."-@?YKBF_u! '=4 p$k0ex4s{|GA[0\G\U!(0PmZ!w9-e H朌R{ ͢jP 3`#gt|#yRohDܞOnEۛށܢyߟ8g1ԢiQn<8,BNm2#g1SFT+ɮb0=oc.R84PJIxinhIx40Fw$M0R6 l}yb"Q+? _ZܙUD煢W%[7^1^ozoIU$1ŨBB:#бbU~]QEAmZ,{1 ~\1[U-@i˲v? kcPj?CYmd[+'9yR~B[hI?heM< F-X.4R| no){zs"e|I1ݤ(Nק0iMI`~e?ejcp&Q@_'w]`'f )ho> ~I@f.KlF|/d,cFVI]2-?]vg,e-{rL^Hl`%ՃOjx/zLbMV7BD zc#!$U%aۑyp{I%P"gpp[|*>\j&^ƿ[ؕ0%pIG>zNH=O㱉{G(ZiʕPCYAL\H5Ś~4}|}1d[*qEoC5fr%ȍ' /䲪JIC0N*} +ympHG{VҡNZ8s[30lSL*/Ѧw4Rj Glk0Q]-~YR ^$Q 8CF\BqG7k 襤geEt^=`n|fmʛ_|MO[/_ҏ-Dp /`Yۣ4g0Obn I307an Sř=9< HmO$.w|>UWӟ Cv1s=#b+BxuU}fHȫ~YTkM,݅VnTg=sΙWKQ|+eeY#[-C0^p_ 5Ҋ!TzGG.i>+;"EsG-%O"cը9:_(kV6ě,T4'D+3Y/F}BDk; eenDHXѳTmݞOTPk@ӧHIʌEO{_'t_~>k5雝b+aB 9>@$XeeOxs yĶ.aƊI().76l04j @4+ߗ@<2\sϰcLdTED)* eS!Mڵ)IVkNų/x<,-y;sEIoZ7~)9%!Y2mS)-|1.*ٹo5 GbA^D1pi;wĕ@GtCh}A֦˟[Ӷ؞OSg{%m" ggyKbܰ [ f#ߚjsH{mxri7OI.q!fY8$G0$[^*C 5 d ի8xVzO.}0ְaF7C~1Xbz+m+'ԣV62]QuTaҽUpM:|_„^91C0VK4=W T=\ /JBA: µʘ: RGz\Du|yhq08p<`H=c+΢a^U]e)3$tJg}3 v[:/O?4rz_]Iw LK{pLHP>`_㤻޻3PG0fbN|| M}s'}Å~AT]-ι{MkG=xOqDnJpQ~LhdWb,0"BntcUM~oTN^ Xl 5#v29Ϩ4e#1\N:z) (s0 ~~Dﶛ%8gڴ|ݧ}nZo L)#%<:SJ#H!ᤕ>tMW9ǫ7Ďi mHeG>:$an1ft8IZO$DT$FԁZջVT^ ئ"ƭ&>2~'jYfL Ƅ.y8 2<6\m֥%waQZS:Q>9v\D`NW}I{nFh!s Cɔ)|t&Q]>AUpA\E,D@i2(p,+RӕjҔdNB:Gojn<$^c+}+{w2h[~Ri~s8/!'DIxxtyVdlEM!lh{4ߙmA Ss)m+hZ>Tkױ| Y(ޜŃ kdS3Q=t7qlQ*w|l++[z֠'"ihwh lF8F#itsp{ >N|?3HMٖ @jc%Ɗ;8ĭ>ȵ^kr~wm bn "_ 2,> iuk*Wc(DkY58Ӟ  k_K]\p嵕Q#q9fSn{Z(&q PR&-;qйlΛ6^K GGZ|WDrϰ* ^-Շi#aޟÛNYxxYwp?A{)JYLEwV_Bf 㹏uA Ojqa٩iKCqv y^=x&)˅q#sZqj2ih8:>ᆘ뎈`6( 2Lr|-"E9n_g7ϕMPS vI;f[}b^k dq?#RgY|/ xK ,2{"0q~.d g+\Z$AN0NudܡYs?]!:Y2'ߨBUvx)BC^L ]~A*-꽈Ja:5T:/HG,k-jVUs7GVI+oJ)E*ĬV5W#RͻIހZ7|R{ޤ ˁt7/_$+%p3Y)*]Ig-܄ۮ{yZ{B\wEk QdDV|.|;z\S9dZd8Lk?ҹ$k:NT$"q?U#+Jɟ&O ^VT߳=WZzY^}.)<,LϬ*¨w.!?1Q kI+k  ݵD275aHs{J?+:} )jM/ Tp_؏}BO|z+,Ӎ {ܜ wf',z/gA>L$7rdELu:+'ayV#U:C_dDG\n}kPlc{xFEPp[@iΒI6blRf,uۂPXRpM2dTW=B4s܉:tUcnXdqG%98 )}ds dEΟ5V1k;tQ'fҩʯ^V\yXh5'=1)$&_(ͩGՎFPy+nΩ ͷV&)PjQ)3|<~u$wzuB=a}3編`S~JBm=άB`CǐSWzVe@3.(d-wIS_1bN'ĝ[O>3*CKQ\#Rݸ=Q{奄;Vd\vXAeom Μd;~25^L [.DD@Gx}w'8lѤxi(Ժ#=hsui[*?:/ad5MuI@lh`&1W⇏T~Dnh9"JJz'Iw)ԓ۵`p#J=X+o 0U :]wfCzW) $%}We,QԫP3nħ?fU/#T;cMKrre ㄾ$PŨZZf$8$3zzX^S.;?ɀ>߈2A`t~+Srb*rG!w%J' A+vjU˘fN858!S!?}4Ѩ.^Oir)@Х]nWN"geˇI,F/uk.Ef1T1e`.vww׬$zۅL 4ȱ!د '_֏ze"P" d4n/%.vscb( y:*0mC I0r#2N) Iѣ ^&*t`@@m_HU3iB`btC(f3hҗ~V|fX`2r~JS?s`Od;5!N6 rR5y9י͜Aɤck;qطG˶km@\c w&f y/ h!Zh8(~Pt(ޖK ΢PxA1R@ʼnHsmlV3eRU)[3C8Bt\dsJ8ޞ=y%-iP+5\[^ $b<r2Sh>X#8B]﬿ͷI,:#TB.̮'H*$WmQ|ǔS Kޅ_Zt2DDa Xl)THR,/]6H̔5o?CGw ;'5u.bgE5Z-wt1kWwLbSM.1D"wRl{*kolTX0K6O"!QևؤMSqRE|qV4ENhjֲpafHt{XrkP wS1jFu9 !(Ro~c+BC^kKY h=@]uMnLhSז+rkYnz #N > pë?`ͶK2>I!n8F,ַyΞT-2WFœ5>4&I6tb @l;XH Kt`K`88rr<:~CTPɬxɭHWM;]ycH 35o}w˂2(d\Lx˄aՑ9&b̏:Q{&'vFS,Tr1sR9 Ʌc EN`WB۞ՁN=vDo ۼZY!/ޯ݇Ht&C0rArE!K\}FFl]ۇe$kṷo7inVVfilfR9KTr s|'j *D:I&G-1d%/ po {+΍mog/D@88ȳ`St5DG\򈽣LP^F+̆+Blb\9N @ק2}-"r=h`UrJ4i&6X]rZ8]E \4R+ɦ<߆7Ix}x7J,8ōhlVҿ7"_28;**6=BAiLJ Sߢ[S<֟f \VLkXWx69]tcqoL ~Bx1lyUlhN ͦ}ٮsrp-p>zENxFcX"̍xPx$e]W` ˣ #+} n-MRxs֣T?U5Tq!&y{Dl =X= K8pFlEiKX}:'4tѸai@C$,,T9"7[M5OCMhV3n`{0pO xgy Y{ʜƊŶ.ĺ7Q-:΃5,wmLz$)WIѣ(NzcGbM6}5(ZdA@8;h_ CW0:S3Hځ8мʆ&9žA@ zUSO]_m!l[Leߋ/gYMs~"f0bj"G P&·͘aKhbWf-0KD¡/С*ǍM {ڠ--V}b.TբLŌAd L7@D {rɟ7\ll&m%!*%V*[Q'84a'a&;ڷMg^JM+AEoxUjB=M@7m!<@G"ir)Vxl;8}җG}̇*:PPE0B!Qp G+r ¿,[{oDHuEy}1־/IFJxmta[6 ~ڗv%m6qP(w%YFQávXƇEY>4UNAtX$Z49 ?3Y`ѭv *_>8)F #5\>F5H_(y~O@L7)o\ S,r|rr=ኘQҳ*t値%ۢ䍄,~jz92q0IlC W[7@A zZ)SA1܀Kh-cŤd`ڠzJc gvEKtYQ?ѺM3e`vn \YőϱuJ299HљnF7GNփܰ.Aܜ z-8:A%3HL"@ޞ] R83j-̥!Fo4t5[8r,XVmOqP(Hm"iH#5DHJDJF]q>#G&yfeY i]E-l^Љ#vUj 37ZP\N\NV\!\#9JI4 -q'izx5A]zPT)XK]l*nT▫"F`%t}eGol[ax'_y(pHJ[I0[peI 3b-:S?Ko\>WB@_Z$`7@߂FKxB ?1yH+ )ò/߾}C*l70_c1Oky5۾S[ ڜ6WbSg쮏zZ!PRD%Fe{}]E1?-`I!K*Jpy D] Kag};+(&@9.|yFXbGe)@-$|LӾ4d{]Y㵄ם1jpqT;.Mr)=B[Bn %b oX+~j=9<:9:[,+w j}=iZOslkcQώ)-AifuݘڋQ֍s ng|Lc~fmACaTTj/y5фkNL!Y&} >v^(Pdyf>;ܾHLB[ ҇N@L!6UrB¶g6:bcJ)O蟬2.ƹ*?!LmJZ61e= )I./rrv}sR_S6UoRJ;%j""WB&|gR79F*΅HZC TkQc>X3EL'r b$hh,k b$EIÕH)#"=MeV5Բ  x)$@W-d D_a Jr c CiT=+x]\Q#ض8a-mCYsk gQAJv^hv3]VN=Y$"ǝe[ 1n{iR}@gE “l'E[.3Xag/dWmUD ad B+x[ba/'ԧeC[ގi/"s3sG`p# Rl 7_hHTR]| rez]n6/Gtd>}*Q0U^%ӶUwJv!bшGy 2}K<'/'դ4D}RO-.FЍG(XmI6j-:~Ȍ+妚긽D1R6:K&7B;d ő7\! ud mpTdNC=h'ReMa 5yNj>tقv<o?ϭ(caE8JC`z~VW~{sG d6{E⢚uy&\_=iofJpqYͰ2I9xǶL5NLQٞ%&! }s; K# H?E8)vD%}*"PjсL⭦'ӥqT.إT)pnd]#lIԗQ՝_JVOhedBpPMr?"{UXTg!얍KR '!_Qƒթ:/0y6;m4%>}C0-!3$?ym%(ZBu3Bi0&CJ/ZAA9ce;i%50}9 "lAU3r@JIR V]P ԳCӹH伯rz,Ql O`*G:~tC:\pF1-8ij8;W#I'n$4q 9Ɨ0Wj-?8sK]ď&ip@V ׋CTR-Z>i QMl֌)ٕ &hBh=,!oK-9A&\^"!m3xvm%‘|uK~_X@u&xQ\%4)ƮH|z"w21~({6=xP8G[ud]yţ9cOUd *p#<0JPn)C*&˚@іu8,v %&SP뛆=bHRQ¨CFJp15U~y~c vUFEGaپ2V)ڕkVQ9 s73_w2Nn6us<k}1 Wo?f뮢d-a~u=MWXxyRaK8Zww{a^<@w90T;#>C hF֥ĥrTGZl*r ^xLgk6JE'@/+[ў{dC~ڱ`.~E(: h-_BrT^E ދHg^ٻ屈 t2ԖxmP=#ܺr'= ,U3#s_[B2iJ3K޳KDYW(OĖvQ\+ k7m4JE"lku-C.OTQطCi |\xk4 k Ea3y7@# H%o}  TEa׻H0TZD*YbX7@Vpp_T@P-aڀ:@յAVwqŊӤZ`$$-GH"I99gH !1֞E}iJN.} DM85}KiW%TW`oHRi5rL0dUwj).feQ %@ ugf4펀"3+>==i[$;v47N0BO%y%\'è?fJ'B5_bUI$ SuQx8\-^cAB"O0HhkHqZf}rRYD/$/1'$u?O)Mj 4NђdK¬? Y2.υGv#i7i.[ky2ޡ]/Wʌ:kk^ul3:W\W=dU ;.e}8>pB (nc2g*NI^s(7Yhr>Y(.N.Pڒw A-4L+] Y5RP'J-8֖g 'qz@Npqܸ:dSnz+dT8OE;;T;B40 TmSSq.كPmy1 ឡ/dd$-|:I}Dm\hi-Ru]߿{! 8pվ>F-ί9@,O DɠPźx}1-TB?a$9jWaň }\%;!E qC!#"HmQ* bʌ&;cx>ɜڕemƾg>d +D6Hz 殬(.PH=,ZFX:5yAf45#uŲ7FšK 71¼{+%GOAIR0JӃ_ZL#i&V@^"J.DZ?XKH&R΃'Ǖ$[V+4oNjY^_o8 EdJ`']X?l~Qov70[8SSv6rm[sL5|-tblцS=CEyʸPi mnڙ/ sa|g'kbXWR}@}}gd }L3Zi!ˇf{%5?*BYte7j`ﻛǸyMd5O]x&H` FL1 5hyhe߷}=c$2$ 6a -ȧĀ(xo|sN$d8(Қ]ŖV{U]v"2dQwׯڂ^Ui{~סbbygDw$V,1G+Z΄YpuUni ,xr7c1kYxjtvdG8߰X'9A?ӴN8c[&5Ք̛7z r Ԏf$gTi9[,J/JdCM 9h%ɕ<1~U`idd._'4je>\Cn wR$6zN=4 VxA.z>Cpfc4XjLNC D8;H@>zoQvH@)*FNjѦLH}w ̣̊Xzj&uLy%B(/?Mi>fRmy/eԖe"x$jbS|&Lu Y*c gTx0->R L`m;;iǴ_fF(뱵cۗ" %yA>j m1GGMqn4ŹgQ(#5FK.E2 V7 1͋8?HѣH_ɣϟUcfPab־w`43:>u +)T Z -,%n.f_+{ʳ~ M)]306%ĊaH뀐s43Tfpj K4ii Dgj &zOg~)G. ٭C0c25y0쫞snm0(sl'Vadl)rTʍ]v&@/j;_ZTZ+.|DGjUbs8}$=ϥJ+pS'I_2e9ϵ΍Ks=ZDka/_ sʰg#>[!첃 EdzQTjJyDa:U\SШ?8SpWn5/V(bJEB`#*r#h}9g'lXG06AAO\s׸d1>+m}3enֺaa\ L@>%0 M4c߃޴*?Ld(q%7o}Mˋ[XlMV=D/9Az0؈c8C&B]l}Zly Z+8,CὣB?5?+VWFq{Cy?4#$SJ(isGK QJjG=ܽMa93q%~׆ jw@IUmUV;9nWN U{3D,rZ/^1S壂m p_Pw䳀|!C7 h3X=|RKAWN+Llà h}n& 6ۀ@ KSzϲnsL1ց9'#.}Rmd<$ |7ڗ׼w,{Z{ ? _sGͰmAH(< LԷTdXKa6] U*K;9+UpLmK[b_S>}<'˔ycЩ&T.+-9NxJdN Qw<;ñw^9Q@(aG!\78`2Wftw{cS#Zu_K+2f{EUo 2qՇr<"3 [ >bʄS׉dSfI1HݷI_gdb,#kjr$c&e=q7]E_*7ĝu³a2\>i‰Ztt57 )G"nj|4QF[qB4pm̌qڏٌ HS_=ϹjDixF$N⎍VAWD%%L}b̩9zn0r>_ː=)FQm-RDl."x %|ؖqjeF0C-E`$8&3[Փ5搋 ;{1\h˳olQK1P$# {P\`N/GKPF[nk3"@4Q|+w:44lN)u1<.k-x2c~kX xvN/搝79맋]"i*^YAHIc'#L>:aݲ $$qP1!o,CmV{9o}{u"%pU\x+U1>h{G~>aj!B10*2|WBkB*]'8"jIdNZQfϸ = V N:V~:^7xxsA7)ۿ q^=27Nk'ЈPx;oPmq)U ~@\SI+8hA/]ձ(8` & qB#8B pyǮ-0cqts.5m X+”ӓfF$ظv+\ٸ M鉜4J,v+gmtoB-q'ǩ5l!;\N!n|۫hͥz܈eßJ//)ֈ'0aM`oOTjv[ /k{QMns4B *?ihnQa;vB=C(9[uNNJP lGW>$D򅙹: <(Tfw'[[y|˷ vHF1Kɸы*LE!Ŏ!*@P\Dd)~DDCk+lN_ѷ ĬR9<.N:W9d(*ÚvOm_lC-G3:ĺ)PbdZ0%{ŠrqP4uhT)czY{rܺBnS‾!Zb {3zn2kϿ%NCFɐCY(T[ X1ԥ2cI!7]&<.T:vUs͛>8M9Aӓzlߘz-pT)Tg2rUp\JJAj2Ιi WJ* \CLrkb1&N<0FgR`GT`*fU &-k7AJHj(qpZV¢G&(t@Yn|H5y|?m{Vs ֟GvS^mM E\RqpCV0)Ƒnp]wido =ܓ#/رYQu9ʒXa?.yK>ͱRt;Ν `G̍\lc8_qޖ<#ȯcJ(UVm8r{8.6%fI{TT(RP#Ŭ+t YzG5/bݎqg-hsv nSf-IZa4{} ,d$㤽 B$GV.OMv,yyW*uT]2G? !탨8l?>xa$+eK~H9H V| ɗA)3{sCb-V\g|yqf:;]Z߫$Krs:m$H U9TUd-u N6J:тﶈ?=G:Wv1`AT5xVlH}گL(I/.UyGg Omj]Aǫ>7{fYn T>M[p=LG ȵm:Wx,7> sKdF}>O [vڔgfaQnBXQ8-e̝C ShAYZ*{ݞaDO9= aG'j.rZnоG=#ܭAfNzjiԹQ4cO ѭ;uATbό0j{/1X Vfd1׻U\ NF"f9*NvhCLqp7?ڮ@|mO^a!{Ɇu#* ɪj*Ҁb\ *^O*ysdA2OA\B61cE<:v5o\7Q]}RbٚXF)M舗9ZkJ} p6cկImkp-b Mp ȅ B9$~!y^1Li*#}}ufpNFpe4kPL-o*PnÀNԭMnKs77TkDsB^q64.S8 xfD#BO)Jo1& Լ)ōPu2#v>!"9÷!3>hiN=N(׆)_aFTGOVk ׌D ގ}F\-}Pt)P%l3Jr;"?PV>I^pzzWa)_I+ǩ "'~BAe8'nOd{74f Midn)nI׈ԛ'9)ojk}>H[ !|v4t1/-{aɴ|z- v.;'Zߣ.F+?s_-Vź]B<L1!^Odbй`S ~LIC K,a6,Qm84jvCG A 4) v#$ښ<ї aN-+:nEWu?9ڭ I5;{r ,i\\*쿲:4sK)DUwzhBď?~O5hR&PTG&UjN-kbطEmE:N R0&&bAd2fjX)Xu,Tdkz4Dcg78'5_]w*PO}!NVEJg_6;/hBk,/CP^B@E ngϮ&7Z-=\1o27wSAAB\.߻,ZDYqԆyTAh >Gja]E&1J2>VOfbĦa;W?z)k.T}#pT0R\yg7 )0uazg9p€΁wlǥ6sr(cl/( xlVVԔޛ23Y+'dF}ٸnnZhulgK=@q[rI~X˩2Zun{ R+{SjtDUs1ZjtK)qF9Ql}hbd1ar,3Mɬt Ą6]hMa>m'^vlo89Jr&I:od3HrY&&t^CgZ3Y Y I+4lB=g=;J\uh "ΉRRAq thZ72ÃǷaw~s1V4}(' {UGrgA[BR@JD= h-ߑ\süv\_*;a7M}p4X֗#OB9'[OVށNb&K@ukTOo08r=J`&cR@E+Vaj)#\}gYE]Jݒդ FBQ,M2[̎3/-Sn'W8v7rKB!;i7P:lj4Czq(hs ^<|t&%LnA6.ٽ'/Bde4DVp)C4U rquD (k/&lRM ф۔XyN{˃ nu<$N?+ ׸{>^ߡ5Q!86'ױz7V:"u~R6UArvX{w <=w)Ô/R<\q Z]HچV<$b?yO,ؙ I/^QFN!*셆vIӱ+ PrQc"":.>ojFI/~-,NJsI=;gς)ۣ9vD0|L:=6_<Ӯ 5 ED%/ >HET|V ^bAzm s*M$9D;:^c])A` |bht7CH,K'>tBTYh6DQ(4.*{5g{N2}$҄"Lt_m2PȬo Y(wSĭO"owٹ{ ""Mbt Ƚ,,Ilc&i S͆[bFRbܥ==Vot˸H߇؆ lGw"<.+vCH[bj֗.@ Eh)p2K+y+່ohњntZHfZ\|LB_?pЎ'{=nToso/TE2n#*q:t^6M"Ѐ!՚Y;W݊uaqj;\']>Z]:i?[(8(wkES ܨJP@ {N6V[ާKv+K3E4uTG)}-* 3ëGR/ CC`jO}٬g̟92jjPL TKYf LVu҉UQxNX8rKHޟ( es2AhiыЊX?t̏rlő5,u:HH _+U{V_HܴKy':G``(:؜lNtxvq6Mgmۃ!):+VG|D7dUrݿL҂-H("wN+=x.FY#J8۸GV^]CF$TUyBWEp='DٙP\B7;n(ۅjk$$< r٥׽ [Jrـ*h&Q9 o"o$Tw)wQw[A]< `ugv0e͐3,Q~בyf4Mt9VԔQj#fQv $[c=ޣJ)rڠαZ*f%5@WS G-0ufpʹ:J-O?IjXx5A3ה[.*C9ː}_ʝf;ҳǢAA6s}:v&u]42d#qנ~D:WV@㝈%/p׺dHeO$Y)EgP|uEa|nMj4+ h Ė6&h-VKS)oZA l4;BAd=ewzSYw 跪k`Z>'$P4CM6̏'1iIf$TzjmVmT |`jRF:a~'ulAV|3FP*QWx' {T~2h4>7hٷr"CTh"ls`_KN35kǪ!6"~&GpIs{#Zdu|׿u^GӐѯդ'JMS7\5N91"3l?UR0(1j q̀lP^B34:O `5K3$ iMIi 25+OKW}*\Ri;G"%0f\(ڪ>\DX͞;nZ_Bu˷Vo?5*#u=ʭFI9 \%TV/Ëp9K 6!ޠWw(iK$),l̉ѮIMtj?eJǔx6ɯjO. }`f;S y}%hϲ%|eJ./ R:dQʷܺ2`h )X ßIo v=^s~#:;CZ2ZF'G7`wJhmu-o̹Fiţ{ Ɋ1ߌf'}X<7ІndO%# Q,LY>;lK糮ET.PWz)Ub lʶUjnMkaFWbRf%6VQ61+l@s07ywty$tb6c?RuKŭ.X~Iݓ"@8t֔9d0jk`&`–$&] H t]X֐Qf_Y%~.r{&v/? ؔ!^"@DI R_Y6OɆxnX%h'un@QQc8kB]PM7./Wb,/p>_`r&`NыUU8 ONZ9 bM3 E**١\ cJ1ypwmX>hlNmELsA]roZX Q!31zϺX GrC!Ο4!:Q`AB@{9krj Gb4`Y};5q ^+ Om"1%\1!ngp0nN^fn{9QQS_3}Ɉ&ڎB{@K.P~@DBu ҟ˔nB+x7Z鯫SRCCLFEts}sbx sHB~ٿ$0q_@l*E)3$-tnPʝi/e~\s6)D2ѲHE|;G~KVrILדM|CZtVRw(cw[LG|~4J,?;m FGj@zO{*#4)*lyx29S[_KӍ RiPm⻛auKP+u/nV}Za.iI\VOK576RS qkFbIƁ$GL2no5XCZ,0L<2q> (~R(!YW{nOyƻ:+KHҲET5ȭUAqaM/8^@vRGZi 'O4}eاM J3txm(伺yuy}Tdߺz,c TN:Z\ΚoyG+)9/I$@ekvή~OrU/5V0HVl BH x|~̺+e2Kę-H`߸p/д387wW}]ҭދ ~8g^{iAxBPGٟsNB@ivęՐ Sn!6e:LL/6O_/$#…/kip%.37;{V-3CxoDi=f`KKMGIsOc)]}~7֧Dؾ!=JƷ?á ժdf9b"AMKw@\x&`VvWsVz6NU.-pјp^Ͳ[):ڪ >o3._fꒀ @fTd/|a{aQ97esh1O/ny0h~̜ 9iNU1^-EgteCzԲJF$92aų f/7kI/ AhS},$6&zWGL =v!r}m&_ 2~desE0bJ6lĵioNVBYKzz@^%sZhg6 @DI|SLJ] ,D\S7M(U ^PVKU5ڱ}i1l[9w &dTy(rA$ B&# #'匌sQA k@}#D%!4SXV'}E.6U9f4ŐIVkAoMjlV;z ޙ:4cp*G!E.|UdVk<+ǟ "sH|y,𫘃\E4آ.IxOgWvڃ2Mk?\ٷ}PCOF2@m_69/q|;U|voɂn>t'dif2g7kCPRcwدC*}vIZ~{t5lGv\U(˭s_&kM N 51FC^]i$X|]D]-5)4VOP'"u@+nY tWA$ w 6:Ѵ͈Nȕ,ªFc7z%/܎ 7=dL%w"ǩL}{QMړ/yw‡M|/bVid:S:x{ܶ| =\\v.t$a"@ĿMh旪#kWc@{*p4^r,݋v|@BVї1oqdntА#A'K~fb%jDzfɨNH>yֺ໋A7\t3G! ȩ4u^q'؍ >uޠ~L嵁'`"$Ј.Pwg?; _QfаqJ+K .?'ZS8pQGroqO\ۻPP "ЄoiA&OM[_:lĻA4lF^¬w Zo$w{w~=YxH1մ X~1j (.&Cu kF ŁbR%+l^bJ<LWYr,E'2|J~& )KX:DYZTNxNb~ 0S{rf6PY"~ڹoKD\?u/Sqiz|whVt&:fϻ|`Gde ~i4\wȲZ7H߬d) =YU{iyn1M` @'sF74G\q3Ӆir UҔA{w S@fܙܨo w9hWǕr]wԖkhMD*P̄m #߃t%_;1I@qiblW|x9 2˞b;/oݢ >JUL^)\C:<%u)2b@9|@z}UR=]+JS~ 0WܦO΀W̦fUJe*zTL5iJG`gU7 p>ӞThFR'u7gk,1dtIVD =Ѓs;R \-NNՠc@ë 6 p3~ǣLc +N~yvkBK  SC):T.ZgP=aԏ#Gta#M&*G`gZ:ɳjgskkjt̒d_GNWKHnA 3^0^L!PN^vLU-fsJK-sW#QzOK}z ojgf.]Wa: m3F|e a4Bc˧hRД1&t ' u%E/DNFd˱cbZLb\Ommkc\vTW^|6d4D/Ti~b[(^:nIYKEM'v*{*1M%'BkV,p'6zU]aZj!~ h"\A2j$.䳮c7]~WnĪ_K^lc8Q!*mė,l)&%/7巖JV@O Y}%m78MNMlB!5`:[W-%p?CՎYkxDjvh+tw:)u{p.z&OޛA|o mX wVx \r_}2 @  $ٯ}EJDE`of7MQ;J轒?4ZX]Q&bB"vb?Ɣx1 o-/8^HC|'3՗u:Zv?̒iYpog@_5T޲qZ]B6O\';POaFQ,'agS\uIxl{%FD@e3ъ@f `(lͨPܨWC3%GyýhKH| hRPL#&N6'`;ƢSO?9%կc5ϹEWyIi tB2/b#Uxx$O J5ښб-נVD#Dž2BV.sr2K^mkS YBp;kJȨTOoZ=ެ=EbFȒhڽtiϙI> w-$vL_O23 OpL1) 뒑eo$ 7n2002ey[X\ڏsd۠ tsXbpH•E/rr*㤟qS"{lS8{ Y Z|IS֑ا@w.ַָ^PV'53*$n斏&VWQyP0l\үWp1)&{WkhmoC ӷ2@L3K/PVؗ.#{}@0 o2e<\967׆3yjLWč͜,$o$ӹ[Ƨw@;:LxDi<]Uw!g*dUz=#w %~&Tf kզGlT(?%<*M8?0jYޣѥ`ζrqevSo|s 5]qoZDP~Ayo`xdZۺ X: /+c$Č,R$1U PZ{sQd5ɗg˂f٬\e_8Q#垇3 ..]ӎ? HA[⪞z$g1nXqqc[t|E28hch\ _\2s&Fkk-({:mz7wdN<"_rpr/_ȴ4/?G[خ#Mh? إ]}yG/7s qCBLJaLEv"d>uƪ/UI Ɔ 3: G'=:\Gک]l?c9XJxXQgγ3h1 Ra?m{̠mLw_=ҺW%S)+|UOAٞn_ j=D,hdI3qJV⻪8 *q^+V@Eurk3+Ӷ 1Ur Bwc5a_/]Q~fE;[g`h k|ˡ HzaX&,/+tл+)N}O:o: sK}XTþ&:Eȱ fʓĹ.Pm ܝ"aD)ڛ?\Gb3FH6b էZH aL_.]2k48Ƣ6eA@_9UYĥ*(z^nJν/O^Gk]M \Ub]WoU SMעsz")(߳⎝h[k ج=j:"}dbhGbSI[K})[GAjw&!E+;/dBN+#ETdӿ(XF MDm@.l 4MSzL*}F=C ryps o*ih/uJ0["T_qA G.a#G%ӴhClX=|(`_|IyjN^ TJ:0TW$]YV:kv9s.R#Wp5D/]+ύ%y\fo΁@A6sO HGJ||W#oÿx,dV4,<"Pjq6~VQPl}RԤ>ڨA&x qhx8W]o:U,tW)rT p!Ј:PR[Us"3Xռ˺t=J}Eݝ R-,Ui()gĎY67p;;|H<ǎŕ&8*'`n_nrs^ʹ.<?nswhg=e%~hx]FQum\3c-vܮt!HWUCxQvt E7\C"+ZA kڠZ<-<+w/\̋9hg?{R@?,BT:p c,T@ʈȬt?]qv"ֱ Z˂sF$=A#1pm6g{d03l }m Æ[ a'r}*/nYMjJ8J*K5۵9sx&͹veB- Y_R惂9)FڬMet=V_Au).Sqc#ҳ^iҋSqճw>ԪdӆQA }L7YC0Nbz;c,A(UXfLzȢHO# M`,*0f|txXZ޸;?p_պ>/?]zYp@DPKHkO83VA0<ԓ k[\9y܇po1s(+hX2o0dr{MJot_˓cnckD,zOtFPL xI7x,xy5 Hb~?)uI>hPz'YyXZj3["O/5"GUZ7d{^7"D(f s3MOt5/tиcog}!HuUG(%WhCV<"&uRGL3wFת 3mHǼ~lS>Nj N^$%1kꏿ,?~2>s7?_! ; SJcG:#?5Q-$0o_DK&~qJ'z/xm_Dѓ=B`mQN &W \P5$4zi Jt0goɈN7+s+ 57Jbͭ]ZhGm k]rEzO+7//*SD+qA{L5o=uY_p:p <g奞焅c6R9+o;ov}V,x7M>ڄE׆hοstzb#V nA6K)'a71U$ϫooE" :ofo\{n '/4jzc0^639:PRg߭:tW+dH=%0}knqP)GP;7nK;%w:<ڕF\s[(m4͔`(lf,w[6t%K:6,ⶰ^x܃.aX|ԠII~Cĥd܋jn4U3TTof'g0-p_㏺P`3ИI 7Oc;&pHia/eD8*E&"9) |.k_SHLi|@NUKҦ>Νy ǵ,8|Fxʍԏ4 ̂g\#1ܢ}cD&h(yte]6Ph&N]kVnwx'wAq\$ky)-w?`s>89~{.>.s{,CJ[4po2=KB V1biD`iqDs) ob>ZۘSK3k,^n ͤ]m/0*u X"wnoÎ?<]Phe׀O$ cHNH:7ȖfBTGD6XbmH7IR(̓iҜcQHGSC>foZ#P+{}3ml8G/8X})rT\7.Pd,UW:|lu=KIbY2`9ֈ*'s*ZB_Tq J4D9UC)4fBJmTܜ Bۃ3uC=Ӏ>*THΩ#Á;l6K75*~EMNt0|!Îv{;=}y8B##z'=A{sz}VR̬&=sӄ}HHuZ|ea_5º {%m@*dQIFYl:U6E*9 >{-r9Z؇?>!/ۃ .J2F@' Rf\vq8=d+'~.3o=&/i=vTAz1D<\؋[U)?CW}Dv `/|os³i<'<2Ø7ToU>)'^_giix#Jkg%ui֕nc~NeSO~XN5,ۖ1X`J }ʴwbȁy3~:Ѕ7(Ms`h-F@rβOsnJjZxy9]I,M #[@͂s WBQVG.^3LC+ 伜0{g-!u07B}ZPޜ%@k eXQ88st#";^h[}3MoE&a4mR;}aQ[6+m$H5V Bn@rpwtnuZLz4mJ$%4"kR\Iq{#lY:=ɭsO5bͶ`axȏ|m L2(.,@-HmՃJaR٪GA[}X76F8ftY5Tr;E6#Ӛ(wK;B+-Aff' Q6k~oơVԞa\ RBKFb*J<13 H =G; $ FR/>6ED4hT1YZ~nW'CZ/.&H0è8ZEn-څ<3hd vҾX=+1kID7m[푇(SEchkEꢔյtUf|xd6ȡ9V*O-N?8{=Z9[:ri&<i'(Șm"˝&,8O^< m:@Z@gȯDF̖H8l , c4R%r=@tFO@ZFJ'F찆1Ҵ)sVXS'DZE KH!#M{wv /蚧r@v!,۷ pɛ)|gs` l7DA{'VAS\~JމI5.V U_Rlт@|*B60^+C@eχڀ&k&dˎ#NA=.;LdiYʖH׿8JDMСCNAwPQ##]ΊCC!濖x/ Gzyn=zEx" DL|nYcNhRJ,3U|S=*K.j)BXԟfMGiԃk?R\Xm=$>]{Mbͯig*%kgAl0#ugj^3 XR "|?~✩nA([y w  #E`ܗ-p୊n˲4*mʞ~T.9^fmu@%A3)*^-V !=I%΁" BDm$a2%BlUqDR,]7!7ytlW [>7i!24W6Ii%^ԫ1FF%moҊ s-8I@蚎G_(7HßXBBԉk%"ĞC_9*4 (dcf ϛ6gj#<8K0Լ% bHǼE>tfe:Ռl-1Rm\6/R!FfUnЋ?1 bTu(S1*h}#nApaYJf7L0XcfpQ:k[c:O/˼΃g$He_E;#5o|/{Axǭ`gk;]![n 4W xɋ,s Z\od~& zd )AMɀLʤ%wGE}DZ]E];s@;tc[7ތ }'FhaWatmnhC@W1QݹiX|>U d!㽯Ⱥۥf`u. E3KL~'G: k6"H,$8]74N=x;r4:RM"YX mYEz87(vPogۖ#bD2M^]ó0`@= />4i{=`:v]q؄7Yr) qWjۗ8q-{)W4ut1\2Cre7I L t) 8 QLM 謑ւF=l,ݻ\#捽ȑ"YPWZG%5[Fހ$c 2e'_Yt.`#hvQX1:~v>KG3K!nB`BrwL{~Lv'JS'W+^מ[BOKozc84}E gtWi&I :9;fɳ;- \]{@O7d4q_a { d63JڿHS/j%LW`;ma(WڼWV2DZ|/'Ĵd!:\ô>bh. .Q8r!~" 94nlx]S+"tےK'ZѢ]9oy /4)bå@aԇܣ]~aFLqXpT0= PdXt٬riO3f.#ûSZMӤyq5}YDZ@౥}ri6-V:վQ$II\߆xOCb,0"BntcUM/AZJo{-"(Ia^)FU_t&>y '2!F ǿW GG8K?=HOPe^S(3꿆Z}[4>Pk l'&>'@ `nN;+cf$ςФ =LN݈M:de4Qɩ9xdIs\̇Gn#re( NdfCmZz)gwm_)#4wmZ :C&e}J[\$wI 2%˓NJ0bwAGvkf !=3YJ"%ļ5L=3Hx0&'L-}y`s2Nh0 dM{jڃPn!jc NxRI]_W4ךl*Ccx|{-[=lYKcpL-j;0:_9Au)Aץ??uo ڊOX8] ؂⩁^)ReAZܔz'_Ҵ{{%>A ` 12-_pc!'5a.y^Tp+ﭥ5D1gtH{rC-q N y"1;r==\kaMPTH)[?%d^k.s;BH%AX"Jk7,kAXᮎUA,ʼnM^z!4|ho`9 ],`M@ʁ! !NB^G%n3%D>(P.ɹZ՟nHQ?/w` z)-`Z_.Ld/8ᤷ(4p%1Ay*K8HP~9x]j*rd F)'i&7KU=ؖ64bB 靂:\)#0L/ ۚ/j2 V`󶔠ӿ2jMvnҽf;ɹwJ\94Ki_j)m:D~i@;XyrDLgA>&ۅcy,o1>S^U2hN»VPU O8M3v $Qppsky36*P] h]Mql{)Ɵ_fs(CU꿴ihCTM2=Iݶ708h傥kk UV3(ܣB]%tw2 qO(DI.ij5v348ͨ."W`5̅`$2t1+#^Vlnhv`vX= = _hڂ=V#jUj$4DB4#~$ U4+5P"Fa't|2KL߄93z\FIm0an~|k҉DD*P&! r``, P%~r^Vl1³L 1N2>S!%1SG)fJeO:o\ )Ή|Ȝۭ$Oxo]de{=?w==M>d+]k~B0n_3  *a} z&Qs6 3\vp՞_ma`f0m8|g 4рf)!6Z_+hsIDmjO^DP /xkp|aWITem,?ͣK;vkEɃ[7 Fhs+?\'Ƴjْ5 NF<AdZRK$fΦװ.|etMɣUG9h9jt/':8GN?FcJNqN>%WzO;KүB"vvB=eh _G;"#aݧOw jzܼ=u6KDk3w=dF jW06_ӠFN{ G i¥tk\aшvR951}'Ki!0X>(?!mz5R~sP}v b.#kZJ;Gu)~v2>Gsj 'GcW cK˂k/|/`^)hg}6䐃6po Eݝ|r{L"55?^:2'5&I&iQ߯ZP$bWl IR|!:&}:ycFM[gq;7a^;OK ꅄGD1TDг'㢲kٰ;Nw'F8\{$_й.: |~ʒjI.NAlO/%Q*,tpL4-+PPZJcB$r6rFs8@.WCoJp&= 8w=% ݡ)Pws(Wt巿9N @^f LɩՃz._d`Q%<"2p]T#3Dp׎7,ds'E$ ` Sy'Hղ[":4?$,wfil!د~ O@ץ0$HIֽ3; ѱ3a yoVtK?Ob3Ѱ̄A1Ćŝ\y)f0x7k@J3V $GD0::?;0/PJ3_r&`@-Fe+S;}RLK>^ / Q7"Bpj.^K BV\pbI;Z8ۃ<In ҷ|ԧ Б> Oٵ^ނw}l<=S9Σ;}\iʴ(H;c/~0T-zYGvsHU88z\4Y3]2-Z@GTG#z,՞m@RMtX\jq"khl wν g[TI3}YrY`G3Wϸ2Ɓ2ZL`}‹ xC@`snס3Ts:M/GH,چ*9 \fő|.'sH$L _ xCSSSkMuw ݹ+Ъvr=Xqhk-$Mf*#lrUjqݳx :{#{$ݶ|PbCSI:7;ɟ5:qlpZ/3Sd$ 2$C a=&y6s(uݽnCdA 0;[8S1,Ӷ8[ >"!2E/X^i4Ewo2Y`ZA@E R 9@3;>DfT 0"\,X>N ۓh:;̓sF{:n[7a9$J;mQn]tt;|6R&65WC3H?-ѧ R ec&l7-@w̑!߻i W84*l{WAu%xdN"2ĸֿ)g>B{ˏD\3c@ v`YY@eK`s٦4ļE]mFү մW}U_ X0ݑt1ŜH&>BHUB+~h*&ST?R!K{M[h߼/>@AD DvgeR򪿩&ͪ(UX4"ZqH'2|K@sredI' 0vP4Ucg3 N 09)PmUy7Fϑ'1o{Obm*i@DJkO|03KUS&+5qvg48)m[ZԹmkEg OM'}=H ,O⍢64|7"Dk⢜PYP_?ʱM2VG uކXZ4,O'&%/gVV,4ҷ{0j"*yqK{ga|MQy`Ǿ1X! Hװ` ^.]>g=wIRk%6pKz"2g+ ,} Gȱ0ϕj^>dKO!E!X/<"n;u 7n:wL\x^&O~i(65f5ll(E!Wxq5WbQZܸՅM ~h'_sq%K}qfj)u@2ssL;/7끦k}fSJJ,&1KJZ-[Q C!pIrdO]Muħ橇:ql9|kZK~}I}6G&}[_4i1,.Ĵ<PbgW:(E?HX8rTL$(^['׀ADPpE~7JD}w7;*|lƋy~%KHu@qO|E T&Dm77zvۿ.=#bÍtS!w4Nۆg_L5GŠU~1ɽ'R:/JK~.L,%)(د*`H.rhK3T7!x#`ͩbW.eKS؞9r qb *(Q*!7L$Vr?͔u86ENb?ߎ潺Y"#Ny5 {=OhWKiYbچG8#v~p_픲ٝRRSA--y/Fޱ0:#v2%3ב&cg@ c ' :Mv(17N2@A=F͝fb1^ODA|FFM(Tc,y$?ChσB-(ԲtS^4?DP`}_BoѢq񫸰 f?-P} +zU,33(48DH(WY9ɪd<_D[p)f; 4ǭųaHi-v7jGsb:Qu:AM$'cX)Z\AJ'VtX7BX@XTBܙ ۶+#8`v3#L![QSU@$SGbdzZ#$^X![z''9f1iDr:lap[z|)l=k]&Wh~b*N! 7::u*,!4 ?44ֳ`.ʡ7 D6sbP E~j# 9c[;8\ <6&x\I#pċk rPJO2u$W9(X+<4 Wq&P 4 qcH7G11D? 0aLH_$'cR wyPqhed|pJebs]" \e¨@ᲄ! cbT byb%*n|Ua1T,b^utH.b3TЖFmZ"U'gᶈ,BR$*gdTӸuVm+g 3ヹlpa 1ԏI;?:|džQW1q5f?E<atG2\7b}J)'8Ke4N$Taf.HyԘuRv(%4fIz6 Yys]* 4waHی_r53AVg 1mQ@+PhdN9%gQU1_3>0ɵFlOD \ϞyvV RGk:lÂ}AAS0mo9bjՕ"`0{w`&n^]a7wx9Ȳ5d?w-ˁ wq!L Ѽi @Sc} VAưͲ)|Du"iFd:rЍAF7֙U*ɌMFxHHy!nKhvNfܶ#zQ4fO H[eԺ>DO5IRaK8Zww{a^<@wx W!`5<5t~1?/UL@u2%szSEoswAx!ͰcщظZ#/Zw h~Sv &S;!A.v.}vH4xz \L+90$\1axE6G Sf>x{5(w%{b#K,O)akjwTw]asHj Sȁ3/ۆ_bUwVL*>{C\0 e,ԴB[n"€ V8vg#fiO3!gi漂JfA\ʆ?Y|CG@RlJo\8<^'laqxYrFDM/2?$nLBwUU>F@qřGjjc1{XKG/@S2ҖAO 4nmYu*mӵppH%H]\4xzicZbm{jB|.R4wu~v- Ǯ}HGs۸}mޥ̲𕞈Us[ D.AȕKHtS_,e0s|΃Ѥ _#m7Eޤ{WlXƧBktÃon3+Xm @3% @l> a@zVSB!;_C93gXS}P 8OxsRkyD} NpL@,5%m Zb7`N;h|yBNjL7+]NF )3"%;޷bYuG\*TM R̒*> /BkC1dK,yyB3Lkº+fkrGMD}|9ϛYvXV%qfksUIpW$#K[b%>ō|'w4&|=gDNUV}&C.lb$Q?4h'"q7Z:@RF)*-/rPo!u "o:I=D0]h`"/IߧÄl]ao E٢W@H)yРog ). hfE2r117/e[Élޘ9W;V;NwvUj6DsGFttCX&c /D(1W=d&:.);پ[7F,<LCR+jR??W5JIz6u}* UӬCh$smm,_}Z\Ip~O-p zJI\{|?)8/IQ8f-K)dt5_ԧamDhW㞁>W_l~&aKI1h3T"ТYrիJъDvRtBJث ?9M {4q وлqQno*>vxmN"+8NZ^)]Ier͚#vXog~@j^sRjw F݅NeԨvG>io%Pi>xUr&3HtH/AZp*Գ!=zGMqQ/ MW_'@ < 4k'm&>f*8xh/WTn=d 9,I(wA:&@w@\1DՁ6qE  cpZKXS*'6-@C# vAB-Ov#NT\ǎzƑRcj r#KuRgD뮩S؊m[> 6U #GٜțÆyj dKln[YP=md)$lژC-^[= (E38µ@ tC!s_Y?Xf/47^Nt׶T&el&cjB7vbCW^ygqbx]}{ %I C0FtQ/!MB P *8^*kJғXw>~iiMXНFdoYtzap2^ h߱zZ=3#˽+<@h=lOql$6Ũ*{ ^tƕ( 5,U!V']( E*?`-Ou凳yRJhL] U%i(>zm-g`lxV-ǃ 9픛P?"/beޯ^QSބ M-a G` @:n|c @FG%|'  Aa*{8*7l|bUa;=t$I9AF::;׈߈\U`CcH}'JLsތE͵Iژa֘9K{sջ.zr4nZ$qK!'*GF_zpv:3\:tCA+Uև8_{ By5~D1rZeﴠ0׌TJăQOX +kdؾ!Cߧ 0r( Fxݘ!*'#؉xj9iHg+A2o[  k:Y&c RzDO'Ԡb6 ˬ :?[nt+a:cĀ] N##q,WBB&N)e')P 0u3嘞]p@Z[cܷ#d5`,GqBw$["aT=N'l˖ .F {jX qװihw`^L/{܋q8\Jr'1cg_z8 "4sƜKʱq-%nj{JQE7za8X y7BYBiGt4B#=nu ~(:MGeo#Ef&Āߐ[7D-tb#'hH:=cej;2$&4f* *D!* G* 0mx;L0fPLa.5xp_*l( Dأ4V2V )#^x0_8WTWXx;[,]tc>'OY /ɘrR1гݡU~%Xs䶗P5ʞ7y8gVONӾZlK PjD?Pe+\Q#❨^ eCz-b!8vX~KES:.5; _e}cx5n4Y& "Nh'te9.Zz+MJ_{b߇_)^gT1@hz LvF٪=6*B}2J1mn`{bR])ΩI0C0i_)r1_ =D-aus=NBΝAR/mAn @)IiYFu $$i!F()UQ=yv1t'LԗR`^5Ȯb t 1g}mpr;6_M} %*"WaҰ᠗z^o,- ȧKbڃȚXw,cMuj2hArA)#8Klu;[&Z@㺻7+ ܽMa93q%~׆ jw@IUmUUpcmp:@N]`g x%$0' O>TS1wnsGF+]hܬ8K\ )ǽaU~"{,?h贳!&^6 Wn* Pu&>LӫWqCu=X׵"`I"BPǡStcwZzԞtjŮ9umҗ]=(U~B\%<0oOYU]h6FSO\_ UW)}dm(@|"iB?Ugx5flUdžHjC;*QOs~,Ɲ .=]r-1q0d2zj*iKRh\JpR;g8K9̸*Jh9\SL%r0frjJ4d 0yC)\iP8Mn:ќ||aQINg5[@buU HM8K%x4 =OsW>o}/Avn2J:sҵ˗_£'i_E&&b:+rrGʔj4dn@^Iŝ-p0Tu)J7`7s!8}1Z;rja`pF/0Cɑ-I+ {}g8081"946Dl[ BnbMV$&^dql^&HLWS^X>D%;f=D1-ja=}9h }A׫^("l/.bG8;W=ΦO{4#v'Jex%QpE+AEKD0pބKΩ>$$)Ǥ 8#zsQV~!YsT5rC/P%{O $B'HNW\{u229$Ly2hV n0wI:Y<^<[D /s7]UX4IjOu1O F J?lҵ&)x8( ] ]_j@:TNKK2P h#9hk T )wp F;},T0<ul'icE{NIFHYj؅_M4z"OH!_W쐡A`jf_8nF#Hr`H삧,#Iho z}ğHYR>=+nzIjoʧ#W+d*iJsNq\yQTbzcYfΟ`^jMh [p޿PLW yv;r,O]#5Bz](ω/YHpmVqjl7%"n!fq&σƋ 2˭G գۮŎS$`k#Fmw}{܍VnN(+휐Cj-QYf &ʿ= d*?򣽮 {)@4XܤbY>'+&ϼ15gC [utAQrO{߳ ,MsFGU"׺Ac4yE6Wy:LiLbd|32Sn3ԬrXp o~еxĪu˷[_S//>s$al/lNft!6&!6dZxy>广}VމUZ\&v|Ʊ{;#t BlhMg2F8-)D qU&ee()rN1#DאLE2]"`Q**IJnWK![x^1"cU ;KU-$sǷT;uF!:á*D^<-8 w-6YˌLЋovyG|5΁?k[3|.0AFAޯX ~ߡӷCIa)m͔kPyJSPҩ(r77cǟ?I4NXEFuGiSko %(f9ҏc5C$ ÕTl} 9%KZA\NɅ=DCst4t%h8ٷ巤%./>3$7@Qga>*'z]\n_Cj2=b}qThdjVk☪k8aq4NU+1AҰ jX';(g˵1DZr+@-T=6c:~iQzC8]KVnjf=x(ש|ip&RqNp[]܀}ÜF }RǘQ_}̢UI 4;͎>1 e@-B3'Qno}>=ͻN%?/,T?9敍1=/fBPT8tOh?ee<:CT.V" FZJTN@Xnh9'&/Y2PH3ria#kZSʶ8N#)^Ŭ clK+:K7^CXE=Q(n*O`kN)Qħ CCxmjv$49;]  OXYC@%{._:S/`|E-8y3(R`a<("$Fiˑd'$*~wK+No:o5 6O%ĞˢTnԶł'/dk Ŵ'_(S2)V y痒M ?4V+Lw_ dc}v ӟ&c gČDiO6ɃPǒ6   S/,&uj @{;Z 3?lW|NAŲ؄V^o VT/IR+%G~tU"yJ 탵@6%`=9ݞ/NqHwX1Ξs]5o`ꑕbtħj?|C65*30I ']<Ԛ4s` j A;$\USV;6HQ28<6z2m|ڗ7{-ϣkrw5.ф17G\ #"pݵ_dN_»bǖ<$aVw,z@^̆r>X:i[FA /1\tNI(精3r\_h  J]+t%k# *YGEp2sj:'{ G7yo DHwe hk-➋:ttc+ᆠ v!U[ҹX2oSp1uKI>W:&g7BENo UKpi@dI>>!l҈5{!՟ؐQvU 6"h( b:l0B% ص.F5WֻQ9Vd>M|֜+ l'+a*듍U yHזW Z.mj:aƃ_3!d$ēf8q0F!S5ڎOP*N!zKZ;\- HC4g/ Dw3omIKPa.6/5?(uQ-紾?'֣2)00m>hZAntKG{EM"i`ȬgVӄ |VlBu6OaK@]x5i Vgx!}, So7ԍL@O@ݜj9I\:YXl9/{+SYuiFKuqI%7.3T I$ʯT]{=ZX\N"jf:,/4m/z@&" 1)FD%&eoUcsMN#CD*ۮ2bȴIfK;fr P^,UA8/V`5h{/c\,:-TM8,x11psX62m"^4B>ġ́+qA/RXT4tָ(mOm(lͯ;Cipl4ߖZGfai.}R!0*9E]Eα5\|4MK͓?ںRLy5kp 2M jc6CxF6̗7+pgtfpFm>0cw{cf*hjgXx74ѱKIWZl988Y)!jQBohh%[[ 4DC5Z -9TX&A4:%7boh+ܰ55OK7bU%\?oȀ | ';9#Qp!g> DxW%%9Ȁ38'_.Wh"`$6 ŕٞm"f>& UZE\`ǯ_;6@] ? TuM=z<Ș̬& F]"g̉/&cjuDS'0X#q-w&w\WڄwĔb#Ӿ΍~MQQ[5SooP&^m 䍕nb}^V9Z fi1M46߸= mSV&4L(fG,ϥ֟ftƶ9 D8.|&6 6'6&#;Eޕ5U{䆯Ѥ`f[7`Raٹ'7Ɏ`ev>CnTVcH2ayUnU9(KZ7]V!n2:C0P0]S^.BL_ 6Kˣ tBn+ZmH.13 )b羓4 =UP%OgM\Nve)Ρŝ ٷLxB%OoՑÀ*ҘnځUu?#gz[e'lȟWu9Dn2(,]}sWI uȚd aB&$XoZxX B'K N>xk3u=S[pWbK YDZ:,PBP{ 7- g:1CZJ>@L)f\p+d& E`ɀ)W5i\X0m(/DꥤC#/y~›m9Z9c$a1=H&N/a =Tn"5jHljo@P5ZR^0Bbg3A '.۟[rڎoNuv8`'ݳ xnKH'A"|}Y( Kg ,nEH`+xF3A† \LȻ6! 3ĪN-Xˮ!O礪RfXQiK.; nd!Nvږ)>lC#OČ;1I*l/t1lkOB寏\_Qnv];eX$"9"9i'!;eW]lm9B@]5ǕPttrjzvDBOp YF$E7L=PFHqzzd>S2wJYexS" "bNoZ+^,@%Z71͡L),RR5Kn_R19 ߁R;a[&'0wY,Lځ&|1yDp`zeeZ/cV:LZ[q4!PG+ *!ޡ##@l4yt$샯 JE\Cf6!sڑ;4ӬN gHI/EJ1_61m{hlɣM}WEщeAr}]naB?ro7Y @/ |*a'NR]W&`1`BZ# ;6͂ n=;o1"vb< 芐9 z|i-_vS3>tb6V-M-յ |ƒ[~[T] }iDٟB֪t`Cd W Ha35}9$v4%[1]L@v4DN:,"H>|##hM_ǂFF- O"z|سa`%안$%O^6tYiO@Gw3Ү_3͉ChӈtO/T2L"?nשּ2ozW= .ҊF=*I[Ŏ.MOq2[U]k*AF 4e5X]i~5ɆbXˇRS31J>NԴ'eaF0d[@47؃TDeJ[3_YS:P[WɅ{[EeˤZzwB{ y̮"CٙT[WlSc. %!CߵkYUIs{SlL4NZ=-]#o0 Nkp ZߡS-~?U EYN|7Y"Y)f {6)LqEaǹKl=ea}$D宯FRV|fT띚K-E+Pg $?M<:V3R8g *=gU[2@ %OK&!^]9J!{Z+$ |ls˃pQs\.F):)6hg @[ڋ'jفi$m 輷ĶݦYJ- MZN"@cmrKgaI&MN&fSZws쇼(0Xajt?Yq^ H|Ryqu~`|p. y !- )ib(r #+rTV5a);7.|xg_HPpy>"iE}kq큝<($UPKIsW3w$ȽrYX9fD0 3x4BJ2yz0LJS1_D^`h9ջ`,a> Rj2|jJde҈Ԃ˧,`Pe>n贘# (-'T5Wt&X&V[1T%u-M#$(xglH+E/?t2dwQ l`&"%FlW!g_g([)ǐu 3PQG/kjS/>9 f}Y%~'fg؁w0֙,5K:xσv2$6(:5q6+ ֙t^<ϫ,=ed?[^A{򗏚{l~Q/#ӎ\dMl&|U8d;91=p"Ojy ?Nďoi)2o-ppEY2 f@._tEfv-̏Jf,jIIS 1Q?[ԓ&ĺt!&h(M&Xf!c#jCkoRsNCs@0XQ+_:&jIɷ~]FZ.J_5{4_@볥iMIwPWc ˾>R$\v4--@s\^:"BJ ֒l2*]+#~4F{q}v,=BGyҹMŰgŚݛY >5dR -yZ<{ +D0)^H!i n'!*VdJ3Ug ܩ1Y$Pĉ96Jwp7#9do -0ӷ4QuIsdԂ>ÃAHxn .|n n&A;؆U#NzsV`=M!@&|"`5qoKtaX; 2.#A|ӫYbh5A tUu A;WMއo&MCb|MG~2o9Q>I'c+(8 >#e4ja/ssgZDSo82*τ V[q,K$PR ^N|.Rz3ܢV1٭|7Z0@"?1IWxMrr$;@ yWe2> JhcLLP/TڔC}D8F ^-tX:<,8 4X}}"7&Ů}r+^KH]V?{K,@[BԕH"$Ww"2ǪAa+R=޲6R'3s7o3GUH]t*Σ8,< {ۑUK,o ܎5R@-3MO]Ȕ:6@幃tt&pѾ7Ȏ쉄ϖ2P7Jg +5NZx'`CV~O{y]OoD2Q%)]î. m:"좤0LѥUݑ['Y\F؁-[Hd^LRG)F'JF[>;:/BUFzl=Gz^ kavTKbsF j <<,҇mVFs4 RA&V(OՌJJRGқ^s`L+rPЭCYc/98'7v&p2ܱ'}78q9ͥ*=;+q cd%/꧅VT+^~޴ў3M]zhðT-O]~H$5)ƔLZ:DL8![ւFap&l_ߚuI?_o|h7MQC>fcn`עGV} Âx#/N\mo yuD[.͖ĝw^x$ i1%$'&%Y*>ͪ'%;oldI޹L0s@?¥m,c E )F|c[ E ٦Jp {߻'F'%59\= 0{'oҟov.5պ_a,aΩg:]"mzXȥއ^G0g\@?YL0%XCϏ 諗^qbU\ #ީdWVc%Zf+9;IЈ1X+ @6 m :_D9} l׺b>nꢨaY͟02(ہ߽̐ޛRWLkڕɇeYw; h'U%/$L1\"FombLJvpX d/'mQؾxo+*zq?¨/5X7I(YK*=H{UN6+ C'\0Cc5X[L_{ˤ 2_oV'lMc\|ĘV]%Tjס@B]ߑ-/z6ԇcx7xH!I1 K/"4[)QmݻtΰNkyHA4&=BHRES 6wPAn3S{|aiC^Tǀpk;wj\:vHN˶=k)Y*aO1l[ j$𤡑OQ̽5ZxgnXIjfB}!6q=AݜTdRZ;yM@Sլ}]Ǖ{;`1ۑcf-NnFX,X6 haF/wI>e>R<([/jѧeIy7w[ܓ] ץpأzB0&?,Y%9 $asQGnl'1m^+E E%l2 K0N:UQ끝OgENЅ TLѹb? 0k/>kŤT#[ 3ƃwx-WYvβ샿^[ӰVF4A) r.$fzBmD0qe>Z-0_ᑥ)tXv!G+x!KE;GȨ/_g&X ; Dy ֢/'4<Z3)\h{K-ڲ.ynp,0AbËVCٹ6{0ȥ緂%J$8[?PTïME-6li;]2dAUT~HSs" 8}Vh0Ңxr v:EK J @ [t茐IRAPt٬i+3j(t \Z@;vjd 4pWPs'JF>磞;rV)at?ݟ`90&O6W*ط744 4v6 t}I8bOdc7bU-4n%$ec b  7e̩:YϞpۍMj )֛ oa7&@&'$G ީWd&2uFF]D8bڹFHpA0ax7i.#d E Ο"fO r&vy} Frͯ5[%0 _ $TMmv!]_g?]l@6O:\M+]Ib[pY pUPIy%vExJC9^:aY ^ &fO 593q<NJ&:Q-!pOފ"͹ " 1E10SZe3Q1Þ549 tzVȑ֠`'\p}g9&k0"\OHb4,Q_ y _zl΀O;$ڡMޱYrUfWX,$EbLOߑk\x(݆6N\RR<fƚy۟3rW~ڦlh8[5WT w*ʁm6,]+th-!Q:W\X|p-MfyyҺD./~D;>v/`"$Fn1/`Jͪ BUEvOnYgav$9Fro4Au䳿ƣu|!K/ǗT9 VɶpDO=EPykvdt+Izvح\uS_eJ+Q̆}ePWPDz|z $/B+C/˚2cio<ܜSS>,fׇ1 ʶVhE[+a/yu/8I(fd@6Q# DXn ұ [lXR\tBe[t| 2]Be x^c6@cDvl;ޅ.YMb nAܲ4GmvL')+i9mRjd [ "'7ybBW زGPd$Vq7ޱ5 ˋ  p)8I{i&A,\C?ozG^I3%2] W~+fc=+8!S0a9%9CwZL+ڦUY+Tߓ˯)f٣aGe-X;\1pqy}`4%ҏ> ̲)DuHB|k쿕0 tx5 2w'VC3 g2uBzOE@tq> |24LRW"Y>9i?{i"ZA%u~[jq239 U'-+OPlw'dQBks%yoIh a*x UŴ;jksj:b\͎ [v5fBE+H؝ (4bE7.v/F7[>(Fᘠϥ {F-^S~#>(a-@ M[*k i2~ 厃|4##@ WhF>9-:Ʃe!*o K bn+76|h  1H \A065&'{!3fx;>P{R:QiJ ="גnjõ7a=g?Z; Vme8# s e%є>Hl쨃a[I4A'T\ox[BoHz|ahDK3g%4sGhu>?Fm2+m1 Qf!.}1$Uaf|"DWZMEHvPX L";V^|UuY؄h"C,< 먭A6r^>Dv7~sDiXt?6_nWQ\˥ERFADiQ]jcnM ]PV i{өn7 P16JUXqܲ-槦>R կ *6y9kPyey`* 4 \i^R 0\#h1}7l*w^z0p%"b 1~ 5ft\.I6zz Vpv]:$^ϐCa_xԆmmw|!@9iZտBS2I߄ nt LfJ]j,` g@xG,[]R!v!Yd~ek =! SOWA5 Zh+#}w]^ٱUl} j OQBO ޕ-˹=P?4PYzH`uÆ)M]_O}j/7h扛5Aa+T7mX!̟#l.=?|ؿOmؙ:3)i}|| i:~q7IͶ=|"3p$~i8a б| /qD |ђimӗ5_<=i!!H&­Ydʠv>*2,n"a뚲-y;/5wwC+$QoM’~\mZm gwʩ+(f"bҮQLM`LeLd3j*Iȩcȋ=hqwu?T:s#e4&CWF'x"-TK@PַUu=M9D.Aw Pi=f~^DD֘-v - Ff/z r҂tx*״&w L/W0髆Q(sT5Bt_L |Iқ?Nh#+#B;Yj7qzannjh3hB7"6YءNFfy'!Pо#QĔ8֮3# i?ꖽE'qSqϫ53YVީ֛ W2HšYrVHɾΛޓ{FGLd/MGzp ̬5>Jw`js8'Ԙ1\ݺ-(xڄ&6\顤<0C%{Q8Q}piWdN'~23Ig*=+B`ff __%((5T8 ZMz敿}/H"":НСyu-͕V|b|gDQ]P&nd>+x+HK ddȈ9F%l ,Q[p^;_c] "I3wXU`{T5y*grS lHGM:uvpNF".g~;pp*;_2;ch'tCCPZ+J D7c\l6[3A׎fV7(4BqDj }4y_(|6]7[røy6 >@HwDTl[ D^~ USp Oͽ ėIH4(X|*pGN%,Z5MsmWH$sZZ3a V3+X$lxߏ@n&FsWqQe/E\3yAc&CH`Ӥ ^vږC ݨ J\Lsz젥>/B;we~"@ɻ''#܇3BZ !($[~k(RÚh5AhwxKi,:s-gh?G8߈HtFA2>*'TjV혌 ?]kUEѩ8ց}E^+aTJEKe+l ,cGXr(Ջ;Z@cZ^\ini^5!] 3N(=4L%{e(^u mN(Jb7uK.6ܙNijswl!ǰC&:ivM7ZGn?bJ"m/&` ]͙B~wLn^ x04Jd4rzxtpˤ*k5u|d`oXߴ^D Flp0Sm_O1jFP =' #d!Rq\hIN-""5#\ '*$m֬<[\+"Hy.ߍhB( HgW VF0ax)olBJ,nSO q7 bG"WQ |i~h *zS!nZm $#Z.8a!#֑ C"!hNQLE$e2.21+ʱO,y ܍PzFS"܎O?-\DƙߓG|t`+K었**TR`V2]bsGz9U^2ֻok :A"lȵ4=߂!S;IIg;f*+e1]b~]r--,)gc0CRWyep@Gxv raP uPяd[!n> ' kPEH†#$ CvCOtbv +͈Fy>@É!D9uENeǏz򞡝ɧ8fڶ Ǚ qdy8vAƥ%={N>ŌW4MRڋLf8c\.{-Y\֐%6ZW4Z"AƺhYb V2;Ҵ奶BEsO#CqkTyռd1u4*2q aLvoS/~$.k*m mPa$|ObbWipXc ۡ7"DBGN!jPGvlc|{@!|\> $;MkPxvebGɱ4Z6k|d[R䎛Z8h8xHe % IoU`Cq>;8miUX{6J~9 'bYU#d2gʱOk M Mi 9,g% TM]lEA`<`slYV.p[^@7*EJ离]\k̦DtȯEZz7m&4IResڴOM='1alAX,=1#QVzdziM?}]h>_@1A 07Ÿ@v3ĵ|=G,%zGJI$I$p U+Ix߷v:󧾅- hRę'VmP0<}8/3Q$[xe'҈acZtdt =rX2ں)", $@Du)FK dP12و+y?s=N*uy~%VX]`%wP҃ptbe-E_F ^wk陸/m\ХK4]krCAfAS ܥxo- ÌRs &56XĖp?Ӟ62WB 44qpH NzPp.n!^Þy1?C_f|$v҉FH͉9ږ{Gڬ.= @od|41Y\F9eDzzo[D<_}gOGB 5|@lQA/Ϲ8<S?,4+f!io"g1ۈܴt4 (K*sl~iSW)^{$!I{D(P^[$9}T8HKgvO]qڹ$]5*3z@Lj9c`:L/2qQy lGvJNx {vèu[eԓ*j7*)*+Xqam8Z@$1!K \'5*vvZe^Xj~ =#z~y^ jj,3~J^ RN.a1ze5*`iojijIx+i(, %GuqrIi(]SB-$FVy+d3`,K"X<"wLV2ŝZDFv_ɪ)Lua=7l񨵔rf2)9N Q_{BcVQDGWvR мܟL"c۳!NfuUsځKa9s`u}CFa /,CۗclUrNR״J)&rgqm@wrGBpQ 5KA í$0AFVܡ/ (s<5Rm AA|@E@ET`p[Jt 3z1&u\ >UIA— RU{x7 fn_DʚgwM@?̌UL|7.-bN&b9WQE9ޯ/ns z9IFN7Y_m>!0/@Pexu6`˭îF}¢APLyO$?? W/{x㪧s8=4YOإN,i;dNxZ9Jz.#4*?= ` g_Bw솲c9;1ƃ6?/aNB{&mCQmi >YJ%rVE-O}`e. 4Sg X-,W6- 8BHOFOd2-sXXu" OA$˝2nR4j+6Qh8]͈~(pRzEp3](e~,? 2^\X{iSR beq%wMf+ϒUBL{^d^Or(@#MюbRQLTHع/@ĉcb}ٽH$x6| e{r. wGJ zD4H1&0]v!ibM_|ipמ*% :,:;!É:K冔!$e*Y.\RǠ9΋ib\gG{4[X$bXhLjju7!7wFQU %{8# 2nԸoGgz&:sREōd2d>DA\S NŌV5=#wxn971sao}y:,t*^_$&_AHWQ܉ ANqY6,"GkVtUˮZNғ|r^(gİ&<ʶD")Ű#_O=Cǜ( 79Z`wj^u3 {uE8'(wUN%kf j+C1akmS'ɊA~if\nyTra'|2mݫd6F$ cSc_mapper-0.8.1.1/images/mapper-icon/Mapper.ico000066400000000000000000011677361325266516600206030ustar00rootroot00000000000000 ( V`` ~ 00 %&   hv(   <@H9 C6/.)# finKLPT t_G<2*$  9:<*47;}hTI>71-(&$! !QPO:=A{m`YMF=:41.*(&$#"   u>>=<;530,*'$#"   -.0;qqqEEE<:7@CH.6Bxtgf`XVPKJFC>:83/*(&$#"!   &˸CB@^epx{;742/.,+*('&%$#"!    _cjo~P`w|uqib[SPJG@=730.,*('$$#"!  (((1&&&9&&&=&&&?&&&@&&&@&&&A&&&A&&&A&&&B&&&B&&&B&&&B&&&B&&&C&&&C&&&C&&&C&&&C&&&C&&&C&&&C&&&C&&&C&&&C&&&C&&&C&&&C&&&C&&&C&&&C&&&C&&&C&&&C&&&C&&&C&&&C&&&C&&&C&&&C&&&C&&&C&&&C&&&D&&&C&&&C&&&C&&&C&&&C&&&C&&&C&&&C&&&C&&&C&&&C&&&C&&&C&&&C&&&C&&&D&&&C%%%D%%%D%%%D%%%D%%%D%%%D%%%E%%%E%%%E%%%E%%%E%%%E%%%E%%%E%%%E%%%E%%%E%%%F%%%F%%%F%%%G%%%G%%%G%%%H%%%H%%%H$$$I$$$J$$$K$$$K$$$L$$$M###N$$$O###O"""Q"""R"""S"""U"""U!!!V!!!X [YEGK}|Xi }ytqplhgeca``_ ] \ \ [ Y!!!Y!!!X!!!X!!!W!!!V!!!U"""T"""T"""S"""R"""Q"""Q###P###O###N###N###M###M$$$L$$$L$$$K$$$K$$$J$$$J$$$I$$$I$$$H$$$H$$$H$$$H$$$H%%%H%%%G%%%G%%%G%%%F%%%F%%%G%%%F%%%F%%%E%%%E%%%E%%%E%%%E%%%D%%%D%%%D%%%D%%%D%%%D%%%C%%%C%%%C&&&C&&&C&&&B&&&B&&&B&&&B&&&A&&&A&&&?&&&=&&&:&&&5(((1 r|k~=?B431777777888888888:::<<>>>>>???AAADDDHHHMMMQQQXXX[[[^^^dddjjjppp|||zt~vKPX875<<<<<<===>>>??????AAADDDGGGKKKOOOSSSWWW[[[bbbgggooovvv“|tyPV`874=<<@????>AA@BA@BA@DDCEEDEEFHHHLLLPPPTTT\\\aaahhhmmmsssÓ|t|T\i442/2603703814914925:69=BBBCCCEEEHHHNNNTTTZZZ```dediiinooxyyÓ|tY}DDEEEEC @d:CCC =3]HWl !$$$$!(2A@@???@@@BBBFGGKLLSTTUVVYZ[`aadeehijopqzzz~`F7 >ABCCCCCDB$StÓ|t-[DMMMMMLC~NsDKKKDVyEXt "$$ (89==>???@@@??@BBBDEEHHINNOQRSTTU[[\___effhiinoprstz{|k"OAHKKKLLLLIC&UwÓ}tW{JFMMMMMMKDj6`FKKKDYm ().5???==>>>??????@AAACCCDEEHIILMNPQQUUUYZZ`_`cddfggjjknopwxyiNAIKKLLLLLIC&UxÓ}tMsIELMMMMMMHH.ZGKKKD`w1013>>><==>>??????@@@ABBBCCCEFFGHIPPPRRRVWWZZ[^__cddghhlmmpqrvwx~~eMCIKKLLLLLJD%UsÓ}t7cFFLMMMMMMLD+Y,YHKKKDj;>A;::<====>>>?>>?AAABBBCCCEEEEFFKKKNOOQQQVWX[\\^__bccfggklmnoouvv~aKBIKLLLLMMJD"RlÓ}tUz QDHMMMMMMMMGF]+XHKKKCbv<98>>>==>==>>>?@@@BBBBBBCCCDEEFGGJKKNOOSTTUVVZ[[^__cccgghijknootuu{}}^KCIKLLLMMMKDO]Ó}un/\EFKMMMMMMMMHD;f-YHKKKCa4ISA?=>>?>>>==?@@@@@@BBBBBBDDDEFFGHHKLMPPQRSSXYY[[\^__bccfggklmnoosutz{{]~KCKLLLMMMMKEJNuÓ}uEmKDIMMMMMMMMMHD4`3_GKKKCi1j+x\}FFECDDBCB@AAABBAAAAAABCCBBBEEEEFFIIIMNNPQQSSTVWW]^^_`abccghhlmnnoorttwyy{}~]~LDJLLMMMMMLFG8eÓ}u['VGHLMMMMMMMMLFF8d=fFKKKCwJw+%z i h4WgKFDDEEDDDCDDCDDBBCBCCBCCCDDDEEFGHIKKKLLOOOSTTZ[[\]]`abdeeghhkklpqrrsrvwx}~~^MCJLLMMMMMMHE$UcÓ}u7bHGKMMMMMMMMMIDKLtIpDKKKC]~T r pm hdJQTKIIIJIGIIFFFFFGEGGDDDDDEEEFFGGHIIIKKLMMPQQVWWXZY\\]abbcdehiijlloppqssvwx{|}f PCILMMMMMMNKFI=gÓ}uq%UDHMMMMMMMMMKFE,[o`DKKKECjXytpk g+`{SOLMNNKLLIIJJKKHHHEFGFFGEEFGGGHIIIIIJKLMNNQQRVWWXXY]]^bcdcdehijmnnnppsttwxx{||~~n&UCIMMMMMNNNNIEOGpē~uw"SDJMMMMMMMMLHDPQwDKKKH+W}~<tsn ieDYdSONOPPONNMNNIJJIIJJJJJJJIIJIJJIIIIIJKLLNOORRSWXXZZ[]]^bbcfgghiimooopqstuvwwz{|2^DGLMMMNNNNNMHENAkuÓ~u+YDJMMMMMMMMIDH=hDKKKKFO4^sItql g eWWWSRQRRRQRROPPMMNMNOLMMLMMKLLJKKJKKKKLMMNPPQSSTXYY[\]^_`abbfggijjkmmqrrtuuwxxyz{}EmIEJMMNNNNNNNMHFG$UÓ~uÒ}u8dDMMMMMMIEIptDLLLLD1wtpjeElnjgdeeabc`aa_aa^^_\\^[\\\]]\]^\]]ZZ[XYYYZZXZZ[\]\]]_``aabdeeeffhhhklmmnopqpstuvwxz{|}~oÒ}u2_EMMMMMMD*YMLLLLG7bhtrn ggdpvnkjeffdeedeeeffabb`aa`aa__`^__^^^\]^\]]\]]_``^^_`aaabbceeefgghhkllklmnppppqstuyz{{|}|}}Ò}u:eEMMMMMJE[UyELLLLCD|tpke2luqpmnnkllghifggefeccd`aaabb`ab```_`aaab__`__`^__abbabcccdefggghjkklmmmnooqqrstvwxwyy{|}~Ò~uW{CMMMMMHNHLLLLH(Vw#usn i d[uzvuopqmnniikfghceedeeeffccccddbcdeffbbc``aaabdeeccddeefffijjkllklmnooppqqssstuvwxxyz||}}~Ò~uDMMMMMG,ZPuEMLLLC\~Ytrmf"kxz|wwwtuvpqqllmghihiighhgghghhghhffgcdeddeddedefdffeefgghijjklllnnnooopqqsstuvvwxxyz{|}~Ò~u'VIMMMMG2_KKLLLKE4xtpkcKv}yz{tuuqqropqklmlmnjkkfhhhijghideeghhgghffgghigghhhigiiklmklmnoopqrqrsuwwwyywxxy{{|}~~Ò~uuCMMMMH*YgCMLLLH!Pktrn hgrz{|wxyvwwrrsnnolmmjlljkkklmjkkijjfghijjijjijjijkjkllmmoopoqqqrrrsttuuwxyxyyz||{|}Ò~u-[IMMMLK-ZILLLLF6aH|tqld4p{||wxxwxxstuqrrnooopqlmnnnokmmjkklmmijkkllkllmmnmnnooppqqsststuvwwvxwyzzzz{|}~~đ~uEMMMMDmFLLLLLDIoy&uto i ea~}~z{|yzzvvvrrsprroppoopnoooppoppopqmnollmlmmmoonoorrsstutuuvwxwxyyzz{|}{}}~Ñ~upEMMMK"RhCMLLLLCQv\trm ek~{{|yzzwxytvvtuusstnoppqqopqoppqrroopnopppqrrstuuuvvuvvxyyxyzz{{}~~}Ñ~uMtFMMMFPv@iFMLLLLBNs7xtpk eYYbr}}~}~z{{vwxtuvuwwtuvqrssttrrsprrqrrqrrqrrtuuuvvwyywxxyz{{}}|}~}ÑuAjGMMMEw)WHMLLLLCBjntrni a 4b%9]Wcu{{|xyywyyxyytuvuvvtuustuuvvstustttuvvwxyyzyyzz{{|}}~Ðu>hHMMMEOILLLLLD3^K}tql iU+X )V8^K[ouz~{|}zz{xxyxxxvxxvwxwxxuvwuvvwyyvwxxxxyyz{||{||ď uClGMMMEoLILLLLLF"R{)vtoi hCq+V +X )V0Z8Kgclx}}~z{|zz{y{{xyzxyywxxwxyxyzzz{z{||}}}~~ď uTyEMMMFHpMHLLLLLHH}_trm i_3a.Z/[,X )V *U$;^IXoms}~~|}~z||{||{{|{{}z{||}~z{|||}~Ď u{DMMMKQ"RFLLLLLJCRv:xtpk hP~-Z0]/].Z.Z +W (T-W*AaLYmnt||}~{|}|}~||}|}~}~ď uGMMMMDx/\DLLLLLLC/Zptsni d;j-Y0^0\.\/[.Z-X *W )T-V'>^FUjfnx{}}}~Ŏ u1^IMMMJ%UBjBLLLLLLGGpNtql iX0_0]0]/\0]0\0[.Z0[-X *V )S,U!8[8IbYcrouzō utCMMMMD``DJLLLLLKB;c},vtoj iFu,]0_1_0]1^0\1\0\/Z-X.Z.W+T (R 'P/V(>_BRh_gsvyō u QKMMMLGIGLLLLLLEHmbtrm hb(Hq0_ ,^.]0^2^1^1^0]/[0\/[.Y.X.X,V (T 'Q )Q2W*@_CSjajwx|Ō unCMMMMI$T/\CLLLLLLJA0Z>ytpkdJI]y!>g ._ -\/]1_1_1]1]0\/[/Z/Z.Y.X,U-U+T (S (S )U2Z)?`BSl^jyw~Ō  u*YIMMMMF6bWzCILLLLLLEDMrs tsn hfp{BWuc:OmYgzt|Ō uELMMMME>h!PELMMLLLKDKgStqlc1vr|GZv&Ah0^ ,[ .^/^1]1]0]/[.Z.Z.W.X.X.X.Y/Z/Z.Y ,Y *X +Z.\;d3KmSc|r}Ō uaCMMMMME7bMsDJMMLLKKIB$Rx~/vtojbh|Wg~2Jm8d -] ,\-]/]/\/[/[/Y/Z/Y/Z/[/Z/Z/Z/[0]1^/] -] -] .^:f1JpNa~q}ŋ  u=gEMMMMMF$T$SDLMMLKKKHB+Xetrn ekkwI]y,Fl5b ,\ +[ ,Z/[/\/[/Z/[/[/[0[0[0]1^2_2`2a2b1a /b .a 0c:j0KtMbr~Ƌ  u+ZGMMMMMIGf[GGMMLKKKKGB-YAztpkbJgtFZv+Ci4` ,[ +Z ,Z.[0\0[0\0]1^1^1_2a3b3c3c4d4f4e3f1f 0f 1g8k+JvH`n~ŋ  u%UGMMMMMLD,[9dDJMKKKKKKFB*Wyu!tsn i ckvL_x/Gj9b-[ ,[ ,Z.[1^1^1_2`3b3b3d3d4e5g5h6h6i7j6j4j2j 2j8p)IyD^nŊ   u*YFMMMMMMIDe2_ ,\ -]/^1`3c3d3d4e5f5g6h6i7j7k8l8m9n9p7n4n 5o9r)K}FbsƊ   u;fCMMMMMMMHE4alODJKKKKJJIF@KWw2wtpjai{boBWu*El6c ._ .` /b2d5g5g6i6h7k7k8m8n9o9o:p:q;s;s9s7s 6t>x/RWqʼn   u]EIMMMMMMMHD#TX}]JCJKKKJJIIF@CAfhtrn fjmyL_{0Jq;i 0e 0e1f3h7j8l8l8m9o9o:q;r;sy;w9x9x"HDdwƉ   u$TDLMMMMMMMLGG.\`W{ICJKJJKIIHG@>+TjD|tpkaHwWj7Qx!@o3h 1i 3i5k8n:o:q;rw>y?z@|@|>|;|:|D=_tƉ   u\HFMMMMMNNNNJFF*YTzWzJCIJKJJHHGFA=EHhw$usn h cas?Z'Gv7m 3l4m7p;s~;~EBdƉ   uHpFFLMMNNNNNNNKGEO@jr]MBHKJJHHGEEC>=}(LpYtrmd+uk|D_)Jz:r5p6r:u=w>y?z@{@{A~ABBCCB?="LUtƈ   uLsJEKNNNNNNNNNNMHFG-\V{l%TBFJIHHGFEDC=|D4xtpk`hnFa)K:u8u:w=z?{A}ABBBCDDEC?A9_ƈ   uf(XEGLNNNNNNNNNNNJGEPEn3^CDHIGGEDDC9z?sktrn gig}=]E9x;z>}BBBCDDEEFFB?'Rmƈ   uOv QEGLNNNNNNNNNNNLHELAkIoHAEGFFDCB;{{H|tqlbEOk)N<};|?DCDEEFGGGE9ƈ  uNu$TFFJNNNNNNNNNNNMHEPQwf(T@AEEDC=}&Wy'uto icXs*P==BDEFFGGH@ƈ  ua4aIFHLNNNNNNNNNNLGF5bBgE>ADC 7zW\trmc'sRp"K=@EFGGH@ƈ  u|Fo!REFJNNNNNNNNNNIE+Zj-V?=>D7xtpk_e~;`B?EGH@ƈ  u]0]GFINNNNNNNNNJD0^NnI2w3intrn ghUtI?E@ƈ un7cIFJNNNNNNNNIFJr}4XvK}tqlaBe#O8ƈ ul.\FGMNNNNNNNGP{)vto icz`~Lj uOvLFLNNNNNOMECl_trm g _7Zj~Lj u)**bNFMNNOOOOHR:xtpk g R 4s?y9Ymƈ  ugghZ~HHOOOOOOLG^ptsni eH;w:v 7tAz;[sƈ  u{>y?z>y:x 7vC|Ca|Lj  uKKK~OHOOOOOOH S},vtoj hOz?|@|@{@{@{@|ytpk gX=z?{@|A|@{A|@|@|?{@{{:x 8x ENiLJ  u=>>CnENOOOOOF3aStql fiSp&L:y:z?|A~A~AA~@}?}?|?|?|=z7v:x5VqLj   u+,,eFMOOOOOI&W~/vto idKi!H 9z<{A~A~A~?|@}@|?|?|@|?|:y 7u"GZsLj  u]^^JKOOOOOJPetrn em@bC :{=}@}@~@}@~@}@}@}?|@|=z8w@|DbLj  u/0/444!SIOOOOOKKAztpk`Vk2U=~:|>~A@@~@}@}@~?|?{?{:y:x2StLJ  u~.]GOOOOOLIu!tsn h cTq H :|<}@AA~@}?}@}@}@}A|;y8w$IazLJ  uVVWWWX=hFOOOOOMHVtqlb5|y7Z=:|@~A@}@~@~@~@}A}@}=z 8xD~ToLJ  u))* KsEOOOOOMGz2wtpj_yNlE :{=|AA@A~A~A@|@}>{8x@}LiLj  uZFOOOOONFuhtrn elh*P;}<}@A~AA~AAA~A?}:z>}JhLj  umnoeFNOOOONFtD|tpk`Q=_>~:|@CBBAAAA@~:{@~LkLj  uqGNOOOONFow$usn h cOmD:|@CBABAAA@;|BZuƈ   ujkk###zGMOOOONFoYtrmb/ya{!J;|?~CBBBBBBA:|HnLj   u ///HMOOOONFo4xtpk^tn&M;}?BBBBBBC@ :}0TLj   uTUU444GMOOOONFoktrn fjs)P;~?CCCCCCB>>Polj   u666HMOOOONFtH}tql`Ms&O ;~ACCDCBCB ;~!JLj   uFFF9:9HMOOOONFuy'uto hbkI;BDCBCCD@=OoLj   u899556HMOOOONGz\trmb+v\xC>DCDCDCC <'Pƈ  u+,,-..HMOOOONF~7xtpk^nBd=ADDDDDE@AnLj  u&&&"""HMOOOONGntrn fh$M <DDDDEEC =Mnlj   u$$$mnnGMOOOOMGK}tqlaGVt?AEEEEEE =6]lj   u!!! OPPGNOOOOMH{)vto ia$N=DEEEEF?(Rlj   u$$$001GNOOOOMJ_trmc'tJk>DEFFFFB MƉ   u*+*_``{GNOOOOK"T:xtpk^htCBFFFGGBJƉ   u677001uFNOOOOH@kptsn gf!L@FGGGGBLƊ   uHHI EEFoFOOOOOEx|}}NtqlaB1X?GGGGGB%QƊ  u_``LLMbFOOOOMM[[\556 ***},vtoi`=b?GGHHHA3[Ɗ  u$%%BBCCmHOOOOGKsmno9::qrsbtrmd"qDi?GHHHH <Ɗ  uDEF,--cdd!RLOOOPJdef222 >ytpk_aBg@HHHH@Ɗ  ustt### NOOIRSSL=}+El^__.// EEE***XXXs tsn h e8`AIHHAƊ  uPPPBBB5Z6p+T 2  556qrrStqma<)UCHIAƋ  u>>? sttmnnllmHII//. !%$$U^c~/vtoj_HFIAƋ  u=== !!!>??hijxyy{||;UbetrndnAIAŋ  uJKK 222abbKKL}Aztpk_YPsBAŋ  umnn333 999677lmm444-5u!tsn h b#P >ŋ   u%'Jmno444 X}Vtqla7~9ŋ   u,*P:rGQKPPQ 2wtpj^~(Tŋ  u/00##$ """556PQQz{{SQSQOMNdee'((5N[htrn elwŌ  uiEOOOOHAklmm)**pD|tpl_TŌ  u%VLOOOOFhfdd?NUw$usn h bō  uEOOOOOG{Ztrmb0zō  uDnGOOOONGw4xtpkaQ|Ŏ  u"SKOOOONFhktrnjX!TSQĎ uIMOOOONERxםЋמW{3_LDDCCAI6^lH}tqklA`KLMĎ uGNOOOOOF:f[ҫЧШЧҫ]y BEIKLKKJIHC?Jry'utoji!/MMMĎ uGMOOOOOH(X԰.ЧҪҫҫҫҪЧ԰0FLMMMLLKKJIHHE >@a\trmk W}WXYď uHLOOOOOJO׶?ШҫҫҫҫҫҫҫϧظD'VJNMMMLLKKJJIHHG>3\7xtpkk6Lqrrď uLKOOOOOLJ՘ЧҫҫҫҫҫҫҫҫҫЧءlDNNNMMLIHHJJIIHGG6|M~ntrnj d<<<ď u=b$UHOOOOOLHڽUШҫҫҫҫҫҫҫҫҫШ]$ULNNNMGJ*X0\)VGBHIIE@NoL}tqlk Hg ď uDC1^8dFOOOOOMJ׶@ѩҫҫҫҫҫҫҫҫҫѨظFEONNNG_mLEBL{)vtoij$3ccdĐ uEMHGaOvFOOOOOKQط@ѩҫҫҫҫҫҫҫҫҫѨٹHV|GONNK*Y!O>e_trmk YĐ uEMMLD(WcFNOOOOI/^ۿYШҫҫҫҫҫҫҫҫҫШf7dJONNFg:xtpk iHa Đ uEMMMMJD@jmGOOOOOFKsۣϧҫҫҫҫҫҫҫҫҫШݰ%VMOONFptsn giĐ uDMMMMMMGHRxaEOOOOOEuٺIЧҫҫҫҫҫҫҫϦۿXONOOOGNtqlaJđ u BIMMMMMMLEK`IrFOOOONIֵ;ϦѪҫҫҫѩЦعGJOOONL},vtoi`đ~ u-[CLMMMMMMLENc1_IOOOOJ,[qԯ+ШЧЧԱ/{JOOONLbtrmd"q!!! đ~ ujKFMMMMMMMKDNbPLOOOOF`ܫMNOOOH>ytpk^b<<=Ñ~uHoEHMMMMMMMKDMZ}GOOOONI#UMOOOGs tsn h dZ[[Ē~u5aDIMMMMMMMLEJOu\FOOOOI?j0^KNLI?Stqma=sttĒ~u*YDJMMMMMMMLFHBl+ZKOOOOF9e @L,[@kPw~/vtoj_&&'Ñ~uy&VDJMMMMMMMNHF5aHOOOOI?jetrndn223Ò~ux&VCJMMMMMNNNJE)XtMuHOOOOHAztpk_Z:;;Ò}uz*XDIMMMNNNNNKE QaJNOOOFfu"tsn h b9::111Ó}u0]DHMNNNNNNNLFKPvGpIOOOJ8dVtqla7~445LMMÓ}u;fFGMNNNNNNNMGHEnFOOONP2wtpj^''(sttÓ}uIqIFLNNNNNNNNHFj]ЦҪҫҫҫҫҪЦۿW-]ENOOOOONMMMMIBZ}{)vto iaĐuPGNOOOOOH)YGOOOM"TwӬ ЧШШЧҫt^KFJNOONNKGC(V_trmc'tĐ uClENOOOOOJOIOOOOGҎkjьh6cOGGFF"RBj:xtpk^iď urJKOOOOOLJ,[LOOOGvptsn gfď u#THOOOOOMGxMuHOOOIFpNtql`CŎ u8dFOOOOONFiFOOOL%U},vtoi`ō uMuFOOOOONE]HNNNNEbtrmd!qō ubFNOOOOOEPw-[KMMMEj>ytpk__Ō utFMOOOOOEEnPvELLKE=es tsn h dŋ uHLOOOOOF:fCKJIFKStqla:Ɗ uJKOOOOOH.]DHGFE=~/vtoj`}Ɗ  uNJOOOOOI"T)SBEB;{1sXqetrn emƉ  u"SIOOOOOKLAc 7y 7v_rAztpk`Sƈ  uhmHb;W-N"HGHM!V-a:lFd)XGOOOOOMGkWq$KzvShA\4R'KGGJQ'[3f@tQ}u"tsn h cLJ  uc\4CABCEGHIIIHGECBACZ2}^5`FOOOOONEGpkGN"@BBDFHIIIIHFDBB@L iD9BBBBBBBBBBBBB> KVtqlb2ydž uyX/AAEIJJJJJJJJJJJJJJJJFB@V+sBkENOOOOOG&WqNH@CHJJJJJJJJJJJJJJJJHC@GmJ§BJJJJJJJJJJJJJGS(2wtpj_tDž ukJ? EIIIIIIIIIIIIIIIIIIIIIIIE? HdV{EMOOOOOKH^b<@BHIIIIIIIIIIIIIIIIIIIIIIHB@^7W@IIIIIIIIIIIIIFR(htrn ekDŽ uK>FIHHHHHHHHHHHHHHHHHHHHHHHHHHG?IyqGKOOOOONF(XiG?BHHHHHHHHHHHHHHHHHHHHHHHHHHHHC?dA÷-?HHHHHHHHHHHHHEQ(D|tpkaNȃ uʿa<= FHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHF= [5OHOOOOOOKE9d{DAHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHACq#?HHHHHHHHHHHHHEQ(tlw$usn h cȂ uEAHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGHAB7bENOOOONNHD0\qgD< FHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGHF< a<&>GGGGGGGGGGGGGDP(`GGGGGGGGGGGGGDP'gF64xtpk]z upP: EFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; fD:f#T+Z*Z)X(U%S"P MJD 5tD@FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA@=FFFFFFFFFFFFFCO(hJ0XVktrn fj~~~ ulL: EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE; b?{;g5b8c5`1\-W)R$M?z@AEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEB=<EEEEEEEEEEEEEBN'hJ6 5 H}tql_O}}} usU: EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE; fErHoAh?f;a5Z%Ky÷AAEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEB><EEEEEEEEEEEEEBN'iK6 :E1y'uto hb||| uq8 DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD8 wZ{TtFh8Z_xI"?DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD@BȾ; DDDDDDDDDDDDDAM'iK5 =4iv\trmb+x~|| u: BCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCB7 d{^<;CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC=P,: CCCCCCCCCCCCC@M'hK5 <::7xtpk]x凁~ uD>BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB@=Ƽn6 BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB8 pT9 BBBBBBBBBBBBB?L'5 iK4 <;4 SOntrn gqXhq  uhJ9 BAAAAAAAAAAAAAAAAAAAAA<98 8 8 9 ;?AAAAAAAAAAAAAAAAAAAAB;X6:@AAAAAAAAAAAAAAAAAAAAB?;9 8 8 8 9<AAAAAAAAAAAAAAAAAAAAAA7 8 AAAAAAAAAAAAA>K&eF6 DhK4 ;;:1 L}tql hVy  u7 AAAAAAAAAAAAAAAAAAAA<7 ;W4iHx]pgpQb@I#7 9 ?AAAAAAAAAAAAAAAAAAA5 }Y7:AAAAAAAAAAAAAAAAAAA?9 6 H"a@qRhpx\iHW4<7 <AAAAAAAAAAAAAAAAAAA=I#8 AAAAAAAAAAAAA>K&6 A8 pThK4 ;;:7 ?*{)vtoi k1Fo uO,;@@@@@@@@@@@@@@@@@@<6 R0~qV?7 ?@@@@@@@@@@@@@@@@@=B5 @@@@@@@@@@@@@@@@@@?7 ?pTT16 <@@@@@@@@@@@@@@@@@@5 m7 @@@@@@@@@@@@@=J&G"<@?7 iL3 ;::91dn_trmhd, u3 ??????????????????6 I%sY7;?????????????????4 ~fL(;?????????????????;6 pTK(5 ?????????????????=?6 ?????????????<I&x`5 ???:O-hM2 ::9875:xtpkj Hg uN+9>>>>>>>>>>>>>>>>>3 iMA8>>>>>>>>>>>>>>>><?2>>>>>>>>>>>>>>>>>9?mR4 =>>>>>>>>>>>>>>>>4 h5 >>>>>>>>>>>>>;H%7 =>>>>2phL1 :99871MGptsnii"2J u3 =================3 iI'7>==============>2 uU67===============>8F#Ǿo4 ================:C4 =============:G%T47=====;9hL1 998777-zOtqlj X ucG6 ================2 {eB8===============8L*4 ================8Ai3 ================24 =============:G%|1=======5 ^AhM1 99877749$},vtojl7Or u;;<<<<<<<<<<<<<<<4 aD5;<<<<<<<<<<<<<<<2 yb3 <<<<<<<<<<<<<<<;5aE4 <<<<<<<<<<<<<<<5]?3 <<<<<<<<<<<<<9F%;:<<<<<<<<1 hM0 9887766._fbtrmh e!, u1;;;;;;;;;;;;;;;9>j1<;;;;;;;;;;;;;;2 v]L+7;;;;;;;;;;;;;;<1k>9;;;;;;;;;;;;;;:92 ;;;;;;;;;;;;;8E$cG3 <;;;;;;;;8AhL/ 887766540>yt o m)y5JU ueJ2 ::::::::::::::;/D"6::::::::::::::5M-Ż2 :::::::::::::::6G%~/;::::::::::::::01 :::::::::::::7D$0 ::::::::::;0 qYhM. 77665554. H@s  t|At:64(%($#'0/2  uɿ2 :::::::::::::::/{?7::::::::::::::0 gM2 ::::::::::::::7C!p0 ::::::::::::::5O11 :::::::::::::7D$xc/:::::::::::::4N/hM/ 87766555424~/ 2~zvsQJF@?@435('*'&**)+z u0 9999999999999987iO1 999999999999990 u^S43 999999999999990 t]3 89999999999999860 99999999999996D#2 899999999999999-vgL.7766555433+Z_erfddMJJ<:?434 ('*0  uV78==============4 aB6 ==============4 4 ==============4 mP4 ==============4 mO4 =============:G%I'8=====================<6lO3 <<;;;::9988771L.wurfcaB@@,+-)(+%$(/.1 u\;<BBBBBBBBBBBBBB9 b@;BBBBBBBBBBBBBB:9 BBBBBBBBBBBBBB9oO9 BBBBBBBBBBBBBB9 sU9 BBBBBBBBBBBBB?L'i7 BBBBBBBBBBBBBBBBBBBBBBB<X6pQ8 A@@@@??>>==<<7O0ZXVIGGA?@313'&))(,Y ua?=CCCCCCCCCCCCCC: ƹ[8>CCCCCCCCCCCCCBA: CCCCCCCCCCCCCC;jI: CCCCCCCCCCCCCC: e: CCCCCCCCCCCCC@L'ɿ=BCCCCCCCCCCCCCCCCCCCCCCCC8 }qQ9 BBAAAA@@??>==8Q1~}ca`JHI979)(+%#'aab uhF<CCCCCCCCCCCCCC: J$@CCCCCCCCCCCCC@K%ö: CCCCCCCCCCCCCC=_=8Q2tsqUSS?>>KIJuuuu usV: CCCCCCCCCCCCCC: vɾ;CCCCCCCCCCCCCC?W3<CCCCCCCCCCCCCCAI"lL;CCCCCCCCCCCCCC: : CCCCCCCCCCCCC@R.ya>CCCCCCCCCCCCCCCCCCCCCCCCCCC; sVkL<BBBBAAA@@??>>8R2olj000(u}: CCCCCCCCCCCCCC<gE: CCCCCCCCCCCCCC=c@L&ACCCCCCCCCCCCCC9 R+@CCCCCCCCCCCCCC: : CCCCCCCCCCCCCCD@CCCCCCCCCCCCCCCCCCCCCCCCCCCCCBEBBBBBAAAA@@??>9S3u: DDDDDDDDDDDDDDBGgE=DDDDDDDDDDDDDD; uW_;>DDDDDDDDDDDDDD; p; DDDDDDDDDDDDDDD=; DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDACDDDDDDDDDDDDDCCCCCCCCBBBAA@@?:S3RRRMu>DDDDDDDDDDDDDDD9 BCDDDDDDDDDDDDDD; uW<DDDDDDDDDDDDDD?U/z^<DDDDDDDDDDDDDD@S-; DDDDDDDDDDDDDDDDDDDDDDDDDDDDDBN&HCDDDDDDDDDDDDDDDCCCCCBBBBAA@@:U3uV1@DDDDDDDDDDDDDD?]:k: DDDDDDDDDDDDDDD; Ź: DDDDDDDDDDDDDDD: FBDDDDDDDDDDDDDD=jH; DDDDDDDDDDDDDDDDDDDDDDDDDDDDC?ùt9 DDDDDDDDDDDDDDDCCCCCCBBBAAA@;U3}}}xuuW<DDDDDDDDDDDDDDD; DBDDDDDDDDDDDDDDAO'>DDDDDDDDDDDDDDD>]:f; EDDDDDDDDDDDDDD: ; DDDDDDDDDDDDDDDDDDDDDDDDDDDE; z_L%@DDDDDDDDDDDDDDDCCCCCCBBBAA@;U4:::(u: EEEEEEEEEEEEEEEAT.oP=EEEEEEEEEEEEEEE=qQ\7@EEEEEEEEEEEEEEE; ø@DEEEEEEEEEEEEEEE><EEEEEEEEEEEEEEEEEEEEEEEEEEEBH: EEEEEEEEEEEEEEEEDDDDDCCCBBA<V4uJ!CEEEEEEEEEEEEEEE; }c;EEEEEEEEEEEEEEEE: t; EEEEEEEEEEEEEEECEX2?EEEEEEEEEEEEEEE@[5<EEEEEEEEEEEEEEEEEEEEEEEEEEE: a>>EEEEEEEEEEEEEEEDDDDDDCCCBB<W6000 uuX<EEEEEEEEEEEEEEEE<@CEEEEEEEEEEEEEEEBL#@DEEEEEEEEEEEEEEE?W3lL< EEEEEEEEEEEEEEEE; y<EEEEEEEEEEEEEEEEEEEEEEEEEE@Y5>DEEEEEEEEEEEEEEEDDDDDCCCCB=W5...u=EEEEEEEEEEEEEEEEC>CBEEEEEEEEEEEEEEEE< chG>EEEEEEEEEEEEEEEE=^;pP; EEEEEEEEEEEEEEEECC<EEEEEEEEEEEEEEEEEEEEEEEEEE<y\< EEEEEEEEEEEEEEEDDDDDDCCCB=W6 ua>?EEEEEEEEEEEEEEEEC=l@BEEEEEEEEEEEEEEEED?;EEEEEEEEEEEEEEEEE=S-`=; EEEEEEEEEEEEEEEEE<sU<EEEEEEEEEEEEEEEEEEEEEEEEE=oPGBEEEEEEEEEEEEEEEDDDDDDCCC=X6...u< FFFFFFFFFFFFFFFFFE; \7ĸfD< DFFFFFFFFFFFFFFFFF=pP_;@FFFFFFFFFFFFFFFFFABoH?FFFFFFFFFFFFFFFFFE?Ǽ=FFFFFFFFFFFFFFFFFFFFFFFFDD: FFFFFFFFFFFFFFFFEEEEEDDD?Y6dddSuc@?FFFFFFFFFFFFFFFFFF@?dAjIA?FFFFFFFFFFFFFFFFFFEA<FFFFFFFFFFFFFFFFFFE< L#g÷ɾqQ(< DFFFFFFFFFFFFFFFFFF= tU=FFFFFFFFFFFFFFFFFFFFFFFF; tX2AFFFFFFFFFFFFFFFEEEEEEDD?Z6􌌌!!!u¶?EFFFFFFFFFFFFFFFFFFF@< E`H< ?EFFFFFFFFFFFFFFFFFFF< cmM>FFFFFFFFFFFFFFFFFFFC==S+iFwWmoxZjGV/?=BFFFFFFFFFFFFFFFFFFFDE=FFFFFFFFFFFFFFFFFFFFFFFBR*=FFFFFFFFFFFFFFFFEEEEEED?[6߽FFF6uf; FFFFFFFFFFFFFFFFFFFFFEA>===>@DFFFFFFFFFFFFFFFFFFFFFBN%FDFFFFFFFFFFFFFFFFFFFFFC?====?BFFFFFFFFFFFFFFFFFFFFFF: |=FFFFFFFFFFFFFFFFFFFFFFF< nM>FFFFFFFFFFFFFFFEEEEEED?[7ŏuuud  uV0@FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE<; FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF?a<=FFFFFFFFFFFFFFFFFFFFFF?fDDDFFFFFFFFFFFFFFFEEEEEE?[7uuu|JJJH%%% uŻCDGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG; ftU< GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGCG>GGGGGGGGGGGGGGGGGGGGGFAȾq< GGGGGGGGGGGGGGGGFFFFFA\6ƃRRRC  u=FGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG>dA]7@GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGF?>GGGGGGGGGGGGGGGGGGGGG= eR)CGGGGGGGGGGGGGGGFFFFFA]8ƆNNN@ihhhT uv< FGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG@V/P(AGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGF= >GGGGGGGGGGGGGGGGGGGGDM!= GGGGGGGGGGGGGGGGFFFFA]8Ƈ  uj< FGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG@Q*N$AGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGF= u>GGGGGGGGGGGGGGGGGGGG< fC@GGGGGGGGGGGGGGGGFFFA]8ʼn up= EGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG?V0R*?GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGE>y>GGGGGGGGGGGGGGGGGGGA^9ǻAFGGGGGGGGGGGGGGGFFFA]8Ŋ  u@BGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG< c@`;< GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGBB>GGGGGGGGGGGGGGGGGGF?c=GGGGGGGGGGGGGGGGFFA]9ŋ  uP&?HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHD>b{]>DHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHG>Q(?HHHHHHHHHHHHHHHHHH?vVL!EHHHHHHHHHHHHHHHHGB^9Ō  tqQ>DHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHG?N$M"?GHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHC?tT?HHHHHHHHHHHHHHHHHFH= HHHHHHHHHHHHHHHHHB^9č tU-= EHHHHHHHHHHHHHHHHHHHHHHHHHHHG@B}_{]B@GHHHHHHHHHHHHHHHHHHHHHHHHHHHD= W/?HHHHHHHHHHHHHHHHH= z^8BHHHHHHHHHHHHHHHHC^9Ď tQ'= BHHHHHHHHHHHHHHHHHHHHHHHF?BpOƺŹoNB?FHHHHHHHHHHHHHHHHHHHHHHHB= R(?HHHHHHHHHHHHHHHHCW/@HHHHHHHHHHHHHHHHC_9ď t`;A?BGHHHHHHHHHHHHHHHHE@> M"}`ɾȼ|^M"> AEHHHHHHHHHHHHHHHHGB?AatT@HHHHHHHHHHHHHHHC_9Đ~ tɽp`:E??ACDFGGGFECB@??R)rQqPQ(??@BDEGGGGFDC@??Ea;sɾ6???????????????9kIE= ??????????????:X0đ~tqmJbxjYK>5/+(%#  CCC4Ļ̻̽͸͵αϭЦGRa)(%---111887AAAJJJWWWhhhwww݂ڋؓ՜ԡӥҨҪѬЯϱϳϵιμ;Ϳ̿̿ħ///(ZZZD_k~988<<=@ABJJJUTTdddvww>>>4VVVBz-[$TJq_MnTf"&;<>BBBMMMYZZfggwxyl+V#R+X4`<<<3VVVB2_ B>/\;d8uTj&0>=>>AAAEEENOOYZZcddopq~UxH @ A*Y}<<<4VVVCClI ? ?JQw@hi A)X/\ A9c- qly}~uttlmnijkhhighihijlmnqrrvwx{||;;;5VVVCFGEAUyqiEt~}sttopqnopnnnnopsttwxx{|};;;6VVVC ?.\v ABUyCz f7Urlpz~xyxtuustttvvxyz|}~;;;6VVVC @*Yg AA@hpR+X-CcWbsuy}~|}|{||~:::7VVVCHEvCA&T[lBp &U )W3[3GdS_onsy~:::8VVVCEn @1^MAE^0tc)Gm1^(Y'W (V.W%<]>Og\gux}999:VVVCGCOu>g A @%SujPUf~-Gk3_ )Y&U%S (T0Y$>b>Rp^l~999:VVVC ACDm}M @A5`GzifsBVu%?f0\ )X'V'X *\1a"?k?Wzct999;VVVCsC BPxVzE ?B4^!k4xfrGZu,Ej6c .`+`+c.f4k!Cv?[i}999<VVVC Q @ A Q\HoD > ?(Si_eqt~Uh7Qw?o4l0k0m3p;x)NVr888=VVVCfO A @D&VRxMrG < ;CDj3 rndw@\"F{9w5v6y;~(Qd888=VVVC@jL A ? AL=g{c!O ;/wWyfSay2V? 9 :?888>VVVCRx&VD @ @LPv:_>K| gc~'Q3h888>VVVCd)XC AG[$k3yw999>VVVC휝7cC C%Vd i&^^s888>VVVC<<<P DGq7w a :x:w/Rd{888>VVVCCCCDEE>i D COv}oY;y5v 5v>{:Zw888>VVVC,,,z{|dE D9fO~ hb\t!J 7y4v 6w!GXq888>VVVC}~ xyyHD-\'l9Bc?~ 5x 5v>{Ca888>VVVCmnoKD&Widta{#J 7y 6x:z7X888>VVVCJKKND$U; ul}2W:| 7z :{7Y888>VVVC/00VWWPD#UfO:];} 8|<~Ji888>VVVCz{{ZZ[PD$USe2X :~ 9}G999>VVVCnooQRRPD&W*m,xwF : b ; ;Bg999=VVVCuvvkkkN DAl> wkc> LleE D'X]/B@DFGFD@> S'Υf>:@@@; *m)u666AVVVCwU< BGGGGGGGGGE< R'PED;exc=; DGGGGGGGGGD; a:ƘlHAGGGBmct444D VVVC_9: EEEEEEEEEEEEF@@_.[E7`R&=FEEEEEEEEEEEF=KĹjF>EEE@L,> vm;75E VVVCgD9 DCCCCC@=>ACCCCD@>sY/<DCCCCC?=?BCCCCD=L#iE<CCC>ÿC]UhN720F VVVCy4BAAAB;:R,c@^:G7 ?BAAA;R+vV6 BAAAA9 =W1cA[6B8 @AAAB7 kJgD:AAA<tVI$1Wm +:q VVVCA=????5 ];~@;????2w6 ?????5 oPĽh9=????4 eD8???:F4 H%2 G2-u \ 'VVVCr1====4 lO@:===:I$jM4 ====3 o6<===6 aAdD6===8x]2 7U3H%5.s|qq=Y  UUUCR06;;;7L)1 ;;;;1>9;;;3 dGi1 ;;;:9»bC4 ;;;73 ;;/yG%445B ~"_ B UUUC89:::/S25:::/}e0 :::94 >8:::.aC3 :::6R14::7:ûG%46-VP 4_>88#!# UTTC.8887;l.8881 aDy-8884N.dH1 888.~h`B1 8884w,9889/ fJG&354+]VWTS,*-jTTTC-8884L+-8883S4h.8880 fJ~g-888/ nT`B1 88837688888-D#354/ >,|zx:89&%(TTTC6 @@@<V06 @@@<W2i6 @@@8 pPm5 @@@8 pQgE9@@@;ǽpQ7 @@@@@@<M(O+;=<:3 b`_-+- TTTC9 CCCAO&|9 CCC>c?ɿt9 CCC>eAvW;CCC: ~`iF<CCCFqRACCCCBDCD<mMQ+@A@>;pno...7UUUC?CDDD=jG=DDD; vVǽ: DDDBHS*ADDD9 zjF=DDDD@CDDDBCCDDD@CCBB@=UUUCQ'BDDD<wW@DDDE9 ž¸BDDDE: |; EDDD< jF=DDDDDDDE<wW; EDDDDCCCA?RRRcVVVC|]; EEED>]7?EEEDBĺa;?EEECFȿL!BEEEBR(jF>EEEEEEEDDjG>EEEEEDDB@VVVC>EEEECCŻb<=FEEF=nL9 FEEFAM#R(?FEEF9 qjF>EEEEEEF: rADEEEEDDCAž@@@EVVVCvU< FFFFD=`9quSF?GFFFE?\4?FFFFB@iExykG@BFFFFCM!kF?FFFFFFBS)g; FFFFFEDBVVVCN"AGFFGFB< < < ?EGFFFG9qADGFFGF@< < < @FGFFGF; kF?FFFFFF< N#CFFFFFDCzzzv佽888;VVVCEBGGGGGGGGGGGGGG; tQ= DGGGGGGGGGGGGGF: slF@GGGGG@hB< GGGGGED111: VVVCO$= GGGGGGGGGGGD; |[D?GGGGGGGGGGGB>ylG@GGGGFBb;AGGGGFD999< VVVCxWA>EGHHHHGB< Q$źiC>@FHHHHHFA= \2kD@FFFF< c@FFFFED:::9VVVB_V+D?> >AJd=vSQ%C?> ?BM mHrLIO"O"O"MV+`HO"O"O"N!LǾ;;;6VVVB;;;4VVVB<<<3XXXC===3OOO9777+???(0` $(-6ao &), ejt06?i)  NSY#(vV=' (PPPPQQRRRRQTSTUUUVUW~~037(((---CCC]]]sqqqf[VTSRSRRRQQQP (dfikkktttQ (?iJLs>e]__YZZ_``z{{AkNY|Q(2^+Y}Kpqq`abcdetvvc#TQ(Mw)Usstooo{||Q (N#Ql~~R (MZ|)W~K&T (MgM|6^]lxT (v'U)U7ay3Nsn>ZkX (MtK+Xen#P)R_y}_s7VC5YX (Z}"P*X?b?RuY ((TBkz[ (EFFIq*Yey@Z~x[ (888e P|Or'Fs+Kzi{[ (ABBzNzo.O'J|r\ (z{{{N&|v'K9[[ (stughh|NyGg&N\ (//0&''jjk\},L|z{|FGG$$$;TZv\(yzz<==UVV YYY#$$)BOz\(w*Xlmmb1\(Ip1\vDŽCk7]j{[(Vz)WĩɣȢUyMr~c{zZ(;eTx@gϺex4_%wxxZ(qO9c(UyPQQ[(x$T/]|lY|ٿ:T \(+Y0\e.[%TMGo`$RڿɣȤ'YwRmz[(N_hfkɥ&ʪ4>gJ#P\;\(4]Bl9b{](Mq5_\(nb99ma'Tl`77k_.7.%L(\M=ACC@<k`L[L<?BB?:i^5B52e (XL;=8558=;i_WK;=7436;9g]2=12hxwx)49G6F69449F6E472.9.RIRIAAca`KKK*i`6336g^g^6314e\+6,//++.;11;..;1/9,-90-66,/J-+5C77A55A75@34@<|o;>>;{n:?y*mbB==@k`k`@=;?i^6CA@=^K^K=@AA;ǀ)=BL8M8A>=AK8L8@<6DDB:8ACC6π(nbAC>;;?B@\Mma@C>;;?B@[L7EE@yo>DE7ա (nb?DGGD?]Nma?DGGD?\M9FD9K7CF9d% (oc99nbnb99nb096690X (T ((((()*++*)((((((()*+++)))))*,,,******--,+*( @ QQQOVa& ^^^:::  +++*****)444'>GT z Z8 $***--,'''  """WcqRQOmmlݜwwxC^^^ih[xu`_]qrrc\yxuD^^^fLqGlolkpqqwusD^^^fIqyfYnnnE^^^fOuhXz! >sms|lllF^^^fuaGm~< KfATr@SpPb}wghhH^^^fNs_zLmd{]lxTjHcKimkhJ^^^fwKruR igeL^^^fxLs! -mybccL^^^fEl]=jH]XobbbM^^^f666Ek' 7x@_tccbM ^^^f]^^9::ikkRw~ n½MlljfN ^^^fEFFeec&7&2YXVefglntC>:wC "fedN ^^^fDh`u WdccN ^^^fDl԰.֨Jr`yS eeeL ^^^fpHl}x! BtpnhhhL ^^^fKrru^<S;xd5 F"D"W=@BB^gD"F$~m3 iR1 cK_F/ xd9 8 s`K.?/?fE#-(4 S8}n8*+8sbr])?4 &Ž[[[Nk}hrsq[p[kleAUUU5ffgghgfffffghhgggghhhgggǾh˿ihwww(nnn(  PPOVURTSQNNN:97JOVi $n *(((QPPWVSJIIYZZCܥߠஹabf퐎ߞ('&LvvxwJ#CyimyJT J$<JtJ~Xht-LJJϠKzxJ@tJU:\>wojTW;gR^EvR>:8K[D]Dq]zhsbiSlY[E{Ku__DjQbJbCK*gO7 Jv`bG]@u_iNRSNIJJJJJKJJKJJL-mapper-0.8.1.1/images/mapper-icon/README.txt000066400000000000000000000005541325266516600203400ustar00rootroot00000000000000There are two Photoshop files for the icons, one for large sized icons and one for small sizes. Due to having a few different effects on them the icons where made in PS. The main parts are vector and should be fine to scale up if needed (the large icon is 512px now). The Photoshop files are licensed under Creative Commons Attribution 4.0 International Licenses.mapper-0.8.1.1/images/mapper-icon/mapper-help.xcf000066400000000000000000000141441325266516600215560ustar00rootroot00000000000000gimp xcf file BB gimp-image-grid(style solid) (fgcolor (color-rgba 0.000000 0.000000 0.000000 1.000000)) (bgcolor (color-rgba 1.000000 1.000000 1.000000 1.000000)) (xspacing 10.000000) (yspacing 10.000000) (spacing-unit inches) (xoffset 0.000000) (yoffset 0.000000) (offset-unit inches) V Question mark     =gimp-text-layer!(text "?") (font "Sans Bold") (font-size 28.000000) (font-size-unit points) (hinting yes) (antialias yes) (language "de-de") (base-direction ltr) (color (color-rgb 0.000000 0.000000 0.000000)) (justify left) (box-mode fixed) (box-width 17.000000) (box-height 27.000000) (box-unit pixels) gĆ'  dَS( *  :'9>" - +     Shadow       %f  缾𨮵󲺼򕟩~quqxcbcluKUUV`jt>EHQ[dr08BKU^hqz!+5>FPYc !*4>JU#/=H "/9 ",f  缾𨮵󲺽򕠩~qtqxcbcmuKUTV`jt>EIR[dr08BKU^hqz!*5=FPYb  *3=JU#/>G#/: !,f  缾𨮵󳻽򕟩~qtqxbcblvKUUW`js=DIR[er08BKU^hqz!+4=FPZc !*3>JU#/=G"/9 !,frrr  u r u uurrw"u_uruuubr  Background       wneUľмֽ[yϼֺŹл⾾ٸʸĽἾ̷ˠǟܱȫҚީٴ͚ȗ٫çk̷ԪŭߕXɺԪ´Ȫ]Űa^綰ɿc^ŪǧEW̧h^Kp^ʺ]ݛ}عnh^۩.⧣ɹe^᩵覊鴾c^̈́Fc72VgtѾ:Ŵd^^:kޯʭఽf^6Хb^ң˥֏b^Ҩ輪񯏦c^һϩe^Ū߹봢h^ĥrp}h^賫l^Ǎn^īݥהkqs^޹ҽ]r۸u^Olx^: +*)4T  *,'"TfUUVQaUUTX^wnӆhrs[[lU# 8nb] [y"$m R KF d `.̅OF;d ""=dg(!!b~%YN]ɜk1 [ Qahȩj6 Mf4_^aޢԿXQ#{̢:6Yٰk]翴Ѫ߰ҾY^~ήf^rm~~xwٸi^rدʰƫe^^:kwîׯºlj^6k׷_c^ңlȰj]ob^Ҩsӵyc^⽓rڜƭg^s֓mʴxjcik^m޿fTSbh^uzӱssl^qغn^qlɭlqu^ʉ۶_rĆx^پqQmw^: +**4G  *-' "TfUUVQVUUTX^wnՔ}qpkUE-(4S}8*+8sr)?4&[yDF~3i1c_/x98sK?/˒+h5^>m|l^IyfYn^LGبopw^h[xÖu`qc\y^ӣcRmw^:  +**4>  *-' "TfUUVQOUUTX^5ffgghgffghhgghghih(kAfNg?g^h(gf@ffR fL fM fM fL fL fN fN fN fM fMfLfLfJfHfFfEfDiDWC'zZ8$ & mapper-0.8.1.1/images/mapper.png000066400000000000000000000061641325266516600164250ustar00rootroot00000000000000PNG  IHDR szzsRGBbKGD pHYs  tIME 2F IDATXu{U}?{}͝' 0 3 5Z"U]REkcZ),mm}$m5Y+YJdߨm H!D0ay{gsٻ~Ohd28PJFJwBW7s!ĕB<ϻ޲,,mcf2_ B-RX&4u 0ZrhalF;Jc&^.G4:RJ\eB)Mlܸ]wݥl6Vwt2nT (Yt8.JhSd!94v0H_s(ޭxdnxד7w S㺮=o@i*2{jֿC,irkٸvllFS)-"[v>O G͸=CIb`|zQY7+2ԮyS9"mG| n,4Py%Idk4D/cI%xSK =Zt"OWWKg0t<qҗw- }G1$Z^҂ln`٬= xYG4X>ډJg;Nρ#x s GxVa>Vag}Jt(`&Yp"SͿCzA՚ʋh\ 'Oqر7|ڔRtwnB{}+'{"J8'WYJŤI8v/G"l;K1 dB!l!dεĢu|=2 ɓTv=}Gk|#\GkZ_xIl[)_J)sWxl:г13BkeтV>۷늼n&G)q3ɋ\,4 g)^Te4GƸh2?rc ߢCcݶp8yL"3>9}~4A"~1;Hz^=Vp8G$-%X܌e .ضmLӤm?}:v[q1i-m>V^Mtd|UfRtŧBlJ1R^m~͈KСCktuur ?|4662'ﻝQ*?线&yIu4^ZKBb儋qƢoyyi)ZcY>By{a￿o_Ώ~rLՒOߣטӔ͘KVHy+OʹQ8.YGѕ[z-=73931i.IyŋYbuiϵ?אBD ! IzBZ=ebnk+#uvT7>ª&LD&c?-h=\̈́V 0 ` d2V>cd\Tl_1ĺST3k˦/[>N}LYE@^Tf30N}LʥpY#}"{>MlDms7Ķ=҆/\nRi ͩ]1tϙhҤ6Yx8!` TR;ϑ0V7(sRH"M:>N{TG;*;Ny#bbPZ.(ʆ\u5%9jB i2uSb8Z]4Llo( ͸8cD/}#c͸z&+`ɔN-@061MKM4Bx9' !C]48W(X1 d=mu) O SgP\Y'uI{\RDSc# ΃tf1۔C\bh̩j& A*"iȟ *,7ٔ=zsgGʊ̙馦&T>?+oMw%_ 1B,['ZkA@k%Z(|F;ܾ( `0H\ѩT !+!H$: y睞if !>@_QSQVxmێN*җZTii^dɄoi1M۶ŢEuF IENDB`mapper-0.8.1.1/images/minus.png000066400000000000000000000005711325266516600162700ustar00rootroot00000000000000PNG  IHDR szzsRGBbKGD pHYs  tIME $/>bYIDATX=JAYdE423175cx LfBA*F3UϠgdgŤ꽮j$I䟱e&>YVkw_s6Fˎ^_HPruwPԒ^Ar 3t E˽дH>G4h`C a֙5H ".BQ6!J5skIHZGVڣ6ۋ'$IOg~ m\IENDB`mapper-0.8.1.1/images/move-to-gps.png000066400000000000000000000043111325266516600173060ustar00rootroot00000000000000PNG  IHDR szzsBIT|d pHYs  tEXtSoftwarewww.inkscape.org<FIDATXWklTfwwYgͣjZJB[ B(ĀS"!-%WUE][Ub*IӪ*jTb(&"«Pwm{k?58ҕ97sΜ9CiKI\g}8盧n= yXp8`nmm=;7kV݄ʊJ@o;wǧ ri)_]ד•dqF,u7o~Qڂs.@pkׯ}ҥL$f/ܹ ~ܤe3pa&<,ӂ;v !wi麮>H$2hmm}m0W&Fs.\vcghr<lzs_ҾZBIUVUȤ3YW_T59S:f =ilH$\40 6T- 3!-Y}`R)!#t|?oSlR|ϼOMir9 F ^{؈BZO)=Sefݝm/K7W篾~ @}VΪcw{diA0$_1)2@L\@ǝL63Ank8|}Ӟ3.1ϲ'@:<B=`ُQeG~(Ś&*sSآ^km~aC?@Ē=*_#BPJy9E(H_Ȉ?RqwUAU,֫jXJ_ڿAļ܅$w[z{m㽈{4AMsl[sT~VQ:Een#t >q3o:Xt&D*DTt Q>> ~3' O^J!5E)9N: H%\Fhy+òRQ)9&>J*nQ%DF +jXo1өdvnvBDʅ8 ~+X$lvo@}ީeՔI41.cP"1]"%Džm;c+2\m\jмi7\ 8?;V^ܝʤ{DiAF$7w{-% RvI޷t]~B"q.8.v(l߾wIa,\`DJڽ,2-3Yknn溮g۶mKT]ǎ+z:͖-[^zp8| eAYY*8JRTe8^L0=ɱEhiA2"8O0FHÊq dBOӂREQ*sr9>Q*ڽp8(-tKkB Hdi4=8!uB uLP *  .0@(Y`w16vc5sy Ѽɯ?K_h4m/ t]w4McBڡ;!@=p xdB@. 4if 땹\[wؑDQ ^"лpl50CDf>kVݸ~zZ5>vf:IENDB`mapper-0.8.1.1/images/move.png000066400000000000000000000034601325266516600161030ustar00rootroot00000000000000PNG  IHDR szzsRGBIDATX՗Ol\W{jv;\94鐄MJ Tb,"X EuduXTb 4R#Ц#CbvQ!RQRg<334ICKܰ୞={|W'/B1,ʥBm=y:mllTR_U.Kc[F7y{A_oA4z~1q}X8( G^{ />0!]10X|hrb IO{v>61N!^?M@._T' aBJE##M6XlTq?, R}d{_Nwwgbnĉ~ _}gH$1TոR tr[Jgl=Cz-rp6>q_~=Wj;^aĎ%Dq,,-ʸ ͦ8Tmc,eƷH l0{"oޜ].|q?ږ#{&I m/nz~m|<ߣ()d.uҸnMItgɤS(%h~>x:B666d\%mZRB"B 5J H:1r±-Ыf!<0FG1fT[w !0!@#nF 1 @չ ;>"編/1,KTZpv &B&IbXML$$:!z-z5&Ifunhr3g<ĩ"J}=^M}n|EI8LGBYAWY-V)N,ʲm;dҮ::a=MceRWg|ThI~׃߻o_NXء =G4T5yxnG TY8ѨcӗqK=#k@R-@,e'Rwv[z\y J Qޣ\7j٫@=Jp4NIi,,0{ tGn~b/( bLѺQ36Ƥ TdPm LѺV[vrz7, +|qR#Z*yKYJJYH qmݝ]3-m˥B.[R-KWJ}ܐpChic&ʥB0Qu2l$It^5[G''>dz^=63{)3<>y%rmlxlR)eIl[I;T ӝ3+J'/Rcä.Bʲ%rՎKCM㓗ZVZ2a )[ݖ `F'7ɋ++lB?OS[tu9]y=7~w6IENDB`mapper-0.8.1.1/images/new.png000066400000000000000000000017601325266516600157270ustar00rootroot00000000000000PNG  IHDR szzsBIT|dIDATXŗKO[G3sm .L %Rd n*Tj.*FAjC"5ܙŵøT=hf4s,_l'n`m[g/B;`}}\.1c' Cs}w<^x;LOoWxqζj_s)Q߬D"0 cY7!45)H caNj9GXnӅq"lr>VjD0fTm<еJD[< =$>֜#{/U@K8|1ཏUX#u[^csg zb&F 3yƂ zUFdoKqas/@RhBbj9<1jKԣJpݝ_/n 7AFSWrY?]W8i1iqs^E/Ӷ9&P? 2ŋHarfqL9B$7 œ=,7K/@Z ¬LUi&@ו۞ ig"@Jeh):FH<6660f0q4da^jee%bJR8I A՜3![RC0Ҭ,*H*Qz䁻[Y2F譌,UM(cNU\.Ag