Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Textures for relief/bridges/waterbodies in CityTiler #48

Merged
merged 4 commits into from
Jan 31, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions py3dtilers/CityTiler/CityTiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ def __init__(self):
type=str,
help='Path(es) to the database configuration file(s)')

self.parser.add_argument('object_type',
self.parser.add_argument('--type',
nargs='?',
default='building',
type=str,
Expand Down Expand Up @@ -163,18 +163,18 @@ def main():

cursor = open_data_base(args.db_config_path[0])

if args.object_type == "building":
if args.type == "building":
objects_type = CityMBuildings
city_tiler.create_directory('junk_buildings')
if args.with_BTH:
CityMBuildings.set_bth()
elif args.object_type == "relief":
elif args.type == "relief":
city_tiler.create_directory('junk_reliefs')
objects_type = CityMReliefs
elif args.object_type == "water":
elif args.type == "water":
city_tiler.create_directory('junk_water_bodies')
objects_type = CityMWaterBodies
elif args.object_type == "bridge":
elif args.type == "bridge":
city_tiler.create_directory('junk_bridges')
objects_type = CityMBridges

Expand All @@ -196,13 +196,13 @@ def main():
tileset.add_asset_extras(origin)

cursor.close()
if args.object_type == "building":
if args.type == "building":
tileset.write_to_directory('junk_buildings')
elif args.object_type == "relief":
elif args.type == "relief":
tileset.write_to_directory('junk_reliefs')
elif args.object_type == "water":
elif args.type == "water":
tileset.write_to_directory('junk_water_bodies')
elif args.object_type == "bridge":
elif args.type == "bridge":
tileset.write_to_directory('junk_bridges')


Expand Down
10 changes: 5 additions & 5 deletions py3dtilers/CityTiler/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,30 +33,30 @@ The output folder contains:

### Objects type

By default, the tiler will treat the data as __buildings__. You can change the type by adding one the 3 keywords:
By default, the tiler will treat the data as __buildings__. You can change the type by adding the flag `--type` followed by one the 4 keywords:

* `building`

```bash
citygml-tiler --db_config_path <path_to_file>/Config.yml building
citygml-tiler --db_config_path <path_to_file>/Config.yml --type building
```

* `relief`

```bash
citygml-tiler --db_config_path <path_to_file>/Config.yml relief
citygml-tiler --db_config_path <path_to_file>/Config.yml --type relief
```

* `water`

```bash
citygml-tiler --db_config_path <path_to_file>/Config.yml water
citygml-tiler --db_config_path <path_to_file>/Config.yml --type water
```

* `bridge`

```bash
citygml-tiler --db_config_path <path_to_file>/Config.yml bridge
citygml-tiler --db_config_path <path_to_file>/Config.yml --type bridge
```

### Split surfaces
Expand Down
40 changes: 35 additions & 5 deletions py3dtilers/CityTiler/citym_cityobject.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,6 @@ def get_gml_id(self):
"""
return super().get_batchtable_data()['gml_id']

def get_texture(self):
stream = self.objects_type.get_image_from_binary(self.texture_uri, self.objects_type, CityMCityObjects.gml_cursor)
texture = Texture(stream, self.geom.triangles[1])
return texture.get_texture_image()


class CityMCityObjects(ObjectsToTile):
"""
Expand All @@ -54,6 +49,21 @@ class CityMCityObjects(ObjectsToTile):
def __init__(self, cityMCityObjects=None):
super().__init__(cityMCityObjects)

def get_textures(self):
"""
Return a dictionary of all the textures where the keys are the IDs of the geometries.
:return: a dictionary of textures
"""
texture_dict = dict()
uri_dict = dict()
for object_to_tile in self.get_objects():
uri = object_to_tile.texture_uri
if uri not in uri_dict:
stream = self.get_image_from_binary(uri, self.__class__, CityMCityObjects.gml_cursor)
uri_dict[uri] = Texture(stream)
texture_dict[object_to_tile.get_id()] = uri_dict[uri].get_cropped_texture_image(object_to_tile.geom.triangles[1])
return texture_dict

@staticmethod
def set_cursor(cursor):
CityMCityObjects.gml_cursor = cursor
Expand Down Expand Up @@ -171,3 +181,23 @@ def get_image_from_binary(textureUri, objects_type, cursor):
LEFT_THUMB = imageBinaryData[0][0]
stream = BytesIO(LEFT_THUMB)
return stream

@staticmethod
def sql_query_centroid():
"""
Virtual method: all CityMCityObjects and childs classes instances should
implement this method.

:return: no return value.
"""
pass

@staticmethod
def sql_query_geometries_with_texture_coordinates():
"""
Virtual method: all CityMCityObjects and childs classes instances should
implement this method.

:return: no return value.
"""
pass
55 changes: 54 additions & 1 deletion py3dtilers/CityTiler/citym_waterbody.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ def sql_query_geometries(waterbodies_ids=None, split_surfaces=False):
"""
:param waterbodies_ids: a formatted list of (city)gml identifier corresponding to
objects_type type objects whose geometries are sought.
:param split_surfaces: a boolean specifying if the surfaces of each relief tile will stay
:param split_surfaces: a boolean specifying if the surfaces of each water body tile will stay
splitted or be merged into one geometry

:return: a string containing the right sql query that should be executed.
Expand All @@ -94,3 +94,56 @@ def sql_query_geometries(waterbodies_ids=None, split_surfaces=False):
"GROUP BY waterbody.id "

return query

@staticmethod
def sql_query_geometries_with_texture_coordinates(water_bodies_ids=None):
"""
param water_bodies_ids: a formatted list of (city)gml identifier corresponding to
objects_type type objects whose geometries are sought.
:return: a string containing the right sql query that should be executed.
"""
# cityobjects_ids contains ids of water_bodies
query = \
("SELECT surface_geometry.id, "
"ST_AsBinary(ST_Multi(surface_geometry.geometry)) as geom, "
"ST_AsBinary(ST_Multi(ST_Translate(ST_Scale(textureparam.texture_coordinates, 1, -1), 0, 1))) as uvs, "
"tex_image_uri AS uri "
"FROM citydb.waterbody JOIN citydb.waterbod_to_waterbnd_srf "
"ON waterbody.id=waterbod_to_waterbnd_srf.waterbody_id "
"JOIN citydb.waterboundary_surface "
"ON waterbod_to_waterbnd_srf.waterboundary_surface_id=waterboundary_surface.id "
"JOIN citydb.surface_geometry "
"ON surface_geometry.root_id=waterboundary_surface.lod3_surface_id "
"JOIN citydb.textureparam "
"ON textureparam.surface_geometry_id=surface_geometry.id "
"JOIN citydb.surface_data "
"ON textureparam.surface_data_id=surface_data.id "
"JOIN citydb.tex_image "
"ON surface_data.tex_image_id=tex_image.id "
"WHERE waterbody.id IN " + water_bodies_ids)
return query

@staticmethod
def sql_query_centroid(id):
clementcolin marked this conversation as resolved.
Show resolved Hide resolved
"""
param id: the ID of the cityGML object
return: the [x, y, z] coordinates of the centroid of the cityGML object
"""

query = \
"SELECT " + \
"ST_X(ST_3DClosestPoint(ST_Multi(ST_Collect(surface_geometry.geometry)) " + \
",ST_Centroid(ST_Multi(ST_Collect(surface_geometry.geometry))))), " + \
"ST_Y(ST_3DClosestPoint(ST_Multi(ST_Collect(surface_geometry.geometry)) " + \
",ST_Centroid(ST_Multi(ST_Collect(surface_geometry.geometry))))), " + \
"ST_Z(ST_3DClosestPoint(ST_Multi(ST_Collect(surface_geometry.geometry)) " + \
",ST_Centroid(ST_Multi(ST_Collect(surface_geometry.geometry))))) " + \
"FROM citydb.waterbody JOIN citydb.waterbod_to_waterbnd_srf " + \
"ON waterbody.id=waterbod_to_waterbnd_srf.waterbody_id " + \
"JOIN citydb.waterboundary_surface " + \
"ON waterbod_to_waterbnd_srf.waterboundary_surface_id=waterboundary_surface.id " + \
"JOIN citydb.surface_geometry ON surface_geometry.root_id=waterboundary_surface.lod3_surface_id " + \
"WHERE waterbody.id = " + str(id) + \
" GROUP BY waterbody.id"

return query
10 changes: 10 additions & 0 deletions py3dtilers/Common/object_to_tile.py
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,16 @@ def scale_objects(self, scale_factor):
object_to_tile.set_triangles(new_geom)
object_to_tile.set_box()

def get_textures(self):
"""
Return a dictionary of all the textures where the keys are the IDs of the geometries.
:return: a dictionary of textures
"""
texture_dict = dict()
for object_to_tile in self.get_objects():
texture_dict[object_to_tile.get_id()] = object_to_tile.get_texture()
return texture_dict

@staticmethod
def create_batch_table_extension(extension_name, ids=None, objects=None):
pass
Expand Down
4 changes: 2 additions & 2 deletions py3dtilers/ObjTiler/obj.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,8 @@ def parse_geom(self, mesh):
self.geom.triangles.append(uvs)
if mesh.materials[0].texture is not None:
path = str(mesh.materials[0].texture._path).replace('\\', '/')
texture = Texture(path, self.geom.triangles[1])
self.set_texture(texture.get_texture_image())
texture = Texture(path)
self.set_texture(texture.get_cropped_texture_image(self.geom.triangles[1]))
self.set_box()

return True
Expand Down
3 changes: 2 additions & 1 deletion py3dtilers/Texture/atlas.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@ def __init__(self, objects_to_tile):
objects_with_id_key = dict()
textures_with_id_key = dict()

textures = objects_to_tile.get_textures()
for object_to_tile in objects_to_tile:
objects_with_id_key[object_to_tile.get_id()] = object_to_tile.geom
textures_with_id_key[object_to_tile.get_id()] = object_to_tile.get_texture()
textures_with_id_key[object_to_tile.get_id()] = textures[object_to_tile.get_id()]

# Sort textures by size, starting by the biggest one
textures_sorted = sorted(textures_with_id_key.items(),
Expand Down
15 changes: 8 additions & 7 deletions py3dtilers/Texture/texture.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,19 @@ class Texture():

folder = None

def __init__(self, image_path, triangles):
def __init__(self, image_path):
"""
:param image_path: path to the image (or a stream with image bytes)
:param triangles: the uvs
Create a pillow.image:
"""
image = Image.open(image_path)
image = self.cropImage(image, triangles)
self.texture_image = image.convert("RGBA")
self.image = Image.open(image_path)

def get_texture_image(self):
return self.texture_image
def get_cropped_texture_image(self, uvs):
"""
:param uvs: the uvs
"""
image = self.cropImage(self.image, uvs)
return image.convert("RGBA")

def cropImage(self, image, triangles):
minX = 2
Expand Down