Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 6 additions & 0 deletions app/actions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -822,6 +822,9 @@ export const getUIStateFromAIState = (aiState: AIState): UIState => {
const image = analysisResult.image as string;
const mapboxImage = analysisResult.mapboxImage as string;
const googleImage = analysisResult.googleImage as string;
const mapboxImageLabel = analysisResult.mapboxImageLabel as string;
const googleImageLabel = analysisResult.googleImageLabel as string;
const analysisFocus = analysisResult.analysisFocus as string;

return {
id,
Expand All @@ -831,6 +834,9 @@ export const getUIStateFromAIState = (aiState: AIState): UIState => {
mapboxImage={mapboxImage}
googleImage={googleImage}
initialImage={image}
mapboxImageLabel={mapboxImageLabel}
googleImageLabel={googleImageLabel}
analysisFocus={analysisFocus}
/>
{geoJson && (
<GeoJsonLayer id={id} data={geoJson} />
Expand Down
38 changes: 33 additions & 5 deletions components/resolution-carousel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,19 @@ interface ResolutionCarouselProps {
mapboxImage?: string | null
googleImage?: string | null
initialImage?: string | null
mapboxImageLabel?: string
googleImageLabel?: string
analysisFocus?: string
}

export function ResolutionCarousel({ mapboxImage, googleImage, initialImage }: ResolutionCarouselProps) {
export function ResolutionCarousel({
mapboxImage,
googleImage,
initialImage,
mapboxImageLabel,
googleImageLabel,
analysisFocus
}: ResolutionCarouselProps) {
const actions = useActions<typeof AI>() as any
const [, setMessages] = useUIState<typeof AI>()
const [isAnalyzing, setIsAnalyzing] = React.useState(false)
Expand Down Expand Up @@ -75,12 +85,27 @@ export function ResolutionCarousel({ mapboxImage, googleImage, initialImage }: R
}

// Individual slides
if (mapboxImage) slides.push({ type: 'image', src: mapboxImage, showAnalysis: false, label: 'MAPBOX' })
if (googleImage) slides.push({ type: 'image', src: googleImage, showAnalysis: true, label: 'GOOGLE SATELLITE' })
if (mapboxImage) slides.push({
type: 'image',
src: mapboxImage,
showAnalysis: false,
label: mapboxImageLabel || 'MAPBOX'
})
if (googleImage) slides.push({
type: 'image',
src: googleImage,
showAnalysis: true,
label: googleImageLabel || 'GOOGLE SATELLITE'
})

// Fallback
if (slides.length === 0 && initialImage) {
slides.push({ type: 'image', src: initialImage, showAnalysis: false, label: 'MAP CAPTURE' })
slides.push({
type: 'image',
src: initialImage,
showAnalysis: false,
label: analysisFocus || 'MAP CAPTURE'
})
}

if (slides.length === 0) return null
Expand All @@ -102,6 +127,9 @@ export function ResolutionCarousel({ mapboxImage, googleImage, initialImage }: R
{isAnalyzing ? 'ANALYZING...' : 'QCX-TERRA ANALYSIS'}
</Button>
)}
<div className="mt-1 text-[10px] text-muted-foreground uppercase tracking-widest text-center">
{item.label}
</div>
</div>
)
}
Expand Down Expand Up @@ -130,7 +158,7 @@ export function ResolutionCarousel({ mapboxImage, googleImage, initialImage }: R
{isAnalyzing ? 'ANALYZING...' : 'QCX-TERRA ANALYSIS'}
</Button>
)}
<div className="mt-1 text-[10px] text-muted-foreground uppercase tracking-widest">
<div className="mt-1 text-[10px] text-muted-foreground uppercase tracking-widest text-center">
{slide.label}
</div>
</>
Expand Down
15 changes: 13 additions & 2 deletions lib/agents/resolution-search.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,12 @@ export async function resolutionSearch(messages: CoreMessage[], timezone: string
const systemPrompt = `
As a geospatial analyst, your task is to analyze the provided satellite image of a geographic location.

**CRITICAL GUIDELINES:**
1. **ACCURACY & CONFIDENCE:** Your analysis MUST be grounded strictly in the visual evidence of the image. Do NOT generate GeoJSON features (points, polygons) for landmarks or features that you are not 100% confident in. If you are unsure, omit the GeoJSON feature entirely.
2. **STRICT CONTEXTUAL ALIGNMENT:** Prioritize the user's intent and any user-drawn features as the primary subjects of analysis. Do NOT provide "general" points of interest unless they directly relate to the analysis focus or the user's query.
3. **NO RANDOM OVERLAYS:** Ensure that any boxes or markers you generate are accurately placed within the spatial context of the image and clearly correspond to real objects visible in the satellite view. Avoid "random" or disconnected labeling.
4. **SUPPRESSION OF NOISE:** It is better to return fewer, accurate results than many low-confidence or out-of-context features.

**Temporal Context:**
The current local time at this location is ${localTime} (timezone: ${timezone}).
This temporal information is important for understanding the current state and any time-sensitive features visible in the image.
Expand All @@ -117,17 +123,22 @@ Please incorporate this recent news context into your analysis where relevant.`
${drawnFeatures && drawnFeatures.length > 0 ? `**User-Drawn Features:**
The user has drawn the following features on the map for your reference:
${drawnFeatures.map(f => `- ${f.type} (${f.measurement}): ${JSON.stringify(f.geometry)}`).join('\n')}
Use these user-drawn areas/lines as primary areas of interest for your analysis.` : ''}
Use these user-drawn areas/lines as primary areas of interest for your analysis.
IMPORTANT: In your summary, explicitly state what features you are analyzing and why. Any GeoJSON you generate should strictly align with or augment these drawn areas.` : ''}

**Analysis Requirements:**

1. **Land Feature Classification:** Identify and describe the different types of land cover visible in the image (e.g., urban areas, forests, water bodies, agricultural fields).
2. **Points of Interest (POI):** Detect and name any significant landmarks, infrastructure (e.g., bridges, major roads), or notable buildings.
2. **Points of Interest (POI):** Detect and name any significant landmarks, infrastructure (e.g., bridges, major roads), or notable buildings ONLY if they are clearly visible.
3. **Temporal Analysis:** Consider how the time of day and season might affect what's visible in the image.
4. **Coordinate Extraction:** If possible, confirm or refine the geocoordinates (latitude/longitude) of the center of the image.
5. **COG Applicability:** Determine if this location would benefit from Cloud Optimized GeoTIFF (COG) analysis for high-precision temporal or spectral data.
6. **News Integration:** Reference any recent news or events that may be relevant to the current state of the location.
7. **Structured Output:** Return your findings in a structured JSON format including summary, geoJson (if any), news context, and any extracted coordinates or COG information. Use the provided schema.
8. **Contextual Labeling:** Generate highly specific, descriptive labels for the map images that reflect the primary analysis focus. Avoid generic labels like "Mapbox" or "Google".
- If user-drawn features are present, the labels MUST reference them (e.g., "Analysis of drawn ${drawnFeatures?.[0]?.type || 'area'}: [feature type]").
- If a specific location is known, include it (e.g., "[${locationName}] satellite view - [Focus Area]").
- The 'analysisFocus' field should capture the specific theme of this analysis (e.g., "Coastal erosion assessment", "Industrial zone monitoring").

Your analysis should be based on the visual information in the image, the temporal context provided, and your general knowledge. Do not attempt to access external websites or perform web searches beyond what has been provided.

Expand Down
9 changes: 7 additions & 2 deletions lib/schema/resolution-search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export const resolutionSearchSchema = z.object({
name: z.string().describe('Name of the feature or point of interest'),
description: z.string().optional().describe('Description of the feature')
}))
}).optional().describe('A collection of points of interest and classified land features to be overlaid on the map.'),
}).optional().describe('A collection of HIGH-CONFIDENCE points of interest and classified land features to be overlaid on the map. ONLY include features that are clearly identifiable and highly relevant to the analysis focus or user-drawn areas.'),

// Flattened top-level fields for better xAI compatibility
extractedLatitude: z.number().optional().describe('The extracted latitude of the center of the image.'),
Expand All @@ -35,7 +35,12 @@ export const resolutionSearchSchema = z.object({
title: z.string(),
summary: z.string(),
relevance: z.string()
})).optional().describe('List of recent news items relevant to the location.')
})).optional().describe('List of recent news items relevant to the location.'),

// New fields for contextual labels
mapboxImageLabel: z.string().optional().describe('A contextual label describing what the Mapbox image shows, based on the analysis focus.'),
googleImageLabel: z.string().optional().describe('A contextual label describing what the Google Satellite image shows, based on the analysis focus.'),
analysisFocus: z.string().optional().describe('A brief phrase describing the primary focus of the analysis (e.g., "Urban infrastructure analysis", "Forest coverage assessment").')
})

export type ResolutionSearch = z.infer<typeof resolutionSearchSchema>;
72 changes: 64 additions & 8 deletions server.log
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,69 @@ $ next dev --turbo
- Environments: .env

✓ Starting...
✓ Compiled middleware in 433ms
✓ Ready in 1802ms
Attention: Next.js now collects completely anonymous telemetry regarding usage.
This information is used to shape Next.js' roadmap and prioritize features.
You can learn more, including how to opt-out if you'd not like to participate in this anonymous program, by visiting the following URL:
https://nextjs.org/telemetry

✓ Compiled middleware in 363ms
✓ Ready in 1710ms
○ Compiling / ...
✓ Compiled / in 27.9s
✓ Compiled / in 29.3s
Chat DB actions loaded. Ensure getCurrentUserId() is correctly implemented for server-side usage if applicable.
GET / 200 in 30481ms
HEAD / 200 in 1010ms
GET / 200 in 986ms
GET / 200 in 1129ms
[?25h
GET / 200 in 34985ms
GET / 200 in 35005ms
GET / 200 in 1472ms
GET / 200 in 2734ms
GET / 200 in 1145ms
GET / 200 in 1843ms
GET / 200 in 1137ms
GET / 200 in 1370ms
GET / 200 in 1107ms
GET / 200 in 1355ms
HEAD / 200 in 995ms
GET / 200 in 1896ms
GET / 200 in 1894ms
GET / 200 in 2017ms
GET / 200 in 2021ms
GET / 200 in 2087ms
GET / 200 in 2246ms
GET / 200 in 2268ms
GET / 200 in 2245ms
[Auth] Supabase URL or Anon Key is not set for server-side auth.
POST / 200 in 1605ms
GET / 200 in 1178ms
GET / 200 in 2469ms
GET / 200 in 2179ms
GET / 200 in 2328ms
GET / 200 in 1137ms
[Auth] Supabase URL or Anon Key is not set for server-side auth.
POST / 200 in 2000ms
[Auth] Supabase URL or Anon Key is not set for server-side auth.
POST / 200 in 1989ms
GET / 200 in 1181ms
[Auth] Supabase URL or Anon Key is not set for server-side auth.
POST / 200 in 1073ms
GET / 200 in 1048ms
GET / 200 in 1104ms
[Auth] Supabase URL or Anon Key is not set for server-side auth.
POST / 200 in 1055ms
✓ Compiled middleware in 2ms
○ Compiling /test-carousel ...
✓ Compiled /test-carousel in 6.1s
⨯ Error: `useUIState` must be used inside an <AI> provider.
at ResolutionCarousel (components/resolution-carousel.tsx:39:35)
37 | }: ResolutionCarouselProps) {
38 | const actions = useActions<typeof AI>() as any
> 39 | const [, setMessages] = useUIState<typeof AI>()
| ^
40 | const [isAnalyzing, setIsAnalyzing] = React.useState(false)
41 |
42 | const handleQCXAnalysis = async () => { {
digest: '3019542783'
}
GET /test-carousel 500 in 7348ms
✓ Compiled middleware in 38ms
✓ Compiled in 1094ms
✓ Compiled in 1332ms
✓ Compiled in 215ms