diff --git a/.gitignore b/.gitignore
index e01f0e10d..cd3199732 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,4 +6,6 @@
target/
/ingestion-tool/src/temp/
temp.txt
+temp.java
+temp.xml
/out/
\ No newline at end of file
diff --git a/CHANGES.md b/CHANGES.md
index 65bacf9ea..a895a379c 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,4 +1,36 @@
+### Updates from version 1.5.8 to 1.6.0
+* Migrated to SNAP 11, several refactorings and library updates
+* The VariableCache now uses an LRU cache with a listener to automatically free the oldest cached, no longer
+ needed variables and file handles when the maximum cache size is reached.
+* fix wrong corner point order of era5 interpolation in SatelliteFields.
+ Instead, the four era5 interpolation vertices are read directly from the variable
+* Remove reading of subset from entire era5 variable array and remove to read the entire era5 variable in VariableCache
+* Remove creation of rectangles for InterpolationContext and remove field era5Region in InterpolationContext
+* era5 post processing .. can now also handle satellite longitude data which not fits the range [-180 to 180].
+ In such cases (e.g. Windsat-Coriolis [0 to 360]), longitude data will be converted so that it fits into the
+ required range of [-180 to 180] to be able to create correct interpolation of era5 data.
+* era5 post processing .. In era5-post-processing-general-info.xml a fill value per
+ variable can be defined.
+* era5 post processing .. can now load generalized information from an optional
+ era5-post-processing-general-info.xml in the config directory.
+ A general info file can contain variable definition for satellite-fields.
+* Upgrade Mockito to version 4.11.0 and also include artifact mockito-inline for static mocking
+* era5 post processing .. setter and getter of variable names in class SatelliteFieldsConfiguration was
+ generalized. E.g. config.set_an_ml_q_name("abcde") was replaced by config.setVarName("an_ml_q", "abcde")
+* era5 post processing .. "length" attribute of tag " is no
+ longer optional.
+* era5 post processing .. The translation of variable name to variable name for file access has been made
+ switchable.
+* post processing .. added PostProcessingContext to method PostProcessingPlugin.createPostProcessing(...).
+ Now plugins can perform extended initializations. E.g. by loading extra files from config directory.
+* post processing .. the config directory has been added to the PostProcessingContext so that plugins can load
+ additional generalized information required for the plugin from this directory.
+
### Updates from version 1.5.7 to 1.5.8
+* added support for SMAP Salinity L2C data
+* added functionality to configure several global attributes before computing MMD files.
+* added support for NOAA TAO insitu data
+* added support for NOAA NDBC insitu data
* added support for Windsat Coriolis data
* added conda environment
* extended workflow to run without scheduler on plain console
diff --git a/CITATION.cff b/CITATION.cff
new file mode 100644
index 000000000..00205d227
--- /dev/null
+++ b/CITATION.cff
@@ -0,0 +1,37 @@
+cff-version: 1.2.0
+title: Multisensor Matchup System (MMS)
+message: >-
+ If you use this software, please cite it using the
+ metadata from this file.
+type: software
+authors:
+ - given-names: Thomas
+ family-names: Block
+ email: tom.block@brockmann-consult.de
+ affiliation: Brockmann Consult GmbH
+ - given-names: Sabine
+ family-names: Embacher
+ email: sabine.embacher@brockmann-consult.de
+ affiliation: Brockmann Consult GmbH
+identifiers:
+ - type: doi
+ value: 10.5281/zenodo.8116892
+repository-code: 'https://github.com/bcdev/MMS'
+url: 'https://www.brockmann-consult.de/'
+abstract: >-
+ The Multisensor matchup System (MMS) is a software suite
+ that allows to detect simulatneous measurements of remote
+ sensing (satellite) and in-situ instruments.
+
+
+ Its main purpose is to support the validation of satellite
+ based instruments by generating cross-sensor matches of
+ either instruments from different satellite platforms or
+ between satellite and ground-based measurements.
+keywords:
+ - matchup
+ - calibration
+ - validation
+ - earth observation
+ - satellite
+license: GPL-3.0
diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 000000000..94a9ed024
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ Copyright (C)
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+.
diff --git a/MMS_Manual_v_1_5.pdf b/MMS_Manual_v_1_5.pdf
deleted file mode 100644
index 8db1e15bf..000000000
Binary files a/MMS_Manual_v_1_5.pdf and /dev/null differ
diff --git a/MMS_Manual_v_1_6.pdf b/MMS_Manual_v_1_6.pdf
new file mode 100644
index 000000000..d99900cd8
Binary files /dev/null and b/MMS_Manual_v_1_6.pdf differ
diff --git a/build_and_install_on_cems.sh b/build_and_install_on_cems.sh
index 5ad00b845..3e71c50fe 100755
--- a/build_and_install_on_cems.sh
+++ b/build_and_install_on_cems.sh
@@ -6,5 +6,5 @@ mvn clean install package assembly:directory
# echo "clean up bin dir"
echo "copy build result to bin dir"
rm -rf /gws/nopw/j04/fiduceo/Software/mms/bin/*
-cp -a target/fiduceo-master-1.5.8-MMS/* /gws/nopw/j04/fiduceo/Software/mms/bin
+cp -a target/fiduceo-master-1.6.2-MMS/* /gws/nopw/j04/fiduceo/Software/mms/bin
diff --git a/build_and_install_on_cems_sst.sh b/build_and_install_on_cems_sst.sh
index bfb4e7b1c..94890f6b8 100755
--- a/build_and_install_on_cems_sst.sh
+++ b/build_and_install_on_cems_sst.sh
@@ -6,5 +6,5 @@ mvn clean install package assembly:directory
# echo "clean up bin dir"
echo "copy build result to bin dir"
rm -rf /gws/nopw/j04/esacci_sst/mms_new/bin/*
-cp -a target/fiduceo-master-1.5.8-MMS/* /gws/nopw/j04/esacci_sst/mms_new/bin
+cp -a target/fiduceo-master-1.6.2-MMS/* /gws/nopw/j04/esacci_sst/mms_new/bin
diff --git a/cems/pom.xml b/cems/pom.xml
index 82da1d8fa..887eee1ad 100644
--- a/cems/pom.xml
+++ b/cems/pom.xml
@@ -25,7 +25,7 @@
fiduceo-master
com.bc.fiduceo
- 1.5.8
+ 1.6.2
4.0.0
diff --git a/cems/src/main/bin/mms-env.sh b/cems/src/main/bin/mms-env.sh
index 88f5c6445..d2faa76a2 100755
--- a/cems/src/main/bin/mms-env.sh
+++ b/cems/src/main/bin/mms-env.sh
@@ -8,13 +8,14 @@
# project and user settings
# -------------------------
-export PROJECT=bc_fiduceo # only LSF
-export MMS_USER=tblock01 # only SLURM
+export PROJECT=bc_fiduceo # only LSF
+export MMS_USER=tblock01 # only SLURM
+export MMS_ACCOUNT=esacci_sst # only SLURM_2
export MMS_ENV_NAME=mms-env.sh
# Java and Python runtime definitions
# -----------------------------------
-export MMS_JAVA_EXEC='/gws/nopw/j04/esacci_sst/mms_new/software/jdk1.8.0_202/bin/java'
+export MMS_JAVA_EXEC='/gws/nopw/j04/esacci_sst/mms_new/software/openjdk-11.0.26'
export PM_EXE_DIR=/gws/nopw/j04/esacci_sst/mms_new/bin
export PM_PYTHON_EXEC='/gws/nopw/j04/esacci_sst/mms_new/software/conda_envs/sst-cci-mms/bin/python'
@@ -23,7 +24,8 @@ export PATH=${PM_EXE_DIR}:$PATH
# export scheduling engine
# ------------------------
-export SCHEDULER='SLURM'
+export SCHEDULER='SLURM_2'
+# export SCHEDULER='SLURM'
# export SCHEDULER='LSF'
# ensure that processes exit
@@ -35,6 +37,9 @@ fi
export PM_LOG_DIR=${WORKING_DIR}/log
+# --------------------------------------
+# --- LSF ------------------------------
+# --------------------------------------
if [ "$SCHEDULER" == "LSF" ]; then
submit_job() {
@@ -61,6 +66,9 @@ if [ "$SCHEDULER" == "LSF" ]; then
fi
}
+# --------------------------------------
+# --- SLURM ----------------------------
+# --------------------------------------
elif [ "$SCHEDULER" == "SLURM" ]; then
submit_job() {
@@ -87,6 +95,35 @@ elif [ "$SCHEDULER" == "SLURM" ]; then
fi
}
+# --------------------------------------
+# --- SLURM_2 --------------------------
+# --------------------------------------
+elif [ "$SCHEDULER" == "SLURM_2" ]; then
+
+submit_job() {
+ jobname=$1
+ command=$2
+
+ bsubmit="sbatch -A ${MMS_ACCOUNT} --mem=20000 -p standard -q short -n 1 -t 04:00:00 -o ${PM_LOG_DIR}/${jobname}.out -e ${PM_LOG_DIR}/${jobname}.err --job-name ${jobname} ${PM_EXE_DIR}/${command} ${@:3}"
+
+ rm -f ${PM_LOG_DIR}/${jobname}.out
+ rm -f ${PM_LOG_DIR}/${jobname}.err
+
+ # line contains the console output of the bsub command
+ line=`${bsubmit}`
+
+ if echo ${line} | grep -qF 'Submitted batch job'
+ then
+ # extract the job_id from the bsub message, concatenate '_' and jobname to form an identifier
+ # and dump to std_out to be fetched by pmonitor
+ job_id=`echo ${line} | awk '{ print substr($4,0,length($4)) }'`
+ echo "${job_id}_${jobname}"
+ else
+ echo "`date -u +%Y%m%d-%H%M%S` - submit of ${jobname} failed: ${line}"
+ exit 1
+ fi
+ }
+
else
echo "Invalid scheduler"
exit 1
diff --git a/cems/src/main/bin/post_processing_run.sh b/cems/src/main/bin/post_processing_run.sh
index 821651933..8a536bc0f 100644
--- a/cems/src/main/bin/post_processing_run.sh
+++ b/cems/src/main/bin/post_processing_run.sh
@@ -8,6 +8,6 @@ end_date=$3
job_config=$4
config_dir=$5
-echo "`date -u +%Y%m%d-%H%M%S` ingestion ${start_date} - ${end_date} ..."
+echo "`date -u +%Y%m%d-%H%M%S` post_processing ${start_date} - ${end_date} ..."
${PM_EXE_DIR}/post-processing-tool.sh -i ${input_dir} -start ${start_date} -end ${end_date} -j ${job_config} -c ${config_dir}
diff --git a/cems/src/main/python/jasmin/jasmin_job_monitor.py b/cems/src/main/python/jasmin/jasmin_job_monitor.py
index 12675208e..2d3f90230 100644
--- a/cems/src/main/python/jasmin/jasmin_job_monitor.py
+++ b/cems/src/main/python/jasmin/jasmin_job_monitor.py
@@ -48,8 +48,10 @@ def _create_scheduler_interface():
return LSFInterface()
elif scheduler_name == "SLURM":
return SLURMInterface()
+ elif scheduler_name == "SLURM_2":
+ return SLURMInterface()
else:
- raise ValueError("Environment variable 'SCHEDULER' invalid")
+ raise ValueError("Environment variable 'SCHEDULER' invalid" + scheduler_name)
else:
raise ValueError("Environment variable 'SCHEDULER' is not set")
@@ -188,10 +190,13 @@ def resolve_status_from_log(self, check_log_dict):
class SLURMInterface:
user_name = None
+ scheduler_name = None
def __init__(self):
if "MMS_USER" in os.environ:
self.user_name = os.environ["MMS_USER"]
+ if "SCHEDULER" in os.environ:
+ self.scheduler_name = os.environ["SCHEDULER"]
else:
raise RuntimeError("Missing environment variable 'MMS_USER'")
@@ -224,7 +229,12 @@ def _extract_id_and_status(self, line):
if len(tokens) < 7:
raise ValueError("unable to handle 'squeue' result: " + line)
- status_code = self._status_to_enum(tokens[4])
+ if self.scheduler_name == "SLURM_2":
+ status_token = tokens[7]
+ else:
+ status_token = tokens[4]
+
+ status_code = self._status_to_enum(status_token)
return {tokens[0]: status_code}
@staticmethod
diff --git a/cems/src/main/python/jasmin/jasmin_job_monitor_test.py b/cems/src/main/python/jasmin/jasmin_job_monitor_test.py
index adcc7b202..4c4f2b7bb 100644
--- a/cems/src/main/python/jasmin/jasmin_job_monitor_test.py
+++ b/cems/src/main/python/jasmin/jasmin_job_monitor_test.py
@@ -2,8 +2,8 @@
import io
import unittest
-from jasmin.jasmin_job_monitor import JasminJobMonitor, LSFInterface, SLURMInterface
-from jasmin.status_codes import StatusCodes
+from jasmin_job_monitor import JasminJobMonitor, LSFInterface, SLURMInterface
+from status_codes import StatusCodes
class JasminJobMonitorTest(unittest.TestCase):
@@ -62,6 +62,25 @@ def test_parse_job_status_SLURM(self):
finally:
del os.environ["MMS_USER"]
+ def test_parse_job_status_SLURM_2(self):
+ output = " JOBID PARTIT QOS NAME USER NODE CPUS ST TIME TIME_LEFT PRIORITY NODELIST(REASON)\n" \
+ "8466477 standa short ingest-slstr-s3a-uor tblock01 1 1 R 0:37 3:59:23 77911 (NonZeroExitCode)\n" \
+ "8466478 standa short ingest-slstr-s3a-uor tblock01 1 1 F 0:37 3:59:23 77911 (NonZeroExitCode)"
+
+
+ try:
+ os.environ["MMS_USER"] = "HarryPotter"
+ os.environ["SCHEDULER"] = "SLURM_2"
+
+ scheduler = SLURMInterface()
+ job_status_dict = scheduler.parse_jobs_call(output)
+ self.assertEqual(2, len(job_status_dict))
+ self.assertEqual(StatusCodes.RUNNING, job_status_dict["8466477"])
+ self.assertEqual(StatusCodes.FAILED, job_status_dict["8466478"])
+ finally:
+ del os.environ["MMS_USER"]
+ del os.environ["SCHEDULER"]
+
def test_status_to_enum_LSF(self):
scheduler = LSFInterface()
diff --git a/cems/src/main/python/workflow.py b/cems/src/main/python/workflow.py
index 3552ab219..09ca9571d 100644
--- a/cems/src/main/python/workflow.py
+++ b/cems/src/main/python/workflow.py
@@ -408,16 +408,23 @@ def run_matchup(self, hosts, num_parallel_tasks, simulation=False, logdir='trace
monitor.wait_for_completion()
- def run_post_processing(self, hosts, num_parallel_tasks, simulation=False, logdir='trace'):
+ def run_post_processing(self, hosts, num_parallel_tasks, simulation=False, logdir='trace', synchronous=False):
"""
-
:param hosts: list
:param num_parallel_tasks: int
:param simulation: bool
:param logdir: str
+ :param synchronous: bool:
+ decides whether post_processing_run.sh (True) or post_processing_start.sh (False) is called.
:return:
"""
- monitor = self._get_monitor(hosts, [('post_processing_start.sh', num_parallel_tasks)], logdir, simulation)
+
+ if synchronous:
+ runs_script = 'post_processing_run.sh'
+ else:
+ runs_script = 'post_processing_start.sh'
+
+ monitor = self._get_monitor(hosts, [(runs_script, num_parallel_tasks)], logdir, simulation, synchronous)
production_period = self.get_production_period()
date = production_period.get_start_date()
while date < production_period.get_end_date():
@@ -430,7 +437,7 @@ def run_post_processing(self, hosts, num_parallel_tasks, simulation=False, logdi
pre_condition = 'mmd-' + start_string + '-' + end_string
post_condition = 'post-processing-' + start_string + '-' + end_string + '-' + self.usecase_config
- job = Job(job_name, 'post_processing_start.sh', [pre_condition], [post_condition],
+ job = Job(job_name, runs_script, [pre_condition], [post_condition],
[self.input_dir, start_string, end_string, self.usecase_config, self._get_config_dir()])
monitor.execute(job)
diff --git a/core/pom.xml b/core/pom.xml
index 09322a5be..4ff871bb5 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -5,7 +5,7 @@
fiduceo-master
com.bc.fiduceo
- 1.5.8
+ 1.6.2
4.0.0
@@ -39,13 +39,13 @@
- org.esa.s3tbx
- s3tbx-avhrr-reader
+ eu.esa.opt
+ opttbx-avhrr-reader
- org.esa.s3tbx
- s3tbx-sentinel3-reader
+ eu.esa.opt
+ opttbx-sentinel3-reader
@@ -89,14 +89,10 @@
netcdfAll
-
-
-
-
-
-
-
-
+
+ org.jdom
+ jdom2
+
junit
@@ -110,7 +106,7 @@
org.hamcrest
- hamcrest-all
+ hamcrest
test
@@ -118,6 +114,12 @@
jimfs
test
+
+ org.apache.commons
+ commons-text
+ 1.10.0
+ test
+
diff --git a/core/src/main/bin/mmd_qc_tool.bat b/core/src/main/bin/mmd_qc_tool.bat
new file mode 100644
index 000000000..e51e105c7
--- /dev/null
+++ b/core/src/main/bin/mmd_qc_tool.bat
@@ -0,0 +1,30 @@
+@ECHO OFF
+
+:: -======================-
+:: User configurable values
+:: -======================-
+
+SET INSTALLDIR=%~dp0%
+
+::------------------------------------------------------------------
+:: You can adjust the Java minimum and maximum heap space here.
+:: Just change the Xms and Xmx options. Space is given in megabyte.
+:: '-Xms64M' sets the minimum heap space to 64 megabytes
+:: '-Xmx512M' sets the maximum heap space to 512 megabytes
+::------------------------------------------------------------------
+SET JAVA_OPTS=-Xms64M -Xmx8192M
+
+
+:: -======================-
+:: Other values
+:: -======================-
+
+SET JAVAEXE=java.exe
+SET LIBDIR=%INSTALLDIR%\lib
+SET OLD_CLASSPATH=%CLASSPATH%
+
+SET CLASSPATH=%LIBDIR%\*;%LIBDIR%
+
+CALL "%JAVAEXE%" %JAVA_OPTS% -classpath "%CLASSPATH%" com.bc.fiduceo.qc.MmdQCToolMain %*
+
+SET CLASSPATH=%OLD_CLASSPATH%
diff --git a/core/src/main/bin/mmd_qc_tool.sh b/core/src/main/bin/mmd_qc_tool.sh
new file mode 100644
index 000000000..6cb78d379
--- /dev/null
+++ b/core/src/main/bin/mmd_qc_tool.sh
@@ -0,0 +1,37 @@
+#!/bin/bash
+
+# -======================-
+# User configurable values
+# -======================-
+
+export INSTALLDIR="$(dirname $0)"
+
+#------------------------------------------------------------------
+# You can adjust the Java minimum and maximum heap space here.
+# Just change the Xms and Xmx options. Space is given in megabyte.
+# '-Xms64M' sets the minimum heap space to 64 megabytes
+# '-Xmx512M' sets the maximum heap space to 512 megabytes
+#------------------------------------------------------------------
+export JAVA_OPTS="-Xmx8192M"
+
+# check if we`re running on CEMS, if so take the java executable externally defined
+if [ -z "${MMS_JAVA_EXEC}" ]; then
+ # not set, use what we have on the system
+ export JAVA_EXE="$(which java)"
+else
+ # yes, we seem to be on CEMS
+ export JAVA_EXE="$MMS_JAVA_EXEC"
+fi
+
+
+# -======================-
+# Other values
+# -======================-
+
+export LIBDIR="$INSTALLDIR"/lib
+export OLD_CLASSPATH="$CLASSPATH"
+CLASSPATH="$LIBDIR/*:$LIBDIR"
+
+"$JAVA_EXE" "$JAVA_OPTS" -classpath "$CLASSPATH" com.bc.fiduceo.qc.MmdQCToolMain "$@"
+
+export CLASSPATH="$OLD_CLASSPATH"
diff --git a/core/src/main/java/com/bc/fiduceo/archive/ArchiveConfig.java b/core/src/main/java/com/bc/fiduceo/archive/ArchiveConfig.java
index 454ba90bf..dba7742fc 100644
--- a/core/src/main/java/com/bc/fiduceo/archive/ArchiveConfig.java
+++ b/core/src/main/java/com/bc/fiduceo/archive/ArchiveConfig.java
@@ -42,11 +42,11 @@
import com.bc.fiduceo.util.JDomUtils;
-import org.jdom.Attribute;
-import org.jdom.Document;
-import org.jdom.Element;
-import org.jdom.JDOMException;
-import org.jdom.input.SAXBuilder;
+import org.jdom2.Attribute;
+import org.jdom2.Document;
+import org.jdom2.Element;
+import org.jdom2.JDOMException;
+import org.jdom2.input.SAXBuilder;
import java.io.ByteArrayInputStream;
import java.io.IOException;
diff --git a/core/src/main/java/com/bc/fiduceo/core/GeoRect.java b/core/src/main/java/com/bc/fiduceo/core/GeoRect.java
deleted file mode 100644
index 9f032aea2..000000000
--- a/core/src/main/java/com/bc/fiduceo/core/GeoRect.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package com.bc.fiduceo.core;
-
-public class GeoRect {
-
- private final float lonMin;
- private final float lonMax;
- private final float latMin;
- private final float latMax;
-
- public GeoRect(float lonMin, float lonMax, float latMin, float latMax) {
- this.lonMin = lonMin;
- this.lonMax = lonMax;
- this.latMin = latMin;
- this.latMax = latMax;
- }
-
- public float getLonMin() {
- return lonMin;
- }
-
- public float getLonMax() {
- return lonMax;
- }
-
- public float getLatMin() {
- return latMin;
- }
-
- public float getLatMax() {
- return latMax;
- }
-}
diff --git a/core/src/main/java/com/bc/fiduceo/core/IntRange.java b/core/src/main/java/com/bc/fiduceo/core/IntRange.java
new file mode 100644
index 000000000..8b15dbbae
--- /dev/null
+++ b/core/src/main/java/com/bc/fiduceo/core/IntRange.java
@@ -0,0 +1,40 @@
+package com.bc.fiduceo.core;
+
+public class IntRange {
+
+ private int min;
+ private int max;
+
+ public IntRange() {
+ this(Integer.MAX_VALUE, Integer.MIN_VALUE);
+ }
+
+ public IntRange(int min, int max) {
+ this.min = min;
+ this.max = max;
+ }
+
+ public int getMin() {
+ return min;
+ }
+
+ public int getMax() {
+ return max;
+ }
+
+ public void setMin(int min) {
+ this.min = min;
+ }
+
+ public void setMax(int max) {
+ this.max = max;
+ }
+
+ public int getLength() {
+ return max - min + 1;
+ }
+
+ public boolean contains(int value) {
+ return value >= min && value <= max;
+ }
+}
diff --git a/core/src/main/java/com/bc/fiduceo/core/SystemConfig.java b/core/src/main/java/com/bc/fiduceo/core/SystemConfig.java
index 01a10173e..5020e9709 100644
--- a/core/src/main/java/com/bc/fiduceo/core/SystemConfig.java
+++ b/core/src/main/java/com/bc/fiduceo/core/SystemConfig.java
@@ -22,10 +22,10 @@
import com.bc.fiduceo.archive.ArchiveConfig;
import com.bc.fiduceo.util.JDomUtils;
-import org.jdom.Document;
-import org.jdom.Element;
-import org.jdom.JDOMException;
-import org.jdom.input.SAXBuilder;
+import org.jdom2.Document;
+import org.jdom2.Element;
+import org.jdom2.JDOMException;
+import org.jdom2.input.SAXBuilder;
import java.io.File;
import java.io.FileInputStream;
diff --git a/core/src/main/java/com/bc/fiduceo/core/UseCaseConfig.java b/core/src/main/java/com/bc/fiduceo/core/UseCaseConfig.java
index 90fe07698..1c37ffdbf 100644
--- a/core/src/main/java/com/bc/fiduceo/core/UseCaseConfig.java
+++ b/core/src/main/java/com/bc/fiduceo/core/UseCaseConfig.java
@@ -21,12 +21,12 @@
package com.bc.fiduceo.core;
import org.esa.snap.core.util.StringUtils;
-import org.jdom.Document;
-import org.jdom.Element;
-import org.jdom.JDOMException;
-import org.jdom.input.SAXBuilder;
-import org.jdom.output.Format;
-import org.jdom.output.XMLOutputter;
+import org.jdom2.Document;
+import org.jdom2.Element;
+import org.jdom2.JDOMException;
+import org.jdom2.input.SAXBuilder;
+import org.jdom2.output.Format;
+import org.jdom2.output.XMLOutputter;
import java.io.IOException;
import java.io.InputStream;
diff --git a/core/src/main/java/com/bc/fiduceo/db/DbMaintenanceTool.java b/core/src/main/java/com/bc/fiduceo/db/DbMaintenanceTool.java
index 54d300842..5fb5ccd82 100644
--- a/core/src/main/java/com/bc/fiduceo/db/DbMaintenanceTool.java
+++ b/core/src/main/java/com/bc/fiduceo/db/DbMaintenanceTool.java
@@ -8,7 +8,7 @@
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
-import org.apache.commons.lang.StringUtils;
+import org.apache.commons.lang3.StringUtils;
import java.io.IOException;
import java.io.OutputStream;
diff --git a/core/src/main/java/com/bc/fiduceo/db/DbMaintenanceToolMain.java b/core/src/main/java/com/bc/fiduceo/db/DbMaintenanceToolMain.java
index 2e683c4fb..dbf6ab92d 100644
--- a/core/src/main/java/com/bc/fiduceo/db/DbMaintenanceToolMain.java
+++ b/core/src/main/java/com/bc/fiduceo/db/DbMaintenanceToolMain.java
@@ -1,9 +1,6 @@
package com.bc.fiduceo.db;
-import org.apache.commons.cli.CommandLine;
-import org.apache.commons.cli.CommandLineParser;
-import org.apache.commons.cli.ParseException;
-import org.apache.commons.cli.PosixParser;
+import org.apache.commons.cli.*;
import java.io.IOException;
import java.sql.SQLException;
@@ -18,7 +15,7 @@ public static void main(String[] args) throws ParseException {
return;
}
- final CommandLineParser parser = new PosixParser();
+ final CommandLineParser parser = new DefaultParser();
final CommandLine commandLine = parser.parse(DbMaintenanceTool.getOptions(), args);
if (commandLine.hasOption("h") || commandLine.hasOption("--help")) {
maintenanceTool.printUsageTo(System.out);
diff --git a/core/src/main/java/com/bc/fiduceo/geometry/GeometryUtil.java b/core/src/main/java/com/bc/fiduceo/geometry/GeometryUtil.java
index 54fc796ab..a17dbc345 100644
--- a/core/src/main/java/com/bc/fiduceo/geometry/GeometryUtil.java
+++ b/core/src/main/java/com/bc/fiduceo/geometry/GeometryUtil.java
@@ -127,6 +127,54 @@ public static String toKml(Polygon polygon) {
return builder.toString();
}
+ public static String toKml(MultiPolygon multiPolygon) {
+ final StringBuilder builder = new StringBuilder();
+
+ builder.append("\n");
+ builder.append("\n");
+ builder.append("\n");
+ builder.append(" \n");
+
+ int index = 1;
+ for (Polygon polygon : multiPolygon.getPolygons()) {
+ builder.append(" \n");
+ builder.append(" part_" + index + "\n");
+ builder.append(" #polygonStyle\n");
+ builder.append(" \n");
+ builder.append(" clampToGround\n");
+ builder.append(" \n");
+ builder.append(" \n");
+ builder.append(" \n");
+ final Point[] coordinates = polygon.getCoordinates();
+ for (final Point coordinate : coordinates) {
+ builder.append(" ");
+ builder.append(coordinate.getLon());
+ builder.append(",");
+ builder.append(coordinate.getLat());
+ builder.append(",0\n");
+ }
+ builder.append(" \n");
+ builder.append(" \n");
+ builder.append(" \n");
+ builder.append(" \n");
+ builder.append(" \n");
+ index++;
+ }
+
+ builder.append("\n");
+ builder.append("");
+
+ return builder.toString();
+ }
+
public static String toKml(float[] lats, float[] lons) {
final StringBuilder builder = new StringBuilder();
builder.append("\n");
diff --git a/core/src/main/java/com/bc/fiduceo/geometry/Point.java b/core/src/main/java/com/bc/fiduceo/geometry/Point.java
index c738630ea..ffd5d2d55 100644
--- a/core/src/main/java/com/bc/fiduceo/geometry/Point.java
+++ b/core/src/main/java/com/bc/fiduceo/geometry/Point.java
@@ -32,5 +32,5 @@ public interface Point extends Geometry {
void setLat(double lat);
- boolean equals(Point other);
+ boolean equals(Object other);
}
diff --git a/core/src/main/java/com/bc/fiduceo/geometry/jts/JTSPoint.java b/core/src/main/java/com/bc/fiduceo/geometry/jts/JTSPoint.java
index 273e48907..965731959 100644
--- a/core/src/main/java/com/bc/fiduceo/geometry/jts/JTSPoint.java
+++ b/core/src/main/java/com/bc/fiduceo/geometry/jts/JTSPoint.java
@@ -80,7 +80,11 @@ public void setLat(double lat) {
}
@Override
- public boolean equals(Point other) {
+ public boolean equals(Object o) {
+ if (o == null || !(o instanceof JTSPoint)) {
+ return false;
+ }
+ final JTSPoint other = (JTSPoint) o;
return other == this || other.getLon() == getLon() && other.getLat() == getLat();
}
}
diff --git a/core/src/main/java/com/bc/fiduceo/geometry/s2/BcS2Point.java b/core/src/main/java/com/bc/fiduceo/geometry/s2/BcS2Point.java
index 9d8f0128d..dacfde8a4 100644
--- a/core/src/main/java/com/bc/fiduceo/geometry/s2/BcS2Point.java
+++ b/core/src/main/java/com/bc/fiduceo/geometry/s2/BcS2Point.java
@@ -103,7 +103,11 @@ public String toString() {
}
@Override
- public boolean equals(Point other) {
+ public boolean equals(Object o) {
+ if (o == null || !(o instanceof BcS2Point)) {
+ return false;
+ }
+ final BcS2Point other = (BcS2Point) o;
return other == this || other.getLon() == getLon() && other.getLat() == getLat();
}
diff --git a/core/src/main/java/com/bc/fiduceo/location/PixelGeoCodingPixelLocator.java b/core/src/main/java/com/bc/fiduceo/location/PixelGeoCodingPixelLocator.java
new file mode 100644
index 000000000..89ff2c2fe
--- /dev/null
+++ b/core/src/main/java/com/bc/fiduceo/location/PixelGeoCodingPixelLocator.java
@@ -0,0 +1,55 @@
+package com.bc.fiduceo.location;
+
+import org.esa.snap.core.dataio.geocoding.*;
+import org.esa.snap.core.dataio.geocoding.forward.PixelForward;
+import org.esa.snap.core.dataio.geocoding.inverse.PixelQuadTreeInverse;
+import org.esa.snap.core.datamodel.GeoPos;
+import org.esa.snap.core.datamodel.PixelPos;
+import ucar.ma2.Array;
+import ucar.ma2.DataType;
+
+import java.awt.geom.Point2D;
+
+public class PixelGeoCodingPixelLocator implements PixelLocator {
+
+ private final ComponentGeoCoding geoCoding;
+ private boolean hasHalfPxOffset;
+
+ public PixelGeoCodingPixelLocator(Array longitudes, Array latitudes, String lonVariableName, String latVariableName, double groundResolutionInKm, GeoChecks geoChecks) {
+ final double[] lonArray = (double[]) longitudes.get1DJavaArray(DataType.DOUBLE);
+ final double[] latArray = (double[]) latitudes.get1DJavaArray(DataType.DOUBLE);
+ final int[] shape = longitudes.getShape();
+
+ final GeoRaster geoRaster = new GeoRaster(lonArray, latArray, lonVariableName, latVariableName, shape[1], shape[0], groundResolutionInKm);
+ final ForwardCoding forwardCoding = ComponentFactory.getForward(PixelForward.KEY);
+ final InverseCoding inverseCoding = ComponentFactory.getInverse(PixelQuadTreeInverse.KEY);
+ geoCoding = new ComponentGeoCoding(geoRaster, forwardCoding, inverseCoding, geoChecks);
+ geoCoding.initialize();
+
+ hasHalfPxOffset = true;
+ }
+
+ public void setHasHalfPxOffset(boolean hasHalfPxOffset) {
+ this.hasHalfPxOffset = hasHalfPxOffset;
+ }
+
+ @Override
+ public Point2D getGeoLocation(double x, double y, Point2D g) {
+ PixelPos pixelPos;
+
+ if (hasHalfPxOffset) {
+ pixelPos = new PixelPos(x + 0.5, y + 0.5);
+ } else {
+ pixelPos = new PixelPos(x, y);
+ }
+
+ final GeoPos geoPos = geoCoding.getGeoPos(pixelPos, null);
+ return new Point2D.Double(geoPos.lon, geoPos.lat);
+ }
+
+ @Override
+ public Point2D[] getPixelLocation(double lon, double lat) {
+ final PixelPos pixelPos = geoCoding.getPixelPos(new GeoPos(lat, lon), null);
+ return new Point2D[]{new Point2D.Double(pixelPos.getX() - 0.5, pixelPos.getY() - 0.5)};
+ }
+}
diff --git a/core/src/main/java/com/bc/fiduceo/qc/GlobalPlot.java b/core/src/main/java/com/bc/fiduceo/qc/GlobalPlot.java
new file mode 100644
index 000000000..f60de287f
--- /dev/null
+++ b/core/src/main/java/com/bc/fiduceo/qc/GlobalPlot.java
@@ -0,0 +1,58 @@
+package com.bc.fiduceo.qc;
+
+import org.esa.snap.core.datamodel.GeoPos;
+
+import javax.imageio.ImageIO;
+import java.awt.*;
+import java.awt.image.BufferedImage;
+import java.io.*;
+import java.util.ArrayList;
+
+class GlobalPlot {
+
+ static byte[] imageBuffer;
+
+ private BufferedImage image;
+
+ private GlobalPlot() throws IOException {
+ image = ImageIO.read(new ByteArrayInputStream(imageBuffer));
+ }
+
+ static GlobalPlot create() throws IOException {
+ if (imageBuffer == null) {
+ final InputStream is = GlobalPlot.class.getResourceAsStream("bluemarble-2048.png");
+ if (is == null) {
+ throw new IllegalStateException("The internal resource file could not be read.");
+ }
+ final DataInputStream dis = new DataInputStream(is);
+ final int fileSize = dis.available();
+ imageBuffer = new byte[fileSize];
+ dis.readFully(imageBuffer);
+ }
+
+ return new GlobalPlot();
+ }
+
+ void plot(ArrayList pointList) {
+ final Graphics2D graphics = image.createGraphics();
+ graphics.setColor(Color.magenta);
+
+ for (final GeoPos point : pointList) {
+ final double x = (point.getLon() + 180.0) / 360.0;
+ final double y = (90.0 - point.getLat()) / 180.0;
+ final int i = (int) (y * 1024);
+ final int k = (int) (x * 2048);
+ graphics.fill(new Rectangle(k, i, 1, 1));
+ }
+
+ graphics.dispose();
+ }
+
+ void writeTo(String pngFilePath) throws IOException {
+ ImageIO.write(image, "png", new File(pngFilePath));
+ }
+
+ public void dispose() {
+ image = null;
+ }
+}
diff --git a/core/src/main/java/com/bc/fiduceo/qc/MmdQCTool.java b/core/src/main/java/com/bc/fiduceo/qc/MmdQCTool.java
index a4c0abd5a..7a4487c44 100644
--- a/core/src/main/java/com/bc/fiduceo/qc/MmdQCTool.java
+++ b/core/src/main/java/com/bc/fiduceo/qc/MmdQCTool.java
@@ -3,19 +3,20 @@
import com.bc.fiduceo.FiduceoConstants;
import com.bc.fiduceo.log.FiduceoLogger;
import com.bc.fiduceo.util.NetCDFUtils;
+import com.bc.fiduceo.util.TimeUtils;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
+import org.esa.snap.core.datamodel.GeoPos;
+import org.esa.snap.core.util.StringUtils;
+import org.esa.snap.core.util.io.FileUtils;
import ucar.ma2.Array;
import ucar.ma2.IndexIterator;
import ucar.ma2.InvalidRangeException;
import ucar.nc2.NetcdfFile;
-import java.io.File;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.PrintWriter;
+import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
@@ -31,9 +32,6 @@ class MmdQCTool {
private final static Logger logger = FiduceoLogger.getLogger();
- private FileMessages fileMessages;
- private MatchupAccumulator matchupAccumulator;
-
// package access for testing only tb 2023-02-14
static Options getOptions() {
final Options options = new Options();
@@ -45,10 +43,22 @@ static Options getOptions() {
inputOption.setRequired(true);
options.addOption(inputOption);
+ final Option outputOption = new Option("o", "outdir", true, "Defines the result output directory.");
+ options.addOption(outputOption);
+
final Option timeOption = new Option("t", "time", true, "Defines matchup time variable name.");
timeOption.setRequired(true);
options.addOption(timeOption);
+ final Option plotOption = new Option("p", "plot", false, "Enables plotting the matchup locations onto a global map. Requires 'lon' and 'lat' to be set.");
+ options.addOption(plotOption);
+
+ final Option lonOption = new Option("lon", "longitude", true, "Defines the variable name for the longitude.");
+ options.addOption(lonOption);
+
+ final Option latOption = new Option("lat", "latitude", true, "Defines the variable name for the latitude.");
+ options.addOption(latOption);
+
return options;
}
@@ -68,7 +78,14 @@ static void writeReport(OutputStream outputStream, MatchupAccumulator accumulato
final int size = messageMap.size();
writer.println(size + " file(s) with errors");
if (size > 0) {
- // @todo 1 tb/tb add error messages per file here
+ for (Map.Entry> entry : messageMap.entrySet()) {
+ writer.println(entry.getKey());
+
+ final List messages = entry.getValue();
+ for (final String message : messages) {
+ writer.println(" - " + message);
+ }
+ }
}
writer.println();
@@ -89,13 +106,21 @@ void run(CommandLine commandLine) throws IOException {
final List mmdFiles = getInputFiles(inputDirOption);
logger.info("Found " + mmdFiles.size() + " input file(s) to analyze.");
- fileMessages = new FileMessages();
- matchupAccumulator = new MatchupAccumulator();
+ final String outDirString = commandLine.getOptionValue("o");
+ File outDir;
+ if (StringUtils.isNullOrEmpty(outDirString)) {
+ outDir = new File(".");
+ } else {
+ outDir = new File(outDirString);
+ }
+
+ final FileMessages fileMessages = new FileMessages();
+ final MatchupAccumulator matchupAccumulator = new MatchupAccumulator();
// loop over files
for (final Path mmdFile : mmdFiles) {
-
try (final NetcdfFile netcdfFile = NetCDFUtils.openReadOnly(mmdFile.toAbsolutePath().toString())) {
+ matchupAccumulator.countFile();
// read time variable center pixel
final String timeVariableName = commandLine.getOptionValue("t");
@@ -106,17 +131,62 @@ void run(CommandLine commandLine) throws IOException {
final int time = iterator.getIntNext();
matchupAccumulator.add(time);
}
+
+ if (commandLine.hasOption("p")) {
+ plotMatchups(mmdFile, netcdfFile, commandLine, outDir);
+ }
+
} catch (IOException | InvalidRangeException ioException) {
fileMessages.add(mmdFile.getFileName().toString(), ioException.getMessage());
}
}
- // write report
- final TreeMap treeMap = new TreeMap<>(matchupAccumulator.getDaysMap());
- final Set> entries = treeMap.entrySet();
- for (final Map.Entry entry : entries) {
- System.out.println(entry.getKey() + ": " + entry.getValue());
+ final Date now = TimeUtils.createNow();
+ final String timeString = TimeUtils.format(now, "yyyy-MM-dd");
+ final String qcFileName = "mmd_qc_report_" + timeString + ".txt";
+ final File reportFile = new File(outDir, qcFileName);
+ if (!reportFile.createNewFile()) {
+ throw new IOException("unable to create report file: " + reportFile.getAbsolutePath());
+ }
+ try (FileOutputStream outStream = new FileOutputStream(reportFile)) {
+ writeReport(outStream, matchupAccumulator, fileMessages);
+ }
+ }
+
+ private static void plotMatchups(Path mmdFile, NetcdfFile netcdfFile, CommandLine commandLine, File outDir) throws IOException, InvalidRangeException {
+ final String lonVariable = commandLine.getOptionValue("lon");
+ final String latVariable = commandLine.getOptionValue("lat");
+
+ if (StringUtils.isNullOrEmpty(lonVariable) || StringUtils.isNullOrEmpty(latVariable)) {
+ throw new IllegalArgumentException("must provide lon and lat variable names for plotting");
+ }
+
+ final String filenameWithoutExtension = FileUtils.getFilenameWithoutExtension(mmdFile.toFile());
+ final String pngFile = filenameWithoutExtension.concat(".png");
+ final File pngFilePath = new File(outDir, pngFile);
+
+ final GlobalPlot filePlot = GlobalPlot.create();
+ final Array latitudes = NetCDFUtils.getCenterPosArrayFromMMDFile(netcdfFile, latVariable, null,
+ null, FiduceoConstants.MATCHUP_COUNT);
+
+ final Array longitudes = NetCDFUtils.getCenterPosArrayFromMMDFile(netcdfFile, lonVariable, null,
+ null, FiduceoConstants.MATCHUP_COUNT);
+
+ int numMatches = 1;
+ int[] shape = latitudes.getShape();
+ if (shape.length > 0) {
+ numMatches = shape[0];
}
+
+ final ArrayList pointList = new ArrayList<>();
+ for (int i = 0; i < numMatches; i++) {
+ final GeoPos geoPos = new GeoPos(latitudes.getFloat(i), longitudes.getFloat(i));
+ pointList.add(geoPos);
+ }
+
+ filePlot.plot(pointList);
+ filePlot.writeTo(pngFilePath.getAbsolutePath());
+ filePlot.dispose();
}
private List getInputFiles(String inputDirOption) throws IOException {
@@ -140,7 +210,7 @@ static void printUsageTo(OutputStream outputStream) {
writer.write(ls + ls);
final HelpFormatter helpFormatter = new HelpFormatter();
- helpFormatter.printHelp(writer, 120, "matchup-tool ", "Valid options are:",
+ helpFormatter.printHelp(writer, 120, "mmd-qc-tool ", "Valid options are:",
getOptions(), 3, 3, "");
writer.flush();
diff --git a/core/src/main/java/com/bc/fiduceo/qc/MmdQCToolMain.java b/core/src/main/java/com/bc/fiduceo/qc/MmdQCToolMain.java
index 7415a53c7..f012cb416 100644
--- a/core/src/main/java/com/bc/fiduceo/qc/MmdQCToolMain.java
+++ b/core/src/main/java/com/bc/fiduceo/qc/MmdQCToolMain.java
@@ -1,10 +1,7 @@
package com.bc.fiduceo.qc;
import com.bc.fiduceo.log.FiduceoLogger;
-import org.apache.commons.cli.CommandLine;
-import org.apache.commons.cli.CommandLineParser;
-import org.apache.commons.cli.ParseException;
-import org.apache.commons.cli.PosixParser;
+import org.apache.commons.cli.*;
public class MmdQCToolMain {
@@ -16,7 +13,7 @@ public static void main(String[] args) throws ParseException {
return;
}
- final CommandLineParser parser = new PosixParser();
+ final CommandLineParser parser = new DefaultParser();
final CommandLine commandLine = parser.parse(MmdQCTool.getOptions(), args);
if (commandLine.hasOption("h") || commandLine.hasOption("--help")) {
MmdQCTool.printUsageTo(System.err);
diff --git a/core/src/main/java/com/bc/fiduceo/reader/RawDataReader.java b/core/src/main/java/com/bc/fiduceo/reader/RawDataReader.java
index 13d8f2a5d..e2f8a3aa8 100644
--- a/core/src/main/java/com/bc/fiduceo/reader/RawDataReader.java
+++ b/core/src/main/java/com/bc/fiduceo/reader/RawDataReader.java
@@ -25,6 +25,7 @@
import ucar.ma2.*;
import java.awt.*;
+import java.awt.geom.Rectangle2D;
import java.io.IOException;
/**
@@ -131,12 +132,18 @@ private static Array readFrom1DArray(int offsetX, int offsetY, int windowWidth,
}
}
- private static boolean isWindowInside(int winOffSetX, int winOffSetY, int windowWidth, int windowHeight, int rawWidth, int rawHeight) {
+ public static boolean isWindowInside(int winOffSetX, int winOffSetY, int windowWidth, int windowHeight, int rawWidth, int rawHeight) {
final Rectangle windowRec = new Rectangle(winOffSetX, winOffSetY, windowWidth, windowHeight);
final Rectangle arrayRectangle = new Rectangle(0, 0, rawWidth, rawHeight);
return arrayRectangle.contains(windowRec);
}
+ public static Rectangle2D getInsideWindow(int winOffSetX, int winOffSetY, int windowWidth, int windowHeight, int rawWidth, int rawHeight) {
+ final Rectangle windowRec = new Rectangle(winOffSetX, winOffSetY, windowWidth, windowHeight);
+ final Rectangle arrayRectangle = new Rectangle(0, 0, rawWidth, rawHeight);
+ return arrayRectangle.createIntersection(windowRec);
+ }
+
// package access for testing only tb 2016-04-18
static InputDimension getInputDimension(int rank, int[] shape) {
if (rank == 1 && shape[0] == 1) {
diff --git a/core/src/main/java/com/bc/fiduceo/reader/ReaderPlugin.java b/core/src/main/java/com/bc/fiduceo/reader/ReaderPlugin.java
index 33dba6e39..6e67dc426 100644
--- a/core/src/main/java/com/bc/fiduceo/reader/ReaderPlugin.java
+++ b/core/src/main/java/com/bc/fiduceo/reader/ReaderPlugin.java
@@ -1,7 +1,5 @@
package com.bc.fiduceo.reader;
-import com.bc.fiduceo.geometry.GeometryFactory;
-
public interface ReaderPlugin {
/**
diff --git a/core/src/main/java/com/bc/fiduceo/reader/amsu_mhs/AMSUA_L1B_Reader.java b/core/src/main/java/com/bc/fiduceo/reader/amsu_mhs/AMSUA_L1B_Reader.java
new file mode 100644
index 000000000..ccdc86269
--- /dev/null
+++ b/core/src/main/java/com/bc/fiduceo/reader/amsu_mhs/AMSUA_L1B_Reader.java
@@ -0,0 +1,54 @@
+package com.bc.fiduceo.reader.amsu_mhs;
+
+import com.bc.fiduceo.core.Dimension;
+import com.bc.fiduceo.core.Interval;
+import com.bc.fiduceo.reader.*;
+import com.bc.fiduceo.reader.amsu_mhs.nat.*;
+import com.bc.fiduceo.reader.amsu_mhs.nat.record_types.MDR;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.*;
+
+import static com.bc.fiduceo.reader.amsu_mhs.nat.EPS_Constants.*;
+
+public class AMSUA_L1B_Reader extends Abstract_L1B_NatReader {
+
+ public static final String RESOURCE_KEY = "AMSUA_L1B";
+
+ AMSUA_L1B_Reader(ReaderContext readerContext) {
+ super(readerContext);
+ }
+
+ @Override
+ public void open(File file) throws IOException {
+ initializeRegistry(RESOURCE_KEY);
+ readDataToCache(file, EPS_Constants.AMSUA_FOV_COUNT);
+
+ final List mdrs = cache.getMdrs();
+ ensureMdrVersionSupported(mdrs.get(0).getHeader());
+ }
+
+ @Override
+ public AcquisitionInfo read() throws IOException {
+ return super.read(new Interval(6, 20));
+ }
+
+ @Override
+ public String getRegEx() {
+ return "AMSA_[A-Z0-9x]{3}_1B_M0[123]_[0-9]{14}Z_[0-9]{14}Z_[A-Z0-9x]{1}_[A-Z0-9x]{1}_[0-9]{14}Z\\.nat";
+ }
+
+ @Override
+ public Dimension getProductSize() throws IOException {
+ return super.getProductSize(AMSUA_FOV_COUNT);
+ }
+
+ static void ensureMdrVersionSupported(GENERIC_RECORD_HEADER header) {
+ final byte recordSubClass = header.getRecordSubClass();
+ final byte recordSubClassVersion = header.getRecordSubClassVersion();
+ if (recordSubClass != 2 || recordSubClassVersion != 3) {
+ throw new IllegalStateException("Unsupported MDR version: " + recordSubClass + " v " + recordSubClassVersion);
+ }
+ }
+}
diff --git a/core/src/main/java/com/bc/fiduceo/reader/amsu_mhs/AMSUA_L1B_ReaderPlugin.java b/core/src/main/java/com/bc/fiduceo/reader/amsu_mhs/AMSUA_L1B_ReaderPlugin.java
new file mode 100644
index 000000000..7283e9085
--- /dev/null
+++ b/core/src/main/java/com/bc/fiduceo/reader/amsu_mhs/AMSUA_L1B_ReaderPlugin.java
@@ -0,0 +1,26 @@
+package com.bc.fiduceo.reader.amsu_mhs;
+
+import com.bc.fiduceo.reader.DataType;
+import com.bc.fiduceo.reader.Reader;
+import com.bc.fiduceo.reader.ReaderContext;
+import com.bc.fiduceo.reader.ReaderPlugin;
+
+public class AMSUA_L1B_ReaderPlugin implements ReaderPlugin {
+
+ private static final String[] SENSOR_KEYS = {"amsua-ma-l1b", "amsua-mb-l1b", "amsua-mc-l1b"};
+
+ @Override
+ public Reader createReader(ReaderContext readerContext) {
+ return new AMSUA_L1B_Reader(readerContext);
+ }
+
+ @Override
+ public String[] getSupportedSensorKeys() {
+ return SENSOR_KEYS;
+ }
+
+ @Override
+ public DataType getDataType() {
+ return DataType.POLAR_ORBITING_SATELLITE;
+ }
+}
diff --git a/core/src/main/java/com/bc/fiduceo/reader/amsu_mhs/MHS_L1B_Reader.java b/core/src/main/java/com/bc/fiduceo/reader/amsu_mhs/MHS_L1B_Reader.java
new file mode 100644
index 000000000..ed4abf758
--- /dev/null
+++ b/core/src/main/java/com/bc/fiduceo/reader/amsu_mhs/MHS_L1B_Reader.java
@@ -0,0 +1,41 @@
+package com.bc.fiduceo.reader.amsu_mhs;
+
+import com.bc.fiduceo.core.Dimension;
+import com.bc.fiduceo.core.Interval;
+import com.bc.fiduceo.reader.*;
+import com.bc.fiduceo.reader.amsu_mhs.nat.*;
+
+import java.io.File;
+import java.io.IOException;
+
+import static com.bc.fiduceo.reader.amsu_mhs.nat.EPS_Constants.*;
+
+public class MHS_L1B_Reader extends Abstract_L1B_NatReader {
+
+ public static final String RESOURCE_KEY = "MHS_L1B";
+
+ MHS_L1B_Reader(ReaderContext readerContext) {
+ super(readerContext);
+ }
+
+ @Override
+ public void open(File file) throws IOException {
+ initializeRegistry(RESOURCE_KEY);
+ readDataToCache(file, EPS_Constants.MHS_FOV_COUNT);
+ }
+
+ @Override
+ public AcquisitionInfo read() throws IOException {
+ return super.read(new Interval(10, 20));
+ }
+
+ @Override
+ public String getRegEx() {
+ return "MHSx_[A-Z0-9x]{3}_1B_M0[123]_[0-9]{14}Z_[0-9]{14}Z_[A-Z0-9x]{1}_[A-Z0-9x]{1}_[0-9]{14}Z\\.nat";
+ }
+
+ @Override
+ public Dimension getProductSize() throws IOException {
+ return super.getProductSize(MHS_FOV_COUNT);
+ }
+}
diff --git a/core/src/main/java/com/bc/fiduceo/reader/amsu_mhs/MHS_L1B_ReaderPlugin.java b/core/src/main/java/com/bc/fiduceo/reader/amsu_mhs/MHS_L1B_ReaderPlugin.java
new file mode 100644
index 000000000..6ed23b0c8
--- /dev/null
+++ b/core/src/main/java/com/bc/fiduceo/reader/amsu_mhs/MHS_L1B_ReaderPlugin.java
@@ -0,0 +1,26 @@
+package com.bc.fiduceo.reader.amsu_mhs;
+
+import com.bc.fiduceo.reader.DataType;
+import com.bc.fiduceo.reader.Reader;
+import com.bc.fiduceo.reader.ReaderContext;
+import com.bc.fiduceo.reader.ReaderPlugin;
+
+public class MHS_L1B_ReaderPlugin implements ReaderPlugin {
+
+ private static final String[] SENSOR_KEYS = {"mhs-ma-l1b", "mhs-mb-l1b", "mhs-mc-l1b"};
+
+ @Override
+ public Reader createReader(ReaderContext readerContext) {
+ return new MHS_L1B_Reader(readerContext);
+ }
+
+ @Override
+ public String[] getSupportedSensorKeys() {
+ return SENSOR_KEYS;
+ }
+
+ @Override
+ public DataType getDataType() {
+ return DataType.POLAR_ORBITING_SATELLITE;
+ }
+}
diff --git a/core/src/main/java/com/bc/fiduceo/reader/amsu_mhs/nat/Abstract_L1B_NatReader.java b/core/src/main/java/com/bc/fiduceo/reader/amsu_mhs/nat/Abstract_L1B_NatReader.java
new file mode 100644
index 000000000..99f636ec8
--- /dev/null
+++ b/core/src/main/java/com/bc/fiduceo/reader/amsu_mhs/nat/Abstract_L1B_NatReader.java
@@ -0,0 +1,313 @@
+package com.bc.fiduceo.reader.amsu_mhs.nat;
+
+import com.bc.fiduceo.core.Dimension;
+import com.bc.fiduceo.core.Interval;
+import com.bc.fiduceo.geometry.Geometry;
+import com.bc.fiduceo.geometry.GeometryFactory;
+import com.bc.fiduceo.geometry.Polygon;
+import com.bc.fiduceo.location.PixelGeoCodingPixelLocator;
+import com.bc.fiduceo.location.PixelLocator;
+import com.bc.fiduceo.reader.*;
+import com.bc.fiduceo.reader.amsu_mhs.nat.record_types.MPHR;
+import com.bc.fiduceo.reader.time.TimeLocator;
+import com.bc.fiduceo.reader.time.TimeLocator_StartStopDate;
+import com.bc.fiduceo.util.NetCDFUtils;
+import com.bc.fiduceo.util.VariableProxy;
+import org.esa.snap.core.dataio.geocoding.GeoChecks;
+import org.esa.snap.core.util.StringUtils;
+import ucar.ma2.Array;
+import ucar.ma2.ArrayInt;
+import ucar.ma2.DataType;
+import ucar.ma2.InvalidRangeException;
+import ucar.nc2.Attribute;
+import ucar.nc2.Variable;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.*;
+
+import static com.bc.fiduceo.core.NodeType.UNDEFINED;
+import static com.bc.fiduceo.reader.amsu_mhs.nat.EPS_Constants.*;
+import static ucar.ma2.DataType.INT;
+
+abstract public class Abstract_L1B_NatReader implements Reader {
+
+ protected VariableRegistry registry;
+ protected EpsVariableCache cache;
+ protected final GeometryFactory geometryFactory;
+ private Dimension productSize;
+ private static final int NUM_SPLITS = 2;
+
+ private PixelLocator pixelLocator;
+
+ public Abstract_L1B_NatReader(ReaderContext readerContext) {
+ this.geometryFactory = readerContext.getGeometryFactory();
+ pixelLocator = null;
+ productSize = null;
+ }
+
+ public AcquisitionInfo read(Interval interval) throws IOException {
+ final AcquisitionInfo acquisitionInfo = new AcquisitionInfo();
+ acquisitionInfo.setNodeType(UNDEFINED);
+
+ final MPHR recordMPHR = cache.getMPHR();
+ setSensingDates(acquisitionInfo, recordMPHR);
+
+ final Array lon = cache.getScaled(LON_VAR_NAME);
+ final Array lat = cache.getScaled(LAT_VAR_NAME);
+
+ final Geometries geometries = extractGeometries(lon, lat, NUM_SPLITS, interval);
+ acquisitionInfo.setBoundingGeometry(geometries.getBoundingGeometry());
+ ReaderUtils.setTimeAxes(acquisitionInfo, geometries.getTimeAxesGeometry(), geometryFactory);
+
+ return acquisitionInfo;
+ }
+
+ @Override
+ public Array readRaw(int centerX, int centerY, Interval interval, String variableName) throws IOException, InvalidRangeException {
+ final Array rawData = cache.getRaw(variableName);
+ final VariableDefinition variableDef = registry.getVariableDef(variableName);
+ final Number fillValue = EpsReaderUtils.getFillValue(variableDef.getData_type());
+ final Dimension productSize = getProductSize();
+ return RawDataReader.read(centerX, centerY, interval, fillValue, rawData, productSize);
+ }
+
+ @Override
+ public Array readScaled(int centerX, int centerY, Interval interval, String variableName) throws IOException, InvalidRangeException {
+ final Array rawData = readRaw(centerX, centerY, interval, variableName);
+ final VariableDefinition variableDef = registry.getVariableDef(variableName);
+ return EpsReaderUtils.scale(rawData, variableDef.getScale_factor());
+ }
+
+ @Override
+ public String getLongitudeVariableName() {
+ return LON_VAR_NAME;
+ }
+
+ @Override
+ public String getLatitudeVariableName() {
+ return LAT_VAR_NAME;
+ }
+
+ @Override
+ public PixelLocator getPixelLocator() throws IOException {
+ if (pixelLocator == null) {
+ final Array lon = cache.getScaled(LON_VAR_NAME);
+ final Array lat = cache.getScaled(LAT_VAR_NAME);
+
+ final PixelGeoCodingPixelLocator pixelGeoCodingPixelLocator = new PixelGeoCodingPixelLocator(lon, lat, LON_VAR_NAME, LAT_VAR_NAME, 48.0, GeoChecks.POLES);
+ pixelGeoCodingPixelLocator.setHasHalfPxOffset(false);
+ pixelLocator = pixelGeoCodingPixelLocator;
+ }
+ return pixelLocator;
+ }
+
+ @Override
+ public PixelLocator getSubScenePixelLocator(Polygon sceneGeometry) throws IOException {
+ return getPixelLocator();
+ }
+
+ @Override
+ public TimeLocator getTimeLocator() throws IOException {
+ // for the test file, the array returned contains only zeros - same as for MHS
+ // According to the sparse documentation, I would expect this to contain seconds since epoch
+ // tb 2025-09-04
+ // Array timeAttitude = cache.getRaw("TIME_ATTITUDE");
+
+ // Instead, we interpolate between header start and stop times tb 2025-09-04
+ final MPHR mphr = cache.getMPHR();
+ final Date sensingStart = mphr.getSensingStart();
+ final Date sensingStop = mphr.getSensingStop();
+
+ return new TimeLocator_StartStopDate(sensingStart, sensingStop, getProductSize().getNy());
+ }
+
+ @Override
+ public int[] extractYearMonthDayFromFilename(String fileName) {
+ final String[] strings = fileName.split("_");
+ final String dateTimePart = strings[4];
+
+ final int[] ymd = new int[3];
+ ymd[0] = Integer.parseInt(dateTimePart.substring(0, 4));
+ ymd[1] = Integer.parseInt(dateTimePart.substring(4, 6));
+ ymd[2] = Integer.parseInt(dateTimePart.substring(6, 8));
+ return ymd;
+ }
+
+ protected static void setSensingDates(AcquisitionInfo acquisitionInfo, MPHR recordMPHR) throws IOException {
+ final Date sensingStart = recordMPHR.getSensingStart();
+ acquisitionInfo.setSensingStart(sensingStart);
+ final Date sensingEnd = recordMPHR.getSensingStop();
+ acquisitionInfo.setSensingStop(sensingEnd);
+ }
+
+ protected void initializeRegistry(String resourceKey) {
+ registry = VariableRegistry.load(resourceKey);
+ }
+
+ protected void readDataToCache(File file, int sensorKey) throws IOException {
+ final byte[] rawDataBuffer;
+ try (FileInputStream fis = new FileInputStream(file)) {
+ rawDataBuffer = fis.readAllBytes();
+ }
+ cache = new EpsVariableCache(rawDataBuffer, registry, sensorKey);
+ }
+
+ @Override
+ public void close() throws IOException {
+ if (cache != null) {
+ cache.clear();
+ cache = null;
+ }
+ if (registry != null) {
+ registry.clear();
+ registry = null;
+ }
+ pixelLocator = null;
+ productSize = null;
+ }
+
+ @Override
+ public List getVariables() throws InvalidRangeException, IOException {
+ final ArrayList variables = new ArrayList<>();
+
+ final Map regVariables = registry.getVariables();
+ final Set keySet = regVariables.keySet();
+ for (String variableName : keySet) {
+ final VariableDefinition variableDefinition = regVariables.get(variableName);
+ final int productDataType = variableDefinition.getProductData_type();
+ final DataType netcdfDataType = NetCDFUtils.getNetcdfDataType(productDataType);
+ final List attributes = extractCFAttributes(variableDefinition);
+
+ final VariableProxy variable = new VariableProxy(variableName, netcdfDataType, attributes);
+ variables.add(variable);
+ }
+
+ return variables;
+ }
+
+ @Override
+ public ArrayInt.D2 readAcquisitionTime(int x, int y, Interval interval) throws IOException, InvalidRangeException {
+ final int width = interval.getX();
+ final int height = interval.getY();
+ final int[] timeArray = new int[width * height];
+
+ final Dimension size = getProductSize();
+ final int sceneRasterHeight = size.getNy();
+ final int sceneRasterWidth = size.getNx();
+ final int halfHeight = height / 2;
+ final int halfWidth = width / 2;
+ int writeOffset = 0;
+ final int fillValue = NetCDFUtils.getDefaultFillValue(int.class).intValue();
+ final TimeLocator timeLocator = getTimeLocator();
+
+ for (int yRead = y - halfHeight; yRead <= y + halfHeight; yRead++) {
+ int lineTimeSeconds = fillValue;
+ if (yRead >= 0 && yRead < sceneRasterHeight) {
+ final long lineTimeMillis = timeLocator.getTimeFor(x, yRead);
+ lineTimeSeconds = (int) Math.round(lineTimeMillis * 0.001);
+ }
+
+ for (int xRead = x - halfWidth; xRead <= x + halfWidth; xRead++) {
+ if (xRead >= 0 && xRead < sceneRasterWidth) {
+ timeArray[writeOffset] = lineTimeSeconds;
+ } else {
+ timeArray[writeOffset] = fillValue;
+ }
+ ++writeOffset;
+ }
+ }
+
+ final int[] shape = new int[]{interval.getY(), interval.getX()};
+ return (ArrayInt.D2) Array.factory(INT, shape, timeArray);
+ }
+
+ public Dimension getProductSize(int fov_count) {
+ if (productSize == null) {
+ final int numScanLines = cache.getMdrs().size();
+ productSize = new Dimension("size", fov_count, numScanLines);
+ }
+ return productSize;
+ }
+
+ // public access for testing only tb 2025-09-17
+ public static List extractCFAttributes(VariableDefinition variableDefinition) {
+ final ArrayList attributes = new ArrayList<>();
+
+ final String units = variableDefinition.getUnits();
+ if (StringUtils.isNotNullAndNotEmpty(units)) {
+ attributes.add(new Attribute("units", units));
+ }
+
+ final double scaleFactor = variableDefinition.getScale_factor();
+ if (scaleFactor != 1.0) {
+ attributes.add(new Attribute("scale_factor", scaleFactor));
+ attributes.add(new Attribute("add_offset", 0.0));
+ }
+
+ final String dataType = variableDefinition.getData_type();
+ if (StringUtils.isNotNullAndNotEmpty(dataType)) {
+ final Number fillValue = EpsReaderUtils.getFillValue(dataType);
+ if (fillValue != null) {
+ attributes.add(new Attribute("_FillValue", fillValue));
+ }
+ }
+
+ final String flagMeanings = variableDefinition.getFlag_meanings();
+ final String flagValues = variableDefinition.getFlag_values();
+ if (StringUtils.isNotNullAndNotEmpty(flagMeanings) && StringUtils.isNotNullAndNotEmpty(flagValues)) {
+ attributes.add(new Attribute("flag_meanings", flagMeanings));
+
+ final Array valuesArray = toValuesArray(flagValues, variableDefinition.getData_type());
+ attributes.add(new Attribute("flag_values", valuesArray));
+ }
+
+ final String standardName = variableDefinition.getStandard_name();
+ if (StringUtils.isNotNullAndNotEmpty(standardName)) {
+ attributes.add(new Attribute("standard_name", standardName));
+ }
+
+ return attributes;
+ }
+
+ // public access for testing only tb 2025-09-17
+ public static Array toValuesArray(String valuesString, String dataType) {
+ final String[] valueStrings = StringUtils.split(valuesString, new char[]{','}, true);
+ final int snapDataType = EpsReaderUtils.mapToProductData(dataType);
+
+ Array dataVector = Array.factory(NetCDFUtils.getNetcdfDataType(snapDataType), new int[]{valueStrings.length});
+
+ for (int i = 0; i < valueStrings.length; i++) {
+ dataVector.setDouble(i, Double.parseDouble(valueStrings[i]));
+ }
+ return dataVector;
+ }
+
+ protected Geometries extractGeometries(Array longitudes, Array latitudes, int numSplits, Interval interval) throws IOException {
+ final Geometries geometries = new Geometries();
+ final BoundingPolygonCreator boundingPolygonCreator = getBoundingPolygonCreator(interval);
+
+ Geometry boundingGeometry = boundingPolygonCreator.createBoundingGeometryClockwise(longitudes, latitudes);
+ Geometry timeAxisGeometry;
+
+ if (!boundingGeometry.isValid()) {
+ boundingGeometry = boundingPolygonCreator.createBoundingGeometrySplitted(longitudes, latitudes, numSplits, true);
+ if (!boundingGeometry.isValid()) {
+ throw new RuntimeException("Invalid bounding geometry detected");
+ }
+ timeAxisGeometry = boundingPolygonCreator.createTimeAxisGeometrySplitted(longitudes, latitudes, numSplits);
+ } else {
+ timeAxisGeometry = boundingPolygonCreator.createTimeAxisGeometry(longitudes, latitudes);
+ }
+
+ geometries.setBoundingGeometry(boundingGeometry);
+ geometries.setTimeAxesGeometry(timeAxisGeometry);
+
+ return geometries;
+ }
+
+ private BoundingPolygonCreator getBoundingPolygonCreator(Interval interval) {
+ return new BoundingPolygonCreator(interval, geometryFactory);
+ }
+}
diff --git a/core/src/main/java/com/bc/fiduceo/reader/amsu_mhs/nat/DATA_LAYOUT.java b/core/src/main/java/com/bc/fiduceo/reader/amsu_mhs/nat/DATA_LAYOUT.java
new file mode 100644
index 000000000..c628aa044
--- /dev/null
+++ b/core/src/main/java/com/bc/fiduceo/reader/amsu_mhs/nat/DATA_LAYOUT.java
@@ -0,0 +1,27 @@
+package com.bc.fiduceo.reader.amsu_mhs.nat;
+
+public enum DATA_LAYOUT {
+
+ VECTOR,
+ ARRAY;
+
+ public static DATA_LAYOUT fromString(String dataLayout) {
+ if (dataLayout.equals("ARRAY")) {
+ return DATA_LAYOUT.ARRAY;
+ } else if (dataLayout.equals("VECTOR")) {
+ return DATA_LAYOUT.VECTOR;
+ } else {
+ throw new IllegalArgumentException("Unsupported data layout: " + dataLayout);
+ }
+ }
+
+ public static String toString(DATA_LAYOUT dataLayout) {
+ if (dataLayout == DATA_LAYOUT.VECTOR) {
+ return "VECTOR";
+ } else if (dataLayout == DATA_LAYOUT.ARRAY) {
+ return "ARRAY";
+ } else {
+ throw new IllegalArgumentException("Unsupported data layout: " + dataLayout);
+ }
+ }
+}
diff --git a/core/src/main/java/com/bc/fiduceo/reader/amsu_mhs/nat/EPS_Constants.java b/core/src/main/java/com/bc/fiduceo/reader/amsu_mhs/nat/EPS_Constants.java
new file mode 100644
index 000000000..a51519b4c
--- /dev/null
+++ b/core/src/main/java/com/bc/fiduceo/reader/amsu_mhs/nat/EPS_Constants.java
@@ -0,0 +1,19 @@
+package com.bc.fiduceo.reader.amsu_mhs.nat;
+
+public class EPS_Constants {
+
+ public static final int GENERIC_RECORD_HEADER_SIZE = 20;
+
+ public static final int MHS_FOV_COUNT = 90;
+ public static final int MHS_L1B_EARTH_LOCATIONS_OFFSET = 3318;
+ public static final int MHS_EARTH_LOCATIONS_TOTAL_BYTE_SIZE = 720;
+ public static final int MHS_EARTH_LOCATIONS_SCALE_FACTOR = 10000;
+
+ public static final int AMSUA_FOV_COUNT = 30;
+
+ public static String SENSING_START_KEY = "SENSING_START";
+ public static String SENSING_STOP_KEY = "SENSING_END";
+
+ public static String LON_VAR_NAME = "longitude";
+ public static String LAT_VAR_NAME = "latitude";
+}
diff --git a/core/src/main/java/com/bc/fiduceo/reader/amsu_mhs/nat/EpsReaderUtils.java b/core/src/main/java/com/bc/fiduceo/reader/amsu_mhs/nat/EpsReaderUtils.java
new file mode 100644
index 000000000..08836ce0b
--- /dev/null
+++ b/core/src/main/java/com/bc/fiduceo/reader/amsu_mhs/nat/EpsReaderUtils.java
@@ -0,0 +1,164 @@
+package com.bc.fiduceo.reader.amsu_mhs.nat;
+
+import com.bc.fiduceo.reader.ReaderUtils;
+import org.esa.snap.core.datamodel.ProductData;
+import ucar.ma2.*;
+
+import java.math.BigInteger;
+import java.nio.ByteBuffer;
+
+public class EpsReaderUtils {
+
+ /**
+ * reads eps data types "byte", "boolean", "enumerated" from buffer
+ */
+ public static byte readInt8(ByteBuffer buffer, int offset) {
+ return buffer.get(offset);
+ }
+
+ /**
+ * reads eps data types "u-byte" from buffer
+ */
+ public static short readUInt8(ByteBuffer buffer, int offset) {
+ return (short) Byte.toUnsignedInt(buffer.get(offset));
+ }
+
+ /**
+ * reads eps data types "integer2" from buffer
+ */
+ public static short readInt16(ByteBuffer buffer, int offset) {
+ return buffer.getShort(offset);
+ }
+
+ /**
+ * reads eps data types "u-integer2" from buffer
+ */
+ public static int readUInt16(ByteBuffer buffer, int offset) {
+ return Short.toUnsignedInt(buffer.getShort(offset));
+ }
+
+ /**
+ * reads eps data types "integer-4" from buffer
+ */
+ public static int readInt32(ByteBuffer buffer, int offset) {
+ return buffer.getInt(offset);
+ }
+
+ /**
+ * reads eps data types "u-integer4" from buffer
+ */
+ public static long readUInt32(ByteBuffer buffer, int offset) {
+ return Integer.toUnsignedLong(buffer.getInt(offset));
+ }
+
+ /**
+ * reads eps data types "integer8" from buffer
+ */
+ public static long readInt64(ByteBuffer buffer, int offset) {
+ return buffer.getLong(offset);
+ }
+
+ /**
+ * reads eps data types "u-integer8" from buffer
+ */
+ public static BigInteger readUInt64(ByteBuffer buffer, int offset) {
+ byte[] bytes = new byte[8];
+ buffer.position(offset);
+ buffer.get(bytes);
+ return new BigInteger(1, bytes); // unsigned interpretation
+ }
+
+ public static int mapToProductData(String value) {
+ switch (value.toLowerCase()) {
+ case "byte":
+ case "boolean":
+ case "enumerated":
+ return ProductData.TYPE_INT8;
+ case "u-byte":
+ return ProductData.TYPE_UINT8;
+ case "integer2":
+ return ProductData.TYPE_INT16;
+ case "u-integer2":
+ return ProductData.TYPE_UINT16;
+ case "integer4":
+ return ProductData.TYPE_INT32;
+ case "u-integer4":
+ return ProductData.TYPE_UINT32;
+ case "integer8":
+ return ProductData.TYPE_INT64;
+ case "u-integer8":
+ return ProductData.TYPE_UINT64;
+ default:
+ throw new IllegalArgumentException("Unknown data type: " + value);
+ }
+ }
+
+ public static Array scale(Array array, double scaleFactor) {
+ if (ReaderUtils.mustScale(scaleFactor, 0.0)) {
+ final MAMath.ScaleOffset scaleOffset = new MAMath.ScaleOffset(scaleFactor, 0.0);
+ return MAMath.convert2Unpacked(array, scaleOffset);
+ }
+ return array;
+ }
+
+ public static Array initializeArray(int dataType, int numScanLines, int numFOVs) {
+ Array array;
+
+ switch (dataType) {
+ case ProductData.TYPE_INT8:
+ array = new ArrayByte.D2(numScanLines, numFOVs, false);
+ break;
+ case ProductData.TYPE_UINT8:
+ array = new ArrayByte.D2(numScanLines, numFOVs, true);
+ break;
+ case ProductData.TYPE_INT16:
+ array = new ArrayShort.D2(numScanLines, numFOVs, false);
+ break;
+ case ProductData.TYPE_UINT16:
+ array = new ArrayShort.D2(numScanLines, numFOVs, true);
+ break;
+ case ProductData.TYPE_INT32:
+ array = new ArrayInt.D2(numScanLines, numFOVs, false);
+ break;
+ case ProductData.TYPE_UINT32:
+ array = new ArrayInt.D2(numScanLines, numFOVs, true);
+ break;
+ case ProductData.TYPE_INT64:
+ array = new ArrayLong.D2(numScanLines, numFOVs, false);
+ break;
+ case ProductData.TYPE_UINT64:
+ array = new ArrayLong.D2(numScanLines, numFOVs, true);
+ break;
+ default:
+ array = new ArrayDouble.D2(numScanLines, numFOVs);
+ }
+
+ if (numFOVs == 1) {
+ array = array.reduce();
+ }
+ return array;
+ }
+
+ public static Number getFillValue(String dataType) {
+ switch (dataType) {
+ case "byte":
+ return Byte.MIN_VALUE;
+ case "u-byte":
+ return 255;
+ case "integer2":
+ return Short.MIN_VALUE;
+ case "u-integer2":
+ return 65535;
+ case "integer4":
+ return Integer.MIN_VALUE;
+ case "u-integer4":
+ return 4294967295L;
+ case "integer8":
+ return Long.MIN_VALUE;
+ case "u-integer8":
+ return Long.MAX_VALUE; // @todo 2 tb/* check how we can handle uint64 in Java anyways 2025-09-16
+ default:
+ throw new IllegalArgumentException("Unsupported data type: " + dataType);
+ }
+ }
+}
diff --git a/core/src/main/java/com/bc/fiduceo/reader/amsu_mhs/nat/EpsVariableCache.java b/core/src/main/java/com/bc/fiduceo/reader/amsu_mhs/nat/EpsVariableCache.java
new file mode 100644
index 000000000..df4809f8f
--- /dev/null
+++ b/core/src/main/java/com/bc/fiduceo/reader/amsu_mhs/nat/EpsVariableCache.java
@@ -0,0 +1,128 @@
+package com.bc.fiduceo.reader.amsu_mhs.nat;
+
+import com.bc.fiduceo.reader.ReaderUtils;
+import com.bc.fiduceo.reader.amsu_mhs.nat.record_types.MDR;
+import com.bc.fiduceo.reader.amsu_mhs.nat.record_types.MPHR;
+import org.esa.snap.core.datamodel.ProductData;
+import ucar.ma2.*;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class EpsVariableCache {
+
+ private final int numFOVs;
+ private final List records;
+ private final VariableRegistry registry;
+
+ private final Map rawDataCache;
+
+ public EpsVariableCache(byte[] rawDataBuffer, VariableRegistry registry, int numFOVs) {
+ this.records = RecordFactory.parseRecords(rawDataBuffer);
+ this.registry = registry;
+ this.rawDataCache = new HashMap<>();
+ this.numFOVs = numFOVs;
+ }
+
+ public void clear() {
+ rawDataCache.clear();
+ }
+
+ public MPHR getMPHR() {
+ return MdrUtilities.getMphr(records);
+ }
+
+ public List getMdrs() {
+ // @todo 2 tb this should be a lazy-loaded instance
+ return MdrUtilities.getMdrList(records);
+ }
+
+ public Array getRaw(String variableName) {
+ Array array = rawDataCache.get(variableName);
+
+ if (array == null) {
+ array = decodeRawDataArray(variableName);
+ rawDataCache.put(variableName, array);
+ }
+
+ return array;
+ }
+
+ public Array getScaled(String variableName) {
+ final Array rawArray = getRaw(variableName);
+
+ final VariableDefinition varDef = registry.getVariableDef(variableName);
+ double scaleFactor = varDef.getScale_factor();
+ return EpsReaderUtils.scale(rawArray, scaleFactor);
+ }
+
+ private Array decodeRawDataArray(String variableName) {
+ Array array;
+ VariableDefinition varDef = registry.getVariableDef(variableName);
+ List mdrs = getMdrs();
+ int numScanLines = mdrs.size();
+ int stride = varDef.getStride();
+ int offset = varDef.getOffset();
+ int dataType = varDef.getProductData_type();
+ int size = ProductData.getElemSize(dataType);
+
+ DATA_LAYOUT dataLayout = DATA_LAYOUT.fromString(varDef.getData_layout());
+ if (dataLayout == DATA_LAYOUT.ARRAY) {
+ array = EpsReaderUtils.initializeArray(dataType, numScanLines, numFOVs);
+
+ for (int yy = 0; yy < numScanLines; yy++) {
+ MDR mdr = mdrs.get(yy);
+ byte[] payload = mdr.getPayload();
+
+ for (int xx = 0; xx < numFOVs; xx++) {
+ int valueSpecificOffset = offset + xx * stride * size;
+ // @todo 1 tb/tb why as double???
+ double value = readValue(payload, valueSpecificOffset, varDef);
+
+ array.setDouble(array.getIndex().set(yy, xx), value);
+ }
+ }
+ return array;
+ } else if (dataLayout == DATA_LAYOUT.VECTOR) {
+ array = EpsReaderUtils.initializeArray(dataType, numScanLines, 1);
+ for (int y = 0; y < numScanLines; y++) {
+ MDR mdr = mdrs.get(y);
+ byte[] payload = mdr.getPayload();
+ double value = readValue(payload, offset, varDef);
+ array.setDouble(array.getIndex().set(y), value);
+ }
+ return array;
+ } else {
+ throw new IllegalStateException("Unsupported data layout: " + dataLayout);
+ }
+ }
+
+ private double readValue(byte[] payload, int offset, VariableDefinition def) {
+ ByteBuffer buffer = ByteBuffer.wrap(payload).order(ByteOrder.BIG_ENDIAN);
+ int type = def.getProductData_type();
+
+ switch (type) {
+ case ProductData.TYPE_INT8:
+ return EpsReaderUtils.readInt8(buffer, offset);
+ case ProductData.TYPE_UINT8:
+ return EpsReaderUtils.readUInt8(buffer, offset);
+ case ProductData.TYPE_INT16:
+ return EpsReaderUtils.readInt16(buffer, offset);
+ case ProductData.TYPE_UINT16:
+ return EpsReaderUtils.readUInt16(buffer, offset);
+ case ProductData.TYPE_INT32:
+ return EpsReaderUtils.readInt32(buffer, offset);
+ case ProductData.TYPE_UINT32:
+ return EpsReaderUtils.readUInt32(buffer, offset);
+ case ProductData.TYPE_INT64:
+ return EpsReaderUtils.readInt64(buffer, offset);
+ case ProductData.TYPE_UINT64:
+ return EpsReaderUtils.readUInt64(buffer, offset).doubleValue();
+ default:
+ throw new IllegalArgumentException("Unsupported data type: " + type);
+ }
+ }
+}
diff --git a/core/src/main/java/com/bc/fiduceo/reader/amsu_mhs/nat/GENERIC_RECORD_HEADER.java b/core/src/main/java/com/bc/fiduceo/reader/amsu_mhs/nat/GENERIC_RECORD_HEADER.java
new file mode 100644
index 000000000..b798e301f
--- /dev/null
+++ b/core/src/main/java/com/bc/fiduceo/reader/amsu_mhs/nat/GENERIC_RECORD_HEADER.java
@@ -0,0 +1,51 @@
+package com.bc.fiduceo.reader.amsu_mhs.nat;
+
+import java.nio.ByteBuffer;
+
+public class GENERIC_RECORD_HEADER {
+
+ private final RECORD_CLASS recordClass;
+ private final INSTRUMENT_GROUP instrumentGroup;
+ private final int recordSize;
+ private final byte recordSubClass;
+ private final byte recordSubClassVersion;
+
+ GENERIC_RECORD_HEADER(RECORD_CLASS recordClass, INSTRUMENT_GROUP instrumentGroup, byte recordSubClass, byte recordSubClassVersion, int recordSize) {
+ this.recordClass = recordClass;
+ this.instrumentGroup = instrumentGroup;
+ this.recordSize = recordSize;
+ this.recordSubClass = recordSubClass;
+ this.recordSubClassVersion = recordSubClassVersion;
+ }
+
+ public static GENERIC_RECORD_HEADER parse(byte[] data) {
+ final RECORD_CLASS recordClass = RECORD_CLASS.fromByte(data[0]);
+ final INSTRUMENT_GROUP instrumentGroup = INSTRUMENT_GROUP.fromByte(data[1]);
+ final byte recordSubClass = data[2];
+ final byte recordSubClassVersion = data[3];
+
+ final ByteBuffer buffer = ByteBuffer.wrap(data,4,4);
+ final int recordSize = buffer.getInt();
+ return new GENERIC_RECORD_HEADER(recordClass, instrumentGroup, recordSubClass,recordSubClassVersion, recordSize);
+ }
+
+ public RECORD_CLASS getRecordClass() {
+ return recordClass;
+ }
+
+ public INSTRUMENT_GROUP getInstrumentGroup() {
+ return instrumentGroup;
+ }
+
+ public int getRecordSize() {
+ return recordSize;
+ }
+
+ public byte getRecordSubClass() {
+ return recordSubClass;
+ }
+
+ public byte getRecordSubClassVersion() {
+ return recordSubClassVersion;
+ }
+}
diff --git a/core/src/main/java/com/bc/fiduceo/reader/amsu_mhs/nat/INSTRUMENT_GROUP.java b/core/src/main/java/com/bc/fiduceo/reader/amsu_mhs/nat/INSTRUMENT_GROUP.java
new file mode 100644
index 000000000..6d14d05bb
--- /dev/null
+++ b/core/src/main/java/com/bc/fiduceo/reader/amsu_mhs/nat/INSTRUMENT_GROUP.java
@@ -0,0 +1,59 @@
+package com.bc.fiduceo.reader.amsu_mhs.nat;
+
+public enum INSTRUMENT_GROUP {
+ GENERIC,
+ AMSUA,
+ ASCAT,
+ ATOVS,
+ AVHRR3,
+ GOME,
+ GRAS,
+ HIRS4,
+ IASI,
+ MHS,
+ SEM,
+ ADCS,
+ SBUV,
+ DUMMY,
+ IASI_L2,
+ ARCHIVE;
+
+ public static INSTRUMENT_GROUP fromByte(byte b) {
+ switch (b) {
+ case 0:
+ return GENERIC;
+ case 1:
+ return AMSUA;
+ case 2:
+ return ASCAT;
+ case 3:
+ return ATOVS;
+ case 4:
+ return AVHRR3;
+ case 5:
+ return GOME;
+ case 6:
+ return GRAS;
+ case 7:
+ return HIRS4;
+ case 8:
+ return IASI;
+ case 9:
+ return MHS;
+ case 10:
+ return SEM;
+ case 11:
+ return ADCS;
+ case 12:
+ return SBUV;
+ case 13:
+ return DUMMY;
+ case 15:
+ return IASI_L2;
+ case 99:
+ return ARCHIVE;
+ default:
+ throw new IllegalArgumentException("Unknown instrument group: " + b);
+ }
+ }
+}
diff --git a/core/src/main/java/com/bc/fiduceo/reader/amsu_mhs/nat/MdrUtilities.java b/core/src/main/java/com/bc/fiduceo/reader/amsu_mhs/nat/MdrUtilities.java
new file mode 100644
index 000000000..5302a54dd
--- /dev/null
+++ b/core/src/main/java/com/bc/fiduceo/reader/amsu_mhs/nat/MdrUtilities.java
@@ -0,0 +1,28 @@
+package com.bc.fiduceo.reader.amsu_mhs.nat;
+
+import com.bc.fiduceo.reader.amsu_mhs.nat.record_types.MDR;
+import com.bc.fiduceo.reader.amsu_mhs.nat.record_types.MPHR;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class MdrUtilities {
+
+ public static MPHR getMphr(List records) {
+ return (MPHR) records.get(0);
+ }
+
+ public static List getMdrList(List records) {
+ List mdrs = new ArrayList<>();
+
+ for (Record record : records) {
+ GENERIC_RECORD_HEADER header = record.getHeader();
+ RECORD_CLASS recordClass = header.getRecordClass();
+
+ if (recordClass == RECORD_CLASS.MDR) {
+ mdrs.add((MDR) record);
+ }
+ }
+ return mdrs;
+ }
+}
diff --git a/core/src/main/java/com/bc/fiduceo/reader/amsu_mhs/nat/RECORD_CLASS.java b/core/src/main/java/com/bc/fiduceo/reader/amsu_mhs/nat/RECORD_CLASS.java
new file mode 100644
index 000000000..47bd982f3
--- /dev/null
+++ b/core/src/main/java/com/bc/fiduceo/reader/amsu_mhs/nat/RECORD_CLASS.java
@@ -0,0 +1,38 @@
+package com.bc.fiduceo.reader.amsu_mhs.nat;
+
+public enum RECORD_CLASS {
+ RESERVED,
+ MPHR, // Main Product Header Record
+ SPHR, // Secondary Product Header Record
+ IPR, // Internal Pointer Record
+ GEADR, // Global External Auxiliary Data Record
+ GIADR, // Global Internal Auxiliary Data Record
+ VEADR, // Variable External Auxiliary Data Record
+ VIADR, // Variable Internal Auxiliary Data Record
+ MDR; // Measurement Data Record
+
+ public static RECORD_CLASS fromByte(byte b) {
+ switch (b) {
+ case 0:
+ return RESERVED;
+ case 1:
+ return MPHR;
+ case 2:
+ return SPHR;
+ case 3:
+ return IPR;
+ case 4:
+ return GEADR;
+ case 5:
+ return GIADR;
+ case 6:
+ return VEADR;
+ case 7:
+ return VIADR;
+ case 8:
+ return MDR;
+ default:
+ throw new IllegalArgumentException("Unknown record class: " + b);
+ }
+ }
+}
diff --git a/core/src/main/java/com/bc/fiduceo/reader/amsu_mhs/nat/Record.java b/core/src/main/java/com/bc/fiduceo/reader/amsu_mhs/nat/Record.java
new file mode 100644
index 000000000..49dccebe8
--- /dev/null
+++ b/core/src/main/java/com/bc/fiduceo/reader/amsu_mhs/nat/Record.java
@@ -0,0 +1,20 @@
+package com.bc.fiduceo.reader.amsu_mhs.nat;
+
+public class Record {
+
+ private final GENERIC_RECORD_HEADER header;
+ private final byte[] payload;
+
+ public Record(GENERIC_RECORD_HEADER header, byte[] payload) {
+ this.header = header;
+ this.payload = payload;
+ }
+
+ public GENERIC_RECORD_HEADER getHeader() {
+ return header;
+ }
+
+ public byte[] getPayload() {
+ return payload;
+ }
+}
diff --git a/core/src/main/java/com/bc/fiduceo/reader/amsu_mhs/nat/RecordFactory.java b/core/src/main/java/com/bc/fiduceo/reader/amsu_mhs/nat/RecordFactory.java
new file mode 100644
index 000000000..d1f74c43c
--- /dev/null
+++ b/core/src/main/java/com/bc/fiduceo/reader/amsu_mhs/nat/RecordFactory.java
@@ -0,0 +1,64 @@
+package com.bc.fiduceo.reader.amsu_mhs.nat;
+
+import com.bc.fiduceo.reader.amsu_mhs.nat.record_types.*;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static com.bc.fiduceo.reader.amsu_mhs.nat.INSTRUMENT_GROUP.AMSUA;
+
+public class RecordFactory {
+
+ public static List parseRecords(byte[] allBytes) {
+ List records = new ArrayList<>();
+ int index = 0;
+
+ while (index < allBytes.length) {
+ byte[] headerBytes = new byte[EPS_Constants.GENERIC_RECORD_HEADER_SIZE];
+ System.arraycopy(allBytes, index, headerBytes, 0, EPS_Constants.GENERIC_RECORD_HEADER_SIZE);
+
+ GENERIC_RECORD_HEADER header = GENERIC_RECORD_HEADER.parse(headerBytes);
+ int recordSize = header.getRecordSize();
+ byte[] payload = new byte[recordSize];
+ System.arraycopy(allBytes, index, payload, 0, recordSize);
+
+ Record record = createRecord(header, payload);
+ records.add(record);
+
+ index += recordSize;
+ }
+
+ return records;
+ }
+
+ public static Record createRecord(GENERIC_RECORD_HEADER header, byte[] payload) {
+ switch (header.getRecordClass()) {
+ case MPHR:
+ return new MPHR(header, payload);
+ case SPHR:
+ return new SPHR(header, payload);
+ case IPR:
+ return new IPR(header, payload);
+ case GEADR:
+ return new GEADR(header, payload);
+ case GIADR:
+ return new GIADR(header, payload);
+ case VEADR:
+ return new VEADR(header, payload);
+ case VIADR:
+ return new VIADR(header, payload);
+ case MDR:
+ return createMDR(header, payload);
+ default:
+ return new Record(header, payload);
+ }
+ }
+
+ private static MDR createMDR(GENERIC_RECORD_HEADER header, byte[] payload) {
+ if (header.getInstrumentGroup() == AMSUA) {
+ return new ASMUSA_MDR(header, payload);
+ }
+
+ return new MDR(header, payload);
+ }
+}
diff --git a/core/src/main/java/com/bc/fiduceo/reader/amsu_mhs/nat/VariableDefinition.java b/core/src/main/java/com/bc/fiduceo/reader/amsu_mhs/nat/VariableDefinition.java
new file mode 100644
index 000000000..6dcef4c06
--- /dev/null
+++ b/core/src/main/java/com/bc/fiduceo/reader/amsu_mhs/nat/VariableDefinition.java
@@ -0,0 +1,98 @@
+package com.bc.fiduceo.reader.amsu_mhs.nat;
+
+public class VariableDefinition {
+
+ private String data_type;
+ private String data_layout;
+ private int offset;
+ private int stride;
+ private double scale_factor;
+ private String units;
+ private String flag_values;
+ private String flag_meanings;
+ private String standard_name;
+
+ public VariableDefinition() {
+ scale_factor = 1.0;
+ units = "";
+ data_layout = "ARRAY";
+ offset = 0;
+ stride = 0;
+ }
+
+ public int getProductData_type() {
+ return EpsReaderUtils.mapToProductData(data_type);
+ }
+
+ public String getData_type() {
+ return data_type;
+ }
+
+ public void setData_type(String data_type) {
+ this.data_type = data_type;
+ }
+
+ public int getOffset() {
+ return offset;
+ }
+
+ public void setOffset(int offset) {
+ this.offset = offset;
+ }
+
+ public int getStride() {
+ return stride;
+ }
+
+ public void setStride(int stride) {
+ this.stride = stride;
+ }
+
+ public double getScale_factor() {
+ return scale_factor;
+ }
+
+ public void setScale_factor(double scale_factor) {
+ this.scale_factor = scale_factor;
+ }
+
+ public String getUnits() {
+ return units;
+ }
+
+ public void setUnits(String units) {
+ this.units = units;
+ }
+
+ public String getFlag_values() {
+ return flag_values;
+ }
+
+ public void setFlag_values(String flag_values) {
+ this.flag_values = flag_values;
+ }
+
+ public String getFlag_meanings() {
+ return flag_meanings;
+ }
+
+ public void setFlag_meanings(String flag_meanings) {
+ this.flag_meanings = flag_meanings;
+ }
+
+ public String getData_layout() {
+ return data_layout;
+ }
+
+ public void setData_layout(String data_layout) {
+ this.data_layout = data_layout;
+ }
+
+ public String getStandard_name() {
+ return standard_name;
+ }
+
+ public void setStandard_name(String standard_name) {
+ this.standard_name = standard_name;
+ }
+}
diff --git a/core/src/main/java/com/bc/fiduceo/reader/amsu_mhs/nat/VariableRegistry.java b/core/src/main/java/com/bc/fiduceo/reader/amsu_mhs/nat/VariableRegistry.java
new file mode 100644
index 000000000..b3fb67c47
--- /dev/null
+++ b/core/src/main/java/com/bc/fiduceo/reader/amsu_mhs/nat/VariableRegistry.java
@@ -0,0 +1,97 @@
+package com.bc.fiduceo.reader.amsu_mhs.nat;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+
+public class VariableRegistry {
+
+ private Map variables;
+
+ public static VariableRegistry load(String resourceKey) {
+ ObjectMapper mapper = new ObjectMapper();
+ String path = resourceKey + "/variables.json";
+ try (InputStream in = VariableRegistry.class.getResourceAsStream(path)) {
+ if (in == null) {
+ throw new RuntimeException("Resource not found: " + path);
+ }
+ final VariableRegistry variableRegistry = mapper.readValue(in, VariableRegistry.class);
+ variableRegistry.initialize();
+ return variableRegistry;
+ } catch (IOException e) {
+ throw new RuntimeException("Failed to load registry from: " + path, e);
+ }
+ }
+
+ public void clear() {
+ if (variables != null) {
+ variables.clear();
+ }
+ }
+
+ private void initialize() {
+ final ArrayList toBeDeleted = new ArrayList<>();
+ final HashMap toBeAdded = new HashMap<>();
+
+ for (final String key: variables.keySet()) {
+ if (key.contains("*")) {
+ // expand to bands wise reading variable
+ // and mark to be removed
+ final VariableDefinition variableDefinition = variables.get(key);
+ toBeAdded.putAll(expandWildcardVariable(key, variableDefinition));
+ toBeDeleted.add(key);
+ }
+ }
+
+ for (String key: toBeDeleted) {
+ variables.remove(key);
+ }
+ variables.putAll(toBeAdded);
+ }
+
+ private Map expandWildcardVariable(String key, VariableDefinition variableDefinition) {
+ final Map expanded = new HashMap<>();
+ final int stride = variableDefinition.getStride();
+ final int initialOffset = variableDefinition.getOffset();
+
+ for (int offset = 0; offset < stride; offset++) {
+ final VariableDefinition layerVariableDefinition = new VariableDefinition();
+ layerVariableDefinition.setOffset(initialOffset + 4 * offset);
+ layerVariableDefinition.setStride(stride);
+ layerVariableDefinition.setScale_factor(variableDefinition.getScale_factor());
+ layerVariableDefinition.setData_type(variableDefinition.getData_type());
+ layerVariableDefinition.setUnits(variableDefinition.getUnits());
+ layerVariableDefinition.setStandard_name(variableDefinition.getStandard_name());
+
+ final String layerKey = buildLayerKey(key, offset);
+ expanded.put(layerKey, layerVariableDefinition);
+ }
+
+ return expanded;
+ }
+
+ private String buildLayerKey(String key, int offset) {
+ String bandNumber = String.format("%02d", offset + 1);
+ return key.replace("*", bandNumber);
+ }
+
+ public Map getVariables() {
+ return variables;
+ }
+
+ public void setVariables(Map variables) {
+ this.variables = variables;
+ }
+
+ public VariableDefinition getVariableDef(String variableName) {
+ VariableDefinition def = variables.get(variableName);
+ if (def == null) {
+ throw new IllegalArgumentException("Variable not defined: " + variableName);
+ }
+ return def;
+ }
+}
diff --git a/core/src/main/java/com/bc/fiduceo/reader/amsu_mhs/nat/record_types/ASMUSA_MDR.java b/core/src/main/java/com/bc/fiduceo/reader/amsu_mhs/nat/record_types/ASMUSA_MDR.java
new file mode 100644
index 000000000..c91fb9e05
--- /dev/null
+++ b/core/src/main/java/com/bc/fiduceo/reader/amsu_mhs/nat/record_types/ASMUSA_MDR.java
@@ -0,0 +1,10 @@
+package com.bc.fiduceo.reader.amsu_mhs.nat.record_types;
+
+import com.bc.fiduceo.reader.amsu_mhs.nat.GENERIC_RECORD_HEADER;
+
+public class ASMUSA_MDR extends MDR {
+
+ public ASMUSA_MDR(GENERIC_RECORD_HEADER header, byte[] payload) {
+ super(header, payload);
+ }
+}
diff --git a/core/src/main/java/com/bc/fiduceo/reader/amsu_mhs/nat/record_types/GEADR.java b/core/src/main/java/com/bc/fiduceo/reader/amsu_mhs/nat/record_types/GEADR.java
new file mode 100644
index 000000000..be55cb750
--- /dev/null
+++ b/core/src/main/java/com/bc/fiduceo/reader/amsu_mhs/nat/record_types/GEADR.java
@@ -0,0 +1,11 @@
+package com.bc.fiduceo.reader.amsu_mhs.nat.record_types;
+
+import com.bc.fiduceo.reader.amsu_mhs.nat.GENERIC_RECORD_HEADER;
+import com.bc.fiduceo.reader.amsu_mhs.nat.Record;
+
+public class GEADR extends Record {
+
+ public GEADR(GENERIC_RECORD_HEADER header, byte[] payload) {
+ super(header, payload);
+ }
+}
diff --git a/core/src/main/java/com/bc/fiduceo/reader/amsu_mhs/nat/record_types/GIADR.java b/core/src/main/java/com/bc/fiduceo/reader/amsu_mhs/nat/record_types/GIADR.java
new file mode 100644
index 000000000..082fe2843
--- /dev/null
+++ b/core/src/main/java/com/bc/fiduceo/reader/amsu_mhs/nat/record_types/GIADR.java
@@ -0,0 +1,11 @@
+package com.bc.fiduceo.reader.amsu_mhs.nat.record_types;
+
+import com.bc.fiduceo.reader.amsu_mhs.nat.GENERIC_RECORD_HEADER;
+import com.bc.fiduceo.reader.amsu_mhs.nat.Record;
+
+public class GIADR extends Record {
+
+ public GIADR(GENERIC_RECORD_HEADER header, byte[] payload) {
+ super(header, payload);
+ }
+}
diff --git a/core/src/main/java/com/bc/fiduceo/reader/amsu_mhs/nat/record_types/IPR.java b/core/src/main/java/com/bc/fiduceo/reader/amsu_mhs/nat/record_types/IPR.java
new file mode 100644
index 000000000..719fc6d98
--- /dev/null
+++ b/core/src/main/java/com/bc/fiduceo/reader/amsu_mhs/nat/record_types/IPR.java
@@ -0,0 +1,11 @@
+package com.bc.fiduceo.reader.amsu_mhs.nat.record_types;
+
+import com.bc.fiduceo.reader.amsu_mhs.nat.GENERIC_RECORD_HEADER;
+import com.bc.fiduceo.reader.amsu_mhs.nat.Record;
+
+public class IPR extends Record {
+
+ public IPR(GENERIC_RECORD_HEADER header, byte[] payload) {
+ super(header, payload);
+ }
+}
diff --git a/core/src/main/java/com/bc/fiduceo/reader/amsu_mhs/nat/record_types/MDR.java b/core/src/main/java/com/bc/fiduceo/reader/amsu_mhs/nat/record_types/MDR.java
new file mode 100644
index 000000000..0ff510d44
--- /dev/null
+++ b/core/src/main/java/com/bc/fiduceo/reader/amsu_mhs/nat/record_types/MDR.java
@@ -0,0 +1,38 @@
+package com.bc.fiduceo.reader.amsu_mhs.nat.record_types;
+
+import com.bc.fiduceo.reader.amsu_mhs.nat.GENERIC_RECORD_HEADER;
+import com.bc.fiduceo.reader.amsu_mhs.nat.Record;
+import org.apache.commons.lang3.Conversion;
+
+public class MDR extends Record {
+
+ public MDR(GENERIC_RECORD_HEADER header, byte[] payload) {
+ super(header, payload);
+ }
+
+ public int[] parseVariable(String variableName) {
+ byte[] payload = getPayload();
+ int offset = 0;
+ int count = 0;
+ int stride = 0;
+ int size = 0;
+ if (variableName.equals("latitude")) {
+ offset = 2082;
+ count = 30;
+ stride = 2;
+ size = 4;
+ } else if (variableName.equals("longitude")) {
+ offset = 2086;
+ count = 30;
+ stride = 2;
+ size = 4;
+ }
+
+
+ int[] variableRawData = new int[count];
+ for (int i = 0; i < count; i++) {
+ variableRawData[i] = Conversion.byteArrayToInt(payload, offset + stride * i * size, variableRawData[i], 0, size);
+ }
+ return variableRawData;
+ }
+}
diff --git a/core/src/main/java/com/bc/fiduceo/reader/amsu_mhs/nat/record_types/MPHR.java b/core/src/main/java/com/bc/fiduceo/reader/amsu_mhs/nat/record_types/MPHR.java
new file mode 100644
index 000000000..a8b4ff742
--- /dev/null
+++ b/core/src/main/java/com/bc/fiduceo/reader/amsu_mhs/nat/record_types/MPHR.java
@@ -0,0 +1,84 @@
+package com.bc.fiduceo.reader.amsu_mhs.nat.record_types;
+
+import com.bc.fiduceo.reader.amsu_mhs.nat.GENERIC_RECORD_HEADER;
+import com.bc.fiduceo.reader.amsu_mhs.nat.Record;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.TimeZone;
+
+import static com.bc.fiduceo.reader.amsu_mhs.nat.EPS_Constants.SENSING_START_KEY;
+import static com.bc.fiduceo.reader.amsu_mhs.nat.EPS_Constants.SENSING_STOP_KEY;
+
+public class MPHR extends Record {
+
+ public static final int PRODUCT_NAME_OFFSET = 20;
+ public static final int STRING_SIZE = 100;
+ private final HashMap attributesMap;
+
+ public MPHR(GENERIC_RECORD_HEADER header, byte[] payload) {
+ super(header, payload);
+ attributesMap = new HashMap<>();
+ }
+
+ public Date getDate(String key) throws IOException {
+ String attributeValue = attributesMap.get(key);
+ if (attributeValue == null) {
+ int offset;
+ if (key.equals(SENSING_START_KEY)) {
+ offset = 700;
+ } else if (key.equals(SENSING_STOP_KEY)) {
+ offset = 748;
+ } else {
+ throw new IllegalStateException("Unknown attribute key: " + key);
+ }
+ attributeValue = extractStringAttribute(offset, 48);
+ attributesMap.put(key, attributeValue);
+ }
+
+ return parseDate(attributeValue);
+ }
+
+ public Date getSensingStart() throws IOException {
+ return getDate(SENSING_START_KEY);
+ }
+
+ public Date getSensingStop() throws IOException {
+ return getDate(SENSING_STOP_KEY);
+ }
+
+
+ private Date parseDate(String date) throws IOException {
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssX");
+ sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
+ try {
+ return sdf.parse(date);
+ } catch (ParseException e) {
+ throw new IOException("Could not parse time: " + date, e);
+ }
+ }
+
+ public String getProductName() {
+ String productName = attributesMap.get("PRODUCT_NAME");
+ if (productName == null) {
+ productName = extractStringAttribute(PRODUCT_NAME_OFFSET, STRING_SIZE);
+ attributesMap.put("PRODUCT_NAME", productName);
+ }
+ return productName;
+ }
+
+ private String extractStringAttribute(int offset, int size) {
+ final byte[] nameBuffer = new byte[100];
+ System.arraycopy(getPayload(), offset, nameBuffer, 0, size);
+ final String attributeString = new String(nameBuffer, StandardCharsets.US_ASCII).trim();
+ final String[] parts = attributeString.split("=");
+ if (parts.length != 2) {
+ throw new IllegalStateException("Invalid attribute formatting: " + attributeString);
+ }
+ return parts[1].trim();
+ }
+}
diff --git a/core/src/main/java/com/bc/fiduceo/reader/amsu_mhs/nat/record_types/SPHR.java b/core/src/main/java/com/bc/fiduceo/reader/amsu_mhs/nat/record_types/SPHR.java
new file mode 100644
index 000000000..92793d266
--- /dev/null
+++ b/core/src/main/java/com/bc/fiduceo/reader/amsu_mhs/nat/record_types/SPHR.java
@@ -0,0 +1,11 @@
+package com.bc.fiduceo.reader.amsu_mhs.nat.record_types;
+
+import com.bc.fiduceo.reader.amsu_mhs.nat.GENERIC_RECORD_HEADER;
+import com.bc.fiduceo.reader.amsu_mhs.nat.Record;
+
+public class SPHR extends Record {
+
+ public SPHR(GENERIC_RECORD_HEADER header, byte[] payload) {
+ super(header, payload);
+ }
+}
diff --git a/core/src/main/java/com/bc/fiduceo/reader/amsu_mhs/nat/record_types/VEADR.java b/core/src/main/java/com/bc/fiduceo/reader/amsu_mhs/nat/record_types/VEADR.java
new file mode 100644
index 000000000..81a609cd0
--- /dev/null
+++ b/core/src/main/java/com/bc/fiduceo/reader/amsu_mhs/nat/record_types/VEADR.java
@@ -0,0 +1,11 @@
+package com.bc.fiduceo.reader.amsu_mhs.nat.record_types;
+
+import com.bc.fiduceo.reader.amsu_mhs.nat.GENERIC_RECORD_HEADER;
+import com.bc.fiduceo.reader.amsu_mhs.nat.Record;
+
+public class VEADR extends Record {
+
+ public VEADR(GENERIC_RECORD_HEADER header, byte[] payload) {
+ super(header, payload);
+ }
+}
diff --git a/core/src/main/java/com/bc/fiduceo/reader/amsu_mhs/nat/record_types/VIADR.java b/core/src/main/java/com/bc/fiduceo/reader/amsu_mhs/nat/record_types/VIADR.java
new file mode 100644
index 000000000..0eae1df47
--- /dev/null
+++ b/core/src/main/java/com/bc/fiduceo/reader/amsu_mhs/nat/record_types/VIADR.java
@@ -0,0 +1,11 @@
+package com.bc.fiduceo.reader.amsu_mhs.nat.record_types;
+
+import com.bc.fiduceo.reader.amsu_mhs.nat.GENERIC_RECORD_HEADER;
+import com.bc.fiduceo.reader.amsu_mhs.nat.Record;
+
+public class VIADR extends Record {
+
+ public VIADR(GENERIC_RECORD_HEADER header, byte[] payload) {
+ super(header, payload);
+ }
+}
diff --git a/core/src/main/java/com/bc/fiduceo/reader/avhrr_frac/AVHRR_FRAC_Reader.java b/core/src/main/java/com/bc/fiduceo/reader/avhrr_frac/AVHRR_FRAC_Reader.java
index ad91956cd..7a2bca870 100644
--- a/core/src/main/java/com/bc/fiduceo/reader/avhrr_frac/AVHRR_FRAC_Reader.java
+++ b/core/src/main/java/com/bc/fiduceo/reader/avhrr_frac/AVHRR_FRAC_Reader.java
@@ -7,9 +7,10 @@
import com.bc.fiduceo.reader.ReaderContext;
import com.bc.fiduceo.reader.ReaderUtils;
import com.bc.fiduceo.reader.snap.SNAP_Reader;
+import com.bc.fiduceo.reader.time.TimeLocator_StartStopDate;
import com.bc.fiduceo.reader.time.TimeLocator;
import com.bc.fiduceo.util.NetCDFUtils;
-import org.esa.s3tbx.dataio.avhrr.AvhrrConstants;
+import eu.esa.opt.dataio.avhrr.AvhrrConstants;
import org.esa.snap.core.datamodel.ProductData;
import org.esa.snap.core.datamodel.RasterDataNode;
import org.esa.snap.core.datamodel.VirtualBand;
@@ -75,7 +76,7 @@ public PixelLocator getSubScenePixelLocator(Polygon sceneGeometry) throws IOExce
public TimeLocator getTimeLocator() {
final ProductData.UTC startTime = product.getStartTime();
final ProductData.UTC endTime = product.getEndTime();
- return new AVHRR_FRAC_TimeLocator(startTime.getAsDate(), endTime.getAsDate(), product.getSceneRasterHeight());
+ return new TimeLocator_StartStopDate(startTime.getAsDate(), endTime.getAsDate(), product.getSceneRasterHeight());
}
@Override
diff --git a/core/src/main/java/com/bc/fiduceo/reader/insitu/InsituReader.java b/core/src/main/java/com/bc/fiduceo/reader/insitu/InsituReader.java
new file mode 100644
index 000000000..c5e8a16b2
--- /dev/null
+++ b/core/src/main/java/com/bc/fiduceo/reader/insitu/InsituReader.java
@@ -0,0 +1,12 @@
+package com.bc.fiduceo.reader.insitu;
+
+import com.bc.fiduceo.reader.netcdf.NetCDFReader;
+import ucar.ma2.Array;
+
+import java.io.IOException;
+
+public abstract class InsituReader extends NetCDFReader {
+
+ abstract public Array getSourceArray(String variableName) throws IOException;
+
+}
diff --git a/core/src/main/java/com/bc/fiduceo/reader/insitu/generic/CsvFormatConfig.java b/core/src/main/java/com/bc/fiduceo/reader/insitu/generic/CsvFormatConfig.java
new file mode 100644
index 000000000..ae429fc91
--- /dev/null
+++ b/core/src/main/java/com/bc/fiduceo/reader/insitu/generic/CsvFormatConfig.java
@@ -0,0 +1,165 @@
+package com.bc.fiduceo.reader.insitu.generic;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.PropertyNamingStrategies;
+import org.esa.snap.core.datamodel.ProductData;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+public class CsvFormatConfig {
+
+ private String name;
+ private String delimiter;
+ private char commentChar;
+ private String regex;
+ private String longitudeName;
+ private String latitudeName;
+ private String timeName = "time";
+ private String timeFormat;
+ private List timeVars;
+ private boolean locationFromStationDatabase;
+ private List variables;
+ private StationDatabase stationDatabase;
+
+ public static CsvFormatConfig loadConfig(String resourceKey) {
+ ObjectMapper mapper = new ObjectMapper();
+ mapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE);
+ String path = resourceKey + "_config.json";
+
+ try (InputStream in = CsvFormatConfig.class.getResourceAsStream(path)) {
+ if (in == null) {
+ throw new RuntimeException("Resource not found: " + path);
+ }
+
+ return mapper.readValue(in, CsvFormatConfig.class);
+ } catch (IOException e) {
+ throw new RuntimeException("Failed to load config from: " + path, e);
+ }
+ }
+
+ public List getAllVariables() {
+ List vars = new ArrayList<>();
+
+ if (stationDatabase != null) {
+ vars.addAll(stationDatabase.getVariables());
+ }
+
+ if (hasTimeVars()) {
+ GenericVariable time = new GenericVariable(timeName, 'v',"int", ProductData.TYPE_INT64, -1.0, "seconds since 1970-01-01", null, "time", null);
+ vars.add(time);
+
+ for (GenericVariable var : variables) {
+ if (!timeVars.contains(var.getName())) {
+ vars.add(var);
+ }
+ }
+ } else {
+ vars.addAll(variables);
+ }
+
+ return vars;
+ }
+
+ private boolean hasTimeVars() {
+ return timeVars != null && !timeVars.isEmpty();
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getDelimiter() {
+ return delimiter;
+ }
+
+ public void setDelimiter(String delimiter) {
+ this.delimiter = delimiter;
+ }
+
+ public char getCommentChar() {
+ return commentChar;
+ }
+
+ public void setCommentChar(char commentChar) {
+ this.commentChar = commentChar;
+ }
+
+ public String getRegex() {
+ return regex;
+ }
+
+ public void setRegex(String regex) {
+ this.regex = regex;
+ }
+
+ public String getLongitudeName() {
+ return longitudeName;
+ }
+
+ public void setLongitudeName(String longitudeName) {
+ this.longitudeName = longitudeName;
+ }
+
+ public String getLatitudeName() {
+ return latitudeName;
+ }
+
+ public void setLatitudeName(String latitudeName) {
+ this.latitudeName = latitudeName;
+ }
+
+ public String getTimeName() {
+ return timeName;
+ }
+
+ public void setTimeName(String timeName) {
+ this.timeName = timeName;
+ }
+
+ public String getTimeFormat() {
+ return timeFormat;
+ }
+
+ public void setTimeFormat(String timeFormat) {
+ this.timeFormat = timeFormat;
+ }
+
+ public List getTimeVars() {
+ return timeVars;
+ }
+
+ public void setTimeVars(List timeVars) {
+ this.timeVars = timeVars;
+ }
+
+ public boolean isLocationFromStationDatabase() {
+ return locationFromStationDatabase;
+ }
+
+ public void setLocationFromStationDatabase(boolean locationFromStationDatabase) {
+ this.locationFromStationDatabase = locationFromStationDatabase;
+ }
+
+ public List getVariables() {
+ return variables;
+ }
+
+ public void setVariables(List variables) {
+ this.variables = variables;
+ }
+
+ public StationDatabase getStationDatabase() {
+ return stationDatabase;
+ }
+
+ public void setStationDatabase(StationDatabase stationDatabase) {
+ this.stationDatabase = stationDatabase;
+ }
+}
diff --git a/core/src/main/java/com/bc/fiduceo/reader/insitu/generic/GbovReaderPlugin.java b/core/src/main/java/com/bc/fiduceo/reader/insitu/generic/GbovReaderPlugin.java
new file mode 100644
index 000000000..4d30942eb
--- /dev/null
+++ b/core/src/main/java/com/bc/fiduceo/reader/insitu/generic/GbovReaderPlugin.java
@@ -0,0 +1,27 @@
+package com.bc.fiduceo.reader.insitu.generic;
+
+import com.bc.fiduceo.reader.DataType;
+import com.bc.fiduceo.reader.Reader;
+import com.bc.fiduceo.reader.ReaderContext;
+import com.bc.fiduceo.reader.ReaderPlugin;
+
+
+public class GbovReaderPlugin implements ReaderPlugin {
+
+ private final String[] SUPPORTED_KEYS = {"gbov"};
+
+ @Override
+ public Reader createReader(ReaderContext readerContext) {
+ return new GenericCsvReader(GenericCsvHelper.RESOURCE_KEY_GBOV);
+ }
+
+ @Override
+ public String[] getSupportedSensorKeys() {
+ return SUPPORTED_KEYS;
+ }
+
+ @Override
+ public DataType getDataType() {
+ return DataType.INSITU;
+ }
+}
diff --git a/core/src/main/java/com/bc/fiduceo/reader/insitu/generic/GenericCsvHelper.java b/core/src/main/java/com/bc/fiduceo/reader/insitu/generic/GenericCsvHelper.java
new file mode 100644
index 000000000..16c049880
--- /dev/null
+++ b/core/src/main/java/com/bc/fiduceo/reader/insitu/generic/GenericCsvHelper.java
@@ -0,0 +1,251 @@
+package com.bc.fiduceo.reader.insitu.generic;
+
+import com.bc.fiduceo.util.TimeUtils;
+import ucar.ma2.DataType;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.time.Instant;
+import java.time.ZoneOffset;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.List;
+
+public class GenericCsvHelper {
+
+ public static final String RESOURCE_KEY_NDBC_SM = "NDBC_SM";
+ public static final String RESOURCE_KEY_NDBC_CW = "NDBC_CW";
+ public static final String RESOURCE_KEY_GBOV = "GBOV";
+
+
+ public static int[] extractYearMonthDayFromFilename(String filename, String resourceKey) {
+ int[] ymd = new int[3];
+ if (resourceKey.equals(GenericCsvHelper.RESOURCE_KEY_NDBC_SM) || resourceKey.equals(GenericCsvHelper.RESOURCE_KEY_NDBC_CW)) {
+ final int dotIndex = filename.indexOf('.');
+ final String yearString = filename.substring(dotIndex - 4, dotIndex);
+ ymd[0] = Integer.parseInt(yearString);
+ ymd[1] = 1;
+ ymd[2] = 1;
+
+ return ymd;
+ } else if (resourceKey.equals(GenericCsvHelper.RESOURCE_KEY_GBOV)) {
+ final String[] fileSplit = filename.split("__");
+ final String startDate = fileSplit[3];
+ final String yearString = startDate.substring(0, 4);
+ final String monthString = startDate.substring(4, 6);
+ final String dayString = startDate.substring(6, 8);
+ ymd[0] = Integer.parseInt(yearString);
+ ymd[1] = Integer.parseInt(monthString);
+ ymd[2] = Integer.parseInt(dayString);
+
+ return ymd;
+ }
+ throw new IllegalStateException("Unsupported format for file: '" + filename + "' and resourceKey '" + resourceKey + "'.");
+ }
+
+ public static List parseData(File file, CsvFormatConfig config, String resourceKey) throws IOException {
+ List records = new ArrayList<>();
+
+ try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
+ String line;
+ while ((line = reader.readLine()) != null) {
+ if (line.isEmpty() || line.charAt(0) == config.getCommentChar()) {
+ continue;
+ }
+
+ String[] tokens = tokenize(line, config.getDelimiter());
+ List vars = config.getVariables();
+
+ if (tokens.length < vars.size()) continue;
+
+ GenericRecord record = parseLine(tokens, vars, resourceKey, config);
+ records.add(record);
+ }
+ }
+
+ return records;
+ }
+
+ private static String[] tokenize(String line, String delimiter) {
+ if ("space".equalsIgnoreCase(delimiter)) {
+ return line.trim().split("\\s+");
+ }
+ return line.split(delimiter);
+ }
+
+ private static GenericRecord parseLine(String[] tokens, List vars, String resourceKey, CsvFormatConfig config) {
+ GenericRecord record = new GenericRecord();
+
+ if (resourceKey.equals(GenericCsvHelper.RESOURCE_KEY_NDBC_CW) || resourceKey.equals(GenericCsvHelper.RESOURCE_KEY_NDBC_SM)) {
+ GenericVariable yyVar = null, mmVar = null, ddVar = null, hhVar = null, minVar = null;
+ int yyIndex = -1, mmIndex = -1, ddIndex = -1, hhIndex = -1, minIndex = -1;
+
+ for (int ii = 0; ii < vars.size(); ii++) {
+ String name = vars.get(ii).getName();
+ switch (name) {
+ case "YY": yyVar = vars.get(ii); yyIndex = ii; break;
+ case "MM": mmVar = vars.get(ii); mmIndex = ii; break;
+ case "DD": ddVar = vars.get(ii); ddIndex = ii; break;
+ case "hh": hhVar = vars.get(ii); hhIndex = ii; break;
+ case "mm": minVar = vars.get(ii); minIndex = ii; break;
+ }
+ }
+
+ if (yyIndex >= 0 && mmIndex >= 0 && ddIndex >= 0 && hhIndex >= 0 && minIndex >= 0) {
+ int year = ((Number) parseValue(tokens[yyIndex], yyVar.getType())).intValue();
+ int month = ((Number) parseValue(tokens[mmIndex], mmVar.getType())).intValue();
+ int day = ((Number) parseValue(tokens[ddIndex], ddVar.getType())).intValue();
+ int hour = ((Number) parseValue(tokens[hhIndex], hhVar.getType())).intValue();
+ int minute = ((Number) parseValue(tokens[minIndex], minVar.getType())).intValue();
+
+ final Calendar calendar = TimeUtils.getUTCCalendar();
+ calendar.setTimeInMillis(0);
+ calendar.set(Calendar.YEAR, year);
+ calendar.set(Calendar.MONTH, month - 1); // calendar wants month zero-based
+ calendar.set(Calendar.DAY_OF_MONTH, day);
+ calendar.set(Calendar.HOUR_OF_DAY, hour);
+ calendar.set(Calendar.MINUTE, minute);
+ int seconds = (int) (calendar.getTimeInMillis() * 0.001);
+
+ record.put(config.getTimeName(), seconds);
+ }
+
+ for (int ii = 0; ii < vars.size(); ii++) {
+ String name = vars.get(ii).getName();
+ if (Arrays.asList("YY", "MM", "DD", "hh", "mm").contains(name)) continue;
+ record.put(name, parseValue(tokens[ii], vars.get(ii).getType()));
+ }
+ } else {
+ for (int ii = 0; ii < vars.size(); ii++) {
+ GenericVariable var = vars.get(ii);
+
+ if (var.getName().equals(config.getTimeName())) {
+ String value = (String) parseValue(tokens[ii], "string");
+ DateTimeFormatter formatter = DateTimeFormatter.ofPattern(config.getTimeFormat()).withZone(ZoneOffset.UTC);
+ Instant instant = Instant.from(formatter.parse(value));
+ record.put(config.getTimeName(), (int) instant.getEpochSecond());
+ continue;
+ }
+
+ record.put(var.getName(), parseValue(tokens[ii], var.getType()));
+ }
+ }
+
+ return record;
+ }
+
+ public static Object parseValue(String token, String type) {
+ if (token == null || token.isEmpty()) {
+ switch (type) {
+ case "float":
+ return Float.NaN;
+ case "double":
+ return Double.NaN;
+ default:
+ return null;
+ }
+ }
+
+ token = token.trim();
+
+ switch (type) {
+ case "byte":
+ return Byte.parseByte(token);
+ case "short":
+ return Short.parseShort(token);
+ case "int":
+ return Integer.parseInt(token);
+ case "long":
+ return Long.parseLong(token);
+ case "float":
+ return Float.parseFloat(token);
+ case "double":
+ return Double.parseDouble(token);
+ default:
+ return token;
+ }
+ }
+
+ public static String getPrimaryIdFromFilename(File file, String resourceKey) {
+ String filename = file.getName();
+ if (resourceKey.equals(RESOURCE_KEY_NDBC_SM) || resourceKey.equals(RESOURCE_KEY_NDBC_CW)) {
+ if (filename.length() >= 5) {
+ return file.getName().substring(0,5);
+ }
+ }
+ if (resourceKey.equals(RESOURCE_KEY_GBOV)) {
+ String[] split = filename.split("__");
+ return split[1].replace("--", " ");
+ }
+ throw new IllegalArgumentException("Unsupported format for file: " + file.getAbsolutePath());
+ }
+
+ public static String getSecondaryIdFromFilename(File file, String resourceKey) {
+ String filename = file.getName();
+ if (resourceKey != null && resourceKey.equals(RESOURCE_KEY_GBOV)) {
+ String[] split = filename.split("__");
+ return split[2].replace("--", " ");
+ }
+ return null;
+ }
+
+ public static DataType getNcDataType(String type) {
+ switch (type) {
+ case "byte":
+ return DataType.BYTE;
+ case "short":
+ return DataType.SHORT;
+ case "int":
+ return DataType.INT;
+ case "long":
+ return DataType.LONG;
+ case "float":
+ return DataType.FLOAT;
+ case "double":
+ return DataType.DOUBLE;
+ default:
+ return DataType.STRING;
+ }
+ }
+
+ public static Class getFillValueClass(String type) {
+ switch (type) {
+ case "byte":
+ return byte.class;
+ case "short":
+ return short.class;
+ case "int":
+ return int.class;
+ case "long":
+ return long.class;
+ case "float":
+ return float.class;
+ case "double":
+ return double.class;
+ default:
+ throw new RuntimeException("not implemented for type " + type);
+ }
+ }
+
+ public static Number castFillValue(Double fillValue, String type) {
+ switch (type.toLowerCase()) {
+ case "byte":
+ return fillValue.byteValue();
+ case "short":
+ return fillValue.shortValue();
+ case "int":
+ return fillValue.intValue();
+ case "long":
+ return fillValue.longValue();
+ case "float":
+ return fillValue.floatValue();
+ case "double":
+ default:
+ return fillValue;
+ }
+ }
+}
diff --git a/core/src/main/java/com/bc/fiduceo/reader/insitu/generic/GenericCsvReader.java b/core/src/main/java/com/bc/fiduceo/reader/insitu/generic/GenericCsvReader.java
new file mode 100644
index 000000000..40523bd5f
--- /dev/null
+++ b/core/src/main/java/com/bc/fiduceo/reader/insitu/generic/GenericCsvReader.java
@@ -0,0 +1,276 @@
+package com.bc.fiduceo.reader.insitu.generic;
+
+import com.bc.fiduceo.core.Dimension;
+import com.bc.fiduceo.core.Interval;
+import com.bc.fiduceo.core.NodeType;
+import com.bc.fiduceo.geometry.Polygon;
+import com.bc.fiduceo.location.PixelLocator;
+import com.bc.fiduceo.reader.AcquisitionInfo;
+import com.bc.fiduceo.reader.Reader;
+import com.bc.fiduceo.reader.netcdf.StringVariable;
+import com.bc.fiduceo.reader.time.TimeLocator;
+import com.bc.fiduceo.reader.time.TimeLocator_MillisSince1970;
+import com.bc.fiduceo.util.NetCDFUtils;
+import com.bc.fiduceo.util.VariableProxy;
+import ucar.ma2.Array;
+import ucar.ma2.ArrayInt;
+import ucar.ma2.DataType;
+import ucar.ma2.InvalidRangeException;
+import ucar.nc2.Attribute;
+import ucar.nc2.Variable;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * Generic CSV/ASCII reader driven by an json config file.
+ */
+public class GenericCsvReader implements Reader {
+
+ private String resourceKey;
+
+ private CsvFormatConfig config;
+ private List variables;
+ private List records;
+ private GenericRecord stationDatabaseRecord;
+
+ public GenericCsvReader(String resourceKey) {
+ this.resourceKey = resourceKey;
+ }
+
+ @Override
+ public void open(File file) throws IOException {
+ config = CsvFormatConfig.loadConfig(resourceKey);
+ records = GenericCsvHelper.parseData(file, config, resourceKey);
+ variables = config.getAllVariables();
+
+ StationDatabase stationDatabase = config.getStationDatabase();
+ if (stationDatabase != null) {
+ String primaryId = GenericCsvHelper.getPrimaryIdFromFilename(file, resourceKey);
+ String secondaryId = GenericCsvHelper.getSecondaryIdFromFilename(file, resourceKey);
+ stationDatabaseRecord = stationDatabase.extractRecord(primaryId, secondaryId);
+ }
+ }
+
+ @Override
+ public void close() throws IOException {
+ if (records != null) {
+ for (GenericRecord record : records) {
+ record.getValues().clear();
+ }
+ records.clear();
+ records = null;
+ }
+ if (variables != null) {
+ variables.clear();
+ variables = null;
+ }
+ if (stationDatabaseRecord != null) {
+ stationDatabaseRecord.getValues().clear();
+ stationDatabaseRecord = null;
+ }
+ if (config != null) {
+ config.getVariables().clear();
+ final StationDatabase stationDatabase = config.getStationDatabase();
+ if (stationDatabase != null) {
+ stationDatabase.getStations().clear();
+ stationDatabase.getVariables().clear();
+ }
+ config = null;
+ }
+ }
+
+ @Override
+ public AcquisitionInfo read() throws IOException {
+ AcquisitionInfo acquisitionInfo = new AcquisitionInfo();
+
+ int minTime = Integer.MAX_VALUE;
+ int maxTime = Integer.MIN_VALUE;
+ String timeName = config.getTimeName();
+ for (final GenericRecord record : records) {
+ int time = (int) record.getValues().get(timeName);
+ if (time < minTime) {
+ minTime = time;
+ }
+ if (time > maxTime) {
+ maxTime = time;
+ }
+ }
+
+ acquisitionInfo.setSensingStart(new Date(minTime * 1000L));
+ acquisitionInfo.setSensingStop(new Date(maxTime * 1000L));
+
+ acquisitionInfo.setNodeType(NodeType.UNDEFINED);
+
+ return acquisitionInfo;
+ }
+
+ @Override
+ public String getRegEx() {
+ if (config == null) {
+ config = CsvFormatConfig.loadConfig(resourceKey);
+ }
+ return config.getRegex();
+ }
+
+ @Override
+ public PixelLocator getPixelLocator() throws IOException {
+ throw new RuntimeException("not implemented");
+ }
+
+ @Override
+ public PixelLocator getSubScenePixelLocator(Polygon sceneGeometry) throws IOException {
+ throw new RuntimeException("not implemented");
+ }
+
+ @Override
+ public TimeLocator getTimeLocator() throws IOException {
+ long[] timeArray = new long[records.size()];
+
+ int ii = 0;
+ for (final GenericRecord record : records) {
+ String timeName = config.getTimeName();
+ int timeInSeconds = (int) record.getValues().get(timeName);
+ timeArray[ii] = timeInSeconds * 1000L;
+ ii++;
+ }
+
+ return new TimeLocator_MillisSince1970(timeArray);
+ }
+
+ @Override
+ public int[] extractYearMonthDayFromFilename(String fileName) {
+ return GenericCsvHelper.extractYearMonthDayFromFilename(fileName, resourceKey);
+ }
+
+ @Override
+ public Array readRaw(int centerX, int centerY, Interval interval, String variableName) throws IOException, InvalidRangeException {
+ GenericVariable variable = findVariable(variableName);
+ if (variable != null) {
+ Object o;
+ if (variable.getOrigin() == 's') {
+ o = stationDatabaseRecord.get(variableName);
+ } else {
+ GenericRecord record = records.get(centerY);
+ o = record.get(variableName);
+ }
+ return createResultArray(o, variable.getFillValue(), variable.getType(), interval);
+ }
+
+ return null;
+ }
+
+ @Override
+ public Array readScaled(int centerX, int centerY, Interval interval, String variableName) throws IOException, InvalidRangeException {
+ return readRaw(centerX, centerY, interval, variableName);
+ }
+
+ @Override
+ public ArrayInt.D2 readAcquisitionTime(int x, int y, Interval interval) throws IOException, InvalidRangeException {
+ String timeName = config.getTimeName();
+ final Array timeArray = readRaw(x, y, interval, timeName);
+
+ return (ArrayInt.D2) timeArray;
+ }
+
+ @Override
+ public List getVariables() throws InvalidRangeException, IOException {
+ final ArrayList variables = new ArrayList<>();
+ List attributes;
+
+ for (GenericVariable var : this.variables) {
+ attributes = new ArrayList<>();
+ setNcAttributes(attributes, var);
+
+ DataType ncDataType = GenericCsvHelper.getNcDataType(var.getType());
+ VariableProxy varProxy = new VariableProxy(var.getName(), ncDataType, attributes);
+
+ if (ncDataType == DataType.STRING) {
+ StringVariable stringVar = new StringVariable(varProxy, 50);
+ variables.add(stringVar);
+ } else {
+ variables.add(varProxy);
+ }
+ }
+
+ return variables;
+ }
+
+ @Override
+ public Dimension getProductSize() throws IOException {
+ return new Dimension("product_size", 1, records.size());
+ }
+
+ @Override
+ public String getLongitudeVariableName() {
+ return config.getLongitudeName();
+ }
+
+ @Override
+ public String getLatitudeVariableName() {
+ return config.getLatitudeName();
+ }
+
+ private GenericVariable findVariable(String variableName) {
+ for (GenericVariable variable : variables) {
+ if (variable.getName().equals(variableName)) {
+ return variable;
+ }
+ }
+ return null;
+ }
+
+ protected Array createResultArray(Object value, Number fillValue, String dataType, Interval interval) {
+ if (value instanceof String) {
+ final Array resultArray = Array.factory(DataType.STRING, new int[]{1, 1});
+ resultArray.setObject(0, value);
+ return resultArray;
+ }
+
+ if (fillValue == null) {
+ fillValue = NetCDFUtils.getDefaultFillValue(GenericCsvHelper.getFillValueClass(dataType));
+ }
+
+ DataType ncDataType = GenericCsvHelper.getNcDataType(dataType);
+
+ final int windowHeight = interval.getY();
+ final int windowWidth = interval.getX();
+ final Array windowArray = NetCDFUtils.create(ncDataType,
+ new int[]{windowHeight, windowWidth},
+ fillValue);
+
+ final int windowCenterX = windowWidth / 2;
+ final int windowCenterY = windowHeight / 2;
+
+ Object safeValue = value != null ? value : fillValue;
+ windowArray.setObject(windowWidth * windowCenterY + windowCenterX, safeValue);
+ return windowArray;
+ }
+
+ private void setNcAttributes(List attributes, GenericVariable var) {
+ if (var.getUnits() != null) {
+ attributes.add(new Attribute(NetCDFUtils.CF_UNITS_NAME, var.getUnits()));
+ }
+ if (var.getFillValue() != null) {
+ Number typedFillValue = GenericCsvHelper.castFillValue(var.getFillValue(), var.getType());
+ attributes.add(new Attribute(NetCDFUtils.CF_FILL_VALUE_NAME, typedFillValue));
+ } else {
+ if (!var.getType().equals("string")) {
+ Number fillValue = NetCDFUtils.getDefaultFillValue(GenericCsvHelper.getFillValueClass(var.getType()));
+ attributes.add(new Attribute(NetCDFUtils.CF_FILL_VALUE_NAME, fillValue));
+ }
+ }
+ if (var.getCfStandard() != null) {
+ attributes.add(new Attribute(NetCDFUtils.CF_STANDARD_NAME, var.getCfStandard()));
+ }
+ if (var.getLongName() != null) {
+ attributes.add(new Attribute(NetCDFUtils.CF_LONG_NAME, var.getLongName()));
+ }
+ if (var.getAncillaryVariables() != null) {
+ attributes.add(new Attribute(NetCDFUtils.CF_ANCILLARY_VARIABLES_NAME, var.getAncillaryVariables()));
+ }
+ }
+}
diff --git a/core/src/main/java/com/bc/fiduceo/reader/insitu/generic/GenericRecord.java b/core/src/main/java/com/bc/fiduceo/reader/insitu/generic/GenericRecord.java
new file mode 100644
index 000000000..644ef8f48
--- /dev/null
+++ b/core/src/main/java/com/bc/fiduceo/reader/insitu/generic/GenericRecord.java
@@ -0,0 +1,21 @@
+package com.bc.fiduceo.reader.insitu.generic;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+public class GenericRecord {
+
+ private final Map values = new LinkedHashMap<>();
+
+ public void put(String variableName, Object value) {
+ values.put(variableName, value);
+ }
+
+ public Object get(String variableName) {
+ return values.get(variableName);
+ }
+
+ public Map getValues() {
+ return values;
+ }
+}
diff --git a/core/src/main/java/com/bc/fiduceo/reader/insitu/generic/GenericVariable.java b/core/src/main/java/com/bc/fiduceo/reader/insitu/generic/GenericVariable.java
new file mode 100644
index 000000000..ef2accf4f
--- /dev/null
+++ b/core/src/main/java/com/bc/fiduceo/reader/insitu/generic/GenericVariable.java
@@ -0,0 +1,125 @@
+package com.bc.fiduceo.reader.insitu.generic;
+
+import org.esa.snap.core.datamodel.ProductData;
+
+public class GenericVariable {
+
+ private String name;
+ private char origin = 'v'; // 'v' = variable (default), 's' = station
+ private String type;
+ private int productData;
+ private Double fillValue;
+ private String units;
+ private String longName;
+ private String cfStandard;
+ private String ancillaryVariables;
+
+ public GenericVariable() {
+ }
+
+ public GenericVariable(String name, char origin, String type, int productData, Double fillValue, String units, String longName, String cfStandard, String ancillaryVariables) {
+ this.name = name;
+ this.origin = origin;
+ this.type = type;
+ this.productData = productData;
+ this.fillValue = fillValue;
+ this.units = units;
+ this.longName = longName;
+ this.cfStandard = cfStandard;
+ this.ancillaryVariables = ancillaryVariables;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public char getOrigin() {
+ return origin;
+ }
+
+ public void setOrigin(char origin) {
+ this.origin = origin;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ setProductData(parseProductData(type));
+ this.type = type;
+ }
+
+ public int getProductData() {
+ return productData;
+ }
+
+ public void setProductData(int productData) {
+ this.productData = productData;
+ }
+
+ public Double getFillValue() {
+ return fillValue;
+ }
+
+ public void setFillValue(Double fillValue) {
+ this.fillValue = fillValue;
+ }
+
+ public String getUnits() {
+ return units;
+ }
+
+ public void setUnits(String units) {
+ this.units = units;
+ }
+
+ public String getLongName() {
+ return longName;
+ }
+
+ public void setLongName(String longName) {
+ this.longName = longName;
+ }
+
+ public String getCfStandard() {
+ return cfStandard;
+ }
+
+ public void setCfStandard(String cfStandard) {
+ this.cfStandard = cfStandard;
+ }
+
+ public String getAncillaryVariables() {
+ return ancillaryVariables;
+ }
+
+ public void setAncillaryVariables(String ancillaryVariables) {
+ this.ancillaryVariables = ancillaryVariables;
+ }
+
+ private static int parseProductData(String type) {
+ switch (type.toLowerCase()) {
+ case "byte":
+ return ProductData.TYPE_INT8;
+ case "short":
+ return ProductData.TYPE_INT16;
+ case "int":
+ return ProductData.TYPE_INT32;
+ case "long":
+ return ProductData.TYPE_INT64;
+ case "float":
+ return ProductData.TYPE_FLOAT32;
+ case "double":
+ return ProductData.TYPE_FLOAT64;
+ case "string":
+ return ProductData.TYPE_ASCII;
+ default:
+ throw new IllegalArgumentException("Unsupported type: " + type);
+ }
+ }
+}
diff --git a/core/src/main/java/com/bc/fiduceo/reader/insitu/generic/StationDatabase.java b/core/src/main/java/com/bc/fiduceo/reader/insitu/generic/StationDatabase.java
new file mode 100644
index 000000000..7cce6c43f
--- /dev/null
+++ b/core/src/main/java/com/bc/fiduceo/reader/insitu/generic/StationDatabase.java
@@ -0,0 +1,115 @@
+package com.bc.fiduceo.reader.insitu.generic;
+
+import java.util.List;
+
+public class StationDatabase {
+
+ private String primaryId;
+ private String secondaryId;
+ private List variables;
+ private List> stations;
+
+
+ public String getPrimaryId() {
+ return primaryId;
+ }
+
+ public void setPrimaryId(String primaryId) {
+ this.primaryId = primaryId;
+ }
+
+ public String getSecondaryId() {
+ return secondaryId;
+ }
+
+ public void setSecondaryId(String secondaryId) {
+ this.secondaryId = secondaryId;
+ }
+
+ public List getVariables() {
+ return variables;
+ }
+
+ public void setVariables(List variables) {
+ this.variables = variables;
+ }
+
+ public List> getStations() {
+ return stations;
+ }
+
+ public void setStations(List> stations) {
+ this.stations = stations;
+ }
+
+ public GenericRecord extractRecord(String primaryIdValue, String secondaryIdValue) {
+ int primaryIndex = -1;
+ int secondaryIndex = -1;
+ for (int ii = 0; ii < variables.size(); ii++) {
+ final GenericVariable var = variables.get(ii);
+ if (var.getName().equals(primaryId)) {
+ primaryIndex = ii;
+ }
+ if (var.getName().equals(secondaryId)) {
+ secondaryIndex = ii;
+ }
+ }
+
+ if (primaryIndex == -1) {
+ throw new IllegalStateException("Station database does not contain variable '" + primaryId + "'.");
+ }
+ if (secondaryId != null && secondaryIndex == -1) {
+ throw new IllegalStateException("Station database does not contain variable '" + secondaryId + "'.");
+ }
+
+ List
" +
" " +
" 41" +
@@ -372,7 +378,7 @@ private void writeConfiguration_coo1(boolean overwrite) throws IOException {
configBuffer.append(" ");
configBuffer.append(" ");
configBuffer.append(" ");
- configBuffer.append(" ");
+ configBuffer.append(" ");
configBuffer.append(" slstr.s3ant_nwp_time");
configBuffer.append(" slstr-s3a-nt_acquisition_time");
configBuffer.append(" slstr-s3a-nt_longitude_tx");
@@ -409,12 +415,18 @@ private void writeConfiguration_coo1_with_sensorRef() throws IOException {
" slstr-s3a-nt" +
" " +
" " +
- " " +
+ " " +
" {sensor-ref}_nwp_time" +
" {sensor-ref}_acquisition_time" +
" {sensor-ref}_longitude_tx" +
" {sensor-ref}_latitude_tx" +
" {sensor-ref}.blowVert" +
+ " nwp_lnsp" +
+ " nwp_q" +
+ " nwp_t" +
+ " nwp_o3" +
+ " nwp_msl" +
+ " nwp_tcc" +
" " +
" \n" +
" \n" +
diff --git a/post-processing-tool/src/test/java/com/bc/fiduceo/post/PostProcessingToolMain_IO_Test.java b/post-processing-tool/src/test/java/com/bc/fiduceo/post/PostProcessingToolMain_IO_Test.java
index 76cd6bc8c..c029bdc71 100644
--- a/post-processing-tool/src/test/java/com/bc/fiduceo/post/PostProcessingToolMain_IO_Test.java
+++ b/post-processing-tool/src/test/java/com/bc/fiduceo/post/PostProcessingToolMain_IO_Test.java
@@ -23,10 +23,10 @@
import com.bc.fiduceo.IOTestRunner;
import com.bc.fiduceo.TestUtil;
import org.esa.snap.core.util.io.FileUtils;
-import org.jdom.Document;
-import org.jdom.Element;
-import org.jdom.output.Format;
-import org.jdom.output.XMLOutputter;
+import org.jdom2.Document;
+import org.jdom2.Element;
+import org.jdom2.output.Format;
+import org.jdom2.output.XMLOutputter;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
diff --git a/post-processing-tool/src/test/java/com/bc/fiduceo/post/PostProcessingToolTest.java b/post-processing-tool/src/test/java/com/bc/fiduceo/post/PostProcessingToolTest.java
index 6ccf65b06..045ba59ee 100644
--- a/post-processing-tool/src/test/java/com/bc/fiduceo/post/PostProcessingToolTest.java
+++ b/post-processing-tool/src/test/java/com/bc/fiduceo/post/PostProcessingToolTest.java
@@ -24,10 +24,10 @@
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
-import org.jdom.Document;
-import org.jdom.Element;
-import org.jdom.output.Format;
-import org.jdom.output.XMLOutputter;
+import org.jdom2.Document;
+import org.jdom2.Element;
+import org.jdom2.output.Format;
+import org.jdom2.output.XMLOutputter;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
@@ -127,7 +127,7 @@ public void testPrintUsage() {
PostProcessingTool.printUsageTo(out);
final String ls = System.lineSeparator();
- final String expected = "post-processing-tool version 1.5.8" + ls +
+ final String expected = "post-processing-tool version 1.6.2" + ls +
"" + ls +
"usage: post-processing-tool " + ls +
"Valid options are:" + ls +
diff --git a/post-processing-tool/src/test/java/com/bc/fiduceo/post/PostProcessingTool_IOTest.java b/post-processing-tool/src/test/java/com/bc/fiduceo/post/PostProcessingTool_IOTest.java
index c49fad003..645e0a454 100644
--- a/post-processing-tool/src/test/java/com/bc/fiduceo/post/PostProcessingTool_IOTest.java
+++ b/post-processing-tool/src/test/java/com/bc/fiduceo/post/PostProcessingTool_IOTest.java
@@ -24,11 +24,11 @@
import com.bc.fiduceo.TestUtil;
import com.bc.fiduceo.core.SystemConfig;
import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.DefaultParser;
import org.apache.commons.cli.Options;
-import org.apache.commons.cli.PosixParser;
import org.esa.snap.core.datamodel.ProductData;
import org.esa.snap.core.util.io.FileUtils;
-import org.jdom.Element;
+import org.jdom2.Element;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -83,7 +83,7 @@ public void setUp() throws Exception {
}
@After
- public void tearDown() {
+ public void tearDown() {
if (testDir.isDirectory()) {
FileUtils.deleteTree(testDir);
}
@@ -92,7 +92,7 @@ public void tearDown() {
@Test
public void testInitialisation() throws Exception {
final Options options = PostProcessingTool.getOptions();
- final PosixParser parser = new PosixParser();
+ final DefaultParser parser = new DefaultParser();
final CommandLine commandLine = parser.parse(options, new String[]{
"-j", processingConfigName,
"-i", "/mmd_files",
@@ -105,6 +105,9 @@ public void testInitialisation() throws Exception {
final PostProcessingContext context = PostProcessingTool.initializeContext(commandLine);
+ assertNotNull(context.getConfigDirectory());
+ assertEquals(testDir.toPath().resolve("config"), context.getConfigDirectory());
+
final String separator = FileSystems.getDefault().getSeparator();
assertEquals(separator + "mmd_files", context.getMmdInputDirectory().toString());
assertEquals("03-May-2011 00:00:00", ProductData.UTC.createDateFormat().format(context.getStartDate()));
diff --git a/post-processing-tool/src/test/java/com/bc/fiduceo/post/SourceTargetManagerTest.java b/post-processing-tool/src/test/java/com/bc/fiduceo/post/SourceTargetManagerTest.java
index bec3400bc..4baa8e9d6 100644
--- a/post-processing-tool/src/test/java/com/bc/fiduceo/post/SourceTargetManagerTest.java
+++ b/post-processing-tool/src/test/java/com/bc/fiduceo/post/SourceTargetManagerTest.java
@@ -22,10 +22,10 @@
import com.bc.fiduceo.TestUtil;
import com.bc.fiduceo.post.plugin.DummyPostProcessingPlugin;
import org.esa.snap.core.util.io.FileUtils;
-import org.jdom.Document;
-import org.jdom.Element;
-import org.jdom.output.Format;
-import org.jdom.output.XMLOutputter;
+import org.jdom2.Document;
+import org.jdom2.Element;
+import org.jdom2.output.Format;
+import org.jdom2.output.XMLOutputter;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
diff --git a/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/DummyPostProcessingPlugin.java b/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/DummyPostProcessingPlugin.java
index e225bd45a..28e80279c 100644
--- a/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/DummyPostProcessingPlugin.java
+++ b/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/DummyPostProcessingPlugin.java
@@ -19,15 +19,16 @@
package com.bc.fiduceo.post.plugin;
import com.bc.fiduceo.post.PostProcessing;
+import com.bc.fiduceo.post.PostProcessingContext;
import com.bc.fiduceo.post.PostProcessingPlugin;
-import org.jdom.Element;
+import org.jdom2.Element;
public class DummyPostProcessingPlugin implements PostProcessingPlugin {
public static final String DUMMY_POST_PROCESSING_NAME = "dummy-post-processing";
@Override
- public PostProcessing createPostProcessing(Element element) {
+ public PostProcessing createPostProcessing(Element element, PostProcessingContext context) {
return new DummyPostProcessing(element.getValue());
}
diff --git a/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/ElevationToSolZenAnglePluginTest.java b/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/ElevationToSolZenAnglePluginTest.java
index c18b098af..011352fb7 100644
--- a/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/ElevationToSolZenAnglePluginTest.java
+++ b/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/ElevationToSolZenAnglePluginTest.java
@@ -3,8 +3,8 @@
import com.bc.fiduceo.TestUtil;
import com.bc.fiduceo.post.PostProcessing;
-import org.jdom.Element;
-import org.jdom.JDOMException;
+import org.jdom2.Element;
+import org.jdom2.JDOMException;
import org.junit.Before;
import org.junit.Test;
@@ -37,7 +37,7 @@ public void testGetPostProcessingName() {
public void testCreatePostProcessing() throws JDOMException, IOException {
final Element rootElement = TestUtil.createDomElement(FULL_CONFIG);
- final PostProcessing postProcessing = plugin.createPostProcessing(rootElement);
+ final PostProcessing postProcessing = plugin.createPostProcessing(rootElement, null);
assertNotNull(postProcessing);
assertTrue(postProcessing instanceof ElevationToSolZenAngle);
}
diff --git a/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/amsr/AddAmsr2ScanDataQualityPluginTest.java b/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/amsr/AddAmsr2ScanDataQualityPluginTest.java
index c940d1708..d7bfa8af7 100644
--- a/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/amsr/AddAmsr2ScanDataQualityPluginTest.java
+++ b/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/amsr/AddAmsr2ScanDataQualityPluginTest.java
@@ -25,10 +25,8 @@
import com.bc.fiduceo.TestUtil;
import com.bc.fiduceo.post.PostProcessing;
-import com.bc.fiduceo.post.plugin.amsr.AddAmsr2ScanDataQuality;
-import com.bc.fiduceo.post.plugin.amsr.AddAmsr2ScanDataQualityPlugin;
-import org.jdom.Element;
-import org.jdom.JDOMException;
+import org.jdom2.Element;
+import org.jdom2.JDOMException;
import org.junit.*;
import java.io.IOException;
@@ -58,7 +56,7 @@ public void testGetPostProcessingName() {
public void testCreatePostProcessing() throws JDOMException, IOException {
final Element rootElement = TestUtil.createDomElement(FULL_CONFIG);
- final PostProcessing postProcessing = plugin.createPostProcessing(rootElement);
+ final PostProcessing postProcessing = plugin.createPostProcessing(rootElement, null);
assertNotNull(postProcessing);
assertTrue(postProcessing instanceof AddAmsr2ScanDataQuality);
}
diff --git a/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/amsr/AddAmsrSolarAnglesPluginTest.java b/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/amsr/AddAmsrSolarAnglesPluginTest.java
index 8dfa22c7e..e05d5533f 100644
--- a/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/amsr/AddAmsrSolarAnglesPluginTest.java
+++ b/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/amsr/AddAmsrSolarAnglesPluginTest.java
@@ -26,10 +26,8 @@
import com.bc.fiduceo.TestUtil;
import com.bc.fiduceo.post.PostProcessing;
-import com.bc.fiduceo.post.plugin.amsr.AddAmsrSolarAngles;
-import com.bc.fiduceo.post.plugin.amsr.AddAmsrSolarAnglesPlugin;
-import org.jdom.Element;
-import org.jdom.JDOMException;
+import org.jdom2.Element;
+import org.jdom2.JDOMException;
import org.junit.*;
import java.io.IOException;
@@ -61,7 +59,7 @@ public void testGetPostProcessingName() {
public void testCreatePostProcessing() throws JDOMException, IOException {
final Element rootElement = TestUtil.createDomElement(FULL_CONFIG);
- final PostProcessing postProcessing = plugin.createPostProcessing(rootElement);
+ final PostProcessing postProcessing = plugin.createPostProcessing(rootElement, null);
assertNotNull(postProcessing);
assertTrue(postProcessing instanceof AddAmsrSolarAngles);
}
diff --git a/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/avhrr_fcdr/AddAvhrrCorrCoeffsPluginTest.java b/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/avhrr_fcdr/AddAvhrrCorrCoeffsPluginTest.java
index abec91598..91c3bd1d3 100644
--- a/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/avhrr_fcdr/AddAvhrrCorrCoeffsPluginTest.java
+++ b/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/avhrr_fcdr/AddAvhrrCorrCoeffsPluginTest.java
@@ -2,8 +2,8 @@
import com.bc.fiduceo.TestUtil;
import com.bc.fiduceo.post.PostProcessing;
-import org.jdom.Element;
-import org.jdom.JDOMException;
+import org.jdom2.Element;
+import org.jdom2.JDOMException;
import org.junit.Before;
import org.junit.Test;
@@ -36,7 +36,7 @@ public void testCreatePostProcessing() throws JDOMException, IOException {
" " +
"");
- final PostProcessing postProcessing = plugin.createPostProcessing(element);
+ final PostProcessing postProcessing = plugin.createPostProcessing(element, null);
assertNotNull(postProcessing);
assertTrue(postProcessing instanceof AddAvhrrCorrCoeffs);
}
diff --git a/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/avhrr_fcdr/AddAvhrrCorrCoeffsTest.java b/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/avhrr_fcdr/AddAvhrrCorrCoeffsTest.java
index 8345fdca1..e0b8d12fe 100644
--- a/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/avhrr_fcdr/AddAvhrrCorrCoeffsTest.java
+++ b/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/avhrr_fcdr/AddAvhrrCorrCoeffsTest.java
@@ -1,8 +1,8 @@
package com.bc.fiduceo.post.plugin.avhrr_fcdr;
import com.bc.fiduceo.TestUtil;
-import org.jdom.Element;
-import org.jdom.JDOMException;
+import org.jdom2.Element;
+import org.jdom2.JDOMException;
import org.junit.Test;
import java.io.IOException;
diff --git a/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/caliop/flag/CALIOP_L2_VFM_FLAGS_PPPluginTest.java b/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/caliop/flag/CALIOP_L2_VFM_FLAGS_PPPluginTest.java
index 0ff337863..3ee456342 100644
--- a/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/caliop/flag/CALIOP_L2_VFM_FLAGS_PPPluginTest.java
+++ b/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/caliop/flag/CALIOP_L2_VFM_FLAGS_PPPluginTest.java
@@ -21,7 +21,7 @@
package com.bc.fiduceo.post.plugin.caliop.flag;
import com.bc.fiduceo.post.PostProcessing;
-import org.jdom.Element;
+import org.jdom2.Element;
import org.junit.Before;
import org.junit.Test;
@@ -53,7 +53,7 @@ public void getPostProcessingName() {
@Test
public void createPostProcessing() {
- final PostProcessing pp = ppPlugin.createPostProcessing(createValidRootElement());
+ final PostProcessing pp = ppPlugin.createPostProcessing(createValidRootElement(), null);
assertNotNull(pp);
assertEquals(CALIOP_L2_VFM_FLAGS_PP.class, pp.getClass());
final CALIOP_L2_VFM_FLAGS_PP cfpp = (CALIOP_L2_VFM_FLAGS_PP) pp;
@@ -67,7 +67,7 @@ public void createPostProcessing() {
public void createPostProcessing_wrongRootTag() {
final Element rootElement = createValidRootElement();
try {
- ppPlugin.createPostProcessing(rootElement.setName("wrongRootName"));
+ ppPlugin.createPostProcessing(rootElement.setName("wrongRootName"), null);
fail("RuntimeException expected");
} catch (RuntimeException expected) {
assertEquals(RuntimeException.class.getTypeName(), expected.getClass().getTypeName());
@@ -80,7 +80,7 @@ public void createPostProcessing_sourceFileVarName_missingElement() {
final Element rootElement = createValidRootElement();
rootElement.removeChild(TAG_MMD_SOURCE_FILE_VARIABE_NAME);
try {
- ppPlugin.createPostProcessing(rootElement);
+ ppPlugin.createPostProcessing(rootElement, null);
fail("RuntimeException expected");
} catch (RuntimeException expected) {
assertEquals(RuntimeException.class.getTypeName(), expected.getClass().getTypeName());
@@ -93,7 +93,7 @@ public void createPostProcessing_sourceFileVarName_empty() {
final Element rootElement = createValidRootElement();
rootElement.getChild(TAG_MMD_SOURCE_FILE_VARIABE_NAME).setText(" ");
try {
- ppPlugin.createPostProcessing(rootElement);
+ ppPlugin.createPostProcessing(rootElement, null);
fail("RuntimeException expected");
} catch (RuntimeException expected) {
assertEquals(RuntimeException.class.getTypeName(), expected.getClass().getTypeName());
@@ -107,7 +107,7 @@ public void createPostProcessing_processingVersionVarName_missingElement() {
final Element rootElement = createValidRootElement();
rootElement.removeChild(TAG_MMD_PROCESSING_VERSION_VARIABE_NAME);
try {
- ppPlugin.createPostProcessing(rootElement);
+ ppPlugin.createPostProcessing(rootElement, null);
fail("RuntimeException expected");
} catch (RuntimeException expected) {
assertEquals(RuntimeException.class.getTypeName(), expected.getClass().getTypeName());
@@ -120,7 +120,7 @@ public void createPostProcessing_processingVersionVarName_empty() {
final Element rootElement = createValidRootElement();
rootElement.getChild(TAG_MMD_PROCESSING_VERSION_VARIABE_NAME).setText(" ");
try {
- ppPlugin.createPostProcessing(rootElement);
+ ppPlugin.createPostProcessing(rootElement, null);
fail("RuntimeException expected");
} catch (RuntimeException expected) {
assertEquals(RuntimeException.class.getTypeName(), expected.getClass().getTypeName());
@@ -133,7 +133,7 @@ public void createPostProcessing_yVarName_missingElement() {
final Element rootElement = createValidRootElement();
rootElement.removeChild(TAG_MMD_Y_VARIABE_NAME);
try {
- ppPlugin.createPostProcessing(rootElement);
+ ppPlugin.createPostProcessing(rootElement, null);
fail("RuntimeException expected");
} catch (RuntimeException expected) {
assertEquals(RuntimeException.class.getTypeName(), expected.getClass().getTypeName());
@@ -146,7 +146,7 @@ public void createPostProcessing_yVarName_empty() {
final Element rootElement = createValidRootElement();
rootElement.getChild(TAG_MMD_Y_VARIABE_NAME).setText(" ");
try {
- ppPlugin.createPostProcessing(rootElement);
+ ppPlugin.createPostProcessing(rootElement, null);
fail("RuntimeException expected");
} catch (RuntimeException expected) {
assertEquals(RuntimeException.class.getTypeName(), expected.getClass().getTypeName());
@@ -159,7 +159,7 @@ public void createPostProcessing_targetVarName_missingElement() {
final Element rootElement = createValidRootElement();
rootElement.removeChild(TAG_TARGET_FCF_VARIABLE_NAME);
try {
- ppPlugin.createPostProcessing(rootElement);
+ ppPlugin.createPostProcessing(rootElement, null);
fail("RuntimeException expected");
} catch (RuntimeException expected) {
assertEquals(RuntimeException.class.getTypeName(), expected.getClass().getTypeName());
@@ -172,7 +172,7 @@ public void createPostProcessing_targetVarName_empty() {
final Element rootElement = createValidRootElement();
rootElement.getChild(TAG_TARGET_FCF_VARIABLE_NAME).setText(" ");
try {
- ppPlugin.createPostProcessing(rootElement);
+ ppPlugin.createPostProcessing(rootElement, null);
fail("RuntimeException expected");
} catch (RuntimeException expected) {
assertEquals(RuntimeException.class.getTypeName(), expected.getClass().getTypeName());
diff --git a/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/caliop/sst_wp100/CALIOP_SST_WP100_CLay_PPPluginTest.java b/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/caliop/sst_wp100/CALIOP_SST_WP100_CLay_PPPluginTest.java
index f405e1d11..94cc4a8d6 100644
--- a/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/caliop/sst_wp100/CALIOP_SST_WP100_CLay_PPPluginTest.java
+++ b/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/caliop/sst_wp100/CALIOP_SST_WP100_CLay_PPPluginTest.java
@@ -21,7 +21,7 @@
package com.bc.fiduceo.post.plugin.caliop.sst_wp100;
import com.bc.fiduceo.post.PostProcessing;
-import org.jdom.Element;
+import org.jdom2.Element;
import org.junit.Before;
import org.junit.Test;
@@ -53,7 +53,7 @@ public void getPostProcessingName() {
@Test
public void createPostProcessing() {
- final PostProcessing pp = ppPlugin.createPostProcessing(createValidRootElement());
+ final PostProcessing pp = ppPlugin.createPostProcessing(createValidRootElement(), null);
assertNotNull(pp);
assertEquals(CALIOP_SST_WP100_CLay_PP.class, pp.getClass());
final CALIOP_SST_WP100_CLay_PP cwp100_CLay_pp = (CALIOP_SST_WP100_CLay_PP) pp;
@@ -67,7 +67,7 @@ public void createPostProcessing() {
public void createPostProcessing_wrongRootTag() {
final Element rootElement = createValidRootElement();
try {
- ppPlugin.createPostProcessing(rootElement.setName("wrongRootName"));
+ ppPlugin.createPostProcessing(rootElement.setName("wrongRootName"), null);
fail("RuntimeException expected");
} catch (RuntimeException expected) {
assertEquals(RuntimeException.class.getTypeName(), expected.getClass().getTypeName());
@@ -80,7 +80,7 @@ public void createPostProcessing_sourceFileVarName_missingElement() {
final Element rootElement = createValidRootElement();
rootElement.removeChild(TAG_MMD_SOURCE_FILE_VARIABE_NAME);
try {
- ppPlugin.createPostProcessing(rootElement);
+ ppPlugin.createPostProcessing(rootElement, null);
fail("RuntimeException expected");
} catch (RuntimeException expected) {
assertEquals(RuntimeException.class.getTypeName(), expected.getClass().getTypeName());
@@ -93,7 +93,7 @@ public void createPostProcessing_sourceFileVarName_empty() {
final Element rootElement = createValidRootElement();
rootElement.getChild(TAG_MMD_SOURCE_FILE_VARIABE_NAME).setText(" ");
try {
- ppPlugin.createPostProcessing(rootElement);
+ ppPlugin.createPostProcessing(rootElement, null);
fail("RuntimeException expected");
} catch (RuntimeException expected) {
assertEquals(RuntimeException.class.getTypeName(), expected.getClass().getTypeName());
@@ -107,7 +107,7 @@ public void createPostProcessing_processingVersionVarName_missingElement() {
final Element rootElement = createValidRootElement();
rootElement.removeChild(TAG_MMD_PROCESSING_VERSION);
try {
- ppPlugin.createPostProcessing(rootElement);
+ ppPlugin.createPostProcessing(rootElement, null);
fail("RuntimeException expected");
} catch (RuntimeException expected) {
assertEquals(RuntimeException.class.getTypeName(), expected.getClass().getTypeName());
@@ -120,7 +120,7 @@ public void createPostProcessing_processingVersionVarName_empty() {
final Element rootElement = createValidRootElement();
rootElement.getChild(TAG_MMD_PROCESSING_VERSION).setText(" ");
try {
- ppPlugin.createPostProcessing(rootElement);
+ ppPlugin.createPostProcessing(rootElement, null);
fail("RuntimeException expected");
} catch (RuntimeException expected) {
assertEquals(RuntimeException.class.getTypeName(), expected.getClass().getTypeName());
@@ -133,7 +133,7 @@ public void createPostProcessing_yVarName_missingElement() {
final Element rootElement = createValidRootElement();
rootElement.removeChild(TAG_MMD_Y_VARIABE_NAME);
try {
- ppPlugin.createPostProcessing(rootElement);
+ ppPlugin.createPostProcessing(rootElement, null);
fail("RuntimeException expected");
} catch (RuntimeException expected) {
assertEquals(RuntimeException.class.getTypeName(), expected.getClass().getTypeName());
@@ -146,7 +146,7 @@ public void createPostProcessing_yVarName_empty() {
final Element rootElement = createValidRootElement();
rootElement.getChild(TAG_MMD_Y_VARIABE_NAME).setText(" ");
try {
- ppPlugin.createPostProcessing(rootElement);
+ ppPlugin.createPostProcessing(rootElement, null);
fail("RuntimeException expected");
} catch (RuntimeException expected) {
assertEquals(RuntimeException.class.getTypeName(), expected.getClass().getTypeName());
@@ -159,7 +159,7 @@ public void createPostProcessing_targetPrefix_missingElement() {
final Element rootElement = createValidRootElement();
rootElement.removeChild(TAG_TARGET_VARIABE_PREFIX);
try {
- ppPlugin.createPostProcessing(rootElement);
+ ppPlugin.createPostProcessing(rootElement, null);
fail("RuntimeException expected");
} catch (RuntimeException expected) {
assertEquals(RuntimeException.class.getTypeName(), expected.getClass().getTypeName());
@@ -172,7 +172,7 @@ public void createPostProcessing_targetPrefix_empty() {
final Element rootElement = createValidRootElement();
rootElement.getChild(TAG_TARGET_VARIABE_PREFIX).setText(" ");
try {
- ppPlugin.createPostProcessing(rootElement);
+ ppPlugin.createPostProcessing(rootElement, null);
fail("RuntimeException expected");
} catch (RuntimeException expected) {
assertEquals(RuntimeException.class.getTypeName(), expected.getClass().getTypeName());
diff --git a/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/era5/ArrayUtilsTest.java b/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/era5/ArrayUtilsTest.java
new file mode 100644
index 000000000..d63f8512d
--- /dev/null
+++ b/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/era5/ArrayUtilsTest.java
@@ -0,0 +1,142 @@
+package com.bc.fiduceo.post.plugin.era5;
+
+import org.junit.Test;
+import ucar.ma2.Array;
+import ucar.ma2.DataType;
+import ucar.ma2.Index;
+
+import static org.junit.Assert.*;
+
+public class ArrayUtilsTest {
+
+ @Test
+ public void testMergeALongX_3D() {
+ final int[] dataLeft = new int[]{0, 1, 2, 3,
+ 4, 5, 6, 7,
+ 8, 9, 10, 11,
+ // ------------
+ 12, 13, 14, 15,
+ 16, 17, 18, 19,
+ 20, 21, 22, 23};
+ final int[] shapeLeft = new int[]{2, 3, 4};
+
+ final Array left = Array.factory(DataType.INT, shapeLeft, dataLeft);
+
+ final int[] dataRight = new int[]{100, 101, 102,
+ 104, 105, 106,
+ 108, 109, 110,
+ // ----------
+ 112, 113, 114,
+ 116, 117, 118,
+ 120, 121, 122};
+ final int[] shapeRight = new int[]{2, 3, 3};
+ final Array right = Array.factory(DataType.INT, shapeRight, dataRight);
+
+ final Array merged = ArrayUtils.mergeAlongX(left, right);
+ int[] shape = merged.getShape();
+ assertArrayEquals(new int[]{2, 3, 7}, shape);
+
+ final Index index = merged.getIndex();
+ index.set(0, 0, 0);
+ assertEquals(0, merged.getInt(index));
+ index.set(0, 0, 3);
+ assertEquals(3, merged.getInt(index));
+ index.set(0, 0, 4);
+ assertEquals(100, merged.getInt(index));
+ index.set(0, 0, 6);
+ assertEquals(102, merged.getInt(index));
+
+ index.set(1, 2, 0);
+ assertEquals(20, merged.getInt(index));
+ index.set(1, 2, 3);
+ assertEquals(23, merged.getInt(index));
+ index.set(1, 2, 4);
+ assertEquals(120, merged.getInt(index));
+ index.set(1, 2, 6);
+ assertEquals(122, merged.getInt(index));
+ }
+
+ @Test
+ public void testMergeALongX_4D() {
+ final int[] dataLeft = new int[]{0, 1, 2, 3,
+ 4, 5, 6, 7,
+ 8, 9, 10, 11,
+ // ------------
+ 12, 13, 14, 15,
+ 16, 17, 18, 19,
+ 20, 21, 22, 23,
+ // ============
+ 30, 31, 32, 33,
+ 34, 35, 36, 37,
+ 38, 39, 40, 41,
+ // ------------
+ 42, 43, 44, 45,
+ 46, 47, 48, 49,
+ 50, 51, 52, 53};
+ final int[] shapeLeft = new int[]{2, 2, 3, 4};
+
+ final Array left = Array.factory(DataType.INT, shapeLeft, dataLeft);
+
+ final int[] dataRight = new int[]{100, 101, 102,
+ 104, 105, 106,
+ 108, 109, 110,
+ // ----------
+ 112, 113, 114,
+ 116, 117, 118,
+ 120, 121, 122,
+ // ==========
+ 200, 201, 202,
+ 204, 205, 206,
+ 208, 209, 210,
+ // ----------
+ 212, 213, 214,
+ 216, 217, 218,
+ 220, 221, 222};
+ final int[] shapeRight = new int[]{2, 2, 3, 3};
+ final Array right = Array.factory(DataType.INT, shapeRight, dataRight);
+
+ final Array merged = ArrayUtils.mergeAlongX(left, right);
+ int[] shape = merged.getShape();
+ assertArrayEquals(new int[]{2, 2, 3, 7}, shape);
+
+ final Index index = merged.getIndex();
+ index.set(0, 0, 0, 0);
+ assertEquals(0, merged.getInt(index));
+ index.set(0, 0, 0, 3);
+ assertEquals(3, merged.getInt(index));
+ index.set(0, 0, 0, 4);
+ assertEquals(100, merged.getInt(index));
+ index.set(0, 0, 0, 6);
+ assertEquals(102, merged.getInt(index));
+
+ index.set(1, 1, 2, 0);
+ assertEquals(50, merged.getInt(index));
+ index.set(1, 1, 2, 3);
+ assertEquals(53, merged.getInt(index));
+ index.set(1, 1, 2, 4);
+ assertEquals(220, merged.getInt(index));
+ index.set(1, 1, 2, 6);
+ assertEquals(222, merged.getInt(index));
+ }
+
+ @Test
+ public void testMergeALongX_invalidDimension() {
+ Array left = Array.factory(DataType.FLOAT, new int[]{4, 6});
+ Array right = Array.factory(DataType.FLOAT, new int[]{4, 6});
+
+ try {
+ ArrayUtils.mergeAlongX(left, right);
+ fail("IllegalArgumentException expected");
+ } catch (IllegalArgumentException expected) {
+ }
+
+ left = Array.factory(DataType.FLOAT, new int[]{2, 2, 2, 4, 6});
+ right = Array.factory(DataType.FLOAT, new int[]{2, 2, 2, 4, 6});
+
+ try {
+ ArrayUtils.mergeAlongX(left, right);
+ fail("IllegalArgumentException expected");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+}
diff --git a/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/era5/BilinearInterpolatorTest.java b/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/era5/BilinearInterpolatorTest.java
index c44dfd85c..51511c826 100644
--- a/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/era5/BilinearInterpolatorTest.java
+++ b/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/era5/BilinearInterpolatorTest.java
@@ -82,5 +82,23 @@ public void testConstructAndGetCoordinates() {
assertEquals(62, interpolator.getXMin());
assertEquals(129, interpolator.getYMin());
+ assertEquals(Integer.MIN_VALUE, interpolator.getRelXMin());
+ assertEquals(Integer.MIN_VALUE, interpolator.getRelYMin());
+ }
+
+ @Test
+ public void testSetGetRelXMin() {
+ final BilinearInterpolator interpolator = new BilinearInterpolator(0.0, 0.0, 63, 130);
+
+ interpolator.setRelXMin(5);
+ assertEquals(5, interpolator.getRelXMin());
+ }
+
+ @Test
+ public void testSetGetRelYMin() {
+ final BilinearInterpolator interpolator = new BilinearInterpolator(0.0, 0.0, 63, 130);
+
+ interpolator.setRelYMin(6);
+ assertEquals(6, interpolator.getRelYMin());
}
}
diff --git a/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/era5/Era5ArchiveTest.java b/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/era5/Era5ArchiveTest.java
index 4f20da184..6eeabcf11 100644
--- a/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/era5/Era5ArchiveTest.java
+++ b/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/era5/Era5ArchiveTest.java
@@ -1,11 +1,10 @@
package com.bc.fiduceo.post.plugin.era5;
import com.bc.fiduceo.util.TimeUtils;
-import org.junit.Ignore;
+import org.junit.Before;
import org.junit.Test;
import java.io.File;
-import java.io.IOException;
import java.util.Calendar;
import static junit.framework.TestCase.assertEquals;
@@ -14,10 +13,18 @@
public class Era5ArchiveTest {
private static final String SEP = File.separator;
+ private Configuration config;
+
+ @Before
+ public void setUp() throws Exception {
+ config = new Configuration();
+ }
@Test
- public void testConstructAndGet() {
- final Era5Archive era5Archive = new Era5Archive("archive" + SEP + "era5", Era5Collection.ERA_5);
+ public void testConstructAndGet_translateVariableNameToFileAccessName_true() {
+ config.setNWPAuxDir("archive" + SEP + "era5");
+ config.setTranslateVariableNameToFileAccessName(true); // default is true
+ final Era5Archive era5Archive = new Era5Archive(config, Era5Collection.ERA_5);
// Friday, 30. May 2008 11:00:00
// 1212145200
@@ -39,21 +46,50 @@ public void testConstructAndGet() {
assertEquals(expected, era5Archive.get("fc_sfc_mslhf", 1212400800));
}
+ @Test
+ public void testConstructAndGet_translateVariableNameToFileAccessName_false() {
+ config.setNWPAuxDir("archive" + SEP + "era5");
+ config.setTranslateVariableNameToFileAccessName(false);
+ final Era5Archive era5Archive = new Era5Archive(config, Era5Collection.ERA_5);
+
+ // Friday, 30. May 2008 11:00:00
+ // 1212145200
+ String expected = assemblePath("archive", "era5", "an_ml", "2008", "05", "30", "ecmwf-era5_oper_an_ml_200805301100.q.nc");
+ assertEquals(expected, era5Archive.get("an_ml_q", 1212145200));
+
+ expected = assemblePath("archive", "era5", "an_sfc", "2008", "05", "30", "ecmwf-era5_oper_an_sfc_200805301100.t2m.nc");
+ assertEquals(expected, era5Archive.get("an_sfc_t2m", 1212145200));
+
+ expected = assemblePath("archive", "era5", "fc_sfc", "2008", "05", "30", "ecmwf-era5_oper_fc_sfc_2008053006005.msnlwrf.nc");
+ assertEquals(expected, era5Archive.get("fc_sfc_msnlwrf", 1212145200));
+
+ // Monday, 2. June 2008 10:00:00
+ // 1212400800
+ expected = assemblePath("archive", "era5", "an_ml", "2008", "06", "02", "ecmwf-era5_oper_an_ml_200806021000.t.nc");
+ assertEquals(expected, era5Archive.get("an_ml_t", 1212400800));
+
+ expected = assemblePath("archive", "era5", "fc_sfc", "2008", "06", "02", "ecmwf-era5_oper_fc_sfc_2008060206004.mslhf.nc");
+ assertEquals(expected, era5Archive.get("fc_sfc_mslhf", 1212400800));
+ }
+
@Test
public void testGetFileName_era5() {
- final Era5Archive archive = new Era5Archive("whatever", Era5Collection.ERA_5);
+ config.setNWPAuxDir("whatever");
+ final Era5Archive archive = new Era5Archive(config, Era5Collection.ERA_5);
assertEquals("ecmwf-era5_oper_an_ml_201108231900.q.nc", archive.getFileName("an_ml", "q", "201108231900"));
}
@Test
public void testGetFileName_era5t() {
- final Era5Archive archive = new Era5Archive("whatever", Era5Collection.ERA_5T);
+ config.setNWPAuxDir("whatever");
+ final Era5Archive archive = new Era5Archive(config, Era5Collection.ERA_5T);
assertEquals("ecmwf-era5t_oper_an_ml_201108232000.q.nc", archive.getFileName("an_ml", "q", "201108232000"));
}
@Test
public void testGetFileName_era51() {
- final Era5Archive archive = new Era5Archive("whatever", Era5Collection.ERA_51);
+ config.setNWPAuxDir("whatever");
+ final Era5Archive archive = new Era5Archive(config, Era5Collection.ERA_51);
assertEquals("ecmwf-era51_oper_an_fc_201108232000.q.nc", archive.getFileName("an_fc", "q", "201108232000"));
}
diff --git a/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/era5/Era5PostProcessingPluginTest.java b/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/era5/Era5PostProcessingPluginTest.java
index 257feff6d..f0f9f4a71 100644
--- a/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/era5/Era5PostProcessingPluginTest.java
+++ b/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/era5/Era5PostProcessingPluginTest.java
@@ -2,22 +2,27 @@
import com.bc.fiduceo.TestUtil;
import com.bc.fiduceo.post.PostProcessing;
-import org.jdom.Element;
-import org.jdom.JDOMException;
+import com.bc.fiduceo.post.PostProcessingContext;
+import org.jdom2.Element;
+import org.jdom2.JDOMException;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
+import java.nio.file.Paths;
import static org.junit.Assert.*;
public class Era5PostProcessingPluginTest {
private Era5PostProcessingPlugin plugin;
+ private PostProcessingContext context;
@Before
public void setUp() {
plugin = new Era5PostProcessingPlugin();
+ context = new PostProcessingContext();
+ context.setConfigDirectory(Paths.get(".config"));
}
@Test
@@ -32,7 +37,7 @@ public void testCreateConfiguration_missing_nwpAuxDir() throws JDOMException, IO
final Element rootElement = TestUtil.createDomElement(XML);
try {
- Era5PostProcessingPlugin.createConfiguration(rootElement);
+ Era5PostProcessingPlugin.createConfiguration(rootElement, context);
fail("RuntimeException expected");
} catch (RuntimeException expected) {
}
@@ -45,7 +50,7 @@ public void testCreateConfiguration_nwpAuxDir() throws JDOMException, IOExceptio
"";
final Element rootElement = TestUtil.createDomElement(XML);
- final Configuration configuration = Era5PostProcessingPlugin.createConfiguration(rootElement);
+ final Configuration configuration = Era5PostProcessingPlugin.createConfiguration(rootElement, context);
assertEquals("/where/the/data/is", configuration.getNWPAuxDir());
}
@@ -80,25 +85,25 @@ public void testCreateConfiguration_satelliteFields() throws JDOMException, IOEx
"";
final Element rootElement = TestUtil.createDomElement(XML);
- final Configuration configuration = Era5PostProcessingPlugin.createConfiguration(rootElement);
+ final Configuration configuration = Era5PostProcessingPlugin.createConfiguration(rootElement, context);
assertEquals("The-One", configuration.getEra5Collection());
final SatelliteFieldsConfiguration satConfig = configuration.getSatelliteFields();
assertNotNull(satConfig);
- assertEquals("Kjuh", satConfig.get_an_q_name());
- assertEquals("tea", satConfig.get_an_t_name());
- assertEquals("ozone", satConfig.get_an_o3_name());
- assertEquals("pressure", satConfig.get_an_lnsp_name());
- assertEquals("tempi", satConfig.get_an_t2m_name());
- assertEquals("blowUp", satConfig.get_an_u10_name());
- assertEquals("blowVert", satConfig.get_an_v10_name());
- assertEquals("concentrate", satConfig.get_an_siconc_name());
- assertEquals("meanPress", satConfig.get_an_msl_name());
- assertEquals("skinTemp", satConfig.get_an_skt_name());
- assertEquals("ozeanTemp", satConfig.get_an_sst_name());
- assertEquals("cloudy", satConfig.get_an_tcc_name());
- assertEquals("steam!", satConfig.get_an_tcwv_name());
+ assertEquals("Kjuh", satConfig.getVarName("an_ml_q"));
+ assertEquals("tea", satConfig.getVarName("an_ml_t"));
+ assertEquals("ozone", satConfig.getVarName("an_ml_o3"));
+ assertEquals("pressure", satConfig.getVarName("an_ml_lnsp"));
+ assertEquals("tempi", satConfig.getVarName("an_sfc_t2m") );
+ assertEquals("blowUp", satConfig.getVarName("an_sfc_u10") );
+ assertEquals("blowVert", satConfig.getVarName("an_sfc_v10") );
+ assertEquals("concentrate", satConfig.getVarName("an_sfc_siconc") );
+ assertEquals("meanPress", satConfig.getVarName("an_sfc_msl"));
+ assertEquals("skinTemp", satConfig.getVarName("an_sfc_skt"));
+ assertEquals("ozeanTemp", satConfig.getVarName("an_sfc_sst"));
+ assertEquals("cloudy", satConfig.getVarName("an_sfc_tcc"));
+ assertEquals("steam!", satConfig.getVarName("an_sfc_tcwv"));
assertEquals(5, satConfig.get_x_dim());
assertEquals("left", satConfig.get_x_dim_name());
@@ -145,25 +150,25 @@ public void testCreateConfiguration_satelliteFields_with_sensorRef() throws JDOM
"";
final Element rootElement = TestUtil.createDomElement(XML);
- final Configuration configuration = Era5PostProcessingPlugin.createConfiguration(rootElement);
+ final Configuration configuration = Era5PostProcessingPlugin.createConfiguration(rootElement, context);
assertEquals("The-One", configuration.getEra5Collection());
final SatelliteFieldsConfiguration satConfig = configuration.getSatelliteFields();
assertNotNull(satConfig);
- assertEquals("hirs-n08_Kjuh", satConfig.get_an_q_name());
- assertEquals("hirs-n08_tea", satConfig.get_an_t_name());
- assertEquals("hirs-n08_ozone", satConfig.get_an_o3_name());
- assertEquals("hirs-n08_pressure", satConfig.get_an_lnsp_name());
- assertEquals("hirs-n08_tempi", satConfig.get_an_t2m_name());
- assertEquals("hirs-n08_blowUp", satConfig.get_an_u10_name());
- assertEquals("hirs-n08_blowVert", satConfig.get_an_v10_name());
- assertEquals("hirs-n08_concentrate", satConfig.get_an_siconc_name());
- assertEquals("hirs-n08_meanPress", satConfig.get_an_msl_name());
- assertEquals("hirs-n08_skinTemp", satConfig.get_an_skt_name());
- assertEquals("hirs-n08_ozeanTemp", satConfig.get_an_sst_name());
- assertEquals("hirs-n08_cloudy", satConfig.get_an_tcc_name());
- assertEquals("hirs-n08_steam!", satConfig.get_an_tcwv_name());
+ // assertEquals("hirs-n08_Kjuh", satConfig.getVarName("an_ml_q"));
+ // assertEquals("hirs-n08_tea", satConfig.getVarName("an_ml_t"));
+ // assertEquals("hirs-n08_ozone", satConfig.getVarName("an_ml_o3"));
+ // assertEquals("hirs-n08_pressure", satConfig.getVarName("an_ml_lnsp"));
+ assertEquals("hirs-n08_tempi", satConfig.getVarName("an_sfc_t2m") );
+ assertEquals("hirs-n08_blowUp", satConfig.getVarName("an_sfc_u10") );
+ assertEquals("hirs-n08_blowVert", satConfig.getVarName("an_sfc_v10") );
+ assertEquals("hirs-n08_concentrate", satConfig.getVarName("an_sfc_siconc") );
+ // assertEquals("hirs-n08_meanPress", satConfig.getVarName("an_sfc_msl"));
+ assertEquals("hirs-n08_skinTemp", satConfig.getVarName("an_sfc_skt"));
+ assertEquals("hirs-n08_ozeanTemp", satConfig.getVarName("an_sfc_sst"));
+ // assertEquals("hirs-n08_cloudy", satConfig.getVarName("an_sfc_tcc"));
+ assertEquals("hirs-n08_steam!", satConfig.getVarName("an_sfc_tcwv"));
assertEquals(5, satConfig.get_x_dim());
assertEquals("left", satConfig.get_x_dim_name());
@@ -178,6 +183,9 @@ public void testCreateConfiguration_satelliteFields_with_sensorRef() throws JDOM
assertEquals("sensor_clock", satConfig.get_time_variable_name());
}
+ // todo sabine .. done 2021-02-20
+ // The "length" attribute of tag " is no longer optional.
+/*
@Test
public void testCreateConfiguration_satelliteFields_zDimNotSet() throws JDOMException, IOException {
final String XML = "" +
@@ -186,7 +194,7 @@ public void testCreateConfiguration_satelliteFields_zDimNotSet() throws JDOMExce
" " +
" " +
" " +
- " " +
+ " " +
" era5-time" +
" along_way" +
" alattemacchiato" +
@@ -195,9 +203,10 @@ public void testCreateConfiguration_satelliteFields_zDimNotSet() throws JDOMExce
"";
final Element rootElement = TestUtil.createDomElement(XML);
- final Configuration configuration = Era5PostProcessingPlugin.createConfiguration(rootElement);
+ final Configuration configuration = Era5PostProcessingPlugin.createConfiguration(rootElement, null);
assertEquals(137, configuration.getSatelliteFields().get_z_dim());
}
+*/
@Test
public void testCreateConfiguration_matchupFields() throws JDOMException, IOException {
@@ -226,7 +235,7 @@ public void testCreateConfiguration_matchupFields() throws JDOMException, IOExce
"";
final Element rootElement = TestUtil.createDomElement(XML);
- final Configuration configuration = Era5PostProcessingPlugin.createConfiguration(rootElement);
+ final Configuration configuration = Era5PostProcessingPlugin.createConfiguration(rootElement, context);
final MatchupFieldsConfiguration matchupConfig = configuration.getMatchupFields();
assertNotNull(matchupConfig);
@@ -278,7 +287,7 @@ public void testCreateConfiguration_matchupFields_with_insituRef() throws JDOMEx
"";
final Element rootElement = TestUtil.createDomElement(XML);
- final Configuration configuration = Era5PostProcessingPlugin.createConfiguration(rootElement);
+ final Configuration configuration = Era5PostProcessingPlugin.createConfiguration(rootElement, context);
final MatchupFieldsConfiguration matchupConfig = configuration.getMatchupFields();
assertNotNull(matchupConfig);
@@ -311,7 +320,7 @@ public void testCreatePostProcessing() throws JDOMException, IOException {
"";
final Element rootElement = TestUtil.createDomElement(XML);
- final PostProcessing postProcessing = plugin.createPostProcessing(rootElement);
+ final PostProcessing postProcessing = plugin.createPostProcessing(rootElement, context);
assertNotNull(postProcessing);
assertTrue(postProcessing instanceof Era5PostProcessing);
}
diff --git a/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/era5/Era5PostProcessingPluginTest_parseGeneralizedInformations.java b/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/era5/Era5PostProcessingPluginTest_parseGeneralizedInformations.java
new file mode 100644
index 000000000..299ee2452
--- /dev/null
+++ b/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/era5/Era5PostProcessingPluginTest_parseGeneralizedInformations.java
@@ -0,0 +1,317 @@
+package com.bc.fiduceo.post.plugin.era5;
+
+import com.bc.fiduceo.TestUtil;
+import com.google.common.jimfs.Jimfs;
+import org.jdom2.Element;
+import org.jdom2.JDOMException;
+import org.jdom2.input.JDOMParseException;
+import org.jdom2.output.Format;
+import org.jdom2.output.XMLOutputter;
+import org.junit.Test;
+import org.mockito.MockedStatic;
+import org.mockito.Mockito;
+
+import java.io.*;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Map;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.when;
+
+public class Era5PostProcessingPluginTest_parseGeneralizedInformations {
+
+ @Test
+ public void testParseGeneralizedInformation_unableToCreateInputStream_mockIOException() {
+ //preparation
+ final Path configPathMock = Mockito.mock(Path.class);
+ final Path infoPathMock = Mockito.mock(Path.class);
+ when(configPathMock.resolve(Mockito.anyString())).thenReturn(infoPathMock);
+ when(infoPathMock.toString()).thenReturn("somePathName");
+ try (MockedStatic filesMock = Mockito.mockStatic(Files.class)) {
+ filesMock.when(() -> Files.exists(infoPathMock)).thenReturn(true);
+ filesMock.when(() -> Files.newInputStream(infoPathMock)).thenThrow(new IOException("mocked"));
+
+ //execution
+ Era5PostProcessingPlugin.parseGeneralizedInformation(new Configuration(), configPathMock);
+
+ //verification
+ fail("RuntimeException expected");
+ } catch (Throwable e) {
+ assertThat(e.getClass(), is(equalTo(RuntimeException.class)));
+ assertEquals("Unable to create input stream from somePathName.", e.getMessage());
+ final Throwable cause = e.getCause();
+ assertThat(cause.getClass(), is(equalTo(IOException.class)));
+ assertEquals("mocked", cause.getMessage());
+ }
+ }
+
+ @Test
+ public void testParseGeneralizedInformation_generalInfoIsNotValidXml() throws IOException {
+ //preparation
+ final String XML = "habi dubi \n" +
+ " babi \n" +
+ " \n" +
+ "";
+ final Path configPath = Jimfs.newFileSystem().getPath("config");
+
+ final Path xmlPath = configPath.resolve(Era5PostProcessingPlugin.ERA5_POST_PROCESSING_GENERAL_INFO_XML);
+ Files.createDirectories(configPath);
+ try (
+ OutputStream os = Files.newOutputStream(xmlPath);
+ PrintWriter pw = new PrintWriter(os)
+ ) {
+ pw.println(XML);
+ }
+
+ try {
+ //execution
+ Era5PostProcessingPlugin.parseGeneralizedInformation(new Configuration(), configPath);
+
+ //verification
+ fail("RuntimeException expected");
+ } catch (Throwable e) {
+ assertThat(e.getClass(), is(equalTo(RuntimeException.class)));
+ assertEquals("XML document " + xmlPath + " could not be read in.", e.getMessage());
+ final Throwable cause = e.getCause();
+ assertThat(cause.getClass(), is(equalTo(JDOMParseException.class)));
+ assertThat(cause.getMessage(), startsWith("Error on line 1:"));
+ }
+ }
+
+ @Test
+ public void testParseGeneralizedInformation_fromFile_rootTagIsNot_era5() throws IOException, JDOMException {
+ //preparation
+ final String XML = "\n" +
+ " \n" +
+ " \n" +
+ "";
+ final Path configPath = output(XML);
+
+ try {
+ //execution
+ Era5PostProcessingPlugin.parseGeneralizedInformation(new Configuration(), configPath);
+
+ //verification
+ fail("RuntimeException expected");
+ } catch (Throwable e) {
+ assertThat(e.getClass(), is(equalTo(RuntimeException.class)));
+ assertEquals("Root tag expected in config"+ File.separator + "era5-post-processing-general-info.xml", e.getMessage());
+ final Throwable cause = e.getCause();
+ assertThat(cause.getClass(), is(equalTo(Throwable.class)));
+ assertEquals("Root tag expected", cause.getMessage());
+ }
+ }
+
+ @Test
+ public void testParseGeneralizedInformation_rootTagIsNot_era5() throws IOException, JDOMException {
+ //preparation
+ final String XML = "\n" +
+ " \n" +
+ " \n" +
+ "";
+ final Element rootElement = TestUtil.createDomElement(XML);
+
+ try {
+ //execution
+ Era5PostProcessingPlugin.parseGeneralizedInformation(rootElement, new Configuration());
+
+ //verification
+ fail("Throwable expected");
+ } catch (Throwable e) {
+ assertThat(e.getClass(), is(equalTo(Throwable.class)));
+ assertEquals("Root tag expected", e.getMessage());
+ }
+ }
+
+ @Test
+ public void testParseGeneralizedInformation_unknownAttributeName() throws IOException, JDOMException {
+ //preparation
+ final String XML = "\n" +
+ " \n" +
+ " \n" +
+ " true\n" +
+ " \n" +
+ " K\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ "";
+ final Element rootElement = TestUtil.createDomElement(XML);
+
+ try {
+ //execution
+ Era5PostProcessingPlugin.parseGeneralizedInformation(rootElement, new Configuration());
+
+ //verification
+ fail("Throwable expected");
+ } catch (Throwable e) {
+ assertThat(e.getClass(), is(equalTo(Throwable.class)));
+ assertEquals("Unknown attribute name Pumpernikel", e.getMessage());
+ }
+ }
+
+ @Test
+ public void testParseGeneralizedInformation_noGenaralInformationFile() throws IOException, JDOMException {
+ //preparation
+ final Path configPath = Jimfs.newFileSystem().getPath("config");
+ Files.createDirectories(configPath);
+ final Configuration config = new Configuration();
+
+ //execution
+ Era5PostProcessingPlugin.parseGeneralizedInformation(config, configPath);
+
+ //verification
+ assertThat(config.getSatelliteFields(), is(nullValue()));
+ }
+
+ @Test
+ public void testParseGeneralizedInformation_readFromFile() throws IOException, JDOMException {
+ //preparation
+ final String XML = "\n" +
+ " \n" +
+ " \n" +
+ " true\n" +
+ " \n" +
+ " K\n" +
+ " Temperature\n" +
+ " air_temperature\n" +
+ " 24.3\n" +
+ " \n" +
+ " \n" +
+ " kg kg**-1\n" +
+ " Specific humidity\n" +
+ " specific_humidity\n" +
+ " 24.3e-5\n" +
+ " \n" +
+ " \n" +
+ " kg kg**-1\n" +
+ " Specific cloud liquid water content\n" +
+ " \n" +
+ " \n" +
+ " kg kg**-1\n" +
+ " Specific rain water content\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " kg m**-2\n" +
+ " Total column cloud liquid water\n" +
+ " \n" +
+ " \n" +
+ " kg m**-2\n" +
+ " Total column cloud ice water\n" +
+ " \n" +
+ " \n" +
+ " kg m**-2\n" +
+ " Total column rain water\n" +
+ " \n" +
+ " \n" +
+ " kg m**-2\n" +
+ " Total column snow water\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ "";
+ final Path configPath = output(XML);
+ final Configuration configuration = new Configuration();
+
+ //execution
+ Era5PostProcessingPlugin.parseGeneralizedInformation(configuration, configPath);
+
+ //verification
+ final SatelliteFieldsConfiguration satConf = configuration.getSatelliteFields();
+ assertNotNull(satConf);
+ final Map genVars = satConf.getGeneralizedVariables();
+ assertNotNull(genVars);
+ assertThat(genVars.size(), is(8));
+
+ TemplateVariable var = genVars.get("an_pl_t");
+ assertNotNull(var);
+ assertThat(var.getName(), is("t"));
+ assertThat(var.getUnits(), is("K"));
+ assertThat(var.getLongName(), is("Temperature"));
+ assertThat(var.getStandardName(), is("air_temperature"));
+ assertThat(var.getFillValue(), is(24.3f));
+
+ var = genVars.get("an_pl_q");
+ assertNotNull(var);
+ assertThat(var.getName(), is("q"));
+ assertThat(var.getUnits(), is("kg kg**-1"));
+ assertThat(var.getLongName(), is("Specific humidity"));
+ assertThat(var.getStandardName(), is("specific_humidity"));
+ assertThat(var.getFillValue(), is(24.3e-5f));
+ assertThat(var.is3d(), is(true));
+
+ var = genVars.get("an_pl_clwc");
+ assertNotNull(var);
+ assertThat(var.getName(), is("clwc"));
+ assertThat(var.getUnits(), is("kg kg**-1"));
+ assertThat(var.getLongName(), is("Specific cloud liquid water content"));
+ assertThat(var.getStandardName(), is(nullValue()));
+ assertThat(var.getFillValue(), is(9.96921E36F));
+ assertThat(var.is3d(), is(true));
+
+ var = genVars.get("an_pl_crwc");
+ assertNotNull(var);
+ assertThat(var.getName(), is("crwc"));
+ assertThat(var.getUnits(), is("kg kg**-1"));
+ assertThat(var.getLongName(), is("Specific rain water content"));
+ assertThat(var.getStandardName(), is(nullValue()));
+ assertThat(var.getFillValue(), is(9.96921E36F));
+ assertThat(var.is3d(), is(true));
+
+ var = genVars.get("an_sfc_tclw");
+ assertNotNull(var);
+ assertThat(var.getName(), is("tclw"));
+ assertThat(var.getUnits(), is("kg m**-2"));
+ assertThat(var.getLongName(), is("Total column cloud liquid water"));
+ assertThat(var.getStandardName(), is(nullValue()));
+ assertThat(var.getFillValue(), is(9.96921E36F));
+ assertThat(var.is3d(), is(false));
+
+ var = genVars.get("an_sfc_tciw");
+ assertNotNull(var);
+ assertThat(var.getName(), is("tciw"));
+ assertThat(var.getUnits(), is("kg m**-2"));
+ assertThat(var.getLongName(), is("Total column cloud ice water"));
+ assertThat(var.getStandardName(), is(nullValue()));
+ assertThat(var.getFillValue(), is(9.96921E36F));
+ assertThat(var.is3d(), is(false));
+
+ var = genVars.get("an_sfc_tcrw");
+ assertNotNull(var);
+ assertThat(var.getName(), is("tcrw"));
+ assertThat(var.getUnits(), is("kg m**-2"));
+ assertThat(var.getLongName(), is("Total column rain water"));
+ assertThat(var.getStandardName(), is(nullValue()));
+ assertThat(var.getFillValue(), is(9.96921E36F));
+ assertThat(var.is3d(), is(false));
+
+ var = genVars.get("an_sfc_tcsw");
+ assertNotNull(var);
+ assertThat(var.getName(), is("tcsw"));
+ assertThat(var.getUnits(), is("kg m**-2"));
+ assertThat(var.getLongName(), is("Total column snow water"));
+ assertThat(var.getStandardName(), is(nullValue()));
+ assertThat(var.getFillValue(), is(9.96921E36F));
+ assertThat(var.is3d(), is(false));
+ }
+
+ private static Path output(String XML) throws JDOMException, IOException {
+ final Element rootElement = TestUtil.createDomElement(XML);
+ final Path configPath = Jimfs.newFileSystem().getPath("config");
+ final Path xmlPath = configPath.resolve(Era5PostProcessingPlugin.ERA5_POST_PROCESSING_GENERAL_INFO_XML);
+ Files.createDirectories(configPath);
+ try (OutputStream os = Files.newOutputStream(xmlPath)) {
+ new XMLOutputter(Format.getPrettyFormat())
+ .output(rootElement, os);
+ }
+ return configPath;
+ }
+}
+
diff --git a/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/era5/Era5PostProcessingTest.java b/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/era5/Era5PostProcessingTest.java
index 21d1a43f9..9c92df8a9 100644
--- a/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/era5/Era5PostProcessingTest.java
+++ b/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/era5/Era5PostProcessingTest.java
@@ -36,12 +36,6 @@ public void testGetInterpolationContext() {
interpolator = context.get(2, 1);
assertEquals(834, interpolator.getXMin());
assertEquals(247, interpolator.getYMin());
-
- final Rectangle era5Region = context.getEra5Region();
- assertEquals(834, era5Region.x);
- assertEquals(247, era5Region.y);
- assertEquals(3, era5Region.width);
- assertEquals(3, era5Region.height);
}
@Test
@@ -58,12 +52,6 @@ public void testGetInterpolationContext_singlePixel() {
BilinearInterpolator interpolator = context.get(0, 0);
assertEquals(835, interpolator.getXMin());
assertEquals(247, interpolator.getYMin());
-
- final Rectangle era5Region = context.getEra5Region();
- assertEquals(835, era5Region.x);
- assertEquals(247, era5Region.y);
- assertEquals(2, era5Region.width);
- assertEquals(2, era5Region.height);
}
@Test
@@ -120,6 +108,7 @@ public void testGetLonMin() {
assertEquals(1344, Era5PostProcessing.getEra5LonMin(-23.8f));
assertEquals(1438, Era5PostProcessing.getEra5LonMin(-0.26f));
assertEquals(1439, Era5PostProcessing.getEra5LonMin(-0.18f));
+ assertEquals(1439, Era5PostProcessing.getEra5LonMin(-0.01236f));
assertEquals(0, Era5PostProcessing.getEra5LonMin(0.f));
assertEquals(173, Era5PostProcessing.getEra5LonMin(43.32f));
assertEquals(718, Era5PostProcessing.getEra5LonMin(179.58f));
@@ -171,6 +160,10 @@ public void testCreateInterpolator() {
assertEquals(0.2799999713897705, interpolator.getA(), 1e-8);
assertEquals(1.0, interpolator.getB(), 1e-8);
+ interpolator = Era5PostProcessing.createInterpolator(-0.01236f, 0.f, 1439, 359);
+ assertEquals(1439, interpolator.getXMin(), 1e-8);
+ assertEquals(359, interpolator.getYMin(), 1e-8);
+
interpolator = Era5PostProcessing.createInterpolator(43.32f, -22.19f, 173, 448);
assertEquals(0.279998779296875, interpolator.getA(), 1e-8);
assertEquals(0.7600021362304688, interpolator.getB(), 1e-8);
diff --git a/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/era5/InterpolationContextTest.java b/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/era5/InterpolationContextTest.java
index a990f98f9..c5106a4cd 100644
--- a/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/era5/InterpolationContextTest.java
+++ b/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/era5/InterpolationContextTest.java
@@ -1,9 +1,8 @@
package com.bc.fiduceo.post.plugin.era5;
+import com.bc.fiduceo.core.IntRange;
import org.junit.Test;
-import java.awt.*;
-
import static org.junit.Assert.*;
public class InterpolationContextTest {
@@ -85,15 +84,109 @@ public void testGet_outOfBounds() {
}
@Test
- public void testSetGetEra5Region() {
- final Rectangle rectangle = new Rectangle(5, 6, 7, 8);
- final InterpolationContext context = new InterpolationContext(6, 4);
-
- context.setEra5Region(rectangle);
- final Rectangle era5Region = context.getEra5Region();
- assertEquals(5, era5Region.x);
- assertEquals(6, era5Region.y);
- assertEquals(7, era5Region.width);
- assertEquals(8, era5Region.height);
+ public void testGetXRanges_emptyContext() {
+ final InterpolationContext context = new InterpolationContext(5, 3);
+
+ final IntRange[] xRanges = context.getXRanges();
+ assertEquals(2, xRanges.length);
+ assertEquals(Integer.MAX_VALUE, xRanges[0].getMin());
+ assertEquals(Integer.MIN_VALUE, xRanges[0].getMax());
+ assertNull(xRanges[1]);
+ }
+
+ @Test
+ public void testGetXRanges_oneInterpolator() {
+ final InterpolationContext context = new InterpolationContext(4, 5);
+
+ final BilinearInterpolator interpolator = new BilinearInterpolator(0.3, 0.5, 6, 7);
+ context.set(0, 0, interpolator);
+
+ final IntRange[] xRanges = context.getXRanges();
+ assertEquals(2, xRanges.length);
+ assertEquals(6, xRanges[0].getMin());
+ assertEquals(7, xRanges[0].getMax());
+
+ assertEquals(0, interpolator.getRelXMin());
+ assertEquals(0, interpolator.getRelYMin());
+ }
+
+ @Test
+ public void testGetXRanges_threeInterpolators() {
+ final InterpolationContext context = new InterpolationContext(4, 5);
+
+ BilinearInterpolator interpolator = new BilinearInterpolator(0.3, 0.5, 6, 7);
+ context.set(0, 0, interpolator);
+
+ interpolator = new BilinearInterpolator(0.3, 0.5, 7, 8);
+ context.set(1, 1, interpolator);
+
+ interpolator = new BilinearInterpolator(0.3, 0.5, 8, 9);
+ context.set(2, 2, interpolator);
+
+ final IntRange[] xRanges = context.getXRanges();
+ assertEquals(2, xRanges.length);
+ assertEquals(6, xRanges[0].getMin());
+ assertEquals(9, xRanges[0].getMax());
+ }
+
+ @Test
+ public void testGetXRanges_threeInterpolators_antimeridianCase() {
+ final InterpolationContext context = new InterpolationContext(4, 5);
+
+ BilinearInterpolator interpolator = new BilinearInterpolator(0.3, 0.5, 1438, 7);
+ context.set(0, 0, interpolator);
+
+ interpolator = new BilinearInterpolator(0.3, 0.5, 1439, 8);
+ context.set(1, 1, interpolator);
+
+ interpolator = new BilinearInterpolator(0.3, 0.5, 0, 9);
+ context.set(2, 2, interpolator);
+
+ final IntRange[] xRanges = context.getXRanges();
+ assertEquals(2, xRanges.length);
+ assertEquals(1438, xRanges[0].getMin());
+ assertEquals(1439, xRanges[0].getMax());
+
+ assertEquals(0, xRanges[1].getMin());
+ assertEquals(1, xRanges[1].getMax());
+ }
+
+ @Test
+ public void testGetYRange_emptyContext() {
+ final InterpolationContext context = new InterpolationContext(5, 3);
+
+ final IntRange yRange = context.getYRange();
+ assertEquals(Integer.MAX_VALUE, yRange.getMin());
+ assertEquals(Integer.MIN_VALUE, yRange.getMax());
+ }
+
+ @Test
+ public void testGetYRange_oneInterpolator() {
+ final InterpolationContext context = new InterpolationContext(4, 5);
+
+ final BilinearInterpolator interpolator = new BilinearInterpolator(0.3, 0.5, 6, 7);
+ context.set(0, 0, interpolator);
+
+ final IntRange yRange = context.getYRange();
+ assertEquals(7, yRange.getMin());
+ assertEquals(8, yRange.getMax());
+ }
+
+ @Test
+ public void testGetYRange_threeInterpolators() {
+ final InterpolationContext context = new InterpolationContext(4, 5);
+
+ BilinearInterpolator interpolator = new BilinearInterpolator(0.3, 0.5, 6, 7);
+ context.set(0, 0, interpolator);
+
+ interpolator = new BilinearInterpolator(0.3, 0.5, 7, 7);
+ context.set(1, 0, interpolator);
+
+ interpolator = new BilinearInterpolator(0.3, 0.5, 7, 8);
+ context.set(1, 1, interpolator);
+
+ final IntRange yRange = context.getYRange();
+ assertEquals(7, yRange.getMin());
+ assertEquals(9, yRange.getMax());
}
}
diff --git a/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/era5/SatelliteFieldsConfigurationTest.java b/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/era5/SatelliteFieldsConfigurationTest.java
index b23cc3281..a4c26208b 100644
--- a/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/era5/SatelliteFieldsConfigurationTest.java
+++ b/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/era5/SatelliteFieldsConfigurationTest.java
@@ -16,19 +16,19 @@ public void setUp() {
@Test
public void testConstructionAndDefaultValues() {
- assertEquals("nwp_q", config.get_an_q_name());
- assertEquals("nwp_t", config.get_an_t_name());
- assertEquals("nwp_o3", config.get_an_o3_name());
- assertEquals("nwp_lnsp", config.get_an_lnsp_name());
- assertEquals("nwp_t2m", config.get_an_t2m_name());
- assertEquals("nwp_siconc", config.get_an_siconc_name());
- assertEquals("nwp_u10", config.get_an_u10_name());
- assertEquals("nwp_v10", config.get_an_v10_name());
- assertEquals("nwp_msl", config.get_an_msl_name());
- assertEquals("nwp_skt", config.get_an_skt_name());
- assertEquals("nwp_sst", config.get_an_sst_name());
- assertEquals("nwp_tcc", config.get_an_tcc_name());
- assertEquals("nwp_tcwv", config.get_an_tcwv_name());
+ assertEquals("nwp_q", config.getVarName("an_ml_q"));
+ assertEquals("nwp_t", config.getVarName("an_ml_t"));
+ assertEquals("nwp_o3", config.getVarName("an_ml_o3"));
+ assertEquals("nwp_lnsp", config.getVarName("an_ml_lnsp"));
+ assertEquals("nwp_t2m", config.getVarName("an_sfc_t2m") );
+ assertEquals("nwp_siconc", config.getVarName("an_sfc_siconc") );
+ assertEquals("nwp_u10", config.getVarName("an_sfc_u10") );
+ assertEquals("nwp_v10", config.getVarName("an_sfc_v10") );
+ assertEquals("nwp_msl", config.getVarName("an_sfc_msl"));
+ assertEquals("nwp_skt", config.getVarName("an_sfc_skt"));
+ assertEquals("nwp_sst", config.getVarName("an_sfc_sst"));
+ assertEquals("nwp_tcc", config.getVarName("an_sfc_tcc"));
+ assertEquals("nwp_tcwv", config.getVarName("an_sfc_tcwv"));
assertEquals(-1, config.get_x_dim());
assertEquals(-1, config.get_y_dim());
@@ -44,81 +44,33 @@ public void testConstructionAndDefaultValues() {
}
@Test
- public void testSetGet_an_q() {
- config.set_an_q_name("anku");
- assertEquals("anku", config.get_an_q_name());
- }
-
- @Test
- public void testSetGet_an_t() {
- config.set_an_t_name("tee");
- assertEquals("tee", config.get_an_t_name());
- }
-
- @Test
- public void testSetGet_an_o3() {
- config.set_an_o3_name("ozzi");
- assertEquals("ozzi", config.get_an_o3_name());
- }
-
- @Test
- public void testSetGet_an_lnsp() {
- config.set_an_lnsp_name("pratt");
- assertEquals("pratt", config.get_an_lnsp_name());
- }
-
- @Test
- public void testSetGet_an_t2m() {
- config.set_an_t2m_name("tempi");
- assertEquals("tempi", config.get_an_t2m_name());
- }
-
- @Test
- public void testSetGet_an_u10() {
- config.set_an_u10_name("windu");
- assertEquals("windu", config.get_an_u10_name());
- }
-
- @Test
- public void testSetGet_an_v10() {
- config.set_an_v10_name("Vicky");
- assertEquals("Vicky", config.get_an_v10_name());
- }
-
- @Test
- public void testSetGet_an_siconc() {
- config.set_an_siconc_name("sieglinde");
- assertEquals("sieglinde", config.get_an_siconc_name());
- }
-
- @Test
- public void testSetGet_an_mslc() {
- config.set_an_msl_name("meanSurf");
- assertEquals("meanSurf", config.get_an_msl_name());
- }
-
- @Test
- public void testSetGet_an_skt() {
- config.set_an_skt_name("scinny");
- assertEquals("scinny", config.get_an_skt_name());
- }
-
- @Test
- public void testSetGet_an_sst() {
- config.set_an_sst_name("seaTemp");
- assertEquals("seaTemp", config.get_an_sst_name());
- }
-
- @Test
- public void testSetGet_an_tcc() {
- config.set_an_tcc_name("cloudCover");
- assertEquals("cloudCover", config.get_an_tcc_name());
- }
-
- @Test
- public void testSetGet_an_tcwv() {
- config.set_an_tcwv_name("steamy");
- assertEquals("steamy", config.get_an_tcwv_name());
+ public void testSetGetVarName() {
+ config.setVarName("an_ml_q","anku");
+ assertEquals("anku", config.getVarName("an_ml_q"));
+ config.setVarName("an_ml_t","tee");
+ assertEquals("tee", config.getVarName("an_ml_t"));
+ config.setVarName("an_ml_o3", "ozzi");
+ assertEquals("ozzi", config.getVarName("an_ml_o3"));
+ config.setVarName("an_ml_lnsp", "pratt");
+ assertEquals("pratt", config.getVarName("an_ml_lnsp"));
+ config.setVarName("an_sfc_t2m", "tempi");
+ assertEquals("tempi", config.getVarName("an_sfc_t2m") );
+ config.setVarName("an_sfc_u10", "windu");
+ assertEquals("windu", config.getVarName("an_sfc_u10") );
+ config.setVarName("an_sfc_v10", "Vicky");
+ assertEquals("Vicky", config.getVarName("an_sfc_v10") );
+ config.setVarName("an_sfc_siconc", "sieglinde");
+ assertEquals("sieglinde", config.getVarName("an_sfc_siconc") );
+ config.setVarName("an_sfc_msl", "meanSurf");
+ assertEquals("meanSurf", config.getVarName("an_sfc_msl"));
+ config.setVarName("an_sfc_skt", "scinny");
+ assertEquals("scinny", config.getVarName("an_sfc_skt"));
+ config.setVarName("an_sfc_sst", "seaTemp");
+ assertEquals("seaTemp", config.getVarName("an_sfc_sst"));
+ config.setVarName("an_sfc_tcc", "cloudCover");
+ assertEquals("cloudCover", config.getVarName("an_sfc_tcc"));
+ config.setVarName("an_sfc_tcwv", "steamy");
+ assertEquals("steamy", config.getVarName("an_sfc_tcwv"));
}
@Test
@@ -179,44 +131,44 @@ public void testGetVariablesWithReplacement() {
config.set_time_variable_name("{sensor-ref}_acquisition-time");
assertEquals("avhrr-n14_acquisition-time", config.get_time_variable_name());
- config.set_an_q_name("a_{sensor-ref}_n_q");
- assertEquals("a_avhrr-n14_n_q", config.get_an_q_name());
+ config.setVarName("an_ml_q", "a_{sensor-ref}_n_q");
+ assertEquals("a_avhrr-n14_n_q", config.getVarName("an_ml_q"));
- config.set_an_t_name("an_{sensor-ref}_t");
- assertEquals("an_avhrr-n14_t", config.get_an_t_name());
+ config.setVarName("an_ml_t","an_{sensor-ref}_t");
+ assertEquals("an_avhrr-n14_t", config.getVarName("an_ml_t"));
- config.set_an_o3_name("{sensor-ref}_o3");
- assertEquals("avhrr-n14_o3", config.get_an_o3_name());
+ config.setVarName("an_ml_o3", "{sensor-ref}_o3");
+ assertEquals("avhrr-n14_o3", config.getVarName("an_ml_o3"));
- config.set_an_lnsp_name("{sensor-ref}_lnsp");
- assertEquals("avhrr-n14_lnsp", config.get_an_lnsp_name());
+ config.setVarName("an_ml_lnsp", "{sensor-ref}_lnsp");
+ assertEquals("avhrr-n14_lnsp", config.getVarName("an_ml_lnsp"));
- config.set_an_t2m_name("{sensor-ref}_t2m");
- assertEquals("avhrr-n14_t2m", config.get_an_t2m_name());
+ config.setVarName("an_sfc_t2m", "{sensor-ref}_t2m");
+ assertEquals("avhrr-n14_t2m", config.getVarName("an_sfc_t2m") );
- config.set_an_siconc_name("{sensor-ref}_siconc");
- assertEquals("avhrr-n14_siconc", config.get_an_siconc_name());
+ config.setVarName("an_sfc_siconc", "{sensor-ref}_siconc");
+ assertEquals("avhrr-n14_siconc", config.getVarName("an_sfc_siconc") );
- config.set_an_u10_name("{sensor-ref}_u10");
- assertEquals("avhrr-n14_u10", config.get_an_u10_name());
+ config.setVarName("an_sfc_u10", "{sensor-ref}_u10");
+ assertEquals("avhrr-n14_u10", config.getVarName("an_sfc_u10") );
- config.set_an_v10_name("{sensor-ref}_v10");
- assertEquals("avhrr-n14_v10", config.get_an_v10_name());
+ config.setVarName("an_sfc_v10", "{sensor-ref}_v10");
+ assertEquals("avhrr-n14_v10", config.getVarName("an_sfc_v10") );
- config.set_an_msl_name("{sensor-ref}_msl");
- assertEquals("avhrr-n14_msl", config.get_an_msl_name());
+ config.setVarName("an_sfc_msl", "{sensor-ref}_msl");
+ assertEquals("avhrr-n14_msl", config.getVarName("an_sfc_msl"));
- config.set_an_skt_name("{sensor-ref}_skt");
- assertEquals("avhrr-n14_skt", config.get_an_skt_name());
+ config.setVarName("an_sfc_skt", "{sensor-ref}_skt");
+ assertEquals("avhrr-n14_skt", config.getVarName("an_sfc_skt"));
- config.set_an_sst_name("{sensor-ref}_sst");
- assertEquals("avhrr-n14_sst", config.get_an_sst_name());
+ config.setVarName("an_sfc_sst", "{sensor-ref}_sst");
+ assertEquals("avhrr-n14_sst", config.getVarName("an_sfc_sst"));
- config.set_an_tcc_name("{sensor-ref}_tcc");
- assertEquals("avhrr-n14_tcc", config.get_an_tcc_name());
+ config.setVarName("an_sfc_tcc", "{sensor-ref}_tcc");
+ assertEquals("avhrr-n14_tcc", config.getVarName("an_sfc_tcc"));
- config.set_an_tcwv_name("{sensor-ref}_tcwv");
- assertEquals("avhrr-n14_tcwv", config.get_an_tcwv_name());
+ config.setVarName("an_sfc_tcwv", "{sensor-ref}_tcwv");
+ assertEquals("avhrr-n14_tcwv", config.getVarName("an_sfc_tcwv"));
}
@Test
@@ -274,7 +226,6 @@ public void testVerify_y_dim() {
} catch (IllegalArgumentException expected) {
}
}
-
@Test
public void testVerify_y_dim_name() {
prepareConfig();
@@ -287,6 +238,18 @@ public void testVerify_y_dim_name() {
}
}
+ @Test
+ public void testVerify_z_dim() {
+ prepareConfig();
+ config.set_z_dim(-2);
+
+ try {
+ config.verify();
+ fail("IllegalArgumentException expected");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
@Test
public void testVerify_z_dim_name() {
prepareConfig();
@@ -299,6 +262,17 @@ public void testVerify_z_dim_name() {
}
}
+ @Test
+ public void test_dont_verify_z_dim_if_z_dim_name_is_not_set() {
+ //preparation
+ prepareConfig();
+ config.set_z_dim(-2);
+ config.set_z_dim_name(null);
+
+ //execution
+ config.verify();
+ }
+
@Test
public void testVerify_nwp_time_variable_name() {
prepareConfig();
diff --git a/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/era5/SatelliteFieldsTest.java b/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/era5/SatelliteFieldsTest.java
index b894a7718..3229701ae 100644
--- a/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/era5/SatelliteFieldsTest.java
+++ b/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/era5/SatelliteFieldsTest.java
@@ -1,7 +1,6 @@
package com.bc.fiduceo.post.plugin.era5;
import com.bc.fiduceo.FiduceoConstants;
-import org.checkerframework.checker.units.qual.A;
import org.junit.Test;
import ucar.ma2.Array;
import ucar.ma2.DataType;
@@ -9,13 +8,18 @@
import ucar.nc2.Dimension;
import ucar.nc2.NetcdfFile;
import ucar.nc2.NetcdfFileWriter;
-import ucar.nc2.Variable;
import java.awt.*;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import static org.junit.Assert.*;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.*;
@@ -26,7 +30,7 @@ public class SatelliteFieldsTest {
public void testGetVariables() {
final SatelliteFields satelliteFields = new SatelliteFields();
final SatelliteFieldsConfiguration config = new SatelliteFieldsConfiguration();
- config.set_an_skt_name("Skate");
+ config.setVarName("an_sfc_skt", "Skate");
final Map variables = satelliteFields.getVariables(config);
assertEquals(13, variables.size());
@@ -46,6 +50,48 @@ public void testGetVariables() {
assertFalse(template.is3d());
}
+ @Test
+ public void testGetVariables_withGeneralizedVariables() {
+ //preparation
+ final SatelliteFieldsConfiguration config = new SatelliteFieldsConfiguration();
+ final TemplateVariable tempVar1 = new TemplateVariable("skt", "u1", "ln1", "sn1", true);
+ final TemplateVariable tempVar2 = new TemplateVariable("kbb", "u2", "ln2", null, false);
+ tempVar1.setFill_value(123.4f);
+ tempVar2.setFill_value(123.4e-1f);
+ final HashMap generalizedVars = new HashMap<>();
+ generalizedVars.put("an_sfc_skt", tempVar1);
+ generalizedVars.put("an_sfc_kbb", tempVar2);
+ config.setGeneralizedVariables(generalizedVars);
+
+ config.setSensorRef("DerSensor");
+ config.setVarName("an_sfc_skt", "{sensor-ref}.und-skt");
+ config.setVarName("an_sfc_kbb", "{sensor-ref}.morgenrot");
+
+ final SatelliteFields satelliteFields = new SatelliteFields();
+
+ //execution
+ final Map variables = satelliteFields.getVariables(config);
+
+ //verification
+ assertEquals(2, variables.size());
+
+ TemplateVariable template = variables.get("an_sfc_skt");
+ assertEquals("sn1", template.getStandardName());
+ assertEquals("u1", template.getUnits());
+ assertEquals("ln1", template.getLongName());
+ assertEquals("DerSensor.und-skt", template.getName());
+ assertThat(template.getFillValue(), is(123.4f));
+ assertTrue(template.is3d());
+
+ template = variables.get("an_sfc_kbb");
+ assertNull(template.getStandardName());
+ assertEquals("u2", template.getUnits());
+ assertEquals("ln2", template.getLongName());
+ assertEquals("DerSensor.morgenrot", template.getName());
+ assertThat(template.getFillValue(), is(12.34f));
+ assertFalse(template.is3d());
+ }
+
@Test
public void testSetGetDimensions_2D() {
final NetcdfFile ncFile = mock(NetcdfFile.class);
diff --git a/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/era5/SatelliteFields_IO_Test.java b/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/era5/SatelliteFields_IO_Test.java
deleted file mode 100644
index 9f430dab4..000000000
--- a/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/era5/SatelliteFields_IO_Test.java
+++ /dev/null
@@ -1,150 +0,0 @@
-package com.bc.fiduceo.post.plugin.era5;
-
-
-import com.bc.fiduceo.IOTestRunner;
-import com.bc.fiduceo.TestUtil;
-import org.checkerframework.checker.units.qual.C;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import ucar.ma2.Array;
-import ucar.ma2.InvalidRangeException;
-import ucar.nc2.NetcdfFile;
-import ucar.nc2.Variable;
-
-import java.awt.*;
-import java.io.File;
-import java.io.IOException;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-
-@RunWith(IOTestRunner.class)
-public class SatelliteFields_IO_Test {
-
- @Test
- public void testReadSubset_2D() throws IOException, InvalidRangeException {
- final File anMlFile = TestUtil.getTestDataFileAsserted("era-5/v1/an_ml/2008/05/30/ecmwf-era5_oper_an_ml_200805301100.lnsp.nc");
-
- try (NetcdfFile netcdfFile = NetcdfFile.open(anMlFile.getAbsolutePath())) {
- final Variable lnsp = netcdfFile.findVariable("lnsp");
- assertNotNull(lnsp);
- Array lnspFull = lnsp.read().reduce();
-
- final Rectangle era5Positions = new Rectangle(1200, 400, 3, 3);
-
- final Array lnspArray = SatelliteFields.readSubset(1, era5Positions, new VariableCache.CacheEntry(lnsp, lnspFull));
- assertNotNull(lnspArray);
-
- final int[] shape = lnspArray.getShape();
- assertEquals(2, shape.length);
- assertEquals(3, shape[0]);
- assertEquals(3, shape[1]);
-
- assertEquals(11.503242492675781f, lnspArray.getFloat(0), 1e-8);
- assertEquals(11.503653526306152f, lnspArray.getFloat(1), 1e-8);
- assertEquals(11.505866050720215f, lnspArray.getFloat(2), 1e-8);
- }
- }
-
- @Test
- public void testReadSubset_3D() throws IOException, InvalidRangeException {
- final File o3File = TestUtil.getTestDataFileAsserted("era-5/v1/an_ml/2008/05/30/ecmwf-era5_oper_an_ml_200805301100.o3.nc");
-
- try (NetcdfFile netcdfFile = NetcdfFile.open(o3File.getAbsolutePath())) {
- final Variable o3 = netcdfFile.findVariable("o3");
- assertNotNull(o3);
- Array o3Full = o3.read().reduce();
-
- final Rectangle era5Positions = new Rectangle(1201, 401, 3, 3);
-
- final Array lnspArray = SatelliteFields.readSubset(137, era5Positions, new VariableCache.CacheEntry(o3, o3Full));
- assertNotNull(lnspArray);
-
- final int[] shape = lnspArray.getShape();
- assertEquals(3, shape.length);
- assertEquals(137, shape[0]);
- assertEquals(3, shape[1]);
- assertEquals(3, shape[2]);
-
- assertEquals(1.9680332741245365E-7f, lnspArray.getFloat(1), 1e-8);
- assertEquals(1.9680332741245365E-7f, lnspArray.getFloat(2), 1e-8);
- assertEquals(1.9680332741245365E-7f, lnspArray.getFloat(3), 1e-8);
- }
- }
-
- @Test
- public void testReadSubset_2D_antiMeridianOverlap_right() throws IOException, InvalidRangeException {
- final File anMlFile = TestUtil.getTestDataFileAsserted("era-5/v1/an_ml/2008/06/02/ecmwf-era5_oper_an_ml_200806021000.lnsp.nc");
-
- try (NetcdfFile netcdfFile = NetcdfFile.open(anMlFile.getAbsolutePath())) {
- final Variable lnsp = netcdfFile.findVariable("lnsp");
- assertNotNull(lnsp);
- Array lnspFull = lnsp.read().reduce();
-
- final Rectangle era5Positions = new Rectangle(1439, 402, 3, 3);
-
- final Array lnspArray = SatelliteFields.readSubset(1, era5Positions, new VariableCache.CacheEntry(lnsp, lnspFull));
- assertNotNull(lnspArray);
-
- final int[] shape = lnspArray.getShape();
- assertEquals(2, shape.length);
- assertEquals(3, shape[0]);
- assertEquals(3, shape[1]);
-
- assertEquals(11.529290199279785f, lnspArray.getFloat(0), 1e-8);
- assertEquals(11.529301643371582f, lnspArray.getFloat(1), 1e-8);
- assertEquals(11.529279708862305f, lnspArray.getFloat(2), 1e-8);
- }
- }
-
- @Test
- public void testReadSubset_2D_antiMeridianOverlap_right_JasminCrash() throws IOException, InvalidRangeException {
- final File anMlFile = TestUtil.getTestDataFileAsserted("era-5/v1/an_ml/2008/06/02/ecmwf-era5_oper_an_ml_200806021000.lnsp.nc");
-
- try (NetcdfFile netcdfFile = NetcdfFile.open(anMlFile.getAbsolutePath())) {
- final Variable lnsp = netcdfFile.findVariable("lnsp");
- assertNotNull(lnsp);
- Array lnspFull = lnsp.read().reduce();
-
- final Rectangle era5Positions = new Rectangle(1438, 66, 2, 2);
-
- final Array lnspArray = SatelliteFields.readSubset(1, era5Positions, new VariableCache.CacheEntry(lnsp, lnspFull));
- assertNotNull(lnspArray);
-
- final int[] shape = lnspArray.getShape();
- assertEquals(2, shape.length);
- assertEquals(2, shape[0]);
- assertEquals(2, shape[1]);
-
- assertEquals(11.532238960266113f, lnspArray.getFloat(0), 1e-8);
- assertEquals(11.532217025756836f, lnspArray.getFloat(1), 1e-8);
- assertEquals(11.532645225524902f, lnspArray.getFloat(2), 1e-8);
- assertEquals(11.532743453979492f, lnspArray.getFloat(3), 1e-8);
- }
- }
-
- @Test
- public void testReadSubset_2D_antiMeridianOverlap_left() throws IOException, InvalidRangeException {
- final File anMlFile = TestUtil.getTestDataFileAsserted("era-5/v1/an_sfc/2000/05/28/ecmwf-era5_oper_an_sfc_200005281700.10u.nc");
-
- try (NetcdfFile netcdfFile = NetcdfFile.open(anMlFile.getAbsolutePath())) {
- final Variable lnsp = netcdfFile.findVariable("u10");
- assertNotNull(lnsp);
- Array lnspFull = lnsp.read().reduce();
-
- final Rectangle era5Positions = new Rectangle(-1, 403, 3, 3);
-
- final Array lnspArray = SatelliteFields.readSubset(1, era5Positions, new VariableCache.CacheEntry(lnsp, lnspFull));
- assertNotNull(lnspArray);
-
- final int[] shape = lnspArray.getShape();
- assertEquals(2, shape.length);
- assertEquals(3, shape[0]);
- assertEquals(3, shape[1]);
-
- assertEquals(-6.088078022003174f, lnspArray.getFloat(0), 1e-8);
- assertEquals(-5.904586315155029f, lnspArray.getFloat(1), 1e-8);
- assertEquals(-6.2205610275268555f, lnspArray.getFloat(2), 1e-8);
- }
- }
-}
diff --git a/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/era5/VariableCacheTest.java b/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/era5/VariableCacheTest.java
index 199b93b77..29a47ab34 100644
--- a/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/era5/VariableCacheTest.java
+++ b/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/era5/VariableCacheTest.java
@@ -28,7 +28,9 @@ public void setUp() throws IOException {
final File era5RootDir = new File(testDataDirectory, "era-5" + File.separator + "v1");
assertTrue(era5RootDir.isDirectory());
- final Era5Archive era5Archive = new Era5Archive(era5RootDir.getAbsolutePath(), Era5Collection.ERA_5);
+ final Configuration config = new Configuration();
+ config.setNWPAuxDir(era5RootDir.getAbsolutePath());
+ final Era5Archive era5Archive = new Era5Archive(config, Era5Collection.ERA_5);
variableCache = new VariableCache(era5Archive, 3);
}
@@ -40,58 +42,67 @@ public void tearDown() throws IOException {
@Test
public void testGet() throws IOException {
- VariableCache.CacheEntry cacheEntry = variableCache.get("an_ml_lnsp", 1212145200);
- assertEquals("time latitude longitude", cacheEntry.variable.getDimensionsString());
- assertEquals("Logarithm of surface pressure", NetCDFUtils.getAttributeString(cacheEntry.variable, "long_name", null));
+ Variable variable;
- int[] shape = cacheEntry.array.getShape();
- assertEquals(2, shape.length);
- assertEquals(DataType.SHORT, cacheEntry.array.getDataType());
+ variable = variableCache.get("an_ml_lnsp", 1212145200);
+ assertEquals("time latitude longitude", variable.getDimensionsString());
+ assertEquals("Logarithm of surface pressure", NetCDFUtils.getAttributeString(variable, "long_name", null));
- cacheEntry = variableCache.get("an_ml_q", 1212145200);
- assertEquals("time level latitude longitude", cacheEntry.variable.getDimensionsString());
- assertEquals(3.786489628510026E-7, NetCDFUtils.getAttributeFloat(cacheEntry.variable, "scale_factor", Float.NaN), 1e-8);
-
- shape = cacheEntry.array.getShape();
+ int[] shape = variable.getShape();
+ assertArrayEquals(new int[]{1, 721, 1440}, shape);
assertEquals(3, shape.length);
- assertEquals(DataType.SHORT, cacheEntry.array.getDataType());
+ assertEquals(DataType.SHORT, variable.getDataType());
+
+ variable = variableCache.get("an_ml_q", 1212145200);
+ assertEquals("time level latitude longitude", variable.getDimensionsString());
+ assertEquals(3.786489628510026E-7, NetCDFUtils.getAttributeFloat(variable, "scale_factor", Float.NaN), 1e-8);
+
+ shape = variable.getShape();
+ assertArrayEquals(new int[]{1, 137, 721, 1440}, shape);
+ assertEquals(4, shape.length);
+ assertEquals(DataType.SHORT, variable.getDataType());
}
@Test
public void testCallGetTwice() throws IOException {
- final VariableCache.CacheEntry cacheEntry_1 = variableCache.get("an_ml_o3", 1212400800);
+ final Variable variable_1 = variableCache.get("an_ml_o3", 1212400800);
- final VariableCache.CacheEntry cacheEntry_2 = variableCache.get("an_ml_o3", 1212400800);
- assertSame(cacheEntry_1, cacheEntry_2);
+ final Variable variable_2 = variableCache.get("an_ml_o3", 1212400800);
+
+ assertSame(variable_1, variable_2);
}
@Test
public void testCallGetTwice_closeInbetween() throws IOException {
- VariableCache.CacheEntry cacheEntry_1 = variableCache.get("an_sfc_t2m", 1212145200);
+ final Variable variable_1 = variableCache.get("an_sfc_t2m", 1212145200);
variableCache.close();
- VariableCache.CacheEntry cacheEntry_2 = variableCache.get("an_sfc_t2m", 1212145200);
- assertNotSame(cacheEntry_1, cacheEntry_2);
+ final Variable variable_2 = variableCache.get("an_sfc_t2m", 1212145200);
+
+ assertNotSame(variable_1, variable_2);
}
@Test
public void testGet_removeFunctionalityOnFullCache() throws IOException, InterruptedException {
- VariableCache.CacheEntry cached_u10 = variableCache.get("an_sfc_u10", 1212400800);
- sleep(50);
+ final int secondsOfOneHour = 60 * 60;
+ final int era5TimeStamp_start = 959533200; // timestamp for 17:00 2000-05-28
+ int era5TimeStamp = era5TimeStamp_start;
+
+ Variable cached_u10 = variableCache.get("an_sfc_u10", era5TimeStamp);
- variableCache.get("an_sfc_v10", 1212400800);
- sleep(50);
+ era5TimeStamp += secondsOfOneHour;
+ variableCache.get("an_sfc_u10", era5TimeStamp); // one hour later
- variableCache.get("an_sfc_siconc", 1212400800);
- sleep(50);
+ era5TimeStamp += secondsOfOneHour;
+ variableCache.get("an_sfc_u10", era5TimeStamp); // one hour later
// now the cache is full tb 2020-11-25
- variableCache.get("an_sfc_msl", 1212400800);
- sleep(50);
+ era5TimeStamp += secondsOfOneHour;
+ variableCache.get("an_sfc_u10", era5TimeStamp); // one hour later
// now the first u10 variable is removed from cache and opening it again must result in a new object tb 2020-11-25
- VariableCache.CacheEntry cached_u10_new = variableCache.get("an_sfc_u10", 1212400800);
+ Variable cached_u10_new = variableCache.get("an_sfc_u10", era5TimeStamp_start);
assertNotSame(cached_u10, cached_u10_new);
}
diff --git a/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/era5/VariableUtilsTest.java b/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/era5/VariableUtilsTest.java
index cecd55352..8333fbac7 100644
--- a/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/era5/VariableUtilsTest.java
+++ b/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/era5/VariableUtilsTest.java
@@ -22,6 +22,7 @@ public void testAddAttributes() {
verify(variable, times(1)).addAttribute(new Attribute("long_name", "a_long_name"));
verify(variable, times(1)).addAttribute(new Attribute("standard_name", "a_standard_name"));
verify(variable, times(1)).addAttribute(new Attribute("_FillValue", 9.96921E36f));
+ verify(variable, times(1)).addAttribute(new Attribute("missing_value", 9.96921E36f));
verifyNoMoreInteractions(variable);
}
@@ -35,6 +36,7 @@ public void testAddAttributes_missingStandarName() {
verify(variable, times(1)).addAttribute(new Attribute("units", "gramm"));
verify(variable, times(1)).addAttribute(new Attribute("long_name", "Heffalump"));
verify(variable, times(1)).addAttribute(new Attribute("_FillValue", 9.96921E36f));
+ verify(variable, times(1)).addAttribute(new Attribute("missing_value", 9.96921E36f));
verifyNoMoreInteractions(variable);
}
diff --git a/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/gruan_uleic/AddGruanSourcePluginTest.java b/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/gruan_uleic/AddGruanSourcePluginTest.java
index 3e1663390..b02ea3e4b 100644
--- a/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/gruan_uleic/AddGruanSourcePluginTest.java
+++ b/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/gruan_uleic/AddGruanSourcePluginTest.java
@@ -1,8 +1,8 @@
package com.bc.fiduceo.post.plugin.gruan_uleic;
import com.bc.fiduceo.post.PostProcessing;
-import org.jdom.Element;
-import org.jdom.JDOMException;
+import org.jdom2.Element;
+import org.jdom2.JDOMException;
import org.junit.Before;
import org.junit.Test;
@@ -29,7 +29,7 @@ public void testGetPostProcessingName() {
public void testCreatePostProcessing() throws JDOMException, IOException {
final Element rootElement = AddGruanSourceTest.createFullConfigElement();
- final PostProcessing postProcessing = plugin.createPostProcessing(rootElement);
+ final PostProcessing postProcessing = plugin.createPostProcessing(rootElement, null);
assertTrue(postProcessing instanceof AddGruanSource);
}
}
diff --git a/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/gruan_uleic/AddGruanSourceTest.java b/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/gruan_uleic/AddGruanSourceTest.java
index da3f7b004..b4e166f65 100644
--- a/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/gruan_uleic/AddGruanSourceTest.java
+++ b/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/gruan_uleic/AddGruanSourceTest.java
@@ -7,8 +7,8 @@
import com.bc.fiduceo.post.PostProcessingConfig;
import com.bc.fiduceo.post.PostProcessingContext;
import org.checkerframework.checker.units.qual.A;
-import org.jdom.Element;
-import org.jdom.JDOMException;
+import org.jdom2.Element;
+import org.jdom2.JDOMException;
import org.junit.Before;
import org.junit.Test;
import ucar.ma2.DataType;
diff --git a/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/hirs/flag/HirsL1CloudyFlagsPluginTest.java b/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/hirs/flag/HirsL1CloudyFlagsPluginTest.java
index f7b76c818..d94e96dc1 100644
--- a/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/hirs/flag/HirsL1CloudyFlagsPluginTest.java
+++ b/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/hirs/flag/HirsL1CloudyFlagsPluginTest.java
@@ -37,7 +37,7 @@
import com.bc.fiduceo.post.PostProcessing;
import com.google.common.jimfs.Configuration;
import com.google.common.jimfs.Jimfs;
-import org.jdom.Element;
+import org.jdom2.Element;
import org.junit.*;
import java.nio.file.FileSystem;
@@ -78,7 +78,7 @@ public void setUp() throws Exception {
@Test
public void testCreatePostProcessing() {
plugin.setFileSystem(virtualFS);
- final PostProcessing postProcessing = plugin.createPostProcessing(element);
+ final PostProcessing postProcessing = plugin.createPostProcessing(element, null);
assertNotNull(postProcessing);
assertEquals("com.bc.fiduceo.post.plugin.hirs.flag.HirsL1CloudyFlags", postProcessing.getClass().getTypeName());
@@ -104,7 +104,7 @@ public void testCreatePostProcessing_wrongElementName() throws Exception {
element.setName("wrong_name");
try {
- plugin.createPostProcessing(element);
+ plugin.createPostProcessing(element, null);
fail("RuntimeException expected");
} catch (RuntimeException expected) {
assertEquals("Illegal XML Element. Tagname 'hirs-l1-cloudy-flags' expected.", expected.getMessage());
@@ -116,7 +116,7 @@ public void testCreatePostProcessing_11_1_um_VarNameTextIsMissing() throws Excep
element.getChild(TAG_VAR_NAME_BT_11_1_uM).setText(null);
try {
- plugin.createPostProcessing(element);
+ plugin.createPostProcessing(element, null);
fail("RuntimeException expected");
} catch (RuntimeException expected) {
assertEquals("Value of element '" + TAG_VAR_NAME_BT_11_1_uM + "' expected", expected.getMessage());
@@ -128,7 +128,7 @@ public void testCreatePostProcessing_11_1_um_VarNameTextIsEmpty() throws Excepti
element.getChild(TAG_VAR_NAME_BT_11_1_uM).setText("");
try {
- plugin.createPostProcessing(element);
+ plugin.createPostProcessing(element, null);
fail("RuntimeException expected");
} catch (RuntimeException expected) {
assertEquals("Value of element '" + TAG_VAR_NAME_BT_11_1_uM + "' expected", expected.getMessage());
@@ -140,7 +140,7 @@ public void testCreatePostProcessing_11_1_um_VarNameElementIsMissing() throws Ex
element.removeChild(TAG_VAR_NAME_BT_11_1_uM);
try {
- plugin.createPostProcessing(element);
+ plugin.createPostProcessing(element, null);
fail("RuntimeException expected");
} catch (RuntimeException expected) {
assertEquals("Child element '" + TAG_VAR_NAME_BT_11_1_uM + "' expected", expected.getMessage());
@@ -152,7 +152,7 @@ public void testCreatePostProcessing_6_5_um_VarNameTextIsMissing() throws Except
element.getChild(TAG_VAR_NAME_BT_6_5_uM).setText(null);
try {
- plugin.createPostProcessing(element);
+ plugin.createPostProcessing(element, null);
fail("RuntimeException expected");
} catch (RuntimeException expected) {
assertEquals("Value of element '" + TAG_VAR_NAME_BT_6_5_uM + "' expected", expected.getMessage());
@@ -164,7 +164,7 @@ public void testCreatePostProcessing_6_5_um_VarNameTextIsEmpty() throws Exceptio
element.getChild(TAG_VAR_NAME_BT_6_5_uM).setText("");
try {
- plugin.createPostProcessing(element);
+ plugin.createPostProcessing(element, null);
fail("RuntimeException expected");
} catch (RuntimeException expected) {
assertEquals("Value of element '" + TAG_VAR_NAME_BT_6_5_uM + "' expected", expected.getMessage());
@@ -176,7 +176,7 @@ public void testCreatePostProcessing_6_5_um_VarNameElementIsMissing() throws Exc
element.removeChild(TAG_VAR_NAME_BT_6_5_uM);
try {
- plugin.createPostProcessing(element);
+ plugin.createPostProcessing(element, null);
fail("RuntimeException expected");
} catch (RuntimeException expected) {
assertEquals("Child element '" + TAG_VAR_NAME_BT_6_5_uM + "' expected", expected.getMessage());
@@ -188,7 +188,7 @@ public void testCreatePostProcessing_FlagVarNameTextIsMissing() throws Exception
element.getChild(TAG_VAR_NAME_CLOUD_FLAGS).setText(null);
try {
- plugin.createPostProcessing(element);
+ plugin.createPostProcessing(element, null);
fail("RuntimeException expected");
} catch (RuntimeException expected) {
assertEquals("Value of element '" + TAG_VAR_NAME_CLOUD_FLAGS + "' expected", expected.getMessage());
@@ -200,7 +200,7 @@ public void testCreatePostProcessing_FlagVarNameTextIsEmpty() throws Exception {
element.getChild(TAG_VAR_NAME_CLOUD_FLAGS).setText("");
try {
- plugin.createPostProcessing(element);
+ plugin.createPostProcessing(element, null);
fail("RuntimeException expected");
} catch (RuntimeException expected) {
assertEquals("Value of element '" + TAG_VAR_NAME_CLOUD_FLAGS + "' expected", expected.getMessage());
@@ -212,7 +212,7 @@ public void testCreatePostProcessing_FlagVarNameElementIsMissing() throws Except
element.removeChild(TAG_VAR_NAME_CLOUD_FLAGS);
try {
- plugin.createPostProcessing(element);
+ plugin.createPostProcessing(element, null);
fail("RuntimeException expected");
} catch (RuntimeException expected) {
assertEquals("Child element '" + TAG_VAR_NAME_CLOUD_FLAGS + "' expected", expected.getMessage());
@@ -224,7 +224,7 @@ public void testCreatePostProcessing_DistanceFilePathIsMissing() throws Exceptio
element.getChild(TAG_DISTANCE_PRODUCT_FILE_PATH).setText(null);
try {
- plugin.createPostProcessing(element);
+ plugin.createPostProcessing(element, null);
fail("RuntimeException expected");
} catch (RuntimeException expected) {
assertEquals("Value of element 'point_distance-product-file-path' expected", expected.getMessage());
@@ -236,7 +236,7 @@ public void testCreatePostProcessing_DistanceFilePathTextIsEmpty() throws Except
element.getChild(TAG_DISTANCE_PRODUCT_FILE_PATH).setText("");
try {
- plugin.createPostProcessing(element);
+ plugin.createPostProcessing(element, null);
fail("RuntimeException expected");
} catch (RuntimeException expected) {
assertEquals("Value of element 'point_distance-product-file-path' expected", expected.getMessage());
@@ -248,7 +248,7 @@ public void testCreatePostProcessing_DistanceFilePathElementIsMissing() throws E
element.removeChild(TAG_DISTANCE_PRODUCT_FILE_PATH);
try {
- plugin.createPostProcessing(element);
+ plugin.createPostProcessing(element, null);
fail("RuntimeException expected");
} catch (RuntimeException expected) {
assertEquals("Child element 'point_distance-product-file-path' expected", expected.getMessage());
@@ -261,7 +261,7 @@ public void testCreatePostProcessing_LatitudeVarNameTextIsMissing() throws Excep
element.getChild(TAG_VAR_NAME_LATITUDE).setText(null);
try {
- plugin.createPostProcessing(element);
+ plugin.createPostProcessing(element, null);
fail("RuntimeException expected");
} catch (RuntimeException expected) {
assertEquals("Value of element '" + TAG_VAR_NAME_LATITUDE + "' expected", expected.getMessage());
@@ -273,7 +273,7 @@ public void testCreatePostProcessing_LatitudeVarNameTextIsEmpty() throws Excepti
element.getChild(TAG_VAR_NAME_LATITUDE).setText("");
try {
- plugin.createPostProcessing(element);
+ plugin.createPostProcessing(element, null);
fail("RuntimeException expected");
} catch (RuntimeException expected) {
assertEquals("Value of element '" + TAG_VAR_NAME_LATITUDE + "' expected", expected.getMessage());
@@ -285,7 +285,7 @@ public void testCreatePostProcessing_LatitudeVarNameElementIsMissing() throws Ex
element.removeChild(TAG_VAR_NAME_LATITUDE);
try {
- plugin.createPostProcessing(element);
+ plugin.createPostProcessing(element, null);
fail("RuntimeException expected");
} catch (RuntimeException expected) {
assertEquals("Child element '" + TAG_VAR_NAME_LATITUDE + "' expected", expected.getMessage());
@@ -297,7 +297,7 @@ public void testCreatePostProcessing_LongitudeVarNameTextIsMissing() throws Exce
element.getChild(TAG_VAR_NAME_LONGITUDE).setText(null);
try {
- plugin.createPostProcessing(element);
+ plugin.createPostProcessing(element, null);
fail("RuntimeException expected");
} catch (RuntimeException expected) {
assertEquals("Value of element '" + TAG_VAR_NAME_LONGITUDE + "' expected", expected.getMessage());
@@ -309,7 +309,7 @@ public void testCreatePostProcessing_LongitudeVarNameTextIsEmpty() throws Except
element.getChild(TAG_VAR_NAME_LONGITUDE).setText("");
try {
- plugin.createPostProcessing(element);
+ plugin.createPostProcessing(element, null);
fail("RuntimeException expected");
} catch (RuntimeException expected) {
assertEquals("Value of element '" + TAG_VAR_NAME_LONGITUDE + "' expected", expected.getMessage());
@@ -321,7 +321,7 @@ public void testCreatePostProcessing_LongitudeVarNameElementIsMissing() throws E
element.removeChild(TAG_VAR_NAME_LONGITUDE);
try {
- plugin.createPostProcessing(element);
+ plugin.createPostProcessing(element, null);
fail("RuntimeException expected");
} catch (RuntimeException expected) {
assertEquals("Child element '" + TAG_VAR_NAME_LONGITUDE + "' expected", expected.getMessage());
@@ -333,7 +333,7 @@ public void testCreatePostProcessing_SourceFileNameVarNameTextIsMissing() throws
element.getChild(TAG_VAR_NAME_SOURCE_FILE_NAME).setText(null);
try {
- plugin.createPostProcessing(element);
+ plugin.createPostProcessing(element, null);
fail("RuntimeException expected");
} catch (RuntimeException expected) {
assertEquals("Value of element '" + TAG_VAR_NAME_SOURCE_FILE_NAME + "' expected", expected.getMessage());
@@ -345,7 +345,7 @@ public void testCreatePostProcessing_SourceFileNameVarNameTextIsEmpty() throws E
element.getChild(TAG_VAR_NAME_SOURCE_FILE_NAME).setText("");
try {
- plugin.createPostProcessing(element);
+ plugin.createPostProcessing(element, null);
fail("RuntimeException expected");
} catch (RuntimeException expected) {
assertEquals("Value of element '" + TAG_VAR_NAME_SOURCE_FILE_NAME + "' expected", expected.getMessage());
@@ -357,7 +357,7 @@ public void testCreatePostProcessing_SourceFileNameVarNameElementIsMissing() thr
element.removeChild(TAG_VAR_NAME_SOURCE_FILE_NAME);
try {
- plugin.createPostProcessing(element);
+ plugin.createPostProcessing(element, null);
fail("RuntimeException expected");
} catch (RuntimeException expected) {
assertEquals("Child element '" + TAG_VAR_NAME_SOURCE_FILE_NAME + "' expected", expected.getMessage());
@@ -369,7 +369,7 @@ public void testCreatePostProcessing_ProcessingVersionVarNameTextIsMissing() thr
element.getChild(TAG_VAR_NAME_PROCESSING_VERSION).setText(null);
try {
- plugin.createPostProcessing(element);
+ plugin.createPostProcessing(element, null);
fail("RuntimeException expected");
} catch (RuntimeException expected) {
assertEquals("Value of element '" + TAG_VAR_NAME_PROCESSING_VERSION + "' expected", expected.getMessage());
@@ -381,7 +381,7 @@ public void testCreatePostProcessing_ProcessingVersionVarNameTextIsEmpty() throw
element.getChild(TAG_VAR_NAME_PROCESSING_VERSION).setText("");
try {
- plugin.createPostProcessing(element);
+ plugin.createPostProcessing(element, null);
fail("RuntimeException expected");
} catch (RuntimeException expected) {
assertEquals("Value of element '" + TAG_VAR_NAME_PROCESSING_VERSION + "' expected", expected.getMessage());
@@ -393,7 +393,7 @@ public void testCreatePostProcessing_ProcessingVersionVarNameElementIsMissing()
element.removeChild(TAG_VAR_NAME_PROCESSING_VERSION);
try {
- plugin.createPostProcessing(element);
+ plugin.createPostProcessing(element, null);
fail("RuntimeException expected");
} catch (RuntimeException expected) {
assertEquals("Child element '" + TAG_VAR_NAME_PROCESSING_VERSION + "' expected", expected.getMessage());
@@ -405,7 +405,7 @@ public void testCreatePostProcessing_XVarNameTextIsMissing() throws Exception {
element.getChild(TAG_VAR_NAME_SOURCE_X).setText(null);
try {
- plugin.createPostProcessing(element);
+ plugin.createPostProcessing(element, null);
fail("RuntimeException expected");
} catch (RuntimeException expected) {
assertEquals("Value of element '" + TAG_VAR_NAME_SOURCE_X + "' expected", expected.getMessage());
@@ -417,7 +417,7 @@ public void testCreatePostProcessing_XVarNameTextIsEmpty() throws Exception {
element.getChild(TAG_VAR_NAME_SOURCE_X).setText("");
try {
- plugin.createPostProcessing(element);
+ plugin.createPostProcessing(element, null);
fail("RuntimeException expected");
} catch (RuntimeException expected) {
assertEquals("Value of element '" + TAG_VAR_NAME_SOURCE_X + "' expected", expected.getMessage());
@@ -429,7 +429,7 @@ public void testCreatePostProcessing_XVarNameElementIsMissing() throws Exception
element.removeChild(TAG_VAR_NAME_SOURCE_X);
try {
- plugin.createPostProcessing(element);
+ plugin.createPostProcessing(element, null);
fail("RuntimeException expected");
} catch (RuntimeException expected) {
assertEquals("Child element '" + TAG_VAR_NAME_SOURCE_X + "' expected", expected.getMessage());
@@ -441,7 +441,7 @@ public void testCreatePostProcessing_YVarNameTextIsMissing() throws Exception {
element.getChild(TAG_VAR_NAME_SOURCE_Y).setText(null);
try {
- plugin.createPostProcessing(element);
+ plugin.createPostProcessing(element, null);
fail("RuntimeException expected");
} catch (RuntimeException expected) {
assertEquals("Value of element '" + TAG_VAR_NAME_SOURCE_Y + "' expected", expected.getMessage());
@@ -453,7 +453,7 @@ public void testCreatePostProcessing_YVarNameTextIsEmpty() throws Exception {
element.getChild(TAG_VAR_NAME_SOURCE_Y).setText("");
try {
- plugin.createPostProcessing(element);
+ plugin.createPostProcessing(element, null);
fail("RuntimeException expected");
} catch (RuntimeException expected) {
assertEquals("Value of element '" + TAG_VAR_NAME_SOURCE_Y + "' expected", expected.getMessage());
@@ -465,7 +465,7 @@ public void testCreatePostProcessing_processingVersionVarNameElementIsMissing()
element.removeChild(TAG_VAR_NAME_SOURCE_Y);
try {
- plugin.createPostProcessing(element);
+ plugin.createPostProcessing(element, null);
fail("RuntimeException expected");
} catch (RuntimeException expected) {
assertEquals("Child element '" + TAG_VAR_NAME_SOURCE_Y + "' expected", expected.getMessage());
@@ -477,7 +477,7 @@ public void testCreatePostProcessing_SensorNameTextIsMissing() throws Exception
element.getChild(TAG_SENSOR_NAME).setText(null);
try {
- plugin.createPostProcessing(element);
+ plugin.createPostProcessing(element, null);
fail("RuntimeException expected");
} catch (RuntimeException expected) {
assertEquals("Value of element '" + TAG_SENSOR_NAME + "' expected", expected.getMessage());
@@ -489,7 +489,7 @@ public void testCreatePostProcessing_SensorNameTextIsEmpty() throws Exception {
element.getChild(TAG_SENSOR_NAME).setText("");
try {
- plugin.createPostProcessing(element);
+ plugin.createPostProcessing(element, null);
fail("RuntimeException expected");
} catch (RuntimeException expected) {
assertEquals("Value of element '" + TAG_SENSOR_NAME + "' expected", expected.getMessage());
@@ -501,7 +501,7 @@ public void testCreatePostProcessing_SensorNameElementIsMissing() throws Excepti
element.removeChild(TAG_SENSOR_NAME);
try {
- plugin.createPostProcessing(element);
+ plugin.createPostProcessing(element, null);
fail("RuntimeException expected");
} catch (RuntimeException expected) {
assertEquals("Child element '" + TAG_SENSOR_NAME + "' expected", expected.getMessage());
@@ -513,7 +513,7 @@ public void testCreatePostProcessing_SourceBtVarNameTextIsMissing() throws Excep
element.getChild(TAG_VAR_NAME_SOURCE_BT_11_1_mM).setText(null);
try {
- plugin.createPostProcessing(element);
+ plugin.createPostProcessing(element, null);
fail("RuntimeException expected");
} catch (RuntimeException expected) {
assertEquals("Value of element '" + TAG_VAR_NAME_SOURCE_BT_11_1_mM + "' expected", expected.getMessage());
@@ -525,7 +525,7 @@ public void testCreatePostProcessing_SourceBtVarNameTextIsEmpty() throws Excepti
element.getChild(TAG_VAR_NAME_SOURCE_BT_11_1_mM).setText("");
try {
- plugin.createPostProcessing(element);
+ plugin.createPostProcessing(element, null);
fail("RuntimeException expected");
} catch (RuntimeException expected) {
assertEquals("Value of element '" + TAG_VAR_NAME_SOURCE_BT_11_1_mM + "' expected", expected.getMessage());
@@ -537,7 +537,7 @@ public void testCreatePostProcessing_SourceBtVarNameElementIsMissing() throws Ex
element.removeChild(TAG_VAR_NAME_SOURCE_BT_11_1_mM);
try {
- plugin.createPostProcessing(element);
+ plugin.createPostProcessing(element, null);
fail("RuntimeException expected");
} catch (RuntimeException expected) {
assertEquals("Child element '" + TAG_VAR_NAME_SOURCE_BT_11_1_mM + "' expected", expected.getMessage());
diff --git a/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/iasi/AddIASISpectrumPluginTest.java b/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/iasi/AddIASISpectrumPluginTest.java
index e20dc2688..a415ca51f 100644
--- a/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/iasi/AddIASISpectrumPluginTest.java
+++ b/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/iasi/AddIASISpectrumPluginTest.java
@@ -21,8 +21,8 @@
package com.bc.fiduceo.post.plugin.iasi;
import com.bc.fiduceo.post.PostProcessing;
-import org.jdom.Element;
-import org.jdom.JDOMException;
+import org.jdom2.Element;
+import org.jdom2.JDOMException;
import org.junit.Before;
import org.junit.Test;
@@ -49,7 +49,7 @@ public void testGetPostProcessingName() {
public void testCreatePostProcessing() throws JDOMException, IOException {
final Element rootElement = AddIASISpectrumTest.createFullConfigElement();
- final PostProcessing postProcessing = plugin.createPostProcessing(rootElement);
+ final PostProcessing postProcessing = plugin.createPostProcessing(rootElement, null);
assertTrue(postProcessing instanceof AddIASISpectrum);
}
}
diff --git a/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/iasi/AddIASISpectrumTest.java b/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/iasi/AddIASISpectrumTest.java
index cf46e0221..abb399136 100644
--- a/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/iasi/AddIASISpectrumTest.java
+++ b/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/iasi/AddIASISpectrumTest.java
@@ -26,8 +26,8 @@
import com.bc.fiduceo.reader.iasi.IASI_Reader;
import com.bc.fiduceo.util.NetCDFUtils;
import org.hamcrest.CoreMatchers;
-import org.jdom.Element;
-import org.jdom.JDOMException;
+import org.jdom2.Element;
+import org.jdom2.JDOMException;
import org.junit.Before;
import org.junit.Test;
import ucar.ma2.Array;
diff --git a/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/land_distance/AddLandDistancePluginTest.java b/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/land_distance/AddLandDistancePluginTest.java
index a5c535926..4f6fb47ba 100644
--- a/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/land_distance/AddLandDistancePluginTest.java
+++ b/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/land_distance/AddLandDistancePluginTest.java
@@ -2,8 +2,8 @@
import com.bc.fiduceo.post.PostProcessing;
-import org.jdom.Element;
-import org.jdom.JDOMException;
+import org.jdom2.Element;
+import org.jdom2.JDOMException;
import org.junit.Before;
import org.junit.Test;
@@ -30,7 +30,7 @@ public void testGetPostProcessingName() {
public void testCreatePostProcessing() throws JDOMException, IOException {
final Element configElement = AddLandDistanceTest.createFullConfigElement();
- final PostProcessing postProcessing = plugin.createPostProcessing(configElement);
+ final PostProcessing postProcessing = plugin.createPostProcessing(configElement, null);
assertTrue(postProcessing instanceof AddLandDistance);
}
diff --git a/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/land_distance/AddLandDistanceTest.java b/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/land_distance/AddLandDistanceTest.java
index cb048df13..79ef594c7 100644
--- a/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/land_distance/AddLandDistanceTest.java
+++ b/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/land_distance/AddLandDistanceTest.java
@@ -5,8 +5,8 @@
import com.bc.fiduceo.TestUtil;
import com.bc.fiduceo.post.util.DistanceToLandMap;
import com.bc.fiduceo.util.NetCDFUtils;
-import org.jdom.Element;
-import org.jdom.JDOMException;
+import org.jdom2.Element;
+import org.jdom2.JDOMException;
import org.junit.Test;
import ucar.ma2.Array;
import ucar.ma2.DataType;
diff --git a/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/nwp/NwpPostProcessingPluginTest.java b/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/nwp/NwpPostProcessingPluginTest.java
index 53535bfc8..bcdfc2ddf 100644
--- a/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/nwp/NwpPostProcessingPluginTest.java
+++ b/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/nwp/NwpPostProcessingPluginTest.java
@@ -22,8 +22,8 @@
import com.bc.fiduceo.TestUtil;
-import org.jdom.Element;
-import org.jdom.JDOMException;
+import org.jdom2.Element;
+import org.jdom2.JDOMException;
import org.junit.Before;
import org.junit.Test;
diff --git a/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/nwp/NwpPostProcessingPlugin_IO_Test.java b/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/nwp/NwpPostProcessingPlugin_IO_Test.java
index 3676bfd45..7fa194a16 100644
--- a/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/nwp/NwpPostProcessingPlugin_IO_Test.java
+++ b/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/nwp/NwpPostProcessingPlugin_IO_Test.java
@@ -24,8 +24,8 @@
import com.bc.fiduceo.IOTestRunner;
import com.bc.fiduceo.TestUtil;
import com.bc.fiduceo.post.PostProcessing;
-import org.jdom.Element;
-import org.jdom.JDOMException;
+import org.jdom2.Element;
+import org.jdom2.JDOMException;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -73,7 +73,7 @@ public void testCreatePostProcessing() throws JDOMException, IOException {
"";
final Element rootElement = TestUtil.createDomElement(config);
- final PostProcessing postProcessing = plugin.createPostProcessing(rootElement);
+ final PostProcessing postProcessing = plugin.createPostProcessing(rootElement, null);
assertNotNull(postProcessing);
assertTrue(postProcessing instanceof NwpPostProcessing);
}
diff --git a/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/point_distance/SphericalDistancePluginTest.java b/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/point_distance/SphericalDistancePluginTest.java
index ca5220083..6f99628b0 100644
--- a/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/point_distance/SphericalDistancePluginTest.java
+++ b/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/point_distance/SphericalDistancePluginTest.java
@@ -23,7 +23,7 @@
import static org.junit.Assert.*;
import com.bc.fiduceo.post.PostProcessing;
-import org.jdom.Element;
+import org.jdom2.Element;
import org.junit.*;
import java.util.Arrays;
@@ -50,7 +50,7 @@ public void setUp() throws Exception {
@Test
public void testCreatePostProcessing() {
- final PostProcessing postProcessing = plugin.createPostProcessing(element);
+ final PostProcessing postProcessing = plugin.createPostProcessing(element, null);
assertNotNull(postProcessing);
assertEquals("com.bc.fiduceo.post.plugin.point_distance.SphericalDistance", postProcessing.getClass().getTypeName());
@@ -78,7 +78,7 @@ public void testCreatePostProcessing_wrongElementName() throws Exception {
element.setName("wrong_name");
try {
- plugin.createPostProcessing(element);
+ plugin.createPostProcessing(element, null);
fail("RuntimeException expected");
} catch (RuntimeException expected) {
assertEquals("Illegal XML Element. Tagname 'spherical-distance' expected.", expected.getMessage());
@@ -90,7 +90,7 @@ public void testCreatePostProcessing_targetElementIsMissing() throws Exception {
element.removeChild(TAG_NAME_TARGET);
try {
- plugin.createPostProcessing(element);
+ plugin.createPostProcessing(element, null);
fail("RuntimeException expected");
} catch (RuntimeException expected) {
assertEquals("Child element 'target' expected", expected.getMessage());
@@ -102,7 +102,7 @@ public void testCreatePostProcessing_targetVarNameElementIsMissing() throws Exce
element.getChild(TAG_NAME_TARGET).removeChild(TAG_NAME_VAR_NAME);
try {
- plugin.createPostProcessing(element);
+ plugin.createPostProcessing(element, null);
fail("RuntimeException expected");
} catch (RuntimeException expected) {
assertEquals("Child element 'var-name' expected", expected.getMessage());
@@ -114,7 +114,7 @@ public void testCreatePostProcessing_targetVarNameElement_valueIsMissing() throw
element.getChild(TAG_NAME_TARGET).getChild(TAG_NAME_VAR_NAME).setText("");
try {
- plugin.createPostProcessing(element);
+ plugin.createPostProcessing(element, null);
fail("RuntimeException expected");
} catch (RuntimeException expected) {
assertEquals("Value of element 'var-name' expected", expected.getMessage());
@@ -126,7 +126,7 @@ public void testCreatePostProcessing_targetDataTypeElementIsMissing() throws Exc
element.getChild(TAG_NAME_TARGET).removeChild(TAG_NAME_DATA_TYPE);
try {
- plugin.createPostProcessing(element);
+ plugin.createPostProcessing(element, null);
fail("RuntimeException expected");
} catch (RuntimeException expected) {
assertEquals("Child element 'data-type' expected", expected.getMessage());
@@ -138,7 +138,7 @@ public void testCreatePostProcessing_targetDataTypeElement_valueIsMissing() thro
element.getChild(TAG_NAME_TARGET).getChild(TAG_NAME_DATA_TYPE).setText("");
try {
- plugin.createPostProcessing(element);
+ plugin.createPostProcessing(element, null);
fail("RuntimeException expected");
} catch (RuntimeException expected) {
assertEquals("Value of element 'data-type' expected", expected.getMessage());
@@ -150,7 +150,7 @@ public void testCreatePostProcessing_primaryLatElemElementIsMissing() throws Exc
element.removeChild(TAG_NAME_PRIM_LAT_VAR);
try {
- plugin.createPostProcessing(element);
+ plugin.createPostProcessing(element, null);
fail("RuntimeException expected");
} catch (RuntimeException expected) {
assertEquals("Child element 'primary-lat-variable' expected", expected.getMessage());
@@ -162,7 +162,7 @@ public void testCreatePostProcessing_primaryLatElemElement_valueIsMissing() thro
element.getChild(TAG_NAME_PRIM_LAT_VAR).setText("");
try {
- plugin.createPostProcessing(element);
+ plugin.createPostProcessing(element, null);
fail("RuntimeException expected");
} catch (RuntimeException expected) {
assertEquals("Value of element 'primary-lat-variable' expected", expected.getMessage());
@@ -174,7 +174,7 @@ public void testCreatePostProcessing_primaryLonElemElementIsMissing() throws Exc
element.removeChild(TAG_NAME_PRIM_LON_VAR);
try {
- plugin.createPostProcessing(element);
+ plugin.createPostProcessing(element, null);
fail("RuntimeException expected");
} catch (RuntimeException expected) {
assertEquals("Child element 'primary-lon-variable' expected", expected.getMessage());
@@ -186,7 +186,7 @@ public void testCreatePostProcessing_primaryLonElemElement_valueIsMissing() thro
element.getChild(TAG_NAME_PRIM_LON_VAR).setText("");
try {
- plugin.createPostProcessing(element);
+ plugin.createPostProcessing(element, null);
fail("RuntimeException expected");
} catch (RuntimeException expected) {
assertEquals("Value of element 'primary-lon-variable' expected", expected.getMessage());
@@ -198,7 +198,7 @@ public void testCreatePostProcessing_secundaryLatElemElementIsMissing() throws E
element.removeChild(TAG_NAME_SECO_LAT_VAR);
try {
- plugin.createPostProcessing(element);
+ plugin.createPostProcessing(element, null);
fail("RuntimeException expected");
} catch (RuntimeException expected) {
assertEquals("Child element 'secondary-lat-variable' expected", expected.getMessage());
@@ -210,7 +210,7 @@ public void testCreatePostProcessing_secundaryLatElemElement_valueIsMissing() th
element.getChild(TAG_NAME_SECO_LAT_VAR).setText("");
try {
- plugin.createPostProcessing(element);
+ plugin.createPostProcessing(element, null);
fail("RuntimeException expected");
} catch (RuntimeException expected) {
assertEquals("Value of element 'secondary-lat-variable' expected", expected.getMessage());
@@ -222,7 +222,7 @@ public void testCreatePostProcessing_secundaryLonElemElementIsMissing() throws E
element.removeChild(TAG_NAME_SECO_LON_VAR);
try {
- plugin.createPostProcessing(element);
+ plugin.createPostProcessing(element, null);
fail("RuntimeException expected");
} catch (RuntimeException expected) {
assertEquals("Child element 'secondary-lon-variable' expected", expected.getMessage());
@@ -234,7 +234,7 @@ public void testCreatePostProcessing_secundaryLonElemElement_valueIsMissing() th
element.getChild(TAG_NAME_SECO_LON_VAR).setText("");
try {
- plugin.createPostProcessing(element);
+ plugin.createPostProcessing(element, null);
fail("RuntimeException expected");
} catch (RuntimeException expected) {
assertEquals("Value of element 'secondary-lon-variable' expected", expected.getMessage());
diff --git a/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/sstInsitu/SstInsituTimeSeriesPluginTest.java b/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/sstInsitu/SstInsituTimeSeriesPluginTest.java
index 4859e4d96..4396c1507 100644
--- a/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/sstInsitu/SstInsituTimeSeriesPluginTest.java
+++ b/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/sstInsitu/SstInsituTimeSeriesPluginTest.java
@@ -21,20 +21,13 @@
import com.bc.fiduceo.post.PostProcessing;
import com.bc.fiduceo.post.PostProcessingPlugin;
-import org.jdom.Element;
+import org.jdom2.Element;
import org.junit.Before;
import org.junit.Test;
import java.util.Arrays;
-import static com.bc.fiduceo.post.plugin.sstInsitu.SstInsituTimeSeriesPlugin.TAG_NAME_FILE_NAME_VARIABLE;
-import static com.bc.fiduceo.post.plugin.sstInsitu.SstInsituTimeSeriesPlugin.TAG_NAME_INSITU_SENSOR;
-import static com.bc.fiduceo.post.plugin.sstInsitu.SstInsituTimeSeriesPlugin.TAG_NAME_SECONDARY_SENSOR_MATCHUP_TIME_VARIABLE;
-import static com.bc.fiduceo.post.plugin.sstInsitu.SstInsituTimeSeriesPlugin.TAG_NAME_SST_INSITU_TIME_SERIES;
-import static com.bc.fiduceo.post.plugin.sstInsitu.SstInsituTimeSeriesPlugin.TAG_NAME_TIME_RANGE_SECONDS;
-import static com.bc.fiduceo.post.plugin.sstInsitu.SstInsituTimeSeriesPlugin.TAG_NAME_TIME_SERIES_SIZE;
-import static com.bc.fiduceo.post.plugin.sstInsitu.SstInsituTimeSeriesPlugin.TAG_NAME_VERSION;
-import static com.bc.fiduceo.post.plugin.sstInsitu.SstInsituTimeSeriesPlugin.TAG_NAME_Y_VARIABLE;
+import static com.bc.fiduceo.post.plugin.sstInsitu.SstInsituTimeSeriesPlugin.*;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.is;
@@ -57,6 +50,7 @@ public void setUp() {
new Element(TAG_NAME_VERSION).addContent("v03.3"),
new Element(TAG_NAME_TIME_RANGE_SECONDS).addContent("" + 36 * 60 * 60),
new Element(TAG_NAME_TIME_SERIES_SIZE).addContent("96"),
+ new Element(TAG_NAME_INSITU_TIME_VARIABLE).addContent("drifter-sirds.acquisition_time"),
new Element(TAG_NAME_SECONDARY_SENSOR_MATCHUP_TIME_VARIABLE).addContent("amsre.acquisition_time")
));
}
@@ -68,7 +62,7 @@ public void testThatPluginImplementsInterfacePostProcessingPlugin() {
@Test
public void testThatPluginReturnsASstInsituTimeSeriesPostProcessing() {
- final PostProcessing postProcessing = plugin.createPostProcessing(element);
+ final PostProcessing postProcessing = plugin.createPostProcessing(element, null);
assertThat(postProcessing, is(not(nullValue())));
assertThat(postProcessing, instanceOf(PostProcessing.class));
@@ -78,7 +72,7 @@ public void testThatPluginReturnsASstInsituTimeSeriesPostProcessing() {
@Test
public void createPostProcessing_throwsExceptionIfTheNameOfTheRootElementIsWrong() {
try {
- plugin.createPostProcessing(new Element("wrongName"));
+ plugin.createPostProcessing(new Element("wrongName"), null);
fail("RuntimeException expected");
} catch (RuntimeException expected) {
assertThat(expected.getMessage(), is(equalTo("Illegal XML Element. Tagname '" + plugin.getPostProcessingName() + "' expected.")));
@@ -92,6 +86,7 @@ public void testParseConfiguration() {
assertEquals(36 * 60 * 60, configuration.timeRangeSeconds);
assertEquals(96, configuration.timeSeriesSize);
assertEquals("amsre.acquisition_time", configuration.matchupTimeVarName);
+ assertEquals("drifter-sirds.acquisition_time", configuration.insituTimeVarName);
assertNull(configuration.insituSensorName);
}
diff --git a/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/sstInsitu/SstInsituTimeSeriesTest.java b/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/sstInsitu/SstInsituTimeSeriesTest.java
index 0b9cbe490..166364363 100644
--- a/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/sstInsitu/SstInsituTimeSeriesTest.java
+++ b/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/sstInsitu/SstInsituTimeSeriesTest.java
@@ -203,4 +203,15 @@ public void testAddInsituVariables() throws Exception {
verify(newVar, times(3)).addAttribute(any(Attribute.class));
verifyNoMoreInteractions(writer, insituReader, newVar, v3, v2);
}
+
+ @Test
+ public void testFileNameMatches() {
+ assertTrue(SstInsituTimeSeries.nameMatches("insitu_0_WMOID_42531_19960904_19960909.nc"));
+ assertTrue(SstInsituTimeSeries.nameMatches("insitu_0_WMOID_46942_19951026_19951027.nc"));
+ assertTrue(SstInsituTimeSeries.nameMatches("SSTCCI2_refdata_gtmba2_201204.nc"));
+ assertTrue(SstInsituTimeSeries.nameMatches("SSTCCI2_refdata_drifter_201701.nc"));
+
+ assertFalse(SstInsituTimeSeries.nameMatches("PIRATA_0N35W_sss_2016-10.txt"));
+ assertFalse(SstInsituTimeSeries.nameMatches("ASCAT-vs-AMSR2-vs-ERA5-vs-DMISIC0-2016-N.text"));
+ }
}
diff --git a/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/sstInsitu/SstInsituTimeSeries_IO_Test.java b/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/sstInsitu/SstInsituTimeSeries_IO_Test.java
index 5a2b1f6fb..dd699da31 100644
--- a/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/sstInsitu/SstInsituTimeSeries_IO_Test.java
+++ b/post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/sstInsitu/SstInsituTimeSeries_IO_Test.java
@@ -33,10 +33,10 @@
import com.bc.fiduceo.reader.insitu.sst_cci.SSTInsituReader;
import com.bc.fiduceo.util.NetCDFUtils;
import org.esa.snap.core.util.io.FileUtils;
-import org.jdom.Document;
-import org.jdom.Element;
-import org.jdom.output.Format;
-import org.jdom.output.XMLOutputter;
+import org.jdom2.Document;
+import org.jdom2.Element;
+import org.jdom2.output.Format;
+import org.jdom2.output.XMLOutputter;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -56,11 +56,7 @@
import java.nio.file.Path;
import java.util.Arrays;
-import static com.bc.fiduceo.post.plugin.sstInsitu.SstInsituTimeSeriesPlugin.TAG_NAME_SECONDARY_SENSOR_MATCHUP_TIME_VARIABLE;
-import static com.bc.fiduceo.post.plugin.sstInsitu.SstInsituTimeSeriesPlugin.TAG_NAME_SST_INSITU_TIME_SERIES;
-import static com.bc.fiduceo.post.plugin.sstInsitu.SstInsituTimeSeriesPlugin.TAG_NAME_TIME_RANGE_SECONDS;
-import static com.bc.fiduceo.post.plugin.sstInsitu.SstInsituTimeSeriesPlugin.TAG_NAME_TIME_SERIES_SIZE;
-import static com.bc.fiduceo.post.plugin.sstInsitu.SstInsituTimeSeriesPlugin.TAG_NAME_VERSION;
+import static com.bc.fiduceo.post.plugin.sstInsitu.SstInsituTimeSeriesPlugin.*;
import static com.bc.fiduceo.util.NetCDFUtils.CF_FILL_VALUE_NAME;
import static com.bc.fiduceo.util.NetCDFUtils.CF_LONG_NAME;
import static com.bc.fiduceo.util.NetCDFUtils.CF_UNITS_NAME;
@@ -144,6 +140,7 @@ public void prepare() throws Exception {
configuration.timeRangeSeconds = 124;
configuration.timeSeriesSize = 16;
configuration.matchupTimeVarName = "matchupTimeVarName";
+ configuration.insituTimeVarName = "insituTimeVarName";
final SstInsituTimeSeries insituTimeSeries = new SstInsituTimeSeries(configuration);
insituTimeSeries.setContext(context);
@@ -208,6 +205,7 @@ public void computeInsituRange() throws Exception {
configuration.timeRangeSeconds = seconds;
configuration.timeSeriesSize = 300;
configuration.matchupTimeVarName = "matchupTimeVarName";
+ configuration.insituTimeVarName = "insitu.time";
final SstInsituTimeSeries insituTimeSeries = new SstInsituTimeSeries(configuration);
final ReaderFactory readerFactory = ReaderFactory.create(new GeometryFactory("S2"), null, null, null); // we don't need temp file support here tb 2018-01-23
@@ -260,6 +258,7 @@ public void computeOneProduct() throws Exception {
new Element(TAG_NAME_VERSION).addContent("v03.3"),
new Element(TAG_NAME_TIME_RANGE_SECONDS).addContent("" + 80000),
new Element(TAG_NAME_TIME_SERIES_SIZE).addContent("10"),
+ new Element(TAG_NAME_INSITU_TIME_VARIABLE).addContent("insitu.time"),
new Element(TAG_NAME_SECONDARY_SENSOR_MATCHUP_TIME_VARIABLE).addContent("amsre.acquisition_time")
))
)
diff --git a/post-processing-tool/src/test/java/com/bc/fiduceo/post/util/PPUtilsTest.java b/post-processing-tool/src/test/java/com/bc/fiduceo/post/util/PPUtilsTest.java
new file mode 100644
index 000000000..41222a2b2
--- /dev/null
+++ b/post-processing-tool/src/test/java/com/bc/fiduceo/post/util/PPUtilsTest.java
@@ -0,0 +1,49 @@
+package com.bc.fiduceo.post.util;
+
+import org.junit.Test;
+import ucar.ma2.Array;
+
+import static org.hamcrest.Matchers.*;
+import static org.hamcrest.MatcherAssert.*;
+import static org.junit.Assert.*;
+
+public class PPUtilsTest {
+
+ @Test
+ public void convertToFitTheRangeMinus180to180() {
+ final int farOutside = 360 * 10;
+
+ final double[] lons = {
+ -180,
+ -179.999999999,
+ 179.999999999,
+ 180,
+ -190,
+ 190,
+ -30 - farOutside,
+ 40 + farOutside,
+ Double.NaN,
+ Double.NEGATIVE_INFINITY,
+ Double.POSITIVE_INFINITY
+ };
+
+ final Array lonArray = Array.makeFromJavaArray(lons);
+ PPUtils.convertToFitTheRangeMinus180to180(lonArray);
+
+ final double[] expected = {
+ /* Before: -180 after -> */ -180,
+ /* Before: -179.999999999 after -> */ -179.999999999,
+ /* Before: 179.999999999 after -> */ 179.999999999,
+ /* Before: 180 after -> */ 180,
+ /* Before: -190 after -> */ 170,
+ /* Before: 190 after -> */ -170,
+ /* Before: -30 - farOutside after -> */ -30,
+ /* Before: 40 + farOutside after -> */ 40,
+ /* Before: Double.NaN after -> */ Double.NaN,
+ /* Before: Double.NEGATIVE_INFINITY after -> */ Double.NEGATIVE_INFINITY,
+ /* Before: Double.POSITIVE_INFINITY after -> */ Double.POSITIVE_INFINITY
+ };
+
+ assertArrayEquals(expected, (double[]) lonArray.getStorage(), 0.000001);
+ }
+}
\ No newline at end of file