{"id":37017316,"url":"https://github.com/pjazdzyk/unitility","last_synced_at":"2026-04-06T19:01:09.619Z","repository":{"id":138647352,"uuid":"591261560","full_name":"pjazdzyk/unitility","owner":"pjazdzyk","description":"Unitility: Units of Measurement and Physical Quantity Converter for JAVA, with Spring Boot and Quarkus support for web applications development.  Immutable, thread-safe, content rich, easy to use.","archived":false,"fork":false,"pushed_at":"2026-04-04T23:19:07.000Z","size":1341,"stargazers_count":8,"open_issues_count":0,"forks_count":4,"subscribers_count":1,"default_branch":"master","last_synced_at":"2026-04-05T01:19:45.684Z","etag":null,"topics":["api-measurement","converters","geocoordinates","geolocation","imperial-units","java","longitude-and-latitude","physics","si-unit-conversion","si-units","thermodynamic-properties","thermodynamics","unit-conversion","unit-converter","unit-system","units-of-measure","units-of-measurement","units-of-measurements"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/pjazdzyk.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":"NOTICE","maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null},"funding":{"github":["pjazdzyk"]}},"created_at":"2023-01-20T10:20:30.000Z","updated_at":"2026-04-04T23:16:28.000Z","dependencies_parsed_at":null,"dependency_job_id":"37e7cec3-6dee-48c8-85b8-7af1472a2539","html_url":"https://github.com/pjazdzyk/unitility","commit_stats":{"total_commits":194,"total_committers":1,"mean_commits":194.0,"dds":0.0,"last_synced_commit":"65b03d38636222511aaa762bb802b2f32113dec4"},"previous_names":[],"tags_count":27,"template":false,"template_full_name":null,"purl":"pkg:github/pjazdzyk/unitility","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pjazdzyk%2Funitility","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pjazdzyk%2Funitility/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pjazdzyk%2Funitility/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pjazdzyk%2Funitility/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pjazdzyk","download_url":"https://codeload.github.com/pjazdzyk/unitility/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pjazdzyk%2Funitility/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31485516,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-06T17:22:55.647Z","status":"ssl_error","status_checked_at":"2026-04-06T17:22:54.741Z","response_time":112,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api-measurement","converters","geocoordinates","geolocation","imperial-units","java","longitude-and-latitude","physics","si-unit-conversion","si-units","thermodynamic-properties","thermodynamics","unit-conversion","unit-converter","unit-system","units-of-measure","units-of-measurement","units-of-measurements"],"created_at":"2026-01-14T01:59:21.520Z","updated_at":"2026-04-06T19:01:09.609Z","avatar_url":"https://github.com/pjazdzyk.png","language":"Java","funding_links":["https://github.com/sponsors/pjazdzyk"],"categories":[],"sub_categories":[],"readme":"# UNITILITY - The Ultimate Units of Measurement and Physical Quantity Converter\n\nIntroducing \u003cstrong\u003eUNITILITY\u003c/strong\u003e -  the Java library designed to simplify physics unit conversions and provide intuitive \nbusiness types that represent aggregated physical quantities, including their type, value, and unit—all in one. \u003cbr\u003e\n\n**If your project involves engineering or scientific work, you’ll love Unitility!** \u003cbr\u003e\n\nWith a wide range of value objects that represent commonly used physical quantities, this solution is built using plain\nJava for optimal speed and lightweight functionality.\nWhether you're looking to convert units within the same type or customize to meet your specific needs, the UNITILITY **core** \nmodule offers quick and easy usage in your project, without requiring heavy frameworks or external libraries.\nAdditional framework support is available through the extension modules. \u003cbr\u003e\n\n**\u003cspan style=\"color: green;\"\u003e - Thread-Safe Architecture:\u003c/span\u003e** Developed to ensure thread safety,\nallowing for concurrent access without compromising data integrity through the utilization of immutable objects. \u003cbr\u003e\n**\u003cspan style=\"color: teal;\"\u003e - Kotlin and Groovy friendly:\u003c/span\u003e** Developed to take some advantages of Kotlin and Groovy \nfeatures, such as overloaded operators.\n\n[![Unitility](https://img.shields.io/github/v/release/pjazdzyk/Unitility?label=Unitility\u0026color=13ADF3\u0026logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMi41bW0iIGhlaWdodD0iMTQuNW1tIiB2aWV3Qm94PSIwIDAgMjI1MCAxNDUwIj4NCiAgPHBvbHlnb24gZmlsbD0iIzUwN0QxNCIgcG9pbnRzPSIyMjQxLjAzLDE1Ljg4IDExMzYuMzgsMTUuODQgOTA1Ljg4LDQxNS4xIDIwMTAuNTMsNDE1LjA5IiAvPg0KICA8cG9seWdvbiBmaWxsPSIjNzFBQjIzIiBwb2ludHM9IjExMTYuMzgsMTUuODQgNjU1Ljk5LDE1Ljg0IDQ5NC4xNSwyOTYuMTcgNzI0LjM1LDY5NC44OCIgLz4NCiAgPHBvbHlnb24gZmlsbD0iIzhBQzkzNCIgcG9pbnRzPSI0ODQuMTUsMzA2LjE3IDI1NS4wNiw3MDIuOTYgMzg3LjY2LDkzMi42NCA4NDUuODMsOTMyLjYzIiAvPg0KICA8cG9seWdvbiBmaWxsPSIjNThEMEZGIiBwb2ludHM9Ii03LjE3LDE0NDAuMDkgMTA5Ny45NywxNDQwLjA4IDEzMjguNDcsMTA0MC44MyAyMjMuMzIsMTA0MC44NSIgLz4NCiAgPHBvbHlnb24gZmlsbD0iIzEzQURGMyIgcG9pbnRzPSIxNzM5LjA0LDExNjAuOTEgMTUwOS4wOSw3NjIuNjQgMTExNy45NywxNDQwLjA4IDExODYuOTMsMTQ0MC4wOCAxNTc3Ljg3LDE0NDAuMDgiIC8+DQogIDxwb2x5Z29uIGZpbGw9IiMwMzkzRDAiIHBvaW50cz0iMTk3OC44LDc1Mi45NiAxODQ2LjIsNTIzLjMgMTM4Ni42OCw1MjMuMyAxNzQ5LjA0LDExNTAuOTEiIC8+DQo8L3N2Zz4=)](https://github.com/pjazdzyk/Unitility)\n\u0026nbsp;\n![Build And Test](https://github.com/pjazdzyk/unitility/actions/workflows/build-test-analyze.yml/badge.svg) \u003cbr\u003e\n[![Vulnerabilities](https://sonarcloud.io/api/project_badges/measure?project=pjazdzyk_unitility\u0026metric=vulnerabilities)](https://sonarcloud.io/summary/new_code?id=pjazdzyk_unitility)\n\u0026nbsp;\n[![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=pjazdzyk_unitility\u0026metric=security_rating)](https://sonarcloud.io/summary/new_code?id=pjazdzyk_unitility)\n\u0026nbsp;\n[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=pjazdzyk_unitility\u0026metric=alert_status)](https://sonarcloud.io/summary/new_code?id=pjazdzyk_unitility)\n\u0026nbsp;\n[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=pjazdzyk_unitility\u0026metric=coverage)](https://sonarcloud.io/summary/new_code?id=pjazdzyk_unitility)\n\u0026nbsp;\n\n\u003e AUTHOR: **Piotr Jazdzyk**, MSc Eng \u003cbr\u003e\n\u003e LINKEDIN: https://www.linkedin.com/in/pjazdzyk \u003cbr\u003e\n\n## TABLE OF CONTENTS\n1. [Installation](#1-installation) \u003cbr\u003e\n2. [Tech \u0026 dependencies](#2-tech-and-dependencies) \u003cbr\u003e\n3. [Supported physical quantities and units](#3-supported-physical-quantities-and-units) \u003cbr\u003e\n4. [Usage and functionality](#4-usage-and-functionality) \u003cbr\u003e\n   4.1 [Working with physical quantities](#41-working-with-physical-quantities) \u003cbr\u003e\n   4.2 [Parsing quantities from string](#42-parsing-quantities-from-string) \u003cbr\u003e\n   4.3 [Logic operations](#43-logical-operations) \u003cbr\u003e\n   4.4 [Arithmetic operations](#44-arithmetic-operations) \u003cbr\u003e\n   4.5 [Power and logarithmic operations](#45-power-and-logarithmic-operations) \u003cbr\u003e\n   4.6 [Trigonometric operations](#46-trigonometric-operations) \u003cbr\u003e\n   4.7 [Equality and sorting](#47-equality-and-sorting) \u003cbr\u003e\n5. [Unitility extension modules](#5-unitility-extension-modules) \u003cbr\u003e\n   5.1 [Jackson serializers / deserializers](#51-jackson-serializers-and-deserializers) \u003cbr\u003e\n   \u0026nbsp; 5.1.1 [Default ser-de](#511-default-ser-de) \u003cbr\u003e\n   \u0026nbsp; 5.1.2 [Plain SI value ser-de](#512-plain-si-value-ser-de) \u003cbr\u003e\n   5.2 [SpringBoot web extension](#52-spring-boot-module) \u003cbr\u003e\n   5.3 [Quarkus web extension](#53-quarkus-module) \u003cbr\u003e\n   5.4 [Jakarta Validation](#54-jakarta-validation) \u003cbr\u003e\n6. [Creating custom units and quantities](#6-creating-custom-quantities) \u003cbr\u003e\n   6.1 [Custom unit](#61-custom-unit) \u003cbr\u003e\n   6.2 [Registering custom quantity in Spring](#62-custom-physical-quantity) \u003cbr\u003e\n   6.3 [Registering custom quantity in Quarkus](#63-registering-custom-quantities-in-spring) \u003cbr\u003e\n7. [Compatibility with other JVM languages](#7-compatibility-with-other-jvm-languages) \u003cbr\u003e\n   7.1 [Groovy](#71-groovy---using-overloaded-operators) \u003cbr\u003e\n   7.2 [Kotlin](#72-kotlin---using-overloaded-operators) \u003cbr\u003e\n8. [Special types: geographic](#8-special-types-geographic) \u003cbr\u003e\n   8.1 [Latitude, Longitude, GeoCoordinate](#81-geographic-latitude-longitude-and-geocoordinate) \u003cbr\u003e\n   8.2 [Bearing](#82-bearing) \u003cbr\u003e\n   8.3 [GeoDistance (Haversine equations)](#83-geodistance---spherical-distance-between-two-coordinates) \u003cbr\u003e\n   8.4 [Parsing geographic quantities and JSON structures](#84-parsing-geographic-quantities-and-json-structures) \u003cbr\u003e\n9. [Persistence](#9-persistence) \u003cbr\u003e\n   9.1 [Attribute Converters - Default SI](#91-attribute-converters---default-si) \u003cbr\u003e\n10. [Collaboration, attribution and citation](#10-collaboration-attribution-and-citation) \u003cbr\u003e\n11. [Acknowledgments](#11-acknowledgments) \u003cbr\u003e\n\n## 1. INSTALLATION\n\nCopy the Maven dependency provided below to your pom.xml file, and you are ready to go. For other package managers,\ncheck maven central repository:\n[UNITILITY](https://search.maven.org/artifact/com.synerset/unitility/4.0.1/jar?eh=).\n\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003ecom.synerset\u003c/groupId\u003e\n    \u003cartifactId\u003eunitility-core\u003c/artifactId\u003e\n    \u003cversion\u003e4.0.1\u003c/version\u003e\n\u003c/dependency\u003e\n```\nIf you use frameworks to develop web applications, it is recommended to use Unitility extension modules, \nto provide PhysicalQuantity instances serialization and deserialization including path and query parameters.\u003cbr\u003e\n\nExtension for the Spring Boot framework:\n\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003ecom.synerset\u003c/groupId\u003e\n    \u003cartifactId\u003eunitility-spring\u003c/artifactId\u003e\n    \u003cversion\u003e4.0.1\u003c/version\u003e\n\u003c/dependency\u003e\n```\nExtension for the Quarkus framework:\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003ecom.synerset\u003c/groupId\u003e\n    \u003cartifactId\u003eunitility-quarkus\u003c/artifactId\u003e\n    \u003cversion\u003e4.0.1\u003c/version\u003e\n\u003c/dependency\u003e\n```\nExtensions include CORE module, so you don't have to put it separate in your pom.\nSee section on [Spring](#52-spring-boot-module) or [Quarkus](#53-quarkus-module) for better explanation.\nUnitility has also provided **[BOM](https://github.com/pjazdzyk/unitility/blob/master/unitility-bom/pom.xml)** which can be used for dependency management.\n\n## 2. TECH AND DEPENDENCIES\n\n\u003cstrong\u003eUnitility\u003c/strong\u003e is developed using the following technologies: \u003cbr\u003e\n\nCore: \u003cbr\u003e\n![image](https://img.shields.io/badge/21-Java-orange?style=for-the-badge) \u0026nbsp;\n![image](https://img.shields.io/badge/apache_maven-C71A36?style=for-the-badge\u0026logo=apachemaven\u0026logoColor=white) \u0026nbsp;\n![image](https://img.shields.io/badge/Junit5-25A162?style=for-the-badge\u0026logo=junit5\u0026logoColor=white) \u0026nbsp;\n\nExtension modules:\u003cbr\u003e\n![image](https://img.shields.io/badge/Quarkus-000000?style=for-the-badge\u0026logo=quarkus) \u0026nbsp;\n![image](https://img.shields.io/badge/Spring_Boot-F2F4F9?style=for-the-badge\u0026logo=spring-boot) \u0026nbsp;\n\nCI/CD:\u003cbr\u003e\n![image](https://img.shields.io/badge/GitHub_Actions-2088FF?style=for-the-badge\u0026logo=github-actions\u0026logoColor=white)\n\u0026nbsp;\n![image](https://img.shields.io/badge/Sonar%20cloud-F3702A?style=for-the-badge\u0026logo=sonarcloud\u0026logoColor=white) \u0026nbsp;\n\nThe Key concept of the core module is to use plain Java, to minimize any issues with compatibility, conflicting\ntransitive dependencies or excessive dependency version management.\n\n## 3. SUPPORTED PHYSICAL QUANTITIES AND UNITS\n\nAt the current level of development, the following units are listed below. Each quantity includes the most popular SI \nunits and at least one Imperial unit.\n\n#### COMMON:\n\n* Distance, Length, Width, Height, Diameter, Thickness, Perimeter: meter [m], centimetre [cm], millimetre [mm], kilometre [km], mile [mi], nautical mile [nmi], feet [ft], inch [in], yard [yd], decameter [dam], hectometer[hm], data mile [datmi],\n* Area: square meter [m²], square kilometre [km²], square centimetre [cm²], square millimetre [mm²], are [a],\n  hectare [ha], square inch [in²], square foot [ft²], square yard [yd²], acre [ac], square mile [mi²]\n* Volume: cubic meter [m³], cubic centimetre [L], liter [L], hectolitre [hL], millilitre [mL], ounce [fl.oz], pint [pt],\n  gallon (US) [gal_us], gallon (UK) [gal_uk]\n* Mass: kilogram [kg], gram [g], milligram [mg], tonne [t], ounce [oz], pound [lb]\n* Angle: degrees [°], radians [rad]\n* Ratio: percent [%], Decimal [-]\n* LinearMassDensity: Kilogram per metre [kg/m], Tonne per metre [t/m], Ounce per foot [oz/ft], Pound per foot [lb/ft]\n* Velocity: meter per second [m/s], centimeter per second [cm/s], kilometer per hour [km/h], inch per second, [in/s], feet per second [ft/s], mile per hour [mph], knot [kn], Mach [Mach]\n* AngularVelocity: radians per second [rad/s], revolutions per minute, [rpm], revolutions per second [rps], degrees per second [°/s]\n* Curvature: radians per meter [rad/m], radians per foot [rad/ft], degrees per meter [°/m], degrees per foot [°/ft], degrees per hundred feet [°/100ft]\n* Data size: bits [bit], bytes [b], kilobytes[kb], megabytes [mb], gigabyte [gb], terabyte [tb], petabyte [pb]\n\n#### MECHANICAL:\n\n* Force: newton [N], kilonewton [kN], kilopond [kp], dyne [dyn], pound force [lbf], poundal [pdl]\n* Momentum: Kilogram meter per second [kg·m/s], Gram centimeter per second [g·cm/s], Pound feet per second [lb·ft/s]\n* Torque: Newton meter [N·m], Millinewton meter [mN·m], Kilopond meter [kp·m], Foot pound [ft·lb], Inch pound [in·lb]\n\n#### ELECTRIC:\n\n* Capacitance: farad [F], microfarad [µF], nanofarad [nF], picofarad [pF]\n* Charge: coulomb [C], millicoulomb [mC], microcoulomb [µC], nanocoulomb [nC], picocoulomb [pC], kilocoulomb [kC], megacoulomb [MC]\n* Conductance: siemens [S], millisiemens [mS], microsiemens [µS], nanosiemens [nS], picosiemens [pS], kilosiemens [kS]\n* Current: ampere [A], milliampere [mA], microampere [µA], nanoampere [nA], kiloampere [kA], megaampere [MA]\n* Resistance: ohm [Ω], milliohm [mΩ]], kiloohm [kΩ]], megaohm [MΩ]]\n* Voltage: volt [V], millivolt [mV], microvolt [µV], nanovolt [nV], kilovolt [kV], megavolt [MV]\n\n#### THERMODYNAMIC:\n\n* Temperature: Kelvin [K], Celsius [°C], Fahrenheit [°F]\n* Pressure: Pascal [Pa], Hectopascal [hPa], Kilopascal [kPa], Megapascal [MPa], Bar [bar], Milli Bar [mbar] PSI [psi], Torr [Torr],\n  Meters of water in 10°C [mH₂O_10], Meters of water in 60°C [mH₂O_60], Meters of water in 95°C [mH₂O_95],\n  Millimeters of mercury in 10°C [mmHg_10], Millimeters of mercury in 60°C [mmHg_60], Millimeters of mercury in 95°C [mmHg_95],\n* Energy: Joule [J], Milli joule [mJ], Kilojoule [kJ], Megajoule [MJ], British Thermal Unit [BTU], Calorie [cal], Kilocalorie [kcal], Watt\n  hour [Wh], Kilowatt hour [kWh]\n* Power: Watt [W], Kilowatt [kW], Megawatt [MW], BTU/hour [BTU/h], Horse Power [HP]\n* Specific heat: Joules per kilogram Kelvin [J/(kg·K)], Kilojoules per kilogram Kelvin [kJ/(kg·K)], BTU per pound\n  Fahrenheit [BTU/(lb·°F)]\n* Density: Kilogram per cubic meter [kg/m³], Pound per cubic foot [lb/ft³], Pounds per cubic inch [lb/in³], Pounds per Gallon US [lb/gal_US]\n* Dynamic viscosity: Kilogram per meter second [kg/(m·s)], Pascal second [Pa·s], Poise [P]\n* Kinematic viscosity: Square meter per second [m²/s], Square foot per second [ft²/s]\n* Specific enthalpy: Joule per kilogram [J/kg], Kilojoule per kilogram [kJ/kg], BTU per pound [BTU/lb]\nit* Specific entropy: Joule per kilogram Kelvin [J/(kg·K)], Kilojoule per kilogram Kelvin [kJ/(kg·K)], BTU per pound Fahrenheit [BTU/(lb·°F)]\n* Thermal conductivity: Watts per meter Kelvin [W/(m·K)], Kilowatts per meter Kelvin [kW/(m·K)], BTU per hour foot\n  Fahrenheit [BTU/(h·ft·°F)]\n* Thermal diffusivity: Square meter per second [m²/s], Square feet per second [ft²/s]\n* Molar enthalpy: Joule per mole [J/mol], Kilojoule per mole [kJ/mol], Megajoule per kilomole [MJ/kmol], Millijoule per millimole [mJ/mmol], BTU per pound-mole [BTU/lb-mol], Calorie per mole [cal/mol], Kilocalorie per mole [kcal/mol]\n* Molar entropy: Joule per mole Kelvin [J/(mol·K)], Kilojoule per mole Kelvin [kJ/(mol·K)], Calorie per mole Kelvin [cal/(mol·K)]\n* Molar fraction: dimensionless [-]\n* Molar mass: gram per mole [g/mol], kilogram per kilomole [kg/kmol], kilogram per mole [kg/mol], milligram per millimole [mg/mmol], pound per pound-mole [lb/lb-mol], ounce per mole [oz/mol]\n* Molar volume: cubic meter per mole [m³/mol], liter per mole [L/mol], cubic decimeter per mole [dm³/mol], cubic centimeter per mole [cm³/mol], milliliter per mole [mL/mol], cubic foot per pound-mole [ft³/lb-mol], cubic inch per pound-mole [in³/lb-mol]\n* Isothermal compressibility: Inverse Pascal [1/Pa], Inverse kilopascal [1/kPa], Inverse megapascal [1/MPa], Inverse bar [1/bar], Inverse PSI [1/psi], Inverse atmosphere [1/atm]\n* Compressibility factor: dimensionless [-]\n\n#### FLOWS:\n\n* Mass flow: Kilogram per second [kg/s], Kilogram per hour [kg/h], Tonne per hour [t/h], Pound per second [lb/s],\n* Volumetric flow: Cubic meters per second [m³/s], Cubic meters per minute [m³/min], Cubic meters per hour [m³/h], Litre\n  per second [L/s], Litre per minute [L/min], Litre per hour [L/h], Gallons per second US [gal/s_US], Gallons per minute US [gal/min_US],\n  Gallons per hour US [gal/h_US], Gallons per second UK [gal/s_UK], Gallons per minute UK [gal/min_UK], Gallons per hour UK [gal/h_UK]\n\n#### HUMID AIR SPECIFIC:\n\n* Humidity ratio: Kilogram per kilogram [kg/kg], Gram per kilogram [g/kg], Pound per pound [lb/lb]\n* Relative humidity: Percent [%], Decimal [-]\n\n#### HYDRAULIC:\n\n* Linear resistance: Pascal per meter [Pa/m], Inch of water per 100 feet [inH₂O/100ft], Inch of mercury per 100 feet [inHg/100ft]\n* Friction factor: [-]\n* LocalLoss factor: [-]\n* Rotation speed To Flow Rate Ratio: Radians per second per meter per second [rad·s⁻¹/m³·s⁻¹], revolutions per minute per gallons per minute [rpm/gpm]\n\n#### SIMILARITY NUMBERS:\n\n* Grashof number, Prandtl number, Reynolds number, Bypass factor\n\n#### ACOUSTIC:\n* Sound power: Watt [W], Decibel [dB]\n* Sound pressure: Pascal [Pa], Decibel [dB]\n\n#### OSCILLATION:\n* Frequency: Hertz [Hz], Kilohertz [kHz], Megahertz [MHz], Gigahertz [GHz], Cycles per minute [cpm]\n\n#### SPECIAL TYPES:\n#### Geographic:\n\n* Latitude: degrees [°], radians [rad]\n* Longitude: degrees [°], radians [rad]\n* Bearing: degrees [°]\n* Geo coordinate: [latitude, longitude]\n* Geo distance: meter [m], kilometer [km], mile [mi], nautical mile [nmi] \u003cbr\u003e\n\nAll Geographic quantities can be constructed from DMS format (degrees-minutes-seconds), for i.e.: 20°7'22.8\"S.\n\n## 4. USAGE AND FUNCTIONALITY\n\n### 4.1 Working with physical quantities\n\nPhysicalQuantity of specific **type** is an aggregate of **value** and **unit** with embedded converters. \nUnitility supports all logic operations and arithmetic transformations.\nIt was decided to use a double type for value as for most engineering purposes BigDecimal level of accuracy is not required.\n\n**Creating quantities:**\u003cbr\u003e\nBelow is a simple example of how to work with units and convert property from one unit to another or to convert as doubles\nfor further calculations:\n\n```java\n// Creating temperature instance of specified units\nTemperature temperature = Temperature.ofCelsius(20.5);                  // {20.50 °C}\n// Getting converted value for calculations\ndouble valueInCelsius = temperature.getInCelsius();                     // 20.50 °C\ndouble valueInKelvins = temperature.getInKelvins();                     // 293.65 K\ndouble valueInFahrenheits = temperature.getInFahrenheits();             // 68.90 °F\n// Checking property current unit, value, and value in base unit\nTemperatureUnit unit = temperature.getUnitType();                       // CELSIUS\nTemperatureUnit baseUnit = unit.getBaseUnit();                          // KELVIN \ndouble valueInUnit = temperature.getValue();                            // 20.50 °C\ndouble valueInBaseUnits = temperature.getBaseValue();                   // 293.65 K\n// Changing property unit and converting back to base unit\nTemperature tempInFahrenheits = temperature.toUnit(TemperatureUnits.FAHRENHEIT);   // {68.90 °F}\nTemperature tempInKelvins = temperature.toBaseUnit();                              // {293.65 K}\n```\n\nAll quantities have smart toEngineeringFormat() method, which will always adjust values decimal precision to capture by\ndefault specified relevant digits depending on your unit type and its value.\nThis way you have guaranteed an elegant output without any additional effort of reformatting. Values will be rounded up\nusing HALF_EVEN approach.\n\n```java\nDistance bigDistance = Distance.ofMeters(10);\nDistance smallDistance = Distance.ofMeters(0.000123678);\nString bigOutput = bigDistance.toEngineeringFormat(3);      // outputs: 10 [m]\nString smallOutput = smallDistance.toEngineeringFormat(3);  // outputs: 0.000124 [m]\n```\nCurious how Unitility integrates with a real project?\nExplore the [hvac-engine](https://github.com/pjazdzyk/hvac-engine) project, a comprehensive library tailored for\nHVAC engineers, offering advanced capabilities for psychrometrics and thermodynamic analysis of humid air. \nThis project served as the driving force behind the development of the library, showcasing its proficiency in addressing\nthe complex needs of HVAC professionals.\n\nFor a more specific example illustrating the integration of this library, take a look at the \n[unitility-example-project](https://github.com/pjazdzyk/unitility-example-project).\nThis project focuses on a dry air property physics app and demonstrates the library's functionality in a functional \nprogramming style using the io.vavr library.\n\n### 4.2 Parsing quantities from string\n\nThe physical quantity can be instantiated from a string representing the commonly used engineering style of writing \nvalues with units: \"{value}[{unit}]\", for example, \"20.5 [K]\", but it will also accept input without square brackets.\nTo parse a valid string into a PhysicalQuantity, you need to obtain an instance of the parsing factory provided in the core module. \nThe default parsing factory includes parsers for all supported physical quantities and their related units.\n\n```java\n// Create default parsing registry\nPhysicalQuantityParsingFactory parsingFactory = PhysicalQuantityParsingFactory.getDefaultParsingFactory();\n// Examples of string in engineering format with unit in square brackets\nString k1 = \"15.1 [W p mxK)]\";\nString k2 = \"15.1 [W/(m.K)]\";\nString k3 = \"   1 5 .  1 [   WpmK   ]\";\nString k4 = \"15.1 W/mK\";\nString k5 = \"15.1\"; // This will be resolved to default SI unit of W/mK\n// All above strings are properly resolved to Thermal Conductivity, even partially malformed k3.\nThermalConductivity thermCond1 = parsingFactory.parse(ThermalConductivity.class, k1); // {15.1 W/(m·K)}\nThermalConductivity thermCond2 = parsingFactory.parse(ThermalConductivity.class, k2); // {15.1 W/(m·K)}\nThermalConductivity thermCond3 = parsingFactory.parse(ThermalConductivity.class, k3); // {15.1 W/(m·K)}\nThermalConductivity thermCond4 = parsingFactory.parse(ThermalConductivity.class, k4); // {15.1 W/(m·K)}\nThermalConductivity thermCond5 = parsingFactory.parse(ThermalConductivity.class, k5); // {15.1 W/(m·K)}\n```\n\nParsers are designed to interpret the numeric part as the value and the content within square brackets as the unit\nsymbol. Parsers allow for a limited deviation in input style, as illustrated in the example below. The table presents \nalternative ways of expressing units in an input string:\n\n| NAME               | DEF | ALT     | EXAMPLES           |\n|--------------------|-----|---------|--------------------|\n| decimal separator  | .   | ,       | 20.1 or 20,1       |\n| degrees symbol     | °   | o, deg  | 20°, 20o, 20deg    |\n| multiplication     | ·   | x  or . | Pa·m, Pa x m, Pa.m | \n| division           | /   | p       | m/s, mps           |\n| square             | ²   | 2       | m², m2             |\n| cubic              | ³   | 3       | m³, m3             |\n| negative exponents | ⁻¹  | -1      | m⁻¹, m-1           |\n\nIMPORTANT! In case quantity includes both milli and Mega, parser works in CASE SENSITIVE mode. And in such case\nfor mega you have to provide \"MV\" for mega volts, and \"mV\" for milli volts.\n\nPlease note that this method of creating quantities is designed to be used for deserializers. \u003cBr\u003e\n**In your code, you should create units in a programmatic way, not parsing from strings.**\n\nIMPORTANT:\nWhen parsing from string values should be provided using dot \".\" as decimal separator.\u003cbr\u003e\nDO NOT USE grouping separators. \u003cbr\u003e\n\nThis will parse properly:\u003cbr\u003e\n```text\n1000000.00 [Pa] -\u003e OK\n```\nThis also ill parse properly:\u003cbr\u003e\n```text\n1_000_000.00 [Pa] -\u003e OK\n```\nBut this will not: \u003cbr\u003e\n```text\n1,000,0000.00 [Pa] -\u003e will FAIL\n```\n\n### 4.3 Logical operations\n\n* basic logic operations\n\n```java\nTemperature smallerTemp = Temperature.ofCelsius(-20.0);             \nTemperature greaterTemp = Temperature.ofCelsius(0.0);               \nTemperature smallerOrEqualTemp = Temperature.ofCelsius(-20.0);      \nTemperature greaterOrEqualTemp = Temperature.ofCelsius(0.0);        \nsmallerTemp.isLowerThan(greaterTemp);                            // is true   \ngreaterTemp.isGreaterThan(smallerTemp);                          // is true\nsmallerTemp.isEqualOrLowerThan(smallerOrEqualTemp);              // is true\ngreaterTemp.isEqualOrGreaterThan(greaterOrEqualTemp);            // is true\n```\n\nOnly quantities of the same type can be compared. When conducting a comparison, the algorithm will harmonize the target\nunit with the source unit to ensure a valid comparison.\n\n### 4.4 Arithmetic operations\n\nThe developer's choice determines whether calculations are performed using double values or with physical quantity\nobjects through the available transformation methods:\n\n* adding or subtracting quantities of the same type:\n\n```java\nTemperature sourceTemperature = Temperature.ofCelsius(20);\nTemperature temperatureToAdd = Temperature.ofKelvins(20 + 273.15);\nTemperature actualTemperature = sourceTemperature.add(temperatureToAdd); // results in: 40 °C\n```\n\nPerforming addition or subtraction will yield a PhysicalQuantity with the same unit. The algorithm will convert the unit\nof the addend quantity to match that of the augend, ensuring that the operation is conducted within a consistent unit.\nThe unit of the resulting quantity will be set to match that of the augend.\n\n* multiply or divide quantities by scalar:\n\n```java\nTemperature temperature = Temperature.ofCelsius(20);\nTemperature actualTemperature = temperature.multiply(2); // results in 40 °C\n```\n\n* multiply or divide quantities by different quantity with a result as double:\n\n```java\nTemperature sourceTemperature = Temperature.ofCelsius(20);\nPressure pressure = Pressure.ofPascal(2);\ndouble actualDivideResult = sourceTemperature.divide(pressure); // results in 40 °C\n```\n\nIn the provided example, division and multiplication both yield a double value. This outcome arises from the current\nabsence of a unit resolver within the developmental stage.\nThe existing unit values are directly employed for multiplication or division operations.\n\n* ceil, floor, roundHalfEven with relevant digits:\n\n```java\nThermalConductivity thermCond = ThermalConductivity.ofWattsPerMeterKelvin(0.00366);\nThermalConductivity ceilResult = thermCond.ceil();                  // 1\nThermalConductivity floorResult = thermCond.floor();                // 0\nThermalConductivity roundedResult = thermCond.roundHalfEven(2);     // 0.0037\n```\nPlease note that rounding function is based on concept of relevant digits. It is NOT a simple truncation to a specified\nnumber of decimal places. This function will evaluate where relevant digits starts and will attempt to keep the specified\nnumber by user. For an example for roundHalfEven(3) will output: \u003cbr\u003e\n0.12345 -\u003e roundHalfEven(3)  -\u003e 0.123 \u003cbr\u003e\n0.00012345 -\u003e roundHalfEven(3)  -\u003e 0.000123 \u003cbr\u003e\n\n### 4.5 Power and logarithmic operations\n\nUnitility supports power and logarithmic operations. Please note that if you use another PhysicalQuantity as an argument, \nthe method will resolve it to a double type (since squared units are not supported).\n\n* natural logarithm, logarithm with base of 10, custom base logarithm of 3:\n\n```java\nDistance roadLength = Distance.ofKilometers(100);\nDistance reducedDistance1 = roadLength.log();         // Distance{4.60517(..)}\nDistance reducedDistance2 = roadLength.log10();       // Distance{2.0}\n\nDistance myDistance = Distance.ofKilometers(19683);\nDistance reducedMyDistance = myDistance.logBase(3);   // Distance{9.0}\n```\n\n* power of exponent 2:\n\n```java\nDistance roadLength = Distance.ofKilometers(2);     \nDistance reducedDistance1 = roadLength.power(2);       // Distance{4.0}\n```\n\nAs previously stated, if other physicalQuantity instance is being passed as argument, it will be resolved to double:\n\n```java\ndouble leghtValue = roadLength.power(Distance.ofKilometers(2));  // 2.0\n```\n\n### 4.6 Trigonometric operations\n\nAngleUnit type physical quantities support trigonometric functions such as sin(), cos(), tan(), and cot(), \nincluding their hyperbolic and inverse variants. Here's an example of usage:\n\n```java\nAngle exampleAngle = Angle.ofDegrees(90);\ndouble sinResult = exampleAngle.sin();       // 1\ndouble cosResult = exampleAngle.cos();       // 0\n\nAngle angle45 = exampleAngle.withValue(45);\ndouble tanResult = angle45.tan();            // 1\ndouble cotResult = angle45.cot();            // 1\n\nAngle angleRad1 = exampleAngle.toRadians().withValue(1);\ndouble aSinResult = angleRad1.asin();        // 1.570796(..)\ndouble aCosResult = angleRad1.acos();        // 0\ndouble aTanResult = angleRad1.atan();        // 0.785398(..)\ndouble aCotResult = angleRad1.acot();        // 0.785398(..)\n```\nPlease keep in mind that not all values belong to the domain of a given trigonometric function.\nSome might throw an exception if they hold invalid value for a specific function.\nValue will be automatically converted to radians before using Java Math trigonometric functions.\n\n### 4.7 Equality and sorting\n\nEach PhysicalQuantity class has implemented the equals and hashCode methods and implements the Comparable interface, \nincluding additional methods that might be helpful during development. \u003cbr\u003e\n\n---\n\n**Equality condition:** PhysicalQuantity instances are equal, if they are of the same unit Type (i.e.: TemperatureUnit) and\ntheir values converted to the BASE unit are equal. \u003cbr\u003e\n\n---\n\nIn other words, equality is set to represent equality in the physical context. By default, values stored as double \ntypes are compared directly with their full decimal digit precision. In some cases, you might need to assume equality \nfor a specified level of accuracy beyond which you will consider quantities as equal in your business application. \nPlease find examples below:\n\n```java\n// Basic equality\nTemperature tempInC1 = Temperature.ofCelsius(20.0);\nTemperature tempInC2 = Temperature.ofCelsius(20.0);\nTemperature tempInK = Temperature.ofKelvins(20 + 273.15);\ntempInC1.equals(tempInC2);  // true\ntempInC1.equals(tempInK);   // true\n\n// Equality with precision\nTemperature manyDigits = Temperature.ofFahrenheit(21.1234567);\nTemperature fewDigits = Temperature.ofFahrenheit(21.123);\nmanyDigits.equals(fewDigits);                               // false\nmanyDigits.equalsWithPrecision(fewDigits, 0.001);           // true\n```\nPrecision has to be provided as commonly phrased \"epsilon\" which simply is a double value representing required decimal\naccuracy.\n\n**Sorting** is based on the same principle as equality, quantities are compared and sorted based on their physical context,\nmeaning that base unit and value in base unit are used to determine the order of elements.\n```java\n// Unsorted array of quantities\nTemperature[] temperatures = {\n    Temperature.ofCelsius(54),     // equiv. of: 327.15 K\n    Temperature.ofKelvins(10),     // equiv. of: 10.00 K\n    Temperature.ofCelsius(-20),    // equiv. of: 253.15 K\n    Temperature.ofCelsius(100)     // equiv. of: 373.15 K\n};\n// Sorting quantities        \nArrays.sort(temperatures);  // sorted order: 10K, -20oC, 54oC, 100oC\n```\nHashcode follows the same concept and is calculated for quantity base value and base unit.\n\n## 5. UNITILITY EXTENSION MODULES\n\nIn most web applications, especially in microservice architecture, data is frequently exchanged between services.\nPhysicalQuantity will have to be represented as a JSON structure in request/response objects or even as one string, \noften used as a request/query parameter or path param/variable. To address this, additional modules have been provided \nwith ready serializers/deserializers and integration with the most popular frameworks.\n\n---\n\n**IMPORTANT:** \u003cbr\u003e\n**a)** JSON request body of PhysicalQuantity should follow the structure shown in the section below (5.1) \u003cbr\u003e\n**b)** Sending request via path param or query param, should be used i.e.: 20.0oC, without square brackets or special characters.\u003cbr\u003e\n\n---\n\nJson request body example:\n```json\n{\n    \"airTemperature\": {\n      \"value\": 20.5,\n      \"unit\": \"°C\"\n    },\n    \"airPressure\": {\n      \"value\": 101325.0,\n      \"unit\": \"Pa\"\n    },\n    \"relativeHumidity\": {\n      \"value\": 55.0,\n      \"unit\": \"%\"\n    }\n}\n```\nUsing as path param:\n```text\n/api/v1/temperatures/20.5C\n```\nMake sure that quantities used in path variables or query parameters are **without** square brackets. Parsers are designed to\nfilter them out, but some recent versions of Tomcat server have issues with \"[]\" so it is better to avoid using them.\nOther special characters can be easily replaced with simpler equivalents, as presented in the table in [section 4.2](#42-parsing-quantities-from-string).\n\n## 5.1 Jackson serializers and deserializers\n### 5.1.1 Default ser-de\n\nThe Jackson library is used in many frameworks, enabling the serialization of objects into a JSON structure and\ndeserialization back to Java objects. To include this module in your project, use the following dependency:\n\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003ecom.synerset\u003c/groupId\u003e\n    \u003cartifactId\u003eunitility-jackson\u003c/artifactId\u003e\n    \u003cversion\u003e4.0.1\u003c/version\u003e\n\u003c/dependency\u003e\n```\nPhysicalQuantity JSON structure for valid serialization / deserialization has been defined as in the following example:\n```json\n{\n  \"value\": 10.5,\n  \"unit\": \"oC\"\n}\n```\nLet's assume we have an object aggregating PhysicalQuantity objects: azimuth as Angle.ofDegrees(90) and length\nDistance.ofKilometers(1.0). Serialized structure will result as shown below:\n```json\n{\n  \"length\": {\"value\": 1, \"unit\": \"km\"},\n  \"azimuth\": {\"value\": 90, \"unit\": \"°\"}\n}\n```\n\nThe Jackson module provides a configured SimpleModule with registered StdSerializer and JsonDeserializer instances. \nEach PhysicalQuantity class has its own defined deserializer. Deserializers use PhysicalQuantityParsingFactory to\nget the appropriate parser depending on the class type. This module is part of the Spring and Quarkus modules, \ntherefore, it does not need to be added explicitly if framework extensions are included in the project.\n\n### 5.1.2 Plain SI value ser-de\nAn additional set of ser-de has been provided for all cases where unit symbol is not required in the response structure,\nallowing for smaller, flat and more convenient response payloads. As a consequence, physical quantity value is converted\nto its SI base unit value during serialization. And per analogy, it is expected that incoming values for a deserialization\nprocess represent value in base SI unit.\n\nUsing the same example with length and azimuth, the serialized structure will result as shown below:\n```json\n{\n  \"length\": 1000.0,               // km were serialized with value in meters\n  \"azimuth\": 1.5707963267948966   // degrees were serialized with value in radians\n}\n```\nPlease note that values are serialized in SI base unit (meters for length, and radians for azimuth).\n\nTo change default serde for plain SI values, you have tu manually register predefined JacksonModule: \n`PhysicalQuantityJacksonModulePlainSIValue` in the instance of the `ObjectMapper`. If you already added SpringBoot or Quarkus\nUnitility modules, default Unitility Jackson serde is provided automatically via an autoconfiguration pattern.\nIn both cases, PlanSiValue Jackson module must be programmatically registered. \n\nThe Last registered module will be used for handling PhysicalQuantity serialization and deserialization. \n\nSpring Boot example:\n```java\n@Configuration\npublic class CustomModuleConfiguration {\n    @Autowired\n    void registerPlanSiValueModule(ObjectMapper objectMapper, @Qualifier(\"defaultParsingFactory\") PhysicalQuantityParsingFactory parsingFactory) {\n        objectMapper.registerModule(new PhysicalQuantityJacksonModulePlainSIValue(parsingFactory));\n    }\n}\n```\n\nQuarkus example:\n```java\n@ApplicationScoped\nclass PhysicalQuantityObjectMapperCustomizer implements ObjectMapperCustomizer {\n    @Override\n    public void customize(ObjectMapper objectMapper, @DefaultParsingFactory PhysicalQuantityParsingFactory parsingFactory) {\n        objectMapper.registerModule(new PhysicalQuantityJacksonModulePlainSIValue(parsingFactory));\n    }\n}\n```\nThis behavior of which unit should be chosen for deserialization of single value is mostly governed by `PhysicalQuantityParsingFactory`.\nIt is possible for developers to create their own implementation of PhysicalQuantityParsingFactory. Method `getDefaultUnit(Class\u003cQ\u003e targetClass)` is\nused to determine default unit for a single value quantity. \n\n## 5.2 Spring Boot module\nSpring Boot module includes **unitility-jackson** and **unitility-core** modules, and it will automatically\ncreate required beans through the autoconfiguration dependency injection mechanism. To use Spring extension module, \nadd the following dependency:\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003ecom.synerset\u003c/groupId\u003e\n    \u003cartifactId\u003eunitility-spring\u003c/artifactId\u003e\n    \u003cversion\u003e4.0.1\u003c/version\u003e\n\u003c/dependency\u003e\n```\nAdding Spring module to the project will automatically:\n- register PhysicalQuantityJacksonModule in ObjectMapper bean (JSON serialization/deserialization)\n- register Converter instances in WebMvcConfigurer bean (query/path params serialization/deserialization)\n\nThis configuration allows using PhysicalQuantity types in request / response objects and in query parameters\nor path variables, as in the example below:\n```java\n@RestController\npublic class DefaultUnitsController {\n\n    @GetMapping(\"/temperatures/{temperature}\")\n    public Temperature getTemperature(@PathVariable(\"temperature\") Temperature temperature) {\n        // Some code\n        return temperature;\n    }\n\n    @PostMapping(\"/temperatures\")\n    public Temperature postTemperature(@RequestBody Temperature temperature) {\n        // Some code\n        return temperature;\n    }\n    \n}\n```\nFor special cases when custom unit is created, which is not a part of standard Unitiltiy package, additional configuration\nsteps must be carried out to ensure that custom unit is properly resolved from JSON or path/query params. For more details\nsee a section: [Registering custom quantity in Spring](#63-registering-custom-quantities-in-spring).\u003cbr\u003e\n\n## 5.3 Quarkus module\nQuarkus module includes **unitility-jackson** and **unitility-core** modules, and it will be automatically\ndiscovered through Jandex index and will create required CDI beans. To use Quarkus extension module,\nadd following dependency:\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003ecom.synerset\u003c/groupId\u003e\n    \u003cartifactId\u003eunitility-quarkus\u003c/artifactId\u003e\n    \u003cversion\u003e4.0.1\u003c/version\u003e\n\u003c/dependency\u003e\n```\nAdding Quarkus module to the project will automatically:\n- register PhysicalQuantityJacksonModule in ObjectMapper using ObjectMapperCustomizer bean (JSON serialization/deserialization)\n- register ParamConverter instances in Jakarta ParamConverterProvider bean (query/path params serialization/deserialization)\n\nThis configuration allows using PhysicalQuantity types in request / response objects and in query parameters\nor path variables, as in the example below\n\n```java\npublic class DefaultUnitsResource {\n\n    @GET\n    @Path(\"/temperatures/{temperature}\")\n    @Produces(MediaType.APPLICATION_JSON)\n    public Temperature getTemperature(@PathParam(\"temperature\") Temperature temperature) {\n        // Some code\n        return temperature;\n    }\n\n    @POST\n    @Path(\"/temperatures\")\n    @Produces(MediaType.APPLICATION_JSON)\n    public Temperature postTemperature(Temperature temperature) {\n        // Some code\n        return temperature;\n    }\n}\n```\nFor special cases when custom unit is created, which is not a part of standard Unitiltiy package, additional configuration\nsteps must be carried out to ensure that custom unit is properly resolved from JSON or path/query params. For more details\nsee a section: [Registering custom quantity in Quarkus](#64-registering-custom-quantities-in-quarkus).\n\n## 5.4 Jakarta validation\nThe validation module encompasses preconfigured validator classes along with corresponding annotations designed for bean \nvalidation purposes. The available annotations are as follows:\n- @PhysicalMin\n- @PhysicalMax\n- @PhysicalRange\n\nThese annotations are designed to accept a string containing a value and its corresponding unit, representing a physical \nquantity. The validator will utilize the default parsing factory utility to generate a PhysicalQuantity instance reflecting\nthe provided limiting value. Subsequently, it will compare this instance to the validated quantity. Both quantities will \nundergo comparison based on their base units, allowing users to conveniently specify limits in any of the supported units.\n\nBy default, the provided limits are inclusive. To designate a value as exclusive, employ the parameter \"inclusive = false\".\n\nValidators will be automatically discovered via Spring Boot or Quarkus dependency injection mechanisms once the dependency\nis added to the pom.xml file or any other dependency management tool.\n\nExample of specifying minimum value limit, inclusive:\n```java\n    @GetMapping(\"/dry-air\")\n    DryAirResponse getDryAir(@RequestParam @PhysicalMin(\"-150oC\") Temperature temperature);\n```\n\nExample of specifying maximum value as exclusive limit:\n```java\n    @GetMapping(\"/dry-air\")\n    DryAirResponse getDryAir(@RequestParam @PhysicalMax(value = \"373.15K\", inclusive = false) Temperature temperature);\n```\n\nExample of specifying range, where both limits are exclusive:\n```java\n    @GetMapping(\"/dry-air\")\n    DryAirResponse getDryAir(@RequestParam @PhysicalRange(min = \"-150oC\", minIncl = false, max = \"1000oC\", maxIncl = false) Temperature temperature);\n```\nKeep in mind that validated class must be annotated with: @Validated annotation.\n\nFollowing dependencies MUST be added to the project in order to unitility-validation to work:\n- SpringBoot: **[spring-boot-starter-validation](https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-validation)**\n- Quarkus: **[quarkus-hibernate-validator](https://mvnrepository.com/artifact/io.quarkus/quarkus-hibernate-validator)**\n\n## 6. CREATING CUSTOM QUANTITIES\nThe Unitility includes a set of the most commonly used quantities and related units with an emphasis on thermodynamics. \nHowever, the framework foundation can be successfully used to define almost any unit from economy, biology, electronics, \nand even for logistics to represent the quantity of bottles in different sized packages. Sooner or later, a developer \nmight face a case where he would like to add a new unit or quantity to the library. I will be including requested units on\na regular basis. If this is not urgent, please go to the [ISSUES](https://github.com/pjazdzyk/unitility/issues) page and \nlet me know what is needed. If you can't wait, below are instructions on how to create a custom unit and also how to ensure \nthat all your custom units/quantities are registered correctly in Spring or Quarkus.\n\n### 6.1 Custom unit\nIf you need to extend standard unit definitions for a given quantity, the simplest way is to create a new unit\nclass extending the interface of the desired unit family. To present this, let's add a new unit of [Rankine](https://en.wikipedia.org/wiki/Rankine_scale)\ndegree to Temperature family. Create class or enum (my preference) and extend [TemperatureUnits](https://github.com/pjazdzyk/unitility/blob/master/unitility-core/src/main/java/com/synerset/unitility/unitsystem/thermodynamic/Temperature.java)\ninterface and implement required methods to convert from base unit to Rankine and vice versa.\nTo ensure valid conversion with standard Temperature unit family, set the same base unit type as in the\nlibrary default [TemperatureUnits](https://github.com/pjazdzyk/unitility/blob/master/unitility-core/src/main/java/com/synerset/unitility/unitsystem/thermodynamic/TemperatureUnits.java):\n```java\n@Override\npublic TemperatureUnit getBaseUnit() {\n   return TemperatureUnits.KELVIN;\n}\n```\nAfter this is done, you can use your own custom unit in Temperature class and convert between standard library units\nand your own custom units:\n```java\nTemperature tempInC = Temperature.ofKelvins(100);                    // 100.00 K\nTemperature tempInR = Temperature.of(200, CustomTempUnits.RANKINE);  // eq. of 111.111 K\nTemperature totalTemp = temperatureInC.plus(temperatureInR);         // 211.111 K\n```\n\n### 6.2 Custom physical quantity\nIn this section, we create new CustomAngle quantity together with new CustomAngleUnit of **Revolutions [rev]**. \nTo do this, we need to create a new class, CustomAngle, and extend the CalculableQuantity\u003cAngleUnit, CustomAngle\u003e interface.\nCalculableQuantity is PhysicalQuantity with arithmetic operations handling.\nIn typical case, most of the required methods are defined as default in the interface, which should be sufficient for most cases. \nHowever, some needs to be implemented for the new unit, as shown in the example here:\n[CustomAngle](https://github.com/pjazdzyk/unitility-spring-example/blob/master/src/main/java/com/synerset/unitility/spring/examples/newquantity/customunit/CustomAngle.java).\n\nIn the next step, create your implementation of AngleUnit interface, which should include the way, how to\nconvert one unit from another. Here is an example based on enum to introduce revolution unit: \n[CustomAngleUnits](https://github.com/pjazdzyk/unitility-spring-example/blob/master/src/main/java/com/synerset/unitility/spring/examples/newquantity/customunit/CustomAngleUnits.java).\n\nAfter this is done, we can create and use our custom angles:\n```java\n// Creating instance of custom unit\nCustomAngle revolutions = CustomAngle.ofRevolutions(1);         // CustomAngle{1.0 rev}\n// Converting to any unit implementing AngleUnit type\nCustomAngle degrees = revolutions.toUnit(AngleUnits.DEGREES);   // CustomAngle{360.0°}\n// All transformations work withing the units implementing AngleUnit\nCustomAngle resultingRevolutions = revolutions.plus(degrees);   // CustomAngle{2.0 rev}\n```\n\n### 6.3 Registering custom quantities in SPRING\n\nAfter creating a custom unit, to ensure that it is properly resolved from JSON or path/query params, the following steps\nmust be taken to make it work. A complete example of a new custom unit properly registered in a Spring framework can be \nfound here: [unitility-spring-example](https://github.com/pjazdzyk/unitility-spring-example).\n\nAs a first step, a new parsing factory has to be created, which must include currently supported quantities and custom\nuser quantities:\n[CustomParsingFactory](https://github.com/pjazdzyk/unitility-spring-example/blob/master/src/main/java/com/synerset/unitility/spring/examples/newquantity/CustomParsingFactoryWithAngle.java).\n\nAfter a new parsing factory is created and all standard and new custom quantities parsers are properly registered,\nyou can now create a configuration and register new JacksonModule and new Converter in FormatterRegistry: \n[CustomAngleConfiguration](https://github.com/pjazdzyk/unitility-spring-example/blob/master/src/main/java/com/synerset/unitility/spring/examples/newquantity/CustomAngleConfiguration.java).\n\nAfter this step is done, you can freely use your CustomAngle unit in your web application:\n```java\n@RestController\npublic class CustomAngleController {\n\n    private final Logger logger = LoggerFactory.getLogger(this.getClass().getSimpleName());\n\n    @GetMapping(\"/angles/{angle}\")\n    public CustomAngle getCustomAnglePath(@PathVariable(\"angle\") CustomAngle customAngle) {\n        processCustomAngle(customAngle);\n        return customAngle;\n    }\n\n    @PostMapping(\"/angles\")\n    public CustomAngle getCustomAngleBody(@RequestBody CustomAngle customAngle) {\n        processCustomAngle(customAngle);\n        return customAngle;\n    }\n}\n```\n\n### 6.4 Registering custom quantities in Quarkus\nRegistering custom unit in Quarkus is a bit different compared to Spring.\nA Complete example of new custom unit properly registered in a Quarkus framework can be\nfound here: [unitility-quarkus-example](https://github.com/pjazdzyk/unitility-quarkus-example).\nIn this case, a first step is the same, new parsing factory must be created to include currently supported \nand new custom quantities created by user: [CustomParsingFactory](https://github.com/pjazdzyk/unitility-quarkus-example/blob/master/src/main/java/com/synerset/unitility/quarkus/example/newquantity/CustomParsingFactoryWithAngle.java).\n\nAfter a new parsing factory is created and all standard and new custom quantities parsers are properly registered,\nyou can now create a new ObjectMapperCustomizer and register JacksonModule with new parsing factory: \n[CustomObjectMapperCustomizer](https://github.com/pjazdzyk/unitility-quarkus-example/blob/master/src/main/java/com/synerset/unitility/quarkus/example/newquantity/CustomAngleObjectMapperCustomizer.java).\n\nThe Last step is to define new PathParamConverterProvider and register ParamConverters for all custom quantities:\n[CustomAngleParamProvider](https://github.com/pjazdzyk/unitility-quarkus-example/blob/master/src/main/java/com/synerset/unitility/quarkus/example/newquantity/CustomAngleProvider.java).\n\nAfter this step is done, you can freely use your CustomAngle unit in your web application:\n```java\n@RestController\npublic class CustomAngleController {\n\n    private final Logger logger = LoggerFactory.getLogger(this.getClass().getSimpleName());\n\n    @GetMapping(\"/angles/{angle}\")\n    public CustomAngle getCustomAnglePath(@PathVariable(\"angle\") CustomAngle customAngle) {\n        processCustomAngle(customAngle);\n        return customAngle;\n    }\n\n    @PostMapping(\"/angles\")\n    public CustomAngle getCustomAngleBody(@RequestBody CustomAngle customAngle) {\n        processCustomAngle(customAngle);\n        return customAngle;\n    }\n}\n```\nIn Quarkus, IntellijJ might highlight PhysicalQuantity types when used as path or query parameters, but when you run the\napplication, they will be correctly resolved.\n\n## 7. COMPATIBILITY WITH OTHER JVM LANGUAGES\nUnitility can be easily used with other JVM-based programming languages. There are some features of these languages that \nmake using Unitility easier and more elegant, for example, through the use of overloaded operators. I am more than happy \nto provide even greater integration with other JVM languages, please use [ISSUES](https://github.com/pjazdzyk/unitility/issues)\nto leave there any suggestion in that regard.\n\n### 7.1 Groovy - using overloaded operators\nPlease find the below example of Unitility usage in Groovy, taking advantage of overloaded operators:\n```groovy\n// Temperature examples\ndef t1 = Temperature.ofCelsius(20)\ndef t2 = Temperature.ofCelsius(10)\ndef t3 = Temperature.ofKelvins(303.15)  // =30 oC\n// Adding \u0026 subtracting: The same unit\ndef t4 = t1 + t2                // Temperature{30.0°C}\ndef t5 = t1 - t2                // Temperature{10.0°C}\ndef t6 = t1 + 15.5              // Temperature{35.5°C}    \n// Adding \u0026 subtracting: Different units of the same quantity,\n// resolving to a first addend unit type.\ndef t7 = t1 + t3                // Temperature{50.0°C}\ndef t8 = t1 - t3                // Temperature{-10.0°C}\n// Multiply\ndef t9 = t1 * t2                // will resolve to double = 200.0\ndef t10 = t1 * 2                // Temperature{40.0°C}\n// Divide\ndef t11 = t1 / t2               // will resolve to double = 2.0\ndef t12 = t1 / 2                // Temperature{10.0°C}\n// Logical operations\ndef isGreater = t1 \u003e t2         // true\ndef isLower = t1 \u003c t2           // false\ndef isGreaterOrEq = t1 \u003e= t2    // true\ndef isEqual = t1 == t1          // true\n```\n\n### 7.2 Kotlin - using overloaded operators\nUsage in Kotlin is analogous, please find the below some examples:\n```kotlin\n// Temperature examples\nval t1 = Temperature.ofCelsius(20.0)\nval t2 = Temperature.ofCelsius(10.0)\nval t3 = Temperature.ofKelvins(303.15) // =30 oC\n\n// Adding \u0026 subtracting: The same unit\nval t4 = t1 + t2\nval t5 = t1 - t2\nval t6 = t1 + 15.5\nprintln(t4) // Temperature{30.0°C}\nprintln(t5) // Temperature{10.0°C}\nprintln(t6) // Temperature{35.5°C}\n\n// Adding \u0026 subtracting: Different units of the same quantity,\n// resolving to a first addend unit type.\nval t7 = t1 + t3\nval t8 = t1 - t3\nprintln(t7) // Temperature{50.0°C}\nprintln(t8) // Temperature{-10.0°C}\n\n// Multiply\nval t9 = t1 * t2\nval t10 = t1 * 2.0\nprintln(t9) // will resolve to double = 200.0\nprintln(t10) // Temperature{40.0°C}\n\n// Divide\nval t11 = t1 / t2\nval t12 = t1 / 2.0\nprintln(t11) // will resolve to double = 2.0\nprintln(t12) // Temperature{10.0°C}\n\n// Logical operations\nval isGreater = t1 \u003e t2         // true\nval isLower = t1 \u003c t2           // false\nval isGreaterOrEq = t1 \u003e= t2    // true\nval isEqual = t1 == t1          // true\n```\n\n## 8. SPECIAL TYPES: GEOGRAPHIC\nA set of dedicated types has been provided to cover spatial types used in expressing geographic measures: Latitude, \nLongitude, GeoCoordinate and GeoDistance. These classes allow representing coordinates on Earth and real curvature\ndistance between these coordinates.\n\n### 8.1 Geographic Latitude, Longitude and GeoCoordinate\nThe **Latitude** class includes methods that allow for easy conversion to the ICAO Annex 15 compliant Degrees-Minutes-Seconds\n(DMS) format with a default resolution of 0.01 seconds. This format provides a standardized representation of geographic\ncoordinates, making it convenient for aviation and other applications where ICAO-compliant DMS notation is required.\nLatitude range is: -90 to 90 degrees. \u003cbr\u003e\nThe **Longitude** class, analogous to the Latitude class, represents a geographic longitude coordinate. It adheres to the\nstandard range of -180 to +180 degrees, covering the westernmost point at -180 degrees and the easternmost point\nat +180 degrees. Like Latitude, it supports ICAO Annex 15 compliant DMS formatting with configurable resolution.\n\n```java\n// Latitude and Longitude types are based on Angular units\nLatitude latitude = Latitude.ofDegrees(-20.123);\nLongitude longitude = Longitude.ofDegrees(20.123);\n\n// ICAO Annex 15 compliant DMS formatting with 0.01 seconds resolution\nString latInDMS = latitude.toDMSFormat();           // Outputs: 20°07'22.80\"S\nString lonInDMS = longitude.toDMSFormat();          // Outputs: 020°07'22.80\"E\n\n// Custom resolution DMS formatting\nString latInDMSHighPrec = latitude.toDMSFormat(0.001); // Outputs: 20°07'22.800\"S\nString lonInDMSLowPrec = longitude.toDMSFormat(0.1);   // Outputs: 020°07'22.8\"E\n\n// Engineering format\nString latInENG = latitude.toEngineeringFormat();   // Outputs: -20.123 [°]\n```\nYou can also create Latitude or Longitude instance providing degrees, minutes and seconds:\n```java\n// Instance from degrees, minutes, seconds\nLatitude latFromDMS = Latitude.ofDegMinSec(20, 7, 22.8);     // Latitude{-20.123°}\nLongitude longFromDMS = Longitude.ofDegMinSec(20, 7, 22.8);  // Longitude{20.123°}\n\n// From ICAO DMS string format\nLatitude latFromICAO = Latitude.ofDMSFormat(\"20°07'22.80\\\"S\");\nLongitude lonFromICAO = Longitude.ofDMSFormat(\"020°07'22.80\\\"E\");\n```\n\nThe **GeoCoordinate** class combines both Latitude and Longitude to form a complete geographic coordinate. It facilitates\neasy management and manipulation of spatial data, allowing seamless integration into various applications requiring\nprecise location information. All DMS outputs are ICAO Annex 15 compliant by default.\n\n```java\n// GeoCoordinate class represents a coordinate of specific point in the globe, using Latitude and Longitude and optional name\nGeoCoordinate coordinateExample = GeoCoordinate.of(latitude, longitude, \"my location\");\n\n// GeoCoordinate can be reduced to ICAO DMS format, ENG format, or decimal degrees format\nString geoCoordDMS = coordinateExample.toDMSFormat();             // 20°07'22.80\"S, 020°07'22.80\"E\nString geoCoordDMSHighPrec = coordinateExample.toDMSFormat(0.001); // 20°07'22.800\"S, 020°07'22.800\"E\nString geoCoordEND = coordinateExample.toEngineeringFormat();     // -20.123 [°], 20.123 [°]\nString geoCoordDEC = coordinateExample.toDecimalDegrees();        // -20.123, 20.123\n\n// Create from ICAO DMS strings\nGeoCoordinate icaoCoord = GeoCoordinate.ofDMSFormat(\"20°07'22.80\\\"S\", \"020°07'22.80\\\"E\");\n```\n\nLatitude and Longitude do not enforce any angular value limit, but GeoCoordinate will do. Make sure that your\nlatitude and longitude values fall withing planet Earth's acceptable limits.\n\n### 8.2 Bearing\nThe Bearing class represents a direction relative to the Earth's true north, encapsulating both the true bearing and the\nsigned relative bearing. The true bearing is provided as an absolute value in the range of \u003c0, 360\u003e degrees, while the \nrelative signed bearing falls within the range of \u003c-180, 180\u003e. This class allows you to easily work with bearings, whether \nyou're converting between true or signed bearings, or working with cardinal directions. \u003cbr\u003e\n\nHere are a few examples of how to create new Bearing instances:\n```java\nBearing bearing = Bearing.of(270);\nBearing bearingSigned = Bearing.ofSigned(-90);\n// You can also access current bearing from GeoDistance (defined above, from Wrocław to New York) instance:\ndouble signedBearingValue = geoDistance.getBearing().getSignedValue(); // -61.07916, be default Bearing is always in degrees\ndouble trueBearing = geoDistance.getBearing().getValue(); // 298.920844°, be default Bearing is always in degrees\n```\nBearing can be also created from other Bearing instance or provided cardinal direction constant:\n```java\nBearing bearingNorth = Bearing.of(CardinalDirection.NORTH); // Bearing{0.0°}\nBearing bearingNorthWithOffset = Bearing.of(CardinalDirection.NORTH, Bearing.ofSigned(-10)); // Bearing{350.0°}\nBearing bearing = Bearing.of(270);\nBearing bearingFromOtherBearing = bearing.from(Bearing.ofSigned(20)); // Bearing{290.0°}\n```\n\n### 8.3 GeoDistance - spherical distance between two coordinates\nThe **GeoDistance** class represents the spherical distance between two coordinates on Earth, considering \nthe curvature of the Earth. It incorporates calculations involving the start and target coordinates, true bearing, \nand distance in specified units. The underlying [Haversine equations](https://en.wikipedia.org/wiki/Haversine_formula) \nserve as the basis for the curved distance calculation. To create a GeoDistance object, you can initialize it with start\nand target coordinates along with the desired unit type for representing the distance. See the example below:\n```java\nGeoCoordinate wroclaw = GeoCoordinate.of(Latitude.ofDegrees(51.102772), Longitude.ofDegrees(16.885802));\nGeoCoordinate newYork = GeoCoordinate.of(Latitude.ofDegrees(40.712671), Longitude.ofDegrees(-74.004655));\nGeoDistance geoDistance = GeoDistance.ofKilometers(wroclaw, newYork);\nString distanceInEng = geoDistance.toEngineeringFormat();   // 6669.896095258197 [km]\nBearing trueBearing = geoDistance.getBearing();             // Bearing{298.920844°}\n```\n\nGeoDistance can be initialized with a starting coordinate, bearing, and distance. \nIn this case, the target coordinate will be computed based on the given parameters. \n**The provided distance MUST represent the true geodesic (curved) distance along the Earth's surface.**\n```java\nGeoDistance toNewYork = GeoDistance.of(wroclaw, bearing, Distance.ofKilometers(6669.896095258197));\nString targetCoordinate = toNewYork.getTargetCoordinate().toDecimalDegrees();   // 40.712671, -74.004655\n```\nAs you can see the results, we have set heading towards New York from Wrocław with previously calculated curved distance\nbetween these two cities, and we have reached to exactly the same spot.\u003cbr\u003e\n\n**Progression and translation:** \u003cbr\u003e\nThe GeoDistance class supports two methods to simulate distance change:\u003cbr\u003e\na) methods named: with(), which accepts new target coordinate or bearing and distance, retaining current starting point\ncoordinate and calculates distance to the new coordinates (specified or calculated):\n```java\n// Creating geo coordinate representing city of Wellington\nGeoCoordinate wellington = GeoCoordinate.of(Latitude.ofDegrees(-41.289463), Longitude.ofDegrees(174.774913));\n// Setting new target to Wellington, current start is not changed (New York)\nGeoDistance toWellington = toNewYork.with(wellington);\nDistance distance = toWellington.getDistance();                                      // 18005.6198226 km\nString wroclawCoordinate = toWellington.getStartCoordinate().toDecimalDegrees();     // 51.102772, 16.885802\nString wellingtonCoordinate = toWellington.getTargetCoordinate().toDecimalDegrees(); // -41.289463, 174.774913\n```\nPreviously, toNewYork represented the distance from Wrocław to New York. Following this change, it now represents the \ndistance from Wrocław to Wellington, along with the updated bearing and calculated distance. \u003cbr\u003e\n\nb) methods named translate() will accept a new target coordinate or a bearing and distance. The previous target coordinate \nwill be set as the starting coordinate, and the distance to the new coordinates, whether explicitly specified or derived, will be \ncalculated.\n\n```java\n// Previous target is now current start (Wrocław), and current target is set from an argument (Wellington)\nGeoDistance toWellingtonBis = toNewYork.translate(wellington);\nDistance distanceBis = toWellingtonBis.getDistance();                                      // 14403.6934729 km\nString wroclawCoordinateBis = toWellingtonBis.getStartCoordinate().toDecimalDegrees();     // 40.712671, -74.004655\nString wellingtonCoordinateBis = toWellingtonBis.getTargetCoordinate().toDecimalDegrees(); // -41.289463, 174.774913\n```\nPreviously, toNewYork represented the distance from Wrocław to New York. After this change, it now represents the distance\nfrom New York to Wellington, along with the updated bearing and calculated distance. \u003cbr\u003e\n\n### 8.4 Parsing geographic quantities and JSON structures\nParsers have been provided mostly for the purpose of deserialization in Spring Boot or Quarkus, but they can also be used directly \nin the code:\n```java\nhysicalQuantityParsingFactory geoParsingFactory = PhysicalQuantityParsingFactory.getDefaultParsingFactory();\nLatitude parsedLat1 = geoParsingFactory.parse(Latitude.class, \"20°7'22.8\\\"S\");\nLatitude parsedLat2 = geoParsingFactory.parse(Latitude.class, \"20deg 7min 22.8sec\");\n```\nLatitude, Longitude can be used as JSON request body or as value in path variable / query param. Path param usage example:\n```text\nLatitude path param usage example:\n/routes/latitude/20°7'22.8\"S/longitude/-14°7'12.4\"W\n/routes/latitude/20o7min22.8secS/longitude/-14o7min12.4secW\n/routes/latitude/20.123deg/longitude/-14.123deg\n/routes/latitude/20.123/longitude/-14.123\n```\n\nAs you can observe, the deserializer behaves liberally, accepting input in various formats to accommodate users' \npreferences. Latitude, Longitude, GeoCoordinate, and GeoDistance each have their own JSON deserializers, enabling their \nuse in request bodies. A couple of simple examples are provided below. \u003cbr\u003e\n\nExample: Latitude in DMS format as a JSON request body:\n```json\n{\n  \"value\" : \"20°7'22.80000000000399\\\"S\"\n}\n```\nExample: Latitude as a pair of value and unit symbol:\n```json\n{\n  \"value\" : -20.123456,\n  \"unit\": \"deg\"\n}\n```\nExample: Latitude as single value, which will always be resolved to decimal degrees:\n```json\n{\n   \"value\" : -20.123456\n}\n```\nThe **GeoCoordinate** JSON structure follows the same pattern.\u003cbr\u003e\nExample: GeoCoordinate defined by Latitude in DMS format and longitude as a value-unit pair:\n```json\n{\n  \"latitude\" : {\n      \"value\" : \"20°7'22.80\\\"S\"\n    },\n  \"longitude\" : {\n      \"value\" : -14.1234,\n      \"unit\": \"deg\"\n    }\n}\n```\n**GeoDistance** is serialized or deserialized from the exemplary structures as presented below.\u003cbr\u003e\n\nExample: GeoDistance - the base case, where both start and target coordinates are known:\n```json\n{\n    \"startCoordinate\" : {\n        \"latitude\": {\n            \"value\": -20.123,\n            \"unit\": \"deg\"\n        },\n        \"longitude\": {\n            \"value\": 20.123,\n            \"unit\": \"°\"\n        },\n        \"name\": \"MyStartLoc\"\n    },\n    \"targetCoordinate\" : {\n        \"latitude\": {\n            \"value\": -20.123,\n            \"unit\": \"°\"\n    },\n    \"longitude\": {\n        \"value\": 20.123,\n        \"unit\": \"°\"\n      },\n      \"name\": \"MyTargetLoc\"\n    }\n}\n```\nExample: GeoDistance in a case where the start coordinate, true bearing, and distance to the target are known:\n```json\n{\n    \"startCoordinate\" : {\n        \"latitude\": {\n            \"value\": -20.123,\n            \"unit\": \"deg\"\n        },\n        \"longitude\": {\n            \"value\": 20.123,\n            \"unit\": \"°\"\n        },\n        \"name\": \"MyStartLoc\"\n    }, \n    \"bearing\": {\n        \"value\": 339.877,\n        \"unit\": \"deg\"\n    }, \n    \"distance\": {\n        \"value\": 1000.0,\n        \"unit\": \"km\"\n    }\n}\n```\nGeneral note: \u003cbr\u003e\n- Latitude \u0026 Longitude: field \"**value**\" for Latitude and Longitude is mandatory, it can accept double value or a string in DMS or ENG format,\nfield \"**unit**\" is optional, it is omitted when value is provided as DMS or ENG format. In case of value provided as a \ndouble, unit will be automatically resolved to decimal degrees.\n- GeoCoordinate: fields \"**latitude**\" and \"**longitude**\" are mandatory, \"**name**\" is optional.\n- GeoDistance: field \"**startCoordinate**\" is mandatory. For variant with two coordinates \"**targetCoordinate**\" must be provided,\nif not available the \"**bearing**\" and \"**distance**\" must be provided.\n\nArithmetic transformations: \u003cbr\u003e\nFor Latitude, Longitude, and GeoDistance, arithmetic operations function in the same manner as for other PhysicalQuantities.\nGeoCoordinate does not support arithmetic operations, as it is just a composite data objects used to construct GeoDistance.\nIt's important to note that the natural behavior is such that the result of arithmetic operations will be based on the with()\nprogression. This means that the added value will maintain the starting point, adjust the distance, and recalculate the \ntarget coordinate in the process. \u003cbr\u003e\n\n```java\n// Sum of two GeoQuantities\nGeoCoordinate start = GeoCoordinate.of(Latitude.ofDegrees(51.1), Longitude.ofDegrees(16.9));\nGeoCoordinate target = GeoCoordinate.of(Latitude.ofDegrees(40.8), Longitude.ofDegrees(-74.1));\n\nGeoDistance firstGeoDistance = GeoDistance.ofKilometers(start, target);         // 6670.048729447209 km\nGeoDistance secondGeoDistance = firstGeoDistance.translate(Distance.ofKilometers(1000)); // 1000 km\n\nGeoDistance sumOfDistances = firstGeoDistance.plus(secondGeoDistance);          // 7670.048729447209 km and new target\nGeoDistance greaterDistance = sumOfDistances.plus(Distance.ofKilometers(1000)); // 8670.048729447209 km and new target\nGeoDistance evenGreaterDistance = greaterDistance.plus(1000);                   // 9670.048729447209 km and new target\n```\n## 9. PERSISTENCE\nPhysicalQuantity is composed of value and unit, and in some cases eg: geographic quantities, it can be even more complex.\nTo simplify persisting Entities in a database using Hibernate or other ORMs implementing JPA specification, a set of converters\nhas been provided to speed up the development process.\n\n### 9.1 Attribute Converters - Default SI\nAt this moment there is only one set of converters available, utilizing \"plain SI\" strategy. It means, that for standard\nvalue-unit quantities, only the value converted to SI base unit is persisted in the database column, allowing for fast and\nefficient data storage.\n\nUse of the converters is fairy simple, ensure that you have proper hibernate or other JPA ORM dependency in our system.\nBelow is an example of converter usage:\n\n```java\n@Entity\npublic class ComponentDimension {\n\n    @Id\n    @GeneratedValue(strategy = GenerationType.IDENTITY)\n    private Long id;\n\n    @ManyToOne(fetch = FetchType.LAZY)\n    @JoinColumn(name = \"conduit_id\",\n            foreignKey = @ForeignKey(name = \"fk_component_dimension_conduit\"))\n    private Conduit conduit;\n    \n    @Convert(converter = DistancePlainSiConverter.class)  // Converter is used here\n    private Distance nominalSize;\n\n    @Convert(converter = SDRPlainSiConverter.class)       // Converter is used here\n    private SDR sdr;\n}\n```\nSpecial consideration is required for Geographic quantities. Longitude, Latitude and Bearing can be represented in the same\nway as any other simply physical quantity, but GeoCoordinate and GeoDistance is more complex.\n\nThe `GeoCoordinate` is converted to string, comprising longitude and latitude separated by comma `,`:\n```java\n'90.0,40.0'\n```\nThe GeoDistance is converted do string, comprising start geo coordinate and target geo coordinate separated by semicolon `;`: \n```java\n'90.0,40.0;40.0,90.0'\n```\nThis may cause problems if these values are being used as filtering criteria in queries.\n\n## 10. COLLABORATION, ATTRIBUTION, AND CITATION\n\nI welcome other developers who are interested in physics and engineering to collaborate on this project.\nAny contributions or suggestions would be greatly appreciated.\u003cbr\u003e\nFeel free to contact me if yoy have any questions or comments.\n\n---\n\n**If there are particular units relevant to your scientific field that you'd like me to include, please inform me.\u003cbr\u003e**\n**I aim for this library to be helpful and simplify your life.**\n\n---\n\nThis work is licensed under the terms of the **APACHE 2.0** License with the additional requirement that proper attribution be\ngiven\nto the [Piotr Jażdżyk](https://www.linkedin.com/in/pjazdzyk) as the original author in all derivative works and\npublications.\n\nFor citation and attribution, I have provided badges that you can include in your project to showcase your usage\nof the library: \u003cbr\u003e\n\nSmall shield with referenced most recent version tag:\u003cbr\u003e\n[![Unitility](https://img.shields.io/github/v/release/pjazdzyk/Unitility?label=Unitility\u0026color=13ADF3\u0026logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMi41bW0iIGhlaWdodD0iMTQuNW1tIiB2aWV3Qm94PSIwIDAgMjI1MCAxNDUwIj4NCiAgPHBvbHlnb24gZmlsbD0iIzUwN0QxNCIgcG9pbnRzPSIyMjQxLjAzLDE1Ljg4IDExMzYuMzgsMTUuODQgOTA1Ljg4LDQxNS4xIDIwMTAuNTMsNDE1LjA5IiAvPg0KICA8cG9seWdvbiBmaWxsPSIjNzFBQjIzIiBwb2ludHM9IjExMTYuMzgsMTUuODQgNjU1Ljk5LDE1Ljg0IDQ5NC4xNSwyOTYuMTcgNzI0LjM1LDY5NC44OCIgLz4NCiAgPHBvbHlnb24gZmlsbD0iIzhBQzkzNCIgcG9pbnRzPSI0ODQuMTUsMzA2LjE3IDI1NS4wNiw3MDIuOTYgMzg3LjY2LDkzMi42NCA4NDUuODMsOTMyLjYzIiAvPg0KICA8cG9seWdvbiBmaWxsPSIjNThEMEZGIiBwb2ludHM9Ii03LjE3LDE0NDAuMDkgMTA5Ny45NywxNDQwLjA4IDEzMjguNDcsMTA0MC44MyAyMjMuMzIsMTA0MC44NSIgLz4NCiAgPHBvbHlnb24gZmlsbD0iIzEzQURGMyIgcG9pbnRzPSIxNzM5LjA0LDExNjAuOTEgMTUwOS4wOSw3NjIuNjQgMTExNy45NywxNDQwLjA4IDExODYuOTMsMTQ0MC4wOCAxNTc3Ljg3LDE0NDAuMDgiIC8+DQogIDxwb2x5Z29uIGZpbGw9IiMwMzkzRDAiIHBvaW50cz0iMTk3OC44LDc1Mi45NiAxODQ2LjIsNTIzLjMgMTM4Ni42OCw1MjMuMyAxNzQ5LjA0LDExNTAuOTEiIC8+DQo8L3N2Zz4=)](https://github.com/pjazdzyk/Unitility)\n\n```markdown\n[![Unitility](https://img.shields.io/github/v/release/pjazdzyk/Unitility?label=Unitility\u0026color=13ADF3\u0026logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMi41bW0iIGhlaWdodD0iMTQuNW1tIiB2aWV3Qm94PSIwIDAgMjI1MCAxNDUwIj4NCiAgPHBvbHlnb24gZmlsbD0iIzUwN0QxNCIgcG9pbnRzPSIyMjQxLjAzLDE1Ljg4IDExMzYuMzgsMTUuODQgOTA1Ljg4LDQxNS4xIDIwMTAuNTMsNDE1LjA5IiAvPg0KICA8cG9seWdvbiBmaWxsPSIjNzFBQjIzIiBwb2ludHM9IjExMTYuMzgsMTUuODQgNjU1Ljk5LDE1Ljg0IDQ5NC4xNSwyOTYuMTcgNzI0LjM1LDY5NC44OCIgLz4NCiAgPHBvbHlnb24gZmlsbD0iIzhBQzkzNCIgcG9pbnRzPSI0ODQuMTUsMzA2LjE3IDI1NS4wNiw3MDIuOTYgMzg3LjY2LDkzMi42NCA4NDUuODMsOTMyLjYzIiAvPg0KICA8cG9seWdvbiBmaWxsPSIjNThEMEZGIiBwb2ludHM9Ii03LjE3LDE0NDAuMDkgMTA5Ny45NywxNDQwLjA4IDEzMjguNDcsMTA0MC44MyAyMjMuMzIsMTA0MC44NSIgLz4NCiAgPHBvbHlnb24gZmlsbD0iIzEzQURGMyIgcG9pbnRzPSIxNzM5LjA0LDExNjAuOTEgMTUwOS4wOSw3NjIuNjQgMTExNy45NywxNDQwLjA4IDExODYuOTMsMTQ0MC4wOCAxNTc3Ljg3LDE0NDAuMDgiIC8+DQogIDxwb2x5Z29uIGZpbGw9IiMwMzkzRDAiIHBvaW50cz0iMTk3OC44LDc1Mi45NiAxODQ2LjIsNTIzLjMgMTM4Ni42OCw1MjMuMyAxNzQ5LjA0LDExNTAuOTEiIC8+DQo8L3N2Zz4=)](https://github.com/pjazdzyk/Unitility)\n```\n\nTech shield with version tag for manual adjustment (you can indicate which version you actually use): \u003cbr\u003e\n[![Unitility](https://img.shields.io/badge/UNITILITY-v4.0.0-13ADF3?style=for-the-badge\u0026logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMi41bW0iIGhlaWdodD0iMTQuNW1tIiB2aWV3Qm94PSIwIDAgMjI1MCAxNDUwIj4NCiAgPHBvbHlnb24gZmlsbD0iIzUwN0QxNCIgcG9pbnRzPSIyMjQxLjAzLDE1Ljg4IDExMzYuMzgsMTUuODQgOTA1Ljg4LDQxNS4xIDIwMTAuNTMsNDE1LjA5IiAvPg0KICA8cG9seWdvbiBmaWxsPSIjNzFBQjIzIiBwb2ludHM9IjExMTYuMzgsMTUuODQgNjU1Ljk5LDE1Ljg0IDQ5NC4xNSwyOTYuMTcgNzI4LjM1LDY5NC44OCIgLz4NCiAgPHBvbHlnb24gZmlsbD0iIzhBQzkzNCIgcG9pbnRzPSI0ODQuMTUsMzA2LjE3IDI1NS4wNiw3MDIuOTYgMzg3LjY2LDkzMi42NCA4NDUuODMsOTMyLjYzIiAvPg0KICA8cG9seWdvbiBmaWxsPSIjNThEMEZGIiBwb2ludHM9Ii03LjE3LDE0NDAuMDkgMTA5Ny45NywxNDQwLjA4IDEzMjguNDcsMTA0MC44MyAyMjMuMzIsMTA0MC44NSIgLz4NCiAgPHBvbHlnb24gZmlsbD0iIzEzQURGMyIgcG9pbnRzPSIxNzM5LjA0LDExNjAuOTEgMTUwOS4wOSw3NjIuNjQgMTExNy45NywxNDQwLjA4IDExODYuOTMsMTQ0MC4wOCAxNTc3Ljg3LDE0NDAuMDgiIC8+DQogIDxwb2x5Z29uIGZpbGw9IiMwMzkzRDAiIHBvaW50cz0iMTk3OC44LDc1Mi45NiAxODQ2LjIsNTIzLjMgMTM4Ni42OCw1MjMuMyAxNzQ5LjA0LDExNTAuOTEiIC8+DQo8L3N2Zz4=)](https://github.com/pjazdzyk/Unitility)\n\n```markdown\n[![Unitility](https://img.shields.io/badge/UNITILITY-v4.0.0-13ADF3?style=for-the-badge\u0026logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMi41bW0iIGhlaWdodD0iMTQuNW1tIiB2aWV3Qm94PSIwIDAgMjI1MCAxNDUwIj4NCiAgPHBvbHlnb24gZmlsbD0iIzUwN0QxNCIgcG9pbnRzPSIyMjQxLjAzLDE1Ljg4IDExMzYuMzgsMTUuODQgOTA1Ljg4LDQxNS4xIDIwMTAuNTMsNDE1LjA5IiAvPg0KICA8cG9seWdvbiBmaWxsPSIjNzFBQjIzIiBwb2ludHM9IjExMTYuMzgsMTUuODQgNjU1Ljk5LDE1Ljg0IDQ5NC4xNSwyOTYuMTcgNzI4LjM1LDY5NC44OCIgLz4NCiAgPHBvbHlnb24gZmlsbD0iIzhBQzkzNCIgcG9pbnRzPSI0ODQuMTUsMzA2LjE3IDI1NS4wNiw3MDIuOTYgMzg3LjY2LDkzMi42NCA4NDUuODMsOTMyLjYzIiAvPg0KICA8cG9seWdvbiBmaWxsPSIjNThEMEZGIiBwb2ludHM9Ii03LjE3LDE0NDAuMDkgMTA5Ny45NywxNDQwLjA4IDEzMjguNDcsMTA0MC44MyAyMjMuMzIsMTA0MC44NSIgLz4NCiAgPHBvbHlnb24gZmlsbD0iIzEzQURGMyIgcG9pbnRzPSIxNzM5LjA0LDExNjAuOTEgMTUwOS4wOSw3NjIuNjQgMTExNy45NywxNDQwLjA4IDExODYuOTMsMTQ0MC4wOCAxNTc3Ljg3LDE0NDAuMDgiIC8+DQogIDxwb2x5Z29uIGZpbGw9IiMwMzkzRDAiIHBvaW50cz0iMTk3OC44LDc1Mi45NiAxODQ2LjIsNTIzLjMgMTM4Ni42OCw1MjMuMyAxNzQ5LjA0LDExNTAuOTEiIC8+DQo8L3N2Zz4=)](https://github.com/pjazdzyk/Unitility)\n```\n\n## 11. ACKNOWLEDGMENTS\nSpecial thank you to [msummersgill](https://github.com/msummersgill) for your valuable contributions, ideas, and improvements!  \nYour support is greatly appreciated.\n\nI extend my heartfelt gratitude to the [Silesian University of Technology](https://www.polsl.pl/en/) as it is here\nthat my professional identity was forged, transforming me into an engineer to my very core.\nAmicus Plato, sed magis amica veritas.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpjazdzyk%2Funitility","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpjazdzyk%2Funitility","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpjazdzyk%2Funitility/lists"}