Skip to content

Commit

Permalink
Merge pull request #417 from Deltares/feat/308-improve-file-float-for…
Browse files Browse the repository at this point in the history
…matting

#308: Improve file float formatting
  • Loading branch information
priscavdsluis committed Nov 17, 2022
2 parents 5aa1364 + 1f3dbfe commit aed6dc1
Show file tree
Hide file tree
Showing 23 changed files with 269 additions and 89 deletions.
4 changes: 2 additions & 2 deletions hydrolib/core/basemodel.py
Original file line number Diff line number Diff line change
Expand Up @@ -1085,7 +1085,7 @@ def _serialize(self, data: dict) -> None:
return

path.parent.mkdir(parents=True, exist_ok=True)
self._get_serializer()(path, data)
self._get_serializer()(path, data, self.serializer_config)

def dict(self, *args, **kwargs):
kwargs["exclude"] = self._exclude_fields()
Expand Down Expand Up @@ -1114,7 +1114,7 @@ def _ext(cls) -> str:
return ".test"

@abstractclassmethod
def _get_serializer(cls) -> Callable[[Path, Dict], None]:
def _get_serializer(cls) -> Callable[[Path, Dict, SerializerConfig], None]:
return DummySerializer.serialize

@abstractclassmethod
Expand Down
2 changes: 1 addition & 1 deletion hydrolib/core/io/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ def parse(filepath: Path) -> Dict:

class DummySerializer:
@staticmethod
def serialize(path: Path, data: Dict) -> None:
def serialize(path: Path, data: Dict, config) -> None:
path.parent.mkdir(parents=True, exist_ok=True)
with path.open("w") as f:
f.write(str(data))
2 changes: 1 addition & 1 deletion hydrolib/core/io/dflowfm/polyfile/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ def _serialize(self, _: dict) -> None:
from .serializer import write_polyfile

# We skip the passed dict for a better one.
write_polyfile(self._resolved_filepath, self.objects)
write_polyfile(self._resolved_filepath, self.objects, self.serializer_config)

@classmethod
def _ext(cls) -> str:
Expand Down
48 changes: 38 additions & 10 deletions hydrolib/core/io/dflowfm/polyfile/serializer.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
from itertools import chain
from pathlib import Path
from typing import Dict, Iterable, Optional, Sequence
from typing import Generator, Iterable, Optional, Sequence

from hydrolib.core.basemodel import SerializerConfig
from hydrolib.core.io.dflowfm.polyfile.models import (
Description,
Metadata,
Expand Down Expand Up @@ -40,41 +41,68 @@ def serialize_metadata(metadata: Metadata) -> Iterable[str]:
return [metadata.name, f"{metadata.n_rows} {metadata.n_columns}"]

@staticmethod
def serialize_point(point: Point) -> str:
def serialize_point(point: Point, config: SerializerConfig) -> str:
"""Serialize this Point to a string which can be used within a polyfile.
the point data is indented with 4 spaces, and the individual values are
separated by 4 spaces as well.
Args:
point (Point): The point to serialize.
config (SerializerConfig): The serialization configuration.
Returns:
str: The serialised equivalent of this Point
"""
z_val = f"{point.z} " if point.z is not None else ""
data_vals = " ".join(str(v) for v in point.data)
return f" {point.x} {point.y} {z_val}{data_vals}".rstrip()
space = 4 * " "
format = lambda v: f"{v:{config.float_format}}"
return space + space.join(
format(v) for v in Serializer._get_point_values(point)
)

@staticmethod
def _get_point_values(point: Point) -> Generator[float, None, None]:
yield point.x
yield point.y
if point.z:
yield point.z
for value in point.data:
yield value

@staticmethod
def serialize_poly_object(obj: PolyObject) -> Iterable[str]:
def serialize_poly_object(
obj: PolyObject, config: SerializerConfig
) -> Iterable[str]:
"""Serialize this PolyObject to a string which can be used within a polyfile.
Args:
obj (PolyObject): The poly object to serializer.
config (SerializerConfig): The serialization configuration.
Returns:
str: The serialised equivalent of this Point
str: The serialised equivalent of this PolyObject
"""

description = Serializer.serialize_description(obj.description)
metadata = Serializer.serialize_metadata(obj.metadata)
points = map(Serializer.serialize_point, obj.points)
points = [Serializer.serialize_point(obj, config) for obj in obj.points]
return chain(description, metadata, points)


def write_polyfile(path: Path, data: Sequence[PolyObject]) -> None:
def write_polyfile(
path: Path, data: Sequence[PolyObject], config: SerializerConfig
) -> None:
"""Write the data to a new file at path
Args:
path (Path): The path to write the data to
data (Sequence[PolyObject]): The poly objects to write
config (SerializerConfig): The serialization configuration.
"""
serialized_data = chain.from_iterable(map(Serializer.serialize_poly_object, data))
serialized_poly_objects = [
Serializer.serialize_poly_object(poly_object, config) for poly_object in data
]
serialized_data = chain.from_iterable(serialized_poly_objects)

path.parent.mkdir(parents=True, exist_ok=True)

Expand Down
7 changes: 4 additions & 3 deletions hydrolib/core/io/dflowfm/xyz/models.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
from typing import Callable, List, Optional
from pathlib import Path
from typing import Callable, Dict, List, Optional

from pydantic import Field

from hydrolib.core.basemodel import BaseModel, ParsableFileModel
from hydrolib.core.basemodel import BaseModel, ParsableFileModel, SerializerConfig

from .parser import XYZParser
from .serializer import XYZSerializer
Expand Down Expand Up @@ -54,7 +55,7 @@ def _filename(cls) -> str:
return "sample"

@classmethod
def _get_serializer(cls) -> Callable:
def _get_serializer(cls) -> Callable[[Path, Dict, SerializerConfig], None]:
return XYZSerializer.serialize

@classmethod
Expand Down
31 changes: 27 additions & 4 deletions hydrolib/core/io/dflowfm/xyz/serializer.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,37 @@
from pathlib import Path
from typing import Dict
from typing import Dict, Generator

from hydrolib.core.basemodel import SerializerConfig


class XYZSerializer:
@staticmethod
def serialize(path: Path, data: Dict) -> None:
def serialize(path: Path, data: Dict, config: SerializerConfig) -> None:
"""
Serializes the XYZ data to the file at the specified path.
Attributes:
path (Path): The path to the destination file.
data (Dict): The data to be serialized.
config (SerializerConfig): The serialization configuration.
"""
path.parent.mkdir(parents=True, exist_ok=True)

space = 1 * " "
format_float = lambda x: f"{x:{config.float_format}}"

with path.open("w") as f:
for point in data["points"]:
geometry: str = space.join(
[format_float(p) for p in XYZSerializer._get_point_values(point)]
)
if point.comment:
f.write(f"{point.x} {point.y} {point.z} # {point.comment}\n")
f.write(f"{geometry} # {point.comment}\n")
else:
f.write(f"{point.x} {point.y} {point.z}\n")
f.write(f"{geometry}\n")

@staticmethod
def _get_point_values(point) -> Generator[float, None, None]:
yield point.x
yield point.y
yield point.z
11 changes: 8 additions & 3 deletions hydrolib/core/io/dimr/models.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
from abc import ABC, abstractclassmethod
from datetime import datetime
from pathlib import Path
from typing import Callable, List, Literal, Optional, Type, Union
from typing import Callable, Dict, List, Literal, Optional, Type, Union

from pydantic import Field, validator

from hydrolib.core import __version__
from hydrolib.core.basemodel import BaseModel, FileModel, ParsableFileModel
from hydrolib.core.basemodel import (
BaseModel,
FileModel,
ParsableFileModel,
SerializerConfig,
)
from hydrolib.core.io.dflowfm.mdu.models import FMModel
from hydrolib.core.io.dimr.parser import DIMRParser
from hydrolib.core.io.dimr.serializer import DIMRSerializer
Expand Down Expand Up @@ -330,7 +335,7 @@ def _filename(cls) -> str:
return "dimr_config"

@classmethod
def _get_serializer(cls) -> Callable:
def _get_serializer(cls) -> Callable[[Path, Dict, SerializerConfig], None]:
return DIMRSerializer.serialize

@classmethod
Expand Down
15 changes: 10 additions & 5 deletions hydrolib/core/io/dimr/serializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,21 @@

from lxml import etree as e

from hydrolib.core.basemodel import SerializerConfig


class DIMRSerializer:
"""A serializer for DIMR files."""

@staticmethod
def serialize(path: Path, data: dict):
def serialize(path: Path, data: dict, config: SerializerConfig):
"""
Serializes the DIMR data to the file at the specified path.
Attributes:
path (Path): The path to the destination file.
data (Dict): The data to be serialized.
config (SerializerConfig): The serialization configuration.
"""

path.parent.mkdir(parents=True, exist_ok=True)
Expand All @@ -33,7 +36,7 @@ def serialize(path: Path, data: dict):
attrib=attrib,
nsmap=namespaces,
)
DIMRSerializer._build_tree(root, data)
DIMRSerializer._build_tree(root, data, config)

to_string = minidom.parseString(e.tostring(root))
xml = to_string.toprettyxml(indent=" ", encoding="utf-8")
Expand All @@ -42,25 +45,27 @@ def serialize(path: Path, data: dict):
f.write(xml)

@staticmethod
def _build_tree(root, data: dict):
def _build_tree(root, data: dict, config: SerializerConfig):
name = data.pop("name", None)
if name:
root.set("name", name)

for key, val in data.items():
if isinstance(val, dict):
c = e.Element(key)
DIMRSerializer._build_tree(c, val)
DIMRSerializer._build_tree(c, val, config)
root.append(c)
elif isinstance(val, List):
for item in val:
c = e.Element(key)
DIMRSerializer._build_tree(c, item)
DIMRSerializer._build_tree(c, item, config)
root.append(c)
else:
c = e.Element(key)
if isinstance(val, datetime):
c.text = val.isoformat(sep="T", timespec="auto")
elif isinstance(val, float):
c.text = f"{val:{config.float_format}}"
else:
c.text = str(val)
root.append(c)
5 changes: 3 additions & 2 deletions hydrolib/core/io/rr/meteo/models.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
from datetime import datetime, timedelta
from pathlib import Path
from typing import Callable, Dict, List, Tuple

from hydrolib.core.basemodel import BaseModel, ParsableFileModel
from hydrolib.core.basemodel import BaseModel, ParsableFileModel, SerializerConfig

from .parser import BuiParser
from .serializer import write_bui_file
Expand Down Expand Up @@ -64,7 +65,7 @@ def _ext(cls) -> str:
return ".bui"

@classmethod
def _get_serializer(cls) -> Callable:
def _get_serializer(cls) -> Callable[[Path, Dict, SerializerConfig], None]:
return write_bui_file

@classmethod
Expand Down
Loading

0 comments on commit aed6dc1

Please sign in to comment.