Skip to content

Commit 52f5d9a

Browse files
better
1 parent 09d2164 commit 52f5d9a

7 files changed

Lines changed: 607 additions & 46 deletions

File tree

dist/index.html

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,15 @@
66
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
77
<title>🗺️ MapPornCircleJerk - Create Cursed Maps</title>
88
<meta name="description" content="Color countries however you want and create the most cursed maps the internet has ever seen! No rules, just chaos! 🎨" />
9-
<script type="module" crossorigin src="/mapporncirclejerk/assets/index-6585bf59.js"></script>
10-
<link rel="stylesheet" href="/mapporncirclejerk/assets/index-61e42d00.css">
9+
<script type="module" crossorigin src="/assets/index-15b4ed19.js"></script>
10+
<link rel="stylesheet" href="/assets/index-a2b07217.css">
1111
</head>
1212
<body>
1313
<div id="app">
1414
<div id="sidebar"></div>
1515
<div id="map"></div>
1616
</div>
1717

18+
<script defer src="http://cloud.umami.is/script.js" data-website-id="3b3aa24f-7565-42d9-b4f2-f4471bc7039c"></script>
1819
</body>
1920
</html>

src/map/MapManager.ts

Lines changed: 135 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ export class MapManager {
88
private map: L.Map;
99
private state: AppState;
1010
private layerMap: Map<string, L.Layer> = new Map();
11+
private svgOverlay: SVGSVGElement | null = null;
12+
private flagPatterns: Map<string, string> = new Map();
1113

1214
constructor(containerId: string, state: AppState) {
1315
this.state = state;
@@ -20,14 +22,17 @@ export class MapManager {
2022
maxZoom: DEFAULT_CONFIG.maxZoom,
2123
zoomControl: true,
2224
worldCopyJump: false, // Prevent world wrapping
23-
maxBounds: [[-90, -180], [90, 180]], // Constrain to single world view
25+
maxBounds: [[-85, -180], [85, 180]], // Constrain to valid tile bounds
2426
maxBoundsViscosity: 1.0, // Make bounds hard limit
2527
});
2628

27-
L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
29+
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
2830
attribution: '© OpenStreetMap contributors',
2931
crossOrigin: 'anonymous',
30-
noWrap: true
32+
noWrap: true,
33+
bounds: [[-85, -180], [85, 180]], // Limit tile loading to valid range
34+
minZoom: DEFAULT_CONFIG.minZoom,
35+
maxZoom: DEFAULT_CONFIG.maxZoom,
3136
}).addTo(this.map);
3237

3338
// Add scale control
@@ -135,6 +140,9 @@ export class MapManager {
135140
}
136141
});
137142

143+
// Initialize SVG overlay for flag patterns
144+
this.initializeSVGOverlay();
145+
138146
// Listen for state changes
139147
this.state.addListener((countryId, _color) => {
140148
if (countryId === '*') {
@@ -146,6 +154,89 @@ export class MapManager {
146154
});
147155
}
148156

157+
private initializeSVGOverlay(): void {
158+
// Get the map's SVG overlay or create one
159+
// const mapContainer = this.map.getContainer();
160+
const panes = (this.map as any)._panes;
161+
162+
// Look for existing SVG in overlay pane
163+
let svgElement = panes.overlayPane.querySelector('svg');
164+
165+
if (!svgElement) {
166+
// If no SVG exists, we'll create patterns in the first GeoJSON layer's SVG
167+
// This will be done when layers are added
168+
return;
169+
}
170+
171+
this.svgOverlay = svgElement;
172+
this.ensureSVGDefs();
173+
}
174+
175+
private ensureSVGDefs(): void {
176+
if (!this.svgOverlay) return;
177+
178+
let defs = this.svgOverlay.querySelector('defs');
179+
if (!defs) {
180+
defs = document.createElementNS('http://www.w3.org/2000/svg', 'defs');
181+
this.svgOverlay.insertBefore(defs, this.svgOverlay.firstChild);
182+
}
183+
}
184+
185+
private createFlagPattern(flagCode: string): string {
186+
const patternId = `flag-pattern-${flagCode.toLowerCase()}`;
187+
188+
// Check if pattern already exists
189+
if (this.flagPatterns.has(flagCode)) {
190+
return this.flagPatterns.get(flagCode)!;
191+
}
192+
193+
// Find or create SVG element with defs
194+
const mapContainer = this.map.getContainer();
195+
const panes = (this.map as any)._panes;
196+
let svgElement = panes.overlayPane.querySelector('svg');
197+
198+
if (!svgElement) {
199+
// Create a hidden SVG element for patterns
200+
svgElement = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
201+
svgElement.style.position = 'absolute';
202+
svgElement.style.width = '0';
203+
svgElement.style.height = '0';
204+
mapContainer.appendChild(svgElement);
205+
}
206+
207+
this.svgOverlay = svgElement;
208+
this.ensureSVGDefs();
209+
210+
const defs = this.svgOverlay!.querySelector('defs')!;
211+
212+
// Check if pattern already exists in DOM
213+
if (defs.querySelector(`#${patternId}`)) {
214+
this.flagPatterns.set(flagCode, patternId);
215+
return patternId;
216+
}
217+
218+
// Create pattern element with repeating tiles
219+
const pattern = document.createElementNS('http://www.w3.org/2000/svg', 'pattern');
220+
pattern.setAttribute('id', patternId);
221+
pattern.setAttribute('patternUnits', 'userSpaceOnUse');
222+
pattern.setAttribute('width', '60'); // Tile size in pixels
223+
pattern.setAttribute('height', '40'); // Tile size in pixels (3:2 flag ratio)
224+
pattern.setAttribute('patternTransform', 'scale(1)');
225+
226+
// Create image element with flag
227+
const image = document.createElementNS('http://www.w3.org/2000/svg', 'image');
228+
image.setAttribute('href', `https://flagcdn.com/w160/${flagCode.toLowerCase()}.png`);
229+
image.setAttribute('width', '60');
230+
image.setAttribute('height', '40');
231+
image.setAttribute('preserveAspectRatio', 'none'); // Stretch to fit tile
232+
233+
pattern.appendChild(image);
234+
defs.appendChild(pattern);
235+
236+
this.flagPatterns.set(flagCode, patternId);
237+
return patternId;
238+
}
239+
149240
loadCountries(geojson: GeoJSON.FeatureCollection<GeoJSON.Geometry, CountryProperties>): void {
150241
// Filter out features with null geometries
151242
const validFeatures = geojson.features.filter(feature => {
@@ -176,6 +267,7 @@ export class MapManager {
176267
id: countryId,
177268
name: props.NAME || props.NAME_LONG || props.ADMIN,
178269
color: null,
270+
flag: null,
179271
feature: feature,
180272
};
181273
this.state.addCountry(countryState);
@@ -202,7 +294,21 @@ export class MapManager {
202294

203295
const countryId = this.getCountryId(feature.properties);
204296
const color = this.state.getCountryColor(countryId);
297+
const flag = this.state.getCountryFlag(countryId);
298+
299+
// If flag is set, use pattern fill
300+
if (flag) {
301+
const patternId = this.createFlagPattern(flag);
302+
return {
303+
fillColor: `url(#${patternId})`,
304+
fillOpacity: 0.9,
305+
color: '#ffffff',
306+
weight: 1,
307+
className: 'flag-filled',
308+
};
309+
}
205310

311+
// Otherwise use color
206312
return {
207313
fillColor: color || '#d3d3d3',
208314
fillOpacity: 0.7,
@@ -264,8 +370,18 @@ export class MapManager {
264370
target.closeTooltip();
265371
},
266372
click: () => {
267-
const selectedColor = this.state.getSelectedColor();
268-
this.state.setCountryColor(countryId, selectedColor);
373+
// Apply color or flag based on paint mode
374+
if (this.state.getPaintMode() === 'color') {
375+
const selectedColor = this.state.getSelectedColor();
376+
this.state.setCountryColor(countryId, selectedColor);
377+
// Clear flag if switching to color
378+
this.state.clearCountryFlag(countryId);
379+
} else {
380+
const selectedFlag = this.state.getSelectedFlag();
381+
this.state.setCountryFlag(countryId, selectedFlag);
382+
// Clear color if switching to flag
383+
this.state.clearCountryColor(countryId);
384+
}
269385

270386
// Save to localStorage after each change
271387
this.state.saveToLocalStorage();
@@ -331,10 +447,22 @@ export class MapManager {
331447
const country = this.state.getCountry(countryId);
332448
if (country) {
333449
const color = this.state.getCountryColor(countryId);
450+
const flag = this.state.getCountryFlag(countryId);
451+
452+
let fillColor = color || '#d3d3d3';
453+
let fillOpacity = 0.7;
454+
455+
// Use flag pattern if flag is set
456+
if (flag) {
457+
const patternId = this.createFlagPattern(flag);
458+
fillColor = `url(#${patternId})`;
459+
fillOpacity = 0.9;
460+
}
461+
334462
const style = {
335463
fill: true,
336-
fillColor: color || '#d3d3d3',
337-
fillOpacity: 0.7,
464+
fillColor: fillColor,
465+
fillOpacity: fillOpacity,
338466
stroke: true,
339467
color: '#ffffff',
340468
weight: 1,

0 commit comments

Comments
 (0)