├── .gitignore
├── .travis.yml
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── Dockerfile
├── LICENSE.md
├── Makefile
├── README.md
├── __init__.py
├── algorithm.py
├── help
├── Makefile
├── make.bat
└── source
│ ├── conf.py
│ └── index.rst
├── i18n
├── es.qm
└── es.ts
├── massive_change_detection.py
├── metadata.txt
├── pb_tool.cfg
├── plugin_upload.py
├── provider.py
├── pylintrc
├── requirements.txt
├── scripts
├── compile-strings.sh
├── run-env-linux.sh
└── update-strings.sh
└── test
├── __init__.py
├── data
├── after.tif
├── before.tif
├── lots.cpg
├── lots.dbf
├── lots.prj
├── lots.qpj
├── lots.shp
└── lots.shx
├── qgis_interface.py
├── tenbytenraster.asc
├── tenbytenraster.asc.aux.xml
├── tenbytenraster.keywords
├── tenbytenraster.lic
├── tenbytenraster.prj
├── tenbytenraster.qml
├── test_generate_vector_algorithm.py
├── test_init.py
├── test_multiband_difference_algorithm.py
├── test_qgis_environment.py
├── test_translations.py
└── utilities.py
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | wheels/
23 | *.egg-info/
24 | .installed.cfg
25 | *.egg
26 | MANIFEST
27 |
28 | # PyInstaller
29 | # Usually these files are written by a python script from a template
30 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
31 | *.manifest
32 | *.spec
33 |
34 | # Installer logs
35 | pip-log.txt
36 | pip-delete-this-directory.txt
37 |
38 | # Unit test / coverage reports
39 | htmlcov/
40 | .tox/
41 | .coverage
42 | .coverage.*
43 | .cache
44 | nosetests.xml
45 | coverage.xml
46 | *.cover
47 | .hypothesis/
48 | .pytest_cache/
49 | .noseids
50 |
51 | # Translations
52 | *.mo
53 | *.pot
54 |
55 | # Django stuff:
56 | *.log
57 | local_settings.py
58 | db.sqlite3
59 |
60 | # Flask stuff:
61 | instance/
62 | .webassets-cache
63 |
64 | # Scrapy stuff:
65 | .scrapy
66 |
67 | # Sphinx documentation
68 | docs/_build/
69 |
70 | # PyBuilder
71 | target/
72 |
73 | # Jupyter Notebook
74 | .ipynb_checkpoints
75 |
76 | # pyenv
77 | .python-version
78 |
79 | # celery beat schedule file
80 | celerybeat-schedule
81 |
82 | # SageMath parsed files
83 | *.sage.py
84 |
85 | # Environments
86 | .env
87 | .venv
88 | env/
89 | venv/
90 | ENV/
91 | env.bak/
92 | venv.bak/
93 |
94 | # Spyder project settings
95 | .spyderproject
96 | .spyproject
97 |
98 | # Rope project settings
99 | .ropeproject
100 |
101 | # mkdocs documentation
102 | /site
103 |
104 | # mypy
105 | .mypy_cache/
106 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | services:
2 | - docker
3 | before_install:
4 | - make docker_build
5 | install:
6 | - BUILD_DIR=${TRAVIS_BUILD_DIR} make docker_prepare
7 | script:
8 | - make docker_run
9 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ## Version 0.1
4 |
5 | - First version with multiband image difference
6 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as
6 | contributors and maintainers pledge to making participation in our project and
7 | our community a harassment-free experience for everyone, regardless of age, body
8 | size, disability, ethnicity, gender identity and expression, level of experience,
9 | education, socio-economic status, nationality, personal appearance, race,
10 | religion, or sexual identity and orientation.
11 |
12 | ## Our Standards
13 |
14 | Examples of behavior that contributes to creating a positive environment
15 | include:
16 |
17 | * Using welcoming and inclusive language
18 | * Being respectful of differing viewpoints and experiences
19 | * Gracefully accepting constructive criticism
20 | * Focusing on what is best for the community
21 | * Showing empathy towards other community members
22 |
23 | Examples of unacceptable behavior by participants include:
24 |
25 | * The use of sexualized language or imagery and unwelcome sexual attention or
26 | advances
27 | * Trolling, insulting/derogatory comments, and personal or political attacks
28 | * Public or private harassment
29 | * Publishing others' private information, such as a physical or electronic
30 | address, without explicit permission
31 | * Other conduct which could reasonably be considered inappropriate in a
32 | professional setting
33 |
34 | ## Our Responsibilities
35 |
36 | Project maintainers are responsible for clarifying the standards of acceptable
37 | behavior and are expected to take appropriate and fair corrective action in
38 | response to any instances of unacceptable behavior.
39 |
40 | Project maintainers have the right and responsibility to remove, edit, or
41 | reject comments, commits, code, wiki edits, issues, and other contributions
42 | that are not aligned to this Code of Conduct, or to ban temporarily or
43 | permanently any contributor for other behaviors that they deem inappropriate,
44 | threatening, offensive, or harmful.
45 |
46 | ## Scope
47 |
48 | This Code of Conduct applies both within project spaces and in public spaces
49 | when an individual is representing the project or its community. Examples of
50 | representing a project or community include using an official project e-mail
51 | address, posting via an official social media account, or acting as an appointed
52 | representative at an online or offline event. Representation of a project may be
53 | further defined and clarified by project maintainers.
54 |
55 | ## Enforcement
56 |
57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
58 | reported by contacting the project team at
59 | [contact@dymaxionlabs.com](mailto:contact@dymaxionlabs.com). All complaints
60 | will be reviewed and investigated and will result in a response that is deemed
61 | necessary and appropriate to the circumstances. The project team is obligated
62 | to maintain confidentiality with regard to the reporter of an incident.
63 | Further details of specific enforcement policies may be posted separately.
64 |
65 | Project maintainers who do not follow or enforce the Code of Conduct in good
66 | faith may face temporary or permanent repercussions as determined by other
67 | members of the project's leadership.
68 |
69 | ## Attribution
70 |
71 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
72 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
73 |
74 | [homepage]: https://www.contributor-covenant.org
75 |
76 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to this project
2 |
3 | Please take a moment to review this document in order to make the contribution
4 | process easy and effective for everyone involved.
5 |
6 | Following these guidelines helps to communicate that you respect the time of
7 | the developers managing and developing this open source project. In return,
8 | they should reciprocate that respect in addressing your issue or assessing
9 | patches and features.
10 |
11 |
12 | ## Using the issue tracker
13 |
14 | The issue tracker is the preferred channel for [bug reports](#bugs),
15 | [features requests](#features) and [submitting pull
16 | requests](#pull-requests), but please respect the following restrictions:
17 |
18 | * Please **do not** use the issue tracker for personal support requests (use
19 | [Stack Overflow](http://stackoverflow.com), our
20 | [Gitter channel](https://gitter.im/dymaxionlabs/massive-change-detection), or contact us
21 | directly to [contact@dymaxionlabs.com](mailto:contact@dymaxionlabs.com)).
22 |
23 | * Please **do not** derail or troll issues. Keep the discussion on topic and
24 | respect the opinions of others.
25 |
26 |
27 |
28 | ## Bug reports
29 |
30 | A bug is a _demonstrable problem_ that is caused by the code in the repository.
31 | Good bug reports are extremely helpful - thank you!
32 |
33 | Guidelines for bug reports:
34 |
35 | 1. **Use the GitHub issue search** — check if the issue has already been
36 | reported.
37 |
38 | 2. **Check if the issue has been fixed** — try to reproduce it using the
39 | latest `master` or development branch in the repository.
40 |
41 | 3. **Isolate the problem** — create a [reduced test
42 | case](http://css-tricks.com/reduced-test-cases/) and a live example.
43 |
44 | A good bug report shouldn't leave others needing to chase you up for more
45 | information. Please try to be as detailed as possible in your report. What is
46 | your environment? What steps will reproduce the issue? What browser(s) and OS
47 | experience the problem? What would you expect to be the outcome? All these
48 | details will help people to fix any potential bugs.
49 |
50 | Example:
51 |
52 | > Short and descriptive example bug report title
53 | >
54 | > A summary of the issue and the browser/OS environment in which it occurs. If
55 | > suitable, include the steps required to reproduce the bug.
56 | >
57 | > 1. This is the first step
58 | > 2. This is the second step
59 | > 3. Further steps, etc.
60 | >
61 | > `` - a link to the reduced test case
62 | >
63 | > Any other information you want to share that is relevant to the issue being
64 | > reported. This might include the lines of code that you have identified as
65 | > causing the bug, and potential solutions (and your opinions on their
66 | > merits).
67 |
68 |
69 |
70 | ## Feature requests
71 |
72 | Feature requests are welcome. But take a moment to find out whether your idea
73 | fits with the scope and aims of the project. It's up to *you* to make a strong
74 | case to convince the project's developers of the merits of this feature. Please
75 | provide as much detail and context as possible.
76 |
77 |
78 |
79 | ## Pull requests
80 |
81 | Good pull requests - patches, improvements, new features - are a fantastic
82 | help. They should remain focused in scope and avoid containing unrelated
83 | commits.
84 |
85 | **Please ask first** before embarking on any significant pull request (e.g.
86 | implementing features, refactoring code, porting to a different language),
87 | otherwise you risk spending a lot of time working on something that the
88 | project's developers might not want to merge into the project.
89 |
90 | Please adhere to the coding conventions used throughout a project (indentation,
91 | accurate comments, etc.) and any other requirements (such as test coverage).
92 |
93 | Follow this process if you'd like your work considered for inclusion in the
94 | project:
95 |
96 | 1. [Fork](http://help.github.com/fork-a-repo/) the project, clone your fork,
97 | and configure the remotes:
98 |
99 | ```bash
100 | # Clone your fork of the repo into the current directory
101 | git clone https://github.com//massive-change-detection
102 | # Navigate to the newly cloned directory
103 | cd massive-change-detection
104 | # Assign the original repo to a remote called "upstream"
105 | git remote add upstream https://github.com/dymaxionlabs/massive-change-detection
106 | ```
107 |
108 | 2. If you cloned a while ago, get the latest changes from upstream:
109 |
110 | ```bash
111 | git checkout
112 | git pull upstream
113 | ```
114 |
115 | 3. Create a new topic branch (off the main project development branch) to
116 | contain your feature, change, or fix:
117 |
118 | ```bash
119 | git checkout -b
120 | ```
121 |
122 | 4. Commit your changes in logical chunks. Please adhere to these [git commit
123 | message guidelines](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html)
124 | or your code is unlikely be merged into the main project. Use Git's
125 | [interactive rebase](https://help.github.com/articles/interactive-rebase)
126 | feature to tidy up your commits before making them public.
127 |
128 | 5. Locally merge (or rebase) the upstream development branch into your topic branch:
129 |
130 | ```bash
131 | git pull [--rebase] upstream
132 | ```
133 |
134 | 6. Push your topic branch up to your fork:
135 |
136 | ```bash
137 | git push origin
138 | ```
139 |
140 | 7. [Open a Pull Request](https://help.github.com/articles/using-pull-requests/)
141 | with a clear title and description.
142 |
143 | **IMPORTANT**: By submitting a patch, you agree to allow the project owner to
144 | license your work under the same license as that used by the project.
145 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM dymaxionlabs/qgis-testing-environment:qgis-2.18
2 | MAINTAINER Damián Silvani
3 |
4 | ADD requirements.txt /tmp/
5 | RUN LC_ALL=C DEBIAN_FRONTEND=noninteractive \
6 | pip install -r /tmp/requirements.txt
7 |
8 | ENV PYTHONPATH /usr/share/qgis/python/plugins:$PYTHONPATH
9 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 | Preamble
9 |
10 | The GNU General Public License is a free, copyleft license for
11 | software and other kinds of works.
12 |
13 | The licenses for most software and other practical works are designed
14 | to take away your freedom to share and change the works. By contrast,
15 | the GNU General Public License is intended to guarantee your freedom to
16 | share and change all versions of a program--to make sure it remains free
17 | software for all its users. We, the Free Software Foundation, use the
18 | GNU General Public License for most of our software; it applies also to
19 | any other work released this way by its authors. You can apply it to
20 | your programs, too.
21 |
22 | When we speak of free software, we are referring to freedom, not
23 | price. Our General Public Licenses are designed to make sure that you
24 | have the freedom to distribute copies of free software (and charge for
25 | them if you wish), that you receive source code or can get it if you
26 | want it, that you can change the software or use pieces of it in new
27 | free programs, and that you know you can do these things.
28 |
29 | To protect your rights, we need to prevent others from denying you
30 | these rights or asking you to surrender the rights. Therefore, you have
31 | certain responsibilities if you distribute copies of the software, or if
32 | you modify it: responsibilities to respect the freedom of others.
33 |
34 | For example, if you distribute copies of such a program, whether
35 | gratis or for a fee, you must pass on to the recipients the same
36 | freedoms that you received. You must make sure that they, too, receive
37 | or can get the source code. And you must show them these terms so they
38 | know their rights.
39 |
40 | Developers that use the GNU GPL protect your rights with two steps:
41 | (1) assert copyright on the software, and (2) offer you this License
42 | giving you legal permission to copy, distribute and/or modify it.
43 |
44 | For the developers' and authors' protection, the GPL clearly explains
45 | that there is no warranty for this free software. For both users' and
46 | authors' sake, the GPL requires that modified versions be marked as
47 | changed, so that their problems will not be attributed erroneously to
48 | authors of previous versions.
49 |
50 | Some devices are designed to deny users access to install or run
51 | modified versions of the software inside them, although the manufacturer
52 | can do so. This is fundamentally incompatible with the aim of
53 | protecting users' freedom to change the software. The systematic
54 | pattern of such abuse occurs in the area of products for individuals to
55 | use, which is precisely where it is most unacceptable. Therefore, we
56 | have designed this version of the GPL to prohibit the practice for those
57 | products. If such problems arise substantially in other domains, we
58 | stand ready to extend this provision to those domains in future versions
59 | of the GPL, as needed to protect the freedom of users.
60 |
61 | Finally, every program is threatened constantly by software patents.
62 | States should not allow patents to restrict development and use of
63 | software on general-purpose computers, but in those that do, we wish to
64 | avoid the special danger that patents applied to a free program could
65 | make it effectively proprietary. To prevent this, the GPL assures that
66 | patents cannot be used to render the program non-free.
67 |
68 | The precise terms and conditions for copying, distribution and
69 | modification follow.
70 |
71 | TERMS AND CONDITIONS
72 |
73 | 0. Definitions.
74 |
75 | "This License" refers to version 3 of the GNU General Public License.
76 |
77 | "Copyright" also means copyright-like laws that apply to other kinds of
78 | works, such as semiconductor masks.
79 |
80 | "The Program" refers to any copyrightable work licensed under this
81 | License. Each licensee is addressed as "you". "Licensees" and
82 | "recipients" may be individuals or organizations.
83 |
84 | To "modify" a work means to copy from or adapt all or part of the work
85 | in a fashion requiring copyright permission, other than the making of an
86 | exact copy. The resulting work is called a "modified version" of the
87 | earlier work or a work "based on" the earlier work.
88 |
89 | A "covered work" means either the unmodified Program or a work based
90 | on the Program.
91 |
92 | To "propagate" a work means to do anything with it that, without
93 | permission, would make you directly or secondarily liable for
94 | infringement under applicable copyright law, except executing it on a
95 | computer or modifying a private copy. Propagation includes copying,
96 | distribution (with or without modification), making available to the
97 | public, and in some countries other activities as well.
98 |
99 | To "convey" a work means any kind of propagation that enables other
100 | parties to make or receive copies. Mere interaction with a user through
101 | a computer network, with no transfer of a copy, is not conveying.
102 |
103 | An interactive user interface displays "Appropriate Legal Notices"
104 | to the extent that it includes a convenient and prominently visible
105 | feature that (1) displays an appropriate copyright notice, and (2)
106 | tells the user that there is no warranty for the work (except to the
107 | extent that warranties are provided), that licensees may convey the
108 | work under this License, and how to view a copy of this License. If
109 | the interface presents a list of user commands or options, such as a
110 | menu, a prominent item in the list meets this criterion.
111 |
112 | 1. Source Code.
113 |
114 | The "source code" for a work means the preferred form of the work
115 | for making modifications to it. "Object code" means any non-source
116 | form of a work.
117 |
118 | A "Standard Interface" means an interface that either is an official
119 | standard defined by a recognized standards body, or, in the case of
120 | interfaces specified for a particular programming language, one that
121 | is widely used among developers working in that language.
122 |
123 | The "System Libraries" of an executable work include anything, other
124 | than the work as a whole, that (a) is included in the normal form of
125 | packaging a Major Component, but which is not part of that Major
126 | Component, and (b) serves only to enable use of the work with that
127 | Major Component, or to implement a Standard Interface for which an
128 | implementation is available to the public in source code form. A
129 | "Major Component", in this context, means a major essential component
130 | (kernel, window system, and so on) of the specific operating system
131 | (if any) on which the executable work runs, or a compiler used to
132 | produce the work, or an object code interpreter used to run it.
133 |
134 | The "Corresponding Source" for a work in object code form means all
135 | the source code needed to generate, install, and (for an executable
136 | work) run the object code and to modify the work, including scripts to
137 | control those activities. However, it does not include the work's
138 | System Libraries, or general-purpose tools or generally available free
139 | programs which are used unmodified in performing those activities but
140 | which are not part of the work. For example, Corresponding Source
141 | includes interface definition files associated with source files for
142 | the work, and the source code for shared libraries and dynamically
143 | linked subprograms that the work is specifically designed to require,
144 | such as by intimate data communication or control flow between those
145 | subprograms and other parts of the work.
146 |
147 | The Corresponding Source need not include anything that users
148 | can regenerate automatically from other parts of the Corresponding
149 | Source.
150 |
151 | The Corresponding Source for a work in source code form is that
152 | same work.
153 |
154 | 2. Basic Permissions.
155 |
156 | All rights granted under this License are granted for the term of
157 | copyright on the Program, and are irrevocable provided the stated
158 | conditions are met. This License explicitly affirms your unlimited
159 | permission to run the unmodified Program. The output from running a
160 | covered work is covered by this License only if the output, given its
161 | content, constitutes a covered work. This License acknowledges your
162 | rights of fair use or other equivalent, as provided by copyright law.
163 |
164 | You may make, run and propagate covered works that you do not
165 | convey, without conditions so long as your license otherwise remains
166 | in force. You may convey covered works to others for the sole purpose
167 | of having them make modifications exclusively for you, or provide you
168 | with facilities for running those works, provided that you comply with
169 | the terms of this License in conveying all material for which you do
170 | not control copyright. Those thus making or running the covered works
171 | for you must do so exclusively on your behalf, under your direction
172 | and control, on terms that prohibit them from making any copies of
173 | your copyrighted material outside their relationship with you.
174 |
175 | Conveying under any other circumstances is permitted solely under
176 | the conditions stated below. Sublicensing is not allowed; section 10
177 | makes it unnecessary.
178 |
179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
180 |
181 | No covered work shall be deemed part of an effective technological
182 | measure under any applicable law fulfilling obligations under article
183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or
184 | similar laws prohibiting or restricting circumvention of such
185 | measures.
186 |
187 | When you convey a covered work, you waive any legal power to forbid
188 | circumvention of technological measures to the extent such circumvention
189 | is effected by exercising rights under this License with respect to
190 | the covered work, and you disclaim any intention to limit operation or
191 | modification of the work as a means of enforcing, against the work's
192 | users, your or third parties' legal rights to forbid circumvention of
193 | technological measures.
194 |
195 | 4. Conveying Verbatim Copies.
196 |
197 | You may convey verbatim copies of the Program's source code as you
198 | receive it, in any medium, provided that you conspicuously and
199 | appropriately publish on each copy an appropriate copyright notice;
200 | keep intact all notices stating that this License and any
201 | non-permissive terms added in accord with section 7 apply to the code;
202 | keep intact all notices of the absence of any warranty; and give all
203 | recipients a copy of this License along with the Program.
204 |
205 | You may charge any price or no price for each copy that you convey,
206 | and you may offer support or warranty protection for a fee.
207 |
208 | 5. Conveying Modified Source Versions.
209 |
210 | You may convey a work based on the Program, or the modifications to
211 | produce it from the Program, in the form of source code under the
212 | terms of section 4, provided that you also meet all of these conditions:
213 |
214 | a) The work must carry prominent notices stating that you modified
215 | it, and giving a relevant date.
216 |
217 | b) The work must carry prominent notices stating that it is
218 | released under this License and any conditions added under section
219 | 7. This requirement modifies the requirement in section 4 to
220 | "keep intact all notices".
221 |
222 | c) You must license the entire work, as a whole, under this
223 | License to anyone who comes into possession of a copy. This
224 | License will therefore apply, along with any applicable section 7
225 | additional terms, to the whole of the work, and all its parts,
226 | regardless of how they are packaged. This License gives no
227 | permission to license the work in any other way, but it does not
228 | invalidate such permission if you have separately received it.
229 |
230 | d) If the work has interactive user interfaces, each must display
231 | Appropriate Legal Notices; however, if the Program has interactive
232 | interfaces that do not display Appropriate Legal Notices, your
233 | work need not make them do so.
234 |
235 | A compilation of a covered work with other separate and independent
236 | works, which are not by their nature extensions of the covered work,
237 | and which are not combined with it such as to form a larger program,
238 | in or on a volume of a storage or distribution medium, is called an
239 | "aggregate" if the compilation and its resulting copyright are not
240 | used to limit the access or legal rights of the compilation's users
241 | beyond what the individual works permit. Inclusion of a covered work
242 | in an aggregate does not cause this License to apply to the other
243 | parts of the aggregate.
244 |
245 | 6. Conveying Non-Source Forms.
246 |
247 | You may convey a covered work in object code form under the terms
248 | of sections 4 and 5, provided that you also convey the
249 | machine-readable Corresponding Source under the terms of this License,
250 | in one of these ways:
251 |
252 | a) Convey the object code in, or embodied in, a physical product
253 | (including a physical distribution medium), accompanied by the
254 | Corresponding Source fixed on a durable physical medium
255 | customarily used for software interchange.
256 |
257 | b) Convey the object code in, or embodied in, a physical product
258 | (including a physical distribution medium), accompanied by a
259 | written offer, valid for at least three years and valid for as
260 | long as you offer spare parts or customer support for that product
261 | model, to give anyone who possesses the object code either (1) a
262 | copy of the Corresponding Source for all the software in the
263 | product that is covered by this License, on a durable physical
264 | medium customarily used for software interchange, for a price no
265 | more than your reasonable cost of physically performing this
266 | conveying of source, or (2) access to copy the
267 | Corresponding Source from a network server at no charge.
268 |
269 | c) Convey individual copies of the object code with a copy of the
270 | written offer to provide the Corresponding Source. This
271 | alternative is allowed only occasionally and noncommercially, and
272 | only if you received the object code with such an offer, in accord
273 | with subsection 6b.
274 |
275 | d) Convey the object code by offering access from a designated
276 | place (gratis or for a charge), and offer equivalent access to the
277 | Corresponding Source in the same way through the same place at no
278 | further charge. You need not require recipients to copy the
279 | Corresponding Source along with the object code. If the place to
280 | copy the object code is a network server, the Corresponding Source
281 | may be on a different server (operated by you or a third party)
282 | that supports equivalent copying facilities, provided you maintain
283 | clear directions next to the object code saying where to find the
284 | Corresponding Source. Regardless of what server hosts the
285 | Corresponding Source, you remain obligated to ensure that it is
286 | available for as long as needed to satisfy these requirements.
287 |
288 | e) Convey the object code using peer-to-peer transmission, provided
289 | you inform other peers where the object code and Corresponding
290 | Source of the work are being offered to the general public at no
291 | charge under subsection 6d.
292 |
293 | A separable portion of the object code, whose source code is excluded
294 | from the Corresponding Source as a System Library, need not be
295 | included in conveying the object code work.
296 |
297 | A "User Product" is either (1) a "consumer product", which means any
298 | tangible personal property which is normally used for personal, family,
299 | or household purposes, or (2) anything designed or sold for incorporation
300 | into a dwelling. In determining whether a product is a consumer product,
301 | doubtful cases shall be resolved in favor of coverage. For a particular
302 | product received by a particular user, "normally used" refers to a
303 | typical or common use of that class of product, regardless of the status
304 | of the particular user or of the way in which the particular user
305 | actually uses, or expects or is expected to use, the product. A product
306 | is a consumer product regardless of whether the product has substantial
307 | commercial, industrial or non-consumer uses, unless such uses represent
308 | the only significant mode of use of the product.
309 |
310 | "Installation Information" for a User Product means any methods,
311 | procedures, authorization keys, or other information required to install
312 | and execute modified versions of a covered work in that User Product from
313 | a modified version of its Corresponding Source. The information must
314 | suffice to ensure that the continued functioning of the modified object
315 | code is in no case prevented or interfered with solely because
316 | modification has been made.
317 |
318 | If you convey an object code work under this section in, or with, or
319 | specifically for use in, a User Product, and the conveying occurs as
320 | part of a transaction in which the right of possession and use of the
321 | User Product is transferred to the recipient in perpetuity or for a
322 | fixed term (regardless of how the transaction is characterized), the
323 | Corresponding Source conveyed under this section must be accompanied
324 | by the Installation Information. But this requirement does not apply
325 | if neither you nor any third party retains the ability to install
326 | modified object code on the User Product (for example, the work has
327 | been installed in ROM).
328 |
329 | The requirement to provide Installation Information does not include a
330 | requirement to continue to provide support service, warranty, or updates
331 | for a work that has been modified or installed by the recipient, or for
332 | the User Product in which it has been modified or installed. Access to a
333 | network may be denied when the modification itself materially and
334 | adversely affects the operation of the network or violates the rules and
335 | protocols for communication across the network.
336 |
337 | Corresponding Source conveyed, and Installation Information provided,
338 | in accord with this section must be in a format that is publicly
339 | documented (and with an implementation available to the public in
340 | source code form), and must require no special password or key for
341 | unpacking, reading or copying.
342 |
343 | 7. Additional Terms.
344 |
345 | "Additional permissions" are terms that supplement the terms of this
346 | License by making exceptions from one or more of its conditions.
347 | Additional permissions that are applicable to the entire Program shall
348 | be treated as though they were included in this License, to the extent
349 | that they are valid under applicable law. If additional permissions
350 | apply only to part of the Program, that part may be used separately
351 | under those permissions, but the entire Program remains governed by
352 | this License without regard to the additional permissions.
353 |
354 | When you convey a copy of a covered work, you may at your option
355 | remove any additional permissions from that copy, or from any part of
356 | it. (Additional permissions may be written to require their own
357 | removal in certain cases when you modify the work.) You may place
358 | additional permissions on material, added by you to a covered work,
359 | for which you have or can give appropriate copyright permission.
360 |
361 | Notwithstanding any other provision of this License, for material you
362 | add to a covered work, you may (if authorized by the copyright holders of
363 | that material) supplement the terms of this License with terms:
364 |
365 | a) Disclaiming warranty or limiting liability differently from the
366 | terms of sections 15 and 16 of this License; or
367 |
368 | b) Requiring preservation of specified reasonable legal notices or
369 | author attributions in that material or in the Appropriate Legal
370 | Notices displayed by works containing it; or
371 |
372 | c) Prohibiting misrepresentation of the origin of that material, or
373 | requiring that modified versions of such material be marked in
374 | reasonable ways as different from the original version; or
375 |
376 | d) Limiting the use for publicity purposes of names of licensors or
377 | authors of the material; or
378 |
379 | e) Declining to grant rights under trademark law for use of some
380 | trade names, trademarks, or service marks; or
381 |
382 | f) Requiring indemnification of licensors and authors of that
383 | material by anyone who conveys the material (or modified versions of
384 | it) with contractual assumptions of liability to the recipient, for
385 | any liability that these contractual assumptions directly impose on
386 | those licensors and authors.
387 |
388 | All other non-permissive additional terms are considered "further
389 | restrictions" within the meaning of section 10. If the Program as you
390 | received it, or any part of it, contains a notice stating that it is
391 | governed by this License along with a term that is a further
392 | restriction, you may remove that term. If a license document contains
393 | a further restriction but permits relicensing or conveying under this
394 | License, you may add to a covered work material governed by the terms
395 | of that license document, provided that the further restriction does
396 | not survive such relicensing or conveying.
397 |
398 | If you add terms to a covered work in accord with this section, you
399 | must place, in the relevant source files, a statement of the
400 | additional terms that apply to those files, or a notice indicating
401 | where to find the applicable terms.
402 |
403 | Additional terms, permissive or non-permissive, may be stated in the
404 | form of a separately written license, or stated as exceptions;
405 | the above requirements apply either way.
406 |
407 | 8. Termination.
408 |
409 | You may not propagate or modify a covered work except as expressly
410 | provided under this License. Any attempt otherwise to propagate or
411 | modify it is void, and will automatically terminate your rights under
412 | this License (including any patent licenses granted under the third
413 | paragraph of section 11).
414 |
415 | However, if you cease all violation of this License, then your
416 | license from a particular copyright holder is reinstated (a)
417 | provisionally, unless and until the copyright holder explicitly and
418 | finally terminates your license, and (b) permanently, if the copyright
419 | holder fails to notify you of the violation by some reasonable means
420 | prior to 60 days after the cessation.
421 |
422 | Moreover, your license from a particular copyright holder is
423 | reinstated permanently if the copyright holder notifies you of the
424 | violation by some reasonable means, this is the first time you have
425 | received notice of violation of this License (for any work) from that
426 | copyright holder, and you cure the violation prior to 30 days after
427 | your receipt of the notice.
428 |
429 | Termination of your rights under this section does not terminate the
430 | licenses of parties who have received copies or rights from you under
431 | this License. If your rights have been terminated and not permanently
432 | reinstated, you do not qualify to receive new licenses for the same
433 | material under section 10.
434 |
435 | 9. Acceptance Not Required for Having Copies.
436 |
437 | You are not required to accept this License in order to receive or
438 | run a copy of the Program. Ancillary propagation of a covered work
439 | occurring solely as a consequence of using peer-to-peer transmission
440 | to receive a copy likewise does not require acceptance. However,
441 | nothing other than this License grants you permission to propagate or
442 | modify any covered work. These actions infringe copyright if you do
443 | not accept this License. Therefore, by modifying or propagating a
444 | covered work, you indicate your acceptance of this License to do so.
445 |
446 | 10. Automatic Licensing of Downstream Recipients.
447 |
448 | Each time you convey a covered work, the recipient automatically
449 | receives a license from the original licensors, to run, modify and
450 | propagate that work, subject to this License. You are not responsible
451 | for enforcing compliance by third parties with this License.
452 |
453 | An "entity transaction" is a transaction transferring control of an
454 | organization, or substantially all assets of one, or subdividing an
455 | organization, or merging organizations. If propagation of a covered
456 | work results from an entity transaction, each party to that
457 | transaction who receives a copy of the work also receives whatever
458 | licenses to the work the party's predecessor in interest had or could
459 | give under the previous paragraph, plus a right to possession of the
460 | Corresponding Source of the work from the predecessor in interest, if
461 | the predecessor has it or can get it with reasonable efforts.
462 |
463 | You may not impose any further restrictions on the exercise of the
464 | rights granted or affirmed under this License. For example, you may
465 | not impose a license fee, royalty, or other charge for exercise of
466 | rights granted under this License, and you may not initiate litigation
467 | (including a cross-claim or counterclaim in a lawsuit) alleging that
468 | any patent claim is infringed by making, using, selling, offering for
469 | sale, or importing the Program or any portion of it.
470 |
471 | 11. Patents.
472 |
473 | A "contributor" is a copyright holder who authorizes use under this
474 | License of the Program or a work on which the Program is based. The
475 | work thus licensed is called the contributor's "contributor version".
476 |
477 | A contributor's "essential patent claims" are all patent claims
478 | owned or controlled by the contributor, whether already acquired or
479 | hereafter acquired, that would be infringed by some manner, permitted
480 | by this License, of making, using, or selling its contributor version,
481 | but do not include claims that would be infringed only as a
482 | consequence of further modification of the contributor version. For
483 | purposes of this definition, "control" includes the right to grant
484 | patent sublicenses in a manner consistent with the requirements of
485 | this License.
486 |
487 | Each contributor grants you a non-exclusive, worldwide, royalty-free
488 | patent license under the contributor's essential patent claims, to
489 | make, use, sell, offer for sale, import and otherwise run, modify and
490 | propagate the contents of its contributor version.
491 |
492 | In the following three paragraphs, a "patent license" is any express
493 | agreement or commitment, however denominated, not to enforce a patent
494 | (such as an express permission to practice a patent or covenant not to
495 | sue for patent infringement). To "grant" such a patent license to a
496 | party means to make such an agreement or commitment not to enforce a
497 | patent against the party.
498 |
499 | If you convey a covered work, knowingly relying on a patent license,
500 | and the Corresponding Source of the work is not available for anyone
501 | to copy, free of charge and under the terms of this License, through a
502 | publicly available network server or other readily accessible means,
503 | then you must either (1) cause the Corresponding Source to be so
504 | available, or (2) arrange to deprive yourself of the benefit of the
505 | patent license for this particular work, or (3) arrange, in a manner
506 | consistent with the requirements of this License, to extend the patent
507 | license to downstream recipients. "Knowingly relying" means you have
508 | actual knowledge that, but for the patent license, your conveying the
509 | covered work in a country, or your recipient's use of the covered work
510 | in a country, would infringe one or more identifiable patents in that
511 | country that you have reason to believe are valid.
512 |
513 | If, pursuant to or in connection with a single transaction or
514 | arrangement, you convey, or propagate by procuring conveyance of, a
515 | covered work, and grant a patent license to some of the parties
516 | receiving the covered work authorizing them to use, propagate, modify
517 | or convey a specific copy of the covered work, then the patent license
518 | you grant is automatically extended to all recipients of the covered
519 | work and works based on it.
520 |
521 | A patent license is "discriminatory" if it does not include within
522 | the scope of its coverage, prohibits the exercise of, or is
523 | conditioned on the non-exercise of one or more of the rights that are
524 | specifically granted under this License. You may not convey a covered
525 | work if you are a party to an arrangement with a third party that is
526 | in the business of distributing software, under which you make payment
527 | to the third party based on the extent of your activity of conveying
528 | the work, and under which the third party grants, to any of the
529 | parties who would receive the covered work from you, a discriminatory
530 | patent license (a) in connection with copies of the covered work
531 | conveyed by you (or copies made from those copies), or (b) primarily
532 | for and in connection with specific products or compilations that
533 | contain the covered work, unless you entered into that arrangement,
534 | or that patent license was granted, prior to 28 March 2007.
535 |
536 | Nothing in this License shall be construed as excluding or limiting
537 | any implied license or other defenses to infringement that may
538 | otherwise be available to you under applicable patent law.
539 |
540 | 12. No Surrender of Others' Freedom.
541 |
542 | If conditions are imposed on you (whether by court order, agreement or
543 | otherwise) that contradict the conditions of this License, they do not
544 | excuse you from the conditions of this License. If you cannot convey a
545 | covered work so as to satisfy simultaneously your obligations under this
546 | License and any other pertinent obligations, then as a consequence you may
547 | not convey it at all. For example, if you agree to terms that obligate you
548 | to collect a royalty for further conveying from those to whom you convey
549 | the Program, the only way you could satisfy both those terms and this
550 | License would be to refrain entirely from conveying the Program.
551 |
552 | 13. Use with the GNU Affero General Public License.
553 |
554 | Notwithstanding any other provision of this License, you have
555 | permission to link or combine any covered work with a work licensed
556 | under version 3 of the GNU Affero General Public License into a single
557 | combined work, and to convey the resulting work. The terms of this
558 | License will continue to apply to the part which is the covered work,
559 | but the special requirements of the GNU Affero General Public License,
560 | section 13, concerning interaction through a network will apply to the
561 | combination as such.
562 |
563 | 14. Revised Versions of this License.
564 |
565 | The Free Software Foundation may publish revised and/or new versions of
566 | the GNU General Public License from time to time. Such new versions will
567 | be similar in spirit to the present version, but may differ in detail to
568 | address new problems or concerns.
569 |
570 | Each version is given a distinguishing version number. If the
571 | Program specifies that a certain numbered version of the GNU General
572 | Public License "or any later version" applies to it, you have the
573 | option of following the terms and conditions either of that numbered
574 | version or of any later version published by the Free Software
575 | Foundation. If the Program does not specify a version number of the
576 | GNU General Public License, you may choose any version ever published
577 | by the Free Software Foundation.
578 |
579 | If the Program specifies that a proxy can decide which future
580 | versions of the GNU General Public License can be used, that proxy's
581 | public statement of acceptance of a version permanently authorizes you
582 | to choose that version for the Program.
583 |
584 | Later license versions may give you additional or different
585 | permissions. However, no additional obligations are imposed on any
586 | author or copyright holder as a result of your choosing to follow a
587 | later version.
588 |
589 | 15. Disclaimer of Warranty.
590 |
591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
599 |
600 | 16. Limitation of Liability.
601 |
602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
610 | SUCH DAMAGES.
611 |
612 | 17. Interpretation of Sections 15 and 16.
613 |
614 | If the disclaimer of warranty and limitation of liability provided
615 | above cannot be given local legal effect according to their terms,
616 | reviewing courts shall apply local law that most closely approximates
617 | an absolute waiver of all civil liability in connection with the
618 | Program, unless a warranty or assumption of liability accompanies a
619 | copy of the Program in return for a fee.
620 |
621 | END OF TERMS AND CONDITIONS
622 |
623 | How to Apply These Terms to Your New Programs
624 |
625 | If you develop a new program, and you want it to be of the greatest
626 | possible use to the public, the best way to achieve this is to make it
627 | free software which everyone can redistribute and change under these terms.
628 |
629 | To do so, attach the following notices to the program. It is safest
630 | to attach them to the start of each source file to most effectively
631 | state the exclusion of warranty; and each file should have at least
632 | the "copyright" line and a pointer to where the full notice is found.
633 |
634 |
635 | Copyright (C)
636 |
637 | This program is free software: you can redistribute it and/or modify
638 | it under the terms of the GNU General Public License as published by
639 | the Free Software Foundation, either version 3 of the License, or
640 | (at your option) any later version.
641 |
642 | This program is distributed in the hope that it will be useful,
643 | but WITHOUT ANY WARRANTY; without even the implied warranty of
644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
645 | GNU General Public License for more details.
646 |
647 | You should have received a copy of the GNU General Public License
648 | along with this program. If not, see .
649 |
650 | Also add information on how to contact you by electronic and paper mail.
651 |
652 | If the program does terminal interaction, make it output a short
653 | notice like this when it starts in an interactive mode:
654 |
655 | Copyright (C)
656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
657 | This is free software, and you are welcome to redistribute it
658 | under certain conditions; type `show c' for details.
659 |
660 | The hypothetical commands `show w' and `show c' should show the appropriate
661 | parts of the General Public License. Of course, your program's commands
662 | might be different; for a GUI interface, you would use an "about box".
663 |
664 | You should also get your employer (if you work as a programmer) or school,
665 | if any, to sign a "copyright disclaimer" for the program, if necessary.
666 | For more information on this, and how to apply and follow the GNU GPL, see
667 | .
668 |
669 | The GNU General Public License does not permit incorporating your program
670 | into proprietary programs. If your program is a subroutine library, you
671 | may consider it more useful to permit linking proprietary applications with
672 | the library. If this is what you want to do, use the GNU Lesser General
673 | Public License instead of this License. But first, please read
674 | .
675 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | #/***************************************************************************
2 | # MassiveChangeDetection
3 | #
4 | # Change detection tool
5 | # -------------------
6 | # begin : 2018-06-26
7 | # git sha : $Format:%H$
8 | # copyright : (C) 2018 by Dymaxion Labs
9 | # email : contact@dymaxionlabs.com
10 | # ***************************************************************************/
11 | #
12 | #/***************************************************************************
13 | # * *
14 | # * This program is free software; you can redistribute it and/or modify *
15 | # * it under the terms of the GNU General Public License as published by *
16 | # * the Free Software Foundation; either version 2 of the License, or *
17 | # * (at your option) any later version. *
18 | # * *
19 | # ***************************************************************************/
20 |
21 | #################################################
22 | # Edit the following to match your sources lists
23 | #################################################
24 |
25 |
26 | .PHONY: docker_build docker_prepare docker_run test
27 |
28 | #Add iso code for any locales you want to support here (space separated)
29 | # default is no locales
30 | LOCALES = es
31 |
32 | # If locales are enabled, set the name of the lrelease binary on your system. If
33 | # you have trouble compiling the translations, you may have to specify the full path to
34 | # lrelease
35 | LRELEASE = lrelease
36 | #LRELEASE = lrelease-qt4
37 |
38 | # translation
39 | SOURCES = \
40 | __init__.py \
41 | massive_change_detection.py \
42 | provider.py \
43 | algorithm.py
44 |
45 | PLUGINNAME = massive-change-detection
46 |
47 | PY_FILES = \
48 | __init__.py \
49 | massive_change_detection.py \
50 | provider.py \
51 | algorithm.py
52 |
53 | EXTRAS = \
54 | CHANGELOG.md \
55 | LICENSE.md \
56 | metadata.txt \
57 | README.md
58 |
59 | EXTRA_DIRS =
60 |
61 | PEP8EXCLUDE=pydev,resources.py,conf.py,third_party,ui
62 |
63 |
64 | #################################################
65 | # Normally you would not need to edit below here
66 | #################################################
67 |
68 | VERSION ?= $(shell grep 'version=' metadata.txt | cut -d'=' -f2)
69 |
70 | BUILD_DIR ?= $(CURDIR)
71 |
72 | HELP = help/build/html
73 |
74 | PLUGIN_UPLOAD = $(c)/plugin_upload.py
75 |
76 | RESOURCE_SRC=$(shell grep '^ *@@g;s/.*>//g' | tr '\n' ' ')
77 |
78 | QGISDIR=.qgis2
79 |
80 | default: test
81 |
82 | %.py : %.qrc $(RESOURCES_SRC)
83 | pyrcc4 -o $*.py $<
84 |
85 | %.qm : %.ts
86 | $(LRELEASE) $<
87 |
88 | test:
89 | @echo
90 | @echo "----------------------"
91 | @echo "Regression Test Suite"
92 | @echo "----------------------"
93 |
94 | @# Preceding dash means that make will continue in case of errors
95 | @rm -f .coverage
96 | @-export PYTHONPATH=`pwd`:$(PYTHONPATH); \
97 | export QGIS_DEBUG=0; \
98 | export QGIS_LOG_FILE=/dev/null; \
99 | nosetests -v --with-id --with-coverage --cover-package=. \
100 | 3>&1 1>&2 2>&3 3>&- || true
101 | @echo "----------------------"
102 | @echo "If you get a 'no module named qgis.core error, try sourcing"
103 | @echo "the helper script we have provided first then run make test."
104 | @echo "e.g. source run-env-linux.sh ; make test"
105 | @echo "----------------------"
106 |
107 | deploy: doc transcompile
108 | @echo
109 | @echo "------------------------------------------"
110 | @echo "Deploying plugin to your .qgis2 directory."
111 | @echo "------------------------------------------"
112 | # The deploy target only works on unix like operating system where
113 | # the Python plugin directory is located at:
114 | # $HOME/$(QGISDIR)/python/plugins
115 | mkdir -p $(HOME)/$(QGISDIR)/python/plugins/$(PLUGINNAME)
116 | cp -vf $(PY_FILES) $(HOME)/$(QGISDIR)/python/plugins/$(PLUGINNAME)
117 | cp -vf $(EXTRAS) $(HOME)/$(QGISDIR)/python/plugins/$(PLUGINNAME)
118 | cp -vfr i18n $(HOME)/$(QGISDIR)/python/plugins/$(PLUGINNAME)
119 | cp -vfr $(HELP) $(HOME)/$(QGISDIR)/python/plugins/$(PLUGINNAME)/help
120 |
121 | # The dclean target removes compiled python files from plugin directory
122 | # also deletes any .git entry
123 | dclean:
124 | @echo
125 | @echo "-----------------------------------"
126 | @echo "Removing any compiled python files."
127 | @echo "-----------------------------------"
128 | find $(HOME)/$(QGISDIR)/python/plugins/$(PLUGINNAME) -iname "*.pyc" -delete
129 | find $(HOME)/$(QGISDIR)/python/plugins/$(PLUGINNAME) -iname ".git" -prune -exec rm -Rf {} \;
130 |
131 | derase:
132 | @echo
133 | @echo "-------------------------"
134 | @echo "Removing deployed plugin."
135 | @echo "-------------------------"
136 | rm -Rf $(HOME)/$(QGISDIR)/python/plugins/$(PLUGINNAME)
137 |
138 | zip: deploy dclean
139 | @echo
140 | @echo "---------------------------"
141 | @echo "Creating plugin zip bundle."
142 | @echo "---------------------------"
143 | # The zip target deploys the plugin and creates a zip file with the deployed
144 | # content. You can then upload the zip file on http://plugins.qgis.org
145 | mkdir -p dist/
146 | rm -f dist/$(PLUGINNAME)-$(VERSION).zip
147 | cd $(HOME)/$(QGISDIR)/python/plugins; zip -9r $(CURDIR)/dist/$(PLUGINNAME)-$(VERSION).zip $(PLUGINNAME)
148 |
149 | package:
150 | # Create a zip package of the plugin named $(PLUGINNAME).zip.
151 | # This requires use of git (your plugin development directory must be a
152 | # git repository).
153 | # To use, pass a valid commit or tag as follows:
154 | # make package VERSION=Version_0.3.2
155 | @echo
156 | @echo "------------------------------------"
157 | @echo "Exporting plugin to zip package. "
158 | @echo "------------------------------------"
159 | mkdir -p dist/
160 | rm -f dist/$(PLUGINNAME)-$(VERSION).zip
161 | git archive --prefix=$(PLUGINNAME)/ -o dist/$(PLUGINNAME)-$(VERSION).zip $(VERSION)
162 | echo "Created package: dist/$(PLUGINNAME)-$(VERSION).zip"
163 |
164 | transup:
165 | @echo
166 | @echo "------------------------------------------------"
167 | @echo "Updating translation files with any new strings."
168 | @echo "------------------------------------------------"
169 | @chmod +x scripts/update-strings.sh
170 | @scripts/update-strings.sh $(LOCALES)
171 |
172 | transcompile:
173 | @echo
174 | @echo "----------------------------------------"
175 | @echo "Compiled translation files to .qm files."
176 | @echo "----------------------------------------"
177 | @chmod +x scripts/compile-strings.sh
178 | @scripts/compile-strings.sh $(LRELEASE) $(LOCALES)
179 |
180 | transclean:
181 | @echo
182 | @echo "------------------------------------"
183 | @echo "Removing compiled translation files."
184 | @echo "------------------------------------"
185 | rm -f i18n/*.qm
186 |
187 | doc:
188 | @echo
189 | @echo "------------------------------------"
190 | @echo "Building documentation using sphinx."
191 | @echo "------------------------------------"
192 | cd help; make html
193 |
194 | pylint:
195 | @echo
196 | @echo "-----------------"
197 | @echo "Pylint violations"
198 | @echo "-----------------"
199 | @pylint --reports=n --rcfile=pylintrc . || true
200 | @echo
201 | @echo "----------------------"
202 | @echo "If you get a 'no module named qgis.core' error, try sourcing"
203 | @echo "the helper script we have provided first then run make pylint."
204 | @echo "e.g. source run-env-linux.sh ; make pylint"
205 | @echo "----------------------"
206 |
207 | # Run pep8 style checking
208 | #http://pypi.python.org/pypi/pep8
209 | pep8:
210 | @echo
211 | @echo "-----------"
212 | @echo "PEP8 issues"
213 | @echo "-----------"
214 | @pep8 --repeat --ignore=E203,E121,E122,E123,E124,E125,E126,E127,E128 --exclude $(PEP8EXCLUDE) . || true
215 | @echo "-----------"
216 | @echo "Ignored in PEP8 check:"
217 | @echo $(PEP8EXCLUDE)
218 |
219 | docker_build:
220 | @docker build -t dymaxionlabs/massive-change-detection-test-env .
221 |
222 | docker_prepare:
223 | docker run -d --name massive-change-detection-test-env -v $(BUILD_DIR):/tests_directory/$(PLUGINNAME) -e DISPLAY=:99 dymaxionlabs/massive-change-detection-test-env
224 | sleep 3
225 | docker exec -it massive-change-detection-test-env sh -c "qgis_setup.sh $(PLUGINNAME)"
226 |
227 | docker_run:
228 | docker exec -it massive-change-detection-test-env sh -c "cd /tests_directory/$(PLUGINNAME) && make test"
229 |
230 | clean:
231 | rm -rf dist/
232 |
233 | publish: zip
234 | ifndef QGIS_REPOSITORY_PATH
235 | $(error QGIS_REPOSITORY_PATH is undefined)
236 | endif
237 | cp dist/$(PLUGINNAME)-$(VERSION).zip $(QGIS_REPOSITORY_PATH)/$(PLUGINNAME)/
238 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Massive Change Detection - QGIS plugin
2 |
3 | [](https://travis-ci.org/dymaxionlabs/massive-change-detection)
4 | [](https://gitter.im/dymaxionlabs/massive-change-detection?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
5 |
6 | QGIS 2 plugin that applies change detection algorithms on satellite imagery for
7 | building reports for urban planning.
8 |
9 |
10 | ## Install
11 |
12 | ### Add plugins repository
13 |
14 | To install this plugin, you need to add our QGIS Repository first. Go to
15 | `Plugins -> Manage and Install Plugins`.
16 |
17 | On the Settings tab, enable `Show also experimental plugins`, and add a
18 | repository named `Dymaxion Labs` with the following URL:
19 |
20 | ```
21 | https://dymaxionlabs.github.io/qgis-repository/plugins.xml
22 | ```
23 |
24 | After that, press `Reload all repositories` button to load our plugins index
25 | into QGIS.
26 |
27 | ### Install external dependencies
28 |
29 | This plugin makes use of some additional Python dependencies. In a terminal,
30 | run the following:
31 |
32 | ```
33 | pip install --user fiona numpy opencv-python rasterio shapely
34 | ```
35 |
36 | ### Install plugin
37 |
38 | Finally, go to `Plugins -> Manage and Install Plugins`, and in `All` or `Not
39 | Installed` tabs, search for *Massive Change Detection*. Click `Install
40 | plugin`.
41 |
42 | ## Usage
43 |
44 | This is a *processing plugin*, you will have to enable the Processing Toolbox
45 | on QGIS to use it properly.
46 |
47 | ### Step 1: Generate change detection raster
48 |
49 | The first step is to generate a *change detection raster* based on two raster
50 | images. The algorithm detects changes between the *"old"/"before" image* and
51 | the *"new"/"after" image* and generates a new binary raster of changes.
52 |
53 | Both input rasters must be coregistered and be of the same size and resolution.
54 | If you have images of different size and/or resolution, you will need to
55 | manually superimpose and clip them first. You can use [Orfeo
56 | Toolbox](https://www.orfeo-toolbox.org/start/) to do this (see Superimpose).
57 |
58 | Suppose you have two raster layers loaded on QGIS: `before.tif` and
59 | `after.tif`. Select `Massive Change Detection -> Pixel-based algorithms ->
60 | Multiband Difference` from the Processing Toolbox.
61 |
62 | 
63 |
64 | Use `before.tif` layer on `Input old layer` and `after.tif` layer on `Input new
65 | layer`. You can use automatic thresholding (Otsu's method) or manually set a
66 | threshold by unchecking `Automatic thresholding` and choosing a proper value on
67 | `Threshold value`. You can adjust the post-processing filter and its kernel
68 | size for better results. Finally click `Run` to process the image.
69 |
70 | 
71 |
72 | ### Step 2: Build report of changes
73 |
74 | The second step is to build a changed lots report based on a vector layer of
75 | lots and a change detection raster. Lots vector layer must be in the same
76 | projection as rasters.
77 |
78 | Select `Massive Change Detection -> Report -> Generate changed lots data` from
79 | the Processing Toolbox.
80 |
81 | Use your change detection raster (already generated on Step 1) on `Input change
82 | detection layer`, select one of your input raster images (`before.tif`, or
83 | `after.tif`) on `Input image layer`. Then set your lots vector layer on `Input
84 | lots vector layer` field, and select the lot id field on `Lot id field`.
85 |
86 | 
87 |
88 | The selection threshold is related to the percentage of detected change in a
89 | particular lot. For example, 0.5 means that a lot that has changed more than
90 | 50% of its area is marked as *changed*.
91 |
92 | This algorithm will generate both a new vector layer of changed lots and a
93 | table (CSV) layer.
94 |
95 | 
96 |
97 | ## Development
98 |
99 | Clone the repository inside your QGIS plugin directory. On Ubuntu this would
100 | be `~/.qgis2/python/plugins/`:
101 |
102 | ```
103 | cd ~/.qgis2/python/plugins/
104 | git clone https://github.com/dymaxionlabs/massive-change-detection
105 | cd massive-change-detection
106 | ```
107 |
108 | Install packages for test and code coverage.
109 |
110 | ```
111 | pip install --user -r requirements.txt
112 | ```
113 |
114 | If you have installed QGIS from source use `scripts/run-env-linux.sh` to set up
115 | the environment variables so that Python can find QGIS inside your virtual
116 | environment. For example, if it is `/opt/qgis2`:
117 |
118 | ```
119 | source scripts/run-env-linux.sh /opt/qgis2
120 | ```
121 |
122 | Run tests and build code coverage reports with `make test`.
123 |
124 |
125 | ### I18n
126 |
127 | You can find the list of supported locales is in the `LOCALES` definition on
128 | `Makefile`. If you want to add a new language, you have to add it there first.
129 |
130 | Prepare translations strings with `make transup`. This command will search all
131 | translated strings in the repository and generate `.ts` files.
132 |
133 | Edit the translations files (files ending in `.ts`) in `i18n/`.
134 |
135 | Finally compile translation strings with `make transcompile`.
136 |
137 |
138 | ## Issue tracker
139 |
140 | Please report any bugs and enhancement ideas using the GitHub issue tracker:
141 |
142 | https://github.com/dymaxionlabs/massive-change-detection/issues
143 |
144 | Feel free to also ask questions on our [Gitter
145 | channel](https://gitter.im/dymaxionlabs/massive-change-detection), or by email.
146 |
147 |
148 | ## Help wanted
149 |
150 | Any help in testing, development, documentation and other tasks is highly
151 | appreciated and useful to the project.
152 |
153 | For more details, see the file [CONTRIBUTING.md](CONTRIBUTING.md).
154 |
155 |
156 | ## License
157 |
158 | Source code is released under a GNU GPL v3 license. Please refer to
159 | [LICENSE.md](LICENSE.md) for more information.
160 |
--------------------------------------------------------------------------------
/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | Massive Change Detection is a QGIS 2 plugin that applies change detection
4 | algorithms on satellite imagery for building reports for urban planning.
5 |
6 | .. note:: This program is free software; you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by
8 | the Free Software Foundation; either version 3 of the License, or
9 | (at your option) any later version.
10 |
11 | """
12 |
13 | # Copyright (C) 2018 Dymaxion Labs
14 | #
15 | # This program is free software: you can redistribute it and/or modify
16 | # it under the terms of the GNU General Public License as published by
17 | # the Free Software Foundation, either version 3 of the License, or
18 | # (at your option) any later version.
19 | #
20 | # This program is distributed in the hope that it will be useful,
21 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
22 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 | # GNU General Public License for more details.
24 | #
25 | # You should have received a copy of the GNU General Public License
26 | # along with this program. If not, see .
27 |
28 |
29 | __author__ = 'Damián Silvani'
30 | __date__ = '2018-06-26'
31 | __copyright__ = '(C) 2018 by Dymaxion Labs'
32 |
33 |
34 | # noinspection PyPep8Naming
35 | def classFactory(iface): # pylint: disable=invalid-name
36 | """Load MassiveChangeDetection class from file MassiveChangeDetection.
37 |
38 | :param iface: A QGIS interface instance.
39 | :type iface: QgisInterface
40 | """
41 | #
42 | from .massive_change_detection import MassiveChangeDetectionPlugin
43 | return MassiveChangeDetectionPlugin()
44 |
--------------------------------------------------------------------------------
/algorithm.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | Contains both change detection and report generation algorithms.
4 |
5 | .. note:: This program is free software; you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation; either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | """
11 |
12 | __author__ = 'Damián Silvani'
13 | __date__ = '2018-06-26'
14 | __copyright__ = '(C) 2018 by Dymaxion Labs'
15 |
16 | # This will get replaced with a git SHA1 when you do a git archive
17 |
18 | __revision__ = '$Format:%H$'
19 |
20 | from PyQt4.QtCore import QSettings, QCoreApplication, QTranslator
21 | from qgis.core import QgsVectorFileWriter, QgsMessageLog, QgsMapLayerRegistry
22 |
23 | from processing.core.GeoAlgorithm import GeoAlgorithm
24 | from processing.core.ProcessingLog import ProcessingLog
25 | from processing.core.GeoAlgorithmExecutionException import GeoAlgorithmExecutionException
26 | from processing.core.parameters import ParameterRaster, ParameterVector, ParameterBoolean, ParameterNumber, ParameterSelection, ParameterTableField
27 | from processing.core.outputs import OutputRaster, OutputVector, OutputTable
28 | from processing.tools import dataobjects, vector
29 |
30 | from osgeo import gdal
31 | from osgeo.gdalconst import *
32 | import numpy as np
33 | import cv2
34 | import rasterio
35 | import rasterio.mask
36 | import fiona
37 | import os
38 | from shapely.geometry import shape, box
39 |
40 |
41 | class Algorithm(GeoAlgorithm):
42 | def __init__(self):
43 | GeoAlgorithm.__init__(self)
44 | self.pluginDir = os.path.dirname(os.path.realpath(__file__))
45 | self._load_translations()
46 |
47 | def _load_translations(self):
48 | full_locale = QSettings().value("locale/userLocale")
49 | if not full_locale:
50 | return
51 | locale = full_locale[0:2]
52 | localePath = os.path.join(self.pluginDir, 'i18n', '{}.qm'.format(locale))
53 | if os.path.exists(localePath):
54 | translator = QTranslator()
55 | translator.load(localePath)
56 | QCoreApplication.installTranslator(translator)
57 |
58 |
59 | class MultibandDifferenceAlgorithm(Algorithm):
60 | """
61 | This algorithm applies the image difference algorithm over each band in
62 | the raster.
63 | """
64 |
65 | INPUT_A_LAYER = 'INPUT_A_LAYER'
66 | INPUT_B_LAYER = 'INPUT_B_LAYER'
67 | OUTPUT_RASTER_LAYER = 'OUTPUT_RASTER_LAYER'
68 |
69 | AUTO_THRESHOLD = 'AUTO_THRESHOLD'
70 | THRESHOLD = 'THRESHOLD'
71 | FILTER = 'FILTER'
72 | FILTER_TYPES = ['NONE', 'MEDIAN', 'GAUSSIAN']
73 | FILTER_KERNEL_SIZE = 'FILTER_KERNEL_SIZE'
74 |
75 | def defineCharacteristics(self):
76 | self.name = self.tr('Multiband difference')
77 | self.group = self.tr('Pixel-based algorithms')
78 |
79 | # Main parameters
80 | self.addParameter(ParameterRaster(self.INPUT_A_LAYER,
81 | self.tr('Input old layer'), [ParameterRaster], False))
82 | self.addParameter(ParameterRaster(self.INPUT_B_LAYER,
83 | self.tr('Input new layer'), [ParameterRaster], False))
84 |
85 | # Threshold parameters
86 | self.addParameter(ParameterBoolean(
87 | self.AUTO_THRESHOLD,
88 | self.tr('Use automatic thresholding'),
89 | True))
90 | self.addParameter(ParameterNumber(
91 | self.THRESHOLD,
92 | self.tr('Threshold value (if not automatic)'),
93 | 0.0, 1.0, 0.5))
94 |
95 | # Filter parameters
96 | self.addParameter(ParameterSelection(
97 | self.FILTER,
98 | self.tr('Filter type'),
99 | self.FILTER_TYPES, 1))
100 | self.addParameter(ParameterNumber(self.FILTER_KERNEL_SIZE,
101 | self.tr('Filter kernel size'),
102 | 2.0, None, 3.0))
103 |
104 | self.addOutput(OutputRaster(self.OUTPUT_RASTER_LAYER,
105 | self.tr('CD raster')))
106 |
107 | def processAlgorithm(self, progress):
108 | self.progress = progress
109 |
110 | # The first thing to do is retrieve the values of the parameters
111 | # entered by the user
112 | inputAFilename = self.getParameterValue(self.INPUT_A_LAYER)
113 | inputBFilename = self.getParameterValue(self.INPUT_B_LAYER)
114 | outputFilename = self.getOutputValue(self.OUTPUT_RASTER_LAYER)
115 |
116 | if inputAFilename == inputBFilename:
117 | raise GeoAlgorithmExecutionException(
118 | self.tr('You must use two different raster images for inputs A and B'))
119 |
120 | threshold = self.getParameterValue(self.THRESHOLD)
121 | autoThreshold = self.getParameterValue(self.AUTO_THRESHOLD)
122 | if autoThreshold:
123 | threshold = None
124 |
125 | filterType = self.FILTER_TYPES[self.getParameterValue(self.FILTER)]
126 | if filterType == 'NONE':
127 | filterType = None
128 | kernelSize = self.getParameterValue(self.FILTER_KERNEL_SIZE)
129 |
130 | # Open and assign the contents of the raster file to a dataset
131 | datasetA = gdal.Open(inputAFilename, GA_ReadOnly)
132 | datasetB = gdal.Open(inputBFilename, GA_ReadOnly)
133 |
134 | progress.setInfo(self.tr('Reading rasters into arrays'))
135 | arrayA = self._readIntoArray(datasetA)
136 | arrayB = self._readIntoArray(datasetB)
137 |
138 | # Calculate image difference on each band
139 | cds = []
140 | bandCount = arrayA.shape[0]
141 | for i in range(bandCount):
142 | progress.setInfo(self.tr('Calculate image difference on band {}').format(i+1))
143 | cd = self._detectChanges(arrayA[i], arrayB[i],
144 | threshold=threshold,
145 | filterType=filterType,
146 | kernelSize=kernelSize)
147 | cds.append(cd)
148 | cds = np.array(cds)
149 |
150 | # Generate new change detection raster based on results on each band
151 | out = (np.any(cds > 0, axis=0) * 255).astype(np.uint8)
152 |
153 | if not np.any(out):
154 | raise GeoAlgorithmExecutionException(
155 | self.tr('No changed detected. Try to use a lower threshold value or different images'))
156 |
157 | # Create output raster dataset
158 | driver = gdal.GetDriverByName('GTiff')
159 | outDataset = driver.Create(outputFilename,
160 | datasetA.RasterXSize,
161 | datasetA.RasterYSize,
162 | 1,
163 | gdal.GDT_Byte)
164 |
165 | # Write output band
166 | outband = outDataset.GetRasterBand(1)
167 | outband.WriteArray(out)
168 | outband.SetNoDataValue(0)
169 | outband.FlushCache()
170 |
171 | # Check if there is geotransformation or geoprojection
172 | # in the input raster and set them in the resulting dataset
173 | if datasetA.GetGeoTransform() != None:
174 | outDataset.SetGeoTransform(datasetA.GetGeoTransform())
175 |
176 | if datasetA.GetProjection() != None:
177 | outDataset.SetProjection(datasetA.GetProjection())
178 |
179 | # Clean resources
180 | datasetA = datasetB = None
181 | outDataset = None
182 |
183 | def _readIntoArray(self, dataset):
184 | """Return a numpy array from a GDAL dataset"""
185 | bands = []
186 | for i in xrange(dataset.RasterCount):
187 | band = dataset.GetRasterBand(i+1).ReadAsArray(0, 0,
188 | dataset.RasterXSize,
189 | dataset.RasterYSize)
190 | bands.append(band)
191 | return np.array(bands)
192 |
193 | def _normalize(self, img):
194 | vmin, vmax = img.min(), img.max()
195 | norm_img = (img - vmin) / (vmax - vmin)
196 | return norm_img
197 |
198 | def _difference(self, a, b):
199 | a = a.astype(np.int32)
200 | b = b.astype(np.int32)
201 | mean_a, mean_b = a.mean(), b.mean()
202 | std_a, std_b = a.std(), b.std()
203 |
204 | b_norm = ((std_a / std_b) * (b - np.ones(b.shape) * mean_b)) + mean_a
205 | return np.abs(a - b_norm)
206 |
207 | def _threshold(self, src, tau):
208 | return (((src > 0) * (src >= tau)) * 255).astype(np.uint8)
209 |
210 | def _otsuThreshold(self, src):
211 | src = (src * 255).astype(np.uint8)
212 | _, dst = cv2.threshold(src, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
213 | return dst
214 |
215 | def _medianFilter(self, src, kernel_size=3):
216 | if kernel_size % 2 == 0:
217 | raise GeoAlgorithmExecutionException(
218 | self.tr('Kernel size for median filter must be an odd number'))
219 | return cv2.medianBlur(src, kernel_size)
220 |
221 | def _gaussFilter(self, src, kernel_size=3):
222 | return cv2.GaussianBlur(src, (kernel_size, kernel_size), 1, 1)
223 |
224 | def _detectChanges(self, img1, img2, threshold=None, filterType=None, kernelSize=3):
225 | res = self._difference(img1, img2)
226 | res = self._normalize(res)
227 |
228 | if threshold:
229 | res = self._threshold(res, threshold)
230 | self.progress.setInfo(
231 | self.tr('Applied manual threshold of value {}'.format(threshold)))
232 | else:
233 | res = self._otsuThreshold(res)
234 | self.progress.setInfo('Applied Otsu threshold')
235 |
236 | if filterType == 'GAUSSIAN':
237 | res = self._gaussFilter(res, kernelSize)
238 | elif filterType == 'MEDIAN':
239 | res = self._medianFilter(res, kernelSize)
240 | else:
241 | raise GeoAlgorithmExecutionException(self.tr('Unhandled filter type: {}').format(filterType))
242 |
243 | if filterType:
244 | self.progress.setInfo('Applied {} filter with kernel size {}'.format(filterType, kernelSize))
245 |
246 | return res
247 |
248 |
249 | class GenerateVectorAlgorithm(Algorithm):
250 | INPUT_LOTS_LAYER = 'INPUT_LOTS_LAYER'
251 | INPUT_LOT_ID_FIELD = 'INPUT_LOT_ID_FIELD'
252 | INPUT_CD_LAYER = 'INPUT_CD_LAYER'
253 | INPUT_IMG_LAYER = 'INPUT_IMG_LAYER'
254 | OUTPUT_VECTOR_LAYER = 'OUTPUT_VECTOR_LAYER'
255 | OUTPUT_TABLE_LAYER = 'OUTPUT_TABLE_LAYER'
256 |
257 | SELECTION_THRESHOLD = 'SELECTION_THRESHOLD'
258 |
259 | def defineCharacteristics(self):
260 | self.name = self.tr('Generate changed lots data')
261 | self.group = self.tr('Report')
262 |
263 | self.addParameter(ParameterRaster(self.INPUT_CD_LAYER,
264 | self.tr('Input change detection layer'), [ParameterRaster], False))
265 | self.addParameter(ParameterRaster(self.INPUT_IMG_LAYER,
266 | self.tr('Input image layer'), [ParameterRaster], False))
267 | self.addParameter(ParameterVector(self.INPUT_LOTS_LAYER,
268 | self.tr('Input Lots vector layer'), [ParameterVector.VECTOR_TYPE_ANY], False))
269 | self.addParameter(ParameterTableField(self.INPUT_LOT_ID_FIELD,
270 | self.tr('Lot id field'), self.INPUT_LOTS_LAYER))
271 | self.addParameter(ParameterNumber(
272 | self.SELECTION_THRESHOLD,
273 | self.tr('Lot selection threshold value'),
274 | 0.0, 1.0, 0.5))
275 |
276 | self.addOutput(OutputVector(self.OUTPUT_VECTOR_LAYER,
277 | self.tr('CD vector')))
278 | self.addOutput(OutputTable(self.OUTPUT_TABLE_LAYER,
279 | self.tr('CD table')))
280 |
281 | def processAlgorithm(self, progress):
282 | cdFilename = self.getParameterValue(self.INPUT_CD_LAYER)
283 | imgFilename = self.getParameterValue(self.INPUT_IMG_LAYER)
284 | lotsFilename = self.getParameterValue(self.INPUT_LOTS_LAYER)
285 | lotIdFieldName = self.getParameterValue(self.INPUT_LOT_ID_FIELD)
286 | selectionThreshold = self.getParameterValue(self.SELECTION_THRESHOLD)
287 |
288 | outputTable = self.getOutputFromName(self.OUTPUT_TABLE_LAYER)
289 | columns = ['lot_id', 'change', 'area', 'changed_area', 'change_perc']
290 | writer = outputTable.getTableWriter(columns)
291 |
292 | outputVector = self.getOutputValue(self.OUTPUT_VECTOR_LAYER)
293 |
294 | with fiona.open(lotsFilename) as lotsDs, rasterio.open(cdFilename) as cdDs, rasterio.open(imgFilename) as imgDs:
295 | if lotsDs.crs != cdDs.crs:
296 | raise GeoAlgorithmExecutionException(self.tr('Lots vector file has different CRS than rasters: {} != {}').format(lotsDs.crs, cdDs.crs))
297 |
298 | total = 100.0 / len(lotsDs) if len(lotsDs) > 0 else 1
299 | progress.setInfo(self.tr('Processing lot features...'))
300 |
301 | invalidGeomCount = 0
302 |
303 | newSchema = lotsDs.schema.copy()
304 | newSchema['properties']['changed_area'] = 'float'
305 | newSchema['properties']['change_perc'] = 'float'
306 | kwargs = dict(driver=lotsDs.driver,
307 | crs=lotsDs.crs,
308 | schema=newSchema)
309 | bbox = box(*cdDs.bounds)
310 | with fiona.open(outputVector, 'w', **kwargs) as dst:
311 | for i, feat in enumerate(lotsDs):
312 | progress.setPercentage(int(i * total))
313 | lotId = feat['properties'][lotIdFieldName]
314 |
315 | # Skip features with invalid geometries
316 | if not feat['geometry']:
317 | continue
318 |
319 | # Skip features that are not inside rasters bounds
320 | poly = shape(feat['geometry'])
321 | if not bbox.contains(poly):
322 | continue
323 |
324 | # Calculate change percentage
325 | try:
326 | cdImg, _ = rasterio.mask.mask(cdDs, [feat['geometry']], crop=True)
327 | img, _ = rasterio.mask.mask(imgDs, [feat['geometry']], crop=True)
328 | except ValueError as err:
329 | progress.setText(
330 | self.tr('Error on lot id {}: {}. Skipping').format(lotId, err))
331 | continue
332 |
333 | # Skip features with no pixels in raster (too low resolution?)
334 | totalPixels = np.sum(img[0] > 0)
335 | if totalPixels == 0:
336 | progress.setText(
337 | self.tr('Lot {} has no pixels? Skipping...').format(lotId))
338 | continue
339 |
340 | count = np.sum(cdImg[0] > 0)
341 | perc = count / float(totalPixels)
342 | changeDetected = perc >= selectionThreshold
343 |
344 | # Calculate areas
345 | poly = shape(feat['geometry'])
346 | area = poly.area
347 | changedArea = poly.area * perc
348 |
349 | # Build row for table
350 | row = {}
351 | row['lot_id'] = lotId
352 | if changeDetected:
353 | row['change'] = 'Y'
354 | else:
355 | row['change'] = 'N'
356 | row['area'] = float(area)
357 | row['changed_area'] = float(changedArea)
358 | row['change_perc'] = float(perc)
359 |
360 | writer.addRecord([row[k] for k in columns])
361 |
362 | # Add feature in output vector file on change
363 | if changeDetected:
364 | newFeat = feat.copy()
365 | newFeat['properties'] = newFeat['properties'].copy()
366 | newFeat['properties']['changed_area'] = float(changedArea)
367 | newFeat['properties']['change_perc'] = float(perc)
368 | dst.write(newFeat)
369 |
370 | del writer
371 |
--------------------------------------------------------------------------------
/help/Makefile:
--------------------------------------------------------------------------------
1 | # Makefile for Sphinx documentation
2 | #
3 |
4 | # You can set these variables from the command line.
5 | SPHINXOPTS =
6 | SPHINXBUILD = sphinx-build
7 | PAPER =
8 | BUILDDIR = build
9 |
10 | # Internal variables.
11 | PAPEROPT_a4 = -D latex_paper_size=a4
12 | PAPEROPT_letter = -D latex_paper_size=letter
13 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
14 |
15 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest
16 |
17 | help:
18 | @echo "Please use \`make ' where is one of"
19 | @echo " html to make standalone HTML files"
20 | @echo " dirhtml to make HTML files named index.html in directories"
21 | @echo " singlehtml to make a single large HTML file"
22 | @echo " pickle to make pickle files"
23 | @echo " json to make JSON files"
24 | @echo " htmlhelp to make HTML files and a HTML help project"
25 | @echo " qthelp to make HTML files and a qthelp project"
26 | @echo " devhelp to make HTML files and a Devhelp project"
27 | @echo " epub to make an epub"
28 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
29 | @echo " latexpdf to make LaTeX files and run them through pdflatex"
30 | @echo " text to make text files"
31 | @echo " man to make manual pages"
32 | @echo " changes to make an overview of all changed/added/deprecated items"
33 | @echo " linkcheck to check all external links for integrity"
34 | @echo " doctest to run all doctests embedded in the documentation (if enabled)"
35 |
36 | clean:
37 | -rm -rf $(BUILDDIR)/*
38 |
39 | html:
40 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
41 | @echo
42 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
43 |
44 | dirhtml:
45 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
46 | @echo
47 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
48 |
49 | singlehtml:
50 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
51 | @echo
52 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
53 |
54 | pickle:
55 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
56 | @echo
57 | @echo "Build finished; now you can process the pickle files."
58 |
59 | json:
60 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
61 | @echo
62 | @echo "Build finished; now you can process the JSON files."
63 |
64 | htmlhelp:
65 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
66 | @echo
67 | @echo "Build finished; now you can run HTML Help Workshop with the" \
68 | ".hhp project file in $(BUILDDIR)/htmlhelp."
69 |
70 | qthelp:
71 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
72 | @echo
73 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \
74 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
75 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/template_class.qhcp"
76 | @echo "To view the help file:"
77 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/template_class.qhc"
78 |
79 | devhelp:
80 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
81 | @echo
82 | @echo "Build finished."
83 | @echo "To view the help file:"
84 | @echo "# mkdir -p $$HOME/.local/share/devhelp/template_class"
85 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/template_class"
86 | @echo "# devhelp"
87 |
88 | epub:
89 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
90 | @echo
91 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
92 |
93 | latex:
94 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
95 | @echo
96 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
97 | @echo "Run \`make' in that directory to run these through (pdf)latex" \
98 | "(use \`make latexpdf' here to do that automatically)."
99 |
100 | latexpdf:
101 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
102 | @echo "Running LaTeX files through pdflatex..."
103 | make -C $(BUILDDIR)/latex all-pdf
104 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
105 |
106 | text:
107 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
108 | @echo
109 | @echo "Build finished. The text files are in $(BUILDDIR)/text."
110 |
111 | man:
112 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
113 | @echo
114 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man."
115 |
116 | changes:
117 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
118 | @echo
119 | @echo "The overview file is in $(BUILDDIR)/changes."
120 |
121 | linkcheck:
122 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
123 | @echo
124 | @echo "Link check complete; look for any errors in the above output " \
125 | "or in $(BUILDDIR)/linkcheck/output.txt."
126 |
127 | doctest:
128 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
129 | @echo "Testing of doctests in the sources finished, look at the " \
130 | "results in $(BUILDDIR)/doctest/output.txt."
131 |
--------------------------------------------------------------------------------
/help/make.bat:
--------------------------------------------------------------------------------
1 | @ECHO OFF
2 |
3 | REM Command file for Sphinx documentation
4 |
5 | if "%SPHINXBUILD%" == "" (
6 | set SPHINXBUILD=sphinx-build
7 | )
8 | set BUILDDIR=build
9 | set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% source
10 | if NOT "%PAPER%" == "" (
11 | set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
12 | )
13 |
14 | if "%1" == "" goto help
15 |
16 | if "%1" == "help" (
17 | :help
18 | echo.Please use `make ^` where ^ is one of
19 | echo. html to make standalone HTML files
20 | echo. dirhtml to make HTML files named index.html in directories
21 | echo. singlehtml to make a single large HTML file
22 | echo. pickle to make pickle files
23 | echo. json to make JSON files
24 | echo. htmlhelp to make HTML files and a HTML help project
25 | echo. qthelp to make HTML files and a qthelp project
26 | echo. devhelp to make HTML files and a Devhelp project
27 | echo. epub to make an epub
28 | echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
29 | echo. text to make text files
30 | echo. man to make manual pages
31 | echo. changes to make an overview over all changed/added/deprecated items
32 | echo. linkcheck to check all external links for integrity
33 | echo. doctest to run all doctests embedded in the documentation if enabled
34 | goto end
35 | )
36 |
37 | if "%1" == "clean" (
38 | for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
39 | del /q /s %BUILDDIR%\*
40 | goto end
41 | )
42 |
43 | if "%1" == "html" (
44 | %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
45 | echo.
46 | echo.Build finished. The HTML pages are in %BUILDDIR%/html.
47 | goto end
48 | )
49 |
50 | if "%1" == "dirhtml" (
51 | %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
52 | echo.
53 | echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
54 | goto end
55 | )
56 |
57 | if "%1" == "singlehtml" (
58 | %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
59 | echo.
60 | echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
61 | goto end
62 | )
63 |
64 | if "%1" == "pickle" (
65 | %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
66 | echo.
67 | echo.Build finished; now you can process the pickle files.
68 | goto end
69 | )
70 |
71 | if "%1" == "json" (
72 | %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
73 | echo.
74 | echo.Build finished; now you can process the JSON files.
75 | goto end
76 | )
77 |
78 | if "%1" == "htmlhelp" (
79 | %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
80 | echo.
81 | echo.Build finished; now you can run HTML Help Workshop with the ^
82 | .hhp project file in %BUILDDIR%/htmlhelp.
83 | goto end
84 | )
85 |
86 | if "%1" == "qthelp" (
87 | %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
88 | echo.
89 | echo.Build finished; now you can run "qcollectiongenerator" with the ^
90 | .qhcp project file in %BUILDDIR%/qthelp, like this:
91 | echo.^> qcollectiongenerator %BUILDDIR%\qthelp\template_class.qhcp
92 | echo.To view the help file:
93 | echo.^> assistant -collectionFile %BUILDDIR%\qthelp\template_class.ghc
94 | goto end
95 | )
96 |
97 | if "%1" == "devhelp" (
98 | %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
99 | echo.
100 | echo.Build finished.
101 | goto end
102 | )
103 |
104 | if "%1" == "epub" (
105 | %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
106 | echo.
107 | echo.Build finished. The epub file is in %BUILDDIR%/epub.
108 | goto end
109 | )
110 |
111 | if "%1" == "latex" (
112 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
113 | echo.
114 | echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
115 | goto end
116 | )
117 |
118 | if "%1" == "text" (
119 | %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
120 | echo.
121 | echo.Build finished. The text files are in %BUILDDIR%/text.
122 | goto end
123 | )
124 |
125 | if "%1" == "man" (
126 | %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
127 | echo.
128 | echo.Build finished. The manual pages are in %BUILDDIR%/man.
129 | goto end
130 | )
131 |
132 | if "%1" == "changes" (
133 | %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
134 | echo.
135 | echo.The overview file is in %BUILDDIR%/changes.
136 | goto end
137 | )
138 |
139 | if "%1" == "linkcheck" (
140 | %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
141 | echo.
142 | echo.Link check complete; look for any errors in the above output ^
143 | or in %BUILDDIR%/linkcheck/output.txt.
144 | goto end
145 | )
146 |
147 | if "%1" == "doctest" (
148 | %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
149 | echo.
150 | echo.Testing of doctests in the sources finished, look at the ^
151 | results in %BUILDDIR%/doctest/output.txt.
152 | goto end
153 | )
154 |
155 | :end
156 |
--------------------------------------------------------------------------------
/help/source/conf.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #
3 | # MassiveChangeDetection documentation build configuration file, created by
4 | # sphinx-quickstart on Sun Feb 12 17:11:03 2012.
5 | #
6 | # This file is execfile()d with the current directory set to its containing dir.
7 | #
8 | # Note that not all possible configuration values are present in this
9 | # autogenerated file.
10 | #
11 | # All configuration values have a default; values that are commented out
12 | # serve to show the default.
13 |
14 | import sys, os
15 |
16 | # If extensions (or modules to document with autodoc) are in another directory,
17 | # add these directories to sys.path here. If the directory is relative to the
18 | # documentation root, use os.path.abspath to make it absolute, like shown here.
19 | #sys.path.insert(0, os.path.abspath('.'))
20 |
21 | # -- General configuration -----------------------------------------------------
22 |
23 | # If your documentation needs a minimal Sphinx version, state it here.
24 | #needs_sphinx = '1.0'
25 |
26 | # Add any Sphinx extension module names here, as strings. They can be extensions
27 | # coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
28 | extensions = ['sphinx.ext.todo', 'sphinx.ext.pngmath', 'sphinx.ext.viewcode']
29 |
30 | # Add any paths that contain templates here, relative to this directory.
31 | templates_path = ['_templates']
32 |
33 | # The suffix of source filenames.
34 | source_suffix = '.rst'
35 |
36 | # The encoding of source files.
37 | #source_encoding = 'utf-8-sig'
38 |
39 | # The master toctree document.
40 | master_doc = 'index'
41 |
42 | # General information about the project.
43 | project = u'MassiveChangeDetection'
44 | copyright = u'2013, Dymaxion Labs'
45 |
46 | # The version info for the project you're documenting, acts as replacement for
47 | # |version| and |release|, also used in various other places throughout the
48 | # built documents.
49 | #
50 | # The short X.Y version.
51 | version = '0.1'
52 | # The full version, including alpha/beta/rc tags.
53 | release = '0.1'
54 |
55 | # The language for content autogenerated by Sphinx. Refer to documentation
56 | # for a list of supported languages.
57 | #language = None
58 |
59 | # There are two options for replacing |today|: either, you set today to some
60 | # non-false value, then it is used:
61 | #today = ''
62 | # Else, today_fmt is used as the format for a strftime call.
63 | #today_fmt = '%B %d, %Y'
64 |
65 | # List of patterns, relative to source directory, that match files and
66 | # directories to ignore when looking for source files.
67 | exclude_patterns = []
68 |
69 | # The reST default role (used for this markup: `text`) to use for all documents.
70 | #default_role = None
71 |
72 | # If true, '()' will be appended to :func: etc. cross-reference text.
73 | #add_function_parentheses = True
74 |
75 | # If true, the current module name will be prepended to all description
76 | # unit titles (such as .. function::).
77 | #add_TemplateModuleNames = True
78 |
79 | # If true, sectionauthor and moduleauthor directives will be shown in the
80 | # output. They are ignored by default.
81 | #show_authors = False
82 |
83 | # The name of the Pygments (syntax highlighting) style to use.
84 | pygments_style = 'sphinx'
85 |
86 | # A list of ignored prefixes for module index sorting.
87 | #modindex_common_prefix = []
88 |
89 |
90 | # -- Options for HTML output ---------------------------------------------------
91 |
92 | # The theme to use for HTML and HTML Help pages. See the documentation for
93 | # a list of builtin themes.
94 | html_theme = 'default'
95 |
96 | # Theme options are theme-specific and customize the look and feel of a theme
97 | # further. For a list of options available for each theme, see the
98 | # documentation.
99 | #html_theme_options = {}
100 |
101 | # Add any paths that contain custom themes here, relative to this directory.
102 | #html_theme_path = []
103 |
104 | # The name for this set of Sphinx documents. If None, it defaults to
105 | # " v documentation".
106 | #html_title = None
107 |
108 | # A shorter title for the navigation bar. Default is the same as html_title.
109 | #html_short_title = None
110 |
111 | # The name of an image file (relative to this directory) to place at the top
112 | # of the sidebar.
113 | #html_logo = None
114 |
115 | # The name of an image file (within the static path) to use as favicon of the
116 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
117 | # pixels large.
118 | #html_favicon = None
119 |
120 | # Add any paths that contain custom static files (such as style sheets) here,
121 | # relative to this directory. They are copied after the builtin static files,
122 | # so a file named "default.css" will overwrite the builtin "default.css".
123 | html_static_path = ['_static']
124 |
125 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
126 | # using the given strftime format.
127 | #html_last_updated_fmt = '%b %d, %Y'
128 |
129 | # If true, SmartyPants will be used to convert quotes and dashes to
130 | # typographically correct entities.
131 | #html_use_smartypants = True
132 |
133 | # Custom sidebar templates, maps document names to template names.
134 | #html_sidebars = {}
135 |
136 | # Additional templates that should be rendered to pages, maps page names to
137 | # template names.
138 | #html_additional_pages = {}
139 |
140 | # If false, no module index is generated.
141 | #html_domain_indices = True
142 |
143 | # If false, no index is generated.
144 | #html_use_index = True
145 |
146 | # If true, the index is split into individual pages for each letter.
147 | #html_split_index = False
148 |
149 | # If true, links to the reST sources are added to the pages.
150 | #html_show_sourcelink = True
151 |
152 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
153 | #html_show_sphinx = True
154 |
155 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
156 | #html_show_copyright = True
157 |
158 | # If true, an OpenSearch description file will be output, and all pages will
159 | # contain a tag referring to it. The value of this option must be the
160 | # base URL from which the finished HTML is served.
161 | #html_use_opensearch = ''
162 |
163 | # This is the file name suffix for HTML files (e.g. ".xhtml").
164 | #html_file_suffix = None
165 |
166 | # Output file base name for HTML help builder.
167 | htmlhelp_basename = 'TemplateClassdoc'
168 |
169 |
170 | # -- Options for LaTeX output --------------------------------------------------
171 |
172 | # The paper size ('letter' or 'a4').
173 | #latex_paper_size = 'letter'
174 |
175 | # The font size ('10pt', '11pt' or '12pt').
176 | #latex_font_size = '10pt'
177 |
178 | # Grouping the document tree into LaTeX files. List of tuples
179 | # (source start file, target name, title, author, documentclass [howto/manual]).
180 | latex_documents = [
181 | ('index', 'MassiveChangeDetection.tex', u'MassiveChangeDetection Documentation',
182 | u'Dymaxion Labs', 'manual'),
183 | ]
184 |
185 | # The name of an image file (relative to this directory) to place at the top of
186 | # the title page.
187 | #latex_logo = None
188 |
189 | # For "manual" documents, if this is true, then toplevel headings are parts,
190 | # not chapters.
191 | #latex_use_parts = False
192 |
193 | # If true, show page references after internal links.
194 | #latex_show_pagerefs = False
195 |
196 | # If true, show URL addresses after external links.
197 | #latex_show_urls = False
198 |
199 | # Additional stuff for the LaTeX preamble.
200 | #latex_preamble = ''
201 |
202 | # Documents to append as an appendix to all manuals.
203 | #latex_appendices = []
204 |
205 | # If false, no module index is generated.
206 | #latex_domain_indices = True
207 |
208 |
209 | # -- Options for manual page output --------------------------------------------
210 |
211 | # One entry per manual page. List of tuples
212 | # (source start file, name, description, authors, manual section).
213 | man_pages = [
214 | ('index', 'TemplateClass', u'MassiveChangeDetection Documentation',
215 | [u'Dymaxion Labs'], 1)
216 | ]
217 |
--------------------------------------------------------------------------------
/help/source/index.rst:
--------------------------------------------------------------------------------
1 | .. MassiveChangeDetection documentation master file, created by
2 | sphinx-quickstart on Sun Feb 12 17:11:03 2012.
3 | You can adapt this file completely to your liking, but it should at least
4 | contain the root `toctree` directive.
5 |
6 | Welcome to MassiveChangeDetection's documentation!
7 | ============================================
8 |
9 | Contents:
10 |
11 | .. toctree::
12 | :maxdepth: 2
13 |
14 | Indices and tables
15 | ==================
16 |
17 | * :ref:`genindex`
18 | * :ref:`modindex`
19 | * :ref:`search`
20 |
21 |
--------------------------------------------------------------------------------
/i18n/es.qm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dymaxionlabs/massive-change-detection/fce3b7aa1caa009931e2a5f0bb1a76976ce7cc72/i18n/es.qm
--------------------------------------------------------------------------------
/i18n/es.ts:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | @default
5 |
6 |
7 | Good morning
8 | Buenos días
9 |
10 |
11 |
12 | GenerateVectorAlgorithm
13 |
14 |
15 | Input change detection layer
16 | Raster de detección de cambios
17 |
18 |
19 |
20 | Input image layer
21 | Raster imagen
22 |
23 |
24 |
25 | Input Lots vector layer
26 | Capa de parcelas
27 |
28 |
29 |
30 | Lot id field
31 | Campo Id de parcela
32 |
33 |
34 |
35 | Lot selection threshold value
36 | Umbral de selección de cambio
37 |
38 |
39 |
40 | CD vector
41 | Shapefile de cambios
42 |
43 |
44 |
45 | CD table
46 | Tabla de cambios
47 |
48 |
49 |
50 | Lots vector file has different CRS than rasters: {} != {}
51 | Shapefile de parcelas tiene diference proyección que la de los rasters
52 |
53 |
54 |
55 | Processing lot features...
56 | Procesando polígonos de parcelas...
57 |
58 |
59 |
60 | Error on lot id {}: {}. Skipping
61 | Error en lote id: {}: {}. Ignorando
62 |
63 |
64 |
65 | Lot {} has no pixels? Skipping...
66 | Lote {} no tiene píxeles? Ignorando...
67 |
68 |
69 |
70 | Generate changed lots data
71 | Generar datos de parcelas cambiadas
72 |
73 |
74 |
75 | Report
76 | Reportes
77 |
78 |
79 |
80 | MultibandDifferenceAlgorithm
81 |
82 |
83 | Multiband difference
84 | Diferencia multibanda
85 |
86 |
87 |
88 | Pixel-based algorithms
89 | Algoritmos basados en píxeles
90 |
91 |
92 |
93 | Input old layer
94 | Imagen vieja
95 |
96 |
97 |
98 | Input new layer
99 | Imagen nueva
100 |
101 |
102 |
103 | Use automatic thresholding
104 | Calcular umbral automático
105 |
106 |
107 |
108 | Threshold value (if not automatic)
109 | Valor del umbral (si no es automático)
110 |
111 |
112 |
113 | Filter type
114 | Tipo de filtro
115 |
116 |
117 |
118 | Filter kernel size
119 | Tamaño del kernel del filtro
120 |
121 |
122 |
123 | CD raster
124 | Imagen de cambios
125 |
126 |
127 |
128 | You must use two different raster images for inputs A and B
129 | Debes usar dos imágenes de entrada diferentes
130 |
131 |
132 |
133 | Reading rasters into arrays
134 | Leyendo rasters en arrays
135 |
136 |
137 |
138 | Calculate image difference on band {}
139 | Calculando diferencia de imagen en banda {}
140 |
141 |
142 |
143 | No changed detected. Try to use a lower threshold value or different images
144 | No se detectaron cambios. Intenta usar un umbral más bajo o diferentes imágenes.
145 |
146 |
147 |
148 | Kernel size for median filter must be an odd number
149 | El tamaño del kernel para el filtro mediana debe ser un número impar.
150 |
151 |
152 |
153 | Applied manual threshold of value {}
154 | Se aplicó umbral manual de valor {}
155 |
156 |
157 |
158 | Unhandled filter type: {}
159 | Filtro desconocido: {}
160 |
161 |
162 |
163 |
--------------------------------------------------------------------------------
/massive_change_detection.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | Main plugin module
4 |
5 | .. note:: This program is free software; you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation; either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | """
11 |
12 | __author__ = 'Damián Silvani'
13 | __date__ = '2018-06-26'
14 | __copyright__ = '(C) 2018 by Dymaxion Labs'
15 |
16 | # This will get replaced with a git SHA1 when you do a git archive
17 |
18 | __revision__ = '$Format:%H$'
19 |
20 | import os
21 | import sys
22 | import inspect
23 |
24 | from PyQt4.QtCore import QSettings, QTranslator, QCoreApplication
25 | from processing.core.Processing import Processing
26 | from .provider import MassiveChangeDetectionProvider
27 |
28 | cmd_folder = os.path.split(inspect.getfile(inspect.currentframe()))[0]
29 |
30 | if cmd_folder not in sys.path:
31 | sys.path.insert(0, cmd_folder)
32 |
33 |
34 | class MassiveChangeDetectionPlugin:
35 | def __init__(self):
36 | self.provider = MassiveChangeDetectionProvider()
37 | self.pluginDir = os.path.dirname(os.path.realpath(__file__))
38 | self._load_translations()
39 |
40 | def initGui(self):
41 | Processing.addProvider(self.provider)
42 |
43 | def unload(self):
44 | Processing.removeProvider(self.provider)
45 |
46 | def _load_translations(self):
47 | full_locale = QSettings().value("locale/userLocale")
48 | if not full_locale:
49 | return
50 | locale = full_locale[0:2]
51 | localePath = os.path.join(self.pluginDir, 'i18n', '{}.qm'.format(locale))
52 | if os.path.exists(localePath):
53 | translator = QTranslator()
54 | translator.load(localePath)
55 | QCoreApplication.installTranslator(translator)
56 |
--------------------------------------------------------------------------------
/metadata.txt:
--------------------------------------------------------------------------------
1 | # This file contains metadata for your plugin. Since
2 | # version 2.0 of QGIS this is the proper way to supply
3 | # information about a plugin. The old method of
4 | # embedding metadata in __init__.py will
5 | # is no longer supported since version 2.0.
6 |
7 | # This file should be included when you package your plugin.# Mandatory items:
8 |
9 | [general]
10 | name=Massive Change Detection
11 | qgisMinimumVersion=2.0
12 | description=Change detection tool
13 | version=0.1
14 | author=Dymaxion Labs
15 | email=contact@dymaxionlabs.com
16 |
17 | about=Provide a brief description of the plugin and its purpose.
18 |
19 | tracker=https://github.com/dymaxionlabs/massive-change-detection-qgis/issues
20 | repository=https://github.com/dymaxionlabs/massive-change-detection-qgis
21 | # End of mandatory metadata
22 |
23 | # Recommended items:
24 |
25 | # Uncomment the following line and add your changelog:
26 | # changelog=
27 |
28 | # Tags are comma separated with spaces allowed
29 | tags=python
30 |
31 | homepage=https://github.com/dymaxionlabs/massive-change-detection-qgis
32 | category=Analysis
33 | icon=icon.png
34 | # experimental flag
35 | experimental=True
36 |
37 | # deprecated flag (applies to the whole plugin, not just a single version)
38 | deprecated=False
39 |
40 |
--------------------------------------------------------------------------------
/pb_tool.cfg:
--------------------------------------------------------------------------------
1 | #/***************************************************************************
2 | # MassiveChangeDetection
3 | #
4 | # Configuration file for plugin builder tool (pb_tool)
5 | # -------------------
6 | # begin : 2018-06-26
7 | # copyright : (C) 2018 by Dymaxion Labs
8 | # email : contact@dymaxionlabs.com
9 | # ***************************************************************************/
10 | #
11 | #/***************************************************************************
12 | # * *
13 | # * This program is free software; you can redistribute it and/or modify *
14 | # * it under the terms of the GNU General Public License as published by *
15 | # * the Free Software Foundation; either version 2 of the License, or *
16 | # * (at your option) any later version. *
17 | # * *
18 | # ***************************************************************************/
19 | #
20 | #
21 | # You can install pb_tool using:
22 | # pip install http://geoapt.net/files/pb_tool.zip
23 | #
24 | # Consider doing your development (and install of pb_tool) in a virtualenv.
25 | #
26 | # For details on setting up and using pb_tool, see:
27 | # http://spatialgalaxy.net/qgis-plugin-development-with-pb_tool
28 | #
29 | # Issues and pull requests here:
30 | # https://github.com/g-sherman/plugin_build_tool:
31 | #
32 | # Sane defaults for your plugin generated by the Plugin Builder are
33 | # already set below.
34 | #
35 | # As you add Python source files and UI files to your plugin, add
36 | # them to the appropriate [files] section below.
37 |
38 | [plugin]
39 | # Name of the plugin. This is the name of the directory that will
40 | # be created in .qgis2/python/plugins
41 | name: MassiveChangeDetection
42 |
43 | [files]
44 | # Python files that should be deployed with the plugin
45 | python_files: __init__.py massive_change_detection.py
46 |
47 | # The main dialog file that is loaded (not compiled)
48 | main_dialog:
49 |
50 | # Other ui files for dialogs you create (these will be compiled)
51 | compiled_ui_files:
52 |
53 | # Resource file(s) that will be compiled
54 | resource_files:
55 |
56 | # Other files required for the plugin
57 | extras: metadata.txt
58 |
59 | # Other directories to be deployed with the plugin.
60 | # These must be subdirectories under the plugin directory
61 | extra_dirs:
62 |
63 | # ISO code(s) for any locales (translations), separated by spaces.
64 | # Corresponding .ts files must exist in the i18n directory
65 | locales:
66 |
67 | [help]
68 | # the built help directory that should be deployed with the plugin
69 | dir: help/build/html
70 | # the name of the directory to target in the deployed plugin
71 | target: help
72 |
73 |
74 |
75 |
--------------------------------------------------------------------------------
/plugin_upload.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # coding=utf-8
3 |
4 | """This script uploads a plugin package on the server.
5 | Authors: A. Pasotti, V. Picavet
6 | git sha : $TemplateVCSFormat
7 | """
8 |
9 | import sys
10 | import getpass
11 | import xmlrpclib
12 | from optparse import OptionParser
13 |
14 | # Configuration
15 | PROTOCOL = 'http'
16 | SERVER = 'plugins.qgis.org'
17 | PORT = '80'
18 | ENDPOINT = '/plugins/RPC2/'
19 | VERBOSE = False
20 |
21 |
22 | def main(parameters, arguments):
23 | """Main entry point.
24 |
25 | :param parameters: Command line parameters.
26 | :param arguments: Command line arguments.
27 | """
28 | address = "%s://%s:%s@%s:%s%s" % (
29 | PROTOCOL,
30 | parameters.username,
31 | parameters.password,
32 | parameters.server,
33 | parameters.port,
34 | ENDPOINT)
35 | print "Connecting to: %s" % hide_password(address)
36 |
37 | server = xmlrpclib.ServerProxy(address, verbose=VERBOSE)
38 |
39 | try:
40 | plugin_id, version_id = server.plugin.upload(
41 | xmlrpclib.Binary(open(arguments[0]).read()))
42 | print "Plugin ID: %s" % plugin_id
43 | print "Version ID: %s" % version_id
44 | except xmlrpclib.ProtocolError, err:
45 | print "A protocol error occurred"
46 | print "URL: %s" % hide_password(err.url, 0)
47 | print "HTTP/HTTPS headers: %s" % err.headers
48 | print "Error code: %d" % err.errcode
49 | print "Error message: %s" % err.errmsg
50 | except xmlrpclib.Fault, err:
51 | print "A fault occurred"
52 | print "Fault code: %d" % err.faultCode
53 | print "Fault string: %s" % err.faultString
54 |
55 |
56 | def hide_password(url, start=6):
57 | """Returns the http url with password part replaced with '*'.
58 |
59 | :param url: URL to upload the plugin to.
60 | :type url: str
61 |
62 | :param start: Position of start of password.
63 | :type start: int
64 | """
65 | start_position = url.find(':', start) + 1
66 | end_position = url.find('@')
67 | return "%s%s%s" % (
68 | url[:start_position],
69 | '*' * (end_position - start_position),
70 | url[end_position:])
71 |
72 |
73 | if __name__ == "__main__":
74 | parser = OptionParser(usage="%prog [options] plugin.zip")
75 | parser.add_option(
76 | "-w", "--password", dest="password",
77 | help="Password for plugin site", metavar="******")
78 | parser.add_option(
79 | "-u", "--username", dest="username",
80 | help="Username of plugin site", metavar="user")
81 | parser.add_option(
82 | "-p", "--port", dest="port",
83 | help="Server port to connect to", metavar="80")
84 | parser.add_option(
85 | "-s", "--server", dest="server",
86 | help="Specify server name", metavar="plugins.qgis.org")
87 | options, args = parser.parse_args()
88 | if len(args) != 1:
89 | print "Please specify zip file.\n"
90 | parser.print_help()
91 | sys.exit(1)
92 | if not options.server:
93 | options.server = SERVER
94 | if not options.port:
95 | options.port = PORT
96 | if not options.username:
97 | # interactive mode
98 | username = getpass.getuser()
99 | print "Please enter user name [%s] :" % username,
100 | res = raw_input()
101 | if res != "":
102 | options.username = res
103 | else:
104 | options.username = username
105 | if not options.password:
106 | # interactive mode
107 | options.password = getpass.getpass()
108 | main(options, args)
109 |
--------------------------------------------------------------------------------
/provider.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | Main plugin provider class
4 |
5 | .. note:: This program is free software; you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation; either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | """
11 |
12 | __author__ = 'Damián Silvani'
13 | __date__ = '2018-06-26'
14 | __copyright__ = '(C) 2018 by Dymaxion Labs'
15 |
16 | # This will get replaced with a git SHA1 when you do a git archive
17 |
18 | __revision__ = '$Format:%H$'
19 |
20 | from processing.core.AlgorithmProvider import AlgorithmProvider
21 | from processing.core.ProcessingConfig import Setting, ProcessingConfig
22 | from .algorithm import MultibandDifferenceAlgorithm, GenerateVectorAlgorithm
23 |
24 |
25 | class MassiveChangeDetectionProvider(AlgorithmProvider):
26 |
27 | def __init__(self):
28 | AlgorithmProvider.__init__(self)
29 |
30 | self.activate = True
31 |
32 | # Load algorithms
33 | self.alglist = [
34 | MultibandDifferenceAlgorithm(),
35 | GenerateVectorAlgorithm()
36 | ]
37 |
38 | for alg in self.alglist:
39 | alg.provider = self
40 |
41 | def getName(self):
42 | """This is the name that will appear on the toolbox group.
43 |
44 | It is also used to create the command line name of all the
45 | algorithms from this provider.
46 | """
47 | return 'Massive Change Detection'
48 |
49 | def getDescription(self):
50 | """This is the provired full name.
51 | """
52 | return 'Massive Change Detection'
53 |
54 | def getIcon(self):
55 | """We return the default icon.
56 | """
57 | return AlgorithmProvider.getIcon(self)
58 |
59 | def _loadAlgorithms(self):
60 | """Here we fill the list of algorithms in self.algs.
61 |
62 | This method is called whenever the list of algorithms should
63 | be updated. If the list of algorithms can change (for instance,
64 | if it contains algorithms from user-defined scripts and a new
65 | script might have been added), you should create the list again
66 | here.
67 |
68 | In this case, since the list is always the same, we assign from
69 | the pre-made list. This assignment has to be done in this method
70 | even if the list does not change, since the self.algs list is
71 | cleared before calling this method.
72 | """
73 | self.algs = self.alglist
74 |
--------------------------------------------------------------------------------
/pylintrc:
--------------------------------------------------------------------------------
1 | [MASTER]
2 |
3 | # Specify a configuration file.
4 | #rcfile=
5 |
6 | # Python code to execute, usually for sys.path manipulation such as
7 | # pygtk.require().
8 | #init-hook=
9 |
10 | # Profiled execution.
11 | profile=no
12 |
13 | # Add files or directories to the blacklist. They should be base names, not
14 | # paths.
15 | ignore=CVS
16 |
17 | # Pickle collected data for later comparisons.
18 | persistent=yes
19 |
20 | # List of plugins (as comma separated values of python modules names) to load,
21 | # usually to register additional checkers.
22 | load-plugins=
23 |
24 |
25 | [MESSAGES CONTROL]
26 |
27 | # Enable the message, report, category or checker with the given id(s). You can
28 | # either give multiple identifier separated by comma (,) or put this option
29 | # multiple time. See also the "--disable" option for examples.
30 | #enable=
31 |
32 | # Disable the message, report, category or checker with the given id(s). You
33 | # can either give multiple identifiers separated by comma (,) or put this
34 | # option multiple times (only on the command line, not in the configuration
35 | # file where it should appear only once).You can also use "--disable=all" to
36 | # disable everything first and then reenable specific checks. For example, if
37 | # you want to run only the similarities checker, you can use "--disable=all
38 | # --enable=similarities". If you want to run only the classes checker, but have
39 | # no Warning level messages displayed, use"--disable=all --enable=classes
40 | # --disable=W"
41 | # see http://stackoverflow.com/questions/21487025/pylint-locally-defined-disables-still-give-warnings-how-to-suppress-them
42 | disable=locally-disabled,C0103
43 |
44 |
45 | [REPORTS]
46 |
47 | # Set the output format. Available formats are text, parseable, colorized, msvs
48 | # (visual studio) and html. You can also give a reporter class, eg
49 | # mypackage.mymodule.MyReporterClass.
50 | output-format=text
51 |
52 | # Put messages in a separate file for each module / package specified on the
53 | # command line instead of printing them on stdout. Reports (if any) will be
54 | # written in a file name "pylint_global.[txt|html]".
55 | files-output=no
56 |
57 | # Tells whether to display a full report or only the messages
58 | reports=yes
59 |
60 | # Python expression which should return a note less than 10 (10 is the highest
61 | # note). You have access to the variables errors warning, statement which
62 | # respectively contain the number of errors / warnings messages and the total
63 | # number of statements analyzed. This is used by the global evaluation report
64 | # (RP0004).
65 | evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
66 |
67 | # Add a comment according to your evaluation note. This is used by the global
68 | # evaluation report (RP0004).
69 | comment=no
70 |
71 | # Template used to display messages. This is a python new-style format string
72 | # used to format the message information. See doc for all details
73 | #msg-template=
74 |
75 |
76 | [BASIC]
77 |
78 | # Required attributes for module, separated by a comma
79 | required-attributes=
80 |
81 | # List of builtins function names that should not be used, separated by a comma
82 | bad-functions=map,filter,apply,input
83 |
84 | # Regular expression which should only match correct module names
85 | module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
86 |
87 | # Regular expression which should only match correct module level names
88 | const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$
89 |
90 | # Regular expression which should only match correct class names
91 | class-rgx=[A-Z_][a-zA-Z0-9]+$
92 |
93 | # Regular expression which should only match correct function names
94 | function-rgx=[a-z_][a-z0-9_]{2,30}$
95 |
96 | # Regular expression which should only match correct method names
97 | method-rgx=[a-z_][a-z0-9_]{2,30}$
98 |
99 | # Regular expression which should only match correct instance attribute names
100 | attr-rgx=[a-z_][a-z0-9_]{2,30}$
101 |
102 | # Regular expression which should only match correct argument names
103 | argument-rgx=[a-z_][a-z0-9_]{2,30}$
104 |
105 | # Regular expression which should only match correct variable names
106 | variable-rgx=[a-z_][a-z0-9_]{2,30}$
107 |
108 | # Regular expression which should only match correct attribute names in class
109 | # bodies
110 | class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$
111 |
112 | # Regular expression which should only match correct list comprehension /
113 | # generator expression variable names
114 | inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$
115 |
116 | # Good variable names which should always be accepted, separated by a comma
117 | good-names=i,j,k,ex,Run,_
118 |
119 | # Bad variable names which should always be refused, separated by a comma
120 | bad-names=foo,bar,baz,toto,tutu,tata
121 |
122 | # Regular expression which should only match function or class names that do
123 | # not require a docstring.
124 | no-docstring-rgx=__.*__
125 |
126 | # Minimum line length for functions/classes that require docstrings, shorter
127 | # ones are exempt.
128 | docstring-min-length=-1
129 |
130 |
131 | [MISCELLANEOUS]
132 |
133 | # List of note tags to take in consideration, separated by a comma.
134 | notes=FIXME,XXX,TODO
135 |
136 |
137 | [TYPECHECK]
138 |
139 | # Tells whether missing members accessed in mixin class should be ignored. A
140 | # mixin class is detected if its name ends with "mixin" (case insensitive).
141 | ignore-mixin-members=yes
142 |
143 | # List of classes names for which member attributes should not be checked
144 | # (useful for classes with attributes dynamically set).
145 | ignored-classes=SQLObject
146 |
147 | # When zope mode is activated, add a predefined set of Zope acquired attributes
148 | # to generated-members.
149 | zope=no
150 |
151 | # List of members which are set dynamically and missed by pylint inference
152 | # system, and so shouldn't trigger E0201 when accessed. Python regular
153 | # expressions are accepted.
154 | generated-members=REQUEST,acl_users,aq_parent
155 |
156 |
157 | [VARIABLES]
158 |
159 | # Tells whether we should check for unused import in __init__ files.
160 | init-import=no
161 |
162 | # A regular expression matching the beginning of the name of dummy variables
163 | # (i.e. not used).
164 | dummy-variables-rgx=_$|dummy
165 |
166 | # List of additional names supposed to be defined in builtins. Remember that
167 | # you should avoid to define new builtins when possible.
168 | additional-builtins=
169 |
170 |
171 | [FORMAT]
172 |
173 | # Maximum number of characters on a single line.
174 | max-line-length=80
175 |
176 | # Regexp for a line that is allowed to be longer than the limit.
177 | ignore-long-lines=^\s*(# )??$
178 |
179 | # Allow the body of an if to be on the same line as the test if there is no
180 | # else.
181 | single-line-if-stmt=no
182 |
183 | # List of optional constructs for which whitespace checking is disabled
184 | no-space-check=trailing-comma,dict-separator
185 |
186 | # Maximum number of lines in a module
187 | max-module-lines=1000
188 |
189 | # String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
190 | # tab).
191 | indent-string=' '
192 |
193 |
194 | [SIMILARITIES]
195 |
196 | # Minimum lines number of a similarity.
197 | min-similarity-lines=4
198 |
199 | # Ignore comments when computing similarities.
200 | ignore-comments=yes
201 |
202 | # Ignore docstrings when computing similarities.
203 | ignore-docstrings=yes
204 |
205 | # Ignore imports when computing similarities.
206 | ignore-imports=no
207 |
208 |
209 | [IMPORTS]
210 |
211 | # Deprecated modules which should not be used, separated by a comma
212 | deprecated-modules=regsub,TERMIOS,Bastion,rexec
213 |
214 | # Create a graph of every (i.e. internal and external) dependencies in the
215 | # given file (report RP0402 must not be disabled)
216 | import-graph=
217 |
218 | # Create a graph of external dependencies in the given file (report RP0402 must
219 | # not be disabled)
220 | ext-import-graph=
221 |
222 | # Create a graph of internal dependencies in the given file (report RP0402 must
223 | # not be disabled)
224 | int-import-graph=
225 |
226 |
227 | [DESIGN]
228 |
229 | # Maximum number of arguments for function / method
230 | max-args=5
231 |
232 | # Argument names that match this expression will be ignored. Default to name
233 | # with leading underscore
234 | ignored-argument-names=_.*
235 |
236 | # Maximum number of locals for function / method body
237 | max-locals=15
238 |
239 | # Maximum number of return / yield for function / method body
240 | max-returns=6
241 |
242 | # Maximum number of branch for function / method body
243 | max-branches=12
244 |
245 | # Maximum number of statements in function / method body
246 | max-statements=50
247 |
248 | # Maximum number of parents for a class (see R0901).
249 | max-parents=7
250 |
251 | # Maximum number of attributes for a class (see R0902).
252 | max-attributes=7
253 |
254 | # Minimum number of public methods for a class (see R0903).
255 | min-public-methods=2
256 |
257 | # Maximum number of public methods for a class (see R0904).
258 | max-public-methods=20
259 |
260 |
261 | [CLASSES]
262 |
263 | # List of interface methods to ignore, separated by a comma. This is used for
264 | # instance to not check methods defines in Zope's Interface base class.
265 | ignore-iface-methods=isImplementedBy,deferred,extends,names,namesAndDescriptions,queryDescriptionFor,getBases,getDescriptionFor,getDoc,getName,getTaggedValue,getTaggedValueTags,isEqualOrExtendedBy,setTaggedValue,isImplementedByInstancesOf,adaptWith,is_implemented_by
266 |
267 | # List of method names used to declare (i.e. assign) instance attributes.
268 | defining-attr-methods=__init__,__new__,setUp
269 |
270 | # List of valid names for the first argument in a class method.
271 | valid-classmethod-first-arg=cls
272 |
273 | # List of valid names for the first argument in a metaclass class method.
274 | valid-metaclass-classmethod-first-arg=mcs
275 |
276 |
277 | [EXCEPTIONS]
278 |
279 | # Exceptions that will emit a warning when being caught. Defaults to
280 | # "Exception"
281 | overgeneral-exceptions=Exception
282 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | fiona
2 | numpy
3 | opencv-python
4 | rasterio
5 | shapely
6 |
7 | coverage
8 | nose
9 | pylint
10 |
--------------------------------------------------------------------------------
/scripts/compile-strings.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | LRELEASE=$1
3 | LOCALES=$2
4 |
5 |
6 | for LOCALE in ${LOCALES}
7 | do
8 | echo "Processing: ${LOCALE}.ts"
9 | # Note we don't use pylupdate with qt .pro file approach as it is flakey
10 | # about what is made available.
11 | $LRELEASE i18n/${LOCALE}.ts
12 | done
13 |
--------------------------------------------------------------------------------
/scripts/run-env-linux.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | QGIS_PREFIX_PATH=/usr/local/qgis-2.0
4 | if [ -n "$1" ]; then
5 | QGIS_PREFIX_PATH=$1
6 | fi
7 |
8 | echo ${QGIS_PREFIX_PATH}
9 |
10 |
11 | export QGIS_PREFIX_PATH=${QGIS_PREFIX_PATH}
12 | export QGIS_PATH=${QGIS_PREFIX_PATH}
13 | export LD_LIBRARY_PATH=${QGIS_PREFIX_PATH}/lib
14 | export PYTHONPATH=${QGIS_PREFIX_PATH}/share/qgis/python:${QGIS_PREFIX_PATH}/share/qgis/python/plugins:${PYTHONPATH}
15 |
16 | echo "QGIS PATH: $QGIS_PREFIX_PATH"
17 | export QGIS_DEBUG=0
18 | export QGIS_LOG_FILE=/tmp/inasafe/realtime/logs/qgis.log
19 |
20 | export PATH=${QGIS_PREFIX_PATH}/bin:$PATH
21 |
22 | echo "This script is intended to be sourced to set up your shell to"
23 | echo "use a QGIS 2.0 built in $QGIS_PREFIX_PATH"
24 | echo
25 | echo "To use it do:"
26 | echo "source $BASH_SOURCE /your/optional/install/path"
27 | echo
28 | echo "Then use the make file supplied here e.g. make guitest"
29 |
--------------------------------------------------------------------------------
/scripts/update-strings.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | LOCALES=$*
3 |
4 | # Get newest .py files so we don't update strings unnecessarily
5 |
6 | CHANGED_FILES=0
7 | PYTHON_FILES=`find . -regex ".*\(ui\|py\)$" -type f`
8 | for PYTHON_FILE in $PYTHON_FILES
9 | do
10 | CHANGED=$(stat -c %Y $PYTHON_FILE)
11 | if [ ${CHANGED} -gt ${CHANGED_FILES} ]
12 | then
13 | CHANGED_FILES=${CHANGED}
14 | fi
15 | done
16 |
17 | # Qt translation stuff
18 | # for .ts file
19 | UPDATE=false
20 | for LOCALE in ${LOCALES}
21 | do
22 | TRANSLATION_FILE="i18n/$LOCALE.ts"
23 | if [ ! -f ${TRANSLATION_FILE} ]
24 | then
25 | # Force translation string collection as we have a new language file
26 | touch ${TRANSLATION_FILE}
27 | UPDATE=true
28 | break
29 | fi
30 |
31 | MODIFICATION_TIME=$(stat -c %Y ${TRANSLATION_FILE})
32 | if [ ${CHANGED_FILES} -gt ${MODIFICATION_TIME} ]
33 | then
34 | # Force translation string collection as a .py file has been updated
35 | UPDATE=true
36 | break
37 | fi
38 | done
39 |
40 | if [ ${UPDATE} == true ]
41 | # retrieve all python files
42 | then
43 | print ${PYTHON_FILES}
44 | # update .ts
45 | echo "Please provide translations by editing the translation files below:"
46 | for LOCALE in ${LOCALES}
47 | do
48 | echo "i18n/"${LOCALE}".ts"
49 | # Note we don't use pylupdate with qt .pro file approach as it is flakey
50 | # about what is made available.
51 | pylupdate4 -noobsolete ${PYTHON_FILES} -ts i18n/${LOCALE}.ts
52 | done
53 | else
54 | echo "No need to edit any translation files (.ts) because no python files"
55 | echo "has been updated since the last update translation. "
56 | fi
57 |
--------------------------------------------------------------------------------
/test/__init__.py:
--------------------------------------------------------------------------------
1 | # import qgis libs so that ve set the correct sip api version
2 | import qgis # pylint: disable=W0611 # NOQA
--------------------------------------------------------------------------------
/test/data/after.tif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dymaxionlabs/massive-change-detection/fce3b7aa1caa009931e2a5f0bb1a76976ce7cc72/test/data/after.tif
--------------------------------------------------------------------------------
/test/data/before.tif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dymaxionlabs/massive-change-detection/fce3b7aa1caa009931e2a5f0bb1a76976ce7cc72/test/data/before.tif
--------------------------------------------------------------------------------
/test/data/lots.cpg:
--------------------------------------------------------------------------------
1 | UTF-8
--------------------------------------------------------------------------------
/test/data/lots.dbf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dymaxionlabs/massive-change-detection/fce3b7aa1caa009931e2a5f0bb1a76976ce7cc72/test/data/lots.dbf
--------------------------------------------------------------------------------
/test/data/lots.prj:
--------------------------------------------------------------------------------
1 | PROJCS["WGS_1984_UTM_Zone_21S",GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",-57],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",10000000],UNIT["Meter",1]]
--------------------------------------------------------------------------------
/test/data/lots.qpj:
--------------------------------------------------------------------------------
1 | PROJCS["WGS 84 / UTM zone 21S",GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",-57],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",10000000],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AXIS["Easting",EAST],AXIS["Northing",NORTH],AUTHORITY["EPSG","32721"]]
2 |
--------------------------------------------------------------------------------
/test/data/lots.shp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dymaxionlabs/massive-change-detection/fce3b7aa1caa009931e2a5f0bb1a76976ce7cc72/test/data/lots.shp
--------------------------------------------------------------------------------
/test/data/lots.shx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dymaxionlabs/massive-change-detection/fce3b7aa1caa009931e2a5f0bb1a76976ce7cc72/test/data/lots.shx
--------------------------------------------------------------------------------
/test/qgis_interface.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | """QGIS plugin implementation.
3 |
4 | .. note:: This program is free software; you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation; either version 2 of the License, or
7 | (at your option) any later version.
8 |
9 | .. note:: This source code was copied from the 'postgis viewer' application
10 | with original authors:
11 | Copyright (c) 2010 by Ivan Mincik, ivan.mincik@gista.sk
12 | Copyright (c) 2011 German Carrillo, geotux_tuxman@linuxmail.org
13 | Copyright (c) 2014 Tim Sutton, tim@linfiniti.com
14 |
15 | """
16 |
17 | __author__ = 'tim@linfiniti.com'
18 | __revision__ = '$Format:%H$'
19 | __date__ = '10/01/2011'
20 | __copyright__ = (
21 | 'Copyright (c) 2010 by Ivan Mincik, ivan.mincik@gista.sk and '
22 | 'Copyright (c) 2011 German Carrillo, geotux_tuxman@linuxmail.org'
23 | 'Copyright (c) 2014 Tim Sutton, tim@linfiniti.com'
24 | )
25 |
26 | import logging
27 | from PyQt4.QtCore import QObject, pyqtSlot, pyqtSignal
28 | from qgis.core import QgsMapLayerRegistry
29 | from qgis.gui import QgsMapCanvasLayer
30 | LOGGER = logging.getLogger('QGIS')
31 |
32 |
33 | #noinspection PyMethodMayBeStatic,PyPep8Naming
34 | class QgisInterface(QObject):
35 | """Class to expose QGIS objects and functions to plugins.
36 |
37 | This class is here for enabling us to run unit tests only,
38 | so most methods are simply stubs.
39 | """
40 | currentLayerChanged = pyqtSignal(QgsMapCanvasLayer)
41 |
42 | def __init__(self, canvas):
43 | """Constructor
44 | :param canvas:
45 | """
46 | QObject.__init__(self)
47 | self.canvas = canvas
48 | # Set up slots so we can mimic the behaviour of QGIS when layers
49 | # are added.
50 | LOGGER.debug('Initialising canvas...')
51 | # noinspection PyArgumentList
52 | QgsMapLayerRegistry.instance().layersAdded.connect(self.addLayers)
53 | # noinspection PyArgumentList
54 | QgsMapLayerRegistry.instance().layerWasAdded.connect(self.addLayer)
55 | # noinspection PyArgumentList
56 | QgsMapLayerRegistry.instance().removeAll.connect(self.removeAllLayers)
57 |
58 | # For processing module
59 | self.destCrs = None
60 |
61 | @pyqtSlot('QStringList')
62 | def addLayers(self, layers):
63 | """Handle layers being added to the registry so they show up in canvas.
64 |
65 | :param layers: list list of map layers that were added
66 |
67 | .. note:: The QgsInterface api does not include this method,
68 | it is added here as a helper to facilitate testing.
69 | """
70 | #LOGGER.debug('addLayers called on qgis_interface')
71 | #LOGGER.debug('Number of layers being added: %s' % len(layers))
72 | #LOGGER.debug('Layer Count Before: %s' % len(self.canvas.layers()))
73 | current_layers = self.canvas.layers()
74 | final_layers = []
75 | for layer in current_layers:
76 | final_layers.append(QgsMapCanvasLayer(layer))
77 | for layer in layers:
78 | final_layers.append(QgsMapCanvasLayer(layer))
79 |
80 | self.canvas.setLayerSet(final_layers)
81 | #LOGGER.debug('Layer Count After: %s' % len(self.canvas.layers()))
82 |
83 | @pyqtSlot('QgsMapLayer')
84 | def addLayer(self, layer):
85 | """Handle a layer being added to the registry so it shows up in canvas.
86 |
87 | :param layer: list list of map layers that were added
88 |
89 | .. note: The QgsInterface api does not include this method, it is added
90 | here as a helper to facilitate testing.
91 |
92 | .. note: The addLayer method was deprecated in QGIS 1.8 so you should
93 | not need this method much.
94 | """
95 | pass
96 |
97 | @pyqtSlot()
98 | def removeAllLayers(self):
99 | """Remove layers from the canvas before they get deleted."""
100 | self.canvas.setLayerSet([])
101 |
102 | def newProject(self):
103 | """Create new project."""
104 | # noinspection PyArgumentList
105 | QgsMapLayerRegistry.instance().removeAllMapLayers()
106 |
107 | # ---------------- API Mock for QgsInterface follows -------------------
108 |
109 | def zoomFull(self):
110 | """Zoom to the map full extent."""
111 | pass
112 |
113 | def zoomToPrevious(self):
114 | """Zoom to previous view extent."""
115 | pass
116 |
117 | def zoomToNext(self):
118 | """Zoom to next view extent."""
119 | pass
120 |
121 | def zoomToActiveLayer(self):
122 | """Zoom to extent of active layer."""
123 | pass
124 |
125 | def addVectorLayer(self, path, base_name, provider_key):
126 | """Add a vector layer.
127 |
128 | :param path: Path to layer.
129 | :type path: str
130 |
131 | :param base_name: Base name for layer.
132 | :type base_name: str
133 |
134 | :param provider_key: Provider key e.g. 'ogr'
135 | :type provider_key: str
136 | """
137 | pass
138 |
139 | def addRasterLayer(self, path, base_name):
140 | """Add a raster layer given a raster layer file name
141 |
142 | :param path: Path to layer.
143 | :type path: str
144 |
145 | :param base_name: Base name for layer.
146 | :type base_name: str
147 | """
148 | pass
149 |
150 | def activeLayer(self):
151 | """Get pointer to the active layer (layer selected in the legend)."""
152 | # noinspection PyArgumentList
153 | layers = QgsMapLayerRegistry.instance().mapLayers()
154 | for item in layers:
155 | return layers[item]
156 |
157 | def addToolBarIcon(self, action):
158 | """Add an icon to the plugins toolbar.
159 |
160 | :param action: Action to add to the toolbar.
161 | :type action: QAction
162 | """
163 | pass
164 |
165 | def removeToolBarIcon(self, action):
166 | """Remove an action (icon) from the plugin toolbar.
167 |
168 | :param action: Action to add to the toolbar.
169 | :type action: QAction
170 | """
171 | pass
172 |
173 | def addToolBar(self, name):
174 | """Add toolbar with specified name.
175 |
176 | :param name: Name for the toolbar.
177 | :type name: str
178 | """
179 | pass
180 |
181 | def mapCanvas(self):
182 | """Return a pointer to the map canvas."""
183 | return self.canvas
184 |
185 | def mainWindow(self):
186 | """Return a pointer to the main window.
187 |
188 | In case of QGIS it returns an instance of QgisApp.
189 | """
190 | pass
191 |
192 | def addDockWidget(self, area, dock_widget):
193 | """Add a dock widget to the main window.
194 |
195 | :param area: Where in the ui the dock should be placed.
196 | :type area:
197 |
198 | :param dock_widget: A dock widget to add to the UI.
199 | :type dock_widget: QDockWidget
200 | """
201 | pass
202 |
203 | def legendInterface(self):
204 | """Get the legend."""
205 | return self.canvas
206 |
--------------------------------------------------------------------------------
/test/tenbytenraster.asc:
--------------------------------------------------------------------------------
1 | NCOLS 10
2 | NROWS 10
3 | XLLCENTER 1535380.000000
4 | YLLCENTER 5083260.000000
5 | DX 10
6 | DY 10
7 | NODATA_VALUE -9999
8 | 0 1 2 3 4 5 6 7 8 9
9 | 0 1 2 3 4 5 6 7 8 9
10 | 0 1 2 3 4 5 6 7 8 9
11 | 0 1 2 3 4 5 6 7 8 9
12 | 0 1 2 3 4 5 6 7 8 9
13 | 0 1 2 3 4 5 6 7 8 9
14 | 0 1 2 3 4 5 6 7 8 9
15 | 0 1 2 3 4 5 6 7 8 9
16 | 0 1 2 3 4 5 6 7 8 9
17 | 0 1 2 3 4 5 6 7 8 9
18 | CRS
19 | NOTES
20 |
--------------------------------------------------------------------------------
/test/tenbytenraster.asc.aux.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Point
4 |
5 |
6 |
7 | 9
8 | 4.5
9 | 0
10 | 2.872281323269
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/test/tenbytenraster.keywords:
--------------------------------------------------------------------------------
1 | title: Tenbytenraster
2 |
--------------------------------------------------------------------------------
/test/tenbytenraster.lic:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Tim Sutton, Linfiniti Consulting CC
5 |
6 |
7 |
8 | tenbytenraster.asc
9 | 2700044251
10 | Yes
11 | Tim Sutton
12 | Tim Sutton (QGIS Source Tree)
13 | Tim Sutton
14 | This data is publicly available from QGIS Source Tree. The original
15 | file was created and contributed to QGIS by Tim Sutton.
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/test/tenbytenraster.prj:
--------------------------------------------------------------------------------
1 | GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]]
--------------------------------------------------------------------------------
/test/tenbytenraster.qml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | 0
26 |
27 |
--------------------------------------------------------------------------------
/test/test_generate_vector_algorithm.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | """GenerateVectorAlgorithm Test
3 |
4 | .. note:: This program is free software; you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation; either version 2 of the License, or
7 | (at your option) any later version.
8 |
9 | """
10 |
11 | __author__ = 'Damián Silvani'
12 | __date__ = '2018-07-25'
13 | __copyright__ = '(C) 2018 by Dymaxion Labs'
14 |
15 | import unittest
16 |
17 | class GenerateVectorAlgorithmTest(unittest.TestCase):
18 | """Test vector generation algorithm work."""
19 |
20 | def setUp(self):
21 | """Runs before each test."""
22 | pass
23 |
24 | def tearDown(self):
25 | """Runs after each test."""
26 | pass
27 |
28 | def test_true(self):
29 | """Test that it works."""
30 | self.assertTrue(True)
31 |
32 |
33 | if __name__ == "__main__":
34 | suite = unittest.makeSuite(GenerateVectorAlgorithmTest)
35 | runner = unittest.TextTestRunner(verbosity=2)
36 | runner.run(suite)
37 |
--------------------------------------------------------------------------------
/test/test_init.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | """Tests QGIS plugin init."""
3 |
4 | __author__ = 'Tim Sutton '
5 | __revision__ = '$Format:%H$'
6 | __date__ = '17/10/2010'
7 | __license__ = "GPL"
8 | __copyright__ = 'Copyright 2012, Australia Indonesia Facility for '
9 | __copyright__ += 'Disaster Reduction'
10 |
11 | import os
12 | import unittest
13 | import logging
14 | import ConfigParser
15 |
16 | LOGGER = logging.getLogger('QGIS')
17 |
18 |
19 | class TestInit(unittest.TestCase):
20 | """Test that the plugin init is usable for QGIS.
21 |
22 | Based heavily on the validator class by Alessandro
23 | Passoti available here:
24 |
25 | http://github.com/qgis/qgis-django/blob/master/qgis-app/
26 | plugins/validator.py
27 |
28 | """
29 |
30 | def test_read_init(self):
31 | """Test that the plugin __init__ will validate on plugins.qgis.org."""
32 |
33 | # You should update this list according to the latest in
34 | # https://github.com/qgis/qgis-django/blob/master/qgis-app/
35 | # plugins/validator.py
36 |
37 | required_metadata = [
38 | 'name',
39 | 'description',
40 | 'version',
41 | 'qgisMinimumVersion',
42 | 'email',
43 | 'author']
44 |
45 | file_path = os.path.abspath(os.path.join(
46 | os.path.dirname(__file__), os.pardir,
47 | 'metadata.txt'))
48 | LOGGER.info(file_path)
49 | metadata = []
50 | parser = ConfigParser.ConfigParser()
51 | parser.optionxform = str
52 | parser.read(file_path)
53 | message = 'Cannot find a section named "general" in %s' % file_path
54 | assert parser.has_section('general'), message
55 | metadata.extend(parser.items('general'))
56 |
57 | for expectation in required_metadata:
58 | message = ('Cannot find metadata "%s" in metadata source (%s).' % (
59 | expectation, file_path))
60 |
61 | self.assertIn(expectation, dict(metadata), message)
62 |
63 | if __name__ == '__main__':
64 | unittest.main()
65 |
--------------------------------------------------------------------------------
/test/test_multiband_difference_algorithm.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | """MultibandDifferenceAlgorithm Test
3 |
4 | .. note:: This program is free software; you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation; either version 2 of the License, or
7 | (at your option) any later version.
8 |
9 | """
10 |
11 | __author__ = 'Damián Silvani'
12 | __date__ = '2018-07-25'
13 | __copyright__ = '(C) 2018 by Dymaxion Labs'
14 |
15 | import unittest
16 | import os
17 |
18 | from PyQt4 import QtGui, QtTest
19 | from qgis.core import (
20 | QgsMapLayerRegistry,
21 | QgsRasterLayer)
22 | from qgis.gui import QgsMapCanvasLayer
23 |
24 | import sys
25 | print("*** {}".format(sys.path))
26 |
27 | from utilities import get_qgis_app
28 | APP, CANVAS, IFACE, _ = get_qgis_app()
29 |
30 |
31 | class MultibandDifferenceAlgorithmTest(unittest.TestCase):
32 | """Test multiband difference algorithm."""
33 |
34 | def _setUp(self):
35 | """Runs before each test."""
36 |
37 | # create a map canvas widget
38 | CANVAS.setCanvasColor(QtGui.QColor('white'))
39 | CANVAS.enableAntiAliasing(True)
40 |
41 | # load a shapefile
42 | self.beforePath = os.path.join('data', 'before.tif')
43 | beforeLayer = QgsRasterLayer(self.beforePath, 'before', 'ogr')
44 |
45 | # add the layer to the canvas and zoom to it
46 | QgsMapLayerRegistry.instance().addMapLayer(beforeLayer)
47 | CANVAS.setLayerSet([QgsMapCanvasLayer(beforeLayer)])
48 | CANVAS.setExtent(beforeLayer.extent())
49 |
50 | # import the plugin to be tested
51 | import massive_change_detection
52 | self.plugin = massive_change_detection.classFactory(IFACE)
53 | self.plugin.initGui()
54 | self.dlg = self.plugin.dlg
55 |
56 | def _test_populated(self):
57 | '''Are the combo boxes populated correctly?'''
58 | self.assertEqual(self.dlg.ui.comboBox_raster.currentText(), '')
59 | self.assertEqual(self.dlg.ui.comboBox_vector.currentText(), 'MasterMap')
60 | self.assertEqual(self.dlg.ui.comboBox_all1.currentText(), '')
61 | self.dlg.ui.comboBox_all1.setCurrentIndex(1)
62 | self.assertEqual(self.dlg.ui.comboBox_all1.currentText(), 'MasterMap')
63 |
64 | def _test_dlg_name(self):
65 | self.assertEqual(self.dlg.windowTitle(), 'Testing')
66 |
67 | def _test_click_widget(self):
68 | '''The OK button should close the dialog'''
69 | self.dlg.show()
70 | self.assertEqual(self.dlg.isVisible(), True)
71 | okWidget = self.dlg.ui.buttonBox.button(self.dlg.ui.buttonBox.Ok)
72 | QtTest.QTest.mouseClick(okWidget, Qt.LeftButton)
73 | self.assertEqual(self.dlg.isVisible(), False)
74 |
75 | def _tearDown(self):
76 | """Runs after each test."""
77 | self.plugin.unload()
78 | del(self.plugin)
79 |
80 |
81 | if __name__ == "__main__":
82 | suite = unittest.makeSuite(MultibandDifferenceAlgorithmTest)
83 | runner = unittest.TextTestRunner(verbosity=2)
84 | runner.run(suite)
85 |
--------------------------------------------------------------------------------
/test/test_qgis_environment.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | """Tests for QGIS functionality.
3 |
4 | .. note:: This program is free software; you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation; either version 2 of the License, or
7 | (at your option) any later version.
8 |
9 | """
10 | __author__ = 'tim@linfiniti.com'
11 | __date__ = '20/01/2011'
12 | __copyright__ = ('Copyright 2012, Australia Indonesia Facility for '
13 | 'Disaster Reduction')
14 |
15 | import os
16 | import unittest
17 | from qgis.core import (
18 | QgsProviderRegistry,
19 | QgsCoordinateReferenceSystem,
20 | QgsRasterLayer)
21 |
22 | from utilities import get_qgis_app
23 | QGIS_APP = get_qgis_app()
24 |
25 |
26 | class QGISTest(unittest.TestCase):
27 | """Test the QGIS Environment"""
28 |
29 | def test_qgis_environment(self):
30 | """QGIS environment has the expected providers"""
31 |
32 | r = QgsProviderRegistry.instance()
33 | self.assertIn('gdal', r.providerList())
34 | self.assertIn('ogr', r.providerList())
35 | self.assertIn('postgres', r.providerList())
36 |
37 | def test_projection(self):
38 | """Test that QGIS properly parses a wkt string.
39 | """
40 | crs = QgsCoordinateReferenceSystem()
41 | wkt = (
42 | 'GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",'
43 | 'SPHEROID["WGS_1984",6378137.0,298.257223563]],'
44 | 'PRIMEM["Greenwich",0.0],UNIT["Degree",'
45 | '0.0174532925199433]]')
46 | crs.createFromWkt(wkt)
47 | auth_id = crs.authid()
48 | expected_auth_id = 'EPSG:4326'
49 | self.assertEqual(auth_id, expected_auth_id)
50 |
51 | # now test for a loaded layer
52 | path = os.path.join(os.path.dirname(__file__), 'tenbytenraster.asc')
53 | title = 'TestRaster'
54 | layer = QgsRasterLayer(path, title)
55 | auth_id = layer.crs().authid()
56 | self.assertEqual(auth_id, expected_auth_id)
57 |
58 | if __name__ == '__main__':
59 | unittest.main()
60 |
--------------------------------------------------------------------------------
/test/test_translations.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | """Safe Translations Test.
3 |
4 | .. note:: This program is free software; you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation; either version 2 of the License, or
7 | (at your option) any later version.
8 |
9 | """
10 | from utilities import get_qgis_app
11 |
12 | __author__ = 'ismailsunni@yahoo.co.id'
13 | __date__ = '12/10/2011'
14 | __copyright__ = ('Copyright 2012, Australia Indonesia Facility for '
15 | 'Disaster Reduction')
16 | import unittest
17 | import os
18 |
19 | from PyQt4.QtCore import QCoreApplication, QTranslator
20 |
21 | QGIS_APP = get_qgis_app()
22 |
23 |
24 | class SafeTranslationsTest(unittest.TestCase):
25 | """Test translations work."""
26 |
27 | def setUp(self):
28 | """Runs before each test."""
29 | if 'LANG' in os.environ.iterkeys():
30 | os.environ.__delitem__('LANG')
31 |
32 | def tearDown(self):
33 | """Runs after each test."""
34 | if 'LANG' in os.environ.iterkeys():
35 | os.environ.__delitem__('LANG')
36 |
37 | def test_qgis_translations(self):
38 | """Test that translations work."""
39 | parent_path = os.path.join(__file__, os.path.pardir, os.path.pardir)
40 | dir_path = os.path.abspath(parent_path)
41 | file_path = os.path.join(
42 | dir_path, 'i18n', 'es.qm')
43 | translator = QTranslator()
44 | translator.load(file_path)
45 | QCoreApplication.installTranslator(translator)
46 |
47 | expected_message = u'Buenos días'
48 | real_message = QCoreApplication.translate("@default", 'Good morning')
49 | self.assertEqual(real_message, expected_message)
50 |
51 |
52 | if __name__ == "__main__":
53 | suite = unittest.makeSuite(SafeTranslationsTest)
54 | runner = unittest.TextTestRunner(verbosity=2)
55 | runner.run(suite)
56 |
--------------------------------------------------------------------------------
/test/utilities.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | """Common functionality used by regression tests."""
3 |
4 | import sys
5 | import logging
6 |
7 |
8 | LOGGER = logging.getLogger('QGIS')
9 | QGIS_APP = None # Static variable used to hold hand to running QGIS app
10 | CANVAS = None
11 | PARENT = None
12 | IFACE = None
13 |
14 |
15 | def get_qgis_app():
16 | """ Start one QGIS application to test against.
17 |
18 | :returns: Handle to QGIS app, canvas, iface and parent. If there are any
19 | errors the tuple members will be returned as None.
20 | :rtype: (QgsApplication, CANVAS, IFACE, PARENT)
21 |
22 | If QGIS is already running the handle to that app will be returned.
23 | """
24 |
25 | try:
26 | from PyQt4 import QtGui, QtCore
27 | from qgis.core import QgsApplication
28 | from qgis.gui import QgsMapCanvas
29 | from qgis_interface import QgisInterface
30 | except ImportError:
31 | return None, None, None, None
32 |
33 | global QGIS_APP # pylint: disable=W0603
34 |
35 | if QGIS_APP is None:
36 | gui_flag = True # All test will run qgis in gui mode
37 | #noinspection PyPep8Naming
38 | QGIS_APP = QgsApplication(sys.argv, gui_flag)
39 | # Make sure QGIS_PREFIX_PATH is set in your env if needed!
40 | QGIS_APP.initQgis()
41 | s = QGIS_APP.showSettings()
42 | LOGGER.debug(s)
43 |
44 | global PARENT # pylint: disable=W0603
45 | if PARENT is None:
46 | #noinspection PyPep8Naming
47 | PARENT = QtGui.QWidget()
48 |
49 | global CANVAS # pylint: disable=W0603
50 | if CANVAS is None:
51 | #noinspection PyPep8Naming
52 | CANVAS = QgsMapCanvas(PARENT)
53 | CANVAS.resize(QtCore.QSize(400, 400))
54 |
55 | global IFACE # pylint: disable=W0603
56 | if IFACE is None:
57 | # QgisInterface is a stub implementation of the QGIS plugin interface
58 | #noinspection PyPep8Naming
59 | IFACE = QgisInterface(CANVAS)
60 |
61 | return QGIS_APP, CANVAS, IFACE, PARENT
62 |
--------------------------------------------------------------------------------