Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
CityGML
CityGMLViewer
Commits
bb8385f5
Commit
bb8385f5
authored
May 17, 2022
by
Matthias Betz
Browse files
added parameters for color configuration files
parent
282708a8
Changes
4
Hide whitespace changes
Inline
Side-by-side
src/main/java/de/hft/stuttgart/citygml/viewer/CityGMLViewer.java
View file @
bb8385f5
...
...
@@ -23,6 +23,9 @@ import java.io.IOException;
import
java.nio.ByteBuffer
;
import
java.nio.DoubleBuffer
;
import
java.nio.FloatBuffer
;
import
java.nio.file.Files
;
import
java.nio.file.Path
;
import
java.nio.file.Paths
;
import
java.util.ArrayList
;
import
java.util.List
;
import
java.util.concurrent.ExecutionException
;
...
...
@@ -71,9 +74,11 @@ import de.hft.stuttgart.citygml.viewer.datastructure.BoundingBox;
import
de.hft.stuttgart.citygml.viewer.datastructure.Polygon
;
import
de.hft.stuttgart.citygml.viewer.math.Vector3d
;
import
de.hft.stuttgart.citygml.viewer.parser.CityGMLParser
;
import
de.hft.stuttgart.citygml.viewer.parser.ColorHandler
;
import
de.hft.stuttgart.citygml.viewer.parser.FeatureMapper
;
import
de.hft.stuttgart.citygml.viewer.parser.ObservedInputStream
;
import
de.hft.stuttgart.citygml.viewer.parser.ParserConfiguration
;
import
de.hft.stuttgart.citygml.viewer.parser.PolygonColorMapper
;
public
class
CityGMLViewer
{
...
...
@@ -100,6 +105,10 @@ public class CityGMLViewer {
private
static
final
String
CITY_OBJECT_MEMBER
=
"cityObjectMember"
;
private
static
List
<
QName
>
chunkProperties
=
new
ArrayList
<>();
private
static
PolygonColorMapper
colorMapper
=
null
;
private
static
ColorHandler
colorHandler
=
null
;
private
static
boolean
useDebug
=
false
;
static
{
chunkProperties
.
add
(
new
QName
(
CityGMLConstants
.
CITYGML_1_0_CORE_NAMESPACE
,
CITY_OBJECT_MEMBER
));
chunkProperties
.
add
(
new
QName
(
CityGMLConstants
.
CITYGML_2_0_CORE_NAMESPACE
,
CITY_OBJECT_MEMBER
));
...
...
@@ -107,23 +116,9 @@ public class CityGMLViewer {
}
public
static
void
main
(
String
[]
args
)
{
boolean
useDebug
=
false
;
for
(
String
arg
:
args
)
{
if
(
arg
.
equals
(
"-debug"
))
{
useDebug
=
true
;
}
}
File
f
=
null
;
if
(
args
!=
null
&&
args
.
length
==
1
)
{
f
=
new
File
(
args
[
0
]);
if
(!
f
.
exists
()
||
f
.
isDirectory
())
{
// no file given
f
=
null
;
}
}
if
(
f
==
null
)
{
f
=
showFileChooserDialog
();
}
f
=
parseArguments
(
args
,
f
);
try
{
setupWindow
(
f
,
useDebug
);
}
catch
(
Exception
e
)
{
...
...
@@ -131,7 +126,7 @@ public class CityGMLViewer {
if
(
e
.
getCause
()
!=
null
)
{
message
+=
"\nCause: "
+
e
.
getCause
().
getClass
().
getSimpleName
()
+
": "
+
e
.
getCause
().
getMessage
();
}
JOptionPane
.
showMessage
Dialog
(
null
,
message
,
"Error"
,
JOptionPane
.
ERROR_MESSAGE
);
showError
Dialog
(
message
);
}
finally
{
for
(
PolygonViewInformation
view
:
viewing
)
{
view
.
destroy
();
...
...
@@ -139,6 +134,68 @@ public class CityGMLViewer {
}
}
private
static
File
parseArguments
(
String
[]
args
,
File
f
)
{
Path
colorsPath
=
null
;
Path
mappingPath
=
null
;
boolean
nextColorsFile
=
false
;
boolean
nextMappingsFile
=
false
;
for
(
String
arg
:
args
)
{
if
(
nextColorsFile
)
{
nextColorsFile
=
false
;
String
file
=
arg
;
Path
path
=
Paths
.
get
(
file
);
if
(!
Files
.
exists
(
path
)
||
!
Files
.
isRegularFile
(
path
))
{
showErrorDialog
(
"Name specified for colors is not a file"
);
System
.
exit
(
1
);
}
colorsPath
=
path
;
}
else
if
(
nextMappingsFile
)
{
nextMappingsFile
=
false
;
String
file
=
arg
;
Path
path
=
Paths
.
get
(
file
);
if
(!
Files
.
exists
(
path
)
||
!
Files
.
isRegularFile
(
path
))
{
showErrorDialog
(
"Name specified for mapping is not a file"
);
System
.
exit
(
2
);
}
mappingPath
=
path
;
}
else
if
(
arg
.
equals
(
"-debug"
))
{
useDebug
=
true
;
}
else
if
(
arg
.
equals
(
"-colors"
))
{
// next argument must be a file for the color properties
nextColorsFile
=
true
;
}
else
if
(
arg
.
equals
(
"-mapping"
))
{
nextMappingsFile
=
true
;
}
else
{
// this must be the citygml file
String
file
=
arg
;
Path
path
=
Paths
.
get
(
file
);
if
(!
Files
.
exists
(
path
)
||
!
Files
.
isRegularFile
(
path
))
{
showErrorDialog
(
"CityGML file specified does is not a file or does not exist"
);
System
.
exit
(
3
);
}
}
}
// default values for missing parameters
if
(
colorsPath
==
null
)
{
colorsPath
=
Paths
.
get
(
"color.properties"
);
}
if
(
mappingPath
==
null
)
{
mappingPath
=
Paths
.
get
(
"colorMappings.csv"
);
}
if
(
f
==
null
)
{
f
=
showFileChooserDialog
();
}
colorMapper
=
new
PolygonColorMapper
(
mappingPath
);
colorHandler
=
new
ColorHandler
(
colorsPath
);
return
f
;
}
public
static
void
showErrorDialog
(
String
msg
)
{
JOptionPane
.
showMessageDialog
(
null
,
msg
,
"Error"
,
JOptionPane
.
ERROR_MESSAGE
);
}
private
static
File
showFileChooserDialog
()
{
File
f
=
null
;
try
(
MemoryStack
stack
=
MemoryStack
.
stackPush
())
{
...
...
@@ -160,7 +217,7 @@ public class CityGMLViewer {
int
[]
pixels
=
new
int
[
width
*
height
];
int
bindex
;
// allocate space for RBG pixels
ByteBuffer
fb
=
ByteBuffer
.
allocateDirect
(
width
*
height
*
3
);
ByteBuffer
fb
=
MemoryUtil
.
memAlloc
(
width
*
height
*
3
);
// grab a copy of the current frame contents as RGB
GL11
.
glReadPixels
(
0
,
0
,
width
,
height
,
GL11
.
GL_RGB
,
GL11
.
GL_UNSIGNED_BYTE
,
fb
);
...
...
@@ -171,6 +228,7 @@ public class CityGMLViewer {
bindex
=
i
*
3
;
pixels
[
i
]
=
(
fb
.
get
(
bindex
)
<<
16
)
+
(
fb
.
get
(
bindex
+
1
)
<<
8
)
+
(
fb
.
get
(
bindex
+
2
));
}
MemoryUtil
.
memFree
(
fb
);
// Allocate colored pixel to buffered Image
imageIn
.
setRGB
(
0
,
0
,
width
,
height
,
pixels
,
0
,
width
);
...
...
@@ -405,7 +463,7 @@ public class CityGMLViewer {
while
(
reader
.
hasNext
())
{
CityGMLChunk
nextChunk
=
reader
.
nextChunk
();
mappers
.
add
(
service
.
submit
(()
->
{
FeatureMapper
mapper
=
new
FeatureMapper
(
config
);
FeatureMapper
mapper
=
new
FeatureMapper
(
config
,
colorMapper
,
colorHandler
);
AbstractFeature
feature
=
nextChunk
.
build
();
feature
.
accept
(
mapper
);
return
mapper
;
...
...
src/main/java/de/hft/stuttgart/citygml/viewer/parser/ColorHandler.java
View file @
bb8385f5
...
...
@@ -2,6 +2,7 @@ package de.hft.stuttgart.citygml.viewer.parser;
import
java.awt.Color
;
import
java.io.FileReader
;
import
java.nio.file.Path
;
import
java.util.Properties
;
import
java.util.logging.Logger
;
...
...
@@ -9,19 +10,19 @@ public class ColorHandler {
private
static
final
Logger
logger
=
Logger
.
getLogger
(
ColorHandler
.
class
.
getName
());
private
static
Color
groundColor
=
new
Color
(
0.9411765f
,
0.9019608f
,
0.54901963f
);
private
static
Color
roofColor
=
Color
.
RED
;
private
static
Color
doorColor
=
Color
.
ORANGE
;
private
static
Color
windowColor
=
new
Color
(
0.0f
,
0.5019608f
,
0.5019608f
);
private
static
Color
wallColor
=
Color
.
WHITE
;
private
static
Color
bridgeColor
=
new
Color
(
1.0f
,
0.49803922f
,
0.3137255f
);
private
static
Color
landColor
=
new
Color
(
0.64705884f
,
0.16470589f
,
0.16470589f
);
private
static
Color
transportationColor
=
new
Color
(
1.0f
,
1.0f
,
0.0f
);
private
static
Color
vegetationColor
=
new
Color
(
0.5647059f
,
0.93333334f
,
0.5647059f
);
private
static
Color
waterColor
=
new
Color
(
0.5294118f
,
0.80784315f
,
0.98039216f
);
private
Color
groundColor
=
new
Color
(
0.9411765f
,
0.9019608f
,
0.54901963f
);
private
Color
roofColor
=
Color
.
RED
;
private
Color
doorColor
=
Color
.
ORANGE
;
private
Color
windowColor
=
new
Color
(
0.0f
,
0.5019608f
,
0.5019608f
);
private
Color
wallColor
=
Color
.
WHITE
;
private
Color
bridgeColor
=
new
Color
(
1.0f
,
0.49803922f
,
0.3137255f
);
private
Color
landColor
=
new
Color
(
0.64705884f
,
0.16470589f
,
0.16470589f
);
private
Color
transportationColor
=
new
Color
(
1.0f
,
1.0f
,
0.0f
);
private
Color
vegetationColor
=
new
Color
(
0.5647059f
,
0.93333334f
,
0.5647059f
);
private
Color
waterColor
=
new
Color
(
0.5294118f
,
0.80784315f
,
0.98039216f
);
static
{
try
(
FileReader
reader
=
new
FileReader
(
"color.properties"
))
{
public
ColorHandler
(
Path
path
)
{
try
(
FileReader
reader
=
new
FileReader
(
path
.
toFile
()
))
{
Properties
props
=
new
Properties
();
props
.
load
(
reader
);
Color
parsedGroundColor
=
parseColor
(
props
.
getProperty
(
"groundColor"
));
...
...
@@ -69,10 +70,7 @@ public class ColorHandler {
}
}
private
ColorHandler
()
{
}
private
static
Color
parseColor
(
String
colorString
)
{
private
Color
parseColor
(
String
colorString
)
{
if
(
colorString
==
null
)
{
return
null
;
}
...
...
@@ -88,43 +86,43 @@ public class ColorHandler {
}
}
public
static
Color
getGroundColor
()
{
public
Color
getGroundColor
()
{
return
groundColor
;
}
public
static
Color
getRoofColor
()
{
public
Color
getRoofColor
()
{
return
roofColor
;
}
public
static
Color
getDoorColor
()
{
public
Color
getDoorColor
()
{
return
doorColor
;
}
public
static
Color
getWindowColor
()
{
public
Color
getWindowColor
()
{
return
windowColor
;
}
public
static
Color
getWallColor
()
{
public
Color
getWallColor
()
{
return
wallColor
;
}
public
static
Color
getBridgeColor
()
{
public
Color
getBridgeColor
()
{
return
bridgeColor
;
}
public
static
Color
getLandColor
()
{
public
Color
getLandColor
()
{
return
landColor
;
}
public
static
Color
getTransportationColor
()
{
public
Color
getTransportationColor
()
{
return
transportationColor
;
}
public
static
Color
getVegetationColor
()
{
public
Color
getVegetationColor
()
{
return
vegetationColor
;
}
public
static
Color
getWaterColor
()
{
public
Color
getWaterColor
()
{
return
waterColor
;
}
...
...
src/main/java/de/hft/stuttgart/citygml/viewer/parser/FeatureMapper.java
View file @
bb8385f5
...
...
@@ -79,9 +79,14 @@ public class FeatureMapper extends ObjectWalker {
private
ProjCoordinate
p1
=
new
ProjCoordinate
();
private
ProjCoordinate
p2
=
new
ProjCoordinate
();
public
FeatureMapper
(
ParserConfiguration
config
)
{
currentColor
=
ColorHandler
.
getWallColor
();
private
PolygonColorMapper
colorMapper
;
private
ColorHandler
colorHandler
;
public
FeatureMapper
(
ParserConfiguration
config
,
PolygonColorMapper
colorMapper
,
ColorHandler
colorHandler
)
{
this
.
colorMapper
=
colorMapper
;
this
.
colorHandler
=
colorHandler
;
this
.
config
=
config
;
currentColor
=
colorHandler
.
getWallColor
();
lod1Polygons
=
new
ArrayList
<>();
lod2Polygons
=
new
ArrayList
<>();
lod3Polygons
=
new
ArrayList
<>();
...
...
@@ -95,7 +100,7 @@ public class FeatureMapper extends ObjectWalker {
}
Color
setColor
=
currentColor
;
if
(
gmlPoly
.
getId
()
!=
null
)
{
Color
mappedColor
=
PolygonC
olorMapper
.
getColorForPolygon
(
gmlPoly
.
getId
());
Color
mappedColor
=
c
olorMapper
.
getColorForPolygon
(
gmlPoly
.
getId
());
if
(
mappedColor
!=
null
)
{
setColor
=
mappedColor
;
}
...
...
@@ -140,46 +145,46 @@ public class FeatureMapper extends ObjectWalker {
}
currentPolygons
.
add
(
viewerPoly
);
}
@Override
public
void
visit
(
OtherConstruction
otherConstruction
)
{
currentColor
=
C
olorHandler
.
getWallColor
();
currentColor
=
c
olorHandler
.
getWallColor
();
super
.
visit
(
otherConstruction
);
}
@Override
public
void
visit
(
CityFurniture
cityFurniture
)
{
currentColor
=
C
olorHandler
.
getWallColor
();
currentColor
=
c
olorHandler
.
getWallColor
();
super
.
visit
(
cityFurniture
);
}
@Override
public
void
visit
(
CityObjectGroup
cityObjectGroup
)
{
currentColor
=
C
olorHandler
.
getWallColor
();
currentColor
=
c
olorHandler
.
getWallColor
();
super
.
visit
(
cityObjectGroup
);
}
@Override
public
void
visit
(
GenericLogicalSpace
genericLogicalSpace
)
{
currentColor
=
C
olorHandler
.
getWallColor
();
currentColor
=
c
olorHandler
.
getWallColor
();
super
.
visit
(
genericLogicalSpace
);
}
@Override
public
void
visit
(
GenericOccupiedSpace
genericOccupiedSpace
)
{
currentColor
=
C
olorHandler
.
getWallColor
();
currentColor
=
c
olorHandler
.
getWallColor
();
super
.
visit
(
genericOccupiedSpace
);
}
@Override
public
void
visit
(
GenericThematicSurface
genericThematicSurface
)
{
currentColor
=
C
olorHandler
.
getWallColor
();
currentColor
=
c
olorHandler
.
getWallColor
();
super
.
visit
(
genericThematicSurface
);
}
@Override
public
void
visit
(
GenericUnoccupiedSpace
genericUnoccupiedSpace
)
{
currentColor
=
C
olorHandler
.
getWallColor
();
currentColor
=
c
olorHandler
.
getWallColor
();
super
.
visit
(
genericUnoccupiedSpace
);
}
...
...
@@ -270,10 +275,10 @@ public class FeatureMapper extends ObjectWalker {
parseAbstractGeometry
(
ab
.
getLod2Solid
(),
lod2Polygons
);
parseAbstractGeometry
(
ab
.
getLod3Solid
(),
lod3Polygons
);
}
@Override
public
void
visit
(
AbstractBuilding
ab
)
{
currentColor
=
C
olorHandler
.
getWallColor
();
currentColor
=
c
olorHandler
.
getWallColor
();
super
.
visit
(
ab
);
DeprecatedPropertiesOfAbstractBuilding
deprecatedProperties
=
ab
.
getDeprecatedProperties
();
parseAbstractGeometry
(
deprecatedProperties
.
getLod1MultiSurface
(),
lod1Polygons
);
...
...
@@ -303,13 +308,13 @@ public class FeatureMapper extends ObjectWalker {
currentPolygons
=
lod1Polygons
;
break
;
}
currentColor
=
C
olorHandler
.
getLandColor
();
currentColor
=
c
olorHandler
.
getLandColor
();
super
.
visit
(
tinRelief
);
}
@Override
public
void
visit
(
WaterBody
wb
)
{
currentColor
=
C
olorHandler
.
getWaterColor
();
currentColor
=
c
olorHandler
.
getWaterColor
();
super
.
visit
(
wb
);
parseAbstractGeometry
(
wb
.
getDeprecatedProperties
().
getLod1MultiSurface
(),
lod1Polygons
);
parseAbstractGeometry
(
wb
.
getDeprecatedProperties
().
getLod4Solid
(),
lod4Polygons
);
...
...
@@ -317,7 +322,7 @@ public class FeatureMapper extends ObjectWalker {
@Override
public
void
visit
(
PlantCover
pc
)
{
currentColor
=
C
olorHandler
.
getVegetationColor
();
currentColor
=
c
olorHandler
.
getVegetationColor
();
super
.
visit
(
pc
);
parseAbstractGeometry
(
pc
.
getDeprecatedProperties
().
getLod1MultiSurface
(),
lod1Polygons
);
parseMultiSolid
(
pc
.
getDeprecatedProperties
().
getLod1MultiSolid
(),
lod1Polygons
);
...
...
@@ -325,7 +330,6 @@ public class FeatureMapper extends ObjectWalker {
parseMultiSolid
(
pc
.
getDeprecatedProperties
().
getLod3MultiSolid
(),
lod3Polygons
);
parseMultiSolid
(
pc
.
getDeprecatedProperties
().
getLod4MultiSolid
(),
lod4Polygons
);
}
private
void
parseMultiSolid
(
MultiSolidProperty
ms
,
List
<
Polygon
>
polygons
)
{
if
(
ms
==
null
||
ms
.
getObject
()
==
null
)
{
...
...
@@ -338,27 +342,27 @@ public class FeatureMapper extends ObjectWalker {
@Override
public
void
visit
(
SolitaryVegetationObject
svo
)
{
currentColor
=
C
olorHandler
.
getVegetationColor
();
currentColor
=
c
olorHandler
.
getVegetationColor
();
super
.
visit
(
svo
);
parseAbstractGeometry
(
svo
.
getDeprecatedProperties
().
getLod1Geometry
(),
lod1Polygons
);
parseAbstractGeometry
(
svo
.
getDeprecatedProperties
().
getLod2Geometry
(),
lod2Polygons
);
parseAbstractGeometry
(
svo
.
getDeprecatedProperties
().
getLod3Geometry
(),
lod3Polygons
);
parseAbstractGeometry
(
svo
.
getDeprecatedProperties
().
getLod4Geometry
(),
lod4Polygons
);
}
@Override
public
void
visit
(
GroundSurface
groundSurface
)
{
Color
oldColor
=
currentColor
;
currentColor
=
C
olorHandler
.
getGroundColor
();
currentColor
=
c
olorHandler
.
getGroundColor
();
// process window
super
.
visit
(
groundSurface
);
currentColor
=
oldColor
;
}
@Override
public
void
visit
(
RoofSurface
roofSurface
)
{
Color
oldColor
=
currentColor
;
currentColor
=
C
olorHandler
.
getRoofColor
();
currentColor
=
c
olorHandler
.
getRoofColor
();
// process window
super
.
visit
(
roofSurface
);
currentColor
=
oldColor
;
...
...
@@ -377,13 +381,13 @@ public class FeatureMapper extends ObjectWalker {
@Override
public
void
visit
(
LandUse
landUse
)
{
currentColor
=
C
olorHandler
.
getLandColor
();
currentColor
=
c
olorHandler
.
getLandColor
();
super
.
visit
(
landUse
);
}
@Override
public
void
visit
(
AbstractTransportationSpace
ats
)
{
currentColor
=
C
olorHandler
.
getTransportationColor
();
currentColor
=
c
olorHandler
.
getTransportationColor
();
DeprecatedPropertiesOfAbstractTransportationSpace
deprecatedProperties
=
ats
.
getDeprecatedProperties
();
parseAbstractGeometry
(
ats
.
getLod1Solid
(),
lod1Polygons
);
parseAbstractGeometry
(
ats
.
getLod2Solid
(),
lod1Polygons
);
...
...
@@ -394,25 +398,24 @@ public class FeatureMapper extends ObjectWalker {
parseAbstractGeometry
(
ats
.
getLod3MultiSurface
(),
lod3Polygons
);
parseAbstractGeometry
(
deprecatedProperties
.
getLod4MultiSurface
(),
lod4Polygons
);
}
@Override
public
void
visit
(
Window
window
)
{
Color
oldColor
=
currentColor
;
currentColor
=
C
olorHandler
.
getWindowColor
();
currentColor
=
c
olorHandler
.
getWindowColor
();
// process window
super
.
visit
(
window
);
currentColor
=
oldColor
;
}
@Override
public
void
visit
(
Door
door
)
{
Color
oldColor
=
currentColor
;
currentColor
=
C
olorHandler
.
getDoorColor
();
currentColor
=
c
olorHandler
.
getDoorColor
();
super
.
visit
(
door
);
currentColor
=
oldColor
;
}
@Override
public
void
visit
(
AbstractFillingElement
ao
)
{
super
.
visit
(
ao
);
...
...
@@ -428,7 +431,7 @@ public class FeatureMapper extends ObjectWalker {
@Override
public
void
visit
(
AbstractBridge
ab
)
{
currentColor
=
C
olorHandler
.
getBridgeColor
();
currentColor
=
c
olorHandler
.
getBridgeColor
();
super
.
visit
(
ab
);
parseAbstractGeometry
(
ab
.
getDeprecatedProperties
().
getLod1MultiSurface
(),
lod1Polygons
);
parseAbstractGeometry
(
ab
.
getDeprecatedProperties
().
getLod4MultiSurface
(),
lod4Polygons
);
...
...
@@ -437,7 +440,7 @@ public class FeatureMapper extends ObjectWalker {
@Override
public
void
visit
(
BuildingInstallation
bi
)
{
currentColor
=
C
olorHandler
.
getWallColor
();
currentColor
=
c
olorHandler
.
getWallColor
();
super
.
visit
(
bi
);
}
...
...
src/main/java/de/hft/stuttgart/citygml/viewer/parser/PolygonColorMapper.java
View file @
bb8385f5
...
...
@@ -14,15 +14,11 @@ import javax.swing.JOptionPane;
public
class
PolygonColorMapper
{
private
static
final
String
IGNORING_ALL_POLYGON_COLOR_MAPPINGS
=
"\nIgnoring all polygon color mappings"
;
private
static
Function
<
String
,
Color
>
producer
;
private
static
Map
<
String
,
Color
>
colorMap
;
private
Function
<
String
,
Color
>
producer
;
private
Map
<
String
,
Color
>
colorMap
;
static
{
loadColorMap
();
}
private
static
void
loadColorMap
()
{
private
void
loadColorMap
(
Path
path
)
{
Path
colorMappingPath
=
Path
.
of
(
"colorMappings.csv"
);
if
(!
Files
.
exists
(
colorMappingPath
))
{
producer
=
s
->
null
;
...
...
@@ -73,11 +69,12 @@ public class PolygonColorMapper {
}
public
static
Color
getColorForPolygon
(
String
polygonId
)
{
public
Color
getColorForPolygon
(
String
polygonId
)
{
return
producer
.
apply
(
polygonId
);
}
private
PolygonColorMapper
()
{
public
PolygonColorMapper
(
Path
path
)
{
loadColorMap
(
path
);
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a 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