← Tutte le idee Giochi e hobby

isometric.nyc

Una gigantesca mappa in pixel art di New York

Cosa fa

Un'enorme mappa della città di New York disegnata in pixel art isometrica, da esplorare nel browser.

Come guadagna

Presentato su Hacker News dal suo creatore, che non dichiara incassi. Idee nostre per guadagnare con un progetto simile: stampe e poster della mappa, versioni personalizzate per quartiere.

Rendilo tuo

  • La mappa in pixel art di una città italiana, quartiere per quartiere, finanziata con la vendita dei poster.
  • Gli abitanti possono chiedere di aggiungere il loro negozio a pagamento.

Le frasi da scrivere all'AI

1. Base del sito e grafica

Crea un'app web in italiano chiamata "Città in Pixel": una grande mappa di una città italiana (es. Milano, Roma, Napoli) disegnata in pixel art isometrica, da esplorare nel browser con zoom, luoghi da scoprire e poster stampabili di ogni quartiere. Usa React, Vite, TypeScript e Tailwind CSS.

Pagine da creare:
1. Pagina iniziale con la mappa a tutto schermo
2. Esplorazione con zoom e spostamento, punti d'interesse cliccabili
3. Scheda del luogo con curiosità e link
4. Negozio dei poster: scegli il riquadro della mappa e il formato
5. Pagina "Come è stata fatta"

Stile: pixel art luminosa: colori pastello dei palazzi, cielo azzurro #BAE6FD, interfaccia minima con riquadri in stile videogioco, carattere "Pixelify Sans" per i titoli e "Inter" per i testi. Nessuna immagine generica di stock.

Tutto deve funzionare perfettamente da telefono. Tutti i testi in italiano, chiari e senza parole tecniche.

2. Database, account e sicurezza

Collega il progetto a Supabase e attiva la registrazione con email e password.

Crea queste tabelle:
- "luoghi": nome, posizione sulla mappa, descrizione, categoria
- "ordini": riquadro scelto della mappa, formato del poster, indirizzo, pagato, stato della spedizione

Regole di sicurezza obbligatorie:
- luoghi e mappa sono pubblici
- ogni cliente vede solo i suoi ordini
- "pagato" lo cambia solo il pagamento

Usa esattamente il codice SQL che trovi nei "Dettagli tecnici" di questa scheda: eseguilo nel SQL Editor di Supabase prima di continuare, poi collega le pagine alle tabelle.

3. Una mappa enorme a tasselli

La mappa è un'immagine gigantesca divisa in tasselli di 256×256 pixel a più livelli di zoom (come Google Maps), caricati solo quando servono. Si mostra con Leaflet in modalità "immagine" invece che geografica (codice nei Dettagli tecnici).

La pixel art si disegna a mano o con l'aiuto di strumenti, partendo dai dati aperti di OpenStreetMap (strade, edifici, parchi) con licenza ODbL, da citare in ogni pagina. Si lavora per quartieri, pubblicandoli man mano: "Nuovo quartiere: Navigli!" fa tornare la gente.

Punti d'interesse cliccabili con curiosità brevi, e una modalità notte con le finestre illuminate.

4. Poster stampati

Nel negozio il cliente trascina un riquadro sulla mappa, sceglie il formato (A3, 50×70, 70×100) e la cornice, e vede l'anteprima. Il file ad alta risoluzione per la stampa si genera dai tasselli del livello più dettagliato.

Dopo il pagamento l'ordine va a una tipografia online con API di stampa su richiesta, che spedisce direttamente. Pannello degli ordini con lo stato.

5. Pagamenti con Stripe

Aggiungi un pagamento singolo di da 29 € per un poster A3 spedito. Il pagamento conferma l'ordine del poster e lo invia in stampa.

A pagamento riuscito il webhook imposta "pagato" a true nella tabella "ordini", sulla riga del ordine indicata in client_reference_id.

Per la sessione di pagamento e il webhook segui la guida "Pagamenti con Stripe" di Fattibile.

6. Controlli specifici di questo progetto

Oltre ai controlli della guida "Controllo finale", verifica in particolare:
1. Licenze: verifica che in ogni pagina e su ogni poster ci sia l'attribuzione "© contributori di OpenStreetMap" richiesta dalla licenza dei dati.
Correggi quello che non va e scrivimi cosa hai cambiato.

Dettagli tecnici

Mappa di un'immagine gigante con Leaflet

Leaflet in modalità "CRS.Simple" mostra un'immagine enorme divisa in tasselli, con zoom e spostamento, invece di una mappa geografica.

import L from "leaflet";

// L'immagine completa è larga 65.536 pixel: 8 livelli di zoom di tasselli 256x256
const LATO = 65_536;
const ZOOM_MASSIMO = 8;

export function creaMappa(contenitore: HTMLElement) {
  const mappa = L.map(contenitore, {
    crs: L.CRS.Simple,                 // coordinate in pixel, non latitudine/longitudine
    minZoom: 0,
    maxZoom: ZOOM_MASSIMO,
    zoomSnap: 0.5,
  });

  // Converte i pixel dell'immagine in coordinate della mappa
  const daPixel = (x: number, y: number) => mappa.unproject([x, y], ZOOM_MASSIMO);
  const bordi = L.latLngBounds(daPixel(0, LATO), daPixel(LATO, 0));

  L.tileLayer("https://tasselli.cittainpixel.it/{z}/{x}/{y}.png", {
    bounds: bordi,
    noWrap: true,
    maxNativeZoom: ZOOM_MASSIMO,
    attribution: "Dati © contributori di OpenStreetMap (ODbL)",
  }).addTo(mappa);

  mappa.fitBounds(bordi);
  mappa.setMaxBounds(bordi.pad(0.1));   // non si esce troppo dall'immagine
  return { mappa, daPixel };
}

// Un punto d'interesse alla posizione in pixel dell'immagine
export function aggiungiLuogo(m: { mappa: L.Map; daPixel: (x: number, y: number) => L.LatLng }, x: number, y: number, nome: string) {
  L.marker(m.daPixel(x, y)).addTo(m.mappa).bindPopup("<b>" + nome + "</b>");
}

Luoghi e ordini dei poster

I punti d'interesse pubblici e gli ordini dei poster, con il riquadro scelto in pixel.

create table luoghi (
  id serial primary key,
  nome text not null,
  x int not null, y int not null,       -- posizione in pixel sull'immagine intera
  categoria text not null,
  descrizione text not null
);
alter table luoghi enable row level security;
create policy "luoghi pubblici" on luoghi for select using (true);

create table ordini (
  id uuid primary key default gen_random_uuid(),
  utente_id uuid not null references auth.users(id) on delete cascade,
  riquadro jsonb not null,              -- { x, y, larghezza, altezza } in pixel
  formato text not null check (formato in ('A3', '50x70', '70x100')),
  indirizzo jsonb not null,
  pagato boolean not null default false,
  spedizione text
);
alter table ordini enable row level security;
create policy "i miei ordini" on ordini for select using (auth.uid() = utente_id);
create policy "ordino" on ordini for insert with check (auth.uid() = utente_id and not pagato);

Pezzi comuni a tutti i progetti

Glossario

JSONB
Un tipo di colonna che contiene dati strutturati flessibili, come un piccolo documento.
Tasselli (tiles)
Piccoli quadrati in cui si divide un'immagine enorme, caricati solo quando servono: così la mappa è veloce.
ODbL
La licenza dei dati di OpenStreetMap: si possono usare liberamente citando la fonte.