mirror of
https://github.com/geoffsee/osm-maker-vibes.git
synced 2025-09-08 22:46:45 +00:00
added support for jsonc config
This commit is contained in:
40
config.jsonc
Normal file
40
config.jsonc
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
{
|
||||||
|
// OSM Maker Configuration File
|
||||||
|
// This file demonstrates JSONC (JSON with Comments) support
|
||||||
|
|
||||||
|
"osmData": {
|
||||||
|
"useLocalExtract": false, // Set to true to use local OSM file
|
||||||
|
"localFilePath": "virginia.osm.pbf",
|
||||||
|
|
||||||
|
/* Bounding box configuration
|
||||||
|
* Defines the geographic area to process
|
||||||
|
*/
|
||||||
|
"boundingBox": {
|
||||||
|
"south": 37.115, // Southern latitude boundary
|
||||||
|
"west": -76.396, // Western longitude boundary
|
||||||
|
"north": 37.139, // Northern latitude boundary
|
||||||
|
"east": -76.345, // Eastern longitude boundary
|
||||||
|
"description": "Poquoson, VA" // Human-readable description
|
||||||
|
},
|
||||||
|
|
||||||
|
"overpassTimeout": 25 // Timeout for Overpass API queries in seconds
|
||||||
|
},
|
||||||
|
|
||||||
|
// Projection settings for coordinate transformation
|
||||||
|
"projection": {
|
||||||
|
"origin": {
|
||||||
|
"latitude": 37.120907, // Center point latitude
|
||||||
|
"longitude": -76.333694 // Center point longitude
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/* Output configuration
|
||||||
|
* Controls how the final 3D model is generated and handled
|
||||||
|
*/
|
||||||
|
"output": {
|
||||||
|
"fileName": "municipality.glb", // Output file name
|
||||||
|
"autoOpen": true // Whether to automatically open the generated file
|
||||||
|
}
|
||||||
|
|
||||||
|
// End of configuration
|
||||||
|
}
|
@@ -1,6 +1,7 @@
|
|||||||
package org.example
|
package org.example
|
||||||
|
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class Config(
|
data class Config(
|
||||||
@@ -41,4 +42,87 @@ data class OriginConfig(
|
|||||||
data class OutputConfig(
|
data class OutputConfig(
|
||||||
val fileName: String,
|
val fileName: String,
|
||||||
val autoOpen: Boolean
|
val autoOpen: Boolean
|
||||||
)
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses JSONC (JSON with Comments) content by removing comments and parsing as regular JSON
|
||||||
|
*/
|
||||||
|
fun parseJsonc(jsoncContent: String): Config {
|
||||||
|
val cleanedJson = removeJsoncComments(jsoncContent)
|
||||||
|
return Json.decodeFromString<Config>(cleanedJson)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes both single-line (//) and multi-line (/* */) comments from JSONC content
|
||||||
|
*/
|
||||||
|
private fun removeJsoncComments(content: String): String {
|
||||||
|
val result = StringBuilder()
|
||||||
|
var i = 0
|
||||||
|
var inString = false
|
||||||
|
var escaped = false
|
||||||
|
|
||||||
|
while (i < content.length) {
|
||||||
|
val char = content[i]
|
||||||
|
|
||||||
|
when {
|
||||||
|
// Handle escape sequences in strings
|
||||||
|
escaped -> {
|
||||||
|
result.append(char)
|
||||||
|
escaped = false
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
// Handle string boundaries
|
||||||
|
char == '"' && !escaped -> {
|
||||||
|
inString = !inString
|
||||||
|
result.append(char)
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
// Handle escape character
|
||||||
|
char == '\\' && inString -> {
|
||||||
|
escaped = true
|
||||||
|
result.append(char)
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
// Skip comments when not in string
|
||||||
|
!inString && char == '/' && i + 1 < content.length -> {
|
||||||
|
when (content[i + 1]) {
|
||||||
|
// Single-line comment
|
||||||
|
'/' -> {
|
||||||
|
// Skip until end of line
|
||||||
|
i += 2
|
||||||
|
while (i < content.length && content[i] != '\n') {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
// Keep the newline for proper formatting
|
||||||
|
if (i < content.length) {
|
||||||
|
result.append('\n')
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Multi-line comment
|
||||||
|
'*' -> {
|
||||||
|
// Skip until */
|
||||||
|
i += 2
|
||||||
|
while (i + 1 < content.length && !(content[i] == '*' && content[i + 1] == '/')) {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
if (i + 1 < content.length) {
|
||||||
|
i += 2 // Skip the closing */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
result.append(char)
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Regular character
|
||||||
|
else -> {
|
||||||
|
result.append(char)
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.toString()
|
||||||
|
}
|
||||||
|
@@ -4,9 +4,10 @@ import kotlinx.serialization.json.Json
|
|||||||
import kotlin.test.Test
|
import kotlin.test.Test
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
import kotlin.test.assertNotNull
|
import kotlin.test.assertNotNull
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
class ConfigTest {
|
class ConfigTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testConfigDeserialization() {
|
fun testConfigDeserialization() {
|
||||||
val jsonString = """
|
val jsonString = """
|
||||||
@@ -35,9 +36,9 @@ class ConfigTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
|
|
||||||
val config = Json.decodeFromString<Config>(jsonString)
|
val config = Json.decodeFromString<Config>(jsonString)
|
||||||
|
|
||||||
assertNotNull(config)
|
assertNotNull(config)
|
||||||
assertEquals(false, config.osmData.useLocalExtract)
|
assertEquals(false, config.osmData.useLocalExtract)
|
||||||
assertEquals("virginia.osm.pbf", config.osmData.localFilePath)
|
assertEquals("virginia.osm.pbf", config.osmData.localFilePath)
|
||||||
@@ -52,4 +53,155 @@ class ConfigTest {
|
|||||||
assertEquals("municipality.glb", config.output.fileName)
|
assertEquals("municipality.glb", config.output.fileName)
|
||||||
assertEquals(true, config.output.autoOpen)
|
assertEquals(true, config.output.autoOpen)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
@Test
|
||||||
|
fun testJsoncWithSingleLineComments() {
|
||||||
|
val jsoncString = """
|
||||||
|
{
|
||||||
|
// This is a comment about OSM data configuration
|
||||||
|
"osmData": {
|
||||||
|
"useLocalExtract": false, // Use remote data instead
|
||||||
|
"localFilePath": "virginia.osm.pbf",
|
||||||
|
"boundingBox": {
|
||||||
|
"south": 37.115,
|
||||||
|
"west": -76.396,
|
||||||
|
"north": 37.139,
|
||||||
|
"east": -76.345,
|
||||||
|
"description": "Poquoson, VA" // A small city in Virginia
|
||||||
|
},
|
||||||
|
"overpassTimeout": 25 // Timeout in seconds
|
||||||
|
},
|
||||||
|
"projection": {
|
||||||
|
"origin": {
|
||||||
|
"latitude": 37.120907, // Center latitude
|
||||||
|
"longitude": -76.333694 // Center longitude
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"output": {
|
||||||
|
"fileName": "municipality.glb", // Output file name
|
||||||
|
"autoOpen": true // Automatically open the file
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""".trimIndent()
|
||||||
|
|
||||||
|
val config = parseJsonc(jsoncString)
|
||||||
|
|
||||||
|
assertNotNull(config)
|
||||||
|
assertEquals(false, config.osmData.useLocalExtract)
|
||||||
|
assertEquals("virginia.osm.pbf", config.osmData.localFilePath)
|
||||||
|
assertEquals("Poquoson, VA", config.osmData.boundingBox.description)
|
||||||
|
assertEquals(25, config.osmData.overpassTimeout)
|
||||||
|
assertEquals("municipality.glb", config.output.fileName)
|
||||||
|
assertEquals(true, config.output.autoOpen)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testJsoncWithMultiLineComments() {
|
||||||
|
val jsoncString = """
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* OSM Data Configuration
|
||||||
|
* This section configures how OSM data is obtained
|
||||||
|
*/
|
||||||
|
"osmData": {
|
||||||
|
"useLocalExtract": false,
|
||||||
|
"localFilePath": "virginia.osm.pbf",
|
||||||
|
"boundingBox": {
|
||||||
|
"south": 37.115,
|
||||||
|
"west": -76.396,
|
||||||
|
"north": 37.139,
|
||||||
|
"east": -76.345,
|
||||||
|
"description": "Poquoson, VA"
|
||||||
|
},
|
||||||
|
"overpassTimeout": 25
|
||||||
|
},
|
||||||
|
/* Projection settings */
|
||||||
|
"projection": {
|
||||||
|
"origin": {
|
||||||
|
"latitude": 37.120907,
|
||||||
|
"longitude": -76.333694
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/*
|
||||||
|
* Output configuration
|
||||||
|
* Controls how the final file is generated
|
||||||
|
*/
|
||||||
|
"output": {
|
||||||
|
"fileName": "municipality.glb",
|
||||||
|
"autoOpen": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""".trimIndent()
|
||||||
|
|
||||||
|
val config = parseJsonc(jsoncString)
|
||||||
|
|
||||||
|
assertNotNull(config)
|
||||||
|
assertEquals(false, config.osmData.useLocalExtract)
|
||||||
|
assertEquals("Poquoson, VA", config.osmData.boundingBox.description)
|
||||||
|
assertEquals("municipality.glb", config.output.fileName)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testJsoncWithMixedComments() {
|
||||||
|
val jsoncString = """
|
||||||
|
{
|
||||||
|
// Single line comment at the top
|
||||||
|
"osmData": {
|
||||||
|
/* Multi-line comment
|
||||||
|
about local extract */
|
||||||
|
"useLocalExtract": false, // Inline comment
|
||||||
|
"localFilePath": "virginia.osm.pbf",
|
||||||
|
"boundingBox": {
|
||||||
|
"south": 37.115, // Southern boundary
|
||||||
|
"west": -76.396,
|
||||||
|
"north": 37.139,
|
||||||
|
"east": -76.345,
|
||||||
|
"description": "Poquoson, VA"
|
||||||
|
},
|
||||||
|
"overpassTimeout": 25
|
||||||
|
},
|
||||||
|
"projection": {
|
||||||
|
"origin": {
|
||||||
|
"latitude": 37.120907,
|
||||||
|
"longitude": -76.333694
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"output": {
|
||||||
|
"fileName": "municipality.glb",
|
||||||
|
"autoOpen": true
|
||||||
|
}
|
||||||
|
// Final comment
|
||||||
|
}
|
||||||
|
""".trimIndent()
|
||||||
|
|
||||||
|
val config = parseJsonc(jsoncString)
|
||||||
|
|
||||||
|
assertNotNull(config)
|
||||||
|
assertEquals(false, config.osmData.useLocalExtract)
|
||||||
|
assertEquals("Poquoson, VA", config.osmData.boundingBox.description)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testLoadActualJsoncFile() {
|
||||||
|
val configFile = File("config.jsonc")
|
||||||
|
if (!configFile.exists()) {
|
||||||
|
println("Skipping test - config.jsonc file not found")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val configText = configFile.readText()
|
||||||
|
val config = parseJsonc(configText)
|
||||||
|
|
||||||
|
assertNotNull(config)
|
||||||
|
assertEquals(false, config.osmData.useLocalExtract)
|
||||||
|
assertEquals("virginia.osm.pbf", config.osmData.localFilePath)
|
||||||
|
assertEquals("Poquoson, VA", config.osmData.boundingBox.description)
|
||||||
|
assertEquals(25, config.osmData.overpassTimeout)
|
||||||
|
assertEquals(37.120907, config.projection.origin.latitude)
|
||||||
|
assertEquals(-76.333694, config.projection.origin.longitude)
|
||||||
|
assertEquals("municipality.glb", config.output.fileName)
|
||||||
|
assertEquals(true, config.output.autoOpen)
|
||||||
|
|
||||||
|
println("✅ Successfully loaded and parsed config.jsonc file with comments!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -10,22 +10,39 @@ fun main() {
|
|||||||
LOAD AND DEMONSTRATE JSON CONFIGURATION
|
LOAD AND DEMONSTRATE JSON CONFIGURATION
|
||||||
---------------------------------------------------------------- */
|
---------------------------------------------------------------- */
|
||||||
|
|
||||||
val configFile = File("config.json")
|
// Try to find configuration file (prefer .jsonc, fallback to .json)
|
||||||
if (!configFile.exists()) {
|
val configFile = when {
|
||||||
println("Error: config.json not found in current directory")
|
File("config.jsonc").exists() -> File("config.jsonc")
|
||||||
println("Please ensure config.json exists in the working directory")
|
File("config.json").exists() -> File("config.json")
|
||||||
return
|
else -> {
|
||||||
|
println("Error: No configuration file found")
|
||||||
|
println("Please ensure either config.jsonc or config.json exists in the working directory")
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val config = try {
|
val config = try {
|
||||||
val configText = configFile.readText()
|
val configText = configFile.readText()
|
||||||
Json.decodeFromString<Config>(configText)
|
when (configFile.extension.lowercase()) {
|
||||||
|
"jsonc" -> {
|
||||||
|
println("📄 Loading JSONC configuration from ${configFile.name}")
|
||||||
|
parseJsonc(configText)
|
||||||
|
}
|
||||||
|
"json" -> {
|
||||||
|
println("📄 Loading JSON configuration from ${configFile.name}")
|
||||||
|
Json.decodeFromString<Config>(configText)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
println("Error: Unsupported configuration file format: ${configFile.extension}")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
println("Error reading configuration: ${e.message}")
|
println("Error reading configuration: ${e.message}")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
println("\n✅ Configuration loaded successfully from config.json!")
|
println("\n✅ Configuration loaded successfully from ${configFile.name}!")
|
||||||
println("📍 Area: ${config.osmData.boundingBox.description}")
|
println("📍 Area: ${config.osmData.boundingBox.description}")
|
||||||
println("📂 Use local extract: ${config.osmData.useLocalExtract}")
|
println("📂 Use local extract: ${config.osmData.useLocalExtract}")
|
||||||
println("📄 Local file path: ${config.osmData.localFilePath}")
|
println("📄 Local file path: ${config.osmData.localFilePath}")
|
||||||
@@ -67,6 +84,6 @@ fun main() {
|
|||||||
println("5. Auto-open is disabled, file would remain closed")
|
println("5. Auto-open is disabled, file would remain closed")
|
||||||
}
|
}
|
||||||
|
|
||||||
println("\n✨ JSON configuration reading implementation complete!")
|
println("\n✨ JSON/JSONC configuration reading implementation complete!")
|
||||||
println("🎉 The app now successfully reads configuration from JSON instead of using hardcoded values.")
|
println("🎉 The app now successfully reads configuration from JSON and JSONC files with comment support!")
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user