2 puntos por GN⁺ 2025-02-02 | 1 comentarios | Compartir por WhatsApp
  • Para explorar en una sola pantalla los datos de más de 100 millones de libros que maneja Anna’s Archive, se necesita un enfoque que convierta el espacio limitado de identificadores ISBN en un mapa
  • ISBN13, excluyendo el prefijo 978-/979- y el checksum final, es un espacio unidimensional de unos 2 mil millones de slots, y los rangos de países y editoriales se asignan por prefijos
  • Una disposición simple por filas o una curva de Hilbert pueden dificultar ver la estructura del ISBN, por lo que se usa la Bookshelf-Curve, que traslada los dígitos decimales a coordenadas 2D
  • Los tiles del mapa se entregan como archivos estáticos PNG y JSON, y shaders WebGL/GLSL procesan al instante el renderizado y filtrado, como año de publicación, proporción de disponibilidad, comparación de datasets y resaltado de editoriales
  • Está implementado como un frontend estático basado en ThreeJS, React, MobX y react-threejs-fiber, y la optimización del renderizado de grandes volúmenes de texto y del rendimiento al hacer zoom y desplazarse determina su usabilidad real

Por qué tomar el espacio ISBN como objetivo de visualización

  • Las bibliotecas llevan mucho tiempo recopilando el conocimiento de la humanidad, y en la era digital podría ser posible recolectar de forma amplia las obras que cumplen ciertos criterios
  • Anna’s Archive, una de las bibliotecas en la sombra, necesitaba una visualización que permitiera explorar más de 100 millones de libros a la vez
  • Los datos objetivo incluyen título, autor, país, editorial, fecha de publicación, cantidad de bibliotecas que lo poseen y disponibilidad digital, entre otros
  • La visualización interactiva permite seleccionar datasets, buscar libros individuales, filtrar por año de publicación y realizar visualizaciones compuestas basadas en shaders personalizados
  • Al hacer zoom al máximo, la representación cambia para que cada libro parezca estar colocado en una estantería

Estructura del ISBN

  • ISBN13 es un número de 13 dígitos asignado a casi todos los libros publicados
  • Actualmente, los primeros tres dígitos del ISBN13 están fijados en 978- o 979-, y el último dígito es un checksum
  • Por lo tanto, el espacio real de ISBN13 puede verse como de aproximadamente 2 mil millones de slots
  • Los prefijos 978- y 979- existen porque ISBN13 es un subconjunto del European Article Number, y este prefijo fijo se llama Bookland
  • Los bloques ISBN grandes son asignados por la International ISBN Agency a organizaciones internacionales, y cada país vuelve a dividir su bloque por editorial
  • Los bloques ISBN siempre se asignan por prefijo (prefix)
    • 978-4 está asignado a Japón
    • Japón asigna 978-4-312 a una editorial
    • Esa editorial puede asignar elementos en el rango 000000-99999 para crear ISBN como 978-4-312-99999-X
  • Cuanto más largo es el prefijo de país, menor es la cantidad de libros que se pueden asignar dentro de él
    • Japón tiene un espacio de 100 millones de ISBN
    • Singapur empieza con el prefijo 978-9971- y solo permite 100 mil ISBN

Cómo desplegar el ISBN unidimensional en dos dimensiones

  • Al quitar el prefijo 978-/979- y el checksum, el ISBN se convierte en un valor decimal unidimensional entre 0 y 2 mil millones
  • La forma más simple sería llenar píxeles por filas desde arriba, pero las regiones pequeñas se alargarían horizontalmente y dificultarían ver la estructura del ISBN
  • Una curva de Hilbert hace que las regiones pequeñas sean más cercanas a un cuadrado, pero puede generar artefactos de estructura espacial que no existen en los datos
  • Para evitar estructuras que no están en los datos, esta visualización usa la Bookshelf-Curve, basada en la naturaleza decimal del ISBN

Bookshelf-Curve

  • La Bookshelf-Curve coloca los 0-9 del primer dígito decimal de lado a lado, y los 0-9 del siguiente dígito de arriba abajo, repitiendo este método recursivamente
  • Para mantener la proporción rectangular de dos niveles, la relación de aspecto de cada bloque es √10, es decir, aproximadamente 3.16:1
  • Esta estructura es similar al concepto de relación de aspecto recursiva del papel A4, pero en lugar de dividir en dos, divide en 10 segmentos
  • La transformación de coordenadas es simple: usa como coordenadas los dígitos del ISBN saltando uno de por medio
  • El código de implementación terminó en 50 líneas debido al intento de generalizarlo, pero el principio es lo bastante fácil de entender como para seguir a mano la ubicación de un ISBN específico

Tiles de mapa y codificación de datos

  • Se genera una imagen de tile para cada prefijo ISBN
  • El tamaño objetivo de cada tile era de unos 100kB, y como resultado el tamaño del tile fue de 2000 × 633 píxeles
  • La información almacenada en cada píxel varía según el dataset
    • El dataset de año de publicación almacena el valor restando 1800 dentro de un rango de 8 bits
    • El canal rojo contiene el año promedio de publicación de los libros dentro de ese píxel
    • El canal azul contiene la proporción de libros existentes
    • Por ejemplo, si existe el 50% de los libros, el valor del canal azul es 127/255
  • En el nivel máximo de zoom, para mayor precisión, se mapea 1 libro a 1 píxel

Renderizado basado en shaders GLSL

  • Al principio se almacenaban datos RGB directamente en los tiles, pero para aumentar la flexibilidad se empezaron a almacenar datos más abstractos, y el renderizado real se procesa en la GPU con un GLSL fragment shader
  • Este enfoque permite elegir la paleta de colores más tarde, aplicar transformaciones y filtros arbitrarios al instante, o combinar varios datasets sobre la marcha
  • Los datos de año de publicación almacenan el rango 1800-2055, pero como el 95% de los datos está en el rango 1985-2024, el shader puede comprimir la visualización a ese intervalo
  • La función heatmapColor(float) transforma un valor 0-1 en una escala de color elegida por el usuario
  • La sintaxis $dataset_x no es sintaxis propia de GLSL, sino una sintaxis de plantilla simple basada en expresiones regulares para cargar solo las imágenes que el shader realmente lee
  • En la opción ⚙️ Advanced de la visualización se puede modificar directamente el shader, y los cambios se reflejan en tiempo real
  • El significado de cada píxel de dataset está documentado en el README

Representación para que parezca una estantería al hacer zoom

  • En la vista completamente ampliada, se aplica a cada píxel un estilo que lo hace parecer un libro
  • Cada libro se procesa con ancho, alto y patrón aleatorios
  • No coincide exactamente con la forma de un libro real, pero hace que la pantalla con zoom máximo se vea como una escena más viva
  • Todo este estilo está implementado en el shader
  • Como se necesitaba el mismo resultado de generación aleatoria en GLSL y JavaScript, fue complicado calcular la altura de los libros para alinear los límites del texto
  • Pasando el valor de zoom actual como uniform y haciendo fade-in entre dos niveles de zoom, el estilo de estantería puede aparecer de forma suave

Texto, estructura de árbol y rendimiento

  • Igual que los tiles de imagen, el texto también se renderiza como una estructura jerárquica según el nivel de zoom y el culling del view frustum
  • Para la implementación se usa react-threejs-fiber
  • React agrega elementos a la escena de forma recursiva cuando la vista se desplaza, y para cada prefijo compone un Plane, texto HTML y nodos de prefijos hijos
  • Al renderizar mucho texto, al principio hubo grandes problemas de rendimiento
  • El mejor enfoque fue colocar HTML dentro de <foreignObject /> de SVG, renderizarlo en un canvas con drawImage y luego cargarlo como textura
  • Este método funcionaba, pero durante el renderizado generaba cada vez un retraso síncrono de 20ms
  • Como no se puede dibujar SVG en un WebWorker, era difícil moverlo a un WebWorker
  • El problema real de rendimiento mejoró en gran parte reduciendo la cantidad de elementos HTML, limitando el contenido DOM agregado por frame y, especialmente, eliminando la pila de filtros CSS text-shadow

Códigos de barras y rangos de editoriales

  • Al máximo zoom, cada libro muestra un código de barras
  • El código de barras se agregó para reforzar la idea de que los libros están ordenados según el ISBN
  • En lugar de usar una biblioteca de renderizado de códigos de barras, se usó la fuente TTF Libre Barcode, que renderiza números de 13 dígitos como código de barras e incluso calcula el dígito de control
  • Gracias a la optimización del renderizado de texto del sistema operativo, este método fue cómodo y también tuvo buen rendimiento
  • Cada grupo suele tener un rango grande a nivel de país, y las editoriales tienen rangos más pequeños dentro de él
  • A grupos y editoriales se les asigna un color aleatorio propio
  • Incluso si una editorial tiene varios rangos, se usa el mismo color
  • Para resaltar todos los rangos al mismo tiempo, se asigna un ID único a cada editorial y se almacena como componentes RGB
  • La coloración de editoriales todavía tiene puntos que no resultan satisfactorios
    • Los colores de las editoriales chocan con la escala de color del mapa de calor
    • Los rangos de países son difíciles de ver

Navegación por búsqueda y trayectorias de vuelo

  • Al buscar un libro o hacer clic en el minimapa, se hace un desplazamiento en vuelo hacia esa ubicación
  • Calcular una trayectoria de vuelo agradable fue más difícil de lo esperado
  • Con ajuste parabólico los resultados eran comunes, y luego se probó un enfoque que usa un espacio de transformación separado
  • El resultado final no es perfecto y se siente algo sobrediseñado, con unas 500 líneas de código, pero funciona mejor que el enfoque original

Arquitectura y pipeline de procesamiento

  • No se necesita backend
    • Los tiles de imagen se almacenan como PNG
    • El árbol de datos se almacena como JSON
    • Basta con subir HTML, JS, CSS, PNG y JSON a un host de archivos estáticos como GitHub Pages
  • El frontend usa ThreeJS, React y MobX
  • Esta combinación resultó cómoda para crear escenas 2D/3D declarativas, reactivas y aceleradas por GPU con componentes reutilizables
  • Los scripts de procesamiento están escritos en su mayoría en JS y generan directamente JSON y PNG
  • Algunos scripts debían leer archivos fuente de 250GB y generar salida en SQLite, por lo que se escribieron en Rust

Resultado y código

  • El resultado es una forma flexible de visualizar libros publicados con ISBN asignado
  • Este proyecto fue posible gracias a que ISBN está diseñado con un espacio de identificadores apenas lo suficientemente pequeño
  • Si se hubiera hecho el mismo trabajo con UUID, la mayor parte habría sido un enorme espacio vacío
  • El código fuente está disponible en isbn-visualization

1 comentarios

 
GN⁺ 2025-02-02
Comentarios de Hacker News
  • Cuando estaban empezando Amazon, querían hacer exactamente algo así, pero usando la clasificación triple de la Library of Congress en lugar del ISBN
    Al final, los proveedores de datos, Baker & Taylor y Books In Print, aplastaban la clasificación triple en una sola cadena, así que no podían encontrar los límites de forma confiable y tuvieron que abandonarlo antes siquiera de arrancar bien
    Además, implementar este tipo de exploración “a vuelo” en la web de 1994~1995 claramente no habría sido fácil, así que es un trabajo impresionante

    • Me da curiosidad qué significa aquí la clasificación triple de LoC
      He revisado bastante tanto LoC Classification como LoC Subject Headings, y es una pena que ninguno de los dos se ofrezca libremente en un formato legible por máquina realmente útil. Con PDFs se puede hacer alguna trampa
      En particular, la sección de derecho estatal de Classification tenía una densidad de clasificación muy desigual entre estados; si no recuerdo mal, NY y CA eran abrumadoramente complejos, PA iba en un lejano tercer lugar, y muchos estados “flyover” eran casi ridículamente simples y parecidos entre sí. Eso parece reflejar la complejidad del derecho estatutario, la regulación y la jurisprudencia de cada estado
      Otro dato histórico interesante es que el sistema de clasificación y la división alfabética de nivel superior parecen venir directamente de la biblioteca personal de Thomas Jefferson, que fue el origen de la LoC
      Si te interesa, en Hathi Trust puedes ver bastante de la historia del desarrollo y expansión de Classification en los informes anuales del Librarian of Congress presentados al Congreso
      Classification: <https://www.loc.gov/catdir/cpso/lcco/>
      Subject headings: <https://id.loc.gov/authorities/subjects.html>
      Informes anuales — recientes: <https://www.loc.gov/about/reports-and-budgets/annual-reports...> / archivo histórico hasta ~1866: <https://catalog.hathitrust.org/Record/000072049>
    • Habiendo tratado con Baker & Taylor antes, no me sorprende en absoluto
      Era una de las empresas más atrasadas técnicamente con las que trabajé, y hasta que cerré la cuenta a inicios de 2020 seguían manejando órdenes de compra y conciliaciones en papel, PDF y correo electrónico. Creo que en algún momento incluso me hicieron enviar documentos por fax
  • Como no es raro que se asignen ISBN duplicados a libros distintos [0], la expresión “todos los libros del espacio ISBN” puede ser una exageración
    También hay libros con ISBN incorrectos cuyo dígito de control no coincide con el resto del ISBN. Si al corregir el dígito de control termina coincidiendo con otro libro, entonces quedaría fuera del espacio ISBN asumido por la entrada del blog
    [0] https://scis.edublogs.org/2017/09/28/the-dreaded-case-of-dup...

    • También podría simplemente no haber sido asignado
      Estaba viendo los ISBN conocidos más bajos de editoriales checas cuando apareció un color distinto, y https://books.google.cz/books?vid=ISBN9788000000015&redir_es... no parece ser un ISBN válido :-) aunque no sé si ese libro realmente trae un ISBN falso tan descarado o si es un error en los datos de Google Books
  • Es una presentación impresionante
    Pero esta pantalla no refleja el catálogo ISBN completo, sino solo lo que tiene Anna's Archive. Por el sesgo de recopilación de Anna, se nota una inclinación hacia ciertos rangos de idiomas, y las zonas marcadas en negro son elementos que no están en el archivo

    • No es del todo exacto. Anna's Archive tiene por un lado una base de datos de libros que posee realmente como archivos, y por otro una base de datos de libros de los que solo conoce los metadatos
      La base de datos de metadatos viene de varias fuentes y, hasta donde sé, es bastante completa. Lo negro probablemente sea en su mayoría zonas a las que no se les asignó ningún libro
  • De verdad es genial, un proyecto de pasión asombroso y un recurso excelente
    Si haces zoom, se ven los títulos y los códigos de barras, y al pasar el mouse incluso aparecen la portada y los detalles. Tiene casi todo lo que uno podría pedir
    Como idea de mejora, estaría bien un checkbox para ocultar el panel blanco flotante de la esquina superior izquierda y el elemento de la esquina superior derecha. Me gusta “sumergirme” en este tipo de visualizaciones, y esos elementos flotantes me sacan un poco de la experiencia, así que reducen un poco la diversión y la funcionalidad

  • Este es un caso perfecto para Microsoft Silverlight PivotViewer, una gran interfaz web que Microsoft usaba en neuroimagen antes de descontinuarla
    Hay una impresionante charla TED de Gary W. Flake mostrando cómo se usaba
    https://m.youtube.com/watch?v=LT_x9s67yWA
    También hay un artículo de IEEE que escribió en 2011. Qué lástima que esto no sea un estándar web
    https://www.dropbox.com/scl/fi/bl8zkjs3y47q3377hh3ya/Yan_Wil...

  • Es una visualización muy buena
    Aquí hay más aportes geniales: https://software.annas-archive.li/AnnaArchivist/annas-archiv...
    Mi trabajo está en https://isbnviz.pages.dev

  • Al hacer zoom se ve como una estantería. Está realmente genial

    • Posible mejora: las ediciones de bolsillo y las de tapa dura aparecen una junto a la otra, pero se ven iguales. No estoy seguro sobre los libros electrónicos
  • Excelente. Se siente como una versión real de la Library of Babel: https://libraryofbabel.info/
    Entre tantas tonterías de VR, por una biblioteca infinita o un museo infinito en la vida real sí que podría pagar

    • Lamentablemente, en esta implementación en particular los autores no recibirían nada de ese dinero
      Si fuera un proyecto que diera acceso a todos los textos descargables ofrecidos legalmente, sería mucho más interesante. Por ejemplo, con una interfaz como la de este sitio:
      https://onlinebooks.library.upenn.edu/
  • La presentación en su formato actual se sintió un poco abrumadora
    Me tomó algo de tiempo entender que la sección de presets en la esquina superior izquierda en realidad lleva a ejes adicionales de visualización de datos como incluir AA, rareza e inclusión de Google Books. Aun así, la visualización y la profundidad de los datos son muy ricas
    También me gustó https://archive.anarchy.cool/blog/all-isbns.html#visualizing, que muestra el aspecto de los clústeres regionales
    El preset por año también fue interesante por sí solo para ver la actividad editorial por región. Poland ha estado muy activa últimamente, mientras que Norway se ve relativamente tranquila. China parece haber ido en aumento desde alrededor de 2005 y haber crecido muchísimo en los últimos 10 años
    También hay algo raro en United States. Nunca había oído hablar de Blackstone Audio, Blurb Inc. y Draft2Digital, pero emitieron una cantidad enorme de ISBN

    • Sí, es bastante ruidoso, y hasta cierto punto eso es intencional porque prioriza una alta densidad de datos
      Hay una vista un poco más minimalista (menos colores y mostrando solo un nivel de texto a la vez) aquí:
      https://phiresky.github.io/isbn-visualization/?dataset=all&g...
      Probablemente también se podría ajustar más, por ejemplo ocultando parte del texto, como la sección de N publishers, o reduciendo la información al pasar el mouse
  • Las trayectorias de vuelo se pueden modelar de forma bastante razonable como movimiento sobre el semiplano hiperbólico. Se puede tomar x como la posición sobre la ruta lineal entre los puntos extremos, e y como la longitud de un lado del viewport
    Pensé en dos métricas que terminan siendo equivalentes. Primero, una basada en asumir un mapa jerárquico de tiles y minimizar la cantidad de tiles cargados. Si se usa y como la longitud de un lado del viewport, el costo de moverse horizontalmente una distancia x es x/y tiles, y hacer zoom de y_0 a y_1 carga abs(log_2(y_1/y_0)) tiles, así que coincide con ds = dy/y. Al combinarlas, queda ds^2 = (dx^2 + dy^2)/y^2, que es exactamente la métrica del semiplano superior
    La otra forma es verlo como minimizar en cierto sentido el “flujo óptico” del viewport. Esto también da la misma métrica, salvo por un factor de escala. Si haces paneo una distancia x sin zoom, todo se mueve x/y, es decir, en proporción al viewport. Si haces zoom por un factor k, un píxel en (u,v) pasa a (ku,kv), así que el desplazamiento es (u,v)(k-1). Si la longitud del lado pasa de y a y+dy, entonces queda (u,v)dy/y, así que dependiendo de cómo promedies el desplazamiento obtienes alguna constante por dy/y
    Entonces, la geodésica deseada es un horociclo, un círculo centrado en y=0, pero calcular el movimiento a lo largo de la curva requiere algo de trabajo. Si tomas el arco de θ_0 a θ_1, el tiempo total sale de integrar dtheta/y = dθ/sin(θ), así que para hacerlo de forma exacta hay que invertir t = ln(csc(θ)-cot(θ)). Pensé que sería mejor aproximarlo, pero Mathematica me dijo que θ = atan2(1-2
    e^(2t), 2
    e^t), así que no está tan mal
    Comparado con la lógica de “blub space”, parece que ahí la métrica efectiva es ds^2 = dz^2 + (z+1)^2 dx^2. Son coordenadas polares con z=1/y como nivel de zoom, y usando dz=dy/y^2 queda ds^2 = dy^2/y^4 + dx^2*(1/y^2 + ...). Es decir, la implementación actual parece dedicar mucho más tiempo al paneo en niveles altos de zoom que el modelo hiperbólico. Reducir de 4x a 2x cuesta el doble que reducir de 2x a 1x, aunque visualmente se siente igual

    • Al probarlo de verdad, resultó ser muy distinto de lo que esperaba, y hacía mucho más zoom out. Era porque me faltó parte del código de zoom
      Su zoom no es el factor de escala, sino que corresponde a mi “y”, así que la métrica queda ds^2 = dy^2 + (C-y)^2 dx^2, donde C es un poco mayor que el nivel máximo de zoom. También hay un manejo especial para cuando la curva intenta alejarse todavía más haciendo zoom out
      Si se normaliza para que el costo de paneo de extremo a extremo sea el mismo estando completamente alejado (zoom=1), entonces en niveles de zoom muy profundos el costo de paneo es casi plano, mientras que en niveles relativamente menos profundos es mayor que en el modelo hiperbólico. Eso parece contribuir a que las distancias cortas se sientan como si el viewport se moviera muy rápido, mientras que las largas parezcan hacer zoom out casi hasta el final. Como los niveles intermedios de zoom salen perjudicados, conviene más ir casi hasta el final