Guía de producción

Esta sección recoge el conocimiento operativo que las cargas de trabajo de producción necesitan además de la referencia de funciones: cuándo npoint / tnpoint son el tipo adecuado, cómo fluyen los SRID desde el registro de la red, los peligros numéricos que afectan a las cargas de trabajo de seguimiento restringidas a una red, y la historia de durabilidad de los valores serializados. La mayoría de estos puntos aparecen por separado a lo largo del capítulo; el objetivo aquí es un único lugar que un nuevo contribuyente pueda leer de principio a fin antes de instrumentar una aplicación.

Cuándo usar tnpoint frente a tgeompoint

Un tgeompoint rastrea una posición en el espacio libre; un tnpoint rastrea una posición restringida a una red conocida de rutas. Cada valor de punto de red es un par (rid, pos) donde rid es el identificador de ruta y pos es la posición relativa a lo largo de esa ruta, normalizada a [0, 1] (del inicio de la ruta al final de la ruta).

Use tgeompoint cuando el objeto en movimiento no esté restringido a una red, cuando la topología de la red sea desconocida, o cuando solo importe la geometría de la trayectoria — trazas GPS de vehículos a nivel de datos crudos, movimiento de animales, flujos AIS.

Use tnpoint cuando el movimiento esté sobre una red conocida y se desee semántica relativa a la red: distancias medidas a lo largo de la ruta, no en el espacio libre; agregaciones agrupadas por segmento de ruta; uniones espaciales restringidas a la "misma red". Las cargas de trabajo canónicas son el seguimiento de redes ferroviarias y de carreteras, donde los flujos emparejados con el mapa se convierten en valores tnpoint.

La componente de posición de un tnpoint se puede recuperar como un tgeompoint mediante la conversión estándar (o como una geometry mediante geometry(npoint)); una geometry se puede proyectar de vuelta sobre la red cargada con npoint(geometry). En consecuencia, una sola columna tnpoint subsume una columna tgeompoint emparejada con el mapa cuando se necesita semántica relativa a la red.

Herencia del SRID desde el registro de la red

Un valor npoint no lleva su propio SRID; hereda el SRID de la ruta referenciada por su campo rid. El conjunto de rutas conocidas — el registro ways — se carga en una caché por proceso (véase meos/src/npoint/ways_meos.c) la primera vez que se ejecuta una función de red, y está indexado por el gid de ruta. Todo accesor de npoint que necesite la geometría de la ruta (npoint_to_geompoint, npoint_to_stbox, todos los predicados espaciales y funciones de distancia) consulta esta caché.

En la extensión de PostgreSQL la caché se rellena automáticamente desde la tabla public.ways. En MEOS independiente la aplicación debe llamar a meos_register_ways(...) con la tabla de rutas antes de cualquier operación npoint; meos_finalize_ways() es idempotente y se puede llamar varias veces de forma segura. Las operaciones entre tipos entre un npoint y una geometría validan que el SRID de la ruta coincida con el SRID de la geometría; las discrepancias generan un error explícito en lugar de reproyectar silenciosamente.

Peligros numéricos

Cuatro peligros afectan de forma fiable a las cargas de trabajo reales de seguimiento restringidas a una red. La implementación mitiga cada uno como sigue:

  • Limitación del parámetro de posición a [0, 1]. Una posición fuera de [0, 1] no tiene significado en una ruta que no se ha extendido.

    Mitigación: npoint_make rechaza pos < 0 o pos > 1 en la construcción con el mensaje "The relative position must be a real number between 0 and 1", y la misma comprobación se aplica en las entradas de los analizadores WKT, WKB y MFJSON. Los extremos 0.0 (inicio de ruta) y 1.0 (fin de ruta) son válidos y admitidos — el conjunto de pruebas ejercita ambos. Los datos de flujo corruptos o sin recortar fallan de forma explícita en lugar de contaminar una consulta posterior.

  • Manejo de SRID mixtos. Un tnpoint hereda su SRID de su ruta, mientras que un argumento geometry lleva su SRID explícitamente.

    Mitigación: cada operador entre tipos (tnpoint <-> geometry, tnpoint && geometry, distance(tnpoint, geometry)) valida que los dos SRID coincidan mediante ensure_same_srid; las discrepancias generan un error explícito en lugar de reproyectar silenciosamente un lado sobre el otro.

  • Ruta no registrada. Un npoint que referencia un gid de ruta no presente en la caché de ways no puede resolverse a una geometría.

    Mitigación: npoint_make llama a ensure_route_exists en la construcción; los constructores SQL WKT, WKB, MFJSON y npoint(...) fluyen todos por esta comprobación y generan "There is no route with gid value <n> in table ways" si no se encuentra. Los usuarios de MEOS independiente que omitan el arranque meos_register_ways(...) verán este error en la primera operación de red, no más tarde como una consulta silenciosa de resultado cero.

  • Operaciones entre redes. Dos valores tnpoint cuyas rutas pertenecen a redes diferentes (o a componentes conexas diferentes de la misma red) no pueden relacionarse mediante distancia de red.

    Mitigación: los operadores de distancia y de relación espacial entre dos valores tnpoint bajan primero ambos lados a tgeompoint y calculan el resultado como distancia / topología en el espacio libre. Este es un comportamiento documentado: quienes necesiten distancia de red verdadera deben restringir su consulta a una sola componente conexa aguas arriba.

Durabilidad y almacenamiento

La representación en disco de un npoint es una tupla de 16 bytes: un id de ruta de 8 bytes seguido de una posición de 8 bytes. La disposición de bytes es estable: asBinary(tnpoint) / tnpointFromBinary(bytea) realizan un ciclo de ida y vuelta que preserva el valor bit a bit, y las serializaciones WKB / HexEWKB siguen el patrón estándar de los tipos temporales de bandera de endianidad seguida de la carga útil. El par asMFJSON(tnpoint) / tnpointFromMFJSON(text) es bidireccional, por lo que MovingFeatures-JSON es un formato de intercambio viable en igualdad de condiciones con WKB. Cada instante se representa como {"route":<route>,"position":<position>}, con los nombres de campo coincidiendo con la superficie de accesores SQL (route, getPosition) según la convención de descriptores en lenguaje natural.

El id de ruta se emite como un número JSON, que realiza un ciclo de ida y vuelta sin pérdida entre los codificadores y decodificadores de MEOS (ambos conservan la precisión completa de int64 mediante INT64_FORMAT / strtoll). Sin embargo, no es sin pérdida en consumidores JavaScript / ECMAScript: el tipo Number tiene una mantisa de 53 bits, por lo que los ids de ruta más allá de 2^53 (~9 × 10^15) pierden precisión cuando los analiza un JSON.parse genérico del lado del navegador. Para redes derivadas de OSM esto importa en la práctica — los ids de vía de OSM cruzaron 2^53 en 2025 — y la mitigación recomendada del lado de JS es analizar con una biblioteca que materialice los números como BigInt (por ejemplo json-bigint) en lugar de confiar en el comportamiento por defecto. Esta es una decisión de diseño deliberada, no un descuido: la alternativa (emitir la ruta como una cadena JSON, como hace th3index para su carga útil de celda int64) divergiría de la forma JSON que devuelven todos los accesores numéricos de este tipo y rompería el ciclo de ida y vuelta con el análisis ingenuo de enteros en cada consumidor que maneja correctamente los enteros de 64 bits.

La salida de pg_dump de una tabla que contiene columnas npoint o tnpoint usa la representación WKT por defecto; pg_dump --binary-upgrade usa la representación WKB. Ambas son estables en el ciclo de ida y vuelta, pero un volcado solo se puede restaurar en una base de datos cuya tabla public.ways contenga todos los ids de ruta referenciados: el registro de rutas es lógicamente una clave foránea en cada columna npoint, aunque no se imponga como tal.