An interactive map for discovering and preserving LGBTQ+ third places β bars, cafes, libraries, community centers, and historical sites that serve as vital gathering spaces for queer communities.
Every place submission is recorded on the Solana blockchain for transparent, tamper-proof preservation. Community upvotes drive a safety score, and an AI-powered guide (Gemini) helps users explore movements, figures, and history.
- Interactive Map β Browse LGBTQ+ places on a Mapbox-powered map with clustering, category-colored markers, and 3D building view
- Place Submissions β Submit new places with descriptions, historical events, related figures, movement tags, and community labels
- Solana Verification β Every submission and upvote is recorded as a memo transaction on Solana Devnet
- Safety Heatmap β Toggle a heatmap layer showing community-derived safety scores across regions
- AI Guide β Chat with a Gemini-powered assistant to explore LGBTQ+ history, find places, discover figures, and browse timelines
- Search & Filter β Filter by category (bar, cafe, library, etc.), type (current/historical), and sort by upvotes, safety, or distance
- Featured Places β Randomized featured section highlighting notable locations
- Mobile-Responsive β Full mobile drawer with tabbed navigation for all features
| Layer | Technology |
|---|---|
| Frontend | React 19, TypeScript, Vite, TailwindCSS 4 |
| Routing | TanStack Router |
| State | Zustand |
| Map | Mapbox GL JS, Supercluster |
| Animations | Framer Motion |
| AI Chat | Vercel AI SDK + Google Gemini |
| Backend | Python, Flask, Gunicorn |
| Database | MongoDB 7 (MongoEngine ODM) |
| Cache | Redis 7 |
| Blockchain | Solana (SPL Memo Program) |
| Containers | Docker Compose |
- Docker and Docker Compose
- Solana CLI (for keypair generation)
git clone --recurse-submodules https://github.com/your-org/qwermap.git
cd qwermapThe frontend lives in the
qwermap-uigit submodule. The--recurse-submodulesflag ensures it's pulled automatically.
mkdir -p keys
solana-keygen new --outfile ./keys/devnet.json --no-bip39-passphrase
solana airdrop 2 --keypair ./keys/devnet.json --url devnetThis creates a devnet wallet used to sign place submissions and upvotes on-chain.
cp .env.example .envEdit .env and fill in:
| Variable | Where to get it |
|---|---|
VITE_MAPBOX_TOKEN |
Mapbox Access Tokens |
VITE_GOOGLE_GENERATIVE_AI_API_KEY |
Google AI Studio |
docker compose up --buildThis starts five services:
| Service | URL | Description |
|---|---|---|
| ui | http://localhost:3000 | React frontend |
| api | http://localhost:8000/v1 | Flask REST API |
| mongo | localhost:27017 | MongoDB database |
| redis | localhost:6379 | Rate limiting cache |
| mongo-seed | (runs once) | Seeds 18 demo places around LA |
- Open http://localhost:3000 β you should see markers around West Hollywood / LA
- Click a marker to view place details, events timeline, and on-chain transaction ID
- Submit a new place via the Add tab β it records a Solana transaction
- Upvote a place β a toast shows the Solana transaction ID
- Toggle the Safety Map button in the header to see the heatmap overlay
qwermap/
βββ backend/ # Python Flask API
β βββ app.py # Flask entry point
β βββ config.py # Environment config
β βββ db.py # MongoDB connection
β βββ models.py # MongoEngine document models
β βββ seed.py # Database seeder (18 LA places)
β βββ routes/
β β βββ places.py # GET/POST /v1/places
β β βββ interactions.py # POST /v1/places/:id/upvote
β β βββ safety.py # GET /v1/safety-scores
β β βββ moderation.py # Moderation queue endpoints
β βββ services/
β β βββ solana_service.py # Solana transaction signing
β β βββ rate_limit.py # Redis-based rate limiting
β βββ utils/
β βββ validation.py # Input validation & enums
β βββ errors.py # Error response formatting
βββ qwermap-ui/ # React frontend (git submodule)
β βββ src/
β β βββ components/ # React components
β β βββ routes/ # TanStack Router pages
β β βββ store/ # Zustand state stores
β β βββ hooks/ # Custom React hooks
β β βββ api/ # API client layer
β β βββ types/ # TypeScript types
β β βββ styles.css # Theme & global styles
β βββ Dockerfile
βββ keys/ # Solana keypair (gitignored)
βββ docker-compose.yml # Full-stack orchestration
βββ backend-spec.yaml # OpenAPI 3.0.3 specification
βββ .env.example # Environment variable template
| Method | Endpoint | Description |
|---|---|---|
GET |
/v1/places?lat=&lon= |
Get places near coordinates |
GET |
/v1/places/:id |
Get place details |
POST |
/v1/places |
Submit a new place (Solana-backed) |
POST |
/v1/places/:id/upvote |
Upvote a place (Solana-backed) |
GET |
/v1/safety-scores?lat=&lon= |
Aggregated regional safety score |
GET |
/v1/safety-scores/heatmap?lat=&lon= |
Heatmap grid data |
GET |
/v1/moderation/queue |
Pending submissions |
PATCH |
/v1/moderation/places/:id |
Approve/reject a submission |
Full API specification: backend-spec.yaml
If you prefer running services locally:
Backend:
cd backend
pip install -r requirements.txt
cp .env.example .env.local # edit with your local MongoDB/Redis URIs
python app.pyFrontend:
cd qwermap-ui
npm install # or: bun install
cp .env.example .env.local
npm run dev # starts on http://localhost:3000You'll need MongoDB and Redis running locally (or update the connection strings to point to hosted instances).
| Problem | Solution |
|---|---|
| Seed data didn't load | docker compose logs mongo-seed |
| API errors | docker compose logs api |
| Solana transaction failures | Check balance: solana balance --keypair ./keys/devnet.json --url devnet |
| Map doesn't render | Verify VITE_MAPBOX_TOKEN is set in .env |
| AI chat not responding | Verify VITE_GOOGLE_GENERATIVE_AI_API_KEY is set in .env |
| Port conflicts | Stop other services on ports 3000, 8000, 27017, or 6379 |
MIT
