2 pontos por GN⁺ 2025-02-02 | 1 comentários | Compartilhar no WhatsApp
  • Para explorar, em uma única tela, os dados de mais de 100 milhões de livros tratados pelo Anna’s Archive, foi necessário adotar uma abordagem que transformasse o espaço limitado de identificadores do ISBN em um mapa
  • O ISBN13, excluindo os prefixos 978-/979- e o dígito verificador final, forma um espaço unidimensional de cerca de 2 bilhões de slots, e faixas de países e editoras são atribuídas por unidade de prefixo
  • Como um layout simples por linhas ou uma Hilbert curve pode dificultar a visualização da estrutura do ISBN, foi usada a Bookshelf-Curve, que transfere os dígitos decimais para coordenadas 2D
  • Os tiles do mapa são fornecidos como arquivos estáticos PNG e JSON, e shaders WebGL/GLSL processam instantaneamente renderização e filtragem como ano de publicação, taxa de acervo, comparação entre datasets e destaque de editoras
  • A implementação foi feita como um frontend estático baseado em ThreeJS, React, MobX e react-threejs-fiber, e a otimização de desempenho para renderização de muito texto e navegação com zoom é o que determina a usabilidade real

Por que usar o espaço ISBN como alvo de visualização

  • Bibliotecas vêm reunindo o conhecimento da humanidade há muito tempo, e na era digital pode se tornar possível coletar de forma abrangente obras que atendam a certos critérios
  • O Anna’s Archive, uma das shadow libraries, precisava de uma visualização que permitisse explorar mais de 100 milhões de livros de uma vez
  • Os dados incluem título, autor, país, editora, período de publicação, número de bibliotecas que possuem o livro e disponibilidade digital
  • A visualização interativa oferece seleção de dataset, busca de livros individuais, filtro por ano de publicação e visualizações compostas baseadas em shaders personalizados
  • No nível máximo de zoom, a representação muda para algo que faz cada livro parecer encaixado em uma estante

A estrutura do ISBN

  • ISBN13 é um número de 13 dígitos atribuído a quase todos os livros publicados
  • Atualmente, os três primeiros dígitos do ISBN13 são fixos como 978- ou 979-, e o último dígito é um checksum
  • Portanto, o espaço real do ISBN13 pode ser visto como cerca de 2 bilhões de slots
  • Os prefixos 978- e 979- existem porque o ISBN13 é um subconjunto do European Article Number, e esse prefixo fixo é chamado de Bookland
  • Grandes blocos de ISBN são atribuídos pela International ISBN Agency a organizações internacionais, e cada país subdivide seu bloco por editora
  • Blocos de ISBN são sempre atribuídos por unidade de prefixo
    • 978-4 é atribuído ao Japão
    • O Japão atribui 978-4-312 a uma editora
    • Essa editora pode atribuir itens na faixa 000000-99999, criando ISBNs como 978-4-312-99999-X
  • Quanto mais longo o prefixo do país, menor o número de livros que pode ser atribuído dentro dele
    • O Japão tem um espaço de 100 milhões de ISBNs
    • Singapura começa com o prefixo 978-9971- e só pode ter 100 mil ISBNs

Como expandir um ISBN unidimensional em 2D

  • Removendo os prefixos 978-/979- e o checksum, o ISBN se torna um valor unidimensional decimal entre 0 e 2 bilhões
  • A forma mais simples seria preencher pixels em linhas de cima para baixo, mas isso faz pequenas regiões se alongarem horizontalmente e dificulta enxergar a estrutura do ISBN
  • A Hilbert curve deixa pequenas regiões mais próximas de quadrados, mas pode introduzir artefatos de estrutura espacial que não existem nos dados
  • Para evitar estruturas inexistentes nos dados, esta visualização usa a Bookshelf-Curve, aproveitando a natureza decimal do ISBN

Bookshelf-Curve

  • A Bookshelf-Curve coloca 0-9 do primeiro dígito decimal lado a lado, e distribui 0-9 do próximo dígito para cima e para baixo, repetindo isso recursivamente
  • Para manter a proporção dos retângulos em dois níveis, a razão de aspecto de cada bloco é √10, ou cerca de 3,16:1
  • Essa estrutura é semelhante ao conceito de proporção recursiva de uma folha A4, mas em vez de dividir em dois, divide em 10 segmentos
  • A conversão de coordenadas é simples, pois usa alternadamente os dígitos do ISBN como coordenadas
  • O código de implementação ficou com 50 linhas por causa de uma tentativa de generalização, mas o princípio é fácil de entender a ponto de ser possível localizar manualmente a posição de um ISBN específico

Tiles do mapa e codificação dos dados

  • São geradas imagens de tile para cada prefixo ISBN
  • O tamanho-alvo de cada tile era cerca de 100kB, e o resultado foi um tile de 2000 × 633 pixels
  • As informações armazenadas em cada pixel variam conforme o dataset
    • No dataset de ano de publicação, é armazenado em 8 bits o valor com 1800 subtraído
    • O canal vermelho guarda o ano médio de publicação dos livros naquele pixel
    • O canal azul guarda a proporção de livros existentes
    • Por exemplo, se 50% dos livros existem, o valor do canal azul será 127/255
  • No nível máximo de zoom, para precisão, 1 livro é mapeado para 1 pixel

Renderização baseada em shaders GLSL

  • No início, os dados RGB eram armazenados diretamente nos tiles, mas para ganhar flexibilidade passaram a armazenar dados mais abstratos, enquanto a renderização real é feita no fragment shader GLSL da GPU
  • Essa abordagem permite escolher a paleta de cores depois, aplicar transformações e filtros arbitrários instantaneamente ou combinar vários datasets em tempo real
  • Os dados de ano de publicação armazenam a faixa 1800-2055, mas como 95% dos dados estão entre 1985-2024, o shader pode comprimir a visualização para esse intervalo
  • A função heatmapColor(float) converte um valor 0-1 para a escala de cores escolhida pelo usuário
  • A sintaxe $dataset_x não é sintaxe nativa do GLSL, mas uma sintaxe simples de template baseada em regex para carregar apenas as imagens que o shader realmente lê
  • Na opção ⚙️ Advanced da visualização, é possível editar diretamente o shader, e o resultado das mudanças aparece em tempo real
  • O significado de cada pixel de dataset está documentado no README

Fazer o zoom parecer uma estante de livros

  • Na visualização totalmente ampliada, cada pixel recebe um estilo que o faz parecer um livro
  • Cada livro recebe largura, altura e padrão aleatórios
  • Não corresponde exatamente ao formato real de um livro, mas faz a cena no zoom máximo parecer mais viva
  • Todo esse estilo é implementado no shader
  • Como era necessário obter o mesmo resultado de geração aleatória em GLSL e JavaScript, a parte de calcular a altura do livro e alinhar os limites do texto foi complicada
  • Passando o valor atual de zoom como uniform e aplicando um fade entre dois níveis de zoom, é possível introduzir suavemente o estilo de estante

Texto, estrutura em árvore e desempenho

  • Assim como os tiles de imagem, o texto também é renderizado em estrutura hierárquica de acordo com o nível de zoom e o view frustum culling
  • A implementação usa react-threejs-fiber
  • O React adiciona elementos da cena recursivamente enquanto a visualização se move, compondo Plane, texto HTML e nós filhos de prefixo para cada prefixo
  • No início, houve grandes problemas de desempenho ao renderizar muito texto
  • A melhor abordagem foi colocar HTML dentro de <foreignObject /> em SVG, renderizar isso em canvas com drawImage e então carregar como textura
  • Essa abordagem funcionou, mas criava uma latência de 20ms de forma síncrona em cada renderização
  • Como não era possível desenhar SVG em um WebWorker, foi difícil mover isso para um WebWorker
  • A maior parte dos problemas reais de desempenho foi resolvida reduzindo o número de elementos HTML, limitando o conteúdo DOM adicionado por frame e, principalmente, removendo a pilha de filtros CSS text-shadow

Códigos de barras e faixas de editoras

  • No zoom máximo, cada livro exibe um código de barras
  • O código de barras foi adicionado para reforçar a ideia de que os livros estão ordenados por ISBN
  • Em vez de usar uma biblioteca de renderização de código de barras, foi usada a fonte TTF Libre Barcode, que renderiza números de 13 dígitos como código de barras e até calcula o dígito verificador
  • Graças às otimizações de renderização de texto do sistema operacional, essa abordagem foi prática e teve bom desempenho
  • Cada grupo normalmente representa uma grande faixa por país, e as editoras ocupam faixas menores dentro dela
  • Cada grupo e cada editora recebem uma cor aleatória única
  • Mesmo quando uma editora possui várias faixas, a mesma cor é usada
  • Para destacar simultaneamente todas as faixas, foi atribuído um ID único a cada editora e ele foi armazenado como componentes RGB
  • Ainda há pontos insatisfatórios nas cores das editoras
    • As cores das editoras entram em conflito com a escala de cores do heatmap
    • As faixas de países são difíceis de visualizar

Navegação de busca e trajetória de voo

  • Ao buscar um livro ou clicar no minimapa, a interface faz um voo até aquela posição
  • Calcular uma trajetória de voo visualmente agradável foi mais difícil do que parecia
  • Com ajuste parabólico, o resultado foi apenas mediano, e depois foi tentada uma abordagem usando um espaço de transformação separado
  • O resultado final não é perfeito e parece um tanto excessivamente projetado, com cerca de 500 linhas de código, mas funciona melhor do que a abordagem original

Arquitetura e pipeline de processamento

  • Não é necessário backend
    • Os tiles de imagem são salvos como PNG
    • A árvore de dados é salva como JSON
    • Basta publicar HTML, JS, CSS, PNG e JSON em um host de arquivos estáticos como o GitHub Pages
  • O frontend usa ThreeJS, React e MobX
  • Essa combinação foi conveniente para criar cenas 2D/3D reativas, declarativas e aceleradas por GPU com componentes reutilizáveis
  • Os scripts de processamento foram escritos majoritariamente em JS e geram JSON e PNG diretamente
  • Alguns scripts precisaram ler arquivos-fonte de 250GB e exportar para SQLite, então foram escritos em Rust

Resultado e código

  • O resultado é uma forma flexível de visualizar livros publicados aos quais foi atribuído ISBN
  • Este projeto só foi possível porque o ISBN foi projetado com um espaço de identificadores pequeno, mas ainda suficiente
  • Se o mesmo trabalho fosse feito com UUID, a maior parte seria um enorme espaço vazio
  • O código-fonte está disponível em isbn-visualization

1 comentários

 
GN⁺ 2025-02-02
Opiniões no Hacker News
  • Quando começamos a Amazon, eu queria fazer exatamente algo assim, mas usando a classificação tripla da Library of Congress em vez de ISBN
    No fim, os fornecedores de dados, Baker & Taylor e Books In Print, achatavam a classificação tripla em uma única string, então não era possível encontrar os limites de forma estável, e tivemos de desistir antes mesmo de começar direito
    Implementar esse tipo de navegação em “voo” na web de 1994–1995 certamente também não teria sido fácil, então é um trabalho incrível

    • Fiquei curioso sobre o que seria essa classificação tripla da LoC mencionada aqui
      Já examinei bastante a LoC Classification e os LoC Subject Headings, e é uma pena que nenhum dos dois seja oferecido livremente em um formato útil para leitura por máquina. Com PDFs dá para fazer alguns malabarismos
      Em especial, a seção de direito estadual da Classification tinha uma densidade de classificação muito desigual entre os estados; pelo que me lembro, NY e CA eram de longe os mais complexos, PA vinha em um terceiro lugar um pouco atrás, e muitos estados “flyover” eram quase ridiculamente simples e parecidos entre si. Isso parece refletir a complexidade das leis codificadas, regulações e jurisprudência de cada estado
      Outro fato histórico interessante é que o sistema de classificação e as divisões de nível superior em ordem alfabética parecem vir diretamente da biblioteca pessoal de Thomas Jefferson, que deu origem à LoC
      Se tiver interesse, nos relatórios anuais enviados ao Congresso pelo Librarian of Congress disponíveis no Hathi Trust dá para ver bastante sobre a história do desenvolvimento e da expansão da Classification
      Classification: <https://www.loc.gov/catdir/cpso/lcco/>
      Subject headings: <https://id.loc.gov/authorities/subjects.html>
      Annual reports — Recentes: <https://www.loc.gov/about/reports-and-budgets/annual-reports...> / Arquivo histórico até cerca de 1866: <https://catalog.hathitrust.org/Record/000072049>
    • Para quem já lidou com a Baker & Taylor no passado, isso não surpreende nem um pouco
      Foi uma das empresas mais atrasadas tecnologicamente com que já negociei, e até o início de 2020, quando encerramos a conta, eles ainda processavam pedidos de compra e conciliações em papel, PDF e e-mail. Acho que em algum momento até nos fizeram enviar documentos por fax
  • Como não é raro ISBNs serem atribuídos em duplicidade a livros diferentes [0], a expressão “todos os livros no espaço de ISBN” pode ser um exagero
    Também há livros com ISBNs inválidos, cujo dígito verificador não bate com o restante do ISBN. Se, ao corrigir o dígito verificador, ele passar a coincidir com outro livro, então esse livro fica fora do espaço de ISBN assumido pelo post do blog
    [0] https://scis.edublogs.org/2017/09/28/the-dreaded-case-of-dup...

    • Também pode ser que ele nem tenha sido atribuído
      Eu estava olhando os menores ISBNs conhecidos de editoras tchecas e apareceu uma cor diferente; https://books.google.cz/books?vid=ISBN9788000000015&redir_es... não parece ser um ISBN válido :-) Mas não sei se esse livro contém um ISBN falso tão descarado ou se é um erro nos dados do Google Books
  • É uma apresentação impressionante
    Mas esta tela não reflete o catálogo completo de ISBNs, e sim apenas o acervo do Anna's Archive. Por causa do viés da coleta da Anna, há um viés visível para certas faixas de idioma, e as áreas em preto são itens que não estão no arquivo

    • Não é totalmente exato. O Anna's Archive tem um banco de dados de livros que possui como arquivos reais e outro banco de dados de livros dos quais conhece apenas os metadados
      O banco de metadados vem de várias fontes e, até onde sei, é bastante completo. O preto provavelmente representa, em sua maior parte, áreas sem livros atribuídos
  • É realmente muito legal, um projeto de paixão surpreendente e um excelente recurso
    Ao dar zoom, dá para ver títulos e códigos de barras; ao passar o mouse, aparecem capas e detalhes. Tem quase tudo que se poderia desejar
    Como ideia de melhoria, seria bom ter uma caixa de seleção para ocultar o painel branco flutuante no canto superior esquerdo e os elementos no canto superior direito. Gosto de “mergulhar” nesse tipo de visualização, e esses elementos flutuantes me tiram um pouco dessa experiência, reduzindo um pouco a diversão e a funcionalidade

  • Este é um caso perfeito para o Microsoft Silverlight PivotViewer, uma excelente interface web usada em neuroimagem antes de a Microsoft descontinuá-la
    Há uma apresentação TED impressionante em que Gary W. Flake mostra como usá-la
    https://m.youtube.com/watch?v=LT_x9s67yWA
    Também há um artigo da IEEE de 2011. É uma pena que isso não seja um padrão da web
    https://www.dropbox.com/scl/fi/bl8zkjs3y47q3377hh3ya/Yan_Wil...

  • Uma visualização muito legal
    Há outras submissões ainda mais interessantes aqui: https://software.annas-archive.li/AnnaArchivist/annas-archiv...
    Meu trabalho está em https://isbnviz.pages.dev

  • Ao ampliar, parece uma estante. Muito legal mesmo

    • Possível melhoria: edições de bolso e encadernadas aparecem lado a lado, mas parecem idênticas. Não sei bem quanto aos e-books
  • Excelente. Parece uma Library of Babel da vida real: https://libraryofbabel.info/
    Entre todo tipo de besteira em VR, acho que eu até pagaria por uma biblioteca infinita ou um museu infinito do mundo real

    • Infelizmente, nesta implementação específica, os autores não receberiam nada desse dinheiro
      Teria sido muito mais interessante se fosse um projeto que desse acesso a todos os textos para download disponibilizados legalmente. Por exemplo, como uma interface para um lugar assim:
      https://onlinebooks.library.upenn.edu/
  • A apresentação no formato atual pareceu um pouco esmagadora
    Levei um tempo para entender que a área de presets no canto superior esquerdo na verdade leva a eixos adicionais de visualização de dados, como incluir ou não AA, raridade e incluir ou não Google Books. Ainda assim, a visualização e a profundidade dos dados são ricas
    Também gostei de https://archive.anarchy.cool/blog/all-isbns.html#visualizing, que mostra a aparência dos clusters regionais
    O preset por ano, por si só, também foi interessante para ver a atividade editorial por região. A Poland está muito ativa recentemente, enquanto a Norway parece relativamente tranquila. A China parece ter começado a crescer por volta de 2005 e ficado enorme nos últimos 10 anos
    Há algo estranho também nos United States. Nunca ouvi falar delas, mas Blackstone Audio, Blurb Inc. e Draft2Digital emitiram uma quantidade enorme de ISBNs

    • É verdade que há bastante ruído, e isso parece em parte intencional, já que o foco é uma alta densidade de dados
      Uma visualização um pouco mais minimalista (menos cores e apenas um nível de texto exibido ao mesmo tempo) está aqui:
      https://phiresky.github.io/isbn-visualization/?dataset=all&g...
      Acho que também daria para ajustar mais, por exemplo ocultando alguns textos, como a parte de N publishers, ou reduzindo as informações no mouseover
  • A trajetória de voo pode ser pensada de forma bastante razoável se modelada como movimento no semiplano superior hiperbólico. Basta tomar x como a posição no caminho linear entre os pontos finais, e y como o comprimento de um lado do viewport
    Pensei em duas métricas que acabam ficando iguais. Primeiro, supondo um mapa hierárquico de tiles e minimizando o número de tiles carregados. Usando y como o comprimento de um lado do viewport, o custo de se mover horizontalmente em x é x/y tiles, e ao dar zoom de y_0 para y_1 você carrega abs(log_2(y_1/y_0)) tiles, o que coincide com ds = dy/y. Combinando, temos ds^2 = (dx^2 + dy^2)/y^2, exatamente a métrica do semiplano superior
    Outra forma é ver isso como a minimização, em algum sentido, do “fluxo óptico” do viewport. Isso também leva à mesma métrica, mudando apenas a escala. Se você faz pan em x sem zoom, tudo se move x/y, isto é, uma fração relativa ao viewport. Se amplia por um fator k, um pixel em (u,v) vai para (ku,kv), então o deslocamento é (u,v)(k-1). Quando o comprimento de um lado passa de y para y+dy, isso vira (u,v)dy/y, portanto, dependendo de como se calcula a média dos deslocamentos, resulta em algum múltiplo constante de dy/y
    Então a geodésica desejada é um horociclo, um círculo centrado em y=0, mas é preciso um pouco de trabalho para calcular o movimento ao longo da curva. Se pegarmos um arco de θ_0 a θ_1, o tempo total vem da integral de dtheta/y = dθ/sin(θ), então, para ser exato, seria preciso inverter t = ln(csc(θ)-cot(θ)). Achei que seria melhor aproximar, mas o Mathematica informou que θ = atan2(1-2
    e^(2t), 2
    e^t), então não é tão ruim
    Comparando com a lógica de “blub space”, a métrica efetiva ali parece ser ds^2 = dz^2 + (z+1)^2 dx^2. É uma coordenada polar com z=1/y como nível de zoom, e usando dz=dy/y^2 temos ds^2 = dy^2/y^4 + dx^2*(1/y^2 + ...). Ou seja, a implementação existente parece gastar muito mais tempo fazendo pan em níveis altos de zoom do que o modelo hiperbólico. Isso porque o custo de reduzir de 4x para 2x é o dobro do de reduzir de 2x para 1x, embora visualmente seja o mesmo

    • Ao testar de fato, foi bem diferente do que eu esperava, e deu muito mais zoom out. Foi porque deixei passar parte do código de zoom
      O zoom deles não é um fator de escala, mas corresponde ao meu “y”, então a métrica é ds^2 = dy^2 + (C-y)^2 dx^2. Aqui, C é um pouco maior que o nível máximo de zoom. Também há tratamento especial para quando essa curva tenta dar ainda mais zoom out
      Normalizando para que o custo de fazer pan até o fim no estado totalmente afastado (zoom=1) seja igual, em um nível de zoom muito profundo o custo de pan fica quase plano, enquanto em um nível relativamente menos ampliado o custo é maior que no modelo hiperbólico. Por isso, em distâncias curtas, o viewport parece se mover muito rápido, e em distâncias longas ele parece dar zoom out quase até o fim. Como o nível intermediário de zoom é desfavorável, acaba sendo melhor ir quase até o final