.gitignore DELETED
@@ -1,162 +0,0 @@
1
- # Byte-compiled / optimized / DLL files
2
- __pycache__/
3
- *.py[cod]
4
- *$py.class
5
-
6
- # C extensions
7
- *.so
8
-
9
- # Distribution / packaging
10
- .Python
11
- build/
12
- develop-eggs/
13
- dist/
14
- downloads/
15
- eggs/
16
- .eggs/
17
- lib/
18
- lib64/
19
- parts/
20
- sdist/
21
- var/
22
- wheels/
23
- share/python-wheels/
24
- *.egg-info/
25
- .installed.cfg
26
- *.egg
27
- MANIFEST
28
-
29
- # PyInstaller
30
- # Usually these files are written by a python script from a template
31
- # before PyInstaller builds the exe, so as to inject date/other infos into it.
32
- *.manifest
33
- *.spec
34
-
35
- # Installer logs
36
- pip-log.txt
37
- pip-delete-this-directory.txt
38
-
39
- # Unit test / coverage reports
40
- htmlcov/
41
- .tox/
42
- .nox/
43
- .coverage
44
- .coverage.*
45
- .cache
46
- nosetests.xml
47
- coverage.xml
48
- *.cover
49
- *.py,cover
50
- .hypothesis/
51
- .pytest_cache/
52
- cover/
53
-
54
- # Translations
55
- *.mo
56
- *.pot
57
-
58
- # Django stuff:
59
- *.log
60
- local_settings.py
61
- db.sqlite3
62
- db.sqlite3-journal
63
-
64
- # Flask stuff:
65
- instance/
66
- .webassets-cache
67
-
68
- # Scrapy stuff:
69
- .scrapy
70
-
71
- # Sphinx documentation
72
- docs/_build/
73
-
74
- # PyBuilder
75
- .pybuilder/
76
- target/
77
-
78
- # Jupyter Notebook
79
- .ipynb_checkpoints
80
-
81
- # IPython
82
- profile_default/
83
- ipython_config.py
84
-
85
- # pyenv
86
- # For a library or package, you might want to ignore these files since the code is
87
- # intended to run in multiple environments; otherwise, check them in:
88
- # .python-version
89
-
90
- # pipenv
91
- # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92
- # However, in case of collaboration, if having platform-specific dependencies or dependencies
93
- # having no cross-platform support, pipenv may install dependencies that don't work, or not
94
- # install all needed dependencies.
95
- #Pipfile.lock
96
-
97
- # poetry
98
- # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
99
- # This is especially recommended for binary packages to ensure reproducibility, and is more
100
- # commonly ignored for libraries.
101
- # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
102
- #poetry.lock
103
-
104
- # pdm
105
- # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
106
- #pdm.lock
107
- # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
108
- # in version control.
109
- # https://pdm.fming.dev/latest/usage/project/#working-with-version-control
110
- .pdm.toml
111
- .pdm-python
112
- .pdm-build/
113
-
114
- # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
115
- __pypackages__/
116
-
117
- # Celery stuff
118
- celerybeat-schedule
119
- celerybeat.pid
120
-
121
- # SageMath parsed files
122
- *.sage.py
123
-
124
- # Environments
125
- .env
126
- .venv
127
- env/
128
- venv/
129
- ENV/
130
- env.bak/
131
- venv.bak/
132
-
133
- # Spyder project settings
134
- .spyderproject
135
- .spyproject
136
-
137
- # Rope project settings
138
- .ropeproject
139
-
140
- # mkdocs documentation
141
- /site
142
-
143
- # mypy
144
- .mypy_cache/
145
- .dmypy.json
146
- dmypy.json
147
-
148
- # Pyre type checker
149
- .pyre/
150
-
151
- # pytype static type analyzer
152
- .pytype/
153
-
154
- # Cython debug symbols
155
- cython_debug/
156
-
157
- # PyCharm
158
- # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
159
- # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
160
- # and can be added to the global gitignore or merged into this file. For a more nuclear
161
- # option (not recommended) you can uncomment the following to ignore the entire idea folder.
162
- #.idea/
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Dockerfile CHANGED
@@ -1,14 +1,16 @@
1
- FROM ghcr.io/astral-sh/uv:python3.11-bookworm-slim
2
 
3
  WORKDIR /code
4
 
5
- RUN mkdir /.cache
6
- RUN chmod 777 /.cache
7
-
8
  COPY ./requirements.txt /code/requirements.txt
9
- RUN uv pip install -r /code/requirements.txt --system
 
10
 
11
  COPY . .
12
 
13
- ENTRYPOINT []
14
- CMD ["panel", "serve", "/code/examples/*.py", "--glob", "--address", "0.0.0.0", "--port", "7860", "--allow-websocket-origin", "*", "--static-dirs", "thumbnails=./thumbnails"]
 
 
 
 
 
1
+ FROM python:3.11
2
 
3
  WORKDIR /code
4
 
 
 
 
5
  COPY ./requirements.txt /code/requirements.txt
6
+ RUN python3 -m pip install --no-cache-dir --upgrade pip
7
+ RUN python3 -m pip install --no-cache-dir --upgrade -r /code/requirements.txt
8
 
9
  COPY . .
10
 
11
+ CMD ["panel", "serve", "/code/app.py", "--address", "0.0.0.0", "--port", "7860", "--allow-websocket-origin", "*"]
12
+
13
+ RUN mkdir /.cache
14
+ RUN chmod 777 /.cache
15
+ RUN mkdir .chroma
16
+ RUN chmod 777 .chroma
README.md CHANGED
@@ -1,6 +1,6 @@
1
  ---
2
  title: Aladin
3
- emoji: 🛸
4
  colorFrom: gray
5
  colorTo: green
6
  sdk: docker
@@ -8,33 +8,4 @@ pinned: false
8
  license: mit
9
  ---
10
 
11
- This project demonstrates **how to enable interactive sky visualization** in Python data apps by
12
- combining [ipyaladin](https://github.com/cds-astro/ipyaladin) and [Panel](https://panel.holoviz.org/index.html).
13
-
14
- The [examples](examples) are resembling the examples at [ipyaladin/examples](https://github.com/cds-astro/ipyaladin/tree/master/examples).
15
-
16
- Created by [awesome-panel](https://awesome-panel.org).
17
-
18
- ## Install
19
-
20
- ```bash
21
- pip install -r requirements_dev.txt
22
- ```
23
-
24
- ## Serve
25
-
26
- ```bash
27
- panel serve examples/*.py --dev --static-dirs thumbnails=./thumbnails
28
- ```
29
-
30
- ## Ruff
31
-
32
- ```bash
33
- ruff format
34
- ruff check --fix;
35
- ```
36
-
37
- ## Known Issues
38
-
39
- - [CORS](https://github.com/cds-astro/ipyaladin/issues/118)
40
- - [jslinking](https://github.com/bokeh/ipywidgets_bokeh/issues/39#issuecomment-2365192366)
 
1
  ---
2
  title: Aladin
3
+ emoji: 📈
4
  colorFrom: gray
5
  colorTo: green
6
  sdk: docker
 
8
  license: mit
9
  ---
10
 
11
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app.py ADDED
@@ -0,0 +1,147 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import io
2
+ import random
3
+ from typing import List, Tuple
4
+
5
+ import aiohttp
6
+ import panel as pn
7
+ from PIL import Image
8
+ from transformers import CLIPModel, CLIPProcessor
9
+
10
+ pn.extension(design="bootstrap", sizing_mode="stretch_width")
11
+
12
+ ICON_URLS = {
13
+ "brand-github": "https://github.com/holoviz/panel",
14
+ "brand-twitter": "https://twitter.com/Panel_Org",
15
+ "brand-linkedin": "https://www.linkedin.com/company/panel-org",
16
+ "message-circle": "https://discourse.holoviz.org/",
17
+ "brand-discord": "https://discord.gg/AXRHnJU6sP",
18
+ }
19
+
20
+
21
+ async def random_url(_):
22
+ pet = random.choice(["cat", "dog"])
23
+ api_url = f"https://api.the{pet}api.com/v1/images/search"
24
+ async with aiohttp.ClientSession() as session:
25
+ async with session.get(api_url) as resp:
26
+ return (await resp.json())[0]["url"]
27
+
28
+
29
+ @pn.cache
30
+ def load_processor_model(
31
+ processor_name: str, model_name: str
32
+ ) -> Tuple[CLIPProcessor, CLIPModel]:
33
+ processor = CLIPProcessor.from_pretrained(processor_name)
34
+ model = CLIPModel.from_pretrained(model_name)
35
+ return processor, model
36
+
37
+
38
+ async def open_image_url(image_url: str) -> Image:
39
+ async with aiohttp.ClientSession() as session:
40
+ async with session.get(image_url) as resp:
41
+ return Image.open(io.BytesIO(await resp.read()))
42
+
43
+
44
+ def get_similarity_scores(class_items: List[str], image: Image) -> List[float]:
45
+ processor, model = load_processor_model(
46
+ "openai/clip-vit-base-patch32", "openai/clip-vit-base-patch32"
47
+ )
48
+ inputs = processor(
49
+ text=class_items,
50
+ images=[image],
51
+ return_tensors="pt", # pytorch tensors
52
+ )
53
+ outputs = model(**inputs)
54
+ logits_per_image = outputs.logits_per_image
55
+ class_likelihoods = logits_per_image.softmax(dim=1).detach().numpy()
56
+ return class_likelihoods[0]
57
+
58
+
59
+ async def process_inputs(class_names: List[str], image_url: str):
60
+ """
61
+ High level function that takes in the user inputs and returns the
62
+ classification results as panel objects.
63
+ """
64
+ try:
65
+ main.disabled = True
66
+ if not image_url:
67
+ yield "##### ⚠️ Provide an image URL"
68
+ return
69
+
70
+ yield "##### ⚙ Fetching image and running model..."
71
+ try:
72
+ pil_img = await open_image_url(image_url)
73
+ img = pn.pane.Image(pil_img, height=400, align="center")
74
+ except Exception as e:
75
+ yield f"##### 😔 Something went wrong, please try a different URL!"
76
+ return
77
+
78
+ class_items = class_names.split(",")
79
+ class_likelihoods = get_similarity_scores(class_items, pil_img)
80
+
81
+ # build the results column
82
+ results = pn.Column("##### 🎉 Here are the results!", img)
83
+
84
+ for class_item, class_likelihood in zip(class_items, class_likelihoods):
85
+ row_label = pn.widgets.StaticText(
86
+ name=class_item.strip(), value=f"{class_likelihood:.2%}", align="center"
87
+ )
88
+ row_bar = pn.indicators.Progress(
89
+ value=int(class_likelihood * 100),
90
+ sizing_mode="stretch_width",
91
+ bar_color="secondary",
92
+ margin=(0, 10),
93
+ design=pn.theme.Material,
94
+ )
95
+ results.append(pn.Column(row_label, row_bar))
96
+ yield results
97
+ finally:
98
+ main.disabled = False
99
+
100
+
101
+ # create widgets
102
+ randomize_url = pn.widgets.Button(name="Randomize URL", align="end")
103
+
104
+ image_url = pn.widgets.TextInput(
105
+ name="Image URL to classify",
106
+ value=pn.bind(random_url, randomize_url),
107
+ )
108
+ class_names = pn.widgets.TextInput(
109
+ name="Comma separated class names",
110
+ placeholder="Enter possible class names, e.g. cat, dog",
111
+ value="cat, dog, parrot",
112
+ )
113
+
114
+ input_widgets = pn.Column(
115
+ "##### 😊 Click randomize or paste a URL to start classifying!",
116
+ pn.Row(image_url, randomize_url),
117
+ class_names,
118
+ )
119
+
120
+ # add interactivity
121
+ interactive_result = pn.panel(
122
+ pn.bind(process_inputs, image_url=image_url, class_names=class_names),
123
+ height=600,
124
+ )
125
+
126
+ # add footer
127
+ footer_row = pn.Row(pn.Spacer(), align="center")
128
+ for icon, url in ICON_URLS.items():
129
+ href_button = pn.widgets.Button(icon=icon, width=35, height=35)
130
+ href_button.js_on_click(code=f"window.open('{url}')")
131
+ footer_row.append(href_button)
132
+ footer_row.append(pn.Spacer())
133
+
134
+ # create dashboard
135
+ main = pn.WidgetBox(
136
+ input_widgets,
137
+ interactive_result,
138
+ footer_row,
139
+ )
140
+
141
+ title = "Panel Demo - Image Classification"
142
+ pn.template.BootstrapTemplate(
143
+ title=title,
144
+ main=main,
145
+ main_max_width="min(50%, 698px)",
146
+ header_background="#F08080",
147
+ ).servable(title=title)
examples/01_getting_started.py DELETED
@@ -1,42 +0,0 @@
1
- # See also https://github.com/cds-astro/ipyaladin/blob/master/examples/01_Getting_Started.ipynb
2
- """# 🛸 Getting Started
3
-
4
- Displays an interactive sky view of the
5
- [Mars Viking MDIM21 survey](https://alasky.u-strasbg.fr/Planets/Mars_Viking_MDIM21/).
6
-
7
- Powered by \
8
- [ipyaladin](https://github.com/cds-astro/ipyaladin) and \
9
- [Panel](https://panel.holoviz.org/index.html).
10
-
11
- Check out the [**App Gallery**](./)
12
- """
13
-
14
- import panel as pn
15
- from ipyaladin import Aladin
16
-
17
- pn.extension("ipywidgets", sizing_mode="stretch_width")
18
-
19
- aladin = Aladin(
20
- target="159.2135528 -58.6241989",
21
- survey="https://alasky.u-strasbg.fr/Planets/Mars_Viking_MDIM21",
22
- fov=10,
23
- height=800,
24
- )
25
-
26
- panel = pn.panel(aladin, height=800)
27
-
28
- pn.template.FastListTemplate(
29
- site="ipyaladin and Panel",
30
- site_url="./",
31
- title="01 Getting Started",
32
- sidebar=[
33
- pn.pane.PNG(
34
- "https://avatars.githubusercontent.com/u/26145382?s=200&v=4",
35
- link_url="https://cds.unistra.fr/",
36
- ),
37
- __doc__,
38
- ],
39
- main=[panel],
40
- accent="#296bb5",
41
- main_layout=None,
42
- ).servable()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
examples/06_linked_widgets.py DELETED
@@ -1,57 +0,0 @@
1
- """# 🛸 Linked Widgets
2
-
3
- Displays 3 linked `Aladin` widgets. Try zoming or panning.
4
-
5
- Powered by \
6
- [ipyaladin](https://github.com/cds-astro/ipyaladin) and \
7
- [Panel](https://panel.holoviz.org/index.html).
8
-
9
- Check out the [**App Gallery**](./)
10
- """
11
-
12
- import panel as pn
13
- from ipyaladin import Aladin
14
- from ipywidgets import Box, Layout, widgets
15
-
16
- pn.extension("ipywidgets", sizing_mode="stretch_width")
17
-
18
- cosmetic_options = {"show_projection_control": False, "show_fullscreen_control": False}
19
-
20
- a = Aladin(layout=Layout(width="100%"), target="M 81", fov=0.3, **cosmetic_options)
21
- b = Aladin(layout=Layout(width="100%"), survey="P/DSS2/red", **cosmetic_options)
22
- c = Aladin(layout=Layout(width="100%"), survey="P/2MASS/color", **cosmetic_options)
23
-
24
- # synchronize target between 3 widgets
25
- widgets.jslink((a, "_target"), (b, "_target"))
26
- widgets.jslink((b, "_target"), (c, "_target"))
27
-
28
- # synchronize FoV (zoom level) between 3 widgets
29
- widgets.jslink((a, "_fov"), (b, "_fov"))
30
- widgets.jslink((b, "_fov"), (c, "_fov"))
31
-
32
- items = [a, b, c]
33
-
34
- box_layout = Layout(
35
- display="flex",
36
- flex_flow="column",
37
- align_items="stretch",
38
- border="solid",
39
- width="100%",
40
- )
41
- box = Box(children=items, layout=box_layout)
42
-
43
- pn.template.FastListTemplate(
44
- site="ipyaladin and Panel",
45
- site_url="./",
46
- title="Linked Widgets",
47
- sidebar=[
48
- pn.pane.PNG(
49
- "https://avatars.githubusercontent.com/u/26145382?s=200&v=4",
50
- link_url="https://cds.unistra.fr/",
51
- ),
52
- __doc__,
53
- ],
54
- main=[box],
55
- accent="#296bb5",
56
- main_layout=None,
57
- ).servable()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
examples/07_on_click_callback.py DELETED
@@ -1,93 +0,0 @@
1
- """# 🛸 On Click Callback
2
-
3
- Once all cells are executed, click anywhere to get the closest star name. Be patient and the associated photometric points appear after a few seconds.
4
-
5
- Powered by \
6
- [ipyaladin](https://github.com/cds-astro/ipyaladin) and \
7
- [Panel](https://panel.holoviz.org/index.html).
8
-
9
- Check out the [**App Gallery**](./)
10
- """
11
-
12
- import panel as pn
13
- import requests
14
- from ipyaladin import Aladin
15
- from ipywidgets import Layout
16
-
17
- pn.extension("ipywidgets", sizing_mode="stretch_width")
18
-
19
- LoadingIndicator = "Loading..."
20
-
21
- aladin = Aladin(
22
- layout=Layout(width="100%"),
23
- height=800,
24
- target="M 36",
25
- fov=0.3,
26
- show_fullscreen_control=False,
27
- )
28
- info = pn.pane.HTML("<h2>Click the widget</h2>")
29
- image = pn.pane.PNG(embed=True, sizing_mode="fixed")
30
- box = pn.Column(aladin, info, image)
31
-
32
-
33
- def process_result(data: dict) -> None:
34
- """Process the result of a click event on the Aladin widget.
35
-
36
- Parameters
37
- ----------
38
- data : dict
39
- The data returned by the click event.
40
- """
41
- box.loading = True
42
-
43
- info.value = ""
44
- ra = data["ra"]
45
- dec = data["dec"]
46
- query = (
47
- f"SELECT TOP 1 main_id, ra, dec, DISTANCE(POINT('ICRS', {ra}, {dec}), "
48
- f"POINT('ICRS', ra, dec)) as d FROM basic "
49
- f"WHERE CONTAINS(POINT('ICRS', ra, dec), CIRCLE('ICRS', {ra}, {dec}, {aladin.fov.deg / 10}))=1 "
50
- "ORDER BY d ASC"
51
- )
52
-
53
- r = requests.get(
54
- "http://simbad.u-strasbg.fr/simbad/sim-tap/sync",
55
- params={
56
- "query": query,
57
- "request": "doQuery",
58
- "lang": "adql",
59
- "format": "json",
60
- "phase": "run",
61
- },
62
- )
63
- obj_name = ""
64
- obj_coo = None
65
- obj_data = r.json()["data"]
66
- if len(obj_data) == 0:
67
- return
68
-
69
- obj_name = obj_data[0][0]
70
- obj_coo = [obj_data[0][1], obj_data[0][2]]
71
-
72
- info.object = f"<h2>{obj_name}</h2><br><br>"
73
- image.object = f"https://cdsportal.u-strasbg.fr/cgi-bin/PhotVizPreview/plot?ra={obj_coo[0]}&dec={obj_coo[1]}&radius_arcsec=5&w=200&h=150&point_size=4"
74
- box.loading = False
75
-
76
-
77
- aladin.set_listener("click", process_result)
78
-
79
- pn.template.FastListTemplate(
80
- site="ipyaladin and Panel",
81
- site_url="./",
82
- title="On Click Callback",
83
- sidebar=[
84
- pn.pane.PNG(
85
- "https://avatars.githubusercontent.com/u/26145382?s=200&v=4",
86
- link_url="https://cds.unistra.fr/",
87
- ),
88
- __doc__,
89
- ],
90
- main=[box],
91
- accent="#296bb5",
92
- main_layout=None,
93
- ).servable()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
pyproject.toml DELETED
@@ -1,5 +0,0 @@
1
- [tool.ruff]
2
- line-length = 120
3
-
4
- [tool.ruff.lint]
5
- extend-select = ["I"]
 
 
 
 
 
 
requirements.txt CHANGED
@@ -1,3 +1,6 @@
1
- ipyaladin
2
- ipywidgets_bokeh
3
- panel
 
 
 
 
1
+ panel
2
+ jupyter
3
+ transformers
4
+ numpy
5
+ torch
6
+ aiohttp
requirements_dev.txt DELETED
@@ -1,3 +0,0 @@
1
- -r requirements.txt
2
- ruff
3
- watchfiles
 
 
 
 
thumbnails/01_getting_started.png DELETED
Binary file (3.66 kB)
 
thumbnails/06_linked_widgets.png DELETED
Binary file (3.29 kB)
 
thumbnails/07_on_click_callback.png DELETED
Binary file (4.2 kB)