Skip to content

Add natural language search #3

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 21 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
29d61fe
Hack to demo natural language search
sunu May 19, 2025
c428150
feat: add config option to enable/disable semantic searh
vgeorge Jun 13, 2025
d6ae6b9
fix: do not display default search form when natural query is enabled
vgeorge Jun 13, 2025
fa3879c
feat: move natural language input to SearchFilter component
vgeorge Jun 18, 2025
ff0078f
feat: improve natural language search UI with better user guidance
vgeorge Jun 18, 2025
b2f91e0
feat: add natural language search with explanation display and loadin…
vgeorge Jun 18, 2025
1f66dfd
feat: populate temporal extent from natural language search response
vgeorge Jun 18, 2025
35a52cb
feat: display natural language search intersects polygon on map with …
vgeorge Jun 18, 2025
b03fd4d
feat: populate collections filter from natural language search response
vgeorge Jun 18, 2025
ae83557
feat: populate items per page from natural language search response
vgeorge Jun 18, 2025
6b83c8e
fix: do not query items from natural language API
vgeorge Jun 23, 2025
3fbba13
fix: restore setFilters logic
vgeorge Jun 23, 2025
f8022dc
refactor: change spatial extend from checkbox to radio group in prepa…
vgeorge Jun 23, 2025
7b2e020
feat: use natural language area as intersects param
vgeorge Jun 23, 2025
7b456c5
feat: add branch preview deployment workflow with semantic search sup…
vgeorge Jun 25, 2025
e1f5ea7
fix: update deployment url
vgeorge Jun 25, 2025
fe831dd
Add separator to clearly distinguish form-based and natural language …
AliceR Jun 26, 2025
9e868ee
Clarify instructions for natural language search
AliceR Jun 26, 2025
f3e15e9
Add separate button for direct nl search, making it the primary action
AliceR Jun 26, 2025
e21da43
Display spatial extend of natural search area
AliceR Jun 30, 2025
6be1a20
Merge pull request #7 from developmentseed/nls-demo-ux-improvements
vgeorge Jul 1, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions .github/workflows/deploy-branch.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
name: Deploy Branch Preview
on:
push:
branches-ignore:
- main
- develop
pull_request:
branches:
- main
- develop

jobs:
deploy-preview:
runs-on: ubuntu-latest
env:
SEMANTIC_SEARCH_API_URL: ${{ secrets.SEMANTIC_SEARCH_API_URL }}
steps:
- uses: actions/setup-node@v4
with:
node-version: "lts/*"

- uses: actions/checkout@v4

- name: Get branch name
id: branch-name
run: |
if [ "${{ github.event_name }}" = "pull_request" ]; then
echo "branch_name=${{ github.head_ref }}" >> $GITHUB_OUTPUT
else
echo "branch_name=${{ github.ref_name }}" >> $GITHUB_OUTPUT
fi
- name: Install dependencies
run: npm install

- name: Build for preview
run: npm run build -- --pathPrefix="/stac-browser/preview/${{ steps.branch-name.outputs.branch_name }}" --historyMode=hash --semanticSearchApiUrl="${{ env.SEMANTIC_SEARCH_API_URL }}"

- name: Deploy to GitHub Pages
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: dist
exclude_assets: "report.html"
user_name: "STAC Browser CI"
user_email: github@developmentseed.org
destination_dir: preview/${{ steps.branch-name.outputs.branch_name }}
publish_branch: gh-pages-preview

- name: Comment PR
if: github.event_name == 'pull_request'
uses: actions/github-script@v7
with:
script: |
const url = `https://${{ github.repository_owner }}.github.io/${{ github.event.repository.name }}/preview/${{ steps.branch-name.outputs.branch_name }}/`;
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: `🚀 **Preview deployed!**\n\nYour changes are now available at: ${url}\n\nThis preview will be automatically updated on each push to this branch.`
});
45 changes: 44 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ It's not officially supported, but you may also be able to use it for
certain _OGC API - Records_ and _OGC API - Features_ compliant servers.

**Please note that STAC Browser is currently with limited funding for both maintenance, bug fixes and improvements. This means issues and PRs may be addressed very slowly.
If you care about STAC Browser and have some funds to support the future of STAC Browser, please contact matthias@mohr.ws**
If you care about STAC Browser and have some funds to support the future of STAC Browser, please contact <matthias@mohr.ws>**

**Table of Contents:**

Expand All @@ -37,6 +37,7 @@ If you care about STAC Browser and have some funds to support the future of STAC
- [Translation](#translation)
- [Customize through root catalog](#customize-through-root-catalog)
- [Custom extensions](#custom-extensions)
- [Natural Language Search](#natural-language-search)
- [Docker](#docker)
- [Contributing](#contributing)
- [Adding a new language](#adding-a-new-language)
Expand Down Expand Up @@ -269,6 +270,48 @@ STAC Browser supports some non-standardized extensions to the STAC specification
Add a `name` field and it will be used as title in the tab header, the same applies for the core Asset Object.
3. A link with relation type `icon` and a Browser-supported media type in any STAC entity will show an icon in the header and the lists of Catalogs, Collections and Items.

### Natural Language Search

STAC Browser supports natural language search functionality that allows users to search for STAC items using descriptive queries in natural language (e.g., "satellite images of forests in California from 2023").

To enable natural language search:

1. **Configure the API URL**: Set the `semanticSearchApiUrl` option in your configuration file:

```javascript
// config.js
module.exports = {
// ... other config options
semanticSearchApiUrl: "https://your-semantic-search-api.com",
// ... other config options
};
```

2. **API Requirements**: Your semantic search API should accept POST requests to `/items/search` with the following format:

```json
{
"query": "your natural language query",
"limit": 10
}
```

And return results in this format:

```json
{
"results": {
"items": [
// Array of STAC items
]
}
}
```

3. **User Interface**: Once configured, users will see the "Natural Language Query" search interface where they can enter descriptive queries.

**Note**: Natural language search is disabled by default. The feature will only appear in the interface when `semanticSearchApiUrl` is configured with a valid API URL.

## Docker

You can use the Docker to work with STAC Browser. Please read [Docker documentation](docs/docker.md) for more details.
Expand Down
3 changes: 2 additions & 1 deletion config.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,5 +48,6 @@ module.exports = {
requestQueryParameters: {},
socialSharing: ['email', 'bsky', 'mastodon', 'x'],
preprocessSTAC: null,
authConfig: null
authConfig: null,
semanticSearchApiUrl: false,
};
3 changes: 3 additions & 0 deletions src/components/ApiCapabilitiesMixin.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ export default {
canFilterFreeText() {
return this.supportsConformance(this.conformances.FreeText);
},
canSupportNaturalLanguage() {
return true;
},
cql() {
if (!this.supportsConformance(this.conformances.CqlFilters)) {
return null;
Expand Down
20 changes: 20 additions & 0 deletions src/components/Map.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
<l-tile-layer v-for="xyz of xyzLinks" ref="xyzOverlays" :key="xyz.url" layerType="overlay" v-bind="xyz" />
<LWMSTileLayer v-for="wms of wmsLinks" ref="wmsOverlays" :key="wms.url" layerType="overlay" v-bind="wms" />
<l-geo-json v-if="geojson" ref="geojson" :geojson="geojson" :options="{onEachFeature: showPopup}" :optionsStyle="{color: secondaryColor, weight: secondaryWeight}" />
<l-geo-json v-if="intersectsPolygon" ref="intersectsLayer" :geojson="intersectsPolygon" :options="{onEachFeature: showIntersectsPopup}" :optionsStyle="{color: '#3B82F6', weight: 2, fillColor: '#3B82F6', fillOpacity: 0.15, opacity: 0.8, dashArray: '5, 5'}" @ready="onIntersectsLayerReady" />
</l-map>
<b-popover
v-if="popover && selectedItem" placement="left" triggers="manual" :show="selectedItem !== null"
Expand Down Expand Up @@ -180,6 +181,15 @@ export default {
}
}
return wmsLinks;
},
naturalLanguageDescription() {
return this.naturalLanguageExplanation;
},
intersectsPolygon() {
if (this.stacLayerData && this.stacLayerData.intersects) {
return this.stacLayerData.intersects;
}
return null;
}
},
watch: {
Expand Down Expand Up @@ -428,6 +438,12 @@ export default {
this.$refs.geojson.mapObject.bringToFront();
}
},
onIntersectsLayerReady() {
const layer = this.$refs.intersectsLayer && this.$refs.intersectsLayer.mapObject;
if (layer && this.map) {
this.fitBounds(layer);
}
},
fitBounds(layer, noPadding = false) {
let fitOptions = {
padding: noPadding ? [0,0] : [90,90],
Expand All @@ -452,6 +468,10 @@ export default {
}
layer.bindPopup(html);
},
showIntersectsPopup(feature, layer) {
const html = `<h3>${this.$t('search.naturalLanguageSearchArea')}</h3><p>${this.$t('search.naturalLanguageSearchAreaDescription')}</p>`;
layer.bindPopup(html);
},
addBoundsSelector() {
this.areaSelect = L.areaSelect({ // eslint-disable-line
width: 300,
Expand Down
Loading
Loading