Showing preview only (1,234K chars total). Download the full file or copy to clipboard to get everything.
Repository: domlysz/BlenderGIS
Branch: master
Commit: 2add45ffec54
Files: 90
Total size: 1.2 MB
Directory structure:
gitextract_f4gt1_zi/
├── .gitignore
├── LICENSE
├── README.md
├── __init__.py
├── clients/
│ ├── QtMapServiceClient.py
│ └── QtMapServiceClient.ui
├── core/
│ ├── __init__.py
│ ├── basemaps/
│ │ ├── __init__.py
│ │ ├── gpkg.py
│ │ ├── mapservice.py
│ │ └── servicesDefs.py
│ ├── checkdeps.py
│ ├── errors.py
│ ├── georaster/
│ │ ├── __init__.py
│ │ ├── bigtiffwriter.py
│ │ ├── georaster.py
│ │ ├── georef.py
│ │ ├── img_utils.py
│ │ └── npimg.py
│ ├── lib/
│ │ ├── Tyf/
│ │ │ ├── VERSION
│ │ │ ├── __init__.py
│ │ │ ├── decoders.py
│ │ │ ├── encoders.py
│ │ │ ├── gkd.py
│ │ │ ├── ifd.py
│ │ │ ├── tags.py
│ │ │ └── values.py
│ │ ├── imageio/
│ │ │ ├── README.md
│ │ │ ├── __init__.py
│ │ │ ├── core/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── fetching.py
│ │ │ │ ├── findlib.py
│ │ │ │ ├── format.py
│ │ │ │ ├── functions.py
│ │ │ │ ├── request.py
│ │ │ │ └── util.py
│ │ │ ├── freeze.py
│ │ │ ├── plugins/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── _freeimage.py
│ │ │ │ └── freeimage.py
│ │ │ ├── resources/
│ │ │ │ └── shipped_resources_go_here
│ │ │ └── testing.py
│ │ ├── imghdr.py
│ │ ├── shapefile.py
│ │ └── shapefile123.py
│ ├── maths/
│ │ ├── __init__.py
│ │ ├── akima.py
│ │ ├── fillnodata.py
│ │ ├── interpo.py
│ │ └── kmeans1D.py
│ ├── proj/
│ │ ├── __init__.py
│ │ ├── ellps.py
│ │ ├── reproj.py
│ │ ├── srs.py
│ │ ├── srv.py
│ │ └── utm.py
│ ├── settings.json
│ ├── settings.py
│ └── utils/
│ ├── __init__.py
│ ├── bbox.py
│ ├── gradient.py
│ ├── timing.py
│ └── xy.py
├── geoscene.py
├── issue_template.md
├── operators/
│ ├── __init__.py
│ ├── add_camera_exif.py
│ ├── add_camera_georef.py
│ ├── io_export_shp.py
│ ├── io_get_dem.py
│ ├── io_import_asc.py
│ ├── io_import_georaster.py
│ ├── io_import_osm.py
│ ├── io_import_shp.py
│ ├── lib/
│ │ └── osm/
│ │ ├── nominatim.py
│ │ └── overpy/
│ │ ├── __about__.py
│ │ ├── __init__.py
│ │ ├── exception.py
│ │ └── helper.py
│ ├── mesh_delaunay_voronoi.py
│ ├── mesh_earth_sphere.py
│ ├── nodes_terrain_analysis_builder.py
│ ├── nodes_terrain_analysis_reclassify.py
│ ├── object_drop.py
│ ├── utils/
│ │ ├── __init__.py
│ │ ├── bgis_utils.py
│ │ ├── delaunay_voronoi.py
│ │ └── georaster_utils.py
│ └── view3d_mapviewer.py
└── prefs.py
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
__pycache__/
*.py[cod]
*$py.class
================================================
FILE: LICENSE
================================================
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
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.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
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:
<program> Copyright (C) <year> <name of author>
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
<https://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
<https://www.gnu.org/licenses/why-not-lgpl.html>.
================================================
FILE: README.md
================================================
Blender GIS
==========
Blender minimum version required : v2.83
Note : Since 2022, the OpenTopography web service requires an API key. Please register to opentopography.org and request a key. This service is still free.
[Wiki](https://github.com/domlysz/BlenderGIS/wiki/Home) - [FAQ](https://github.com/domlysz/BlenderGIS/wiki/FAQ) - [Quick start guide](https://github.com/domlysz/BlenderGIS/wiki/Quick-start) - [Flowchart](https://raw.githubusercontent.com/wiki/domlysz/blenderGIS/flowchart.jpg)
--------------------
## Functionalities overview
**GIS datafile import :** Import in Blender most commons GIS data format : Shapefile vector, raster image, geotiff DEM, OpenStreetMap xml.
There are a lot of possibilities to create a 3D terrain from geographic data with BlenderGIS, check the [Flowchart](https://raw.githubusercontent.com/wiki/domlysz/blenderGIS/flowchart.jpg) to have an overview.
Exemple : import vector contour lines, create faces by triangulation and put a topographic raster texture.

**Grab geodata directly from the web :** display dynamics web maps inside Blender 3d view, requests for OpenStreetMap data (buildings, roads ...), get true elevation data from the NASA SRTM mission.

**And more :** Manage georeferencing informations of a scene, compute a terrain mesh by Delaunay triangulation, drop objects on a terrain mesh, make terrain analysis using shader nodes, setup new cameras from geotagged photos, setup a camera to render with Blender a new georeferenced raster.
================================================
FILE: __init__.py
================================================
# -*- coding:utf-8 -*-
# ***** GPL LICENSE BLOCK *****
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# All rights reserved.
# ***** GPL LICENSE BLOCK *****
import bpy
bl_info = {
'name': 'BlenderGIS',
'description': 'Various tools for handle geodata',
'author': 'domlysz',
'license': 'GPL',
'deps': '',
'version': (2, 2, 14),
'blender': (2, 83, 0),
'location': 'View3D > Tools > GIS',
'warning': '',
'wiki_url': 'https://github.com/domlysz/BlenderGIS/wiki',
'tracker_url': 'https://github.com/domlysz/BlenderGIS/issues',
'link': '',
'support': 'COMMUNITY',
'category': '3D View'
}
class BlenderVersionError(Exception):
pass
if bl_info['blender'] > bpy.app.version:
raise BlenderVersionError(f"This addon requires Blender >= {bl_info['blender']}")
if bpy.app.version[0] > 5: #prevent breaking changes on major release
raise BlenderVersionError(f"This addon is not tested against Blender {bpy.app.version[0]}.x breaking changes")
#Modules
CAM_GEOPHOTO = True
CAM_GEOREF = True
EXPORT_SHP = True
GET_DEM = True
IMPORT_GEORASTER = True
IMPORT_OSM = True
IMPORT_SHP = True
IMPORT_ASC = True
DELAUNAY = True
TERRAIN_NODES = True
TERRAIN_RECLASS = True
BASEMAPS = True
DROP = True
EARTH_SPHERE = True
import os, sys, tempfile
from datetime import datetime
def getAppData():
home = os.path.expanduser('~')
loc = os.path.join(home, '.bgis')
if not os.path.exists(loc):
os.mkdir(loc)
return loc
APP_DATA = getAppData()
import logging
from logging.handlers import RotatingFileHandler
#temporary set log level, will be overriden reading addon prefs
#logsFormat = "%(levelname)s:%(name)s:%(lineno)d:%(message)s"
logsFormat = '{levelname}:{name}:{lineno}:{message}'
logsFileName = 'bgis.log'
try:
#logsFilePath = os.path.join(os.path.dirname(__file__), logsFileName)
logsFilePath = os.path.join(APP_DATA, logsFileName)
#logging.basicConfig(level=logging.getLevelName('DEBUG'), format=logsFormat, style='{', filename=logsFilePath, filemode='w')
logHandler = RotatingFileHandler(logsFilePath, mode='a', maxBytes=512000, backupCount=1)
except PermissionError:
#logsFilePath = os.path.join(bpy.app.tempdir, logsFileName)
logsFilePath = os.path.join(tempfile.gettempdir(), logsFileName)
logHandler = RotatingFileHandler(logsFilePath, mode='a', maxBytes=512000, backupCount=1)
logHandler.setFormatter(logging.Formatter(logsFormat, style='{'))
logger = logging.getLogger(__name__)
logger.addHandler(logHandler)
logger.setLevel(logging.DEBUG)
logger.info('###### Starting new Blender session : {}'.format(datetime.now().strftime('%Y-%m-%d %H:%M:%S')))
def _excepthook(exc_type, exc_value, exc_traceback):
if 'BlenderGIS' in exc_traceback.tb_frame.f_code.co_filename:
logger.error("Uncaught exception", exc_info=(exc_type, exc_value, exc_traceback))
sys.__excepthook__(exc_type, exc_value, exc_traceback)
sys.excepthook = _excepthook #warn, this is a global variable, can be overrided by another addon
####
'''
Workaround for `sys.excepthook` thread
https://stackoverflow.com/questions/1643327/sys-excepthook-and-threading
'''
import threading
init_original = threading.Thread.__init__
def init(self, *args, **kwargs):
init_original(self, *args, **kwargs)
run_original = self.run
def run_with_except_hook(*args2, **kwargs2):
try:
run_original(*args2, **kwargs2)
except Exception:
sys.excepthook(*sys.exc_info())
self.run = run_with_except_hook
threading.Thread.__init__ = init
####
import ssl
if (not os.environ.get('PYTHONHTTPSVERIFY', '') and
getattr(ssl, '_create_unverified_context', None)):
ssl._create_default_https_context = ssl._create_unverified_context
#from .core.checkdeps import HAS_GDAL, HAS_PYPROJ, HAS_PIL, HAS_IMGIO
from .core.settings import settings
#Import all modules which contains classes that must be registed (classes derived from bpy.types.*)
from . import prefs
from . import geoscene
if CAM_GEOPHOTO:
from .operators import add_camera_exif
if CAM_GEOREF:
from .operators import add_camera_georef
if EXPORT_SHP:
from .operators import io_export_shp
if GET_DEM:
from .operators import io_get_dem
if IMPORT_GEORASTER:
from .operators import io_import_georaster
if IMPORT_OSM:
from .operators import io_import_osm
if IMPORT_SHP:
from .operators import io_import_shp
if IMPORT_ASC:
from .operators import io_import_asc
if DELAUNAY:
from .operators import mesh_delaunay_voronoi
if TERRAIN_NODES:
from .operators import nodes_terrain_analysis_builder
if TERRAIN_RECLASS:
from .operators import nodes_terrain_analysis_reclassify
if BASEMAPS:
from .operators import view3d_mapviewer
if DROP:
from .operators import object_drop
if EARTH_SPHERE:
from .operators import mesh_earth_sphere
import bpy.utils.previews as iconsLib
icons_dict = {}
class BGIS_OT_logs(bpy.types.Operator):
bl_idname = "bgis.logs"
bl_description = 'Display BlenderGIS logs'
bl_label = "Logs"
def execute(self, context):
if logsFileName in bpy.data.texts:
logs = bpy.data.texts[logsFileName]
else:
logs = bpy.data.texts.load(logsFilePath)
bpy.ops.screen.area_split(direction='VERTICAL', factor=0.5)
area = bpy.context.area
area.type = 'TEXT_EDITOR'
area.spaces[0].text = logs
bpy.ops.text.reload()
return {'FINISHED'}
class VIEW3D_MT_menu_gis_import(bpy.types.Menu):
bl_label = "Import"
def draw(self, context):
if IMPORT_SHP:
self.layout.operator("importgis.shapefile_file_dialog", icon_value=icons_dict["shp"].icon_id, text='Shapefile (.shp)')
if IMPORT_GEORASTER:
self.layout.operator("importgis.georaster", icon_value=icons_dict["raster"].icon_id, text="Georeferenced raster (.tif .jpg .jp2 .png)")
if IMPORT_OSM:
self.layout.operator("importgis.osm_file", icon_value=icons_dict["osm"].icon_id, text="Open Street Map xml (.osm)")
if IMPORT_ASC:
self.layout.operator('importgis.asc_file', icon_value=icons_dict["asc"].icon_id, text="ESRI ASCII Grid (.asc)")
class VIEW3D_MT_menu_gis_export(bpy.types.Menu):
bl_label = "Export"
def draw(self, context):
if EXPORT_SHP:
self.layout.operator('exportgis.shapefile', text="Shapefile (.shp)", icon_value=icons_dict["shp"].icon_id)
class VIEW3D_MT_menu_gis_webgeodata(bpy.types.Menu):
bl_label = "Web geodata"
def draw(self, context):
if BASEMAPS:
self.layout.operator("view3d.map_start", icon_value=icons_dict["layers"].icon_id)
if IMPORT_OSM:
self.layout.operator("importgis.osm_query", icon_value=icons_dict["osm"].icon_id)
if GET_DEM:
self.layout.operator("importgis.dem_query", icon_value=icons_dict["raster"].icon_id)
class VIEW3D_MT_menu_gis_camera(bpy.types.Menu):
bl_label = "Camera"
def draw(self, context):
if CAM_GEOREF:
self.layout.operator("camera.georender", icon_value=icons_dict["georefCam"].icon_id, text='Georender')
if CAM_GEOPHOTO:
self.layout.operator("camera.geophotos", icon_value=icons_dict["exifCam"].icon_id, text='Geophotos')
self.layout.operator("camera.geophotos_setactive", icon='FILE_REFRESH')
class VIEW3D_MT_menu_gis_mesh(bpy.types.Menu):
bl_label = "Mesh"
def draw(self, context):
if DELAUNAY:
self.layout.operator("tesselation.delaunay", icon_value=icons_dict["delaunay"].icon_id, text='Delaunay')
self.layout.operator("tesselation.voronoi", icon_value=icons_dict["voronoi"].icon_id, text='Voronoi')
if EARTH_SPHERE:
self.layout.operator("earth.sphere", icon="WORLD", text='lonlat to sphere')
#self.layout.operator("earth.curvature", icon="SPHERECURVE", text='Earth curvature correction')
self.layout.operator("earth.curvature", icon_value=icons_dict["curve"].icon_id, text='Earth curvature correction')
class VIEW3D_MT_menu_gis_object(bpy.types.Menu):
bl_label = "Object"
def draw(self, context):
if DROP:
self.layout.operator("object.drop", icon_value=icons_dict["drop"].icon_id, text='Drop')
class VIEW3D_MT_menu_gis_nodes(bpy.types.Menu):
bl_label = "Nodes"
def draw(self, context):
if TERRAIN_NODES:
self.layout.operator("analysis.nodes", icon_value=icons_dict["terrain"].icon_id, text='Terrain analysis')
class VIEW3D_MT_menu_gis(bpy.types.Menu):
bl_label = "GIS"
# Set the menu operators and draw functions
def draw(self, context):
layout = self.layout
layout.operator("bgis.pref_show", icon='PREFERENCES')
layout.separator()
layout.menu('VIEW3D_MT_menu_gis_webgeodata', icon="URL")
layout.menu('VIEW3D_MT_menu_gis_import', icon='IMPORT')
layout.menu('VIEW3D_MT_menu_gis_export', icon='EXPORT')
layout.menu('VIEW3D_MT_menu_gis_camera', icon='CAMERA_DATA')
layout.menu('VIEW3D_MT_menu_gis_mesh', icon='MESH_DATA')
layout.menu('VIEW3D_MT_menu_gis_object', icon='CUBE')
layout.menu('VIEW3D_MT_menu_gis_nodes', icon='NODETREE')
layout.separator()
layout.operator("bgis.logs", icon='TEXT')
menus = [
VIEW3D_MT_menu_gis,
VIEW3D_MT_menu_gis_webgeodata,
VIEW3D_MT_menu_gis_import,
VIEW3D_MT_menu_gis_export,
VIEW3D_MT_menu_gis_camera,
VIEW3D_MT_menu_gis_mesh,
VIEW3D_MT_menu_gis_object,
VIEW3D_MT_menu_gis_nodes
]
def add_gis_menu(self, context):
if context.mode == 'OBJECT':
self.layout.menu('VIEW3D_MT_menu_gis')
def register():
#icons
global icons_dict
icons_dict = iconsLib.new()
icons_dir = os.path.join(os.path.dirname(__file__), "icons")
for icon in os.listdir(icons_dir):
name, ext = os.path.splitext(icon)
icons_dict.load(name, os.path.join(icons_dir, icon), 'IMAGE')
#operators
prefs.register()
geoscene.register()
for menu in menus:
try:
bpy.utils.register_class(menu)
except ValueError as e:
logger.warning('{} is already registered, now unregister and retry... '.format(menu))
bpy.utils.unregister_class(menu)
bpy.utils.register_class(menu)
bpy.utils.register_class(BGIS_OT_logs)
if BASEMAPS:
view3d_mapviewer.register()
if IMPORT_GEORASTER:
io_import_georaster.register()
if IMPORT_SHP:
io_import_shp.register()
if EXPORT_SHP:
io_export_shp.register()
if IMPORT_OSM:
io_import_osm.register()
if IMPORT_ASC:
io_import_asc.register()
if DELAUNAY:
mesh_delaunay_voronoi.register()
if DROP:
object_drop.register()
if GET_DEM:
io_get_dem.register()
if CAM_GEOPHOTO:
add_camera_exif.register()
if CAM_GEOREF:
add_camera_georef.register()
if TERRAIN_NODES:
nodes_terrain_analysis_builder.register()
if TERRAIN_RECLASS:
nodes_terrain_analysis_reclassify.register()
if EARTH_SPHERE:
mesh_earth_sphere.register()
#menus
bpy.types.VIEW3D_MT_editor_menus.append(add_gis_menu)
#shortcuts
if not bpy.app.background: #no ui when running as background
wm = bpy.context.window_manager
kc = wm.keyconfigs.active
if '3D View' in kc.keymaps:
km = kc.keymaps['3D View']
if BASEMAPS:
kmi = km.keymap_items.new(idname='view3d.map_start', type='NUMPAD_ASTERIX', value='PRESS')
#Setup prefs
preferences = bpy.context.preferences.addons[__package__].preferences
logger.setLevel(logging.getLevelName(preferences.logLevel)) #will affect all child logger
#update core settings according to addon prefs
settings.proj_engine = preferences.projEngine
settings.img_engine = preferences.imgEngine
settings.maptiler_api_key = preferences.maptiler_api_key
def unregister():
global icons_dict
iconsLib.remove(icons_dict)
if not bpy.app.background: #no ui when running as background
wm = bpy.context.window_manager
if '3D View' in wm.keyconfigs.active.keymaps:
km = wm.keyconfigs.active.keymaps['3D View']
if BASEMAPS:
if 'view3d.map_start' in km.keymap_items:
kmi = km.keymap_items.remove(km.keymap_items['view3d.map_start'])
bpy.types.VIEW3D_MT_editor_menus.remove(add_gis_menu)
for menu in menus:
bpy.utils.unregister_class(menu)
bpy.utils.unregister_class(BGIS_OT_logs)
prefs.unregister()
geoscene.unregister()
if BASEMAPS:
view3d_mapviewer.unregister()
if IMPORT_GEORASTER:
io_import_georaster.unregister()
if IMPORT_SHP:
io_import_shp.unregister()
if EXPORT_SHP:
io_export_shp.unregister()
if IMPORT_OSM:
io_import_osm.unregister()
if IMPORT_ASC:
io_import_asc.unregister()
if DELAUNAY:
mesh_delaunay_voronoi.unregister()
if DROP:
object_drop.unregister()
if GET_DEM:
io_get_dem.unregister()
if CAM_GEOPHOTO:
add_camera_exif.unregister()
if CAM_GEOREF:
add_camera_georef.unregister()
if TERRAIN_NODES:
nodes_terrain_analysis_builder.unregister()
if TERRAIN_RECLASS:
nodes_terrain_analysis_reclassify.unregister()
if EARTH_SPHERE:
mesh_earth_sphere.unregister()
if __name__ == "__main__":
register()
================================================
FILE: clients/QtMapServiceClient.py
================================================
# -*- coding:utf-8 -*-
import sys, os, time
import sys, os
sys.path.append(os.path.abspath('..'))
from PyQt4 import QtGui, QtCore, uic
import threading
import tempfile
from core.basemaps import GRIDS, SOURCES, MapService, BBoxRequest, BBoxRequestMZ
from core.lib import shapefile
from core.proj import reprojPts
from xml.etree import ElementTree as etree
import re
#on the fly ui dialogs compilation
mainForm, mainBase = uic.loadUiType('QtMapServiceClient.ui')
projSysLst={
2154 : "Lambert 93",
3942 : "Lambert CC42",
3943 : "Lambert CC43",
3944 : "Lambert CC44",
3945 : "Lambert CC45",
3946 :"Lambert CC46",
3947 : "Lambert CC47",
3948 : "Lambert CC48",
3949 : "Lambert CC49",
3950 : "Lambert CC50"
}
def getShpExtent(pathShp):
shp = shapefile.Reader(pathShp)
shapes = shp.shapes() #we expect only one feature !
if len(shapes) != 1:
return
else:
extent = shapes[0].bbox #xmin, ymin, xmax, ymax
return extent
def getKmlExtent(kmlFile, crs2):
def formatCoor(coorText):
coorText = coorText.strip()
coordinates = []
for elem in str(coorText).split(" "):
coordinates.append(tuple(map(float, elem.split(","))))
return coordinates
def namespace(element):
m = re.match('\{.*\}', element.tag)
return m.group(0) if m else ''
root = etree.parse(kmlFile).getroot()
ns = namespace(root)
polygons = []
for poly in root.iter(ns+"Polygon"):
for attributes in poly.iter(ns+"coordinates"):
polygons.append(formatCoor(attributes.text))
if len(polygons) != 1:
return
else:
pts = polygons[0] #first feature
pts = reprojPts(4326, crs2, pts)
xmin = min([pt[0] for pt in pts])
ymin = min([pt[1] for pt in pts])
xmax = max([pt[0] for pt in pts])
ymax = max([pt[1] for pt in pts])
extent = [xmin, ymin, xmax, ymax]
return list(map(round,extent))
class QtMapServiceClient(QtGui.QMainWindow, mainForm):
def __init__(self):
#UI init
QtGui.QMainWindow.__init__(self)
self.setupUi(self)
#
for k, v in SOURCES.items():
self.cbProvider.addItem(v['name'], k) #text, data
self.extent = None
self.inCacheFolder.setText(tempfile.gettempdir())
self.btCacheFolder.clicked.connect(self.setCacheFolder)
self.btBrowseOutFolder.clicked.connect(self.setInOutFolder)
self.btOkMosaic.clicked.connect(self.uiDoProcess)
self.btCancel.clicked.connect(self.uiDoCancelThread)
self.btExtentShp.clicked.connect(self.uiDoReadShpExtent)
self.cbProvider.currentIndexChanged.connect(self.uiDoUpdateProvider)
self.cbLayer.currentIndexChanged.connect(self.uiDoUpdateScales)
self.cbZoom.currentIndexChanged.connect(self.uiDoUpdateRes)
self.chkJPG.stateChanged.connect(self.uiUpdateMaskOption)
self.chkSeedCache.stateChanged.connect(self.uiUpdateSeedOption)
#
self.uiDoUpdateProvider()
self.inVectorFile.setText("*.kml *.shp...")
@property
def provider(self):
k = self.cbProvider.itemData(self.cbProvider.currentIndex())
cacheFolder = str(self.inCacheFolder.text())
return MapService(k, cacheFolder)
@property
def layer(self):
return self.cbLayer.itemData(self.cbLayer.currentIndex())
@property
def outProj(self):
return self.cbOutProj.itemData(self.cbOutProj.currentIndex())
@property
def zoom(self):
z = self.cbZoom.itemData(self.cbZoom.currentIndex())
if z is not None:
return int(z)
@property
def rq(self):
if self.extent is not None and self.zoom is not None:
rq = self.provider.srcTms.bboxRequest(self.extent, self.zoom)
return rq
def uiUpdateMaskOption(self):
if self.chkJPG.isChecked():
self.chkMask.setEnabled(True)
else:
self.chkMask.setEnabled(False)
def uiUpdateSeedOption(self):
if self.chkSeedCache.isChecked():
self.chkRecurseUpZoomLevels.setEnabled(True)
self.chkReproj.setEnabled(False)
self.cbOutProj.setEnabled(False)
self.chkBuildOverview.setEnabled(False)
self.chkJPG.setEnabled(False)
self.chkMask.setEnabled(False)
self.chkBigtiff.setEnabled(False)
self.inName.setEnabled(False)
self.inOutFolder.setEnabled(False)
self.btBrowseOutFolder.setEnabled(False)
else:
self.chkRecurseUpZoomLevels.setEnabled(False)
self.chkReproj.setEnabled(True)
self.cbOutProj.setEnabled(True)
self.chkBuildOverview.setEnabled(True)
self.chkJPG.setEnabled(True)
self.chkMask.setEnabled(True)
self.chkBigtiff.setEnabled(True)
self.inName.setEnabled(True)
self.inOutFolder.setEnabled(True)
self.btBrowseOutFolder.setEnabled(True)
def uiDoUpdateProvider(self):
'''Triggered when cbProvider idx change'''
#clear comboboxes
self.cbLayer.clear()
self.cbOutProj.clear()
#seed layers combobox
for layerKey, layer in self.provider.layers.items():
self.cbLayer.addItem(layer.name, layerKey)
#reproj sys
for k, v in projSysLst.items():
self.cbOutProj.addItem(v, k)
self.cbOutProj.setCurrentIndex(self.cbOutProj.findData(2154))
#
self.updateExtent()
def uiDoUpdateScales(self):
'''Triggered when cbLayer idx change'''
if self.layer is not None:
lay = self.provider.layers[self.layer]
self.cbZoom.clear()
for z in range(lay.zmin, lay.zmax):
self.cbZoom.addItem(str(z), str(z))
def uiDoUpdateRes(self, zoomLevel):
'''Triggered when cbZoom idx change'''
if self.rq is not None:
self.lbRes.setText(str(round(self.rq.res, 2))+" m/px")
self.uiDoRequestInfos()
def uiDoReadShpExtent(self):
path = str(self.setOpenFileName('Shapefile (*.shp *.kml)'))
self.inVectorFile.setText(path)
self.updateExtent()
def updateExtent(self):
path = self.inVectorFile.text()
if not os.path.exists(path):
pass
else:
ext = path[-3:]
if ext == 'shp':
self.extent = getShpExtent(path) #xmin, ymin, xmax, ymax
elif ext == 'kml':
self.extent = getKmlExtent(path, self.provider.srcTms.CRS)
if not self.extent:
QtGui.QMessageBox.information(self, "Cannot read vector extent file", "This file must contains only one polygon")
return
#
self.uiDoRequestInfos()
self.inVectorFile.setText(path)
def uiDoRequestInfos(self):
if self.rq is not None:
tileSize = self.rq.tileSize
res = self.rq.res
cols, rows = self.rq.nbTilesX, self.rq.nbTilesY
n = self.rq.nbTiles
#rqTiles = rq.tiles #[(x,y,z)]
#
xmin, ymin, xmax, ymax = self.extent
dstX = xmax-xmin
dstY = ymax-ymin
txtEmprise = str(round(dstX)) + " x " + str(round(dstY)) + " m"
#
nbPx = int(cols * tileSize * rows * tileSize)
if nbPx > 1000000:
txtNbPx = str(int(nbPx/1000000)) + " Mpix"
else:
txtNbPx = str(nbPx) + " pix"
#
txtNbTiles = str(n) + " tile(s)"
#
resultStr = txtNbTiles + " (" + str(cols) + 'x' + str(rows) + ") - " + txtNbPx + " - " + txtEmprise
self.requestInfos.setText(resultStr)
def uiDoProcess(self):
outFolder = str(self.inOutFolder.text())
nameTemplate = str(self.inName.text())
cacheFolder = str(self.inCacheFolder.text())
if not self.chkSeedCache:
if not os.path.exists(outFolder):
QtGui.QMessageBox.information(self, "Error", "Output folder does not exists")
return
if not nameTemplate:
QtGui.QMessageBox.information(self, "Error", "Basename is not defined")
return
if not os.path.exists(cacheFolder):
QtGui.QMessageBox.information(self, "Error", "Cache folder does not exists")
return
#Options
reproj = self.chkReproj.isChecked()
outProj = self.cbOutProj.itemData(self.cbOutProj.currentIndex())
reprojOptions = (reproj, outProj)
buildOvv = self.chkBuildOverview.isChecked()
jpgInTiff = self.chkJPG.isChecked()
mask = self.chkMask.isChecked()
bigTiff = self.chkBigtiff.isChecked()
#Start map service
self.btOkMosaic.setEnabled(False)
if self.chkReproj:
outCRS = self.outProj
else:
outCRS = None
outFile = outFolder + os.sep + nameTemplate + '.tif'
seedOnly = self.chkSeedCache.isChecked()
recurseUpZoomLevels = self.chkRecurseUpZoomLevels.isChecked()
self.thread = DownloadTiles(self.provider, self.layer, self.extent, self.zoom, outFile, outCRS, seedOnly, recurseUpZoomLevels)
self.thread.finished.connect(self.uiProcessFinished)
self.thread.terminated.connect(self.uiProcessFinished)
self.thread.updateBar1.connect(self.uiDoUpdateBar1)
self.thread.configBar1.connect(self.uiDoConfigBar1)
self.thread.processInfo.connect(self.updateProcessInfo)
self.thread.start()
def uiProcessFinished(self):
self.updateUi()
QtGui.QMessageBox.information(self, "Info", "Finished")
def uiDoCancelThread(self):
try:
self.thread.cancel()
except:
pass
def uiSendQuestion(self, titre, msg):
choice = QtGui.QMessageBox.question(self, titre, msg, QtGui.QMessageBox.Yes, QtGui.QMessageBox.No)
if choice == QtGui.QMessageBox.Yes:
return True
else:
return False
def updateUi(self):
self.btOkMosaic.setEnabled(True)
def uiDoUpdateBar1(self, num):
self.pBar1.setValue(num)
def uiDoConfigBar1(self, nb):
self.pBar1.setMinimum(0)
self.pBar1.setMaximum(nb)
def updateProcessInfo(self, txt):
self.processInfo.setText(txt)
#Set des inputbox
def setInOutFolder(self):
path = self.setExistingDirectory()
if path:
self.inOutFolder.setText(path)
def setCacheFolder(self):
path = self.setExistingDirectory()
if path:
self.inCacheFolder.setText(path)
def setInFolder(self):
path = self.setExistingDirectory()
if path:
self.inVectorFile.setText(path)
#Standard dialogs
def setOpenFileName(self, filtre):
fileName = QtGui.QFileDialog.getOpenFileName(self, "Select file", QtCore.QDir.rootPath(),filtre)
return QtCore.QDir.toNativeSeparators(fileName)
def setExistingDirectory(self):
directory = QtGui.QFileDialog.getExistingDirectory(self, "Select directory", QtCore.QDir.rootPath(), QtGui.QFileDialog.ShowDirsOnly)
return QtCore.QDir.toNativeSeparators(directory)
def setSaveFileName(self):
saveFileName = QtGui.QFileDialog.getSaveFileName(self, "Save file", QtCore.QDir.rootPath())
return QtCore.QDir.toNativeSeparators(saveFileName)
class DownloadTiles(QtCore.QThread):
#custum signals
configBar1 = QtCore.pyqtSignal(int)
updateBar1 = QtCore.pyqtSignal(int)
processInfo = QtCore.pyqtSignal(str)
def __init__(self, srv, layer, extent, zoom, outFile, outCRS, seedOnly, recurseUpZoomLevels):
QtCore.QThread.__init__(self, None)
self.srv = srv
self.layer = layer
self.extent = extent
self.outFile = outFile
self.outCRS = outCRS
self.seedOnly = seedOnly
if recurseUpZoomLevels and seedOnly:
self.zoom = list(range(self.srv.layers[self.layer].zmin, zoom+1))
self.rq = BBoxRequestMZ(self.srv.srcTms, self.extent, self.zoom)
print(self.rq.nbTiles, self.srv.srcTms.bboxRequest(self.extent, zoom).nbTiles)
else:
self.zoom = zoom
self.rq = self.srv.srcTms.bboxRequest(self.extent, self.zoom)
def run(self):
self.srv.start()
self.configBar1.emit(self.rq.nbTiles)
#self.configBar1.emit(0) #alternative moves
if self.seedOnly:
thread = threading.Thread(target=self.seedCache)
else:
thread = threading.Thread(target=self.getImage)
#thread.setDaemon(True) #daemon threads will die when the main non-daemon thread have exited.
thread.start()
while thread.isAlive():
time.sleep(0.05)
self.processInfo.emit(self.srv.report)
self.updateBar1.emit(self.srv.cptTiles)
self.srv.stop()
def seedCache(self):
self.srv.seedCache(self.layer, self.extent, self.zoom, toDstGrid=False)
def getImage(self):
self.srv.getImage(self.layer, self.extent, self.zoom, path=self.outFile, bigTiff=True, outCRS=self.outCRS, toDstGrid=False)
def cancel(self):
self.srv.stop()
'''
#no need for pausing because downloading tiles are saved in cache,
#so restarting an aborted process will reuse existing tiles
def pause(self):
self.srv.pause()
def resume(self):
self.srv.resume()
'''
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
window = QtMapServiceClient()
window.show()
sys.exit(app.exec_())
================================================
FILE: clients/QtMapServiceClient.ui
================================================
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>QtMapService</class>
<widget class="QDialog" name="QtMapService">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>350</width>
<height>380</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>350</width>
<height>380</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>350</width>
<height>380</height>
</size>
</property>
<property name="windowTitle">
<string>MapService Qt Client</string>
</property>
<widget class="QProgressBar" name="pBar1">
<property name="geometry">
<rect>
<x>8</x>
<y>316</y>
<width>291</width>
<height>23</height>
</rect>
</property>
<property name="font">
<font>
<family>Liberation Sans</family>
</font>
</property>
<property name="statusTip">
<string/>
</property>
<property name="value">
<number>0</number>
</property>
</widget>
<widget class="QLineEdit" name="inName">
<property name="geometry">
<rect>
<x>106</x>
<y>252</y>
<width>135</width>
<height>20</height>
</rect>
</property>
<property name="font">
<font>
<family>Liberation Sans</family>
</font>
</property>
</widget>
<widget class="QPushButton" name="btOkMosaic">
<property name="geometry">
<rect>
<x>300</x>
<y>310</y>
<width>41</width>
<height>35</height>
</rect>
</property>
<property name="font">
<font>
<family>Liberation Sans</family>
</font>
</property>
<property name="text">
<string>Go</string>
</property>
<property name="autoDefault">
<bool>true</bool>
</property>
<property name="default">
<bool>false</bool>
</property>
</widget>
<widget class="QLabel" name="label_60">
<property name="geometry">
<rect>
<x>128</x>
<y>95</y>
<width>81</width>
<height>19</height>
</rect>
</property>
<property name="font">
<font>
<family>Liberation Sans</family>
<underline>false</underline>
</font>
</property>
<property name="text">
<string>Zoom</string>
</property>
</widget>
<widget class="QComboBox" name="cbZoom">
<property name="geometry">
<rect>
<x>164</x>
<y>91</y>
<width>77</width>
<height>25</height>
</rect>
</property>
<property name="font">
<font>
<family>Liberation Sans</family>
</font>
</property>
</widget>
<widget class="QPushButton" name="btCancel">
<property name="geometry">
<rect>
<x>300</x>
<y>354</y>
<width>41</width>
<height>21</height>
</rect>
</property>
<property name="font">
<font>
<family>Liberation Sans</family>
</font>
</property>
<property name="text">
<string>Stop</string>
</property>
<property name="autoDefault">
<bool>true</bool>
</property>
<property name="default">
<bool>false</bool>
</property>
</widget>
<widget class="QPushButton" name="btBrowseOutFolder">
<property name="geometry">
<rect>
<x>302</x>
<y>280</y>
<width>31</width>
<height>21</height>
</rect>
</property>
<property name="font">
<font>
<family>Liberation Sans</family>
</font>
</property>
<property name="text">
<string>...</string>
</property>
</widget>
<widget class="QPushButton" name="btExtentShp">
<property name="geometry">
<rect>
<x>305</x>
<y>63</y>
<width>29</width>
<height>21</height>
</rect>
</property>
<property name="font">
<font>
<family>Liberation Sans</family>
</font>
</property>
<property name="text">
<string>...</string>
</property>
</widget>
<widget class="QLabel" name="label_55">
<property name="geometry">
<rect>
<x>6</x>
<y>280</y>
<width>101</width>
<height>20</height>
</rect>
</property>
<property name="font">
<font>
<family>Liberation Sans</family>
<underline>true</underline>
</font>
</property>
<property name="text">
<string>Output folder</string>
</property>
</widget>
<widget class="QLineEdit" name="inOutFolder">
<property name="geometry">
<rect>
<x>105</x>
<y>280</y>
<width>195</width>
<height>20</height>
</rect>
</property>
<property name="font">
<font>
<family>Liberation Sans</family>
</font>
</property>
</widget>
<widget class="QComboBox" name="cbLayer">
<property name="geometry">
<rect>
<x>5</x>
<y>92</y>
<width>117</width>
<height>25</height>
</rect>
</property>
<property name="font">
<font>
<family>Liberation Sans</family>
</font>
</property>
</widget>
<widget class="QLineEdit" name="processInfo">
<property name="enabled">
<bool>false</bool>
</property>
<property name="geometry">
<rect>
<x>8</x>
<y>354</y>
<width>286</width>
<height>19</height>
</rect>
</property>
<property name="font">
<font>
<family>Liberation Sans</family>
</font>
</property>
</widget>
<widget class="QLabel" name="label_69">
<property name="geometry">
<rect>
<x>6</x>
<y>254</y>
<width>83</width>
<height>16</height>
</rect>
</property>
<property name="font">
<font>
<family>Liberation Sans</family>
<underline>true</underline>
</font>
</property>
<property name="text">
<string>Basename</string>
</property>
</widget>
<widget class="QCheckBox" name="chkReproj">
<property name="geometry">
<rect>
<x>11</x>
<y>171</y>
<width>101</width>
<height>21</height>
</rect>
</property>
<property name="font">
<font>
<family>Liberation Sans</family>
</font>
</property>
<property name="text">
<string>Reprojection</string>
</property>
<property name="checked">
<bool>false</bool>
</property>
</widget>
<widget class="QComboBox" name="cbOutProj">
<property name="geometry">
<rect>
<x>112</x>
<y>168</y>
<width>153</width>
<height>23</height>
</rect>
</property>
<property name="font">
<font>
<family>Liberation Sans</family>
</font>
</property>
</widget>
<widget class="QLineEdit" name="inVectorFile">
<property name="geometry">
<rect>
<x>82</x>
<y>63</y>
<width>219</width>
<height>20</height>
</rect>
</property>
<property name="font">
<font>
<family>Liberation Sans</family>
</font>
</property>
</widget>
<widget class="QLineEdit" name="requestInfos">
<property name="enabled">
<bool>false</bool>
</property>
<property name="geometry">
<rect>
<x>5</x>
<y>124</y>
<width>331</width>
<height>19</height>
</rect>
</property>
<property name="font">
<font>
<family>Liberation Sans</family>
</font>
</property>
</widget>
<widget class="QLineEdit" name="lbRes">
<property name="enabled">
<bool>false</bool>
</property>
<property name="geometry">
<rect>
<x>248</x>
<y>92</y>
<width>85</width>
<height>23</height>
</rect>
</property>
<property name="font">
<font>
<family>Liberation Sans</family>
</font>
</property>
</widget>
<widget class="QLabel" name="label_61">
<property name="geometry">
<rect>
<x>8</x>
<y>10</y>
<width>55</width>
<height>20</height>
</rect>
</property>
<property name="font">
<font>
<family>Liberation Sans</family>
<underline>true</underline>
</font>
</property>
<property name="text">
<string>Provider</string>
</property>
</widget>
<widget class="QComboBox" name="cbProvider">
<property name="geometry">
<rect>
<x>82</x>
<y>8</y>
<width>147</width>
<height>25</height>
</rect>
</property>
<property name="font">
<font>
<family>Liberation Sans</family>
</font>
</property>
</widget>
<widget class="QLabel" name="label_59">
<property name="geometry">
<rect>
<x>8</x>
<y>62</y>
<width>71</width>
<height>20</height>
</rect>
</property>
<property name="font">
<font>
<family>Liberation Sans</family>
<underline>true</underline>
</font>
</property>
<property name="text">
<string>Extent file</string>
</property>
</widget>
<widget class="QLabel" name="label_70">
<property name="geometry">
<rect>
<x>8</x>
<y>198</y>
<width>153</width>
<height>16</height>
</rect>
</property>
<property name="font">
<font>
<family>Liberation Sans</family>
<underline>true</underline>
</font>
</property>
<property name="text">
<string>Tiff format options</string>
</property>
</widget>
<widget class="QWidget" name="layoutWidget">
<property name="geometry">
<rect>
<x>11</x>
<y>216</y>
<width>337</width>
<height>33</height>
</rect>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QCheckBox" name="chkBuildOverview">
<property name="font">
<font>
<family>Liberation Sans</family>
</font>
</property>
<property name="toolTip">
<string>Build pyramids for speed up display performance</string>
</property>
<property name="text">
<string>Pyramids</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="chkJPG">
<property name="font">
<font>
<family>Liberation Sans</family>
</font>
</property>
<property name="toolTip">
<string>Use JPEG compression (destructive, no alpha channel)</string>
</property>
<property name="text">
<string>Comp. jpeg</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="chkMask">
<property name="font">
<font>
<family>Liberation Sans</family>
</font>
</property>
<property name="toolTip">
<string>Use an internal mask to store alpha and nodata values (useful with jpeg compression)</string>
</property>
<property name="text">
<string>Mask</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="chkBigtiff">
<property name="font">
<font>
<family>Liberation Sans</family>
</font>
</property>
<property name="toolTip">
<string>Allows creating raster greater than 4GB</string>
</property>
<property name="text">
<string>Bigtiff</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QCheckBox" name="chkSeedCache">
<property name="geometry">
<rect>
<x>11</x>
<y>146</y>
<width>135</width>
<height>21</height>
</rect>
</property>
<property name="font">
<font>
<family>Liberation Sans</family>
</font>
</property>
<property name="text">
<string>Only seed cache</string>
</property>
<property name="checked">
<bool>false</bool>
</property>
</widget>
<widget class="QLineEdit" name="inCacheFolder">
<property name="geometry">
<rect>
<x>82</x>
<y>39</y>
<width>217</width>
<height>20</height>
</rect>
</property>
<property name="font">
<font>
<family>Liberation Sans</family>
</font>
</property>
</widget>
<widget class="QPushButton" name="btCacheFolder">
<property name="geometry">
<rect>
<x>304</x>
<y>39</y>
<width>29</width>
<height>21</height>
</rect>
</property>
<property name="font">
<font>
<family>Liberation Sans</family>
</font>
</property>
<property name="text">
<string>...</string>
</property>
</widget>
<widget class="QLabel" name="label_62">
<property name="geometry">
<rect>
<x>8</x>
<y>38</y>
<width>79</width>
<height>20</height>
</rect>
</property>
<property name="font">
<font>
<family>Liberation Sans</family>
<underline>true</underline>
</font>
</property>
<property name="text">
<string>Cache folder</string>
</property>
</widget>
<widget class="QCheckBox" name="chkRecurseUpZoomLevels">
<property name="enabled">
<bool>false</bool>
</property>
<property name="geometry">
<rect>
<x>163</x>
<y>146</y>
<width>215</width>
<height>21</height>
</rect>
</property>
<property name="font">
<font>
<family>Liberation Sans</family>
</font>
</property>
<property name="text">
<string>Get recurse up zoom levels</string>
</property>
<property name="checked">
<bool>false</bool>
</property>
</widget>
</widget>
<tabstops>
<tabstop>inVectorFile</tabstop>
<tabstop>btExtentShp</tabstop>
<tabstop>cbLayer</tabstop>
<tabstop>cbZoom</tabstop>
<tabstop>lbRes</tabstop>
<tabstop>requestInfos</tabstop>
<tabstop>chkReproj</tabstop>
<tabstop>cbOutProj</tabstop>
<tabstop>chkBuildOverview</tabstop>
<tabstop>inName</tabstop>
<tabstop>inOutFolder</tabstop>
<tabstop>btBrowseOutFolder</tabstop>
<tabstop>btOkMosaic</tabstop>
<tabstop>processInfo</tabstop>
<tabstop>btCancel</tabstop>
</tabstops>
<resources/>
<connections/>
<designerdata>
<property name="gridDeltaX">
<number>2</number>
</property>
<property name="gridDeltaY">
<number>2</number>
</property>
<property name="gridSnapX">
<bool>true</bool>
</property>
<property name="gridSnapY">
<bool>true</bool>
</property>
<property name="gridVisible">
<bool>true</bool>
</property>
</designerdata>
</ui>
================================================
FILE: core/__init__.py
================================================
import logging
logging.basicConfig(level=logging.getLevelName('INFO'))
from .checkdeps import HAS_GDAL, HAS_PYPROJ, HAS_IMGIO, HAS_PIL
from .settings import settings
from .errors import OverlapError
from .utils import XY, BBOX
from .proj import SRS, Reproj, reprojPt, reprojPts, reprojBbox, reprojImg
from .georaster import GeoRef, GeoRaster, NpImage
from .basemaps import GRIDS, SOURCES, MapService, GeoPackage, TileMatrix
from .lib import shapefile
================================================
FILE: core/basemaps/__init__.py
================================================
from .servicesDefs import GRIDS, SOURCES
from .mapservice import MapService, TileMatrix, BBoxRequest, BBoxRequestMZ
from .gpkg import GeoPackage
================================================
FILE: core/basemaps/gpkg.py
================================================
# -*- coding:utf-8 -*-
# ***** GPL LICENSE BLOCK *****
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# All rights reserved.
# ***** GPL LICENSE BLOCK *****
import logging
log = logging.getLogger(__name__)
import os
import io
import math
import datetime
import sqlite3
#http://www.geopackage.org/spec/#tiles
#https://github.com/GitHubRGI/geopackage-python/blob/master/Packaging/tiles2gpkg_parallel.py
#https://github.com/Esri/raster2gpkg/blob/master/raster2gpkg.py
#table_name refer to the name of the table witch contains tiles data
#here for simplification, table_name will always be named "gpkg_tiles"
class GeoPackage():
MAX_DAYS = 90
def __init__(self, path, tm):
self.dbPath = path
self.name = os.path.splitext(os.path.basename(path))[0]
#Get props from TileMatrix object
self.auth, self.code = tm.CRS.split(':')
self.code = int(self.code)
self.tileSize = tm.tileSize
self.xmin, self.ymin, self.xmax, self.ymax = tm.globalbbox
self.resolutions = tm.getResList()
if not self.isGPKG():
self.create()
self.insertMetadata()
self.insertCRS(self.code, str(self.code), self.auth)
#self.insertCRS(3857, "Web Mercator")
#self.insertCRS(4326, "WGS84")
self.insertTileMatrixSet()
def isGPKG(self):
if not os.path.exists(self.dbPath):
return False
db = sqlite3.connect(self.dbPath)
#check application id
app_id = db.execute("PRAGMA application_id").fetchone()
if not app_id[0] == 1196437808:
db.close()
return False
#quick check of table schema
try:
db.execute('SELECT table_name FROM gpkg_contents LIMIT 1')
db.execute('SELECT srs_name FROM gpkg_spatial_ref_sys LIMIT 1')
db.execute('SELECT table_name FROM gpkg_tile_matrix_set LIMIT 1')
db.execute('SELECT table_name FROM gpkg_tile_matrix LIMIT 1')
db.execute('SELECT zoom_level, tile_column, tile_row, tile_data FROM gpkg_tiles LIMIT 1')
except Exception as e:
log.error('Incorrect GPKG schema', exc_info=True)
db.close()
return False
else:
db.close()
return True
def create(self):
"""Create default geopackage schema on the database."""
db = sqlite3.connect(self.dbPath) #this attempt will create a new file if not exist
cursor = db.cursor()
# Add GeoPackage version 1.0 ("GP10" in ASCII) to the Sqlite header
cursor.execute("PRAGMA application_id = 1196437808;")
cursor.execute("""
CREATE TABLE gpkg_contents (
table_name TEXT NOT NULL PRIMARY KEY,
data_type TEXT NOT NULL,
identifier TEXT UNIQUE,
description TEXT DEFAULT '',
last_change DATETIME NOT NULL DEFAULT
(strftime('%Y-%m-%dT%H:%M:%fZ','now')),
min_x DOUBLE,
min_y DOUBLE,
max_x DOUBLE,
max_y DOUBLE,
srs_id INTEGER,
CONSTRAINT fk_gc_r_srs_id FOREIGN KEY (srs_id)
REFERENCES gpkg_spatial_ref_sys(srs_id));
""")
cursor.execute("""
CREATE TABLE gpkg_spatial_ref_sys (
srs_name TEXT NOT NULL,
srs_id INTEGER NOT NULL PRIMARY KEY,
organization TEXT NOT NULL,
organization_coordsys_id INTEGER NOT NULL,
definition TEXT NOT NULL,
description TEXT);
""")
cursor.execute("""
CREATE TABLE gpkg_tile_matrix_set (
table_name TEXT NOT NULL PRIMARY KEY,
srs_id INTEGER NOT NULL,
min_x DOUBLE NOT NULL,
min_y DOUBLE NOT NULL,
max_x DOUBLE NOT NULL,
max_y DOUBLE NOT NULL,
CONSTRAINT fk_gtms_table_name FOREIGN KEY (table_name)
REFERENCES gpkg_contents(table_name),
CONSTRAINT fk_gtms_srs FOREIGN KEY (srs_id)
REFERENCES gpkg_spatial_ref_sys(srs_id));
""")
cursor.execute("""
CREATE TABLE gpkg_tile_matrix (
table_name TEXT NOT NULL,
zoom_level INTEGER NOT NULL,
matrix_width INTEGER NOT NULL,
matrix_height INTEGER NOT NULL,
tile_width INTEGER NOT NULL,
tile_height INTEGER NOT NULL,
pixel_x_size DOUBLE NOT NULL,
pixel_y_size DOUBLE NOT NULL,
CONSTRAINT pk_ttm PRIMARY KEY (table_name, zoom_level),
CONSTRAINT fk_ttm_table_name FOREIGN KEY (table_name)
REFERENCES gpkg_contents(table_name));
""")
cursor.execute("""
CREATE TABLE gpkg_tiles (
id INTEGER PRIMARY KEY AUTOINCREMENT,
zoom_level INTEGER NOT NULL,
tile_column INTEGER NOT NULL,
tile_row INTEGER NOT NULL,
tile_data BLOB NOT NULL,
last_modified TIMESTAMP DEFAULT (datetime('now','localtime')),
UNIQUE (zoom_level, tile_column, tile_row));
""")
db.close()
def insertMetadata(self):
db = sqlite3.connect(self.dbPath)
query = """INSERT INTO gpkg_contents (
table_name, data_type,
identifier, description,
min_x, min_y, max_x, max_y,
srs_id)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?);"""
db.execute(query, ("gpkg_tiles", "tiles", self.name, "Created with BlenderGIS", self.xmin, self.ymin, self.xmax, self.ymax, self.code))
db.commit()
db.close()
def insertCRS(self, code, name, auth='EPSG', wkt=''):
db = sqlite3.connect(self.dbPath)
db.execute(""" INSERT INTO gpkg_spatial_ref_sys (
srs_id,
organization,
organization_coordsys_id,
srs_name,
definition)
VALUES (?, ?, ?, ?, ?)
""", (code, auth, code, name, wkt))
db.commit()
db.close()
def insertTileMatrixSet(self):
db = sqlite3.connect(self.dbPath)
#Tile matrix set
query = """INSERT OR REPLACE INTO gpkg_tile_matrix_set (
table_name, srs_id,
min_x, min_y, max_x, max_y)
VALUES (?, ?, ?, ?, ?, ?);"""
db.execute(query, ('gpkg_tiles', self.code, self.xmin, self.ymin, self.xmax, self.ymax))
#Tile matrix of each levels
for level, res in enumerate(self.resolutions):
w = math.ceil( (self.xmax - self.xmin) / (self.tileSize * res) )
h = math.ceil( (self.ymax - self.ymin) / (self.tileSize * res) )
query = """INSERT OR REPLACE INTO gpkg_tile_matrix (
table_name, zoom_level,
matrix_width, matrix_height,
tile_width, tile_height,
pixel_x_size, pixel_y_size)
VALUES (?, ?, ?, ?, ?, ?, ?, ?);"""
db.execute(query, ('gpkg_tiles', level, w, h, self.tileSize, self.tileSize, res, res))
db.commit()
db.close()
def hasTile(self, x, y, z):
if self.getTile(x ,y, z) is not None:
return True
else:
return False
def getTile(self, x, y, z):
'''return tilde_data if tile exists otherwie return None'''
#connect with detect_types parameter for automatically convert date to Python object
db = sqlite3.connect(self.dbPath, detect_types=sqlite3.PARSE_DECLTYPES)
query = 'SELECT tile_data, last_modified FROM gpkg_tiles WHERE zoom_level=? AND tile_column=? AND tile_row=?'
result = db.execute(query, (z, x, y)).fetchone()
db.close()
if result is None:
return None
timeDelta = datetime.datetime.now() - result[1]
if timeDelta.days > self.MAX_DAYS:
return None
return result[0]
def putTile(self, x, y, z, data):
db = sqlite3.connect(self.dbPath)
query = """INSERT OR REPLACE INTO gpkg_tiles
(tile_column, tile_row, zoom_level, tile_data) VALUES (?,?,?,?)"""
db.execute(query, (x, y, z, data))
db.commit()
db.close()
def listExistingTiles(self, tiles):
"""
input : tiles list [(x,y,z)]
output : tiles list set [(x,y,z)] of existing records in cache db"""
db = sqlite3.connect(self.dbPath, detect_types=sqlite3.PARSE_DECLTYPES)
# split out the axises
x, y, z = zip(*tiles)
query = "SELECT tile_column, tile_row, zoom_level FROM gpkg_tiles " \
"WHERE julianday() - julianday(last_modified) < ?" \
"AND zoom_level BETWEEN ? AND ? AND tile_column BETWEEN ? AND ? AND tile_row BETWEEN ? AND ?"
result = db.execute(
query,
(
GeoPackage.MAX_DAYS,
min(z), max(z),
min(x), max(x),
min(y), max(y)
)
).fetchall()
db.close()
return set(result)
def listMissingTiles(self, tiles):
existing = self.listExistingTiles(tiles)
return set(tiles) - existing # difference
def getTiles(self, tiles):
"""tiles = list of (x,y,z) tuple
return list of (x,y,z,data) tuple"""
db = sqlite3.connect(self.dbPath, detect_types=sqlite3.PARSE_DECLTYPES)
# split out the axises
x, y, z = zip(*tiles)
query = "SELECT tile_column, tile_row, zoom_level, tile_data FROM gpkg_tiles " \
"WHERE julianday() - julianday(last_modified) < ?" \
"AND zoom_level BETWEEN ? AND ? AND tile_column BETWEEN ? AND ? AND tile_row BETWEEN ? AND ?"
result = db.execute(
query,
(
GeoPackage.MAX_DAYS,
min(z), max(z),
min(x), max(x),
min(y), max(y)
)
).fetchall()
db.close()
return result
def putTiles(self, tiles):
"""tiles = list of (x,y,z,data) tuple"""
db = sqlite3.connect(self.dbPath)
query = """INSERT OR REPLACE INTO gpkg_tiles
(tile_column, tile_row, zoom_level, tile_data) VALUES (?,?,?,?)"""
db.executemany(query, tiles)
db.commit()
db.close()
================================================
FILE: core/basemaps/mapservice.py
================================================
# -*- coding:utf-8 -*-
# ***** GPL LICENSE BLOCK *****
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# All rights reserved.
# ***** GPL LICENSE BLOCK *****
#built-in imports
import logging
log = logging.getLogger(__name__)
import math
import threading
import queue
import time
import urllib.request
from ..lib import imghdr
import sys, time, os
#core imports
from .servicesDefs import GRIDS, SOURCES
from .gpkg import GeoPackage
from ..georaster import NpImage, GeoRef, BigTiffWriter
from ..utils import BBOX
from ..proj.reproj import reprojPt, reprojBbox, reprojImg
from ..proj.ellps import dd2meters, meters2dd
from ..proj.srs import SRS
from .. import settings
USER_AGENT = settings.user_agent
TIMEOUT = 4
# Set mosaic backgroung image color, it will be the base color for area not covered
# by the map service (ie when requests return non valid data)
MOSAIC_BKG_COLOR = (128,128,128,255)
EMPTY_TILE_COLOR = (255,192,203,255) #color for cached tile with empty data
CORRUPTED_TILE_COLOR = (255,0,0,255) #color for cached tile which is non valid image data
class TileMatrix():
"""
Will inherit attributes from grid source definition
"CRS" >> epsg code
"bbox" >> (xmin, ymin, xmax, ymax)
"bboxCRS" >> epsg code
"tileSize"
"originLoc" >> "NW" or SW
"resFactor"
"initRes" >> optional
"nbLevels" >> optional
or
"resolutions"
# Three ways to define a grid:
# - submit a list of "resolutions" (This parameters override the others)
# - submit "resFactor" and "initRes"
# - submit just "resFactor" (initRes will be computed)
"""
defaultNbLevels = 24
def __init__(self, gridDef):
#create class attributes from grid dictionnary
for k, v in gridDef.items():
setattr(self, k, v)
#Convert bbox to grid crs is needed
if self.bboxCRS != self.CRS: #WARN here we assume crs is 4326, TODO
lonMin, latMin, lonMax, latMax = self.bbox
self.xmin, self.ymax = self.geoToProj(lonMin, latMax)
self.xmax, self.ymin = self.geoToProj(lonMax, latMin)
else:
self.xmin, self.xmax = self.bbox[0], self.bbox[2]
self.ymin, self.ymax = self.bbox[1], self.bbox[3]
if not hasattr(self, 'resolutions'):
#Set resFactor if not submited
if not hasattr(self, 'resFactor'):
self.resFactor = 2
#Set initial resolution if not submited
if not hasattr(self, 'initRes'):
# at zoom level zero, 1 tile covers whole bounding box
dx = abs(self.xmax - self.xmin)
dy = abs(self.ymax - self.ymin)
dst = max(dx, dy)
self.initRes = dst / self.tileSize
#Set number of levels if not submited
if not hasattr(self, 'nbLevels'):
self.nbLevels = self.defaultNbLevels
else:
self.resolutions.sort(reverse=True)
self.nbLevels = len(self.resolutions)
# Define tile matrix origin
if self.originLoc == "NW":
self.originx, self.originy = self.xmin, self.ymax
elif self.originLoc == "SW":
self.originx, self.originy = self.xmin, self.ymin
else:
raise NotImplementedError
#Determine unit of CRS (decimal degrees or meters)
self.crs = SRS(self.CRS)
if self.crs.isGeo:
self.units = 'degrees'
else: #(if units cannot be determined we assume its meters)
self.units = 'meters'
@property
def globalbbox(self):
return self.xmin, self.ymin, self.xmax, self.ymax
def geoToProj(self, long, lat):
"""convert longitude latitude in decimal degrees to grid crs"""
if self.CRS == 'EPSG:4326':
return long, lat
else:
return reprojPt(4326, self.CRS, long, lat)
def projToGeo(self, x, y):
"""convert grid crs coords to longitude latitude in decimal degrees"""
if self.CRS == 'EPSG:4326':
return x, y
else:
return reprojPt(self.CRS, 4326, x, y)
def getResList(self):
if hasattr(self, 'resolutions'):
return self.resolutions
else:
return [self.initRes / self.resFactor**zoom for zoom in range(self.nbLevels)]
def getRes(self, zoom):
"""Resolution (meters/pixel) for given zoom level (measured at Equator)"""
if hasattr(self, 'resolutions'):
if zoom > len(self.resolutions):
zoom = len(self.resolutions)
return self.resolutions[zoom]
else:
return self.initRes / self.resFactor**zoom
def getNearestZoom(self, res, rule='closer'):
"""
Return the zoom level closest to the submited resolution
rule in ['closer', 'lower', 'higher']
lower return the previous zoom level, higher return the next
"""
resLst = self.getResList() #ordered
for z1, v1 in enumerate(resLst):
if v1 == res:
return z1
if z1 == len(resLst) - 1:
return z1
z2 = z1+1
v2 = resLst[z2]
if v2 == res:
return z2
if v1 > res > v2:
if rule == 'lower':
return z1
elif rule == 'higher':
return z2
else: #closer
d1 = v1 - res
d2 = res - v2
if d1 < d2:
return z1
else:
return z2
def getPrevResFac(self, z):
"""return res factor to previous zoom level"""
return self.getFromToResFac(z, z-1)
def getNextResFac(self, z):
"""return res factor to next zoom level"""
return self.getFromToResFac(z, z+1)
def getFromToResFac(self, z1, z2):
"""return res factor from z1 to z2"""
if z1 == z2:
return 1
if z1 < z2:
if z2 >= self.nbLevels - 1:
return 1
else:
return self.getRes(z2) / self.getRes(z1)
elif z1 > z2:
if z2 <= 0:
return 1
else:
return self.getRes(z2) / self.getRes(z1)
def getTileNumber(self, x, y, zoom):
"""Convert projeted coords to tiles number"""
res = self.getRes(zoom)
geoTileSize = self.tileSize * res
dx = x - self.originx
if self.originLoc == "NW":
dy = self.originy - y
else:
dy = y - self.originy
col = dx / geoTileSize
row = dy / geoTileSize
col = int(math.floor(col))
row = int(math.floor(row))
return col, row
def getTileCoords(self, col, row, zoom):
"""
Convert tiles number to projeted coords
(top left pixel if matrix origin is NW)
"""
res = self.getRes(zoom)
geoTileSize = self.tileSize * res
x = self.originx + (col * geoTileSize)
if self.originLoc == "NW":
y = self.originy - (row * geoTileSize)
else:
y = self.originy + (row * geoTileSize) #bottom left
y += geoTileSize #top left
return x, y
def getTileBbox(self, col, row, zoom):
xmin, ymax = self.getTileCoords(col, row, zoom)
xmax = xmin + (self.tileSize * self.getRes(zoom))
ymin = ymax - (self.tileSize * self.getRes(zoom))
return xmin, ymin, xmax, ymax
def bboxRequest(self, bbox, zoom):
return BBoxRequest(self, bbox, zoom)
class BBoxRequestMZ():
'''Multiple Zoom BBox request'''
def __init__(self, tm, bbox, zooms):
self.tm = tm
self.bboxrequests = {}
for z in zooms:
self.bboxrequests[z] = BBoxRequest(tm, bbox, z)
@property
def tiles(self):
tiles = []
for bboxrequest in self.bboxrequests.values():
tiles.extend(bboxrequest.tiles)
return tiles
@property
def nbTiles(self):
return len(self.tiles)
def __getitem__(self, z):
return self.bboxrequests[z]
class BBoxRequest():
def __init__(self, tm, bbox, zoom):
self.tm = tm
self.zoom = zoom
self.tileSize = tm.tileSize
self.res = tm.getRes(zoom)
xmin, ymin, xmax, ymax = bbox
#Get first tile indices (top left of requested bbox)
self.firstCol, self.firstRow = tm.getTileNumber(xmin, ymax, zoom)
#correction of top left coord
xmin, ymax = tm.getTileCoords(self.firstCol, self.firstRow, zoom)
self.bbox = BBOX(xmin, ymin, xmax, ymax)
#Total number of tiles required
self.nbTilesX = math.ceil( (xmax - xmin) / (self.tileSize * self.res) )
self.nbTilesY = math.ceil( (ymax - ymin) / (self.tileSize * self.res) )
@property
def cols(self):
return [self.firstCol+i for i in range(self.nbTilesX)]
@property
def rows(self):
if self.tm.originLoc == "NW":
return [self.firstRow+i for i in range(self.nbTilesY)]
else:
return [self.firstRow-i for i in range(self.nbTilesY)]
@property
def tiles(self):
return [(c, r, self.zoom) for c in self.cols for r in self.rows]
@property
def nbTiles(self):
return self.nbTilesX * self.nbTilesY
#megapixel, geosize
class MapService():
"""
Represent a tile service from source
Will inherit attributes from source definition
name
description
service >> 'WMS', 'TMS' or 'WMTS'
grid >> key identifier of the tile matrix used by this source
matrix >> for WMTS only, name of the matrix as refered in url
quadTree >> boolean, for TMS only. Flag if tile coords are stord through a quadkey
layers >> a list layers with the following attributes
urlkey
name
description
format >> 'jpeg' or 'png'
style
zmin & zmax
urlTemplate
referer
Service status code
0 = no running tasks
1 = getting cache (create a new db if needed)
2 = downloading
3 = building mosaic
4 = reprojecting
"""
# resampling algo for reprojection
RESAMP_ALG = 'BL' #NN:Nearest Neighboor, BL:Bilinear, CB:Cubic, CBS:Cubic Spline, LCZ:Lanczos
def __init__(self, srckey, cacheFolder, dstGridKey=None):
#create class attributes from source dictionnary
self.srckey = srckey
source = SOURCES[self.srckey]
for k, v in source.items():
setattr(self, k, v)
#Build objects from layers definitions
class Layer(): pass
layersObj = {}
for layKey, layDict in self.layers.items():
lay = Layer()
for k, v in layDict.items():
setattr(lay, k, v)
layersObj[layKey] = lay
self.layers = layersObj
#Build source tile matrix set
self.srcGridKey = self.grid
self.srcTms = TileMatrix(GRIDS[self.srcGridKey])
#Build destination tile matrix set
self.setDstGrid(dstGridKey)
#Init cache dict
self.cacheFolder = cacheFolder
self.caches = {}
#Fake browser header
self.headers = {
'Accept' : 'image/png,image/*;q=0.8,*/*;q=0.5' ,
'Accept-Charset' : 'ISO-8859-1,utf-8;q=0.7,*;q=0.7' ,
#'Accept-Encoding' : 'gzip,deflate', #urllib2 doesn't automatically uncompress the data
'Accept-Language' : 'fr,en-us,en;q=0.5' ,
#'Keep-Alive': 115 ,
'Proxy-Connection' : 'keep-alive',
'User-Agent' : USER_AGENT,
'Referer' : self.referer}
#Downloading progress
self.running = False #flag using to stop getTiles() / getImage() process
self.nbTiles = 0
self.cptTiles = 0
#codes that indicate the current status of the service
self.status = 0
self.lock = threading.RLock()
def reportLoop(self):
msg = self.report
while self.running:
time.sleep(0.05)
if self.report != msg:
#sys.stdout.write("\033[F") #back to previous line
sys.stdout.write("\033[K") #clear line
sys.stdout.flush()
print(self.report, end='\r') #'\r' will move the cursor back to the beginning of the line
msg = self.report
def start(self):
self.running = True
reporter = threading.Thread(target=self.reportLoop)
reporter.setDaemon(True) #daemon threads will die when the main non-daemon thread have exited.
reporter.start()
def stop(self):
self.running = False
@property
def report(self):
if self.status == 0:
return ''
if self.status == 1:
return 'Get cache database...'
if self.status == 2:
return 'Downloading... ' + str(self.cptTiles)+'/'+str(self.nbTiles)
if self.status == 3:
return 'Building mosaic...'
if self.status == 4:
return 'Reprojecting...'
def setDstGrid(self, grdkey):
'''Set destination tile matrix'''
if grdkey is not None and grdkey != self.srcGridKey:
self.dstGridKey = grdkey
self.dstTms = TileMatrix(GRIDS[grdkey])
else:
self.dstGridKey = None
self.dstTms = None
def getCache(self, laykey, useDstGrid):
'''Return existing cache for requested layer or built it if not exists'''
if useDstGrid:
if self.dstGridKey is not None:
grdkey = self.dstGridKey
tm = self.dstTms
else:
raise ValueError('No destination grid defined')
else:
grdkey = self.srcGridKey
tm = self.srcTms
mapKey = self.srckey + '_' + laykey + '_' + grdkey
cache = self.caches.get(mapKey)
if cache is None:
dbPath = os.path.join(self.cacheFolder, mapKey + ".gpkg")
self.caches[mapKey] = GeoPackage(dbPath, tm)
return self.caches[mapKey]
else:
return cache
def getTM(self, dstGrid=False):
if dstGrid:
if self.dstTms is not None:
return self.dstTms
else:
raise ValueError('No destination grid defined')
else:
return self.srcTms
def buildUrl(self, laykey, col, row, zoom):
"""
Receive tiles coords in source tile matrix space and build request url
"""
url = self.urlTemplate
lay = self.layers[laykey]
tm = self.srcTms
if self.service == 'TMS':
url = url.replace("{LAY}", lay.urlKey)
if not self.quadTree:
url = url.replace("{X}", str(col))
url = url.replace("{Y}", str(row))
url = url.replace("{Z}", str(zoom))
else:
quadkey = self.getQuadKey(col, row, zoom)
url = url.replace("{QUADKEY}", quadkey)
if self.service == 'WMTS':
url = self.urlTemplate['BASE_URL']
if url[-1] != '?' :
url += '?'
params = ['='.join([k,v]) for k, v in self.urlTemplate.items() if k != 'BASE_URL']
url += '&'.join(params)
url = url.replace("{LAY}", lay.urlKey)
url = url.replace("{FORMAT}", lay.format)
url = url.replace("{STYLE}", lay.style)
url = url.replace("{MATRIX}", self.matrix)
url = url.replace("{X}", str(col))
url = url.replace("{Y}", str(row))
url = url.replace("{Z}", str(zoom))
if self.service == 'WMS':
url = self.urlTemplate['BASE_URL']
if url[-1] != '?' :
url += '?'
params = ['='.join([k,v]) for k, v in self.urlTemplate.items() if k != 'BASE_URL']
url += '&'.join(params)
url = url.replace("{LAY}", lay.urlKey)
url = url.replace("{FORMAT}", lay.format)
url = url.replace("{STYLE}", lay.style)
url = url.replace("{CRS}", str(tm.CRS))
url = url.replace("{WIDTH}", str(tm.tileSize))
url = url.replace("{HEIGHT}", str(tm.tileSize))
xmin, ymax = tm.getTileCoords(col, row, zoom)
xmax = xmin + tm.tileSize * tm.getRes(zoom)
ymin = ymax - tm.tileSize * tm.getRes(zoom)
if self.urlTemplate['VERSION'] == '1.3.0' and tm.CRS == 'EPSG:4326':
bbox = ','.join(map(str,[ymin,xmin,ymax,xmax]))
else:
bbox = ','.join(map(str,[xmin,ymin,xmax,ymax]))
url = url.replace("{BBOX}", bbox)
return url
def getQuadKey(self, x, y, z):
"Converts TMS tile coordinates to Microsoft QuadTree"
quadKey = ""
for i in range(z, 0, -1):
digit = 0
mask = 1 << (i-1)
if (x & mask) != 0:
digit += 1
if (y & mask) != 0:
digit += 2
quadKey += str(digit)
return quadKey
def isTileInMapsBounds(self, col, row, zoom, tm):
'''Test if the tile is not out of tile matrix bounds'''
x,y = tm.getTileCoords(col, row, zoom) #top left
if row < 0 or col < 0:
return False
elif not tm.xmin <= x < tm.xmax or not tm.ymin < y <= tm.ymax:
return False
else:
return True
def downloadTile(self, laykey, col, row, zoom):
"""
Download bytes data of requested tile in source tile matrix space
Return None if unable to download a valid stream
"""
url = self.buildUrl(laykey, col, row, zoom)
log.debug(url)
try:
#make request
req = urllib.request.Request(url, None, self.headers)
handle = urllib.request.urlopen(req, timeout=TIMEOUT)
#open image stream
data = handle.read()
handle.close()
except Exception as e:
log.error("Can't download tile x{} y{}. Error {}".format(col, row, e))
data = None
#Make sure the stream is correct
if data is not None:
format = imghdr.what(None, data)
if format is None:
data = None
if data is None:
log.debug("Invalid tile data for request {}".format(url))
return data
def tileRequest(self, laykey, col, row, zoom, toDstGrid=True):
"""
Return bytes data of the requested tile or None if unable to get valid data
Tile is downloaded from map service and, if needed, reprojected to fit the destination grid
"""
#Select tile matrix set
tm = self.getTM(toDstGrid)
#don't try to get tiles out of map bounds
if not self.isTileInMapsBounds(col, row, zoom, tm):
return None
if not toDstGrid:
data = self.downloadTile(laykey, col, row, zoom)
else:
data = self.buildDstTile(laykey, col, row, zoom)
return data
def buildDstTile(self, laykey, col, row, zoom):
'''build a tile that fit the destination tile matrix'''
#get tile bbox
bbox = self.dstTms.getTileBbox(col, row, zoom)
xmin, ymin, xmax, ymax = bbox
#get closest zoom level
res = self.dstTms.getRes(zoom)
if self.dstTms.units == 'degrees' and self.srcTms.units == 'meters':
res2 = dd2meters(res)
elif self.srcTms.units == 'degrees' and self.dstTms.units == 'meters':
res2 = meters2dd(res)
else:
res2 = res
_zoom = self.srcTms.getNearestZoom(res2)
_res = self.srcTms.getRes(_zoom)
#reproj bbox
crs1, crs2 = self.srcTms.CRS, self.dstTms.CRS
try:
_bbox = reprojBbox(crs2, crs1, bbox)
except Exception as e:
log.warning('Cannot reproj tile bbox - ' + str(e))
return None
#list, download and merge the tiles required to build this one (recursive call)
mosaic = self.getImage(laykey, _bbox, _zoom, toDstGrid=False, nbThread=4, cpt=False)
if mosaic is None:
return None
#Reprojection
tileSize = self.dstTms.tileSize
img = NpImage(reprojImg(crs1, crs2, mosaic.toGDAL(), out_ul=(xmin,ymax), out_size=(tileSize,tileSize), out_res=res, sqPx=True, resamplAlg=self.RESAMP_ALG))
return img.toBLOB()
def seedTiles(self, laykey, tiles, toDstGrid=True, nbThread=10, buffSize=5000, cpt=True):
"""
Seed the cache by downloading the requested tiles from map service
Downloads are performed through thread to speed up
buffSize : maximum number of tiles keeped in memory before put them in cache database
"""
def downloading(laykey, tilesQueue, tilesData, toDstGrid):
'''Worker that process the queue and seed tilesData array [(x,y,z,data)]'''
#infinite loop that processes items into the queue
while not tilesQueue.empty(): #empty is True if all item was get but it not tell if all task was done
#cancel thread if requested
if not self.running:
break
#Get a job into the queue
col, row, zoom = tilesQueue.get() #get() pop the item from queue
#do the job
data = self.tileRequest(laykey, col, row, zoom, toDstGrid)
if data is not None:
tilesData.put( (col, row, zoom, data) ) #will block if the queue is full
if cpt:
self.cptTiles += 1
#self.nTaskDone += 1
#flag it's done
tilesQueue.task_done() #it's just a count of finished tasks used by join() to know if the work is finished
def finished():
#return self.nTaskDone == nMissing
#self.nTaskDone is not reliable because the recursive call to getImage will
#start multiple threads to seedTiles() and all these process will increments nTaskDone
return not any([t.is_alive() for t in threads])
def putInCache(tilesData, jobs, cache):
while True:
if tilesData.full() or \
( (finished() or not self.running) and not tilesData.empty()):
data = [tilesData.get() for i in range(tilesData.qsize())]
with self.lock:
cache.putTiles(data)
if finished() and tilesData.empty():
break
if not self.running:
break
if cpt:
#init cpt progress
self.nbTiles = len(tiles)
self.cptTiles = 0
#self.nTaskDone = 0
#Get cache db
if cpt:
self.status = 1
cache = self.getCache(laykey, toDstGrid)
missing = cache.listMissingTiles(tiles)
nMissing = len(missing)
nExists = self.nbTiles - len(missing)
log.debug("{} tiles requested, {} already in cache, {} remains to download".format(self.nbTiles, nExists, nMissing))
if cpt:
self.cptTiles += nExists
#Downloading tiles
if cpt:
self.status = 2
if len(missing) > 0:
#Result queue
tilesData = queue.Queue(maxsize=buffSize)
#Seed the queue
jobs = queue.Queue()
for tile in missing:
jobs.put(tile)
#Launch threads
threads = []
for i in range(nbThread):
t = threading.Thread(target=downloading, args=(laykey, jobs, tilesData, toDstGrid))
t.setDaemon(True)
threads.append(t)
t.start()
seeder = threading.Thread(target=putInCache, args=(tilesData, jobs, cache))
seeder.setDaemon(True)
seeder.start()
seeder.join()
#Make sure all threads has finished
for t in threads:
t.join()
#Reinit status and cpt progress
if cpt:
self.status = 0
self.nbTiles, self.cptTiles = 0, 0
def getTiles(self, laykey, tiles, toDstGrid=True, nbThread=10, cpt=True):
"""
Return bytes data of requested tiles
input: [(x,y,z)] >> output: [(x,y,z,data)]
Tiles are downloaded from map service or directly pick up from cache database.
"""
#seed the cache
self.seedTiles(laykey, tiles, toDstGrid=toDstGrid, nbThread=10, cpt=cpt)
#request the cache and return
cache = self.getCache(laykey, toDstGrid)
return cache.getTiles(tiles) #[(x,y,z,data)]
def getTile(self, laykey, col, row, zoom, toDstGrid=True):
return self.getTiles(laykey, [col, row, zoom], toDstGrid)[0]
def bboxRequest(self, bbox, zoom, dstGrid=True):
#Select tile matrix set
tm = self.getTM(dstGrid)
return BBoxRequest(tm, bbox, zoom)
def seedCache(self, laykey, bbox, zoom, toDstGrid=True, nbThread=10, buffSize=5000):
"""
Seed the cache with the tiles covering the requested bbox
"""
#Select tile matrix set
tm = self.getTM(toDstGrid)
if isinstance(zoom, list):
rq = BBoxRequestMZ(tm, bbox, zoom)
else:
rq = BBoxRequest(tm, bbox, zoom)
self.seedTiles(laykey, rq.tiles, toDstGrid=toDstGrid, nbThread=10, buffSize=5000)
def getImage(self, laykey, bbox, zoom, path=None, bigTiff=False, outCRS=None, toDstGrid=True, nbThread=10, cpt=True):
"""
Build a mosaic of tiles covering the requested bounding box
#laykey (str)
#bbox
#zoom (int)
#path (str): if None the function will return a georeferenced NpImage object. If not None, then the resulting output will be
writen as geotif file on disk and the function will return None
#bigTiff (bool): if true then the raster will be writen by small part with the help of GDAL API. If false the raster will be
writen at one, in this case all the tiles must fit in memory otherwise it will raise a memory overflow error
#outCRS : destination CRS if a reprojection if expected (require GDAL support)
#toDstGrid (bool) : decide if the function will seed the destination tile matrix sets for this MapService instance
(different from the source tile matrix set)
#nbThread (int) : nimber of threads that will be used for downloading tiles
#cpt (bool) : define if the service must report or not tiles downloading count for this request
"""
#Select tile matrix set
tm = self.getTM(toDstGrid)
#Get request
rq = BBoxRequest(tm, bbox, zoom)
tileSize = rq.tileSize
res = rq.res
cols, rows = rq.cols, rq.rows
rqTiles = rq.tiles #[(x,y,z)]
##method 1) Seed the cache with all required tiles
self.seedCache(laykey, bbox, zoom, toDstGrid=toDstGrid, nbThread=nbThread, buffSize=5000)
cache = self.getCache(laykey, toDstGrid)
if not self.running:
if cpt:
self.status = 0
return
#Get georef parameters
img_w, img_h = len(cols) * tileSize, len(rows) * tileSize
xmin, ymin, xmax, ymax = rq.bbox
georef = GeoRef((img_w, img_h), (res, -res), (xmin, ymax), pxCenter=False, crs=tm.crs)
if bigTiff and path is None:
raise ValueError('No output path defined for creating bigTiff')
if not bigTiff:
#Create numpy image in memory
mosaic = NpImage.new(img_w, img_h, bkgColor=MOSAIC_BKG_COLOR, georef=georef)
chunkSize = rq.nbTiles
else:
#Create bigtiff file on disk
mosaic = BigTiffWriter(path, img_w, img_h, georef)
ds = mosaic.ds
chunkSize = 5 #number of tiles to extract in one cache request
#Build mosaic
for i in range(0, rq.nbTiles, chunkSize):
chunkTiles = rqTiles[i:i+chunkSize]
##method 1) Get cached tiles
tiles = cache.getTiles(chunkTiles) #[(x,y,z,data)]
##method 2) Get tiles from www or cache (all tiles must fit in memory)
#tiles = self.getTiles(laykey, chunkTiles, toDstGrid, nbThread, cpt)
if cpt:
self.status = 3
for tile in tiles:
if not self.running:
if cpt:
self.status = 0
return None
col, row, z, data = tile
#TODO corrupted or empty tiles must be deleted from cache are fetched again
if data is None:
#create an empty tile
img = NpImage.new(tileSize, tileSize, bkgColor=EMPTY_TILE_COLOR)
else:
try:
img = NpImage(data)
except Exception as e:
log.error('Corrupted tile on cache', exc_info=True)
#create an empty tile if we are unable to get a valid stream
img = NpImage.new(tileSize, tileSize, bkgColor=CORRUPTED_TILE_COLOR)
posx = (col - rq.firstCol) * tileSize
posy = abs((row - rq.firstRow)) * tileSize
mosaic.paste(img, posx, posy)
if not self.running:
if cpt:
self.status = 0
return None
#Reproject if needed
if outCRS is not None and outCRS != tm.CRS:
if cpt:
self.status = 4
time.sleep(0.1) #make sure client have enough time to get the new status...
if not bigTiff:
mosaic = NpImage(reprojImg(tm.CRS, outCRS, mosaic.toGDAL(), sqPx=True, resamplAlg=self.RESAMP_ALG))
else:
outPath = path[:-4] + '_' + str(outCRS) + '.tif'
ds = reprojImg(tm.CRS, outCRS, mosaic.ds, sqPx=True, resamplAlg=self.RESAMP_ALG, path=outPath)
#build overviews for file output
if bigTiff:
ds.BuildOverviews(overviewlist=[2,4,8,16,32])
ds = None
if not bigTiff and path is not None:
mosaic.save(path)
#Finish
if cpt:
self.status = 0
if path is None:
return mosaic
else:
return None
================================================
FILE: core/basemaps/servicesDefs.py
================================================
# -*- coding:utf-8 -*-
import math
####################################
# Tiles maxtrix definitions
####################################
# Three ways to define a grid (inpired by http://mapproxy.org/docs/1.8.0/configuration.html#id6):
# - submit a list of resolutions > "resolutions": [32,16,8,4] (This parameters override the others)
# - submit just "resFactor", initial res is computed such as at zoom level zero, 1 tile covers whole bounding box
# - submit "resFactor" and "initRes"
# About Web Mercator
# Technically, the Mercator projection is defined for any latitude up to (but not including)
# 90 degrees, but it makes sense to cut it off sooner because it grows exponentially with
# increasing latitude. The logic behind this particular cutoff value, which is the one used
# by Google Maps, is that it makes the projection square. That is, the rectangle is equal in
# the X and Y directions. In this case the maximum latitude attained must correspond to y = w/2.
# y = 2*pi*R / 2 = pi*R --> y/R = pi
# lat = atan(sinh(y/R)) = atan(sinh(pi))
# wm_origin = (-20037508, 20037508) with 20037508 = GRS80.perimeter / 2
cutoff_lat = math.atan(math.sinh(math.pi)) * 180/math.pi #= 85.05112°
GRIDS = {
"WM" : {
"name" : 'Web Mercator',
"description" : 'Global grid in web mercator projection',
"CRS": 'EPSG:3857',
"bbox": [-180, -cutoff_lat, 180, cutoff_lat], #w,s,e,n
"bboxCRS": 'EPSG:4326',
#"bbox": [-20037508, -20037508, 20037508, 20037508],
#"bboxCRS": 3857,
"tileSize": 256,
"originLoc": "NW", #North West or South West
"resFactor" : 2
},
"WGS84" : {
"name" : 'WGS84',
"description" : 'Global grid in wgs84 projection',
"CRS": 'EPSG:4326',
"bbox": [-180, -90, 180, 90], #w,s,e,n
"bboxCRS": 'EPSG:4326',
"tileSize": 256,
"originLoc": "NW", #North West or South West
"resFactor" : 2
},
#this one produce valid MBtiles files, because origin is bottom left
"WM_SW" : {
"name" : 'Web Mercator TMS',
"description" : 'Global grid in web mercator projection, origin South West',
"CRS": 'EPSG:3857',
"bbox": [-180, -cutoff_lat, 180, cutoff_lat], #w,s,e,n
"bboxCRS": 'EPSG:4326',
#"bbox": [-20037508, -20037508, 20037508, 20037508],
#"bboxCRS": 'EPSG:3857',
"tileSize": 256,
"originLoc": "SW", #North West or South West
"resFactor" : 2
},
#####################
#Custom grid example
######################
# >> France Lambert 93
"LB93" : {
"name" : 'Fr Lambert 93',
"description" : 'Local grid in French Lambert 93 projection',
"CRS": 'EPSG:2154',
"bbox": [99200, 6049600, 1242500, 7110500], #w,s,e,n
"bboxCRS": 'EPSG:2154',
"tileSize": 256,
"originLoc": "NW", #North West or South West
"resFactor" : 2
},
# >> Another France Lambert 93 (submited list of resolution)
"LB93_2" : {
"name" : 'Fr Lambert 93 v2',
"description" : 'Local grid in French Lambert 93 projection',
"CRS": 'EPSG:2154',
"bbox": [99200, 6049600, 1242500, 7110500], #w,s,e,n
"bboxCRS": 'EPSG:2154',
"tileSize": 256,
"originLoc": "SW", #North West or South West
"resolutions" : [4000, 2000, 1000, 500, 250, 100, 50, 25, 10, 5, 2, 1, 0.5, 0.25, 0.1] #15 levels
},
# >> France Lambert 93 used by CRAIG WMTS
# WMTS resolution = ScaleDenominator * 0.00028
# (0.28 mm = physical distance of a pixel (WMTS assumes a DPI 90.7)
"LB93_CRAIG" : {
"name" : 'Fr Lambert 93 CRAIG',
"description" : 'Local grid in French Lambert 93 projection',
"CRS": 'EPSG:2154',
"bbox": [-357823.23, 6037001.46, 1313634.34, 7230727.37], #w,s,e,n
"bboxCRS": 'EPSG:2154',
"tileSize": 256,
"originLoc": "NW",
"initRes": 1354.666,
"resFactor" : 2
},
}
####################################
# Sources definitions
####################################
#With TMS or WMTS, grid must match the one used by the service
#With WMS you can use any grid you want but the grid CRS must
#match one of those provided by the WMS service
#The grid associated to the source define the CRS
#A source can have multiple layers but have only one grid
#so to support multiple grid it's necessary to duplicate source definition
SOURCES = {
###############
# TMS examples
###############
"GOOGLE" : {
"name" : 'Google',
"description" : 'Google map',
"service": 'TMS',
"grid": 'WM',
"quadTree": False,
"layers" : {
"SAT" : {"urlKey" : 's', "name" : 'Satellite', "description" : '', "format" : 'jpeg', "zmin" : 0, "zmax" : 22},
"MAP" : {"urlKey" : 'm', "name" : 'Map', "description" : '', "format" : 'png', "zmin" : 0, "zmax" : 22}
},
"urlTemplate": "http://mt0.google.com/vt/lyrs={LAY}&x={X}&y={Y}&z={Z}",
"referer": "https://www.google.com/maps"
},
"OSM" : {
"name" : 'OSM',
"description" : 'Open Street Map',
"service": 'TMS',
"grid": 'WM',
"quadTree": False,
"layers" : {
"MAPNIK" : {"urlKey" : '', "name" : 'Mapnik', "description" : '', "format" : 'png', "zmin" : 0, "zmax" : 19}
},
"urlTemplate": "https://tile.openstreetmap.org/{Z}/{X}/{Y}.png",
"referer": "" #https://www.openstreetmap.org will return 418 error
},
"BING" : {
"name" : 'Bing',
"description" : 'Microsoft Bing Map',
"service": 'TMS',
"grid": 'WM',
"quadTree": True,
"layers" : {
"SAT" : {"urlKey" : 'A', "name" : 'Satellite', "description" : '', "format" : 'jpeg', "zmin" : 0, "zmax" : 22},
"MAP" : {"urlKey" : 'G', "name" : 'Map', "description" : '', "format" : 'png', "zmin" : 0, "zmax" : 22}
},
"urlTemplate": "http://ak.dynamic.t0.tiles.virtualearth.net/comp/ch/{QUADKEY}?it={LAY}",
"referer": "http://www.bing.com/maps"
},
"ESRI" : {
"name" : 'Esri',
"description" : 'Esri ArcGIS',
"service": 'TMS',
"grid": 'WM',
"quadTree": False,
"layers" : {
"AERIAL" : {"urlKey" : 'World_Imagery', "name" : 'Aerial', "description" : '', "format" : 'jpeg', "zmin" : 0, "zmax" : 23},
"NATGEO" : {"urlKey" : 'NatGeo_World_Map', "name" : 'National Geographic', "description" : '', "format" : 'jpeg', "zmin" : 0, "zmax" : 16},
"USATOPO" : {"urlKey" : 'USA_Topo_Maps', "name" : 'USA Topo', "description" : '', "format" : 'jpeg', "zmin" : 0, "zmax" : 15},
"PHYSICAL" : {"urlKey" : 'World_Physical_Map', "name" : 'Physical', "description" : '', "format" : 'jpeg', "zmin" : 0, "zmax" : 8},
"RELIEF" : {"urlKey" : 'World_Shaded_Relief', "name" : 'Shaded Relief', "description" : '', "format" : 'jpeg', "zmin" : 0, "zmax" : 13},
"STREET" : {"urlKey" : 'World_Street_Map', "name" : 'Street Map', "description" : '', "format" : 'jpeg', "zmin" : 0, "zmax" : 23},
"TOPO" : {"urlKey" : 'World_Topo_Map', "name" : 'Topo with relief', "description" : '', "format" : 'jpeg', "zmin" : 0, "zmax" : 23},
"TERRAINB" : {"urlKey" : 'World_Terrain_Base', "name" : 'Terrain Base', "description" : '', "format" : 'jpeg', "zmin" : 0, "zmax" : 13},
"CANVASLIGHTB" : {"urlKey" : 'Canvas/World_Light_Gray_Base', "name" : 'Canvas Light Gray Base', "description" : '', "format" : 'jpeg', "zmin" : 0, "zmax" : 23},
"CANVASDARKB" : {"urlKey" : 'Canvas/World_Dark_Gray_Base', "name" : 'Canvas Dark Gray Base', "description" : '', "format" : 'jpeg', "zmin" : 0, "zmax" : 23},
"OCEANB" : {"urlKey" : 'Ocean/World_Ocean_Base', "name" : 'Ocean Base', "description" : '', "format" : 'jpeg', "zmin" : 0, "zmax" : 23}
},
"urlTemplate": "https://server.arcgisonline.com/ArcGIS/rest/services/{LAY}/MapServer/tile/{Z}/{Y}/{X}",
"referer": "https://server.arcgisonline.com/arcgis/rest/services"
},
###############
# WMS examples
###############
#with WMS you can set source grid as you want, the only condition is that the grid
#crs must match one on crs provided by WMS
"OSM_WMS" : {
"name" : 'OSM WMS',
"description" : 'Open Street Map WMS',
"service": 'WMS',
"grid": 'WM',
"layers" : {
"WRLD" : {"urlKey" : 'osm_auto:all', "name" : 'WMS', "description" : '', "format" : 'png', "style" : '', "zmin" : 0, "zmax" : 20}
},
"urlTemplate": {
"BASE_URL" : ' http://maps.heigit.org/osm-wms/service?',
"SERVICE" : 'WMS',
"VERSION" : '1.1.1',
"REQUEST" : 'GetMap',
"SRS" : '{CRS}', #EPSG:xxxx
"LAYERS" : '{LAY}',
"FORMAT" : 'image/{FORMAT}',
"STYLES" : '{STYLE}',
"BBOX" : '{BBOX}', #xmin,ymin,xmax,ymax, in "SRS" projection
"WIDTH" : '{WIDTH}',
"HEIGHT" : '{HEIGHT}',
"TRANSPARENT" : "False"
},
"referer": "http://www.osm-wms.de/"
},
"GEOPORTAIL" : {
"name" : 'Geoportail',
"description" : 'Geoportail.fr',
"service": 'WMTS',
"grid": 'WM',
"matrix" : 'PM',
"layers" : {
"ORTHO" : {"urlKey" : 'ORTHOIMAGERY.ORTHOPHOTOS', "name" : 'Orthophotos', "description" : '',
"format" : 'jpeg', "style" : 'normal', "zmin" : 0, "zmax" : 22},
"CAD" : {"urlKey" : 'CADASTRALPARCELS.PARCELS', "name" : 'Cadastre', "description" : '',
"format" : 'png', "style" : 'bdparcellaire', "zmin" : 0, "zmax" : 22}
},
"urlTemplate": {
"BASE_URL" : 'https://data.geopf.fr/wmts?',
"SERVICE" : 'WMTS',
"VERSION" : '1.0.0',
"REQUEST" : 'GetTile',
"LAYER" : '{LAY}',
"STYLE" : '{STYLE}',
"FORMAT" : 'image/{FORMAT}',
"TILEMATRIXSET" : '{MATRIX}',
"TILEMATRIX" : '{Z}',
"TILEROW" : '{Y}',
"TILECOL" : '{X}'
},
"referer": "http://www.geoportail.gouv.fr/accueil"
},
"GEOPORTAIL2" : {
"name" : 'Geoportail ©scan',
"description" : 'Geoportail.fr',
"service": 'WMTS',
"grid": 'WM',
"matrix" : 'PM',
"layers" : {
"SCAN25" : {"urlKey" : 'GEOGRAPHICALGRIDSYSTEMS.MAPS.SCAN25TOUR', "name" : 'Scan25', "description" : '',
"format" : 'jpeg', "style" : 'normal', "zmin" : 0, "zmax" : 22},
"SCAN" : {"urlKey" : 'GEOGRAPHICALGRIDSYSTEMS.MAPS', "name" : 'Scan', "description" : '',
"format" : 'jpeg', "style" : 'normal', "zmin" : 0, "zmax" : 22}
},
"urlTemplate": {
"BASE_URL" : 'https://data.geopf.fr/private/wmts?',
"SERVICE" : 'WMTS',
"VERSION" : '1.0.0',
"REQUEST" : 'GetTile',
"LAYER" : '{LAY}',
"STYLE" : '{STYLE}',
"FORMAT" : 'image/{FORMAT}',
"TILEMATRIXSET" : '{MATRIX}',
"TILEMATRIX" : '{Z}',
"TILEROW" : '{Y}',
"TILECOL" : '{X}',
"apikey" : "ign_scan_ws"
},
"referer": "http://www.geoportail.gouv.fr/accueil"
}
}
"""
#http://wms.craig.fr/ortho?SERVICE=WMS&REQUEST=GetCapabilities
# example of valid location in Auvergne : lat 45.77 long 3.082
"CRAIG_WMS" : {
"name" : 'CRAIG WMS',
"description" : "Centre Régional Auvergnat de l'Information Géographique",
"service": 'WMS',
"grid": 'LB93',
"layers" : {
"ORTHO" : {"urlKey" : 'auvergne', "name" : 'Auv25cm_2013', "description" : '', "format" : 'png', "style" : 'default', "zmin" : 0, "zmax" : 22}
},
"urlTemplate": {
"BASE_URL" : 'http://wms.craig.fr/ortho?',
"SERVICE" : 'WMS',
"VERSION" : '1.3.0',
"REQUEST" : 'GetMap',
"CRS" : '{CRS}',
"LAYERS" : '{LAY}',
"FORMAT" : 'image/{FORMAT}',
"STYLES" : '{STYLE}',
"BBOX" : '{BBOX}', #xmin,ymin,xmax,ymax, in "SRS" projection
"WIDTH" : '{WIDTH}',
"HEIGHT" : '{HEIGHT}',
"TRANSPARENT" : "False"
},
"referer": "http://www.craig.fr/"
},
###############
# WMTS examples
###############
# http://tiles.craig.fr/ortho/service?service=WMTS&REQUEST=GetCapabilities
# example of valid location in Auvergne : lat 45.77 long 3.082
"CRAIG_WMTS93" : {
"name" : 'CRAIG WMTS93',
"description" : "Centre Régional Auvergnat de l'Information Géographique",
"service": 'WMTS',
"grid": 'LB93_CRAIG',
"matrix" : 'lambert93',
"layers" : {
"ORTHO" : {"urlKey" : 'ortho_2013', "name" : 'Auv25cm_2013', "description" : '',
"format" : 'jpeg', "style" : 'default', "zmin" : 0, "zmax" : 15}
},
"urlTemplate": {
"BASE_URL" : 'http://tiles.craig.fr/ortho/service?',
"SERVICE" : 'WMTS',
"VERSION" : '1.0.0',
"REQUEST" : 'GetTile',
"LAYER" : '{LAY}',
"STYLE" : '{STYLE}',
"FORMAT" : 'image/{FORMAT}',
"TILEMATRIXSET" : '{MATRIX}',
"TILEMATRIX" : '{Z}',
"TILEROW" : '{Y}',
"TILECOL" : '{X}'
},
"referer": "http://www.craig.fr/"
},
}
"""
================================================
FILE: core/checkdeps.py
================================================
import logging
log = logging.getLogger(__name__)
#GDAL
try:
from osgeo import gdal
except:
HAS_GDAL = False
log.debug('GDAL Python binding unavailable')
else:
HAS_GDAL = True
log.debug('GDAL Python binding available')
#PyProj
try:
import pyproj
except:
HAS_PYPROJ = False
log.debug('PyProj unavailable')
else:
HAS_PYPROJ = True
log.debug('PyProj available')
#PIL/Pillow
try:
from PIL import Image
except:
HAS_PIL = False
log.debug('Pillow unavailable')
else:
HAS_PIL = True
log.debug('Pillow available')
#Imageio freeimage plugin
try:
from .lib import imageio
imageio.plugins._freeimage.get_freeimage_lib() #try to download freeimage lib
except Exception as e:
log.error("Cannot install ImageIO's Freeimage plugin", exc_info=True)
HAS_IMGIO = False
else:
HAS_IMGIO = True
log.debug('ImageIO Freeimage plugin available')
================================================
FILE: core/errors.py
================================================
class OverlapError(Exception):
def __init__(self):
pass
def __str__(self):
return "Non overlap data"
class ReprojError(Exception):
def __init__(self, value):
self.value = value
def __str__(self):
return repr(self.value)
class ApiKeyError(Exception):
def __init__(self):
pass
def __str__(self):
return "Missing or wrong API key"
================================================
FILE: core/georaster/__init__.py
================================================
from .georef import GeoRef
from .georaster import GeoRaster
from .npimg import NpImage
from .bigtiffwriter import BigTiffWriter
from .img_utils import getImgFormat, getImgDim, isValidStream
================================================
FILE: core/georaster/bigtiffwriter.py
================================================
# -*- coding:utf-8 -*-
# This file is part of BlenderGIS
# ***** GPL LICENSE BLOCK *****
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# All rights reserved.
# ***** GPL LICENSE BLOCK *****
import os
import numpy as np
from .npimg import NpImage
from ..checkdeps import HAS_GDAL, HAS_PIL, HAS_IMGIO
if HAS_GDAL:
from osgeo import gdal
class BigTiffWriter():
'''
This class is designed to write a bigtif with jpeg compression
writing a large tiff file without trigger a memory overflow is possible with the help of GDAL library
jpeg compression allows to maintain a reasonable file size
transparency or nodata are stored in an internal tiff mask because it's not possible to have an alpha channel when using jpg compression
'''
def __del__(self):
# properly close gdal dataset
self.ds = None
def __init__(self, path, w, h, georef, geoTiffOptions={'TFW':'YES', 'TILED':'YES', 'BIGTIFF':'YES', 'COMPRESS':'JPEG', 'JPEG_QUALITY':80, 'PHOTOMETRIC':'YCBCR'}):
'''
path = fule system path for the ouput tiff
w, h = width and height in pixels
georef : a Georef object used to set georeferencing informations, optional
geoTiffOptions : GDAL create option for tiff format
'''
if not HAS_GDAL:
raise ImportError("GDAL interface unavailable")
#control path validity
self.w = w
self.h = h
self.size = (w, h)
self.path = path
self.georef = georef
if geoTiffOptions.get('COMPRESS', None) == 'JPEG':
#JPEG in tiff cannot have an alpha band, workaround is to use internal tiff mask
self.useMask = True
gdal.SetConfigOption('GDAL_TIFF_INTERNAL_MASK', 'YES')
n = 3 #RGB
else:
self.useMask = False
n = 4 #RGBA
self.nbBands = n
options = [str(k) + '=' + str(v) for k, v in geoTiffOptions.items()]
driver = gdal.GetDriverByName("GTiff")
gdtype = gdal.GDT_Byte #GDT_UInt16, GDT_Int16, GDT_UInt32, GDT_Int32
self.dtype = 'uint8'
self.ds = driver.Create(path, w, h, n, gdtype, options)
if self.useMask:
self.ds.CreateMaskBand(gdal.GMF_PER_DATASET)#The mask band is shared between all bands on the dataset
self.mask = self.ds.GetRasterBand(1).GetMaskBand()
self.mask.Fill(255)
elif n == 4:
self.ds.GetRasterBand(4).Fill(255)
#Write georef infos
self.ds.SetGeoTransform(self.georef.toGDAL())
if self.georef.crs is not None:
self.ds.SetProjection(self.georef.crs.getOgrSpatialRef().ExportToWkt())
#self.georef.toWorldFile(os.path.splitext(path)[0] + '.tfw')
def paste(self, data, x, y):
'''data = numpy array or NpImg'''
img = NpImage(data)
data = img.data
#Write RGB
for bandIdx in range(3): #writearray is available only at band level
bandArray = data[:,:,bandIdx]
self.ds.GetRasterBand(bandIdx+1).WriteArray(bandArray, x, y)
#Process alpha
hasAlpha = data.shape[2] == 4
if hasAlpha:
alpha = data[:,:,3]
if self.useMask:
self.mask.WriteArray(alpha, x, y)
else:
self.ds.GetRasterBand(4).WriteArray(alpha, x, y)
else:
pass # replaced by fill method
'''
#make alpha band or internal mask fully opaque
h, w = data.shape[0], data.shape[1]
alpha = np.full((h, w), 255, np.uint8)
if self.useMask:
self.mask.WriteArray(alpha, x, y)
else:
self.ds.GetRasterBand(4).WriteArray(alpha, x, y)
'''
def __repr__(self):
return '\n'.join([
"* Data infos :",
" size {}".format(self.size),
" type {}".format(self.dtype),
" number of bands {}".format(self.nbBands),
"* Georef & Geometry : \n{}".format(self.georef)
])
================================================
FILE: core/georaster/georaster.py
================================================
# -*- coding:utf-8 -*-
# This file is part of BlenderGIS
# ***** GPL LICENSE BLOCK *****
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# All rights reserved.
# ***** GPL LICENSE BLOCK *****
import os
import logging
log = logging.getLogger(__name__)
from ..lib import Tyf #geotags reader
from .georef import GeoRef
from .npimg import NpImage
from .img_utils import getImgFormat, getImgDim
from ..utils import XY as xy
from ..errors import OverlapError
from ..checkdeps import HAS_GDAL
if HAS_GDAL:
from osgeo import gdal
class GeoRaster():
'''A class to represent a georaster file'''
def __init__(self, path, subBoxGeo=None, useGDAL=False):
'''
subBoxGeo : a BBOX object in CRS coordinate space
useGDAL : use GDAL (if available) for extract raster informations
'''
self.path = path
self.wfPath = self._getWfPath()
self.format = None #image file format (jpeg, tiff, png ...)
self.size = None #raster dimension (width, height) in pixel
self.depth = None #8, 16, 32
self.dtype = None #int, uint, float
self.nbBands = None #number of bands
self.noData = None
self.georef = None
if not useGDAL or not HAS_GDAL:
self.format = getImgFormat(path)
if self.format not in ['TIFF', 'BMP', 'PNG', 'JPEG', 'JPEG2000']:
raise IOError("Unsupported format {}".format(self.format))
if self.isTiff:
self._fromTIFF()
if not self.isGeoref and self.hasWorldFile:
self.georef = GeoRef.fromWorldFile(self.wfPath, self.size)
else:
pass
else:
# Try to read file header
w, h = getImgDim(self.path)
if w is None or h is None:
raise IOError("Unable to read raster size")
else:
self.size = xy(w, h)
#georef
if self.hasWorldFile:
self.georef = GeoRef.fromWorldFile(self.wfPath, self.size)
#TODO add function to extract dtype, nBands & depth from jpg, png, bmp or jpeg2000
else:
self._fromGDAL()
if not self.isGeoref:
raise IOError("Unable to read georef infos from worldfile or geotiff tags")
if subBoxGeo is not None:
self.georef.setSubBoxGeo(subBoxGeo)
#GeoGef delegation by composition instead of inheritance
#this special method is called whenever the requested attribute or method is not found in the object
def __getattr__(self, attr):
return getattr(self.georef, attr)
############################################
# Initialization Helpers
############################################
def _getWfPath(self):
'''Try to find a worlfile path for this raster'''
ext = self.path[-3:].lower()
extTest = []
extTest.append(ext[0] + ext[2] +'w')# tfx, jgw, pgw ...
extTest.append(extTest[0]+'x')# tfwx
extTest.append(ext+'w')# tifw
extTest.append('wld')#*.wld
extTest.extend( [ext.upper() for ext in extTest] )
for wfExt in extTest:
pathTest = self.path[0:len(self.path)-3] + wfExt
if os.path.isfile(pathTest):
return pathTest
return None
def _fromTIFF(self):
'''Use Tyf to extract raster infos from geotiff tags'''
if not self.isTiff or not self.fileExists:
return
tif = Tyf.open(self.path)[0]
#Warning : Tyf object does not support k in dict test syntax nor get() method, use try block instead
self.size = xy(tif['ImageWidth'], tif['ImageLength'])
self.nbBands = tif['SamplesPerPixel']
self.depth = tif['BitsPerSample']
if self.nbBands > 1:
self.depth = self.depth[0]
sampleFormatMap = {1:'uint', 2:'int', 3:'float', None:'uint', 6:'complex'}
try:
self.dtype = sampleFormatMap[tif['SampleFormat']]
except KeyError:
self.dtype = 'uint'
try:
self.noData = float(tif['GDAL_NODATA'])
except KeyError:
self.noData = None
#Get Georef
try:
self.georef = GeoRef.fromTyf(tif)
except Exception as e:
log.warning('Cannot extract georefencing informations from tif tags')#, exc_info=True)
pass
def _fromGDAL(self):
'''Use GDAL to extract raster infos and init'''
if self.path is None or not self.fileExists:
raise IOError("Cannot find file on disk")
ds = gdal.Open(self.path, gdal.GA_ReadOnly)
self.size = xy(ds.RasterXSize, ds.RasterYSize)
self.format = ds.GetDriver().ShortName
if self.format in ['JP2OpenJPEG', 'JP2ECW', 'JP2KAK', 'JP2MrSID'] :
self.format = 'JPEG2000'
self.nbBands = ds.RasterCount
b1 = ds.GetRasterBand(1) #first band (band index does not count from 0)
self.noData = b1.GetNoDataValue()
ddtype = gdal.GetDataTypeName(b1.DataType)#Byte, UInt16, Int16, UInt32, Int32, Float32, Float64
if ddtype == "Byte":
self.dtype = 'uint'
self.depth = 8
else:
self.dtype = ddtype[0:len(ddtype)-2].lower()
self.depth = int(ddtype[-2:])
#Get Georef
self.georef = GeoRef.fromGDAL(ds)
#Close (gdal has no garbage collector)
ds, b1 = None, None
#######################################
# Dynamic properties
#######################################
@property
def fileExists(self):
'''Test if the file exists on disk'''
return os.path.isfile(self.path)
@property
def baseName(self):
if self.path is not None:
folder, fileName = os.path.split(self.path)
baseName, ext = os.path.splitext(fileName)
return baseName
@property
def isTiff(self):
'''Flag if the image format is TIFF'''
if self.format in ['TIFF', 'GTiff']:
return True
else:
return False
@property
def hasWorldFile(self):
return self.wfPath is not None
@property
def isGeoref(self):
'''Flag if georef parameters have been extracted'''
if self.georef is not None:
if self.origin is not None and self.pxSize is not None and self.rotation is not None:
return True
else:
return False
else:
return False
@property
def isOneBand(self):
return self.nbBands == 1
@property
def isFloat(self):
return self.dtype in ['Float', 'float']
@property
def ddtype(self):
'''
Get data type and depth in a concatenate string like
'int8', 'int16', 'uint16', 'int32', 'uint32', 'float32' ...
Can be used to define numpy or gdal data type
'''
if self.dtype is None or self.depth is None:
return None
else:
return self.dtype + str(self.depth)
def __repr__(self):
return '\n'.join([
'* Paths infos :',
' path {}'.format(self.path),
' worldfile {}'.format(self.wfPath),
' format {}'.format(self.format),
"* Data infos :",
" size {}".format(self.size),
" bit depth {}".format(self.depth),
" data type {}".format(self.dtype),
" number of bands {}".format(self.nbBands),
" nodata value {}".format(self.noData),
"* Georef & Geometry : \n{}".format(self.georef)
])
#######################################
# Methods
#######################################
def toGDAL(self):
'''Get GDAL dataset'''
return gdal.Open(self.path, gdal.GA_ReadOnly)
def readAsNpArray(self, subset=True):
'''Read raster pixels values as Numpy Array'''
if subset and self.subBoxGeo is not None:
#georef = GeoRef(self.size, self.pxSize, self.subBoxGeoOrigin, rot=self.rotation, pxCenter=True)
img = NpImage(self.path, subBoxPx=self.subBoxPx, noData=self.noData, georef=self.georef, adjustGeoref=True)
else:
img = NpImage(self.path, noData=self.noData, georef=self.georef)
return img
================================================
FILE: core/georaster/georef.py
================================================
# -*- coding:utf-8 -*-
# This file is part of BlenderGIS
# ***** GPL LICENSE BLOCK *****
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# All rights reserved.
# ***** GPL LICENSE BLOCK *****
import math
from ..proj import SRS
from ..utils import XY as xy, BBOX
from ..errors import OverlapError
class GeoRef():
'''
Represents georefencing informations of a raster image
Note : image origin is upper-left whereas map origin is lower-left
'''
def __init__(self, rSize, pxSize, origin, rot=xy(0,0), pxCenter=True, subBoxGeo=None, crs=None):
'''
rSize : dimensions of the raster in pixels (width, height) tuple
pxSize : dimension of a pixel in map units (x scale, y scale) tuple. y is always negative
origin : upper left geo coords of pixel center, (x, y) tuple
pxCenter : set it to True is the origin anchor point is located at pixel center
or False if it's lolcated at pixel corner
rotation : rotation terms (xrot, yrot) <--> (yskew, xskew)
subBoxGeo : a BBOX object that define the working extent (subdataset) in geographic coordinate space
'''
self.rSize = xy(*rSize)
self.origin = xy(*origin)
self.pxSize = xy(*pxSize)
if not pxCenter:
#adjust topleft coord to pixel center
self.origin[0] += abs(self.pxSize.x/2)
self.origin[1] -= abs(self.pxSize.y/2)
self.rotation = xy(*rot)
if subBoxGeo is not None:
# Define a subbox at init is optionnal, we can also do it later
# Setting the subBox will check if the box overlap the raster extent
self.setSubBoxGeo(subBoxGeo)
else:
self.subBoxGeo = None
if crs is not None:
if isinstance(crs, SRS):
self.crs = crs
else:
raise IOError("CRS must be SRS() class object not " + str(type(crs)))
else:
self.crs = crs
############################################
# Alternative constructors
############################################
@classmethod
def fromGDAL(cls, ds):
'''init from gdal dataset instance'''
geoTrans = ds.GetGeoTransform()
if geoTrans is not None:
xmin, resx, rotx, ymax, roty, resy = geoTrans
w, h = ds.RasterXSize, ds.RasterYSize
try:
crs = SRS.fromGDAL(ds)
except Exception as e:
crs = None
return cls((w, h), (resx, resy), (xmin, ymax), rot=(rotx, roty), pxCenter=False, crs=crs)
else:
return None
@classmethod
def fromWorldFile(cls, wfPath, rasterSize):
'''init from a worldfile'''
try:
with open(wfPath,'r') as f:
wf = f.readlines()
pxSize = xy(float(wf[0].replace(',','.')), float(wf[3].replace(',','.')))
rotation = xy(float(wf[1].replace(',','.')), float(wf[2].replace(',','.')))
origin = xy(float(wf[4].replace(',','.')), float(wf[5].replace(',','.')))
return cls(rasterSize, pxSize, origin, rot=rotation, pxCenter=True, crs=None)
except Exception as e:
raise IOError("Unable to read worldfile. {}".format(e))
@classmethod
def fromTyf(cls, tif):
'''read geotags from Tyf instance'''
#Warning : Tyf object does not support k in dict test syntax nor get() method, use try block instead
w, h = tif['ImageWidth'], tif['ImageLength']
#Search for a transformation matrix
try:
#34264: ("ModelTransformation", "a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p")
# 4x4 transform matrix in 3D space
transfoMatrix = tif['ModelTransformationTag']
except KeyError:
transfoMatrix = None
#Search for upper left coord and pixel scales
try:
#33922: ("ModelTiepoint", "I,J,K,X,Y,Z")
modelTiePoint = tif['ModelTiepointTag']
#33550 ("ModelPixelScale", "ScaleX, ScaleY, ScaleZ")
modelPixelScale = tif['ModelPixelScaleTag']
except KeyError:
modelTiePoint = None
modelPixelScale = None
if transfoMatrix is not None:
a,b,c,d, \
e,f,g,h, \
i,j,k,l, \
m,n,o,p = transfoMatrix
#get only 2d affine parameters
origin = xy(d, h)
pxSize = xy(a, f)
rotation = xy(e, b)
elif modelTiePoint is not None and modelPixelScale is not None:
origin = xy(*modelTiePoint[3:5])
pxSize = xy(*modelPixelScale[0:2])
pxSize[1] = -pxSize.y #make negative value
rotation = xy(0, 0)
else:
raise IOError("Unable to read geotags")
#Define anchor point for top left coord
# http://www.remotesensing.org/geotiff/spec/geotiff2.5.html#2.5.2
# http://www.remotesensing.org/geotiff/spec/geotiff6.html#6.3.1.2
# >> 1 = area (cell anchor = top left corner)
# >> 2 = point (cell anchor = center)
#geotags = Tyf.gkd.Gkd(tif)
#cellAnchor = geotags['GTRasterTypeGeoKey']
try:
geotags = tif['GeoKeyDirectoryTag']
except KeyError:
cellAnchor = 1 #if this key is missing then RasterPixelIsArea is the default
else:
#get GTRasterTypeGeoKey value
cellAnchor = geotags[geotags.index(1025)+3] #http://www.remotesensing.org/geotiff/spec/geotiff2.4.html
if cellAnchor == 1:
#adjust topleft coord to pixel center
origin[0] += abs(pxSize.x/2)
origin[1] -= abs(pxSize.y/2)
#TODO extract crs (transcript geokeys to proj4 string)
return cls((w, h), pxSize, origin, rot=rotation, pxCenter=True, crs=None)
############################################
# Export
############################################
def toGDAL(self):
'''return a tuple of georef parameters ordered to define geotransformation of a gdal datasource'''
xmin, ymax = self.corners[0]
xres, yres = self.pxSize
xrot, yrot = self.rotation
return (xmin, xres, xrot, ymax, yrot, yres)
def toWorldFile(self, path):
'''export geo transformation to a worldfile'''
xmin, ymax = self.origin
xres, yres = self.pxSize
xrot, yrot = self.rotation
wf = (xres, xrot, yrot, yres, xmin, ymax)
f = open(path,'w')
f.write( '\n'.join(list(map(str, wf))) )
f.close()
############################################
# Dynamic properties
############################################
@property
def hasCRS(self):
return self.crs is not None
@property
def hasRotation(self):
return self.rotation.x != 0 or self.rotation.y != 0
#TODO
#def getCorners(self, center=True):
#def getUL(self, center=True)
"""
@property
def ul(self):
'''upper left corner'''
return self.geoFromPx(0, yPxRange, True)
@property
def ur(self):
'''upper right corner'''
return self.geoFromPx(xPxRange, yPxRange, True)
@property
def bl(self):
'''bottom left corner'''
return self.geoFromPx(0, 0, True)
@property
def br(self):
'''bottom right corner'''
return self.geoFromPx(xPxRange, 0, True)
"""
@property
def cornersCenter(self):
'''
(x,y) geo coordinates of image corners (upper left, upper right, bottom right, bottom left)
(pt1, pt2, pt3, pt4) <--> (upper left, upper right, bottom right, bottom left)
The coords are located at the pixel center
'''
xPxRange = self.rSize.x - 1
yPxRange = self.rSize.y - 1
#pixel center
pt1 = self.geoFromPx(0, 0, pxCenter=True)#upperLeft
pt2 = self.geoFromPx(xPxRange, 0, pxCenter=True)#upperRight
pt3 = self.geoFromPx(xPxRange, yPxRange, pxCenter=True)#bottomRight
pt4 = self.geoFromPx(0, yPxRange, pxCenter=True)#bottomLeft
return (pt1, pt2, pt3, pt4)
@property
def corners(self):
'''
(x,y) geo coordinates of image corners (upper left, upper right, bottom right, bottom left)
(pt1, pt2, pt3, pt4) <--> (upper left, upper right, bottom right, bottom left)
Represent the true corner location (upper left for pt1, upper right for pt2 ...)
'''
#get corners at center
pt1, pt2, pt3, pt4 = self.cornersCenter
#pixel center offset
xOffset = abs(self.pxSize.x/2)
yOffset = abs(self.pxSize.y/2)
pt1 = xy(pt1.x - xOffset, pt1.y + yOffset)
pt2 = xy(pt2.x + xOffset, pt2.y + yOffset)
pt3 = xy(pt3.x + xOffset, pt3.y - yOffset)
pt4 = xy(pt4.x - xOffset, pt4.y - yOffset)
return (pt1, pt2, pt3, pt4)
@property
def bbox(self):
'''Return a bbox class object'''
pts = self.corners
xmin = min([pt.x for pt in pts])
xmax = max([pt.x for pt in pts])
ymin = min([pt.y for pt in pts])
ymax = max([pt.y for pt in pts])
return BBOX(xmin=xmin, ymin=ymin, xmax=xmax, ymax=ymax)
@property
def bboxPx(self):
return BBOX(xmin=0, ymin=0, xmax=self.rSize.x, ymax=self.rSize.y)
@property
def center(self):
'''(x,y) geo coordinates of image center'''
return xy(self.corners[0].x + self.geoSize.x/2, self.corners[0].y - self.geoSize.y/2)
@property
def geoSize(self):
'''raster dimensions (width, height) in map units'''
return xy(self.rSize.x * abs(self.pxSize.x), self.rSize.y * abs(self.pxSize.y))
@property
def orthoGeoSize(self):
'''ortho geo size when affine transfo applied a rotation'''
pxWidth = math.sqrt(self.pxSize.x**2 + self.rotation.x**2)
pxHeight = math.sqrt(self.pxSize.y**2 + self.rotation.y**2)
return xy(self.rSize.x*pxWidth, self.rSize.y*pxHeight)
@property
def orthoPxSize(self):
'''ortho pixels size when affine transfo applied a rotation'''
pxWidth = math.sqrt(self.pxSize.x**2 + self.rotation.x**2)
pxHeight = math.sqrt(self.pxSize.y**2 + self.rotation.y**2)
return xy(pxWidth, pxHeight)
def geoFromPx(self, xPx, yPx, reverseY=False, pxCenter=True):
"""
Affine transformation (cf. ESRI WorldFile spec.)
Return geo coords of the center of an given pixel
xPx = the column number of the pixel in the image counting from left
yPx = the row number of the pixel in the image counting from top
use reverseY option is yPx is counting from bottom instead of top
Number of pixels is range from 0 (not 1)
"""
if pxCenter:
#force pixel center, in this case we need to cast the inputs to floor integer
xPx, yPx = math.floor(xPx), math.floor(yPx)
ox, oy = self.origin.x, self.origin.y
else:
#normal behaviour, coord at pixel's top left corner
ox = self.origin.x - abs(self.pxSize.x/2)
oy = self.origin.y + abs(self.pxSize.y/2)
if reverseY:#the users given y pixel in the image counting from bottom
yPxRange = self.rSize.y - 1
yPx = yPxRange - yPx
x = self.pxSize.x * xPx + self.rotation.y * yPx + ox
y = self.pxSize.y * yPx + self.rotation.x * xPx + oy
return xy(x, y)
def pxFromGeo(self, x, y, reverseY=False, round2Floor=False):
"""
Affine transformation (cf. ESRI WorldFile spec.)
Return pixel position of given geographic coords
use reverseY option to get y pixels counting from bottom
Pixels position is range from 0 (not 1)
"""
# aliases for more readability
pxSizex, pxSizey = self.pxSize
rotx, roty = self.rotation
offx = self.origin.x - abs(self.pxSize.x/2)
offy = self.origin.y + abs(self.pxSize.y/2)
# transfo
xPx = (pxSizey*x - rotx*y + rotx*offy - pxSizey*offx) / (pxSizex*pxSizey - rotx*roty)
yPx = (-roty*x + pxSizex*y + roty*offx - pxSizex*offy) / (pxSizex*pxSizey - rotx*roty)
if reverseY:#the users want y pixel position counting from bottom
yPxRange = self.rSize.y - 1
yPx = yPxRange - yPx
yPx += 1 #adjust because the coord start at pixel's top left coord
#round to floor
if round2Floor:
xPx, yPx = math.floor(xPx), math.floor(yPx)
return xy(xPx, yPx)
#Alias
def pxToGeo(self, xPx, yPx, reverseY=False):
return self.geoFromPx(xPx, yPx, reverseY)
def geoToPx(self, x, y, reverseY=False, round2Floor=False):
return self.pxFromGeo(x, y, reverseY, round2Floor)
############################################
# Subbox handlers
############################################
def setSubBoxGeo(self, subBoxGeo):
'''set a subbox in geographic coordinate space
if needed, coords will be adjusted to avoid being outside raster size'''
if self.hasRotation:
raise IOError("A subbox cannot be define if the raster has rotation parameter")
#Before set the property, ensure that the desired subbox overlap the raster extent
if not self.bbox.overlap(subBoxGeo):
raise OverlapError()
elif self.bbox.isWithin(subBoxGeo):
#Ignore because subbox is greater than raster extent
return
else:
#convert the subbox in pixel coordinate space
xminPx, ymaxPx = self.pxFromGeo(subBoxGeo.xmin, subBoxGeo.ymin, round2Floor=True)#y pixels counting from top
xmaxPx, yminPx = self.pxFromGeo(subBoxGeo.xmax, subBoxGeo.ymax, round2Floor=True)#idem
subBoxPx = BBOX(xmin=xminPx, ymin=yminPx, xmax=xmaxPx, ymax=ymaxPx)#xmax and ymax include
#set the subbox
self.setSubBoxPx(subBoxPx)
def setSubBoxPx(self, subBoxPx):
if not self.bboxPx.overlap(subBoxPx):
raise OverlapError()
xminPx, xmaxPx = subBoxPx.xmin, subBoxPx.xmax
yminPx, ymaxPx = subBoxPx.ymin, subBoxPx.ymax
#adjust against raster size if needed
#we count pixel number from 0 but size represents total number of pixel (counting from 1), so we must use size-1
sizex, sizey = self.rSize
if xminPx < 0: xminPx = 0
if xmaxPx >= sizex: xmaxPx = sizex - 1
if yminPx < 0: yminPx = 0
if ymaxPx >= sizey: ymaxPx = sizey - 1
#get the adjusted geo coords at pixels center
xmin, ymin = self.geoFromPx(xminPx, ymaxPx)
xmax, ymax = self.geoFromPx(xmaxPx, yminPx)
#set the subbox
self.subBoxGeo = BBOX(xmin=xmin, ymin=ymin, xmax=xmax, ymax=ymax)
def applySubBox(self):
if self.subBoxGeo is not None:
self.rSize = self.subBoxPxSize
self.origin = self.subBoxGeoOrigin
self.subBoxGeo = None
def getSubBoxGeoRef(self):
return GeoRef(self.subBoxPxSize, self.pxSize, self.subBoxGeoOrigin, pxCenter=True, crs=self.crs)
@property
def subBoxPx(self):
'''return the subbox as bbox object in pixels coordinates space'''
if self.subBoxGeo is None:
return None
xmin, ymax = self.pxFromGeo(self.subBoxGeo.xmin, self.subBoxGeo.ymin, round2Floor=True)#y pixels counting from top
xmax, ymin = self.pxFromGeo(self.subBoxGeo.xmax, self.subBoxGeo.ymax, round2Floor=True)
return BBOX(xmin=xmin, ymin=ymin, xmax=xmax, ymax=ymax)#xmax and ymax include
@property
def subBoxPxSize(self):
'''dimension of the subbox in pixels'''
if self.subBoxGeo is None:
return None
bbpx = self.subBoxPx
w, h = bbpx.xmax - bbpx.xmin, bbpx.ymax - bbpx.ymin
return xy(w+1, h+1)
@property
def subBoxGeoSize(self):
'''dimension of the subbox in map units'''
if self.subBoxGeo is None:
return None
sizex, sizey = self.subBoxPxSize
return xy(sizex * abs(self.pxSize.x), sizey * abs(self.pxSize.y))
@property
def subBoxPxOrigin(self):
'''pixel coordinate of subbox origin'''
if self.subBoxGeo is None:
return None
return xy(self.subBoxPx.xmin, self.subBoxPx.ymin)
@property
def subBoxGeoOrigin(self):
'''geo coordinate of subbox origin, adjusted at pixel center'''
if self.subBoxGeo is None:
return None
return xy(self.subBoxGeo.xmin, self.subBoxGeo.ymax)
####
def __repr__(self):
s = [
' spatial ref system {}'.format(self.crs),
' origin geo {}'.format(self.origin),
' pixel size {}'.format(self.pxSize),
' rotation {}'.format(self.rotation),
' bounding box {}'.format(self.bbox),
' geoSize {}'.format(self.geoSize)
]
if self.subBoxGeo is not None:
s.extend([
' subbox origin (geo space) {}'.format(self.subBoxGeoOrigin),
' subbox origin (px space) {}'.format(self.subBoxPxOrigin),
' subbox (geo space) {}'.format(self.subBoxGeo),
' subbox (px space) {}'.format(self.subBoxPx),
' sub geoSize {}'.format(self.subBoxGeoSize),
' sub pxSize {}'.format(self.subBoxPxSize),
])
return '\n'.join(s)
================================================
FILE: core/georaster/img_utils.py
================================================
# -*- coding:utf-8 -*-
# This file is part of BlenderGIS
# ***** GPL LICENSE BLOCK *****
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# All rights reserved.
# ***** GPL LICENSE BLOCK *****
import struct
from ..lib import imghdr
def isValidStream(data):
if data is None:
return False
format = imghdr.what(None, data)
if format is None:
return False
return True
def getImgFormat(filepath):
"""
Read header of an image file and try to determine it's format
no requirements, support JPEG, JPEG2000, PNG, GIF, BMP, TIFF, EXR
"""
format = None
with open(filepath, 'rb') as fhandle:
head = fhandle.read(32)
# handle GIFs
if head[:6] in (b'GIF87a', b'GIF89a'):
format = 'GIF'
# handle PNG
elif head.startswith(b'\211PNG\r\n\032\n'):
format = 'PNG'
# handle JPEGs
#elif head[6:10] in (b'JFIF', b'Exif')
elif (b'JFIF' in head or b'Exif' in head or b'8BIM' in head) or head.startswith(b'\xff\xd8'):
format = 'JPEG'
# handle JPEG2000s
elif head.startswith(b'\x00\x00\x00\x0cjP \r\n\x87\n'):
format = 'JPEG2000'
# handle BMP
elif head.startswith(b'BM'):
format = 'BMP'
# handle TIFF
elif head[:2] in (b'MM', b'II'):
format = 'TIFF'
# handle EXR
elif head.startswith(b'\x76\x2f\x31\x01'):
format = 'EXR'
return format
def getImgDim(filepath):
"""
Return (width, height) for a given img file content
no requirements, support JPEG, JPEG2000, PNG, GIF, BMP
"""
width, height = None, None
with open(filepath, 'rb') as fhandle:
head = fhandle.read(32)
# handle GIFs
if head[:6] in (b'GIF87a', b'GIF89a'):
try:
width, height = struct.unpack("<hh", head[6:10])
except struct.error:
raise ValueError("Invalid GIF file")
# handle PNG
elif head.startswith(b'\211PNG\r\n\032\n'):
try:
width, height = struct.unpack(">LL", head[16:24])
except struct.error:
# Maybe this is for an older PNG version.
try:
width, height = struct.unpack(">LL", head[8:16])
except struct.error:
raise ValueError("Invalid PNG file")
# handle JPEGs
elif (b'JFIF' in head or b'Exif' in head or b'8BIM' in head) or head.startswith(b'\xff\xd8'):
try:
fhandle.seek(0) # Read 0xff next
size = 2
ftype = 0
while not 0xc0 <= ftype <= 0xcf:
fhandle.seek(size, 1)
byte = fhandle.read(1)
while ord(byte) == 0xff:
byte = fhandle.read(1)
ftype = ord(byte)
size = struct.unpack('>H', fhandle.read(2))[0] - 2
# We are at a SOFn block
fhandle.seek(1, 1) # Skip `precision' byte.
height, width = struct.unpack('>HH', fhandle.read(4))
except struct.error:
raise ValueError("Invalid JPEG file")
# handle JPEG2000s
elif head.startswith(b'\x00\x00\x00\x0cjP \r\n\x87\n'):
fhandle.seek(48)
try:
height, width = struct.unpack('>LL', fhandle.read(8))
except struct.error:
raise ValueError("Invalid JPEG2000 file")
# handle BMP
elif head.startswith(b'BM'):
imgtype = 'BMP'
try:
width, height = struct.unpack("<LL", head[18:26])
except struct.error:
raise ValueError("Invalid BMP file")
return width, height
================================================
FILE: core/georaster/npimg.py
================================================
# -*- coding:utf-8 -*-
# This file is part of BlenderGIS
# ***** GPL LICENSE BLOCK *****
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# All rights reserved.
# ***** GPL LICENSE BLOCK *****
import os
import io
import random
import numpy as np
from .georef import GeoRef
from ..proj.reproj import reprojImg
from ..maths.fillnodata import replace_nans #inpainting function (ie fill nodata)
from ..utils import XY as xy
from ..checkdeps import HAS_GDAL, HAS_PIL, HAS_IMGIO
from .. import settings
if HAS_PIL:
from PIL import Image
if HAS_GDAL:
from osgeo import gdal
if HAS_IMGIO:
from ..lib import imageio
class NpImage():
'''Represent an image as Numpy array'''
def _getIFACE(self):
engine = settings.img_engine
if engine == 'AUTO':
if HAS_GDAL:
return 'GDAL'
elif HAS_IMGIO:
return 'IMGIO'
elif HAS_PIL:
return 'PIL'
else:
raise ImportError("No image engine available")
elif engine == 'GDAL'and HAS_GDAL:
return 'GDAL'
elif engine == 'IMGIO' and HAS_IMGIO:
return 'IMGIO'
elif engine == 'PIL'and HAS_PIL:
return 'PIL'
else:
raise ImportError(str(engine) + " interface unavailable")
#GeoGef delegation by composition instead of inheritance
#this special method is called whenever the requested attribute or method is not found in the object
def __getattr__(self, attr):
if self.isGeoref:
return getattr(self.georef, attr)
else:#TODO raise specific msg if request for a georef attribute and not self.isgeoref
raise AttributeError(str(type(self)) + 'object has no attribute' + str(attr))
def __init__(self, data, subBoxPx=None, noData=None, georef=None, adjustGeoref=False):
'''
init from file path, bytes data, Numpy array, NpImage, PIL Image or GDAL dataset
subBoxPx : a BBOX object in pixel coordinates space used as data filter (will by applyed) (y counting from top)
noData : the value used to represent nodata, will be used to define a numpy mask
georef : a Georef object used to set georeferencing informations, optional
adjustGeoref: determine if the submited georef must be adjusted against the subbox or if its already correct
Notes :
* With GDAL the subbox filter can be applyed at reading level whereas with others imaging
library, all the data must be extracted before we can extract the subset (using numpy slice).
In this case, the dataset must fit entirely in memory otherwise it will raise an overflow error
* If no georef was submited and when the class is init using gdal support or from another npImage instance,
existing georef of input data will be automatically extracted and adjusted against the subbox
'''
self.IFACE = self._getIFACE()
self.data = None
self.subBoxPx = subBoxPx
self.noData = noData
self.georef = georef
if self.subBoxPx is not None and self.georef is not None:
if adjustGeoref:
self.georef.setSubBoxPx(subBoxPx)
self.georef.applySubBox()
#init from another NpImage instance
if isinstance(data, NpImage):
self.data = self._applySubBox(data.data)
if data.isGeoref and not self.isGeoref:
self.georef = data.georef
#adjust georef against subbox
if self.subBoxPx is not None:
self.georef.setSubBoxPx(subBoxPx)
self.georef.applySubBox()
#init from numpy array
if isinstance(data, np.ndarray):
self.data = self._applySubBox(data)
#init from bytes data (BLOB)
if isinstance(data, bytes):
self.data = self._npFromBLOB(data)
#init from file path
if isinstance(data, str):
if os.path.exists(data):
self.data = self._npFromPath(data)
else:
raise ValueError('Unable to load image data')
#init from GDAL dataset instance
if HAS_GDAL:
if isinstance(data, gdal.Dataset):
self.data = self._npFromGDAL(data)
#init from PIL Image instance
if HAS_PIL:
if isinstance(data, Image.Image):
self.data = self._npFromPIL(data)
if self.data is None:
raise ValueError('Unable to load image data')
#Mask nodata value to avoid bias when computing min or max statistics
if self.noData is not None:
self.data = np.ma.masked_array(self.data, self.data == self.noData)
@property
def size(self):
return xy(self.data.shape[1], self.data.shape[0])
@property
def isGeoref(self):
'''Flag if georef parameters have been extracted'''
if self.georef is not None:
return True
else:
return False
@property
def nbBands(self):
if len(self.data.shape) == 2:
return 1
elif len(self.data.shape) == 3:
return self.data.shape[2]
@property
def hasAlpha(self):
return self.nbBands == 4
@property
def isOneBand(self):
return self.nbBands == 1
@property
def dtype(self):
'''return string ['int8', 'uint8', 'int16', 'uint16', 'int32', 'uint32', 'float32', 'float64']'''
return self.data.dtype
@property
def isFloat(self):
if self.dtype in ['float16', 'float32', 'float64']:
return True
else:
return False
def getMin(self, bandIdx=0):
if self.nbBands == 1:
return self.data.min()
else:
return self.data[:,:,bandIdx].min()
def getMax(self, bandIdx=0):
if self.nbBands == 1:
return self.data.max()
else:
return self.data[:,:,bandIdx].max()
@classmethod
def new(cls, w, h, bkgColor=(255,255,255,255), noData=None, georef=None):
r, g, b, a = bkgColor
data = np.empty((h, w, 4), np.uint8)
data[:,:,0] = r
data[:,:,1] = g
data[:,:,2] = b
data[:,:,3] = a
return cls(data, noData=noData, georef=georef)
def _applySubBox(self, data):
'''Use numpy slice to extract subset of data'''
if self.subBoxPx is not None:
x1, x2 = self.subBoxPx.xmin, self.subBoxPx.xmax+1
y1, y2 = self.subBoxPx.ymin, self.subBoxPx.ymax+1
if len(data.shape) == 2: #one band
data = data[y1:y2, x1:x2]
else:
data = data[y1:y2, x1:x2, :]
self.subBoxPx = None
return data
def _npFromPath(self, path):
'''Get Numpy array from a file path'''
if self.IFACE == 'PIL':
img = Image.open(path)
return self._npFromPIL(img)
elif self.IFACE == 'IMGIO':
return self._npFromImgIO(path)
elif self.IFACE == 'GDAL':
ds = gdal.Open(path)
return self._npFromGDAL(ds)
def _npFromBLOB(self, data):
'''Get Numpy array from Bytes data'''
if self.IFACE == 'PIL':
#convert bytes object to bytesio (stream buffer) and open it with PIL
img = Image.open(io.BytesIO(data))
data = self._npFromPIL(img)
elif self.IFACE == 'IMGIO':
img = io.BytesIO(data)
data = self._npFromImgIO(img)
elif self.IFACE == 'GDAL':
#Use a virtual memory file to create gdal dataset from buffer
#build a random name to make the function thread safe
vsipath = '/vsimem/' + ''.join(random.choice('abcdefghijklmnopqrstuvwxyz') for i in range(5))
gdal.FileFromMemBuffer(vsipath, data)
ds = gdal.Open(vsipath)
data = self._npFromGDAL(ds)
ds = None
gdal.Unlink(vsipath)
return data
def _npFromImgIO(self, img):
'''Use ImageIO to extract numpy array from image path or bytesIO'''
data = imageio.imread(img)
return self._applySubBox(data)
def _npFromPIL(self, img):
'''Get Numpy array from PIL Image instance'''
if img.mode == 'P': #palette (indexed color)
img = img.convert('RGBA')
data = np.asarray(img)
#data.setflags(write=True) #PIL return a non writable array
return self._applySubBox(data)
def _npFromGDAL(self, ds):
'''Get Numpy array from GDAL dataset instance'''
if self.subBoxPx is not None:
startx, starty = self.subBoxPx.xmin, self.subBoxPx.ymin
width = (self.subBoxPx.xmax - self.subBoxPx.xmin) + 1
height = (self.subBoxPx.ymax - self.subBoxPx.ymin) + 1
data = ds.ReadAsArray(startx, starty, width, height)
else:
data = ds.ReadAsArray()
if len(data.shape) == 3: #multiband
data = np.rollaxis(data, 0, 3) # because first axis is band index
else: #one band raster or indexed color (= palette = pseudo color table (pct))
ctable = ds.GetRasterBand(1).GetColorTable()
if ctable is not None:
#Swap index values to their corresponding color (rgba)
nbColors = ctable.GetCount()
keys = np.array( [i for i in range(nbColors)] )
values = np.array( [ctable.GetColorEntry(i) for i in range(nbColors)] )
sortIdx = np.argsort(keys)
idx = np.searchsorted(keys, data, sorter=sortIdx)
data = values[sortIdx][idx]
#Try to extract georef
if not self.isGeoref:
self.georef = GeoRef.fromGDAL(ds)
#adjust georef against subbox
if self.subBoxPx is not None and self.georef is not None:
self.georef.applySubBox()
return data
def toBLOB(self, ext='PNG'):
'''Get bytes raw data'''
if ext == 'JPG':
ext = 'JPEG'
if self.IFACE == 'PIL':
b = io.BytesIO()
img = Image.fromarray(self.data)
img.save(b, format=ext)
data = b.getvalue() #convert bytesio to bytes
elif self.IFACE == 'IMGIO':
if ext == 'JPEG' and self.hasAlpha:
self.removeAlpha()
data = imageio.imwrite(imageio.RETURN_BYTES, self.data, format=ext)
elif self.IFACE == 'GDAL':
mem = self.toGDAL()
#build a random name to make the function thread safe
name = ''.join(random.choice('abcdefghijklmnopqrstuvwxyz') for i in range(5))
vsiname = '/vsimem/' + name + '.png'
out = gdal.GetDriverByName(ext).CreateCopy(vsiname, mem)
# Read /vsimem/output.png
f = gdal.VSIFOpenL(vsiname, 'rb')
gdal.VSIFSeekL(f, 0, 2) # seek to end
size = gdal.VSIFTellL(f)
gdal.VSIFSeekL(f, 0, 0) # seek to beginning
data = gdal.VSIFReadL(1, size, f)
gdal.VSIFCloseL(f)
# Cleanup
gdal.Unlink(vsiname)
mem = None
return data
def toPIL(self):
'''Get PIL Image instance'''
return Image.fromarray(self.data)
def toGDAL(self):
'''Get GDAL memory driver dataset'''
w, h = self.size
n = self.nbBands
dtype = str(self.dtype)
if dtype == 'uint8': dtype = 'byte'
dtype = gdal.GetDataTypeByName(dtype)
mem = gdal.GetDriverByName('MEM').Create('', w, h, n, dtype)
#writearray is available only at band level
if self.isOneBand:
mem.GetRasterBand(1).WriteArray(self.data)
else:
for bandIdx in range(n):
bandArray = self.data[:,:,bandIdx]
mem.GetRasterBand(bandIdx+1).WriteArray(bandArray)
#write georef
if self.isGeoref:
mem.SetGeoTransform(self.georef.toGDAL())
if self.georef.crs is not None:
mem.SetProjection(self.georef.crs.getOgrSpatialRef().ExportToWkt())
return mem
def removeAlpha(self):
if self.hasAlpha:
self.data = self.data[:, :, 0:3]
def addAlpha(self, opacity=255):
if self.nbBands == 3:
w, h = self.size
alpha = np.empty((h,w), dtype=self.dtype)
alpha.fill(opacity)
alpha = np.expand_dims(alpha, axis=2)
self.data = np.append(self.data, alpha, axis=2)
def save(self, path):
'''
save the numpy array to a new image file
output format is defined by path extension
'''
imgFormat = path[-3:]
if self.IFACE == 'PIL':
self.toPIL().save(path)
elif self.IFACE == 'IMGIO':
if imgFormat == 'jpg' and self.hasAlpha:
self.removeAlpha()
imageio.imwrite(path, self.data)#float32 support ok
elif self.IFACE == 'GDAL':
if imgFormat == 'png':
driver = 'PNG'
elif imgFormat == 'jpg':
driver = 'JPEG'
elif imgFormat == 'tif':
driver = 'Gtiff'
else:
raise ValueError('Cannot write to '+ imgFormat + ' image format')
#Some format like jpg or png has no create method implemented
#because we can't write data at random with these formats
#so we must use an intermediate memory driver, write data to it
#and then write the output file with the createcopy method
mem = self.toGDAL()
out = gdal.GetDriverByName(driver).CreateCopy(path, mem)
mem = out = None
if self.isGeoref:
self.georef.toWorldFile(os.path.splitext(path)[0] + '.wld')
def paste(self, data, x, y):
img = NpImage(data)
data = img.data
w, h = img.size
if img.isOneBand and self.isOneBand:
self.data[y:y+h, x:x+w] = data
elif (not img.isOneBand and self.isOneBand) or (img.isOneBand and not self.isOneBand):
raise ValueError('Paste error, cannot mix one band with multiband')
if self.hasAlpha:
n = img.nbBands
self.data[y:y+h, x:x+w, 0:n] = data
else:
n = self.nbBands
self.data[y:y+h, x:x+w, :] = data[:, :, 0:n]
def cast2float(self):
if not self.isFloat:
self.data = self.data.astype('float32')
def fillNodata(self):
#if not self.noData in self.data:
if not np.ma.is_masked(self.data):
#do not process it if its not necessary
return
if self.IFACE == 'GDAL':
# gdal.FillNodata need a band object to apply on
# so we create a memory datasource (1 band, float)
height, width = self.data.shape
ds = gdal.GetDriverByName('MEM').Create('', width, height, 1, gdal.GetDataTypeByName('float32'))
b = ds.GetRasterBand(1)
b.SetNoDataValue(self.noData)
self.data = np.ma.filled(self.data, self.noData)# Fill mask with nodata value
b.WriteArray(self.data)
gdal.FillNodata(targetBand=b, maskBand=None, maxSearchDist=max(self.size.xy), smoothingIterations=0)
self.data = b.ReadAsArray()
ds, b = None, None
else: #Call the inpainting function
# Cast to float
self.cast2float()
# Fill mask with NaN (warning NaN is a special value for float arrays only)
self.data = np.ma.filled(self.data, np.NaN)
# Inpainting
self.data = replace_nans(self.data, max_iter=5, tolerance=0.5, kernel_size=2, method='localmean')
def reproj(self, crs1, crs2, out_ul=None, out_size=None, out_res=None, sqPx=False, resamplAlg='BL'):
ds1 = self.toGDAL()
if not self.isGeoref:
raise IOError('Unable to reproject non georeferenced image')
ds2 = reprojImg(crs1, crs2, ds1, out_ul=out_ul, out_size=out_size, out_res=out_res, sqPx=sqPx, resamplAlg=resamplAlg)
return NpImage(ds2)
def __repr__(self):
return '\n'.join([
"* Data infos :",
" size {}".format(self.size),
" type {}".format(self.dtype),
" number of bands {}".format(self.nbBands),
" nodata value {}".format(self.noData),
"* Statistics : min {} max {}".format(self.getMin(), self.getMax()),
"* Georef & Geometry : \n{}".format(self.georef)
])
================================================
FILE: core/lib/Tyf/VERSION
================================================
1.2.5
================================================
FILE: core/lib/Tyf/__init__.py
================================================
# -*- encoding:utf-8 -*-
__copyright__ = "Copyright © 2012-2015, THOORENS Bruno - http://bruno.thoorens.free.fr/licences/tyf.html"
__author__ = "THOORENS Bruno"
__tiff__ = (6, 0)
__geotiff__ = (1, 8, 1)
import io, os, sys, struct, operator, collections
__PY3__ = True if sys.version_info[0] >= 3 else False
unpack = lambda fmt, fileobj: struct.unpack(fmt, fileobj.read(struct.calcsize(fmt)))
pack = lambda fmt, fileobj, value: fileobj.write(struct.pack(fmt, *value))
TYPES = {
1: ("B", "UCHAR or USHORT"),
2: ("c", "ASCII"),
3: ("H", "UBYTE"),
4: ("L", "ULONG"),
5: ("LL", "URATIONAL"),
6: ("b", "CHAR or SHORT"),
7: ("c", "UNDEFINED"),
8: ("h", "BYTE"),
9: ("l", "LONG"),
10: ("ll", "RATIONAL"),
11: ("f", "FLOAT"),
12: ("d", "DOUBLE"),
}
# assure compatibility python 2 & 3
if __PY3__:
from io import BytesIO as StringIO
TYPES[2] = ("s", "ASCII")
TYPES[7] = ("s", "UDEFINED")
import functools
reduce = functools.reduce
long = int
import urllib.request as urllib
else:
from StringIO import StringIO
import urllib
reduce = __builtins__["reduce"]
from . import ifd, gkd, tags
def _read_IFD(obj, fileobj, offset, byteorder="<"):
# fileobj seek must be on the start offset
fileobj.seek(offset)
# get number of entry
nb_entry, = unpack(byteorder+"H", fileobj)
# for each entry
for i in range(nb_entry):
# read tag, type and count values
tag, typ, count = unpack(byteorder+"HHL", fileobj)
# extract data
data = fileobj.read(struct.calcsize("=L"))
if not isinstance(data, bytes):
data = data.encode()
_typ = TYPES[typ][0]
# create a tifftag
tt = ifd.TiffTag(tag, typ, name=obj.tagname)
# initialize what we already know
# tt.type = typ
tt.count = count
# to know if ifd entry value is an offset
tt._determine_if_offset()
# if value is offset
if tt.value_is_offset:
# read offset value
value, = struct.unpack(byteorder+"L", data)
fmt = byteorder + _typ*count
bckp = fileobj.tell()
# go to offset in the file
fileobj.seek(value)
# if ascii type, convert to bytes
if typ == 2: tt.value = b"".join(e for e in unpack(fmt, fileobj))
# else if undefined type, read data
elif typ == 7: tt.value = fileobj.read(count)
# else unpack data
else: tt.value = unpack(fmt, fileobj)
# go back to ifd entry
fileobj.seek(bckp)
# if value is in the ifd entry
else:
if typ in [2, 7]:
tt.value = data[:count]
else:
fmt = byteorder + _typ*count
tt.value = struct.unpack(fmt, data[:count*struct.calcsize("="+_typ)])
obj.addtag(tt)
def from_buffer(obj, fileobj, offset, byteorder="<", custom_sub_ifd={}):
# read data from offset
_read_IFD(obj, fileobj, offset, byteorder)
# get next ifd offset
next_ifd, = unpack(byteorder+"L", fileobj)
# finding by default those SubIFD
sub_ifd = {34665:"Exif tag", 34853:"GPS tag", 40965:"Interoperability tag"}
# adding other SubIFD if asked
sub_ifd.update(custom_sub_ifd)
## read registered SubIFD
for key,value in sub_ifd.items():
if key in obj:
obj.sub_ifd[key] = ifd.Ifd(tagname=value)
_read_IFD(obj.sub_ifd[key], fileobj, obj[key], byteorder)
return next_ifd
# for speed reason : load raster only if asked or if needed
def _load_raster(obj, fileobj):
# striped raster data
if 273 in obj:
for offset,bytecount in zip(obj.get(273).value, obj.get(279).value):
fileobj.seek(offset)
obj.stripes += (fileobj.read(bytecount), )
# free raster data
elif 288 in obj:
for offset,bytecount in zip(obj.get(288).value, obj.get(289).value):
fileobj.seek(offset)
obj.free += (fileobj.read(bytecount), )
# tiled raster data
elif 324 in obj:
for offset,bytecount in zip(obj.get(324).value, obj.get(325).value):
fileobj.seek(offset)
obj.tiles += (fileobj.read(bytecount), )
# get interExchange (thumbnail data for JPEG/EXIF data)
if 513 in obj:
fileobj.seek(obj[513])
obj.jpegIF = fileobj.read(obj[514])
def _write_IFD(obj, fileobj, offset, byteorder="<"):
# go where obj have to be written
fileobj.seek(offset)
# sort data to be writen
tags = sorted(list(dict.values(obj)), key=lambda e:e.tag)
# write number of entries
pack(byteorder+"H", fileobj, (len(tags),))
first_entry_offset = fileobj.tell()
# write all ifd entries
for t in tags:
# write tag, type & count
pack(byteorder+"HHL", fileobj, (t.tag, t.type, t.count))
# if value is not an offset
if not t.value_is_offset:
value = t._fill()
n = len(value)
if __PY3__ and t.type in [2, 7]:
fmt = str(n)+TYPES[t.type][0]
value = (value,)
else:
fmt = n*TYPES[t.type][0]
pack(byteorder+fmt, fileobj, value)
else:
pack(byteorder+"L", fileobj, (0,))
next_ifd_offset = fileobj.tell()
pack(byteorder+"L", fileobj, (0,))
# prepare jumps
data_offset = fileobj.tell()
step1 = struct.calcsize("=HHLL")
step2 = struct.calcsize("=HHL")
# comme back to first ifd entry
fileobj.seek(first_entry_offset)
for t in tags:
# for each tag witch value needs offset
if t.value_is_offset:
# go to offset value location (jump over tag, type, count)
fileobj.seek(step2, 1)
# write offset where value is about to be stored
pack(byteorder+"L", fileobj, (data_offset,))
# remember where i am in ifd entries
bckp = fileobj.tell()
# go to offset where value is about to be stored
fileobj.seek(data_offset)
# prepare value according to python version
if __PY3__ and t.type in [2, 7]:
fmt = str(t.count)+TYPES[t.type][0]
value = (t.value,)
else:
fmt = t.count*TYPES[t.type][0]
value = t.value
# write value
# print(">>>", fmt, value)
pack(byteorder+fmt, fileobj, value)
# remmember where to put next value
data_offset = fileobj.tell()
# go to where I was in ifd entries
fileobj.seek(bckp)
else:
fileobj.seek(step1, 1)
return next_ifd_offset
def to_buffer(obj, fileobj, offset, byteorder="<"):
obj._check()
size = obj.size
raw_offset = offset + size["ifd"] + size["data"]
# add SubIFD sizes...
for tag, p_ifd in sorted(obj.sub_ifd.items(), key=lambda e:e[0]):
obj.set(tag, 4, raw_offset)
size = p_ifd.size
raw_offset = raw_offset + size["ifd"] + size["data"]
# knowing where raw image have to be writen, update [Strip/Free/Tile]Offsets
if 273 in obj:
_279 = obj.get(279).value
stripoffsets = (raw_offset,)
for bytecount in _279[:-1]:
stripoffsets += (stripoffsets[-1]+bytecount, )
obj.set(273, 4, stripoffsets)
next_ifd = stripoffsets[-1] + _279[-1]
elif 288 in obj:
_289 = obj.get(289).value
freeoffsets = (raw_offset,)
for bytecount in _289[:-1]:
freeoffsets += (freeoffsets[-1]+bytecount, )
obj.set(288, 4, freeoffsets)
next_ifd = freeoffsets[-1] + _289[-1]
elif 324 in obj:
_325 = obj.get(325).value
tileoffsets = (raw_offset,)
for bytecount in _325[:-1]:
tileoffsets += (tileoffsets[-1]+bytecount, )
obj.set(324, 4, tileoffsets)
next_ifd = tileoffsets[-1] + _325[-1]
elif 513 in obj:
interexchangeoffset = raw_offset
obj.set(513, 4, raw_offset)
next_ifd = interexchangeoffset + obj[514]
else:
next_ifd = raw_offset
# write IFD
next_ifd_offset = _write_IFD(obj, fileobj, offset, byteorder)
# write SubIFD
for tag, p_ifd in sorted(obj.sub_ifd.items(), key=lambda e:e[0]):
_write_IFD(p_ifd, fileobj, obj[tag], byteorder)
# write raster data
if len(obj.stripes):
for offset,data in zip(stripoffsets, obj.stripes):
fileobj.seek(offset)
fileobj.write(data)
elif len(obj.free):
for offset,data in zip(freeoffsets, obj.stripes):
fileobj.seek(offset)
fileobj.write(data)
elif len(obj.tiles):
for offset,data in zip(tileoffsets, obj.tiles):
fileobj.seek(offset)
fileobj.write(data)
elif obj.jpegIF != b"":
fileobj.seek(interexchangeoffset)
fileobj.write(obj.jpegIF)
fileobj.seek(next_ifd_offset)
return next_ifd
def _fileobj(f, mode):
if hasattr(f, "close"):
fileobj = f
_close = False
else:
fileobj = io.open(f, mode)
_close = True
return fileobj, _close
class TiffFile(list):
gkd = property(lambda obj: [gkd.Gkd(ifd) for ifd in obj], None, None, "list of geotiff directory")
has_raster = property(lambda obj: reduce(operator.__or__, [ifd.has_raster for ifd in obj]), None, None, "")
raster_loaded = property(lambda obj: reduce(operator.__and__, [ifd.raster_loaded for ifd in obj]), None, None, "")
def __init__(self, fileobj):
# Initialize a TiffFile object from buffer fileobj, fileobj have to be in 'wb' mode
# determine byteorder
first, = unpack(">H", fileobj)
byteorder = "<" if first == 0x4949 else ">"
magic_number, = unpack(byteorder+"H", fileobj)
if magic_number not in [0x732E,0x2A]: #29486, 42
fileobj.close()
if magic_number == 0x2B: # 43
raise IOError("BigTIFF file not supported")
else:
raise IOError("Bad magic number. Not a valid TIFF file")
next_ifd, = unpack(byteorder+"L", fileobj)
ifds = []
while next_ifd != 0:
i = ifd.Ifd(sub_ifd={
34665:[tags.exfT,"Exif tag"],
34853:[tags.gpsT,"GPS tag"]
})
next_ifd = from_buffer(i, fileobj, next_ifd, byteorder)
ifds.append(i)
if hasattr(fileobj, "name"):
self._filename = fileobj.name
else:
for i in ifds:
_load_raster(i, fileobj)
list.__init__(self, ifds)
def __getitem__(self, item):
if isinstance(item, tuple): return list.__getitem__(self, item[0])[item[-1]]
else: return list.__getitem__(self, item)
def __add__(self, value):
self.load_raster()
if isinstance(value, TiffFile):
value.load_raster()
for i in value: self.append(i)
elif isinstance(value, ifd.Ifd):
self.append(value)
return self
__iadd__ = __add__
def load_raster(self, idx=None):
if hasattr(self, "_filename"):
in_ = io.open(self._filename, "rb")
for ifd in iter(self) if idx == None else [self[idx]]:
if not ifd.raster_loaded: _load_raster(ifd, in_)
in_.close()
def save(self, f, byteorder="<", idx=None):
self.load_raster()
fileobj, _close = _fileobj(f, "wb")
pack(byteorder+"HH", fileobj, (0x4949 if byteorder == "<" else 0x4d4d, 0x2A,))
next_ifd = 8
for i in iter(self) if idx == None else [self[idx]]:
pack(byteorder+"L", fileobj, (next_ifd,))
next_ifd = to_buffer(i, fileobj, next_ifd, byteorder)
if _close: fileobj.close()
class JpegFile(collections.OrderedDict):
jfif = property(lambda obj: collections.OrderedDict.__getitem__(obj, 0xffe0), None, None, "JFIF data")
exif = property(lambda obj: collections.OrderedDict.__getitem__(obj, 0xffe1)[0], None, None, "Image IFD")
ifd1 = property(lambda obj: collections.OrderedDict.__getitem__(obj, 0xffe1)[1], None, None, "Thumbnail IFD")
def __init__(self, fileobj):
markers = collections.OrderedDict()
marker, = unpack(">H", fileobj)
if marker != 0xffd8: raise Exception("not a valid jpeg file")
while marker != 0xffd9: # EOI (End Of Image) Marker
marker, count = unpack(">HH", fileobj)
# here is raster data marker, copy all after marker id
if marker == 0xffda:
fileobj.seek(-2, 1)
markers[0xffda] = fileobj.read()[:-2]
# say it is the end of the file
marker = 0xffd9
elif marker == 0xffe1:
string = StringIO(fileobj.read(count-2)[6:])
try: markers[marker] = TiffFile(string)
except: setattr(markers, "_0xffe1", string.getvalue())
string.close()
else:
markers[marker] = fileobj.read(count-2)
collections.OrderedDict.__init__(self, markers)
def __getitem__(self, item):
try: return collections.OrderedDict.__getitem__(self, 0xffe1)[0,item]
except KeyError: return collections.OrderedDict.__getitem__(self, item)
def _pack(self, marker, fileobj):
data = self[marker]
if marker == 0xffda:
pack(">H", fileobj, (marker,))
elif marker == 0xffe1:
string = StringIO()
self[marker].save(string)
data = b"Exif\x00\x00" + string.getvalue()
pack(">HH", fileobj, (marker, len(data) + 2))
string.close()
else:
pack(">HH", fileobj, (marker, len(data) + 2))
fileobj.write(data)
def save(self, f):
fileobj, _close = _fileobj(f, "wb")
pack(">H", fileobj, (0xffd8,))
for key in self: self._pack(key, fileobj)
pack(">H", fileobj, (0xffd9,))
if _close: fileobj.close()
def save_thumbnail(self, f):
try:
ifd = self.ifd1
except IndexError:
pass
else:
compression = ifd[259]
if hasattr(f, "close"):
fileobj = f
_close = False
else:
fileobj = io.open(os.path.splitext(f)[0] + (".jpg" if compression == 6 else ".tif"), "wb")
_close = True
if compression == 6:
fileobj.write(ifd.jpegIF)
elif compression == 1:
self[0xffe1].save(fileobj, idx=1)
if _close: fileobj.close()
def dump_exif(self, f):
fileobj, _close = _fileobj(f, "wb")
self[0xffe1].save(fileobj)
if _close: fileobj.close()
def load_exif(self, f):
fileobj, _close = _fileobj(f, "rb")
self[0xffe1] = TiffFile(fileobj)
self[0xffe1].load_raster()
if _close: fileobj.close()
def strip_exif(self):
for key in [k for k in self.exif.sub_ifd if k in self.exif]:
self.exif.pop(key)
self.exif.sub_ifd = {}
for key in list(k for k in self.exif if k not in tags.bTT):
self.exif.pop(key)
while len(self[0xffe1]) > 1:
self[0xffe1].pop(-1)
def jpeg_extract(f):
fileobj, _close = _fileobj(f, "rb")
ifd = False
marker, = unpack(">H", fileobj)
if marker != 0xffd8: raise Exception("not a valid jpeg file")
while marker != 0xffd9:
marker, count = unpack(">HH", fileobj)
if marker == 0xffe1:
string = StringIO(fileobj.read(count-2)[6:])
ifd = TiffFile(string)
string.close()
marker = 0xffd9
else:
fileobj.read(count-2)
if _close: fileobj.close()
return ifd
def open(f):
fileobj, _close = _fileobj(f, "rb")
first, = unpack(">H", fileobj)
fileobj.seek(0)
if first == 0xffd8: obj = JpegFile(fileobj)
elif first in [0x4d4d, 0x4949]: obj = TiffFile(fileobj)
if _close: fileobj.close()
try: return obj
except: raise Exception("file is not a valid JPEG nor TIFF image")
'''
# if PIL exists do some overridings
try: from PIL import Image as _Image
except ImportError: pass
else:
def _getexif(im):
try:
data = im.info["exif"]
except KeyError:
return None
fileobj = io.BytesIO(data[6:])
exif = TiffFile(fileobj)
fileobj.close()
return exif
class Image(_Image.Image):
_image_ = _Image.Image
@staticmethod
def open(*args, **kwargs):
return _Image.open(*args, **kwargs)
def save(self, fp, format="JPEG", **params):
ifd = params.pop("ifd", False)
if ifd != False:
fileobj = StringIO()
if isinstance(ifd, TiffFile):
ifd.load_raster()
ifd.save(fileobj)
elif isinstance(ifd, JpegFile):
ifd[0xffe1].save(fileobj)
data = fileobj.getvalue()
fileobj.close()
if len(data) > 0:
params["exif"] = b"Exif\x00\x00" + (data.encode() if isinstance(data, str) else data)
Image._image_.save(self, fp, format="JPEG", **params)
_Image.Image = Image
from PIL import JpegImagePlugin
JpegImagePlugin._getexif = _getexif
del _getexif
'''
================================================
FILE: core/lib/Tyf/decoders.py
================================================
# -*- encoding:utf-8 -*-
# Copyright 2012-2015, THOORENS Bruno - http://bruno.thoorens.free.fr/licences/tyf.html
import datetime
###############
# type decoders
_1 = _3 = _4 = _6 = _8 = _9 = _11 = _12 = lambda value: value[0] if len(value) == 1 else value
_2 = lambda value: value[:-1]
def _5(value):
result = tuple((float(n)/(1 if d==0 else d)) for n,d in zip(value[0::2], value[1::2]))
return result[0] if len(result) == 1 else result
_7 = lambda value: value
_10 = _5
#######################
# Tag-specific decoders
# XPTitle XPComment XBAuthor
_0x9c9b = _0x9c9c = _0x9c9d = lambda value : "".join(chr(e) for e in value[0::2]).encode()[:-1]
# UserComment GPSProcessingMethod
_0x9286 = _0x1b = lambda value: value[8:]
#GPSLatitudeRef
_0x1 = lambda value: 1 if value in [b"N\x00", b"N"] else -1
#GPSLatitude
def _0x2(value):
degrees, minutes, seconds = _5(value)
return (seconds/60 + minutes)/60 + degrees
#GPSLatitudeRef
_0x3 = lambda value: 1 if value in [b"E\x00", b"E"] else -1
#GPSLongitude
_0x4 = _0x2
#GPSAltitudeRef
_0x5 = lambda value: 1 if value == 0 else -1
# GPSTimeStamp
_0x7 = lambda value: datetime.time(*[int(e) for e in _5(value)])
# GPSDateStamp
_0x1d = lambda value: datetime.datetime.strptime(_2(value).decode(), "%Y:%m:%d")
# DateTime DateTimeOriginal DateTimeDigitized
_0x132 = _0x9003 = _0x9004 = lambda value: datetime.datetime.strptime(_2(value).decode(), "%Y:%m:%d %H:%M:%S")
================================================
FILE: core/lib/Tyf/encoders.py
================================================
# -*- encoding:utf-8 -*-
# Copyright 2012-2015, THOORENS Bruno - http://bruno.thoorens.free.fr/licences/tyf.html
from . import reduce
import math, fractions, datetime
###############
# type encoders
_m_short = 0
_M_short = 2**8
def _1(value):
value = int(value)
return (_m_short, ) if value < _m_short else \
(_M_short, ) if value > _M_short else \
(value, )
def _2(value):
if not isinstance(value, bytes):
value = value.encode()
value += b"\x00" if value[-1] != b"\x00" else ""
return value
_m_byte = 0
_M_byte = 2**16
def _3(value):
value = int(value)
return (_m_byte, ) if value < _m_byte else \
(_M_byte, ) if value > _M_byte else \
(value, )
_m_long = 0
_M_long = 2**32
def _4(value):
value = int(value)
return (_m_long, ) if value < _m_long else \
(_M_long, ) if value > _M_long else \
(value, )
def _5(value):
if not isinstance(value, tuple): value = (value, )
return reduce(tuple.__add__, [(f.numerator, f.denominator) for f in [fractions.Fraction(str(v)).limit_denominator(10000000) for v in value]])
_m_s_short = -_M_short/2
_M_s_short = _M_short/2-1
def _6(value):
value = int(value)
return (_m_s_short, ) if value < _m_s_short else \
(_M_s_short, ) if value > _M_s_short else \
(value, )
def _7(value):
if not isinstance(value, bytes):
value = value.encode()
return value
_m_s_byte = -_M_byte/2
_M_s_byte = _M_byte/2-1
def _8(value):
value = int(value)
return (_m_s_byte, ) if value < _m_s_byte else \
(_M_s_byte, ) if value > _M_s_byte else \
(value, )
_m_s_long = -_M_long/2
_M_s_long = _M_long/2-1
def _9(value):
value = int(value)
return (_m_s_long, ) if value < _m_s_long else \
(_M_s_long, ) if value > _M_s_long else \
(value, )
_10 = _5
def _11(value):
return (float(value), )
_12 = _11
#######################
# Tag-specific encoders
# XPTitle XPComment XBAuthor
_0x9c9b = _0x9c9c = _0x9c9d = lambda value : reduce(tuple.__add__, [(ord(e), 0) for e in value])
# UserComment GPSProcessingMethod
_0x9286 = _0x1b = lambda value: b"ASCII\x00\x00\x00" + (value.encode() if not isinstance(value, bytes) else value)
# GPSLatitudeRef
_0x1 = lambda value: b"N\x00" if bool(value >= 0) == True else b"S\x00"
# GPSLatitude
def _0x2(value):
value = abs(value)
degrees = math.floor(value)
minutes = (value - degrees) * 60
seconds = (minutes - math.floor(minutes)) * 60
minutes = math.floor(minutes)
if seconds >= (60.-0.0001):
seconds = 0.
minutes += 1
if minutes >= (60.-0.0001):
minutes = 0.
degrees += 1
return _5((degrees, minutes, seconds))
#GPSLongitudeRef
_0x3 = lambda value: b"E\x00" if bool(value >= 0) == True else b"W\x00"
#GPSLongitude
_0x4 = _0x2
#GPSAltitudeRef
_0x5 = lambda value: _3(1 if value < 0 else 0)
#GPSAltitude
_0x6 = lambda value: _5(abs(value))
# GPSTimeStamp
_0x7 = lambda value: _5(tuple(float(e) for e in [value.hour, value.minute, value.second]))
# GPSDateStamp
_0x1d = lambda value: _2(value.strftime("%Y:%m:%d"))
# DateTime DateTimeOriginal DateTimeDigitized
_0x132 = _0x9003 = _0x9004 = lambda value: _2(value.strftime("%Y:%m:%d %H:%M:%S"))
================================================
FILE: core/lib/Tyf/gkd.py
================================================
# -*- encoding: utf-8 -*-
# Copyright 2012-2015, THOORENS Bruno - http://bruno.thoorens.free.fr/licences/tyf.html
# ~ http://www.remotesensing.org/geotiff/spec/geotiffhome.html
from . import ifd, tags, values, __geotiff__, __PY3__
import collections
GeoKeyModel = {
33550: collections.namedtuple("ModelPixelScale", "ScaleX, ScaleY, ScaleZ"),
33922: collections.namedtuple("ModelTiepoint", "I,J,K,X,Y,Z"),
34264: collections.namedtuple("ModelTransformation", "a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p")
}
def Transform(obj, x=0., y=0., z1=0.,z2=1.):
return (
obj[0] * x + obj[1] * y + obj[2] * z1 + obj[3] * z2,
obj[4] * x + obj[5] * y + obj[6] * z1 + obj[7] * z2,
obj[8] * x + obj[9] * y + obj[10] * z1 + obj[11] * z2,
obj[12] * x + obj[13] * y + obj[14] * z1 + obj[15] * z2
)
_TAGS = {
# GeoTIFF Configuration GeoKeys
1024: ("GTModelTypeGeoKey", [3], 0, None),
1025: ("GTRasterTypeGeoKey", [3], 1, None),
1026: ("GTCitationGeoKey", [2], None, None), # ASCII text
# Geographic CS Parameter GeoKeys
2048: ("GeographicTypeGeoKey", [3], 4326, None), # epsg datum code [4001 - 4999]
2049: ("GeogCitationGeoKey", [2], None, None), # ASCII text
2050: ("GeogGeodeticDatumGeoKey", [3], None, None), # use 2048 !
2051: ("GeogPrimeMeridianGeoKey", [3], 8901, None), # epsg prime meridian code [8001 - 8999]
2052: ("GeogLinearUnitsGeoKey", [3], 9001, None), # epsg linear unit code [9000 - 9099]
2053: ("GeogLinearUnitSizeGeoKey", [12], None, None), # custom unit in meters
2054: ("GeogAngularUnitsGeoKey", [3], 9101, None),
2055: ("GeogAngularUnitsSizeGeoKey", [12], None, None), # custom unit in radians
2056: ("GeogEllipsoidGeoKey", [3], None, None), # epsg ellipsoid code [7000 - 7999]
2057: ("GeogSemiMajorAxisGeoKey", [12], None, None),
2058: ("GeogSemiMinorAxisGeoKey", [12], None, None),
2059: ("GeogInvFlatteningGeoKey", [12], None, None),
2060: ("GeogAzimuthUnitsGeoKey",[3], None, None),
2061: ("GeogPrimeMeridianLongGeoKey", [12], None, None), # custom prime meridian value in GeogAngularUnits
# Projected CS Parameter GeoKeys
3072: ("ProjectedCSTypeGeoKey", [3], None, None), # epsg grid code [20000 - 32760]
3073: ("PCSCitationGeoKey", [2], None, None), # ASCII text
3074: ("ProjectionGeoKey", [3], None, None), # [10000 - 19999]
3075: ("ProjCoordTransGeoKey", [3], None, None),
3076: ("ProjLinearUnitsGeoKey", [3], None, None),
3077: ("ProjLinearUnitSizeGeoKey", [12], None, None), # custom unit in meters
3078: ("ProjStdParallel1GeoKey", [12], None, None),
3079: ("ProjStdParallel2GeoKey", [12], None, None),
3080: ("ProjNatOriginLongGeoKey", [12], None, None),
3081: ("ProjNatOriginLatGeoKey", [12], None, None),
3082: ("ProjFalseEastingGeoKey", [12], None, None),
3083: ("ProjFalseNorthingGeoKey", [12], None, None),
3084: ("ProjFalseOriginLongGeoKey", [12], None, None),
3085: ("ProjFalseOriginLatGeoKey", [12], None, None),
3086: ("ProjFalseOriginEastingGeoKey", [12], None, None),
3087: ("ProjFalseOriginNorthingGeoKey", [12], None, None),
3088: ("ProjCenterLongGeoKey", [12], None, None),
3089: ("ProjCenterLatGeoKey", [12], None, None),
3090: ("ProjCenterEastingGeoKey", [12], None, None),
3091: ("ProjFalseOriginNorthingGeoKey", [12], None, None),
3092: ("ProjScaleAtNatOriginGeoKey", [12], None, None),
3093: ("ProjScaleAtCenterGeoKey", [12], None, None),
3094: ("ProjAzimuthAngleGeoKey", [12], None, None),
3095: ("ProjStraightVertPoleLongGeoKey", [12], None, None),
# Vertical CS Parameter Keys
4096: ("VerticalCSTypeGeoKey", [3], None, None),
4097: ("VerticalCitationGeoKey", [2], None, None),
4098: ("VerticalDatumGeoKey", [3], None, None),
4099: ("VerticalUnitsGeoKey", [3], None, None),
}
_2TAG = dict((v[0], t) for t,v in _TAGS.items())
_2KEY = dict((v, k) for k,v in _2TAG.items())
if __PY3__:
import functools
reduce = functools.reduce
long = int
class GkdTag(ifd.TiffTag):
strict = True
def __init__(self, tag=0x0, value=None, name="GeoTiff Tag"):
self.name = name
if tag == 0: return
self.key, types, default, self.comment = _TAGS.get(tag, ("Unknown", [0,], None, "Undefined tag"))
value = default if value == None else value
self.tag = tag
restricted = getattr(values, self.key, {})
if restricted:
reverse = dict((v,k) for k,v in restricted.items())
if value in restricted:
self.meaning = restricted.get(value)
elif value in reverse:
value = reverse[value]
self.meaning = value
elif GkdTag.strict:
raise ValueError('"%s" value must be one of %s, get %s instead' % (self.key, list(restricted.keys()), value))
s
gitextract_f4gt1_zi/ ├── .gitignore ├── LICENSE ├── README.md ├── __init__.py ├── clients/ │ ├── QtMapServiceClient.py │ └── QtMapServiceClient.ui ├── core/ │ ├── __init__.py │ ├── basemaps/ │ │ ├── __init__.py │ │ ├── gpkg.py │ │ ├── mapservice.py │ │ └── servicesDefs.py │ ├── checkdeps.py │ ├── errors.py │ ├── georaster/ │ │ ├── __init__.py │ │ ├── bigtiffwriter.py │ │ ├── georaster.py │ │ ├── georef.py │ │ ├── img_utils.py │ │ └── npimg.py │ ├── lib/ │ │ ├── Tyf/ │ │ │ ├── VERSION │ │ │ ├── __init__.py │ │ │ ├── decoders.py │ │ │ ├── encoders.py │ │ │ ├── gkd.py │ │ │ ├── ifd.py │ │ │ ├── tags.py │ │ │ └── values.py │ │ ├── imageio/ │ │ │ ├── README.md │ │ │ ├── __init__.py │ │ │ ├── core/ │ │ │ │ ├── __init__.py │ │ │ │ ├── fetching.py │ │ │ │ ├── findlib.py │ │ │ │ ├── format.py │ │ │ │ ├── functions.py │ │ │ │ ├── request.py │ │ │ │ └── util.py │ │ │ ├── freeze.py │ │ │ ├── plugins/ │ │ │ │ ├── __init__.py │ │ │ │ ├── _freeimage.py │ │ │ │ └── freeimage.py │ │ │ ├── resources/ │ │ │ │ └── shipped_resources_go_here │ │ │ └── testing.py │ │ ├── imghdr.py │ │ ├── shapefile.py │ │ └── shapefile123.py │ ├── maths/ │ │ ├── __init__.py │ │ ├── akima.py │ │ ├── fillnodata.py │ │ ├── interpo.py │ │ └── kmeans1D.py │ ├── proj/ │ │ ├── __init__.py │ │ ├── ellps.py │ │ ├── reproj.py │ │ ├── srs.py │ │ ├── srv.py │ │ └── utm.py │ ├── settings.json │ ├── settings.py │ └── utils/ │ ├── __init__.py │ ├── bbox.py │ ├── gradient.py │ ├── timing.py │ └── xy.py ├── geoscene.py ├── issue_template.md ├── operators/ │ ├── __init__.py │ ├── add_camera_exif.py │ ├── add_camera_georef.py │ ├── io_export_shp.py │ ├── io_get_dem.py │ ├── io_import_asc.py │ ├── io_import_georaster.py │ ├── io_import_osm.py │ ├── io_import_shp.py │ ├── lib/ │ │ └── osm/ │ │ ├── nominatim.py │ │ └── overpy/ │ │ ├── __about__.py │ │ ├── __init__.py │ │ ├── exception.py │ │ └── helper.py │ ├── mesh_delaunay_voronoi.py │ ├── mesh_earth_sphere.py │ ├── nodes_terrain_analysis_builder.py │ ├── nodes_terrain_analysis_reclassify.py │ ├── object_drop.py │ ├── utils/ │ │ ├── __init__.py │ │ ├── bgis_utils.py │ │ ├── delaunay_voronoi.py │ │ └── georaster_utils.py │ └── view3d_mapviewer.py └── prefs.py
SYMBOL INDEX (1406 symbols across 66 files)
FILE: __init__.py
class BlenderVersionError (line 39) | class BlenderVersionError(Exception):
function getAppData (line 67) | def getAppData():
function _excepthook (line 97) | def _excepthook(exc_type, exc_value, exc_traceback):
function init (line 113) | def init(self, *args, **kwargs):
class BGIS_OT_logs (line 177) | class BGIS_OT_logs(bpy.types.Operator):
method execute (line 182) | def execute(self, context):
class VIEW3D_MT_menu_gis_import (line 195) | class VIEW3D_MT_menu_gis_import(bpy.types.Menu):
method draw (line 197) | def draw(self, context):
class VIEW3D_MT_menu_gis_export (line 207) | class VIEW3D_MT_menu_gis_export(bpy.types.Menu):
method draw (line 209) | def draw(self, context):
class VIEW3D_MT_menu_gis_webgeodata (line 213) | class VIEW3D_MT_menu_gis_webgeodata(bpy.types.Menu):
method draw (line 215) | def draw(self, context):
class VIEW3D_MT_menu_gis_camera (line 223) | class VIEW3D_MT_menu_gis_camera(bpy.types.Menu):
method draw (line 225) | def draw(self, context):
class VIEW3D_MT_menu_gis_mesh (line 232) | class VIEW3D_MT_menu_gis_mesh(bpy.types.Menu):
method draw (line 234) | def draw(self, context):
class VIEW3D_MT_menu_gis_object (line 243) | class VIEW3D_MT_menu_gis_object(bpy.types.Menu):
method draw (line 245) | def draw(self, context):
class VIEW3D_MT_menu_gis_nodes (line 249) | class VIEW3D_MT_menu_gis_nodes(bpy.types.Menu):
method draw (line 251) | def draw(self, context):
class VIEW3D_MT_menu_gis (line 255) | class VIEW3D_MT_menu_gis(bpy.types.Menu):
method draw (line 258) | def draw(self, context):
function add_gis_menu (line 284) | def add_gis_menu(self, context):
function register (line 289) | def register():
function unregister (line 362) | def unregister():
FILE: clients/QtMapServiceClient.py
function getShpExtent (line 33) | def getShpExtent(pathShp):
function getKmlExtent (line 42) | def getKmlExtent(kmlFile, crs2):
class QtMapServiceClient (line 75) | class QtMapServiceClient(QtGui.QMainWindow, mainForm):
method __init__ (line 77) | def __init__(self):
method provider (line 106) | def provider(self):
method layer (line 112) | def layer(self):
method outProj (line 116) | def outProj(self):
method zoom (line 120) | def zoom(self):
method rq (line 126) | def rq(self):
method uiUpdateMaskOption (line 132) | def uiUpdateMaskOption(self):
method uiUpdateSeedOption (line 138) | def uiUpdateSeedOption(self):
method uiDoUpdateProvider (line 162) | def uiDoUpdateProvider(self):
method uiDoUpdateScales (line 177) | def uiDoUpdateScales(self):
method uiDoUpdateRes (line 185) | def uiDoUpdateRes(self, zoomLevel):
method uiDoReadShpExtent (line 191) | def uiDoReadShpExtent(self):
method updateExtent (line 196) | def updateExtent(self):
method uiDoRequestInfos (line 214) | def uiDoRequestInfos(self):
method uiDoProcess (line 240) | def uiDoProcess(self):
method uiProcessFinished (line 286) | def uiProcessFinished(self):
method uiDoCancelThread (line 291) | def uiDoCancelThread(self):
method uiSendQuestion (line 297) | def uiSendQuestion(self, titre, msg):
method updateUi (line 304) | def updateUi(self):
method uiDoUpdateBar1 (line 307) | def uiDoUpdateBar1(self, num):
method uiDoConfigBar1 (line 310) | def uiDoConfigBar1(self, nb):
method updateProcessInfo (line 314) | def updateProcessInfo(self, txt):
method setInOutFolder (line 318) | def setInOutFolder(self):
method setCacheFolder (line 323) | def setCacheFolder(self):
method setInFolder (line 328) | def setInFolder(self):
method setOpenFileName (line 334) | def setOpenFileName(self, filtre):
method setExistingDirectory (line 338) | def setExistingDirectory(self):
method setSaveFileName (line 342) | def setSaveFileName(self):
class DownloadTiles (line 348) | class DownloadTiles(QtCore.QThread):
method __init__ (line 355) | def __init__(self, srv, layer, extent, zoom, outFile, outCRS, seedOnly...
method run (line 372) | def run(self):
method seedCache (line 391) | def seedCache(self):
method getImage (line 394) | def getImage(self):
method cancel (line 397) | def cancel(self):
FILE: core/basemaps/gpkg.py
class GeoPackage (line 37) | class GeoPackage():
method __init__ (line 41) | def __init__(self, path, tm):
method isGPKG (line 63) | def isGPKG(self):
method create (line 89) | def create(self):
method insertMetadata (line 167) | def insertMetadata(self):
method insertCRS (line 180) | def insertCRS(self, code, name, auth='EPSG', wkt=''):
method insertTileMatrixSet (line 194) | def insertTileMatrixSet(self):
method hasTile (line 224) | def hasTile(self, x, y, z):
method getTile (line 230) | def getTile(self, x, y, z):
method putTile (line 244) | def putTile(self, x, y, z, data):
method listExistingTiles (line 253) | def listExistingTiles(self, tiles):
method listMissingTiles (line 281) | def listMissingTiles(self, tiles):
method getTiles (line 286) | def getTiles(self, tiles):
method putTiles (line 314) | def putTiles(self, tiles):
FILE: core/basemaps/mapservice.py
class TileMatrix (line 53) | class TileMatrix():
method __init__ (line 78) | def __init__(self, gridDef):
method globalbbox (line 134) | def globalbbox(self):
method geoToProj (line 138) | def geoToProj(self, long, lat):
method projToGeo (line 145) | def projToGeo(self, x, y):
method getResList (line 153) | def getResList(self):
method getRes (line 159) | def getRes(self, zoom):
method getNearestZoom (line 169) | def getNearestZoom(self, res, rule='closer'):
method getPrevResFac (line 200) | def getPrevResFac(self, z):
method getNextResFac (line 204) | def getNextResFac(self, z):
method getFromToResFac (line 208) | def getFromToResFac(self, z1, z2):
method getTileNumber (line 223) | def getTileNumber(self, x, y, zoom):
method getTileCoords (line 238) | def getTileCoords(self, col, row, zoom):
method getTileBbox (line 254) | def getTileBbox(self, col, row, zoom):
method bboxRequest (line 261) | def bboxRequest(self, bbox, zoom):
class BBoxRequestMZ (line 264) | class BBoxRequestMZ():
method __init__ (line 266) | def __init__(self, tm, bbox, zooms):
method tiles (line 274) | def tiles(self):
method nbTiles (line 281) | def nbTiles(self):
method __getitem__ (line 284) | def __getitem__(self, z):
class BBoxRequest (line 288) | class BBoxRequest():
method __init__ (line 290) | def __init__(self, tm, bbox, zoom):
method cols (line 311) | def cols(self):
method rows (line 315) | def rows(self):
method tiles (line 322) | def tiles(self):
method nbTiles (line 326) | def nbTiles(self):
class MapService (line 333) | class MapService():
method __init__ (line 365) | def __init__(self, srckey, cacheFolder, dstGridKey=None):
method reportLoop (line 416) | def reportLoop(self):
method start (line 427) | def start(self):
method stop (line 433) | def stop(self):
method report (line 437) | def report(self):
method setDstGrid (line 450) | def setDstGrid(self, grdkey):
method getCache (line 460) | def getCache(self, laykey, useDstGrid):
method getTM (line 481) | def getTM(self, dstGrid=False):
method buildUrl (line 491) | def buildUrl(self, laykey, col, row, zoom):
method getQuadKey (line 548) | def getQuadKey(self, x, y, z):
method isTileInMapsBounds (line 562) | def isTileInMapsBounds(self, col, row, zoom, tm):
method downloadTile (line 573) | def downloadTile(self, laykey, col, row, zoom):
method tileRequest (line 605) | def tileRequest(self, laykey, col, row, zoom, toDstGrid=True):
method buildDstTile (line 626) | def buildDstTile(self, laykey, col, row, zoom):
method seedTiles (line 667) | def seedTiles(self, laykey, tiles, toDstGrid=True, nbThread=10, buffSi...
method getTiles (line 766) | def getTiles(self, laykey, tiles, toDstGrid=True, nbThread=10, cpt=True):
method getTile (line 779) | def getTile(self, laykey, col, row, zoom, toDstGrid=True):
method bboxRequest (line 783) | def bboxRequest(self, bbox, zoom, dstGrid=True):
method seedCache (line 789) | def seedCache(self, laykey, bbox, zoom, toDstGrid=True, nbThread=10, b...
method getImage (line 802) | def getImage(self, laykey, bbox, zoom, path=None, bigTiff=False, outCR...
FILE: core/errors.py
class OverlapError (line 4) | class OverlapError(Exception):
method __init__ (line 5) | def __init__(self):
method __str__ (line 7) | def __str__(self):
class ReprojError (line 10) | class ReprojError(Exception):
method __init__ (line 11) | def __init__(self, value):
method __str__ (line 13) | def __str__(self):
class ApiKeyError (line 16) | class ApiKeyError(Exception):
method __init__ (line 17) | def __init__(self):
method __str__ (line 19) | def __str__(self):
FILE: core/georaster/bigtiffwriter.py
class BigTiffWriter (line 34) | class BigTiffWriter():
method __del__ (line 43) | def __del__(self):
method __init__ (line 48) | def __init__(self, path, w, h, georef, geoTiffOptions={'TFW':'YES', 'T...
method paste (line 100) | def paste(self, data, x, y):
method __repr__ (line 130) | def __repr__(self):
FILE: core/georaster/georaster.py
class GeoRaster (line 41) | class GeoRaster():
method __init__ (line 45) | def __init__(self, path, subBoxGeo=None, useGDAL=False):
method __getattr__ (line 98) | def __getattr__(self, attr):
method _getWfPath (line 106) | def _getWfPath(self):
method _fromTIFF (line 121) | def _fromTIFF(self):
method _fromGDAL (line 149) | def _fromGDAL(self):
method fileExists (line 177) | def fileExists(self):
method baseName (line 181) | def baseName(self):
method isTiff (line 187) | def isTiff(self):
method hasWorldFile (line 194) | def hasWorldFile(self):
method isGeoref (line 197) | def isGeoref(self):
method isOneBand (line 207) | def isOneBand(self):
method isFloat (line 210) | def isFloat(self):
method ddtype (line 213) | def ddtype(self):
method __repr__ (line 225) | def __repr__(self):
method toGDAL (line 244) | def toGDAL(self):
method readAsNpArray (line 248) | def readAsNpArray(self, subset=True):
FILE: core/georaster/georef.py
class GeoRef (line 29) | class GeoRef():
method __init__ (line 35) | def __init__(self, rSize, pxSize, origin, rot=xy(0,0), pxCenter=True, ...
method fromGDAL (line 72) | def fromGDAL(cls, ds):
method fromWorldFile (line 87) | def fromWorldFile(cls, wfPath, rasterSize):
method fromTyf (line 100) | def fromTyf(cls, tif):
method toGDAL (line 167) | def toGDAL(self):
method toWorldFile (line 174) | def toWorldFile(self, path):
method hasCRS (line 189) | def hasCRS(self):
method hasRotation (line 193) | def hasRotation(self):
method cornersCenter (line 220) | def cornersCenter(self):
method corners (line 236) | def corners(self):
method bbox (line 254) | def bbox(self):
method bboxPx (line 264) | def bboxPx(self):
method center (line 268) | def center(self):
method geoSize (line 273) | def geoSize(self):
method orthoGeoSize (line 278) | def orthoGeoSize(self):
method orthoPxSize (line 285) | def orthoPxSize(self):
method geoFromPx (line 292) | def geoFromPx(self, xPx, yPx, reverseY=False, pxCenter=True):
method pxFromGeo (line 321) | def pxFromGeo(self, x, y, reverseY=False, round2Floor=False):
method pxToGeo (line 346) | def pxToGeo(self, xPx, yPx, reverseY=False):
method geoToPx (line 348) | def geoToPx(self, x, y, reverseY=False, round2Floor=False):
method setSubBoxGeo (line 355) | def setSubBoxGeo(self, subBoxGeo):
method setSubBoxPx (line 375) | def setSubBoxPx(self, subBoxPx):
method applySubBox (line 394) | def applySubBox(self):
method getSubBoxGeoRef (line 400) | def getSubBoxGeoRef(self):
method subBoxPx (line 404) | def subBoxPx(self):
method subBoxPxSize (line 413) | def subBoxPxSize(self):
method subBoxGeoSize (line 422) | def subBoxGeoSize(self):
method subBoxPxOrigin (line 430) | def subBoxPxOrigin(self):
method subBoxGeoOrigin (line 437) | def subBoxGeoOrigin(self):
method __repr__ (line 445) | def __repr__(self):
FILE: core/georaster/img_utils.py
function isValidStream (line 27) | def isValidStream(data):
function getImgFormat (line 36) | def getImgFormat(filepath):
function getImgDim (line 70) | def getImgDim(filepath):
FILE: core/georaster/npimg.py
class NpImage (line 45) | class NpImage():
method _getIFACE (line 48) | def _getIFACE(self):
method __getattr__ (line 72) | def __getattr__(self, attr):
method __init__ (line 79) | def __init__(self, data, subBoxPx=None, noData=None, georef=None, adju...
method size (line 149) | def size(self):
method isGeoref (line 153) | def isGeoref(self):
method nbBands (line 161) | def nbBands(self):
method hasAlpha (line 168) | def hasAlpha(self):
method isOneBand (line 172) | def isOneBand(self):
method dtype (line 176) | def dtype(self):
method isFloat (line 181) | def isFloat(self):
method getMin (line 187) | def getMin(self, bandIdx=0):
method getMax (line 193) | def getMax(self, bandIdx=0):
method new (line 200) | def new(cls, w, h, bkgColor=(255,255,255,255), noData=None, georef=None):
method _applySubBox (line 209) | def _applySubBox(self, data):
method _npFromPath (line 221) | def _npFromPath(self, path):
method _npFromBLOB (line 232) | def _npFromBLOB(self, data):
method _npFromImgIO (line 256) | def _npFromImgIO(self, img):
method _npFromPIL (line 261) | def _npFromPIL(self, img):
method _npFromGDAL (line 269) | def _npFromGDAL(self, ds):
method toBLOB (line 302) | def toBLOB(self, ext='PNG'):
method toPIL (line 339) | def toPIL(self):
method toGDAL (line 344) | def toGDAL(self):
method removeAlpha (line 367) | def removeAlpha(self):
method addAlpha (line 371) | def addAlpha(self, opacity=255):
method save (line 380) | def save(self, path):
method paste (line 415) | def paste(self, data, x, y):
method cast2float (line 433) | def cast2float(self):
method fillNodata (line 437) | def fillNodata(self):
method reproj (line 462) | def reproj(self, crs1, crs2, out_ul=None, out_size=None, out_res=None,...
method __repr__ (line 469) | def __repr__(self):
FILE: core/lib/Tyf/__init__.py
function _read_IFD (line 46) | def _read_IFD(obj, fileobj, offset, byteorder="<"):
function from_buffer (line 97) | def from_buffer(obj, fileobj, offset, byteorder="<", custom_sub_ifd={}):
function _load_raster (line 116) | def _load_raster(obj, fileobj):
function _write_IFD (line 137) | def _write_IFD(obj, fileobj, offset, byteorder="<"):
function to_buffer (line 204) | def to_buffer(obj, fileobj, offset, byteorder="<"):
function _fileobj (line 271) | def _fileobj(f, mode):
class TiffFile (line 282) | class TiffFile(list):
method __init__ (line 288) | def __init__(self, fileobj):
method __getitem__ (line 321) | def __getitem__(self, item):
method __add__ (line 325) | def __add__(self, value):
method load_raster (line 335) | def load_raster(self, idx=None):
method save (line 342) | def save(self, f, byteorder="<", idx=None):
class JpegFile (line 356) | class JpegFile(collections.OrderedDict):
method __init__ (line 362) | def __init__(self, fileobj):
method __getitem__ (line 384) | def __getitem__(self, item):
method _pack (line 388) | def _pack(self, marker, fileobj):
method save (line 402) | def save(self, f):
method save_thumbnail (line 411) | def save_thumbnail(self, f):
method dump_exif (line 432) | def dump_exif(self, f):
method load_exif (line 437) | def load_exif(self, f):
method strip_exif (line 443) | def strip_exif(self):
function jpeg_extract (line 453) | def jpeg_extract(f):
function open (line 472) | def open(f):
FILE: core/lib/Tyf/decoders.py
function _5 (line 12) | def _5(value):
function _0x2 (line 30) | def _0x2(value):
FILE: core/lib/Tyf/encoders.py
function _1 (line 10) | def _1(value):
function _2 (line 16) | def _2(value):
function _3 (line 24) | def _3(value):
function _4 (line 32) | def _4(value):
function _5 (line 38) | def _5(value):
function _6 (line 44) | def _6(value):
function _7 (line 50) | def _7(value):
function _8 (line 57) | def _8(value):
function _9 (line 65) | def _9(value):
function _11 (line 73) | def _11(value):
function _0x2 (line 89) | def _0x2(value):
FILE: core/lib/Tyf/gkd.py
function Transform (line 14) | def Transform(obj, x=0., y=0., z1=0.,z2=1.):
class GkdTag (line 85) | class GkdTag(ifd.TiffTag):
method __init__ (line 88) | def __init__(self, tag=0x0, value=None, name="GeoTiff Tag"):
method __setattr__ (line 109) | def __setattr__(self, attr, value):
method _encode (line 112) | def _encode(self, value, types):
method _decode (line 120) | def _decode(self):
class Gkd (line 125) | class Gkd(dict):
method __init__ (line 130) | def __init__(self, value={}, **pairs):
method __getitem__ (line 134) | def __getitem__(self, tag):
method __setitem__ (line 138) | def __setitem__(self, tag, value):
method get (line 142) | def get(self, tag, error=None):
method to_ifd (line 146) | def to_ifd(self):
method from_ifd (line 170) | def from_ifd(self, ifd = {}, **kw):
method getModelTransformation (line 193) | def getModelTransformation(self, tie_index=0):
method tags (line 214) | def tags(self):
FILE: core/lib/Tyf/ifd.py
class TiffTag (line 7) | class TiffTag(object):
method __init__ (line 21) | def __init__(self, tag, type=None, value=None, name="Tiff tag"):
method __setattr__ (line 30) | def __setattr__(self, attr, value):
method __repr__ (line 45) | def __repr__(self):
method _encode (line 48) | def _encode(self, value):
method _decode (line 51) | def _decode(self):
method _determine_if_offset (line 54) | def _determine_if_offset(self):
method _fill (line 60) | def _fill(self):
method calcsize (line 67) | def calcsize(self):
class Ifd (line 71) | class Ifd(dict):
method __init__ (line 84) | def __init__(self, sub_ifd={}, **kwargs):
method __setitem__ (line 95) | def __setitem__(self, tag, value):
method __getitem__ (line 107) | def __getitem__(self, tag):
method _check (line 113) | def _check(self):
method set (line 118) | def set(self, tag, typ, value):
method get (line 130) | def get(self, tag):
method addtag (line 135) | def addtag(self, tifftag):
method tags (line 140) | def tags(self):
method set_location (line 147) | def set_location(self, longitude, latitude, altitude=0.):
method get_location (line 154) | def get_location(self):
method load_location (line 162) | def load_location(self, zoom=15, size="256x256", mcolor="0xff00ff", fo...
method dump_location (line 182) | def dump_location(self, tilename, zoom=15, size="256x256", mcolor="0xf...
FILE: core/lib/Tyf/tags.py
function get (line 292) | def get(tag):
function _2tag (line 301) | def _2tag(tag, family=None):
FILE: core/lib/imageio/core/fetching.py
class InternetNotAllowedError (line 22) | class InternetNotAllowedError(IOError):
function get_remote_file (line 29) | def get_remote_file(fname, directory=None, force_download=False):
function _fetch_file (line 103) | def _fetch_file(url, file_name, print_destination=True):
function _chunk_read (line 158) | def _chunk_read(response, local_file, chunk_size=8192, initial_size=0):
function _chunk_write (line 195) | def _chunk_write(chunk, local_file, progress):
function _sizeof_fmt (line 202) | def _sizeof_fmt(num):
FILE: core/lib/imageio/core/findlib.py
function looks_lib (line 34) | def looks_lib(fname):
function generate_candidate_libs (line 47) | def generate_candidate_libs(lib_names, lib_dirs=None):
function load_lib (line 97) | def load_lib(exact_lib_names, lib_names, lib_dirs=None):
FILE: core/lib/imageio/core/format.py
class Format (line 45) | class Format:
method __init__ (line 79) | def __init__(self, name, description, extensions=None, modes=None):
method __repr__ (line 106) | def __repr__(self):
method __str__ (line 110) | def __str__(self):
method doc (line 114) | def doc(self):
method name (line 123) | def name(self):
method description (line 129) | def description(self):
method extensions (line 135) | def extensions(self):
method modes (line 142) | def modes(self):
method get_reader (line 147) | def get_reader(self, request):
method get_writer (line 160) | def get_writer(self, request):
method can_read (line 173) | def can_read(self, request):
method can_write (line 180) | def can_write(self, request):
method _can_read (line 187) | def _can_read(self, request): # pragma: no cover
method _can_write (line 190) | def _can_write(self, request): # pragma: no cover
class _BaseReaderWriter (line 195) | class _BaseReaderWriter(object):
method __init__ (line 201) | def __init__(self, format, request):
method format (line 210) | def format(self):
method request (line 217) | def request(self):
method __enter__ (line 223) | def __enter__(self):
method __exit__ (line 227) | def __exit__(self, type, value, traceback):
method __del__ (line 232) | def __del__(self):
method close (line 238) | def close(self):
method closed (line 250) | def closed(self):
method _checkClosed (line 255) | def _checkClosed(self, msg=None):
method _open (line 265) | def _open(self, **kwargs):
method _close (line 277) | def _close(self):
class Reader (line 290) | class Reader(_BaseReaderWriter):
method get_length (line 305) | def get_length(self):
method get_data (line 319) | def get_data(self, index, **kwargs):
method get_next_data (line 333) | def get_next_data(self, **kwargs):
method get_meta_data (line 343) | def get_meta_data(self, index=None):
method iter_data (line 364) | def iter_data(self):
method __iter__ (line 385) | def __iter__(self):
method __len__ (line 388) | def __len__(self):
method _get_length (line 393) | def _get_length(self):
method _get_data (line 403) | def _get_data(self, index):
method _get_meta_data (line 413) | def _get_meta_data(self, index):
class Writer (line 426) | class Writer(_BaseReaderWriter):
method append_data (line 441) | def append_data(self, im, meta=None):
method set_meta_data (line 468) | def set_meta_data(self, meta):
method _append_data (line 489) | def _append_data(self, im, meta):
method _set_meta_data (line 493) | def _set_meta_data(self, meta):
class FormatManager (line 498) | class FormatManager:
method __init__ (line 510) | def __init__(self):
method __repr__ (line 513) | def __repr__(self):
method __iter__ (line 516) | def __iter__(self):
method __len__ (line 519) | def __len__(self):
method __str__ (line 522) | def __str__(self):
method __getitem__ (line 530) | def __getitem__(self, name):
method add_format (line 564) | def add_format(self, format, overwrite=False):
method search_read_format (line 583) | def search_read_format(self, request):
method search_write_format (line 611) | def search_write_format(self, request):
method get_format_names (line 640) | def get_format_names(self):
method show (line 645) | def show(self):
FILE: core/lib/imageio/core/functions.py
function help (line 53) | def help(name=None):
function get_reader (line 74) | def get_reader(uri, format=None, mode='?', **kwargs):
function get_writer (line 114) | def get_writer(uri, format=None, mode='?', **kwargs):
function imread (line 157) | def imread(uri, format=None, **kwargs):
function imwrite (line 186) | def imwrite(uri, im, format=None, **kwargs):
function mimread (line 230) | def mimread(uri, format=None, **kwargs):
function mimwrite (line 276) | def mimwrite(uri, ims, format=None, **kwargs):
function volread (line 326) | def volread(uri, format=None, **kwargs):
function volwrite (line 352) | def volwrite(uri, im, format=None, **kwargs):
function mvolread (line 397) | def mvolread(uri, format=None, **kwargs):
function mvolwrite (line 435) | def mvolwrite(uri, ims, format=None, **kwargs):
FILE: core/lib/imageio/core/request.py
class Request (line 59) | class Request(object):
method __init__ (line 86) | def __init__(self, uri, mode, **kwargs):
method _parse_uri (line 121) | def _parse_uri(self, uri):
method filename (line 247) | def filename(self):
method mode (line 256) | def mode(self):
method kwargs (line 266) | def kwargs(self):
method get_file (line 273) | def get_file(self):
method get_local_filename (line 322) | def get_local_filename(self):
method finish (line 341) | def finish(self):
method get_result (line 388) | def get_result(self):
method firstbytes (line 396) | def firstbytes(self):
method _read_first_bytes (line 404) | def _read_first_bytes(self, N=256):
function read_n_bytes (line 430) | def read_n_bytes(f, N):
FILE: core/lib/imageio/core/util.py
function urlopen (line 36) | def urlopen(*args, **kwargs):
function image_as_uint (line 51) | def image_as_uint(im, bitdepth=None):
class ImageList (line 116) | class ImageList(list):
method __init__ (line 117) | def __init__(self, meta=None):
method meta (line 126) | def meta(self):
class Image (line 132) | class Image(np.ndarray):
method __new__ (line 140) | def __new__(cls, array, meta=None):
method _copy_meta (line 156) | def _copy_meta(self, meta):
method meta (line 166) | def meta(self):
method __array_finalize__ (line 171) | def __array_finalize__(self, ob):
method __array_wrap__ (line 180) | def __array_wrap__(self, out, context=None):
function asarray (line 192) | def asarray(a):
class Dict (line 211) | class Dict(_dict):
method __getattribute__ (line 224) | def __getattribute__(self, key):
method __setattr__ (line 233) | def __setattr__(self, key, val):
method __dir__ (line 245) | def __dir__(self):
class BaseProgressIndicator (line 252) | class BaseProgressIndicator:
method __init__ (line 270) | def __init__(self, name):
method start (line 278) | def start(self, action='', unit='', max=0):
method status (line 294) | def status(self):
method set_progress (line 302) | def set_progress(self, progress=0, force=False):
method increase_progress (line 331) | def increase_progress(self, extra_progress):
method finish (line 338) | def finish(self, message=None):
method fail (line 350) | def fail(self, message=None):
method write (line 361) | def write(self, message):
method _start (line 374) | def _start(self):
method _stop (line 377) | def _stop(self):
method _update_progress (line 380) | def _update_progress(self, progressText):
method _write (line 383) | def _write(self, message):
class StdoutProgressIndicator (line 387) | class StdoutProgressIndicator(BaseProgressIndicator):
method _start (line 394) | def _start(self):
method _update_progress (line 404) | def _update_progress(self, progressText):
method _stop (line 416) | def _stop(self):
method _write (line 421) | def _write(self, message):
function appdata_dir (line 431) | def appdata_dir(appname=None, roaming=False):
function resource_dirs (line 486) | def resource_dirs():
function get_platform (line 515) | def get_platform():
function has_module (line 543) | def has_module(module_name):
FILE: core/lib/imageio/freeze.py
function get_includes (line 8) | def get_includes():
function get_excludes (line 16) | def get_excludes():
FILE: core/lib/imageio/plugins/_freeimage.py
function get_freeimage_lib (line 42) | def get_freeimage_lib():
class FI_TYPES (line 70) | class FI_TYPES(object):
class IO_FLAGS (line 132) | class IO_FLAGS(object):
class METADATA_MODELS (line 241) | class METADATA_MODELS(object):
class METADATA_DATATYPE (line 254) | class METADATA_DATATYPE(object):
class Freeimage (line 296) | class Freeimage(object):
method __init__ (line 378) | def __init__(self):
method lib (line 407) | def lib(self):
method has_lib (line 418) | def has_lib(self):
method load_freeimage (line 425) | def load_freeimage(self):
method _load_freeimage (line 451) | def _load_freeimage(self):
method _register_api (line 482) | def _register_api(self):
method __enter__ (line 491) | def __enter__(self):
method __exit__ (line 495) | def __exit__(self, *args):
method _reset_log (line 499) | def _reset_log(self):
method _get_error_message (line 505) | def _get_error_message(self):
method _show_any_warnings (line 517) | def _show_any_warnings(self):
method get_output_log (line 525) | def get_output_log(self):
method getFIF (line 532) | def getFIF(self, filename, mode, bytes=None):
method create_bitmap (line 574) | def create_bitmap(self, filename, ftype, flags=0):
method create_multipage_bitmap (line 580) | def create_multipage_bitmap(self, filename, ftype, flags=0):
class FIBaseBitmap (line 587) | class FIBaseBitmap(object):
method __init__ (line 588) | def __init__(self, fi, filename, ftype, flags):
method __del__ (line 596) | def __del__(self):
method close (line 599) | def close(self):
method _set_bitmap (line 611) | def _set_bitmap(self, bitmap, close_func=None):
method get_meta_data (line 623) | def get_meta_data(self):
method set_meta_data (line 690) | def set_meta_data(self, metadata):
class FIBitmap (line 763) | class FIBitmap(FIBaseBitmap):
method allocate (line 767) | def allocate(self, array):
method load_from_filename (line 802) | def load_from_filename(self, filename=None):
method save_to_filename (line 836) | def save_to_filename(self, filename=None):
method get_image_data (line 900) | def get_image_data(self):
method set_image_data (line 937) | def set_image_data(self, array):
method _wrap_bitmap_bits_in_array (line 997) | def _wrap_bitmap_bits_in_array(self, shape, dtype, save):
method _finish_wrapped_array (line 1050) | def _finish_wrapped_array(self, array): # IS_PYPY
method _get_type_and_shape (line 1077) | def _get_type_and_shape(self):
method quantize (line 1130) | def quantize(self, quantizer=0, palettesize=256):
class FIMultipageBitmap (line 1174) | class FIMultipageBitmap(FIBaseBitmap):
method load_from_filename (line 1178) | def load_from_filename(self, filename=None):
method save_to_filename (line 1225) | def save_to_filename(self, filename=None):
method __len__ (line 1252) | def __len__(self):
method get_page (line 1256) | def get_page(self, index):
method append_bitmap (line 1276) | def append_bitmap(self, bitmap):
FILE: core/lib/imageio/plugins/freeimage.py
class FreeimageFormat (line 22) | class FreeimageFormat(Format):
method fif (line 43) | def fif(self):
method _can_read (line 46) | def _can_read(self, request):
method _can_write (line 58) | def _can_write(self, request):
class Reader (line 71) | class Reader(Format.Reader):
method _get_length (line 73) | def _get_length(self):
method _open (line 76) | def _open(self, flags=0):
method _close (line 81) | def _close(self):
method _get_data (line 84) | def _get_data(self, index):
method _get_meta_data (line 89) | def _get_meta_data(self, index):
class Writer (line 96) | class Writer(Format.Writer):
method _open (line 98) | def _open(self, flags=0):
method _close (line 104) | def _close(self):
method _append_data (line 111) | def _append_data(self, im, meta):
method _set_meta_data (line 132) | def _set_meta_data(self, meta):
class FreeimageBmpFormat (line 141) | class FreeimageBmpFormat(FreeimageFormat):
class Writer (line 155) | class Writer(FreeimageFormat.Writer):
method _open (line 156) | def _open(self, flags=0, compression=False):
method _append_data (line 166) | def _append_data(self, im, meta):
class FreeimagePngFormat (line 171) | class FreeimagePngFormat(FreeimageFormat):
class Reader (line 195) | class Reader(FreeimageFormat.Reader):
method _open (line 196) | def _open(self, flags=0, ignoregamma=False):
class Writer (line 206) | class Writer(FreeimageFormat.Writer):
method _open (line 207) | def _open(self, flags=0, compression=9, quantize=0, interlaced=False):
method _append_data (line 223) | def _append_data(self, im, meta):
class FreeimageJpegFormat (line 243) | class FreeimageJpegFormat(FreeimageFormat):
class Reader (line 274) | class Reader(FreeimageFormat.Reader):
method _open (line 275) | def _open(self, flags=0, exifrotate=True, quickread=False):
method _get_data (line 285) | def _get_data(self, index):
method _rotate (line 290) | def _rotate(self, im, meta):
class Writer (line 318) | class Writer(FreeimageFormat.Writer):
method _open (line 319) | def _open(self, flags=0, quality=75, progressive=False, optimize=False,
method _append_data (line 337) | def _append_data(self, im, meta):
function _create_predefined_freeimage_formats (line 403) | def _create_predefined_freeimage_formats():
function create_freeimage_formats (line 416) | def create_freeimage_formats():
FILE: core/lib/imageio/testing.py
function run_tests_if_main (line 41) | def run_tests_if_main(show_coverage=False):
function get_test_dir (line 66) | def get_test_dir():
function clean_test_dir (line 81) | def clean_test_dir(strict=False):
function need_internet (line 90) | def need_internet():
function test_unit (line 97) | def test_unit(cov_report='term'):
function test_style (line 113) | def test_style():
function _enable_faulthandler (line 167) | def _enable_faulthandler():
function _clear_imageio (line 179) | def _clear_imageio():
class FileForTesting (line 186) | class FileForTesting(object):
method __init__ (line 189) | def __init__(self, original):
method write (line 192) | def write(self, msg):
method flush (line 198) | def flush(self):
method revert (line 201) | def revert(self):
function _get_style_test_options (line 205) | def _get_style_test_options(filename):
function _test_style (line 227) | def _test_style(filename, ignore):
FILE: core/lib/imghdr.py
function what (line 11) | def what(file, h=None):
function test_jpeg (line 37) | def test_jpeg(h, f):
function test_png (line 44) | def test_png(h, f):
function test_gif (line 50) | def test_gif(h, f):
function test_tiff (line 57) | def test_tiff(h, f):
function test_rgb (line 64) | def test_rgb(h, f):
function test_pbm (line 71) | def test_pbm(h, f):
function test_pgm (line 79) | def test_pgm(h, f):
function test_ppm (line 87) | def test_ppm(h, f):
function test_rast (line 95) | def test_rast(h, f):
function test_xbm (line 102) | def test_xbm(h, f):
function test_bmp (line 109) | def test_bmp(h, f):
function test_webp (line 115) | def test_webp(h, f):
function test_exr (line 121) | def test_exr(h, f):
function test (line 131) | def test():
function testall (line 146) | def testall(list, recursive, toplevel):
FILE: core/lib/shapefile.py
function b (line 87) | def b(v, encoding='utf-8', encodingErrors='strict'):
function u (line 101) | def u(v, encoding='utf-8', encodingErrors='strict'):
function is_string (line 115) | def is_string(v):
function b (line 119) | def b(v, encoding='utf-8', encodingErrors='strict'):
function u (line 133) | def u(v, encoding='utf-8', encodingErrors='strict'):
function is_string (line 147) | def is_string(v):
class _Array (line 153) | class _Array(array.array):
method __repr__ (line 156) | def __repr__(self):
function signed_area (line 159) | def signed_area(coords):
class Shape (line 168) | class Shape(object):
method __init__ (line 169) | def __init__(self, shapeType=NULL, points=None, parts=None, partTypes=...
method __geo_interface__ (line 188) | def __geo_interface__(self):
method _from_geojson (line 265) | def _from_geojson(geoj):
method shapeTypeName (line 341) | def shapeTypeName(self):
class _Record (line 344) | class _Record(list):
method __init__ (line 360) | def __init__(self, field_positions, values, oid=None):
method __getattr__ (line 375) | def __getattr__(self, item):
method __setattr__ (line 393) | def __setattr__(self, key, value):
method __getitem__ (line 409) | def __getitem__(self, item):
method __setitem__ (line 430) | def __setitem__(self, key, value):
method oid (line 449) | def oid(self):
method as_dict (line 453) | def as_dict(self):
method __repr__ (line 460) | def __repr__(self):
method __dir__ (line 463) | def __dir__(self):
class ShapeRecord (line 474) | class ShapeRecord(object):
method __init__ (line 477) | def __init__(self, shape=None, record=None):
method __geo_interface__ (line 482) | def __geo_interface__(self):
class Shapes (line 487) | class Shapes(list):
method __repr__ (line 493) | def __repr__(self):
method __geo_interface__ (line 497) | def __geo_interface__(self):
class ShapeRecords (line 501) | class ShapeRecords(list):
method __repr__ (line 507) | def __repr__(self):
method __geo_interface__ (line 511) | def __geo_interface__(self):
class ShapefileException (line 515) | class ShapefileException(Exception):
class Reader (line 519) | class Reader(object):
method __init__ (line 537) | def __init__(self, *args, **kwargs):
method __str__ (line 584) | def __str__(self):
method __enter__ (line 597) | def __enter__(self):
method __exit__ (line 603) | def __exit__(self, exc_type, exc_val, exc_tb):
method __len__ (line 609) | def __len__(self):
method __iter__ (line 613) | def __iter__(self):
method __geo_interface__ (line 619) | def __geo_interface__(self):
method shapeTypeName (line 632) | def shapeTypeName(self):
method load (line 635) | def load(self, shapefile=None):
method load_shp (line 652) | def load_shp(self, shapefile_name):
method load_shx (line 665) | def load_shx(self, shapefile_name):
method load_dbf (line 678) | def load_dbf(self, shapefile_name):
method __del__ (line 691) | def __del__(self):
method close (line 694) | def close(self):
method __getFileObj (line 702) | def __getFileObj(self, f):
method __restrictIndex (line 713) | def __restrictIndex(self, i):
method __shpHeader (line 723) | def __shpHeader(self):
method __shape (line 747) | def __shape(self):
method __shapeIndex (line 820) | def __shapeIndex(self, i=None):
method shape (line 842) | def shape(self, i=0):
method shapes (line 856) | def shapes(self):
method iterShapes (line 871) | def iterShapes(self):
method __dbfHeader (line 881) | def __dbfHeader(self):
method __recordFmt (line 914) | def __recordFmt(self):
method __record (line 927) | def __record(self, oid=None):
method record (line 993) | def record(self, i=0):
method records (line 1004) | def records(self):
method iterRecords (line 1017) | def iterRecords(self):
method shapeRecord (line 1029) | def shapeRecord(self, i=0):
method shapeRecords (line 1035) | def shapeRecords(self):
method iterShapeRecords (line 1041) | def iterShapeRecords(self):
class Writer (line 1048) | class Writer(object):
method __init__ (line 1050) | def __init__(self, target=None, shapeType=None, autoBalance=False, **k...
method __len__ (line 1085) | def __len__(self):
method __enter__ (line 1091) | def __enter__(self):
method __exit__ (line 1097) | def __exit__(self, exc_type, exc_val, exc_tb):
method __del__ (line 1103) | def __del__(self):
method close (line 1106) | def close(self):
method __getFileObj (line 1142) | def __getFileObj(self, f):
method __shpFileLength (line 1154) | def __shpFileLength(self):
method __bbox (line 1167) | def __bbox(self, s):
method __zbox (line 1189) | def __zbox(self, s):
method __mbox (line 1208) | def __mbox(self, s):
method shapeTypeName (line 1234) | def shapeTypeName(self):
method bbox (line 1237) | def bbox(self):
method zbox (line 1243) | def zbox(self):
method mbox (line 1247) | def mbox(self):
method __shapefileHeader (line 1251) | def __shapefileHeader(self, fileObj, headerType='shp'):
method __dbfHeader (line 1303) | def __dbfHeader(self):
method shape (line 1337) | def shape(self, s):
method __shpRecord (line 1355) | def __shpRecord(self, s):
method __shxRecord (line 1498) | def __shxRecord(self, offset, length):
method record (line 1504) | def record(self, *recordList, **recordDict):
method __dbfRecord (line 1535) | def __dbfRecord(self, record):
method balance (line 1602) | def balance(self):
method null (line 1612) | def null(self):
method point (line 1617) | def point(self, x, y):
method pointm (line 1624) | def pointm(self, x, y, m=None):
method pointz (line 1632) | def pointz(self, x, y, z=0, m=None):
method multipoint (line 1642) | def multipoint(self, points):
method multipointm (line 1649) | def multipointm(self, points):
method multipointz (line 1657) | def multipointz(self, points):
method line (line 1667) | def line(self, lines):
method linem (line 1673) | def linem(self, lines):
method linez (line 1680) | def linez(self, lines):
method poly (line 1689) | def poly(self, polys):
method polym (line 1697) | def polym(self, polys):
method polyz (line 1706) | def polyz(self, polys):
method multipatch (line 1717) | def multipatch(self, parts, partTypes):
method _shapeparts (line 1743) | def _shapeparts(self, parts, shapeType):
method field (line 1762) | def field(self, name, fieldType="C", size="50", decimal=0):
function test (line 1854) | def test(**kwargs):
FILE: core/lib/shapefile123.py
function b (line 46) | def b(v):
function u (line 61) | def u(v):
function is_string (line 85) | def is_string(v):
class _Array (line 91) | class _Array(array.array):
method __repr__ (line 94) | def __repr__(self):
function signed_area (line 97) | def signed_area(coords):
class _Shape (line 107) | class _Shape:
method __init__ (line 108) | def __init__(self, shapeType=None):
method __geo_interface__ (line 122) | def __geo_interface__(self):
class _ShapeRecord (line 193) | class _ShapeRecord:
method __init__ (line 195) | def __init__(self, shape=None, record=None):
class ShapefileException (line 199) | class ShapefileException(Exception):
class Reader (line 203) | class Reader:
method __init__ (line 221) | def __init__(self, *args, **kwargs):
method load (line 256) | def load(self, shapefile=None):
method __getFileObj (line 281) | def __getFileObj(self, f):
method __restrictIndex (line 292) | def __restrictIndex(self, i):
method __shpHeader (line 302) | def __shpHeader(self):
method __shape (line 320) | def __shape(self):
method __shapeIndex (line 380) | def __shapeIndex(self, i=None):
method shape (line 400) | def shape(self, i=0):
method shapes (line 414) | def shapes(self):
method iterShapes (line 429) | def iterShapes(self):
method __dbfHeaderLength (line 439) | def __dbfHeaderLength(self):
method __dbfHeader (line 449) | def __dbfHeader(self):
method __recordFmt (line 474) | def __recordFmt(self):
method __record (line 482) | def __record(self):
method record (line 525) | def record(self, i=0):
method records (line 536) | def records(self):
method iterRecords (line 549) | def iterRecords(self):
method shapeRecord (line 561) | def shapeRecord(self, i=0):
method shapeRecords (line 567) | def shapeRecords(self):
method iterShapeRecords (line 574) | def iterShapeRecords(self):
class Writer (line 581) | class Writer:
method __init__ (line 583) | def __init__(self, shapeType=None):
method __getFileObj (line 597) | def __getFileObj(self, f):
method __shpFileLength (line 609) | def __shpFileLength(self):
method __bbox (line 668) | def __bbox(self, shapes, shapeTypes=[]):
method __zbox (line 680) | def __zbox(self, shapes, shapeTypes=[]):
method __mbox (line 691) | def __mbox(self, shapes, shapeTypes=[]):
method bbox (line 701) | def bbox(self):
method zbox (line 707) | def zbox(self):
method mbox (line 711) | def mbox(self):
method __shapefileHeader (line 715) | def __shapefileHeader(self, fileObj, headerType='shp'):
method __dbfHeader (line 747) | def __dbfHeader(self):
method __shpRecords (line 778) | def __shpRecords(self):
method __shxRecords (line 894) | def __shxRecords(self):
method __dbfRecords (line 902) | def __dbfRecords(self):
method null (line 924) | def null(self):
method point (line 928) | def point(self, x, y, z=0, m=0):
method line (line 934) | def line(self, parts=[], shapeType=POLYLINE):
method poly (line 940) | def poly(self, parts=[], shapeType=POLYGON, partTypes=[]):
method field (line 971) | def field(self, name, fieldType="C", size="50", decimal=0):
method record (line 975) | def record(self, *recordList, **recordDict):
method shape (line 1000) | def shape(self, i):
method shapes (line 1003) | def shapes(self):
method saveShp (line 1007) | def saveShp(self, target):
method saveShx (line 1017) | def saveShx(self, target):
method saveDbf (line 1027) | def saveDbf(self, target):
method save (line 1035) | def save(self, target=None, shp=None, shx=None, dbf=None):
class Editor (line 1065) | class Editor(Writer):
method __init__ (line 1066) | def __init__(self, shapefile=None, shapeType=POINT, autoBalance=1):
method select (line 1079) | def select(self, expr):
method delete (line 1084) | def delete(self, shape=None, part=None, point=None):
method point (line 1113) | def point(self, x=None, y=None, z=None, m=None, shape=None, part=None,...
method validate (line 1160) | def validate(self):
method balance (line 1166) | def balance(self):
method __fieldNorm (line 1175) | def __fieldNorm(self, fieldName):
function test (line 1183) | def test():
FILE: core/maths/akima.py
function interpolate (line 88) | def interpolate(x, y, x_new, axis=-1, out=None):
FILE: core/maths/fillnodata.py
function replace_nans (line 37) | def replace_nans(array, max_iter, tolerance, kernel_size=1, method='loca...
function sincinterp (line 152) | def sincinterp(image, x, y, kernel_size=3 ):
FILE: core/maths/interpo.py
function scale (line 5) | def scale(inVal, inMin, inMax, outMin, outMax):
function linearInterpo (line 10) | def linearInterpo(x1, x2, y1, y2, x):
FILE: core/maths/kmeans1D.py
function kmeans1d (line 34) | def kmeans1d(data, k, cutoff=False, maxIter=False):
function getClustersValues (line 155) | def getClustersValues(data, clusters):
function getBreaks (line 158) | def getBreaks(data, clusters, includeBounds=False):
FILE: core/proj/ellps.py
class Ellps (line 4) | class Ellps():
method __init__ (line 6) | def __init__(self, a, b):
function dd2meters (line 14) | def dd2meters(dst):
function meters2dd (line 22) | def meters2dd(dst):
FILE: core/proj/reproj.py
function webMercToLonLat (line 43) | def webMercToLonLat(x, y):
function lonLatToWebMerc (line 50) | def lonLatToWebMerc(lon, lat):
function reprojImg (line 61) | def reprojImg(crs1, crs2, ds1, out_ul=None, out_size=None, out_res=None,...
class Reproj (line 187) | class Reproj():
method __init__ (line 189) | def __init__(self, crs1, crs2):
method pts (line 253) | def pts(self, pts):
method pt (line 304) | def pt(self, x, y):
method bbox (line 310) | def bbox(self, bbox):
function reprojPt (line 326) | def reprojPt(crs1, crs2, x, y):
function reprojPts (line 336) | def reprojPts(crs1, crs2, pts):
function reprojBbox (line 346) | def reprojBbox(crs1, crs2, bbox):
FILE: core/proj/srs.py
class SRS (line 33) | class SRS():
method validate (line 40) | def validate(cls, crs):
method __init__ (line 48) | def __init__(self, crs):
method fromGDAL (line 88) | def fromGDAL(cls, ds):
method SRID (line 99) | def SRID(self):
method hasCode (line 106) | def hasCode(self):
method hasAuth (line 110) | def hasAuth(self):
method isSRID (line 114) | def isSRID(self):
method isEPSG (line 118) | def isEPSG(self):
method isWM (line 122) | def isWM(self):
method isWGS84 (line 126) | def isWGS84(self):
method isUTM (line 130) | def isUTM(self):
method __str__ (line 133) | def __str__(self):
method __eq__ (line 140) | def __eq__(self, srs2):
method getOgrSpatialRef (line 143) | def getOgrSpatialRef(self):
method getPyProj (line 163) | def getPyProj(self):
method loadProj4 (line 176) | def loadProj4(self):
method isGeo (line 194) | def isGeo(self):
method getWKT (line 207) | def getWKT(self):
FILE: core/proj/srv.py
class MapTilerCoordinates (line 39) | class MapTilerCoordinates():
method __init__ (line 41) | def __init__(self, apiKey=None):
method reprojPt (line 65) | def reprojPt(self, epsg1, epsg2, x1, y1):
method reprojPts (line 84) | def reprojPts(self, epsg1, epsg2, points):
method search (line 123) | def search(self, query):
method getEsriWkt (line 138) | def getEsriWkt(self, epsg):
class EPSGIO (line 149) | class EPSGIO(MapTilerCoordinates):
class TWCC (line 157) | class TWCC():
method reprojPt (line 160) | def reprojPt(epsg1, epsg2, x1, y1):
FILE: core/proj/utm.py
class OutOfRangeError (line 41) | class OutOfRangeError(ValueError):
function longitude_to_zone_number (line 45) | def longitude_to_zone_number(longitude):
function latitude_to_northern (line 48) | def latitude_to_northern(latitude):
function lonlat_to_zone_northern (line 51) | def lonlat_to_zone_northern(lon, lat):
function zone_number_to_central_longitude (line 56) | def zone_number_to_central_longitude(zone_number):
function _code_from_epsg (line 66) | def _code_from_epsg(epsg):
function epsg_to_zone_northern (line 80) | def epsg_to_zone_northern(epsg):
function lonlat_to_epsg (line 89) | def lonlat_to_epsg(longitude, latitude):
function zone_northern_to_epsg (line 96) | def zone_northern_to_epsg(zone, northern):
class UTM (line 105) | class UTM():
method __init__ (line 107) | def __init__(self, zone, north):
method init_from_epsg (line 118) | def init_from_epsg(cls, epsg):
method init_from_lonlat (line 123) | def init_from_lonlat(cls, lon, lat):
method utm_to_lonlat (line 128) | def utm_to_lonlat(self, easting, northing):
method lonlat_to_utm (line 188) | def lonlat_to_utm(self, longitude, latitude):
FILE: core/settings.py
function getAvailableProjEngines (line 7) | def getAvailableProjEngines():
function getAvailableImgEngines (line 17) | def getAvailableImgEngines():
class Settings (line 28) | class Settings():
method __init__ (line 30) | def __init__(self, **kwargs):
method proj_engine (line 40) | def proj_engine(self):
method proj_engine (line 44) | def proj_engine(self, engine):
method img_engine (line 51) | def img_engine(self):
method img_engine (line 55) | def img_engine(self, engine):
FILE: core/utils/bbox.py
class BBOX (line 26) | class BBOX(dict):
method __init__ (line 29) | def __init__(self, *args, **kwargs):
method __str__ (line 56) | def __str__(self):
method __getitem__ (line 62) | def __getitem__(self, attr):
method __setitem__ (line 66) | def __setitem__(self, key, value):
method __iter__ (line 70) | def __iter__(self):
method keys (line 78) | def keys(self):
method items (line 82) | def items(self):
method values (line 86) | def values(self):
method fromXYZ (line 91) | def fromXYZ(cls, lst):
method toXYZ (line 101) | def toXYZ(self):
method fromLatlon (line 109) | def fromLatlon(cls, lst):
method toLatlon (line 114) | def toLatlon(self):
method hasZ (line 119) | def hasZ(self):
method to2D (line 126) | def to2D(self):
method toGeo (line 130) | def toGeo(self, geoscn):
method __eq__ (line 144) | def __eq__(self, bb):
method overlap (line 153) | def overlap(self, bb):
method isWithin (line 159) | def isWithin(self, bb):
method contains (line 166) | def contains(self, bb):
method __add__ (line 173) | def __add__(self, bb):
method shift (line 186) | def shift(self, dx, dy):
method center (line 194) | def center(self):
method dimensions (line 204) | def dimensions(self):
method corners (line 217) | def corners(self):
method ul (line 222) | def ul(self):
method ur (line 226) | def ur(self):
method bl (line 230) | def bl(self):
method br (line 234) | def br(self):
FILE: core/utils/gradient.py
class Color (line 12) | class Color(object):
method __init__ (line 14) | def __init__(self, values=None, space='RGBA'):
method __str__ (line 57) | def __str__(self):
method __eq__ (line 66) | def __eq__(self, other):
method alpha (line 71) | def alpha(self):
method hex (line 77) | def hex(self):
method RGBA (line 84) | def RGBA(self): #values range from 0 to 255
method rgba (line 90) | def rgba(self): #values range from 0 to 1
method HSVA (line 96) | def HSVA(self): #H ranges from 0° to 360°. Other values range from 0 t...
method hsva (line 103) | def hsva(self): #values range from 0 to 1
method RGB (line 110) | def RGB(self):
method rgb (line 116) | def rgb(self):
method HSV (line 122) | def HSV(self):
method hsv (line 129) | def hsv(self):
method getColor (line 136) | def getColor(self, space='RGB', asDict=False):
method from_RGB (line 184) | def from_RGB(self, R, G, B, A=255):
method from_rgb (line 190) | def from_rgb(self, r, g, b, a=1):
method from_HSV (line 196) | def from_HSV(self, H, S, V, A=1):
method from_hsv (line 203) | def from_hsv(self, h, s, v, a=1):
method from_hex (line 210) | def from_hex(self, hex):
class Stop (line 216) | class Stop():
method __init__ (line 217) | def __init__(self, position, color):
method __lt__ (line 221) | def __lt__(self, other):
class Gradient (line 225) | class Gradient():
method __init__ (line 227) | def __init__(self, svg=False, permissive=False):
method __str__ (line 235) | def __str__(self):
method __readSVG (line 238) | def __readSVG(self, svg):
method positions (line 278) | def positions(self):
method colors (line 281) | def colors(self):
method asList (line 285) | def asList(self, space='RGBA'):
method asDict (line 288) | def asDict(self, space='RGBA'):
method addStop (line 292) | def addStop(self, position, color, reorder=True):
method addStops (line 313) | def addStops(self, positions, colors):
method sortStops (line 321) | def sortStops(self):
method rmColor (line 324) | def rmColor(self, color):
method rmPosition (line 336) | def rmPosition(self, pos):
method rescale (line 346) | def rescale(self, toMin, toMax):
method evaluate (line 352) | def evaluate(self, pos, colorSpace = 'RGB', method='LINEAR'):
method getRangeColor (line 431) | def getRangeColor(self, n, interpoSpace='RGB', interpoMethod='LINEAR'):
method exportSVG (line 443) | def exportSVG(self, svgPath, discrete=False):
FILE: core/utils/timing.py
function perf_clock (line 3) | def perf_clock():
FILE: core/utils/xy.py
class XY (line 23) | class XY(object):
method __init__ (line 25) | def __init__(self, x, y, z=None):
method __str__ (line 37) | def __str__(self):
method __repr__ (line 42) | def __repr__(self):
method __getitem__ (line 44) | def __getitem__(self,item):
method __setitem__ (line 46) | def __setitem__(self, idx, value):
method __iter__ (line 48) | def __iter__(self):
method __len__ (line 50) | def __len__(self):
method x (line 53) | def x(self):
method y (line 56) | def y(self):
method z (line 59) | def z(self):
method xy (line 65) | def xy(self):
method xyz (line 68) | def xyz(self):
FILE: geoscene.py
class SK (line 62) | class SK():
class GeoScene (line 81) | class GeoScene():
method __init__ (line 83) | def __init__(self, scn=None):
method _rna_ui (line 91) | def _rna_ui(self):
method view3dToProj (line 99) | def view3dToProj(self, dx, dy):
method projToView3d (line 108) | def projToView3d(self, dx, dy):
method hasCRS (line 118) | def hasCRS(self):
method hasValidCRS (line 122) | def hasValidCRS(self):
method isGeoref (line 128) | def isGeoref(self):
method isFullyGeoref (line 134) | def isFullyGeoref(self):
method isPartiallyGeoref (line 138) | def isPartiallyGeoref(self):
method isBroken (line 142) | def isBroken(self):
method hasOriginGeo (line 149) | def hasOriginGeo(self):
method hasOriginPrj (line 153) | def hasOriginPrj(self):
method setOriginGeo (line 156) | def setOriginGeo(self, lon, lat):
method setOriginPrj (line 165) | def setOriginPrj(self, x, y, synch=True):
method updOriginPrj (line 178) | def updOriginPrj(self, x, y, updObjLoc=True, synch=True):
method updOriginGeo (line 189) | def updOriginGeo(self, lon, lat, updObjLoc=True):
method moveOriginGeo (line 196) | def moveOriginGeo(self, dx, dy, updObjLoc=True):
method moveOriginPrj (line 203) | def moveOriginPrj(self, dx, dy, useScale=True, updObjLoc=True, synch=T...
method _moveObjLoc (line 217) | def _moveObjLoc(self, dx, dy):
method getOriginGeo (line 224) | def getOriginGeo(self):
method getOriginPrj (line 227) | def getOriginPrj(self):
method delOriginGeo (line 230) | def delOriginGeo(self):
method delOriginPrj (line 234) | def delOriginPrj(self):
method delOrigin (line 238) | def delOrigin(self):
method crs (line 243) | def crs(self):
method crs (line 246) | def crs(self, v):
method crs (line 267) | def crs(self):
method lat (line 273) | def lat(self):
method lat (line 276) | def lat(self, v):
method lat (line 284) | def lat(self):
method lon (line 289) | def lon(self):
method lon (line 292) | def lon(self, v):
method lon (line 300) | def lon(self):
method crsx (line 305) | def crsx(self):
method crsx (line 308) | def crsx(self, v):
method crsx (line 316) | def crsx(self):
method crsy (line 321) | def crsy(self):
method crsy (line 324) | def crsy(self, v):
method crsy (line 332) | def crsy(self):
method scale (line 337) | def scale(self):
method scale (line 340) | def scale(self, v):
method scale (line 345) | def scale(self):
method zoom (line 350) | def zoom(self):
method zoom (line 353) | def zoom(self, v):
method zoom (line 358) | def zoom(self):
method hasScale (line 363) | def hasScale(self):
method hasZoom (line 368) | def hasZoom(self):
class GEOSCENE_OT_coords_viewer (line 375) | class GEOSCENE_OT_coords_viewer(Operator):
method poll (line 384) | def poll(cls, context):
method invoke (line 387) | def invoke(self, context, event):
method modal (line 398) | def modal(self, context, event):
class GEOSCENE_OT_set_crs (line 410) | class GEOSCENE_OT_set_crs(Operator):
method draw (line 435) | def draw(self,context):
method invoke (line 444) | def invoke(self, context, event):
method execute (line 447) | def execute(self, context):
class GEOSCENE_OT_init_org (line 460) | class GEOSCENE_OT_init_org(Operator):
method invoke (line 475) | def invoke(self, context, event):
method execute (line 478) | def execute(self, context):
class GEOSCENE_OT_edit_org_geo (line 492) | class GEOSCENE_OT_edit_org_geo(Operator):
method invoke (line 502) | def invoke(self, context, event):
method execute (line 510) | def execute(self, context):
class GEOSCENE_OT_edit_org_prj (line 518) | class GEOSCENE_OT_edit_org_prj(Operator):
method invoke (line 528) | def invoke(self, context, event):
method execute (line 536) | def execute(self, context):
class GEOSCENE_OT_link_org_geo (line 544) | class GEOSCENE_OT_link_org_geo(Operator):
method execute (line 551) | def execute(self, context):
class GEOSCENE_OT_link_org_prj (line 566) | class GEOSCENE_OT_link_org_prj(Operator):
method execute (line 573) | def execute(self, context):
class GEOSCENE_OT_clear_org (line 588) | class GEOSCENE_OT_clear_org(Operator):
method execute (line 595) | def execute(self, context):
class GEOSCENE_OT_clear_georef (line 600) | class GEOSCENE_OT_clear_georef(Operator):
method execute (line 607) | def execute(self, context):
function getLon (line 616) | def getLon(self):
function getLat (line 620) | def getLat(self):
function setLon (line 624) | def setLon(self, lon):
function setLat (line 632) | def setLat(self, lat):
function getCrsx (line 640) | def getCrsx(self):
function getCrsy (line 644) | def getCrsy(self):
function setCrsx (line 648) | def setCrsx(self, x):
function setCrsy (line 656) | def setCrsy(self, y):
class GEOSCENE_PT_georef (line 666) | class GEOSCENE_PT_georef(Panel):
method draw (line 674) | def draw(self, context):
class GLOBAL_PROPS (line 686) | class GLOBAL_PROPS(PropertyGroup):
function georefManagerLayout (line 696) | def georefManagerLayout(self, context):
function register (line 822) | def register():
function unregister (line 832) | def unregister():
FILE: operators/add_camera_exif.py
function newEmpty (line 41) | def newEmpty(scene, name, location):
function newCamera (line 50) | def newCamera(scene, name, location, focalLength):
function newTargetCamera (line 63) | def newTargetCamera(scene, name, location, focalLength):
class CAMERA_OT_geophotos_add (line 76) | class CAMERA_OT_geophotos_add(Operator):
method invoke (line 107) | def invoke(self, context, event):
method execute (line 117) | def execute(self, context):
class CAMERA_OT_geophotos_setactive (line 220) | class CAMERA_OT_geophotos_setactive(Operator):
method listGeoCam (line 226) | def listGeoCam(self, context):
method draw (line 233) | def draw(self, context):
method invoke (line 237) | def invoke(self, context, event):
method execute (line 243) | def execute(self, context):
function register (line 296) | def register():
function unregister (line 305) | def unregister():
FILE: operators/add_camera_georef.py
class CAMERA_OT_add_georender_cam (line 31) | class CAMERA_OT_add_georender_cam(bpy.types.Operator):
method check (line 51) | def check(self, context):
method draw (line 55) | def draw(self, context):
method poll (line 62) | def poll(cls, context):
method execute (line 65) | def execute(self, context):#every times operator redo options are modi...
function register (line 196) | def register():
function unregister (line 205) | def unregister():
FILE: operators/io_export_shp.py
class EXPORTGIS_OT_shapefile (line 21) | class EXPORTGIS_OT_shapefile(Operator, ExportHelper):
method listCollections (line 56) | def listCollections(self, context):
method poll (line 76) | def poll(cls, context):
method draw (line 79) | def draw(self, context):
method execute (line 88) | def execute(self, context):
function register (line 270) | def register():
function unregister (line 278) | def unregister():
FILE: operators/io_get_dem.py
class IMPORTGIS_OT_dem_query (line 26) | class IMPORTGIS_OT_dem_query(Operator):
method invoke (line 34) | def invoke(self, context, event):
method draw (line 48) | def draw(self,context):
method poll (line 58) | def poll(cls, context):
method execute (line 61) | def execute(self, context):
function register (line 167) | def register():
function unregister (line 175) | def unregister():
FILE: operators/io_import_asc.py
class IMPORTGIS_OT_ascii_grid (line 29) | class IMPORTGIS_OT_ascii_grid(Operator, ImportHelper):
method listPredefCRS (line 44) | def listPredefCRS(self, context):
method draw (line 79) | def draw(self, context):
method total_lines (line 97) | def total_lines(self, filename):
method read_row_newlines (line 108) | def read_row_newlines(self, f, ncols):
method read_row_whitespace (line 114) | def read_row_whitespace(self, f, ncols):
method poll (line 150) | def poll(cls, context):
method execute (line 153) | def execute(self, context):
function register (line 300) | def register():
function unregister (line 308) | def unregister():
FILE: operators/io_import_georaster.py
class IMPORTGIS_OT_georaster (line 54) | class IMPORTGIS_OT_georaster(Operator, ImportHelper):
method listObjects (line 62) | def listObjects(self, context):
method listPredefCRS (line 77) | def listPredefCRS(self, context):
method listSubdivisionModes (line 104) | def listSubdivisionModes(self, context):
method draw (line 146) | def draw(self, context):
method crsInputLayout (line 198) | def crsInputLayout(self, context):
method poll (line 207) | def poll(cls, context):
method execute (line 210) | def execute(self, context):
function register (line 484) | def register():
function unregister (line 492) | def unregister():
FILE: operators/io_import_osm.py
function getTags (line 30) | def getTags():
function queryBuilder (line 45) | def queryBuilder(bbox, tags=['building', 'highway'], types=['node', 'way...
function joinBmesh (line 91) | def joinBmesh(src_bm, dest_bm):
class OSM_IMPORT (line 105) | class OSM_IMPORT():
method enumTags (line 108) | def enumTags(self, context):
method listObjects (line 137) | def listObjects(self, context):
method draw (line 162) | def draw(self, context):
method build (line 180) | def build(self, context, result, dstCRS):
class IMPORTGIS_OT_osm_file (line 512) | class IMPORTGIS_OT_osm_file(Operator, OSM_IMPORT):
method invoke (line 532) | def invoke(self, context, event):
method execute (line 540) | def execute(self, context):
class IMPORTGIS_OT_osm_query (line 606) | class IMPORTGIS_OT_osm_query(Operator, OSM_IMPORT):
method check (line 615) | def check(self, context):
method poll (line 620) | def poll(cls, context):
method invoke (line 624) | def invoke(self, context, event):
method execute (line 632) | def execute(self, context):
function register (line 693) | def register():
function unregister (line 702) | def unregister():
FILE: operators/io_import_shp.py
class IMPORTGIS_OT_shapefile_file_dialog (line 54) | class IMPORTGIS_OT_shapefile_file_dialog(Operator):
method invoke (line 75) | def invoke(self, context, event):
method draw (line 79) | def draw(self, context):
method execute (line 84) | def execute(self, context):
class IMPORTGIS_OT_shapefile_props_dialog (line 93) | class IMPORTGIS_OT_shapefile_props_dialog(Operator):
method check (line 104) | def check(self, context):
method listFields (line 107) | def listFields(self, context):
method listPredefCRS (line 121) | def listPredefCRS(self, context):
method listObjects (line 124) | def listObjects(self, context):
method draw (line 207) | def draw(self, context):
method shpCRSInputLayout (line 246) | def shpCRSInputLayout(self, context):
method invoke (line 256) | def invoke(self, context, event):
method execute (line 259) | def execute(self, context):
class IMPORTGIS_OT_shapefile (line 299) | class IMPORTGIS_OT_shapefile(Operator):
method poll (line 334) | def poll(cls, context):
method __del__ (line 337) | def __del__(self):
method execute (line 340) | def execute(self, context):
function register (line 747) | def register():
function unregister (line 756) | def unregister():
FILE: operators/lib/osm/nominatim.py
function nominatimQuery (line 14) | def nominatimQuery(
FILE: operators/lib/osm/overpy/__init__.py
function is_valid_type (line 30) | def is_valid_type(element, cls):
class Overpass (line 42) | class Overpass(object):
method __init__ (line 49) | def __init__(self, overpass_server="http://overpass-api.de/api/interpr...
method query (line 63) | def query(self, query):
method parse_json (line 131) | def parse_json(self, data, encoding="utf-8"):
method parse_xml (line 147) | def parse_xml(self, data, encoding="utf-8"):
class Result (line 173) | class Result(object):
method __init__ (line 179) | def __init__(self, elements=None, api=None):
method expand (line 196) | def expand(self, other):
method append (line 215) | def append(self, element):
method get_elements (line 225) | def get_elements(self, filter_cls, elem_id=None):
method get_ids (line 246) | def get_ids(self, filter_cls):
method get_node_ids (line 254) | def get_node_ids(self):
method get_way_ids (line 257) | def get_way_ids(self):
method get_relation_ids (line 260) | def get_relation_ids(self):
method from_json (line 264) | def from_json(cls, data, api=None):
method from_xml (line 285) | def from_xml(cls, data, api=None, iterparse=False):
method get_node (line 332) | def get_node(self, node_id, resolve_missing=False):
method get_nodes (line 367) | def get_nodes(self, node_id=None, **kwargs):
method get_relation (line 377) | def get_relation(self, rel_id, resolve_missing=False):
method get_relations (line 412) | def get_relations(self, rel_id=None, **kwargs):
method get_way (line 422) | def get_way(self, way_id, resolve_missing=False):
method get_ways (line 457) | def get_ways(self, way_id=None, **kwargs):
method get_bounds (line 467) | def get_bounds(self):
class Element (line 484) | class Element(object):
method __init__ (line 490) | def __init__(self, attributes=None, result=None, tags=None):
class Node (line 505) | class Node(Element):
method __init__ (line 513) | def __init__(self, node_id=None, lat=None, lon=None, **kwargs):
method __repr__ (line 529) | def __repr__(self):
method from_json (line 533) | def from_json(cls, data, result=None):
method from_xml (line 567) | def from_xml(cls, child, result=None):
class Way (line 616) | class Way(Element):
method __init__ (line 624) | def __init__(self, way_id=None, node_ids=None, **kwargs):
method __repr__ (line 641) | def __repr__(self):
method nodes (line 645) | def nodes(self):
method get_nodes (line 651) | def get_nodes(self, resolve_missing=False):
method from_json (line 708) | def from_json(cls, data, result=None):
method from_xml (line 741) | def from_xml(cls, child, result=None):
class Relation (line 792) | class Relation(Element):
method __init__ (line 800) | def __init__(self, rel_id=None, members=None, **kwargs):
method __repr__ (line 813) | def __repr__(self):
method from_json (line 817) | def from_json(cls, data, result=None):
method from_xml (line 863) | def from_xml(cls, child, result=None):
class RelationMember (line 918) | class RelationMember(object):
method __init__ (line 924) | def __init__(self, ref=None, role=None, result=None):
method from_json (line 937) | def from_json(cls, data, result=None):
method from_xml (line 960) | def from_xml(cls, child, result=None):
class RelationNode (line 985) | class RelationNode(RelationMember):
method resolve (line 988) | def resolve(self, resolve_missing=False):
method __repr__ (line 991) | def __repr__(self):
class RelationWay (line 995) | class RelationWay(RelationMember):
method resolve (line 998) | def resolve(self, resolve_missing=False):
method __repr__ (line 1001) | def __repr__(self):
class RelationRelation (line 1005) | class RelationRelation(RelationMember):
method resolve (line 1008) | def resolve(self, resolve_missing=False):
method __repr__ (line 1011) | def __repr__(self):
FILE: operators/lib/osm/overpy/exception.py
class OverPyException (line 1) | class OverPyException(BaseException):
class DataIncomplete (line 6) | class DataIncomplete(OverPyException):
method __init__ (line 11) | def __init__(self, *args, **kwargs):
class ElementDataWrongType (line 20) | class ElementDataWrongType(OverPyException):
method __init__ (line 29) | def __init__(self, type_expected, type_provided=None):
method __str__ (line 33) | def __str__(self):
class OverpassBadRequest (line 40) | class OverpassBadRequest(OverPyException):
method __init__ (line 49) | def __init__(self, query, msgs=None):
method __str__ (line 55) | def __str__(self):
class OverpassGatewayTimeout (line 65) | class OverpassGatewayTimeout(OverPyException):
method __init__ (line 69) | def __init__(self):
class OverpassTooManyRequests (line 73) | class OverpassTooManyRequests(OverPyException):
method __init__ (line 77) | def __init__(self):
class OverpassUnknownContentType (line 81) | class OverpassUnknownContentType(OverPyException):
method __init__ (line 88) | def __init__(self, content_type):
method __str__ (line 91) | def __str__(self):
class OverpassUnknownHTTPStatusCode (line 97) | class OverpassUnknownHTTPStatusCode(OverPyException):
method __init__ (line 104) | def __init__(self, code):
method __str__ (line 107) | def __str__(self):
FILE: operators/lib/osm/overpy/helper.py
function get_street (line 6) | def get_street(street, areacode, api=None):
function get_intersection (line 38) | def get_intersection(street1, street2, areacode, api=None):
FILE: operators/mesh_delaunay_voronoi.py
class Point (line 18) | class Point:
method __init__ (line 19) | def __init__(self, x, y, z):
function unique (line 22) | def unique(L):
function checkEqual (line 41) | def checkEqual(lst):
class OBJECT_OT_tesselation_delaunay (line 45) | class OBJECT_OT_tesselation_delaunay(bpy.types.Operator):
method execute (line 51) | def execute(self, context):
class OBJECT_OT_tesselation_voronoi (line 135) | class OBJECT_OT_tesselation_voronoi(bpy.types.Operator):
method execute (line 151) | def execute(self, context):
function register (line 227) | def register():
function unregister (line 236) | def unregister():
FILE: operators/mesh_earth_sphere.py
function lonlat2xyz (line 12) | def lonlat2xyz(R, lon, lat):
class OBJECT_OT_earth_sphere (line 20) | class OBJECT_OT_earth_sphere(Operator):
method execute (line 28) | def execute(self, context):
function getZDelta (line 59) | def getZDelta(d):
class OBJECT_OT_earth_curvature (line 65) | class OBJECT_OT_earth_curvature(Operator):
method execute (line 71) | def execute(self, context):
function register (line 98) | def register():
function unregister (line 107) | def unregister():
FILE: operators/nodes_terrain_analysis_builder.py
class TERRAIN_ANALYSIS_OT_build_nodes (line 14) | class TERRAIN_ANALYSIS_OT_build_nodes(Operator):
method execute (line 20) | def execute(self, context):
function register (line 364) | def register():
function unregister (line 372) | def unregister():
FILE: operators/nodes_terrain_analysis_reclassify.py
class RECLASS_PG_color (line 46) | class RECLASS_PG_color(PropertyGroup):
method updStop (line 49) | def updStop(item, context):
method updColor (line 79) | def updColor(item, context):
function populateList (line 97) | def populateList(colorRampNode):
function updateAnalysisMode (line 114) | def updateAnalysisMode(scn, context):
function setBounds (line 123) | def setBounds():
function scene_update (line 151) | def scene_update(scn):
class RECLASS_UL_stops (line 184) | class RECLASS_UL_stops(UIList):
method getAspectLabels (line 186) | def getAspectLabels(self):
method draw_item (line 203) | def draw_item(self, context, layout, data, item, icon, active_data, ac...
class RECLASS_PT_reclassify (line 238) | class RECLASS_PT_reclassify(Panel):
method draw (line 245) | def draw(self, context):
class RECLASS_OT_switch_interpolation (line 285) | class RECLASS_OT_switch_interpolation(Operator):
method execute (line 290) | def execute(self, context):
class RECLASS_OT_flip (line 300) | class RECLASS_OT_flip(Operator):
method execute (line 305) | def execute(self, context):
class RECLASS_OT_refresh (line 323) | class RECLASS_OT_refresh(Operator):
method execute (line 328) | def execute(self, context):
class RECLASS_OT_clear (line 334) | class RECLASS_OT_clear(Operator):
method execute (line 339) | def execute(self, context):
class RECLASS_OT_add (line 355) | class RECLASS_OT_add(Operator):
method execute (line 360) | def execute(self, context):
class RECLASS_OT_rm (line 388) | class RECLASS_OT_rm(Operator):
method execute (line 393) | def execute(self, context):
function clearRamp (line 417) | def clearRamp(stops, startColor=(0,0,0,1), endColor=(1,1,1,1), startPos=...
function getValues (line 431) | def getValues():
class RECLASS_OT_auto (line 471) | class RECLASS_OT_auto(Operator):
method invoke (line 491) | def invoke(self, context, event):
method execute (line 502) | def execute(self, context):
class RECLASS_PG_color_preview (line 644) | class RECLASS_PG_color_preview(PropertyGroup):
class RECLASS_OT_quick_gradient (line 649) | class RECLASS_OT_quick_gradient(Operator):
method check (line 665) | def check(self, context):
method initPreview (line 668) | def initPreview(self, context):
method updatePreview (line 686) | def updatePreview(self, context):
method invoke (line 707) | def invoke(self, context, event):
method draw (line 714) | def draw(self, context):
method execute (line 726) | def execute(self, context):
function filesList (line 758) | def filesList(inFolder, ext):
class RECLASS_OT_svg_gradient (line 771) | class RECLASS_OT_svg_gradient(Operator):
method listSVG (line 776) | def listSVG(self, context):
method updatePreview (line 783) | def updatePreview(self, context):
method invoke (line 821) | def invoke(self, context, event):
method draw (line 833) | def draw(self, context):#layout for invoke props modal dialog
method execute (line 847) | def execute(self, context):
class RECLASS_OT_export_svg (line 875) | class RECLASS_OT_export_svg(Operator):
method check (line 903) | def check(self, context):
method invoke (line 906) | def invoke(self, context, event):
method draw (line 911) | def draw(self, context):
method execute (line 923) | def execute(self, context):
function register (line 963) | def register():
function unregister (line 987) | def unregister():
FILE: operators/object_drop.py
function get_align_matrix (line 34) | def get_align_matrix(location, normal):
function get_lowest_world_co (line 43) | def get_lowest_world_co(ob, mat_parent=None):
class OBJECT_OT_drop_to_ground (line 61) | class OBJECT_OT_drop_to_ground(Operator):
method poll (line 85) | def poll(cls, context):
method draw (line 93) | def draw(self, context):
method execute (line 101) | def execute(self, context):
function register (line 164) | def register():
function unregister (line 173) | def unregister():
FILE: operators/utils/bgis_utils.py
function isTopView (line 9) | def isTopView(context):
function mouseTo3d (line 16) | def mouseTo3d(context, x, y):
class DropToGround (line 28) | class DropToGround():
method __init__ (line 31) | def __init__(self, scn, ground, method='OBJ'):
method rayCast (line 41) | def rayCast(self, x, y):
function placeObj (line 69) | def placeObj(mesh, objName):
function adjust3Dview (line 82) | def adjust3Dview(context, bbox, zoomToSelect=True):
function showTextures (line 114) | def showTextures(context):
function addTexture (line 124) | def addTexture(mat, img, uvLay, name='texture'):
class getBBOX (line 152) | class getBBOX():
method fromObj (line 157) | def fromObj(obj, applyTransform = True):
method fromScn (line 172) | def fromScn(cls, scn):
method fromBmesh (line 187) | def fromBmesh(bm):
method fromTopView (line 199) | def fromTopView(context):
FILE: operators/utils/delaunay_voronoi.py
class Context (line 76) | class Context(object):
method __init__ (line 77) | def __init__(self):
method getClipEdges (line 89) | def getClipEdges(self):
method getClipPolygons (line 121) | def getClipPolygons(self, closePoly):
method clipLine (line 177) | def clipLine(self, x1, y1, equation, leftDir):
method inExtent (line 213) | def inExtent(self, x, y):
method orderPts (line 217) | def orderPts(self, edges):
method setClipBuffer (line 255) | def setClipBuffer(self, xpourcent, ypourcent):
method outSite (line 267) | def outSite(self,s):
method outVertex (line 275) | def outVertex(self,s):
method outTriple (line 284) | def outTriple(self,s1,s2,s3):
method outBisector (line 291) | def outBisector(self,edge):
method outEdge (line 298) | def outEdge(self,edge):
function voronoi (line 323) | def voronoi(siteList,context):
function isEqual (line 463) | def isEqual(a,b,relativeError=TOLERANCE):
class Site (line 469) | class Site(object):
method __init__ (line 470) | def __init__(self,x=0.0,y=0.0,sitenum=0):
method dump (line 475) | def dump(self):
method __lt__ (line 478) | def __lt__(self,other):
method __eq__ (line 490) | def __eq__(self,other):
method distance (line 494) | def distance(self,other):
class Edge (line 500) | class Edge(object):
method __init__ (line 506) | def __init__(self):
method dump (line 514) | def dump(self):
method setEndpoint (line 519) | def setEndpoint(self, lrFlag, site):
method bisect (line 526) | def bisect(s1,s2):
class Halfedge (line 559) | class Halfedge(object):
method __init__ (line 560) | def __init__(self,edge=None,pm=Edge.LE):
method dump (line 569) | def dump(self):
method __lt__ (line 580) | def __lt__(self,other):
method __eq__ (line 592) | def __eq__(self,other):
method leftreg (line 596) | def leftreg(self,default):
method rightreg (line 604) | def rightreg(self,default):
method isPointRightOf (line 614) | def isPointRightOf(self,pt):
method intersect (line 657) | def intersect(self,other):
class EdgeList (line 692) | class EdgeList(object):
method __init__ (line 693) | def __init__(self,xmin,xmax,nsites):
method insert (line 708) | def insert(self,left,he):
method delete (line 714) | def delete(self,he):
method gethash (line 720) | def gethash(self,b):
method leftbnd (line 731) | def leftbnd(self,pt):
class PriorityQueue (line 769) | class PriorityQueue(object):
method __init__ (line 770) | def __init__(self,ymin,ymax,nsites):
method __len__ (line 780) | def __len__(self):
method isEmpty (line 783) | def isEmpty(self):
method insert (line 786) | def insert(self,he,site,offset):
method delete (line 798) | def delete(self,he):
method getBucket (line 807) | def getBucket(self,he):
method getMinPt (line 814) | def getMinPt(self):
method popMinHalfedge (line 822) | def popMinHalfedge(self):
class SiteList (line 830) | class SiteList(object):
method __init__ (line 831) | def __init__(self,pointList):
method setSiteNumber (line 845) | def setSiteNumber(self,site):
class Iterator (line 849) | class Iterator(object):
method __init__ (line 850) | def __init__(this,lst): this.generator = (s for s in lst)
method __iter__ (line 851) | def __iter__(this): return this
method next (line 852) | def next(this):
method iterator (line 861) | def iterator(self):
method __iter__ (line 864) | def __iter__(self):
method __len__ (line 867) | def __len__(self):
method _getxmin (line 870) | def _getxmin(self): return self.__xmin
method _getymin (line 871) | def _getymin(self): return self.__ymin
method _getxmax (line 872) | def _getxmax(self): return self.__xmax
method _getymax (line 873) | def _getymax(self): return self.__ymax
method _getextent (line 874) | def _getextent(self): return self.__extent
function computeVoronoiDiagram (line 883) | def computeVoronoiDiagram(points, xBuff=0, yBuff=0, polygonsOutput=False...
function formatEdgesOutput (line 925) | def formatEdgesOutput(edges):
function formatPolygonsOutput (line 940) | def formatPolygonsOutput(polygons):
function computeDelaunayTriangulation (line 956) | def computeDelaunayTriangulation(points):
FILE: operators/utils/georaster_utils.py
function _exportAsMesh (line 33) | def _exportAsMesh(georaster, dx=0, dy=0, step=1, buildFaces=True, flat=F...
function exportAsMesh (line 73) | def exportAsMesh(georaster, dx=0, dy=0, step=1, buildFaces=True, subset=...
function rasterExtentToMesh (line 140) | def rasterExtentToMesh(name, rast, dx, dy, pxLoc='CORNER', reproj=None, ...
function geoRastUVmap (line 161) | def geoRastUVmap(obj, uvLayer, rast, dx, dy, reproj=None):
function setDisplacer (line 183) | def setDisplacer(obj, rast, uvTxtLayer, mid=0, interpolation=False):
class bpyGeoRaster (line 218) | class bpyGeoRaster(GeoRaster):
method __init__ (line 220) | def __init__(self, path, subBoxGeo=None, useGDAL=False, clip=False, fi...
method _load (line 262) | def _load(self, pack=False):
method unload (line 275) | def unload(self):
method isLoaded (line 281) | def isLoaded(self):
method isPacked (line 288) | def isPacked(self):
method toBitDepth (line 303) | def toBitDepth(self, a):
method fromBitDepth (line 310) | def fromBitDepth(self, a):
method getPixelsArray (line 317) | def getPixelsArray(self, bandIdx=None, subset=False):
method flattenPixelsArray (line 372) | def flattenPixelsArray(self, px):
FILE: operators/view3d_mapviewer.py
class BaseMap (line 60) | class BaseMap(GeoScene):
method __init__ (line 64) | def __init__(self, context, srckey, laykey, grdkey=None):
method get (line 140) | def get(self):
method stop (line 147) | def stop(self):
method run (line 153) | def run(self):
method moveOrigin (line 164) | def moveOrigin(self, dx, dy, useScale=True, updObjLoc=True):
method request (line 168) | def request(self):
method place (line 216) | def place(self):
function drawInfosText (line 289) | def drawInfosText(self, context):
function drawZoomBox (line 323) | def drawZoomBox(self, context):
class VIEW3D_OT_map_start (line 352) | class VIEW3D_OT_map_start(Operator):
method check (line 360) | def check(self, context):
method listSources (line 363) | def listSources(self, context):
method listGrids (line 370) | def listGrids(self, context):
method listLayers (line 382) | def listLayers(self, context):
method draw (line 418) | def draw(self, context):
method invoke (line 467) | def invoke(self, context, event):
method execute (line 485) | def execute(self, context):
class VIEW3D_OT_map_viewer (line 534) | class VIEW3D_OT_map_viewer(Operator):
method poll (line 550) | def poll(cls, context):
method __del__ (line 554) | def __del__(self):
method invoke (line 559) | def invoke(self, context, event):
method modal (line 625) | def modal(self, context, event):
class VIEW3D_OT_map_search (line 986) | class VIEW3D_OT_map_search(bpy.types.Operator):
method invoke (line 995) | def invoke(self, context, event):
method execute (line 1002) | def execute(self, context):
function register (line 1030) | def register():
function unregister (line 1040) | def unregister():
FILE: prefs.py
function getAppData (line 19) | def getAppData():
class BGIS_OT_pref_show (line 69) | class BGIS_OT_pref_show(Operator):
method execute (line 76) | def execute(self, context):
class BGIS_PREFS (line 88) | class BGIS_PREFS(AddonPreferences):
method listPredefCRS (line 95) | def listPredefCRS(self, context):
method getProjEngineItems (line 111) | def getProjEngineItems(self, context):
method updateProjEngine (line 123) | def updateProjEngine(self, context):
method getImgEngineItems (line 136) | def getImgEngineItems(self, context):
method updateImgEngine (line 146) | def updateImgEngine(self, context):
method listOsmTags (line 161) | def listOsmTags(self, context):
method getCacheFolder5x (line 176) | def getCacheFolder5x(self, v, isSet):
method getCacheFolder (line 179) | def getCacheFolder(self):
method setCacheFolder5x (line 182) | def setCacheFolder5x(self, newVal, currentVal, isSet):
method setCacheFolder (line 188) | def setCacheFolder(self, value):
method listOverpassServer (line 233) | def listOverpassServer(self, context):
method listDemServer (line 246) | def listDemServer(self, context):
method updateMapTilerApiKey (line 264) | def updateMapTilerApiKey(self, context):
method updateLogLevel (line 290) | def updateLogLevel(self, context):
method draw (line 303) | def draw(self, context):
class PredefCRS (line 377) | class PredefCRS():
method getData (line 385) | def getData():
method getName (line 391) | def getName(cls, key):
method getEnumItems (line 400) | def getEnumItems(cls):
class BGIS_OT_add_predef_crs (line 408) | class BGIS_OT_add_predef_crs(Operator):
method check (line 418) | def check(self, context):
method search (line 421) | def search(self, context):
method updEnum (line 437) | def updEnum(self, context):
method fill (line 445) | def fill(self, context):
method invoke (line 461) | def invoke(self, context, event):
method draw (line 464) | def draw(self, context):
method execute (line 481) | def execute(self, context):
class BGIS_OT_rmv_predef_crs (line 499) | class BGIS_OT_rmv_predef_crs(Operator):
method execute (line 506) | def execute(self, context):
class BGIS_OT_reset_predef_crs (line 516) | class BGIS_OT_reset_predef_crs(Operator):
method execute (line 523) | def execute(self, context):
class BGIS_OT_edit_predef_crs (line 529) | class BGIS_OT_edit_predef_crs(Operator):
method invoke (line 540) | def invoke(self, context, event):
method execute (line 550) | def execute(self, context):
class BGIS_OT_add_osm_tag (line 569) | class BGIS_OT_add_osm_tag(Operator):
method invoke (line 577) | def invoke(self, context, event):
method execute (line 580) | def execute(self, context):
class BGIS_OT_rmv_osm_tag (line 589) | class BGIS_OT_rmv_osm_tag(Operator):
method execute (line 596) | def execute(self, context):
class BGIS_OT_reset_osm_tags (line 606) | class BGIS_OT_reset_osm_tags(Operator):
method execute (line 613) | def execute(self, context):
class BGIS_OT_edit_osm_tag (line 619) | class BGIS_OT_edit_osm_tag(Operator):
method invoke (line 628) | def invoke(self, context, event):
method execute (line 635) | def execute(self, context):
class BGIS_OT_add_dem_server (line 649) | class BGIS_OT_add_dem_server(Operator):
method invoke (line 659) | def invoke(self, context, event):
method execute (line 662) | def execute(self, context):
class BGIS_OT_rmv_dem_server (line 674) | class BGIS_OT_rmv_dem_server(Operator):
method execute (line 681) | def execute(self, context):
class BGIS_OT_reset_dem_server (line 691) | class BGIS_OT_reset_dem_server(Operator):
method execute (line 698) | def execute(self, context):
class BGIS_OT_edit_dem_server (line 704) | class BGIS_OT_edit_dem_server(Operator):
method invoke (line 715) | def invoke(self, context, event):
method execute (line 725) | def execute(self, context):
class EditEnum (line 741) | class EditEnum():
method __init__ (line 748) | def __init__(self, enumName):
method getData (line 753) | def getData(self):
method append (line 758) | def append(self, value, label, tooltip, check=lambda x: True):
method remove (line 765) | def remove(self, key):
method edit (line 771) | def edit(self, key, value, label, tooltip):
method reset (line 775) | def reset(self):
class BGIS_OT_add_overpass_server (line 781) | class BGIS_OT_add_overpass_server(Operator):
method invoke (line 791) | def invoke(self, context, event):
method execute (line 794) | def execute(self, context):
class BGIS_OT_rmv_overpass_server (line 803) | class BGIS_OT_rmv_overpass_server(Operator):
method execute (line 810) | def execute(self, context):
class BGIS_OT_reset_overpass_server (line 820) | class BGIS_OT_reset_overpass_server(Operator):
method execute (line 827) | def execute(self, context):
class BGIS_OT_edit_overpass_server (line 833) | class BGIS_OT_edit_overpass_server(Operator):
method invoke (line 844) | def invoke(self, context, event):
method execute (line 854) | def execute(self, context):
function register (line 886) | def register():
function unregister (line 902) | def unregister():
Condensed preview — 90 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,323K chars).
[
{
"path": ".gitignore",
"chars": 35,
"preview": "__pycache__/\n*.py[cod]\n*$py.class\n\n"
},
{
"path": "LICENSE",
"chars": 35149,
"preview": " GNU GENERAL PUBLIC LICENSE\n Version 3, 29 June 2007\n\n Copyright (C) 2007 Free "
},
{
"path": "README.md",
"chars": 1706,
"preview": "Blender GIS\n==========\nBlender minimum version required : v2.83\n\nNote : Since 2022, the OpenTopography web service requi"
},
{
"path": "__init__.py",
"chars": 13396,
"preview": "# -*- coding:utf-8 -*-\r\n\r\n# ***** GPL LICENSE BLOCK *****\r\n#\r\n# This program is free software: you can redistribute it"
},
{
"path": "clients/QtMapServiceClient.py",
"chars": 11832,
"preview": "# -*- coding:utf-8 -*-\nimport sys, os, time\nimport sys, os\nsys.path.append(os.path.abspath('..'))\n\nfrom PyQt4 import QtG"
},
{
"path": "clients/QtMapServiceClient.ui",
"chars": 14233,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>QtMapService</class>\n <widget class=\"QDialog\" name=\"Qt"
},
{
"path": "core/__init__.py",
"chars": 457,
"preview": "import logging\nlogging.basicConfig(level=logging.getLevelName('INFO'))\n\nfrom .checkdeps import HAS_GDAL, HAS_PYPROJ, HAS"
},
{
"path": "core/basemaps/__init__.py",
"chars": 145,
"preview": "from .servicesDefs import GRIDS, SOURCES\nfrom .mapservice import MapService, TileMatrix, BBoxRequest, BBoxRequestMZ\nfrom"
},
{
"path": "core/basemaps/gpkg.py",
"chars": 9298,
"preview": "# -*- coding:utf-8 -*-\n\n# ***** GPL LICENSE BLOCK *****\n#\n# This program is free software: you can redistribute it and"
},
{
"path": "core/basemaps/mapservice.py",
"chars": 26105,
"preview": "# -*- coding:utf-8 -*-\n\n# ***** GPL LICENSE BLOCK *****\n#\n# This program is free software: you can redistribute it and"
},
{
"path": "core/basemaps/servicesDefs.py",
"chars": 11942,
"preview": "# -*- coding:utf-8 -*-\n\nimport math\n\n####################################\n\n# Tiles maxtrix definitions\n\n#########"
},
{
"path": "core/checkdeps.py",
"chars": 848,
"preview": "import logging\nlog = logging.getLogger(__name__)\n\n#GDAL\ntry:\n\tfrom osgeo import gdal\nexcept:\n\tHAS_GDAL = False\n\tlog.debu"
},
{
"path": "core/errors.py",
"chars": 351,
"preview": "\n\n\nclass OverlapError(Exception):\n\tdef __init__(self):\n\t\tpass\n\tdef __str__(self):\n\t\treturn \"Non overlap data\"\n\nclass Rep"
},
{
"path": "core/georaster/__init__.py",
"chars": 190,
"preview": "from .georef import GeoRef\nfrom .georaster import GeoRaster\nfrom .npimg import NpImage\nfrom .bigtiffwriter import BigTif"
},
{
"path": "core/georaster/bigtiffwriter.py",
"chars": 4094,
"preview": "# -*- coding:utf-8 -*-\n\n# This file is part of BlenderGIS\n\n# ***** GPL LICENSE BLOCK *****\n#\n# This program is free so"
},
{
"path": "core/georaster/georaster.py",
"chars": 7954,
"preview": "# -*- coding:utf-8 -*-\r\n\r\n# This file is part of BlenderGIS\r\n\r\n# ***** GPL LICENSE BLOCK *****\r\n#\r\n# This program is f"
},
{
"path": "core/georaster/georef.py",
"chars": 16170,
"preview": "# -*- coding:utf-8 -*-\r\n\r\n# This file is part of BlenderGIS\r\n\r\n# ***** GPL LICENSE BLOCK *****\r\n#\r\n# This program is f"
},
{
"path": "core/georaster/img_utils.py",
"chars": 3828,
"preview": "# -*- coding:utf-8 -*-\r\n\r\n# This file is part of BlenderGIS\r\n\r\n# ***** GPL LICENSE BLOCK *****\r\n#\r\n# This program is f"
},
{
"path": "core/georaster/npimg.py",
"chars": 15061,
"preview": "# -*- coding:utf-8 -*-\r\n\r\n# This file is part of BlenderGIS\r\n\r\n# ***** GPL LICENSE BLOCK *****\r\n#\r\n# This program is f"
},
{
"path": "core/lib/Tyf/VERSION",
"chars": 5,
"preview": "1.2.5"
},
{
"path": "core/lib/Tyf/__init__.py",
"chars": 14912,
"preview": "# -*- encoding:utf-8 -*-\n__copyright__ = \"Copyright © 2012-2015, THOORENS Bruno - http://bruno.thoorens.free.fr/licences"
},
{
"path": "core/lib/Tyf/decoders.py",
"chars": 1415,
"preview": "# -*- encoding:utf-8 -*-\n# Copyright 2012-2015, THOORENS Bruno - http://bruno.thoorens.free.fr/licences/tyf.html\nimport "
},
{
"path": "core/lib/Tyf/encoders.py",
"chars": 3139,
"preview": "# -*- encoding:utf-8 -*-\n# Copyright 2012-2015, THOORENS Bruno - http://bruno.thoorens.free.fr/licences/tyf.html\nfrom . "
},
{
"path": "core/lib/Tyf/gkd.py",
"chars": 8330,
"preview": "# -*- encoding: utf-8 -*-\n# Copyright 2012-2015, THOORENS Bruno - http://bruno.thoorens.free.fr/licences/tyf.html\n# ~ ht"
},
{
"path": "core/lib/Tyf/ifd.py",
"chars": 6906,
"preview": "# -*- encoding:utf-8 -*-\n# Copyright 2012-2015, THOORENS Bruno - http://bruno.thoorens.free.fr/licences/tyf.html\n\nfrom ."
},
{
"path": "core/lib/Tyf/tags.py",
"chars": 26866,
"preview": "# -*- encoding:utf-8 -*-\n# Copyright 2012-2015, THOORENS Bruno - http://bruno.thoorens.free.fr/licences/tyf.html\n# ~ htt"
},
{
"path": "core/lib/Tyf/values.py",
"chars": 267167,
"preview": "# -*- encoding:utf-8 -*-\n# Copyright 2012-2015, THOORENS Bruno - http://bruno.thoorens.free.fr/licences/tyf.html\n\n## Tif"
},
{
"path": "core/lib/imageio/README.md",
"chars": 323,
"preview": "This is the Python package that is installed on the user's system.\n\nIt consists of a `core` module, which implements the"
},
{
"path": "core/lib/imageio/__init__.py",
"chars": 1322,
"preview": "# -*- coding: utf-8 -*-\n# Copyright (c) 2015, imageio contributors\n# imageio is distributed under the terms of the (new)"
},
{
"path": "core/lib/imageio/core/__init__.py",
"chars": 759,
"preview": "# -*- coding: utf-8 -*-\n# Copyright (c) 2015, imageio contributors\n# Distributed under the (new) BSD License. See LICENS"
},
{
"path": "core/lib/imageio/core/fetching.py",
"chars": 7943,
"preview": "# -*- coding: utf-8 -*-\n# Copyright (c) 2015, imageio contributors\n# Based on code from the vispy project\n# Distributed "
},
{
"path": "core/lib/imageio/core/findlib.py",
"chars": 6260,
"preview": "# -*- coding: utf-8 -*-\n# Copyright (c) 2015, imageio contributors\n# Copyright (C) 2013, Zach Pincus, Almar Klein and ot"
},
{
"path": "core/lib/imageio/core/format.py",
"chars": 23527,
"preview": "# -*- coding: utf-8 -*-\n# Copyright (c) 2015, imageio contributors\n# imageio is distributed under the terms of the (new)"
},
{
"path": "core/lib/imageio/core/functions.py",
"chars": 16647,
"preview": "# -*- coding: utf-8 -*-\n# Copyright (c) 2015, imageio contributors\n# imageio is distributed under the terms of the (new)"
},
{
"path": "core/lib/imageio/core/request.py",
"chars": 17094,
"preview": "# -*- coding: utf-8 -*-\n# Copyright (c) 2015, imageio contributors\n# imageio is distributed under the terms of the (new)"
},
{
"path": "core/lib/imageio/core/util.py",
"chars": 18976,
"preview": "# -*- coding: utf-8 -*-\n# Copyright (c) 2015, imageio contributors\n# imageio is distributed under the terms of the (new)"
},
{
"path": "core/lib/imageio/freeze.py",
"chars": 284,
"preview": "\"\"\" \nHelper functions for freezing imageio.\n\"\"\"\n\nimport sys\n\n\ndef get_includes():\n if sys.version_info[0] == 3:\n "
},
{
"path": "core/lib/imageio/plugins/__init__.py",
"chars": 3152,
"preview": "# -*- coding: utf-8 -*-\n# Copyright (c) 2015, imageio contributors\n# imageio is distributed under the terms of the (new)"
},
{
"path": "core/lib/imageio/plugins/_freeimage.py",
"chars": 51955,
"preview": "# -*- coding: utf-8 -*-\n# Copyright (c) 2015, imageio contributors\n# imageio is distributed under the terms of the (new)"
},
{
"path": "core/lib/imageio/plugins/freeimage.py",
"chars": 16988,
"preview": "# -*- coding: utf-8 -*-\n# Copyright (c) 2015, imageio contributors\n# imageio is distributed under the terms of the (new)"
},
{
"path": "core/lib/imageio/resources/shipped_resources_go_here",
"chars": 0,
"preview": ""
},
{
"path": "core/lib/imageio/testing.py",
"chars": 7102,
"preview": "# -*- coding: utf-8 -*-\n# Copyright (c) 2015, imageio contributors\n# Distributed under the (new) BSD License. See LICENS"
},
{
"path": "core/lib/imghdr.py",
"chars": 3795,
"preview": "\"\"\"Recognize image file formats based on their first few bytes.\"\"\"\n\nfrom os import PathLike\n\n__all__ = [\"what\"]\n\n#------"
},
{
"path": "core/lib/shapefile.py",
"chars": 76938,
"preview": "\"\"\"\r\nshapefile.py\r\nProvides read and write support for ESRI Shapefiles.\r\nauthor: jlawhead<at>geospatialpython.com\r\nversi"
},
{
"path": "core/lib/shapefile123.py",
"chars": 46005,
"preview": "\"\"\"\nshapefile.py\nProvides read and write support for ESRI Shapefiles.\nauthor: jlawhead<at>geospatialpython.com\ndate: 201"
},
{
"path": "core/maths/__init__.py",
"chars": 154,
"preview": "from .interpo import scale, linearInterpo\n'''\nfrom .maths.kmeans1D import kmeans1d, getBreaks\nfrom . import akima\nfrom f"
},
{
"path": "core/maths/akima.py",
"chars": 6341,
"preview": "# -*- coding: utf-8 -*-\r\n# akima.py\r\n\r\n# Copyright (c) 2007-2015, Christoph Gohlke\r\n# Copyright (c) 2007-2015, The Regen"
},
{
"path": "core/maths/fillnodata.py",
"chars": 7134,
"preview": "# -*- coding:utf-8 -*-\r\n\r\n# This file is part of BlenderGIS\r\n\r\n# ***** GPL LICENSE BLOCK *****\r\n#\r\n# This program is f"
},
{
"path": "core/maths/interpo.py",
"chars": 430,
"preview": "\n\n#Scale/normalize function : linear stretch from lowest value to highest value\n########################################"
},
{
"path": "core/maths/kmeans1D.py",
"chars": 6816,
"preview": "\"\"\"\nkmeans1D.py\nAuthor : domlysz@gmail.com\nDate : february 2016\nLicense : GPL\n\nThis file is part of BlenderGIS.\nThis is "
},
{
"path": "core/proj/__init__.py",
"chars": 197,
"preview": "from .srs import SRS\nfrom .reproj import Reproj, reprojPt, reprojPts, reprojBbox, reprojImg\nfrom .srv import EPSGIO, TWC"
},
{
"path": "core/proj/ellps.py",
"chars": 565,
"preview": "import math\n\n\nclass Ellps():\n\t\"\"\"ellipsoid\"\"\"\n\tdef __init__(self, a, b):\n\t\tself.a = a#equatorial radius in meters\n\t\tsel"
},
{
"path": "core/proj/reproj.py",
"chars": 11452,
"preview": "# -*- coding:utf-8 -*-\n\n# ***** GPL LICENSE BLOCK *****\n#\n# This program is free software: you can redistribute it and"
},
{
"path": "core/proj/srs.py",
"chars": 5093,
"preview": "# -*- coding:utf-8 -*-\n\n# ***** GPL LICENSE BLOCK *****\n#\n# This program is free software: you can redistribute it and"
},
{
"path": "core/proj/srv.py",
"chars": 5364,
"preview": "# -*- coding:utf-8 -*-\n\n# ***** GPL LICENSE BLOCK *****\n#\n# This program is free software: you can redistribute it and"
},
{
"path": "core/proj/utm.py",
"chars": 5844,
"preview": "\n\n#Original code from https://github.com/Turbo87/utm\n#>simplified version that only handle utm zones (and not latitude b"
},
{
"path": "core/settings.json",
"chars": 145,
"preview": "{\n\t\"proj_engine\": \"AUTO\",\n\t\"img_engine\": \"AUTO\",\n\t\"user_agent\": \"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:45.0) Gecko/"
},
{
"path": "core/settings.py",
"chars": 1396,
"preview": "# -*- coding:utf-8 -*-\nimport os\nimport json\n\nfrom .checkdeps import HAS_GDAL, HAS_PYPROJ, HAS_IMGIO, HAS_PIL\n\ndef getAv"
},
{
"path": "core/utils/__init__.py",
"chars": 117,
"preview": "from .xy import XY\nfrom .bbox import BBOX\nfrom .gradient import Color, Stop, Gradient\nfrom .timing import perf_clock\n"
},
{
"path": "core/utils/bbox.py",
"chars": 7588,
"preview": "# -*- coding:utf-8 -*-\n\n# This file is part of BlenderGIS\n\n# ***** GPL LICENSE BLOCK *****\n#\n# This program is free so"
},
{
"path": "core/utils/gradient.py",
"chars": 14655,
"preview": "# -*- coding:utf-8 -*-\nimport logging\nlog = logging.getLogger(__name__)\nimport os\nimport colorsys\nfrom xml.dom.minidom i"
},
{
"path": "core/utils/timing.py",
"chars": 259,
"preview": "import time\n\ndef perf_clock():\n if hasattr(time, 'clock'):\n return time.clock()\n elif hasattr(time, 'perf_c"
},
{
"path": "core/utils/xy.py",
"chars": 1882,
"preview": "# -*- coding:utf-8 -*-\n\n# This file is part of BlenderGIS\n\n# ***** GPL LICENSE BLOCK *****\n#\n# This program is free so"
},
{
"path": "geoscene.py",
"chars": 24455,
"preview": "# -*- coding:utf-8 -*-\n\n# ***** GPL LICENSE BLOCK *****\n#\n# This program is free software: you can redistribute it and"
},
{
"path": "issue_template.md",
"chars": 771,
"preview": "<!--\nPlease respect the issue template describe here :\nhttps://github.com/domlysz/BlenderGIS/issues/289\n\nIssues that doe"
},
{
"path": "operators/__init__.py",
"chars": 309,
"preview": "__all__ = [\"add_camera_exif\", \"add_camera_georef\", \"io_export_shp\", \"io_get_srtm\", \"io_import_georaster\", \"io_import_osm"
},
{
"path": "operators/add_camera_exif.py",
"chars": 10537,
"preview": "# -*- coding:utf-8 -*-\n\n# ***** GPL LICENSE BLOCK *****\n#\n# This program is free software: you can redistribute it and"
},
{
"path": "operators/add_camera_georef.py",
"chars": 6430,
"preview": "# -*- coding:utf-8 -*-\n\n# ***** GPL LICENSE BLOCK *****\n#\n# This program is free software: you can redistribute it and"
},
{
"path": "operators/io_export_shp.py",
"chars": 8129,
"preview": "# -*- coding:utf-8 -*-\nimport os\nimport bpy\nimport bmesh\nimport mathutils\n\nimport logging\nlog = logging.getLogger(__name"
},
{
"path": "operators/io_get_dem.py",
"chars": 5855,
"preview": "import os\nimport time\n\nimport logging\nlog = logging.getLogger(__name__)\n\nfrom urllib.request import Request, urlopen\nfro"
},
{
"path": "operators/io_import_asc.py",
"chars": 12022,
"preview": "# Derived from https://github.com/hrbaer/Blender-ASCII-Grid-Import\n\nimport re\nimport os\nimport string\nimport bpy\nimport "
},
{
"path": "operators/io_import_georaster.py",
"chars": 16689,
"preview": "# -*- coding:utf-8 -*-\n\n# This file is part of BlenderGIS\n\n# ***** GPL LICENSE BLOCK *****\n#\n# This program is free so"
},
{
"path": "operators/io_import_osm.py",
"chars": 19359,
"preview": "import os\nimport time\nimport json\nimport random\n\nimport logging\nlog = logging.getLogger(__name__)\n\nimport bpy\nimport bme"
},
{
"path": "operators/io_import_shp.py",
"chars": 23466,
"preview": "# -*- coding:utf-8 -*-\nimport os, sys, time\nimport bpy\nfrom bpy.props import StringProperty, BoolProperty, EnumProperty,"
},
{
"path": "operators/lib/osm/nominatim.py",
"chars": 823,
"preview": "import os, ssl\n\nimport logging\nlog = logging.getLogger(__name__)\n\nimport json\n\nfrom urllib.request import urlopen\nfrom u"
},
{
"path": "operators/lib/osm/overpy/__about__.py",
"chars": 455,
"preview": "__all__ = [\n \"__author__\",\n \"__copyright__\",\n \"__email__\",\n \"__license__\",\n \"__summary__\",\n \"__title__"
},
{
"path": "operators/lib/osm/overpy/__init__.py",
"chars": 32773,
"preview": "from collections import OrderedDict\nfrom decimal import Decimal\nimport re\nimport sys\nimport os\n\nfrom . import exception\n"
},
{
"path": "operators/lib/osm/overpy/exception.py",
"chars": 3031,
"preview": "class OverPyException(BaseException):\n \"\"\"OverPy base exception\"\"\"\n pass\n\n\nclass DataIncomplete(OverPyException):\n"
},
{
"path": "operators/lib/osm/overpy/helper.py",
"chars": 1724,
"preview": "__author__ = 'mjob'\n\nimport overpy\n\n\ndef get_street(street, areacode, api=None):\n \"\"\"\n Retrieve streets in a given"
},
{
"path": "operators/mesh_delaunay_voronoi.py",
"chars": 8763,
"preview": "# -*- coding:utf-8 -*-\n#import DelaunayVoronoi\nimport bpy\nimport time\nfrom .utils import computeVoronoiDiagram, computeD"
},
{
"path": "operators/mesh_earth_sphere.py",
"chars": 2784,
"preview": "import bpy\nfrom bpy.types import Operator\nfrom bpy.props import IntProperty\n\nfrom math import cos, sin, radians, sqrt\nfr"
},
{
"path": "operators/nodes_terrain_analysis_builder.py",
"chars": 15209,
"preview": "# -*- coding:utf-8 -*-\nimport math\nimport bpy\nfrom bpy.types import Panel, Operator\n\nimport logging\nlog = logging.getLog"
},
{
"path": "operators/nodes_terrain_analysis_reclassify.py",
"chars": 30360,
"preview": "# -*- coding:utf-8 -*-\n\nimport os\nimport math\n\nimport bpy\nfrom mathutils import Vector\n\nfrom bpy.props import StringProp"
},
{
"path": "operators/object_drop.py",
"chars": 5861,
"preview": "# ##### BEGIN GPL LICENSE BLOCK #####\n#\n# This program is free software; you can redistribute it and/or\n# modify it un"
},
{
"path": "operators/utils/__init__.py",
"chars": 304,
"preview": "from .bgis_utils import placeObj, adjust3Dview, showTextures, addTexture, getBBOX, DropToGround, mouseTo3d, isTopView\nfr"
},
{
"path": "operators/utils/bgis_utils.py",
"chars": 7102,
"preview": "\nimport bpy\nfrom mathutils import Vector, Matrix\nfrom mathutils.bvhtree import BVHTree\nfrom bpy_extras.view3d_utils impo"
},
{
"path": "operators/utils/delaunay_voronoi.py",
"chars": 30560,
"preview": "# -*- coding: utf-8 -*-\n\n#############################################################################\n#\n# Voronoi diagr"
},
{
"path": "operators/utils/georaster_utils.py",
"chars": 13223,
"preview": "# -*- coding:utf-8 -*-\n\n# This file is part of BlenderGIS\n\n# ***** GPL LICENSE BLOCK *****\n#\n# This program is free so"
},
{
"path": "operators/view3d_mapviewer.py",
"chars": 32850,
"preview": "# -*- coding:utf-8 -*-\n\n# ***** GPL LICENSE BLOCK *****\n#\n# This program is free software: you can redistribute it and"
},
{
"path": "prefs.py",
"chars": 29724,
"preview": "import json\nimport logging\nlog = logging.getLogger(__name__)\nimport sys, os\n\nimport bpy\nfrom bpy.props import StringProp"
}
]
About this extraction
This page contains the full source code of the domlysz/BlenderGIS GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 90 files (1.2 MB), approximately 370.8k tokens, and a symbol index with 1406 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.