Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/v1.2'
Browse files Browse the repository at this point in the history
  • Loading branch information
hugoledoux committed Sep 6, 2016
2 parents a2d37df + b679d2f commit 50bce5a
Show file tree
Hide file tree
Showing 10 changed files with 402 additions and 229 deletions.
43 changes: 27 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,16 @@
Validation of 3D primitives according to the international standard ISO 19107.

The 3D primitives of GML (`<gml:Solid>`, `<gml:CompositeSurface>`, or `<gml:MultiSurface>`) are what it was built for, but it can be used to validate any 3D primitive, also in other formats.
It accepts as input any GML files (or one of the formats built upon it, such as CityGML), [OBJ](https://en.wikipedia.org/wiki/Wavefront_.obj_file), and [POLY](http://wias-berlin.de/software/tetgen/1.5/doc/manual/manual006.html#ff_poly).
It accepts as input any [GML files](https://en.wikipedia.org/wiki/Geography_Markup_Language) (or one of the formats built upon it, such as [CityGML](http://www.citygml.org)), [OBJ](https://en.wikipedia.org/wiki/Wavefront_.obj_file), and [POLY](http://wias-berlin.de/software/tetgen/1.5/doc/manual/manual006.html#ff_poly).
It simply scans the file looking for the 3D primitives and validates these according to the rules in ISO19107 (all the rest is ignored).
In a OBJ file, each primitive will be validated according to the ISO 19107 rules.

For `Solids`, the validation is performed hierarchically, ie first every polygon (embedded in 3D) is validated (by projecting it to a 2D plane and using [GEOS](http://trac.osgeo.org/geos/)), then every shell is validated (must be watertight, no self-intersections, orientation of the normals must be consistent and pointing outwards, etc), and finally the interactions between the shells are analysed.
This means that if a polygon of your solid is not valid, the validator will report that error but will *not* continue the validation (to avoid "cascading" errors).
This means that if one polygon of your solid is not valid, the validator will report that error but will *not* continue the validation (to avoid "cascading" errors).

For `CompositeSurfaces`, the surface formed by the polygons must be a 2-manifold.
For `CompositeSurfaces`, the surface formed by the polygons must be a 2-manifold, and the same hierarchical validation applies.

For `MultiSurfaces`, only the validation of the individual polygons is performed, ie are they valid according to the 2D rules, and are they planar?
For `MultiSurfaces`, only the validation of the individual polygons is performed, ie are they valid according to the 2D rules, and are they planar (we use a tolerance that can be defined)?

Most of the details are available in this scientific article, (if you use val3dity for scientific purposes please cite this article):

Expand All @@ -22,7 +22,7 @@ international standards for geographic information. *Computer-Aided Civil and In

## Web application

If you're you don't want to go through the troubles of compiling and/or installing val3dity, we suggest you use the [web application](http://geovalidation.bk.tudelft.nl/val3dity).
If you don't want to go through the troubles of compiling and/or installing val3dity, we suggest you use the [web application](http://geovalidation.bk.tudelft.nl/val3dity).
You upload your file to our server and get a validation report back.
We delete the file as soon as it has been validated.
However, a file is limited to 50MB.
Expand Down Expand Up @@ -55,30 +55,31 @@ To execute val3dity and see its options:

$ ./val3dity --help

To validate all the solids in a GML or CityGML file:
To validate all the solids in a GML or CityGML file and see a summary output:

$ ./val3dity myfile.gml
$ ./val3dity input.gml

Each `<gml:Solid>` in the file will be individually validated and a summary report will be output.

To validate all the MultiSurfaces in a GML or CityGML file:
For a full report in XML format:

$ ./val3dity myfile.gml -p MS
$ ./val3dity input.gml -r myreport.xml

For a full report in XML format:
To validate all the MultiSurfaces in a GML or CityGML file:

$ ./val3dity input.gml -p MS

$ ./val3dity myfile.gml --oxml myreport.xml

Other formats can also be used as input, the 3D primitives will then validated according to the ISO19107 definitions:
Other formats can also be used as input, the 3D primitives will then be validated according to the ISO19107 definitions:

1. OBJ: a file can contain more than 1 object (lines starting with "o", eg `o myobject`), each will be validated individually
1. [POLY](http://wias-berlin.de/software/tetgen/1.5/doc/manual/manual006.html#ff_poly), there are several examples of test datasets in the folder `data/poly/`, see the `README.txt`

In a OBJ, each primitive will be validated according to the ISO 19107 rules.
In an OBJ file, each primitive will be validated according to the ISO 19107 rules.
Observe that OBJ files have no mechanism to define inner shells, and thus a solid will be formed by only its exterior shell.
Validating one primitive in OBJ as a MultiSurface (`-p MS` option) will validate individually each surface according to the ISO 19107 rules, without ensuring that they form a 2-manifold.
If your OBJ contains only be triangles (often the case), then using the option `-p MS` is rather meaningless since most likely all your triangles are valid; validation could however catch cases where vertices are not referenced by faces (error `309: VERTICES_NOT_USED`) and collapsed triangles.
Validating it as a solid with verify whether the primitive is a 2-manifold, whether it is closed/watertight and whether all normals are pointing outwards.
Validating one primitive in an OBJ as a MultiSurface (`-p MS` option) will validate individually each surface according to the ISO 19107 rules, without ensuring that they form a 2-manifold.
If your OBJ contains only be triangles (often the case), then using the option `-p MS` is rather meaningless since most likely all your triangles are valid; validation could however catch cases where vertices are not referenced by faces (error `309: VERTICES_NOT_USED`), cases where triangles are collapsed to a line/point.
Validating it as a solid verify whether the primitive is a 2-manifold, ie whether it is closed/watertight and whether all normals are pointing outwards.

See the [FAQ for the web application](http://geovalidation.bk.tudelft.nl/val3dity/faq) for more details.

Expand All @@ -92,6 +93,16 @@ It is possible to define 2 tolerances for the planarity of surfaces with the fla

Similarly, the input points in a GML files are snapped together using a tolerance, which can be changed with `--snap_tolerance` (default is 1mm).

It is also possible to consider `Buildings` in a CityGML file when reporting, ie each primitive is validated individually and also a summary of the `Buildings` (which can contain several Solids and/or MultiSurfaces) is given.
In this case, each primitive in the report have the ID of the Building they represent.
It should be noticed that primitives not used for representing a `Building` is ignored when the flag `-B` is used.

$ ./val3dity input.gml -B

One could also do the same but for `Buildings` represented as `MultiSurface`:

$ ./val3dity input.gml -B -p MS

## Error reported

![](https://dl.dropboxusercontent.com/u/8129172/errorcodes.png)
Expand Down
17 changes: 16 additions & 1 deletion Shell.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ Shell::Shell(int id, double tol_snap)
_id = id;
_tol_snap = tol_snap;
_is_valid_2d = -1;
_vertices_added = 0;
}

Shell::~Shell()
Expand Down Expand Up @@ -166,16 +167,30 @@ std::string Shell::get_coords_key(Point3* p)
return s;
}

bool Shell::were_vertices_merged_during_parsing()
{
if (this->number_vertices() == this->_vertices_added)
return false;
else
return true;
}


int Shell::get_number_parsed_vertices()
{
return _vertices_added;
}


int Shell::add_point(Point3 p)
{
_vertices_added += 1;
auto it = _dPts.find(get_coords_key(&p));
if (it == _dPts.end())
{
_lsPts.push_back(p);
_dPts[get_coords_key(&p)] = (_lsPts.size() - 1);
return (_lsPts.size() - 1);

}
return it->second;
}
Expand Down
3 changes: 3 additions & 0 deletions Shell.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ class Shell
std::set<int> get_unique_error_codes();
void translate_vertices(double minx, double miny);
std::string get_poly_representation();
bool were_vertices_merged_during_parsing();
int get_number_parsed_vertices();

private:
int _id;
Expand All @@ -65,6 +67,7 @@ class Shell
CgalPolyhedron* _polyhedron;
double _tol_snap;
int _is_valid_2d; //-1: not done yet; 0: nope; 1: yes it's valid
int _vertices_added;

std::map<int, vector<std::tuple<std::string, std::string> > > _errors;

Expand Down
64 changes: 36 additions & 28 deletions Solid.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,14 @@ typedef CGAL::Polyhedron_copy_3<CgalPolyhedron, CgalPolyhedronE::HalfedgeDS> Pol
//-- to keep track of all gml:Solids in a GML file
int Solid::_counter = 0;

Solid::Solid()
Solid::Solid(InputTypes inputtype)
{
_id = std::to_string(_counter);
_counter++;
_is_valid = -1;
_id_building = "";
_id_buildingpart = "";
_inputtype = inputtype;
}


Expand All @@ -56,6 +59,8 @@ Solid::Solid(Shell* sh)
_shells.push_back(sh);
_id = std::to_string(_counter);
_counter++;
_id_building = "";
_id_buildingpart = "";
}

Solid::~Solid()
Expand Down Expand Up @@ -113,6 +118,26 @@ bool Solid::is_empty()
return false;
}

std::string Solid::get_id_building()
{
return _id_building;
}

void Solid::set_id_building(std::string id)
{
_id_building = id;
}

std::string Solid::get_id_buildingpart()
{
return _id_buildingpart;
}

void Solid::set_id_buildingpart(std::string id)
{
_id_buildingpart = id;
}


void Solid::translate_vertices()
{
Expand Down Expand Up @@ -188,6 +213,16 @@ std::string Solid::get_report_xml()
ss << "\t\t<numbershells>" << (this->num_ishells() + 1) << "</numbershells>" << std::endl;
ss << "\t\t<numberfaces>" << this->num_faces() << "</numberfaces>" << std::endl;
ss << "\t\t<numbervertices>" << this->num_vertices() << "</numbervertices>" << std::endl;
if (this->_inputtype == OBJ)
{
Shell* sh = this->get_oshell();
if (sh->were_vertices_merged_during_parsing() == true)
ss << "\t\t<numberverticesmerged>" << (sh->get_number_parsed_vertices() - sh->number_vertices()) << "</numberverticesmerged>" << std::endl;
}
if (_id_building.empty() == false)
ss << "\t\t<Building>" << this->get_id_building() << "</Building>" << std::endl;
if (_id_buildingpart.empty() == false)
ss << "\t\t<BuildingPart>" << this->get_id_buildingpart() << "</BuildingPart>" << std::endl;
for (auto& err : _errors)
{
for (auto& e : _errors[std::get<0>(err)])
Expand All @@ -213,33 +248,6 @@ std::string Solid::get_report_xml()
return ss.str();
}

std::string Solid::get_report_text()
{
std::stringstream ss;
ss << "===== Primitive " << this->_id << " =====" << std::endl;
for (auto& err : _errors)
{
for (auto& e : _errors[std::get<0>(err)])
{
ss << "\t" << std::get<0>(err) << " -- " << errorcode2description(std::get<0>(err)) << std::endl;
if (std::get<0>(e) == -1)
ss << "\t\tShells: -1" << std::endl;
else if (std::get<1>(e) == -1)
ss << "\t\tShells: " << std::get<0>(e) << std::endl;
else
ss << "\t\tShells: " << std::get<0>(e) << "--" << std::get<1>(e) << std::endl;
ss << "\t\tInfo: " << std::get<2>(e) << std::endl;
}
}
for (auto& sh : _shells)
{
ss << sh->get_report_text();
}
if (this->is_valid() == true)
ss << "\tVALID" << std::endl;
return ss.str();
}


int Solid::num_ishells()
{
Expand Down
12 changes: 10 additions & 2 deletions Solid.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
class Solid
{
public:
Solid();
Solid(InputTypes inputtype = OTHER);
Solid(Shell* sh);
~Solid();

Expand All @@ -47,10 +47,14 @@ class Solid
int num_faces();
int num_vertices();

std::string get_id_building();
void set_id_building(std::string id);
std::string get_id_buildingpart();
void set_id_buildingpart(std::string id);

bool validate(Primitive3D prim, double tol_planarity_d2p, double tol_planarity_normals);
void translate_vertices();
std::string get_report_xml();
std::string get_report_text();
void add_error(int code, int shell1, int shell2, std::string info);
std::set<int> get_unique_error_codes();
std::string get_poly_representation();
Expand All @@ -62,8 +66,12 @@ class Solid
void set_id(std::string id);
private:
std::string _id;
std::string _id_building;
std::string _id_buildingpart;
vector<Shell*> _shells;
int _is_valid;
InputTypes _inputtype;

std::map<int, vector< std::tuple<int, int, std::string> > > _errors;

bool validate_solid_with_nef();
Expand Down
22 changes: 22 additions & 0 deletions data/obj/duplicatevertices.obj
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
v 0 0 0
v 1 0 0
v 0 1 0
v 1 0 0
v 0 0 1
v 0 1 0
v 0 0 0
v 0 1 0
v 0 0 1
v 0 0 0
v 0 0 1
v 1 0 0

v 0 0 0
v 0 0 1
v -1 0 0

f 1 2 3
f 4 5 6
f 7 8 9
f 10 11 12
f 13 14 15
7 changes: 7 additions & 0 deletions definitions.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,5 +70,12 @@ typedef enum
MULTISURFACE = 2,
} Primitive3D;

typedef enum
{
GML = 0,
OBJ = 1,
POLY = 2,
OTHER = 3,
} InputTypes;

#endif
Loading

0 comments on commit 50bce5a

Please sign in to comment.