, 1 April 1990
Ty Coon, President of Vice
That's all there is to it!
================================================
FILE: OSM2World/lib/lp_solve/README.html
================================================
Using lp_solve 5.5 in Java programs
Using lp_solve 5.5 in Java programs
Contents
1. Introduction
2. Installation
3. Usage
4. Implementation notes
5. Building from source
6. Calling lp_solve from Python/Jython
lp_solve is a free (see LGPL for the GNU lesser general public license) linear
(integer) programming solver based on the revised simplex method and the Branch-and-bound method
for the integers. lp_solve has its own community via the Yahoo group
http://groups.yahoo.com/group/lp_solve.
There you can find the latest sources, executables for the common platforms, examples, manuals
and a message board where people can share their thoughts on lp_solve.
lp_solve is written in ANSI C and can be compiled on many different platforms like Linux and Windows.
Basically, lp_solve is a library, a set of routines, that can be called
easily from programming languages like C, C++, C# and VB. Unfortunately, there is no simple and
straightforward way to use native C libraries like lp_solve in Java programs.
This library (also called "Java wrapper") is designed to remedy this shortcoming. It
consists of two main parts:
- A Java class library that is used by Java client programs. It gives access to all
lp_solve routines through the
LpSolve class.
- A native library written in C++, also called 'stub' library, that uses the JNI
(Java Native Interface) API to translate Java method calls into calls to the
corresponding routines of the lp_solve library. Java client
programs do not interact directly with the stub library.
This library must be compiled for each target platform.
Precompiled binaries are included for Windows and Linux operating systems.
There is also a build script for compiling the stub library on Mac OS X.
This document should help you getting started using the Java wrapper and lp_solve in your
Java programs. Read it in addition to the documentation that comes with lp_solve.
Always refer to the lp_solve docs as ultimate reference for
using the routines of the optimization library.
Bug reports, succes stories and requests for changes concerning the Java wrapper are welcome
by email at juergen.ebert@web.de or in the lp_solve discussion group.
The current wrapper version was written to work with lp_solve 5.5.0.9 and was tested
under Windows XP and Linux. As long as the API stays the same, other versions of lp_solve
are likely to work as well. The wrapper requires a Java Runtime Environment 1.3 or later.
The latest version of the Java wrapper can be found in the files section of the lp_solve group.
The wrapper is released under the same LGPL license conditions as lp_solve. A copy of
the LGPL text is contained in the distribution archive.
- Copy the lp_solve dynamic libraries from the archives
lp_solve_5.5_dev.(zip or tar.gz)
and lp_solve_5.5_exe.(zip or tar.gz) to a standard library directory for your target platform.
On Windows, a typical place would be \WINDOWS or \WINDOWS\SYSTEM32.
On Linux, a typical place would be the directory /usr/local/lib.
- Unzip the Java wrapper distribution file to new directory of your choice.
- On Windows, copy the wrapper stub library
lpsolve55j.dll
to the directory that already contains lpsolve55.dll.
- On Linux, copy the wrapper stub library
liblpsolve55j.so
to the directory that already contains liblpsolve55.so. Run ldconfig to include
the library in the shared libray cache.
To create a Java application that uses lp_solve routines, you must perform the
following steps:
- Make sure you have a Java Runtime Environment 1.3 or later installed.
- Install lp_solve and the Java wrapper as described above.
- Copy the archive file
lpsolve55j.jar from the Java wrapper distribution to a
directory that is included in the CLASSPATH of your java program.
- Add an import statement for the package
lpsolve.* at the beginning of your
source file.
- Call
LpSolve.makeLp(...) or one of the other static factory methods of the LpSolve
class to create a LpSolve instance. Each LpSolve instance represents an optimization
problem.
- Call the methods of the
LpSolve instance to define the problem and obtain the solution.
Use the examples and implementation notes later in this documentation for further
information.
- When you run your Java file make sure to include
lpsolve55j.jar in the CLASSPATH.
Also, on Windows, if you installed the native stub library in a directory that is not included
in the PATH variable, you have to define the Java system variable java.library.path
which must point to the installation directory. On Linux, the equivalent of the Windows PATH
variable is called LD_LIBRARY_PATH.
The following program is a very simple example that shows how to program with lp_solve in Java.
import lpsolve.*;
public class Demo {
public static void main(String[] args) {
try {
// Create a problem with 4 variables and 0 constraints
LpSolve solver = LpSolve.makeLp(0, 4);
// add constraints
solver.strAddConstraint("3 2 2 1", LpSolve.LE, 4);
solver.strAddConstraint("0 4 3 1", LpSolve.GE, 3);
// set objective function
solver.strSetObjFn("2 3 -2 3");
// solve the problem
solver.solve();
// print solution
System.out.println("Value of objective function: " + solver.getObjective());
double[] var = solver.getPtrVariables();
for (int i = 0; i < var.length; i++) {
System.out.println("Value of var[" + i + "] = " + var[i]);
}
// delete the problem and free memory
solver.deleteLp();
}
catch (LpSolveException e) {
e.printStackTrace();
}
}
}
The following code fragment shows you how to use callbacks in Java.
The example defines an anonymous inner class that implements the AbortListener
interface which is then passed to the putAbortfunc method.
LpSolve solver = LpSolve.makeLp(0, 4);
AbortListener abortfunc = new AbortListener() {
public boolean abortfunc(LpSolve problem, Object handle) {
System.out.println("Java abortfunc called, handle = " + handle);
return false;
}
};
solver.putAbortfunc(abortfunc, new Integer(123));
Follow these steps to run the demo application, which is a port of the C demo
program that comes with lp_solve to the Java language. You will need a Java Runtime
Environment (JRE) on your machine in order to run the demo. You can download the
latest JRE from http://java.sun.com
- Install lp_solve and the Java wrapper as described above.
- On Windows, go to the
demo directory and
start the batch script "run_demo.bat".
- On Linux, go to the
demo directory of the wrapper distribution and
run "sh run_demo".
In the demo directory you will find
the file LpSolveTest.java which contains more than 100
JUnit test cases (see http://www.junit.org for
details about this highly useful software)
to strengthen the faith in the Java wrapper implementation. The test cases
may also seve as examples of basic lp_solve usage in Java. You will need
the library junit.jar in your CLASSPATH to run the test cases.
junit.jar is included in the lib directory of the Java wrapper. You
can run the test cases directly by starting the batch script "run_unittests.bat" on Windows
or "sh run_unittests" on Linux.
- In general, the Java API tries to follow the original C/C++ API of lp_solve as
closely as possible to make ist easier for programmers who have to write programs for
lp_solve in multiple different programming languages. However, because of fundamental
differences between the Java programming language and C/C++ this is not always strictly
possible. For example, it is not possible in Java to pass simple datatypes by reference.
- Method names have been kept, but changed to Java convention, i.e. underscores have been
removed, the following character is capitalized. For example
str_add_constraint
becomes strAddConstraint in Java.
- The
lprec* argument taken by almost all lp_solve API routines is hidden
completely inside the LpSolve class. All methods that create new lprec
structures were made static methods of the LpSolve class.
- Return values denoting fatal internal errors have been changed to Java Exceptions. For
example
set_row_name returns FALSE if an error has occured. In Java,
setRowName is of type void and throws a LpSolveException.
- Routines that have arguments or return values of type unsigned char, but only allow for TRUE or
FALSE as legal values have been changed to type
boolean. Example:
set_debug(lprec *lp, unsigned char debug) is setDebug(boolean debug) in Java.
- Multiple problems may be solved concurrently by multiple threads, as long as a single
LpSolve object, which represents a problem, is only used by one thread at a
time.
- lp_solve does not require client programs to keep argument buffers allocated between calls
to different library routines, because all input arguments are copied to internal buffers.
Thus, it is easy to avoid memory leaks in the stub library. All objects returned by the
Java wrapper routines are allocated by the JVM and are subject to the standard garbage
collection process.
- The methods
get_ptr_sensitivity_rhs, get_ptr_reduced_costs,
get_ptr_sensitivity_obj, and get_ptr_sensitivity_objex are not implemented,
because it is not possible in Java to pass pointers by reference to a method. Use the corresponding
methods without the Ptr part in the method name instead, which require allocation
of the resulting arrays by the caller.
- See the file
reference.html for details on how the lp_solve API functions
are mapped to the Java methods.
The Java wrapper archive contains precompiled binary libraries for Windows and Linux.
If you just want to use the wrapper there should be no need to build the libs from the sources.
But if you absolutely have to, follow the guidelines in this chapter.
The following prerequisites must be met in order to build the wrapper C library from source
on Windows operating systems:
- Microsoft Visual C++ compiler (I used V 7, others might work)
- Visual Studio envirement variables must be set.
- Sun Java Development Kit 1.4.x installed and JAVA_HOME environment variable set
- lp_solve Windows development archive
lp_solve_5.5_dev.zip unpacked
Change to the lib directory and edit the file build.bat.
Change the path to the directory where you unpacked the lp_solve Windows
archive. Run the script to build lpsolve55j.dll.
The dll will be created in directory win32 or win64 depending on the OS.
The following prerequisites must be met in order to build the wrapper C library from source
on Linux operating systems:
- gcc and g++ compiler installed (I used gcc Version 3.3.1)
- Sun Java Development Kit 1.4.x installed
- lp_solve Linux development archive
lp_solve_5.5_dev.tar.gz unpacked
Change to the lib directory and edit the file build.
Change the paths to the directory where you unpacked the lp_solve linux archive
and where the JDK is installed.
Run sh build to build liblpsolve55j.so.
The dll will be created in directory ux32 or ux64 depending on the OS.
Change to the lib/mac directory and edit the file build_osx.
Change the directory paths as indicated in the comments.
Thanks to Sean P. Kane (spkane@genomatica.com) who provided this build script.
Jython (http://www.jython.org) is a 100% Java implementation of
the popular scripting language Python.
One of the most remarkable features of Jython is the seamless interaction between Python and Java.
Java programmers can add the Jython libraries to their system to allow end users to write scripts that add
functionality to the application. On the other hand, Python/Jython programs can interact with Java packages
or with running Java applications.
lp_solve functions can be called via the Java wrapper from Python/Jython programs.
See the file demo.py in the demo directory of the Java wrapper distribution
for an example program.
To run this program, you must install lp_solve, the Java wrapper, and Jython. Don't forget
to include lpsolve55j.jar in the Java CLASSPATH when you run Jython.
================================================
FILE: OSM2World/lib/osmosis/copying.txt
================================================
Osmosis consists of all files in this archive with the exception of the
third party libraries in the lib and repo subdirectories.
Osmosis is placed into the public domain and where this is not legally
possible everybody is granted a perpetual, irrevocable license to use
this work for any purpose whatsoever.
DISCLAIMERS
By making Osmosis publicly available, it is hoped that users will find the
software useful. However:
* Osmosis comes without any warranty, to the extent permitted by applicable
law.
* Unless required by applicable law, no liability will be accepted by
the authors and distributors of this software for any damages caused
as a result of its use.
================================================
FILE: OSM2World/lib/poly2tri/LICENSE.txt
================================================
Poly2Tri
Copyright (c) 2009-2010, Poly2Tri Contributors
http://code.google.com/p/poly2tri/
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of Poly2Tri nor the names of its contributors may be
used to endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
================================================
FILE: OSM2World/lib/slf4j/LICENSE.txt
================================================
Copyright (c) 2004-2008 QOS.ch
All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
================================================
FILE: OSM2World/lib/trove/AUTHORS.txt
================================================
Rob Eden
Johan Parent
Jeff Randall
Eric D. Friedman
Please do NOT email bug reports or feature requests.
Instead use the very fine bug tracking system and feature request
service on SourceForge: http://sourceforge.net/projects/trove4j/
We'll read your issue just as quickly, and the project's issues will
remain out in the open where everyone can see them. We also monitor
the project forums, so feel free to use those too.
================================================
FILE: OSM2World/lib/trove/LICENSE.txt
================================================
GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the Lesser GPL. It also counts
as the successor of the GNU Library Public License, version 2, hence
the version number 2.1.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it. You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.
When we speak of free software, we are referring to freedom of use,
not price. Our General Public Licenses are designed to make sure that
you have the freedom to distribute copies of free software (and charge
for this service if you wish); that you receive source code or can get
it if you want it; that you can change the software and use pieces of
it in new free programs; and that you are informed that you can do
these things.
To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights. These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.
To protect each distributor, we want to make it very clear that
there is no warranty for the free library. Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.
Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder. Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.
Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License. This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License. We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.
When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library. The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom. The Lesser General
Public License permits more lax criteria for linking other code with
the library.
We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License. It also provides other free software developers Less
of an advantage over competing non-free programs. These disadvantages
are the reason we use the ordinary General Public License for many
libraries. However, the Lesser license provides advantages in certain
special circumstances.
For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard. To achieve this, non-free programs must be
allowed to use the library. A more frequent case is that a free
library does the same job as widely used non-free libraries. In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.
In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software. For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.
Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.
GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (1) uses at run time a
copy of the library already present on the user's computer system,
rather than copying library functions into the executable, and (2)
will operate properly with a modified version of the library, if
the user installs one, as long as the modified version is
interface-compatible with the version that the work was made with.
c) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
d) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
e) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the materials to be distributed need not include anything that is
normally distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded. In such case, this License incorporates the limitation as if
written in the body of this License.
13. The Free Software Foundation may publish revised and/or new
versions of the Lesser General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Libraries
If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).
To apply these terms, attach the following notices to the library. It is
safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.
Copyright (C)
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Also add information on how to contact you by electronic and paper mail.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
, 1 April 1990
Ty Coon, President of Vice
That's all there is to it!
================================================
FILE: OSM2World/lib/trove/README-license.txt
================================================
The Trove library is licensed under the Lesser GNU Public License,
which is included with the distribution in a file called LICENSE.txt.
Other license arrangements are possible, for a fee: contact
ericdf@users.sourceforge.net for terms/pricing.
The PrimeFinder and HashFunctions classes in Trove are subject to the
following license restrictions:
Copyright (c) 1999 CERN - European Organization for Nuclear Research.
Permission to use, copy, modify, distribute and sell this software and
its documentation for any purpose is hereby granted without fee,
provided that the above copyright notice appear in all copies and that
both that copyright notice and this permission notice appear in
supporting documentation. CERN makes no representations about the
suitability of this software for any purpose. It is provided "as is"
without expressed or implied warranty.
================================================
FILE: OSM2World/lib/trove/README.txt
================================================
GNU Trove: High performance collections for Java.
Objectives
The GNU Trove library has two objectives:
1. Provide "free" (as in "free speech" and "free beer"), fast,
lightweight implementations of the java.util Collections API.
These implementations are designed to be pluggable replacements
for their JDK equivalents.
2. Whenever possible, provide the same collections support for
primitive types. This gap in the JDK is often addressed by using
the "wrapper" classes (java.lang.Integer, java.lang.Float, etc.)
with Object-based collections. For most applications, however,
collections which store primitives directly will require less
space and yield significant performance gains.
Hashtable techniques
The Trove maps/sets use open addressing instead of the chaining
approach taken by the JDK hashtables. This eliminates the need to
create Map.Entry wrappper objects for every item in a table and so
reduces the O (big-oh) in the performance of the hashtable algorithm.
The size of the tables used in Trove's maps/sets is always a prime
number, improving the probability of an optimal distribution of
entries across the table, and so reducing the likelihood of
performance-degrading collisions. Trove sets are not backed by maps,
and so using a THashSet does not result in the allocation of an unused
"values" array.
Hashing strategies
Trove's maps/sets support the use of custom hashing strategies,
allowing you to tune collections based on characteristics of the input
data. This feature also allows you to define hash functions when it is
not feasible to override Object.hashCode(). For example, the
java.lang.String class is final, and its implementation of hashCode()
takes O(n) time to complete. In some applications, however, it may be
possible for a custom hashing function to save time by skipping
portions of the string that are invariant.
Using java.util.HashMap, it is not possible to use Java language
arrays as keys. For example, this code:
char[] foo, bar;
foo = new char[] {'a','b','c'};
bar = new char[] {'a','b','c'};
System.out.println(foo.hashCode() == bar.hashCode() ?
"equal" : "not equal");
System.out.println(foo.equals(bar) ? "equal" : "not equal");
produces this output:
not equal
not equal
And so an entry stored in a java.util.HashMap with foo as a key could
not be retrieved with bar, since there is no way to override
hashCode() or equals() on language array objects.
In a gnu.trove.map.hash.TCustomHashMap, however, you can implement a
gnu.trove.strategy.HashingStrategy to enable hashing on arrays:
class CharArrayStrategy implements HashingStrategy {
public int computeHashCode(Object o) {
char[] c = (char[])o;
// use the shift-add-xor class of string hashing functions
// cf. Ramakrishna and Zobel,
// "Performance in Practice of String Hashing Functions"
int h = 31; // seed chosen at random
for (int i = 0; i < c.length; i++) { // could skip invariants
// L=5, R=2 works well for ASCII input
h = h ^ ((h << 5) + (h >> 2) + c[i]);
}
return h;
}
public boolean equals(Object o1, Object o2) {
char[] c1 = (char[])o1;
char[] c2 = (char[])o2;
// could drop this check for fixed-length keys
if (c1.length != c2.length) {
return false;
}
// could skip invariants
for (int i = 0, len = c1.length; i < len; i++) {
if (c1[i] != c2[i]) {
return false;
}
}
return true;
}
}
Iterators in primitive collections
Trove's primitive mappings include access through Iterators as well
as procedures and functions. The API documentation on those classes
contains several examples showing how these can be used effectively
and explaining why their semantics differ from those of
java.util.Iterator.
_________________________________________________________________
Last modified: Sep 9, 2011
================================================
FILE: OSM2World/osm2world-rel20160312-to-bdb0c396e899ee9.patch
================================================
diff --git a/OSM2World/.classpath b/OSM2World/.classpath
new file mode 100644
index 0000000..6a3f3c9
--- /dev/null
+++ b/OSM2World/.classpath
@@ -0,0 +1,79 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/OSM2World/.gitignore b/OSM2World/.gitignore
new file mode 100644
index 0000000..6cc54c9
--- /dev/null
+++ b/OSM2World/.gitignore
@@ -0,0 +1,4 @@
+/bin
+/build
+
+/eclipse-bin/
diff --git a/OSM2World/.project b/OSM2World/.project
new file mode 100644
index 0000000..d66d464
--- /dev/null
+++ b/OSM2World/.project
@@ -0,0 +1,28 @@
+
+
+ Touch Mapper OSM2World
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+
+ org.eclipse.jdt.core.javanature
+
+
+
+ 1477832440066
+
+ 22
+
+ org.eclipse.ui.ide.multiFilter
+ 1.0-name-matches-false-false-*.class
+
+
+
+
diff --git a/OSM2World/build.xml b/OSM2World/build.xml
index 9459e90..cdcc9aa 100644
--- a/OSM2World/build.xml
+++ b/OSM2World/build.xml
@@ -31,12 +31,12 @@
-
+
-
+
diff --git a/OSM2World/src/org/osm2world/core/ConversionFacade.java b/OSM2World/src/org/osm2world/core/ConversionFacade.java
index e71693c..39cba67 100644
--- a/OSM2World/src/org/osm2world/core/ConversionFacade.java
+++ b/OSM2World/src/org/osm2world/core/ConversionFacade.java
@@ -28,6 +28,7 @@ import org.osm2world.core.map_elevation.creation.TerrainInterpolator;
import org.osm2world.core.map_elevation.creation.ZeroInterpolator;
import org.osm2world.core.map_elevation.data.EleConnector;
import org.osm2world.core.math.VectorXYZ;
+import org.osm2world.core.math.AxisAlignedBoundingBoxXZ;
import org.osm2world.core.osm.creation.JOSMFileHack;
import org.osm2world.core.osm.creation.OsmosisReader;
import org.osm2world.core.osm.data.OSMData;
@@ -133,20 +134,20 @@ public class ConversionFacade {
new RoadModule(),
new RailwayModule(),
new BuildingModule(),
- new ParkingModule(),
- new TreeModule(),
- new StreetFurnitureModule(),
- new TrafficSignModule(),
- new WaterModule(),
- new PoolModule(),
- new GolfModule(),
- new CliffModule(),
- new BarrierModule(),
- new PowerModule(),
- new BridgeModule(),
- new TunnelModule(),
- new SurfaceAreaModule(),
- new InvisibleModule()
+ //new ParkingModule(),
+ //new TreeModule(),
+ //new StreetFurnitureModule(),
+ //new TrafficSignModule(),
+ new WaterModule()
+ //new PoolModule(),
+ //new GolfModule(),
+ //new CliffModule(),
+ //new BarrierModule(),
+ //new PowerModule(),
+ //new BridgeModule(),
+ //new TunnelModule(),
+ //new SurfaceAreaModule(),
+ //new InvisibleModule()
);
}
@@ -303,7 +304,7 @@ public class ConversionFacade {
}
}
}
-
+
/* create map data from OSM data */
updatePhase(Phase.MAP_DATA);
@@ -313,6 +314,14 @@ public class ConversionFacade {
OSMToMapDataConverter converter = new OSMToMapDataConverter(mapProjection, config);
MapData mapData = converter.createMapData(osmData);
+ AxisAlignedBoundingBoxXZ boundary = mapData.getBoundary();
+ System.out.println("Map-boundary:["
+ + " minX=" + boundary.minX
+ + " minZ=" + boundary.minZ
+ + " maxX=" + boundary.maxX
+ + " maxZ=" + boundary.maxZ
+ + " ]");
+
/* apply world modules */
updatePhase(Phase.REPRESENTATION);
diff --git a/OSM2World/src/org/osm2world/core/target/obj/ObjTarget.java b/OSM2World/src/org/osm2world/core/target/obj/ObjTarget.java
index 6be2959..17b2b1f 100644
--- a/OSM2World/src/org/osm2world/core/target/obj/ObjTarget.java
+++ b/OSM2World/src/org/osm2world/core/target/obj/ObjTarget.java
@@ -13,10 +13,12 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import org.openstreetmap.josm.plugins.graphview.core.data.TagGroup;
import org.osm2world.core.map_data.data.MapArea;
import org.osm2world.core.map_data.data.MapElement;
import org.osm2world.core.map_data.data.MapNode;
import org.osm2world.core.map_data.data.MapWaySegment;
+import org.osm2world.core.map_elevation.data.EleConnector;
import org.osm2world.core.math.TriangleXYZ;
import org.osm2world.core.math.TriangleXYZWithNormals;
import org.osm2world.core.math.VectorXYZ;
@@ -91,10 +93,21 @@ public class ObjTarget extends FaceTarget {
/* start an object with the object's class
* and the underlying OSM element's name/ref tags */
+ String roadSuffix = null;
+
MapElement element = object.getPrimaryMapElement();
OSMElement osmElement;
if (element instanceof MapNode) {
osmElement = ((MapNode) element).getOsmNode();
+
+ List connectedWaySegments = ((MapNode) element).getConnectedWaySegments();
+ int pedestrians = 0;
+ for (MapWaySegment mapWaySegment : connectedWaySegments) {
+ pedestrians += isPath(mapWaySegment.getTags()) ? 1 : 0;
+ }
+ if (pedestrians >= (connectedWaySegments.size()+1) / 2) {
+ roadSuffix = "::pedestrian";
+ }
} else if (element instanceof MapWaySegment) {
osmElement = ((MapWaySegment) element).getOsmWay();
} else if (element instanceof MapArea) {
@@ -103,18 +116,50 @@ public class ObjTarget extends FaceTarget {
osmElement = null;
}
+ if (roadSuffix == null) {
+ roadSuffix = isPath(osmElement.tags) ? "::pedestrian" : "";
+ }
+
if (osmElement != null && osmElement.tags.containsKey("name")) {
- objStream.println("o " + object.getClass().getSimpleName() + " " + osmElement.tags.getValue("name"));
+ objStream.println("o " + object.getClass().getSimpleName() + " " + osmElement.tags.getValue("name") + roadSuffix);
} else if (osmElement != null && osmElement.tags.containsKey("ref")) {
- objStream.println("o " + object.getClass().getSimpleName() + " " + osmElement.tags.getValue("ref"));
+ objStream.println("o " + object.getClass().getSimpleName() + " " + osmElement.tags.getValue("ref") + roadSuffix);
} else {
- objStream.println("o " + object.getClass().getSimpleName() + anonymousWOCounter ++);
+ objStream.println("o " + object.getClass().getSimpleName() + anonymousWOCounter ++ + roadSuffix);
}
}
}
+ private static boolean isPath(TagGroup tags) {
+ String highwayValue = tags.getValue("highway");
+ if ("path".equals(highwayValue)
+ || "footway".equals(highwayValue)
+ || "cycleway".equals(highwayValue)
+ || "service".equals(highwayValue)
+ || "bridleway".equals(highwayValue)
+ || "living_street".equals(highwayValue)
+ || "pedestrian".equals(highwayValue)
+ || "track".equals(highwayValue)
+ || "steps".equals(highwayValue)) {
+ return true;
+ }
+ if (tags.containsKey("footway")
+ || tags.contains("tourism", "attraction")
+ || tags.contains("man_made", "pier")
+ || tags.contains("man_made", "breakwater")) {
+ return true;
+ }
+ String footValue = tags.getValue("foot");
+ if ("yes".equals(footValue)
+ || "designated".equals(footValue)) {
+ return true;
+ }
+ return false;
+ }
+
+
@Override
public void drawFace(Material material, List vs,
List normals, List> texCoordLists) {
diff --git a/OSM2World/src/org/osm2world/core/world/modules/BuildingModule.java b/OSM2World/src/org/osm2world/core/world/modules/BuildingModule.java
index df24aa2..b9542b6 100644
--- a/OSM2World/src/org/osm2world/core/world/modules/BuildingModule.java
+++ b/OSM2World/src/org/osm2world/core/world/modules/BuildingModule.java
@@ -76,8 +76,8 @@ public class BuildingModule extends ConfigurableWorldModule {
@Override
public void applyTo(MapData mapData) {
- boolean useBuildingColors = config.getBoolean("useBuildingColors", true);
- boolean drawBuildingWindows = config.getBoolean("drawBuildingWindows", true);
+ boolean useBuildingColors = config.getBoolean("useBuildingColors", false);
+ boolean drawBuildingWindows = config.getBoolean("drawBuildingWindows", false);
for (MapArea area : mapData.getMapAreas()) {
@@ -111,23 +111,23 @@ public class BuildingModule extends ConfigurableWorldModule {
this.area = area;
- for (MapOverlap,?> overlap : area.getOverlaps()) {
- MapElement other = overlap.getOther(area);
- if (other instanceof MapArea
- && other.getTags().containsKey("building:part")) {
-
- MapArea otherArea = (MapArea)other;
-
- //TODO: check whether the building contains the part (instead of just touching it)
- if (area.getPolygon().contains(
- otherArea.getPolygon().getOuter())) {
- parts.add(new BuildingPart(this, otherArea,
- otherArea.getPolygon(), useBuildingColors,
- drawBuildingWindows));
- }
-
- }
- }
+// for (MapOverlap,?> overlap : area.getOverlaps()) {
+// MapElement other = overlap.getOther(area);
+// if (other instanceof MapArea
+// && other.getTags().containsKey("building:part")) {
+//
+// MapArea otherArea = (MapArea)other;
+//
+// //TODO: check whether the building contains the part (instead of just touching it)
+// if (area.getPolygon().contains(
+// otherArea.getPolygon().getOuter())) {
+// parts.add(new BuildingPart(this, otherArea,
+// otherArea.getPolygon(), useBuildingColors,
+// drawBuildingWindows));
+// }
+//
+// }
+// }
/* add part(s) for area not covered by building:part polygons */
boolean isBuildingPart = false;
@@ -138,23 +138,23 @@ public class BuildingModule extends ConfigurableWorldModule {
parts.add(new BuildingPart(this, area,
area.getPolygon(), useBuildingColors, drawBuildingWindows));
} else {
- List subtractPolygons = new ArrayList();
-
- for (BuildingPart part : parts) {
- subtractPolygons.add(part.getPolygon().getOuter());
- }
- subtractPolygons.addAll(area.getPolygon().getHoles());
-
- Collection remainingPolys =
- CAGUtil.subtractPolygons(
- area.getPolygon().getOuter(),
- subtractPolygons);
-
- for (PolygonWithHolesXZ remainingPoly : remainingPolys) {
- parts.add(new BuildingPart(this, area, remainingPoly,
- useBuildingColors, drawBuildingWindows));
- }
-
+// List subtractPolygons = new ArrayList();
+//
+// for (BuildingPart part : parts) {
+// subtractPolygons.add(part.getPolygon().getOuter());
+// }
+// subtractPolygons.addAll(area.getPolygon().getHoles());
+//
+// Collection remainingPolys =
+// CAGUtil.subtractPolygons(
+// area.getPolygon().getOuter(),
+// subtractPolygons);
+//
+// for (PolygonWithHolesXZ remainingPoly : remainingPolys) {
+// parts.add(new BuildingPart(this, area, remainingPoly,
+// useBuildingColors, drawBuildingWindows));
+// }
+//
}
/* create connectors along the outline.
@@ -319,19 +319,19 @@ public class BuildingModule extends ConfigurableWorldModule {
this.area = area;
this.polygon = polygon;
- setAttributes(useBuildingColors, drawBuildingWindows);
-
- for (MapNode node : area.getBoundaryNodes()) {
- if ((node.getTags().contains("building", "entrance")
- || node.getTags().containsKey("entrance"))
- && node.getRepresentations().isEmpty()) {
-
- BuildingEntrance entrance = new BuildingEntrance(this, node);
- entrances.add(entrance);
- node.addRepresentation(entrance);
-
- }
- }
+ setAttributes(false, false);
+
+// for (MapNode node : area.getBoundaryNodes()) {
+// if ((node.getTags().contains("building", "entrance")
+// || node.getTags().containsKey("entrance"))
+// && node.getRepresentations().isEmpty()) {
+//
+// BuildingEntrance entrance = new BuildingEntrance(this, node);
+// entrances.add(entrance);
+// node.addRepresentation(entrance);
+//
+// }
+// }
}
@@ -349,10 +349,11 @@ public class BuildingModule extends ConfigurableWorldModule {
@Override
public void renderTo(Target> target) {
+ renderFloor(target, 0);
- renderWalls(target, roof);
+// renderWalls(target, roof);
- roof.renderTo(target);
+// roof.renderTo(target);
}
@@ -380,8 +381,9 @@ public class BuildingModule extends ConfigurableWorldModule {
double floorHeight = calculateFloorHeight(roof);
boolean renderFloor = (floorHeight > 0);
- if (area.getOverlaps().isEmpty()) {
-
+ if (true || area.getOverlaps().isEmpty()) {
+ // Always draw buildings whole. Otherwise there may be
+ // eg. ground level tunnels through them, which would screw things up.
renderWalls(target, roof.getPolygon(), false,
baseEle, floorHeight, roof);
@@ -732,64 +734,68 @@ public class BuildingModule extends ConfigurableWorldModule {
/* determine roof shape */
- boolean explicitRoofTagging = true;
-
- if (!("no".equals(area.getTags().getValue("roof:lines"))) && hasComplexRoof(area)) {
- roof = new ComplexRoof();
- } else {
-
- String roofShape = getValue("roof:shape");
- if (roofShape == null) { roofShape = getValue("building:roof:shape"); }
-
- if (roofShape == null) {
- roofShape = defaultRoofShape;
- explicitRoofTagging = false;
- }
-
- try {
-
- if ("pyramidal".equals(roofShape)) {
- roof = new PyramidalRoof();
- } else if ("onion".equals(roofShape)) {
- roof = new OnionRoof();
- } else if ("skillion".equals(roofShape)) {
- roof = new SkillionRoof();
- } else if ("gabled".equals(roofShape)) {
- roof = new GabledRoof();
- } else if ("hipped".equals(roofShape)) {
- roof = new HippedRoof();
- } else if ("half-hipped".equals(roofShape)) {
- roof = new HalfHippedRoof();
- } else if ("gambrel".equals(roofShape)) {
- roof = new GambrelRoof();
- } else if ("mansard".equals(roofShape)) {
- roof = new MansardRoof();
- } else if ("dome".equals(roofShape)) {
- roof = new DomeRoof();
- } else if ("round".equals(roofShape)) {
- roof = new RoundRoof();
- } else {
- roof = new FlatRoof();
- }
-
- } catch (InvalidGeometryException e) {
- System.err.println("falling back to FlatRoof: " + e);
- roof = new FlatRoof();
- explicitRoofTagging = false;
- }
-
- }
-
+// boolean explicitRoofTagging = true;
+//
+// if (!("no".equals(area.getTags().getValue("roof:lines"))) && hasComplexRoof(area)) {
+// roof = new ComplexRoof();
+// } else {
+//
+// String roofShape = getValue("roof:shape");
+// if (roofShape == null) { roofShape = getValue("building:roof:shape"); }
+//
+// if (roofShape == null) {
+// roofShape = defaultRoofShape;
+// explicitRoofTagging = false;
+// }
+//
+// try {
+//
+// if ("pyramidal".equals(roofShape)) {
+// roof = new PyramidalRoof();
+// } else if ("onion".equals(roofShape)) {
+// roof = new OnionRoof();
+// } else if ("skillion".equals(roofShape)) {
+// roof = new SkillionRoof();
+// } else if ("gabled".equals(roofShape)) {
+// roof = new GabledRoof();
+// } else if ("hipped".equals(roofShape)) {
+// roof = new HippedRoof();
+// } else if ("half-hipped".equals(roofShape)) {
+// roof = new HalfHippedRoof();
+// } else if ("gambrel".equals(roofShape)) {
+// roof = new GambrelRoof();
+// } else if ("mansard".equals(roofShape)) {
+// roof = new MansardRoof();
+// } else if ("dome".equals(roofShape)) {
+// roof = new DomeRoof();
+// } else if ("round".equals(roofShape)) {
+// roof = new RoundRoof();
+// } else {
+// roof = new FlatRoof();
+// }
+//
+// } catch (InvalidGeometryException e) {
+// System.err.println("falling back to FlatRoof: " + e);
+// roof = new FlatRoof();
+// explicitRoofTagging = false;
+// }
+//
+// }
+
+ roof = new FlatRoof();
+ boolean explicitRoofTagging = false;
+
/* determine height */
- double fallbackHeight = buildingLevels * defaultHeightPerLevel;
-
- fallbackHeight += roof.getRoofHeight();
-
- fallbackHeight = parseHeight(buildingTags, (float)fallbackHeight);
-
- double height = parseHeight(tags, (float)fallbackHeight);
- heightWithoutRoof = height - roof.getRoofHeight();
+// double fallbackHeight = buildingLevels * defaultHeightPerLevel;
+//
+// fallbackHeight += roof.getRoofHeight();
+//
+// fallbackHeight = parseHeight(buildingTags, (float)fallbackHeight);
+//
+// double height = parseHeight(tags, (float)fallbackHeight);
+ double height = 100f;
+ heightWithoutRoof = height - roof.getRoofHeight();
/* determine materials */
diff --git a/OSM2World/src/org/osm2world/core/world/modules/RailwayModule.java b/OSM2World/src/org/osm2world/core/world/modules/RailwayModule.java
index efaec57..11671b7 100644
--- a/OSM2World/src/org/osm2world/core/world/modules/RailwayModule.java
+++ b/OSM2World/src/org/osm2world/core/world/modules/RailwayModule.java
@@ -40,7 +40,9 @@ public class RailwayModule extends ConfigurableWorldModule {
for (MapWaySegment segment : grid.getMapWaySegments()) {
if (segment.getTags().containsAny("railway", RAILWAY_VALUES)) {
- segment.addRepresentation(new Rail(segment));
+ if (! TunnelModule.isTunnel(segment.getTags())) {
+ segment.addRepresentation(new Rail(segment));
+ }
}
}
@@ -66,13 +68,16 @@ public class RailwayModule extends ConfigurableWorldModule {
private static class Rail extends AbstractNetworkWaySegmentWorldObject
implements RenderableToAllTargets, TerrainBoundaryWorldObject {
+
+ private static float TM_SCALE = Float.parseFloat(System.getenv("TOUCH_MAPPER_SCALE"));
+ private static float TM_EXTRUDER = Float.parseFloat(System.getenv("TOUCH_MAPPER_EXTRUDER_WIDTH"));
- private static final float GROUND_WIDTH = 2.25f;
- private static final float RAIL_DIST = 1.5f;
+ private static float sizesScaling = TM_SCALE / 3100; // historically things were tuned for scale 3100
+
+ private static final float GROUND_WIDTH = 2.25f * sizesScaling;
+ private static final float RAIL_DIST = 1.5f * sizesScaling;
- private static final float SLEEPER_WIDTH = 2.0f;
- private static final float SLEEPER_LENGTH = 0.75f;
- private static final float SLEEPER_HEIGHT = 0.125f;
+ private static final float SLEEPER_HEIGHT = 0.125f * sizesScaling;
private static final List RAIL_SHAPE = asList(
new VectorXYZ(-0.45f, 0, 0), new VectorXYZ(-0.1f, 0.1f, 0),
@@ -132,37 +137,37 @@ public class RailwayModule extends ConfigurableWorldModule {
getOutline(false), getOutline(true),
1 - ((GROUND_WIDTH - RAIL_DIST) / GROUND_WIDTH) / 2);
- for (List railLine : railLines) {
-
- List> stripVectors =
- WorldModuleGeometryUtil.createShapeExtrusionAlong(
- RAIL_SHAPE, railLine,
- Collections.nCopies(railLine.size(), VectorXYZ.Y_UNIT));
-
- for (List stripVector : stripVectors) {
- target.drawTriangleStrip(Materials.RAIL_DEFAULT, stripVector, null);
- }
-
- }
-
-
- /* draw railway ties/sleepers */
-
- List sleeperPositions = GeometryUtil.equallyDistributePointsAlong(3, false,
- getStartWithOffset(), getEndWithOffset());
-
- for (VectorXZ sleeperPosition : sleeperPositions) {
-
- //TODO interpolate ele, also using additional points inbetween
-
-// VectorXYZ sleeperPositionXYZ =
-// segment.getElevationProfile().getWithEle(sleeperPosition);
-//
-// target.drawBox(Materials.RAIL_SLEEPER_DEFAULT,
-// sleeperPositionXYZ, segment.getDirection(),
-// SLEEPER_HEIGHT, SLEEPER_WIDTH, SLEEPER_LENGTH);
-
- }
+// for (List railLine : railLines) {
+//
+// List> stripVectors =
+// WorldModuleGeometryUtil.createShapeExtrusionAlong(
+// RAIL_SHAPE, railLine,
+// Collections.nCopies(railLine.size(), VectorXYZ.Y_UNIT));
+//
+// for (List stripVector : stripVectors) {
+// target.drawTriangleStrip(Materials.RAIL_DEFAULT, stripVector, null);
+// }
+//
+// }
+//
+//
+// /* draw railway ties/sleepers */
+//
+// List sleeperPositions = GeometryUtil.equallyDistributePointsAlong(3, false,
+// getStartWithOffset(), getEndWithOffset());
+//
+// for (VectorXZ sleeperPosition : sleeperPositions) {
+//
+// //TODO interpolate ele, also using additional points inbetween
+//
+//// VectorXYZ sleeperPositionXYZ =
+//// segment.getElevationProfile().getWithEle(sleeperPosition);
+////
+//// target.drawBox(Materials.RAIL_SLEEPER_DEFAULT,
+//// sleeperPositionXYZ, segment.getDirection(),
+//// SLEEPER_HEIGHT, SLEEPER_WIDTH, SLEEPER_LENGTH);
+//
+// }
}
diff --git a/OSM2World/src/org/osm2world/core/world/modules/RoadModule.java b/OSM2World/src/org/osm2world/core/world/modules/RoadModule.java
index 66bc74f..b9a11b5 100644
--- a/OSM2World/src/org/osm2world/core/world/modules/RoadModule.java
+++ b/OSM2World/src/org/osm2world/core/world/modules/RoadModule.java
@@ -61,21 +61,24 @@ public class RoadModule extends ConfigurableWorldModule {
for (MapWaySegment line : grid.getMapWaySegments()) {
if (isRoad(line.getTags())) {
- line.addRepresentation(new Road(line, line.getTags()));
+ if (! isTunnelOrSimilar(line.getTags())) {
+ line.addRepresentation(new Road(line, line.getTags()));
+ }
}
}
for (MapArea area : grid.getMapAreas()) {
if (isRoad(area.getTags())) {
-
- List coords = new ArrayList();
- for (MapNode node : area.getBoundaryNodes()) {
- coords.add(node.getPos());
+ if (! isTunnelOrSimilar(area.getTags())) {
+ List coords = new ArrayList();
+ for (MapNode node : area.getBoundaryNodes()) {
+ coords.add(node.getPos());
+ }
+ coords.remove(coords.size()-1);
+
+ area.addRepresentation(new RoadArea(area));
}
- coords.remove(coords.size()-1);
-
- area.addRepresentation(new RoadArea(area));
}
}
@@ -86,7 +89,7 @@ public class RoadModule extends ConfigurableWorldModule {
List connectedRoads = getConnectedRoads(node, false);
- if (connectedRoads.size() > 2) {
+ if (connectedRoads.size() == 3) {
node.addRepresentation(new RoadJunction(node));
@@ -112,6 +115,18 @@ public class RoadModule extends ConfigurableWorldModule {
}
+ public static final boolean isTunnelOrSimilar(TagGroup tags) {
+ if (tags.containsKey("tunnel")
+ && !"no".equals(tags.getValue("tunnel"))) {
+ return true;
+ };
+ if (tags.containsKey("indoor")
+ && !"no".equals(tags.getValue("indoor"))) {
+ return true;
+ };
+ return false;
+ }
+
private static boolean isRoad(TagGroup tags) {
if (tags.containsKey("highway")
&& !tags.contains("highway", "construction")
@@ -119,15 +134,24 @@ public class RoadModule extends ConfigurableWorldModule {
return true;
} else {
return tags.contains("railway", "platform")
- || tags.contains("leisure", "track");
+ || tags.contains("leisure", "track")
+ || tags.contains("tourism", "attraction")
+ || tags.contains("man_made", "pier")
+ || tags.contains("man_made", "breakwater");
}
}
private static boolean isSteps(TagGroup tags) {
- return tags.contains(new Tag("highway","steps"));
+ return false;
+// return tags.contains(new Tag("highway","steps"));
}
+ // See also ObjTarget.isPath()
private static boolean isPath(TagGroup tags) {
+ if (tags.contains("man_made", "pier")
+ || tags.contains("man_made", "breakwater")) {
+ return true;
+ }
String highwayValue = tags.getValue("highway");
return "path".equals(highwayValue)
|| "footway".equals(highwayValue)
@@ -339,6 +363,9 @@ public class RoadModule extends ConfigurableWorldModule {
*/
private static List buildLaneConnections(
MapNode node, boolean isJunction, boolean isCrossing) {
+ if (true) {
+ return new ArrayList();
+ }
List roads = getConnectedRoads(node, true);
@@ -434,6 +461,9 @@ public class RoadModule extends ConfigurableWorldModule {
private static List buildLaneConnections_allOneway(
MapNode node, List inboundOnewayRoadsLTR,
List outboundOnewayRoadsLTR) {
+ if (true) {
+ return new ArrayList();
+ }
List inboundLanes = new ArrayList();
List outboundLanes = new ArrayList();
@@ -754,7 +784,16 @@ public class RoadModule extends ConfigurableWorldModule {
public static class Road
extends AbstractNetworkWaySegmentWorldObject
implements RenderableToAllTargets, TerrainBoundaryWorldObject {
-
+
+ private static float TM_SCALE = Float.parseFloat(System.getenv("TOUCH_MAPPER_SCALE"));
+ private static float TM_EXTRUDER = Float.parseFloat(System.getenv("TOUCH_MAPPER_EXTRUDER_WIDTH"));
+
+ // Using exactly extruder width will leave many roads unprinted. The ideal road width may
+ // be much less than the goal because edges where roads connect are not perpendicular
+ // to road direction. Further unevenness in road width is caused by fattening in Blender.
+ // Further, Simplify3D often uses value eg. 1.2x extruder width, depending on printer-dependent configuration.
+ private static final float ROAD_WIDTH_MULTIPLIER = 1.6f;
+
protected static final float DEFAULT_LANE_WIDTH = 3.5f;
protected static final float DEFAULT_ROAD_CLEARING = 5;
@@ -799,11 +838,15 @@ public class RoadModule extends ConfigurableWorldModule {
this.steps = isSteps(tags);
if (steps) {
+ // Never executed
this.laneLayout = null;
this.width = parseWidth(tags, 1.0f);
} else {
this.laneLayout = buildBasicLaneLayout();
- this.width = calculateWidth();
+ float mmToUnits = TM_SCALE / 1000;
+ float minRoadWidth = TM_EXTRUDER * ROAD_WIDTH_MULTIPLIER * mmToUnits;
+ float sizesScaling = TM_SCALE / 3100; // historically things were tuned for scale 3100
+ this.width = Math.max(minRoadWidth, calculateWidth() * 0.8f * sizesScaling);
laneLayout.setCalculatedValues(width);
}
diff --git a/OSM2World/src/org/osm2world/core/world/modules/WaterModule.java b/OSM2World/src/org/osm2world/core/world/modules/WaterModule.java
index 40e66c8..2be92c1 100644
--- a/OSM2World/src/org/osm2world/core/world/modules/WaterModule.java
+++ b/OSM2World/src/org/osm2world/core/world/modules/WaterModule.java
@@ -45,6 +45,7 @@ public class WaterModule extends ConfigurableWorldModule {
//TODO: add canal, ditch, drain
+ private static final float MIN_WATER_WIDTH = 2.2f;
private static final Tag WATER_TAG = new Tag("natural", "water");
private static final Tag RIVERBANK_TAG = new Tag("waterway", "riverbank");
@@ -52,11 +53,11 @@ public class WaterModule extends ConfigurableWorldModule {
static {
WATERWAY_WIDTHS = new HashMap();
- WATERWAY_WIDTHS.put("river", 3f);
- WATERWAY_WIDTHS.put("stream", 0.5f);
- WATERWAY_WIDTHS.put("canal", 2f);
- WATERWAY_WIDTHS.put("ditch", 1f);
- WATERWAY_WIDTHS.put("drain", 1f);
+ WATERWAY_WIDTHS.put("river", MIN_WATER_WIDTH * 1.5f);
+ WATERWAY_WIDTHS.put("stream", MIN_WATER_WIDTH);
+ WATERWAY_WIDTHS.put("canal", MIN_WATER_WIDTH * 1.2f);
+ WATERWAY_WIDTHS.put("ditch", MIN_WATER_WIDTH);
+ WATERWAY_WIDTHS.put("drain", MIN_WATER_WIDTH);
}
//TODO: apply to is almost always the same! create a superclass handling this!
@@ -121,8 +122,10 @@ public class WaterModule extends ConfigurableWorldModule {
}
public float getWidth() {
- return WorldModuleParseUtil.parseWidth(segment.getTags(),
- WATERWAY_WIDTHS.get(segment.getTags().getValue("waterway")));
+ return 2 * Math.max(
+ 1,
+ WorldModuleParseUtil.parseWidth(segment.getTags(),
+ WATERWAY_WIDTHS.get(segment.getTags().getValue("waterway"))));
}
@Override
@@ -164,37 +167,37 @@ public class WaterModule extends ConfigurableWorldModule {
List rightWaterBorder = createLineBetween(
leftOutline, rightOutline, 0.95f);
- modifyLineHeight(leftWaterBorder, -0.2f);
- modifyLineHeight(rightWaterBorder, -0.2f);
-
- List leftGround = createLineBetween(
- leftOutline, rightOutline, 0.35f);
- List rightGround = createLineBetween(
- leftOutline, rightOutline, 0.65f);
-
- modifyLineHeight(leftGround, -1);
- modifyLineHeight(rightGround, -1);
-
- /* render ground */
-
- @SuppressWarnings("unchecked") // generic vararg is intentional
- List> strips = asList(
- createTriangleStripBetween(
- leftOutline, leftWaterBorder),
- createTriangleStripBetween(
- leftWaterBorder, leftGround),
- createTriangleStripBetween(
- leftGround, rightGround),
- createTriangleStripBetween(
- rightGround, rightWaterBorder),
- createTriangleStripBetween(
- rightWaterBorder, rightOutline)
- );
-
- for (List strip : strips) {
- target.drawTriangleStrip(TERRAIN_DEFAULT, strip,
- texCoordLists(strip, TERRAIN_DEFAULT, GLOBAL_X_Z));
- }
+// modifyLineHeight(leftWaterBorder, -0.2f);
+// modifyLineHeight(rightWaterBorder, -0.2f);
+//
+// List leftGround = createLineBetween(
+// leftOutline, rightOutline, 0.35f);
+// List rightGround = createLineBetween(
+// leftOutline, rightOutline, 0.65f);
+//
+// modifyLineHeight(leftGround, -1);
+// modifyLineHeight(rightGround, -1);
+//
+// /* render ground */
+//
+// @SuppressWarnings("unchecked") // generic vararg is intentional
+// List> strips = asList(
+// createTriangleStripBetween(
+// leftOutline, leftWaterBorder),
+// createTriangleStripBetween(
+// leftWaterBorder, leftGround),
+// createTriangleStripBetween(
+// leftGround, rightGround),
+// createTriangleStripBetween(
+// rightGround, rightWaterBorder),
+// createTriangleStripBetween(
+// rightWaterBorder, rightOutline)
+// );
+//
+// for (List strip : strips) {
+// target.drawTriangleStrip(TERRAIN_DEFAULT, strip,
+// texCoordLists(strip, TERRAIN_DEFAULT, GLOBAL_X_Z));
+// }
/* render water */
================================================
FILE: OSM2World/resources/osm2world_definitions.inc
================================================
/*
* This is an example for a POVRay include file that can be used to configure the rendering
* of .pov files generated with OSM2World (http://osm2world.org/).
*
* You can leave out any definition from this file, the rendering will then fall back
* to (mostly very basic) default materials and shapes.
* Please be aware that many possible definitions are not listed in this example file.
* Examine the "material and object definitions" section of a .pov file generated
* by OSM2World to see what definitions are used by that particular scene.
*/
#include "metals.inc"
#include "woods.inc"
#include "textures.inc"
#include "colors.inc"
// chooses the season - possible values: spring, summer, autumn, winter
// (has no effect on the generated scenes yet, could be used in custom model declarations)
#declare season="summer"
// chooses the time of day - possible values: day, night
// (has no effect on the generated scenes yet, could be used in custom model declarations)
#declare time="day"
// activate this #include if you have downloaded the tree models
// that are available from http://osm2world.org/download (optional)
// #include "osm2world_trees.inc"
// texture definitions
#declare texture_ASPHALT=texture { pigment { color rgb <0.2, 0.2, 0.2> } }
#declare texture_WATER=texture { T_Chrome_2D normal { waves 0.2 frequency 1000.0 scale 4.0 turbulence 0.1 rotate <0,-45,0> } }
#declare texture_WOOD=texture { T_Wood5 }
#declare texture_TERRAIN_DEFAULT=texture { pigment { color rgb <0.2, 0.73, 0.2> } finish { ambient 0.5 diffuse 0.5 } }
// sky sphere definition
// activating this creates a pure blue sky.
// This is useful when generating tiles. With patterned skies,
// there will often be visible tile borders in reflecting water.
//#declare sky_sphere_def = sky_sphere { pigment { rgb <0.0, 0.0, 1.0> } }
// activating this creates reflective windows
// However, you need a material map which is a indexed png with 3 colors:
// Color 1 is for the transparent part, color 2 is for the window glass, color 3
// is for the rest of the window, i.e. the window frame
//#declare texture_BUILDING_WINDOWS = texture {
// material_map {
// png "./textures/transparent.png"
// texture { pigment { color rgb<0,0,0> transmit 1 } }
// texture {
// pigment { image_map {
// png "./textures/MarekCompositeWall00001_transparent.png"
// } }
// finish {
// ambient 0.5
// diffuse 0.5
// phong 1.0
// phong_size 150
// reflection 0.5
// }
// }
// texture {
// pigment { image_map {
// png "./textures/MarekCompositeWall00001_transparent.png"
// } }
// finish {
// ambient 0.5
// diffuse 0.5
// }
// }
// } }
================================================
FILE: OSM2World/resources/shaders/background.fragment
================================================
/* Simple fragment shader for drawing a 2d geometry with texture to screen
*/
#version 130
uniform mat4 ModelViewMatrix;
// corresponds with output from vertex shader, gets interpolated
in vec2 TexCoord;
// output to buffer
out vec4 FragColor;
uniform sampler2D Tex;
void main()
{
FragColor = texture2D(Tex, TexCoord);
}
================================================
FILE: OSM2World/resources/shaders/background.vertex
================================================
/* Simple vertex shader for drawing a 2d geometry with texture to screen
*/
#version 130
// per vertex input
in vec2 VertexPosition;
in vec2 VertexTexCoord;
// input for at least primitives
uniform mat4 ModelViewProjectionMatrix;
// output to fragment shader for interpolation
out vec2 TexCoord;
void main()
{
TexCoord = VertexTexCoord;
gl_Position = ModelViewProjectionMatrix * vec4(VertexPosition.x, VertexPosition.y, 0.0, 1.0);
}
================================================
FILE: OSM2World/resources/shaders/default.fragment
================================================
/* Fragment shader with a per fragment phong lighting model, bumpmaps/normalmaps, shadowmaps and SSAO.
*/
#version 130
// TODO: set from java to match MAX_TEXTURE_LAYERS there
#define MAX_TEXTURE_LAYERS 4
#define SSAO_RANDOM_ROTATION
uniform bool isShadowed;
uniform bool useLighting;
struct LightInfo {
vec4 Position; // Light position in world coords.
vec3 La; // Ambient light intensity
vec3 Ld; // Diffuse light intensity
vec3 Ls; // Specular light intensity
};
uniform LightInfo Light;
struct MaterialInfo {
vec3 Color; // Material color
float Ka; // Ambient reflectivity
float Kd; // Diffuse reflectivity
float Ks; // Specular reflectivity
int Shininess; // Specular shininess factor
};
uniform MaterialInfo Material;
uniform mat4 ModelViewMatrix;
uniform mat4 ProjectionMatrix;
// corresponds with output from vertex shader, gets interpolated
in vec3 VertexEye;
in vec3 NormalEye;
in vec4 TangentEye;
in vec2 BumpMapCoord;
in vec4 ShadowCoord;
in vec2 TexCoord0;
in vec2 TexCoord1;
in vec2 TexCoord2;
in vec2 TexCoord3;
uniform bool useTexture[MAX_TEXTURE_LAYERS];
uniform sampler2D Tex[MAX_TEXTURE_LAYERS];
uniform sampler2D BumpMap;
uniform bool useShadowMap;
uniform sampler2DShadow ShadowMap;
uniform bool useSSAO;
uniform sampler2D DepthMap;
uniform sampler2D NoiseTex;
uniform vec2 uNoiseScale;
const int MAX_KERNEL_SIZE = 128;
const float SSAO_SAMPLE_THRESHOLD = 0.15;
uniform int uKernelSize;
uniform vec3 uKernelOffsets[MAX_KERNEL_SIZE];
uniform float uRadius = 1;
uniform bool useAlphaTreshold;
uniform float alphaTreshold;
uniform bool useBumpMaps;
// output to buffer
out vec4 FragColor;
/*
* shading with phong model (ambient, diffuse, specular)
* lightDir, viewDir and norm have to be in the same coordinate system (e.g. eye or tangent space)
*/
vec3 phongModel( vec3 lightDir, vec3 viewDir, vec3 norm, out vec3 ambient, out vec3 diffuse, out vec3 spec )
{
vec3 r = reflect( -lightDir, norm );
ambient = Light.La;
float sDotN = max( dot(lightDir,norm), 0.0 );
diffuse = Light.Ld * sDotN;
spec = vec3(0.0);
if( sDotN > 0.0 )
spec = Light.Ls *
pow( max( dot(r,viewDir), 0.0 ), Material.Shininess );
return ambient + diffuse + spec;
}
const int levels = 3;
const float scaleFactor = 1.0 / levels;
// toon shading (only ambient and diffuse)
vec3 toonShade( vec3 lightDir, vec3 norm, out vec3 ambient, out vec3 diffuse )
{
ambient = Light.La;
float cosine = max( dot(lightDir,norm) , 0.0 );
diffuse = Light.Ld * floor( cosine * levels ) * scaleFactor;
return ambient + diffuse;
}
//float linearizeDepthPerspective(in float depth, in mat4 projMatrix) {
// return -projMatrix[3][2] / ((depth*2-1.0) + projMatrix[2][2]);
//}
float linearizeDepth(in float depth, in mat4 projMatrix) {
float zNDC = depth*2-1.0;
return -(zNDC * projMatrix[3][3] - projMatrix[3][2]) / (zNDC * projMatrix[2][3] - projMatrix[2][2]);
}
float ssao( vec3 origin, vec3 normal, out vec3 debug )
{
/*
vec3 rvec = texture(uTexRandom, vTexcoord * uNoiseScale).xyz * 2.0 - 1.0;
vec3 tangent = normalize(rvec - normal * dot(rvec, normal));
vec3 bitangent = cross(normal, tangent);
mat3 tbn = mat3(tangent, bitangent, normal);
*/
#ifdef SSAO_RANDOM_ROTATION
//vec3 rvec = texture(NoiseTex, gl_FragCoord.xy * uNoiseScale).xyz;
vec3 rvec = texture(NoiseTex, gl_FragCoord.xy/4.0).xyz;
//vec3 rvec = vec3(gl_FragCoord.xy, 0.0);
#else
vec3 rvec = vec3(1.0, 0.0, 0.0);
#endif
vec3 tangent = normalize(rvec - normal * dot(rvec, normal));
//vec3 c1 = cross(normal, vec3(0.0, 0.0, 1.0));
//vec3 c2 = cross(normal, vec3(0.0, 1.0, 0.0));
//tangent = normalize(length(c1) > length(c2) ? c1 : c2);
vec3 bitangent = cross(normal, tangent);
mat3 tbn = mat3(tangent, bitangent, normal);
float occlusion = 0.0;
int skipped = 0;
// performance improvement: loop could be unrolled with constant kernel size
for (int i = 0; i < uKernelSize; ++i)
{
// get sample position:
vec3 sample = tbn * (uKernelOffsets[i]);
//vec3 sample = uKernelOffsets[i];
/*if ( dot(sample, normal) < SSAO_SAMPLE_THRESHOLD )
{
skipped += 1;
continue;
}*/
sample = sample * uRadius + origin;
// project sample position:
vec4 offset = vec4(sample, 1.0);
offset = ProjectionMatrix * offset;
offset.xy /= offset.w;
offset.xy = offset.xy * 0.5 + 0.5;
// skip samples outside of depth map texture
if ( offset.x < 0 || offset.y < 0 || offset.x > 1 || offset.y > 1 )
{
skipped += 1;
continue;
}
// get sample depth:
float sampleDepth = texture(DepthMap, offset.xy).r;
sampleDepth = linearizeDepth(sampleDepth, ProjectionMatrix);
// range check & accumulate:
//float rangeCheck= abs(origin.z - sampleDepth) < uRadius ? 1.0 : 0.0;
//occlusion += (sampleDepth <= sample.z ? 1.0 : 0.0) * rangeCheck;
float rangeCheck = smoothstep(0.0, 1.0, uRadius / abs(origin.z - sampleDepth));
occlusion += rangeCheck * step(sample.z, sampleDepth);
}
//debug = tbn * vec3(0.0, 0.0, 1.0);
//debug = vec3(skipped/float(255));
debug = rvec;
/*vec4 test = ProjectionMatrix * vec4(origin, 1.0);
test.xy /= test.w;
test.xy = test.xy * 0.5 + 0.5;
float d = texture(DepthMap, test.xy).r;
d = linearizeDepth(d, ProjectionMatrix);
return abs(origin.z - d);
return origin.z - linearizeDepth(((test.z / test.w)*0.5 + 0.5), ProjectionMatrix);
//return 1.0 - abs(((test.z / test.w)*0.5 + 0.5) - d);
*/
// normalize and invert
if ( uKernelSize > skipped )
occlusion = 1.0 - (occlusion / float(uKernelSize-skipped));
else
occlusion = 1.0;
//occlusion = occlusion / float(uKernelSize);
return occlusion;
}
void main()
{
vec3 ambient, diff, spec=vec3(0.0);
vec4 texColor = vec4(1.0);
// apply textures
// TODO: autogenerate based on MAX_TEXTURE_LAYERS
if ( useTexture[0] ) {
texColor = texture( Tex[0], TexCoord0 );
}
texColor = texColor * vec4(Material.Color, 1.0); // color first layer only
if ( useTexture[1] ) {
vec4 layerColor = texture( Tex[1], TexCoord1 );
texColor = mix(texColor, vec4(layerColor.rgb,1), layerColor.a);
}
if ( useTexture[2] ) {
vec4 layerColor = texture( Tex[2], TexCoord2 );
texColor = mix(texColor, vec4(layerColor.rgb,1), layerColor.a);
}
if ( useTexture[3] ) {
vec4 layerColor = texture( Tex[3], TexCoord3 );
texColor = mix(texColor, vec4(layerColor.rgb,1), layerColor.a);
}
if ( useLighting ) {
vec3 normalEye = normalize(NormalEye);
float occlusionFactor = 1.0;
vec3 debug = vec3(0.0);
if ( useSSAO )
occlusionFactor = ssao( VertexEye, normalEye, debug );
if ( isShadowed ) {
ambient = Light.La;
diff = vec3(0.0);
} else {
vec3 viewDirection, lightDirection;
vec4 lightPositionEye = ModelViewMatrix * Light.Position;
// transform light and view direction to eye space
if( ProjectionMatrix[3][3] == 0.0 )
viewDirection = normalize(-VertexEye); // perspective projection
else
viewDirection = vec3(0.0, 0.0, 1.0); // orthographic projection: camera is infinite far away, so vertex coordinates don't matter
// directional light?
if( Light.Position.w == 0.0 )
lightDirection = normalize(vec3(lightPositionEye));
else
lightDirection = normalize(vec3(lightPositionEye) - VertexEye);
vec3 normal = normalEye;
if ( useBumpMaps ) {
vec3 tangentEye = normalize(TangentEye.xyz);
// compute bitangent
vec3 bitangentEye = normalize( cross( normalEye, tangentEye ) ) * TangentEye.w;
// transformation from eye to tangent space
mat3 tangentMatrix = mat3(
tangentEye.x, bitangentEye.x, normalEye.x,
tangentEye.y, bitangentEye.y, normalEye.y,
tangentEye.z, bitangentEye.z, normalEye.z );
// transform light and view direction to tangent space
viewDirection = tangentMatrix * viewDirection;
lightDirection = tangentMatrix * lightDirection;
normal = normalize(texture( BumpMap, BumpMapCoord ).xyz * 2.0 - 1.0);
}
phongModel( lightDirection, viewDirection, normal, ambient, diff, spec );
//toonShade( lightDirection, normal, ambient, diff );
}
// shadow map lookup
float shadowFactor = 1.0;
if ( useShadowMap ) {
//vec3 shadowCoordCart = ShadowCoord.xyz / ShadowCoord.w; // from homogene coordiantes to carthesian
//float shadowDepthValue = texture(ShadowMap, shadowCoordCart.xy).r; // lookup of depth value in shadow map
//float fragmentDepthValue = shadowCoordCart.z; // fragment depth value to compare
//if (shadowDepthValue < fragmentDepthValue)
// shadowFactor = 0.0; // in shadow
// PCF (percentage closer filtering): blur with adjacent shadow map comparison results
float sum = 0 ;
// Sum contributions from texels around ShadowCoord
sum += textureProjOffset(ShadowMap, ShadowCoord, ivec2(-1,-1));
sum += textureProjOffset(ShadowMap, ShadowCoord, ivec2(-1,1));
sum += textureProjOffset(ShadowMap, ShadowCoord, ivec2(1,1));
sum += textureProjOffset(ShadowMap, ShadowCoord, ivec2(1,-1));
shadowFactor = sum * 0.25;
}
if ( isShadowed )
shadowFactor = 0.0;
diff = diff * shadowFactor * occlusionFactor;
spec = spec * shadowFactor * occlusionFactor;
ambient = ambient * occlusionFactor;
FragColor = vec4(ambient*Material.Ka + diff*Material.Kd, 1.0) * texColor + vec4(spec*Material.Ks, 0.0);
//FragColor = texColor;
//FragColor = vec4(occlusionFactor, occlusionFactor, occlusionFactor, FragColor.a);
//FragColor = vec4(normalEye, FragColor.a);
//FragColor = vec4(debug, FragColor.a);
} else {
FragColor = texColor;
}
if ( useAlphaTreshold ) {
if ( FragColor.a < alphaTreshold )
FragColor.a = 0.0;
else
FragColor.a = 1.0;
}
if( FragColor.a < 0.01 )
discard;
}
================================================
FILE: OSM2World/resources/shaders/default.vertex
================================================
/* Vertex shader with support for bumpmaps/normalmaps, shadowmaps and phong lighting model
*/
#version 130
// TODO: set from java to match MAX_TEXTURE_LAYERS there
#define MAX_TEXTURE_LAYERS 4
// per vertex input
in vec3 VertexPosition;
in vec3 VertexNormal;
in vec4 VertexTangent;
in vec2 VertexBumpMapCoord;
in vec2 VertexTexCoord0;
in vec2 VertexTexCoord1;
in vec2 VertexTexCoord2;
in vec2 VertexTexCoord3;
// input for at least primitives
uniform mat4 ProjectionMatrix;
uniform mat4 ModelViewMatrix;
uniform mat4 ModelViewProjectionMatrix;
uniform mat4 NormalMatrix;
uniform mat4 ShadowMatrix;
// output to fragment shader for interpolation
out vec3 VertexEye; // in eye space
out vec3 NormalEye;
out vec4 TangentEye; // last coordinate is the handedness to reconstruct the bitangent
out vec2 BumpMapCoord;
out vec4 ShadowCoord;
out vec2 TexCoord0;
out vec2 TexCoord1;
out vec2 TexCoord2;
out vec2 TexCoord3;
void main()
{
TexCoord0 = VertexTexCoord0;
TexCoord1 = VertexTexCoord1;
TexCoord2 = VertexTexCoord2;
TexCoord3 = VertexTexCoord3;
BumpMapCoord = VertexBumpMapCoord;
// Transform to eye coordinates
NormalEye = normalize(vec3(NormalMatrix * vec4(VertexNormal, 1.0)));
TangentEye = vec4(normalize(vec3(NormalMatrix * vec4(VertexTangent.xyz, 1.0))), VertexTangent.w);
vec4 veye = ModelViewMatrix * vec4(VertexPosition, 1.0);
VertexEye = veye.xyz / veye.w;
// Transform to shadow coordinates
ShadowCoord = ShadowMatrix * vec4(VertexPosition, 1.0);
gl_Position = ModelViewProjectionMatrix * vec4(VertexPosition, 1.0);
}
================================================
FILE: OSM2World/resources/shaders/nonarea.fragment
================================================
/* Simple fragment shader with static color, no lighting, no textures
*/
#version 130
// corresponds with output from vertex shader, gets interpolated
in vec4 Color;
// output to buffer
out vec4 FragColor;
void main()
{
FragColor = Color;
}
================================================
FILE: OSM2World/resources/shaders/nonarea.vertex
================================================
/* Simple vertex shader with static color, no lighting, no textures
*/
#version 130
// per vertex input
in vec3 VertexPosition;
in vec4 VertexColor;
// input for at least primitives
uniform mat4 ModelViewProjectionMatrix;
// output to fragment shader for interpolation
out vec4 Color;
void main()
{
Color = VertexColor;
gl_Position = ModelViewProjectionMatrix * vec4(VertexPosition, 1.0);
}
================================================
FILE: OSM2World/resources/shaders/shadowmap.fragment
================================================
/* Simple fragment shader with texture layers for depth buffer calculation
*/
#version 130
// TODO: set from java to match MAX_TEXTURE_LAYERS there
#define MAX_TEXTURE_LAYERS 4
// corresponds with output from vertex shader, gets interpolated
in vec2 TexCoord0;
in vec2 TexCoord1;
in vec2 TexCoord2;
in vec2 TexCoord3;
uniform bool useTexture[MAX_TEXTURE_LAYERS];
uniform sampler2D Tex[MAX_TEXTURE_LAYERS];
uniform bool useAlphaTreshold;
uniform float alphaTreshold;
void main()
{
float alpha = 1.0;
// apply textures
// TODO: autogenerate based on MAX_TEXTURE_LAYERS
if ( useTexture[0] ) {
alpha = texture( Tex[0], TexCoord0 ).a;
}
if ( useTexture[1] ) {
float layerAlpha = texture( Tex[1], TexCoord1 ).a;
alpha = mix(alpha, 1, layerAlpha);
}
if ( useTexture[2] ) {
float layerAlpha = texture( Tex[2], TexCoord2 ).a;
alpha = mix(alpha, 1, layerAlpha);
}
if ( useTexture[3] ) {
float layerAlpha = texture( Tex[3], TexCoord3 ).a;
alpha = mix(alpha, 1, layerAlpha);
}
if ( useAlphaTreshold ) {
if ( alpha < alphaTreshold )
alpha = 0.0;
else
alpha = 1.0;
}
if( alpha < 0.01 )
discard;
// nothing to output, only depth buffer needed
}
================================================
FILE: OSM2World/resources/shaders/shadowmap.vertex
================================================
/* Simple vertex shader with vertex position texture layers computation
*/
#version 130
// per vertex input
in vec3 VertexPosition;
in vec2 VertexTexCoord0;
in vec2 VertexTexCoord1;
in vec2 VertexTexCoord2;
in vec2 VertexTexCoord3;
// input for at least primitives
uniform mat4 ModelViewProjectionMatrix;
// output to fragment shader for interpolation
out vec2 TexCoord0;
out vec2 TexCoord1;
out vec2 TexCoord2;
out vec2 TexCoord3;
void main()
{
TexCoord0 = VertexTexCoord0;
TexCoord1 = VertexTexCoord1;
TexCoord2 = VertexTexCoord2;
TexCoord3 = VertexTexCoord3;
gl_Position = ModelViewProjectionMatrix * vec4(VertexPosition, 1.0);
}
================================================
FILE: OSM2World/resources/shaders/shadowvolume.fragment
================================================
/* Empty fragment shader. Only depth buffer will be calculated.
*/
#version 130
void main()
{
// nothing to output, only depth buffer needed
}
================================================
FILE: OSM2World/resources/shaders/shadowvolume.vertex
================================================
/* Simple vertex shader with vertex position
*/
#version 130
// per vertex input
in vec4 VertexPosition;
// input for at least primitives
uniform mat4 ModelViewProjectionMatrix;
void main()
{
gl_Position = ModelViewProjectionMatrix * VertexPosition;
}
================================================
FILE: OSM2World/src/org/openstreetmap/josm/plugins/graphview/core/data/DataSource.java
================================================
package org.openstreetmap.josm.plugins.graphview.core.data;
/**
* source of OSM data that can be used to build graphs from
*
* @param node type
* @param way type
* @param relation type
*/
public interface DataSource {
/** returns all nodes */
public Iterable getNodes();
/** returns all ways */
public Iterable getWays();
/** returns all relations */
public Iterable getRelations();
/** returns a node's latitude */
public double getLat(N node);
/** returns a node's longitude */
public double getLon(N node);
/** returns a way's nodes */
public Iterable getNodes(W way);
/** returns a relation's members */
public Iterable getMembers(R relation);
/** returns a node's tags */
public TagGroup getTagsN(N node);
/** returns a way's tags */
public TagGroup getTagsW(W way);
/** returns a relation's tags */
public TagGroup getTagsR(R relation);
/** returns a relation member's role */
public String getRole(M member);
/** returns a relation member's member object */
public Object getMember(M member);
/** returns whether a relation member is a node */
public boolean isNMember(M member);
/** returns whether a relation member is a way */
public boolean isWMember(M member);
/** returns whether a relation member is a relation */
public boolean isRMember(M member);
/**
* adds an observer.
* Does nothing if the parameter is already an observer of this DataSource.
*
* @param observer observer object, != null
*/
public void addObserver(DataSourceObserver observer);
/**
* deletes an observer that has been added using {@link #addObserver(DataSourceObserver)}.
* Does nothing if the parameter isn't currently an observer of this DataSource.
*
* @param observer observer object, != null
*/
public void deleteObserver(DataSourceObserver observer);
}
================================================
FILE: OSM2World/src/org/openstreetmap/josm/plugins/graphview/core/data/DataSourceObserver.java
================================================
package org.openstreetmap.josm.plugins.graphview.core.data;
/**
* observer that will be informed about changes in a DataSource
* if it has been registered using {@link DataSource#addObserver(DataSourceObserver)}.
* Not every DataSource will send updates, because some don't change.
*/
public interface DataSourceObserver {
/**
* informs this observer about changes in an observed data source
* @param dataSource observed data source that has changed; != null
*/
public void update(DataSource, ?, ?, ?> dataSource);
}
================================================
FILE: OSM2World/src/org/openstreetmap/josm/plugins/graphview/core/data/EmptyTagGroup.java
================================================
package org.openstreetmap.josm.plugins.graphview.core.data;
import java.util.Collections;
import java.util.Iterator;
public final class EmptyTagGroup implements TagGroup {
public static final EmptyTagGroup EMPTY_TAG_GROUP = new EmptyTagGroup();
private EmptyTagGroup() { }
@Override
public boolean contains(Tag tag) {
return false;
}
@Override
public boolean containsAny(Iterable tag) {
return false;
}
@Override
public boolean contains(String key, String value) {
return false;
}
@Override
public boolean containsAny(Iterable key, String value) {
return false;
}
@Override
public boolean containsAny(String key, Iterable values) {
return false;
}
@Override
public boolean containsAny(Iterable keys, Iterable values) {
return false;
}
@Override
public boolean containsAnyKey(Iterable keys) {
return false;
}
@Override
public boolean containsKey(String key) {
return false;
}
@Override
public boolean containsValue(String value) {
return false;
}
@Override
public boolean containsAnyValue(Iterable value) {
return false;
}
@Override
public String getValue(String key) {
return null;
}
@Override
public int size() {
return 0;
}
@Override
public boolean isEmpty() {
return true;
}
@Override
public Iterator iterator() {
return Collections.emptyList().iterator();
}
@Override
public String toString() {
return "{}";
}
}
================================================
FILE: OSM2World/src/org/openstreetmap/josm/plugins/graphview/core/data/MapBasedTagGroup.java
================================================
package org.openstreetmap.josm.plugins.graphview.core.data;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
/**
* TagGroup that uses a key-value-Map to store tags
*/
public class MapBasedTagGroup implements TagGroup {
private final Map tagMap;
/**
* @param tagMap map from keys to values; != null;
* must not be modified after being used as parameter
*/
public MapBasedTagGroup(Map tagMap) {
if (tagMap == null) {
throw new IllegalArgumentException();
}
this.tagMap = tagMap;
}
/**
* @param tags tags to add to the group; != null, each != null
*/
public MapBasedTagGroup(Iterable tags) {
if (tags == null) {
throw new IllegalArgumentException();
}
this.tagMap = new HashMap();
for (Tag tag : tags) {
if (tag == null) {
throw new IllegalArgumentException();
} else {
this.tagMap.put(tag.key, tag.value);
}
}
}
/**
* @param tags tags to add to the group; each != null
*/
public MapBasedTagGroup(Tag... tags) {
this.tagMap = new HashMap(tags.length);
for (Tag tag : tags) {
if (tag == null) {
throw new IllegalArgumentException();
} else {
this.tagMap.put(tag.key, tag.value);
}
}
}
@Override
public String getValue(String key) {
assert key != null;
return tagMap.get(key);
}
@Override
public boolean containsKey(String key) {
assert key != null;
return tagMap.containsKey(key);
}
@Override
public boolean containsAnyKey(Iterable keys) {
for (String key : keys) {
if (this.containsKey(key)) {
return true;
}
}
return false;
}
@Override
public boolean containsValue(String value) {
assert value != null;
return tagMap.containsValue(value);
}
@Override
public boolean containsAnyValue(Iterable values) {
for (String value : values) {
if (this.containsValue(value)) {
return true;
}
}
return false;
}
@Override
public boolean contains(Tag tag) {
assert tag != null;
return tag.value.equals(tagMap.get(tag.key));
}
@Override
public boolean containsAny(Iterable tags) {
for (Tag tag : tags) {
if (this.contains(tag)) {
return true;
}
}
return false;
}
@Override
public boolean contains(String key, String value) {
assert key != null;
assert value != null;
return value.equals(tagMap.get(key));
}
@Override
public boolean containsAny( Iterable keys, String value) {
for (String key : keys) {
if (this.contains(key, value)) {
return true;
}
}
return false;
}
@Override
public boolean containsAny(Iterable keys, Iterable values) {
for (String key : keys) {
if (this.containsAny(key, values)) {
return true;
}
}
return false;
}
@Override
public boolean containsAny(String key, Iterable values) {
for (String value : values) {
if (this.contains(key, value)) {
return true;
}
}
return false;
}
@Override
public int size() {
return tagMap.size();
}
@Override
public boolean isEmpty() {
return tagMap.isEmpty();
}
/**
* returns an Iterator providing access to all Tags.
* The Iterator does not support the {@link Iterator#remove()} method.
*/
@Override
public Iterator iterator() {
Collection tagCollection = new LinkedList();
for (String key : tagMap.keySet()) {
tagCollection.add(new Tag(key, tagMap.get(key)));
}
return Collections.unmodifiableCollection(tagCollection).iterator();
}
@Override
public String toString() {
return tagMap.toString();
}
}
================================================
FILE: OSM2World/src/org/openstreetmap/josm/plugins/graphview/core/data/Tag.java
================================================
package org.openstreetmap.josm.plugins.graphview.core.data;
/**
* immutable representation of an OSM tag (key-value-pair)
*/
public class Tag {
/** key of the tag; != null */
public final String key;
/** value of the tag; != null */
public final String value;
public Tag(String key, String value) {
assert key != null && value != null;
this.key = key;
this.value = value;
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof Tag)) {
return false;
} else {
Tag otherTag = (Tag)obj;
return key.equals(otherTag.key) && value.equals(otherTag.value);
}
}
@Override
public int hashCode() {
return key.hashCode() + value.hashCode(); //TODO: might be a less than optimal hash function
}
@Override
public String toString() {
return key + "=" + value;
}
}
================================================
FILE: OSM2World/src/org/openstreetmap/josm/plugins/graphview/core/data/TagGroup.java
================================================
package org.openstreetmap.josm.plugins.graphview.core.data;
/**
* represents a group of OSM tags (e.g. all tags of a way).
* TagGroups are expected to be immutable, so modifying the tags means creation of a new group.
* This interface requires that keys are unique, which is guaranteed since OSM API 0.6.
*/
public interface TagGroup extends Iterable {
/**
* returns the value for the given key or null if no tag in this group uses that key
* @param key key whose value will be returned; != null
*/
public String getValue(String key);
/**
* returns true if this tag group contains a tag with the given key
* @param key key to check for; != null
*/
public boolean containsKey(String key);
/**
* returns true if this tag group contains a tag with one of the given keys
* @param keys keys to check for; != null
*/
public boolean containsAnyKey(Iterable keys);
/**
* returns true if this tag group contains at least one tag with the given value
* @param value value to check for; != null
*/
public boolean containsValue(String value);
/**
* returns true if this tag group contains at least one tag with one of the given values
* @param values values to check for; != null
*/
public boolean containsAnyValue(Iterable values);
/**
* returns true if this tag group contains the given tag
* @param tag tag to check for; != null
*/
public boolean contains(Tag tag);
/**
* returns true if this tag group contains one of the given tags
* @param tags tags to check for; != null
*/
public boolean containsAny(Iterable tags);
/**
* returns true if this tag group contains the tag
* @param key key of the tag to check for; != null
* @param value value of the tag to check for; != null
*/
public boolean contains(String key, String value);
/**
* returns true if this tag group contains one of the keys
* with value
* @param keys keys of the tag to check for; != null
* @param value value of the tag to check for; != null
*/
public boolean containsAny(Iterable keys, String value);
/**
* returns true if this tag group contains one of the keys
* with one of the values
* @param keys keys of the tag to check for; != null
* @param values values of the tag to check for; != null
*/
public boolean containsAny(Iterable keys, Iterable values);
/**
* returns true if this tag group contains the key with
* one of the values
* @param key key of the tag to check for; != null
* @param values values of the tag to check for; != null
*/
public boolean containsAny(String key, Iterable values);
/**
* returns the number of tags in this group
*/
public int size();
/**
* returns true if this group contains any tags
*/
public boolean isEmpty();
}
================================================
FILE: OSM2World/src/org/openstreetmap/josm/plugins/graphview/core/data/osmosis/OSMFileDataSource.java
================================================
package org.openstreetmap.josm.plugins.graphview.core.data.osmosis;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.openstreetmap.josm.plugins.graphview.core.data.DataSource;
import org.openstreetmap.josm.plugins.graphview.core.data.DataSourceObserver;
import org.openstreetmap.josm.plugins.graphview.core.data.MapBasedTagGroup;
import org.openstreetmap.josm.plugins.graphview.core.data.TagGroup;
import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
import org.openstreetmap.osmosis.core.domain.v0_6.Entity;
import org.openstreetmap.osmosis.core.domain.v0_6.EntityType;
import org.openstreetmap.osmosis.core.domain.v0_6.Node;
import org.openstreetmap.osmosis.core.domain.v0_6.Relation;
import org.openstreetmap.osmosis.core.domain.v0_6.Tag;
import org.openstreetmap.osmosis.core.domain.v0_6.Way;
import org.openstreetmap.osmosis.core.domain.v0_6.WayNode;
import org.openstreetmap.osmosis.core.task.v0_6.Sink;
import org.openstreetmap.osmosis.xml.common.CompressionMethod;
import org.openstreetmap.osmosis.xml.v0_6.XmlReader;
/**
* DataSource providing information from a single .osm file. The file is read
* during the constructor call, there will be no updates when the file is
* changed later. This class uses osmosis to read the file.
*/
public class OSMFileDataSource implements
DataSource {
private static final boolean useDebugLabels = true;
private boolean complete = false;
private synchronized boolean isComplete() {
return complete;
}
private synchronized void setCompleteTrue() {
this.complete = true;
}
private Map nodesById = new HashMap();
private Map waysById = new HashMap();
private Map relationsById = new HashMap();
private Collection ownNodes;
private Collection ownWays;
private Collection ownRelations;
private final Sink sinkImplementation = new Sink() {
public void initialize(Map arg0) {
/* do nothing */
}
public void release() {
/* do nothing */
}
public void complete() {
setCompleteTrue();
}
public void process(EntityContainer entityContainer) {
Entity entity = entityContainer.getEntity();
if (entity instanceof Node) {
nodesById.put(entity.getId(), ((Node) entity));
} else if (entity instanceof Way) {
waysById.put(entity.getId(), ((Way) entity));
} else if (entity instanceof Relation) {
relationsById.put(entity.getId(), ((Relation) entity));
}
}
};
public OSMFileDataSource(File file) throws IOException {
XmlReader reader = new XmlReader(file, false, CompressionMethod.None);
reader.setSink(sinkImplementation);
Thread readerThread = new Thread(reader);
readerThread.start();
while (readerThread.isAlive()) {
try {
readerThread.join();
} catch (InterruptedException e) { /* do nothing */
}
}
if (!isComplete()) {
throw new IOException("couldn't read from file");
}
convertToOwnRepresentation();
}
private void convertToOwnRepresentation() {
ownNodes = new ArrayList(nodesById.size());
ownWays = new ArrayList(waysById.size());
ownRelations = new ArrayList(relationsById.size());
Map nodeMap = new HashMap();
Map wayMap = new HashMap();
Map relationMap = new HashMap();
for (Node node : nodesById.values()) {
OwnNode ownNode = new OwnNode(node.getLatitude(), node
.getLongitude(), tagGroupForEntity(node));
ownNodes.add(ownNode);
nodeMap.put(node, ownNode);
}
for (Way way : waysById.values()) {
List origWayNodes = way.getWayNodes();
List wayNodes = new ArrayList(origWayNodes.size());
for (WayNode origWayNode : origWayNodes) {
Node origNode = nodesById.get(origWayNode.getNodeId());
wayNodes.add(nodeMap.get(origNode));
}
OwnWay ownWay = new OwnWay(tagGroupForEntity(way), wayNodes);
ownWays.add(ownWay);
wayMap.put(way, ownWay);
}
for (Relation relation : relationsById.values()) {
OwnRelation ownRelation = new OwnRelation(
tagGroupForEntity(relation), relation.getMembers().size());
ownRelations.add(ownRelation);
relationMap.put(relation, ownRelation);
}
// add relation members
// (needs to be done *after* creation because relations can be members
// of other relations)
for (Relation relation : relationMap.keySet()) {
OwnRelation ownRelation = relationMap.get(relation);
for (org.openstreetmap.osmosis.core.domain.v0_6.RelationMember member : relation
.getMembers()) {
Object memberObject = null;
if (member.getMemberType() == EntityType.Node) {
memberObject = nodeMap.get(nodesById.get(member
.getMemberId()));
} else if (member.getMemberType() == EntityType.Way) {
memberObject = wayMap.get(waysById
.get(member.getMemberId()));
} else if (member.getMemberType() == EntityType.Relation) {
memberObject = relationMap.get(relationsById.get(member
.getMemberId()));
} else {
continue;
}
OwnMember ownMember = new OwnMember(member
.getMemberRole(), memberObject);
ownRelation.relationMembers.add(ownMember);
}
}
// give up references to original collections
nodesById = null;
waysById = null;
relationsById = null;
}
private TagGroup tagGroupForEntity(Entity entity) {
Map tagMap = new HashMap(entity.getTags().size());
for (Tag tag : entity.getTags()) {
tagMap.put(tag.getKey(), tag.getValue());
}
return new MapBasedTagGroup(tagMap);
}
public void addObserver(DataSourceObserver observer) {
// OSMFileDataSource doesn't check for updates
}
public void deleteObserver(DataSourceObserver observer) {
// OSMFileDataSource doesn't check for updates
}
public double getLat(OwnNode node) {
return node.lat;
}
public double getLon(OwnNode node) {
return node.lon;
}
public List getMembers(OwnRelation relation) {
return relation.relationMembers;
}
public Collection getNodes() {
return ownNodes;
}
public Collection getWays() {
return ownWays;
}
public Collection getRelations() {
return ownRelations;
}
public List getNodes(OwnWay way) {
return way.nodes;
}
public TagGroup getTagsN(OwnNode node) {
return node.tags;
}
public TagGroup getTagsR(OwnRelation relation) {
return relation.tags;
}
public TagGroup getTagsW(OwnWay way) {
return way.tags;
}
public Object getMember(OwnMember member) {
return member.member;
}
public String getRole(OwnMember member) {
return member.role;
}
public boolean isNMember(OwnMember member) {
return member.member instanceof OwnNode;
};
public boolean isWMember(OwnMember member) {
return member.member instanceof OwnWay;
};
public boolean isRMember(OwnMember member) {
return member.member instanceof OwnRelation;
};
public class OwnNode {
private final double lat;
private final double lon;
private final TagGroup tags;
public OwnNode(double lat, double lon, TagGroup tags) {
this.lat = lat;
this.lon = lon;
this.tags = tags;
}
@Override
public String toString() {
if (useDebugLabels && tags.containsKey("debug:label")) {
return tags.getValue("debug:label");
}
return "(" + lat + ", " + lon + ", " + tags + ")";
}
}
public class OwnWay {
private final TagGroup tags;
private final List nodes;
public OwnWay(TagGroup tags, List nodes) {
this.tags = tags;
this.nodes = nodes;
}
@Override
public String toString() {
if (useDebugLabels && tags.containsKey("debug:label")) {
return tags.getValue("debug:label");
}
return nodes.get(0)
+ "->[" + (nodes.size() - 2) + "]->"
+ nodes.get(nodes.size()-1);
}
}
public class OwnRelation {
private final TagGroup tags;
private final List relationMembers;
// content added after constructor call
public OwnRelation(TagGroup tags, int initialMemberSize) {
this.tags = tags;
this.relationMembers =
new ArrayList(initialMemberSize);
}
}
public static class OwnMember {
private final String role;
private final Object member;
public OwnMember(String role, Object member) {
this.role = role;
this.member = member;
}
}
}
================================================
FILE: OSM2World/src/org/openstreetmap/josm/plugins/graphview/core/util/ValueStringParser.java
================================================
package org.openstreetmap.josm.plugins.graphview.core.util;
import java.awt.Color;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public final class ValueStringParser {
/** prevents instantiation */
private ValueStringParser() { }
/** pattern that splits into a part before and after a decimal point */
private static final Pattern DEC_POINT_PATTERN = Pattern.compile("^(\\-?\\d+)\\.(\\d+)$");
public static final Float parseOsmDecimal(String value, boolean allowNegative) {
/* positive integer */
try {
int weight = Integer.parseInt(value);
if (weight >= 0 || allowNegative) {
return (float)weight;
}
} catch (NumberFormatException nfe) {}
/* positive number with decimal point */
Matcher matcher = DEC_POINT_PATTERN.matcher(value);
if (matcher.matches()) {
String stringBeforePoint = matcher.group(1);
String stringAfterPoint = matcher.group(2);
if (stringBeforePoint.length() > 0 || stringAfterPoint.length() > 0) {
try {
boolean negative = stringBeforePoint.startsWith("-");
float beforePoint = Integer.parseInt(stringBeforePoint);
float afterPoint = Integer.parseInt(stringAfterPoint);
double result = Math.abs(beforePoint)
+ Math.pow(10, -stringAfterPoint.length()) * afterPoint;
if (negative) { result = - result; }
if (result >= 0 || allowNegative) {
return (float)result;
}
} catch (NumberFormatException nfe) {}
}
}
return null;
}
private static final Pattern KMH_PATTERN = Pattern.compile("^(\\d+)\\s*km/h$");
private static final Pattern MPH_PATTERN = Pattern.compile("^(\\d+)\\s*mph$");
private static final float KM_PER_MILE = 1.609344f;
/**
* parses a speed value given e.g. for the "maxspeed" key.
*
* @return speed in km/h; null if value had syntax errors
*/
public static final Float parseSpeed(String value) {
/* try numeric speed (implied km/h) */
Float speed = parseOsmDecimal(value, false);
if (speed != null) {
return speed;
}
/* try km/h speed */
Matcher kmhMatcher = KMH_PATTERN.matcher(value);
if (kmhMatcher.matches()) {
String kmhString = kmhMatcher.group(1);
try {
return (float)Integer.parseInt(kmhString);
} catch (NumberFormatException nfe) {}
}
/* try mph speed */
Matcher mphMatcher = MPH_PATTERN.matcher(value);
if (mphMatcher.matches()) {
String mphString = mphMatcher.group(1);
try {
int mph = Integer.parseInt(mphString);
return KM_PER_MILE * mph;
} catch (NumberFormatException nfe) {}
}
/* all possibilities failed */
return null;
}
private static final Pattern M_PATTERN = Pattern.compile("^([\\d\\.]+)\\s*m$");
private static final Pattern KM_PATTERN = Pattern.compile("^([\\d\\.]+)\\s*km$");
private static final Pattern MI_PATTERN = Pattern.compile("^([\\d\\.]+)\\s*mi$");
private static final Pattern FEET_INCHES_PATTERN = Pattern.compile("^([\\d]+)'\\s*([\\d]+)\"");
private static final double M_PER_MI = 1609.344;
private static final double M_PER_INCH = 0.0254f;
/**
* parses a measure value given e.g. for the "width" or "length" key.
*
* @return measure in m; null if value had syntax errors
*/
public static final Float parseMeasure(String value) {
/* try numeric measure (implied m) */
Float measure = parseOsmDecimal(value, false);
if (measure != null) {
return measure;
}
/* try m measure */
Matcher mMatcher = M_PATTERN.matcher(value);
if (mMatcher.matches()) {
String mString = mMatcher.group(1);
return parseOsmDecimal(mString, false);
}
/* try km measure */
Matcher kmMatcher = KM_PATTERN.matcher(value);
if (kmMatcher.matches()) {
String kmString = kmMatcher.group(1);
float km = parseOsmDecimal(kmString, false);
return 1000 * km;
}
/* try mi measure */
Matcher miMatcher = MI_PATTERN.matcher(value);
if (miMatcher.matches()) {
String miString = miMatcher.group(1);
float mi = parseOsmDecimal(miString, false);
return (float)(M_PER_MI * mi);
}
/* try feet/inches measure */
Matcher feetInchesMatcher = FEET_INCHES_PATTERN.matcher(value);
if (feetInchesMatcher.matches()) {
String feetString = feetInchesMatcher.group(1);
String inchesString = feetInchesMatcher.group(2);
try {
int feet = Integer.parseInt(feetString);
int inches = Integer.parseInt(inchesString);
if (feet >= 0 && inches >= 0 && inches < 12) {
return (float)(M_PER_INCH * (12 * feet + inches));
}
} catch (NumberFormatException nfe) {}
}
/* all possibilities failed */
return null;
}
private static final Pattern T_PATTERN = Pattern.compile("^([\\d\\.]+)\\s*t$");
/**
* parses a weight value given e.g. for the "maxweight" or "maxaxleload" key.
*
* @return weight in t; null if value had syntax errors
*/
public static Float parseWeight(String value) {
/* try numeric weight (implied t) */
Float weight = parseOsmDecimal(value, false);
if (weight != null) {
return weight;
}
/* try t weight */
Matcher tMatcher = T_PATTERN.matcher(value);
if (tMatcher.matches()) {
String tString = tMatcher.group(1);
return parseOsmDecimal(tString, false);
}
/* all possibilities failed */
return null;
}
private static final Pattern INCLINE_PATTERN = Pattern.compile("^(\\-?\\d+(?:\\.\\d+)?)\\s*%$");
/**
* parses an incline value as given for the "incline" key.
*
* @return incline in percents; null if value had syntax errors
*/
public static final Float parseIncline(String value) {
Matcher inclineMatcher = INCLINE_PATTERN.matcher(value);
if (inclineMatcher.matches()) {
String inclineString = inclineMatcher.group(1);
return parseOsmDecimal(inclineString, true);
}
return null;
}
/**
* parses an angular value as given for the "direction" key.
*
* @return angle in degrees measured from north, range [0, 360[;
* null if value had syntax errors
*/
public static final Float parseAngle(String value) {
/* try numeric angle */
Float measure = parseOsmDecimal(value, false);
if (measure != null) {
return measure % 360;
}
/* try cardinal directions (represented by letters) */
if ("N" .equals(value)) { return 0.0f; }
if ("NNE".equals(value)) { return 22.5f; }
if ("NE" .equals(value)) { return 45.0f; }
if ("ENE".equals(value)) { return 67.5f; }
if ("E" .equals(value)) { return 90.0f; }
if ("ESE".equals(value)) { return 112.5f; }
if ("SE" .equals(value)) { return 135.0f; }
if ("SSE".equals(value)) { return 157.5f; }
if ("S" .equals(value)) { return 180.0f; }
if ("SSW".equals(value)) { return 202.5f; }
if ("SW" .equals(value)) { return 225.0f; }
if ("WSW".equals(value)) { return 247.5f; }
if ("W" .equals(value)) { return 270.0f; }
if ("WNW".equals(value)) { return 292.5f; }
if ("NW" .equals(value)) { return 315.0f; }
if ("NNW".equals(value)) { return 337.5f; }
return null;
}
/**
* parses an hexadecimal color value
*
* @return color; null if value had syntax errors
*/
public static final Color parseColor(String value) {
try {
return Color.decode(value);
} catch (NumberFormatException e) {
return null;
}
}
}
================================================
FILE: OSM2World/src/org/osm2world/console/CLIArguments.java
================================================
package org.osm2world.console;
import java.io.File;
import java.util.List;
import org.osm2world.core.target.common.rendering.OrthoTilesUtil.CardinalDirection;
import org.osm2world.core.target.common.rendering.TileNumber;
import uk.co.flamingpenguin.jewel.cli.Option;
public interface CLIArguments {
public static final String OUTPUT_PATTERN = "(.*)\\.(?:obj|pov|png|ppm|gd)";
/* input and output */
@Option(description="the .osm input file", shortName="i")
File getInput();
boolean isInput();
@Option(description="output files", shortName="o", pattern=OUTPUT_PATTERN)
List getOutput();
boolean isOutput();
@Option(description="properties file with configuration parameters")
File getConfig();
boolean isConfig();
@Option(description="output size in pixels", pattern=Resolution.PATTERN,
defaultValue="800,600")
Resolution getResolution();
boolean isResolution();
/* camera */
@Option(description="downwards angle of orthographic view in degrees",
longName="oview.angle", defaultValue="30")
double getOviewAngle();
boolean isOviewAngle();
@Option(description="direction from which the orthographic view is rendered",
pattern="[NESW]", longName="oview.from", defaultValue="S")
CardinalDirection getOviewFrom();
boolean isOviewFrom();
@Option(description="lat,lon pairs defining a bounding box for orthographic view",
pattern=LatLonEle.PATTERN, longName="oview.bbox")
List getOviewBoundingBox();
boolean isOviewBoundingBox();
@Option(description="zoom,x,y triples of tiles defining a bounding box for orthographic view",
pattern=TileNumber.PATTERN, longName="oview.tiles")
List getOviewTiles();
boolean isOviewTiles();
@Option(description="lat,lon,ele of camera position for perspective view",
pattern=LatLonEle.PATTERN_WITH_ELE, longName="pview.pos")
LatLonEle getPviewPos();
boolean isPviewPos();
@Option(description="lat,lon,ele of camera look-at for perspective view",
pattern=LatLonEle.PATTERN_WITH_ELE, longName="pview.lookAt")
LatLonEle getPviewLookat();
boolean isPviewLookat();
@Option(description="vertical field of view angle for perspective view, in degrees",
longName="pview.fovy", defaultValue="45")
double getPviewFovy();
boolean isPviewFovy();
@Option(description="aspect ratio (width / height) for perspective view",
longName="pview.aspect")
double getPviewAspect();
boolean isPviewAspect();
/* logging */
@Option(description="writes execution times to the command line")
boolean getPerformancePrint();
@Option(description="appends a line with execution times to a file")
File getPerformanceTable();
boolean isPerformanceTable();
/* other parameters */
@Option(description="start the graphical user interface")
boolean getGui();
@Option(helpRequest=true, description="show this help", shortName="?")
boolean getHelp();
@Option(description="print software version and exit")
boolean getVersion();
/* parameter files */
@Option(description="a file containing one set of parameters per line")
File getParameterFile();
boolean isParameterFile();
}
================================================
FILE: OSM2World/src/org/osm2world/console/CLIArgumentsGroup.java
================================================
package org.osm2world.console;
import static com.google.common.base.Preconditions.*;
import static org.osm2world.console.CLIArgumentsUtil.ProgramMode.CONVERT;
import java.util.ArrayList;
import java.util.List;
/**
* a group of {@link CLIArguments} that represent conversions
* which can be performed at the same time
* and are only distinguished by output format/parameters
*/
public class CLIArgumentsGroup {
private final CLIArguments representative;
private final List cliArgumentsList;
/**
* @param representative the first member of the group.
*/
public CLIArgumentsGroup(CLIArguments representative) {
checkNotNull(representative);
this.representative = representative;
cliArgumentsList = new ArrayList();
cliArgumentsList.add(representative);
}
public void addCLIArguments(CLIArguments cliArguments) {
checkNotNull(cliArguments);
checkArgument(isCompatible(cliArguments), "argument incompatible with group");
cliArgumentsList.add(cliArguments);
}
public CLIArguments getRepresentative() {
return representative;
}
public List getCLIArgumentsList() {
return cliArgumentsList;
}
/**
* checks whether a CLIArguments instance is compatible with this group
*/
public boolean isCompatible(CLIArguments cliArguments) {
return isCompatible(representative, cliArguments);
}
/**
* checks whether two CLIArguments instances can be put into the same group
*/
private static final boolean isCompatible(
CLIArguments args1, CLIArguments args2) {
return CLIArgumentsUtil.getProgramMode(args1) == CONVERT
&& CLIArgumentsUtil.getProgramMode(args2) == CONVERT
&& args1.getInput().equals(args2.getInput())
&& ((args1.isConfig() && args1.getConfig().equals(args2.getConfig()))
|| (!args1.isConfig() && !args2.isConfig()));
}
}
================================================
FILE: OSM2World/src/org/osm2world/console/CLIArgumentsUtil.java
================================================
package org.osm2world.console;
import static org.osm2world.console.CLIArgumentsUtil.ProgramMode.*;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public final class CLIArgumentsUtil {
public static enum ProgramMode {GUI, CONVERT, HELP, VERSION, PARAMFILE};
public static enum OutputMode {OBJ, POV, PNG, PPM, GD};
private CLIArgumentsUtil() { }
public static final boolean isValid(CLIArguments args) {
return getErrorString(args) == null;
}
public static final String getErrorString(CLIArguments args) {
if (getProgramMode(args) == CONVERT) {
if (!args.isInput() || !args.isOutput()) {
return "input and output are required arguments" +
" for a conversion";
}
if (args.isOviewTiles() && args.getOviewTiles().isEmpty()) {
return "at least one tile required";
}
if (args.isOviewBoundingBox()
&& args.getOviewBoundingBox().size() < 2) {
return "bounding box requires at least two lat,lon pairs";
}
if (args.isOviewTiles() && args.isOviewBoundingBox()) {
return "define *either* tiles or bounding box for" +
" orthographic view";
}
for (File outputFile : args.getOutput()) {
if (getOutputMode(outputFile) == null) {
return "cannot identify file type from name " + outputFile
+ "\navailable output types: " + OutputMode.values();
}
}
if ((args.isPviewPos() && !args.isPviewLookat())
|| (args.isPviewLookat() && !args.isPviewPos())) {
return "camera position and look-at for perspective view "
+ "cannot be used separately, both must be defined";
}
if (hasOrthographicArg(args) && hasPerspectiveArg(args)) {
return "you cannot combine arguments for perspective view "
+ "and orthographic view";
}
}
return null;
}
private static final boolean hasOrthographicArg(CLIArguments args) {
return args.isOviewBoundingBox()
|| args.isOviewTiles();
}
private static final boolean hasPerspectiveArg(CLIArguments args) {
return args.isPviewLookat()
|| args.isPviewPos();
}
public static final ProgramMode getProgramMode(CLIArguments args) {
return args.isParameterFile() ? PARAMFILE
: args.getHelp() ? HELP
: args.getVersion() ? VERSION
: args.getGui() ? GUI
: CONVERT;
}
public static final OutputMode getOutputMode(File outputFile) {
if (outputFile.getName().toLowerCase().endsWith(".obj")) {
return OutputMode.OBJ;
} else if (outputFile.getName().toLowerCase().endsWith(".pov")) {
return OutputMode.POV;
} else if (outputFile.getName().toLowerCase().endsWith(".png")) {
return OutputMode.PNG;
} else if (outputFile.getName().toLowerCase().endsWith(".ppm")) {
return OutputMode.PPM;
} else if (outputFile.getName().toLowerCase().endsWith(".gd")) {
return OutputMode.GD;
} else {
return null;
}
}
public static final List getUnparsedParameterGroups(
File parameterFile) throws IOException {
List result = new ArrayList();
BufferedReader in = new BufferedReader(new FileReader(parameterFile));
String line;
while ((line = in.readLine()) != null) {
if (line.startsWith("#")) continue;
if (line.trim().isEmpty()) continue;
List argList = new ArrayList();
Pattern regex = Pattern.compile("[^\\s\"']+|\"([^\"]*)\"|'([^']*)'");
Matcher matcher = regex.matcher(line);
while (matcher.find()) {
if (matcher.group(1) != null) {
// Add double-quoted string without the quotes
argList.add(matcher.group(1));
} else if (matcher.group(2) != null) {
// Add single-quoted string without the quotes
argList.add(matcher.group(2));
} else {
// Add unquoted word
argList.add(matcher.group());
}
}
result.add(argList.toArray(new String[argList.size()]));
}
in.close();
return result;
}
}
================================================
FILE: OSM2World/src/org/osm2world/console/ImageExporter.java
================================================
package org.osm2world.console;
import static java.lang.Math.*;
import static org.osm2world.core.target.jogl.JOGLRenderingParameters.Winding.CCW;
import static org.osm2world.core.util.ConfigUtil.*;
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferInt;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import javax.media.opengl.GL;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.GLCapabilities;
import javax.media.opengl.GLDrawableFactory;
import javax.media.opengl.GLEventListener;
import javax.media.opengl.GLOffscreenAutoDrawable;
import javax.media.opengl.GLProfile;
import org.apache.commons.configuration.Configuration;
import org.osm2world.console.CLIArgumentsUtil.OutputMode;
import org.osm2world.core.ConversionFacade.Results;
import org.osm2world.core.target.TargetUtil;
import org.osm2world.core.target.common.lighting.GlobalLightingParameters;
import org.osm2world.core.target.common.rendering.Camera;
import org.osm2world.core.target.common.rendering.Projection;
import org.osm2world.core.target.jogl.AbstractJOGLTarget;
import org.osm2world.core.target.jogl.JOGLRenderingParameters;
import org.osm2world.core.target.jogl.JOGLTarget;
import org.osm2world.core.target.jogl.JOGLTargetFixedFunction;
import org.osm2world.core.target.jogl.JOGLTargetShader;
import org.osm2world.core.target.jogl.JOGLTextureManager;
import ar.com.hjg.pngj.ImageInfo;
import ar.com.hjg.pngj.ImageLineByte;
import ar.com.hjg.pngj.PngWriter;
import ar.com.hjg.pngj.chunks.PngChunkTextVar;
import ar.com.hjg.pngj.chunks.PngMetadata;
import com.jogamp.opengl.util.awt.AWTGLReadBufferUtil;
public class ImageExporter {
/**
* the width and height of the canvas used for rendering the exported image
* each must not exceed the canvas limit. If the requested image is larger,
* it will be rendered in multiple passes and combined afterwards.
* This is intended to avoid overwhelmingly large canvases
* (which would lead to crashes)
*/
private static final int DEFAULT_CANVAS_LIMIT = 1024;
private final Results results;
private final Configuration config;
private File backgroundImage;
private JOGLTextureManager backgroundTextureManager;
private Color clearColor;
private boolean exportAlpha = false;
private GLOffscreenAutoDrawable drawable;
private ImageExporterGLEventListener listener;
private final int pBufferSizeX;
private final int pBufferSizeY;
/** target prepared in init; null for unbuffered rendering */
private JOGLTarget bufferTarget = null;
private boolean unbufferedRendering;
/**
* Creates an {@link ImageExporter} for later use.
* Also performs calculations that only need to be done once for a group
* of files, based on a {@link CLIArgumentsGroup}.
*
* @param expectedGroup group that should contain at least the arguments
* for the files that will later be requested.
* Basis for optimization preparations.
*/
public ImageExporter(Configuration config, Results results,
CLIArgumentsGroup expectedGroup) {
this.results = results;
this.config = config;
/* parse background color/image and other configuration options */
clearColor = new Color(0, 0, 0, 0);
if (config.containsKey(BG_COLOR_KEY)) {
Color confClearColor = parseColor(config.getString(BG_COLOR_KEY));
if (confClearColor != null) {
clearColor = confClearColor;
} else {
System.err.println("incorrect color value: "
+ config.getString(BG_COLOR_KEY));
}
}
if (config.containsKey(BG_IMAGE_KEY)) {
String fileString = config.getString(BG_IMAGE_KEY);
if (fileString != null) {
backgroundImage = new File(fileString);
if (!backgroundImage.exists()) {
System.err.println("background image file doesn't exist: "
+ backgroundImage);
backgroundImage = null;
}
}
}
exportAlpha = config.getBoolean("exportAlpha", false);
int canvasLimit = config.getInt(CANVAS_LIMIT_KEY, DEFAULT_CANVAS_LIMIT);
/* find out what number and size of image file requests to expect */
int expectedFileCalls = 0;
int expectedMaxSizeX = 1;
int expectedMaxSizeY = 1;
for (CLIArguments args : expectedGroup.getCLIArgumentsList()) {
for (File outputFile : args.getOutput()) {
OutputMode outputMode = CLIArgumentsUtil.getOutputMode(outputFile);
if (outputMode == OutputMode.PNG || outputMode == OutputMode.PPM || outputMode == OutputMode.GD) {
expectedFileCalls += 1;
expectedMaxSizeX = max(expectedMaxSizeX, args.getResolution().x);
expectedMaxSizeY = max(expectedMaxSizeY, args.getResolution().y);
}
}
}
boolean onlyOneRenderPass = (expectedFileCalls <= 1
&& expectedMaxSizeX <= canvasLimit
&& expectedMaxSizeY <= canvasLimit);
unbufferedRendering = onlyOneRenderPass
|| config.getBoolean("forceUnbufferedPNGRendering", false);
/* create GL canvas and set rendering parameters */
GLProfile profile;
if ("shader".equals(config.getString("joglImplementation"))) {
profile = GLProfile.get(GLProfile.GL3);
} else {
profile = GLProfile.get(GLProfile.GL2);
}
GLDrawableFactory factory = GLDrawableFactory.getFactory(profile);
if (! factory.canCreateGLPbuffer(null, profile) && ! factory.canCreateFBO(null, profile)) {
throw new Error("Cannot create GLPbuffer or FBO for OpenGL output!");
}
GLCapabilities cap = new GLCapabilities(profile);
cap.setDoubleBuffered(false);
if (exportAlpha)
cap.setAlphaBits(8);
// set MSAA (Multi Sample Anti-Aliasing)
int msaa = config.getInt("msaa", 0);
if (msaa > 0) {
cap.setSampleBuffers(true);
cap.setNumSamples(msaa);
}
if ("shader".equals(config.getString("joglImplementation"))) {
if ("shadowVolumes".equals(config.getString("shadowImplementation"))
|| "both".equals(config.getString("shadowImplementation"))) {
cap.setStencilBits(8);
}
}
pBufferSizeX = min(canvasLimit, expectedMaxSizeX);
pBufferSizeY = min(canvasLimit, expectedMaxSizeY);
drawable = factory.createOffscreenAutoDrawable(null,
cap, null, pBufferSizeX, pBufferSizeY, null);
listener = new ImageExporterGLEventListener();
drawable.addGLEventListener(listener);
backgroundTextureManager = new JOGLTextureManager(drawable.getGL());
}
protected void finalize() throws Throwable {
freeResources();
}
/**
* manually frees resources that would otherwise remain used
* until the finalize call. It is no longer possible to use
* {@link #writeImageFile(File, CLIArgumentsUtil.OutputMode, int, int, Camera, Projection)}
* afterwards.
*/
public void freeResources() {
if (backgroundTextureManager != null) {
backgroundTextureManager.releaseAll();
backgroundTextureManager = null;
}
if (bufferTarget != null) {
bufferTarget.freeResources();
bufferTarget = null;
}
if (drawable != null) {
drawable.destroy();
drawable = null;
}
}
/**
* renders this ImageExporter's content to a file
*
* @param outputMode one of the image output modes
* @param x horizontal resolution
* @param y vertical resolution
*/
public void writeImageFile(
File outputFile, OutputMode outputMode,
int x, int y,
final Camera camera,
final Projection projection) throws IOException {
/* FIXME: this would be needed for cases where BufferSizes are so unbeliveable large that the temp images go beyond the memory limit
while (((1<<31)/x) <= pBufferSizeY) {
pBufferSizeY /= 2;
}
*/
listener.prepareRendering(camera, projection, x, y);
/* determine the number of "parts" to split the rendering in */
int xParts = 1 + ((x-1) / pBufferSizeX);
int yParts = 1 + ((y-1) / pBufferSizeY);
/* generate ImageWriter */
ImageWriter imageWriter;
switch (outputMode) {
case PNG: imageWriter = new PNGWriter(outputFile, x, y, exportAlpha); break;
case PPM: imageWriter = new PPMWriter(outputFile, x, y); break;
case GD: imageWriter = new GDWriter(outputFile, x, y); break;
default: throw new IllegalArgumentException(
"output mode not supported " + outputMode);
}
/* create image (maybe in multiple parts) */
BufferedImage image = new BufferedImage(x, pBufferSizeY, exportAlpha ? BufferedImage.TYPE_INT_ARGB : BufferedImage.TYPE_INT_RGB);
Graphics2D graphics = image.createGraphics();
graphics.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC));
for (int yPart = yParts-1; yPart >=0 ; --yPart) {
int yStart = yPart * pBufferSizeY;
int yEnd = (yPart+1 < yParts) ? (yStart + (pBufferSizeY-1)) : (y-1);
int ySize = (yEnd - yStart) + 1;
for (int xPart = 0; xPart < xParts; ++xPart) {
/* calculate start, end and size (in pixels)
* of the image part that will be rendered in this pass */
int xStart = xPart * pBufferSizeX;
int xEnd = (xPart+1 < xParts) ? (xStart + (pBufferSizeX-1)) : (x-1);
int xSize = (xEnd - xStart) + 1;
listener.setPart(xStart, yStart, xEnd, yEnd, xSize, ySize);
// render everything
drawable.display();
/* make screenshot and paste into the buffer that will contain
* pBufferSizeY entire image lines */
drawable.getContext().makeCurrent();
AWTGLReadBufferUtil reader = new AWTGLReadBufferUtil(drawable.getGLProfile(), exportAlpha);
BufferedImage imagePart = reader.readPixelsToBufferedImage(drawable.getGL(), 0, 0, xSize, ySize, true);
drawable.getContext().release();
graphics.drawImage(imagePart, xStart, 0, xSize, ySize, null);
}
imageWriter.append(image, ySize);
}
imageWriter.close();
}
private static JOGLTarget createJOGLTarget(GL gl, Results results,
Configuration config) {
JOGLTarget target;
if ("shader".equals(config.getString("joglImplementation"))) {
boolean drawBoundingBox = config.getBoolean("drawBoundingBox", false);
boolean shadowVolumes = "shadowVolumes".equals(config.getString("shadowImplementation"))
|| "both".equals(config.getString("shadowImplementation"));
boolean shadowMaps = "shadowMap".equals(config.getString("shadowImplementation"))
|| "both".equals(config.getString("shadowImplementation"));
int shadowMapWidth = config.getInt("shadowMapWidth", 4096);
int shadowMapHeight = config.getInt("shadowMapHeight", 4096);
int shadowMapCameraFrustumPadding = config.getInt("shadowMapCameraFrustumPadding", 8);
boolean useSSAO = "true".equals(config.getString("useSSAO"));
int SSAOkernelSize = config.getInt("SSAOkernelSize", 16);
float SSAOradius = config.getFloat("SSAOradius", 1);
boolean overwriteProjectionClippingPlanes = "true".equals(config.getString("overwriteProjectionClippingPlanes"));
target = new JOGLTargetShader(gl.getGL3(),
new JOGLRenderingParameters(CCW, false, true, drawBoundingBox, shadowVolumes, shadowMaps, shadowMapWidth, shadowMapHeight,
shadowMapCameraFrustumPadding, useSSAO, SSAOkernelSize, SSAOradius, overwriteProjectionClippingPlanes),
GlobalLightingParameters.DEFAULT);
} else {
target = new JOGLTargetFixedFunction(gl.getGL2(),
new JOGLRenderingParameters(CCW, false, true),
GlobalLightingParameters.DEFAULT);
}
target.setConfiguration(config);
boolean underground = config.getBoolean("renderUnderground", true);
target.setXZBoundary(results.getMapData().getBoundary());
TargetUtil.renderWorldObjects(target, results.getMapData(), underground);
target.finish();
return target;
}
/**
* interface ImageWriter is used to abstract the underlaying image
* format. It can be used for incremental image writes of huge images
*/
public static interface ImageWriter {
void append(BufferedImage img) throws IOException;
void append(BufferedImage img, int lines) throws IOException;
void close() throws IOException;
}
/**
* Implementation of an ImageWriter to write png files
*/
public static class PNGWriter implements ImageWriter {
private ImageInfo imgInfo;
private PngWriter writer;
public PNGWriter(File outputFile, int cols, int rows, boolean alpha) {
imgInfo = new ImageInfo(cols, rows, 8, alpha);
writer = new PngWriter(outputFile, imgInfo, true);
PngMetadata metaData = writer.getMetadata();
metaData.setTimeNow();
metaData.setText(PngChunkTextVar.KEY_Software, "OSM2World");
}
@Override
public void append(BufferedImage img) throws IOException {
append(img, img.getHeight());
}
@Override
public void append(BufferedImage img, int lines) throws IOException {
/* get raw data of image */
DataBuffer imageDataBuffer = img.getRaster().getDataBuffer();
int[] data = (((DataBufferInt)imageDataBuffer).getData());
/* create one ImageLine that will be refilled and written to png */
ImageLineByte bline = new ImageLineByte(imgInfo);
byte[] line = bline.getScanline();
int channels = imgInfo.channels;
for (int i = 0; i < lines; i++) {
for (int d = 0; d < img.getWidth(); d++) {
int val = data[i*img.getWidth()+d];
line[channels*d+0] = (byte) (val >> 16);
line[channels*d+1] = (byte) (val >> 8);
line[channels*d+2] = (byte) val;
if (channels > 3)
line[channels*d+3] = (byte) (val >> 24);
}
writer.writeRow(bline);
}
}
@Override
public void close() throws IOException {
writer.end();
writer.close();
}
}
/**
* Implementation of an ImageWriter to write raw ppm files
*/
public static class PPMWriter implements ImageWriter {
private FileOutputStream out;
private FileChannel fc;
private File outputFile;
private int cols;
private int rows;
public PPMWriter(File outputFile, int cols, int rows) {
this.cols = cols;
this.rows = rows;
this.outputFile = outputFile;
}
private void writeHeader() throws IOException {
out = new FileOutputStream(outputFile);
// write header
Charset charSet = Charset.forName("US-ASCII");
out.write("P6\n".getBytes(charSet));
out.write(String.format("%d %d\n", cols, rows).getBytes(charSet));
out.write("255\n".getBytes(charSet));
fc = out.getChannel();
}
@Override
public void append(BufferedImage img) throws IOException {
append(img, img.getHeight());
}
@Override
public void append(BufferedImage img, int lines) throws IOException {
if (fc == null) {
writeHeader();
}
// collect and write content
ByteBuffer writeBuffer = ByteBuffer.allocate(
3 * img.getWidth() * lines);
DataBuffer imageDataBuffer = img.getRaster().getDataBuffer();
int[] data = (((DataBufferInt)imageDataBuffer).getData());
for (int i = 0; i < img.getWidth() * lines; i++) {
int value = data[i];
writeBuffer.put((byte)(value >>> 16));
writeBuffer.put((byte)(value >>> 8));
writeBuffer.put((byte)(value));
}
writeBuffer.position(0);
fc.write(writeBuffer);
}
@Override
public void close() throws IOException {
if (fc != null) {
fc.close();
}
if (out != null) {
out.close();
}
}
}
/**
* Implementation of an ImageWriter to write the (rare) gd file format
*/
public static class GDWriter implements ImageWriter {
//TODO: dimensions are limited to short!
private FileOutputStream out;
private FileChannel fc;
private File outputFile;
private int cols;
private int rows;
public GDWriter(File outputFile, int cols, int rows) {
this.cols = cols;
this.rows = rows;
this.outputFile = outputFile;
}
private void writeHeader() throws IOException {
out = new FileOutputStream(outputFile);
out.write(0xff);
out.write(0xfe);
//write dimensions
DataOutputStream dOut = new DataOutputStream(out);
dOut.writeShort(cols);
dOut.writeShort(rows);
out.write(0x01);
out.write(0xff);
out.write(0xff);
out.write(0xff);
out.write(0xff);
out.write(0x00);
fc = out.getChannel();
}
@Override
public void append(BufferedImage img) throws IOException {
append(img, img.getHeight());
}
@Override
public void append(BufferedImage img, int lines) throws IOException {
if (fc == null) {
writeHeader();
}
// collect and write content
ByteBuffer writeBuffer = ByteBuffer.allocate(
4 * img.getWidth() * lines);
DataBuffer imageDataBuffer = img.getRaster().getDataBuffer();
int[] data = (((DataBufferInt)imageDataBuffer).getData());
for (int i = 0; i < img.getWidth() * lines; i++) {
int value = data[i];
writeBuffer.put((byte)(value >>> 16));
writeBuffer.put((byte)(value >>> 8));
writeBuffer.put((byte)(value));
writeBuffer.put((byte) 0);
}
writeBuffer.position(0);
fc.write(writeBuffer);
}
@Override
public void close() throws IOException {
if (fc != null) {
fc.close();
}
if (out != null) {
out.close();
}
}
}
public class ImageExporterGLEventListener implements GLEventListener {
private int xStart;
private int yStart;
private int xEnd;
private int yEnd;
private int xSize;
private int ySize;
private int x;
private int y;
private Camera camera;
private Projection projection;
private boolean nodisplay = false;
@Override
public void init(GLAutoDrawable drawable) {
/* render map data into buffer if it needs to be rendered multiple times */
if (!unbufferedRendering ) {
bufferTarget = createJOGLTarget(drawable.getGL(), results, config);
}
}
public void setPart(int xStart, int yStart, int xEnd, int yEnd,
int xSize, int ySize) {
if (this.xSize != xSize || this.ySize != ySize) {
// disable display while resizing. all display calls need to be from @writeImageFile
nodisplay = true;
drawable.setSize(xSize, ySize);
nodisplay = false;
}
this.xStart = xStart;
this.yStart = yStart;
this.xEnd = xEnd;
this.yEnd = yEnd;
this.xSize = xSize;
this.ySize = ySize;
}
public void prepareRendering(Camera camera, Projection projection,
int x, int y) {
this.camera = camera;
this.projection = projection;
this.x = x;
this.y = y;
}
@Override
public void dispose(GLAutoDrawable drawable) {
}
@Override
public void display(GLAutoDrawable drawable) {
if (nodisplay)
return;
/* configure rendering */
AbstractJOGLTarget.clearGL(drawable.getGL(), clearColor);
/* render to pBuffer */
JOGLTarget target = (bufferTarget == null)?
createJOGLTarget(drawable.getGL(), results, config) : bufferTarget;
if (backgroundImage != null) {
target.drawBackgoundImage(backgroundImage,
xStart, yStart, xSize, ySize,
backgroundTextureManager);
}
target.renderPart(camera, projection,
xStart / (double)(x-1), xEnd / (double)(x-1),
yStart / (double)(y-1), yEnd / (double)(y-1));
if (target != bufferTarget) {
target.freeResources();
}
}
@Override
public void reshape(GLAutoDrawable drawable, int x, int y, int width,
int height) {
}
}
}
================================================
FILE: OSM2World/src/org/osm2world/console/LatLonEle.java
================================================
package org.osm2world.console;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* immutable latitude/longitude/elevation coordinate triple
*/
public class LatLonEle {
public final double lat;
public final double lon;
public final double ele;
private static final String DOUBLE_P = "[+-]?\\d+(?:\\.\\d+)?";
/**
* pattern for parseable arguments
*/
public static final String PATTERN = "("+DOUBLE_P+"),("+DOUBLE_P+")";
public static final String PATTERN_WITH_ELE = PATTERN + ",(" + DOUBLE_P + ")";
/**
* regular constructor
*/
public LatLonEle(double lat, double lon, double ele) {
this.lat = lat;
this.lon = lon;
this.ele = ele;
validateValues();
}
/**
* regular constructor (with default elevation of 0)
*/
public LatLonEle(double lat, double lon) {
this(lat, lon, 0);
}
/**
* parsing constructor
* @param arg command line argument to be parsed;
* formats see {@link #PATTERN} and {@link #PATTERN_WITH_ELE}
*/
public LatLonEle(String arg) {
Matcher mEle = Pattern.compile(PATTERN_WITH_ELE).matcher(arg);
Matcher m = Pattern.compile(PATTERN).matcher(arg);
if (mEle.matches()) {
lat = Double.parseDouble(mEle.group(1));
lon = Double.parseDouble(mEle.group(2));
ele = Double.parseDouble(mEle.group(3));
validateValues();
} else if (m.matches()) {
lat = Double.parseDouble(m.group(1));
lon = Double.parseDouble(m.group(2));
ele = 0;
validateValues();
} else {
throw new IllegalArgumentException("argument doesn't match: " + arg);
}
}
/**
* @throws IllegalArgumentException for incorrect field values
*/
private void validateValues() {
if (lat > 90 || lat < -90 || lon > 180 || lon < -180) {
throw new IllegalArgumentException("not valid: " + lat + ", " + lon);
}
}
@Override
public String toString() {
return lat + "," + lon;
}
}
================================================
FILE: OSM2World/src/org/osm2world/console/OSM2World.java
================================================
package org.osm2world.console;
import static org.osm2world.console.CLIArgumentsUtil.getProgramMode;
import static org.osm2world.core.GlobalValues.VERSION_STRING;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import javax.swing.UIManager;
import org.apache.commons.configuration.BaseConfiguration;
import org.apache.commons.configuration.Configuration;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.osm2world.console.CLIArgumentsUtil.ProgramMode;
import org.osm2world.core.GlobalValues;
import org.osm2world.viewer.view.ViewerFrame;
import uk.co.flamingpenguin.jewel.cli.ArgumentValidationException;
import uk.co.flamingpenguin.jewel.cli.CliFactory;
/**
* main class of the OSM2World console application
*/
public class OSM2World {
public static void main(String[] unparsedArgs) {
/* assume --gui if no parameters are given */
if (unparsedArgs.length == 0) {
System.out.println("No parameters, running graphical interface.\n"
+ "If you want to use the command line, use the --help"
+ " parameter for a list of available parameters.");
unparsedArgs = new String[]{"--gui"};
}
/* parse command line arguments */
CLIArguments args = null;
try {
args = parseArguments(unparsedArgs);
} catch (Exception e) {
System.err.println(e.getMessage());
System.exit(1);
}
/* parse lines from parameter file (if one exists) */
List argumentsList = Collections.singletonList(args);
if (args.isParameterFile()) {
argumentsList = new ArrayList();
try {
List unparsedArgsLines = CLIArgumentsUtil
.getUnparsedParameterGroups(args.getParameterFile());
for (String[] unparsedArgsLine : unparsedArgsLines) {
try {
argumentsList.add(parseArguments(unparsedArgsLine));
} catch (Exception e) {
System.err.println("Could not parse parameters from file:");
System.err.println(Arrays.toString(unparsedArgsLine));
System.err.println("Ignoring it. Reason:");
System.err.println(e.getMessage());
}
}
} catch (IOException e) {
System.err.println(e.getMessage());
System.exit(1);
}
}
/* collect parameter groups into compatible groups
* (groups of parameter groups that use the same input and config files) */
List argumentsGroups = new ArrayList();
for (CLIArguments arguments : argumentsList) {
boolean added = false;
for (CLIArgumentsGroup compatibleGroup : argumentsGroups) {
if (compatibleGroup.isCompatible(arguments)) {
// add to existing compatible group
compatibleGroup.addCLIArguments(arguments);
added = true;
break;
}
}
if (!added) {
// start a new compatible group
argumentsGroups.add(new CLIArgumentsGroup(arguments));
}
}
/* execute conversions */
if (argumentsGroups.isEmpty()) {
System.err.println("warning: empty parameter file, doing nothing");
}
for (CLIArgumentsGroup argumentsGroup : argumentsGroups) {
if (argumentsList.size() > 1) {
System.out.print("executing conversion for these parameter lines: ");
for (CLIArguments p : argumentsGroup.getCLIArgumentsList()) {
System.out.print(argumentsList.indexOf(p) + " ");
}
System.out.print("\n");
}
executeArgumentsGroup(argumentsGroup);
}
}
private static CLIArguments parseArguments(String[] unparsedArgs)
throws ArgumentValidationException, Exception {
CLIArguments args = CliFactory.parseArguments(CLIArguments.class, unparsedArgs);
if (!CLIArgumentsUtil.isValid(args)) {
throw new Exception(CLIArgumentsUtil.getErrorString(args));
}
return args;
}
private static void executeArgumentsGroup(CLIArgumentsGroup argumentsGroup) {
/* load configuration file */
Configuration config = new BaseConfiguration();
File configFile = null;
CLIArguments representativeArgs = argumentsGroup.getRepresentative();
if (representativeArgs.isConfig()) {
try {
configFile = representativeArgs.getConfig();
PropertiesConfiguration fileConfig = new PropertiesConfiguration();
fileConfig.setListDelimiter(';');
fileConfig.load(configFile);
config = fileConfig;
} catch (ConfigurationException e) {
System.err.println("could not read config, ignoring it: ");
System.err.println(e);
}
}
/* run selected mode */
ProgramMode programMode = getProgramMode(representativeArgs);
switch (programMode) {
case HELP:
System.out.println(
CliFactory.createCli(CLIArguments.class).getHelpMessage()
+ "\n\nFor more information, see " + GlobalValues.WIKI_URI);
break;
case VERSION:
System.out.println("OSM2World " + VERSION_STRING);
break;
case GUI:
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch(Exception e) {
System.out.println("Error setting native look and feel: " + e);
}
File input = representativeArgs.isInput() ?
representativeArgs.getInput() : null;
new ViewerFrame(config, configFile, input).setVisible(true);
break;
case CONVERT:
try {
Output.output(config, argumentsGroup);
} catch (IOException e) {
e.printStackTrace();
}
break;
}
}
}
================================================
FILE: OSM2World/src/org/osm2world/console/Output.java
================================================
package org.osm2world.console;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.configuration.Configuration;
import org.osm2world.console.CLIArgumentsUtil.OutputMode;
import org.osm2world.core.ConversionFacade;
import org.osm2world.core.ConversionFacade.Phase;
import org.osm2world.core.ConversionFacade.ProgressListener;
import org.osm2world.core.ConversionFacade.Results;
import org.osm2world.core.map_data.creation.MapProjection;
import org.osm2world.core.map_elevation.creation.EleConstraintEnforcer;
import org.osm2world.core.map_elevation.creation.LPEleConstraintEnforcer;
import org.osm2world.core.map_elevation.creation.LeastSquaresInterpolator;
import org.osm2world.core.map_elevation.creation.NaturalNeighborInterpolator;
import org.osm2world.core.map_elevation.creation.NoneEleConstraintEnforcer;
import org.osm2world.core.map_elevation.creation.SimpleEleConstraintEnforcer;
import org.osm2world.core.map_elevation.creation.TerrainInterpolator;
import org.osm2world.core.map_elevation.creation.ZeroInterpolator;
import org.osm2world.core.math.AxisAlignedBoundingBoxXZ;
import org.osm2world.core.math.VectorXYZ;
import org.osm2world.core.math.VectorXZ;
import org.osm2world.core.target.common.rendering.Camera;
import org.osm2world.core.target.common.rendering.OrthoTilesUtil;
import org.osm2world.core.target.common.rendering.OrthoTilesUtil.CardinalDirection;
import org.osm2world.core.target.common.rendering.Projection;
import org.osm2world.core.target.obj.ObjWriter;
import org.osm2world.core.target.povray.POVRayWriter;
import org.osm2world.core.util.functions.DefaultFactory;
public final class Output {
private Output() {}
public static void output(Configuration config,
CLIArgumentsGroup argumentsGroup)
throws IOException {
long start = System.currentTimeMillis();
ConversionFacade cf = new ConversionFacade();
PerformanceListener perfListener =
new PerformanceListener(argumentsGroup.getRepresentative());
cf.addProgressListener(perfListener);
String interpolatorType = config.getString("terrainInterpolator");
if ("ZeroInterpolator".equals(interpolatorType)) {
cf.setTerrainEleInterpolatorFactory(
new DefaultFactory(ZeroInterpolator.class));
} else if ("LeastSquaresInterpolator".equals(interpolatorType)) {
cf.setTerrainEleInterpolatorFactory(
new DefaultFactory(LeastSquaresInterpolator.class));
} else if ("NaturalNeighborInterpolator".equals(interpolatorType)) {
cf.setTerrainEleInterpolatorFactory(
new DefaultFactory(NaturalNeighborInterpolator.class));
}
String enforcerType = config.getString("eleConstraintEnforcer");
if ("NoneEleConstraintEnforcer".equals(enforcerType)) {
cf.setEleConstraintEnforcerFactory(
new DefaultFactory(NoneEleConstraintEnforcer.class));
} else if ("SimpleEleConstraintEnforcer".equals(enforcerType)) {
cf.setEleConstraintEnforcerFactory(
new DefaultFactory(SimpleEleConstraintEnforcer.class));
} else if ("LPEleConstraintEnforcer".equals(enforcerType)) {
cf.setEleConstraintEnforcerFactory(
new DefaultFactory(LPEleConstraintEnforcer.class));
}
Results results = cf.createRepresentations(
argumentsGroup.getRepresentative().getInput(), null, config, null);
ImageExporter exporter = null;
for (CLIArguments args : argumentsGroup.getCLIArgumentsList()) {
Camera camera = null;
Projection projection = null;
if (args.isOviewTiles()) {
camera = OrthoTilesUtil.cameraForTiles(
results.getMapProjection(),
args.getOviewTiles(),
args.getOviewAngle(),
args.getOviewFrom());
projection = OrthoTilesUtil.projectionForTiles(
results.getMapProjection(),
args.getOviewTiles(),
args.getOviewAngle(),
args.getOviewFrom());
} else if (args.isOviewBoundingBox()) {
double angle = args.getOviewAngle();
CardinalDirection from = args.getOviewFrom();
Collection pointsXZ = new ArrayList();
for (LatLonEle l : args.getOviewBoundingBox()) {
pointsXZ.add(results.getMapProjection().calcPos(l.lat, l.lon));
}
AxisAlignedBoundingBoxXZ bounds =
new AxisAlignedBoundingBoxXZ(pointsXZ);
camera = OrthoTilesUtil.cameraForBounds(bounds, angle, from);
projection = OrthoTilesUtil.projectionForBounds(bounds, angle, from);
} else if (args.isPviewPos()) {
MapProjection proj = results.getMapProjection();
LatLonEle pos = args.getPviewPos();
LatLonEle lookAt = args.getPviewLookat();
camera = new Camera();
VectorXYZ posV = proj.calcPos(pos.lat, pos.lon).xyz(pos.ele);
VectorXYZ laV = proj.calcPos(lookAt.lat, lookAt.lon).xyz(lookAt.ele);
camera.setCamera(posV.x, posV.y, posV.z, laV.x, laV.y, laV.z);
projection = new Projection(false,
args.isPviewAspect() ? args.getPviewAspect() :
(double)args.getResolution().x / args.getResolution().y,
args.getPviewFovy(),
0,
1, 50000);
}
for (File outputFile : args.getOutput()) {
OutputMode outputMode =
CLIArgumentsUtil.getOutputMode(outputFile);
switch (outputMode) {
case OBJ:
Integer primitiveThresholdOBJ =
config.getInteger("primitiveThresholdOBJ", null);
if (primitiveThresholdOBJ == null) {
ObjWriter.writeObjFile(outputFile,
results.getMapData(), results.getMapProjection(),
camera, projection);
} else {
ObjWriter.writeObjFiles(outputFile,
results.getMapData(), results.getMapProjection(),
camera, projection, primitiveThresholdOBJ);
}
break;
case POV:
POVRayWriter.writePOVInstructionFile(outputFile,
results.getMapData(), camera, projection);
break;
case PNG:
case PPM:
case GD:
if (camera == null || projection == null) {
System.err.println("camera or projection missing");
}
if (exporter == null) {
exporter = new ImageExporter(
config, results, argumentsGroup);
}
exporter.writeImageFile(outputFile, outputMode,
args.getResolution().x, args.getResolution().y,
camera, projection);
break;
}
}
}
if (exporter != null) {
exporter.freeResources();
exporter = null;
}
if (argumentsGroup.getRepresentative().getPerformancePrint()) {
long timeSec = (System.currentTimeMillis() - start) / 1000;
System.out.println("finished after " + timeSec + " s");
}
if (argumentsGroup.getRepresentative().isPerformanceTable()) {
PrintWriter w = new PrintWriter(new FileWriter(
argumentsGroup.getRepresentative().getPerformanceTable(), true), true);
w.printf("|%6d |%6d |%6d |%6d |%6d |%6d |\n",
(perfListener.getPhaseDuration(Phase.MAP_DATA) + 500) / 1000,
(perfListener.getPhaseDuration(Phase.REPRESENTATION) + 500) / 1000,
(perfListener.getPhaseDuration(Phase.ELEVATION) + 500) / 1000,
(perfListener.getPhaseDuration(Phase.TERRAIN) + 500) / 1000,
(System.currentTimeMillis() - perfListener.getPhaseEnd(Phase.TERRAIN) + 500) / 1000,
(System.currentTimeMillis() - start + 500) / 1000);
w.close();
}
}
private static class PerformanceListener implements ProgressListener {
private final CLIArguments args;
public PerformanceListener(CLIArguments args) {
this.args = args;
}
private Phase currentPhase = null;
private long currentPhaseStart;
private Map phaseStarts = new HashMap();
private Map phaseEnds = new HashMap();
public Long getPhaseStart(Phase phase) {
return phaseStarts.get(phase);
}
public Long getPhaseEnd(Phase phase) {
return phaseEnds.get(phase);
}
public Long getPhaseDuration(Phase phase) {
return getPhaseEnd(phase) - getPhaseStart(phase);
}
@Override
public void updatePhase(Phase newPhase) {
phaseStarts.put(newPhase, System.currentTimeMillis());
if (currentPhase != null) {
phaseEnds.put(currentPhase, System.currentTimeMillis());
if (args.getPerformancePrint()) {
long ms = System.currentTimeMillis() - currentPhaseStart;
System.out.println("phase " + currentPhase
+ " finished after " + ms + " ms");
}
}
currentPhase = newPhase;
currentPhaseStart = System.currentTimeMillis();
}
}
}
================================================
FILE: OSM2World/src/org/osm2world/console/Resolution.java
================================================
package org.osm2world.console;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* immutable representation of an image resolution
* (two positive integers)
*/
public class Resolution {
public final int x;
public final int y;
/**
* pattern for parseable arguments
*/
public static final String PATTERN = "([0-9]{1,9}),([0-9]{1,9})";
/**
* regular constructor
*/
public Resolution(int x, int y) {
this.x = x;
this.y = y;
validateValues();
}
/**
* parsing constructor
* @param arg command line argument to be parsed;
* format see {@link #PATTERN}
*/
public Resolution(String arg) {
Matcher m = Pattern.compile(PATTERN).matcher(arg);
if (m.matches()) {
x = Integer.parseInt(m.group(1));
y = Integer.parseInt(m.group(2));
validateValues();
} else {
throw new IllegalArgumentException("argument doesn't match: " + arg);
}
}
/**
* @throws IllegalArgumentException for incorrect field values
*/
private void validateValues() {
if (x <= 0 || y <= 0) {
throw new IllegalArgumentException("not positive: " + x + ", " + y);
}
}
@Override
public String toString() {
return x + "," + y;
}
}
================================================
FILE: OSM2World/src/org/osm2world/console/package-info.java
================================================
@ParametersAreNonnullByDefault
package org.osm2world.console;
import javax.annotation.ParametersAreNonnullByDefault;
================================================
FILE: OSM2World/src/org/osm2world/console/package.html
================================================
command line interface for OSM2World.
================================================
FILE: OSM2World/src/org/osm2world/core/ConversionFacade.java
================================================
package org.osm2world.core;
import static java.util.Collections.emptyList;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import org.apache.commons.configuration.BaseConfiguration;
import org.apache.commons.configuration.Configuration;
import org.apache.commons.lang.time.StopWatch;
import org.openstreetmap.osmosis.core.domain.v0_6.Bound;
import org.osm2world.core.map_data.creation.MapProjection;
import org.osm2world.core.map_data.creation.MetricMapProjection;
import org.osm2world.core.map_data.creation.OSMToMapDataConverter;
import org.osm2world.core.map_data.creation.OriginMapProjection;
import org.osm2world.core.map_data.data.MapData;
import org.osm2world.core.map_data.object_info.AddressGatherer;
import org.osm2world.core.map_data.object_info.ObjectInfoManager;
import org.osm2world.core.map_elevation.creation.EleConstraintEnforcer;
import org.osm2world.core.map_elevation.creation.EleConstraintValidator;
import org.osm2world.core.map_elevation.creation.LeastSquaresInterpolator;
import org.osm2world.core.map_elevation.creation.NoneEleConstraintEnforcer;
import org.osm2world.core.map_elevation.creation.SRTMData;
import org.osm2world.core.map_elevation.creation.TerrainElevationData;
import org.osm2world.core.map_elevation.creation.TerrainInterpolator;
import org.osm2world.core.map_elevation.creation.ZeroInterpolator;
import org.osm2world.core.map_elevation.data.EleConnector;
import org.osm2world.core.math.VectorXYZ;
import org.osm2world.core.math.AxisAlignedBoundingBoxXZ;
import org.osm2world.core.osm.creation.JOSMFileHack;
import org.osm2world.core.osm.creation.OsmosisReader;
import org.osm2world.core.osm.data.OSMData;
import org.osm2world.core.target.Renderable;
import org.osm2world.core.target.Target;
import org.osm2world.core.target.TargetUtil;
import org.osm2world.core.target.common.material.Materials;
import org.osm2world.core.util.FaultTolerantIterationUtil;
import org.osm2world.core.util.TouchMapperProfile;
import org.osm2world.core.util.FaultTolerantIterationUtil.Operation;
import org.osm2world.core.util.functions.DefaultFactory;
import org.osm2world.core.util.functions.Factory;
import org.osm2world.core.world.creation.WorldCreator;
import org.osm2world.core.world.creation.WorldModule;
import org.osm2world.core.world.data.WorldObject;
import org.osm2world.core.world.modules.BarrierModule;
import org.osm2world.core.world.modules.BridgeModule;
import org.osm2world.core.world.modules.BuildingModule;
import org.osm2world.core.world.modules.CliffModule;
import org.osm2world.core.world.modules.GolfModule;
import org.osm2world.core.world.modules.InvisibleModule;
import org.osm2world.core.world.modules.ParkingModule;
import org.osm2world.core.world.modules.PoolModule;
import org.osm2world.core.world.modules.PowerModule;
import org.osm2world.core.world.modules.RailwayModule;
import org.osm2world.core.world.modules.RoadModule;
import org.osm2world.core.world.modules.StreetFurnitureModule;
import org.osm2world.core.world.modules.SurfaceAreaModule;
import org.osm2world.core.world.modules.TrafficSignModule;
import org.osm2world.core.world.modules.TreeModule;
import org.osm2world.core.world.modules.TunnelModule;
import org.osm2world.core.world.modules.WaterModule;
/**
* provides an easy way to call all steps of the conversion process
* in the correct order
*/
public class ConversionFacade {
/**
* all results of a conversion run
*/
public static final class Results {
private final MapProjection mapProjection;
private final MapData mapData;
private final TerrainElevationData eleData;
private Results(MapProjection mapProjection, MapData mapData, TerrainElevationData eleData) {
this.mapProjection = mapProjection;
this.mapData = mapData;
this.eleData = eleData;
}
public MapProjection getMapProjection() {
return mapProjection;
}
public MapData getMapData() {
return mapData;
}
public TerrainElevationData getEleData() {
return eleData;
}
/**
* collects and returns all representations that implement a
* renderableType, including terrain.
* Convenience method.
*/
public Collection getRenderables(Class renderableType) {
return getRenderables(renderableType, true, true);
}
/**
* @see #getRenderables(Class)
*/
public Collection getRenderables(
Class renderableType, boolean includeGrid, boolean includeTerrain) {
//TODO make use of or drop includeTerrain
Collection representations = new ArrayList();
if (includeGrid) {
for (R r : mapData.getWorldObjects(renderableType)) {
representations.add(r);
}
}
return representations;
}
}
/**
* generates a default list of modules for the conversion
*/
private static final List createDefaultModuleList() {
return Arrays.asList((WorldModule)
new RoadModule(),
new RailwayModule(),
new BuildingModule(),
//new ParkingModule(),
//new TreeModule(),
//new StreetFurnitureModule(),
//new TrafficSignModule(),
new WaterModule()
//new PoolModule(),
//new GolfModule(),
//new CliffModule(),
//new BarrierModule(),
//new PowerModule(),
//new BridgeModule(),
//new TunnelModule(),
//new SurfaceAreaModule(),
//new InvisibleModule()
);
}
private Factory extends OriginMapProjection> mapProjectionFactory =
new DefaultFactory(MetricMapProjection.class);
private Factory extends TerrainInterpolator> terrainEleInterpolatorFactory =
new DefaultFactory(LeastSquaresInterpolator.class);
private Factory extends EleConstraintEnforcer> eleConstraintEnforcerFactory =
new DefaultFactory(NoneEleConstraintEnforcer.class);
/**
* sets the factory that will make {@link MapProjection}
* instances during subsequent calls to
* {@link #createRepresentations(OSMData, List, Configuration, List)}.
*
* @see DefaultFactory
*/
public void setMapProjectionFactory(
Factory extends OriginMapProjection> mapProjectionFactory) {
this.mapProjectionFactory = mapProjectionFactory;
}
/**
* sets the factory that will make {@link EleConstraintEnforcer}
* instances during subsequent calls to
* {@link #createRepresentations(OSMData, List, Configuration, List)}.
*
* @see DefaultFactory
*/
public void setEleConstraintEnforcerFactory(
Factory extends EleConstraintEnforcer> interpolatorFactory) {
this.eleConstraintEnforcerFactory = interpolatorFactory;
}
/**
* sets the factory that will make {@link TerrainInterpolator}
* instances during subsequent calls to
* {@link #createRepresentations(OSMData, List, Configuration, List)}.
*
* @see DefaultFactory
*/
public void setTerrainEleInterpolatorFactory(
Factory extends TerrainInterpolator> enforcerFactory) {
this.terrainEleInterpolatorFactory = enforcerFactory;
}
/**
* performs all necessary steps to go from
* an OSM file to the renderable {@link WorldObject}s.
* Sends updates to {@link ProgressListener}s.
*
* @param osmFile file to read OSM data from; != null
* @param worldModules modules that will create the {@link WorldObject}s
* in the result; null to use a default module list
* @param config set of parameters that controls various aspects
* of the modules' behavior; null to use defaults
* @param targets receivers of the conversion results; can be null if
* you want to handle the returned results yourself
*/
public Results createRepresentations(File osmFile,
List worldModules, Configuration config,
List> targets)
throws IOException {
long totalStart = TouchMapperProfile.start();
if (osmFile == null) {
throw new IllegalArgumentException("osmFile must not be null");
}
OSMData osmData = null;
boolean useJOSMHack = false;
if (JOSMFileHack.isJOSMGenerated(osmFile)) {
useJOSMHack = true;
} else {
/* try to read file using Osmosis */
try {
long readOsmStart = TouchMapperProfile.start();
osmData = new OsmosisReader(osmFile).getData();
TouchMapperProfile.logMillis("input.read_osmosis_ms", readOsmStart);
} catch (IOException e) {
System.out.println("could not read file," +
" trying workaround for files created by JOSM");
useJOSMHack = true;
}
}
/* create a temporary "cleaned up" file as workaround for JOSM files */
if (useJOSMHack) {
File tempFile;
try {
long josmCleanupStart = TouchMapperProfile.start();
tempFile = JOSMFileHack.createTempOSMFile(osmFile);
TouchMapperProfile.logMillis("input.josm_cleanup_ms", josmCleanupStart);
} catch (Exception e2) {
throw new IOException("could not read OSM file" +
" (not even with workaround for JOSM files)", e2);
}
long readCleanedStart = TouchMapperProfile.start();
osmData = new OsmosisReader(tempFile).getData();
TouchMapperProfile.logMillis("input.read_cleaned_osmosis_ms", readCleanedStart);
}
Results results = createRepresentations(osmData, worldModules, config, targets);
TouchMapperProfile.logMillis("conversion.from_file_total_ms", totalStart);
return results;
}
/**
* variant of
* {@link #createRepresentations(File, List, Configuration, List)}
* that accepts {@link OSMData} instead of a file.
* Use this when all data is already
* in memory, for example with editor applications.
*
* @param osmData input data; != null
* @param worldModules modules that will create the {@link WorldObject}s
* in the result; null to use a default module list
* @param config set of parameters that controls various aspects
* of the modules' behavior; null to use defaults
* @param targets receivers of the conversion results; can be null if
* you want to handle the returned results yourself
*
* @throws BoundingBoxSizeException for oversized bounding boxes
*/
public Results createRepresentations(OSMData osmData,
List worldModules, Configuration config,
List> targets)
throws IOException, BoundingBoxSizeException {
long totalStart = TouchMapperProfile.start();
/* check the inputs */
if (osmData == null) {
throw new IllegalArgumentException("osmData must not be null");
}
if (config == null) {
config = new BaseConfiguration();
}
Double maxBoundingBoxDegrees = config.getDouble("maxBoundingBoxDegrees", null);
if (maxBoundingBoxDegrees != null) {
for (Bound bound : osmData.getBounds()) {
if (bound.getTop() - bound.getBottom() > maxBoundingBoxDegrees
|| bound.getRight() - bound.getLeft() > maxBoundingBoxDegrees) {
throw new BoundingBoxSizeException(bound);
}
}
}
/* create map data from OSM data */
updatePhase(Phase.MAP_DATA);
OriginMapProjection mapProjection = mapProjectionFactory.make();
long mapProjectionStart = TouchMapperProfile.start();
mapProjection.setOrigin(osmData);
TouchMapperProfile.logMillis("map_data.map_projection_origin_ms",
mapProjectionStart);
OSMToMapDataConverter converter = new OSMToMapDataConverter(mapProjection, config);
long mapDataCreationStart = TouchMapperProfile.start();
MapData mapData = converter.createMapData(osmData);
TouchMapperProfile.logMillis("map_data.converter_total_ms",
mapDataCreationStart);
AxisAlignedBoundingBoxXZ boundary = mapData.getBoundary();
/* apply world modules */
updatePhase(Phase.REPRESENTATION);
if (worldModules == null) {
worldModules = createDefaultModuleList();
}
Materials.configureMaterials(config);
//this will cause problems if multiple conversions are run
//at the same time, because global variables are being modified
WorldCreator moduleManager =
new WorldCreator(config, worldModules);
long worldModulesStart = TouchMapperProfile.start();
moduleManager.addRepresentationsTo(mapData);
TouchMapperProfile.logMillis("representation.world_creator_total_ms",
worldModulesStart);
/* determine elevations */
updatePhase(Phase.ELEVATION);
String srtmDir = config.getString("srtmDir", null);
TerrainElevationData eleData = null;
if (srtmDir != null) {
eleData = new SRTMData(new File(srtmDir), mapProjection);
}
long elevationsStart = TouchMapperProfile.start();
calculateElevations(mapData, eleData, config);
TouchMapperProfile.logMillis("elevation.total_ms", elevationsStart);
/* create terrain */
updatePhase(Phase.TERRAIN); //TODO this phase may be obsolete
/* supply results to targets and caller */
updatePhase(Phase.FINISHED);
boolean underground = config.getBoolean("renderUnderground", true);
if (targets != null) {
long targetsStart = TouchMapperProfile.start();
for (Target> target : targets) {
TargetUtil.renderWorldObjects(target, mapData, underground);
target.finish();
}
TouchMapperProfile.logMillis("output.targets_render_and_finish_ms",
targetsStart);
}
long addressesStart = TouchMapperProfile.start();
AddressGatherer.gather(osmData, mapData);
TouchMapperProfile.logMillis("output.address_gather_ms", addressesStart);
TouchMapperProfile.logMillis("conversion.from_osm_data_total_ms", totalStart);
return new Results(mapProjection, mapData, eleData);
}
/**
* uses OSM data and an terrain elevation data (usually from an external
* source) to calculate elevations for all {@link EleConnector}s of the
* {@link WorldObject}s
*/
private void calculateElevations(MapData mapData,
TerrainElevationData eleData, Configuration config) {
final TerrainInterpolator interpolator =
(eleData != null)
? terrainEleInterpolatorFactory.make()
: new ZeroInterpolator();
/* provide known elevations from eleData to the interpolator */
StopWatch stopWatch = new StopWatch();
stopWatch.start();
if (!(interpolator instanceof ZeroInterpolator)) {
Collection sites = emptyList();
try {
sites = eleData.getSites(mapData);
System.out.println("time getSites: " + stopWatch);
stopWatch.reset();
stopWatch.start();
} catch (IOException e) {
e.printStackTrace();
}
interpolator.setKnownSites(sites);
System.out.println("time setKnownSites: " + stopWatch);
stopWatch.reset();
stopWatch.start();
}
/* interpolate connectors' elevations */
final List connectors = new ArrayList();
FaultTolerantIterationUtil.iterate(mapData.getWorldObjects(),
new Operation() {
@Override public void perform(WorldObject worldObject) {
for (EleConnector conn : worldObject.getEleConnectors()) {
conn.setPosXYZ(interpolator.interpolateEle(conn.pos));
connectors.add(conn);
}
}
});
System.out.println("time terrain interpolation: " + stopWatch);
stopWatch.reset();
stopWatch.start();
/* enforce constraints defined by WorldObjects */
boolean debugConstraints = config.getBoolean("debugConstraints", false);
final EleConstraintEnforcer enforcer = debugConstraints
? new EleConstraintValidator(mapData,
eleConstraintEnforcerFactory.make())
: eleConstraintEnforcerFactory.make();
enforcer.addConnectors(connectors);
if (!(enforcer instanceof NoneEleConstraintEnforcer)) {
FaultTolerantIterationUtil.iterate(mapData.getWorldObjects(),
new Operation() {
@Override public void perform(WorldObject worldObject) {
worldObject.defineEleConstraints(enforcer);
}
});
}
System.out.println("time add constraints: " + stopWatch);
stopWatch.reset();
stopWatch.start();
enforcer.enforceConstraints();
System.out.println("time enforce constraints: " + stopWatch);
stopWatch.reset();
stopWatch.start();
}
public static enum Phase {
MAP_DATA,
REPRESENTATION,
ELEVATION,
TERRAIN,
FINISHED
}
/**
* implemented by classes that want to be informed about
* a conversion run's progress
*/
public static interface ProgressListener {
/** announces the start of a new phase */
public void updatePhase(Phase newPhase);
// /** announces the fraction of the current phase that is completed */
// public void updatePhaseProgress(float phaseProgress);
}
private List listeners = new ArrayList();
public void addProgressListener(ProgressListener listener) {
listeners.add(listener);
}
private void updatePhase(Phase newPhase) {
for (ProgressListener listener : listeners) {
listener.updatePhase(newPhase);
}
}
// private void updatePhaseProgress(float phaseProgress) {
// for (ProgressListener listener : listeners) {
// listener.updatePhaseProgress(phaseProgress);
// }
// }
/**
* exception to be thrown if the OSM input data covers an area
* larger than the maxBoundingBoxDegrees config property
*/
public static class BoundingBoxSizeException extends RuntimeException {
private static final long serialVersionUID = 2841146365929523046L; //generated VersionID
public final Bound bound;
private BoundingBoxSizeException(Bound bound) {
this.bound = bound;
}
@Override
public String toString() {
return "oversized bounding box: " + bound;
}
}
}
================================================
FILE: OSM2World/src/org/osm2world/core/GlobalValues.java
================================================
package org.osm2world.core;
/**
* globally accessible constants
*/
public final class GlobalValues {
private GlobalValues() {};
/** version string */
public static final String VERSION_STRING = "0.2.0";
/** link to the OSM wiki documentation */
public static final String WIKI_URI = "http://wiki.osm.org/OSM2World";
/** link to OSM2World home page */
public static final String OSM2WORLD_URI = "http://osm2world.org";
}
================================================
FILE: OSM2World/src/org/osm2world/core/heightmap/creation/EmptyTerrainElevationGrid.java
================================================
package org.osm2world.core.heightmap.creation;
import org.osm2world.core.heightmap.data.AbstractCellularTerrainElevation;
import org.osm2world.core.math.AxisAlignedBoundingBoxXZ;
import org.osm2world.core.math.VectorXZ;
public class EmptyTerrainElevationGrid extends
AbstractCellularTerrainElevation {
public EmptyTerrainElevationGrid(AxisAlignedBoundingBoxXZ bounds,
int numPointsX, int numPointsZ) {
super(bounds, numPointsX, numPointsZ);
}
@Override
protected Float getElevation(VectorXZ pos) {
return null;
}
}
================================================
FILE: OSM2World/src/org/osm2world/core/heightmap/creation/FlatTerrainElevation.java
================================================
package org.osm2world.core.heightmap.creation;
import org.osm2world.core.heightmap.data.AbstractCellularTerrainElevation;
import org.osm2world.core.math.AxisAlignedBoundingBoxXZ;
import org.osm2world.core.math.VectorXZ;
public class FlatTerrainElevation extends AbstractCellularTerrainElevation {
public FlatTerrainElevation(AxisAlignedBoundingBoxXZ boundary,
int numPointsX, int numPointsZ) {
super(boundary, numPointsX, numPointsZ);
}
@Override
protected Float getElevation(VectorXZ pos) {
return 0f;
}
}
================================================
FILE: OSM2World/src/org/osm2world/core/heightmap/creation/WaveTerrainElevation.java
================================================
package org.osm2world.core.heightmap.creation;
import org.osm2world.core.heightmap.data.AbstractCellularTerrainElevation;
import org.osm2world.core.math.AxisAlignedBoundingBoxXZ;
import org.osm2world.core.math.VectorXZ;
public class WaveTerrainElevation extends AbstractCellularTerrainElevation {
public WaveTerrainElevation(AxisAlignedBoundingBoxXZ boundary,
int numPointsX, int numPointsZ) {
super(boundary, numPointsX, numPointsZ);
}
@Override
protected Float getElevation(VectorXZ pos) {
return (float) Math.sin(pos.x) * 10;
}
}
================================================
FILE: OSM2World/src/org/osm2world/core/heightmap/data/AbstractCellularTerrainElevation.java
================================================
package org.osm2world.core.heightmap.data;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import org.osm2world.core.math.AxisAlignedBoundingBoxXZ;
import org.osm2world.core.math.PolygonXYZ;
import org.osm2world.core.math.PolygonXZ;
import org.osm2world.core.math.SimplePolygonXZ;
import org.osm2world.core.math.VectorXYZ;
import org.osm2world.core.math.VectorXZ;
public abstract class AbstractCellularTerrainElevation implements
CellularTerrainElevation {
final int numPointsX;
final int numPointsZ;
final Collection terrainPoints;
final TerrainPoint[][] terrainPointGrid;
@Override
public Collection getTerrainPoints() {
return terrainPoints;
}
@Override
public TerrainPoint[][] getTerrainPointGrid() {
return terrainPointGrid;
}
@Override
public PolygonXYZ getBoundaryPolygon() {
List vertices = new ArrayList();
// first row
for (int x = 0; x < numPointsX; x++) {
vertices.add(vectorXYZForPointAt(x, 0));
}
// last column
for (int z = 1; z < numPointsZ - 1; z++) {
vertices.add(vectorXYZForPointAt(numPointsX - 1, z));
}
// last row
for (int x = numPointsX - 1; x >= 0; x--) {
vertices.add(vectorXYZForPointAt(x, numPointsZ - 1));
}
// first column
for (int z = numPointsZ - 2; z >= 0 /* [0][0] will be added again*/; z--) {
vertices.add(vectorXYZForPointAt(0, z));
}
return new PolygonXYZ(vertices);
}
@Override
public PolygonXZ getBoundaryPolygonXZ() {
List vertices = new ArrayList();
// first row
for (int x = 0; x < numPointsX; x++) {
vertices.add(vectorXZForPointAt(x, 0));
}
// last column
for (int z = 1; z < numPointsZ - 1; z++) {
vertices.add(vectorXZForPointAt(numPointsX - 1, z));
}
// last row
for (int x = numPointsX - 1; x >= 0; x--) {
vertices.add(vectorXZForPointAt(x, numPointsZ - 1));
}
// first column
for (int z = numPointsZ - 2; z >= 0 /* [0][0] will be added again*/; z--) {
vertices.add(vectorXZForPointAt(0, z));
}
return new PolygonXZ(vertices);
}
private VectorXYZ vectorXYZForPointAt(int x, int z) {
TerrainPoint point = terrainPointGrid[x][z];
return point.getPos().xyz(point.getEle());
}
private VectorXZ vectorXZForPointAt(int x, int z) {
TerrainPoint point = terrainPointGrid[x][z];
return point.getPos();
}
@Override
public Iterable extends TerrainElevationCell> getCells() {
return new Iterable() {
@Override public Iterator iterator() {
return new CellIterator();
}
};
}
//TODO (duplicated code): merge with independently written version from IntersectionGrid
private final class CellIterator implements Iterator {
int currX = -1, currZ = 0;
@Override
public boolean hasNext() {
return currX + 1 < numPointsX-1 || currZ + 2 < numPointsZ-1;
}
@Override
public CellImpl next() {
currX += 1;
if (currX == numPointsX-1) {
currZ += 1;
currX = 0;
if (currZ == numPointsZ-1) {
throw new NoSuchElementException();
}
}
return new CellImpl(currX, currZ);
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
}
private final class CellImpl implements TerrainElevationCell {
private int leftXIndex;
private int bottomZIndex;
public CellImpl(int leftXIndex, int bottomZIndex) {
assert leftXIndex + 1 < numPointsX
&& bottomZIndex + 1 < numPointsZ;
this.leftXIndex = leftXIndex;
this.bottomZIndex = bottomZIndex;
}
@Override public final TerrainPoint getBottomLeft() {
return terrainPointGrid[leftXIndex][bottomZIndex];
}
@Override public final TerrainPoint getTopLeft() {
return terrainPointGrid[leftXIndex][bottomZIndex+1];
}
@Override public final TerrainPoint getBottomRight() {
return terrainPointGrid[leftXIndex+1][bottomZIndex];
}
@Override public final TerrainPoint getTopRight() {
return terrainPointGrid[leftXIndex+1][bottomZIndex+1];
}
@Override
public Collection getTerrainPoints() {
List terrainPoints = new ArrayList(4);
terrainPoints.add(terrainPointGrid[leftXIndex][bottomZIndex]);
terrainPoints.add(terrainPointGrid[leftXIndex+1][bottomZIndex]);
terrainPoints.add(terrainPointGrid[leftXIndex+1][bottomZIndex+1]);
terrainPoints.add(terrainPointGrid[leftXIndex][bottomZIndex+1]);
return terrainPoints;
}
@Override
public final PolygonXYZ getPolygonXYZ() {
List vertices = new ArrayList(5);
vertices.add(vectorXYZForPointAt(leftXIndex, bottomZIndex));
vertices.add(vectorXYZForPointAt(leftXIndex+1, bottomZIndex));
vertices.add(vectorXYZForPointAt(leftXIndex+1, bottomZIndex+1));
vertices.add(vectorXYZForPointAt(leftXIndex, bottomZIndex+1));
vertices.add(vertices.get(0));
return new PolygonXYZ(vertices);
}
@Override
public final SimplePolygonXZ getPolygonXZ() {
List vertices = new ArrayList(5);
vertices.add(vectorXZForPointAt(leftXIndex, bottomZIndex));
vertices.add(vectorXZForPointAt(leftXIndex+1, bottomZIndex));
vertices.add(vectorXZForPointAt(leftXIndex+1, bottomZIndex+1));
vertices.add(vectorXZForPointAt(leftXIndex, bottomZIndex+1));
vertices.add(vertices.get(0));
return new SimplePolygonXZ(vertices);
}
@Override
public AxisAlignedBoundingBoxXZ getAxisAlignedBoundingBoxXZ() {
return new AxisAlignedBoundingBoxXZ(
Math.min(getTopLeft().getPos().x, getBottomLeft().getPos().x),
Math.min(getBottomLeft().getPos().z, getBottomRight().getPos().z),
Math.max(getTopRight().getPos().x, getBottomRight().getPos().x),
Math.max(getTopLeft().getPos().z, getTopRight().getPos().z));
}
@Override
public String toString() {
return "cell at x: " + leftXIndex + ", z: " + bottomZIndex;
}
/* auto-generated hashCode and equals follow */
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + getOuterType().hashCode();
result = prime * result + bottomZIndex;
result = prime * result + leftXIndex;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
CellImpl other = (CellImpl) obj;
if (!getOuterType().equals(other.getOuterType()))
return false;
if (bottomZIndex != other.bottomZIndex)
return false;
if (leftXIndex != other.leftXIndex)
return false;
return true;
}
private AbstractCellularTerrainElevation getOuterType() {
return AbstractCellularTerrainElevation.this;
}
}
public AbstractCellularTerrainElevation(AxisAlignedBoundingBoxXZ boundary,
int numPointsX, int numPointsZ) {
if (numPointsX < 2 || numPointsZ < 2) {
throw new IllegalArgumentException("need at least 2x2 points for cell grid");
}
this.numPointsX = numPointsX;
this.numPointsZ = numPointsZ;
terrainPoints = new ArrayList(numPointsX * numPointsZ);
terrainPointGrid = new TerrainPoint[numPointsX][numPointsZ];
for (int x = 0; x < numPointsX; x++) {
for (int z = 0; z < numPointsZ; z++) {
VectorXZ pos = new VectorXZ(boundary.minX + x * boundary.sizeX()
/ (numPointsX - 1), boundary.minZ + z * boundary.sizeZ()
/ (numPointsZ - 1));
TerrainPoint terrainPoint = new TerrainPoint(pos,
getElevation(pos));
terrainPointGrid[x][z] = terrainPoint;
terrainPoints.add(terrainPoint);
}
}
}
protected abstract Float getElevation(VectorXZ pos);
}
================================================
FILE: OSM2World/src/org/osm2world/core/heightmap/data/CellularTerrainElevation.java
================================================
package org.osm2world.core.heightmap.data;
import org.osm2world.core.math.PolygonXYZ;
import org.osm2world.core.math.PolygonXZ;
/**
* Terrain elevation data that consists of
* points arranged in a grid structure (2d array).
*
* The grid
*
* - needs to be complete (points might have unknown elevation, though)
*
- does not need to have rectangular grid cells, they may be deformed
*
*/
public interface CellularTerrainElevation extends TerrainElevation {
/**
* @return regular two-dimensional array (not jagged)
*/
TerrainPoint[][] getTerrainPointGrid();
/**
* returns the boundary created from the first and last rows and columns
* of the grid.
* This requires that all {@link TerrainPoint}s' elevations have already
* been set to non-null values.
*/
PolygonXYZ getBoundaryPolygon();
/**
* returns the boundary created from the first and last rows and columns
* of the grid.
*/
PolygonXZ getBoundaryPolygonXZ();
/**
* returns Iterable over cells.
* This is a convenience method for operations that need to be
* performed for all cells. The iterator cannot be used to remove cells.
*/
Iterable extends TerrainElevationCell> getCells();
}
================================================
FILE: OSM2World/src/org/osm2world/core/heightmap/data/TerrainElevation.java
================================================
package org.osm2world.core.heightmap.data;
import java.util.Collection;
/**
* elevation data for the ground.
* Rather abstract interface that doesn't impose restrictions
* on the order of points with known elevation.
*/
public interface TerrainElevation {
public Collection getTerrainPoints();
}
================================================
FILE: OSM2World/src/org/osm2world/core/heightmap/data/TerrainElevationCell.java
================================================
package org.osm2world.core.heightmap.data;
import java.util.Collection;
import org.osm2world.core.math.PolygonXYZ;
import org.osm2world.core.math.SimplePolygonXZ;
import org.osm2world.core.math.datastructures.IntersectionTestObject;
public interface TerrainElevationCell extends IntersectionTestObject {
public TerrainPoint getTopLeft();
public TerrainPoint getBottomLeft();
public TerrainPoint getTopRight();
public TerrainPoint getBottomRight();
public Collection getTerrainPoints();
/** returns the counterclockwise polygon surrounding this cell. */
public SimplePolygonXZ getPolygonXZ();
/**
* returns 3d polygon surrounding this cell.
* ordering and XZ coordinates are the same as for {@link #getPolygonXZ()}.
*/
public PolygonXYZ getPolygonXYZ();
}
================================================
FILE: OSM2World/src/org/osm2world/core/heightmap/data/TerrainPoint.java
================================================
package org.osm2world.core.heightmap.data;
import org.osm2world.core.math.VectorXYZ;
import org.osm2world.core.math.VectorXZ;
public class TerrainPoint {
private final VectorXZ pos;
private Float ele;
public TerrainPoint(VectorXZ pos, Float ele) {
this.pos = pos;
this.ele = ele;
}
public VectorXZ getPos() {
return pos;
}
public VectorXYZ getPosXYZ() {
return pos.xyz(ele);
}
/**
* returns the point's elevation;
* null indicates an unknown elevation
*/
public Float getEle() {
return ele;
}
/**
* sets the point's elevation;
* null indicates an unknown elevation
*/
public void setEle(float ele) {
this.ele = ele;
}
@Override
public String toString() {
return "(" + pos + "," + ele + ")";
}
}
================================================
FILE: OSM2World/src/org/osm2world/core/heightmap/package.html
================================================
terrain heightmaps
================================================
FILE: OSM2World/src/org/osm2world/core/map_data/creation/EmptyTerrainBuilder.java
================================================
package org.osm2world.core.map_data.creation;
import static java.lang.Math.min;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.openstreetmap.josm.plugins.graphview.core.data.EmptyTagGroup;
import org.openstreetmap.josm.plugins.graphview.core.data.MapBasedTagGroup;
import org.openstreetmap.josm.plugins.graphview.core.data.Tag;
import org.osm2world.core.map_data.data.MapArea;
import org.osm2world.core.map_data.data.MapNode;
import org.osm2world.core.math.AxisAlignedBoundingBoxXZ;
import org.osm2world.core.math.VectorGridXZ;
import org.osm2world.core.math.VectorXZ;
import org.osm2world.core.osm.data.OSMNode;
import org.osm2world.core.osm.data.OSMWay;
/**
* utility class for building geometry representing empty terrain.
*/
public class EmptyTerrainBuilder {
/** prevents instantiation */
private EmptyTerrainBuilder() { }
/** tag to be internally used on faked ways around "empty terrain" */
public static final Tag EMPTY_SURFACE_TAG =
new Tag("surface", "osm2world:empty_terrain");
/** faked outline node for the terrain areas */
private static final OSMNode EMPTY_SURFACE_NODE = new OSMNode(
Double.NaN, Double.NaN, EmptyTagGroup.EMPTY_TAG_GROUP, 0);
/** faked outline way for the terrain areas */
private static final OSMWay EMPTY_SURFACE_WAY = new OSMWay(
new MapBasedTagGroup(EMPTY_SURFACE_TAG), 0,
Collections.emptyList());
public static final double POINT_GRID_DIST = 30;
public static final int PATCH_SIZE_POINTS = 10;
/**
* creates a grid of square {@link MapArea}s to represent empty terrain.
* The areas are connected with each other, but do not overlap,
* and cover the entire data bounds.
*
* These areas do not come from OSM data, but they are treated the same
* as mapped areas later on to avoid unnecessary special case handling.
*/
static void createAreasForEmptyTerrain(List mapNodes,
List mapAreas, AxisAlignedBoundingBoxXZ dataBounds) {
VectorGridXZ posGrid = new VectorGridXZ(
dataBounds.pad(POINT_GRID_DIST), POINT_GRID_DIST);
/* create a grid of nodes (leaving points within the future patches blank) */
MapNode[][] nodeGrid = new MapNode[posGrid.sizeX()][posGrid.sizeZ()];
for (int x = 0; x < posGrid.sizeX(); x++) {
for (int z = 0; z < posGrid.sizeZ(); z++) {
if (x % PATCH_SIZE_POINTS == 0 || x == posGrid.sizeX() - 1
|| z % PATCH_SIZE_POINTS == 0 || z == posGrid.sizeZ() - 1) {
VectorXZ pos = posGrid.get(x, z);
MapNode mapNode = new MapNode(pos, EMPTY_SURFACE_NODE);
nodeGrid[x][z] = mapNode;
mapNodes.add(mapNode);
}
}
}
/* create a grid of areas based on the nodes */
// calculate the number of patches, but always round up
int numPatchesX = (nodeGrid.length + PATCH_SIZE_POINTS - 2) / PATCH_SIZE_POINTS;
int numPatchesZ = (nodeGrid[0].length + PATCH_SIZE_POINTS - 2) / PATCH_SIZE_POINTS;
for (int patchX = 0; patchX < numPatchesX; patchX++) {
for (int z = 0; z < numPatchesZ; z++) {
mapAreas.add(createAreaForPatch(nodeGrid,
patchX * PATCH_SIZE_POINTS,
z * PATCH_SIZE_POINTS));
}
}
}
private static MapArea createAreaForPatch(MapNode[][] nodeGrid,
int startX, int startZ) {
int endX = min(startX + PATCH_SIZE_POINTS + 1, nodeGrid.length);
int endZ = min(startZ + PATCH_SIZE_POINTS + 1, nodeGrid[0].length);
List nodes = new ArrayList();
// first row
for (int x = startX; x < endX; x++) {
nodes.add(nodeGrid[x][startZ]);
}
// last column
for (int z = startZ + 1; z < endZ - 1; z++) {
nodes.add(nodeGrid[endX - 1][z]);
}
// last row
for (int x = endX - 1; x >= startX; x--) {
nodes.add(nodeGrid[x][endZ - 1]);
}
// first column
for (int z = endZ - 2; z >= startZ /* start will be added again */; z--) {
nodes.add(nodeGrid[startX][z]);
}
return new MapArea(EMPTY_SURFACE_WAY, nodes);
}
}
================================================
FILE: OSM2World/src/org/osm2world/core/map_data/creation/LatLon.java
================================================
package org.osm2world.core.map_data.creation;
/**
* an immutable coordinate pair with latitude and longitude
*/
public class LatLon {
public final double lat;
public final double lon;
public LatLon(double lat, double lon) {
this.lat = lat;
this.lon = lon;
}
}
================================================
FILE: OSM2World/src/org/osm2world/core/map_data/creation/MapProjection.java
================================================
package org.osm2world.core.map_data.creation;
import org.osm2world.core.math.VectorXZ;
/**
* function that converts latitude/longitude coordinates
* to internally used x/z coordinates
*/
public interface MapProjection {
public VectorXZ calcPos(double lat, double lon);
public VectorXZ calcPos(LatLon latlon);
/**
* inverse for {@link #calcPos(double, double)}
*/
public double calcLat(VectorXZ pos);
/**
* inverse for {@link #calcPos(double, double)}
*/
public double calcLon(VectorXZ pos);
/**
* returns a vector that points one meter to the north
* from the position that becomes the coordinate origin
*/
public VectorXZ getNorthUnit();
/**
* returns the origin (i.e. the latlon that maps to (0,0)
*/
public LatLon getOrigin();
}
================================================
FILE: OSM2World/src/org/osm2world/core/map_data/creation/MercatorProjection.java
================================================
package org.osm2world.core.map_data.creation;
import static java.lang.Math.PI;
import static java.lang.Math.atan;
import static java.lang.Math.exp;
import static java.lang.Math.log;
import static java.lang.Math.pow;
import static java.lang.Math.sin;
import static java.lang.Math.sqrt;
import static java.lang.Math.tan;
import static java.lang.Math.toDegrees;
import static java.lang.Math.toRadians;
final class MercatorProjection {
private MercatorProjection() {
}
private static final double R_MAJOR = 6378137.0;
private static final double R_MINOR = 6356752.3142;
private static final double RATIO = R_MINOR / R_MAJOR;
private static final double ECCENT = sqrt(1.0 - (RATIO * RATIO));
private static final double COM = 0.5 * ECCENT;
public static final double EARTH_CIRCUMFERENCE = 40075016.686;
/**
* Calculate earth circumference at given latitude.
*/
public static double earthCircumference(double latitude) {
return EARTH_CIRCUMFERENCE * Math.cos(toRadians(latitude));
}
/**
* Convert longitude to Mercator projection (range [0..1]).
*/
public static double lonToX(double longitude) {
return (longitude + 180.0) / 360.0;
}
/**
* Convert from Mercator projection (range [0..1]) to longitude.
*/
public static double xToLon(double x) {
return 360.0 * (x - 0.5);
}
/**
* Convert latitude to Mercator projection (range [0..1]).
*/
public static double latToY(double latitude) {
double sinLat = Math.sin(toRadians(latitude));
return Math.log((1.0 + sinLat) / (1.0 - sinLat)) / (4.0 * Math.PI) + 0.5;
}
/**
* Convert from Mercator projection (range [0..1]) to latitude.
*/
public static double yToLat(double y) {
return 360.0 * Math.atan(Math.exp((y - 0.5) * (2.0 * Math.PI))) / Math.PI - 90.0;
}
// This is for the Elliptical Mercator version
public static double latToYElliptical(double lat) {
lat = Math.min(89.5, Math.max(lat, -89.5));
double phi = toRadians(lat);
double sinphi = sin(phi);
double con = ECCENT * sinphi;
con = pow(((1.0 - con) / (1.0 + con)), COM);
double ts = tan(0.5 * ((PI * 0.5) - phi)) / con;
return 0 - R_MAJOR * log(ts);
}
// This is for the Elliptical Mercator version
public static double yToLatElliptical(double y) {
double ts = exp(-y / R_MAJOR);
double phi = PI / 2 - 2 * atan(ts);
double dphi = 1.0;
int i = 0;
while ((Math.abs(dphi) > 0.000000001) && (i < 15))
{
double con = ECCENT * sin(phi);
dphi = PI / 2 - 2 * atan(ts * Math.pow((1.0 - con) / (1.0 + con), COM)) - phi;
phi += dphi;
i++;
}
return toDegrees(phi);
}
}
================================================
FILE: OSM2World/src/org/osm2world/core/map_data/creation/MetricMapProjection.java
================================================
package org.osm2world.core.map_data.creation;
import static org.osm2world.core.map_data.creation.MercatorProjection.*;
import org.osm2world.core.math.VectorXZ;
/**
* Map projection that is intended to use the "dense" space
* of floating point values by making all coordinates relative to
* the origin. 1 meter distance is roughly represented by 1 internal unit.
*/
public class MetricMapProjection extends OriginMapProjection {
private double originX;
private double originY;
private double scaleFactor;
public VectorXZ calcPos(double lat, double lon) {
double x = lonToX(lon) * scaleFactor - originX;
double y = latToY(lat) * scaleFactor - originY;
/* snap to som cm precision, seems to reduce geometry exceptions */
x = Math.round(x * 1000) / 1000.0d;
y = Math.round(y * 1000) / 1000.0d;
return new VectorXZ(x, y); // x and z(!) are 2d here
}
@Override
public VectorXZ calcPos(LatLon latlon) {
return calcPos(latlon.lat, latlon.lon);
}
@Override
public double calcLat(VectorXZ pos) {
return yToLat((pos.z + originY) / scaleFactor);
}
@Override
public double calcLon(VectorXZ pos) {
return xToLon((pos.x + originX) / scaleFactor);
}
@Override
public VectorXZ getNorthUnit() {
return VectorXZ.Z_UNIT;
}
@Override
public void setOrigin(LatLon origin) {
super.setOrigin(origin);
this.scaleFactor = earthCircumference(origin.lat);
this.originY = latToY(origin.lat) * scaleFactor;
this.originX = lonToX(origin.lon) * scaleFactor;
}
}
================================================
FILE: OSM2World/src/org/osm2world/core/map_data/creation/MultipolygonAreaBuilder.java
================================================
package org.osm2world.core.map_data.creation;
import static java.lang.Boolean.*;
import static java.lang.Double.NaN;
import static java.lang.Math.*;
import static java.util.Arrays.asList;
import static java.util.Collections.*;
import static org.osm2world.core.math.GeometryUtil.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import org.openstreetmap.josm.plugins.graphview.core.data.MapBasedTagGroup;
import org.openstreetmap.josm.plugins.graphview.core.data.Tag;
import org.openstreetmap.josm.plugins.graphview.core.data.TagGroup;
import org.osm2world.core.map_data.data.MapArea;
import org.osm2world.core.map_data.data.MapNode;
import org.osm2world.core.math.AxisAlignedBoundingBoxXZ;
import org.osm2world.core.math.InvalidGeometryException;
import org.osm2world.core.math.LineSegmentXZ;
import org.osm2world.core.math.PolygonWithHolesXZ;
import org.osm2world.core.math.SimplePolygonXZ;
import org.osm2world.core.math.VectorXZ;
import org.osm2world.core.math.datastructures.IntersectionTestObject;
import org.osm2world.core.osm.data.OSMData;
import org.osm2world.core.osm.data.OSMElement;
import org.osm2world.core.osm.data.OSMMember;
import org.osm2world.core.osm.data.OSMNode;
import org.osm2world.core.osm.data.OSMRelation;
import org.osm2world.core.osm.data.OSMWay;
import org.osm2world.core.osm.ruleset.HardcodedRuleset;
import org.osm2world.core.osm.ruleset.Ruleset;
/**
* utility class for creating areas from multipolygon relations,
* including those with non-closed member ways.
*
* Known Limitations:
* - This cannot reliably handle touching inner rings consisting
* of non-closed ways.
* - Closed touching rings will not break the calculation,
* but are represented as multiple touching holes.
*
*/
final class MultipolygonAreaBuilder {
/** prevents instantiation */
private MultipolygonAreaBuilder() { }
/**
* Creates areas for a multipolygon relation.
* Also adds this area to the adjacent nodes using
* {@link MapNode#addAdjacentArea(MapArea)}.
*
* @param relation the multipolygon relation
* @param nodeMap map from {@link OSMNode}s to {@link MapNode}s
*
* @return constructed area(s), multiple areas will be created if there
* is more than one outer ring. Empty for invalid multipolygons.
*/
public static final Collection createAreasForMultipolygon(
OSMRelation relation, Map nodeMap) {
if (isSimpleMultipolygon(relation)) {
return createAreasForSimpleMultipolygon(relation, nodeMap);
} else {
return createAreasForAdvancedMultipolygon(relation, nodeMap);
}
}
private static final boolean isSimpleMultipolygon(OSMRelation relation) {
int numberOuters = 0;
boolean closedWays = true;
for (OSMMember member : relation.relationMembers) {
if ("outer".equals(member.role)
&& member.member instanceof OSMWay) {
numberOuters += 1;
}
if (("outer".equals(member.role) || "inner".equals(member.role))
&& member.member instanceof OSMWay) {
if (!((OSMWay)member.member).isClosed()) {
closedWays = false;
break;
}
}
}
return numberOuters == 1 && closedWays;
}
/**
* handles the common simple case with only one outer way.
* Expected to be faster than the more general method
* {@link #createAreasForAdvancedMultipolygon(OSMRelation, Map)}
*
* @param relation has to be a simple multipolygon relation
* @param nodeMap
*/
private static final Collection createAreasForSimpleMultipolygon(
OSMRelation relation, Map nodeMap) {
assert isSimpleMultipolygon(relation);
OSMElement tagSource = null;
List outerNodes = null;
List> holes = new ArrayList>();
for (OSMMember member : relation.relationMembers) {
if (member.member instanceof OSMWay) {
OSMWay way = (OSMWay)member.member;
if ("inner".equals(member.role)) {
List hole = new ArrayList(way.nodes.size());
for (OSMNode node : ((OSMWay)member.member).nodes) {
hole.add(nodeMap.get(node));
//TODO: add area as adjacent to node for inners' nodes, too?
}
holes.add(hole);
} else if ("outer".equals(member.role)) {
tagSource = relation.tags.size() > 1 ? relation : way;
outerNodes = new ArrayList(way.nodes.size());
for (OSMNode node : way.nodes) {
outerNodes.add(nodeMap.get(node));
}
}
}
}
return singleton(new MapArea(tagSource, outerNodes, holes));
}
private static final Collection createAreasForAdvancedMultipolygon(
OSMRelation relation, Map nodeMap) {
List innersAndOuters = new ArrayList();
/* collect ways */
for (OSMMember member : relation.relationMembers) {
if (member.member instanceof OSMWay
&& ("outer".equals(member.role)
|| "inner".equals(member.role)) ) {
innersAndOuters.add(new NodeSequence(
(OSMWay)member.member, nodeMap));
}
}
/* build rings, then polygons from the ways */
List rings = buildRings(innersAndOuters);
if (rings != null) {
return buildPolygonsFromRings(relation, rings);
} else {
return Collections.emptyList();
}
}
/**
* builds closed rings from any mixture of closed and unclosed segments
*
* @return null if building closed rings isn't possible
*/
private static final List buildRings(
List sequences) {
List closedRings = new ArrayList();
NodeSequence currentRing = null;
while (sequences.size() > 0) {
if (currentRing == null) {
// start a new ring with any remaining node sequence
currentRing = sequences.remove(sequences.size() - 1);
} else {
// try to continue the ring by appending a node sequence
NodeSequence assignedSequence = null;
for (NodeSequence sequence : sequences) {
if (currentRing.tryAdd(sequence)) {
assignedSequence = sequence;
break;
}
}
if (assignedSequence != null) {
sequences.remove(assignedSequence);
} else {
return null;
}
}
// check whether the ring under construction is closed
if (currentRing != null && currentRing.isClosed()) {
try {
closedRings.add(new Ring(currentRing));
currentRing = null;
} catch (InvalidGeometryException e) {
throw new InvalidGeometryException(String.format(
"self-intersecting ring (with %d nodes)",
currentRing.size() - 1), e);
}
}
}
if (currentRing != null) {
// the last ring could not be closed
return null;
}
return closedRings;
}
/**
* @param rings rings to build polygons from; will be empty afterwards
*/
private static final Collection buildPolygonsFromRings(
OSMRelation relation, List rings) {
Collection finishedPolygons =
new ArrayList(rings.size() / 2);
/* build polygon */
while (!rings.isEmpty()) {
/* find an outer ring */
Ring outerRing = null;
for (Ring candidate : rings) {
boolean containedInOtherRings = false;
for (Ring otherRing : rings) {
if (otherRing != candidate
&& otherRing.containsRing(candidate)) {
containedInOtherRings = true;
break;
}
}
if (!containedInOtherRings) {
outerRing = candidate;
break;
}
}
/* find inner rings of that ring */
Collection innerRings = new ArrayList();
for (Ring ring : rings) {
if (ring != outerRing && outerRing.containsRing(ring)) {
boolean containedInOtherRings = false;
for (Ring otherRing : rings) {
if (otherRing != ring && otherRing != outerRing
&& otherRing.containsRing(ring)) {
containedInOtherRings = true;
break;
}
}
if (!containedInOtherRings) {
innerRings.add(ring);
}
}
}
/* create a new area and remove the used rings */
List> holes = new ArrayList>(innerRings.size());
List holesXZ = new ArrayList(innerRings.size());
for (Ring innerRing : innerRings) {
holes.add(innerRing.closedNodeSequence);
holesXZ.add(innerRing.getPolygon());
}
MapArea area = new MapArea(relation, outerRing.getNodeLoop(), holes,
new PolygonWithHolesXZ(outerRing.getPolygon(), holesXZ));
finishedPolygons.add(area);
rings.remove(outerRing);
rings.removeAll(innerRings);
}
return finishedPolygons;
}
private static final TagGroup COASTLINE_NODE_TAGS = new MapBasedTagGroup(
new Tag("osm2world:note", "fake node from coastline processing"));
/**
* turns all coastline ways into {@link MapArea}s
* based on an artificial natural=water multipolygon relation.
*
* It relies on the direction-dependent drawing of coastlines.
* If coastlines are incomplete, then it is attempted to connect them
* to proper rings. One assumption being used is that they are complete
* within the file's bounds.
*
* It cannot distinguish between water and land tiles if there is no
* coastline at all (it will then guess based on the tags being used),
* but should be able to handle all other cases.
*/
public static final Collection createAreasForCoastlines(
OSMData osmData, Map nodeMap,
Collection