Documentación / Capas de extensión / Capa GeoJSON

Capa GeoJSON

Paquete de extensión para superponer GeoJSON directamente sobre un mapa. Dado que las características se rasterizan en mosaicos y se dibujan, no es necesario crear un objeto por característica, incluso con decenas de miles de elementos. Los nombres de API, los valores predeterminados de estilo y el comportamiento de las pruebas de impacto están unificados en Android, iOS y React.

ANDROID
com.mapconductor:geojson
IOS
MapConductorGeoJSON
REACT
@mapconductor/react-geojson-layer
Método de dibujo
Mosaicos ráster
Plataforma

01 · Resumen

GeoJSON se analiza y convierte en un modelo de características ligero, luego se dibuja a través de la canalización de mosaicos ráster de MapConductor. Independientemente del proveedor (Google Maps, MapLibre, MapKit, HERE, etc.), el código y la apariencia son idénticos.

Perspectiva

01

Admite FeatureCollection, entidades individuales, geometrías simples y secuencias de texto de RFC 8142. También hay un analizador de flujo.

Dibujar mosaicos

02

Rasteriza entidades en mosaicos de 512px y las coloca en el mapa como una capa ráster. No depende de la función vectorial del proveedor.

Prueba de impacto

03

Detecta clics con las mismas coordenadas que se usaron para dibujar. Admite polígonos con agujeros, geometría multiparte y colecciones de geometría.

Point
MultiPoint
LineString
MultiLineString
Polygon
MultiPolygon
GeometryCollection

Todos los tipos de geometría se admiten de manera idéntica en las tres plataformas.

02 · Uso básico

Simplemente coloque la capa en el ámbito content de la vista del mapa. El análisis se realiza en segundo plano y el resultado se pasa a features.

build.gradle.kts
dependencies {
    implementation("com.mapconductor:geojson:<version>")
}
com.mapconductor.geojson · Compose
val mapViewState = rememberMapLibreMapViewState(
    cameraPosition = MapCameraPosition(
        position = GeoPoint.fromLongLat(139.7671, 35.6812),
        zoom = 12.0,
    ),
)

val layerState = remember { GeoJSONLayerState() }
var features by remember { mutableStateOf(emptyList<GeoJSONFeature>()) }

LaunchedEffect(Unit) {
    features = withContext(Dispatchers.IO) {
        assets.open("wards.geojson").use(GeoJSONParser::parseStream)
    }
}

MapLibreMapView(state = mapViewState) {
    GeoJSONLayer(state = layerState, features = features)
}

Cuando cambia el contenido o el estilo de una característica, la capa invalida la URL del mosaico interna. La caché ráster del SDK del mapa no seguirá devolviendo imágenes antiguas.

03 · Determinación del estilo

El estilo se resuelve en tres capas

Tres capas: 'valores predeterminados para toda la capa', 'anulaciones por característica' y 'determinación dinámica mediante StyleProvider'. Las capas inferiores tienen más prioridad y los elementos no especificados se heredan de las capas superiores. El procedimiento básico es comenzar solo con los valores predeterminados de la capa y agregar capas inferiores solo según sea necesario.

Figura · orden de resolución
LAYER 1
Valores por defecto de la capa
GeoJSONLayerState
La base que se aplica a cada feature: strokeColor, fillColor, strokeWidth, pointRadius.
LAYER 2
Anulación por feature
GeoJSONFeature.strokeColor …
Campos con el mismo nombre en cada feature. Si quedan en null, se usa el valor por defecto de la capa tal cual.
LAYER 3
Resolución dinámica
GeoJSONStyleProvider
Se llama una vez por feature para devolver el estilo final: aquí vive el coloreado según properties.
Si no se define un provider, se aplica DefaultGeoJSONStyleProvider: el valor del feature si existe y, si no, el valor por defecto de la capa. La tercera capa es en realidad una forma de reemplazar esa misma relación.

3-1. Propiedades de estilo

Solo se manejan cuatro propiedades. Además, en el lado de la capa hay opacity, visible y minZoom / maxZoom para controlar la visualización.

Propiedad
Tipo
Valor predeterminado
Descripción
strokeColor
ARGB Int / UIColor / number
#FF1E88E5
Color de las líneas y los contornos de los polígonos.
fillColor
ARGB Int / UIColor / number
#801E88E5
Color de relleno de los polígonos y color de los círculos de los puntos. De manera predeterminada, es semitransparente.
strokeWidth
Float / CGFloat / number
2.0
Ancho de línea (píxeles). Como se rasteriza en mosaicos, el ancho es constante al hacer zoom.
pointRadius
Float / CGFloat / number
8.0
Radio del círculo con el que se dibuja un punto (píxeles).
opacity
Float / Double / number
1.0
Opacidad de toda la capa ráster. Actúa además del alfa de cada color individual.
visible
Boolean
true
Se excluye tanto del dibujo como de la prueba de impacto. En el lado de la característica también existe un campo con el mismo nombre.
minZoom / maxZoom
Int / number
0 / 22
Rango de zoom en el que se muestra la capa ráster generada. Fuera del rango, tampoco se ejecuta la generación de mosaicos.

3-2. Establecer los valores predeterminados de capa

Primero comience aquí. El valor pasado a `GeoJSONLayerState` será la base para todas las entidades. Como el estado es observable, se volverá a dibujar si se asigna más tarde.

Kotlin · ARGB Int
val layerState = remember {
    GeoJSONLayerState(
        strokeColor = Color.argb(220, 30, 136, 229),
        fillColor   = Color.argb(60, 30, 136, 229),
        strokeWidth = 1.5f,
        pointRadius = 8f,
        opacity     = 1f,
        minZoom = 8, maxZoom = 22,
    )
}

// El estado es observable: asigna después y los mosaicos se regeneran
layerState.fillColor = Color.argb(90, 214, 64, 69)

Formatos de especificación de color

Android y React usan enteros de 32 bits ARGB (alfa es el byte más significativo), iOS usa `UIColor` con alfa en el color mismo. React tiene las funciones auxiliares `colorArgb(a,r,g,b)` / `colorRgb(r,g,b)` / `argbToCss()` en el mismo orden que `Color.argb()` de Android. Los colores predeterminados en las 3 plataformas están unificados en #1E88E5 (líneas opacas, relleno con alfa 128).

Anular para cada entidad

Una entidad puede tener por sí misma `strokeColor` / `fillColor` / `strokeWidth` / `pointRadius` / `visible`. Si permanece como `null`, se usa el valor predeterminado; si hay un valor, este prevalece. Si el estilo se determina en el momento de cargar los datos (y no cambia después), este método es el más directo y rápido.

Kotlin · GeoJSONFeature.copy
val parsed = GeoJSONParser.parseStream(input)

// Tras el análisis, lee las propiedades y fija el estilo
val styled = parsed.map { f ->
    when (f.properties["status"]) {
        "alert"  -> f.copy(fillColor = Color.argb(120, 214, 64, 69), strokeWidth = 3f)
        "closed" -> f.copy(visible = false)
        else     -> f   // Se deja en null, así que se usa el valor por defecto de la capa
    }
}

GeoJSONLayer(state = layerState, features = styled)

Determinar dinámicamente con StyleProvider

Los «estilos determinados por reglas», como colorear según los valores de `properties`, resaltar solo la entidad seleccionada o cambiar el umbral desde la IU, se escriben en `StyleProvider`. Se llama para cada entidad, recibe los valores predeterminados de la capa y devuelve el estilo final.

Lo que se pasa

La entidad en sí (incluidas las propiedades) y los valores predeterminados de la capa en ese momento. Lo habitual es copiar los valores predeterminados y cambiar solo una parte.

Lo que devuelve

Un LayerStyle con los 4 elementos completados. Para los elementos que no se tocan, se pueden devolver los valores predeterminados tal cual, para que se aplique la configuración de la primera capa.

Kotlin · GeoJSONStyleProviderInterface
// Es una fun interface, así que basta con un lambda
val densityStyle = GeoJSONStyleProviderInterface { feature, defaultStyle ->
    val pop = (feature.properties["population"] as? Number)?.toInt() ?: 0
    val fill = when {
        pop > 500_000 -> Color.argb(150, 173, 20, 87)
        pop > 200_000 -> Color.argb(120, 244, 143, 177)
        else          -> Color.argb(80, 248, 187, 208)
    }
    defaultStyle.copy(fillColor = fill)   // Lo que no toques conserva su valor por defecto
}

val layerState = remember {
    GeoJSONLayerState(styleProvider = densityStyle)
}

// Cambiarlo después vuelve a evaluar todos los features
layerState.styleProvider = DefaultGeoJSONStyleProvider

Cuando se reemplaza `StyleProvider` o cambia el estado al que hace referencia, se reevalúan los estilos de todas las entidades y se recrean los mosaicos. Dado que se llama para cada entidad, por favor precalcule fuera del proveedor cualquier proceso pesado (expresiones regulares, red, análisis de fechas, etc.).

3-5. Cuál usar

Método
Casos adecuados
Notas
LayerState
Dibujar todos los datos con una sola apariencia.
El más ligero. Primero comience aquí.
Feature override
El estilo se determina al cargar y no cambia después.
Solo mapear el resultado del análisis. No hay costo de llamadas al proveedor.
StyleProvider
El estilo se decide por reglas. Las reglas mismas cambian durante la ejecución.
Consolidar la lógica en un solo lugar. En RN se requiere registro nativo.

04 · Detección de toques

Como `MapConductor` tiene un solo escucha de clics, la aplicación se encarga de reenviar a la capa. No lo registramos automáticamente de forma intencional. `processClick` devuelve `true` solo cuando golpea una entidad.

Kotlin
val layerState = remember {
    GeoJSONLayerState(
        onClick = { feature, position -> selected = feature },
    )
}

MapLibreMapView(
    state = mapViewState,
    onMapClick = { point ->
        // Se evalúa con una tolerancia de 15 px, que sigue al zoom
        val consumed = layerState.processClick(point, 15.0, mapViewState.zoom)
        if (!consumed) selected = null
    },
) {
    GeoJSONLayer(state = layerState, features = features)
}

Tolerancia de píxeles

Si pasa una tolerancia en píxeles y el zoom actual a processClick, la detección seguirá el zoom. Si se omite, se usa la tolerancia predeterminada de coordenadas mundiales (aprox. 0,0002°).

Al superponerse

Se devuelve la entidad dibujada en último lugar (es decir, la de arriba).

Geometrías admitidas

Puntos, líneas, polígonos con agujeros, colecciones de geometría multiparte.

05 · Volumen de datos y carga

Para grandes datos, use un analizador de transmisión y analice en segundo plano. Hay dos formas de mantener las entidades.

Estático, para grandes volúmenes

GeoJSONFeature

Objetos de datos inmutables. Son ligeros porque no crean objetos de estado incluso con decenas de miles de elementos. Para GeoJSON grande, use esto.

Para pocos, que cambian con frecuencia

GeoJSONFeatureState

Se puede actualizar de forma reactiva uno por uno. Con muchos elementos, el costo de la gestión del estado se nota, así que úselo solo para lo necesario.

Kotlin · streaming
// parseStream para una FeatureCollection grande
val features = withContext(Dispatchers.IO) {
    GeoJSONParser.parseStream(input)
}

// GeoJSON Text Sequences, RFC 8142
val seq = withContext(Dispatchers.IO) { GeoJSONSeqParser.parse(file) }
GeoJSONSeqParser.streamParse(file) { feature -> buffer.add(feature) }
Implementación en video · Android + MapLibreKotlin · Jetpack Compose
val layerState = remember { GeoJSONLayerState() }
var features by remember { mutableStateOf(emptyList<GeoJSONFeature>()) }
var loading by remember { mutableStateOf(true) }

// parseStream returns immutable GeoJSONFeature values — no state object per feature
LaunchedEffect(Unit) {
    features = withContext(Dispatchers.IO) {
        context.assets.open("tokyo-buildings.geojson")
            .use(GeoJSONParser::parseStream)
    }
    loading = false
}

MapLibreMapView(state = mapViewState) {
    GeoJSONLayer(state = layerState, features = features)
}

if (loading) {
    LinearProgressIndicator(modifier = Modifier.fillMaxWidth())
}
Video de ejemplo · Cargar GeoJSON grande en teselas
Video aún no grabadoLas teselas se cargan secuencialmente al desplazarse y el dibujo se va completando. Que la operación no se detenga a medida que aumenta la cantidad de datos no se puede transmitir con una imagen estática.

06 · Limitaciones actuales

No se registran automáticamente escuchas de clics en las capas. Reenvíe a `processClick` (diseño intencional).
La tolerancia predeterminada de líneas y puntos para pruebas de impacto es una constante interna. Se puede ajustar pasando una tolerancia en píxeles en cada llamada.
Como se dibuja como teselas de ráster, no se utilizan las consultas nativas de entidades vectoriales del SDK del mapa.
El analizador no lanza excepciones ante entradas no válidas; devuelve una matriz de entidades vacía (iOS).

Páginas relacionadas