diff --git a/README.md b/README.md index 6bba287..d100b27 100644 --- a/README.md +++ b/README.md @@ -4,90 +4,73 @@ ## Traceable Generative Markdown for PDFs -Gemini OCR is a library designed to convert PDF documents into clean, semantic Markdown while maintaining precise traceability back to the source coordinates. It bridges the gap between the readability of Generative AI (Gemini, Document AI Chunking) and the grounded accuracy of traditional OCR (Google Document AI). +`gemini-ocr` provides [anchorite](https://github.com/folded/anchorite) provider +plugins that convert PDFs to traceable Markdown using Google Cloud APIs. -## Key Features - -- **Generative Markdown**: Uses Google's Gemini Pro or Document AI Layout models to generate human-readable Markdown with proper structure (headers, tables, lists). -- **Precision Traceability**: Aligns the generated Markdown text back to the original PDF coordinates using detailed OCR data from Google Document AI. -- **Reverse-Alignment Algorithm**: Implements a robust "reverse-alignment" strategy that starts with the readable text and finds the corresponding bounding boxes, ensuring the Markdown is the ground truth for content. -- **Confidence Metrics**: (New) Includes coverage metrics to quantify how much of the Markdown content is successfully backed by OCR data. -- **Pagination Support**: Automatically handles PDF page splitting and merging logic. - -## Architecture - -The library processes documents in two parallel streams: - -1. **Semantic Stream**: The PDF is sent to a Generative AI model (e.g., Gemini 2.5 Flash) to produce a clean Markdown representation. -2. **Positional Stream**: The PDF is sent to Google Document AI to extract raw bounding boxes and text segments. - -These two streams are then merged using a custom alignment engine (`seq_smith` + `bbox_alignment.py`) which: - -1. Normalizes both text sources. -2. Identifies "anchor" comparisons for reliable alignment. -3. Computes a global alignment using the anchors to constrain the search space. -4. Identifies significant gaps or mismatches. -5. Recursively re-aligns mismatched regions until a high-quality alignment is achieved. - -**Key Features:** - -- **Robust to Cleanliness Issues:** Handles extra headers/footers, watermarks, and noisy OCR artifacts. -- **Scale-Invariant:** Recursion ensures even small missed sections in large documents are recovered. +- **`GeminiMarkdownProvider`** — generates Markdown via the Gemini API +- **`DocAIMarkdownProvider`** — generates Markdown via Document AI Layout +- **`DocAIAnchorProvider`** — extracts bounding boxes via Document AI OCR +- **`DoclingMarkdownProvider`** — generates Markdown via Docling (stub) ## Quick Start ```python import asyncio from pathlib import Path -from gemini_ocr import gemini_ocr, settings + +import anchorite +from gemini_ocr import DocAIAnchorProvider, GeminiMarkdownProvider async def main(): - # Configure settings - ocr_settings = settings.Settings( - project="my-gcp-project", - location="us", - gcp_project_id="my-gcp-project", - layout_processor_id="projects/.../processors/...", - ocr_processor_id="projects/.../processors/...", - mode=settings.OcrMode.GEMINI, + markdown_provider = GeminiMarkdownProvider( + project_id="my-gcp-project", + location="us-central1", + model_name="gemini-2.5-flash", + ) + anchor_provider = DocAIAnchorProvider( + project_id="my-gcp-project", + location="us-central1", + processor_id="projects/.../processors/...", ) - file_path = Path("path/to/document.pdf") + chunks = anchorite.document.chunks(Path("document.pdf")) + result = await anchorite.process_document( + chunks, markdown_provider, anchor_provider, renumber=True + ) - # Process the document - result = await gemini_ocr.process_document(ocr_settings, file_path) + print(result.markdown_content) + print(result.annotate()) # Markdown with inline tags - # Access results - print(f"Coverage: {result.coverage_percent:.2%}") +asyncio.run(main()) +``` - # Get annotated HTML-compatible Markdown - annotated_md = result.annotate() - print(annotated_md[:500]) # View first 500 chars +## Configuration via Environment Variables + +`from_env()` builds providers from environment variables, useful for +twelve-factor deployments: + +```python +import anchorite +from gemini_ocr import from_env -if __name__ == "__main__": - asyncio.run(main()) +markdown_provider, anchor_provider = from_env() +chunks = anchorite.document.chunks(Path("document.pdf")) +result = await anchorite.process_document(chunks, markdown_provider, anchor_provider) ``` -## Configuration - -The `gemini_ocr.settings.Settings` class controls the behavior: - -| Parameter | Type | Description | -| :------------------------------- | :-------- | :--------------------------------------------------------------- | -| `project` | `str` | GCP Project Name | -| `location` | `str` | GCP Location (e.g., `us`, `eu`) | -| `gcp_project_id` | `str` | GCP Project ID (might be same as `project`) | -| `layout_processor_id` | `str` | Document AI Processor ID for Layout (if using `DOCUMENTAI` mode) | -| `ocr_processor_id` | `str` | Document AI Processor ID for OCR (required for bounding boxes) | -| `mode` | `OcrMode` | `GEMINI` (default), `DOCUMENTAI`, or `DOCLING` | -| `gemini_model_name` | `str` | Gemini model to use (default: `gemini-2.5-flash`) | -| `alignment_uniqueness_threshold` | `float` | Min score ratio for unique match (default: `0.5`) | -| `alignment_min_overlap` | `float` | Min overlap fraction for valid match (default: `0.9`) | -| `include_bboxes` | `bool` | Whether to perform alignment (default: `True`) | -| `markdown_page_batch_size` | `int` | Pages per batch for Markdown generation (default: `10`) | -| `ocr_page_batch_size` | `int` | Pages per batch for OCR (default: `10`) | -| `num_jobs` | `int` | Max concurrent jobs (default: `10`) | -| `cache_dir` | `str` | Directory to store API response cache (default: `.docai_cache`) | +| Variable | Description | +| :-------------------------------- | :--------------------------------------------------------------- | +| `GEMINI_OCR_PROJECT_ID` | GCP project ID (required) | +| `GEMINI_OCR_LOCATION` | GCP location (default: `us-central1`) | +| `GEMINI_OCR_MODE` | `gemini` (default), `documentai`, or `docling` | +| `GEMINI_OCR_GEMINI_MODEL_NAME` | Gemini model name (required in `gemini` mode) | +| `GEMINI_OCR_LAYOUT_PROCESSOR_ID` | Document AI processor ID (required in `documentai` mode) | +| `GEMINI_OCR_OCR_PROCESSOR_ID` | Document AI OCR processor ID (enables bounding box extraction) | +| `GEMINI_OCR_DOCUMENTAI_LOCATION` | Document AI endpoint location override | +| `GEMINI_OCR_QUOTA_PROJECT_ID` | Quota project override for Gemini API calls | +| `GEMINI_OCR_GEMINI_PROMPT` | Additional prompt appended to the default Gemini prompt | +| `GEMINI_OCR_CACHE_DIR` | Directory for caching API responses | +| `GEMINI_OCR_INCLUDE_BBOXES` | Set to `false` to skip bounding box extraction (default: `true`) | ## License diff --git a/docs/source/api.rst b/docs/source/api.rst index f8a141b..1c3aa2a 100644 --- a/docs/source/api.rst +++ b/docs/source/api.rst @@ -6,22 +6,22 @@ API Reference :undoc-members: :show-inheritance: -.. automodule:: gemini_ocr.gemini_ocr +.. automodule:: gemini_ocr.gemini :members: :undoc-members: :show-inheritance: -.. automodule:: gemini_ocr.settings +.. automodule:: gemini_ocr.docai_layout :members: :undoc-members: :show-inheritance: -.. automodule:: gemini_ocr.document +.. automodule:: gemini_ocr.docai_ocr :members: :undoc-members: :show-inheritance: -.. automodule:: gemini_ocr.bbox_alignment +.. automodule:: gemini_ocr.gemini_ocr :members: :undoc-members: :show-inheritance: diff --git a/pyproject.toml b/pyproject.toml index 8cd0599..b7ee7a1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "gemini-ocr" -version = "0.4.0" +version = "0.5.0" authors = [ { name = "Tobias Sargeant", email = "tobias.sargeant@gmail.com" }, ] @@ -24,12 +24,13 @@ dependencies = [ "google-genai", "google-cloud-documentai", "pymupdf", - "seq-smith>=0.5.1", "python-dotenv>=1.2.1", "fsspec", "gcsfs", + "anchorite==0.2.0", ] + [dependency-groups] dev = [ "pytest", @@ -50,6 +51,7 @@ indent-width = 4 select = ["A", "B", "C", "E", "F", "G", "I", "N", "Q", "S", "W", "ANN", "ARG", "BLE", "COM", "DJ", "DTZ", "ERA", "EXE", "ICN", "ISC", "NPY", "PD", "PGH", "PIE", "PL", "PT", "PYI", "RET", "RSE", "RUF", "SIM", "SLF", "TCH", "TID", "UP", "YTT"] ignore = [ "ANN101", # missing-type-self + "COM812", # conflicts with ruff format "PD011", # pandas-use-of-dot-values (false positive) ] fixable = ["A", "B", "C", "D", "E", "F", "G", "I", "N", "Q", "S", "T", "W", "ANN", "ARG", "BLE", "COM", "DJ", "DTZ", "ERA", "EXE", "FBT", "ICN", "ISC", "NPY", "PD", "PGH", "PIE", "PL", "PT", "PYI", "RET", "RSE", "RUF", "SIM", "SLF", "TCH", "TID", "UP", "YTT"] diff --git a/run_ocr.py b/run_ocr.py index 3934fd3..d6a744d 100644 --- a/run_ocr.py +++ b/run_ocr.py @@ -6,11 +6,12 @@ import sys import traceback +import anchorite import dotenv import google.auth from google import genai -from gemini_ocr import gemini_ocr, settings +from gemini_ocr import DocAIAnchorProvider, DocAIMarkdownProvider, GeminiMarkdownProvider def _list_models(project: str | None, location: str, quota_project: str | None) -> None: @@ -66,14 +67,18 @@ async def main() -> None: parser.add_argument( "--ocr-processor-id", default=os.environ.get("DOCUMENTAI_OCR_PROCESSOR_ID"), - help="Document AI OCR Processor ID (for secondary bbox pass)", + help="Document AI OCR Processor ID (for bounding box extraction)", ) parser.add_argument( "--model", default=os.environ.get("GEMINI_OCR_GEMINI_MODEL_NAME"), - help="Gemini Model Name (e.g. gemini-2.0-flash-exp)", + help="Gemini Model Name (e.g. gemini-2.0-flash)", + ) + parser.add_argument( + "--gemini-prompt", + default=None, + help="Additional instructions to append to the default Gemini prompt.", ) - parser.add_argument( "--output", type=pathlib.Path, @@ -91,13 +96,11 @@ async def main() -> None: default="gemini", help="OCR generation mode", ) - parser.add_argument( "--list-models", action="store_true", help="List available Gemini models and exit", ) - parser.add_argument( "--no-bbox", action="store_true", @@ -117,34 +120,49 @@ async def main() -> None: print("Error: --project or GOOGLE_CLOUD_PROJECT env var required.") sys.exit(1) - if not args.processor_id: - print("Error: --processor-id or DOCUMENTAI_LAYOUT_PARSER_PROCESSOR_ID env var required.") - sys.exit(1) - - ocr_settings = settings.Settings( - project_id=args.project, - location=args.location, - quota_project_id=args.quota_project, - layout_processor_id=args.processor_id, - ocr_processor_id=args.ocr_processor_id, - gemini_model_name=args.model, - mode=args.mode, - include_bboxes=not args.no_bbox, - cache_dir=args.cache_dir, - ) + cache_dir = str(args.cache_dir) if args.cache_dir else None + + if args.mode == "gemini": + if not args.model: + print("Error: --model or GEMINI_OCR_GEMINI_MODEL_NAME required in gemini mode.") + sys.exit(1) + markdown_provider: anchorite.providers.MarkdownProvider = GeminiMarkdownProvider( + project_id=args.project, + location=args.location, + model_name=args.model, + quota_project_id=args.quota_project, + prompt=args.gemini_prompt, + cache_dir=cache_dir, + ) + else: + if not args.processor_id: + print("Error: --processor-id required in documentai mode.") + sys.exit(1) + markdown_provider = DocAIMarkdownProvider( + project_id=args.project, + location=args.location, + processor_id=args.processor_id, + cache_dir=cache_dir, + ) + + anchor_provider: anchorite.providers.AnchorProvider | None = None + if not args.no_bbox and args.ocr_processor_id: + anchor_provider = DocAIAnchorProvider( + project_id=args.project, + location=args.location, + processor_id=args.ocr_processor_id, + cache_dir=cache_dir, + ) print(f"Processing {args.input_pdf}...") - print(f"Settings: {ocr_settings}") try: - result = await gemini_ocr.process_document(args.input_pdf, settings=ocr_settings) - - output_content = result.annotate() if ocr_settings.include_bboxes else result.markdown_content - - output_path = args.output - output_path.write_text(output_content) + chunks = anchorite.document.chunks(args.input_pdf) + result = await anchorite.process_document(chunks, markdown_provider, anchor_provider, renumber=True) - print(f"Done! Output saved to {output_path}") + output_content = result.annotate() if anchor_provider else result.markdown_content + args.output.write_text(output_content) + print(f"Done! Output saved to {args.output}") except Exception as e: # noqa: BLE001 print(f"Error processing document: {e}") diff --git a/scripts/capture_docai_fixtures.py b/scripts/capture_docai_fixtures.py index 187e8be..ca82031 100644 --- a/scripts/capture_docai_fixtures.py +++ b/scripts/capture_docai_fixtures.py @@ -1,16 +1,21 @@ +"""Capture Document AI layout fixtures for regression tests.""" + import asyncio import json import os +import sys from pathlib import Path +import anchorite import dotenv from google.cloud import documentai -from gemini_ocr import docai, document, settings +sys.path.insert(0, str(Path.cwd() / "src")) + +from gemini_ocr import docai async def capture() -> None: - # Load .env dotenv.load_dotenv() mapping = { @@ -20,47 +25,43 @@ async def capture() -> None: "GOOGLE_OCR_LOCATION": "GEMINI_OCR_LOCATION", } for src, dst in mapping.items(): - if os.getenv(src) and not os.getenv(dst): - os.environ[dst] = os.getenv(src) + val = os.getenv(src) + if val and not os.getenv(dst): + os.environ[dst] = val + + project_id = os.environ["GEMINI_OCR_PROJECT_ID"] + location = os.getenv("GEMINI_OCR_LOCATION", "us-central1") + layout_processor_id = os.environ["GEMINI_OCR_LAYOUT_PROCESSOR_ID"] + documentai_location = os.getenv("GEMINI_OCR_DOCUMENTAI_LOCATION") + cache_dir = os.getenv("GEMINI_OCR_CACHE_DIR") + + process_options = documentai.ProcessOptions( + layout_config=documentai.ProcessOptions.LayoutConfig( + return_bounding_boxes=True, + ), + ) pdf_path = Path("tests/data/hubble-1929.pdf") + chunks = list(anchorite.document.chunks(pdf_path)) - ocr_settings = settings.Settings.from_env() - ocr_settings.mode = settings.OcrMode.DOCUMENTAI - - print(f"Processing with settings: {ocr_settings}") - - chunks = list(document.chunks(pdf_path, page_count=ocr_settings.markdown_page_batch_size)) + print(f"Processing {pdf_path} ({len(chunks)} chunks) with Document AI Layout...") documents = [] for i, chunk in enumerate(chunks): print(f"Calling DocAI Layout for chunk {i}...") - - # docai.process returns documentai.Document - # We need the processor ID. - if ocr_settings.layout_processor_id is None: - raise ValueError("Layout processor ID required") - - process_options = documentai.ProcessOptions( - layout_config=documentai.ProcessOptions.LayoutConfig( - return_bounding_boxes=True, - ), + doc = await docai.process( + project_id, + location, + layout_processor_id, + process_options, + chunk, + documentai_location=documentai_location, + cache_dir=cache_dir, ) + documents.append(type(doc).to_json(doc)) - doc = await docai.process(ocr_settings, process_options, ocr_settings.layout_processor_id, chunk) - - # Serialize to JSON using protojson (built-in to the class usually or via library) - # documentai.Document is a proto-plus wrapper. verify .to_json() or similar. - # Actually Google Proto objects usually have ._pb methods or we can use type(doc).to_json(doc) - - # Let's try standard serialization - json_str = type(doc).to_json(doc) - documents.append(json_str) - - # Save list of JSON strings with open("tests/fixtures/hubble_docai_layout_responses.json", "w") as f: json.dump(documents, f) - print("Saved DocAI layout responses.") diff --git a/scripts/capture_fixtures.py b/scripts/capture_fixtures.py index d67e49f..9176d7f 100644 --- a/scripts/capture_fixtures.py +++ b/scripts/capture_fixtures.py @@ -1,34 +1,23 @@ +"""Capture Gemini markdown and DocAI bounding-box fixtures for regression tests.""" + import asyncio import json import os -import pickle - -# Add src to path so we can import gemini_ocr modules import sys from pathlib import Path +import anchorite import dotenv -sys.path.append(str(Path.cwd() / "src")) - -import typing +sys.path.insert(0, str(Path.cwd() / "src")) -from gemini_ocr import docai_ocr, document, gemini, settings - -# For serializing BBox +from gemini_ocr import DocAIAnchorProvider, GeminiMarkdownProvider async def capture() -> None: - # Load .env dotenv.load_dotenv() - # ... (skipping some unchanged lines in between) ... - - # Re-running DocAI OCR (bboxes) is safer. - # Note: process_document uses batched gather. - # We'll reproduce logic from extract_raw_data roughly but per chunk. - - # Map GOOGLE_OCR_ vars to GEMINI_OCR_ vars if needed + # Support legacy env-var names used in CI/local setups mapping = { "GOOGLE_OCR_PROJECT": "GEMINI_OCR_PROJECT_ID", "GOOGLE_OCR_LAYOUT_PARSER_PROCESSOR_ID": "GEMINI_OCR_LAYOUT_PROCESSOR_ID", @@ -40,40 +29,50 @@ async def capture() -> None: if val and not os.getenv(dst): os.environ[dst] = val - pdf_path = Path("tests/data/hubble-1929.pdf") - - ocr_settings = settings.Settings.from_env() - # Ensure Gemini mode - ocr_settings.mode = settings.OcrMode.GEMINI + project_id = os.environ["GEMINI_OCR_PROJECT_ID"] + location = os.getenv("GEMINI_OCR_LOCATION", "us-central1") + model_name = os.environ["GEMINI_OCR_GEMINI_MODEL_NAME"] + ocr_processor_id = os.environ["GEMINI_OCR_OCR_PROCESSOR_ID"] + cache_dir = os.getenv("GEMINI_OCR_CACHE_DIR") + + markdown_provider = GeminiMarkdownProvider( + project_id=project_id, + location=location, + model_name=model_name, + cache_dir=cache_dir, + ) + anchor_provider = DocAIAnchorProvider( + project_id=project_id, + location=location, + processor_id=ocr_processor_id, + cache_dir=cache_dir, + ) - print(f"Processing {pdf_path}...") - print(f"Settings: {ocr_settings}") + pdf_path = Path("tests/data/hubble-1929.pdf") + chunks = list(anchorite.document.chunks(pdf_path)) - chunks = list(document.chunks(pdf_path, page_count=ocr_settings.markdown_page_batch_size)) + print(f"Processing {pdf_path} ({len(chunks)} chunks)...") - # 1. Capture Gemini Markdown Responses gemini_responses = [] for i, chunk in enumerate(chunks): print(f"Generating Gemini markdown for chunk {i}...") - text = await gemini.generate_markdown(ocr_settings, chunk) + text = await markdown_provider.generate_markdown(chunk) gemini_responses.append(text) with open("tests/fixtures/hubble_gemini_responses.json", "w") as f: json.dump(gemini_responses, f) print("Saved Gemini responses.") - print("Generating DocAI BBoxes...") - - all_chunks_bboxes: list[typing.Any] = [] - + all_chunks_bboxes = [] for i, chunk in enumerate(chunks): print(f"Generating DocAI bboxes for chunk {i}...") - bboxes = await docai_ocr.generate_bounding_boxes(ocr_settings, chunk) - all_chunks_bboxes.append(bboxes) - - with open("tests/fixtures/hubble_docai_bboxes.pkl", "wb") as f: - pickle.dump(all_chunks_bboxes, f) + anchors = await anchor_provider.generate_anchors(chunk) + all_chunks_bboxes.append( + [{"text": a.text, "page": a.page, "boxes": [b._asdict() for b in a.boxes]} for a in anchors], + ) + with open("tests/fixtures/hubble_docai_bboxes.json", "w") as f: + json.dump(all_chunks_bboxes, f) print("Saved DocAI bboxes.") diff --git a/src/gemini_ocr/__init__.py b/src/gemini_ocr/__init__.py index 15c866b..9d506e8 100644 --- a/src/gemini_ocr/__init__.py +++ b/src/gemini_ocr/__init__.py @@ -1,4 +1,13 @@ -from gemini_ocr.gemini_ocr import process_document -from gemini_ocr.settings import Settings +from gemini_ocr.docai_layout import DocAIMarkdownProvider +from gemini_ocr.docai_ocr import DocAIAnchorProvider +from gemini_ocr.docling import DoclingMarkdownProvider +from gemini_ocr.gemini import GeminiMarkdownProvider +from gemini_ocr.gemini_ocr import from_env -__all__ = ["Settings", "process_document"] +__all__ = [ + "DocAIAnchorProvider", + "DocAIMarkdownProvider", + "DoclingMarkdownProvider", + "GeminiMarkdownProvider", + "from_env", +] diff --git a/src/gemini_ocr/bbox_alignment.py b/src/gemini_ocr/bbox_alignment.py deleted file mode 100644 index d309ee9..0000000 --- a/src/gemini_ocr/bbox_alignment.py +++ /dev/null @@ -1,396 +0,0 @@ -import collections -import dataclasses -import logging -import re -import string -from collections.abc import Iterator, Sequence - -import seq_smith - -from gemini_ocr import document, range_ops - -_ALIGN_ALPHABET = string.ascii_lowercase + string.digits + " " -_NON_WORD_CHARS = seq_smith.encode(" ", _ALIGN_ALPHABET) -_GAP_OPEN, _GAP_EXTEND = -2, -2 -_SCORE_MATRIX = seq_smith.make_score_matrix(_ALIGN_ALPHABET, +1, -1) -_UNIQUENESS_THRESHOLD = 0.5 -_MIN_OVERLAP = 0.9 - - -@dataclasses.dataclass(frozen=True) -class _NormalizedSpan: - """A span of text with its normalized form and mapping.""" - - source: str - """The original source text.""" - normalized: bytes - """The normalized byte sequence used for alignment.""" - normalized_to_source: tuple[int, ...] - """Mapping from normalized indices to source indices.""" - - def __len__(self) -> int: - return len(self.normalized) - - def _trim(self) -> None: - normalized = self.normalized.lstrip(_NON_WORD_CHARS) - left_trimmed = len(self.normalized) - len(normalized) - normalized = normalized.rstrip(_NON_WORD_CHARS) - right_trimmed = len(self.normalized) - len(normalized) - left_trimmed - if right_trimmed == 0: - normalized_to_source = self.normalized_to_source[left_trimmed:] - else: - normalized_to_source = self.normalized_to_source[left_trimmed:-right_trimmed] - object.__setattr__(self, "normalized", normalized) - object.__setattr__(self, "normalized_to_source", normalized_to_source) - - def __post_init__(self) -> None: - self._trim() - - -@dataclasses.dataclass(frozen=True) -class _BBoxFragment(_NormalizedSpan): - """A normalized span derived from a bounding box.""" - - bbox: document.BoundingBox - """The original bounding box object.""" - - -@dataclasses.dataclass(frozen=True) -class _DocumentFragment(_NormalizedSpan): - """A normalized span derived from the markdown document.""" - - page_range: tuple[int, int] - """Range of pages (start, end) this fragment might span.""" - - -def _normalize(source: str, span: tuple[int, int] = (-1, -1)) -> tuple[bytes, tuple[int, ...]]: - if span == (-1, -1): - span = (0, len(source)) - - def _normalize_char(c: str) -> str: - if c.lower() in string.ascii_letters + string.digits: - return c.lower() - return " " - - s, e = span - normalized: list[str] = [] - normalized_to_source: list[int] = [] - - for i in range(s, e): - n = _normalize_char(source[i]) - if n == " " and normalized and normalized[-1] == " ": - continue - normalized.append(n) - normalized_to_source.append(i) - - normalized_to_source.append(e) - - span_bytes = seq_smith.encode("".join(normalized), _ALIGN_ALPHABET) - return span_bytes, tuple(normalized_to_source) - - -def _make_bbox_fragment(bbox: document.BoundingBox) -> _BBoxFragment: - span_bytes, normalized_to_source = _normalize(bbox.text) - return _BBoxFragment(bbox.text, span_bytes, normalized_to_source, bbox) - - -def _make_document_fragment( - source: str, - page_range: tuple[int, int], - span: tuple[int, int] = (-1, -1), -) -> _DocumentFragment: - span_bytes, normalized_to_source = _normalize(source, span) - return _DocumentFragment(source, span_bytes, normalized_to_source, page_range) - - -def _a_end(f: seq_smith.AlignmentFragment) -> int: - return f.sa_start if f.fragment_type == seq_smith.FragmentType.AGap else f.sa_start + f.len - - -def _b_end(f: seq_smith.AlignmentFragment) -> int: - return f.sb_start if f.fragment_type == seq_smith.FragmentType.BGap else f.sb_start + f.len - - -def _aligned_range(alignment: seq_smith.Alignment) -> tuple[int, int]: - return (alignment.fragments[0].sa_start, _a_end(alignment.fragments[-1])) - - -def _make_document_fragments(markdown_content: str, page_range: tuple[int, int]) -> Iterator[_DocumentFragment]: - start = 0 - for m in re.finditer(r"", markdown_content): - span = _make_document_fragment(markdown_content, page_range, (start, m.start())) - if len(span): - yield span - start = m.end() - if start < len(markdown_content): - span = _make_document_fragment(markdown_content, page_range, (start, len(markdown_content))) - if len(span): - yield span - - -def _slice_document_fragment( - span: _DocumentFragment, - start: int, - end: int, - page_range: tuple[int, int], -) -> _DocumentFragment: - if start >= end: - return _DocumentFragment(span.source, b"", (), page_range) - - n_sub = span.normalized[start:end] - # normalized_to_source has length len(normalized) + 1. - # slice it from start to end + 1 to include the end boundary - nts_sub = span.normalized_to_source[start : end + 1] - - return _DocumentFragment(span.source, n_sub, nts_sub, page_range) - - -def _compute_candidate_alignments( - document_fragments: list[_DocumentFragment], - bbox_spans: list[_BBoxFragment], - with_ungapped: bool, -) -> dict[_BBoxFragment, list[tuple[int, seq_smith.Alignment]]]: - bbox_span_hsps: dict[_BBoxFragment, list[tuple[int, seq_smith.Alignment]]] = collections.defaultdict(list) - - for i, fragment in enumerate(document_fragments): - spans = [b for b in bbox_spans if range_ops.in_range(b.bbox.page, fragment.page_range)] - if not spans: - continue - if with_ungapped: - alignments = seq_smith.top_k_ungapped_local_align_many( - fragment.normalized, - [s.normalized for s in spans], - _SCORE_MATRIX, - k=2, - filter_overlap_a=False, - filter_overlap_b=False, - ) - for s, a in zip(spans, alignments, strict=True): - bbox_span_hsps[s].extend((i, hsp) for hsp in a) - else: - alignments = seq_smith.local_global_align_many( - fragment.normalized, - [s.normalized for s in spans], - _SCORE_MATRIX, - _GAP_OPEN, - _GAP_EXTEND, - ) - for s, a in zip(spans, alignments, strict=True): - bbox_span_hsps[s].append((i, a)) - return bbox_span_hsps - - -def _assign_high_confidence_spans( - document_fragments: list[_DocumentFragment], - bbox_spans: Sequence[_BBoxFragment], - uniqueness_threshold: float = _UNIQUENESS_THRESHOLD, - min_overlap: float = _MIN_OVERLAP, - with_ungapped: bool = True, -) -> Iterator[tuple[_DocumentFragment, list[_BBoxFragment]]]: - bbox_spans_list = list(bbox_spans) - bbox_span_hsps = _compute_candidate_alignments(document_fragments, bbox_spans_list, with_ungapped) - - assignments = collections.defaultdict(list) - for bbox_span in bbox_spans_list: - bbox_hsps = sorted( - bbox_span_hsps[bbox_span], - key=lambda x: x[1].score, - reverse=True, - ) - - if not bbox_hsps: - continue - - span_idx, hsp = bbox_hsps[0] - if len(bbox_hsps) > 1 and bbox_hsps[1][1].score >= hsp.score * uniqueness_threshold: - continue - if hsp.stats.len >= min_overlap * len(bbox_span): - assignments[span_idx].append((bbox_span, hsp)) - - for i, assigned_bbox_spans in sorted(assignments.items()): - assigned_bbox_spans.sort(key=lambda x: x[1].score, reverse=True) - yield document_fragments[i], [x[0] for x in assigned_bbox_spans] - - -def _page_range_for_range( - page_range: tuple[int, int], - span_range: tuple[int, int], - page_ranges: dict[int, tuple[int, int]], -) -> tuple[int, int]: - """Return the page range for the span range.""" - page_start, page_end = page_range - - for page, r in page_ranges.items(): - if r[0] <= span_range[0]: - page_start = max(page_start, page) - if r[1] >= span_range[1]: - page_end = min(page_end, page + 1) - return page_start, page_end - - -def _is_consistent_with_page_ranges( - r: tuple[int, int], - page: int, - page_ranges: dict[int, Sequence[tuple[int, int]]], -) -> bool: - """Return true if range `r` inferred from a span on page `page` is consistent with `page_ranges`.""" - pre_ranges = [pr for p, pr in page_ranges.items() if p < page] - post_ranges = [pr for p, pr in page_ranges.items() if p > page] - lower_bound = max(pr[-1][1] for pr in pre_ranges) if pre_ranges else r[0] - upper_bound = min(pr[0][0] for pr in post_ranges) if post_ranges else r[1] - return range_ops.contained(r, (lower_bound, upper_bound)) - - -def _assign_spans( - document_fragment: _DocumentFragment, - candidates: list[_BBoxFragment], - match_fraction: float = 0.9, - new_coverage_fraction: float = 0.9, -) -> tuple[list[tuple[_BBoxFragment, tuple[int, int]]], list[_DocumentFragment]]: - candidates = [c for c in candidates if range_ops.in_range(c.bbox.page, document_fragment.page_range)] - - alignments = seq_smith.local_global_align_many( - document_fragment.normalized, - [c.normalized for c in candidates], - _SCORE_MATRIX, - _GAP_OPEN, - _GAP_EXTEND, - ) - - candidates_alignments = sorted(zip(candidates, alignments, strict=True), key=lambda x: x[1].score, reverse=True) - candidates = [x[0] for x in candidates_alignments] - alignments = [x[1] for x in candidates_alignments] - - assignments = [] - covered: list[tuple[int, int]] = [] - - page_ranges: dict[int, Sequence[tuple[int, int]]] = {} - - for candidate, alignment in zip(candidates, alignments, strict=True): - if alignment.stats.num_exact_matches < len(candidate) * match_fraction: - continue - r = _aligned_range(alignment) - if not _is_consistent_with_page_ranges(r, candidate.bbox.page, page_ranges): - continue - r_uncovered = range_ops.subtract_ranges([r], covered) - uncovered_chars = sum(r[1] - r[0] for r in r_uncovered) - if uncovered_chars < len(candidate) * new_coverage_fraction: - continue - - page_ranges[candidate.bbox.page] = range_ops.union_ranges(r_uncovered, page_ranges.get(candidate.bbox.page, [])) - - covered = range_ops.union_ranges(r_uncovered, covered) - - doc_start = document_fragment.normalized_to_source[r_uncovered[0][0]] - doc_end = document_fragment.normalized_to_source[r_uncovered[-1][1]] - - assignments.append((candidate, (doc_start, doc_end))) - - page_spans = {page: (r[0][0], r[-1][1]) for page, r in sorted(page_ranges.items())} - - new_spans = [] - uncovered = range_ops.subtract_ranges([(0, len(document_fragment.normalized))], covered) - - for start, end in uncovered: - page_range = _page_range_for_range(document_fragment.page_range, (start, end), page_spans) - frag = _slice_document_fragment(document_fragment, start, end, page_range) - if len(frag): - new_spans.append(frag) - - return assignments, new_spans - - -def _process_alignment_iteration( - iteration_num: int, - spans: list[_DocumentFragment], - bbox_spans: set[_BBoxFragment], - uniqueness_threshold: float, - min_overlap: float = 0.9, -) -> tuple[list[_DocumentFragment], list[tuple[_BBoxFragment, tuple[int, int]]]]: - logging.debug("--- Iteration %d (Threshold: %f, Overlap: %f) ---", iteration_num, uniqueness_threshold, min_overlap) - logging.debug("%d spans, %d bbox spans.", len(spans), len(bbox_spans)) - - candidates = list( - _assign_high_confidence_spans( - spans, - sorted(bbox_spans, key=lambda x: (x.bbox.page, x.bbox.rect.top, x.bbox.rect.left, x.bbox.text)), - uniqueness_threshold=uniqueness_threshold, - min_overlap=min_overlap, - with_ungapped=iteration_num == 1, - ), - ) - - if not candidates: - return spans, [] - - new_spans = [] - all_assigned_ranges_in_iteration = [] - matched_span_ids = set() - - for doc_span, bbox_candidates in candidates: - matched_span_ids.add(id(doc_span)) - assignments, holes = _assign_spans(doc_span, bbox_candidates) - - if assignments: - all_assigned_ranges_in_iteration.extend(assignments) - - new_spans.extend(holes) - - # Keep spans that weren't involved in any match - for s in spans: - if id(s) not in matched_span_ids: - new_spans.append(s) - - logging.debug("Assigned in this iteration: %s", bool(all_assigned_ranges_in_iteration)) - logging.debug("Remaining bboxes: %d", len(bbox_spans) - len({bbox for bbox, _ in all_assigned_ranges_in_iteration})) - logging.debug("New span count (holes + unvisited): %d", len(new_spans)) - return new_spans, all_assigned_ranges_in_iteration - - -def create_annotated_markdown( - markdown_content: str, - bounding_boxes: list[document.BoundingBox], - uniqueness_threshold: float = _UNIQUENESS_THRESHOLD, - min_overlap: float = _MIN_OVERLAP, -) -> dict[document.BoundingBox, tuple[int, int]]: - """Merges OCR bounding boxes into the markdown content.""" - - # Create initial spans (just the full content) - bbox_spans: set[_BBoxFragment] = {span for span in [_make_bbox_fragment(b) for b in bounding_boxes] if len(span)} - if not bbox_spans: - return {} - - max_page = max(b.page for b in bounding_boxes) - spans = list(_make_document_fragments(markdown_content, (0, max_page + 1))) - - logging.debug("initial span count %d; initial bbox count %d", len(spans), len(bbox_spans)) - - iteration = 0 - all_assigned_ranges: list[tuple[int, tuple[_BBoxFragment, tuple[int, int]]]] = [] - - while True: - iteration += 1 - spans, assigned_ranges = _process_alignment_iteration( - iteration, - spans, - bbox_spans, - uniqueness_threshold, - min_overlap, - ) - if assigned_ranges: - all_assigned_ranges.extend((iteration, s) for s in assigned_ranges) - - # Update bbox_spans to remove assigned ones for next iteration - assigned_set = {bbox for bbox, _ in assigned_ranges} - bbox_spans = {b for b in bbox_spans if b not in assigned_set} - - if not bbox_spans or (iteration > 1 and not assigned_ranges): - break - - logging.debug("Final remaining bbox count %d assigned count %d", len(bbox_spans), len(all_assigned_ranges)) - - # Apply replacements for debugging - # Sort ranges by start index descending to apply safely - all_assigned_ranges.sort(key=lambda x: x[1][1][0], reverse=True) - - return {bbox_span.bbox: (start, end) for iteration, (bbox_span, (start, end)) in all_assigned_ranges} diff --git a/src/gemini_ocr/docai.py b/src/gemini_ocr/docai.py index 0d65603..f7a4990 100644 --- a/src/gemini_ocr/docai.py +++ b/src/gemini_ocr/docai.py @@ -4,66 +4,79 @@ import pathlib import typing +import anchorite from google.api_core import client_options from google.cloud import documentai -from gemini_ocr import document, settings +def _resolve_documentai_location(location: str, documentai_location: str | None) -> str: + if documentai_location is not None: + return documentai_location + return "eu" if location.startswith("eu") else "us" -def _call_docai( - ocr_settings: settings.Settings, - process_options: documentai.ProcessOptions, + +def _call_docai( # noqa: PLR0913 + project_id: str, + location: str, + documentai_location: str | None, processor_id: str, - chunk: document.DocumentChunk, + process_options: documentai.ProcessOptions, + chunk: anchorite.document.DocumentChunk, ) -> documentai.Document: - location = ocr_settings.get_documentai_location() - + resolved_location = _resolve_documentai_location(location, documentai_location) client = documentai.DocumentProcessorServiceClient( - client_options=client_options.ClientOptions(api_endpoint=f"{location}-documentai.googleapis.com"), + client_options=client_options.ClientOptions(api_endpoint=f"{resolved_location}-documentai.googleapis.com"), ) - - name = client.processor_path(ocr_settings.project_id, location, processor_id) - + name = client.processor_path(project_id, resolved_location, processor_id) raw_document = documentai.RawDocument(content=chunk.data, mime_type=chunk.mime_type) request = documentai.ProcessRequest(name=name, raw_document=raw_document, process_options=process_options) - result = client.process_document(request=request) - return result.document + return client.process_document(request=request).document def _generate_cache_path( - ocr_settings: settings.Settings, - process_options: documentai.ProcessOptions, + cache_dir: str | None, + cache: bool, processor_id: str, - chunk: document.DocumentChunk, + process_options: documentai.ProcessOptions, + chunk: anchorite.document.DocumentChunk, ) -> pathlib.Path | None: - if not ocr_settings.cache_dir or not ocr_settings.cache_docai: + if not cache_dir or not cache: return None hasher = hashlib.sha256() hasher.update(documentai.ProcessOptions.to_json(process_options, sort_keys=True).encode()) hasher.update(processor_id.encode()) hasher.update(chunk.document_sha256.encode()) cache_key = f"{hasher.hexdigest()}_{chunk.start_page}_{chunk.end_page}" - - return pathlib.Path(ocr_settings.cache_dir) / "docai" / f"{cache_key}.json" + return pathlib.Path(cache_dir) / "docai" / f"{cache_key}.json" -async def process( - ocr_settings: settings.Settings, - process_options: documentai.ProcessOptions, +async def process( # noqa: PLR0913 + project_id: str, + location: str, processor_id: str, - chunk: document.DocumentChunk, + process_options: documentai.ProcessOptions, + chunk: anchorite.document.DocumentChunk, + *, + documentai_location: str | None = None, + cache_dir: str | None = None, + cache: bool = True, ) -> documentai.Document: - """Runs Document AI OCR.""" - - cache_path = _generate_cache_path(ocr_settings, process_options, processor_id, chunk) + cache_path = _generate_cache_path(cache_dir, cache, processor_id, process_options, chunk) if cache_path and cache_path.exists(): logging.debug("Loaded from DocAI cache: %s", cache_path) return typing.cast("documentai.Document", documentai.Document.from_json(cache_path.read_text())) - doc = await asyncio.to_thread(_call_docai, ocr_settings, process_options, processor_id, chunk) + doc = await asyncio.to_thread( + _call_docai, + project_id, + location, + documentai_location, + processor_id, + process_options, + chunk, + ) - # Save to Cache if cache_path: cache_path.parent.mkdir(parents=True, exist_ok=True) cache_path.write_text(documentai.Document.to_json(doc)) diff --git a/src/gemini_ocr/docai_layout.py b/src/gemini_ocr/docai_layout.py index a0c1ccd..559c03f 100644 --- a/src/gemini_ocr/docai_layout.py +++ b/src/gemini_ocr/docai_layout.py @@ -3,13 +3,14 @@ import textwrap from collections.abc import Generator, Sequence +import anchorite from google.cloud import documentai -from gemini_ocr import docai, document, settings +from gemini_ocr import docai @dataclasses.dataclass -class TableCell: +class _TableCell: """Represents a single cell within a table structure.""" content: str @@ -26,7 +27,7 @@ class TableCell: """True if this cell falls within a header row.""" -class LayoutProcessor: +class _LayoutProcessor: def process( self, blocks: Sequence[documentai.Document.DocumentLayout.DocumentLayoutBlock], @@ -95,7 +96,7 @@ def _process_table_block( def _build_table_grid( self, table_block: documentai.Document.DocumentLayout.DocumentLayoutBlock.LayoutTableBlock, - ) -> tuple[dict[tuple[int, int], TableCell], int, int]: + ) -> tuple[dict[tuple[int, int], _TableCell], int, int]: all_rows = [(r, True) for r in table_block.header_rows] + [(r, False) for r in table_block.body_rows] occupied = set() @@ -116,7 +117,7 @@ def _build_table_grid( row_span = max(1, cell.row_span) col_span = max(1, cell.col_span) - grid[(current_row_idx, current_col_idx)] = TableCell( + grid[(current_row_idx, current_col_idx)] = _TableCell( content=cell_text, row_pos=current_row_idx, col_pos=current_col_idx, @@ -138,7 +139,7 @@ def _build_table_grid( def _render_table( self, - grid: dict[tuple[int, int], TableCell], + grid: dict[tuple[int, int], _TableCell], num_rows: int, num_cols: int, has_header: bool, @@ -177,33 +178,31 @@ def _process_layout_blocks( raise ValueError(f"Unknown block type: {block}") -async def _run_document_ai(settings: settings.Settings, chunk: document.DocumentChunk) -> documentai.Document: - process_options = documentai.ProcessOptions( - layout_config=documentai.ProcessOptions.LayoutConfig( - return_bounding_boxes=True, - ), - ) - - if settings.layout_processor_id is None: - raise ValueError("Layout processor ID is not set") - - return await docai.process(settings, process_options, settings.layout_processor_id, chunk) - - -async def generate_markdown( - settings: settings.Settings, - chunk: document.DocumentChunk, -) -> str: - """Generates Markdown from a Document AI chunk. - - Args: - settings: OCR settings. - chunk: The document chunk (usually a single page or small range). - - Returns: - The generated Markdown string. - """ - doc_result = await _run_document_ai(settings, chunk) - processor = LayoutProcessor() - - return "".join(processor.process(doc_result.document_layout.blocks)) +@dataclasses.dataclass +class DocAIMarkdownProvider(anchorite.providers.MarkdownProvider): + """Markdown provider that generates markdown using Document AI layout.""" + + project_id: str + location: str + processor_id: str + documentai_location: str | None = None + cache_dir: str | None = None + cache: bool = True + + async def generate_markdown(self, chunk: anchorite.document.DocumentChunk) -> str: + process_options = documentai.ProcessOptions( + layout_config=documentai.ProcessOptions.LayoutConfig( + return_bounding_boxes=True, + ), + ) + doc = await docai.process( + self.project_id, + self.location, + self.processor_id, + process_options, + chunk, + documentai_location=self.documentai_location, + cache_dir=self.cache_dir, + cache=self.cache, + ) + return "".join(_LayoutProcessor().process(doc.document_layout.blocks)) diff --git a/src/gemini_ocr/docai_ocr.py b/src/gemini_ocr/docai_ocr.py index ec82faa..858d9bf 100644 --- a/src/gemini_ocr/docai_ocr.py +++ b/src/gemini_ocr/docai_ocr.py @@ -1,52 +1,71 @@ +import dataclasses import logging +import anchorite from google.cloud import documentai -from gemini_ocr import docai, document, settings +from gemini_ocr import docai +_BBOX_VERTEX_COUNT = 4 -async def _run_document_ai(settings: settings.Settings, chunk: document.DocumentChunk) -> documentai.Document: - """Runs Document AI OCR.""" - process_options = documentai.ProcessOptions( - ocr_config=documentai.OcrConfig( - enable_native_pdf_parsing=True, - premium_features=documentai.OcrConfig.PremiumFeatures( - compute_style_info=True, - enable_math_ocr=True, - ), - ), - ) +@dataclasses.dataclass +class DocAIAnchorProvider(anchorite.providers.AnchorProvider): + """Anchor provider that generates bounding boxes using Document AI OCR.""" - return await docai.process(settings, process_options, settings.ocr_processor_id, chunk) + project_id: str + location: str + processor_id: str + documentai_location: str | None = None + cache_dir: str | None = None + cache: bool = True + async def generate_anchors(self, chunk: anchorite.document.DocumentChunk) -> list[anchorite.Anchor]: + process_options = documentai.ProcessOptions( + ocr_config=documentai.OcrConfig( + enable_native_pdf_parsing=True, + premium_features=documentai.OcrConfig.PremiumFeatures( + compute_style_info=True, + enable_math_ocr=True, + ), + ), + ) + doc = await docai.process( + self.project_id, + self.location, + self.processor_id, + process_options, + chunk, + documentai_location=self.documentai_location, + cache_dir=self.cache_dir, + cache=self.cache, + ) -async def generate_bounding_boxes( - settings: settings.Settings, - chunk: document.DocumentChunk, -) -> list[document.BoundingBox]: - doc = await _run_document_ai(settings, chunk) + def _get_text(text_anchor: documentai.Document.TextAnchor) -> str: + if not text_anchor.text_segments: + return "" + return "".join(doc.text[int(s.start_index) : int(s.end_index)] for s in text_anchor.text_segments) - def _get_text(text_anchor: documentai.Document.TextAnchor) -> str: - if not text_anchor.text_segments: - return "" - return "".join( - doc.text[int(segment.start_index) : int(segment.end_index)] for segment in text_anchor.text_segments - ) + anchors = [] + for page_num, page in enumerate(doc.pages): + for block in page.lines: + text = _get_text(block.layout.text_anchor).strip() + vertices = block.layout.bounding_poly.normalized_vertices + if len(vertices) == _BBOX_VERTEX_COUNT: + anchors.append( + anchorite.Anchor( + text=text, + page=page_num + chunk.start_page, + boxes=( + anchorite.BBox( + top=int(vertices[0].y * 1000), + left=int(vertices[0].x * 1000), + bottom=int(vertices[2].y * 1000), + right=int(vertices[2].x * 1000), + ), + ), + ), + ) - bboxes = [] - for page_num, page in enumerate(doc.pages): - for block in page.lines: - text = _get_text(block.layout.text_anchor).strip() - vertices = block.layout.bounding_poly.normalized_vertices - num_vertices = 4 - if len(vertices) == num_vertices: - top = int(vertices[0].y * 1000) - left = int(vertices[0].x * 1000) - bottom = int(vertices[2].y * 1000) - right = int(vertices[2].x * 1000) - rect = document.BBox(top, left, bottom, right) - bboxes.append(document.BoundingBox(page=page_num + chunk.start_page, rect=rect, text=text)) - - logging.debug("Generated %d bounding boxes", len(bboxes)) - return bboxes + logging.debug("Generated %d anchors", len(anchors)) + return anchors diff --git a/src/gemini_ocr/docling.py b/src/gemini_ocr/docling.py index 0a6c5db..52f456b 100644 --- a/src/gemini_ocr/docling.py +++ b/src/gemini_ocr/docling.py @@ -1,8 +1,11 @@ -from gemini_ocr import document, settings +import dataclasses +import anchorite -async def generate_markdown( - settings: settings.Settings, - chunk: document.DocumentChunk, -) -> str: - raise NotImplementedError + +@dataclasses.dataclass +class DoclingMarkdownProvider(anchorite.providers.MarkdownProvider): + """Markdown provider that generates markdown using Docling.""" + + async def generate_markdown(self, chunk: anchorite.document.DocumentChunk) -> str: + raise NotImplementedError diff --git a/src/gemini_ocr/document.py b/src/gemini_ocr/document.py deleted file mode 100644 index 9937375..0000000 --- a/src/gemini_ocr/document.py +++ /dev/null @@ -1,119 +0,0 @@ -import dataclasses -import hashlib -import mimetypes -import pathlib -from collections.abc import Iterator -from typing import BinaryIO, NamedTuple, TypeAlias - -import fitz -import fsspec - -DocumentInput: TypeAlias = pathlib.Path | str | bytes | BinaryIO - - -class BBox(NamedTuple): - """A bounding box tuple (top, left, bottom, right).""" - - top: int - """Top coordinate (y-min: [0-1000]).""" - left: int - """Left coordinate (x-min: [0-1000]).""" - bottom: int - """Bottom coordinate (y-max: [0-1000]).""" - right: int - """Right coordinate (x-max: [0-1000]).""" - - -@dataclasses.dataclass(frozen=True) -class BoundingBox: - """A text segment with its bounding box and page number.""" - - text: str - """The text content.""" - page: int - """Page number (0-indexed).""" - rect: BBox - """The bounding box coordinates.""" - - -@dataclasses.dataclass -class DocumentChunk: - """A chunk of a document (e.g., a subset of pages extracted from a PDF).""" - - document_sha256: str - """SHA256 hash of the original document.""" - start_page: int - """Start page number of this chunk in the original document.""" - end_page: int - """End page number (exclusive) of this chunk.""" - data: bytes - """Raw bytes of the chunk (PDF or image).""" - mime_type: str - """MIME type of the chunk data.""" - - -def _split_pdf_bytes(file_bytes: bytes, page_count: int | None = None) -> Iterator[DocumentChunk]: - doc = fitz.open(stream=file_bytes, filetype="pdf") - doc_page_count = len(doc) - document_sha256 = hashlib.sha256(file_bytes).hexdigest() - if page_count is None: - yield DocumentChunk(document_sha256, 0, doc_page_count, file_bytes, "application/pdf") - return - - for start_page in range(0, doc_page_count, page_count): - new_doc = fitz.open() - end_page = min(start_page + page_count, doc_page_count) - new_doc.insert_pdf(doc, from_page=start_page, to_page=end_page - 1) - yield DocumentChunk(document_sha256, start_page, end_page, new_doc.tobytes(), "application/pdf") - new_doc.close() - - -def _resolve_input(input_source: DocumentInput, mime_type: str | None) -> tuple[bytes, str | None]: - """Resolves input source to bytes and mime_type.""" - file_bytes: bytes - - match input_source: - case str() if "://" in input_source: - with fsspec.open(input_source, "rb") as f: - file_bytes = f.read() # type: ignore[attr-defined] - if mime_type is None: - mime_type, _ = mimetypes.guess_type(input_source) - case str() | pathlib.Path() as path: - path_obj = pathlib.Path(path) - file_bytes = path_obj.read_bytes() - if mime_type is None: - mime_type, _ = mimetypes.guess_type(path_obj) - case bytes(): - file_bytes = input_source - case BinaryIO(): - file_bytes = input_source.read() - case _: - raise ValueError(f"Unsupported input source: {input_source}") - - return file_bytes, mime_type - - -def chunks( - input_source: DocumentInput, - *, - page_count: int | None = None, - mime_type: str | None = None, -) -> Iterator[DocumentChunk]: - """Splits a Document into chunks. - - Supports PDF (splits by pages) and Images (single chunk). - """ - file_bytes, mime_type = _resolve_input(input_source, mime_type) - - # Auto-detect PDF if mime_type is unknown - if mime_type is None and file_bytes.startswith(b"%PDF"): - mime_type = "application/pdf" - - if mime_type and mime_type.startswith("image/"): - yield DocumentChunk(hashlib.sha256(file_bytes).hexdigest(), 0, 0, file_bytes, mime_type) - return - - if mime_type != "application/pdf": - raise ValueError(f"Unsupported file type: {mime_type}") - - yield from _split_pdf_bytes(file_bytes, page_count) diff --git a/src/gemini_ocr/gemini.py b/src/gemini_ocr/gemini.py index 0b63e9e..77ae4c6 100644 --- a/src/gemini_ocr/gemini.py +++ b/src/gemini_ocr/gemini.py @@ -1,14 +1,14 @@ import asyncio +import dataclasses import hashlib import logging import pathlib from typing import Final +import anchorite import google.auth from google import genai -from gemini_ocr import document, settings - GEMINI_PROMPT: Final[str] = """ Carefully transcribe the text for this pdf into a text file with markdown annotations. @@ -52,66 +52,71 @@ * `` ... `` """ # noqa: RUF001 -_GEMINI_PROMPT_SHA256: Final[bytes] = hashlib.sha256(GEMINI_PROMPT.encode()).digest() - - -def _call_gemini(settings: settings.Settings, chunk: document.DocumentChunk) -> genai.types.GenerateContentResponse: - # TODO: consider reusing client - credentials, _ = google.auth.default() - if settings.quota_project_id: - credentials = credentials.with_quota_project(settings.quota_project_id) - elif settings.project_id: - # Fallback to project if quota_project_id is not set - credentials = credentials.with_quota_project(settings.project_id) - - client = genai.Client( - vertexai=True, - project=settings.project_id, - location=settings.location, - credentials=credentials, - ) - - model_name = settings.gemini_model_name - if model_name is None: - raise ValueError("gemini_model_name is required for Gemini mode.") - - contents: list[genai.types.Part | str] = [] - contents.append(genai.types.Part(inline_data=genai.types.Blob(data=chunk.data, mime_type=chunk.mime_type))) - contents.append(GEMINI_PROMPT) - - return client.models.generate_content( - model=model_name, - contents=contents, - config=genai.types.GenerateContentConfig(response_mime_type="text/plain"), - ) - - -def _generate_cache_path(settings: settings.Settings, chunk: document.DocumentChunk) -> pathlib.Path | None: - if not settings.cache_dir or not settings.cache_gemini: - return None - hasher = hashlib.sha256() - hasher.update(_GEMINI_PROMPT_SHA256) - hasher.update(chunk.document_sha256.encode()) - hasher.update((settings.gemini_model_name or "").encode()) - cache_key = f"{hasher.hexdigest()}_{chunk.start_page}_{chunk.end_page}" - return pathlib.Path(settings.cache_dir) / "gemini" / f"{cache_key}.txt" - - -async def generate_markdown(settings: settings.Settings, chunk: document.DocumentChunk) -> str | None: - """Generates markdown for a chunk using the Gemini API.""" - - cache_path = _generate_cache_path(settings, chunk) - - if cache_path and cache_path.exists(): - logging.debug("Loaded from Gemini cache: %s", cache_path) - return cache_path.read_text() - - response = await asyncio.to_thread(_call_gemini, settings, chunk) - text = response.text - - if cache_path and text: - cache_path.parent.mkdir(parents=True, exist_ok=True) - cache_path.write_text(text) - logging.debug("Saved to Gemini cache: %s", cache_path) - return text +@dataclasses.dataclass +class GeminiMarkdownProvider(anchorite.providers.MarkdownProvider): + """Markdown provider that generates markdown using the Gemini API.""" + + project_id: str + location: str + model_name: str + quota_project_id: str | None = None + prompt: str | None = None + cache_dir: str | None = None + cache: bool = True + + def _cache_path(self, chunk: anchorite.document.DocumentChunk) -> pathlib.Path | None: + if not self.cache_dir or not self.cache: + return None + hasher = hashlib.sha256() + hasher.update(GEMINI_PROMPT.encode()) + if self.prompt: + hasher.update(self.prompt.encode()) + hasher.update(chunk.document_sha256.encode()) + hasher.update((self.model_name or "").encode()) + cache_key = f"{hasher.hexdigest()}_{chunk.start_page}_{chunk.end_page}" + return pathlib.Path(self.cache_dir) / "gemini" / f"{cache_key}.txt" + + def _call(self, chunk: anchorite.document.DocumentChunk) -> genai.types.GenerateContentResponse: + credentials, _ = google.auth.default() + if self.quota_project_id: + credentials = credentials.with_quota_project(self.quota_project_id) + elif self.project_id: + credentials = credentials.with_quota_project(self.project_id) + + client = genai.Client( + vertexai=True, + project=self.project_id, + location=self.location, + credentials=credentials, + ) + + contents: list[genai.types.Part | str] = [ + genai.types.Part(inline_data=genai.types.Blob(data=chunk.data, mime_type=chunk.mime_type)), + GEMINI_PROMPT, + ] + if self.prompt: + contents.append(self.prompt) + + return client.models.generate_content( + model=self.model_name, + contents=contents, + config=genai.types.GenerateContentConfig(response_mime_type="text/plain"), + ) + + async def generate_markdown(self, chunk: anchorite.document.DocumentChunk) -> str: + cache_path = self._cache_path(chunk) + + if cache_path and cache_path.exists(): + logging.debug("Loaded from Gemini cache: %s", cache_path) + return cache_path.read_text() + + response = await asyncio.to_thread(self._call, chunk) + text = response.text or "" + + if cache_path and text: + cache_path.parent.mkdir(parents=True, exist_ok=True) + cache_path.write_text(text) + logging.debug("Saved to Gemini cache: %s", cache_path) + + return text diff --git a/src/gemini_ocr/gemini_ocr.py b/src/gemini_ocr/gemini_ocr.py index a0f959d..da441ea 100644 --- a/src/gemini_ocr/gemini_ocr.py +++ b/src/gemini_ocr/gemini_ocr.py @@ -1,201 +1,75 @@ -import asyncio -import collections -import dataclasses -import itertools -import re -import typing - -from gemini_ocr import bbox_alignment, docai_layout, docai_ocr, docling, document, gemini -from gemini_ocr import settings as settings_module - -T = typing.TypeVar("T") - - -@dataclasses.dataclass -class RawOcrData: - """Intermediate data structure holding raw OCR/Markdown output.""" - - markdown_content: str - """The generated markdown string.""" - bounding_boxes: list[document.BoundingBox] - """List of all bounding boxes extracted from the document.""" - - -@dataclasses.dataclass -class OcrResult: - """The final result of the OCR and annotation process.""" - - markdown_content: str - """The generated markdown content.""" - bounding_boxes: dict[document.BoundingBox, tuple[int, int]] - """Mapping of bounding boxes to their span ranges in the markdown.""" - coverage_percent: float - """Percentage of markdown content covered by aligned bounding boxes.""" - - def annotate(self) -> str: - """Annotates the markdown content with bounding box spans.""" - - # 1. Identify math ranges to snap to (to avoid inserting tags inside math) - math_ranges = [] - # Pattern matches $$...$$ (DOTALL) or $...$ (inline, allowing newlines for wrapped text) - pattern = re.compile(r"(\$\$[\s\S]+?\$\$|\$(?:\\.|[^$])+?\$)") - for m in pattern.finditer(self.markdown_content): - math_ranges.append((m.start(), m.end())) - - insertions = [] - for bbox, (span_start, span_end) in self.bounding_boxes.items(): - start, end = span_start, span_end - # Check for overlap with math ranges - for m_start, m_end in math_ranges: - # If overlap (we check if the range intersects the math range) - if max(start, m_start) < min(end, m_end): - # Snap to the math range - start = m_start - end = m_end - break - - length = end - start - bbox_str = f"{bbox.rect.top},{bbox.rect.left},{bbox.rect.bottom},{bbox.rect.right}" - start_tag = f'' - end_tag = "" - - insertions.append((start, False, length, start_tag)) - insertions.append((end, True, length, end_tag)) - - # Sort: - # 1. Index Descending. - # 2. is_end Descending (True/End processed before False/Start). - # 3. Length Ascending (Short processed before Long). - - insertions.sort(key=lambda x: (x[0], x[1], -x[2]), reverse=True) - - chars = list(self.markdown_content) - for index, _, _, text in insertions: - chars.insert(index, text) - - return "".join(chars) - - -async def _generate_markdown_for_chunk( - ocr_settings: settings_module.Settings, - chunk: document.DocumentChunk, -) -> str: - """Generates markdown for a chunk using the Gemini API.""" - - match ocr_settings.mode: - case settings_module.OcrMode.GEMINI: - text = await gemini.generate_markdown(ocr_settings, chunk) - case settings_module.OcrMode.DOCUMENTAI: - text = await docai_layout.generate_markdown(ocr_settings, chunk) - case settings_module.OcrMode.DOCLING: - text = await docling.generate_markdown(ocr_settings, chunk) +import enum +import os + +import anchorite + +from gemini_ocr import docai_layout, docai_ocr, docling +from gemini_ocr import gemini as gemini_module + + +class _OcrMode(enum.StrEnum): + GEMINI = "gemini" + DOCUMENTAI = "documentai" + DOCLING = "docling" + + +def from_env( + prefix: str = "GEMINI_OCR_", +) -> tuple[anchorite.providers.MarkdownProvider, anchorite.providers.AnchorProvider | None]: + """Build providers from environment variables.""" + + def get(key: str) -> str | None: + return os.getenv(prefix + key.upper()) + + def require(key: str) -> str: + val = get(key) + if val is None: + raise ValueError(f"{prefix}{key.upper()} environment variable is required.") + return val + + def getdefault(key: str, default: str) -> str: + return os.getenv(prefix + key.upper(), default) + + project_id = require("project_id") + location = getdefault("location", "us-central1") + documentai_location = get("documentai_location") + cache_dir = get("cache_dir") + mode = _OcrMode(getdefault("mode", _OcrMode.GEMINI)) + include_bboxes = getdefault("include_bboxes", "true").lower() in ("true", "1", "yes") + + match mode: + case _OcrMode.GEMINI: + markdown_provider: anchorite.providers.MarkdownProvider = gemini_module.GeminiMarkdownProvider( + project_id=project_id, + location=location, + model_name=require("gemini_model_name"), + quota_project_id=get("quota_project_id"), + prompt=get("gemini_prompt"), + cache_dir=cache_dir, + ) + case _OcrMode.DOCUMENTAI: + markdown_provider = docai_layout.DocAIMarkdownProvider( + project_id=project_id, + location=location, + processor_id=require("layout_processor_id"), + documentai_location=documentai_location, + cache_dir=cache_dir, + ) + case _OcrMode.DOCLING: + markdown_provider = docling.DoclingMarkdownProvider() case _: - text = None - - return text or "" - - -# --- Merging and Annotation --- - - -async def _batched_gather(tasks: collections.abc.Sequence[collections.abc.Awaitable[T]], batch_size: int) -> list[T]: - """Runs awaitables in batches.""" - results = [] - for i in range(0, len(tasks), batch_size): - batch = tasks[i : i + batch_size] - results.extend(await asyncio.gather(*batch)) - return results - - -async def extract_raw_data( - document_input: document.DocumentInput, - settings: settings_module.Settings | None = None, - markdown_content: str | None = None, -) -> RawOcrData: - """ - Extracts raw OCR data (markdown and bounding boxes) from a file. - - Args: - document_input: The document to process (Path, str, bytes, or stream). - settings: Configuration settings. - markdown_content: Optional existing markdown content. - - Returns: - RawOcrData containing markdown and bounding boxes. - """ - if settings is None: - settings = settings_module.Settings.from_env() - - chunks = list(document.chunks(document_input, page_count=settings.markdown_page_batch_size)) - if not markdown_content: - markdown_work = [_generate_markdown_for_chunk(settings, chunk) for chunk in chunks] - markdown_chunks = await _batched_gather(markdown_work, settings.num_jobs) - - # Renumber tables and figures - counters: collections.Counter[str] = collections.Counter() - - def _renumber(match: re.Match) -> str: - kind = match.group(1) - counters[kind] += 1 - return f"" - - markdown_chunks = [re.sub(r"", _renumber, chunk_text) for chunk_text in markdown_chunks] - markdown_content = "\n".join(markdown_chunks) - - bounding_box_work = [docai_ocr.generate_bounding_boxes(settings, chunk) for chunk in chunks] - bboxes = list(itertools.chain.from_iterable(await _batched_gather(bounding_box_work, settings.num_jobs))) - - return RawOcrData( - markdown_content=markdown_content, - bounding_boxes=bboxes, - ) - - -async def process_document( - document_input: document.DocumentInput, - settings: settings_module.Settings | None = None, - markdown_content: str | None = None, -) -> OcrResult: - """ - Processes a document to generate annotated markdown with OCR bounding boxes. - - Args: - document_input: The document to process (Path, str, bytes, or stream). - settings: Configuration settings. - markdown_content: Optional existing markdown content. - - Returns: - OcrResult containing annotated markdown and stats. - """ - if settings is None: - settings = settings_module.Settings.from_env() - - raw_data = await extract_raw_data(document_input, settings, markdown_content) - annotated_markdown = bbox_alignment.create_annotated_markdown( - raw_data.markdown_content, - raw_data.bounding_boxes, - uniqueness_threshold=settings.alignment_uniqueness_threshold, - min_overlap=settings.alignment_min_overlap, - ) - - # Calculate coverage - if not raw_data.markdown_content: - coverage_percent = 0.0 - else: - spans = list(annotated_markdown.values()) - spans.sort() - merged: list[tuple[int, int]] = [] - for start, end in spans: - if not merged or start > merged[-1][1]: - merged.append((start, end)) - else: - merged[-1] = (merged[-1][0], max(merged[-1][1], end)) - - covered_len = sum(end - start for start, end in merged) - coverage_percent = covered_len / len(raw_data.markdown_content) - - return OcrResult( - markdown_content=raw_data.markdown_content, - bounding_boxes=annotated_markdown, - coverage_percent=coverage_percent, - ) + raise ValueError(f"Unknown mode: {mode}") + + anchor_provider: anchorite.providers.AnchorProvider | None = None + if include_bboxes: + ocr_processor_id = get("ocr_processor_id") + if ocr_processor_id: + anchor_provider = docai_ocr.DocAIAnchorProvider( + project_id=project_id, + location=location, + processor_id=ocr_processor_id, + documentai_location=documentai_location, + cache_dir=cache_dir, + ) + + return markdown_provider, anchor_provider diff --git a/src/gemini_ocr/range_ops.py b/src/gemini_ocr/range_ops.py deleted file mode 100644 index 95d0d94..0000000 --- a/src/gemini_ocr/range_ops.py +++ /dev/null @@ -1,182 +0,0 @@ -import heapq -import itertools -from collections.abc import Callable, Iterator, Sequence -from operator import itemgetter - -_INTERSECTION_OVERLAP_COUNT = 2 - - -def _generate_range_edges( - ranges: Sequence[tuple[int, int]], - start_weight: int, - end_weight: int, -) -> Iterator[tuple[int, int]]: - """Generates edge events for a sweep-line algorithm. - - Yields (position, weight) pairs. - """ - for s, e in ranges: - yield s, start_weight - yield e, end_weight - - -def _sweep_operation( - ranges_a: Sequence[tuple[int, int]], - ranges_b: Sequence[tuple[int, int]], - weights_a: tuple[int, int], - weights_b: tuple[int, int], - predicate: Callable[[int], bool], -) -> list[tuple[int, int]]: - """Generic sweep-line operation. - - Args: - ranges_a: First sequence of intervals. - ranges_b: Second sequence of intervals. - weights_a: (start_weight, end_weight) for A. - weights_b: (start_weight, end_weight) for B. - predicate: Function taking current sum 's' and returning True if we should be outputting. - - Returns: - List of resulting intervals. - """ - # Merge sorted event streams - events = heapq.merge( - _generate_range_edges(ranges_a, *weights_a), - _generate_range_edges(ranges_b, *weights_b), - key=itemgetter(0), - ) - - s = 0 - result = [] - start_pos = -1 - - # Group events by position to handle simultaneous events (e.g. abutments) - for pos, group in itertools.groupby(events, key=itemgetter(0)): - # Calculate total weight change at this position - delta = sum(weight for _, weight in group) - s_next = s + delta - - was_active = predicate(s) - is_active = predicate(s_next) - - if not was_active and is_active: - # Started satisfying predicate - start_pos = pos - elif was_active and not is_active: - # Stopped satisfying predicate - assert start_pos != -1 # noqa: S101 - result.append((start_pos, pos)) - start_pos = -1 - - s = s_next - - return result - - -def subtract_ranges( - ranges_a: Sequence[tuple[int, int]], - ranges_b: Sequence[tuple[int, int]], -) -> list[tuple[int, int]]: - """Calculates the set difference of two sets of disjoint intervals (A - B). - - Args: - ranges_a: A sequence of half-open intervals [start, end), sorted and disjoint. - ranges_b: A sequence of half-open intervals [start, end), sorted and disjoint. - - Returns: - A list of intervals representing the parts of A that are not covered by B. - """ - # A adds 1, B subtracts 1. We want regions where sum == 1. - return _sweep_operation( - ranges_a, - ranges_b, - (+1, -1), - (-1, +1), - lambda s: s == 1, - ) - - -def union_ranges( - ranges_a: Sequence[tuple[int, int]], - ranges_b: Sequence[tuple[int, int]], -) -> list[tuple[int, int]]: - """Calculates the union of two sets of disjoint intervals (A | B). - - Args: - ranges_a: A sequence of half-open intervals [start, end), sorted and disjoint. - ranges_b: A sequence of half-open intervals [start, end), sorted and disjoint. - - Returns: - A list of intervals representing the union of A and B. - Overlapping or adjacent intervals are merged. - """ - # Both add 1. We want regions where sum > 0. - return _sweep_operation( - ranges_a, - ranges_b, - (+1, -1), - (+1, -1), - lambda s: s > 0, - ) - - -def intersect_ranges( - ranges_a: Sequence[tuple[int, int]], - ranges_b: Sequence[tuple[int, int]], -) -> list[tuple[int, int]]: - """Calculates the intersection of two sets of disjoint intervals (A & B). - - Args: - ranges_a: A sequence of half-open intervals [start, end), sorted and disjoint. - ranges_b: A sequence of half-open intervals [start, end), sorted and disjoint. - - Returns: - A list of intervals representing the overlapping parts of A and B. - """ - # Both add 1. We want regions where sum == _INTERSECTION_OVERLAP_COUNT. - return _sweep_operation( - ranges_a, - ranges_b, - (+1, -1), - (+1, -1), - lambda s: s == _INTERSECTION_OVERLAP_COUNT, - ) - - -def in_range(val: int, test_range: tuple[int, int]) -> bool: - """Return true if `val` is in `test_range`. - - Args: - val: The value to test. - test_range: The range to test against. - - Returns: - True if `val` is in `test_range`. - """ - return test_range[0] <= val < test_range[1] - - -def overlaps(r1: tuple[int, int], r2: tuple[int, int]) -> bool: - """Return true if `r1` overlaps `r2`. - - Args: - r1: The first range. - r2: The second range. - - Returns: - True if `r1` overlaps `r2`. - """ - return r1[0] < r2[1] and r1[1] > r2[0] - - -def contained(r1: tuple[int, int], r2: tuple[int, int]) -> bool: - """Return true if `r1` is contained in `r2`. - - Args: - r1: The first range. - r2: The second range. - - Returns: - True if `r1` is contained in `r2`. - """ - return r1[0] >= r2[0] and r1[1] <= r2[1] diff --git a/src/gemini_ocr/settings.py b/src/gemini_ocr/settings.py deleted file mode 100644 index ec30d51..0000000 --- a/src/gemini_ocr/settings.py +++ /dev/null @@ -1,87 +0,0 @@ -import dataclasses -import enum -import os -from typing import Self - - -class OcrMode(enum.StrEnum): - """Processing mode.""" - - GEMINI = "gemini" - """Use Gemini for markdown generation.""" - DOCUMENTAI = "documentai" - """Use Document AI layout mode for markdown generation.""" - DOCLING = "docling" - """Use Docling for markdown generation.""" - - -@dataclasses.dataclass -class Settings: - """gemini-ocr settings.""" - - location: str - """Gemini api endpoint location (e.g. 'us-central1').""" - layout_processor_id: str | None - """Document AI layout processor ID (required for Document AI mode).""" - ocr_processor_id: str | None - """Document AI OCR processor ID.""" - - project_id: str - """GCP project ID.""" - quota_project_id: str | None = None - """GCP quota project ID (defaults to project if None).""" - gemini_model_name: str | None = None - """Name of the Gemini model to use. (required for Gemini mode)""" - - mode: OcrMode = OcrMode.GEMINI - """Processing mode to use.""" - - documentai_location: str | None = None - """DocumentAI api endpoint location (e.g. 'us', 'eu'). If `None`, infers from `location`.""" - - alignment_uniqueness_threshold: float = 0.5 - """Minimum score ratio between best and second-best match.""" - alignment_min_overlap: float = 0.9 - """Minimum overlap fraction required for a valid match.""" - include_bboxes: bool = True - """Whether to perform bounding box alignment.""" - markdown_page_batch_size: int = 10 - """Pages per batch for Markdown generation.""" - ocr_page_batch_size: int = 10 - """Pages per batch for OCR.""" - num_jobs: int = 10 - """Max concurrent jobs.""" - cache_dir: str | None = None - """Directory to store API response cache. `None` disables caching.""" - cache_gemini: bool = True - """Whether to cache Gemini API responses.""" - cache_docai: bool = True - """Whether to cache DocAI API responses.""" - - def get_documentai_location(self) -> str: - if self.documentai_location is None: - return "eu" if self.location.startswith("eu") else "us" - return self.documentai_location - - @classmethod - def from_env(cls, prefix: str = "GEMINI_OCR_") -> Self: - """Create Settings from environment variables.""" - - def get(key: str) -> str | None: - return os.getenv(prefix + key.upper()) - - def getdefault(key: str, default: str) -> str: - return os.getenv(prefix + key.upper(), default) - - project_id = get("project_id") - if project_id is None: - raise ValueError(f"{prefix}PROJECT_ID environment variable is required.") - - return cls( - project_id=project_id, - location=getdefault("location", "us-central1"), - quota_project_id=get("quota_project_id"), - layout_processor_id=get("layout_processor_id"), - ocr_processor_id=get("ocr_processor_id"), - gemini_model_name=get("gemini_model_name"), - ) diff --git a/tests/fixtures/hubble_docai_bboxes.json b/tests/fixtures/hubble_docai_bboxes.json new file mode 100644 index 0000000..2488a70 --- /dev/null +++ b/tests/fixtures/hubble_docai_bboxes.json @@ -0,0 +1 @@ +[[{"text": "Downloaded from https://www.pnas.org by 45.113.94.70 on January 5, 2026 from IP address 45.113.94.70.", "page": 0, "boxes": [{"top": 931, "left": 15, "bottom": 456, "right": 31}]}, {"text": "168", "page": 0, "boxes": [{"top": 105, "left": 195, "bottom": 113, "right": 222}]}, {"text": "ASTRONOMY: E. HUBBLE", "page": 0, "boxes": [{"top": 103, "left": 418, "bottom": 114, "right": 660}]}, {"text": "PROC. N. A. S.", "page": 0, "boxes": [{"top": 103, "left": 763, "bottom": 114, "right": 879}]}, {"text": "appearance the spectrum is very much like spectra of the Milky Way", "page": 0, "boxes": [{"top": 133, "left": 195, "bottom": 147, "right": 880}]}, {"text": "clouds in Sagittarius and Cygnus, and is also similar to spectra of binary", "page": 0, "boxes": [{"top": 149, "left": 195, "bottom": 164, "right": 881}]}, {"text": "stars of the W Ursae Majoris type, where the widening and depth of the", "page": 0, "boxes": [{"top": 167, "left": 195, "bottom": 180, "right": 880}]}, {"text": "lines are affected by the rapid rotation of the stars involved.", "page": 0, "boxes": [{"top": 183, "left": 194, "bottom": 196, "right": 755}]}, {"text": "The wide shallow absorption lines observed in the spectrum of N. G. C.", "page": 0, "boxes": [{"top": 200, "left": 216, "bottom": 213, "right": 879}]}, {"text": "7619 have been noticed in the spectra of other extra-galactic nebulae, and", "page": 0, "boxes": [{"top": 216, "left": 196, "bottom": 230, "right": 880}]}, {"text": "may be due to a dispersion in velocity and a blending of the spectral types", "page": 0, "boxes": [{"top": 234, "left": 196, "bottom": 246, "right": 880}]}, {"text": "of the many stars which presumably exist in the central parts of these", "page": 0, "boxes": [{"top": 250, "left": 195, "bottom": 264, "right": 881}]}, {"text": "nebulae. The lack of depth in the absorption lines seems to be more", "page": 0, "boxes": [{"top": 266, "left": 196, "bottom": 280, "right": 882}]}, {"text": "pronounced among the smaller and fainter nebulae, and in N. G. C. 7619", "page": 0, "boxes": [{"top": 282, "left": 196, "bottom": 297, "right": 880}]}, {"text": "the absorption is very weak.", "page": 0, "boxes": [{"top": 301, "left": 195, "bottom": 313, "right": 459}]}, {"text": "It is hoped that velocities of more of these interesting objects will soon", "page": 0, "boxes": [{"top": 317, "left": 216, "bottom": 330, "right": 881}]}, {"text": "be available.", "page": 0, "boxes": [{"top": 334, "left": 195, "bottom": 345, "right": 311}]}, {"text": "A RELATION BETWEEN DISTANCE AND RADIAL VELOCITY", "page": 0, "boxes": [{"top": 403, "left": 209, "bottom": 415, "right": 870}]}, {"text": "AMONG EXTRA-GALACTIC NEBULAE", "page": 0, "boxes": [{"top": 420, "left": 333, "bottom": 432, "right": 743}]}, {"text": "BY EDWIN HUBBLE", "page": 0, "boxes": [{"top": 446, "left": 449, "bottom": 457, "right": 629}]}, {"text": "MOUNT WILSON OBSERVATORY, CARNEGIE INSTITUTION OF WASHINGTON", "page": 0, "boxes": [{"top": 470, "left": 255, "bottom": 480, "right": 823}]}, {"text": "Communicated January 17, 1929", "page": 0, "boxes": [{"top": 492, "left": 401, "bottom": 503, "right": 668}]}, {"text": "Determinations of the motion of the sun with respect to the extra-", "page": 0, "boxes": [{"top": 518, "left": 218, "bottom": 532, "right": 882}]}, {"text": "galactic nebulae have involved a K term of several hundred kilometers", "page": 0, "boxes": [{"top": 533, "left": 196, "bottom": 548, "right": 882}]}, {"text": "which appears to be variable. Explanations of this paradox have been", "page": 0, "boxes": [{"top": 550, "left": 196, "bottom": 567, "right": 883}]}, {"text": "sought in a correlation between apparent radial velocities and distances,", "page": 0, "boxes": [{"top": 567, "left": 197, "bottom": 582, "right": 880}]}, {"text": "but so far the results have not been convincing. The present paper is a", "page": 0, "boxes": [{"top": 585, "left": 197, "bottom": 598, "right": 884}]}, {"text": "re-examination of the question, based on only those nebular distances", "page": 0, "boxes": [{"top": 600, "left": 196, "bottom": 616, "right": 883}]}, {"text": "which are believed to be fairly reliable.", "page": 0, "boxes": [{"top": 619, "left": 197, "bottom": 632, "right": 558}]}, {"text": "Distances of extra-galactic nebulae depend ultimately upon the appli-", "page": 0, "boxes": [{"top": 635, "left": 220, "bottom": 648, "right": 883}]}, {"text": "cation of absolute-luminosity criteria to involved stars whose types can", "page": 0, "boxes": [{"top": 652, "left": 197, "bottom": 665, "right": 884}]}, {"text": "be recognized. These include, among others, Cepheid variables, novae,", "page": 0, "boxes": [{"top": 668, "left": 197, "bottom": 682, "right": 882}]}, {"text": "and blue stars involved in emission nebulosity. Numerical values depend", "page": 0, "boxes": [{"top": 684, "left": 198, "bottom": 698, "right": 883}]}, {"text": "upon the zero point of the period-luminosity relation among Cepheids,", "page": 0, "boxes": [{"top": 700, "left": 198, "bottom": 716, "right": 882}]}, {"text": "the other criteria merely check the order of the distances. This method", "page": 0, "boxes": [{"top": 717, "left": 198, "bottom": 732, "right": 884}]}, {"text": "is restricted to the few nebulae which are well resolved by existing instru-", "page": 0, "boxes": [{"top": 734, "left": 198, "bottom": 747, "right": 883}]}, {"text": "ments. A study of these nebulae, together with those in which any stars", "page": 0, "boxes": [{"top": 750, "left": 198, "bottom": 764, "right": 883}]}, {"text": "at all can be recognized, indicates the probability of an approximately", "page": 0, "boxes": [{"top": 767, "left": 198, "bottom": 780, "right": 885}]}, {"text": "uniform upper limit to the absolute luminosity of stars, in the late-type", "page": 0, "boxes": [{"top": 783, "left": 198, "bottom": 797, "right": 883}]}, {"text": "spirals and irregular nebulae at least, of the order of M (photographic)", "page": 0, "boxes": [{"top": 799, "left": 198, "bottom": 813, "right": 854}]}, {"text": "-6.3.\u00b9 The apparent luminosities of the brightest stars in such nebulae", "page": 0, "boxes": [{"top": 815, "left": 200, "bottom": 829, "right": 883}]}, {"text": "are thus criteria which, although rough and to be applied with caution,", "page": 0, "boxes": [{"top": 832, "left": 197, "bottom": 845, "right": 883}]}, {"text": "=", "page": 0, "boxes": [{"top": 803, "left": 866, "bottom": 806, "right": 882}]}, {"text": "Check for", "page": 0, "boxes": [{"top": 63, "left": 882, "bottom": 68, "right": 926}]}, {"text": "updates", "page": 0, "boxes": [{"top": 68, "left": 885, "bottom": 75, "right": 922}]}, {"text": "Downloaded from https://www.pnas.org by 45.113.94.70 on January 5, 2026 from IP address 45.113.94.70.", "page": 1, "boxes": [{"top": 931, "left": 15, "bottom": 456, "right": 31}]}, {"text": "VOL. 15, 1929", "page": 1, "boxes": [{"top": 109, "left": 119, "bottom": 119, "right": 227}]}, {"text": "ASTRONOMY: E. HUBBLE", "page": 1, "boxes": [{"top": 109, "left": 343, "bottom": 118, "right": 584}]}, {"text": "169", "page": 1, "boxes": [{"top": 110, "left": 777, "bottom": 118, "right": 805}]}, {"text": "furnish reasonable estimates of the distances of all extra-galactic systems", "page": 1, "boxes": [{"top": 135, "left": 117, "bottom": 151, "right": 804}]}, {"text": "in which even a few stars can be detected.", "page": 1, "boxes": [{"top": 154, "left": 118, "bottom": 165, "right": 507}]}, {"text": "TABLE 1", "page": 1, "boxes": [{"top": 185, "left": 410, "bottom": 192, "right": 478}]}, {"text": "-", "page": 1, "boxes": [{"top": 284, "left": 518, "bottom": 286, "right": 534}]}, {"text": "NEBULAE WHOSE DISTANCES HAVE BEEN ESTIMATED FROM STARS INVOLVED OR FROM", "page": 1, "boxes": [{"top": 197, "left": 117, "bottom": 207, "right": 804}]}, {"text": "MEAN LUMINOSITIES IN A CLUSTER", "page": 1, "boxes": [{"top": 212, "left": 294, "bottom": 220, "right": 591}]}, {"text": "OBJECT", "page": 1, "boxes": [{"top": 227, "left": 179, "bottom": 234, "right": 226}]}, {"text": "m\u2082", "page": 1, "boxes": [{"top": 228, "left": 316, "bottom": 236, "right": 334}]}, {"text": "T", "page": 1, "boxes": [{"top": 228, "left": 423, "bottom": 233, "right": 432}]}, {"text": "mt", "page": 1, "boxes": [{"top": 227, "left": 650, "bottom": 237, "right": 669}]}, {"text": "M", "page": 1, "boxes": [{"top": 226, "left": 761, "bottom": 234, "right": 777}]}, {"text": "S. Mag.", "page": 1, "boxes": [{"top": 238, "left": 166, "bottom": 249, "right": 228}]}, {"text": "0.032", "page": 1, "boxes": [{"top": 239, "left": 408, "bottom": 247, "right": 453}]}, {"text": "+ 170", "page": 1, "boxes": [{"top": 239, "left": 519, "bottom": 248, "right": 570}]}, {"text": "1.5", "page": 1, "boxes": [{"top": 239, "left": 651, "bottom": 247, "right": 678}]}, {"text": "-16.0", "page": 1, "boxes": [{"top": 239, "left": 751, "bottom": 248, "right": 803}]}, {"text": "L. Mag.", "page": 1, "boxes": [{"top": 253, "left": 165, "bottom": 263, "right": 228}]}, {"text": "0.034", "page": 1, "boxes": [{"top": 252, "left": 408, "bottom": 261, "right": 452}]}, {"text": "+ 290", "page": 1, "boxes": [{"top": 251, "left": 519, "bottom": 262, "right": 570}]}, {"text": "0.5", "page": 1, "boxes": [{"top": 253, "left": 650, "bottom": 261, "right": 679}]}, {"text": "17.2", "page": 1, "boxes": [{"top": 253, "left": 768, "bottom": 261, "right": 803}]}, {"text": "N. G. C. 6822", "page": 1, "boxes": [{"top": 266, "left": 118, "bottom": 275, "right": 228}]}, {"text": "0.214", "page": 1, "boxes": [{"top": 267, "left": 408, "bottom": 275, "right": 454}]}, {"text": "130", "page": 1, "boxes": [{"top": 266, "left": 543, "bottom": 275, "right": 570}]}, {"text": "9.0", "page": 1, "boxes": [{"top": 266, "left": 650, "bottom": 275, "right": 678}]}, {"text": "12.7", "page": 1, "boxes": [{"top": 267, "left": 768, "bottom": 275, "right": 805}]}, {"text": "598", "page": 1, "boxes": [{"top": 280, "left": 202, "bottom": 288, "right": 228}]}, {"text": "0.263", "page": 1, "boxes": [{"top": 280, "left": 407, "bottom": 289, "right": 453}]}, {"text": "70", "page": 1, "boxes": [{"top": 280, "left": 552, "bottom": 288, "right": 570}]}, {"text": "7.0", "page": 1, "boxes": [{"top": 280, "left": 650, "bottom": 288, "right": 678}]}, {"text": "15.1", "page": 1, "boxes": [{"top": 281, "left": 768, "bottom": 288, "right": 802}]}, {"text": "221", "page": 1, "boxes": [{"top": 294, "left": 201, "bottom": 301, "right": 227}]}, {"text": "0.275", "page": 1, "boxes": [{"top": 293, "left": 407, "bottom": 303, "right": 452}]}, {"text": "185", "page": 1, "boxes": [{"top": 294, "left": 543, "bottom": 302, "right": 570}]}, {"text": "8.8", "page": 1, "boxes": [{"top": 294, "left": 650, "bottom": 302, "right": 678}]}, {"text": "13.4", "page": 1, "boxes": [{"top": 294, "left": 768, "bottom": 303, "right": 804}]}, {"text": "224", "page": 1, "boxes": [{"top": 307, "left": 201, "bottom": 316, "right": 229}]}, {"text": "0.275", "page": 1, "boxes": [{"top": 307, "left": 407, "bottom": 317, "right": 453}]}, {"text": "220", "page": 1, "boxes": [{"top": 307, "left": 542, "bottom": 316, "right": 569}]}, {"text": "5.0", "page": 1, "boxes": [{"top": 308, "left": 650, "bottom": 316, "right": 678}]}, {"text": "17.2", "page": 1, "boxes": [{"top": 308, "left": 767, "bottom": 316, "right": 803}]}, {"text": "5457", "page": 1, "boxes": [{"top": 321, "left": 193, "bottom": 329, "right": 228}]}, {"text": "17.0", "page": 1, "boxes": [{"top": 321, "left": 300, "bottom": 331, "right": 336}]}, {"text": "0.45", "page": 1, "boxes": [{"top": 322, "left": 407, "bottom": 330, "right": 444}]}, {"text": "+ 200", "page": 1, "boxes": [{"top": 321, "left": 517, "bottom": 332, "right": 570}]}, {"text": "9.9", "page": 1, "boxes": [{"top": 322, "left": 650, "bottom": 330, "right": 678}]}, {"text": "13.3", "page": 1, "boxes": [{"top": 321, "left": 767, "bottom": 331, "right": 803}]}, {"text": "4736", "page": 1, "boxes": [{"top": 334, "left": 192, "bottom": 344, "right": 228}]}, {"text": "17.3", "page": 1, "boxes": [{"top": 335, "left": 301, "bottom": 344, "right": 337}]}, {"text": "0.5", "page": 1, "boxes": [{"top": 336, "left": 407, "bottom": 344, "right": 436}]}, {"text": "+ 290", "page": 1, "boxes": [{"top": 334, "left": 517, "bottom": 345, "right": 570}]}, {"text": "8.4", "page": 1, "boxes": [{"top": 335, "left": 649, "bottom": 344, "right": 679}]}, {"text": "15.1", "page": 1, "boxes": [{"top": 335, "left": 767, "bottom": 344, "right": 802}]}, {"text": "5194", "page": 1, "boxes": [{"top": 349, "left": 194, "bottom": 357, "right": 230}]}, {"text": "17.3", "page": 1, "boxes": [{"top": 349, "left": 301, "bottom": 357, "right": 336}]}, {"text": "0.5", "page": 1, "boxes": [{"top": 349, "left": 406, "bottom": 357, "right": 435}]}, {"text": "+ 270", "page": 1, "boxes": [{"top": 348, "left": 517, "bottom": 359, "right": 570}]}, {"text": "7.4", "page": 1, "boxes": [{"top": 349, "left": 649, "bottom": 357, "right": 679}]}, {"text": "16.1", "page": 1, "boxes": [{"top": 349, "left": 767, "bottom": 357, "right": 802}]}, {"text": "4449", "page": 1, "boxes": [{"top": 363, "left": 192, "bottom": 371, "right": 228}]}, {"text": "17.8", "page": 1, "boxes": [{"top": 363, "left": 301, "bottom": 371, "right": 336}]}, {"text": "0.63", "page": 1, "boxes": [{"top": 363, "left": 407, "bottom": 372, "right": 443}]}, {"text": "+ 200", "page": 1, "boxes": [{"top": 362, "left": 517, "bottom": 372, "right": 569}]}, {"text": "9.5", "page": 1, "boxes": [{"top": 363, "left": 649, "bottom": 371, "right": 678}]}, {"text": "14.5", "page": 1, "boxes": [{"top": 363, "left": 767, "bottom": 371, "right": 803}]}, {"text": "4214", "page": 1, "boxes": [{"top": 376, "left": 192, "bottom": 385, "right": 230}]}, {"text": "18.3", "page": 1, "boxes": [{"top": 377, "left": 301, "bottom": 385, "right": 336}]}, {"text": "0.8", "page": 1, "boxes": [{"top": 377, "left": 406, "bottom": 385, "right": 436}]}, {"text": "+ 300", "page": 1, "boxes": [{"top": 375, "left": 518, "bottom": 386, "right": 568}]}, {"text": "11.3", "page": 1, "boxes": [{"top": 377, "left": 642, "bottom": 385, "right": 677}]}, {"text": "13.2", "page": 1, "boxes": [{"top": 377, "left": 767, "bottom": 385, "right": 802}]}, {"text": "3031", "page": 1, "boxes": [{"top": 390, "left": 192, "bottom": 398, "right": 228}]}, {"text": "18.5", "page": 1, "boxes": [{"top": 390, "left": 301, "bottom": 399, "right": 336}]}, {"text": "0.9", "page": 1, "boxes": [{"top": 390, "left": 406, "bottom": 399, "right": 436}]}, {"text": "30", "page": 1, "boxes": [{"top": 390, "left": 550, "bottom": 399, "right": 570}]}, {"text": "8.3", "page": 1, "boxes": [{"top": 390, "left": 648, "bottom": 399, "right": 678}]}, {"text": "16.4", "page": 1, "boxes": [{"top": 390, "left": 767, "bottom": 399, "right": 803}]}, {"text": "3627", "page": 1, "boxes": [{"top": 404, "left": 192, "bottom": 412, "right": 229}]}, {"text": "18.5", "page": 1, "boxes": [{"top": 404, "left": 301, "bottom": 413, "right": 336}]}, {"text": "0.9", "page": 1, "boxes": [{"top": 405, "left": 407, "bottom": 413, "right": 436}]}, {"text": "+ 650", "page": 1, "boxes": [{"top": 403, "left": 517, "bottom": 413, "right": 568}]}, {"text": "9.1", "page": 1, "boxes": [{"top": 404, "left": 649, "bottom": 412, "right": 677}]}, {"text": "15.7", "page": 1, "boxes": [{"top": 404, "left": 767, "bottom": 413, "right": 803}]}, {"text": "4826", "page": 1, "boxes": [{"top": 418, "left": 192, "bottom": 426, "right": 228}]}, {"text": "18.5", "page": 1, "boxes": [{"top": 418, "left": 301, "bottom": 426, "right": 337}]}, {"text": "0.9", "page": 1, "boxes": [{"top": 418, "left": 407, "bottom": 426, "right": 435}]}, {"text": "+ 150", "page": 1, "boxes": [{"top": 417, "left": 517, "bottom": 428, "right": 569}]}, {"text": "9.0", "page": 1, "boxes": [{"top": 417, "left": 649, "bottom": 426, "right": 677}]}, {"text": "15.7", "page": 1, "boxes": [{"top": 418, "left": 766, "bottom": 426, "right": 803}]}, {"text": "5236", "page": 1, "boxes": [{"top": 432, "left": 193, "bottom": 440, "right": 229}]}, {"text": "18.5", "page": 1, "boxes": [{"top": 432, "left": 301, "bottom": 440, "right": 336}]}, {"text": "0.9", "page": 1, "boxes": [{"top": 432, "left": 407, "bottom": 440, "right": 436}]}, {"text": "+ 500", "page": 1, "boxes": [{"top": 430, "left": 517, "bottom": 441, "right": 569}]}, {"text": "10.4", "page": 1, "boxes": [{"top": 431, "left": 641, "bottom": 440, "right": 678}]}, {"text": "14.4", "page": 1, "boxes": [{"top": 432, "left": 766, "bottom": 440, "right": 802}]}, {"text": "1068", "page": 1, "boxes": [{"top": 445, "left": 194, "bottom": 454, "right": 228}]}, {"text": "18.7", "page": 1, "boxes": [{"top": 445, "left": 300, "bottom": 454, "right": 336}]}, {"text": "1.0", "page": 1, "boxes": [{"top": 446, "left": 408, "bottom": 454, "right": 434}]}, {"text": "+ 920", "page": 1, "boxes": [{"top": 444, "left": 517, "bottom": 455, "right": 570}]}, {"text": "9.1", "page": 1, "boxes": [{"top": 445, "left": 649, "bottom": 453, "right": 676}]}, {"text": "15.9", "page": 1, "boxes": [{"top": 446, "left": 766, "bottom": 454, "right": 802}]}, {"text": "5055", "page": 1, "boxes": [{"top": 459, "left": 194, "bottom": 467, "right": 230}]}, {"text": "19.0", "page": 1, "boxes": [{"top": 459, "left": 301, "bottom": 467, "right": 336}]}, {"text": "1.1", "page": 1, "boxes": [{"top": 459, "left": 408, "bottom": 467, "right": 434}]}, {"text": "+ 450", "page": 1, "boxes": [{"top": 458, "left": 517, "bottom": 469, "right": 568}]}, {"text": "9.6", "page": 1, "boxes": [{"top": 459, "left": 648, "bottom": 467, "right": 676}]}, {"text": "15.6", "page": 1, "boxes": [{"top": 459, "left": 766, "bottom": 467, "right": 801}]}, {"text": "7331", "page": 1, "boxes": [{"top": 473, "left": 193, "bottom": 481, "right": 228}]}, {"text": "19.0", "page": 1, "boxes": [{"top": 473, "left": 301, "bottom": 482, "right": 336}]}, {"text": "1.1", "page": 1, "boxes": [{"top": 473, "left": 408, "bottom": 481, "right": 434}]}, {"text": "+ 500", "page": 1, "boxes": [{"top": 471, "left": 517, "bottom": 482, "right": 568}]}, {"text": "10.4", "page": 1, "boxes": [{"top": 472, "left": 641, "bottom": 481, "right": 676}]}, {"text": "14.8", "page": 1, "boxes": [{"top": 473, "left": 766, "bottom": 482, "right": 802}]}, {"text": "4258", "page": 1, "boxes": [{"top": 487, "left": 192, "bottom": 494, "right": 229}]}, {"text": "19.5", "page": 1, "boxes": [{"top": 487, "left": 301, "bottom": 494, "right": 336}]}, {"text": "1.4", "page": 1, "boxes": [{"top": 487, "left": 408, "bottom": 495, "right": 435}]}, {"text": "+ 500", "page": 1, "boxes": [{"top": 485, "left": 517, "bottom": 496, "right": 568}]}, {"text": "8.7", "page": 1, "boxes": [{"top": 486, "left": 648, "bottom": 494, "right": 677}]}, {"text": "17.0", "page": 1, "boxes": [{"top": 486, "left": 766, "bottom": 495, "right": 802}]}, {"text": "4151", "page": 1, "boxes": [{"top": 501, "left": 192, "bottom": 509, "right": 228}]}, {"text": "20.0", "page": 1, "boxes": [{"top": 501, "left": 299, "bottom": 509, "right": 336}]}, {"text": "1.7", "page": 1, "boxes": [{"top": 501, "left": 408, "bottom": 509, "right": 435}]}, {"text": "+ 960", "page": 1, "boxes": [{"top": 501, "left": 517, "bottom": 509, "right": 568}]}, {"text": "12.0", "page": 1, "boxes": [{"top": 500, "left": 641, "bottom": 508, "right": 676}]}, {"text": "14.2", "page": 1, "boxes": [{"top": 501, "left": 767, "bottom": 509, "right": 802}]}, {"text": "4382", "page": 1, "boxes": [{"top": 515, "left": 191, "bottom": 522, "right": 228}]}, {"text": "2.0", "page": 1, "boxes": [{"top": 515, "left": 406, "bottom": 523, "right": 436}]}, {"text": "+ 500", "page": 1, "boxes": [{"top": 513, "left": 517, "bottom": 523, "right": 568}]}, {"text": "10.0", "page": 1, "boxes": [{"top": 514, "left": 641, "bottom": 522, "right": 676}]}, {"text": "16.5", "page": 1, "boxes": [{"top": 515, "left": 766, "bottom": 523, "right": 803}]}, {"text": "4472", "page": 1, "boxes": [{"top": 528, "left": 192, "bottom": 536, "right": 228}]}, {"text": "2.0", "page": 1, "boxes": [{"top": 528, "left": 406, "bottom": 536, "right": 435}]}, {"text": "+ 850", "page": 1, "boxes": [{"top": 526, "left": 517, "bottom": 538, "right": 568}]}, {"text": "8.8", "page": 1, "boxes": [{"top": 528, "left": 648, "bottom": 536, "right": 677}]}, {"text": "17.7", "page": 1, "boxes": [{"top": 528, "left": 766, "bottom": 536, "right": 802}]}, {"text": "4486", "page": 1, "boxes": [{"top": 541, "left": 191, "bottom": 550, "right": 228}]}, {"text": "2.0", "page": 1, "boxes": [{"top": 542, "left": 406, "bottom": 550, "right": 434}]}, {"text": "+ 800", "page": 1, "boxes": [{"top": 540, "left": 516, "bottom": 551, "right": 568}]}, {"text": "9.7", "page": 1, "boxes": [{"top": 542, "left": 648, "bottom": 549, "right": 676}]}, {"text": "16.8", "page": 1, "boxes": [{"top": 542, "left": 766, "bottom": 550, "right": 803}]}, {"text": "4649", "page": 1, "boxes": [{"top": 556, "left": 192, "bottom": 564, "right": 228}]}, {"text": "2.0", "page": 1, "boxes": [{"top": 556, "left": 406, "bottom": 563, "right": 434}]}, {"text": "+1090", "page": 1, "boxes": [{"top": 555, "left": 517, "bottom": 565, "right": 569}]}, {"text": "9.5", "page": 1, "boxes": [{"top": 555, "left": 648, "bottom": 563, "right": 677}]}, {"text": "17.0", "page": 1, "boxes": [{"top": 555, "left": 766, "bottom": 564, "right": 802}]}, {"text": "Mean", "page": 1, "boxes": [{"top": 572, "left": 117, "bottom": 580, "right": 163}]}, {"text": "-15.5", "page": 1, "boxes": [{"top": 571, "left": 750, "bottom": 581, "right": 802}]}, {"text": "\u0e22", "page": 1, "boxes": [{"top": 621, "left": 134, "bottom": 627, "right": 142}]}, {"text": "m_{s}= photographic magnitude of brightest stars involved.", "page": 1, "boxes": [{"top": 591, "left": 132, "bottom": 603, "right": 622}]}, {"text": "r= distance in units of 106 parsecs. The first two are Shapley's values.", "page": 1, "boxes": [{"top": 605, "left": 135, "bottom": 616, "right": 740}]}, {"text": "= measured velocities in km./sec. N. G. C. 6822, 221, 224 and 5457 are recent", "page": 1, "boxes": [{"top": 618, "left": 168, "bottom": 629, "right": 802}]}, {"text": "determinations by Humason.", "page": 1, "boxes": [{"top": 632, "left": 209, "bottom": 643, "right": 451}]}, {"text": "m_{t}= Holetschek's visual magnitude as corrected by Hopmann. The first three", "page": 1, "boxes": [{"top": 646, "left": 132, "bottom": 656, "right": 803}]}, {"text": "objects were not measured by Holetschek, and the values of me represent", "page": 1, "boxes": [{"top": 660, "left": 209, "bottom": 670, "right": 801}]}, {"text": "estimates by the author based upon such data as are available.", "page": 1, "boxes": [{"top": 673, "left": 209, "bottom": 684, "right": 727}]}, {"text": "M_{t}=", "page": 1, "boxes": [{"top": 685, "left": 132, "bottom": 695, "right": 186}]}, {"text": "total visual absolute magnitude computed from m\u2081 and r.", "page": 1, "boxes": [{"top": 688, "left": 197, "bottom": 698, "right": 651}]}, {"text": "Finally, the nebulae themselves appear to be of a definite order of", "page": 1, "boxes": [{"top": 718, "left": 138, "bottom": 731, "right": 804}]}, {"text": "absolute luminosity, exhibiting a range of four or five magnitudes about", "page": 1, "boxes": [{"top": 735, "left": 117, "bottom": 748, "right": 802}]}, {"text": "an average value M(visual)=-15.2.^{1} The application of this statistical", "page": 1, "boxes": [{"top": 749, "left": 116, "bottom": 766, "right": 802}]}, {"text": "average to individual cases can rarely be used to advantage, but where", "page": 1, "boxes": [{"top": 767, "left": 116, "bottom": 782, "right": 801}]}, {"text": "considerable numbers are involved, and especially in the various clusters", "page": 1, "boxes": [{"top": 785, "left": 116, "bottom": 797, "right": 801}]}, {"text": "of nebulae, mean apparent luminosities of the nebulae themselves offer", "page": 1, "boxes": [{"top": 799, "left": 117, "bottom": 814, "right": 802}]}, {"text": "reliable estimates of the mean distances.", "page": 1, "boxes": [{"top": 817, "left": 116, "bottom": 828, "right": 489}]}, {"text": "Radial velocities of 46 extra-galactic nebulae are now available, but", "page": 1, "boxes": [{"top": 834, "left": 138, "bottom": 846, "right": 802}]}, {"text": "Downloaded from https://www.pnas.org by 45.113.94.70 on January 5, 2026 from IP address 45.113.94.70.", "page": 2, "boxes": [{"top": 931, "left": 15, "bottom": 456, "right": 31}]}, {"text": "170", "page": 2, "boxes": [{"top": 101, "left": 197, "bottom": 109, "right": 224}]}, {"text": "ASTRONOMY: E. HUBBLE", "page": 2, "boxes": [{"top": 99, "left": 419, "bottom": 112, "right": 661}]}, {"text": "PROC. N. A. S.", "page": 2, "boxes": [{"top": 101, "left": 764, "bottom": 110, "right": 881}]}, {"text": "individual distances are estimated for only 24. For one other, N. G. C.", "page": 2, "boxes": [{"top": 129, "left": 196, "bottom": 143, "right": 882}]}, {"text": "3521, an estimate could probably be made, but no photographs are avail-", "page": 2, "boxes": [{"top": 146, "left": 197, "bottom": 159, "right": 882}]}, {"text": "able at Mount Wilson. The data are given in table 1. The first seven", "page": 2, "boxes": [{"top": 162, "left": 197, "bottom": 176, "right": 883}]}, {"text": "distances are the most reliable, depending, except for M 32 the companion of", "page": 2, "boxes": [{"top": 179, "left": 197, "bottom": 192, "right": 885}]}, {"text": "M 31, upon extensive investigations of many stars involved. The next", "page": 2, "boxes": [{"top": 196, "left": 197, "bottom": 209, "right": 883}]}, {"text": "thirteen distances, depending upon the criterion of a uniform upper limit", "page": 2, "boxes": [{"top": 212, "left": 197, "bottom": 226, "right": 883}]}, {"text": "of stellar luminosity, are subject to considerable probable errors but are", "page": 2, "boxes": [{"top": 229, "left": 197, "bottom": 242, "right": 883}]}, {"text": "believed to be the most reasonable values at present available. The last", "page": 2, "boxes": [{"top": 245, "left": 197, "bottom": 259, "right": 883}]}, {"text": "four objects appear to be in the Virgo Cluster. The distance assigned", "page": 2, "boxes": [{"top": 262, "left": 197, "bottom": 275, "right": 883}]}, {"text": "to the cluster, 2\\times10^{6} parsecs, is derived from the distribution of nebular", "page": 2, "boxes": [{"top": 278, "left": 197, "bottom": 291, "right": 883}]}, {"text": "luminosities, together with luminosities of stars in some of the later-type", "page": 2, "boxes": [{"top": 295, "left": 197, "bottom": 308, "right": 884}]}, {"text": "spirals, and differs somewhat from the Harvard estimate of ten million", "page": 2, "boxes": [{"top": 311, "left": 198, "bottom": 325, "right": 884}]}, {"text": "light years.2", "page": 2, "boxes": [{"top": 328, "left": 197, "bottom": 342, "right": 309}]}, {"text": "The data in the table indicate a linear correlation between distances and", "page": 2, "boxes": [{"top": 345, "left": 220, "bottom": 356, "right": 883}]}, {"text": "velocities, whether the latter are used directly or corrected for solar motion,", "page": 2, "boxes": [{"top": 362, "left": 198, "bottom": 375, "right": 883}]}, {"text": "according to the older solutions. This suggests a new solution for the solar", "page": 2, "boxes": [{"top": 376, "left": 199, "bottom": 393, "right": 884}]}, {"text": "motion in which the distances are introduced as coefficients of the K term,", "page": 2, "boxes": [{"top": 395, "left": 199, "bottom": 406, "right": 883}]}, {"text": "i. e., the velocities are assumed to vary directly with the distances, and", "page": 2, "boxes": [{"top": 411, "left": 198, "bottom": 425, "right": 885}]}, {"text": "hence K represents the velocity at unit distance due to this effect. The", "page": 2, "boxes": [{"top": 426, "left": 198, "bottom": 442, "right": 885}]}, {"text": "equations of condition then take the form", "page": 2, "boxes": [{"top": 444, "left": 199, "bottom": 458, "right": 582}]}, {"text": "rK+X~cos~\\alpha~cos~\\delta+Y~sin~\\alpha~cos~\\delta+Z~sin~\\delta=v.", "page": 2, "boxes": [{"top": 473, "left": 295, "bottom": 488, "right": 787}]}, {"text": "Two solutions have been made, one using the 24 nebulae individually,", "page": 2, "boxes": [{"top": 503, "left": 200, "bottom": 516, "right": 885}]}, {"text": "the other combining them into 9 groups according to proximity in direc-", "page": 2, "boxes": [{"top": 519, "left": 199, "bottom": 532, "right": 885}]}, {"text": "tion and in distance. The results are", "page": 2, "boxes": [{"top": 536, "left": 200, "bottom": 547, "right": 552}]}, {"text": "24 OBJECTS", "page": 2, "boxes": [{"top": 564, "left": 385, "bottom": 572, "right": 458}]}, {"text": "-65\\pm50", "page": 2, "boxes": [{"top": 576, "left": 367, "bottom": 588, "right": 468}]}, {"text": "+226\\pm95", "page": 2, "boxes": [{"top": 589, "left": 367, "bottom": 600, "right": 470}]}, {"text": "-105+40", "page": 2, "boxes": [{"top": 599, "left": 366, "bottom": 611, "right": 466}]}, {"text": "KANK HAK", "page": 2, "boxes": [{"top": 575, "left": 308, "bottom": 677, "right": 331}]}, {"text": "9 GROUPS", "page": 2, "boxes": [{"top": 564, "left": 549, "bottom": 571, "right": 611}]}, {"text": "+3\\pm70", "page": 2, "boxes": [{"top": 575, "left": 527, "bottom": 586, "right": 637}]}, {"text": "+230\\pm120", "page": 2, "boxes": [{"top": 589, "left": 527, "bottom": 600, "right": 638}]}, {"text": "-133 \u00b1 70", "page": 2, "boxes": [{"top": 604, "left": 530, "bottom": 613, "right": 635}]}, {"text": "+513 \u00b1 60 km./sec. per 10^{6} parsecs.", "page": 2, "boxes": [{"top": 617, "left": 531, "bottom": 629, "right": 832}]}, {"text": "+465", "page": 2, "boxes": [{"top": 618, "left": 369, "bottom": 629, "right": 412}]}, {"text": "50", "page": 2, "boxes": [{"top": 618, "left": 447, "bottom": 628, "right": 465}]}, {"text": "286\u00b0", "page": 2, "boxes": [{"top": 638, "left": 383, "bottom": 647, "right": 421}]}, {"text": "+40^{\\circ}", "page": 2, "boxes": [{"top": 648, "left": 366, "bottom": 660, "right": 421}]}, {"text": "306 km./sec.", "page": 2, "boxes": [{"top": 665, "left": 384, "bottom": 676, "right": 483}]}, {"text": "269\u00b0", "page": 2, "boxes": [{"top": 637, "left": 545, "bottom": 646, "right": 582}]}, {"text": "+33^{\\circ}", "page": 2, "boxes": [{"top": 648, "left": 528, "bottom": 662, "right": 584}]}, {"text": "247 km./sec.", "page": 2, "boxes": [{"top": 665, "left": 545, "bottom": 675, "right": 645}]}, {"text": "For such scanty material, so poorly distributed, the results are fairly", "page": 2, "boxes": [{"top": 692, "left": 223, "bottom": 708, "right": 888}]}, {"text": "definite. Differences between the two solutions are due largely to the", "page": 2, "boxes": [{"top": 710, "left": 202, "bottom": 722, "right": 888}]}, {"text": "four Virgo nebulae, which, being the most distant objects and all sharing", "page": 2, "boxes": [{"top": 725, "left": 201, "bottom": 740, "right": 888}]}, {"text": "the peculiar motion of the cluster, unduly influence the value of K and", "page": 2, "boxes": [{"top": 741, "left": 202, "bottom": 758, "right": 888}]}, {"text": "hence of V_{0}. New data on more distant objects will be required to reduce", "page": 2, "boxes": [{"top": 758, "left": 202, "bottom": 773, "right": 888}]}, {"text": "the effect of such peculiar motion. Meanwhile round numbers, inter-", "page": 2, "boxes": [{"top": 774, "left": 202, "bottom": 790, "right": 888}]}, {"text": "mediate between the two solutions, will represent the probable order of", "page": 2, "boxes": [{"top": 791, "left": 202, "bottom": 806, "right": 889}]}, {"text": "the values. For instance, let A=277^{\\circ} D=+36^{\\circ} (Gal. long.=32^{\\circ},", "page": 2, "boxes": [{"top": 804, "left": 202, "bottom": 821, "right": 888}]}, {"text": "lat.=+18^{\\circ}) , V_{0}=280~km./sec. , K=+500 km./sec. per million par-", "page": 2, "boxes": [{"top": 823, "left": 197, "bottom": 839, "right": 887}]}, {"text": "Downloaded from https://www.pnas.org by 45.113.94.70 on January 5, 2026 from IP address 45.113.94.70.", "page": 3, "boxes": [{"top": 931, "left": 15, "bottom": 456, "right": 31}]}, {"text": "VOL. 15, 1929", "page": 3, "boxes": [{"top": 108, "left": 120, "bottom": 118, "right": 230}]}, {"text": "ASTRONOMY: E. HUBBLE", "page": 3, "boxes": [{"top": 108, "left": 346, "bottom": 118, "right": 586}]}, {"text": "171", "page": 3, "boxes": [{"top": 110, "left": 781, "bottom": 117, "right": 807}]}, {"text": "secs. Mr. Str\u00f6mberg has very kindly checked the general order of these", "page": 3, "boxes": [{"top": 137, "left": 121, "bottom": 149, "right": 808}]}, {"text": "values by independent solutions for different groupings of the data.", "page": 3, "boxes": [{"top": 153, "left": 121, "bottom": 167, "right": 743}]}, {"text": "A constant term, introduced into the equations, was found to be small", "page": 3, "boxes": [{"top": 170, "left": 142, "bottom": 183, "right": 807}]}, {"text": "and negative. This seems to dispose of the necessity for the old constant", "page": 3, "boxes": [{"top": 187, "left": 121, "bottom": 200, "right": 807}]}, {"text": "K term. Solutions of this sort have been published by Lundmark, who", "page": 3, "boxes": [{"top": 203, "left": 120, "bottom": 216, "right": 807}]}, {"text": "replaced the old K by k+lr+mr^{2}. His favored solution gave k=513,", "page": 3, "boxes": [{"top": 216, "left": 121, "bottom": 232, "right": 806}]}, {"text": "as against the former value of the order of 700, and hence offered little", "page": 3, "boxes": [{"top": 236, "left": 120, "bottom": 249, "right": 807}]}, {"text": "advantage.", "page": 3, "boxes": [{"top": 253, "left": 121, "bottom": 266, "right": 220}]}, {"text": "TABLE 2", "page": 3, "boxes": [{"top": 277, "left": 413, "bottom": 284, "right": 482}]}, {"text": "-", "page": 3, "boxes": [{"top": 336, "left": 307, "bottom": 338, "right": 321}]}, {"text": "NEBULAE WHOSE DISTANCES ARE ESTIMATED FROM RADIAL VELOCITIES", "page": 3, "boxes": [{"top": 291, "left": 156, "bottom": 300, "right": 760}]}, {"text": "OBJECT", "page": 3, "boxes": [{"top": 307, "left": 197, "bottom": 313, "right": 243}]}, {"text": "m:", "page": 3, "boxes": [{"top": 307, "left": 662, "bottom": 315, "right": 682}]}, {"text": "M_{t}", "page": 3, "boxes": [{"top": 304, "left": 769, "bottom": 315, "right": 791}]}, {"text": "N. G. C. 278", "page": 3, "boxes": [{"top": 318, "left": 121, "bottom": 328, "right": 232}]}, {"text": "+ 650", "page": 3, "boxes": [{"top": 317, "left": 307, "bottom": 328, "right": 358}]}, {"text": "-110", "page": 3, "boxes": [{"top": 318, "left": 432, "bottom": 327, "right": 475}]}, {"text": "1.52", "page": 3, "boxes": [{"top": 318, "left": 547, "bottom": 326, "right": 583}]}, {"text": "12.0", "page": 3, "boxes": [{"top": 318, "left": 655, "bottom": 327, "right": 691}]}, {"text": "- 13.9", "page": 3, "boxes": [{"top": 318, "left": 755, "bottom": 327, "right": 807}]}, {"text": "404", "page": 3, "boxes": [{"top": 333, "left": 205, "bottom": 340, "right": 233}]}, {"text": "25", "page": 3, "boxes": [{"top": 332, "left": 339, "bottom": 340, "right": 359}]}, {"text": "- 65", "page": 3, "boxes": [{"top": 332, "left": 432, "bottom": 341, "right": 475}]}, {"text": "11.1", "page": 3, "boxes": [{"top": 332, "left": 655, "bottom": 340, "right": 690}]}, {"text": "584", "page": 3, "boxes": [{"top": 347, "left": 206, "bottom": 354, "right": 233}]}, {"text": "+1800", "page": 3, "boxes": [{"top": 346, "left": 307, "bottom": 355, "right": 359}]}, {"text": "+ 75", "page": 3, "boxes": [{"top": 346, "left": 433, "bottom": 355, "right": 475}]}, {"text": "3.45", "page": 3, "boxes": [{"top": 346, "left": 545, "bottom": 354, "right": 583}]}, {"text": "10.9", "page": 3, "boxes": [{"top": 346, "left": 655, "bottom": 354, "right": 691}]}, {"text": "16.8", "page": 3, "boxes": [{"top": 345, "left": 771, "bottom": 355, "right": 807}]}, {"text": "936", "page": 3, "boxes": [{"top": 359, "left": 205, "bottom": 368, "right": 232}]}, {"text": "+1300", "page": 3, "boxes": [{"top": 360, "left": 308, "bottom": 369, "right": 358}]}, {"text": "+115", "page": 3, "boxes": [{"top": 359, "left": 433, "bottom": 369, "right": 475}]}, {"text": "2.37", "page": 3, "boxes": [{"top": 360, "left": 546, "bottom": 368, "right": 584}]}, {"text": "11.1", "page": 3, "boxes": [{"top": 360, "left": 655, "bottom": 368, "right": 690}]}, {"text": "15.7", "page": 3, "boxes": [{"top": 360, "left": 771, "bottom": 368, "right": 808}]}, {"text": "1023", "page": 3, "boxes": [{"top": 374, "left": 197, "bottom": 382, "right": 233}]}, {"text": "+ 300", "page": 3, "boxes": [{"top": 372, "left": 307, "bottom": 384, "right": 358}]}, {"text": "10", "page": 3, "boxes": [{"top": 374, "left": 457, "bottom": 382, "right": 475}]}, {"text": "0.62", "page": 3, "boxes": [{"top": 374, "left": 546, "bottom": 382, "right": 582}]}, {"text": "10.2", "page": 3, "boxes": [{"top": 374, "left": 655, "bottom": 381, "right": 689}]}, {"text": "13.8", "page": 3, "boxes": [{"top": 373, "left": 772, "bottom": 382, "right": 808}]}, {"text": "1700", "page": 3, "boxes": [{"top": 390, "left": 197, "bottom": 399, "right": 233}]}, {"text": "+ 800", "page": 3, "boxes": [{"top": 389, "left": 307, "bottom": 399, "right": 357}]}, {"text": "+220", "page": 3, "boxes": [{"top": 389, "left": 433, "bottom": 399, "right": 475}]}, {"text": "1.16", "page": 3, "boxes": [{"top": 390, "left": 547, "bottom": 398, "right": 582}]}, {"text": "12.5", "page": 3, "boxes": [{"top": 390, "left": 655, "bottom": 398, "right": 690}]}, {"text": "12.8", "page": 3, "boxes": [{"top": 390, "left": 771, "bottom": 398, "right": 808}]}, {"text": "2681", "page": 3, "boxes": [{"top": 404, "left": 196, "bottom": 412, "right": 232}]}, {"text": "+ 700", "page": 3, "boxes": [{"top": 403, "left": 307, "bottom": 413, "right": 357}]}, {"text": "10", "page": 3, "boxes": [{"top": 404, "left": 457, "bottom": 411, "right": 475}]}, {"text": "1.42", "page": 3, "boxes": [{"top": 403, "left": 547, "bottom": 412, "right": 582}]}, {"text": "10.7", "page": 3, "boxes": [{"top": 404, "left": 655, "bottom": 412, "right": 691}]}, {"text": "15.0", "page": 3, "boxes": [{"top": 403, "left": 771, "bottom": 411, "right": 807}]}, {"text": "2683", "page": 3, "boxes": [{"top": 418, "left": 196, "bottom": 426, "right": 233}]}, {"text": "+ 400", "page": 3, "boxes": [{"top": 417, "left": 308, "bottom": 427, "right": 357}]}, {"text": "+ 65", "page": 3, "boxes": [{"top": 417, "left": 433, "bottom": 427, "right": 475}]}, {"text": "0.67", "page": 3, "boxes": [{"top": 417, "left": 545, "bottom": 426, "right": 583}]}, {"text": "9.9", "page": 3, "boxes": [{"top": 417, "left": 662, "bottom": 426, "right": 690}]}, {"text": "14.3", "page": 3, "boxes": [{"top": 417, "left": 771, "bottom": 426, "right": 806}]}, {"text": "2841", "page": 3, "boxes": [{"top": 432, "left": 196, "bottom": 440, "right": 233}]}, {"text": "+ 600", "page": 3, "boxes": [{"top": 431, "left": 307, "bottom": 441, "right": 359}]}, {"text": "20", "page": 3, "boxes": [{"top": 432, "left": 456, "bottom": 440, "right": 475}]}, {"text": "1.24", "page": 3, "boxes": [{"top": 431, "left": 547, "bottom": 439, "right": 583}]}, {"text": "9.4", "page": 3, "boxes": [{"top": 432, "left": 662, "bottom": 439, "right": 691}]}, {"text": "16.1", "page": 3, "boxes": [{"top": 431, "left": 771, "bottom": 439, "right": 806}]}, {"text": "3034", "page": 3, "boxes": [{"top": 445, "left": 196, "bottom": 453, "right": 234}]}, {"text": "+ 290", "page": 3, "boxes": [{"top": 444, "left": 307, "bottom": 455, "right": 358}]}, {"text": "-105", "page": 3, "boxes": [{"top": 444, "left": 432, "bottom": 454, "right": 476}]}, {"text": "0.79", "page": 3, "boxes": [{"top": 444, "left": 545, "bottom": 453, "right": 582}]}, {"text": "9.0", "page": 3, "boxes": [{"top": 445, "left": 663, "bottom": 453, "right": 691}]}, {"text": "15.5", "page": 3, "boxes": [{"top": 444, "left": 771, "bottom": 453, "right": 807}]}, {"text": "-", "page": 3, "boxes": [{"top": 407, "left": 431, "bottom": 409, "right": 446}]}, {"text": "3115", "page": 3, "boxes": [{"top": 461, "left": 196, "bottom": 470, "right": 233}]}, {"text": "1.00", "page": 3, "boxes": [{"top": 461, "left": 547, "bottom": 470, "right": 582}]}, {"text": "9.5", "page": 3, "boxes": [{"top": 461, "left": 661, "bottom": 469, "right": 691}]}, {"text": "1.74", "page": 3, "boxes": [{"top": 475, "left": 547, "bottom": 484, "right": 582}]}, {"text": "10.0", "page": 3, "boxes": [{"top": 475, "left": 655, "bottom": 483, "right": 689}]}, {"text": "3368", "page": 3, "boxes": [{"top": 475, "left": 196, "bottom": 484, "right": 233}]}, {"text": "3379", "page": 3, "boxes": [{"top": 489, "left": 196, "bottom": 498, "right": 232}]}, {"text": "3489", "page": 3, "boxes": [{"top": 503, "left": 196, "bottom": 511, "right": 234}]}, {"text": "+ 600", "page": 3, "boxes": [{"top": 461, "left": 308, "bottom": 471, "right": 358}]}, {"text": "+ 940", "page": 3, "boxes": [{"top": 476, "left": 308, "bottom": 485, "right": 359}]}, {"text": "+ 810", "page": 3, "boxes": [{"top": 488, "left": 307, "bottom": 499, "right": 358}]}, {"text": "+ 600", "page": 3, "boxes": [{"top": 502, "left": 307, "bottom": 512, "right": 357}]}, {"text": "+105", "page": 3, "boxes": [{"top": 461, "left": 434, "bottom": 470, "right": 476}]}, {"text": "+ 70", "page": 3, "boxes": [{"top": 474, "left": 432, "bottom": 485, "right": 475}]}, {"text": "+ 65", "page": 3, "boxes": [{"top": 489, "left": 433, "bottom": 498, "right": 475}]}, {"text": "+ 50", "page": 3, "boxes": [{"top": 502, "left": 432, "bottom": 512, "right": 474}]}, {"text": "15.5", "page": 3, "boxes": [{"top": 461, "left": 771, "bottom": 470, "right": 806}]}, {"text": "16.2", "page": 3, "boxes": [{"top": 475, "left": 772, "bottom": 484, "right": 806}]}, {"text": "16.4", "page": 3, "boxes": [{"top": 489, "left": 771, "bottom": 497, "right": 807}]}, {"text": "14.0", "page": 3, "boxes": [{"top": 503, "left": 772, "bottom": 511, "right": 807}]}, {"text": "1.49", "page": 3, "boxes": [{"top": 489, "left": 547, "bottom": 497, "right": 582}]}, {"text": "9.4", "page": 3, "boxes": [{"top": 488, "left": 662, "bottom": 497, "right": 691}]}, {"text": "1.10", "page": 3, "boxes": [{"top": 503, "left": 547, "bottom": 511, "right": 582}]}, {"text": "11.2", "page": 3, "boxes": [{"top": 503, "left": 655, "bottom": 511, "right": 690}]}, {"text": "1.27", "page": 3, "boxes": [{"top": 519, "left": 547, "bottom": 527, "right": 582}]}, {"text": "10.1", "page": 3, "boxes": [{"top": 518, "left": 654, "bottom": 527, "right": 689}]}, {"text": "15.4", "page": 3, "boxes": [{"top": 519, "left": 771, "bottom": 527, "right": 808}]}, {"text": "3521", "page": 3, "boxes": [{"top": 519, "left": 196, "bottom": 528, "right": 232}]}, {"text": "3623", "page": 3, "boxes": [{"top": 533, "left": 196, "bottom": 542, "right": 233}]}, {"text": "+ 95", "page": 3, "boxes": [{"top": 519, "left": 432, "bottom": 529, "right": 474}]}, {"text": "+ 35", "page": 3, "boxes": [{"top": 532, "left": 432, "bottom": 543, "right": 475}]}, {"text": "95", "page": 3, "boxes": [{"top": 547, "left": 456, "bottom": 555, "right": 475}]}, {"text": "1.53", "page": 3, "boxes": [{"top": 533, "left": 547, "bottom": 541, "right": 582}]}, {"text": "+ 730", "page": 3, "boxes": [{"top": 519, "left": 307, "bottom": 530, "right": 358}]}, {"text": "+ 800", "page": 3, "boxes": [{"top": 533, "left": 307, "bottom": 543, "right": 358}]}, {"text": "+ 800", "page": 3, "boxes": [{"top": 546, "left": 307, "bottom": 557, "right": 357}]}, {"text": "+ 580", "page": 3, "boxes": [{"top": 560, "left": 306, "bottom": 571, "right": 358}]}, {"text": "9.9", "page": 3, "boxes": [{"top": 533, "left": 662, "bottom": 541, "right": 690}]}, {"text": "4111", "page": 3, "boxes": [{"top": 548, "left": 196, "bottom": 555, "right": 232}]}, {"text": "1.79", "page": 3, "boxes": [{"top": 547, "left": 547, "bottom": 555, "right": 581}]}, {"text": "10.1", "page": 3, "boxes": [{"top": 547, "left": 655, "bottom": 555, "right": 689}]}, {"text": "16.0", "page": 3, "boxes": [{"top": 533, "left": 771, "bottom": 541, "right": 806}]}, {"text": "16.1", "page": 3, "boxes": [{"top": 547, "left": 771, "bottom": 555, "right": 807}]}, {"text": "14.3", "page": 3, "boxes": [{"top": 560, "left": 771, "bottom": 569, "right": 807}]}, {"text": "4526", "page": 3, "boxes": [{"top": 561, "left": 196, "bottom": 569, "right": 233}]}, {"text": "20", "page": 3, "boxes": [{"top": 561, "left": 456, "bottom": 569, "right": 475}]}, {"text": "1.20", "page": 3, "boxes": [{"top": 561, "left": 547, "bottom": 569, "right": 583}]}, {"text": "11.1", "page": 3, "boxes": [{"top": 560, "left": 655, "bottom": 569, "right": 689}]}, {"text": "4565", "page": 3, "boxes": [{"top": 577, "left": 196, "bottom": 586, "right": 233}]}, {"text": "2.35", "page": 3, "boxes": [{"top": 576, "left": 546, "bottom": 585, "right": 583}]}, {"text": "15.9", "page": 3, "boxes": [{"top": 577, "left": 771, "bottom": 585, "right": 807}]}, {"text": "11.0", "page": 3, "boxes": [{"top": 576, "left": 655, "bottom": 586, "right": 690}]}, {"text": "9.1", "page": 3, "boxes": [{"top": 591, "left": 662, "bottom": 598, "right": 690}]}, {"text": "4594", "page": 3, "boxes": [{"top": 592, "left": 196, "bottom": 599, "right": 233}]}, {"text": "+1100", "page": 3, "boxes": [{"top": 577, "left": 307, "bottom": 587, "right": 357}]}, {"text": "+1140", "page": 3, "boxes": [{"top": 591, "left": 307, "bottom": 600, "right": 358}]}, {"text": "+ 900", "page": 3, "boxes": [{"top": 604, "left": 307, "bottom": 615, "right": 358}]}, {"text": "+ 650", "page": 3, "boxes": [{"top": 617, "left": 307, "bottom": 628, "right": 357}]}, {"text": "75", "page": 3, "boxes": [{"top": 577, "left": 456, "bottom": 585, "right": 475}]}, {"text": "+ 25", "page": 3, "boxes": [{"top": 590, "left": 432, "bottom": 600, "right": 475}]}, {"text": "-130", "page": 3, "boxes": [{"top": 604, "left": 433, "bottom": 614, "right": 475}]}, {"text": "-215", "page": 3, "boxes": [{"top": 618, "left": 432, "bottom": 627, "right": 475}]}, {"text": "2.23", "page": 3, "boxes": [{"top": 591, "left": 546, "bottom": 599, "right": 582}]}, {"text": "2.06", "page": 3, "boxes": [{"top": 604, "left": 545, "bottom": 613, "right": 581}]}, {"text": "17.6", "page": 3, "boxes": [{"top": 590, "left": 771, "bottom": 599, "right": 806}]}, {"text": "15.5", "page": 3, "boxes": [{"top": 604, "left": 771, "bottom": 613, "right": 807}]}, {"text": "11.1", "page": 3, "boxes": [{"top": 604, "left": 655, "bottom": 612, "right": 690}]}, {"text": "5005", "page": 3, "boxes": [{"top": 605, "left": 197, "bottom": 613, "right": 232}]}, {"text": "5866", "page": 3, "boxes": [{"top": 619, "left": 197, "bottom": 627, "right": 233}]}, {"text": "1.73", "page": 3, "boxes": [{"top": 619, "left": 547, "bottom": 627, "right": 582}]}, {"text": "11.7", "page": 3, "boxes": [{"top": 618, "left": 655, "bottom": 627, "right": 691}]}, {"text": "-14.5", "page": 3, "boxes": [{"top": 619, "left": 755, "bottom": 626, "right": 807}]}, {"text": "Mean", "page": 3, "boxes": [{"top": 636, "left": 121, "bottom": 644, "right": 167}]}, {"text": "10.5", "page": 3, "boxes": [{"top": 635, "left": 655, "bottom": 643, "right": 691}]}, {"text": "-15.3", "page": 3, "boxes": [{"top": 635, "left": 754, "bottom": 643, "right": 807}]}, {"text": "The residuals for the two solutions given above average 150 and 110", "page": 3, "boxes": [{"top": 666, "left": 143, "bottom": 678, "right": 807}]}, {"text": "km./sec. and should represent the average peculiar motions of the in-", "page": 3, "boxes": [{"top": 682, "left": 122, "bottom": 696, "right": 806}]}, {"text": "dividual nebulae and of the groups, respectively. In order to exhibit", "page": 3, "boxes": [{"top": 699, "left": 122, "bottom": 712, "right": 807}]}, {"text": "the results in a graphical form, the solar motion has been eliminated from", "page": 3, "boxes": [{"top": 714, "left": 122, "bottom": 729, "right": 806}]}, {"text": "the observed velocities and the remainders, the distance terms plus the", "page": 3, "boxes": [{"top": 731, "left": 122, "bottom": 744, "right": 807}]}, {"text": "residuals, have been plotted against the distances. The run of the re-", "page": 3, "boxes": [{"top": 748, "left": 122, "bottom": 761, "right": 805}]}, {"text": "siduals is about as smooth as can be expected, and in general the form of", "page": 3, "boxes": [{"top": 765, "left": 122, "bottom": 778, "right": 810}]}, {"text": "the solutions appears to be adequate.", "page": 3, "boxes": [{"top": 781, "left": 122, "bottom": 796, "right": 464}]}, {"text": "The 22 nebulae for which distances are not available can be treated in", "page": 3, "boxes": [{"top": 798, "left": 143, "bottom": 808, "right": 808}]}, {"text": "two ways. First, the mean distance of the group derived from the mean", "page": 3, "boxes": [{"top": 813, "left": 121, "bottom": 830, "right": 808}]}, {"text": "apparent magnitudes can be compared with the mean of the velocities", "page": 3, "boxes": [{"top": 829, "left": 122, "bottom": 846, "right": 805}]}, {"text": "Downloaded from https://www.pnas.org by 45.113.94.70 on January 5, 2026 from IP address 45.113.94.70.", "page": 4, "boxes": [{"top": 931, "left": 15, "bottom": 456, "right": 31}]}, {"text": "172", "page": 4, "boxes": [{"top": 108, "left": 198, "bottom": 115, "right": 225}]}, {"text": "ASTRONOMY: E. HUBBLE", "page": 4, "boxes": [{"top": 107, "left": 421, "bottom": 116, "right": 662}]}, {"text": "PROC. N. A. S.", "page": 4, "boxes": [{"top": 108, "left": 766, "bottom": 116, "right": 882}]}, {"text": "corrected for solar motion. The result, 745 km./sec. for a distance of", "page": 4, "boxes": [{"top": 135, "left": 197, "bottom": 149, "right": 884}]}, {"text": "1.4\\times10^{6} parsecs, falls between the two previous solutions and indicates", "page": 4, "boxes": [{"top": 151, "left": 199, "bottom": 166, "right": 882}]}, {"text": "a value for K of 530 as against the proposed value, 500 km./sec.", "page": 4, "boxes": [{"top": 169, "left": 197, "bottom": 182, "right": 797}]}, {"text": "Secondly, the scatter of the individual nebulae can be examined by", "page": 4, "boxes": [{"top": 186, "left": 218, "bottom": 199, "right": 883}]}, {"text": "assuming the relation between distances and velocities as previously", "page": 4, "boxes": [{"top": 203, "left": 197, "bottom": 216, "right": 883}]}, {"text": "determined. Distances can then be calculated from the velocities cor-", "page": 4, "boxes": [{"top": 220, "left": 197, "bottom": 231, "right": 882}]}, {"text": "rected for solar motion, and absolute magnitudes can be derived from the", "page": 4, "boxes": [{"top": 237, "left": 196, "bottom": 249, "right": 883}]}, {"text": "apparent magnitudes. The results are given in table 2 and may be", "page": 4, "boxes": [{"top": 253, "left": 197, "bottom": 266, "right": 884}]}, {"text": "compared with the distribution of absolute magnitudes among the nebulae", "page": 4, "boxes": [{"top": 270, "left": 197, "bottom": 283, "right": 883}]}, {"text": "in table 1, whose distances are derived from other criteria. N. G. C. 404", "page": 4, "boxes": [{"top": 286, "left": 196, "bottom": 299, "right": 883}]}, {"text": "+1000 KM", "page": 4, "boxes": [{"top": 356, "left": 269, "bottom": 361, "right": 313}]}, {"text": "500KM", "page": 4, "boxes": [{"top": 416, "left": 279, "bottom": 422, "right": 311}]}, {"text": "VELOCITY", "page": 4, "boxes": [{"top": 435, "left": 318, "bottom": 465, "right": 326}]}, {"text": "DISTANCE", "page": 4, "boxes": [{"top": 513, "left": 410, "bottom": 519, "right": 459}]}, {"text": "O", "page": 4, "boxes": [{"top": 523, "left": 373, "bottom": 528, "right": 381}]}, {"text": "2x10 PARSECS", "page": 4, "boxes": [{"top": 521, "left": 705, "bottom": 528, "right": 786}]}, {"text": "10* PARSECS", "page": 4, "boxes": [{"top": 521, "left": 534, "bottom": 528, "right": 594}]}, {"text": "FIGURE 1", "page": 4, "boxes": [{"top": 531, "left": 499, "bottom": 539, "right": 575}]}, {"text": "Velocity-Distance Relation among Extra-Galactic Nebulae.", "page": 4, "boxes": [{"top": 544, "left": 304, "bottom": 555, "right": 771}]}, {"text": "Radial velocities, corrected for solar motion, are plotted against", "page": 4, "boxes": [{"top": 561, "left": 289, "bottom": 572, "right": 806}]}, {"text": "distances estimated from involved stars and mean luminosities of", "page": 4, "boxes": [{"top": 575, "left": 271, "bottom": 584, "right": 807}]}, {"text": "nebulae in a cluster. The black discs and full line represent the", "page": 4, "boxes": [{"top": 589, "left": 271, "bottom": 599, "right": 807}]}, {"text": "solution for solar motion using the nebulae individually; the circles", "page": 4, "boxes": [{"top": 603, "left": 271, "bottom": 613, "right": 805}]}, {"text": "and broken line represent the solution combining the nebulae into", "page": 4, "boxes": [{"top": 617, "left": 271, "bottom": 627, "right": 807}]}, {"text": "groups; the cross represents the mean velocity corresponding to", "page": 4, "boxes": [{"top": 631, "left": 271, "bottom": 642, "right": 805}]}, {"text": "the mean distance of 22 nebulae whose distances could not be esti-", "page": 4, "boxes": [{"top": 645, "left": 271, "bottom": 654, "right": 806}]}, {"text": "mated individually.", "page": 4, "boxes": [{"top": 658, "left": 271, "bottom": 669, "right": 426}]}, {"text": "can be excluded, since the observed velocity is so small that the peculiar", "page": 4, "boxes": [{"top": 690, "left": 197, "bottom": 703, "right": 883}]}, {"text": "motion must be large in comparison with the distance effect. The object", "page": 4, "boxes": [{"top": 706, "left": 197, "bottom": 719, "right": 882}]}, {"text": "is not necessarily an exception, however, since a distance can be assigned", "page": 4, "boxes": [{"top": 723, "left": 197, "bottom": 736, "right": 882}]}, {"text": "for which the peculiar motion and the absolute magnitude are both within", "page": 4, "boxes": [{"top": 740, "left": 196, "bottom": 752, "right": 883}]}, {"text": "the range previously determined. The two mean magnitudes, -15.3", "page": 4, "boxes": [{"top": 752, "left": 196, "bottom": 771, "right": 883}]}, {"text": "and -15.5 , the ranges, 4.9 and 5.0 mag., and the frequency distributions", "page": 4, "boxes": [{"top": 770, "left": 197, "bottom": 786, "right": 882}]}, {"text": "are closely similar for these two entirely independent sets of data; and", "page": 4, "boxes": [{"top": 788, "left": 197, "bottom": 802, "right": 883}]}, {"text": "even the slight difference in mean magnitudes can be attributed to the", "page": 4, "boxes": [{"top": 804, "left": 196, "bottom": 818, "right": 883}]}, {"text": "selected, very bright, nebulae in the Virgo Cluster. This entirely unforced", "page": 4, "boxes": [{"top": 820, "left": 197, "bottom": 834, "right": 882}]}, {"text": "agreement supports the validity of the velocity-distance relation in a very", "page": 4, "boxes": [{"top": 836, "left": 197, "bottom": 851, "right": 883}]}, {"text": "Downloaded from https://www.pnas.org by 45.113.94.70 on January 5, 2026 from IP address 45.113.94.70.", "page": 5, "boxes": [{"top": 931, "left": 15, "bottom": 456, "right": 31}]}, {"text": "VOL. 15, 1929", "page": 5, "boxes": [{"top": 109, "left": 120, "bottom": 118, "right": 228}]}, {"text": "ASTRONOMY: E. HUBBLE", "page": 5, "boxes": [{"top": 109, "left": 343, "bottom": 118, "right": 584}]}, {"text": "173", "page": 5, "boxes": [{"top": 109, "left": 778, "bottom": 118, "right": 805}]}, {"text": "evident matter. Finally, it is worth recording that the frequency distribu-", "page": 5, "boxes": [{"top": 137, "left": 120, "bottom": 151, "right": 807}]}, {"text": "tion of absolute magnitudes in the two tables combined is comparable", "page": 5, "boxes": [{"top": 153, "left": 120, "bottom": 167, "right": 806}]}, {"text": "with those found in the various clusters of nebulae.", "page": 5, "boxes": [{"top": 170, "left": 120, "bottom": 180, "right": 594}]}, {"text": "The results establish a roughly linear relation between velocities and", "page": 5, "boxes": [{"top": 187, "left": 142, "bottom": 199, "right": 805}]}, {"text": "distances among nebulae for which velocities have been previously pub-", "page": 5, "boxes": [{"top": 203, "left": 120, "bottom": 217, "right": 804}]}, {"text": "lished, and the relation appears to dominate the distribution of velocities.", "page": 5, "boxes": [{"top": 220, "left": 120, "bottom": 233, "right": 803}]}, {"text": "In order to investigate the matter on a much larger scale, Mr. Humason", "page": 5, "boxes": [{"top": 237, "left": 120, "bottom": 249, "right": 805}]}, {"text": "at Mount Wilson has initiated a program of determining velocities", "page": 5, "boxes": [{"top": 253, "left": 120, "bottom": 266, "right": 805}]}, {"text": "of the most distant nebulae that can be observed with confidence.", "page": 5, "boxes": [{"top": 269, "left": 120, "bottom": 280, "right": 806}]}, {"text": "These, naturally, are the brightest nebulae in clusters of nebulae.", "page": 5, "boxes": [{"top": 286, "left": 120, "bottom": 299, "right": 805}]}, {"text": "The first definite result,\u00b9 v=+3779~km./sec. for N. G. C. 7619, is", "page": 5, "boxes": [{"top": 301, "left": 121, "bottom": 316, "right": 807}]}, {"text": "thoroughly consistent with the present conclusions. Corrected for the", "page": 5, "boxes": [{"top": 318, "left": 119, "bottom": 332, "right": 806}]}, {"text": "solar motion, this velocity is +3910, which, with K=500 , corresponds to", "page": 5, "boxes": [{"top": 334, "left": 121, "bottom": 349, "right": 805}]}, {"text": "a distance of 7.8\\times10^{6} parsecs. Since the apparent magnitude is 11.8,", "page": 5, "boxes": [{"top": 350, "left": 120, "bottom": 365, "right": 803}]}, {"text": "the absolute magnitude at such a distance is -17.65, which is of the", "page": 5, "boxes": [{"top": 369, "left": 120, "bottom": 381, "right": 807}]}, {"text": "right order for the brightest nebulae in a cluster. A preliminary dis-", "page": 5, "boxes": [{"top": 385, "left": 120, "bottom": 398, "right": 805}]}, {"text": "tance, derived independently from the cluster of which this nebula appears", "page": 5, "boxes": [{"top": 402, "left": 121, "bottom": 415, "right": 804}]}, {"text": "to be a member, is of the order of 7\\times10^{6} parsecs.", "page": 5, "boxes": [{"top": 416, "left": 120, "bottom": 431, "right": 591}]}, {"text": "New data to be expected in the near future may modify the significance", "page": 5, "boxes": [{"top": 435, "left": 142, "bottom": 448, "right": 805}]}, {"text": "of the present investigation or, if confirmatory, will lead to a solution", "page": 5, "boxes": [{"top": 451, "left": 120, "bottom": 464, "right": 806}]}, {"text": "having many times the weight. For this reason it is thought premature", "page": 5, "boxes": [{"top": 467, "left": 120, "bottom": 481, "right": 804}]}, {"text": "to discuss in detail the obvious consequences of the present results. For", "page": 5, "boxes": [{"top": 484, "left": 120, "bottom": 497, "right": 806}]}, {"text": "example, if the solar motion with respect to the clusters represents the", "page": 5, "boxes": [{"top": 501, "left": 120, "bottom": 514, "right": 805}]}, {"text": "rotation of the galactic system, this motion could be subtracted from the", "page": 5, "boxes": [{"top": 517, "left": 120, "bottom": 530, "right": 806}]}, {"text": "results for the nebulae and the remainder would represent the motion of", "page": 5, "boxes": [{"top": 534, "left": 120, "bottom": 547, "right": 807}]}, {"text": "the galactic system with respect to the extra-galactic nebulae.", "page": 5, "boxes": [{"top": 550, "left": 120, "bottom": 563, "right": 691}]}, {"text": "The outstanding feature, however, is the possibility that the velocity-", "page": 5, "boxes": [{"top": 567, "left": 140, "bottom": 580, "right": 805}]}, {"text": "distance relation may represent the de Sitter effect, and hence that numer-", "page": 5, "boxes": [{"top": 584, "left": 120, "bottom": 596, "right": 804}]}, {"text": "ical data may be introduced into discussions of the general curvature of", "page": 5, "boxes": [{"top": 600, "left": 119, "bottom": 613, "right": 806}]}, {"text": "space. In the de Sitter cosmology, displacements of the spectra arise", "page": 5, "boxes": [{"top": 617, "left": 119, "bottom": 630, "right": 805}]}, {"text": "from two sources, an apparent slowing down of atomic vibrations and a", "page": 5, "boxes": [{"top": 631, "left": 119, "bottom": 646, "right": 805}]}, {"text": "general tendency of material particles to scatter. The latter involves an", "page": 5, "boxes": [{"top": 648, "left": 119, "bottom": 663, "right": 805}]}, {"text": "acceleration and hence introduces the element of time. The relative im-", "page": 5, "boxes": [{"top": 666, "left": 119, "bottom": 677, "right": 804}]}, {"text": "portance of these two effects should determine the form of the relation", "page": 5, "boxes": [{"top": 681, "left": 119, "bottom": 696, "right": 805}]}, {"text": "between distances and observed velocities; and in this connection it may", "page": 5, "boxes": [{"top": 699, "left": 119, "bottom": 711, "right": 805}]}, {"text": "be emphasized that the linear relation found in the present discussion is a", "page": 5, "boxes": [{"top": 716, "left": 119, "bottom": 729, "right": 805}]}, {"text": "first approximation representing a restricted range in distance.", "page": 5, "boxes": [{"top": 732, "left": 119, "bottom": 746, "right": 697}]}, {"text": "1 Mt. Wilson Contr., No. 324; Astroph. J., Chicago, Ill., 64, 1926 (321).", "page": 5, "boxes": [{"top": 753, "left": 142, "bottom": 764, "right": 712}]}, {"text": "Harvard Coll. Obs. Circ., 294, 1926.", "page": 5, "boxes": [{"top": 767, "left": 155, "bottom": 777, "right": 436}]}, {"text": "Mon. Not. R. Astr. Soc., 85, 1925 (865-894).", "page": 5, "boxes": [{"top": 781, "left": 156, "bottom": 791, "right": 516}]}, {"text": "4 These PROCEEDINGS, 15, 1929 (167).", "page": 5, "boxes": [{"top": 794, "left": 143, "bottom": 804, "right": 451}]}]] \ No newline at end of file diff --git a/tests/fixtures/hubble_docai_bboxes.pkl b/tests/fixtures/hubble_docai_bboxes.pkl index b93555b..1c05a5e 100644 Binary files a/tests/fixtures/hubble_docai_bboxes.pkl and b/tests/fixtures/hubble_docai_bboxes.pkl differ diff --git a/tests/fixtures/hubble_docai_golden.md b/tests/fixtures/hubble_docai_golden.md index 418d2fd..09778ce 100644 --- a/tests/fixtures/hubble_docai_golden.md +++ b/tests/fixtures/hubble_docai_golden.md @@ -1,275 +1,274 @@ -appearance the spectrum is very much like spectra of the Milky Way -clouds in Sagittarius and Cygnus, and is also similar to spectra of -binary stars of the W Ursae Majoris type, where the widening and depth -of the lines are affected by the rapid rotation of the stars involved. -The wide shallow absorption lines observed in the spectrum of N. G. C. -7619 have been noticed in the spectra of other extra-galactic nebulae, -and may be due to a dispersion in velocity and a blending of the -spectral types of the many stars which presumably exist in the central -parts of these nebulae. The lack of depth in the absorption lines -seems to be more pronounced among the smaller and fainter nebulae, and -in N. G. C. 7619 the absorption is very weak. It is hoped that -velocities of more of these interesting objects will soon be -available. +appearance the spectrum is very much like spectra of the Milky Way +clouds in Sagittarius and Cygnus, and is also similar to spectra of +binary stars of the W Ursae Majoris type, where the widening and depth +of the lines are affected by the rapid rotation of the stars involved. +The wide shallow absorption lines observed in the spectrum of N. G. C. +7619 have been noticed in the spectra of other extra-galactic nebulae, +and may be due to a dispersion in velocity and a blending of the +spectral types of the many stars which presumably exist in the central +parts of these nebulae. The lack of depth in the absorption lines +seems to be more pronounced among the smaller and fainter nebulae, and +in N. G. C. 7619 the absorption is very weak. It is hoped that +velocities of more of these interesting objects will soon be +available. -## A RELATION BETWEEN DISTANCE AND RADIAL VELOCITY AMONG EXTRA-GALACTIC NEBULAE +# A RELATION BETWEEN DISTANCE AND RADIAL VELOCITY AMONG EXTRA-GALACTIC NEBULAE -BY EDWIN HUBBLE MOUNT WILSON OBSERVATORY, CARNEGIE INSTITUTION OF -WASHINGTON Communicated January 17, 1929 Determinations of the motion -of the sun with respect to the extra-galactic nebulae have involved a -$K$ term of several hundred kilometers which appears to be variable. -Explanations of this paradox have been sought in a correlation between -apparent radial velocities and distances, but so far the results have -not been convincing. The present paper is a re-examination of the -question, based on only those nebular distances which are believed to -be fairly reliable. Distances of extra-galactic nebulae depend -ultimately upon the appli-cation of absolute-luminosity criteria to -involved stars whose types can be recognized. These include, among -others, Cepheid variables, novae, and blue stars involved in emission -nebulosity. Numerical values depend upon the zero point of the period- -luminosity relation among Cepheids, the other criteria merely check -the order of the distances. This method is restricted to the few -nebulae which are well resolved by existing instruments. A study of -these nebulae, together with those in which any stars at all can be -recognized, indicates the probability of an approximately uniform -upper limit to the absolute luminosity of stars, in the late-type -spirals and irregular nebulae at least, of the order of $M$ -(photographic) $=-6.3$. 1 The apparent luminosities of the brightest -stars in such nebulae are thus criteria which, although rough and to -be applied with caution, +#### BY EDWIN HUBBLE -furnish reasonable estimates of the distances of all extra-galactic -systems in which even a few stars can be detected. +MOUNT WILSON OBSERVATORY, CARNEGIE INSTITUTION OF WASHINGTON +Communicated January 17, 1929 Determinations of the motion of the sun +with respect to the extra-galactic nebulae have involved a $K$ term of +several hundred kilometers which appears to be variable. Explanations +of this paradox have been sought in a correlation between apparent +radial velocities and distances, but so far the results have not been +convincing. The present paper is a re-examination of the question, +based on only those nebular distances which are believed to be fairly +reliable. Distances of extra-galactic nebulae depend ultimately upon +the application of absolute-luminosity criteria to involved stars +whose types can be recognized. These include, among others, Cepheid +variables, novae, and blue stars involved in emission nebulosity. +Numerical values depend upon the zero point of the period-luminosity +relation among Cepheids, the other criteria merely check the order of +the distances. This method is restricted to the few nebulae which are +well resolved by existing instruments. A study of these nebulae, +together with those in which any stars at all can be recognized, +indicates the probability of an approximately uniform upper limit to +the absolute luminosity of stars, in the late-type spirals and +irregular nebulae at least, of the order of $M$ (photographic) = +$-6.3.1$ The apparent luminosities of the brightest stars in such +nebulae are thus criteria which, although rough and to be applied with +caution, -## TABLE 1 +furnish reasonable estimates of the distances of all extra-galactic +systems in which even a few stars can be detected. -NEBULAE WHOSE DISTANCES HAVE BEEN ESTIMATED FROM STARS INVOLVED OR -FROM MEAN LUMINOSITIES IN A CLUSTER +## TABLE 1 + +### NEBULAE WHOSE DISTANCES HAVE BEEN ESTIMATED FROM STARS INVOLVED OR FROM MEAN LUMINOSITIES IN A CLUSTER -| OBJECT | $m$ | $r$ | | $m_{t}$ | $M$ | +| | | | | | | | --- | --- | --- | --- | --- | --- | -| S. Mag. | | 0.032 | + 170 | 1.5 | -16.0 | -| L. Mag. | .. | 0.034 | + 290 | 0.5 | 17.2 | -| N. G. C. 6822 | | 0.214 | — 130 | 9.0 | 12.7 | -| 598 | .. | 0.263 | — 70 | 7.0 | 15.1 | -| 221 | .. | 0.275 | — 185 | 8.8 | 13.4 | -| 224 | .. | 0.275 | — 220 | 5.0 | 17.2 | -| 5457 | 17.0 | 0.45 | + 200 | 9.9 | 13.3 | -| 4736 | 17.3 | 0.5 | + 290 | 8.4 | 15.1 | -| 5194 | 17.3 | 0.5 | + 270 | 7.4 | 16.1 | -| 4449 | 17.8 | 0.63 | + 200 | 9.5 | 14.5 | -| 4214 | 18.3 | 0.8 | + 300 | 11.3 | 13.2 | -| 3031 | 18.5 | 0.9 | — 30 | 8.3 | 16.4 | -| 3627 | 18.5 | 0.9 | + 650 | 9.1 | 15.7 | -| 4826 | 18.5 | 0.9 | + 150 | 9.0 | 15.7 | -| 5236 | 18.5 | 0.9 | + 500 | 10.4 | 14.4 | -| 1068 | 18.7 | 1.0 | + 920 | 9.1 | 15.9 | -| 5055 | 19.0 | 1.1 | + 450 | 9.6 | 15.6 | -| 7331 | 19.0 | 1.1 | + 500 | 10.4 | 14.8 | -| 4258 | 19.5 | 1.4 | + 500 | 8.7 | 17.0 | -| 4151 | 20.0 | 1.7 | + 960 | 12.0 | 14.2 | -| 4382 | .. | 2.0 | + 500 | 10.0 | 16.5 | -| 4472 | .. | 2.0 | + 850 | 8.8 | 17.7 | -| 4486 | .. | 2.0 | + 800 | 9.7 | 16.8 | -| 4649 | .. | 2.0 | +1090 | 9.5 | 17.0 | -| Mean | | | | | -15.5 | +| OBJECT | m | r | | m$_{t}$ | M | +| S. Mag. | | 0.032 | + 170 | 1.5 | -16.0 | +| L. Mag. | .. | 0.034 | + 290 | 0.5 | 17.2 | +| N. G. C. 6822 | | 0.214 | − 130 | 9.0 | 12.7 | +| 598 | .. | 0.263 | − 70 | 7.0 | 15.1 | +| 221 | .. | 0.275 | − 185 | 8.8 | 13.4 | +| 224 | .. | 0.275 | − 220 | 5.0 | 17.2 | +| 5457 | 17.0 | 0.45 | + 200 | 9.9 | 13.3 | +| 4736 | 17.3 | 0.5 | + 290 | 8.4 | 15.1 | +| 5194 | 17.3 | 0.5 | + 270 | 7.4 | 16.1 | +| 4449 | 17.8 | 0.63 | + 200 | 9.5 | 14.5 | +| 4214 | 18.3 | 0.8 | + 300 | 11.3 | 13.2 | +| 3031 | 18.5 | 0.9 | − 30 | 8.3 | 16.4 | +| 3627 | 18.5 | 0.9 | + 650 | 9.1 | 15.7 | +| 4826 | 18.5 | 0.9 | + 150 | 9.0 | 15.7 | +| 5236 | 18.5 | 0.9 | + 500 | 10.4 | 14.4 | +| 1068 | 18.7 | 1.0 | + 920 | 9.1 | 15.9 | +| 5055 | 19.0 | 1.1 | + 450 | 9.6 | 15.6 | +| 7331 | 19.0 | 1.1 | + 500 | 10.4 | 14.8 | +| 4258 | 19.5 | 1.4 | + 500 | 8.7 | 17.0 | +| 4151 | 20.0 | 1.7 | + 960 | 12.0 | 14.2 | +| 4382 | .. | 2.0 | + 500 | 10.0 | 16.5 | +| 4472 | .. | 2.0 | + 850 | 8.8 | 17.7 | +| 4486 | .. | 2.0 | + 800 | 9.7 | 16.8 | +| 4649 | .. | 2.0 | +1090 | 9.5 | 17.0 | +| Mean | | | | | -15.5 | -$m_{s}$= photographic magnitude of brightest stars involved. $r$= -distance in units of $10^{6}$ parsecs. The first two are Shapley's -values. $v$= measured velocities in km./sec. N. G. C. 6822, 221, 224 -and 5457 are recent determinations by Humason. $m_{i}$= Holetschek's -visual magnitude as corrected by Hopmann. The first three objects were -not measured by Holetschek, and the values of $m_{i}$ represent -estimates by the author based upon such data as are available. -$M_{t}$= total visual absolute magnitude computed from $m_{t}$ and -$r$. Finally, the nebulae themselves appear to be of a definite order -of absolute luminosity, exhibiting a range of four or five magnitudes -about an average value M (visual) = $-15.2.^{1}$ The application of -this statistical average to individual cases can rarely be used to -advantage, but where considerable numbers are involved, and especially -in the various clusters of nebulae, mean apparent luminosities of the -nebulae themselves offer reliable estimates of the mean distances. -Radial velocities of 46 extra-galactic nebulae are now available, but +m$_{s}$ = photographic magnitude of brightest stars involved. r = +distance in units of 10$^{6}$ parsecs. The first two are Shapley's +values. v = measured velocities in km./sec. N. G. C. 6822, 221, 224 +and 5457 are recent determinations by Humason. m$_{t}$ = Holetschek's +visual magnitude as corrected by Hopmann. The first three objects were +not measured by Holetschek, and the values of m$_{t}$ represent +estimates by the author based upon such data as are available. M$_{t}$ += total visual absolute magnitude computed from m$_{t}$ and r. +Finally, the nebulae themselves appear to be of a definite order of +absolute luminosity, exhibiting a range of four or five magnitudes +about an average value M (visual) = -15.2$^{1}$. The application of +this statistical average to individual cases can rarely be used to +advantage, but where considerable numbers are involved, and especially +in the various clusters of nebulae, mean apparent luminosities of the +nebulae themselves offer reliable estimates of the mean distances. +Radial velocities of 46 extra-galactic nebulae are now available, but -individual distances are estimated for only 24. For one other, N. G. -C. 3521, an estimate could probably be made, but no photographs are -available at Mount Wilson. The data are given in table 1. The first -seven distances are the most reliable, depending, except for M 32 the -companion of M 31, upon extensive investigations of many stars -involved. The next thirteen distances, depending upon the criterion of -a uniform upper limit of stellar luminosity, are subject to -considerable probable errors but are believed to be the most -reasonable values at present available. The last four objects appear -to be in the Virgo Cluster. The distance assigned to the cluster, $2 -\times 10^{6}$ parsecs, is derived from the distribution of nebular -luminosities, together with luminosities of stars in some of the -later-type spirals, and differs somewhat from the Harvard estimate of -ten million light years. 2 The data in the table indicate a linear -correlation between distances and velocities, whether the latter are -used directly or corrected for solar motion, according to the older -solutions. This suggests a new solution for the solar motion in which -the distances are introduced as coefficients of the K term, i. e., the -velocities are assumed to vary directly with the distances, and hence +individual distances are estimated for only 24. For one other, N. G. +C. 3521, an estimate could probably be made, but no photographs are +available at Mount Wilson. The data are given in table 1. The first +seven distances are the most reliable, depending, except for M 32 the +companion of M 31, upon extensive investigations of many stars +involved. The next thirteen distances, depending upon the criterion of +a uniform upper limit of stellar luminosity, are subject to +considerable probable errors but are believed to be the most +reasonable values at present available. The last four objects appear +to be in the Virgo Cluster. The distance assigned to the cluster, $2 +\times 10^6$ parsecs, is derived from the distribution of nebular +luminosities, together with luminosities of stars in some of the +later-type spirals, and differs somewhat from the Harvard estimate of +ten million light years.2 The data in the table indicate a linear +correlation between distances and velocities, whether the latter are +used directly or corrected for solar motion, according to the older +solutions. This suggests a new solution for the solar motion in which +the distances are introduced as coefficients of the K term, i. e., the +velocities are assumed to vary directly with the distances, and hence K represents the velocity at unit distance due to this effect. The -equations of condition then take the form $rK + X \cos \alpha \cos -\delta + Y \sin \alpha \cos \delta + Z \sin \delta = v$ Two solutions -have been made, one using the 24 nebulae individually, the other -combining them into 9 groups according to proximity in direction and +equations of condition then take the form $rK + X \cos \alpha \cos +\delta + Y \sin \alpha \cos \delta + Z \sin \delta = v.$ Two solutions +have been made, one using the 24 nebulae individually, the other +combining them into 9 groups according to proximity in direction and in distance. The results are | | | | | --- | --- | --- | -| | 24 OBJECTS | 9 GROUPS | -| X | $- 65 \pm 50$ | $+ 3 \pm 70$ | -| Y | $+226 \pm 95$ | $+230 \pm 120$ | -| Z | $-195 \pm 40$ | $-133 \pm 70$ | -| K | $+465 \pm 50$ | $+513 \pm 60$ km./sec. per $10^{6}$ parsecs. | -| A | $286^{\circ}$ | $269^{\circ}$ | -| D | $+40^{\circ}$ | $+33^{\circ}$ | -| $V_{0}$ | 306 km./sec. | 247 km./sec. | +| | 24 OBJECTS | 9 GROUPS | +| X | $-65 \pm 50$ | $+3 \pm 70$ | +| Y | $+226 \pm 95$ | $+230 \pm 120$ | +| Z | $-195 \pm 40$ | $-133 \pm 70$ | +| K | $+465 \pm 50$ | $+513 \pm 60 \text{ km./sec. per } 10^6 \text{ parsecs.}$ | +| A | $286^\circ$ | $269^\circ$ | +| D | $+40^\circ$ | $+33^\circ$ | +| V$_0$ | 306 km./sec. | 247 km./sec. | -For such scanty material, so poorly distributed, the results are -fairly definite. Differences between the two solutions are due largely -to the four Virgo nebulae, which, being the most distant objects and -all sharing the peculiar motion of the cluster, unduly influence the -value of K and hence of $V_{0}$. New data on more distant objects will -be required to reduce the effect of such peculiar motion. Meanwhile -round numbers, intermediate between the two solutions, will represent -the probable order of the values. For instance, let $A = 277^{\circ}$, -$D = +36^{\circ}$ (Gal. long. $= 32^{\circ}$, lat. $= +18^{\circ}$), -$V_{0} = 280$ km./sec., $K = +500$ km./sec. per million par- +For such scanty material, so poorly distributed, the results are +fairly definite. Differences between the two solutions are due largely +to the four Virgo nebulae, which, being the most distant objects and +all sharing the peculiar motion of the cluster, unduly influence the +value of K and hence of V$_0$. New data on more distant objects will +be required to reduce the effect of such peculiar motion. Meanwhile +round numbers, intermediate between the two solutions, will represent +the probable order of the values. For instance, let $A = 277^\circ, D += +36^\circ$ (Gal. long. $= 32^\circ$, lat. $= +18^\circ$), $V_0 = +280$ km./sec., $K = +500$ km./sec. per million par- -secs. Mr. Strömberg has very kindly checked the general order of these -values by independent solutions for different groupings of the data. A +secs. Mr. Strömberg has very kindly checked the general order of these +values by independent solutions for different groupings of the data. A constant term, introduced into the equations, was found to be small -and negative. This seems to dispose of the necessity for the old -constant $K$ term. Solutions of this sort have been published by -Lundmark, who replaced the old $K$ by $k+lr+mr^{2}$. His favored -solution gave $k=513$, as against the former value of the order of -700, and hence offered little advantage. +and negative. This seems to dispose of the necessity for the old +constant $K$ term. Solutions of this sort have been published by +Lundmark, who replaced the old $K$ by $k + Ir + mr^{2}$. His favored +solution gave $k = 513$, as against the former value of the order of +700, and hence offered little advantage. -| | | | | | | | -| --- | --- | --- | --- | --- | --- | --- | -| | | | $r$ | $m$ | $M_{c}$ | | -| N. G. C. | OBJECT | | | | | | -| | 278 | $+ 650$ | $-110$ | 1.52 | 12.0 | $–13.9$ | -| | 404 | $– 25$ | $– 65$ | ... | 11.1 | .. | -| | 584 | $+1800$ | $+ 75$ | 3.45 | 10.9 | 16.8 | -| | 936 | $+1300$ | $+115$ | 2.37 | 11.1 | 15.7 | -| | 1023 | $+ 300$ | $– 10$ | 0.62 | 10.2 | 13.8 | -| | 1700 | $+ 800$ | $+220$ | 1.16 | 12.5 | 12.8 | -| | 2681 | $+ 700$ | $– 10$ | 1.42 | 10.7 | 15.0 | -| | 2683 | $+ 400$ | $+ 65$ | 0.67 | 9.9 | 14.3 | -| | 2841 | $+ 600$ | $– 20$ | 1.24 | 9.4 | 16.1 | -| | 3034 | $+ 290$ | $–105$ | 0.79 | 9.0 | 15.5 | -| | 3115 | $+ 600$ | $+105$ | 1.00 | 9.5 | 15.5 | -| | 3368 | $+ 940$ | $+ 70$ | 1.74 | 10.0 | 16.2 | -| | 3379 | $+ 810$ | $+ 65$ | 1.49 | 9.4 | 16.4 | -| | 3489 | $+ 600$ | $+ 50$ | 1.10 | 11.2 | 14.0 | -| | 3521 | $+ 730$ | $+ 95$ | 1.27 | 10.1 | 15.4 | -| | 3623 | $+ 800$ | $+ 35$ | 1.53 | 9.9 | 16.0 | -| | 4111 | $+ 800$ | $– 95$ | 1.79 | 10.1 | 16.1 | -| | 4526 | $+ 580$ | $– 20$ | 1.20 | 11.1 | 14.3 | -| | 4565 | $+1100$ | $– 75$ | 2.35 | 11.0 | 15.9 | -| | 4594 | $+1140$ | $+ 25$ | 2.23 | 9.1 | 17.6 | -| | 5005 | $+ 900$ | $-130$ | 2.06 | 11.1 | 15.5 | -| | 5866 | $+ 650$ | $-215$ | 1.73 | 11.7 | $–14.5$ | -| Mean | | | | 10.5 | $–15.3$ | | +| OBJECT | | | r | m | $M_{t}$ | +| --- | --- | --- | --- | --- | --- | +| N. G. C. 278 | + 650 | –110 | 1.52 | 12.0 | –13.9 | +| 404 | – 25 | – 65 | | 11.1 | .. | +| 584 | +1800 | + 75 | 3.45 | 10.9 | 16.8 | +| 936 | +1300 | +115 | 2.37 | 11.1 | 15.7 | +| 1023 | + 300 | – 10 | 0.62 | 10.2 | 13.8 | +| 1700 | + 800 | +220 | 1.16 | 12.5 | 12.8 | +| 2681 | + 700 | – 10 | 1.42 | 10.7 | 15.0 | +| 2683 | + 400 | + 65 | 0.67 | 9.9 | 14.3 | +| 2841 | + 600 | – 20 | 1.24 | 9.4 | 16.1 | +| 3034 | + 290 | –105 | 0.79 | 9.0 | 15.5 | +| 3115 | + 600 | +105 | 1.00 | 9.5 | 15.5 | +| 3368 | + 940 | + 70 | 1.74 | 10.0 | 16.2 | +| 3379 | + 810 | + 65 | 1.49 | 9.4 | 16.4 | +| 3489 | + 600 | + 50 | 1.10 | 11.2 | 14.0 | +| 3521 | + 730 | + 95 | 1.27 | 10.1 | 15.4 | +| 3623 | + 800 | + 35 | 1.53 | 9.9 | 16.0 | +| 4111 | + 800 | – 95 | 1.79 | 10.1 | 16.1 | +| 4526 | + 580 | – 20 | 1.20 | 11.1 | 14.3 | +| 4565 | +1100 | + 75 | 2.35 | 11.0 | 15.9 | +| 4594 | +1140 | + 25 | 2.23 | 9.1 | 17.6 | +| 5005 | + 900 | –130 | 2.06 | 11.1 | 15.5 | +| 5866 | + 650 | –215 | 1.73 | 11.7 | –14.5 | +| Mean | | | | 10.5 | –15.3 | -The residuals for the two solutions given above average 150 and 110 -km./sec. and should represent the average peculiar motions of the -individual nebulae and of the groups, respectively. In order to -exhibit the results in a graphical form, the solar motion has been -eliminated from the observed velocities and the remainders, the -distance terms plus the residuals, have been plotted against the -distances. The run of the residuals is about as smooth as can be -expected, and in general the form of the solutions appears to be -adequate. The 22 nebulae for which distances are not available can be -treated in two ways. First, the mean distance of the group derived -from the mean apparent magnitudes can be compared with the mean of the -velocities Downloaded from https://www.pnas.org by 45.113.94.70 on -January 5, 2026 from IP address 45.113.94.70. +The residuals for the two solutions given above average 150 and 110 +km./sec. and should represent the average peculiar motions of the +individual nebulae and of the groups, respectively. In order to +exhibit the results in a graphical form, the solar motion has been +eliminated from the observed velocities and the remainders, the +distance terms plus the residuals, have been plotted against the +distances. The run of the residuals is about as smooth as can be +expected, and in general the form of the solutions appears to be +adequate. The 22 nebulae for which distances are not available can be +treated in two ways. First, the mean distance of the group derived +from the mean apparent magnitudes can be compared with the mean of the +velocities -corrected for solar motion. The result, 745 km./sec. for a distance of -$1.4 \times 10^{6}$ parsecs, falls between the two previous solutions -and indicates a value for K of 530 as against the proposed value, 500 -km./sec. Secondly, the scatter of the individual nebulae can be -examined by assuming the relation between distances and velocities as -previously determined. Distances can then be calculated from the -velocities corrected for solar motion, and absolute magnitudes can be -derived from the apparent magnitudes. The results are given in table 2 -and may be compared with the distribution of absolute magnitudes among -the nebulae in talbe 1, whose distances are derived from other -criteria. N. G. C. 404 FIGURE 1 Velocity-Distance Relation among -Extra-Galactic Nebulae. Radial velocities, corrected for solar motion, -are plotted against distances estimated from involved stars and mean -luminosities of nebulae in a cluster. The black discs and full line -represent the solution for solar motion using the nebulae -individually; the circles and broken line represent the solution -combining the nebulae into groups; the cross represents the mean -velocity corresponding to the mean distance of 22 nebulae whose -distances could not be estimated individually. can be excluded, since -the observed velocity is so small that the peculiar motion must be -large in comparison with the distance effect. The object is not +corrected for solar motion. The result, $745~km./sec.$ for a distance +of $1.4 \times 10^{6}$ parsecs, falls between the two previous +solutions and indicates a value for K of 530 as against the proposed +value, $500~km./sec.$ Secondly, the scatter of the individual nebulae +can be examined by assuming the relation between distances and +velocities as previously determined. Distances can then be calculated +from the velocities corrected for solar motion, and absolute +magnitudes can be derived from the apparent magnitudes. The results +are given in table 2 and may be compared with the distribution of +absolute magnitudes among the nebulae in table 1, whose distances are +derived from other criteria. N. G. C. 404 FIGURE 1 Velocity-Distance +Relation among Extra-Galactic Nebulae. Radial velocities, corrected +for solar motion, are plotted against distances estimated from +involved stars and mean luminosities of nebulae in a cluster. The +black discs and full line represent the solution for solar motion +using the nebulae individually; the circles and broken line represent +the solution combining the nebulae into groups; the cross represents +the mean velocity corresponding to the mean distance of 22 nebulae +whose distances could not be estimated individually. can be excluded, +since the observed velocity is so small that the peculiar motion must +be large in comparison with the distance effect. The object is not necessarily an exception, however, since a distance can be assigned -for which the peculiar motion and the absolute magnitude are both -within the range previously determined. The two mean magnitudes, -15.3 -and -15.5, the ranges, 4.9 and 5.0 mag., and the frequency -distributions are closely similar for these two entirely independent -sets of data; and even the slight difference in mean magnitudes can be -attributed to the selected, very bright, nebulae in the Virgo Cluster. -This entirely unforced agreement supports the validity of the +for which the peculiar motion and the absolute magnitude are both +within the range previously determined. The two mean magnitudes, +$−15.3$ and $−15.5$, the ranges, 4.9 and 5.0 mag., and the frequency +distributions are closely similar for these two entirely independent +sets of data; and even the slight difference in mean magnitudes can be +attributed to the selected, very bright, nebulae in the Virgo Cluster. +This entirely unforced agreement supports the validity of the velocity-distance relation in a very -evident matter. Finally, it is worth recording that the frequency -distribution of absolute magnitudes in the two tables combined is -comparable with those found in the various clusters of nebulae. The +evident matter. Finally, it is worth recording that the frequency +distribu- tion of absolute magnitudes in the two tables combined is +comparable with those found in the various clusters of nebulae. The results establish a roughly linear relation between velocities and -distances among nebulae for which velocities have been previously -published, and the relation appears to dominate the distribution of -velocities. In order to investigate the matter on a much larger scale, -Mr. Humason at Mount Wilson has initiated a program of determining -velocities of the most distant nebulae that can be observed with -confidence. These, naturally, are the brightest nebulae in clusters of -nebulae. The first definite result, $v = +3779 ~km./sec.$ for N. G. C. -7619, is thoroughly consistent with the present conclusions. Corrected -for the solar motion, this velocity is $+3910$, which, with $K = 500$, -corresponds to a distance of $7.8 \times 10^{6}$ parsecs. Since the -apparent magnitude is 11.8, the absolute magnitude at such a distance -is $-17.65$, which is of the right order for the brightest nebulae in -a cluster. A preliminary distance, derived independently from the -cluster of which this nebula appears to be a member, is of the order -of $7 \times 10^{6}$ parsecs. New data to be expected in the near -future may modify the significance of the present investigation or, if -confirmatory, will lead to a solution having many times the weight. -For this reason it is thought premature to discuss in detail the -obvious consequences of the present results. For example, if the solar -motion with respect to the clusters represents the rotation of the -galactic system, this motion could be subtracted from the results for -the nebulae and the remainder would represent the motion of the -galactic system with respect to the extra-galactic nebulae. The +distances among nebulae for which velocities have been previously pub- +lished, and the relation appears to dominate the distribution of +velocities. In order to investigate the matter on a much larger scale, +Mr. Humason at Mount Wilson has initiated a program of determining +velocities of the most distant nebulae that can be observed with +confidence. These, naturally, are the brightest nebulae in clusters of +nebulae. The first definite result, $v =+3779 ~km./sec.$ for N. G. C. +7619, is thoroughly consistent with the present conclusions. Corrected +for the solar motion, this velocity is +3910, which, with $K = 500$, +corresponds to a distance of $7.8 \times 10^{6}$ parsecs. Since the +apparent magnitude is 11.8, the absolute magnitude at such a distance +is -17.65, which is of the right order for the brightest nebulae in a +cluster. A preliminary dis- tance, derived independently from the +cluster of which this nebula appears to be a member, is of the order +of $7 \times 10^{6}$ parsecs. New data to be expected in the near +future may modify the significance of the present investigation or, if +confirmatory, will lead to a solution having many times the weight. +For this reason it is thought premature to discuss in detail the +obvious consequences of the present results. For example, if the solar +motion with respect to the clusters represents the rotation of the +galactic system, this motion could be subtracted from the results for +the nebulae and the remainder would represent the motion of the +galactic system with respect to the extra-galactic nebulae. The outstanding feature, however, is the possibility that the velocity- -distance relation may represent the de Sitter effect, and hence that -numerical data may be introduced into discussions of the general -curvature of space. In the de Sitter cosmology, displacements of the -spectra arise from two sources, an apparent slowing down of atomic -vibrations and a general tendency of material particles to scatter. -The latter involves an acceleration and hence introduces the element -of time. The relative importance of these two effects should determine -the form of the relation between distances and observed velocities; -and in this connection it may be emphasized that the linear relation -found in the present discussion is a first approximation representing -a restricted range in distance. +distance relation may represent the de Sitter effect, and hence that +numer- ical data may be introduced into discussions of the general +curvature of space. In the de Sitter cosmology, displacements of the +spectra arise from two sources, an apparent slowing down of atomic +vibrations and a general tendency of material particles to scatter. +The latter involves an acceleration and hence introduces the element +of time. The relative im- portance of these two effects should +determine the form of the relation between distances and observed +velocities; and in this connection it may be emphasized that the +linear relation found in the present discussion is a first +approximation representing a restricted range in distance. - Mt. Wilson Contr., No. 324; Astroph. J., Chicago, Ill., 64, 1926 (321). -- Harvard Coll. Obs. Circ., 294, 1926. -- Mon. Not. R. Astr. Soc., 85, 1925 (865-894). +- Harvard Coll. Obs. Circ., 294, 1926. +- Mon. Not. R. Astr. Soc., 85, 1925 (865-894). - These PROCEEDINGS, 15, 1929 (167). diff --git a/tests/fixtures/hubble_docai_layout_responses.json b/tests/fixtures/hubble_docai_layout_responses.json index ef71e9f..e7a75c5 100644 --- a/tests/fixtures/hubble_docai_layout_responses.json +++ b/tests/fixtures/hubble_docai_layout_responses.json @@ -1 +1 @@ -["{\n \"documentLayout\": {\n \"blocks\": [\n {\n \"blockId\": \"1\",\n \"textBlock\": {\n \"text\": \"168 ASTRONOMY: E. HUBBLE PROC. N. A. S.\",\n \"type\": \"header\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 1,\n \"pageEnd\": 1\n }\n },\n {\n \"blockId\": \"2\",\n \"textBlock\": {\n \"text\": \"appearance the spectrum is very much like spectra of the Milky Way clouds in Sagittarius and Cygnus, and is also similar to spectra of binary stars of the W Ursae Majoris type, where the widening and depth of the lines are affected by the rapid rotation of the stars involved. The wide shallow absorption lines observed in the spectrum of N. G. C. 7619 have been noticed in the spectra of other extra-galactic nebulae, and may be due to a dispersion in velocity and a blending of the spectral types of the many stars which presumably exist in the central parts of these nebulae. The lack of depth in the absorption lines seems to be more pronounced among the smaller and fainter nebulae, and in N. G. C. 7619 the absorption is very weak. It is hoped that velocities of more of these interesting objects will soon be available.\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 1,\n \"pageEnd\": 1\n }\n },\n {\n \"blockId\": \"3\",\n \"textBlock\": {\n \"text\": \"A RELATION BETWEEN DISTANCE AND RADIAL VELOCITY AMONG EXTRA-GALACTIC NEBULAE\",\n \"type\": \"heading-2\",\n \"blocks\": [\n {\n \"blockId\": \"4\",\n \"textBlock\": {\n \"text\": \"BY EDWIN HUBBLE MOUNT WILSON OBSERVATORY, CARNEGIE INSTITUTION OF WASHINGTON Communicated January 17, 1929 Determinations of the motion of the sun with respect to the extra-galactic nebulae have involved a \\\\(K\\\\) term of several hundred kilometers which appears to be variable. Explanations of this paradox have been sought in a correlation between apparent radial velocities and distances, but so far the results have not been convincing. The present paper is a re-examination of the question, based on only those nebular distances which are believed to be fairly reliable. Distances of extra-galactic nebulae depend ultimately upon the appli-cation of absolute-luminosity criteria to involved stars whose types can be recognized. These include, among others, Cepheid variables, novae, and blue stars involved in emission nebulosity. Numerical values depend upon the zero point of the period-luminosity relation among Cepheids, the other criteria merely check the order of the distances. This method is restricted to the few nebulae which are well resolved by existing instruments. A study of these nebulae, together with those in which any stars at all can be recognized, indicates the probability of an approximately uniform upper limit to the absolute luminosity of stars, in the late-type spirals and irregular nebulae at least, of the order of \\\\(M\\\\) (photographic) \\\\(=-6.3\\\\). 1 The apparent luminosities of the brightest stars in such nebulae are thus criteria which, although rough and to be applied with caution,\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 1,\n \"pageEnd\": 1\n }\n },\n {\n \"blockId\": \"5\",\n \"textBlock\": {\n \"text\": \"Downloaded from https://www.pnas.org by 45.113.94.70 on January 5, 2026 from IP address 45.113.94.70.\",\n \"type\": \"footer\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 1,\n \"pageEnd\": 1\n }\n },\n {\n \"blockId\": \"6\",\n \"textBlock\": {\n \"text\": \"VOL. 15, 1929 ASTRONOMY: E. HUBBLE 169\",\n \"type\": \"header\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n },\n {\n \"blockId\": \"7\",\n \"textBlock\": {\n \"text\": \"furnish reasonable estimates of the distances of all extra-galactic systems in which even a few stars can be detected.\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ]\n },\n \"pageSpan\": {\n \"pageStart\": 1,\n \"pageEnd\": 2\n }\n },\n {\n \"blockId\": \"8\",\n \"textBlock\": {\n \"text\": \"TABLE 1\",\n \"type\": \"heading-2\",\n \"blocks\": [\n {\n \"blockId\": \"9\",\n \"textBlock\": {\n \"text\": \"NEBULAE WHOSE DISTANCES HAVE BEEN ESTIMATED FROM STARS INVOLVED OR FROM MEAN LUMINOSITIES IN A CLUSTER\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n },\n {\n \"blockId\": \"10\",\n \"tableBlock\": {\n \"headerRows\": [\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"155\",\n \"textBlock\": {\n \"text\": \"OBJECT\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"156\",\n \"textBlock\": {\n \"text\": \"\\\\(m\\\\)\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"157\",\n \"textBlock\": {\n \"text\": \"\\\\(r\\\\)\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"rowSpan\": 1,\n \"colSpan\": 1,\n \"blocks\": []\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"158\",\n \"textBlock\": {\n \"text\": \"\\\\(m_{t}\\\\)\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"159\",\n \"textBlock\": {\n \"text\": \"\\\\(M\\\\)\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n }\n ],\n \"bodyRows\": [\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"11\",\n \"textBlock\": {\n \"text\": \"S. Mag.\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"rowSpan\": 1,\n \"colSpan\": 1,\n \"blocks\": []\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"12\",\n \"textBlock\": {\n \"text\": \"0.032\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"13\",\n \"textBlock\": {\n \"text\": \"+ 170\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"14\",\n \"textBlock\": {\n \"text\": \"1.5\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"15\",\n \"textBlock\": {\n \"text\": \"-16.0\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"16\",\n \"textBlock\": {\n \"text\": \"L. Mag.\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"17\",\n \"textBlock\": {\n \"text\": \"..\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"18\",\n \"textBlock\": {\n \"text\": \"0.034\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"19\",\n \"textBlock\": {\n \"text\": \"+ 290\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"20\",\n \"textBlock\": {\n \"text\": \"0.5\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"21\",\n \"textBlock\": {\n \"text\": \"17.2\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"22\",\n \"textBlock\": {\n \"text\": \"N. G. C. 6822\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"rowSpan\": 1,\n \"colSpan\": 1,\n \"blocks\": []\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"23\",\n \"textBlock\": {\n \"text\": \"0.214\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"24\",\n \"textBlock\": {\n \"text\": \"\\u2014 130\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"25\",\n \"textBlock\": {\n \"text\": \"9.0\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"26\",\n \"textBlock\": {\n \"text\": \"12.7\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"27\",\n \"textBlock\": {\n \"text\": \"598\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"28\",\n \"textBlock\": {\n \"text\": \"..\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"29\",\n \"textBlock\": {\n \"text\": \"0.263\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"30\",\n \"textBlock\": {\n \"text\": \"\\u2014 70\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"31\",\n \"textBlock\": {\n \"text\": \"7.0\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"32\",\n \"textBlock\": {\n \"text\": \"15.1\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"33\",\n \"textBlock\": {\n \"text\": \"221\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"34\",\n \"textBlock\": {\n \"text\": \"..\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"35\",\n \"textBlock\": {\n \"text\": \"0.275\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"36\",\n \"textBlock\": {\n \"text\": \"\\u2014 185\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"37\",\n \"textBlock\": {\n \"text\": \"8.8\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"38\",\n \"textBlock\": {\n \"text\": \"13.4\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"39\",\n \"textBlock\": {\n \"text\": \"224\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"40\",\n \"textBlock\": {\n \"text\": \"..\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"41\",\n \"textBlock\": {\n \"text\": \"0.275\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"42\",\n \"textBlock\": {\n \"text\": \"\\u2014 220\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"43\",\n \"textBlock\": {\n \"text\": \"5.0\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"44\",\n \"textBlock\": {\n \"text\": \"17.2\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"45\",\n \"textBlock\": {\n \"text\": \"5457\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"46\",\n \"textBlock\": {\n \"text\": \"17.0\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"47\",\n \"textBlock\": {\n \"text\": \"0.45\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"48\",\n \"textBlock\": {\n \"text\": \"+ 200\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"49\",\n \"textBlock\": {\n \"text\": \"9.9\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"50\",\n \"textBlock\": {\n \"text\": \"13.3\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"51\",\n \"textBlock\": {\n \"text\": \"4736\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"52\",\n \"textBlock\": {\n \"text\": \"17.3\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"53\",\n \"textBlock\": {\n \"text\": \"0.5\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"54\",\n \"textBlock\": {\n \"text\": \"+ 290\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"55\",\n \"textBlock\": {\n \"text\": \"8.4\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"56\",\n \"textBlock\": {\n \"text\": \"15.1\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"57\",\n \"textBlock\": {\n \"text\": \"5194\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"58\",\n \"textBlock\": {\n \"text\": \"17.3\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"59\",\n \"textBlock\": {\n \"text\": \"0.5\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"60\",\n \"textBlock\": {\n \"text\": \"+ 270\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"61\",\n \"textBlock\": {\n \"text\": \"7.4\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"62\",\n \"textBlock\": {\n \"text\": \"16.1\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"63\",\n \"textBlock\": {\n \"text\": \"4449\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"64\",\n \"textBlock\": {\n \"text\": \"17.8\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"65\",\n \"textBlock\": {\n \"text\": \"0.63\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"66\",\n \"textBlock\": {\n \"text\": \"+ 200\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"67\",\n \"textBlock\": {\n \"text\": \"9.5\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"68\",\n \"textBlock\": {\n \"text\": \"14.5\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"69\",\n \"textBlock\": {\n \"text\": \"4214\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"70\",\n \"textBlock\": {\n \"text\": \"18.3\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"71\",\n \"textBlock\": {\n \"text\": \"0.8\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"72\",\n \"textBlock\": {\n \"text\": \"+ 300\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"73\",\n \"textBlock\": {\n \"text\": \"11.3\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"74\",\n \"textBlock\": {\n \"text\": \"13.2\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"75\",\n \"textBlock\": {\n \"text\": \"3031\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"76\",\n \"textBlock\": {\n \"text\": \"18.5\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"77\",\n \"textBlock\": {\n \"text\": \"0.9\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"78\",\n \"textBlock\": {\n \"text\": \"\\u2014 30\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"79\",\n \"textBlock\": {\n \"text\": \"8.3\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"80\",\n \"textBlock\": {\n \"text\": \"16.4\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"81\",\n \"textBlock\": {\n \"text\": \"3627\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"82\",\n \"textBlock\": {\n \"text\": \"18.5\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"83\",\n \"textBlock\": {\n \"text\": \"0.9\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"84\",\n \"textBlock\": {\n \"text\": \"+ 650\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"85\",\n \"textBlock\": {\n \"text\": \"9.1\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"86\",\n \"textBlock\": {\n \"text\": \"15.7\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"87\",\n \"textBlock\": {\n \"text\": \"4826\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"88\",\n \"textBlock\": {\n \"text\": \"18.5\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"89\",\n \"textBlock\": {\n \"text\": \"0.9\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"90\",\n \"textBlock\": {\n \"text\": \"+ 150\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"91\",\n \"textBlock\": {\n \"text\": \"9.0\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"92\",\n \"textBlock\": {\n \"text\": \"15.7\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"93\",\n \"textBlock\": {\n \"text\": \"5236\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"94\",\n \"textBlock\": {\n \"text\": \"18.5\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"95\",\n \"textBlock\": {\n \"text\": \"0.9\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"96\",\n \"textBlock\": {\n \"text\": \"+ 500\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"97\",\n \"textBlock\": {\n \"text\": \"10.4\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"98\",\n \"textBlock\": {\n \"text\": \"14.4\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"99\",\n \"textBlock\": {\n \"text\": \"1068\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"100\",\n \"textBlock\": {\n \"text\": \"18.7\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"101\",\n \"textBlock\": {\n \"text\": \"1.0\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"102\",\n \"textBlock\": {\n \"text\": \"+ 920\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"103\",\n \"textBlock\": {\n \"text\": \"9.1\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"104\",\n \"textBlock\": {\n \"text\": \"15.9\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"105\",\n \"textBlock\": {\n \"text\": \"5055\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"106\",\n \"textBlock\": {\n \"text\": \"19.0\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"107\",\n \"textBlock\": {\n \"text\": \"1.1\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"108\",\n \"textBlock\": {\n \"text\": \"+ 450\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"109\",\n \"textBlock\": {\n \"text\": \"9.6\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"110\",\n \"textBlock\": {\n \"text\": \"15.6\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"111\",\n \"textBlock\": {\n \"text\": \"7331\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"112\",\n \"textBlock\": {\n \"text\": \"19.0\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"113\",\n \"textBlock\": {\n \"text\": \"1.1\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"114\",\n \"textBlock\": {\n \"text\": \"+ 500\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"115\",\n \"textBlock\": {\n \"text\": \"10.4\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"116\",\n \"textBlock\": {\n \"text\": \"14.8\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"117\",\n \"textBlock\": {\n \"text\": \"4258\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"118\",\n \"textBlock\": {\n \"text\": \"19.5\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"119\",\n \"textBlock\": {\n \"text\": \"1.4\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"120\",\n \"textBlock\": {\n \"text\": \"+ 500\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"121\",\n \"textBlock\": {\n \"text\": \"8.7\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"122\",\n \"textBlock\": {\n \"text\": \"17.0\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"123\",\n \"textBlock\": {\n \"text\": \"4151\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"124\",\n \"textBlock\": {\n \"text\": \"20.0\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"125\",\n \"textBlock\": {\n \"text\": \"1.7\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"126\",\n \"textBlock\": {\n \"text\": \"+ 960\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"127\",\n \"textBlock\": {\n \"text\": \"12.0\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"128\",\n \"textBlock\": {\n \"text\": \"14.2\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"129\",\n \"textBlock\": {\n \"text\": \"4382\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"130\",\n \"textBlock\": {\n \"text\": \"..\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"131\",\n \"textBlock\": {\n \"text\": \"2.0\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"132\",\n \"textBlock\": {\n \"text\": \"+ 500\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"133\",\n \"textBlock\": {\n \"text\": \"10.0\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"134\",\n \"textBlock\": {\n \"text\": \"16.5\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"135\",\n \"textBlock\": {\n \"text\": \"4472\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"136\",\n \"textBlock\": {\n \"text\": \"..\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"137\",\n \"textBlock\": {\n \"text\": \"2.0\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"138\",\n \"textBlock\": {\n \"text\": \"+ 850\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"139\",\n \"textBlock\": {\n \"text\": \"8.8\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"140\",\n \"textBlock\": {\n \"text\": \"17.7\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"141\",\n \"textBlock\": {\n \"text\": \"4486\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"142\",\n \"textBlock\": {\n \"text\": \"..\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"143\",\n \"textBlock\": {\n \"text\": \"2.0\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"144\",\n \"textBlock\": {\n \"text\": \"+ 800\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"145\",\n \"textBlock\": {\n \"text\": \"9.7\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"146\",\n \"textBlock\": {\n \"text\": \"16.8\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"147\",\n \"textBlock\": {\n \"text\": \"4649\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"148\",\n \"textBlock\": {\n \"text\": \"..\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"149\",\n \"textBlock\": {\n \"text\": \"2.0\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"150\",\n \"textBlock\": {\n \"text\": \"+1090\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"151\",\n \"textBlock\": {\n \"text\": \"9.5\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"152\",\n \"textBlock\": {\n \"text\": \"17.0\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"153\",\n \"textBlock\": {\n \"text\": \"Mean\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"rowSpan\": 1,\n \"colSpan\": 1,\n \"blocks\": []\n },\n {\n \"rowSpan\": 1,\n \"colSpan\": 1,\n \"blocks\": []\n },\n {\n \"rowSpan\": 1,\n \"colSpan\": 1,\n \"blocks\": []\n },\n {\n \"rowSpan\": 1,\n \"colSpan\": 1,\n \"blocks\": []\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"154\",\n \"textBlock\": {\n \"text\": \"-15.5\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n }\n ],\n \"caption\": \"\"\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n },\n {\n \"blockId\": \"160\",\n \"textBlock\": {\n \"text\": \"\\\\(m_{s}\\\\)= photographic magnitude of brightest stars involved. \\\\(r\\\\)= distance in units of \\\\(10^{6}\\\\) parsecs. The first two are Shapley's values. \\\\(v\\\\)= measured velocities in km./sec. N. G. C. 6822, 221, 224 and 5457 are recent determinations by Humason. \\\\(m_{i}\\\\)= Holetschek's visual magnitude as corrected by Hopmann. The first three objects were not measured by Holetschek, and the values of \\\\(m_{i}\\\\) represent estimates by the author based upon such data as are available. \\\\(M_{t}\\\\)= total visual absolute magnitude computed from \\\\(m_{t}\\\\) and \\\\(r\\\\). Finally, the nebulae themselves appear to be of a definite order of absolute luminosity, exhibiting a range of four or five magnitudes about an average value M (visual) = \\\\(-15.2.^{1}\\\\) The application of this statistical average to individual cases can rarely be used to advantage, but where considerable numbers are involved, and especially in the various clusters of nebulae, mean apparent luminosities of the nebulae themselves offer reliable estimates of the mean distances. Radial velocities of 46 extra-galactic nebulae are now available, but\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n },\n {\n \"blockId\": \"161\",\n \"textBlock\": {\n \"text\": \"Downloaded from https://www.pnas.org by 45.113.94.70 on January 5, 2026 from IP address 45.113.94.70.\",\n \"type\": \"footer\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n },\n {\n \"blockId\": \"162\",\n \"textBlock\": {\n \"text\": \"170 ASTRONOMY: E. HUBBLE PROC. N. A. S.\",\n \"type\": \"header\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 3,\n \"pageEnd\": 3\n }\n },\n {\n \"blockId\": \"163\",\n \"textBlock\": {\n \"text\": \"individual distances are estimated for only 24. For one other, N. G. C. 3521, an estimate could probably be made, but no photographs are available at Mount Wilson. The data are given in table 1. The first seven distances are the most reliable, depending, except for M 32 the companion of M 31, upon extensive investigations of many stars involved. The next thirteen distances, depending upon the criterion of a uniform upper limit of stellar luminosity, are subject to considerable probable errors but are believed to be the most reasonable values at present available. The last four objects appear to be in the Virgo Cluster. The distance assigned to the cluster, \\\\(2 \\\\times 10^{6}\\\\) parsecs, is derived from the distribution of nebular luminosities, together with luminosities of stars in some of the later-type spirals, and differs somewhat from the Harvard estimate of ten million light years. 2 The data in the table indicate a linear correlation between distances and velocities, whether the latter are used directly or corrected for solar motion, according to the older solutions. This suggests a new solution for the solar motion in which the distances are introduced as coefficients of the K term, i. e., the velocities are assumed to vary directly with the distances, and hence K represents the velocity at unit distance due to this effect. The equations of condition then take the form \\\\(rK + X \\\\cos \\\\alpha \\\\cos \\\\delta + Y \\\\sin \\\\alpha \\\\cos \\\\delta + Z \\\\sin \\\\delta = v\\\\) Two solutions have been made, one using the 24 nebulae individually, the other combining them into 9 groups according to proximity in direction and in distance. The results are\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 3,\n \"pageEnd\": 3\n }\n },\n {\n \"blockId\": \"164\",\n \"tableBlock\": {\n \"bodyRows\": [\n {\n \"cells\": [\n {\n \"rowSpan\": 1,\n \"colSpan\": 1,\n \"blocks\": []\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"165\",\n \"textBlock\": {\n \"text\": \"24 OBJECTS\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 3,\n \"pageEnd\": 3\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"166\",\n \"textBlock\": {\n \"text\": \"9 GROUPS\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 3,\n \"pageEnd\": 3\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"167\",\n \"textBlock\": {\n \"text\": \"X\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 3,\n \"pageEnd\": 3\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"168\",\n \"textBlock\": {\n \"text\": \"\\\\(- 65 \\\\pm 50\\\\)\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 3,\n \"pageEnd\": 3\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"169\",\n \"textBlock\": {\n \"text\": \"\\\\(+ 3 \\\\pm 70\\\\)\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 3,\n \"pageEnd\": 3\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"170\",\n \"textBlock\": {\n \"text\": \"Y\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 3,\n \"pageEnd\": 3\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"171\",\n \"textBlock\": {\n \"text\": \"\\\\(+226 \\\\pm 95\\\\)\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 3,\n \"pageEnd\": 3\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"172\",\n \"textBlock\": {\n \"text\": \"\\\\(+230 \\\\pm 120\\\\)\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 3,\n \"pageEnd\": 3\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"173\",\n \"textBlock\": {\n \"text\": \"Z\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 3,\n \"pageEnd\": 3\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"174\",\n \"textBlock\": {\n \"text\": \"\\\\(-195 \\\\pm 40\\\\)\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 3,\n \"pageEnd\": 3\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"175\",\n \"textBlock\": {\n \"text\": \"\\\\(-133 \\\\pm 70\\\\)\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 3,\n \"pageEnd\": 3\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"176\",\n \"textBlock\": {\n \"text\": \"K\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 3,\n \"pageEnd\": 3\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"177\",\n \"textBlock\": {\n \"text\": \"\\\\(+465 \\\\pm 50\\\\)\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 3,\n \"pageEnd\": 3\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"178\",\n \"textBlock\": {\n \"text\": \"\\\\(+513 \\\\pm 60\\\\) km./sec. per \\\\(10^{6}\\\\) parsecs.\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 3,\n \"pageEnd\": 3\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"179\",\n \"textBlock\": {\n \"text\": \"A\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 3,\n \"pageEnd\": 3\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"180\",\n \"textBlock\": {\n \"text\": \"\\\\(286^{\\\\circ}\\\\)\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 3,\n \"pageEnd\": 3\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"181\",\n \"textBlock\": {\n \"text\": \"\\\\(269^{\\\\circ}\\\\)\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 3,\n \"pageEnd\": 3\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"182\",\n \"textBlock\": {\n \"text\": \"D\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 3,\n \"pageEnd\": 3\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"183\",\n \"textBlock\": {\n \"text\": \"\\\\(+40^{\\\\circ}\\\\)\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 3,\n \"pageEnd\": 3\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"184\",\n \"textBlock\": {\n \"text\": \"\\\\(+33^{\\\\circ}\\\\)\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 3,\n \"pageEnd\": 3\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"185\",\n \"textBlock\": {\n \"text\": \"\\\\(V_{0}\\\\)\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 3,\n \"pageEnd\": 3\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"186\",\n \"textBlock\": {\n \"text\": \"306 km./sec.\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 3,\n \"pageEnd\": 3\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"187\",\n \"textBlock\": {\n \"text\": \"247 km./sec.\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 3,\n \"pageEnd\": 3\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n }\n ],\n \"headerRows\": [],\n \"caption\": \"\"\n },\n \"pageSpan\": {\n \"pageStart\": 3,\n \"pageEnd\": 3\n }\n },\n {\n \"blockId\": \"188\",\n \"textBlock\": {\n \"text\": \"For such scanty material, so poorly distributed, the results are fairly definite. Differences between the two solutions are due largely to the four Virgo nebulae, which, being the most distant objects and all sharing the peculiar motion of the cluster, unduly influence the value of K and hence of \\\\(V_{0}\\\\). New data on more distant objects will be required to reduce the effect of such peculiar motion. Meanwhile round numbers, intermediate between the two solutions, will represent the probable order of the values. For instance, let \\\\(A = 277^{\\\\circ}\\\\), \\\\(D = +36^{\\\\circ}\\\\) (Gal. long. \\\\( = 32^{\\\\circ}\\\\), lat. \\\\( = +18^{\\\\circ}\\\\)), \\\\(V_{0} = 280\\\\) km./sec., \\\\(K = +500\\\\) km./sec. per million par-\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 3,\n \"pageEnd\": 3\n }\n },\n {\n \"blockId\": \"189\",\n \"textBlock\": {\n \"text\": \"Downloaded from https://www.pnas.org by 45.113.94.70 on January 5, 2026 from IP address 45.113.94.70.\",\n \"type\": \"footer\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 3,\n \"pageEnd\": 3\n }\n },\n {\n \"blockId\": \"190\",\n \"textBlock\": {\n \"text\": \"VOL. 15, 1929 ASTRONOMY: E. HUBBLE 171\",\n \"type\": \"header\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n },\n {\n \"blockId\": \"191\",\n \"textBlock\": {\n \"text\": \"secs. Mr. Str\\u00f6mberg has very kindly checked the general order of these values by independent solutions for different groupings of the data. A constant term, introduced into the equations, was found to be small and negative. This seems to dispose of the necessity for the old constant \\\\(K\\\\) term. Solutions of this sort have been published by Lundmark, who replaced the old \\\\(K\\\\) by \\\\(k+lr+mr^{2}\\\\). His favored solution gave \\\\(k=513\\\\), as against the former value of the order of 700, and hence offered little advantage.\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n },\n {\n \"blockId\": \"192\",\n \"tableBlock\": {\n \"bodyRows\": [\n {\n \"cells\": [\n {\n \"rowSpan\": 1,\n \"colSpan\": 1,\n \"blocks\": []\n },\n {\n \"rowSpan\": 1,\n \"colSpan\": 1,\n \"blocks\": []\n },\n {\n \"rowSpan\": 1,\n \"colSpan\": 1,\n \"blocks\": []\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"193\",\n \"textBlock\": {\n \"text\": \"\\\\(r\\\\)\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"194\",\n \"textBlock\": {\n \"text\": \"\\\\(m\\\\)\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"195\",\n \"textBlock\": {\n \"text\": \"\\\\(M_{c}\\\\)\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"196\",\n \"textBlock\": {\n \"text\": \"N. G. C.\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"197\",\n \"textBlock\": {\n \"text\": \"OBJECT\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"rowSpan\": 1,\n \"colSpan\": 1,\n \"blocks\": []\n },\n {\n \"rowSpan\": 1,\n \"colSpan\": 1,\n \"blocks\": []\n },\n {\n \"rowSpan\": 1,\n \"colSpan\": 1,\n \"blocks\": []\n },\n {\n \"rowSpan\": 1,\n \"colSpan\": 1,\n \"blocks\": []\n }\n ]\n },\n {\n \"cells\": [\n {\n \"rowSpan\": 1,\n \"colSpan\": 1,\n \"blocks\": []\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"198\",\n \"textBlock\": {\n \"text\": \"278\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"199\",\n \"textBlock\": {\n \"text\": \"\\\\(+ 650\\\\)\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"200\",\n \"textBlock\": {\n \"text\": \"\\\\(-110\\\\)\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"201\",\n \"textBlock\": {\n \"text\": \"1.52\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"202\",\n \"textBlock\": {\n \"text\": \"12.0\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"203\",\n \"textBlock\": {\n \"text\": \"\\\\(\\u201313.9\\\\)\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"rowSpan\": 1,\n \"colSpan\": 1,\n \"blocks\": []\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"204\",\n \"textBlock\": {\n \"text\": \"404\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"205\",\n \"textBlock\": {\n \"text\": \"\\\\(\\u2013 25\\\\)\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"206\",\n \"textBlock\": {\n \"text\": \"\\\\(\\u2013 65\\\\)\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"207\",\n \"textBlock\": {\n \"text\": \"...\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"208\",\n \"textBlock\": {\n \"text\": \"11.1\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"209\",\n \"textBlock\": {\n \"text\": \"..\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"rowSpan\": 1,\n \"colSpan\": 1,\n \"blocks\": []\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"210\",\n \"textBlock\": {\n \"text\": \"584\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"211\",\n \"textBlock\": {\n \"text\": \"\\\\(+1800\\\\)\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"212\",\n \"textBlock\": {\n \"text\": \"\\\\(+ 75\\\\)\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"213\",\n \"textBlock\": {\n \"text\": \"3.45\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"214\",\n \"textBlock\": {\n \"text\": \"10.9\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"215\",\n \"textBlock\": {\n \"text\": \"16.8\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"rowSpan\": 1,\n \"colSpan\": 1,\n \"blocks\": []\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"216\",\n \"textBlock\": {\n \"text\": \"936\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"217\",\n \"textBlock\": {\n \"text\": \"\\\\(+1300\\\\)\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"218\",\n \"textBlock\": {\n \"text\": \"\\\\(+115\\\\)\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"219\",\n \"textBlock\": {\n \"text\": \"2.37\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"220\",\n \"textBlock\": {\n \"text\": \"11.1\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"221\",\n \"textBlock\": {\n \"text\": \"15.7\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"rowSpan\": 1,\n \"colSpan\": 1,\n \"blocks\": []\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"222\",\n \"textBlock\": {\n \"text\": \"1023\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"223\",\n \"textBlock\": {\n \"text\": \"\\\\(+ 300\\\\)\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"224\",\n \"textBlock\": {\n \"text\": \"\\\\(\\u2013 10\\\\)\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"225\",\n \"textBlock\": {\n \"text\": \"0.62\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"226\",\n \"textBlock\": {\n \"text\": \"10.2\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"227\",\n \"textBlock\": {\n \"text\": \"13.8\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"rowSpan\": 1,\n \"colSpan\": 1,\n \"blocks\": []\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"228\",\n \"textBlock\": {\n \"text\": \"1700\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"229\",\n \"textBlock\": {\n \"text\": \"\\\\(+ 800\\\\)\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"230\",\n \"textBlock\": {\n \"text\": \"\\\\(+220\\\\)\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"231\",\n \"textBlock\": {\n \"text\": \"1.16\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"232\",\n \"textBlock\": {\n \"text\": \"12.5\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"233\",\n \"textBlock\": {\n \"text\": \"12.8\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"rowSpan\": 1,\n \"colSpan\": 1,\n \"blocks\": []\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"234\",\n \"textBlock\": {\n \"text\": \"2681\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"235\",\n \"textBlock\": {\n \"text\": \"\\\\(+ 700\\\\)\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"236\",\n \"textBlock\": {\n \"text\": \"\\\\(\\u2013 10\\\\)\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"237\",\n \"textBlock\": {\n \"text\": \"1.42\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"238\",\n \"textBlock\": {\n \"text\": \"10.7\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"239\",\n \"textBlock\": {\n \"text\": \"15.0\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"rowSpan\": 1,\n \"colSpan\": 1,\n \"blocks\": []\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"240\",\n \"textBlock\": {\n \"text\": \"2683\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"241\",\n \"textBlock\": {\n \"text\": \"\\\\(+ 400\\\\)\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"242\",\n \"textBlock\": {\n \"text\": \"\\\\(+ 65\\\\)\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"243\",\n \"textBlock\": {\n \"text\": \"0.67\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"244\",\n \"textBlock\": {\n \"text\": \"9.9\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"245\",\n \"textBlock\": {\n \"text\": \"14.3\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"rowSpan\": 1,\n \"colSpan\": 1,\n \"blocks\": []\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"246\",\n \"textBlock\": {\n \"text\": \"2841\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"247\",\n \"textBlock\": {\n \"text\": \"\\\\(+ 600\\\\)\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"248\",\n \"textBlock\": {\n \"text\": \"\\\\(\\u2013 20\\\\)\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"249\",\n \"textBlock\": {\n \"text\": \"1.24\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"250\",\n \"textBlock\": {\n \"text\": \"9.4\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"251\",\n \"textBlock\": {\n \"text\": \"16.1\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"rowSpan\": 1,\n \"colSpan\": 1,\n \"blocks\": []\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"252\",\n \"textBlock\": {\n \"text\": \"3034\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"253\",\n \"textBlock\": {\n \"text\": \"\\\\(+ 290\\\\)\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"254\",\n \"textBlock\": {\n \"text\": \"\\\\(\\u2013105\\\\)\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"255\",\n \"textBlock\": {\n \"text\": \"0.79\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"256\",\n \"textBlock\": {\n \"text\": \"9.0\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"257\",\n \"textBlock\": {\n \"text\": \"15.5\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"rowSpan\": 1,\n \"colSpan\": 1,\n \"blocks\": []\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"258\",\n \"textBlock\": {\n \"text\": \"3115\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"259\",\n \"textBlock\": {\n \"text\": \"\\\\(+ 600\\\\)\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"260\",\n \"textBlock\": {\n \"text\": \"\\\\(+105\\\\)\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"261\",\n \"textBlock\": {\n \"text\": \"1.00\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"262\",\n \"textBlock\": {\n \"text\": \"9.5\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"263\",\n \"textBlock\": {\n \"text\": \"15.5\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"rowSpan\": 1,\n \"colSpan\": 1,\n \"blocks\": []\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"264\",\n \"textBlock\": {\n \"text\": \"3368\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"265\",\n \"textBlock\": {\n \"text\": \"\\\\(+ 940\\\\)\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"266\",\n \"textBlock\": {\n \"text\": \"\\\\(+ 70\\\\)\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"267\",\n \"textBlock\": {\n \"text\": \"1.74\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"268\",\n \"textBlock\": {\n \"text\": \"10.0\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"269\",\n \"textBlock\": {\n \"text\": \"16.2\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"rowSpan\": 1,\n \"colSpan\": 1,\n \"blocks\": []\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"270\",\n \"textBlock\": {\n \"text\": \"3379\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"271\",\n \"textBlock\": {\n \"text\": \"\\\\(+ 810\\\\)\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"272\",\n \"textBlock\": {\n \"text\": \"\\\\(+ 65\\\\)\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"273\",\n \"textBlock\": {\n \"text\": \"1.49\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"274\",\n \"textBlock\": {\n \"text\": \"9.4\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"275\",\n \"textBlock\": {\n \"text\": \"16.4\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"rowSpan\": 1,\n \"colSpan\": 1,\n \"blocks\": []\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"276\",\n \"textBlock\": {\n \"text\": \"3489\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"277\",\n \"textBlock\": {\n \"text\": \"\\\\(+ 600\\\\)\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"278\",\n \"textBlock\": {\n \"text\": \"\\\\(+ 50\\\\)\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"279\",\n \"textBlock\": {\n \"text\": \"1.10\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"280\",\n \"textBlock\": {\n \"text\": \"11.2\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"281\",\n \"textBlock\": {\n \"text\": \"14.0\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"rowSpan\": 1,\n \"colSpan\": 1,\n \"blocks\": []\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"282\",\n \"textBlock\": {\n \"text\": \"3521\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"283\",\n \"textBlock\": {\n \"text\": \"\\\\(+ 730\\\\)\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"284\",\n \"textBlock\": {\n \"text\": \"\\\\(+ 95\\\\)\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"285\",\n \"textBlock\": {\n \"text\": \"1.27\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"286\",\n \"textBlock\": {\n \"text\": \"10.1\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"287\",\n \"textBlock\": {\n \"text\": \"15.4\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"rowSpan\": 1,\n \"colSpan\": 1,\n \"blocks\": []\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"288\",\n \"textBlock\": {\n \"text\": \"3623\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"289\",\n \"textBlock\": {\n \"text\": \"\\\\(+ 800\\\\)\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"290\",\n \"textBlock\": {\n \"text\": \"\\\\(+ 35\\\\)\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"291\",\n \"textBlock\": {\n \"text\": \"1.53\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"292\",\n \"textBlock\": {\n \"text\": \"9.9\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"293\",\n \"textBlock\": {\n \"text\": \"16.0\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"rowSpan\": 1,\n \"colSpan\": 1,\n \"blocks\": []\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"294\",\n \"textBlock\": {\n \"text\": \"4111\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"295\",\n \"textBlock\": {\n \"text\": \"\\\\(+ 800\\\\)\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"296\",\n \"textBlock\": {\n \"text\": \"\\\\(\\u2013 95\\\\)\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"297\",\n \"textBlock\": {\n \"text\": \"1.79\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"298\",\n \"textBlock\": {\n \"text\": \"10.1\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"299\",\n \"textBlock\": {\n \"text\": \"16.1\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"rowSpan\": 1,\n \"colSpan\": 1,\n \"blocks\": []\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"300\",\n \"textBlock\": {\n \"text\": \"4526\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"301\",\n \"textBlock\": {\n \"text\": \"\\\\(+ 580\\\\)\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"302\",\n \"textBlock\": {\n \"text\": \"\\\\(\\u2013 20\\\\)\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"303\",\n \"textBlock\": {\n \"text\": \"1.20\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"304\",\n \"textBlock\": {\n \"text\": \"11.1\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"305\",\n \"textBlock\": {\n \"text\": \"14.3\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"rowSpan\": 1,\n \"colSpan\": 1,\n \"blocks\": []\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"306\",\n \"textBlock\": {\n \"text\": \"4565\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"307\",\n \"textBlock\": {\n \"text\": \"\\\\(+1100\\\\)\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"308\",\n \"textBlock\": {\n \"text\": \"\\\\(\\u2013 75\\\\)\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"309\",\n \"textBlock\": {\n \"text\": \"2.35\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"310\",\n \"textBlock\": {\n \"text\": \"11.0\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"311\",\n \"textBlock\": {\n \"text\": \"15.9\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"rowSpan\": 1,\n \"colSpan\": 1,\n \"blocks\": []\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"312\",\n \"textBlock\": {\n \"text\": \"4594\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"313\",\n \"textBlock\": {\n \"text\": \"\\\\(+1140\\\\)\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"314\",\n \"textBlock\": {\n \"text\": \"\\\\(+ 25\\\\)\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"315\",\n \"textBlock\": {\n \"text\": \"2.23\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"316\",\n \"textBlock\": {\n \"text\": \"9.1\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"317\",\n \"textBlock\": {\n \"text\": \"17.6\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"rowSpan\": 1,\n \"colSpan\": 1,\n \"blocks\": []\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"318\",\n \"textBlock\": {\n \"text\": \"5005\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"319\",\n \"textBlock\": {\n \"text\": \"\\\\(+ 900\\\\)\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"320\",\n \"textBlock\": {\n \"text\": \"\\\\(-130\\\\)\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"321\",\n \"textBlock\": {\n \"text\": \"2.06\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"322\",\n \"textBlock\": {\n \"text\": \"11.1\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"323\",\n \"textBlock\": {\n \"text\": \"15.5\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"rowSpan\": 1,\n \"colSpan\": 1,\n \"blocks\": []\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"324\",\n \"textBlock\": {\n \"text\": \"5866\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"325\",\n \"textBlock\": {\n \"text\": \"\\\\(+ 650\\\\)\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"326\",\n \"textBlock\": {\n \"text\": \"\\\\(-215\\\\)\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"327\",\n \"textBlock\": {\n \"text\": \"1.73\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"328\",\n \"textBlock\": {\n \"text\": \"11.7\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"329\",\n \"textBlock\": {\n \"text\": \"\\\\(\\u201314.5\\\\)\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"330\",\n \"textBlock\": {\n \"text\": \"Mean\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"rowSpan\": 1,\n \"colSpan\": 1,\n \"blocks\": []\n },\n {\n \"rowSpan\": 1,\n \"colSpan\": 1,\n \"blocks\": []\n },\n {\n \"rowSpan\": 1,\n \"colSpan\": 1,\n \"blocks\": []\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"331\",\n \"textBlock\": {\n \"text\": \"10.5\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"332\",\n \"textBlock\": {\n \"text\": \"\\\\(\\u201315.3\\\\)\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n }\n ],\n \"caption\": \"TABLE 2 NEBULAE WHOSE DITANCES ARE ESTIMATED FROM RADIAL VELOCITIES\",\n \"headerRows\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n },\n {\n \"blockId\": \"333\",\n \"textBlock\": {\n \"text\": \"The residuals for the two solutions given above average 150 and 110 km./sec. and should represent the average peculiar motions of the individual nebulae and of the groups, respectively. In order to exhibit the results in a graphical form, the solar motion has been eliminated from the observed velocities and the remainders, the distance terms plus the residuals, have been plotted against the distances. The run of the residuals is about as smooth as can be expected, and in general the form of the solutions appears to be adequate. The 22 nebulae for which distances are not available can be treated in two ways. First, the mean distance of the group derived from the mean apparent magnitudes can be compared with the mean of the velocities Downloaded from https://www.pnas.org by 45.113.94.70 on January 5, 2026 from IP address 45.113.94.70.\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n },\n {\n \"blockId\": \"334\",\n \"textBlock\": {\n \"text\": \"172 ASTRONOMY: E. HUBBLE PROC. N. A. S.\",\n \"type\": \"header\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 5,\n \"pageEnd\": 5\n }\n },\n {\n \"blockId\": \"335\",\n \"textBlock\": {\n \"text\": \"corrected for solar motion. The result, 745 km./sec. for a distance of \\\\(1.4 \\\\times 10^{6}\\\\) parsecs, falls between the two previous solutions and indicates a value for K of 530 as against the proposed value, 500 km./sec. Secondly, the scatter of the individual nebulae can be examined by assuming the relation between distances and velocities as previously determined. Distances can then be calculated from the velocities corrected for solar motion, and absolute magnitudes can be derived from the apparent magnitudes. The results are given in table 2 and may be compared with the distribution of absolute magnitudes among the nebulae in talbe 1, whose distances are derived from other criteria. N. G. C. 404 FIGURE 1 Velocity-Distance Relation among Extra-Galactic Nebulae. Radial velocities, corrected for solar motion, are plotted against distances estimated from involved stars and mean luminosities of nebulae in a cluster. The black discs and full line represent the solution for solar motion using the nebulae individually; the circles and broken line represent the solution combining the nebulae into groups; the cross represents the mean velocity corresponding to the mean distance of 22 nebulae whose distances could not be estimated individually. can be excluded, since the observed velocity is so small that the peculiar motion must be large in comparison with the distance effect. The object is not necessarily an exception, however, since a distance can be assigned for which the peculiar motion and the absolute magnitude are both within the range previously determined. The two mean magnitudes, -15.3 and -15.5, the ranges, 4.9 and 5.0 mag., and the frequency distributions are closely similar for these two entirely independent sets of data; and even the slight difference in mean magnitudes can be attributed to the selected, very bright, nebulae in the Virgo Cluster. This entirely unforced agreement supports the validity of the velocity-distance relation in a very\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 5,\n \"pageEnd\": 5\n }\n },\n {\n \"blockId\": \"336\",\n \"textBlock\": {\n \"text\": \"Downloaded from https://www.pnas.org by 45.113.94.70 on January 5, 2026 from IP address 45.113.94.70. Vol. 15, 1929 ASTRONOMY: E. HUBBLE 173\",\n \"type\": \"header\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 6,\n \"pageEnd\": 6\n }\n },\n {\n \"blockId\": \"337\",\n \"textBlock\": {\n \"text\": \"evident matter. Finally, it is worth recording that the frequency distribution of absolute magnitudes in the two tables combined is comparable with those found in the various clusters of nebulae. The results establish a roughly linear relation between velocities and distances among nebulae for which velocities have been previously published, and the relation appears to dominate the distribution of velocities. In order to investigate the matter on a much larger scale, Mr. Humason at Mount Wilson has initiated a program of determining velocities of the most distant nebulae that can be observed with confidence. These, naturally, are the brightest nebulae in clusters of nebulae. The first definite result, \\\\(v = +3779 ~km./sec.\\\\) for N. G. C. 7619, is thoroughly consistent with the present conclusions. Corrected for the solar motion, this velocity is \\\\(+3910\\\\), which, with \\\\(K = 500\\\\), corresponds to a distance of \\\\(7.8 \\\\times 10^{6}\\\\) parsecs. Since the apparent magnitude is 11.8, the absolute magnitude at such a distance is \\\\(-17.65\\\\), which is of the right order for the brightest nebulae in a cluster. A preliminary distance, derived independently from the cluster of which this nebula appears to be a member, is of the order of \\\\(7 \\\\times 10^{6}\\\\) parsecs. New data to be expected in the near future may modify the significance of the present investigation or, if confirmatory, will lead to a solution having many times the weight. For this reason it is thought premature to discuss in detail the obvious consequences of the present results. For example, if the solar motion with respect to the clusters represents the rotation of the galactic system, this motion could be subtracted from the results for the nebulae and the remainder would represent the motion of the galactic system with respect to the extra-galactic nebulae. The outstanding feature, however, is the possibility that the velocity-distance relation may represent the de Sitter effect, and hence that numerical data may be introduced into discussions of the general curvature of space. In the de Sitter cosmology, displacements of the spectra arise from two sources, an apparent slowing down of atomic vibrations and a general tendency of material particles to scatter. The latter involves an acceleration and hence introduces the element of time. The relative importance of these two effects should determine the form of the relation between distances and observed velocities; and in this connection it may be emphasized that the linear relation found in the present discussion is a first approximation representing a restricted range in distance.\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 6,\n \"pageEnd\": 6\n }\n },\n {\n \"blockId\": \"338\",\n \"listBlock\": {\n \"listEntries\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"339\",\n \"textBlock\": {\n \"text\": \"Mt. Wilson Contr., No. 324; Astroph. J., Chicago, Ill., 64, 1926 (321).\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 6,\n \"pageEnd\": 6\n }\n }\n ]\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"340\",\n \"textBlock\": {\n \"text\": \"Harvard Coll. Obs. Circ., 294, 1926.\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 6,\n \"pageEnd\": 6\n }\n }\n ]\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"341\",\n \"textBlock\": {\n \"text\": \"Mon. Not. R. Astr. Soc., 85, 1925 (865-894).\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 6,\n \"pageEnd\": 6\n }\n }\n ]\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"342\",\n \"textBlock\": {\n \"text\": \"These PROCEEDINGS, 15, 1929 (167).\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 6,\n \"pageEnd\": 6\n }\n }\n ]\n }\n ],\n \"type\": \"ordered\"\n },\n \"pageSpan\": {\n \"pageStart\": 6,\n \"pageEnd\": 6\n }\n }\n ]\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 6\n }\n }\n ]\n },\n \"docid\": \"\",\n \"mimeType\": \"\",\n \"text\": \"\",\n \"textStyles\": [],\n \"pages\": [],\n \"entities\": [],\n \"entityRelations\": [],\n \"textChanges\": [],\n \"revisions\": [],\n \"entitiesRevisions\": [],\n \"entitiesRevisionId\": \"\"\n}"] +["{\n \"documentLayout\": {\n \"blocks\": [\n {\n \"blockId\": \"1\",\n \"textBlock\": {\n \"text\": \"168 ASTRONOMY: E. HUBBLE PROC. N. A. S.\",\n \"type\": \"header\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 1,\n \"pageEnd\": 1\n }\n },\n {\n \"blockId\": \"2\",\n \"textBlock\": {\n \"text\": \"appearance the spectrum is very much like spectra of the Milky Way clouds in Sagittarius and Cygnus, and is also similar to spectra of binary stars of the W Ursae Majoris type, where the widening and depth of the lines are affected by the rapid rotation of the stars involved. The wide shallow absorption lines observed in the spectrum of N. G. C. 7619 have been noticed in the spectra of other extra-galactic nebulae, and may be due to a dispersion in velocity and a blending of the spectral types of the many stars which presumably exist in the central parts of these nebulae. The lack of depth in the absorption lines seems to be more pronounced among the smaller and fainter nebulae, and in N. G. C. 7619 the absorption is very weak. It is hoped that velocities of more of these interesting objects will soon be available.\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 1,\n \"pageEnd\": 1\n }\n },\n {\n \"blockId\": \"3\",\n \"textBlock\": {\n \"text\": \"A RELATION BETWEEN DISTANCE AND RADIAL VELOCITY AMONG EXTRA-GALACTIC NEBULAE\",\n \"type\": \"heading-1\",\n \"blocks\": [\n {\n \"blockId\": \"4\",\n \"textBlock\": {\n \"text\": \"BY EDWIN HUBBLE\",\n \"type\": \"heading-4\",\n \"blocks\": [\n {\n \"blockId\": \"5\",\n \"textBlock\": {\n \"text\": \"MOUNT WILSON OBSERVATORY, CARNEGIE INSTITUTION OF WASHINGTON Communicated January 17, 1929 Determinations of the motion of the sun with respect to the extra-galactic nebulae have involved a \\\\(K\\\\) term of several hundred kilometers which appears to be variable. Explanations of this paradox have been sought in a correlation between apparent radial velocities and distances, but so far the results have not been convincing. The present paper is a re-examination of the question, based on only those nebular distances which are believed to be fairly reliable. Distances of extra-galactic nebulae depend ultimately upon the application of absolute-luminosity criteria to involved stars whose types can be recognized. These include, among others, Cepheid variables, novae, and blue stars involved in emission nebulosity. Numerical values depend upon the zero point of the period-luminosity relation among Cepheids, the other criteria merely check the order of the distances. This method is restricted to the few nebulae which are well resolved by existing instruments. A study of these nebulae, together with those in which any stars at all can be recognized, indicates the probability of an approximately uniform upper limit to the absolute luminosity of stars, in the late-type spirals and irregular nebulae at least, of the order of \\\\(M\\\\) (photographic) = \\\\(-6.3.1\\\\) The apparent luminosities of the brightest stars in such nebulae are thus criteria which, although rough and to be applied with caution,\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 1,\n \"pageEnd\": 1\n }\n },\n {\n \"blockId\": \"6\",\n \"textBlock\": {\n \"text\": \"Downloaded from https://www.pnas.org by 45.113.94.70 on January 5, 2026 from IP address 45.113.94.70.\",\n \"type\": \"footer\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 1,\n \"pageEnd\": 1\n }\n }\n ]\n },\n \"pageSpan\": {\n \"pageStart\": 1,\n \"pageEnd\": 1\n }\n },\n {\n \"blockId\": \"7\",\n \"textBlock\": {\n \"text\": \"VOL. 15, 1929 ASTRONOMY: E. HUBBLE 169\",\n \"type\": \"header\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n },\n {\n \"blockId\": \"8\",\n \"textBlock\": {\n \"text\": \"furnish reasonable estimates of the distances of all extra-galactic systems in which even a few stars can be detected.\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n },\n {\n \"blockId\": \"9\",\n \"textBlock\": {\n \"text\": \"TABLE 1\",\n \"type\": \"heading-2\",\n \"blocks\": [\n {\n \"blockId\": \"10\",\n \"textBlock\": {\n \"text\": \"NEBULAE WHOSE DISTANCES HAVE BEEN ESTIMATED FROM STARS INVOLVED OR FROM MEAN LUMINOSITIES IN A CLUSTER\",\n \"type\": \"heading-3\",\n \"blocks\": [\n {\n \"blockId\": \"11\",\n \"tableBlock\": {\n \"bodyRows\": [\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"12\",\n \"textBlock\": {\n \"text\": \"OBJECT\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"13\",\n \"textBlock\": {\n \"text\": \"m\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"14\",\n \"textBlock\": {\n \"text\": \"r\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"rowSpan\": 1,\n \"colSpan\": 1,\n \"blocks\": []\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"15\",\n \"textBlock\": {\n \"text\": \"m\\\\(_{t}\\\\)\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"16\",\n \"textBlock\": {\n \"text\": \"M\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"17\",\n \"textBlock\": {\n \"text\": \"S. Mag.\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"rowSpan\": 1,\n \"colSpan\": 1,\n \"blocks\": []\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"18\",\n \"textBlock\": {\n \"text\": \"0.032\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"19\",\n \"textBlock\": {\n \"text\": \"+ 170\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"20\",\n \"textBlock\": {\n \"text\": \"1.5\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"21\",\n \"textBlock\": {\n \"text\": \"-16.0\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"22\",\n \"textBlock\": {\n \"text\": \"L. Mag.\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"23\",\n \"textBlock\": {\n \"text\": \"..\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"24\",\n \"textBlock\": {\n \"text\": \"0.034\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"25\",\n \"textBlock\": {\n \"text\": \"+ 290\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"26\",\n \"textBlock\": {\n \"text\": \"0.5\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"27\",\n \"textBlock\": {\n \"text\": \"17.2\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"28\",\n \"textBlock\": {\n \"text\": \"N. G. C. 6822\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"rowSpan\": 1,\n \"colSpan\": 1,\n \"blocks\": []\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"29\",\n \"textBlock\": {\n \"text\": \"0.214\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"30\",\n \"textBlock\": {\n \"text\": \"\\u2212 130\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"31\",\n \"textBlock\": {\n \"text\": \"9.0\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"32\",\n \"textBlock\": {\n \"text\": \"12.7\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"33\",\n \"textBlock\": {\n \"text\": \"598\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"34\",\n \"textBlock\": {\n \"text\": \"..\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"35\",\n \"textBlock\": {\n \"text\": \"0.263\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"36\",\n \"textBlock\": {\n \"text\": \"\\u2212 70\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"37\",\n \"textBlock\": {\n \"text\": \"7.0\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"38\",\n \"textBlock\": {\n \"text\": \"15.1\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"39\",\n \"textBlock\": {\n \"text\": \"221\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"40\",\n \"textBlock\": {\n \"text\": \"..\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"41\",\n \"textBlock\": {\n \"text\": \"0.275\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"42\",\n \"textBlock\": {\n \"text\": \"\\u2212 185\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"43\",\n \"textBlock\": {\n \"text\": \"8.8\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"44\",\n \"textBlock\": {\n \"text\": \"13.4\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"45\",\n \"textBlock\": {\n \"text\": \"224\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"46\",\n \"textBlock\": {\n \"text\": \"..\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"47\",\n \"textBlock\": {\n \"text\": \"0.275\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"48\",\n \"textBlock\": {\n \"text\": \"\\u2212 220\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"49\",\n \"textBlock\": {\n \"text\": \"5.0\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"50\",\n \"textBlock\": {\n \"text\": \"17.2\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"51\",\n \"textBlock\": {\n \"text\": \"5457\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"52\",\n \"textBlock\": {\n \"text\": \"17.0\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"53\",\n \"textBlock\": {\n \"text\": \"0.45\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"54\",\n \"textBlock\": {\n \"text\": \"+ 200\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"55\",\n \"textBlock\": {\n \"text\": \"9.9\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"56\",\n \"textBlock\": {\n \"text\": \"13.3\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"57\",\n \"textBlock\": {\n \"text\": \"4736\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"58\",\n \"textBlock\": {\n \"text\": \"17.3\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"59\",\n \"textBlock\": {\n \"text\": \"0.5\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"60\",\n \"textBlock\": {\n \"text\": \"+ 290\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"61\",\n \"textBlock\": {\n \"text\": \"8.4\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"62\",\n \"textBlock\": {\n \"text\": \"15.1\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"63\",\n \"textBlock\": {\n \"text\": \"5194\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"64\",\n \"textBlock\": {\n \"text\": \"17.3\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"65\",\n \"textBlock\": {\n \"text\": \"0.5\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"66\",\n \"textBlock\": {\n \"text\": \"+ 270\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"67\",\n \"textBlock\": {\n \"text\": \"7.4\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"68\",\n \"textBlock\": {\n \"text\": \"16.1\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"69\",\n \"textBlock\": {\n \"text\": \"4449\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"70\",\n \"textBlock\": {\n \"text\": \"17.8\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"71\",\n \"textBlock\": {\n \"text\": \"0.63\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"72\",\n \"textBlock\": {\n \"text\": \"+ 200\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"73\",\n \"textBlock\": {\n \"text\": \"9.5\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"74\",\n \"textBlock\": {\n \"text\": \"14.5\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"75\",\n \"textBlock\": {\n \"text\": \"4214\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"76\",\n \"textBlock\": {\n \"text\": \"18.3\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"77\",\n \"textBlock\": {\n \"text\": \"0.8\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"78\",\n \"textBlock\": {\n \"text\": \"+ 300\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"79\",\n \"textBlock\": {\n \"text\": \"11.3\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"80\",\n \"textBlock\": {\n \"text\": \"13.2\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"81\",\n \"textBlock\": {\n \"text\": \"3031\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"82\",\n \"textBlock\": {\n \"text\": \"18.5\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"83\",\n \"textBlock\": {\n \"text\": \"0.9\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"84\",\n \"textBlock\": {\n \"text\": \"\\u2212 30\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"85\",\n \"textBlock\": {\n \"text\": \"8.3\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"86\",\n \"textBlock\": {\n \"text\": \"16.4\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"87\",\n \"textBlock\": {\n \"text\": \"3627\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"88\",\n \"textBlock\": {\n \"text\": \"18.5\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"89\",\n \"textBlock\": {\n \"text\": \"0.9\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"90\",\n \"textBlock\": {\n \"text\": \"+ 650\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"91\",\n \"textBlock\": {\n \"text\": \"9.1\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"92\",\n \"textBlock\": {\n \"text\": \"15.7\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"93\",\n \"textBlock\": {\n \"text\": \"4826\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"94\",\n \"textBlock\": {\n \"text\": \"18.5\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"95\",\n \"textBlock\": {\n \"text\": \"0.9\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"96\",\n \"textBlock\": {\n \"text\": \"+ 150\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"97\",\n \"textBlock\": {\n \"text\": \"9.0\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"98\",\n \"textBlock\": {\n \"text\": \"15.7\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"99\",\n \"textBlock\": {\n \"text\": \"5236\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"100\",\n \"textBlock\": {\n \"text\": \"18.5\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"101\",\n \"textBlock\": {\n \"text\": \"0.9\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"102\",\n \"textBlock\": {\n \"text\": \"+ 500\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"103\",\n \"textBlock\": {\n \"text\": \"10.4\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"104\",\n \"textBlock\": {\n \"text\": \"14.4\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"105\",\n \"textBlock\": {\n \"text\": \"1068\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"106\",\n \"textBlock\": {\n \"text\": \"18.7\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"107\",\n \"textBlock\": {\n \"text\": \"1.0\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"108\",\n \"textBlock\": {\n \"text\": \"+ 920\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"109\",\n \"textBlock\": {\n \"text\": \"9.1\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"110\",\n \"textBlock\": {\n \"text\": \"15.9\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"111\",\n \"textBlock\": {\n \"text\": \"5055\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"112\",\n \"textBlock\": {\n \"text\": \"19.0\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"113\",\n \"textBlock\": {\n \"text\": \"1.1\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"114\",\n \"textBlock\": {\n \"text\": \"+ 450\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"115\",\n \"textBlock\": {\n \"text\": \"9.6\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"116\",\n \"textBlock\": {\n \"text\": \"15.6\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"117\",\n \"textBlock\": {\n \"text\": \"7331\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"118\",\n \"textBlock\": {\n \"text\": \"19.0\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"119\",\n \"textBlock\": {\n \"text\": \"1.1\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"120\",\n \"textBlock\": {\n \"text\": \"+ 500\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"121\",\n \"textBlock\": {\n \"text\": \"10.4\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"122\",\n \"textBlock\": {\n \"text\": \"14.8\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"123\",\n \"textBlock\": {\n \"text\": \"4258\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"124\",\n \"textBlock\": {\n \"text\": \"19.5\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"125\",\n \"textBlock\": {\n \"text\": \"1.4\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"126\",\n \"textBlock\": {\n \"text\": \"+ 500\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"127\",\n \"textBlock\": {\n \"text\": \"8.7\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"128\",\n \"textBlock\": {\n \"text\": \"17.0\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"129\",\n \"textBlock\": {\n \"text\": \"4151\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"130\",\n \"textBlock\": {\n \"text\": \"20.0\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"131\",\n \"textBlock\": {\n \"text\": \"1.7\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"132\",\n \"textBlock\": {\n \"text\": \"+ 960\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"133\",\n \"textBlock\": {\n \"text\": \"12.0\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"134\",\n \"textBlock\": {\n \"text\": \"14.2\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"135\",\n \"textBlock\": {\n \"text\": \"4382\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"136\",\n \"textBlock\": {\n \"text\": \"..\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"137\",\n \"textBlock\": {\n \"text\": \"2.0\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"138\",\n \"textBlock\": {\n \"text\": \"+ 500\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"139\",\n \"textBlock\": {\n \"text\": \"10.0\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"140\",\n \"textBlock\": {\n \"text\": \"16.5\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"141\",\n \"textBlock\": {\n \"text\": \"4472\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"142\",\n \"textBlock\": {\n \"text\": \"..\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"143\",\n \"textBlock\": {\n \"text\": \"2.0\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"144\",\n \"textBlock\": {\n \"text\": \"+ 850\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"145\",\n \"textBlock\": {\n \"text\": \"8.8\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"146\",\n \"textBlock\": {\n \"text\": \"17.7\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"147\",\n \"textBlock\": {\n \"text\": \"4486\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"148\",\n \"textBlock\": {\n \"text\": \"..\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"149\",\n \"textBlock\": {\n \"text\": \"2.0\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"150\",\n \"textBlock\": {\n \"text\": \"+ 800\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"151\",\n \"textBlock\": {\n \"text\": \"9.7\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"152\",\n \"textBlock\": {\n \"text\": \"16.8\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"153\",\n \"textBlock\": {\n \"text\": \"4649\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"154\",\n \"textBlock\": {\n \"text\": \"..\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"155\",\n \"textBlock\": {\n \"text\": \"2.0\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"156\",\n \"textBlock\": {\n \"text\": \"+1090\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"157\",\n \"textBlock\": {\n \"text\": \"9.5\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"158\",\n \"textBlock\": {\n \"text\": \"17.0\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"159\",\n \"textBlock\": {\n \"text\": \"Mean\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"rowSpan\": 1,\n \"colSpan\": 1,\n \"blocks\": []\n },\n {\n \"rowSpan\": 1,\n \"colSpan\": 1,\n \"blocks\": []\n },\n {\n \"rowSpan\": 1,\n \"colSpan\": 1,\n \"blocks\": []\n },\n {\n \"rowSpan\": 1,\n \"colSpan\": 1,\n \"blocks\": []\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"160\",\n \"textBlock\": {\n \"text\": \"-15.5\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n }\n ],\n \"headerRows\": [],\n \"caption\": \"\"\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n },\n {\n \"blockId\": \"161\",\n \"textBlock\": {\n \"text\": \"m\\\\(_{s}\\\\) = photographic magnitude of brightest stars involved. r = distance in units of 10\\\\(^{6}\\\\) parsecs. The first two are Shapley's values. v = measured velocities in km./sec. N. G. C. 6822, 221, 224 and 5457 are recent determinations by Humason. m\\\\(_{t}\\\\) = Holetschek's visual magnitude as corrected by Hopmann. The first three objects were not measured by Holetschek, and the values of m\\\\(_{t}\\\\) represent estimates by the author based upon such data as are available. M\\\\(_{t}\\\\) = total visual absolute magnitude computed from m\\\\(_{t}\\\\) and r. Finally, the nebulae themselves appear to be of a definite order of absolute luminosity, exhibiting a range of four or five magnitudes about an average value M (visual) = -15.2\\\\(^{1}\\\\). The application of this statistical average to individual cases can rarely be used to advantage, but where considerable numbers are involved, and especially in the various clusters of nebulae, mean apparent luminosities of the nebulae themselves offer reliable estimates of the mean distances. Radial velocities of 46 extra-galactic nebulae are now available, but\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n }\n ]\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 2\n }\n },\n {\n \"blockId\": \"162\",\n \"textBlock\": {\n \"text\": \"170 ASTRONOMY: E. HUBBLE PROC. N. A. S.\",\n \"type\": \"header\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 3,\n \"pageEnd\": 3\n }\n },\n {\n \"blockId\": \"163\",\n \"textBlock\": {\n \"text\": \"individual distances are estimated for only 24. For one other, N. G. C. 3521, an estimate could probably be made, but no photographs are available at Mount Wilson. The data are given in table 1. The first seven distances are the most reliable, depending, except for M 32 the companion of M 31, upon extensive investigations of many stars involved. The next thirteen distances, depending upon the criterion of a uniform upper limit of stellar luminosity, are subject to considerable probable errors but are believed to be the most reasonable values at present available. The last four objects appear to be in the Virgo Cluster. The distance assigned to the cluster, \\\\(2 \\\\times 10^6\\\\) parsecs, is derived from the distribution of nebular luminosities, together with luminosities of stars in some of the later-type spirals, and differs somewhat from the Harvard estimate of ten million light years.2 The data in the table indicate a linear correlation between distances and velocities, whether the latter are used directly or corrected for solar motion, according to the older solutions. This suggests a new solution for the solar motion in which the distances are introduced as coefficients of the K term, i. e., the velocities are assumed to vary directly with the distances, and hence K represents the velocity at unit distance due to this effect. The equations of condition then take the form \\\\(rK + X \\\\cos \\\\alpha \\\\cos \\\\delta + Y \\\\sin \\\\alpha \\\\cos \\\\delta + Z \\\\sin \\\\delta = v.\\\\) Two solutions have been made, one using the 24 nebulae individually, the other combining them into 9 groups according to proximity in direction and in distance. The results are\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 3,\n \"pageEnd\": 3\n }\n },\n {\n \"blockId\": \"164\",\n \"tableBlock\": {\n \"bodyRows\": [\n {\n \"cells\": [\n {\n \"rowSpan\": 1,\n \"colSpan\": 1,\n \"blocks\": []\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"165\",\n \"textBlock\": {\n \"text\": \"24 OBJECTS\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 3,\n \"pageEnd\": 3\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"166\",\n \"textBlock\": {\n \"text\": \"9 GROUPS\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 3,\n \"pageEnd\": 3\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"167\",\n \"textBlock\": {\n \"text\": \"X\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 3,\n \"pageEnd\": 3\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"168\",\n \"textBlock\": {\n \"text\": \"\\\\( -65 \\\\pm 50\\\\)\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 3,\n \"pageEnd\": 3\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"169\",\n \"textBlock\": {\n \"text\": \"\\\\( +3 \\\\pm 70\\\\)\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 3,\n \"pageEnd\": 3\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"170\",\n \"textBlock\": {\n \"text\": \"Y\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 3,\n \"pageEnd\": 3\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"171\",\n \"textBlock\": {\n \"text\": \"\\\\( +226 \\\\pm 95\\\\)\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 3,\n \"pageEnd\": 3\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"172\",\n \"textBlock\": {\n \"text\": \"\\\\( +230 \\\\pm 120\\\\)\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 3,\n \"pageEnd\": 3\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"173\",\n \"textBlock\": {\n \"text\": \"Z\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 3,\n \"pageEnd\": 3\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"174\",\n \"textBlock\": {\n \"text\": \"\\\\( -195 \\\\pm 40\\\\)\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 3,\n \"pageEnd\": 3\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"175\",\n \"textBlock\": {\n \"text\": \"\\\\( -133 \\\\pm 70\\\\)\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 3,\n \"pageEnd\": 3\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"176\",\n \"textBlock\": {\n \"text\": \"K\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 3,\n \"pageEnd\": 3\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"177\",\n \"textBlock\": {\n \"text\": \"\\\\( +465 \\\\pm 50\\\\)\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 3,\n \"pageEnd\": 3\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"178\",\n \"textBlock\": {\n \"text\": \"\\\\( +513 \\\\pm 60 \\\\text{ km./sec. per } 10^6 \\\\text{ parsecs.}\\\\)\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 3,\n \"pageEnd\": 3\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"179\",\n \"textBlock\": {\n \"text\": \"A\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 3,\n \"pageEnd\": 3\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"180\",\n \"textBlock\": {\n \"text\": \"\\\\( 286^\\\\circ\\\\)\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 3,\n \"pageEnd\": 3\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"181\",\n \"textBlock\": {\n \"text\": \"\\\\( 269^\\\\circ\\\\)\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 3,\n \"pageEnd\": 3\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"182\",\n \"textBlock\": {\n \"text\": \"D\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 3,\n \"pageEnd\": 3\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"183\",\n \"textBlock\": {\n \"text\": \"\\\\( +40^\\\\circ\\\\)\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 3,\n \"pageEnd\": 3\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"184\",\n \"textBlock\": {\n \"text\": \"\\\\( +33^\\\\circ\\\\)\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 3,\n \"pageEnd\": 3\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"185\",\n \"textBlock\": {\n \"text\": \"V\\\\(_0\\\\)\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 3,\n \"pageEnd\": 3\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"186\",\n \"textBlock\": {\n \"text\": \"306 km./sec.\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 3,\n \"pageEnd\": 3\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"187\",\n \"textBlock\": {\n \"text\": \"247 km./sec.\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 3,\n \"pageEnd\": 3\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n }\n ],\n \"headerRows\": [],\n \"caption\": \"\"\n },\n \"pageSpan\": {\n \"pageStart\": 3,\n \"pageEnd\": 3\n }\n },\n {\n \"blockId\": \"188\",\n \"textBlock\": {\n \"text\": \"For such scanty material, so poorly distributed, the results are fairly definite. Differences between the two solutions are due largely to the four Virgo nebulae, which, being the most distant objects and all sharing the peculiar motion of the cluster, unduly influence the value of K and hence of V\\\\(_0\\\\). New data on more distant objects will be required to reduce the effect of such peculiar motion. Meanwhile round numbers, intermediate between the two solutions, will represent the probable order of the values. For instance, let \\\\(A = 277^\\\\circ, D = +36^\\\\circ\\\\) (Gal. long. \\\\( = 32^\\\\circ\\\\), lat. \\\\( = +18^\\\\circ\\\\)), \\\\(V_0 = 280\\\\) km./sec., \\\\(K = +500\\\\) km./sec. per million par-\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 3,\n \"pageEnd\": 3\n }\n },\n {\n \"blockId\": \"189\",\n \"textBlock\": {\n \"text\": \"Downloaded from https://www.pnas.org by 45.113.94.70 on January 5, 2026 from IP address 45.113.94.70.\",\n \"type\": \"footer\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 3,\n \"pageEnd\": 3\n }\n },\n {\n \"blockId\": \"190\",\n \"textBlock\": {\n \"text\": \"VOL. 15, 1929 ASTRONOMY: E. HUBBLE 171\",\n \"type\": \"header\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n },\n {\n \"blockId\": \"191\",\n \"textBlock\": {\n \"text\": \"secs. Mr. Str\\u00f6mberg has very kindly checked the general order of these values by independent solutions for different groupings of the data. A constant term, introduced into the equations, was found to be small and negative. This seems to dispose of the necessity for the old constant \\\\(K\\\\) term. Solutions of this sort have been published by Lundmark, who replaced the old \\\\(K\\\\) by \\\\(k + Ir + mr^{2}\\\\). His favored solution gave \\\\(k = 513\\\\), as against the former value of the order of 700, and hence offered little advantage.\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n },\n {\n \"blockId\": \"192\",\n \"tableBlock\": {\n \"headerRows\": [\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"327\",\n \"textBlock\": {\n \"text\": \"OBJECT\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"rowSpan\": 1,\n \"colSpan\": 1,\n \"blocks\": []\n },\n {\n \"rowSpan\": 1,\n \"colSpan\": 1,\n \"blocks\": []\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"328\",\n \"textBlock\": {\n \"text\": \"r\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"329\",\n \"textBlock\": {\n \"text\": \"m\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"330\",\n \"textBlock\": {\n \"text\": \"\\\\(M_{t}\\\\)\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n }\n ],\n \"bodyRows\": [\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"193\",\n \"textBlock\": {\n \"text\": \"N. G. C. 278\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"194\",\n \"textBlock\": {\n \"text\": \"+ 650\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"195\",\n \"textBlock\": {\n \"text\": \"\\u2013110\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"196\",\n \"textBlock\": {\n \"text\": \"1.52\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"197\",\n \"textBlock\": {\n \"text\": \"12.0\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"198\",\n \"textBlock\": {\n \"text\": \"\\u201313.9\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"199\",\n \"textBlock\": {\n \"text\": \"404\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"200\",\n \"textBlock\": {\n \"text\": \"\\u2013 25\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"201\",\n \"textBlock\": {\n \"text\": \"\\u2013 65\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"rowSpan\": 1,\n \"colSpan\": 1,\n \"blocks\": []\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"202\",\n \"textBlock\": {\n \"text\": \"11.1\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"203\",\n \"textBlock\": {\n \"text\": \"..\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"204\",\n \"textBlock\": {\n \"text\": \"584\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"205\",\n \"textBlock\": {\n \"text\": \"+1800\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"206\",\n \"textBlock\": {\n \"text\": \"+ 75\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"207\",\n \"textBlock\": {\n \"text\": \"3.45\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"208\",\n \"textBlock\": {\n \"text\": \"10.9\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"209\",\n \"textBlock\": {\n \"text\": \"16.8\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"210\",\n \"textBlock\": {\n \"text\": \"936\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"211\",\n \"textBlock\": {\n \"text\": \"+1300\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"212\",\n \"textBlock\": {\n \"text\": \"+115\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"213\",\n \"textBlock\": {\n \"text\": \"2.37\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"214\",\n \"textBlock\": {\n \"text\": \"11.1\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"215\",\n \"textBlock\": {\n \"text\": \"15.7\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"216\",\n \"textBlock\": {\n \"text\": \"1023\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"217\",\n \"textBlock\": {\n \"text\": \"+ 300\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"218\",\n \"textBlock\": {\n \"text\": \"\\u2013 10\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"219\",\n \"textBlock\": {\n \"text\": \"0.62\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"220\",\n \"textBlock\": {\n \"text\": \"10.2\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"221\",\n \"textBlock\": {\n \"text\": \"13.8\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"222\",\n \"textBlock\": {\n \"text\": \"1700\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"223\",\n \"textBlock\": {\n \"text\": \"+ 800\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"224\",\n \"textBlock\": {\n \"text\": \"+220\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"225\",\n \"textBlock\": {\n \"text\": \"1.16\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"226\",\n \"textBlock\": {\n \"text\": \"12.5\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"227\",\n \"textBlock\": {\n \"text\": \"12.8\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"228\",\n \"textBlock\": {\n \"text\": \"2681\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"229\",\n \"textBlock\": {\n \"text\": \"+ 700\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"230\",\n \"textBlock\": {\n \"text\": \"\\u2013 10\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"231\",\n \"textBlock\": {\n \"text\": \"1.42\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"232\",\n \"textBlock\": {\n \"text\": \"10.7\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"233\",\n \"textBlock\": {\n \"text\": \"15.0\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"234\",\n \"textBlock\": {\n \"text\": \"2683\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"235\",\n \"textBlock\": {\n \"text\": \"+ 400\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"236\",\n \"textBlock\": {\n \"text\": \"+ 65\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"237\",\n \"textBlock\": {\n \"text\": \"0.67\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"238\",\n \"textBlock\": {\n \"text\": \"9.9\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"239\",\n \"textBlock\": {\n \"text\": \"14.3\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"240\",\n \"textBlock\": {\n \"text\": \"2841\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"241\",\n \"textBlock\": {\n \"text\": \"+ 600\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"242\",\n \"textBlock\": {\n \"text\": \"\\u2013 20\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"243\",\n \"textBlock\": {\n \"text\": \"1.24\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"244\",\n \"textBlock\": {\n \"text\": \"9.4\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"245\",\n \"textBlock\": {\n \"text\": \"16.1\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"246\",\n \"textBlock\": {\n \"text\": \"3034\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"247\",\n \"textBlock\": {\n \"text\": \"+ 290\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"248\",\n \"textBlock\": {\n \"text\": \"\\u2013105\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"249\",\n \"textBlock\": {\n \"text\": \"0.79\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"250\",\n \"textBlock\": {\n \"text\": \"9.0\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"251\",\n \"textBlock\": {\n \"text\": \"15.5\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"252\",\n \"textBlock\": {\n \"text\": \"3115\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"253\",\n \"textBlock\": {\n \"text\": \"+ 600\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"254\",\n \"textBlock\": {\n \"text\": \"+105\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"255\",\n \"textBlock\": {\n \"text\": \"1.00\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"256\",\n \"textBlock\": {\n \"text\": \"9.5\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"257\",\n \"textBlock\": {\n \"text\": \"15.5\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"258\",\n \"textBlock\": {\n \"text\": \"3368\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"259\",\n \"textBlock\": {\n \"text\": \"+ 940\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"260\",\n \"textBlock\": {\n \"text\": \"+ 70\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"261\",\n \"textBlock\": {\n \"text\": \"1.74\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"262\",\n \"textBlock\": {\n \"text\": \"10.0\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"263\",\n \"textBlock\": {\n \"text\": \"16.2\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"264\",\n \"textBlock\": {\n \"text\": \"3379\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"265\",\n \"textBlock\": {\n \"text\": \"+ 810\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"266\",\n \"textBlock\": {\n \"text\": \"+ 65\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"267\",\n \"textBlock\": {\n \"text\": \"1.49\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"268\",\n \"textBlock\": {\n \"text\": \"9.4\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"269\",\n \"textBlock\": {\n \"text\": \"16.4\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"270\",\n \"textBlock\": {\n \"text\": \"3489\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"271\",\n \"textBlock\": {\n \"text\": \"+ 600\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"272\",\n \"textBlock\": {\n \"text\": \"+ 50\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"273\",\n \"textBlock\": {\n \"text\": \"1.10\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"274\",\n \"textBlock\": {\n \"text\": \"11.2\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"275\",\n \"textBlock\": {\n \"text\": \"14.0\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"276\",\n \"textBlock\": {\n \"text\": \"3521\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"277\",\n \"textBlock\": {\n \"text\": \"+ 730\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"278\",\n \"textBlock\": {\n \"text\": \"+ 95\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"279\",\n \"textBlock\": {\n \"text\": \"1.27\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"280\",\n \"textBlock\": {\n \"text\": \"10.1\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"281\",\n \"textBlock\": {\n \"text\": \"15.4\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"282\",\n \"textBlock\": {\n \"text\": \"3623\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"283\",\n \"textBlock\": {\n \"text\": \"+ 800\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"284\",\n \"textBlock\": {\n \"text\": \"+ 35\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"285\",\n \"textBlock\": {\n \"text\": \"1.53\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"286\",\n \"textBlock\": {\n \"text\": \"9.9\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"287\",\n \"textBlock\": {\n \"text\": \"16.0\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"288\",\n \"textBlock\": {\n \"text\": \"4111\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"289\",\n \"textBlock\": {\n \"text\": \"+ 800\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"290\",\n \"textBlock\": {\n \"text\": \"\\u2013 95\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"291\",\n \"textBlock\": {\n \"text\": \"1.79\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"292\",\n \"textBlock\": {\n \"text\": \"10.1\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"293\",\n \"textBlock\": {\n \"text\": \"16.1\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"294\",\n \"textBlock\": {\n \"text\": \"4526\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"295\",\n \"textBlock\": {\n \"text\": \"+ 580\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"296\",\n \"textBlock\": {\n \"text\": \"\\u2013 20\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"297\",\n \"textBlock\": {\n \"text\": \"1.20\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"298\",\n \"textBlock\": {\n \"text\": \"11.1\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"299\",\n \"textBlock\": {\n \"text\": \"14.3\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"300\",\n \"textBlock\": {\n \"text\": \"4565\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"301\",\n \"textBlock\": {\n \"text\": \"+1100\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"302\",\n \"textBlock\": {\n \"text\": \"+ 75\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"303\",\n \"textBlock\": {\n \"text\": \"2.35\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"304\",\n \"textBlock\": {\n \"text\": \"11.0\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"305\",\n \"textBlock\": {\n \"text\": \"15.9\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"306\",\n \"textBlock\": {\n \"text\": \"4594\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"307\",\n \"textBlock\": {\n \"text\": \"+1140\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"308\",\n \"textBlock\": {\n \"text\": \"+ 25\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"309\",\n \"textBlock\": {\n \"text\": \"2.23\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"310\",\n \"textBlock\": {\n \"text\": \"9.1\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"311\",\n \"textBlock\": {\n \"text\": \"17.6\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"312\",\n \"textBlock\": {\n \"text\": \"5005\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"313\",\n \"textBlock\": {\n \"text\": \"+ 900\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"314\",\n \"textBlock\": {\n \"text\": \"\\u2013130\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"315\",\n \"textBlock\": {\n \"text\": \"2.06\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"316\",\n \"textBlock\": {\n \"text\": \"11.1\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"317\",\n \"textBlock\": {\n \"text\": \"15.5\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"318\",\n \"textBlock\": {\n \"text\": \"5866\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"319\",\n \"textBlock\": {\n \"text\": \"+ 650\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"320\",\n \"textBlock\": {\n \"text\": \"\\u2013215\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"321\",\n \"textBlock\": {\n \"text\": \"1.73\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"322\",\n \"textBlock\": {\n \"text\": \"11.7\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"323\",\n \"textBlock\": {\n \"text\": \"\\u201314.5\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n },\n {\n \"cells\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"324\",\n \"textBlock\": {\n \"text\": \"Mean\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"rowSpan\": 1,\n \"colSpan\": 1,\n \"blocks\": []\n },\n {\n \"rowSpan\": 1,\n \"colSpan\": 1,\n \"blocks\": []\n },\n {\n \"rowSpan\": 1,\n \"colSpan\": 1,\n \"blocks\": []\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"325\",\n \"textBlock\": {\n \"text\": \"10.5\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"326\",\n \"textBlock\": {\n \"text\": \"\\u201315.3\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n }\n ],\n \"rowSpan\": 1,\n \"colSpan\": 1\n }\n ]\n }\n ],\n \"caption\": \"TABLE 2 NEBULAE WHOSE DISTANCES ARE ESTIMATED FROM RADIAL VELOCITIES\"\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n },\n {\n \"blockId\": \"331\",\n \"textBlock\": {\n \"text\": \"The residuals for the two solutions given above average 150 and 110 km./sec. and should represent the average peculiar motions of the individual nebulae and of the groups, respectively. In order to exhibit the results in a graphical form, the solar motion has been eliminated from the observed velocities and the remainders, the distance terms plus the residuals, have been plotted against the distances. The run of the residuals is about as smooth as can be expected, and in general the form of the solutions appears to be adequate. The 22 nebulae for which distances are not available can be treated in two ways. First, the mean distance of the group derived from the mean apparent magnitudes can be compared with the mean of the velocities\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n },\n {\n \"blockId\": \"332\",\n \"textBlock\": {\n \"text\": \"Downloaded from https://www.pnas.org by 45.113.94.70 on January 5, 2026 from IP address 45.113.94.70.\",\n \"type\": \"footer\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 4,\n \"pageEnd\": 4\n }\n },\n {\n \"blockId\": \"333\",\n \"textBlock\": {\n \"text\": \"172 ASTRONOMY: E. HUBBLE PROC. N. A. S.\",\n \"type\": \"header\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 5,\n \"pageEnd\": 5\n }\n },\n {\n \"blockId\": \"334\",\n \"textBlock\": {\n \"text\": \"corrected for solar motion. The result, \\\\(745~km./sec.\\\\) for a distance of \\\\(1.4 \\\\times 10^{6}\\\\) parsecs, falls between the two previous solutions and indicates a value for K of 530 as against the proposed value, \\\\(500~km./sec.\\\\) Secondly, the scatter of the individual nebulae can be examined by assuming the relation between distances and velocities as previously determined. Distances can then be calculated from the velocities corrected for solar motion, and absolute magnitudes can be derived from the apparent magnitudes. The results are given in table 2 and may be compared with the distribution of absolute magnitudes among the nebulae in table 1, whose distances are derived from other criteria. N. G. C. 404 FIGURE 1 Velocity-Distance Relation among Extra-Galactic Nebulae. Radial velocities, corrected for solar motion, are plotted against distances estimated from involved stars and mean luminosities of nebulae in a cluster. The black discs and full line represent the solution for solar motion using the nebulae individually; the circles and broken line represent the solution combining the nebulae into groups; the cross represents the mean velocity corresponding to the mean distance of 22 nebulae whose distances could not be estimated individually. can be excluded, since the observed velocity is so small that the peculiar motion must be large in comparison with the distance effect. The object is not necessarily an exception, however, since a distance can be assigned for which the peculiar motion and the absolute magnitude are both within the range previously determined. The two mean magnitudes, \\\\(\\u221215.3\\\\) and \\\\(\\u221215.5\\\\), the ranges, 4.9 and 5.0 mag., and the frequency distributions are closely similar for these two entirely independent sets of data; and even the slight difference in mean magnitudes can be attributed to the selected, very bright, nebulae in the Virgo Cluster. This entirely unforced agreement supports the validity of the velocity-distance relation in a very\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 5,\n \"pageEnd\": 5\n }\n },\n {\n \"blockId\": \"335\",\n \"textBlock\": {\n \"text\": \"Downloaded from https://www.pnas.org by 45.113.94.70 on January 5, 2026 from IP address 45.113.94.70.\",\n \"type\": \"footer\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 5,\n \"pageEnd\": 5\n }\n },\n {\n \"blockId\": \"336\",\n \"textBlock\": {\n \"text\": \"VOL. 15, 1929 ASTRONOMY: E. HUBBLE 173\",\n \"type\": \"header\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 6,\n \"pageEnd\": 6\n }\n },\n {\n \"blockId\": \"337\",\n \"textBlock\": {\n \"text\": \"evident matter. Finally, it is worth recording that the frequency distribu- tion of absolute magnitudes in the two tables combined is comparable with those found in the various clusters of nebulae. The results establish a roughly linear relation between velocities and distances among nebulae for which velocities have been previously pub- lished, and the relation appears to dominate the distribution of velocities. In order to investigate the matter on a much larger scale, Mr. Humason at Mount Wilson has initiated a program of determining velocities of the most distant nebulae that can be observed with confidence. These, naturally, are the brightest nebulae in clusters of nebulae. The first definite result, \\\\(v =+3779 ~km./sec.\\\\) for N. G. C. 7619, is thoroughly consistent with the present conclusions. Corrected for the solar motion, this velocity is +3910, which, with \\\\(K = 500\\\\), corresponds to a distance of \\\\(7.8 \\\\times 10^{6}\\\\) parsecs. Since the apparent magnitude is 11.8, the absolute magnitude at such a distance is -17.65, which is of the right order for the brightest nebulae in a cluster. A preliminary dis- tance, derived independently from the cluster of which this nebula appears to be a member, is of the order of \\\\(7 \\\\times 10^{6}\\\\) parsecs. New data to be expected in the near future may modify the significance of the present investigation or, if confirmatory, will lead to a solution having many times the weight. For this reason it is thought premature to discuss in detail the obvious consequences of the present results. For example, if the solar motion with respect to the clusters represents the rotation of the galactic system, this motion could be subtracted from the results for the nebulae and the remainder would represent the motion of the galactic system with respect to the extra-galactic nebulae. The outstanding feature, however, is the possibility that the velocity- distance relation may represent the de Sitter effect, and hence that numer- ical data may be introduced into discussions of the general curvature of space. In the de Sitter cosmology, displacements of the spectra arise from two sources, an apparent slowing down of atomic vibrations and a general tendency of material particles to scatter. The latter involves an acceleration and hence introduces the element of time. The relative im- portance of these two effects should determine the form of the relation between distances and observed velocities; and in this connection it may be emphasized that the linear relation found in the present discussion is a first approximation representing a restricted range in distance.\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 6,\n \"pageEnd\": 6\n }\n },\n {\n \"blockId\": \"338\",\n \"listBlock\": {\n \"listEntries\": [\n {\n \"blocks\": [\n {\n \"blockId\": \"339\",\n \"textBlock\": {\n \"text\": \"Mt. Wilson Contr., No. 324; Astroph. J., Chicago, Ill., 64, 1926 (321).\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 6,\n \"pageEnd\": 6\n }\n }\n ]\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"340\",\n \"textBlock\": {\n \"text\": \"Harvard Coll. Obs. Circ., 294, 1926.\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 6,\n \"pageEnd\": 6\n }\n }\n ]\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"341\",\n \"textBlock\": {\n \"text\": \"Mon. Not. R. Astr. Soc., 85, 1925 (865-894).\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 6,\n \"pageEnd\": 6\n }\n }\n ]\n },\n {\n \"blocks\": [\n {\n \"blockId\": \"342\",\n \"textBlock\": {\n \"text\": \"These PROCEEDINGS, 15, 1929 (167).\",\n \"type\": \"paragraph\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 6,\n \"pageEnd\": 6\n }\n }\n ]\n }\n ],\n \"type\": \"ordered\"\n },\n \"pageSpan\": {\n \"pageStart\": 6,\n \"pageEnd\": 6\n }\n },\n {\n \"blockId\": \"343\",\n \"textBlock\": {\n \"text\": \"Downloaded from https://www.pnas.org by 45.113.94.70 on January 5, 2026 from IP address 45.113.94.70.\",\n \"type\": \"footer\",\n \"blocks\": []\n },\n \"pageSpan\": {\n \"pageStart\": 6,\n \"pageEnd\": 6\n }\n }\n ]\n },\n \"pageSpan\": {\n \"pageStart\": 2,\n \"pageEnd\": 6\n }\n }\n ]\n },\n \"pageSpan\": {\n \"pageStart\": 1,\n \"pageEnd\": 2\n }\n }\n ]\n },\n \"docid\": \"\",\n \"mimeType\": \"\",\n \"text\": \"\",\n \"textStyles\": [],\n \"pages\": [],\n \"entities\": [],\n \"entityRelations\": [],\n \"textChanges\": [],\n \"revisions\": [],\n \"entitiesRevisions\": [],\n \"entitiesRevisionId\": \"\"\n}"] \ No newline at end of file diff --git a/tests/fixtures/hubble_gemini_responses.json b/tests/fixtures/hubble_gemini_responses.json index a73d4fb..ab6c717 100644 --- a/tests/fixtures/hubble_gemini_responses.json +++ b/tests/fixtures/hubble_gemini_responses.json @@ -1 +1 @@ -["\n168 ASTRONOMY: E. HUBBLE PROC. N. A. S.\n\nappearance the spectrum is very much like spectra of the Milky Way\nclouds in Sagittarius and Cygnus, and is also similar to spectra of binary\nstars of the W Ursae Majoris type, where the widening and depth of the\nlines are affected by the rapid rotation of the stars involved.\n\nThe wide shallow absorption lines observed in the spectrum of N. G. C.\n7619 have been noticed in the spectra of other extra-galactic nebulae, and\nmay be due to a dispersion in velocity and a blending of the spectral types\nof the many stars which presumably exist in the central parts of these\nnebulae. The lack of depth in the absorption lines seems to be more\npronounced among the smaller and fainter nebulae, and in N. G. C. 7619\nthe absorption is very weak.\n\nIt is hoped that velocities of more of these interesting objects will soon\nbe available.\n\n## A RELATION BETWEEN DISTANCE AND RADIAL VELOCITY AMONG EXTRA-GALACTIC NEBULAE\n\nBY EDWIN HUBBLE\nMOUNT WILSON OBSERVATORY, CARNEGIE INSTITUTION OF WASHINGTON\nCommunicated January 17, 1929\n\nDeterminations of the motion of the sun with respect to the extra-\ngalactic nebulae have involved a $K$ term of several hundred kilometers\nwhich appears to be variable. Explanations of this paradox have been\nsought in a correlation between apparent radial velocities and distances,\nbut so far the results have not been convincing. The present paper is a\nre-examination of the question, based on only those nebular distances\nwhich are believed to be fairly reliable.\n\nDistances of extra-galactic nebulae depend ultimately upon the appli-\ncation of absolute-luminosity criteria to involved stars whose types can\nbe recognized. These include, among others, Cepheid variables, novae,\nand blue stars involved in emission nebulosity. Numerical values depend\nupon the zero point of the period-luminosity relation among Cepheids,\nthe other criteria merely check the order of the distances. This method\nis restricted to the few nebulae which are well resolved by existing instru-\nments. A study of these nebulae, together with those in which any stars\nat all can be recognized, indicates the probability of an approximately\nuniform upper limit to the absolute luminosity of stars, in the late-type\nspirals and irregular nebulae at least, of the order of $M$ (photographic) =\n-6.3.1 The apparent luminosities of the brightest stars in such nebulae\nare thus criteria which, although rough and to be applied with caution,\n\n\nVOL. 15, 1929 ASTRONOMY: E. HUBBLE 169\n\nfurnish reasonable estimates of the distances of all extra-galactic systems\nin which even a few stars can be detected.\n\n\nTABLE 1\nNEBULAE WHOSE DISTANCES HAVE BEEN ESTIMATED FROM STARS INVOLVED OR FROM\nMEAN LUMINOSITIES IN A CLUSTER\n\n| OBJECT | $m_c$ | $r$ | $v_c$ | $m_v$ | $M_v$ |\n| :----------- | :---- | :---- | :------- | :---- | :------- |\n| S. Mag. | .. | 0.032 | + 170 | 1.5 | -16.0 |\n| L. Mag. | .. | 0.034 | + 290 | 0.5 | 17.2 |\n| N. G. C. 6822 | .. | 0.214 | - 130 | 9.0 | 12.7 |\n| 598 | .. | 0.263 | - 70 | 7.0 | 15.1 |\n| 221 | .. | 0.275 | - 185 | 8.8 | 13.4 |\n| 224 | .. | 0.275 | - 220 | 5.0 | 17.2 |\n| 5457 | 17.0 | 0.45 | + 200 | 9.9 | 13.3 |\n| 4736 | 17.3 | 0.5 | + 290 | 8.4 | 15.1 |\n| 5194 | 17.3 | 0.5 | + 270 | 7.4 | 16.1 |\n| 4449 | 17.8 | 0.63 | + 200 | 9.5 | 14.5 |\n| 4214 | 18.3 | 0.8 | + 300 | 11.3 | 13.2 |\n| 3031 | 18.5 | 0.9 | - 30 | 8.3 | 16.4 |\n| 3627 | 18.5 | 0.9 | + 650 | 9.1 | 15.7 |\n| 4826 | 18.5 | 0.9 | + 150 | 9.0 | 15.7 |\n| 5236 | 18.5 | 0.9 | + 500 | 10.4 | 14.4 |\n| 1068 | 18.7 | 1.0 | + 920 | 9.1 | 15.9 |\n| 5055 | 19.0 | 1.1 | + 450 | 9.6 | 15.6 |\n| 7331 | 19.0 | 1.1 | + 500 | 10.4 | 14.8 |\n| 4258 | 19.5 | 1.4 | + 500 | 8.7 | 17.0 |\n| 4151 | 20.0 | 1.7 | + 960 | 12.0 | 14.2 |\n| 4382 | .. | 2.0 | + 500 | 10.0 | 16.5 |\n| 4472 | .. | 2.0 | + 850 | 8.8 | 17.7 |\n| 4486 | .. | 2.0 | + 800 | 9.7 | 16.8 |\n| 4649 | .. | 2.0 | +1090 | 9.5 | 17.0 |\n| Mean | | | | | -15.5 |\n\n$m_c$ = photographic magnitude of brightest stars involved.\n$r$ = distance in units of $10^6$ parsecs. The first two are Shapley's values.\n$v_c$ = measured velocities in km./sec. N. G. C. 6822, 221, 224 and 5457 are recent\ndeterminations by Humason.\n$m_v$ = Holetschek's visual magnitude as corrected by Hopmann. The first three\nobjects were not measured by Holetschek, and the values of $m_v$ represent\nestimates by the author based upon such data as are available.\n$M_v$ = total visual absolute magnitude computed from $m_v$ and $r$.\n\n\nFinally, the nebulae themselves appear to be of a definite order of\nabsolute luminosity, exhibiting a range of four or five magnitudes about\nan average value $M$ (visual) = -15.2.1 The application of this statistical\naverage to individual cases can rarely be used to advantage, but where\nconsiderable numbers are involved, and especially in the various clusters\nof nebulae, mean apparent luminosities of the nebulae themselves offer\nreliable estimates of the mean distances.\n\nRadial velocities of 46 extra-galactic nebulae are now available, but\n\n\n170 ASTRONOMY: E. HUBBLE PROC. N. A. S.\n\nindividual distances are estimated for only 24. For one other, N. G. C.\n3521, an estimate could probably be made, but no photographs are avail-\nable at Mount Wilson. The data are given in table 1. The first seven\ndistances are the most reliable, depending, except for M 32 the companion of\nM 31, upon extensive investigations of many stars involved. The next\nthirteen distances, depending upon the criterion of a uniform upper limit\nof stellar luminosity, are subject to considerable probable errors but are\nbelieved to be the most reasonable values at present available. The last\nfour objects appear to be in the Virgo Cluster. The distance assigned\nto the cluster, $2 \\times 10^6$ parsecs, is derived from the distribution of nebular\nluminosities, together with luminosities of stars in some of the later-type\nspirals, and differs somewhat from the Harvard estimate of ten million\nlight years.2\n\nThe data in the table indicate a linear correlation between distances and\nvelocities, whether the latter are used directly or corrected for solar motion,\naccording to the older solutions. This suggests a new solution for the solar\nmotion in which the distances are introduced as coefficients of the $K$ term,\ni. e., the velocities are assumed to vary directly with the distances, and\nhence $K$ represents the velocity at unit distance due to this effect. The\nequations of condition then take the form\n\n$$ rK + X \\cos \\alpha \\cos \\delta + Y \\sin \\alpha \\cos \\delta + Z \\sin \\delta = v. $$\n\nTwo solutions have been made, one using the 24 nebulae individually,\nthe other combining them into 9 groups according to proximity in direc-\ntion and in distance. The results are\n\n24 OBJECTS\n$X$ = -65 $\\pm$ 50\n$Y$ = +226 $\\pm$ 95\n$Z$ = -195 $\\pm$ 40\n$K$ = +465 $\\pm$ 50\n$A$ = $286^{\\circ}$\n$D$ = $+40^{\\circ}$\n$V_o$ = 306 km./sec.\n\n9 GROUPS\n+3 $\\pm$ 70\n+230 $\\pm$ 120\n-133 $\\pm$ 70\n+513 $\\pm$ 60 km./sec. per $10^6$ parsecs.\n$269^{\\circ}$\n$+33^{\\circ}$\n247 km./sec.\n\nFor such scanty material, so poorly distributed, the results are fairly\ndefinite. Differences between the two solutions are due largely to the\nfour Virgo nebulae, which, being the most distant objects and all sharing\nthe peculiar motion of the cluster, unduly influence the value of $K$ and\nhence of $V_o$. New data on more distant objects will be required to reduce\nthe effect of such peculiar motion. Meanwhile round numbers, inter-\nmediate between the two solutions, will represent the probable order of\nthe values. For instance, let $A = 277^{\\circ}$, $D = +36^{\\circ}$ (Gal. long. = $32^{\\circ}$,\nlat. = $+18^{\\circ}$), $V_o = 280$ km./sec., $K = +500$ km./sec. per million par-\n\n\nVOL. 15, 1929 ASTRONOMY: E. HUBBLE 171\n\nsecs. Mr. Str\u00f6mberg has very kindly checked the general order of these\nvalues by independent solutions for different groupings of the data.\n\nA constant term, introduced into the equations, was found to be small\nand negative. This seems to dispose of the necessity for the old constant\n$K$ term. Solutions of this sort have been published by Lundmark,3 who\nreplaced the old $K$ by $k + lr + mr^2$. His favored solution gave $k = 513$,\nas against the former value of the order of 700, and hence offered little\nadvantage.\n\n\nTABLE 2\nNEBULAE WHOSE DISTANCES ARE ESTIMATED FROM RADIAL VELOCITIES\n\n| OBJECT | $v_c$ | $v_o$ | $r$ | $m_v$ | $M_v$ |\n| :----------- | :------ | :------- | :---- | :---- | :------- |\n| N. G. C. 278 | + 650 | -110 | 1.52 | 12.0 | -13.9 |\n| 404 | + 25 | - 65 | | 11.1 | |\n| 584 | +1800 | + 75 | 3.45 | 10.9 | 16.8 |\n| 936 | +1300 | +115 | 2.37 | 11.1 | 15.7 |\n| 1023 | + 300 | - 10 | 0.62 | 10.2 | 13.8 |\n| 1700 | + 800 | +220 | 1.16 | 12.5 | 12.8 |\n| 2681 | + 700 | - 10 | 1.42 | 10.7 | 15.0 |\n| 2683 | + 400 | + 65 | 0.67 | 9.9 | 14.3 |\n| 2841 | + 600 | - 20 | 1.24 | 9.4 | 16.1 |\n| 3034 | + 290 | -105 | 0.79 | 9.0 | 15.5 |\n| 3115 | + 600 | +105 | 1.00 | 9.5 | 15.5 |\n| 3368 | + 940 | + 70 | 1.74 | 10.0 | 16.2 |\n| 3379 | + 810 | + 65 | 1.49 | 9.4 | 16.4 |\n| 3489 | + 600 | + 50 | 1.10 | 11.2 | 14.0 |\n| 3521 | + 730 | + 95 | 1.27 | 10.1 | 15.4 |\n| 3623 | + 800 | + 35 | 1.53 | 9.9 | 16.0 |\n| 4111 | + 800 | - 95 | 1.79 | 10.1 | 16.1 |\n| 4526 | + 580 | - 20 | 1.20 | 11.1 | 14.3 |\n| 4565 | +1100 | - 75 | 2.35 | 11.0 | 15.9 |\n| 4594 | +1140 | + 25 | 2.23 | 9.1 | 17.6 |\n| 5005 | + 900 | -130 | 2.06 | 11.1 | 15.5 |\n| 5866 | + 650 | -215 | 1.73 | 11.7 | -14.5 |\n| Mean | | | | 10.5 | -15.3 |\n\n\nThe residuals for the two solutions given above average 150 and 110\nkm./sec. and should represent the average peculiar motions of the in-\ndividual nebulae and of the groups, respectively. In order to exhibit\nthe results in a graphical form, the solar motion has been eliminated from\nthe observed velocities and the remainders, the distance terms plus the\nresiduals, have been plotted against the distances. The run of the re-\nsiduals is about as smooth as can be expected, and in general the form of\nthe solutions appears to be adequate.\n\nThe 22 nebulae for which distances are not available can be treated in\ntwo ways. First, the mean distance of the group derived from the mean\napparent magnitudes can be compared with the mean of the velocities\n\n\n172 ASTRONOMY: E. HUBBLE PROC. N. A. S.\n\ncorrected for solar motion. The result, 745 km./sec. for a distance of\n$1.4 \\times 10^6$ parsecs, falls between the two previous solutions and indicates\na value for $K$ of 530 as against the proposed value, 500 km./sec.\n\nSecondly, the scatter of the individual nebulae can be examined by\nassuming the relation between distances and velocities as previously\ndetermined. Distances can then be calculated from the velocities cor-\nrected for solar motion, and absolute magnitudes can be derived from the\napparent magnitudes. The results are given in table 2 and may be\ncompared with the distribution of absolute magnitudes among the nebulae\nin table 1, whose distances are derived from other criteria. N. G. C. 404\n\n\n\"Graph\n\nFIGURE 1\nVelocity-Distance Relation among Extra-Galactic Nebulae.\n\nRadial velocities, corrected for solar motion, are plotted against\ndistances estimated from involved stars and mean luminosities of\nnebulae in a cluster. The black discs and full line represent the\nsolution for solar motion using the nebulae individually; the circles\nand broken line represent the solution combining the nebulae into\ngroups; the cross represents the mean velocity corresponding to\nthe mean distance of 22 nebulae whose distances could not be esti-\nmated individually.\n\n\ncan be excluded, since the observed velocity is so small that the peculiar\nmotion must be large in comparison with the distance effect. The object\nis not necessarily an exception, however, since a distance can be assigned\nfor which the peculiar motion and the absolute magnitude are both within\nthe range previously determined. The two mean magnitudes, -15.3\nand -15.5, the ranges, 4.9 and 5.0 mag., and the frequency distributions\nare closely similar for these two entirely independent sets of data; and\neven the slight difference in mean magnitudes can be attributed to the\nselected, very bright, nebulae in the Virgo Cluster. This entirely unforced\nagreement supports the validity of the velocity-distance relation in a very\n\n\nVOL. 15, 1929 ASTRONOMY: E. HUBBLE 173\n\nevident matter. Finally, it is worth recording that the frequency distribu-\ntion of absolute magnitudes in the two tables combined is comparable\nwith those found in the various clusters of nebulae.4\n\nThe results establish a roughly linear relation between velocities and\ndistances among nebulae for which velocities have been previously pub-\nlished, and the relation appears to dominate the distribution of velocities.\nIn order to investigate the matter on a much larger scale, Mr. Humason\nat Mount Wilson has initiated a program of determining velocities\nof the most distant nebulae that can be observed with confidence.\nThese, naturally, are the brightest nebulae in clusters of nebulae.\nThe first definite result, $v = + 3779$ km./sec. for N. G. C. 7619, is\nthoroughly consistent with the present conclusions. Corrected for the\nsolar motion, this velocity is +3910, which, with $K = 500$, corresponds to\na distance of $7.8 \\times 10^6$ parsecs. Since the apparent magnitude is 11.8,\nthe absolute magnitude at such a distance is -17.65, which is of the\nright order for the brightest nebulae in a cluster. A preliminary dis-\ntance, derived independently from the cluster of which this nebula appears\nto be a member, is of the order of $7 \\times 10^6$ parsecs.\n\nNew data to be expected in the near future may modify the significance\nof the present investigation or, if confirmatory, will lead to a solution\nhaving many times the weight. For this reason it is thought premature\nto discuss in detail the obvious consequences of the present results. For\nexample, if the solar motion with respect to the clusters represents the\nrotation of the galactic system, this motion could be subtracted from the\nresults for the nebulae and the remainder would represent the motion of\nthe galactic system with respect to the extra-galactic nebulae.\n\nThe outstanding feature, however, is the possibility that the velocity-\ndistance relation may represent the de Sitter effect, and hence that numer-\nical data may be introduced into discussions of the general curvature of\nspace. In the de Sitter cosmology, displacements of the spectra arise\nfrom two sources, an apparent slowing down of atomic vibrations and a\ngeneral tendency of material particles to scatter. The latter involves an\nacceleration and hence introduces the element of time. The relative im-\nportance of these two effects should determine the form of the relation\nbetween distances and observed velocities; and in this connection it may\nbe emphasized that the linear relation found in the present discussion is a\nfirst approximation representing a restricted range in distance.\n\n1. *Mt. Wilson Contr.*, No. 324; *Astroph. J.*, Chicago, Ill., 64, 1926 (321).\n2. *Harvard Coll. Obs. Circ.*, 294, 1926.\n3. *Mon. Not. R. Astr. Soc.*, 85, 1925 (865-894).\n4. These *PROCEEDINGS*, 15, 1929 (167)."] +["\nappearance the spectrum is very much like spectra of the Milky Way\nclouds in Sagittarius and Cygnus, and is also similar to spectra of binary\nstars of the W Ursae Majoris type, where the widening and depth of the\nlines are affected by the rapid rotation of the stars involved.\n\nThe wide shallow absorption lines observed in the spectrum of N. G. C.\n7619 have been noticed in the spectra of other extra-galactic nebulae, and\nmay be due to a dispersion in velocity and a blending of the spectral types\nof the many stars which presumably exist in the central parts of these\nnebulae. The lack of depth in the absorption lines seems to be more\npronounced among the smaller and fainter nebulae, and in N. G. C. 7619\nthe absorption is very weak.\n\nIt is hoped that velocities of more of these interesting objects will soon\nbe available.\n\n[Check for updates box is present on the right side of the page.]\n\n# A RELATION BETWEEN DISTANCE AND RADIAL VELOCITY\n# AMONG EXTRA-GALACTIC NEBULAE\n\nBY EDWIN HUBBLE\n\nMOUNT WILSON OBSERVATORY, CARNEGIE INSTITUTION OF WASHINGTON\n\nCommunicated January 17, 1929\n\nDeterminations of the motion of the sun with respect to the extra-\ngalactic nebulae have involved a $K$ term of several hundred kilometers\nwhich appears to be variable. Explanations of this paradox have been\nsought in a correlation between apparent radial velocities and distances,\nbut so far the results have not been convincing. The present paper is a\nre-examination of the question, based on only those nebular distances\nwhich are believed to be fairly reliable.\n\nDistances of extra-galactic nebulae depend ultimately upon the appli-\ncation of absolute-luminosity criteria to involved stars whose types can\nbe recognized. These include, among others, Cepheid variables, novae,\nand blue stars involved in emission nebulosity. Numerical values depend\nupon the zero point of the period-luminosity relation among Cepheids,\nthe other criteria merely check the order of the distances. This method\nis restricted to the few nebulae which are well resolved by existing instru-\nments. A study of these nebulae, together with those in which any stars\nat all can be recognized, indicates the probability of an approximately\nuniform upper limit to the absolute luminosity of stars, in the late-type\nspirals and irregular nebulae at least, of the order of $M$ (photographic) =\n-6.3. $^{1}$ The apparent luminosities of the brightest stars in such nebulae\nare thus criteria which, although rough and to be applied with caution,\n\n\nfurnish reasonable estimates of the distances of all extra-galactic systems\nin which even a few stars can be detected.\n\n\nTABLE 1\nNEBULAE WHOSE DISTANCES HAVE BEEN ESTIMATED FROM STARS INVOLVED OR FROM\nMEAN LUMINOSITIES IN A CLUSTER\n\n| OBJECT | $m_c$ | $r$ | $v$ | $m_e$ | $M_c$ |\n| :------------- | :---- | :----- | :----- | :---- | :------ |\n| S. Mag. | | 0.032 | + 170 | 1.5 | -16.0 |\n| L. Mag. | | 0.034 | + 290 | 0.5 | 17.2 |\n| N. G. C. 6822 | | 0.214 | - 130 | 9.0 | 12.7 |\n| 598 | | 0.263 | - 70 | 7.0 | 15.1 |\n| 221 | | 0.275 | - 185 | 8.8 | 13.4 |\n| 224 | | 0.275 | - 220 | 5.0 | 17.2 |\n| 5457 | 17.0 | 0.45 | + 200 | 9.9 | 13.3 |\n| 4736 | 17.3 | 0.5 | + 290 | 8.4 | 15.1 |\n| 5194 | 17.3 | 0.5 | + 270 | 7.4 | 16.1 |\n| 4449 | 17.8 | 0.63 | + 200 | 9.5 | 14.5 |\n| 4214 | 18.3 | 0.8 | + 300 | 11.3 | 13.2 |\n| 3031 | 18.5 | 0.9 | - 30 | 8.3 | 16.4 |\n| 3627 | 18.5 | 0.9 | + 650 | 9.1 | 15.7 |\n| 4826 | 18.5 | 0.9 | + 150 | 9.0 | 15.7 |\n| 5236 | 18.5 | 0.9 | + 500 | 10.4 | 14.4 |\n| 1068 | 18.7 | 1.0 | + 920 | 9.1 | 15.9 |\n| 5055 | 19.0 | 1.1 | + 450 | 9.6 | 15.6 |\n| 7331 | 19.0 | 1.1 | + 500 | 10.4 | 14.8 |\n| 4258 | 19.5 | 1.4 | + 500 | 8.7 | 17.0 |\n| 4151 | 20.0 | 1.7 | + 960 | 12.0 | 14.2 |\n| 4382 | | 2.0 | + 500 | 10.0 | 16.5 |\n| 4472 | | 2.0 | + 850 | 8.8 | 17.7 |\n| 4486 | | 2.0 | + 800 | 9.7 | 16.8 |\n| 4649 | | 2.0 | +1090 | 9.5 | 17.0 |\n| **Mean** | | | | | **-15.5** |\n\n\n$M_c$ = photographic magnitude of brightest stars involved.\n$r$ = distance in units of $10^6$ parsecs. The first two are Shapley's values.\n$v$ = measured velocities in km./sec. N. G. C. 6822, 221, 224 and 5457 are recent\ndeterminations by Humason.\n$m_e$ = Holetschek's visual magnitude as corrected by Hopmann. The first three\nobjects were not measured by Holetschek, and the values of $m_e$ represent\nestimates by the author based upon such data as are available.\n$M_c$ = total visual absolute magnitude computed from $m_e$ and $r$.\n\nFinally, the nebulae themselves appear to be of a definite order of\nabsolute luminosity, exhibiting a range of four or five magnitudes about\nan average value $M$ (visual) = -15.2.$^{1}$ The application of this statistical\naverage to individual cases can rarely be used to advantage, but where\nconsiderable numbers are involved, and especially in the various clusters\nof nebulae, mean apparent luminosities of the nebulae themselves offer\nreliable estimates of the mean distances.\n\nRadial velocities of 46 extra-galactic nebulae are now available, but\n\n\nindividual distances are estimated for only 24. For one other, N. G. C.\n3521, an estimate could probably be made, but no photographs are avail-\nable at Mount Wilson. The data are given in table 1. The first seven\ndistances are the most reliable, depending, except for M 32 the companion of\nM 31, upon extensive investigations of many stars involved. The next\nthirteen distances, depending upon the criterion of a uniform upper limit\nof stellar luminosity, are subject to considerable probable errors but are\nbelieved to be the most reasonable values at present available. The last\nfour objects appear to be in the Virgo Cluster. The distance assigned\nto the cluster, $2 \\times 10^6$ parsecs, is derived from the distribution of nebular\nluminosities, together with luminosities of stars in some of the later-type\nspirals, and differs somewhat from the Harvard estimate of ten million\nlight years. $^{2}$\n\nThe data in the table indicate a linear correlation between distances and\nvelocities, whether the latter are used directly or corrected for solar motion,\naccording to the older solutions. This suggests a new solution for the solar\nmotion in which the distances are introduced as coefficients of the $K$ term,\ni. e., the velocities are assumed to vary directly with the distances, and\nhence $K$ represents the velocity at unit distance due to this effect. The\nequations of condition then take the form\n\n$rK + X \\cos \\alpha \\cos \\delta + Y \\sin \\alpha \\cos \\delta + Z \\sin \\delta = v$.\n\nTwo solutions have been made, one using the 24 nebulae individually,\nthe other combining them into 9 groups according to proximity in direc-\ntion and in distance. The results are\n\n\n| | **24 OBJECTS** | **9 GROUPS** |\n| :----------- | :------------- | :----------- |\n| $X$ | -65 \u00b1 50 | +3 \u00b1 70 |\n| $Y$ | +226 \u00b1 95 | +230 \u00b1 120 |\n| $Z$ | -195 \u00b1 40 | -133 \u00b1 70 |\n| $K$ | +465 \u00b1 50 | +513 \u00b1 60 km./sec. per $10^6$ parsecs. |\n| $\\Lambda$ | 286\u00b0 | 269\u00b0 |\n| $D$ | +40\u00b0 | +33\u00b0 |\n| $V_0$ | 306 km./sec. | 247 km./sec. |\n\n\nFor such scanty material, so poorly distributed, the results are fairly\ndefinite. Differences between the two solutions are due largely to the\nfour Virgo nebulae, which, being the most distant objects and all sharing\nthe peculiar motion of the cluster, unduly influence the value of $K$ and\nhence of $V_0$. New data on more distant objects will be required to reduce\nthe effect of such peculiar motion. Meanwhile round numbers, inter-\nmediate between the two solutions, will represent the probable order of\nthe values. For instance, let $\\Lambda$ = 277\u00b0, $D$ = +36\u00b0 (Gal. long. = 32\u00b0,\nlat. = +18\u00b0), $V_0$ = 280 km./sec., $K$ = +500 km./sec. per million par-\n\n\nsecs. Mr. Str\u00f6mberg has very kindly checked the general order of these\nvalues by independent solutions for different groupings of the data.\n\nA constant term, introduced into the equations, was found to be small\nand negative. This seems to dispose of the necessity for the old constant\n$K$ term. Solutions of this sort have been published by Lundmark,$^{3}$ who\nreplaced the old $K$ by $k + lr + mr^2$. His favored solution gave $k$ = 513,\nas against the former value of the order of 700, and hence offered little\nadvantage.\n\n\nTABLE 2\nNEBULAE WHOSE DISTANCES ARE ESTIMATED FROM RADIAL VELOCITIES\n\n| OBJECT | $v$ | $v_0$ | $r$ | $m_e$ | $M$ |\n| :------------ | :----- | :---- | :----- | :---- | :------ |\n| N. G. C. 278 | + 650 | -110 | 1.52 | 12.0 | -13.9 |\n| 404 | - 25 | - 65 | | 11.1 | |\n| 584 | +1800 | + 75 | 3.45 | 10.9 | 16.8 |\n| 936 | +1300 | +115 | 2.37 | 11.1 | 15.7 |\n| 1023 | + 300 | - 10 | 0.62 | 10.2 | 13.8 |\n| 1700 | + 800 | +220 | 1.16 | 12.5 | 12.8 |\n| 2681 | + 700 | - 10 | 1.42 | 10.7 | 15.0 |\n| 2683 | + 400 | + 65 | 0.67 | 9.9 | 14.3 |\n| 2841 | + 600 | - 20 | 1.24 | 9.4 | 16.1 |\n| 3034 | + 290 | -105 | 0.79 | 9.0 | 15.5 |\n| 3115 | + 600 | +105 | 1.00 | 9.5 | 15.5 |\n| 3368 | + 940 | + 70 | 1.74 | 10.0 | 16.2 |\n| 3379 | + 810 | + 65 | 1.49 | 9.4 | 16.4 |\n| 3489 | + 600 | + 50 | 1.10 | 11.2 | 14.0 |\n| 3521 | + 730 | + 95 | 1.27 | 10.1 | 15.4 |\n| 3623 | + 800 | + 35 | 1.53 | 9.9 | 16.0 |\n| 4111 | + 800 | - 95 | 1.79 | 10.1 | 16.1 |\n| 4526 | + 580 | - 20 | 1.20 | 11.1 | 14.3 |\n| 4565 | +1100 | - 75 | 2.35 | 11.0 | 15.9 |\n| 4594 | +1140 | + 25 | 2.23 | 9.1 | 17.6 |\n| 5005 | + 900 | -130 | 2.06 | 11.1 | 15.5 |\n| 5866 | + 650 | -215 | 1.73 | 11.7 | -14.5 |\n| **Mean** | | | | **10.5** | **-15.3** |\n\n\nThe residuals for the two solutions given above average 150 and 110\nkm./sec. and should represent the average peculiar motions of the in-\ndividual nebulae and of the groups, respectively. In order to exhibit\nthe results in a graphical form, the solar motion has been eliminated from\nthe observed velocities and the remainders, the distance terms plus the\nresiduals, have been plotted against the distances. The run of the re-\nsiduals is about as smooth as can be expected, and in general the form of\nthe solutions appears to be adequate.\n\nThe 22 nebulae for which distances are not available can be treated in\ntwo ways. First, the mean distance of the group derived from the mean\napparent magnitudes can be compared with the mean of the velocities\n\n\ncorrected for solar motion. The result, 745 km./sec. for a distance of\n$1.4 \\times 10^6$ parsecs, falls between the two previous solutions and indicates\na value for $K$ of 530 as against the proposed value, 500 km./sec.\n\nSecondly, the scatter of the individual nebulae can be examined by\nassuming the relation between distances and velocities as previously\ndetermined. Distances can then be calculated from the velocities cor-\nrected for solar motion, and absolute magnitudes can be derived from the\napparent magnitudes. The results are given in table 2 and may be\ncompared with the distribution of absolute magnitudes among the nebulae\nin table 1, whose distances are derived from other criteria. N. G. C. 404\n\n\nFIGURE 1\n**Velocity-Distance Relation among Extra-Galactic Nebulae.**\nA scatter plot showing radial velocities (y-axis, from 0 to +1000 KM) against distance (x-axis, from 0 to $2 \\times 10^6$ PARSECS). Black discs and a solid line represent the solution for solar motion using individual nebulae. Open circles and a dashed line represent the solution combining nebulae into groups. A cross symbol marks the mean velocity corresponding to the mean distance of 22 nebulae whose distances could not be estimated individually. The solid line, representing individual nebulae, starts near the origin and increases linearly with distance. The dashed line, for grouped nebulae, shows a similar linear increase but with a slightly steeper slope.\n\n\ncan be excluded, since the observed velocity is so small that the peculiar\nmotion must be large in comparison with the distance effect. The object\nis not necessarily an exception, however, since a distance can be assigned\nfor which the peculiar motion and the absolute magnitude are both within\nthe range previously determined. The two mean magnitudes, -15.3\nand -15.5, the ranges, 4.9 and 5.0 mag., and the frequency distributions\nare closely similar for these two entirely independent sets of data; and\neven the slight difference in mean magnitudes can be attributed to the\nselected, very bright, nebulae in the Virgo Cluster. This entirely unforced\nagreement supports the validity of the velocity-distance relation in a very\n\n\nevident matter. Finally, it is worth recording that the frequency distribu-\ntion of absolute magnitudes in the two tables combined is comparable\nwith those found in the various clusters of nebulae. $^{4}$\n\nThe results establish a roughly linear relation between velocities and\ndistances among nebulae for which velocities have been previously pub-\nlished, and the relation appears to dominate the distribution of velocities.\nIn order to investigate the matter on a much larger scale, Mr. Humason\nat Mount Wilson has initiated a program of determining velocities\nof the most distant nebulae that can be observed with confidence.\nThese, naturally, are the brightest nebulae in clusters of nebulae.\nThe first definite result, $v$ = + 3779 km./sec. for N. G. C. 7619, is\nthoroughly consistent with the present conclusions. Corrected for the\nsolar motion, this velocity is +3910, which, with $K$ = 500, corresponds to\na distance of $7.8 \\times 10^6$ parsecs. Since the apparent magnitude is 11.8,\nthe absolute magnitude at such a distance is -17.65, which is of the\nright order for the brightest nebulae in a cluster. A preliminary dis-\ntance, derived independently from the cluster of which this nebula appears\nto be a member, is of the order of $7 \\times 10^6$ parsecs.\n\nNew data to be expected in the near future may modify the significance\nof the present investigation or, if confirmatory, will lead to a solution\nhaving many times the weight. For this reason it is thought premature\nto discuss in detail the obvious consequences of the present results. For\nexample, if the solar motion with respect to the clusters represents the\nrotation of the galactic system, this motion could be subtracted from the\nresults for the nebulae and the remainder would represent the motion of\nthe galactic system with respect to the extra-galactic nebulae.\n\nThe outstanding feature, however, is the possibility that the velocity-\ndistance relation may represent the de Sitter effect, and hence that numer-\nical data may be introduced into discussions of the general curvature of\nspace. In the de Sitter cosmology, displacements of the spectra arise\nfrom two sources, an apparent slowing down of atomic vibrations and a\ngeneral tendency of material particles to scatter. The latter involves an\nacceleration and hence introduces the element of time. The relative im-\nportance of these two effects should determine the form of the relation\nbetween distances and observed velocities; and in this connection it may\nbe emphasized that the linear relation found in the present discussion is a\nfirst approximation representing a restricted range in distance.\n\n1. Mt. Wilson Contr., No. 324; Astroph. J., Chicago, Ill., 64, 1926 (321).\n2. Harvard Coll. Obs. Circ., 294, 1926.\n3. Mon. Not. R. Astr. Soc., 85, 1925 (865-894).\n4. These PROCEEDINGS, 15, 1929 (167)."] \ No newline at end of file diff --git a/tests/fixtures/hubble_golden.md b/tests/fixtures/hubble_golden.md index b0f4c71..e8bd145 100644 --- a/tests/fixtures/hubble_golden.md +++ b/tests/fixtures/hubble_golden.md @@ -1,312 +1,289 @@ -168 ASTRONOMY: E. HUBBLE PROC. N. A. S. - -appearance the spectrum is very much like spectra of the Milky Way -clouds in Sagittarius and Cygnus, and is also similar to spectra of binary -stars of the W Ursae Majoris type, where the widening and depth of the -lines are affected by the rapid rotation of the stars involved. - -The wide shallow absorption lines observed in the spectrum of N. G. C. -7619 have been noticed in the spectra of other extra-galactic nebulae, and -may be due to a dispersion in velocity and a blending of the spectral types -of the many stars which presumably exist in the central parts of these -nebulae. The lack of depth in the absorption lines seems to be more -pronounced among the smaller and fainter nebulae, and in N. G. C. 7619 -the absorption is very weak. - -It is hoped that velocities of more of these interesting objects will soon -be available. - -## A RELATION BETWEEN DISTANCE AND RADIAL VELOCITY AMONG EXTRA-GALACTIC NEBULAE - -BY EDWIN HUBBLE -MOUNT WILSON OBSERVATORY, CARNEGIE INSTITUTION OF WASHINGTON -Communicated January 17, 1929 - -Determinations of the motion of the sun with respect to the extra- -galactic nebulae have involved a $K$ term of several hundred kilometers -which appears to be variable. Explanations of this paradox have been -sought in a correlation between apparent radial velocities and distances, -but so far the results have not been convincing. The present paper is a -re-examination of the question, based on only those nebular distances -which are believed to be fairly reliable. - -Distances of extra-galactic nebulae depend ultimately upon the appli- -cation of absolute-luminosity criteria to involved stars whose types can -be recognized. These include, among others, Cepheid variables, novae, -and blue stars involved in emission nebulosity. Numerical values depend -upon the zero point of the period-luminosity relation among Cepheids, -the other criteria merely check the order of the distances. This method -is restricted to the few nebulae which are well resolved by existing instru- -ments. A study of these nebulae, together with those in which any stars -at all can be recognized, indicates the probability of an approximately -uniform upper limit to the absolute luminosity of stars, in the late-type -spirals and irregular nebulae at least, of the order of $M$ (photographic) = --6.3.1 The apparent luminosities of the brightest stars in such nebulae -are thus criteria which, although rough and to be applied with caution, +appearance the spectrum is very much like spectra of the Milky Way +clouds in Sagittarius and Cygnus, and is also similar to spectra of binary +stars of the W Ursae Majoris type, where the widening and depth of the +lines are affected by the rapid rotation of the stars involved. + +The wide shallow absorption lines observed in the spectrum of N. G. C. +7619 have been noticed in the spectra of other extra-galactic nebulae, and +may be due to a dispersion in velocity and a blending of the spectral types +of the many stars which presumably exist in the central parts of these +nebulae. The lack of depth in the absorption lines seems to be more +pronounced among the smaller and fainter nebulae, and in N. G. C. 7619 +the absorption is very weak. + +It is hoped that velocities of more of these interesting objects will soon +be available. + +[Check for updates box is present on the right side of the page.] + +# A RELATION BETWEEN DISTANCE AND RADIAL VELOCITY +# AMONG EXTRA-GALACTIC NEBULAE + +BY EDWIN HUBBLE + +MOUNT WILSON OBSERVATORY, CARNEGIE INSTITUTION OF WASHINGTON + +Communicated January 17, 1929 + +Determinations of the motion of the sun with respect to the extra- +galactic nebulae have involved a $K$ term of several hundred kilometers +which appears to be variable. Explanations of this paradox have been +sought in a correlation between apparent radial velocities and distances, +but so far the results have not been convincing. The present paper is a +re-examination of the question, based on only those nebular distances +which are believed to be fairly reliable. + +Distances of extra-galactic nebulae depend ultimately upon the appli- +cation of absolute-luminosity criteria to involved stars whose types can +be recognized. These include, among others, Cepheid variables, novae, +and blue stars involved in emission nebulosity. Numerical values depend +upon the zero point of the period-luminosity relation among Cepheids, +the other criteria merely check the order of the distances. This method +is restricted to the few nebulae which are well resolved by existing instru- +ments. A study of these nebulae, together with those in which any stars +at all can be recognized, indicates the probability of an approximately +uniform upper limit to the absolute luminosity of stars, in the late-type +spirals and irregular nebulae at least, of the order of $M$ (photographic) = +-6.3. $^{1}$ The apparent luminosities of the brightest stars in such nebulae +are thus criteria which, although rough and to be applied with caution, -VOL. 15, 1929 ASTRONOMY: E. HUBBLE 169 - -furnish reasonable estimates of the distances of all extra-galactic systems -in which even a few stars can be detected. +furnish reasonable estimates of the distances of all extra-galactic systems +in which even a few stars can be detected. -TABLE 1 -NEBULAE WHOSE DISTANCES HAVE BEEN ESTIMATED FROM STARS INVOLVED OR FROM -MEAN LUMINOSITIES IN A CLUSTER - -| OBJECT | $m_c$ | $r$ | $v_c$ | $m_v$ | $M_v$ | -| :----------- | :---- | :---- | :------- | :---- | :------- | -| S. Mag. | .. | 0.032 | + 170 | 1.5 | -16.0 | -| L. Mag. | .. | 0.034 | + 290 | 0.5 | 17.2 | -| N. G. C. 6822 | .. | 0.214 | - 130 | 9.0 | 12.7 | -| 598 | .. | 0.263 | - 70 | 7.0 | 15.1 | -| 221 | .. | 0.275 | - 185 | 8.8 | 13.4 | -| 224 | .. | 0.275 | - 220 | 5.0 | 17.2 | -| 5457 | 17.0 | 0.45 | + 200 | 9.9 | 13.3 | -| 4736 | 17.3 | 0.5 | + 290 | 8.4 | 15.1 | -| 5194 | 17.3 | 0.5 | + 270 | 7.4 | 16.1 | -| 4449 | 17.8 | 0.63 | + 200 | 9.5 | 14.5 | -| 4214 | 18.3 | 0.8 | + 300 | 11.3 | 13.2 | -| 3031 | 18.5 | 0.9 | - 30 | 8.3 | 16.4 | -| 3627 | 18.5 | 0.9 | + 650 | 9.1 | 15.7 | -| 4826 | 18.5 | 0.9 | + 150 | 9.0 | 15.7 | -| 5236 | 18.5 | 0.9 | + 500 | 10.4 | 14.4 | -| 1068 | 18.7 | 1.0 | + 920 | 9.1 | 15.9 | -| 5055 | 19.0 | 1.1 | + 450 | 9.6 | 15.6 | -| 7331 | 19.0 | 1.1 | + 500 | 10.4 | 14.8 | -| 4258 | 19.5 | 1.4 | + 500 | 8.7 | 17.0 | -| 4151 | 20.0 | 1.7 | + 960 | 12.0 | 14.2 | -| 4382 | .. | 2.0 | + 500 | 10.0 | 16.5 | -| 4472 | .. | 2.0 | + 850 | 8.8 | 17.7 | -| 4486 | .. | 2.0 | + 800 | 9.7 | 16.8 | -| 4649 | .. | 2.0 | +1090 | 9.5 | 17.0 | -| Mean | | | | | -15.5 | - -$m_c$ = photographic magnitude of brightest stars involved. -$r$ = distance in units of $10^6$ parsecs. The first two are Shapley's values. -$v_c$ = measured velocities in km./sec. N. G. C. 6822, 221, 224 and 5457 are recent -determinations by Humason. -$m_v$ = Holetschek's visual magnitude as corrected by Hopmann. The first three -objects were not measured by Holetschek, and the values of $m_v$ represent -estimates by the author based upon such data as are available. -$M_v$ = total visual absolute magnitude computed from $m_v$ and $r$. +TABLE 1 +NEBULAE WHOSE DISTANCES HAVE BEEN ESTIMATED FROM STARS INVOLVED OR FROM +MEAN LUMINOSITIES IN A CLUSTER + +| OBJECT | $m_c$ | $r$ | $v$ | $m_e$ | $M_c$ | +| :------------- | :---- | :----- | :----- | :---- | :------ | +| S. Mag. | | 0.032 | + 170 | 1.5 | -16.0 | +| L. Mag. | | 0.034 | + 290 | 0.5 | 17.2 | +| N. G. C. 6822 | | 0.214 | - 130 | 9.0 | 12.7 | +| 598 | | 0.263 | - 70 | 7.0 | 15.1 | +| 221 | | 0.275 | - 185 | 8.8 | 13.4 | +| 224 | | 0.275 | - 220 | 5.0 | 17.2 | +| 5457 | 17.0 | 0.45 | + 200 | 9.9 | 13.3 | +| 4736 | 17.3 | 0.5 | + 290 | 8.4 | 15.1 | +| 5194 | 17.3 | 0.5 | + 270 | 7.4 | 16.1 | +| 4449 | 17.8 | 0.63 | + 200 | 9.5 | 14.5 | +| 4214 | 18.3 | 0.8 | + 300 | 11.3 | 13.2 | +| 3031 | 18.5 | 0.9 | - 30 | 8.3 | 16.4 | +| 3627 | 18.5 | 0.9 | + 650 | 9.1 | 15.7 | +| 4826 | 18.5 | 0.9 | + 150 | 9.0 | 15.7 | +| 5236 | 18.5 | 0.9 | + 500 | 10.4 | 14.4 | +| 1068 | 18.7 | 1.0 | + 920 | 9.1 | 15.9 | +| 5055 | 19.0 | 1.1 | + 450 | 9.6 | 15.6 | +| 7331 | 19.0 | 1.1 | + 500 | 10.4 | 14.8 | +| 4258 | 19.5 | 1.4 | + 500 | 8.7 | 17.0 | +| 4151 | 20.0 | 1.7 | + 960 | 12.0 | 14.2 | +| 4382 | | 2.0 | + 500 | 10.0 | 16.5 | +| 4472 | | 2.0 | + 850 | 8.8 | 17.7 | +| 4486 | | 2.0 | + 800 | 9.7 | 16.8 | +| 4649 | | 2.0 | +1090 | 9.5 | 17.0 | +| **Mean** | | | | | **-15.5** | -Finally, the nebulae themselves appear to be of a definite order of -absolute luminosity, exhibiting a range of four or five magnitudes about -an average value $M$ (visual) = -15.2.1 The application of this statistical -average to individual cases can rarely be used to advantage, but where -considerable numbers are involved, and especially in the various clusters -of nebulae, mean apparent luminosities of the nebulae themselves offer -reliable estimates of the mean distances. - -Radial velocities of 46 extra-galactic nebulae are now available, but +$M_c$ = photographic magnitude of brightest stars involved. +$r$ = distance in units of $10^6$ parsecs. The first two are Shapley's values. +$v$ = measured velocities in km./sec. N. G. C. 6822, 221, 224 and 5457 are recent +determinations by Humason. +$m_e$ = Holetschek's visual magnitude as corrected by Hopmann. The first three +objects were not measured by Holetschek, and the values of $m_e$ represent +estimates by the author based upon such data as are available. +$M_c$ = total visual absolute magnitude computed from $m_e$ and $r$. + +Finally, the nebulae themselves appear to be of a definite order of +absolute luminosity, exhibiting a range of four or five magnitudes about +an average value $M$ (visual) = -15.2.$^{1}$ The application of this statistical +average to individual cases can rarely be used to advantage, but where +considerable numbers are involved, and especially in the various clusters +of nebulae, mean apparent luminosities of the nebulae themselves offer +reliable estimates of the mean distances. + +Radial velocities of 46 extra-galactic nebulae are now available, but -170 ASTRONOMY: E. HUBBLE PROC. N. A. S. - -individual distances are estimated for only 24. For one other, N. G. C. -3521, an estimate could probably be made, but no photographs are avail- -able at Mount Wilson. The data are given in table 1. The first seven -distances are the most reliable, depending, except for M 32 the companion of -M 31, upon extensive investigations of many stars involved. The next -thirteen distances, depending upon the criterion of a uniform upper limit -of stellar luminosity, are subject to considerable probable errors but are -believed to be the most reasonable values at present available. The last -four objects appear to be in the Virgo Cluster. The distance assigned -to the cluster, $2 \times 10^6$ parsecs, is derived from the distribution of nebular -luminosities, together with luminosities of stars in some of the later-type -spirals, and differs somewhat from the Harvard estimate of ten million -light years.2 - -The data in the table indicate a linear correlation between distances and -velocities, whether the latter are used directly or corrected for solar motion, -according to the older solutions. This suggests a new solution for the solar -motion in which the distances are introduced as coefficients of the $K$ term, -i. e., the velocities are assumed to vary directly with the distances, and -hence $K$ represents the velocity at unit distance due to this effect. The -equations of condition then take the form - -$$ rK + X \cos \alpha \cos \delta + Y \sin \alpha \cos \delta + Z \sin \delta = v. $$ - -Two solutions have been made, one using the 24 nebulae individually, -the other combining them into 9 groups according to proximity in direc- -tion and in distance. The results are - -24 OBJECTS -$X$ = -65 $\pm$ 50 -$Y$ = +226 $\pm$ 95 -$Z$ = -195 $\pm$ 40 -$K$ = +465 $\pm$ 50 -$A$ = $286^{\circ}$ -$D$ = $+40^{\circ}$ -$V_o$ = 306 km./sec. - -9 GROUPS -+3 $\pm$ 70 -+230 $\pm$ 120 --133 $\pm$ 70 -+513 $\pm$ 60 km./sec. per $10^6$ parsecs. -$269^{\circ}$ -$+33^{\circ}$ -247 km./sec. - -For such scanty material, so poorly distributed, the results are fairly -definite. Differences between the two solutions are due largely to the -four Virgo nebulae, which, being the most distant objects and all sharing -the peculiar motion of the cluster, unduly influence the value of $K$ and -hence of $V_o$. New data on more distant objects will be required to reduce -the effect of such peculiar motion. Meanwhile round numbers, inter- -mediate between the two solutions, will represent the probable order of -the values. For instance, let $A = 277^{\circ}$, $D = +36^{\circ}$ (Gal. long. = $32^{\circ}$, -lat. = $+18^{\circ}$), $V_o = 280$ km./sec., $K = +500$ km./sec. per million par- - - -VOL. 15, 1929 ASTRONOMY: E. HUBBLE 171 - -secs. Mr. Strömberg has very kindly checked the general order of these -values by independent solutions for different groupings of the data. - -A constant term, introduced into the equations, was found to be small -and negative. This seems to dispose of the necessity for the old constant -$K$ term. Solutions of this sort have been published by Lundmark,3 who -replaced the old $K$ by $k + lr + mr^2$. His favored solution gave $k = 513$, -as against the former value of the order of 700, and hence offered little -advantage. +individual distances are estimated for only 24. For one other, N. G. C. +3521, an estimate could probably be made, but no photographs are avail- +able at Mount Wilson. The data are given in table 1. The first seven +distances are the most reliable, depending, except for M 32 the companion of +M 31, upon extensive investigations of many stars involved. The next +thirteen distances, depending upon the criterion of a uniform upper limit +of stellar luminosity, are subject to considerable probable errors but are +believed to be the most reasonable values at present available. The last +four objects appear to be in the Virgo Cluster. The distance assigned +to the cluster, $2 \times 10^6$ parsecs, is derived from the distribution of nebular +luminosities, together with luminosities of stars in some of the later-type +spirals, and differs somewhat from the Harvard estimate of ten million +light years. $^{2}$ + +The data in the table indicate a linear correlation between distances and +velocities, whether the latter are used directly or corrected for solar motion, +according to the older solutions. This suggests a new solution for the solar +motion in which the distances are introduced as coefficients of the $K$ term, +i. e., the velocities are assumed to vary directly with the distances, and +hence $K$ represents the velocity at unit distance due to this effect. The +equations of condition then take the form + +$rK + X \cos \alpha \cos \delta + Y \sin \alpha \cos \delta + Z \sin \delta = v$. + +Two solutions have been made, one using the 24 nebulae individually, +the other combining them into 9 groups according to proximity in direc- +tion and in distance. The results are -TABLE 2 -NEBULAE WHOSE DISTANCES ARE ESTIMATED FROM RADIAL VELOCITIES - -| OBJECT | $v_c$ | $v_o$ | $r$ | $m_v$ | $M_v$ | -| :----------- | :------ | :------- | :---- | :---- | :------- | -| N. G. C. 278 | + 650 | -110 | 1.52 | 12.0 | -13.9 | -| 404 | + 25 | - 65 | | 11.1 | | -| 584 | +1800 | + 75 | 3.45 | 10.9 | 16.8 | -| 936 | +1300 | +115 | 2.37 | 11.1 | 15.7 | -| 1023 | + 300 | - 10 | 0.62 | 10.2 | 13.8 | -| 1700 | + 800 | +220 | 1.16 | 12.5 | 12.8 | -| 2681 | + 700 | - 10 | 1.42 | 10.7 | 15.0 | -| 2683 | + 400 | + 65 | 0.67 | 9.9 | 14.3 | -| 2841 | + 600 | - 20 | 1.24 | 9.4 | 16.1 | -| 3034 | + 290 | -105 | 0.79 | 9.0 | 15.5 | -| 3115 | + 600 | +105 | 1.00 | 9.5 | 15.5 | -| 3368 | + 940 | + 70 | 1.74 | 10.0 | 16.2 | -| 3379 | + 810 | + 65 | 1.49 | 9.4 | 16.4 | -| 3489 | + 600 | + 50 | 1.10 | 11.2 | 14.0 | -| 3521 | + 730 | + 95 | 1.27 | 10.1 | 15.4 | -| 3623 | + 800 | + 35 | 1.53 | 9.9 | 16.0 | -| 4111 | + 800 | - 95 | 1.79 | 10.1 | 16.1 | -| 4526 | + 580 | - 20 | 1.20 | 11.1 | 14.3 | -| 4565 | +1100 | - 75 | 2.35 | 11.0 | 15.9 | -| 4594 | +1140 | + 25 | 2.23 | 9.1 | 17.6 | -| 5005 | + 900 | -130 | 2.06 | 11.1 | 15.5 | -| 5866 | + 650 | -215 | 1.73 | 11.7 | -14.5 | -| Mean | | | | 10.5 | -15.3 | +| | **24 OBJECTS** | **9 GROUPS** | +| :----------- | :------------- | :----------- | +| $X$ | -65 ± 50 | +3 ± 70 | +| $Y$ | +226 ± 95 | +230 ± 120 | +| $Z$ | -195 ± 40 | -133 ± 70 | +| $K$ | +465 ± 50 | +513 ± 60 km./sec. per $10^6$ parsecs. | +| $\Lambda$ | 286° | 269° | +| $D$ | +40° | +33° | +| $V_0$ | 306 km./sec. | 247 km./sec. | -The residuals for the two solutions given above average 150 and 110 -km./sec. and should represent the average peculiar motions of the in- -dividual nebulae and of the groups, respectively. In order to exhibit -the results in a graphical form, the solar motion has been eliminated from -the observed velocities and the remainders, the distance terms plus the -residuals, have been plotted against the distances. The run of the re- -siduals is about as smooth as can be expected, and in general the form of -the solutions appears to be adequate. - -The 22 nebulae for which distances are not available can be treated in -two ways. First, the mean distance of the group derived from the mean -apparent magnitudes can be compared with the mean of the velocities +For such scanty material, so poorly distributed, the results are fairly +definite. Differences between the two solutions are due largely to the +four Virgo nebulae, which, being the most distant objects and all sharing +the peculiar motion of the cluster, unduly influence the value of $K$ and +hence of $V_0$. New data on more distant objects will be required to reduce +the effect of such peculiar motion. Meanwhile round numbers, inter- +mediate between the two solutions, will represent the probable order of +the values. For instance, let $\Lambda$ = 277°, $D$ = +36° (Gal. long. = 32°, +lat. = +18°), $V_0$ = 280 km./sec., $K$ = +500 km./sec. per million par- -172 ASTRONOMY: E. HUBBLE PROC. N. A. S. +secs. Mr. Strömberg has very kindly checked the general order of these +values by independent solutions for different groupings of the data. + +A constant term, introduced into the equations, was found to be small +and negative. This seems to dispose of the necessity for the old constant +$K$ term. Solutions of this sort have been published by Lundmark,$^{3}$ who +replaced the old $K$ by $k + lr + mr^2$. His favored solution gave $k$ = 513, +as against the former value of the order of 700, and hence offered little +advantage. + + +TABLE 2 +NEBULAE WHOSE DISTANCES ARE ESTIMATED FROM RADIAL VELOCITIES + +| OBJECT | $v$ | $v_0$ | $r$ | $m_e$ | $M$ | +| :------------ | :----- | :---- | :----- | :---- | :------ | +| N. G. C. 278 | + 650 | -110 | 1.52 | 12.0 | -13.9 | +| 404 | - 25 | - 65 | | 11.1 | | +| 584 | +1800 | + 75 | 3.45 | 10.9 | 16.8 | +| 936 | +1300 | +115 | 2.37 | 11.1 | 15.7 | +| 1023 | + 300 | - 10 | 0.62 | 10.2 | 13.8 | +| 1700 | + 800 | +220 | 1.16 | 12.5 | 12.8 | +| 2681 | + 700 | - 10 | 1.42 | 10.7 | 15.0 | +| 2683 | + 400 | + 65 | 0.67 | 9.9 | 14.3 | +| 2841 | + 600 | - 20 | 1.24 | 9.4 | 16.1 | +| 3034 | + 290 | -105 | 0.79 | 9.0 | 15.5 | +| 3115 | + 600 | +105 | 1.00 | 9.5 | 15.5 | +| 3368 | + 940 | + 70 | 1.74 | 10.0 | 16.2 | +| 3379 | + 810 | + 65 | 1.49 | 9.4 | 16.4 | +| 3489 | + 600 | + 50 | 1.10 | 11.2 | 14.0 | +| 3521 | + 730 | + 95 | 1.27 | 10.1 | 15.4 | +| 3623 | + 800 | + 35 | 1.53 | 9.9 | 16.0 | +| 4111 | + 800 | - 95 | 1.79 | 10.1 | 16.1 | +| 4526 | + 580 | - 20 | 1.20 | 11.1 | 14.3 | +| 4565 | +1100 | - 75 | 2.35 | 11.0 | 15.9 | +| 4594 | +1140 | + 25 | 2.23 | 9.1 | 17.6 | +| 5005 | + 900 | -130 | 2.06 | 11.1 | 15.5 | +| 5866 | + 650 | -215 | 1.73 | 11.7 | -14.5 | +| **Mean** | | | | **10.5** | **-15.3** | + -corrected for solar motion. The result, 745 km./sec. for a distance of -$1.4 \times 10^6$ parsecs, falls between the two previous solutions and indicates -a value for $K$ of 530 as against the proposed value, 500 km./sec. +The residuals for the two solutions given above average 150 and 110 +km./sec. and should represent the average peculiar motions of the in- +dividual nebulae and of the groups, respectively. In order to exhibit +the results in a graphical form, the solar motion has been eliminated from +the observed velocities and the remainders, the distance terms plus the +residuals, have been plotted against the distances. The run of the re- +siduals is about as smooth as can be expected, and in general the form of +the solutions appears to be adequate. -Secondly, the scatter of the individual nebulae can be examined by -assuming the relation between distances and velocities as previously -determined. Distances can then be calculated from the velocities cor- -rected for solar motion, and absolute magnitudes can be derived from the -apparent magnitudes. The results are given in table 2 and may be -compared with the distribution of absolute magnitudes among the nebulae -in table 1, whose distances are derived from other criteria. N. G. C. 404 +The 22 nebulae for which distances are not available can be treated in +two ways. First, the mean distance of the group derived from the mean +apparent magnitudes can be compared with the mean of the velocities + + +corrected for solar motion. The result, 745 km./sec. for a distance of +$1.4 \times 10^6$ parsecs, falls between the two previous solutions and indicates +a value for $K$ of 530 as against the proposed value, 500 km./sec. + +Secondly, the scatter of the individual nebulae can be examined by +assuming the relation between distances and velocities as previously +determined. Distances can then be calculated from the velocities cor- +rected for solar motion, and absolute magnitudes can be derived from the +apparent magnitudes. The results are given in table 2 and may be +compared with the distribution of absolute magnitudes among the nebulae +in table 1, whose distances are derived from other criteria. N. G. C. 404 -Graph showing Velocity-Distance Relation among Extra-Galactic Nebulae. The x-axis is Dis<span class=tance, ranging from 0 to 2x10^6 Parsecs. The y-axis is Velocity, ranging from 0 to 2000 KM. Black discs represent individual nebulae, with a solid line showing the solution for solar motion using individual nebulae. Circles represent nebulae combined into groups, with a broken line showing the solution for these groups. A cross represents the mean velocity corresponding to the mean distance of 22 nebulae whose distances could not be estimated individually. The graph shows a roughly linear increase in velocity with distance."> - -FIGURE 1 -Velocity-Distance Relation among Extra-Galactic Nebulae. - -Radial velocities, corrected for solar motion, are plotted against -distances estimated from involved stars and mean luminosities of -nebulae in a cluster. The black discs and full line represent the -solution for solar motion using the nebulae individually; the circles -and broken line represent the solution combining the nebulae into -groups; the cross represents the mean velocity corresponding to -the mean distance of 22 nebulae whose distances could not be esti- -mated individually. +FIGURE 1 +**Velocity-Distance Relation among Extra-Galactic Nebulae.** +A scatter plot showing radial velocities (y-axis, from 0 to +1000 KM) against distance (x-axis, from 0 to $2 \times 10^6$ PARSECS). Black discs and a solid line represent the solution for solar motion using individual nebulae. Open circles and a dashed line represent the solution combining nebulae into groups. A cross symbol marks the mean velocity corresponding to the mean distance of 22 nebulae whose distances could not be estimated individually. The solid line, representing individual nebulae, starts near the origin and increases linearly with distance. The dashed line, for grouped nebulae, shows a similar linear increase but with a slightly steeper slope. -can be excluded, since the observed velocity is so small that the peculiar -motion must be large in comparison with the distance effect. The object -is not necessarily an exception, however, since a distance can be assigned -for which the peculiar motion and the absolute magnitude are both within -the range previously determined. The two mean magnitudes, -15.3 -and -15.5, the ranges, 4.9 and 5.0 mag., and the frequency distributions -are closely similar for these two entirely independent sets of data; and -even the slight difference in mean magnitudes can be attributed to the -selected, very bright, nebulae in the Virgo Cluster. This entirely unforced -agreement supports the validity of the velocity-distance relation in a very +can be excluded, since the observed velocity is so small that the peculiar +motion must be large in comparison with the distance effect. The object +is not necessarily an exception, however, since a distance can be assigned +for which the peculiar motion and the absolute magnitude are both within +the range previously determined. The two mean magnitudes, -15.3 +and -15.5, the ranges, 4.9 and 5.0 mag., and the frequency distributions +are closely similar for these two entirely independent sets of data; and +even the slight difference in mean magnitudes can be attributed to the +selected, very bright, nebulae in the Virgo Cluster. This entirely unforced +agreement supports the validity of the velocity-distance relation in a very -VOL. 15, 1929 ASTRONOMY: E. HUBBLE 173 - -evident matter. Finally, it is worth recording that the frequency distribu- -tion of absolute magnitudes in the two tables combined is comparable -with those found in the various clusters of nebulae.4 - -The results establish a roughly linear relation between velocities and -distances among nebulae for which velocities have been previously pub- -lished, and the relation appears to dominate the distribution of velocities. -In order to investigate the matter on a much larger scale, Mr. Humason -at Mount Wilson has initiated a program of determining velocities -of the most distant nebulae that can be observed with confidence. -These, naturally, are the brightest nebulae in clusters of nebulae. -The first definite result, $v = + 3779$ km./sec. for N. G. C. 7619, is -thoroughly consistent with the present conclusions. Corrected for the -solar motion, this velocity is +3910, which, with $K = 500$, corresponds to -a distance of $7.8 \times 10^6$ parsecs. Since the apparent magnitude is 11.8, -the absolute magnitude at such a distance is -17.65, which is of the -right order for the brightest nebulae in a cluster. A preliminary dis- -tance, derived independently from the cluster of which this nebula appears -to be a member, is of the order of $7 \times 10^6$ parsecs. - -New data to be expected in the near future may modify the significance -of the present investigation or, if confirmatory, will lead to a solution -having many times the weight. For this reason it is thought premature -to discuss in detail the obvious consequences of the present results. For -example, if the solar motion with respect to the clusters represents the -rotation of the galactic system, this motion could be subtracted from the -results for the nebulae and the remainder would represent the motion of -the galactic system with respect to the extra-galactic nebulae. - -The outstanding feature, however, is the possibility that the velocity- -distance relation may represent the de Sitter effect, and hence that numer- -ical data may be introduced into discussions of the general curvature of -space. In the de Sitter cosmology, displacements of the spectra arise -from two sources, an apparent slowing down of atomic vibrations and a -general tendency of material particles to scatter. The latter involves an -acceleration and hence introduces the element of time. The relative im- -portance of these two effects should determine the form of the relation -between distances and observed velocities; and in this connection it may -be emphasized that the linear relation found in the present discussion is a -first approximation representing a restricted range in distance. - -1. *Mt. Wilson Contr.*, No. 324; *Astroph. J.*, Chicago, Ill., 64, 1926 (321). -2. *Harvard Coll. Obs. Circ.*, 294, 1926. -3. *Mon. Not. R. Astr. Soc.*, 85, 1925 (865-894). -4. These *PROCEEDINGS*, 15, 1929 (167). \ No newline at end of file +evident matter. Finally, it is worth recording that the frequency distribu- +tion of absolute magnitudes in the two tables combined is comparable +with those found in the various clusters of nebulae. $^{4}$ + +The results establish a roughly linear relation between velocities and +distances among nebulae for which velocities have been previously pub- +lished, and the relation appears to dominate the distribution of velocities. +In order to investigate the matter on a much larger scale, Mr. Humason +at Mount Wilson has initiated a program of determining velocities +of the most distant nebulae that can be observed with confidence. +These, naturally, are the brightest nebulae in clusters of nebulae. +The first definite result, $v$ = + 3779 km./sec. for N. G. C. 7619, is +thoroughly consistent with the present conclusions. Corrected for the +solar motion, this velocity is +3910, which, with $K$ = 500, corresponds to +a distance of $7.8 \times 10^6$ parsecs. Since the apparent magnitude is 11.8, +the absolute magnitude at such a distance is -17.65, which is of the +right order for the brightest nebulae in a cluster. A preliminary dis- +tance, derived independently from the cluster of which this nebula appears +to be a member, is of the order of $7 \times 10^6$ parsecs. + +New data to be expected in the near future may modify the significance +of the present investigation or, if confirmatory, will lead to a solution +having many times the weight. For this reason it is thought premature +to discuss in detail the obvious consequences of the present results. For +example, if the solar motion with respect to the clusters represents the +rotation of the galactic system, this motion could be subtracted from the +results for the nebulae and the remainder would represent the motion of +the galactic system with respect to the extra-galactic nebulae. + +The outstanding feature, however, is the possibility that the velocity- +distance relation may represent the de Sitter effect, and hence that numer- +ical data may be introduced into discussions of the general curvature of +space. In the de Sitter cosmology, displacements of the spectra arise +from two sources, an apparent slowing down of atomic vibrations and a +general tendency of material particles to scatter. The latter involves an +acceleration and hence introduces the element of time. The relative im- +portance of these two effects should determine the form of the relation +between distances and observed velocities; and in this connection it may +be emphasized that the linear relation found in the present discussion is a +first approximation representing a restricted range in distance. + +1. Mt. Wilson Contr., No. 324; Astroph. J., Chicago, Ill., 64, 1926 (321). +2. Harvard Coll. Obs. Circ., 294, 1926. +3. Mon. Not. R. Astr. Soc., 85, 1925 (865-894). +4. These PROCEEDINGS, 15, 1929 (167). \ No newline at end of file diff --git a/tests/test_bbox_alignment.py b/tests/test_bbox_alignment.py deleted file mode 100644 index 9b168ea..0000000 --- a/tests/test_bbox_alignment.py +++ /dev/null @@ -1,54 +0,0 @@ -from gemini_ocr import bbox_alignment, document - - -def test_missing_assignment_hyphenation_and_hole_filling() -> None: - markdown_content = """\ -we enter an era of precision cancer medicine, where many drugs are -active in small molecularly defined subgroups of patients (e.g., only -3%-7% of lung cancer patients harbor the drug sensi- tizing EML4-ALK -gene fusion (Soda et al., 2007)), the scarcity of models for many -cancer genotypes and tissues is a limitation. New cell culturing tech- -nologies enable derivation of patient cell lines with high efficiency -and thus make derivation of a larger set of cell lines encompassing -the molecular diversity of cancer a realistic possibility (Liu et al., -2012; Sato et al., 2011). -""" - - bbox_texts = [ - "set of cell lines encompassing the molecular diversity of cancer", - "lines with high efficiency and thus make derivation of a larger", - "are active in small molecularly defined subgroups of patients", - "we enter an era of precision cancer medicine, where many drugs", - "models for many cancer genotypes and tissues is a limitation.", - "tizing EML4-ALK gene fusion [Soda et al., 2007]), the scarcity of", - "(e.g., only 3%-7% of lung cancer patients harbor the drug sensi-", - "a realistic possibility (Liu et al., 2012; Sato et al., 2011).", - "New cell culturing technologies enable derivation of patient cell", - ] - - dummy_rect = document.BBox(0, 0, 0, 0) - bboxes = [document.BoundingBox(text=t, page=0, rect=dummy_rect) for t in bbox_texts] - - # Run alignment - assignments = bbox_alignment.create_annotated_markdown(markdown_content, bboxes) - - # Verify all bboxes are assigned - assert len(assignments) == len(bboxes) - - # Ensure every original bbox is in the assignment dictionary - for bbox in bboxes: - assert bbox in assignments - - -def test_hyphen_match_simple() -> None: - # A simpler unit test for the hyphen logic specifically via Gapped Alignment - markdown = "hyphen- ated" - bbox_text = "hyphenated" - - bboxes = [document.BoundingBox(text=bbox_text, page=1, rect=document.BBox(0, 0, 0, 0))] - - # Run - assignments = bbox_alignment.create_annotated_markdown(markdown, bboxes) - - # Verify assignment - assert bboxes[0] in assignments diff --git a/tests/test_coverage.py b/tests/test_coverage.py deleted file mode 100644 index 444fa71..0000000 --- a/tests/test_coverage.py +++ /dev/null @@ -1,69 +0,0 @@ -from unittest.mock import MagicMock - -import pytest - -from gemini_ocr import bbox_alignment, document, gemini_ocr - - -@pytest.mark.asyncio -async def test_coverage_calculation() -> None: - # Setup - markdown = "Hello World" # length 11 - # Spans: "Hello" (0-5), "World" (6-11). Space (5-6) is missing. - # Total covered: 5 + 5 = 10. Coverage: 10/11 ~ 0.909 - - bbox1 = document.BoundingBox(text="Hello", page=1, rect=document.BBox(0, 0, 0, 0)) - bbox2 = document.BoundingBox(text="World", page=1, rect=document.BBox(0, 0, 0, 0)) - - annotated = {bbox1: (0, 5), bbox2: (6, 11)} - - raw_data = gemini_ocr.RawOcrData(markdown, [bbox1, bbox2]) - - # We need to mock extract_raw_data or just test the logic directly if possible. - # gemini_ocr.process_document calls extract_raw_data then bbox_alignment.create_annotated_markdown - # (which is slow/complex). Since the logic is inside process_document, we should mock the deps. - - # Easier: Mock extract_raw_data and bbox_alignment.create_annotated_markdown - - with pytest.MonkeyPatch.context() as m: - - async def mock_extract(*_args: object, **_kwargs: object) -> gemini_ocr.RawOcrData: - return raw_data - - m.setattr(gemini_ocr, "extract_raw_data", mock_extract) - m.setattr(bbox_alignment, "create_annotated_markdown", lambda *_, **__: annotated) - - settings = MagicMock() - - result = await gemini_ocr.process_document("dummy_path", settings=settings, markdown_content=markdown) # type: ignore[arg-type] - - expected_coverage = 10.0 / 11.0 - assert result.coverage_percent == pytest.approx(expected_coverage) - - -@pytest.mark.asyncio -async def test_coverage_overlap() -> None: - markdown = "Hello" # 5 - # Span 1: 0-3 "Hel" - # Span 2: 2-5 "llo" - # Union: 0-5. Covered: 5/5 = 1.0 - - bbox1 = document.BoundingBox(text="Hel", page=1, rect=document.BBox(0, 0, 0, 0)) - bbox2 = document.BoundingBox(text="llo", page=1, rect=document.BBox(0, 0, 0, 0)) - - annotated = {bbox1: (0, 3), bbox2: (2, 5)} - - raw_data = gemini_ocr.RawOcrData(markdown, [bbox1, bbox2]) - - with pytest.MonkeyPatch.context() as m: - - async def mock_extract(*_args: object, **_kwargs: object) -> gemini_ocr.RawOcrData: - return raw_data - - m.setattr(gemini_ocr, "extract_raw_data", mock_extract) - m.setattr(bbox_alignment, "create_annotated_markdown", lambda *_, **__: annotated) - - settings = MagicMock() - result = await gemini_ocr.process_document("dummy_path", settings=settings, markdown_content=markdown) # type: ignore[arg-type] - - assert result.coverage_percent == 1.0 diff --git a/tests/test_docai_layout.py b/tests/test_docai_layout.py index e9746d5..07bfcf4 100644 --- a/tests/test_docai_layout.py +++ b/tests/test_docai_layout.py @@ -49,7 +49,7 @@ def make_cell(text: str) -> Mock: table_block.table_block.header_rows = [row_1] table_block.table_block.body_rows = [row_2] - processor = docai_layout.LayoutProcessor() + processor = docai_layout._LayoutProcessor() # Processor expects a list of blocks result = "".join(processor.process([text_block, table_block])) @@ -71,7 +71,7 @@ def test_multiple_tables() -> None: table_block.table_block.header_rows = [] table_block.table_block.body_rows = [] - processor = docai_layout.LayoutProcessor() + processor = docai_layout._LayoutProcessor() result = "".join(processor.process([table_block, table_block])) assert result.count("") == 2 diff --git a/tests/test_docai_mode.py b/tests/test_docai_mode.py index 3fb8995..f001d57 100644 --- a/tests/test_docai_mode.py +++ b/tests/test_docai_mode.py @@ -1,113 +1,81 @@ import pathlib from unittest.mock import MagicMock, patch -import fitz +import anchorite import pytest from google.cloud import documentai -from gemini_ocr import gemini_ocr, settings +from gemini_ocr import docai_layout, docai_ocr -@pytest.fixture -def ocr_settings() -> settings.Settings: - return settings.Settings( - project_id="test-project", - location="us-central1", - ocr_processor_id="test-processor", - layout_processor_id="test-layout-processor", - mode=settings.OcrMode.DOCUMENTAI, - cache_dir=None, - ) - - -@patch("gemini_ocr.document.fitz.open") +@patch("anchorite.document.pdfium.PdfDocument") @patch("gemini_ocr.docai.documentai.DocumentProcessorServiceClient") @pytest.mark.asyncio async def test_process_document_docai_mode( mock_client_class: MagicMock, - mock_fitz_open: MagicMock, - ocr_settings: settings.Settings, + mock_pdf_doc: MagicMock, tmp_path: pathlib.Path, ) -> None: - # Create a dummy PDF file dummy_pdf_path = tmp_path / "dummy.pdf" dummy_pdf_path.write_bytes(b"%PDF-1.5\n%dummy") - # Setup Mock API Client mock_client = mock_client_class.return_value mock_client.processor_path.return_value = "projects/p/locations/l/processors/p" - # Create a mock Document object mock_document = documentai.Document() page = documentai.Document.Page() page.dimension.width = 100 page.dimension.height = 100 - # Add a Line to the Page (docai_ocr uses page.lines) line = documentai.Document.Page.Line() line.layout.text_anchor.text_segments = [documentai.Document.TextAnchor.TextSegment(start_index=0, end_index=5)] - - # Bbox for line v1 = documentai.NormalizedVertex(x=0.1, y=0.1) v2 = documentai.NormalizedVertex(x=0.2, y=0.1) v3 = documentai.NormalizedVertex(x=0.2, y=0.2) v4 = documentai.NormalizedVertex(x=0.1, y=0.2) line.layout.bounding_poly.normalized_vertices = [v1, v2, v3, v4] - - # Assign line to page for docai_ocr page.lines = [line] - # Setup DocumentLayout for docai_layout layout_block = documentai.Document.DocumentLayout.DocumentLayoutBlock() layout_block.text_block.text = "Hello" layout_block.text_block.type_ = "paragraph" mock_document.document_layout.blocks = [layout_block] - mock_document.text = "Hello" - # Add a Visual Element (Image) image_el = documentai.Document.Page.VisualElement() image_el.type_ = "image" - # 4 vertices iv1 = documentai.NormalizedVertex(x=0.5, y=0.5) iv2 = documentai.NormalizedVertex(x=0.6, y=0.5) iv3 = documentai.NormalizedVertex(x=0.6, y=0.6) iv4 = documentai.NormalizedVertex(x=0.5, y=0.6) image_el.layout.bounding_poly.normalized_vertices = [iv1, iv2, iv3, iv4] - page.visual_elements = [image_el] mock_document.pages = [page] + mock_process_response = MagicMock() mock_process_response.document = mock_document mock_client.process_document.return_value = mock_process_response - # Setup Mock fitz (PyMuPDF) mock_doc = MagicMock() mock_doc.__len__.return_value = 1 - mock_page = MagicMock() - mock_page.rect = fitz.Rect(0, 0, 1000, 1000) - # Mock get_pixmap to return bytes - mock_pix = MagicMock() - mock_pix.tobytes.return_value = b"fake_image_bytes" - mock_page.get_pixmap.return_value = mock_pix + mock_pdf_doc.return_value = mock_doc - mock_doc.tobytes.return_value = b"fake_pdf_bytes" - mock_doc.__getitem__.return_value = mock_page - mock_fitz_open.return_value = mock_doc - - # Run process_document - result = await gemini_ocr.process_document(dummy_pdf_path, settings=ocr_settings) - - # Assertions - print("Markdown Content:", result.markdown_content) + markdown_provider = docai_layout.DocAIMarkdownProvider( + project_id="test-project", + location="us-central1", + processor_id="test-layout-processor", + ) + anchor_provider = docai_ocr.DocAIAnchorProvider( + project_id="test-project", + location="us-central1", + processor_id="test-processor", + ) - # We now look at result.markdown_content because OcrResult has markdown_content string field. - # The bounding_boxes is a dict mapping BoundingBox -> span (start, end). + chunks = anchorite.document.chunks(dummy_pdf_path) + result = await anchorite.process_document(chunks, markdown_provider, anchor_provider, renumber=True) assert "Hello" in result.markdown_content - - # Check bounding box assignment - assert len(result.bounding_boxes) == 1 - bbox, span = next(iter(result.bounding_boxes.items())) + assert len(result.anchor_spans) == 1 + bbox, span = next(iter(result.anchor_spans.items())) assert bbox.text == "Hello" assert result.markdown_content[span[0] : span[1]] == "Hello" diff --git a/tests/test_gcs_support.py b/tests/test_gcs_support.py index 1d74be2..e3332a2 100644 --- a/tests/test_gcs_support.py +++ b/tests/test_gcs_support.py @@ -1,11 +1,10 @@ import pathlib from unittest.mock import MagicMock, patch +import anchorite import fitz import pytest -from gemini_ocr import document - @pytest.fixture def valid_pdf_bytes() -> bytes: @@ -28,7 +27,7 @@ def test_chunks_gcs_path(valid_pdf_bytes: bytes) -> None: mock_open.return_value = mock_file # Call chunks - chunks = list(document.chunks(gcs_path)) + chunks = list(anchorite.document.chunks(gcs_path)) # Verify fsspec.open called mock_open.assert_called_once_with(gcs_path, "rb") @@ -45,6 +44,6 @@ def test_chunks_local_file(tmp_path: pathlib.Path, valid_pdf_bytes: bytes) -> No pdf_path = tmp_path / "test.pdf" pdf_path.write_bytes(valid_pdf_bytes) - chunks = list(document.chunks(pdf_path)) + chunks = list(anchorite.document.chunks(pdf_path)) assert len(chunks) > 0 assert chunks[0].data == valid_pdf_bytes diff --git a/tests/test_mime_type_inference.py b/tests/test_mime_type_inference.py new file mode 100644 index 0000000..a1715a0 --- /dev/null +++ b/tests/test_mime_type_inference.py @@ -0,0 +1,42 @@ +from unittest import mock + +import anchorite + + +def test_inference_pdf() -> None: + data = b"%PDF-1.4\n..." + + with mock.patch("anchorite.document.pdfium.PdfDocument") as mock_pdf_doc: + mock_pdf_doc.return_value.__len__.return_value = 1 + + chunks = list(anchorite.document.chunks(data)) + assert len(chunks) == 1 + assert chunks[0].mime_type == "application/pdf" + + +def test_inference_png() -> None: + data = b"\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR" + chunks = list(anchorite.document.chunks(data)) + assert len(chunks) == 1 + assert chunks[0].mime_type == "image/png" + + +def test_inference_jpeg() -> None: + data = b"\xff\xd8\xff\xe0\x00\x10JFIF" + chunks = list(anchorite.document.chunks(data)) + assert len(chunks) == 1 + assert chunks[0].mime_type == "image/jpeg" + + +def test_inference_webp() -> None: + data = b"RIFF\x00\x00\x00\x00WEBPVP8 " + chunks = list(anchorite.document.chunks(data)) + assert len(chunks) == 1 + assert chunks[0].mime_type == "image/webp" + + +def test_explicit_mime_type_override() -> None: + data = b"\x89PNG\r\n\x1a\n" + chunks = list(anchorite.document.chunks(data, mime_type="image/jpeg")) + assert len(chunks) == 1 + assert chunks[0].mime_type == "image/jpeg" diff --git a/tests/test_missed_matches.py b/tests/test_missed_matches.py deleted file mode 100644 index 2ff71ec..0000000 --- a/tests/test_missed_matches.py +++ /dev/null @@ -1,62 +0,0 @@ -from gemini_ocr import bbox_alignment, document, gemini_ocr - - -def test_missed_match_histone_modifier() -> None: - markdown_content = """ -| Cell cycle | 3 | 34 | -| DNA repair | 4 | 52 | -| Histone modifier | 5 | 29 | -| OTHER GROWTH/PROLIFERATION SIGNALING | | | -""" - - bbox_text = "Histone modifier" - bbox = document.BoundingBox(text=bbox_text, page=1, rect=document.BBox(0, 0, 0, 0)) - bboxes = [bbox] - - alignments = bbox_alignment.create_annotated_markdown(markdown_content, bboxes) - - assert bbox in alignments - - result = gemini_ocr.OcrResult(markdown_content, alignments, coverage_percent=0.0) - annotated = result.annotate() - - assert 'Histone modifier" in annotated - - -def test_missed_match_mek12() -> None: - markdown_content = """ -| Trametinib | MEK1/2 | BRAF | SKCM | -| Vemurafenib | BRAF | BRAF | SKCM | -""" - bbox_text = "MEK1/2" - bbox = document.BoundingBox(text=bbox_text, page=1, rect=document.BBox(0, 0, 0, 0)) - bboxes = [bbox] - - alignments = bbox_alignment.create_annotated_markdown(markdown_content, bboxes) - - assert bbox in alignments - - result = gemini_ocr.OcrResult(markdown_content, alignments, coverage_percent=0.0) - annotated = result.annotate() - assert ">MEK1/2" in annotated - - -def test_missed_match_duplicate_handling() -> None: - markdown_content = """ -Here is duplicate. -Here is duplicate. -""" - bbox_text = "duplicate" - bbox = document.BoundingBox(text=bbox_text, page=1, rect=document.BBox(0, 0, 0, 0)) - bboxes = [bbox] - - alignments = bbox_alignment.create_annotated_markdown(markdown_content, bboxes) - - assert bbox in alignments - - result = gemini_ocr.OcrResult(markdown_content, alignments, coverage_percent=0.0) - annotated = result.annotate() - - assert annotated.count('duplicate" in annotated diff --git a/tests/test_model_config.py b/tests/test_model_config.py index 4a8913b..2e71271 100644 --- a/tests/test_model_config.py +++ b/tests/test_model_config.py @@ -1,22 +1,20 @@ from unittest.mock import MagicMock, patch +import anchorite import pytest -from gemini_ocr import document, gemini, settings +from gemini_ocr import gemini as gemini_module @pytest.mark.asyncio async def test_generate_markdown_uses_configured_model() -> None: - # Setup settings with custom model - ocr_settings = settings.Settings( + provider = gemini_module.GeminiMarkdownProvider( project_id="test-project", location="us-central1", - layout_processor_id="layout-id", - ocr_processor_id="ocr-id", - gemini_model_name="gemini-1.5-pro-preview-0409", + model_name="gemini-1.5-pro-preview-0409", ) - chunk = document.DocumentChunk( + chunk = anchorite.document.DocumentChunk( document_sha256="hash", start_page=0, end_page=1, @@ -24,20 +22,13 @@ async def test_generate_markdown_uses_configured_model() -> None: mime_type="application/pdf", ) - # Mock genai.Client with patch("google.genai.Client") as mock_client: - mock_client_instance = mock_client.return_value - mock_models = mock_client_instance.models mock_response = MagicMock() mock_response.text = "Markdown content" - mock_models.generate_content.return_value = mock_response + mock_client.return_value.models.generate_content.return_value = mock_response - # Execute - result = await gemini.generate_markdown(ocr_settings, chunk) + result = await provider.generate_markdown(chunk) - # Verify assert result == "Markdown content" - - # Check if generate_content was called with correct model - _args, kwargs = mock_models.generate_content.call_args + _args, kwargs = mock_client.return_value.models.generate_content.call_args assert kwargs["model"] == "gemini-1.5-pro-preview-0409" diff --git a/tests/test_ocr_annotation.py b/tests/test_ocr_annotation.py deleted file mode 100644 index 006d8c0..0000000 --- a/tests/test_ocr_annotation.py +++ /dev/null @@ -1,73 +0,0 @@ -from gemini_ocr import document, gemini_ocr - - -def test_ocr_result_annotate() -> None: - markdown_content = "Hello World" - # span "Hello" is [0, 5) - # span "World" is [6, 11) - - bbox1 = document.BoundingBox(text="Hello", page=1, rect=document.BBox(0, 0, 5, 1)) - span1 = (0, 5) - - bbox2 = document.BoundingBox(text="World", page=1, rect=document.BBox(6, 6, 11, 1)) - span2 = (6, 11) - - result = gemini_ocr.OcrResult( - markdown_content=markdown_content, - bounding_boxes={bbox1: span1, bbox2: span2}, - coverage_percent=1.0, - ) - - annotated = result.annotate() - - # Expected format: {text} - # bbox1: 0,0,5,1 -> "0,0,5,1" - tag1_start = '' - tag_end = "" - - tag2_start = '' - - expected = f"{tag1_start}Hello{tag_end} {tag2_start}World{tag_end}" - - assert annotated == expected - - -def test_ocr_result_annotate_overlap() -> None: - # Test overlapping spans (nested) - content = "Hello" - bbox1 = document.BoundingBox(text="Hello", page=1, rect=document.BBox(0, 0, 5, 1)) - span1 = (0, 5) - - bbox2 = document.BoundingBox(text="He", page=1, rect=document.BBox(0, 0, 1, 1)) - span2 = (0, 2) - - result = gemini_ocr.OcrResult(content, {bbox1: span1, bbox2: span2}, coverage_percent=1.0) - - annotated = result.annotate() - - tag1_start = '' - tag2_start = '' - tag_end = "" - - expected = f"{tag1_start}{tag2_start}He{tag_end}llo{tag_end}" - assert annotated == expected - - -def test_ocr_result_annotate_zero_length() -> None: - # Test zero-length span - # Text content: "Hello" - # bbox1: "" [2, 2) (Insertion point at index 2) - - content = "Hello" - bbox1 = document.BoundingBox(text="", page=1, rect=document.BBox(0, 0, 0, 0)) - span1 = (2, 2) - - result = gemini_ocr.OcrResult(content, {bbox1: span1}, coverage_percent=0.0) - - annotated = result.annotate() - - tag_start = '' - tag_end = "" - - expected = f"He{tag_start}{tag_end}llo" - assert annotated == expected diff --git a/tests/test_ocr_nesting.py b/tests/test_ocr_nesting.py deleted file mode 100644 index e82a81e..0000000 --- a/tests/test_ocr_nesting.py +++ /dev/null @@ -1,29 +0,0 @@ -from gemini_ocr import document, gemini_ocr - - -def test_nested_zero_length_at_start() -> None: - content = "Hello" - bbox_a = document.BoundingBox(text="Hello", page=1, rect=document.BBox(0, 0, 5, 1)) - bbox_b = document.BoundingBox(text="", page=1, rect=document.BBox(0, 0, 0, 0)) - - result = gemini_ocr.OcrResult(content, {bbox_a: (0, 5), bbox_b: (0, 0)}, coverage_percent=0.0) - annotated = result.annotate() - - tag_a = '' - tag_b = '' - - assert f"{tag_a}{tag_b}Hello" == annotated - - -def test_nested_zero_length_at_end() -> None: - content = "Hello" - bbox_a = document.BoundingBox(text="Hello", page=1, rect=document.BBox(0, 0, 5, 1)) - bbox_c = document.BoundingBox(text="", page=1, rect=document.BBox(5, 5, 5, 5)) - - result = gemini_ocr.OcrResult(content, {bbox_a: (0, 5), bbox_c: (5, 5)}, coverage_percent=0.0) - annotated = result.annotate() - - tag_a = '' - tag_c = '' - - assert f"{tag_a}Hello{tag_c}" == annotated diff --git a/tests/test_range_ops.py b/tests/test_range_ops.py deleted file mode 100644 index 2160aee..0000000 --- a/tests/test_range_ops.py +++ /dev/null @@ -1,178 +0,0 @@ -import pytest - -from gemini_ocr import range_ops - - -def test_subtract_ranges_composite() -> None: - # A: [10, 20), [30, 40) - # B: [15, 35) - # Result: [10, 15), [35, 40) - ranges_a = [(10, 20), (30, 40)] - ranges_b = [(15, 35)] - expected = [(10, 15), (35, 40)] - assert range_ops.subtract_ranges(ranges_a, ranges_b) == expected - - -def test_subtract_ranges_disjoint() -> None: - # A: [10, 20) - # B: [30, 40) - # Result: [10, 20) - ranges_a = [(10, 20)] - ranges_b = [(30, 40)] - assert range_ops.subtract_ranges(ranges_a, ranges_b) == [(10, 20)] - - -def test_subtract_ranges_fully_covered() -> None: - # A: [10, 20) - # B: [5, 25) - # Result: empty list - ranges_a = [(10, 20)] - ranges_b = [(5, 25)] - assert range_ops.subtract_ranges(ranges_a, ranges_b) == [] - - -def test_subtract_ranges_multiple_holes() -> None: - # A: [0, 100) - # B: [10, 20), [30, 40), [50, 60) - # Result: [0, 10), [20, 30), [40, 50), [60, 100) - ranges_a = [(0, 100)] - ranges_b = [(10, 20), (30, 40), (50, 60)] - expected = [(0, 10), (20, 30), (40, 50), (60, 100)] - assert range_ops.subtract_ranges(ranges_a, ranges_b) == expected - - -def test_union_ranges_overlapping() -> None: - # A: [10, 20) - # B: [15, 25) - # Result: [10, 25) - ranges_a = [(10, 20)] - ranges_b = [(15, 25)] - assert range_ops.union_ranges(ranges_a, ranges_b) == [(10, 25)] - - -def test_union_ranges_disjoint() -> None: - # A: [10, 20) - # B: [30, 40) - # Result: [10, 20), [30, 40) - ranges_a = [(10, 20)] - ranges_b = [(30, 40)] - assert range_ops.union_ranges(ranges_a, ranges_b) == [(10, 20), (30, 40)] - - -def test_union_ranges_merging_multiple() -> None: - # A: [10, 20), [30, 40) - # B: [15, 35) - # Result: [10, 40) - ranges_a = [(10, 20), (30, 40)] - ranges_b = [(15, 35)] - assert range_ops.union_ranges(ranges_a, ranges_b) == [(10, 40)] - - -def test_intersect_ranges_simple() -> None: - # A: [10, 20) - # B: [15, 25) - # Result: [15, 20) - ranges_a = [(10, 20)] - ranges_b = [(15, 25)] - assert range_ops.intersect_ranges(ranges_a, ranges_b) == [(15, 20)] - - -def test_intersect_ranges_disjoint() -> None: - # A: [10, 20) - # B: [30, 40) - # Result: empty list - ranges_a = [(10, 20)] - ranges_b = [(30, 40)] - assert range_ops.intersect_ranges(ranges_a, ranges_b) == [] - - -def test_intersect_ranges_subset() -> None: - # A: [10, 50) - # B: [20, 30), [35, 40) - # Result: [20, 30), [35, 40) - ranges_a = [(10, 50)] - ranges_b = [(20, 30), (35, 40)] - assert range_ops.intersect_ranges(ranges_a, ranges_b) == [(20, 30), (35, 40)] - - -def test_intersect_ranges_complex() -> None: - # A: [10, 20), [30, 40) - # B: [15, 35) - # Result: [15, 20), [30, 35) - ranges_a = [(10, 20), (30, 40)] - ranges_b = [(15, 35)] - expected = [(15, 20), (30, 35)] - assert range_ops.intersect_ranges(ranges_a, ranges_b) == expected - - -def test_edge_cases_empty() -> None: - assert range_ops.subtract_ranges([], []) == [] - assert range_ops.subtract_ranges([(10, 20)], []) == [(10, 20)] - assert range_ops.subtract_ranges([], [(10, 20)]) == [] - assert range_ops.union_ranges([], []) == [] - assert range_ops.union_ranges([(10, 20)], []) == [(10, 20)] - assert range_ops.union_ranges([], [(10, 20)]) == [(10, 20)] - assert range_ops.intersect_ranges([], []) == [] - assert range_ops.intersect_ranges([(10, 20)], []) == [] - - -def test_adjacent_ranges() -> None: - # Subtract adjacent: [10, 20) - [20, 30) -> [10, 20) - assert range_ops.subtract_ranges([(10, 20)], [(20, 30)]) == [(10, 20)] - # Union adjacent: [10, 20) U [20, 30) -> [10, 30) - assert range_ops.union_ranges([(10, 20)], [(20, 30)]) == [(10, 30)] - # Intersect adjacent: [10, 20) & [20, 30) -> [] - assert range_ops.intersect_ranges([(10, 20)], [(20, 30)]) == [] - - -@pytest.mark.parametrize( - ("val", "test_range", "expected"), - [ - (5, (0, 10), True), - (0, (0, 10), True), - (9, (0, 10), True), - (10, (0, 10), False), - (-1, (0, 10), False), - (5, (10, 20), False), - ], -) -def test_in_range(val: int, test_range: tuple[int, int], expected: bool) -> None: - assert range_ops.in_range(val, test_range) is expected - - -@pytest.mark.parametrize( - ("r1", "r2", "expected"), - [ - # Overlapping cases - ((0, 10), (5, 15), True), - ((5, 15), (0, 10), True), - ((0, 10), (0, 10), True), - ((0, 20), (5, 10), True), - ((5, 10), (0, 20), True), - # Non-overlapping cases / Touching - ((0, 10), (10, 20), False), - ((10, 20), (0, 10), False), - ((0, 5), (15, 20), False), - ], -) -def test_overlaps(r1: tuple[int, int], r2: tuple[int, int], expected: bool) -> None: - assert range_ops.overlaps(r1, r2) is expected - - -@pytest.mark.parametrize( - ("r1", "r2", "expected"), - [ - # Contained - ((5, 10), (0, 20), True), - ((0, 10), (0, 20), True), - ((10, 20), (0, 20), True), - ((0, 20), (0, 20), True), - # Not contained - ((0, 20), (5, 10), False), - ((0, 15), (10, 20), False), - ((-5, 5), (0, 10), False), - ((0, 10), (10, 20), False), - ], -) -def test_contained(r1: tuple[int, int], r2: tuple[int, int], expected: bool) -> None: - assert range_ops.contained(r1, r2) is expected diff --git a/tests/test_regression.py b/tests/test_regression.py index b2c05c4..4affdac 100644 --- a/tests/test_regression.py +++ b/tests/test_regression.py @@ -1,140 +1,134 @@ import json import os import pathlib -import pickle import typing from unittest.mock import AsyncMock, patch +import anchorite import pytest from google.cloud import documentai # type: ignore[import-untyped] -from gemini_ocr import document, gemini_ocr, settings +from gemini_ocr import docai_layout, docai_ocr +from gemini_ocr import gemini as gemini_module FIXTURES_DIR = pathlib.Path(__file__).parent / "fixtures" - -@pytest.fixture -def regression_settings() -> settings.Settings: - return settings.Settings( - project_id="test-project", - location="us-central1", - layout_processor_id="test-layout", - ocr_processor_id="test-ocr", - mode=settings.OcrMode.GEMINI, - cache_dir=None, - ) +PROJECT = "test-project" +LOCATION = "us-central1" @pytest.mark.asyncio -async def test_hubble_regression(regression_settings: settings.Settings) -> None: +async def test_hubble_regression() -> None: pdf_path = pathlib.Path("tests/data/hubble-1929.pdf") if not pdf_path.exists(): pytest.skip("Regression test PDF not found") - # Load fixtures - # Load fixtures with open(FIXTURES_DIR / "hubble_gemini_responses.json") as f: gemini_responses = json.load(f) - with open(FIXTURES_DIR / "hubble_docai_bboxes.pkl", "rb") as f_bin: - docai_bboxes = pickle.load(f_bin) # noqa: S301 + with open(FIXTURES_DIR / "hubble_docai_bboxes.json") as f_json: + bboxes_raw = json.load(f_json) + docai_bboxes = [ + [ + anchorite.Anchor(text=a["text"], page=a["page"], boxes=tuple(anchorite.BBox(**b) for b in a["boxes"])) + for a in chunk + ] + for chunk in bboxes_raw + ] - async def mock_gemini_side_effect(_settings: settings.Settings, chunk: document.DocumentChunk) -> str: + async def mock_gemini_side_effect(chunk: anchorite.document.DocumentChunk) -> str: idx = chunk.start_page // 10 return str(gemini_responses[idx]) - async def mock_ocr_side_effect( - _settings: settings.Settings, - chunk: document.DocumentChunk, - ) -> list[document.BoundingBox]: + async def mock_ocr_side_effect(chunk: anchorite.document.DocumentChunk) -> list[anchorite.Anchor]: idx = chunk.start_page // 10 - return typing.cast("list[document.BoundingBox]", docai_bboxes[idx]) - - # Patch - with patch("gemini_ocr.gemini.generate_markdown", new_callable=AsyncMock) as mock_gemini: - mock_gemini.side_effect = mock_gemini_side_effect + return typing.cast("list[anchorite.Anchor]", docai_bboxes[idx]) - with patch("gemini_ocr.docai_ocr.generate_bounding_boxes", new_callable=AsyncMock) as mock_ocr: - mock_ocr.side_effect = mock_ocr_side_effect - - # Run - result = await gemini_ocr.process_document(pdf_path, settings=regression_settings) + markdown_provider = gemini_module.GeminiMarkdownProvider( + project_id=PROJECT, + location=LOCATION, + model_name="gemini-2.0-flash", + ) + anchor_provider = docai_ocr.DocAIAnchorProvider(project_id=PROJECT, location=LOCATION, processor_id="test-ocr") - # Annotate - output_md = result.annotate() + with ( + patch.object(markdown_provider, "generate_markdown", new=AsyncMock(side_effect=mock_gemini_side_effect)), + patch.object(anchor_provider, "generate_anchors", new=AsyncMock(side_effect=mock_ocr_side_effect)), + ): + chunks = anchorite.document.chunks(pdf_path) + result = await anchorite.process_document(chunks, markdown_provider, anchor_provider, renumber=True) - # Compare with golden - golden_path = FIXTURES_DIR / "hubble_golden.md" + output_md = result.annotate() + golden_path = FIXTURES_DIR / "hubble_golden.md" - if os.environ.get("UPDATE_GOLDEN"): - golden_path.write_text(output_md) + if os.environ.get("UPDATE_GOLDEN"): + golden_path.write_text(output_md) - if not golden_path.exists(): - pytest.fail("Golden file not found. Run with UPDATE_GOLDEN=1 to generate it.") + if not golden_path.exists(): + pytest.fail("Golden file not found. Run with UPDATE_GOLDEN=1 to generate it.") - expected = golden_path.read_text() - assert output_md == expected + assert output_md == golden_path.read_text() @pytest.mark.asyncio -async def test_hubble_docai_regression(regression_settings: settings.Settings) -> None: +async def test_hubble_docai_regression() -> None: pdf_path = pathlib.Path("tests/data/hubble-1929.pdf") if not pdf_path.exists(): pytest.skip("Regression test PDF not found") - docai_settings = regression_settings - docai_settings.mode = settings.OcrMode.DOCUMENTAI - docai_settings.layout_processor_id = "test-layout-id" # Mocked anyway - - # Load fixtures with open(FIXTURES_DIR / "hubble_docai_layout_responses.json") as f: docai_responses_json = json.load(f) - # Deserialize list of JSON strings to documentai.Document objects docai_responses = [ typing.cast("documentai.Document", documentai.Document.from_json(j)) for j in docai_responses_json ] - with open(FIXTURES_DIR / "hubble_docai_bboxes.pkl", "rb") as f_bin: - docai_bboxes = pickle.load(f_bin) # noqa: S301 + with open(FIXTURES_DIR / "hubble_docai_bboxes.json") as f_json: + bboxes_raw = json.load(f_json) + docai_bboxes = [ + [ + anchorite.Anchor(text=a["text"], page=a["page"], boxes=tuple(anchorite.BBox(**b) for b in a["boxes"])) + for a in chunk + ] + for chunk in bboxes_raw + ] async def mock_docai_side_effect( - _settings: settings.Settings, - _process_options: documentai.ProcessOptions, + _project_id: str, + _location: str, _processor_id: str, - chunk: document.DocumentChunk, + _process_options: documentai.ProcessOptions, + chunk: anchorite.document.DocumentChunk, + **_kwargs: object, ) -> documentai.Document: idx = chunk.start_page // 10 return docai_responses[idx] - async def mock_ocr_side_effect( - _settings: settings.Settings, - chunk: document.DocumentChunk, - ) -> list[document.BoundingBox]: + async def mock_ocr_side_effect(chunk: anchorite.document.DocumentChunk) -> list[anchorite.Anchor]: idx = chunk.start_page // 10 - return typing.cast("list[document.BoundingBox]", docai_bboxes[idx]) + return typing.cast("list[anchorite.Anchor]", docai_bboxes[idx]) - # Patch - with patch("gemini_ocr.docai.process", new_callable=AsyncMock) as mock_process: - mock_process.side_effect = mock_docai_side_effect - - with patch("gemini_ocr.docai_ocr.generate_bounding_boxes", new_callable=AsyncMock) as mock_ocr: - mock_ocr.side_effect = mock_ocr_side_effect - - # Run - result = await gemini_ocr.process_document(pdf_path, settings=docai_settings) + markdown_provider = docai_layout.DocAIMarkdownProvider( + project_id=PROJECT, + location=LOCATION, + processor_id="test-layout-id", + ) + anchor_provider = docai_ocr.DocAIAnchorProvider(project_id=PROJECT, location=LOCATION, processor_id="test-ocr") - # Annotate - output_md = result.annotate() + with ( + patch("gemini_ocr.docai.process", new=AsyncMock(side_effect=mock_docai_side_effect)), + patch.object(anchor_provider, "generate_anchors", new=AsyncMock(side_effect=mock_ocr_side_effect)), + ): + chunks = anchorite.document.chunks(pdf_path) + result = await anchorite.process_document(chunks, markdown_provider, anchor_provider, renumber=True) - # Compare with golden - golden_path = FIXTURES_DIR / "hubble_docai_golden.md" + output_md = result.annotate() + golden_path = FIXTURES_DIR / "hubble_docai_golden.md" - if os.environ.get("UPDATE_GOLDEN"): - golden_path.write_text(output_md) + if os.environ.get("UPDATE_GOLDEN"): + golden_path.write_text(output_md) - if not golden_path.exists(): - pytest.fail("Golden file not found. Run with UPDATE_GOLDEN=1 to generate it.") + if not golden_path.exists(): + pytest.fail("Golden file not found. Run with UPDATE_GOLDEN=1 to generate it.") - expected = golden_path.read_text() - assert output_md == expected + assert output_md == golden_path.read_text() diff --git a/tests/test_renumbering.py b/tests/test_renumbering.py deleted file mode 100644 index 6e2298b..0000000 --- a/tests/test_renumbering.py +++ /dev/null @@ -1,40 +0,0 @@ -import re -from unittest.mock import patch - -import pytest - -from gemini_ocr import gemini_ocr, settings - - -@pytest.mark.asyncio -async def test_multi_chunk_renumbering() -> None: - # multiple chunks with overlapping table/figure numbers - # Mock _generate_markdown_for_chunk to return specific text with generic markers - with ( - patch("gemini_ocr.gemini_ocr._generate_markdown_for_chunk") as mock_gen_md, - patch("gemini_ocr.document.chunks", return_value=["c1", "c2"]), - patch("gemini_ocr.docai_ocr.generate_bounding_boxes", return_value=[]), - ): - mock_gen_md.side_effect = [ - "Part 1: Content ", - "Part 2: Content ", - ] - res = await gemini_ocr.process_document( - "dummy.pdf", - settings=settings.Settings( - project_id="test", - location="us", - ocr_processor_id="id", - layout_processor_id="id", - ), - ) - - content = res.markdown_content - - # Check tables are 1, 2, 3 - tables = re.findall(r"", content) - assert tables == ["", "", ""] - - # Check figures are 1, 2, 3 - figures = re.findall(r"", content) - assert figures == ["", "", ""] diff --git a/tests/test_settings.py b/tests/test_settings.py index 01c4cac..fd35b11 100644 --- a/tests/test_settings.py +++ b/tests/test_settings.py @@ -3,68 +3,60 @@ import pytest -from gemini_ocr.settings import Settings +import gemini_ocr +from gemini_ocr import docai_layout, docai_ocr +from gemini_ocr import gemini as gemini_module -def test_settings_from_env() -> None: - """Test loading from environment variable using from_env factory.""" +def test_from_env_gemini_mode() -> None: env = { - "GEMINI_OCR_PROJECT_ID": "env-project", - "GEMINI_OCR_LOCATION": "eu", - "GEMINI_OCR_LAYOUT_PROCESSOR_ID": "env-layout", - "GEMINI_OCR_OCR_PROCESSOR_ID": "env-ocr", + "GEMINI_OCR_PROJECT_ID": "my-project", + "GEMINI_OCR_LOCATION": "us-central1", + "GEMINI_OCR_GEMINI_MODEL_NAME": "gemini-2.0-flash", + "GEMINI_OCR_OCR_PROCESSOR_ID": "ocr-proc", } with patch.dict(os.environ, env, clear=True): - s = Settings.from_env() - assert s.project_id == "env-project" - assert s.get_documentai_location() == "eu" + markdown_provider, anchor_provider = gemini_ocr.from_env() - assert s.layout_processor_id == "env-layout" - assert s.ocr_processor_id == "env-ocr" - assert s.quota_project_id is None + assert isinstance(markdown_provider, gemini_module.GeminiMarkdownProvider) + assert markdown_provider.project_id == "my-project" + assert markdown_provider.model_name == "gemini-2.0-flash" + assert isinstance(anchor_provider, docai_ocr.DocAIAnchorProvider) + assert anchor_provider.processor_id == "ocr-proc" - # Test setting QUOTA_PROJECT_ID - env["GEMINI_OCR_QUOTA_PROJECT_ID"] = "env-quota" - with patch.dict(os.environ, env, clear=True): - s = Settings.from_env() - assert s.quota_project_id == "env-quota" - # Test overridden locations - env_loc = { - "GEMINI_OCR_PROJECT_ID": "p", +def test_from_env_documentai_mode() -> None: + env = { + "GEMINI_OCR_PROJECT_ID": "my-project", "GEMINI_OCR_LOCATION": "europe-west1", + "GEMINI_OCR_MODE": "documentai", + "GEMINI_OCR_LAYOUT_PROCESSOR_ID": "layout-proc", "GEMINI_OCR_DOCUMENTAI_LOCATION": "eu", } - with patch.dict(os.environ, env_loc, clear=True): - s = Settings.from_env() - assert s.get_documentai_location() == "eu" - assert s.location == "europe-west1" + with patch.dict(os.environ, env, clear=True): + markdown_provider, anchor_provider = gemini_ocr.from_env() + assert isinstance(markdown_provider, docai_layout.DocAIMarkdownProvider) + assert markdown_provider.processor_id == "layout-proc" + assert markdown_provider.documentai_location == "eu" + assert anchor_provider is None # no ocr_processor_id set -def test_settings_from_env_defaults() -> None: - """Test default values when using from_env.""" - with patch.dict( - os.environ, - { - "GEMINI_OCR_PROJECT_ID": "test-project", - # LOCATION allows defaults/fallback - }, - clear=True, - ): - # layout_processor_id and ocr_processor_id return None if missing in env - s = Settings.from_env() - assert s.project_id == "test-project" - assert s.get_documentai_location() == "us" # default - assert s.location == "us-central1" - assert s.layout_processor_id is None - assert s.ocr_processor_id is None +def test_from_env_no_bboxes() -> None: + env = { + "GEMINI_OCR_PROJECT_ID": "p", + "GEMINI_OCR_GEMINI_MODEL_NAME": "gemini-2.0-flash", + "GEMINI_OCR_INCLUDE_BBOXES": "false", + } + with patch.dict(os.environ, env, clear=True): + _, anchor_provider = gemini_ocr.from_env() + + assert anchor_provider is None -def test_settings_validation_error() -> None: - """Test validation raises error if missing required env vars in from_env.""" +def test_from_env_missing_project_id() -> None: with ( patch.dict(os.environ, {}, clear=True), pytest.raises(ValueError, match="PROJECT_ID environment variable is required"), ): - Settings.from_env() + gemini_ocr.from_env() diff --git a/uv.lock b/uv.lock index 924c606..3a00a78 100644 --- a/uv.lock +++ b/uv.lock @@ -131,6 +131,20 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e", size = 7490, upload-time = "2025-07-03T22:54:42.156Z" }, ] +[[package]] +name = "anchorite" +version = "0.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "fsspec" }, + { name = "pypdfium2" }, + { name = "seq-smith" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b0/a4/2ee292f6cfea1db2f4c31ee591a6083b38cddb491914b4b172cbd59e1f19/anchorite-0.2.0.tar.gz", hash = "sha256:fdf758a4009eeec433d24668f0f69c3d9074fa66f35539357f35b5a0c89b6273", size = 4262583, upload-time = "2026-03-16T05:49:28.507Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6a/bb/07276db60c98febd920210678607585ac849d06e5717532e1f057acee1f3/anchorite-0.2.0-py3-none-any.whl", hash = "sha256:d2e0b6f4314451a8f1848f39699c9b1596bcb39e109b679f7d5f9352d9f2ac57", size = 25450, upload-time = "2026-03-16T05:49:27.121Z" }, +] + [[package]] name = "annotated-types" version = "0.7.0" @@ -415,16 +429,16 @@ wheels = [ [[package]] name = "gemini-ocr" -version = "0.4.0" +version = "0.5.0" source = { editable = "." } dependencies = [ + { name = "anchorite" }, { name = "fsspec" }, { name = "gcsfs" }, { name = "google-cloud-documentai" }, { name = "google-genai" }, { name = "pymupdf" }, { name = "python-dotenv" }, - { name = "seq-smith" }, ] [package.dev-dependencies] @@ -436,13 +450,13 @@ dev = [ [package.metadata] requires-dist = [ + { name = "anchorite", specifier = "==0.2.0" }, { name = "fsspec" }, { name = "gcsfs" }, { name = "google-cloud-documentai" }, { name = "google-genai" }, { name = "pymupdf" }, { name = "python-dotenv", specifier = ">=1.2.1" }, - { name = "seq-smith", specifier = ">=0.5.1" }, ] [package.metadata.requires-dev] @@ -1291,6 +1305,35 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/dd/c3/d0047678146c294469c33bae167c8ace337deafb736b0bf97b9bc481aa65/pymupdf-1.26.7-cp310-abi3-win_amd64.whl", hash = "sha256:425b1befe40d41b72eb0fe211711c7ae334db5eb60307e9dd09066ed060cceba", size = 18405952, upload-time = "2025-12-11T21:48:02.947Z" }, ] +[[package]] +name = "pypdfium2" +version = "5.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3b/01/be763b9081c7eb823196e7d13d9c145bf75ac43f3c1466de81c21c24b381/pypdfium2-5.6.0.tar.gz", hash = "sha256:bcb9368acfe3547054698abbdae68ba0cbd2d3bda8e8ee437e061deef061976d", size = 270714, upload-time = "2026-03-08T01:05:06.5Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9d/b1/129ed0177521a93a892f8a6a215dd3260093e30e77ef7035004bb8af7b6c/pypdfium2-5.6.0-py3-none-android_23_arm64_v8a.whl", hash = "sha256:fb7858c9707708555b4a719b5548a6e7f5d26bc82aef55ae4eb085d7a2190b11", size = 3346059, upload-time = "2026-03-08T01:04:21.37Z" }, + { url = "https://files.pythonhosted.org/packages/86/34/cbdece6886012180a7f2c7b2c360c415cf5e1f83f1973d2c9201dae3506a/pypdfium2-5.6.0-py3-none-android_23_armeabi_v7a.whl", hash = "sha256:6a7e1f4597317786f994bfb947eef480e53933f804a990193ab89eef8243f805", size = 2804418, upload-time = "2026-03-08T01:04:23.384Z" }, + { url = "https://files.pythonhosted.org/packages/6e/f6/9f9e190fe0e5a6b86b82f83bd8b5d3490348766062381140ca5cad8e00b1/pypdfium2-5.6.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:e468c38997573f0e86f03273c2c1fbdea999de52ba43fee96acaa2f6b2ad35f7", size = 3412541, upload-time = "2026-03-08T01:04:25.45Z" }, + { url = "https://files.pythonhosted.org/packages/ee/8d/e57492cb2228ba56ed57de1ff044c8ac114b46905f8b1445c33299ba0488/pypdfium2-5.6.0-py3-none-macosx_11_0_x86_64.whl", hash = "sha256:ad3abddc5805424f962e383253ccad6a0d1d2ebd86afa9a9e1b9ca659773cd0d", size = 3592320, upload-time = "2026-03-08T01:04:27.509Z" }, + { url = "https://files.pythonhosted.org/packages/f9/8a/8ab82e33e9c551494cbe1526ea250ca8cc4e9e98d6a4fc6b6f8d959aa1d1/pypdfium2-5.6.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f6b5eb9eae5c45076395454522ca26add72ba8bd1fe473e1e4721aa58521470c", size = 3596450, upload-time = "2026-03-08T01:04:29.183Z" }, + { url = "https://files.pythonhosted.org/packages/f5/b5/602a792282312ccb158cc63849528079d94b0a11efdc61f2a359edfb41e9/pypdfium2-5.6.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:258624da8ef45cdc426e11b33e9d83f9fb723c1c201c6e0f4ab5a85966c6b876", size = 3325442, upload-time = "2026-03-08T01:04:30.886Z" }, + { url = "https://files.pythonhosted.org/packages/81/1f/9e48ec05ed8d19d736c2d1f23c1bd0f20673f02ef846a2576c69e237f15d/pypdfium2-5.6.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e9367451c8a00931d6612db0822525a18c06f649d562cd323a719e46ac19c9bb", size = 3727434, upload-time = "2026-03-08T01:04:33.619Z" }, + { url = "https://files.pythonhosted.org/packages/33/90/0efd020928b4edbd65f4f3c2af0c84e20b43a3ada8fa6d04f999a97afe7a/pypdfium2-5.6.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a757869f891eac1cc1372e38a4aa01adac8abc8fe2a8a4e2ebf50595e3bf5937", size = 4139029, upload-time = "2026-03-08T01:04:36.08Z" }, + { url = "https://files.pythonhosted.org/packages/ff/49/a640b288a48dab1752281dd9b72c0679fccea107874e80a65a606b00efa9/pypdfium2-5.6.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:515be355222cc57ae9e62cd5c7c350b8e0c863efc539f80c7d75e2811ba45cb6", size = 3646387, upload-time = "2026-03-08T01:04:38.151Z" }, + { url = "https://files.pythonhosted.org/packages/b0/3b/a344c19c01021eeb5d830c102e4fc9b1602f19c04aa7d11abbe2d188fd8e/pypdfium2-5.6.0-py3-none-manylinux_2_27_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1c4753c7caf7d004211d7f57a21f10d127f5e0e5510a14d24bc073e7220a3ea", size = 3097212, upload-time = "2026-03-08T01:04:40.776Z" }, + { url = "https://files.pythonhosted.org/packages/50/96/e48e13789ace22aeb9b7510904a1b1493ec588196e11bbacc122da330b3d/pypdfium2-5.6.0-py3-none-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c49729090281fdd85775fb8912c10bd19e99178efaa98f145ab06e7ce68554d2", size = 2965026, upload-time = "2026-03-08T01:04:42.857Z" }, + { url = "https://files.pythonhosted.org/packages/cb/06/3100e44d4935f73af8f5d633d3bd40f0d36d606027085a0ef1f0566a6320/pypdfium2-5.6.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:a4a1749a8d4afd62924a8d95cfa4f2e26fc32957ce34ac3b674be6f127ed252e", size = 4131431, upload-time = "2026-03-08T01:04:44.982Z" }, + { url = "https://files.pythonhosted.org/packages/64/ef/d8df63569ce9a66c8496057782eb8af78e0d28667922d62ec958434e3d4b/pypdfium2-5.6.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:36469ebd0fdffb7130ce45ed9c44f8232d91571c89eb851bd1633c64b6f6114f", size = 3747469, upload-time = "2026-03-08T01:04:46.702Z" }, + { url = "https://files.pythonhosted.org/packages/a6/47/fd2c6a67a49fade1acd719fbd11f7c375e7219912923ef2de0ea0ac1544e/pypdfium2-5.6.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:9da900df09be3cf546b637a127a7b6428fb22d705951d731269e25fd3adef457", size = 4337578, upload-time = "2026-03-08T01:04:49.007Z" }, + { url = "https://files.pythonhosted.org/packages/6b/f5/836c83e54b01e09478c4d6bf4912651d6053c932250fcee953f5c72d8e4a/pypdfium2-5.6.0-py3-none-musllinux_1_2_ppc64le.whl", hash = "sha256:45fccd5622233c5ec91a885770ae7dd4004d4320ac05a4ad8fa03a66dea40244", size = 4376104, upload-time = "2026-03-08T01:04:51.04Z" }, + { url = "https://files.pythonhosted.org/packages/6e/7f/b940b6a1664daf8f9bad87c6c99b84effa3611615b8708d10392dc33036c/pypdfium2-5.6.0-py3-none-musllinux_1_2_riscv64.whl", hash = "sha256:282dc030e767cd61bd0299f9d581052b91188e2b87561489057a8e7963e7e0cb", size = 3929824, upload-time = "2026-03-08T01:04:53.544Z" }, + { url = "https://files.pythonhosted.org/packages/88/79/00267d92a6a58c229e364d474f5698efe446e0c7f4f152f58d0138715e99/pypdfium2-5.6.0-py3-none-musllinux_1_2_s390x.whl", hash = "sha256:a1c1dfe950382c76a7bba1ba160ec5e40df8dd26b04a1124ae268fda55bc4cbe", size = 4270201, upload-time = "2026-03-08T01:04:55.81Z" }, + { url = "https://files.pythonhosted.org/packages/e1/ab/b127f38aba41746bdf9ace15ba08411d7ef6ecba1326d529ba414eb1ed50/pypdfium2-5.6.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:43b0341ca6feb6c92e4b7a9eb4813e5466f5f5e8b6baeb14df0a94d5f312c00b", size = 4180793, upload-time = "2026-03-08T01:04:57.961Z" }, + { url = "https://files.pythonhosted.org/packages/0e/8c/a01c8e4302448b614d25a85c08298b0d3e9dfbdac5bd1b2f32c9b02e83d9/pypdfium2-5.6.0-py3-none-win32.whl", hash = "sha256:9dfcd4ff49a2b9260d00e38539ab28190d59e785e83030b30ffaf7a29c42155d", size = 3596753, upload-time = "2026-03-08T01:05:00.566Z" }, + { url = "https://files.pythonhosted.org/packages/9b/5f/2d871adf46761bb002a62686545da6348afe838d19af03df65d1ece786a2/pypdfium2-5.6.0-py3-none-win_amd64.whl", hash = "sha256:c6bc8dd63d0568f4b592f3e03de756afafc0e44aa1fe8878cc4aba1b11ae7374", size = 3716526, upload-time = "2026-03-08T01:05:02.433Z" }, + { url = "https://files.pythonhosted.org/packages/3a/80/0d9b162098597fbe3ac2b269b1682c0c3e8db9ba87679603fdd9b19afaa6/pypdfium2-5.6.0-py3-none-win_arm64.whl", hash = "sha256:5538417b199bdcb3207370c88df61f2ba3dac7a3253f82e1aa2708e6376b6f90", size = 3515049, upload-time = "2026-03-08T01:05:04.587Z" }, +] + [[package]] name = "pytest" version = "9.0.2"