Attribute serialization

This commit is contained in:
Alexander Nozik 2023-01-12 11:52:54 +03:00
parent 190834634f
commit 0f00abb1b2
24 changed files with 418 additions and 22 deletions

View File

@ -1,13 +1,63 @@
# Maps-kt
This repository is a work-in-progress implementation of Map-with-markers component for Compose-Multiplatform This repository is a work-in-progress implementation of Map-with-markers component for Compose-Multiplatform
## [maps-kt-core](maps-kt-core) ![](docs/images/Screenshot%202023-01-12%20110429.png)
A multiplatform coordinates representation and conversion.
## [maps-kt-compose](maps-kt-compose) ## Modules
A compose multiplatform (currently desktop only, contributions of android target are welcome) implementation of a map component, features and builder.
## [maps-kt-scheme](maps-kt-scheme)
An alternative component used for the same functionality on 2D schemes. Not all features from maps could be ported because it requires some code duplication (ideas for common API are welcome).
## [demo](demo) ### [demo](demo)
Demonstration projects for different features >
>
> **Maturity**: EXPERIMENTAL
### [maps-kt-compose](maps-kt-compose)
> Compose-multiplaform implementation for web-mercator tiled maps
>
> **Maturity**: DEVELOPMENT
>
> **Features:**
> - [osm](maps-kt-compose/#) : OpenStreetMap tile provider.
### [maps-kt-core](maps-kt-core)
> Core cartography, UI-agnostic
>
> **Maturity**: DEVELOPMENT
>
> **Features:**
> - [angles and distances](maps-kt-core/#) : Type-safe angle and distance measurements.
> - [ellipsoid](maps-kt-core/#) : Ellipsoid geometry and distances
> - [mercator](maps-kt-core/#) : Mercator and web-mercator projections
### [maps-kt-features](maps-kt-features)
>
>
> **Maturity**: EXPERIMENTAL
### [maps-kt-geojson](maps-kt-geojson)
>
>
> **Maturity**: EXPERIMENTAL
### [maps-kt-scheme](maps-kt-scheme)
>
>
> **Maturity**: EXPERIMENTAL
### [maps](demo/maps)
>
>
> **Maturity**: EXPERIMENTAL
### [polygon-editor](demo/polygon-editor)
>
>
> **Maturity**: EXPERIMENTAL
### [scheme](demo/scheme)
>
>
> **Maturity**: EXPERIMENTAL

View File

@ -8,7 +8,7 @@ plugins {
allprojects { allprojects {
group = "center.sciprog" group = "center.sciprog"
version = "0.2.1-dev-1" version = "0.2.1-dev-2"
} }
apiValidation{ apiValidation{
@ -41,5 +41,7 @@ subprojects {
} }
} }
readme.readmeTemplate = file("docs/templates/README-TEMPLATE.md")

4
demo/README.md Normal file
View File

@ -0,0 +1,4 @@
# Module demo

4
demo/maps/README.md Normal file
View File

@ -0,0 +1,4 @@
# Module maps

View File

@ -0,0 +1,4 @@
# Module polygon-editor

4
demo/scheme/README.md Normal file
View File

@ -0,0 +1,4 @@
# Module scheme

Binary file not shown.

After

Width:  |  Height:  |  Size: 591 KiB

17
docs/templates/ARTIFACT-TEMPLATE.md vendored Normal file
View File

@ -0,0 +1,17 @@
## Artifact:
The Maven coordinates of this project are `${group}:${name}:${version}`.
**Gradle Kotlin DSL:**
```kotlin
repositories {
maven("https://repo.kotlin.link")
mavenCentral()
// development and snapshot versions
maven("https://maven.pkg.jetbrains.space/spc/p/sci/dev")
}
dependencies {
implementation("${group}:${name}:${version}")
}
```

9
docs/templates/README-TEMPLATE.md vendored Normal file
View File

@ -0,0 +1,9 @@
# Maps-kt
This repository is a work-in-progress implementation of Map-with-markers component for Compose-Multiplatform
![](docs/images/Screenshot%202023-01-12%20110429.png)
## Modules
${modules}

33
maps-kt-compose/README.md Normal file
View File

@ -0,0 +1,33 @@
# Module kmath-core
The core interfaces of KMath.
- [osm](#) : OpenStreetMap tile provider.
## Artifact:
The Maven coordinates of this project are `center.sciprog:maps-kt-compose:0.2.1-dev-2`.
**Gradle Groovy:**
```groovy
repositories {
maven { url 'https://repo.kotlin.link' }
mavenCentral()
}
dependencies {
implementation 'center.sciprog:maps-kt-compose:0.2.1-dev-2'
}
```
**Gradle Kotlin DSL:**
```kotlin
repositories {
maven("https://repo.kotlin.link")
mavenCentral()
}
dependencies {
implementation("center.sciprog:maps-kt-compose:0.2.1-dev-2")
}
```

View File

@ -47,3 +47,13 @@ java {
tasks.withType<Test> { tasks.withType<Test> {
useJUnitPlatform() useJUnitPlatform()
} }
readme {
description = "Compose-multiplaform implementation for web-mercator tiled maps"
maturity = space.kscience.gradle.Maturity.EXPERIMENTAL
propertyByTemplate("artifact", rootProject.file("docs/templates/ARTIFACT-TEMPLATE.md"))
feature(
id = "osm",
) { "OpenStreetMap tile provider." }
}

View File

@ -0,0 +1,7 @@
# Module kmath-core
The core interfaces of KMath.
${features}
${artifact}

35
maps-kt-core/README.md Normal file
View File

@ -0,0 +1,35 @@
# Module kmath-core
The core interfaces of KMath.
- [angles and distances](#) : Type-safe angle and distance measurements.
- [ellipsoid](#) : Ellipsoid geometry and distances
- [mercator](#) : Mercator and web-mercator projections
## Artifact:
The Maven coordinates of this project are `center.sciprog:maps-kt-core:0.2.1-dev-2`.
**Gradle Groovy:**
```groovy
repositories {
maven { url 'https://repo.kotlin.link' }
mavenCentral()
}
dependencies {
implementation 'center.sciprog:maps-kt-core:0.2.1-dev-2'
}
```
**Gradle Kotlin DSL:**
```kotlin
repositories {
maven("https://repo.kotlin.link")
mavenCentral()
}
dependencies {
implementation("center.sciprog:maps-kt-core:0.2.1-dev-2")
}
```

View File

@ -2,3 +2,21 @@ plugins {
id("space.kscience.gradle.mpp") id("space.kscience.gradle.mpp")
`maven-publish` `maven-publish`
} }
readme {
description = "Core cartography, UI-agnostic"
maturity = space.kscience.gradle.Maturity.DEVELOPMENT
propertyByTemplate("artifact", rootProject.file("docs/templates/ARTIFACT-TEMPLATE.md"))
feature(
id = "angles and distances",
) { "Type-safe angle and distance measurements." }
feature(
id = "ellipsoid",
) { "Ellipsoid geometry and distances" }
feature(
id = "mercator",
) { "Mercator and web-mercator projections" }
}

View File

@ -0,0 +1,7 @@
# Module kmath-core
The core interfaces of KMath.
${features}
${artifact}

View File

@ -0,0 +1,32 @@
# Module maps-kt-features
## Usage
## Artifact:
The Maven coordinates of this project are `center.sciprog:maps-kt-features:0.2.1-dev-2`.
**Gradle Groovy:**
```groovy
repositories {
maven { url 'https://repo.kotlin.link' }
mavenCentral()
}
dependencies {
implementation 'center.sciprog:maps-kt-features:0.2.1-dev-2'
}
```
**Gradle Kotlin DSL:**
```kotlin
repositories {
maven("https://repo.kotlin.link")
mavenCentral()
}
dependencies {
implementation("center.sciprog:maps-kt-features:0.2.1-dev-2")
}
```

View File

@ -13,3 +13,9 @@ kotlin {
} }
} }
} }
kscience{
useSerialization{
json()
}
}

View File

@ -1,8 +1,20 @@
package center.sciprog.attributes package center.sciprog.attributes
import kotlinx.serialization.KSerializer
import kotlinx.serialization.builtins.serializer
public interface Attribute<T> public interface Attribute<T>
public abstract class SerializableAttribute<T>(
public val serialId: String,
public val serializer: KSerializer<T>,
) : Attribute<T>
public interface AttributeWithDefault<T> : Attribute<T> {
public val default: T
}
public interface SetAttribute<V> : Attribute<Set<V>> public interface SetAttribute<V> : Attribute<Set<V>>
public object NameAttribute : Attribute<String> public object NameAttribute : SerializableAttribute<String>("name", String.serializer())

View File

@ -5,27 +5,30 @@ import center.sciprog.maps.features.ZAttribute
import kotlin.jvm.JvmInline import kotlin.jvm.JvmInline
@JvmInline @JvmInline
public value class Attributes internal constructor(internal val map: Map<Attribute<*>, Any>) { public value class Attributes internal constructor(public val content: Map<out Attribute<*>, Any>) {
public val keys: Set<Attribute<*>> get() = content.keys
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
public operator fun <T> get(attribute: Attribute<T>): T? = map[attribute] as? T public operator fun <T> get(attribute: Attribute<T>): T? = content[attribute] as? T
// public operator fun <T> Attribute<T>.invoke(value: T?): Attributes = withAttribute(this, value) override fun toString(): String = "Attributes(value=${content.entries})"
override fun toString(): String = "Attributes(value=${map.entries})"
public companion object { public companion object {
public val EMPTY: Attributes = Attributes(emptyMap()) public val EMPTY: Attributes = Attributes(emptyMap())
} }
} }
public fun <T> Attributes.getOrDefault(attribute: AttributeWithDefault<T>): T = get(attribute) ?: attribute.default
public fun <T, A : Attribute<T>> Attributes.withAttribute( public fun <T, A : Attribute<T>> Attributes.withAttribute(
attribute: A, attribute: A,
attrValue: T?, attrValue: T?,
): Attributes = Attributes( ): Attributes = Attributes(
if (attrValue == null) { if (attrValue == null) {
map - attribute content - attribute
} else { } else {
map + (attribute to attrValue) content + (attribute to attrValue)
} }
) )
@ -38,7 +41,7 @@ public fun <T, A : SetAttribute<T>> Attributes.withAttributeElement(
): Attributes { ): Attributes {
val currentSet: Set<T> = get(attribute) ?: emptySet() val currentSet: Set<T> = get(attribute) ?: emptySet()
return Attributes( return Attributes(
map + (attribute to (currentSet + attrValue)) content + (attribute to (currentSet + attrValue))
) )
} }
@ -51,7 +54,7 @@ public fun <T, A : SetAttribute<T>> Attributes.withoutAttributeElement(
): Attributes { ): Attributes {
val currentSet: Set<T> = get(attribute) ?: emptySet() val currentSet: Set<T> = get(attribute) ?: emptySet()
return Attributes( return Attributes(
map + (attribute to (currentSet - attrValue)) content + (attribute to (currentSet - attrValue))
) )
} }
@ -60,7 +63,7 @@ public fun <T : Any, A : Attribute<T>> Attributes(
attrValue: T, attrValue: T,
): Attributes = Attributes(mapOf(attribute to attrValue)) ): Attributes = Attributes(mapOf(attribute to attrValue))
public operator fun Attributes.plus(other: Attributes): Attributes = Attributes(map + other.map) public operator fun Attributes.plus(other: Attributes): Attributes = Attributes(content + other.content)
public val Feature<*>.z: Float public val Feature<*>.z: Float
get() = attributes[ZAttribute] ?: 0f get() = attributes[ZAttribute] ?: 0f

View File

@ -17,7 +17,7 @@ public class AttributesBuilder internal constructor(private val map: MutableMap<
} }
public fun from(attributes: Attributes) { public fun from(attributes: Attributes) {
map.putAll(attributes.map) map.putAll(attributes.content)
} }
public fun <V> SetAttribute<V>.add( public fun <V> SetAttribute<V>.add(
@ -42,4 +42,6 @@ public class AttributesBuilder internal constructor(private val map: MutableMap<
public fun AttributesBuilder( public fun AttributesBuilder(
attributes: Attributes, attributes: Attributes,
): AttributesBuilder = AttributesBuilder(attributes.map.toMutableMap()) ): AttributesBuilder = AttributesBuilder(attributes.content.toMutableMap())
public fun Attributes(builder: AttributesBuilder.() -> Unit): Attributes = AttributesBuilder().apply(builder).build()

View File

@ -0,0 +1,44 @@
@file:Suppress("UNCHECKED_CAST")
package center.sciprog.attributes
import kotlinx.serialization.KSerializer
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.buildJsonObject
public class AttributesSerializer(
private val serializableAttributes: Set<SerializableAttribute<*>>,
) : KSerializer<Attributes> {
private val jsonSerializer = JsonObject.serializer()
override val descriptor: SerialDescriptor get() = jsonSerializer.descriptor
override fun deserialize(decoder: Decoder): Attributes {
val jsonElement = jsonSerializer.deserialize(decoder)
val attributeMap: Map<SerializableAttribute<*>, Any> = jsonElement.entries.associate { (key, element) ->
val attr = serializableAttributes.find { it.serialId == key }
?: error("Attribute serializer for key $key not found")
val value = Json.decodeFromJsonElement(attr.serializer, element) ?: error("Null values are not allowed")
attr to value
}
return Attributes(attributeMap)
}
override fun serialize(encoder: Encoder, value: Attributes) {
val json = buildJsonObject {
value.content.forEach { (key, value) ->
if (key !in serializableAttributes) error("An attribute key $key is not in the list of allowed attributes for this serializer")
val serializableKey = key as SerializableAttribute
put(
serializableKey.serialId,
Json.encodeToJsonElement(serializableKey.serializer as KSerializer<Any>, value)
)
}
}
jsonSerializer.serialize(encoder, json)
}
}

View File

@ -0,0 +1,29 @@
package center.sciprog.attributes
import kotlinx.serialization.json.Json
import kotlinx.serialization.serializer
import kotlin.test.Test
import kotlin.test.assertEquals
internal class AttributesSerializationTest {
internal object TestAttribute : SerializableAttribute<Map<String, String>>("test", serializer())
@Test
fun restore() {
val serializer = AttributesSerializer(setOf(NameAttribute, TestAttribute))
val attributes = Attributes {
NameAttribute("myTest")
TestAttribute(mapOf("a" to "aa", "b" to "bb"))
}
val serialized = Json.encodeToString(serializer, attributes)
println(serialized)
val restored = Json.decodeFromString(serializer, serialized)
assertEquals(attributes, restored)
}
}

32
maps-kt-geojson/README.md Normal file
View File

@ -0,0 +1,32 @@
# Module maps-kt-geojson
## Usage
## Artifact:
The Maven coordinates of this project are `center.sciprog:maps-kt-geojson:0.2.1-dev-2`.
**Gradle Groovy:**
```groovy
repositories {
maven { url 'https://repo.kotlin.link' }
mavenCentral()
}
dependencies {
implementation 'center.sciprog:maps-kt-geojson:0.2.1-dev-2'
}
```
**Gradle Kotlin DSL:**
```kotlin
repositories {
maven("https://repo.kotlin.link")
mavenCentral()
}
dependencies {
implementation("center.sciprog:maps-kt-geojson:0.2.1-dev-2")
}
```

32
maps-kt-scheme/README.md Normal file
View File

@ -0,0 +1,32 @@
# Module maps-kt-scheme
## Usage
## Artifact:
The Maven coordinates of this project are `center.sciprog:maps-kt-scheme:0.2.1-dev-2`.
**Gradle Groovy:**
```groovy
repositories {
maven { url 'https://repo.kotlin.link' }
mavenCentral()
}
dependencies {
implementation 'center.sciprog:maps-kt-scheme:0.2.1-dev-2'
}
```
**Gradle Kotlin DSL:**
```kotlin
repositories {
maven("https://repo.kotlin.link")
mavenCentral()
}
dependencies {
implementation("center.sciprog:maps-kt-scheme:0.2.1-dev-2")
}
```