Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
CityDoctor
CityDoctor2
Commits
6d0d664e
Commit
6d0d664e
authored
Sep 10, 2026
by
Matthias Betz
Browse files
Merge remote-tracking branch 'remotes/origin/dev' into dev_baris_aabb_auto_mergeable
parents
a7ed4b64
cc4fe260
Pipeline
#12443
passed with stage
in 2 minutes and 42 seconds
Changes
133
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
CityDoctorParent/CityDoctorValidation/src/assembly/lin/start.sh
View file @
6d0d664e
#!/bin/sh
./runtime/bin/java
-classpath
app/
*
:plugin/
*
de.hft.stuttgart.citydoctor2.CityDoctorValidation
-in
REKaiserwall.gml
-config
testConfigWithStreaming.yml
-xmlReport
output.xml
\ No newline at end of file
./runtime/bin/java
-classpath
app/
*
:plugin/
*
de.hft.stuttgart.citydoctor2.CityDoctorValidation
-in
REKaiserwall.gml
-config
testConfigWithStreaming.yml
-xmlReport
output.xml
-db_location
"
${
TMPDIR
:-
/tmp
}
"
\ No newline at end of file
CityDoctorParent/CityDoctorValidation/src/assembly/no_runtime/start.bat
View file @
6d0d664e
java
-classpath
app
/
*;
plugin
/
*
de
.hft.stuttgart.citydoctor2.CityDoctorValidation
-in
REKaiserwall
.gml
-config
testConfigWithStreaming
.yml
-xmlReport
output
.xml
java
-classpath
app
/
*;
plugin
/
*
de
.hft.stuttgart.citydoctor2.CityDoctorValidation
-in
REKaiserwall
.gml
-config
testConfigWithStreaming
.yml
-xmlReport
output
.xml
-db
_location
%TEMP%
/citydoctor
/
pause
\ No newline at end of file
CityDoctorParent/CityDoctorValidation/src/assembly/no_runtime/start.sh
View file @
6d0d664e
#!/bin/sh
java
-classpath
app/
*
:plugin/
*
de.hft.stuttgart.citydoctor2.CityDoctorValidation
-in
REKaiserwall.gml
-config
testConfigWithStreaming.yml
-xmlReport
output.xml
\ No newline at end of file
java
-classpath
app/
*
:plugin/
*
de.hft.stuttgart.citydoctor2.CityDoctorValidation
-in
REKaiserwall.gml
-config
testConfigWithStreaming.yml
-xmlReport
output.xml
-db_location
#!/bin/sh
./runtime/bin/java
-classpath
app/
*
:plugin/
*
de.hft.stuttgart.citydoctor2.CityDoctorValidation
-in
REKaiserwall.gml
-config
testConfigWithStreaming.yml
-xmlReport
output.xml
-db_location
"
${
TMPDIR
:-
/tmp
}
"
\ No newline at end of file
CityDoctorParent/CityDoctorValidation/src/assembly/win/start.bat
View file @
6d0d664e
"runtime/bin/java.exe"
-classpath
app
/
*;
plugin
/
*
de
.hft.stuttgart.citydoctor2.CityDoctorValidation
-in
REKaiserwall
.gml
-config
testConfigWithStreaming
.yml
-xmlReport
output
.xml
"runtime/bin/java.exe"
-classpath
app
/
*;
plugin
/
*
de
.hft.stuttgart.citydoctor2.CityDoctorValidation
-in
REKaiserwall
.gml
-config
testConfigWithStreaming
.yml
-xmlReport
output
.xml
-db
_location
%TEMP%
/citydoctor
/
pause
\ No newline at end of file
CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/CityDoctorValidation.java
View file @
6d0d664e
...
...
@@ -23,6 +23,7 @@ import java.io.FileNotFoundException;
import
java.io.IOException
;
import
java.util.List
;
import
de.hft.stuttgart.citydoctor2.database.DatabaseSettings
;
import
de.hft.stuttgart.citydoctor2.utils.Localization
;
import
org.apache.logging.log4j.LogManager
;
import
org.apache.logging.log4j.Logger
;
...
...
@@ -70,6 +71,8 @@ public class CityDoctorValidation {
String
xmlOutput
=
getXmlOutput
(
argParser
);
String
pdfOutput
=
getPdfOutput
(
argParser
);
String
outputFile
=
getOutputFile
(
argParser
,
true
);
lookForDbConfigFileParameter
(
argParser
);
lookForDbLocationParameter
(
argParser
);
ValidationConfiguration
config
=
getValidationConfig
(
argParser
);
startValidationProcess
(
inputFile
,
xmlOutput
,
pdfOutput
,
config
,
outputFile
);
...
...
@@ -81,18 +84,42 @@ public class CityDoctorValidation {
if
(
optional
)
{
return
null
;
}
logger
.
error
(
Localization
.
getText
(
"CityDoctorValidation.noOutputFile"
));
logger
.
fatal
(
Localization
.
getText
(
"CityDoctorValidation.noOutputFile"
));
System
.
exit
(
11
);
}
List
<
String
>
outFiles
=
argParser
.
getValues
(
"out"
);
if
(
outFiles
.
size
()
!=
1
)
{
logger
.
error
(
Localization
.
getText
(
"CityDoctorValidation.notExactlyOneOutputFile"
));
logger
.
fatal
(
Localization
.
getText
(
"CityDoctorValidation.notExactlyOneOutputFile"
));
System
.
exit
(
12
);
}
outputFile
=
outFiles
.
get
(
0
);
return
outputFile
;
}
public
static
void
lookForDbConfigFileParameter
(
ArgumentParser
argParser
)
{
if
(!
argParser
.
containsOption
(
"db_config"
))
{
return
;
}
List
<
String
>
dbConfigFiles
=
argParser
.
getValues
(
"db_config"
);
if
(
dbConfigFiles
.
size
()
==
1
)
{
DatabaseSettings
.
loadPropertiesFromFile
(
new
File
(
dbConfigFiles
.
get
(
0
)));
}
else
if
(
dbConfigFiles
.
size
()
>
1
)
{
logger
.
warn
(
"More than one DB config file provided! Using standard config instead."
);
}
}
public
static
void
lookForDbLocationParameter
(
ArgumentParser
argParser
)
{
if
(!
argParser
.
containsOption
(
"db_location"
))
{
return
;
}
List
<
String
>
dbLocations
=
argParser
.
getValues
(
"db_location"
);
if
(
dbLocations
.
size
()
==
1
)
{
DatabaseSettings
.
setDBLocation
(
dbLocations
.
get
(
0
));
}
else
if
(
dbLocations
.
size
()
>
1
)
{
logger
.
warn
(
"More than one DB location provided! Using standard location instead."
);
}
}
/**
* Validates the cityGML input file with a standard validation configuration.
*
...
...
@@ -202,7 +229,7 @@ public class CityDoctorValidation {
if
(
argParser
.
containsOption
(
"pdfreport"
))
{
List
<
String
>
reportFiles
=
argParser
.
getValues
(
"pdfreport"
);
if
(
reportFiles
.
size
()
!=
1
)
{
logger
.
error
(
Localization
.
getText
(
"CityDoctorValidation.notExactlyOnePDFOutput"
));
logger
.
fatal
(
Localization
.
getText
(
"CityDoctorValidation.notExactlyOnePDFOutput"
));
System
.
exit
(
6
);
}
return
reportFiles
.
get
(
0
);
...
...
@@ -214,7 +241,7 @@ public class CityDoctorValidation {
if
(
argParser
.
containsOption
(
"xmlreport"
))
{
List
<
String
>
reportFiles
=
argParser
.
getValues
(
"xmlreport"
);
if
(
reportFiles
.
size
()
!=
1
)
{
logger
.
error
(
Localization
.
getText
(
"CityDoctorValidation.notExactlyOneXMLOutput"
));
logger
.
fatal
(
Localization
.
getText
(
"CityDoctorValidation.notExactlyOneXMLOutput"
));
System
.
exit
(
5
);
}
return
reportFiles
.
get
(
0
);
...
...
@@ -231,7 +258,7 @@ public class CityDoctorValidation {
if
(
argParser
.
containsOption
(
"config"
))
{
List
<
String
>
configFiles
=
argParser
.
getValues
(
"config"
);
if
(
configFiles
.
size
()
!=
1
)
{
logger
.
error
(
Localization
.
getText
(
"CityDoctorValidation.notExactlyOneConfigFile"
));
logger
.
fatal
(
Localization
.
getText
(
"CityDoctorValidation.notExactlyOneConfigFile"
));
System
.
exit
(
4
);
}
return
ValidationConfiguration
.
loadValidationConfigFile
(
configFiles
.
get
(
0
));
...
...
@@ -240,7 +267,7 @@ public class CityDoctorValidation {
logger
.
warn
(
Localization
.
getText
(
"CityDoctorValidation.loadingDefaultConfig"
));
return
ValidationConfiguration
.
loadStandardValidationConfig
();
}
else
{
logger
.
error
(
Localization
.
getText
(
"CityDoctorValidation.noConfig"
));
logger
.
fatal
(
Localization
.
getText
(
"CityDoctorValidation.noConfig"
));
System
.
exit
(
7
);
return
null
;
}
...
...
@@ -252,7 +279,7 @@ public class CityDoctorValidation {
if
(
argParser
.
containsOption
(
"out"
))
{
List
<
String
>
outFiles
=
argParser
.
getValues
(
"out"
);
if
(
outFiles
.
size
()
!=
1
)
{
logger
.
error
(
Localization
.
getText
(
"CityDoctorValidation.multipleOutputFiles"
));
logger
.
fatal
(
Localization
.
getText
(
"CityDoctorValidation.multipleOutputFiles"
));
System
.
exit
(
3
);
}
return
outFiles
.
get
(
0
);
...
...
@@ -271,12 +298,12 @@ public class CityDoctorValidation {
if
(
optional
)
{
return
null
;
}
logger
.
error
(
Localization
.
getText
(
"CityDoctorValidation.noInputFile"
));
logger
.
fatal
(
Localization
.
getText
(
"CityDoctorValidation.noInputFile"
));
System
.
exit
(
1
);
}
List
<
String
>
inFiles
=
argParser
.
getValues
(
"in"
);
if
(
inFiles
.
size
()
!=
1
)
{
logger
.
error
(
Localization
.
getText
(
"CityDoctorValidation.notExactlyOneInputFile"
));
logger
.
fatal
(
Localization
.
getText
(
"CityDoctorValidation.notExactlyOneInputFile"
));
System
.
exit
(
2
);
}
inputFile
=
inFiles
.
get
(
0
);
...
...
CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/CityDoctorValidationCLI.java
View file @
6d0d664e
...
...
@@ -2,6 +2,7 @@ package de.hft.stuttgart.citydoctor2;
import
de.hft.stuttgart.citydoctor2.check.ValidationConfiguration
;
import
de.hft.stuttgart.citydoctor2.database.DatabaseSettings
;
import
de.hft.stuttgart.citydoctor2.exceptions.CityDoctorWriteException
;
import
de.hft.stuttgart.citydoctor2.parser.CityGmlParseException
;
import
de.hft.stuttgart.citydoctor2.parser.InvalidGmlFileException
;
...
...
@@ -26,20 +27,30 @@ public class CityDoctorValidationCLI implements Runnable {
private
File
config
;
@Option
(
names
=
{
"-x"
,
"--xml"
,
"--xmlReport"
},
description
=
"Create a XML validation report at target location"
,
description
=
"Create
s
a XML validation report at target location"
,
defaultValue
=
Option
.
NULL_VALUE
)
private
String
xmlReport
;
@Option
(
names
=
{
"-p"
,
"--pdf"
,
"--pdfReport"
},
description
=
"Create a PDF validation report at target location"
,
description
=
"Create
s
a PDF validation report at target location"
,
defaultValue
=
Option
.
NULL_VALUE
)
private
String
pdfReport
;
@Option
(
names
=
{
"-o"
,
"--out"
,
"--output"
},
description
=
"Create a validated copy of the CityGML input with QualityADE at target location."
,
description
=
"Create
s
a validated copy of the CityGML input with QualityADE at target location."
,
defaultValue
=
Option
.
NULL_VALUE
)
private
String
output
;
@Option
(
names
=
{
"--db_location"
,
"--db_loc"
},
description
=
"Creates the embedded database file at target location instead of CityDoctor's working directory."
,
defaultValue
=
Option
.
NULL_VALUE
)
private
String
dbLocation
;
@Option
(
names
=
{
"-db"
,
"--db_config"
,
"--db_settings"
},
description
=
"Filepath to a .properties file for overriding the default configuration of the embedded database"
,
defaultValue
=
Option
.
NULL_VALUE
)
private
String
dbConfig
;
@Override
public
void
run
(){
try
{
...
...
@@ -49,6 +60,12 @@ public class CityDoctorValidationCLI implements Runnable {
}
else
{
valConfig
=
ValidationConfiguration
.
loadValidationConfigFile
(
config
.
getAbsolutePath
());
}
if
(
dbConfig
!=
null
){
DatabaseSettings
.
loadPropertiesFromFile
(
new
File
(
dbConfig
));
}
if
(
dbLocation
!=
null
)
{
DatabaseSettings
.
setDBLocation
(
dbLocation
);
}
CityDoctorValidation
.
startValidationProcess
(
input
,
xmlReport
,
pdfReport
,
valConfig
,
output
);
}
catch
(
CityDoctorWriteException
|
CityGmlParseException
|
IOException
|
InvalidGmlFileException
e
)
{
throw
new
RuntimeException
(
e
);
...
...
CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/Checker.java
View file @
6d0d664e
...
...
@@ -47,6 +47,7 @@ import java.util.concurrent.ThreadPoolExecutor;
import
java.util.concurrent.TimeUnit
;
import
java.util.concurrent.atomic.AtomicInteger
;
import
javax.annotation.Nullable
;
import
javax.xml.XMLConstants
;
import
javax.xml.transform.Result
;
import
javax.xml.transform.Source
;
...
...
@@ -77,7 +78,6 @@ import de.hft.stuttgart.citydoctor2.datastructure.CityDoctorModel;
import
de.hft.stuttgart.citydoctor2.datastructure.CityObject
;
import
de.hft.stuttgart.citydoctor2.datastructure.FeatureType
;
import
de.hft.stuttgart.citydoctor2.datastructure.GmlId
;
import
de.hft.stuttgart.citydoctor2.datastructure.ImplicitGeometryHolder
;
import
de.hft.stuttgart.citydoctor2.parser.CityGmlConsumer
;
import
de.hft.stuttgart.citydoctor2.parser.CityGmlParseException
;
import
de.hft.stuttgart.citydoctor2.parser.CityGmlParser
;
...
...
@@ -150,8 +150,8 @@ public class Checker {
return
;
}
File
xmlFile
=
new
File
(
xmlOutput
);
if
(
xmlFile
.
getParentFile
()
!=
null
)
{
xmlFile
.
getParentFile
().
mkdirs
(
);
if
(
xmlFile
.
getParentFile
()
!=
null
&&
xmlFile
.
getParentFile
().
mkdirs
()
)
{
logger
.
trace
(
"Xml dir created"
);
}
Reporter
reporter
=
new
XmlValidationReporter
();
try
(
BufferedOutputStream
bos
=
new
BufferedOutputStream
(
new
FileOutputStream
(
xmlFile
.
getAbsolutePath
())))
{
...
...
@@ -166,9 +166,10 @@ public class Checker {
return
;
}
File
pdfFile
=
new
File
(
pdfOutput
);
if
(
pdfFile
.
getParentFile
()
!=
null
)
{
pdfFile
.
getParentFile
().
mkdirs
(
);
if
(
pdfFile
.
getParentFile
()
!=
null
&&
pdfFile
.
getParentFile
().
mkdirs
())
{
logger
.
trace
(
"Pdf dir created"
);
}
Reporter
reporter
=
new
PdfReporter
();
try
(
BufferedOutputStream
bos
=
new
BufferedOutputStream
(
new
FileOutputStream
(
pdfFile
.
getAbsolutePath
())))
{
reporter
.
writeReport
(
checkConfig
,
bos
,
model
,
config
);
...
...
@@ -243,7 +244,6 @@ public class Checker {
private
void
handleSchematronResults
(
SvrlContentHandler
handler
)
{
CityObjectCache
cache
=
model
.
getCache
();
handleSchematronErrorsGlobal
(
handler
.
getGeneralErrors
());
Map
<
String
,
CityObject
>
featureMap
=
new
HashMap
<>();
boolean
onlySchematron
=
execLayers
.
isEmpty
();
if
(
onlySchematron
)
{
CheckableUtilsVisitor
visitor
=
new
CheckableUtilsVisitor
()
{
...
...
@@ -252,12 +252,7 @@ public class Checker {
checkable
.
setValidated
(
true
);
}
};
model
.
createFeatureStream
().
forEach
(
f
->
{
featureMap
.
put
(
f
.
getGmlId
().
getGmlString
(),
f
);
f
.
accept
(
visitor
);
});
}
else
{
model
.
createFeatureStream
().
forEach
(
f
->
featureMap
.
put
(
f
.
getGmlId
().
getGmlString
(),
f
));
model
.
createFeatureStream
().
forEach
(
co
->
co
.
accept
(
visitor
));
}
handler
.
getFeatureErrors
().
forEach
((
k
,
v
)
->
{
String
trimmedId
=
k
.
trim
();
...
...
@@ -641,7 +636,7 @@ public class Checker {
ArrayList
<
Check
>
checkList
=
new
ArrayList
<>();
for
(
CheckId
id
:
enabledCheck
)
{
Check
c
=
checkConfig
.
getCheckForId
(
id
);
c
.
init
(
parameterMap
.
get
(
id
)
,
parserConfig
);
c
.
init
(
parameterMap
,
parserConfig
);
checkList
.
add
(
c
);
}
return
checkList
;
...
...
@@ -657,7 +652,6 @@ public class Checker {
parameterMap
.
compute
(
proto
.
getCheckId
(),
(
k
,
v
)
->
{
if
(
v
==
null
)
{
v
=
new
HashMap
<>();
v
.
put
(
GlobalParameters
.
NUMBER_OF_ROUNDING_PLACES
,
config
.
getNumberOfRoundingPlacesAsString
());
v
.
put
(
GlobalParameters
.
MIN_VERTEX_DISTANCE
,
config
.
getMinVertexDistanceAsString
());
}
v
.
putAll
(
e
.
getValue
().
getParameters
());
...
...
@@ -709,12 +703,11 @@ public class Checker {
(
threadCount
,
threadCount
,
60L
,
TimeUnit
.
SECONDS
,
new
LinkedBlockingQueue
<>());
try
{
long
startTime
=
System
.
nanoTime
();
ImplicitGeometryHolder
.
preparePrototypeGeometriesForChecking
();
List
<
Future
<
GmlId
>>
futures
=
runChecksOnFeatures
(
exec
,
cache
,
features
,
checkedCount
,
l
);
List
<
GmlId
>
missedFeatures
=
getMissedFeatures
(
futures
);
ImplicitGeometryHolder
.
clearMetaDataOfPrototypeGeometries
();
if
(!
missedFeatures
.
isEmpty
()){
logger
.
error
(
Localization
.
getText
(
"Checker.dbUnresponsive"
));
if
(
logger
.
isDebugEnabled
()){
...
...
@@ -748,8 +741,22 @@ public class Checker {
}
}
/**
* Runs the validation on a list of Features. The checks are run asynchronously and multithreaded. Returns a list
* of {@link Future Futures} for evaluation if Features failed to be checked due to exceptions or failing to load from
* the cache. If failed, the respective Future will return the affected Feature's GmlID.
* <p>Checks finding an error in a Feature, or not having their dependencies met, are not a check-failure in this
* context.<p/>
* @param exec the threadpool for the tasks
* @param cache the cache of the model
* @param ids the list of GmlIds to check
* @param checkedCount the number of already checked Features for tracking the total progress of validation across retries
* @param l a listener for the progress of the validation, can be null
* @return a list of Futures containing the GmlId of Features that failed to execute all checks, and contain null otherwise
* @throws InterruptedException if interrupted while invoking the tasks for checking
*/
private
List
<
Future
<
GmlId
>>
runChecksOnFeatures
(
ExecutorService
exec
,
CityObjectCache
cache
,
List
<
GmlId
>
ids
,
AtomicInteger
checkedCount
,
ProgressListener
l
)
@Nullable
ProgressListener
l
)
throws
InterruptedException
{
float
featureSum
=
ids
.
size
()
+
(
float
)
checkedCount
.
get
();
...
...
@@ -763,11 +770,9 @@ public class Checker {
}
if
(
Thread
.
interrupted
()){
Thread
.
currentThread
().
interrupt
();
return
null
;
return
id
;
}
co
.
prepareForChecking
();
executeChecksForCityObject
(
co
);
co
.
clearMetaInformation
();
cache
.
put
(
co
);
checkedCount
.
incrementAndGet
();
if
(
l
!=
null
){
...
...
@@ -782,7 +787,7 @@ public class Checker {
private
List
<
GmlId
>
getMissedFeatures
(
List
<
Future
<
GmlId
>>
futures
)
throws
InterruptedException
{
List
<
GmlId
>
missedList
=
new
ArrayList
<>();
Set
<
ExecutionException
>
errors
=
new
HashSet
<>();
Set
<
String
>
errors
=
new
HashSet
<>();
for
(
Future
<
GmlId
>
future
:
futures
)
{
try
{
GmlId
gmlId
=
future
.
get
();
...
...
@@ -792,7 +797,7 @@ public class Checker {
}
catch
(
ExecutionException
e
){
logger
.
debug
(
"A Task failed due to an unexpected exception"
,
e
);
logger
.
debug
(
e
.
getCause
());
errors
.
add
(
e
);
errors
.
add
(
e
.
getCause
().
toString
()
);
}
}
if
(!
errors
.
isEmpty
())
{
...
...
@@ -840,7 +845,9 @@ public class Checker {
if
(!
filterObject
(
co
))
{
return
;
}
co
.
prepareForChecking
();
executeChecksForCheckable
(
co
);
co
.
clearMetaInformation
();
}
/**
...
...
@@ -939,7 +946,7 @@ public class Checker {
SvrlContentHandler
handler
=
executeSchematronValidationIfAvailable
(
config
,
inputFile
);
CityGmlConsumer
con
=
new
StreamCityGmlConsumer
(
c
,
xmlReporter
,
pdfReporter
,
handler
,
config
,
l
);
// parse and validate
CityGmlParser
.
streamCityGml
(
inputFile
.
getAbsolutePath
()
,
config
.
getParserConfiguration
(),
con
,
outputFile
);
CityGmlParser
.
streamCityGml
(
inputFile
,
config
.
getParserConfiguration
(),
con
,
outputFile
);
// write reports if available
writeReport
(
xmlReporter
);
...
...
CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/CheckContainer.java
View file @
6d0d664e
...
...
@@ -62,7 +62,7 @@ public class CheckContainer extends Check {
}
@Override
public
void
init
(
Map
<
String
,
String
>
parameters
,
ParserConfiguration
config
)
{
public
void
init
(
Map
<
CheckId
,
Map
<
String
,
String
>
>
parameters
,
ParserConfiguration
config
)
{
check
.
init
(
parameters
,
config
);
}
...
...
CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/AllPolygonsWrongOrientationCheck.java
View file @
6d0d664e
...
...
@@ -118,13 +118,15 @@ public class AllPolygonsWrongOrientationCheck extends Check {
// find the centroid of a triangle
Vector3d
centroid
=
t
.
getCentroid
();
// create a point outside of the geometry
Vector3d
outsidePoint
=
bbox
[
0
].
minus
(
Vector3d
.
X
,
5
).
minus
(
Vector3d
.
Y
,
5
).
minus
(
Vector3d
.
Z
,
5
);
Vector3d
outsideDif1
=
new
Vector3d
(
5
,
5
,
5
);
Vector3d
outsidePoint
=
bbox
[
0
].
minus
(
outsideDif1
);
// create a second point outside of the geometry
// the check can fail if the building is exactly oriented so that the ray is
// parallel to a side of the building
// in order to avoid this we check two rays, if one of those says it is oriented
// correctly it is oriented correctly
Vector3d
secondOutsidePoint
=
bbox
[
0
].
minus
(
Vector3d
.
X
,
5
).
minus
(
Vector3d
.
Y
,
10
).
minus
(
Vector3d
.
Z
,
5
);
Vector3d
outsideDif2
=
new
Vector3d
(
7
,
10
,
5
);
Vector3d
secondOutsidePoint
=
bbox
[
0
].
minus
(
outsideDif2
);
return
checkIfGeometryIsWrongOriented
(
tessPolygons
,
centroid
,
outsidePoint
)
||
checkIfGeometryIsWrongOriented
(
tessPolygons
,
centroid
,
secondOutsidePoint
);
}
...
...
@@ -164,8 +166,8 @@ public class AllPolygonsWrongOrientationCheck extends Check {
}
private
Triangle3d
findSuitableTriangle
(
TesselatedPolygon
p1
)
{
Triangle3d
t
=
p1
.
getTriangles
().
get
(
0
)
;
double
maxArea
=
t
.
getArea
()
;
Triangle3d
t
=
null
;
double
maxArea
=
Double
.
NEGATIVE_INFINITY
;
for
(
Triangle3d
tri
:
p1
.
getTriangles
())
{
double
area
=
tri
.
getArea
();
if
(
area
>
maxArea
)
{
...
...
@@ -173,6 +175,9 @@ public class AllPolygonsWrongOrientationCheck extends Check {
t
=
tri
;
}
}
if
(
t
==
null
)
{
throw
new
IllegalStateException
(
"No suitable triangle found"
);
}
return
t
;
}
...
...
CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/DuplicatePointsCheck.java
View file @
6d0d664e
...
...
@@ -30,10 +30,12 @@ import de.hft.stuttgart.citydoctor2.check.CheckId;
import
de.hft.stuttgart.citydoctor2.check.CheckResult
;
import
de.hft.stuttgart.citydoctor2.check.RequirementType
;
import
de.hft.stuttgart.citydoctor2.check.Checkable
;
import
de.hft.stuttgart.citydoctor2.check.GlobalParameters
;
import
de.hft.stuttgart.citydoctor2.check.Requirement
;
import
de.hft.stuttgart.citydoctor2.check.ResultStatus
;
import
de.hft.stuttgart.citydoctor2.check.error.ConsecutivePointSameError
;
import
de.hft.stuttgart.citydoctor2.check.error.RingDuplicatePointError
;
import
de.hft.stuttgart.citydoctor2.checks.Checks
;
import
de.hft.stuttgart.citydoctor2.checks.util.CollectionUtils
;
import
de.hft.stuttgart.citydoctor2.datastructure.LinearRing
;
import
de.hft.stuttgart.citydoctor2.datastructure.Vertex
;
...
...
@@ -52,8 +54,6 @@ import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration;
*/
public
class
DuplicatePointsCheck
extends
Check
{
private
static
final
String
EPSILON_NAME
=
"minVertexDistance"
;
private
static
final
List
<
CheckId
>
dependencies
;
static
{
...
...
@@ -66,11 +66,16 @@ public class DuplicatePointsCheck extends Check {
classes
.
add
(
LinearRing
.
class
);
}
private
double
epsilon
=
0.0001
;
private
double
epsilon
=
Checks
.
MIN_VERTEX_DISTANCE_DEFAULT
;
@Override
public
void
init
(
Map
<
String
,
String
>
params
,
ParserConfiguration
config
)
{
String
epsilonString
=
params
.
get
(
EPSILON_NAME
);
public
void
init
(
Map
<
CheckId
,
Map
<
String
,
String
>>
params
,
ParserConfiguration
config
)
{
Map
<
String
,
String
>
localParameters
=
params
.
get
(
getCheckId
());
if
(
localParameters
==
null
)
{
// no parameters
return
;
}
String
epsilonString
=
localParameters
.
get
(
GlobalParameters
.
MIN_VERTEX_DISTANCE
);
if
(
epsilonString
!=
null
)
{
epsilon
=
Double
.
parseDouble
(
epsilonString
);
}
...
...
CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/InteriorDisconnectedCheck.java
View file @
6d0d664e
...
...
@@ -29,10 +29,12 @@ import de.hft.stuttgart.citydoctor2.check.Check;
import
de.hft.stuttgart.citydoctor2.check.CheckError
;
import
de.hft.stuttgart.citydoctor2.check.CheckId
;
import
de.hft.stuttgart.citydoctor2.check.CheckResult
;
import
de.hft.stuttgart.citydoctor2.check.GlobalParameters
;
import
de.hft.stuttgart.citydoctor2.check.RequirementType
;
import
de.hft.stuttgart.citydoctor2.check.Requirement
;
import
de.hft.stuttgart.citydoctor2.check.ResultStatus
;
import
de.hft.stuttgart.citydoctor2.check.error.PolygonInteriorDisconnectedError
;
import
de.hft.stuttgart.citydoctor2.checks.Checks
;
import
de.hft.stuttgart.citydoctor2.checks.util.CollectionUtils
;
import
de.hft.stuttgart.citydoctor2.datastructure.LinearRing
;
import
de.hft.stuttgart.citydoctor2.datastructure.Polygon
;
...
...
@@ -49,8 +51,6 @@ import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration;
*/
public
class
InteriorDisconnectedCheck
extends
Check
{
private
static
final
String
EPSILON_NAME
=
"minVertexDistance"
;
private
static
final
List
<
CheckId
>
dependencies
;
static
{
...
...
@@ -63,11 +63,16 @@ public class InteriorDisconnectedCheck extends Check {
dependencies
=
Collections
.
unmodifiableList
(
deps
);
}
private
double
epsilon
=
0.0001
;
private
double
epsilon
=
Checks
.
MIN_VERTEX_DISTANCE_DEFAULT
;
@Override
public
void
init
(
Map
<
String
,
String
>
params
,
ParserConfiguration
config
)
{
String
epsilonString
=
params
.
get
(
EPSILON_NAME
);
public
void
init
(
Map
<
CheckId
,
Map
<
String
,
String
>>
params
,
ParserConfiguration
config
)
{
Map
<
String
,
String
>
localParameters
=
params
.
get
(
getCheckId
());
if
(
localParameters
==
null
)
{
// no parameters
return
;
}
String
epsilonString
=
localParameters
.
get
(
GlobalParameters
.
MIN_VERTEX_DISTANCE
);
if
(
epsilonString
!=
null
)
{
epsilon
=
Double
.
parseDouble
(
epsilonString
);
}
...
...
CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/NullAreaCheck.java
View file @
6d0d664e
...
...
@@ -44,9 +44,10 @@ import de.hft.stuttgart.citydoctor2.tesselation.TesselatedRing;
public
class
NullAreaCheck
extends
Check
{
private
static
final
String
DELTA_NAME
=
"delta"
;
private
static
final
List
<
CheckId
>
dependencies
;
private
double
delta
=
0.0001
;
static
{
ArrayList
<
CheckId
>
deps
=
new
ArrayList
<>();
deps
.
add
(
CheckId
.
C_GE_R_TOO_FEW_POINTS
);
...
...
@@ -55,12 +56,16 @@ public class NullAreaCheck extends Check {
dependencies
=
Collections
.
unmodifiableList
(
deps
);
}
private
double
delta
=
0.0001
;
@Override
public
void
init
(
Map
<
String
,
String
>
parameters
,
ParserConfiguration
config
)
{
if
(
parameters
.
containsKey
(
DELTA_NAME
))
{
delta
=
Double
.
parseDouble
(
parameters
.
get
(
DELTA_NAME
));
public
void
init
(
Map
<
CheckId
,
Map
<
String
,
String
>>
params
,
ParserConfiguration
config
)
{
Map
<
String
,
String
>
localParameters
=
params
.
get
(
getCheckId
());
if
(
localParameters
==
null
)
{
// no parameters
return
;
}
String
epsilonString
=
localParameters
.
get
(
DELTA_NAME
);
if
(
epsilonString
!=
null
)
{
delta
=
Double
.
parseDouble
(
epsilonString
);
}
}
...
...
CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/PlanarCheck.java
View file @
6d0d664e
...
...
@@ -81,17 +81,22 @@ public class PlanarCheck extends Check {
private
double
delta
=
0.01
;
@Override
public
void
init
(
Map
<
String
,
String
>
parameters
,
ParserConfiguration
config
)
{
if
(
parameters
.
containsKey
(
TYPE
))
{
planarCheckType
=
parameters
.
get
(
TYPE
).
toLowerCase
();
public
void
init
(
Map
<
CheckId
,
Map
<
String
,
String
>>
parameters
,
ParserConfiguration
config
)
{
Map
<
String
,
String
>
localParameters
=
parameters
.
get
(
getCheckId
());
if
(
localParameters
==
null
)
{
// no parameters
return
;
}
if
(
localParameters
.
containsKey
(
TYPE
))
{
planarCheckType
=
localParameters
.
get
(
TYPE
).
toLowerCase
();
}
else
{
throw
new
IllegalStateException
(
"Parameter "
+
TYPE
+
" is missing from parameters"
);
}
if
(
p
arameters
.
containsKey
(
ANGLE_TOLERANCE
))
{
rad
=
Math
.
toRadians
(
Double
.
parseDouble
(
p
arameters
.
get
(
ANGLE_TOLERANCE
)));
if
(
localP
arameters
.
containsKey
(
ANGLE_TOLERANCE
))
{
rad
=
Math
.
toRadians
(
Double
.
parseDouble
(
localP
arameters
.
get
(
ANGLE_TOLERANCE
)));
}
if
(
p
arameters
.
containsKey
(
DISTANCE_TOLERANCE
))
{
delta
=
Double
.
parseDouble
(
p
arameters
.
get
(
DISTANCE_TOLERANCE
));
if
(
localP
arameters
.
containsKey
(
DISTANCE_TOLERANCE
))
{
delta
=
Double
.
parseDouble
(
localP
arameters
.
get
(
DISTANCE_TOLERANCE
));
}
}
...
...
CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/RingSelfIntCheck.java
View file @
6d0d664e
...
...
@@ -30,6 +30,7 @@ import de.hft.stuttgart.citydoctor2.check.Check;
import
de.hft.stuttgart.citydoctor2.check.CheckError
;
import
de.hft.stuttgart.citydoctor2.check.CheckId
;
import
de.hft.stuttgart.citydoctor2.check.CheckResult
;
import
de.hft.stuttgart.citydoctor2.check.GlobalParameters
;
import
de.hft.stuttgart.citydoctor2.check.Requirement
;
import
de.hft.stuttgart.citydoctor2.check.RequirementType
;
import
de.hft.stuttgart.citydoctor2.check.ResultStatus
;
...
...
@@ -74,13 +75,11 @@ import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration;
public
class
RingSelfIntCheck
extends
Check
{
private
static
final
String
EPSILON_NAME
=
"minVertexDistance"
;
private
static
final
List
<
CheckId
>
dependencies
;
private
double
degeneratedRingTolerance
=
0.01
;
private
double
epsilon
=
Checks
.
MIN_VERTEX_DISTANCE_DEFAULT
;
private
static
final
List
<
CheckId
>
dependencies
;
static
{
ArrayList
<
CheckId
>
deps
=
new
ArrayList
<>();
deps
.
add
(
CheckId
.
C_GE_R_TOO_FEW_POINTS
);
...
...
@@ -139,13 +138,18 @@ public class RingSelfIntCheck extends Check {
}
@Override
public
void
init
(
Map
<
String
,
String
>
parameters
,
ParserConfiguration
config
)
{
String
epsilonString
=
parameters
.
get
(
EPSILON_NAME
);
public
void
init
(
Map
<
CheckId
,
Map
<
String
,
String
>>
parameters
,
ParserConfiguration
config
)
{
Map
<
String
,
String
>
localParameters
=
parameters
.
get
(
getCheckId
());
if
(
localParameters
==
null
)
{
// no parameters
return
;
}
String
epsilonString
=
localParameters
.
get
(
GlobalParameters
.
MIN_VERTEX_DISTANCE
);
if
(
epsilonString
!=
null
)
{
epsilon
=
Double
.
parseDouble
(
epsilonString
);
}
if
(
p
arameters
.
containsKey
(
Requirement
.
DEGENERATED_RING_TOLERANCE
))
{
degeneratedRingTolerance
=
Double
.
parseDouble
(
p
arameters
.
get
(
Requirement
.
DEGENERATED_RING_TOLERANCE
));
if
(
localP
arameters
.
containsKey
(
Requirement
.
DEGENERATED_RING_TOLERANCE
))
{
degeneratedRingTolerance
=
Double
.
parseDouble
(
localP
arameters
.
get
(
Requirement
.
DEGENERATED_RING_TOLERANCE
));
}
}
...
...
CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/SolidSelfIntCheck.java
View file @
6d0d664e
...
...
@@ -108,10 +108,16 @@ public class SolidSelfIntCheck extends Check {
}
@Override
public
void
init
(
Map
<
String
,
String
>
parameters
,
ParserConfiguration
config
)
{
if
(
parameters
.
containsKey
(
PlanarCheck
.
DISTANCE_TOLERANCE
))
{
delta
=
Double
.
parseDouble
(
parameters
.
get
(
PlanarCheck
.
DISTANCE_TOLERANCE
));
public
void
init
(
Map
<
CheckId
,
Map
<
String
,
String
>>
parameters
,
ParserConfiguration
config
)
{
Map
<
String
,
String
>
planarParameters
=
parameters
.
get
(
CheckId
.
C_GE_P_NON_PLANAR
);
if
(
planarParameters
==
null
)
{
// no parameters
return
;
}
planarParameters
.
computeIfPresent
(
PlanarCheck
.
DISTANCE_TOLERANCE
,
(
k
,
v
)
->
{
delta
=
Double
.
parseDouble
(
v
);
return
v
;
});
}
public
SolidSelfIntCheck
()
{
...
...
CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/semantics/IsWallCheck.java
View file @
6d0d664e
...
...
@@ -75,13 +75,18 @@ public class IsWallCheck extends Check {
private
double
upperAngleCos
=
Math
.
cos
(
135
*
Math
.
PI
/
180
);
@Override
public
void
init
(
Map
<
String
,
String
>
params
,
ParserConfiguration
config
)
{
String
lowerAngleString
=
params
.
get
(
LOWER_ANGLE_NAME
);
public
void
init
(
Map
<
CheckId
,
Map
<
String
,
String
>>
params
,
ParserConfiguration
config
)
{
Map
<
String
,
String
>
localParameters
=
params
.
get
(
getCheckId
());
if
(
localParameters
==
null
)
{
// no parameters
return
;
}
String
lowerAngleString
=
localParameters
.
get
(
LOWER_ANGLE_NAME
);
if
(
lowerAngleString
!=
null
)
{
lowerAngleCos
=
Double
.
parseDouble
(
lowerAngleString
);
lowerAngleCos
=
Math
.
cos
(
lowerAngleCos
*
Math
.
PI
/
180
);
}
String
upperAngleString
=
param
s
.
get
(
UPPER_ANGLE_NAME
);
String
upperAngleString
=
localParameter
s
.
get
(
UPPER_ANGLE_NAME
);
if
(
upperAngleString
!=
null
)
{
upperAngleCos
=
Double
.
parseDouble
(
upperAngleString
);
upperAngleCos
=
Math
.
cos
(
upperAngleCos
*
Math
.
PI
/
180
);
...
...
CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/semantics/RoofSurfaceUnfragmentedCheck.java
View file @
6d0d664e
...
...
@@ -29,6 +29,7 @@ import de.hft.stuttgart.citydoctor2.check.CheckId;
import
de.hft.stuttgart.citydoctor2.check.CheckResult
;
import
de.hft.stuttgart.citydoctor2.check.Requirement
;
import
de.hft.stuttgart.citydoctor2.check.RequirementType
;
import
de.hft.stuttgart.citydoctor2.check.ResultStatus
;
import
de.hft.stuttgart.citydoctor2.checks.util.CollectionUtils
;
import
de.hft.stuttgart.citydoctor2.checks.util.UnfragmentedCheck
;
import
de.hft.stuttgart.citydoctor2.datastructure.BoundarySurface
;
...
...
@@ -60,8 +61,13 @@ public class RoofSurfaceUnfragmentedCheck extends Check {
}
@Override
public
void
init
(
Map
<
String
,
String
>
params
,
ParserConfiguration
config
)
{
String
maxAngleString
=
params
.
get
(
MAX_ANGLE_DEVIATION
);
public
void
init
(
Map
<
CheckId
,
Map
<
String
,
String
>>
params
,
ParserConfiguration
config
)
{
Map
<
String
,
String
>
localParameters
=
params
.
get
(
getCheckId
());
if
(
localParameters
==
null
)
{
// no parameters
return
;
}
String
maxAngleString
=
localParameters
.
get
(
MAX_ANGLE_DEVIATION
);
if
(
maxAngleString
!=
null
)
{
maxAngleDeviation
=
Math
.
toRadians
(
Double
.
parseDouble
(
maxAngleString
));
}
...
...
@@ -75,6 +81,10 @@ public class RoofSurfaceUnfragmentedCheck extends Check {
}
// only use lod1 and lod2 polygons
CheckResult
cr
=
UnfragmentedCheck
.
checkForFragmentedBoundarySurfaces
(
this
,
bs
,
maxAngleDeviation
);
if
(
cr
.
getResultStatus
()
==
ResultStatus
.
OK
)
{
// check if neighboring roof surfaces have the same average normal
// they should be merged
}
bs
.
addCheckResult
(
cr
);
}
...
...
CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/util/CollectionUtils.java
View file @
6d0d664e
...
...
@@ -20,7 +20,9 @@ package de.hft.stuttgart.citydoctor2.checks.util;
import
java.util.Arrays
;
import
java.util.HashSet
;
import
java.util.List
;
import
java.util.Set
;
import
java.util.stream.IntStream
;
public
class
CollectionUtils
{
...
...
@@ -40,4 +42,11 @@ public class CollectionUtils {
return
set
;
}
public
static
<
T
>
List
<
List
<
T
>>
partition
(
List
<
T
>
list
,
int
chunkSize
){
return
IntStream
.
iterate
(
0
,
i
->
i
<
list
.
size
(),
i
->
i
+
chunkSize
)
.
mapToObj
(
i
->
list
.
subList
(
i
,
Math
.
min
(
i
+
chunkSize
,
list
.
size
())))
.
toList
();
}
}
CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/util/SelfIntersectionUtil.java
View file @
6d0d664e
...
...
@@ -40,8 +40,6 @@ import org.locationtech.jts.geom.impl.CoordinateArraySequence;
import
org.locationtech.jts.operation.overlay.OverlayOp
;
import
org.locationtech.jts.operation.overlay.snap.SnapIfNeededOverlayOp
;
import
Jama.EigenvalueDecomposition
;
import
Jama.Matrix
;
import
de.hft.stuttgart.citydoctor2.check.GeometrySelfIntersection
;
import
de.hft.stuttgart.citydoctor2.datastructure.ConcretePolygon
;
import
de.hft.stuttgart.citydoctor2.datastructure.Geometry
;
...
...
@@ -55,10 +53,8 @@ import de.hft.stuttgart.citydoctor2.edge.EdgePolygon;
import
de.hft.stuttgart.citydoctor2.edge.IntersectPlanarPolygons
;
import
de.hft.stuttgart.citydoctor2.edge.MeshSurface
;
import
de.hft.stuttgart.citydoctor2.edge.PolygonPolygonIntersection
;
import
de.hft.stuttgart.citydoctor2.math.CovarianceMatrix
;
import
de.hft.stuttgart.citydoctor2.math.MovedPolygon
;
import
de.hft.stuttgart.citydoctor2.math.MovedRing
;
import
de.hft.stuttgart.citydoctor2.math.OrthogonalRegressionPlane
;
import
de.hft.stuttgart.citydoctor2.math.Plane
;
import
de.hft.stuttgart.citydoctor2.math.PlaneSegmentIntersection
;
import
de.hft.stuttgart.citydoctor2.math.PlaneSegmentIntersection.Type
;
...
...
@@ -90,46 +86,80 @@ public class SelfIntersectionUtil {
private
SelfIntersectionUtil
()
{
}
// Tesselation and filtering degenerated triangles
// Tesselation and filtering degenerated triangles
private
static
List
<
TesselatedPolygon
>
tesselateAndFilter
(
Geometry
g
,
double
delta
)
{
List
<
TesselatedPolygon
>
tesselatedPolygons
=
new
ArrayList
<>();
List
<
TesselatedPolygon
>
tesselatedPolygons
=
new
ArrayList
<>();
for
(
Polygon
p
:
g
.
getPolygons
())
{
TesselatedPolygon
tessPolygon
=
EarcutTesselator
.
tesselatePolygon
(
p
);
for
(
Iterator
<
Triangle3d
>
iterator
=
tessPolygon
.
getTriangles
().
iterator
();
iterator
.
hasNext
();)
{
Triangle3d
t
=
iterator
.
next
();
if
(!
t
.
hasMinExtent
(
delta
))
{
iterator
.
remove
();
}
}
tesselatedPolygons
.
add
(
tessPolygon
);
}
return
tesselatedPolygons
;
}
for
(
Polygon
p
:
g
.
getPolygons
())
{
TesselatedPolygon
tessPolygon
=
EarcutTesselator
.
tesselatePolygon
(
p
);
/*
* @ Baris Numanoglu
*
* Version with IdentityHashmap tree query
*/
public
static
List
<
PolygonIntersection
>
calculateSolidSelfIntersection
(
Geometry
g
,
double
delta
,
BoundingVolumeHierarchyTree
<
Polygon
>
tree
)
{
for
(
Iterator
<
Triangle3d
>
it
=
tessPolygon
.
getTriangles
().
iterator
();
it
.
hasNext
();)
{
Triangle3d
t
=
it
.
next
();
List
<
TesselatedPolygon
>
tesselatedPolygons
=
tesselateAndFilter
(
g
,
delta
);
List
<
Vector3d
>
vertices
=
new
ArrayList
<>(
3
);
vertices
.
add
(
t
.
getP1
());
vertices
.
add
(
t
.
getP2
());
vertices
.
add
(
t
.
getP3
());
// Map: original ConcretePolygon (identity) -> index in tesselatedPolygons
IdentityHashMap
<
ConcretePolygon
,
Integer
>
indexByOriginal
=
new
IdentityHashMap
<>();
for
(
int
i
=
0
;
i
<
tesselatedPolygons
.
size
();
i
++)
{
Polygon
p
=
tesselatedPolygons
.
get
(
i
).
getOriginal
();
// returns Polygon
ConcretePolygon
orig
=
p
.
getOriginal
();
// returns ConcretePolygon
indexByOriginal
.
put
(
orig
,
i
);
}
Vector3d
centroid
=
CovarianceMatrix
.
getCentroid
(
vertices
);
EigenvalueDecomposition
ed
=
OrthogonalRegressionPlane
.
decompose
(
vertices
,
centroid
);
List
<
PolygonIntersection
>
intersections
=
new
ArrayList
<>();
Matrix
eigenValues
=
ed
.
getD
();
double
ev1
=
eigenValue
s
.
get
(
1
,
1
);
for
(
int
i
=
0
;
i
<
tesselatedPolygons
.
size
()
-
1
;
i
++)
{
TesselatedPolygon
p1
=
tesselatedPolygon
s
.
get
(
i
);
if
(
ev1
<
delta
)
{
it
.
remove
();
}
}
// Query AABB based on original polygon (consistent with tree construction)
ConcretePolygon
p1Orig
=
p1
.
getOriginal
().
getOriginal
();
// p1:teselatedPoly->Polygon->Concrete Polygon
AABB
q
=
AABB
.
of
(
p1Orig
);
tesselatedPolygons
.
add
(
tessPolygon
);
}
List
<
Polygon
>
candidates
=
tree
.
getAllIntersectingElements
(
q
);
if
(
candidates
.
isEmpty
())
{
continue
;
// no candidate ergo no overlap
}
for
(
Polygon
cand
:
candidates
)
{
ConcretePolygon
candOrig
=
cand
.
getOriginal
();
Integer
jObj
=
indexByOriginal
.
get
(
candOrig
);
if
(
jObj
==
null
)
{
continue
;
// candidate not in tesselated-Liste (RE: edge case)
}
int
j
=
jObj
.
intValue
();
if
(
j
<=
i
)
{
continue
;
// avoids double pairwise checks and self-pair
}
TesselatedPolygon
p2
=
tesselatedPolygons
.
get
(
j
);
// TODO may be later a further tree in here
GeometrySelfIntersection
inter
=
doPolygonsIntersect
(
p1
,
p2
,
delta
);
if
(
inter
!=
null
)
{
intersections
.
add
(
PolygonIntersection
.
triangles
(
inter
.
t1
(),
inter
.
t2
()));
}
}
}
return
te
s
se
latedPolyg
ons
;
return
in
te
r
se
cti
ons
;
}
/*
* @ Baris Numanoglu
*
* New Version with tree query
* */
public
static
List
<
PolygonIntersection
>
calculateSolidSelfIntersectionWithTree
(
Geometry
g
,
double
delta
,
...
...
@@ -188,105 +218,27 @@ public class SelfIntersectionUtil {
);
}
/*
* @ Baris Numanoglu
*
* Version with IdentityHashmap tree query
* */
public
static
List
<
PolygonIntersection
>
calculateSolidSelfIntersection
(
Geometry
g
,
double
delta
,
BoundingVolumeHierarchyTree
<
Polygon
>
tree
)
{
List
<
TesselatedPolygon
>
tesselatedPolygons
=
tesselateAndFilter
(
g
,
delta
);
// Map: original ConcretePolygon (identity) -> index in tesselatedPolygons
IdentityHashMap
<
ConcretePolygon
,
Integer
>
indexByOriginal
=
new
IdentityHashMap
<>();
for
(
int
i
=
0
;
i
<
tesselatedPolygons
.
size
();
i
++)
{
Polygon
p
=
tesselatedPolygons
.
get
(
i
).
getOriginal
();
// returns Polygon
ConcretePolygon
orig
=
p
.
getOriginal
();
// returns ConcretePolygon
indexByOriginal
.
put
(
orig
,
i
);
}
List
<
PolygonIntersection
>
intersections
=
new
ArrayList
<>();
for
(
int
i
=
0
;
i
<
tesselatedPolygons
.
size
()
-
1
;
i
++)
{
TesselatedPolygon
p1
=
tesselatedPolygons
.
get
(
i
);
// Query AABB based on original polygon (consistent with tree construction)
ConcretePolygon
p1Orig
=
p1
.
getOriginal
().
getOriginal
();
// p1:teselatedPoly->Polygon->Concrete Polygon
AABB
q
=
AABB
.
of
(
p1Orig
);
List
<
Polygon
>
candidates
=
tree
.
getAllIntersectingElements
(
q
);
if
(
candidates
.
isEmpty
())
{
continue
;
// no candidate ergo no overlap
}
for
(
Polygon
cand
:
candidates
)
{
ConcretePolygon
candOrig
=
cand
.
getOriginal
();
Integer
jObj
=
indexByOriginal
.
get
(
candOrig
);
if
(
jObj
==
null
)
{
continue
;
// candidate not in tesselated-Liste (RE: edge case)
}
int
j
=
jObj
.
intValue
();
if
(
j
<=
i
)
{
continue
;
// avoids double pairwise checks and self-pair
}
TesselatedPolygon
p2
=
tesselatedPolygons
.
get
(
j
);
// TODO may be later a further tree in here
GeometrySelfIntersection
inter
=
doPolygonsIntersect
(
p1
,
p2
,
delta
);
if
(
inter
!=
null
)
{
intersections
.
add
(
PolygonIntersection
.
triangles
(
inter
.
t1
(),
inter
.
t2
()));
}
}
}
return
intersections
;
}
// Older Version without Tree: going to be used for comparison
// Older Version without Tree: going to be used for comparison
public
static
List
<
PolygonIntersection
>
calculateSolidSelfIntersection
(
Geometry
g
,
double
delta
)
{
List
<
TesselatedPolygon
>
tesselatedPolygons
=
tesselateAndFilter
(
g
,
delta
);
List
<
TesselatedPolygon
>
tesselatedPolygons
=
tesselateAndFilter
(
g
,
delta
);
List
<
PolygonIntersection
>
intersections
=
new
ArrayList
<>();
for
(
int
i
=
0
;
i
<
tesselatedPolygons
.
size
()
-
1
;
i
++)
{
TesselatedPolygon
p1
=
tesselatedPolygons
.
get
(
i
);
for
(
int
j
=
i
+
1
;
j
<
tesselatedPolygons
.
size
();
j
++)
{
TesselatedPolygon
p2
=
tesselatedPolygons
.
get
(
j
);
GeometrySelfIntersection
inter
=
doPolygonsIntersect
(
p1
,
p2
,
delta
);
if
(
inter
!=
null
)
{
intersections
.
add
(
PolygonIntersection
.
triangles
(
inter
.
t1
(),
inter
.
t2
()));
}
}
}
return
intersections
;
List
<
PolygonIntersection
>
intersections
=
new
ArrayList
<>();
for
(
int
i
=
0
;
i
<
tesselatedPolygons
.
size
()
-
1
;
i
++)
{
TesselatedPolygon
p1
=
tesselatedPolygons
.
get
(
i
);
for
(
int
j
=
i
+
1
;
j
<
tesselatedPolygons
.
size
();
j
++)
{
TesselatedPolygon
p2
=
tesselatedPolygons
.
get
(
j
);
GeometrySelfIntersection
inter
=
doPolygonsIntersect
(
p1
,
p2
,
delta
);
if
(
inter
!=
null
)
{
intersections
.
add
(
PolygonIntersection
.
triangles
(
inter
.
t1
(),
inter
.
t2
()));
}
}
}
return
intersections
;
}
// The very oldest Version Without Tree and internal tesselation
public
static
List
<
PolygonIntersection
>
calculateSolidSelfIntersection0
(
Geometry
g
,
double
delta
)
{
List
<
TesselatedPolygon
>
tesselatedPolygons
=
new
ArrayList
<>();
for
(
Polygon
p
:
g
.
getPolygons
())
{
TesselatedPolygon
tessPolygon
=
EarcutTesselator
.
tesselatePolygon
(
p
);
for
(
Iterator
<
Triangle3d
>
iterator
=
tessPolygon
.
getTriangles
().
iterator
();
iterator
.
hasNext
();)
{
Triangle3d
t
=
iterator
.
next
();
List
<
Vector3d
>
vertices
=
new
ArrayList
<>(
3
);
vertices
.
add
(
t
.
getP1
());
vertices
.
add
(
t
.
getP2
());
vertices
.
add
(
t
.
getP3
());
Vector3d
centroid
=
CovarianceMatrix
.
getCentroid
(
vertices
);
EigenvalueDecomposition
ed
=
OrthogonalRegressionPlane
.
decompose
(
vertices
,
centroid
);
Matrix
eigenValues
=
ed
.
getD
();
double
[]
eigenValuesArray
=
new
double
[
3
];
eigenValuesArray
[
0
]
=
eigenValues
.
get
(
0
,
0
);
eigenValuesArray
[
1
]
=
eigenValues
.
get
(
1
,
1
);
eigenValuesArray
[
2
]
=
eigenValues
.
get
(
2
,
2
);
if
(
eigenValuesArray
[
1
]
<
delta
)
{
iterator
.
remove
();
}
}
tesselatedPolygons
.
add
(
tessPolygon
);
}
List
<
TesselatedPolygon
>
tesselatedPolygons
=
tesselateAndFilter
(
g
,
delta
);
List
<
PolygonIntersection
>
intersections
=
new
ArrayList
<>();
for
(
int
i
=
0
;
i
<
tesselatedPolygons
.
size
()
-
1
;
i
++)
{
...
...
@@ -301,7 +253,7 @@ public class SelfIntersectionUtil {
}
return
intersections
;
}
public
static
GeometrySelfIntersection
doesSolidSelfIntersect
(
Geometry
g
,
double
epsilon
)
{
return
selfIntersectionJava
(
g
,
epsilon
);
}
...
...
@@ -621,10 +573,11 @@ public class SelfIntersectionUtil {
return
null
;
}
private
static
GeometrySelfIntersection
doPolygonsIntersect
(
TesselatedPolygon
p1
,
TesselatedPolygon
p2
,
double
epsilon
)
{
private
static
GeometrySelfIntersection
doPolygonsIntersect
(
TesselatedPolygon
p1
,
TesselatedPolygon
p2
,
double
epsilon
)
{
for
(
int
p1Index
=
0
;
p1Index
<
p1
.
getTriangles
().
size
();
p1Index
++)
{
Triangle3d
t1
=
p1
.
getTriangles
().
get
(
p1Index
);
for
(
int
p2Index
=
0
;
p2Index
<
p2
.
getTriangles
().
size
();
p2Index
++)
{
Triangle3d
t1
=
p1
.
getTriangles
().
get
(
p1Index
);
Triangle3d
t2
=
p2
.
getTriangles
().
get
(
p2Index
);
if
(
t1
.
doesIntersect
(
t2
,
epsilon
))
{
logger
.
trace
(
"{} intersects {}"
,
t1
,
t2
);
...
...
CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/util/UnfragmentedCheck.java
View file @
6d0d664e
...
...
@@ -24,7 +24,7 @@ import java.util.List;
import
de.hft.stuttgart.citydoctor2.check.Check
;
import
de.hft.stuttgart.citydoctor2.check.CheckResult
;
import
de.hft.stuttgart.citydoctor2.check.ResultStatus
;
import
de.hft.stuttgart.citydoctor2.check.error.Surface
Unf
ragmentedError
;
import
de.hft.stuttgart.citydoctor2.check.error.Surface
F
ragmentedError
;
import
de.hft.stuttgart.citydoctor2.datastructure.BoundarySurface
;
import
de.hft.stuttgart.citydoctor2.datastructure.Geometry
;
import
de.hft.stuttgart.citydoctor2.datastructure.Polygon
;
...
...
@@ -75,7 +75,7 @@ public class UnfragmentedCheck {
double
dot
=
averageNormal
.
dot
(
n
);
double
acos
=
Math
.
acos
(
dot
);
if
(
acos
>
delta
)
{
Surface
Unf
ragmentedError
err
=
new
Surface
Unf
ragmentedError
(
bs
,
acos
);
Surface
F
ragmentedError
err
=
new
Surface
F
ragmentedError
(
bs
,
acos
);
return
new
CheckResult
(
c
,
ResultStatus
.
ERROR
,
err
);
}
}
...
...
Prev
1
2
3
4
5
6
7
Next
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment