diff --git a/apps/api/src/geocoding/geocoding-init.service.ts b/apps/api/src/geocoding/geocoding-init.service.ts
index 127f11b..5aee233 100644
--- a/apps/api/src/geocoding/geocoding-init.service.ts
+++ b/apps/api/src/geocoding/geocoding-init.service.ts
@@ -38,17 +38,30 @@ export class GeocodingInitService implements OnModuleInit {
FOR EACH ROW EXECUTE FUNCTION sync_place_location();
`);
- // 1. GIST Geometry Indexes (for fast proximity search)
- await this.repo.query('CREATE INDEX IF NOT EXISTS idx_places_syria_location ON places_syria USING gist (location);');
- await this.repo.query('CREATE INDEX IF NOT EXISTS idx_osm_areas_geom ON osm_areas USING gist (geom);');
- await this.repo.query('CREATE INDEX IF NOT EXISTS idx_osm_points_geom ON osm_points_with_area USING gist (geom);');
+ // 3. Triggers for Jordan and Egypt
+ await this.repo.query(`
+ DROP TRIGGER IF EXISTS trg_sync_place_location_jordan ON places_jordan;
+ CREATE TRIGGER trg_sync_place_location_jordan
+ BEFORE INSERT OR UPDATE ON places_jordan
+ FOR EACH ROW EXECUTE FUNCTION sync_place_location();
+ `);
- // 2. GIST Trigram Indexes (for fast fuzzy name search and low server load)
- await this.repo.query('CREATE INDEX IF NOT EXISTS idx_places_syria_names_trgm ON places_syria USING gist (name_ar gist_trgm_ops, name_en gist_trgm_ops);');
- await this.repo.query('CREATE INDEX IF NOT EXISTS idx_osm_areas_names_trgm ON osm_areas USING gist (name_ar gist_trgm_ops);');
- await this.repo.query('CREATE INDEX IF NOT EXISTS idx_osm_points_names_trgm ON osm_points_with_area USING gist (name_ar gist_trgm_ops, name_en gist_trgm_ops);');
+ await this.repo.query(`
+ DROP TRIGGER IF EXISTS trg_sync_place_location_egypt ON places_egypt;
+ CREATE TRIGGER trg_sync_place_location_egypt
+ BEFORE INSERT OR UPDATE ON places_egypt
+ FOR EACH ROW EXECUTE FUNCTION sync_place_location();
+ `);
- this.logger.log('Geocoding database triggers and optimized indexes initialized.');
+ // 4. GIST Geometry Indexes for Jordan and Egypt
+ await this.repo.query('CREATE INDEX IF NOT EXISTS idx_places_jordan_location ON places_jordan USING gist (location);');
+ await this.repo.query('CREATE INDEX IF NOT EXISTS idx_places_egypt_location ON places_egypt USING gist (location);');
+
+ // 5. GIST Trigram Indexes for Jordan and Egypt
+ await this.repo.query('CREATE INDEX IF NOT EXISTS idx_places_jordan_names_trgm ON places_jordan USING gist (name_ar gist_trgm_ops, name_en gist_trgm_ops);');
+ await this.repo.query('CREATE INDEX IF NOT EXISTS idx_places_egypt_names_trgm ON places_egypt USING gist (name_ar gist_trgm_ops, name_en gist_trgm_ops);');
+
+ this.logger.log('Geocoding database triggers and optimized indexes initialized for Syria, Jordan, and Egypt.');
} catch (err) {
this.logger.error('Failed to initialize database geocoding triggers:', err);
}
diff --git a/apps/api/src/geocoding/geocoding.controller.ts b/apps/api/src/geocoding/geocoding.controller.ts
index e33dbd1..8113cbd 100644
--- a/apps/api/src/geocoding/geocoding.controller.ts
+++ b/apps/api/src/geocoding/geocoding.controller.ts
@@ -1,4 +1,4 @@
-import { Controller, Get, Post, Body, Query, UseGuards } from '@nestjs/common';
+import { Controller, Get, Post, Delete, Body, Query, UseGuards, HttpException, HttpStatus } from '@nestjs/common';
import { ApiTags, ApiOperation, ApiQuery } from '@nestjs/swagger';
import { GeocodingService } from './geocoding.service';
import { ApiKeyGuard } from '../common/guards/api-key.guard';
@@ -15,13 +15,15 @@ export class GeocodingController {
@ApiQuery({ name: 'lat', required: false, type: Number })
@ApiQuery({ name: 'lng', required: false, type: Number })
@ApiQuery({ name: 'radius', required: false, type: Number })
+ @ApiQuery({ name: 'country', required: false, enum: ['jordan', 'syria', 'egypt'] })
async search(
@Query('q') query: string,
@Query('lat') lat?: number,
@Query('lng') lng?: number,
@Query('radius') radius?: number,
+ @Query('country') country?: string,
) {
- return this.geocodingService.searchPlaces(query, lat, lng, radius);
+ return this.geocodingService.searchPlaces(query, lat, lng, radius, country);
}
@Get('reverse')
@@ -38,6 +40,25 @@ export class GeocodingController {
return this.geocodingService.addPlace(placeData);
}
+ @Delete('places')
+ @ApiOperation({ summary: 'Delete a place by name or ID' })
+ @ApiQuery({ name: 'name', required: false })
+ @ApiQuery({ name: 'id', required: false, type: Number })
+ @ApiQuery({ name: 'country', required: true, enum: ['jordan', 'syria', 'egypt'] })
+ async deletePlace(
+ @Query('country') country: string,
+ @Query('name') name?: string,
+ @Query('id') id?: number,
+ ) {
+ if (id) {
+ return this.geocodingService.deletePlaceById(Number(id), country);
+ }
+ if (name) {
+ return this.geocodingService.deletePlacesByName(name, country);
+ }
+ throw new HttpException('Name or ID required', HttpStatus.BAD_REQUEST);
+ }
+
@Post('upsert-place')
@ApiOperation({ summary: 'Add or Update a location (Automated Scraper)' })
async upsertPlace(@Body() placeData: any) {
@@ -55,4 +76,10 @@ export class GeocodingController {
async getPlaces(@Query('limit') limit?: number) {
return this.geocodingService.getRecentPlaces(limit);
}
+
+ @Get('geojson')
+ @ApiOperation({ summary: 'Get all user submitted places as GeoJSON for Map Style' })
+ async getGeoJSON() {
+ return this.geocodingService.getAllPlacesGeoJSON();
+ }
}
diff --git a/apps/api/src/geocoding/geocoding.service.ts b/apps/api/src/geocoding/geocoding.service.ts
index 826ed95..f3a479c 100644
--- a/apps/api/src/geocoding/geocoding.service.ts
+++ b/apps/api/src/geocoding/geocoding.service.ts
@@ -45,44 +45,91 @@ export class GeocodingService {
return 'places_syria';
}
- async searchPlaces(query: string, lat?: number, lon?: number, radius: number = 20000) {
+ async searchPlaces(
+ query: string,
+ lat?: number,
+ lon?: number,
+ radius: number = 20000,
+ country?: string,
+ ) {
try {
const cleanQuery = query.trim();
const hasLocation = lat !== undefined && lon !== undefined;
-
- // If we have no location, we search ALL three tables (Syria, Jordan, Egypt)
- // وإلا فسنقوم بالبحث في المنطقة المناسبة فقط لزيادة السرعة والدقة
- const tables = hasLocation ? [this.getTableNameForRepo(this.getRepositoryForCoords(lat, lon))] : ['places_syria', 'places_jordan', 'places_egypt'];
-
- const allResults: any[] = [];
const ILikeQuery = `%${cleanQuery}%`;
+ const allResults: any[] = [];
+
+ // تحويل المسافة بالامتار الى درجات جغرافية تقريبية للفلترة السريعة
+ const radiusInDegrees = radius / 111000;
- for (const tableName of tables) {
- const repo = tableName === 'places_jordan' ? this.placesJordanRepository :
- tableName === 'places_egypt' ? this.placesEgyptRepository :
- this.placesSyriaRepository;
+ // 1. تحديد المنطقة (الأردن، سوريا، مصر)
+ let targetRegion = country?.toLowerCase();
+
+ if (!targetRegion && hasLocation) {
+ const repo = this.getRepositoryForCoords(lat!, lon!);
+ const tableName = this.getTableNameForRepo(repo);
+ targetRegion = tableName.replace('places_', '');
+ }
- const userPlacesQuery = `
+ const userPointSql = hasLocation ? `ST_SetSRID(ST_MakePoint(${lon}, ${lat}), 4326)` : 'NULL';
+
+ // 2. البحث في جداول المستخدم (Syria, Egypt only)
+ const userTables = targetRegion
+ ? (['syria', 'egypt'].includes(targetRegion) ? [`places_${targetRegion}`] : [])
+ : ['places_syria', 'places_egypt'];
+
+ for (const tableName of userTables) {
+ const repo = tableName === 'places_egypt' ? this.placesEgyptRepository : this.placesSyriaRepository;
+ const userQuery = `
SELECT id, name, name_ar, name_en, category, latitude, longitude, address, 'user_submitted' as source,
- CASE WHEN $2::float IS NOT NULL THEN ST_DistanceSphere(location, ST_SetSRID(ST_MakePoint($3, $2), 4326)) ELSE 0 END as distance,
+ CASE WHEN $2::float IS NOT NULL THEN ST_DistanceSphere(location, ${userPointSql}) ELSE 0 END as distance,
similarity(COALESCE(name_ar, ''), $1) + similarity(COALESCE(name_en, ''), $1) as relevance
FROM ${tableName}
WHERE (name_ar % $1 OR name_en % $1 OR name % $1 OR name_ar ILIKE $4 OR name_en ILIKE $4)
- ${hasLocation ? `AND ST_DistanceSphere(location, ST_SetSRID(ST_MakePoint($3, $2), 4326)) <= $5` : ''}
- ORDER BY distance ASC, relevance DESC LIMIT 10
+ ${hasLocation ? `AND location && ST_Expand(${userPointSql}, $5) AND ST_DistanceSphere(location, ${userPointSql}) <= $6` : ''}
+ ORDER BY relevance DESC, distance ASC LIMIT 15
`;
-
- const params = [cleanQuery, lat || null, lon || null, ILikeQuery, radius];
- const results = await repo.query(userPlacesQuery, params);
+ const results = await repo.query(userQuery, [cleanQuery, lat || null, lon || null, ILikeQuery, radiusInDegrees, radius]);
allResults.push(...results);
}
- // Sort combined results by relevance and distance
- const sortedResults = allResults.sort((a, b) => b.relevance - a.relevance || a.distance - b.distance).slice(0, 15);
+ // 3. البحث في قاعدة بيانات OSM (Jordan and Regional)
+ let osmBoundFilter = '';
+ if (targetRegion === 'jordan') {
+ osmBoundFilter = 'AND ST_Contains(ST_MakeEnvelope(34.5, 29.0, 39.5, 33.5, 4326), geom)';
+ } else if (targetRegion === 'syria') {
+ osmBoundFilter = 'AND ST_Contains(ST_MakeEnvelope(35.5, 32.3, 42.4, 37.5, 4326), geom)';
+ } else if (targetRegion === 'egypt') {
+ osmBoundFilter = 'AND ST_Contains(ST_MakeEnvelope(24.5, 22.0, 37.0, 31.8, 4326), geom)';
+ }
+ const osmQuery = `
+ SELECT osm_id as id, name, name_ar, name_en, COALESCE(amenity, shop, 'place') as category,
+ latitude, longitude, addr_street as address, 'osm_global' as source,
+ CASE WHEN $2::float IS NOT NULL THEN ST_DistanceSphere(geom, ${userPointSql}) ELSE 0 END as distance,
+ similarity(COALESCE(name_ar, ''), $1) + similarity(COALESCE(name_en, ''), $1) as relevance
+ FROM osm_points_with_area
+ WHERE (name_ar % $1 OR name_en % $1 OR name % $1 OR name_ar ILIKE $4 OR name_en ILIKE $4)
+ ${osmBoundFilter}
+ ${hasLocation ? `AND geom && ST_Expand(${userPointSql}, $5) AND ST_DistanceSphere(geom, ${userPointSql}) <= $6` : ''}
+ ORDER BY relevance DESC, distance ASC LIMIT 20
+ `;
+ const osmResults = await this.osmPointsRepository.query(osmQuery, [cleanQuery, lat || null, lon || null, ILikeQuery, radiusInDegrees, radius]);
+ allResults.push(...osmResults);
+
+ // 4. التصفية والترتيب النهائي (الأولوية للمسافة إذا كان الموقع معروفاً)
+ const sortedResults = allResults
+ .sort((a, b) => {
+ if (hasLocation) {
+ return (a.distance - b.distance) || (b.relevance - a.relevance);
+ }
+ return (b.relevance - a.relevance) || (a.distance - b.distance);
+ })
+ .slice(0, 20);
+
+ this.logger.debug(`Spatial Search complete (Sorted by ${hasLocation ? 'Distance' : 'Relevance'}). Results: ${sortedResults.length}`);
return { results: sortedResults };
} catch (e) {
- this.logger.error('Search failed:', e);
+ this.logger.error('Optimized Spatial Search failed:', e);
return { results: [] };
}
}
@@ -190,4 +237,80 @@ export class GeocodingService {
.sort((a, b) => b.created_at.getTime() - a.created_at.getTime())
.slice(0, limit);
}
+
+ async getAllPlacesGeoJSON() {
+ try {
+ const query = `
+ SELECT id, name_ar as name, category, latitude, longitude, address, 'syria' as region
+ FROM places_syria WHERE (name_ar IS NOT NULL OR name IS NOT NULL) AND latitude IS NOT NULL
+ UNION ALL
+ SELECT id, name_ar as name, category, latitude, longitude, address, 'jordan' as region
+ FROM places_jordan WHERE (name_ar IS NOT NULL OR name IS NOT NULL) AND latitude IS NOT NULL
+ UNION ALL
+ SELECT id, name_ar as name, category, latitude, longitude, address, 'egypt' as region
+ FROM places_egypt WHERE (name_ar IS NOT NULL OR name IS NOT NULL) AND latitude IS NOT NULL
+ `;
+ const allResults = await this.placesSyriaRepository.query(query);
+
+ const features = allResults.map(p => ({
+ type: 'Feature',
+ geometry: {
+ type: 'Point',
+ coordinates: [parseFloat(p.longitude), parseFloat(p.latitude)]
+ },
+ properties: {
+ id: p.id,
+ name: p.name,
+ category: p.category,
+ address: p.address,
+ region: p.region,
+ icon: p.category === 'mosque' ? 'mosque' : (p.category === 'hospital' ? 'hospital' : 'marker')
+ }
+ }));
+
+ return {
+ type: 'FeatureCollection',
+ features
+ };
+ } catch (e) {
+ this.logger.error('Failed to generate GeoJSON for all places:', e);
+ return { type: 'FeatureCollection', features: [] };
+ }
+ }
+ async deletePlacesByName(name: string, country: string) {
+ try {
+ const repo = this.getRepoByCountry(country);
+ const result = await repo.delete({ name_ar: name });
+ const resultEn = await repo.delete({ name_en: name });
+ const resultGeneric = await repo.delete({ name: name });
+
+ const totalAffected = (result.affected || 0) + (resultEn.affected || 0) + (resultGeneric.affected || 0);
+ this.logger.debug(`Deleted ${totalAffected} places named "${name}" in ${country}`);
+ return { success: true, affected: totalAffected };
+ } catch (e) {
+ this.logger.error(`Failed to delete places named "${name}" in ${country}:`, e);
+ throw new HttpException('Deletion failed', HttpStatus.INTERNAL_SERVER_ERROR);
+ }
+ }
+
+ async deletePlaceById(id: number, country: string) {
+ try {
+ const repo = this.getRepoByCountry(country);
+ const result = await repo.delete(id);
+ this.logger.debug(`Deleted place ID ${id} in ${country}`);
+ return { success: true, affected: result.affected };
+ } catch (e) {
+ this.logger.error(`Failed to delete place ID ${id} in ${country}:`, e);
+ throw new HttpException('Deletion failed', HttpStatus.INTERNAL_SERVER_ERROR);
+ }
+ }
+
+ private getRepoByCountry(country: string) {
+ switch (country?.toLowerCase()) {
+ case 'syria': return this.placesSyriaRepository;
+ case 'jordan': return this.placesJordanRepository;
+ case 'egypt': return this.placesEgyptRepository;
+ default: throw new HttpException('Invalid country: ' + country, HttpStatus.BAD_REQUEST);
+ }
+ }
}
diff --git a/apps/flutter_map_demo/assets/map-demo.html b/apps/flutter_map_demo/assets/map-demo.html
new file mode 100644
index 0000000..94acb02
--- /dev/null
+++ b/apps/flutter_map_demo/assets/map-demo.html
@@ -0,0 +1,655 @@
+
+
+
+
+
+ Intaleq Premium Map V3 - خرائط انطلاقة الذكية
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
جاري حساب المسار الذكي...
+
+
+
+
+
+

+
INTALEQ PREMIUM MAPS
+
+
+
+
+
v3.1.0
+
🗺️ خرائط انطلاقة الذكية
+
حلول جغرافية متقدمة لمنطقة الشرق الأوسط وشمال أفريقيا — عرض ثلاثي الأبعاد وأسماء مباشرة من قاعدة البيانات.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
جاري تحميل الأسماء من قاعدة البيانات...
+
+
+
+
+
+
+
+
+
+
+
+
+ 🛡️ إدارة البيانات
+ ×
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/apps/flutter_map_demo/assets/style.json b/apps/flutter_map_demo/assets/style.json
index c824d82..2b727ae 100644
--- a/apps/flutter_map_demo/assets/style.json
+++ b/apps/flutter_map_demo/assets/style.json
@@ -1,68 +1,88 @@
{
"version": 8,
- "name": "Intaleq Modern Premium",
- "metadata": {},
- "center": [
- 36.276008,
- 33.513685
- ],
+ "name": "Intaleq Premium Map Style",
+ "metadata": {
+ "brand": "Intaleq",
+ "version": "2.1.0",
+ "description": "Google + OSM hybrid style with 3D buildings, railways, subway, waterways, and Intaleq brand palette. DB names integrated as native OSM labels."
+ },
+ "center": [36.276008, 33.513685],
"zoom": 15,
"glyphs": "https://cdn.protomaps.com/fonts/pbf/{fontstack}/{range}.pbf",
"sprite": "https://demotiles.maplibre.org/styles/osm-bright-gl-style/sprite",
"sources": {
"local-osm-polygons": {
"type": "vector",
- "tiles": [
- "https://tiles.intaleqapp.com/planet_osm_polygon/{z}/{x}/{y}"
- ],
+ "tiles": ["https://tiles.intaleqapp.com/planet_osm_polygon/{z}/{x}/{y}"],
"maxzoom": 14,
- "attribution": "© Intaleq Mapping Solutions"
+ "attribution": "© Intaleq | © OpenStreetMap contributors"
},
"local-osm-lines": {
"type": "vector",
- "tiles": [
- "https://tiles.intaleqapp.com/planet_osm_line/{z}/{x}/{y}"
- ],
+ "tiles": ["https://tiles.intaleqapp.com/planet_osm_line/{z}/{x}/{y}"],
"maxzoom": 14
},
"local-osm-points": {
"type": "vector",
- "tiles": [
- "https://tiles.intaleqapp.com/planet_osm_point/{z}/{x}/{y}"
- ],
+ "tiles": ["https://tiles.intaleqapp.com/planet_osm_point/{z}/{x}/{y}"],
"maxzoom": 14
},
- "user-landmarks": {
+ "places_egypt": {
"type": "vector",
- "tiles": [
- "https://tiles.intaleqapp.com/places_syria/{z}/{x}/{y}"
- ],
+ "tiles": ["https://tiles.intaleqapp.com/places_egypt/{z}/{x}/{y}"],
"maxzoom": 14
+ },
+ "intaleq_dynamic_pois": {
+ "type": "geojson",
+ "data": "https://map-saas.intaleqapp.com/api/geocoding/geojson",
+ "cluster": false
}
},
"layers": [
{
"id": "background",
"type": "background",
- "paint": {
- "background-color": "#f8f9fa"
- }
+ "paint": { "background-color": "#EEF2F7" }
},
{
- "id": "water-layer",
+ "id": "landuse-residential",
"type": "fill",
"source": "local-osm-polygons",
"source-layer": "planet_osm_polygon",
- "filter": [
- "in",
- "natural",
- "water",
- "lake",
- "riverbank"
- ],
- "paint": {
- "fill-color": "#a3ccff"
- }
+ "filter": ["==", "landuse", "residential"],
+ "paint": { "fill-color": "#F0F4F8", "fill-opacity": 1 }
+ },
+ {
+ "id": "landuse-commercial",
+ "type": "fill",
+ "source": "local-osm-polygons",
+ "source-layer": "planet_osm_polygon",
+ "filter": ["==", "landuse", "commercial"],
+ "paint": { "fill-color": "#FAF5EE", "fill-opacity": 1 }
+ },
+ {
+ "id": "landuse-industrial",
+ "type": "fill",
+ "source": "local-osm-polygons",
+ "source-layer": "planet_osm_polygon",
+ "filter": ["in", "landuse", "industrial", "railway"],
+ "paint": { "fill-color": "#E4E8EE", "fill-opacity": 1 }
+ },
+ {
+ "id": "landuse-cemetery",
+ "type": "fill",
+ "source": "local-osm-polygons",
+ "source-layer": "planet_osm_polygon",
+ "filter": ["==", "landuse", "cemetery"],
+ "paint": { "fill-color": "#B8D4BA", "fill-opacity": 0.9 }
+ },
+ {
+ "id": "landuse-military",
+ "type": "fill",
+ "source": "local-osm-polygons",
+ "source-layer": "planet_osm_polygon",
+ "filter": ["==", "landuse", "military"],
+ "paint": { "fill-color": "#E2D9CC", "fill-opacity": 0.8 }
},
{
"id": "park-layer",
@@ -70,67 +90,166 @@
"source": "local-osm-polygons",
"source-layer": "planet_osm_polygon",
"filter": [
- "in",
- "leisure",
- "park",
- "garden",
- "nature_reserve",
- "pitch"
+ "any",
+ ["in", "leisure", "park", "garden", "nature_reserve", "pitch", "playground"],
+ ["in", "landuse", "grass", "meadow", "forest"],
+ ["in", "natural", "wood", "scrub", "heath"]
],
"paint": {
- "fill-color": "#dcedc8"
+ "fill-color": ["match", ["get", "leisure"], "pitch", "#9ED4A0", "playground", "#B8E6B8", "#C5E8C5"],
+ "fill-opacity": 0.85
}
},
{
- "id": "landuse-layer",
+ "id": "park-outline",
+ "type": "line",
+ "source": "local-osm-polygons",
+ "source-layer": "planet_osm_polygon",
+ "filter": ["in", "leisure", "park", "garden", "nature_reserve"],
+ "paint": { "line-color": "#94D4A0", "line-width": 0.8, "line-opacity": 0.7 }
+ },
+ {
+ "id": "water-polygon",
"type": "fill",
"source": "local-osm-polygons",
"source-layer": "planet_osm_polygon",
"filter": [
- "in",
- "landuse",
- "residential",
- "commercial",
- "industrial",
- "cemetery"
+ "any",
+ ["in", "natural", "water", "lake", "bay"],
+ ["==", "waterway", "riverbank"],
+ ["in", "landuse", "basin", "reservoir"],
+ ["==", "amenity", "fountain"]
+ ],
+ "paint": { "fill-color": "#9ECFE8", "fill-opacity": 0.95 }
+ },
+ {
+ "id": "water-polygon-outline",
+ "type": "line",
+ "source": "local-osm-polygons",
+ "source-layer": "planet_osm_polygon",
+ "filter": [
+ "any",
+ ["in", "natural", "water", "lake", "bay"],
+ ["==", "waterway", "riverbank"],
+ ["in", "landuse", "basin", "reservoir"]
+ ],
+ "paint": { "line-color": "#6BB8D8", "line-width": 0.8, "line-opacity": 0.8 }
+ },
+ {
+ "id": "waterway-river",
+ "type": "line",
+ "source": "local-osm-lines",
+ "source-layer": "planet_osm_line",
+ "filter": [
+ "all",
+ ["in", "waterway", "river", "canal"],
+ ["!=", "intermittent", "yes"],
+ ["!=", "seasonal", "yes"],
+ ["!=", "tunnel", "yes"]
],
"paint": {
- "fill-color": [
- "match",
- [
- "get",
- "landuse"
- ],
- "residential",
- "#f1f3f4",
- "commercial",
- "#f8f9fa",
- "industrial",
- "#f1f3f4",
- "cemetery",
- "#dcedc8",
- "#f1f3f4"
- ]
+ "line-color": "#6BB8D8",
+ "line-width": ["interpolate", ["linear"], ["zoom"], 10, 1.5, 14, 4, 16, 7],
+ "line-opacity": 0.95
}
},
{
- "id": "building-3d",
- "type": "fill-extrusion",
+ "id": "waterway-stream-drain",
+ "type": "line",
+ "source": "local-osm-lines",
+ "source-layer": "planet_osm_line",
+ "filter": [
+ "all",
+ ["in", "waterway", "stream", "drain", "ditch"],
+ ["!=", "intermittent", "yes"],
+ ["!=", "seasonal", "yes"],
+ ["!=", "tunnel", "yes"]
+ ],
+ "minzoom": 13,
+ "paint": {
+ "line-color": "#7FBFD8",
+ "line-width": ["interpolate", ["linear"], ["zoom"], 13, 0.8, 16, 2.5],
+ "line-opacity": 0.85
+ }
+ },
+ {
+ "id": "railway-area",
+ "type": "fill",
"source": "local-osm-polygons",
"source-layer": "planet_osm_polygon",
- "minzoom": 15,
+ "filter": ["==", "landuse", "railway"],
+ "paint": { "fill-color": "#DDE2EA", "fill-opacity": 0.9 }
+ },
+ {
+ "id": "railway-rail-casing",
+ "type": "line",
+ "source": "local-osm-lines",
+ "source-layer": "planet_osm_line",
"filter": [
- "has",
- "building"
+ "all",
+ ["in", "railway", "rail", "narrow_gauge", "preserved"],
+ ["!=", "service", "yard"],
+ ["!=", "service", "siding"]
],
- "layout": {
- "visibility": "visible"
- },
+ "minzoom": 8,
"paint": {
- "fill-extrusion-color": "#e8eaed",
- "fill-extrusion-height": 20,
- "fill-extrusion-base": 0,
- "fill-extrusion-opacity": 0.8
+ "line-color": "#B0B8C5",
+ "line-width": ["interpolate", ["linear"], ["zoom"], 8, 2, 12, 4, 16, 8]
+ }
+ },
+ {
+ "id": "railway-rail-core",
+ "type": "line",
+ "source": "local-osm-lines",
+ "source-layer": "planet_osm_line",
+ "filter": [
+ "all",
+ ["in", "railway", "rail", "narrow_gauge", "preserved"],
+ ["!=", "service", "yard"],
+ ["!=", "service", "siding"]
+ ],
+ "minzoom": 8,
+ "paint": {
+ "line-color": "#6B7A8E",
+ "line-width": ["interpolate", ["linear"], ["zoom"], 8, 1, 12, 2.5, 16, 5],
+ "line-dasharray": [6, 4]
+ }
+ },
+ {
+ "id": "railway-subway-casing",
+ "type": "line",
+ "source": "local-osm-lines",
+ "source-layer": "planet_osm_line",
+ "filter": ["in", "railway", "subway", "light_rail", "tram", "monorail"],
+ "minzoom": 10,
+ "paint": {
+ "line-color": ["match", ["get", "railway"], "subway", "#CC2233", "light_rail", "#0066CC", "tram", "#8833BB", "monorail", "#008855", "#BB3344"],
+ "line-width": ["interpolate", ["linear"], ["zoom"], 10, 3, 14, 6, 16, 10]
+ }
+ },
+ {
+ "id": "railway-subway-core",
+ "type": "line",
+ "source": "local-osm-lines",
+ "source-layer": "planet_osm_line",
+ "filter": ["in", "railway", "subway", "light_rail", "tram", "monorail"],
+ "minzoom": 10,
+ "paint": {
+ "line-color": ["match", ["get", "railway"], "subway", "#FF3347", "light_rail", "#2288FF", "tram", "#AA44EE", "monorail", "#00BB66", "#FF4455"],
+ "line-width": ["interpolate", ["linear"], ["zoom"], 10, 1.5, 14, 3.5, 16, 6]
+ }
+ },
+ {
+ "id": "road-casing-track-path",
+ "type": "line",
+ "source": "local-osm-lines",
+ "source-layer": "planet_osm_line",
+ "filter": ["in", "highway", "track", "path", "footway", "cycleway", "steps"],
+ "minzoom": 14,
+ "paint": {
+ "line-color": "#C8CDD6",
+ "line-width": ["interpolate", ["linear"], ["zoom"], 14, 1, 16, 4],
+ "line-dasharray": [4, 3]
}
},
{
@@ -138,32 +257,11 @@
"type": "line",
"source": "local-osm-lines",
"source-layer": "planet_osm_line",
- "filter": [
- "in",
- "highway",
- "residential",
- "service",
- "unclassified",
- "living_street",
- "pedestrian",
- "path",
- "track"
- ],
+ "filter": ["in", "highway", "residential", "service", "unclassified", "living_street", "pedestrian"],
"paint": {
- "line-color": "#d4d4d4",
- "line-width": [
- "interpolate",
- [
- "linear"
- ],
- [
- "zoom"
- ],
- 13,
- 1,
- 16,
- 8
- ]
+ "line-color": "#D4D8DF",
+ "line-width": ["interpolate", ["linear"], ["zoom"], 12, 1.5, 16, 10],
+ "line-opacity": 0.7
}
},
{
@@ -171,32 +269,10 @@
"type": "line",
"source": "local-osm-lines",
"source-layer": "planet_osm_line",
- "filter": [
- "in",
- "highway",
- "residential",
- "service",
- "unclassified",
- "living_street",
- "pedestrian",
- "path",
- "track"
- ],
+ "filter": ["in", "highway", "residential", "service", "unclassified", "living_street", "pedestrian"],
"paint": {
- "line-color": "#ffffff",
- "line-width": [
- "interpolate",
- [
- "linear"
- ],
- [
- "zoom"
- ],
- 13,
- 0.5,
- 16,
- 6
- ]
+ "line-color": "#FFFFFF",
+ "line-width": ["interpolate", ["linear"], ["zoom"], 12, 0.8, 16, 8]
}
},
{
@@ -204,27 +280,11 @@
"type": "line",
"source": "local-osm-lines",
"source-layer": "planet_osm_line",
- "filter": [
- "in",
- "highway",
- "tertiary",
- "tertiary_link"
- ],
+ "filter": ["in", "highway", "tertiary", "tertiary_link"],
"paint": {
- "line-color": "#e0e0e0",
- "line-width": [
- "interpolate",
- [
- "linear"
- ],
- [
- "zoom"
- ],
- 12,
- 1.5,
- 16,
- 12
- ]
+ "line-color": "#C9CED8",
+ "line-width": ["interpolate", ["linear"], ["zoom"], 11, 2, 16, 14],
+ "line-opacity": 0.75
}
},
{
@@ -232,27 +292,10 @@
"type": "line",
"source": "local-osm-lines",
"source-layer": "planet_osm_line",
- "filter": [
- "in",
- "highway",
- "tertiary",
- "tertiary_link"
- ],
+ "filter": ["in", "highway", "tertiary", "tertiary_link"],
"paint": {
- "line-color": "#ffffff",
- "line-width": [
- "interpolate",
- [
- "linear"
- ],
- [
- "zoom"
- ],
- 12,
- 1,
- 16,
- 9
- ]
+ "line-color": "#FFFFFF",
+ "line-width": ["interpolate", ["linear"], ["zoom"], 11, 1.2, 16, 11]
}
},
{
@@ -260,27 +303,11 @@
"type": "line",
"source": "local-osm-lines",
"source-layer": "planet_osm_line",
- "filter": [
- "in",
- "highway",
- "secondary",
- "secondary_link"
- ],
+ "filter": ["in", "highway", "secondary", "secondary_link"],
"paint": {
- "line-color": "#cfd8dc",
- "line-width": [
- "interpolate",
- [
- "linear"
- ],
- [
- "zoom"
- ],
- 12,
- 2,
- 16,
- 14
- ]
+ "line-color": "#C4CFDE",
+ "line-width": ["interpolate", ["linear"], ["zoom"], 11, 2.5, 16, 16],
+ "line-opacity": 0.8
}
},
{
@@ -288,27 +315,10 @@
"type": "line",
"source": "local-osm-lines",
"source-layer": "planet_osm_line",
- "filter": [
- "in",
- "highway",
- "secondary",
- "secondary_link"
- ],
+ "filter": ["in", "highway", "secondary", "secondary_link"],
"paint": {
- "line-color": "#f1f5f9",
- "line-width": [
- "interpolate",
- [
- "linear"
- ],
- [
- "zoom"
- ],
- 12,
- 1.5,
- 16,
- 11
- ]
+ "line-color": "#F8FBFF",
+ "line-width": ["interpolate", ["linear"], ["zoom"], 11, 1.8, 16, 13]
}
},
{
@@ -316,27 +326,11 @@
"type": "line",
"source": "local-osm-lines",
"source-layer": "planet_osm_line",
- "filter": [
- "in",
- "highway",
- "primary",
- "primary_link"
- ],
+ "filter": ["in", "highway", "primary", "primary_link"],
"paint": {
- "line-color": "#facc15",
- "line-width": [
- "interpolate",
- [
- "linear"
- ],
- [
- "zoom"
- ],
- 12,
- 3,
- 16,
- 16
- ]
+ "line-color": "#C8B868",
+ "line-width": ["interpolate", ["linear"], ["zoom"], 10, 3, 16, 18],
+ "line-opacity": 0.7
}
},
{
@@ -344,27 +338,10 @@
"type": "line",
"source": "local-osm-lines",
"source-layer": "planet_osm_line",
- "filter": [
- "in",
- "highway",
- "primary",
- "primary_link"
- ],
+ "filter": ["in", "highway", "primary", "primary_link"],
"paint": {
- "line-color": "#fefce8",
- "line-width": [
- "interpolate",
- [
- "linear"
- ],
- [
- "zoom"
- ],
- 12,
- 2,
- 16,
- 12
- ]
+ "line-color": "#EDD870",
+ "line-width": ["interpolate", ["linear"], ["zoom"], 10, 2, 16, 14]
}
},
{
@@ -372,29 +349,11 @@
"type": "line",
"source": "local-osm-lines",
"source-layer": "planet_osm_line",
- "filter": [
- "in",
- "highway",
- "motorway",
- "motorway_link",
- "trunk",
- "trunk_link"
- ],
+ "filter": ["in", "highway", "motorway", "motorway_link", "trunk", "trunk_link"],
"paint": {
- "line-color": "#fb923c",
- "line-width": [
- "interpolate",
- [
- "linear"
- ],
- [
- "zoom"
- ],
- 12,
- 4,
- 16,
- 18
- ]
+ "line-color": "#C8A84B",
+ "line-width": ["interpolate", ["linear"], ["zoom"], 9, 4, 16, 20],
+ "line-opacity": 0.75
}
},
{
@@ -402,188 +361,427 @@
"type": "line",
"source": "local-osm-lines",
"source-layer": "planet_osm_line",
- "filter": [
- "in",
- "highway",
- "motorway",
- "motorway_link",
- "trunk",
- "trunk_link"
- ],
+ "filter": ["in", "highway", "motorway", "motorway_link", "trunk", "trunk_link"],
"paint": {
- "line-color": "#ffedd5",
- "line-width": [
- "interpolate",
- [
- "linear"
- ],
- [
- "zoom"
- ],
- 12,
- 2.5,
- 16,
- 14
- ]
+ "line-color": "#F0C040",
+ "line-width": ["interpolate", ["linear"], ["zoom"], 9, 2.5, 16, 16]
}
},
{
- "id": "road-labels",
+ "id": "building-fill-flat",
+ "type": "fill",
+ "source": "local-osm-polygons",
+ "source-layer": "planet_osm_polygon",
+ "filter": ["has", "building"],
+ "maxzoom": 14,
+ "paint": {
+ "fill-color": "#DDD8D0",
+ "fill-opacity": 0.85,
+ "fill-outline-color": "#C4BEB4"
+ }
+ },
+ {
+ "id": "building-3d",
+ "type": "fill-extrusion",
+ "source": "local-osm-polygons",
+ "source-layer": "planet_osm_polygon",
+ "minzoom": 14,
+ "filter": ["has", "building"],
+ "paint": {
+ "fill-extrusion-color": [
+ "match", ["get", "building"],
+ "commercial", "#DDD5C5",
+ "retail", "#E5D8C8",
+ "industrial", "#D2D8E0",
+ "church", "#DDD4EE",
+ "mosque", "#CCE4D0",
+ "hospital", "#EDD8D8",
+ "school", "#E0E6CC",
+ "university", "#D8E0C8",
+ "hotel", "#D8DCF0",
+ "apartments", "#E0DCD4",
+ "#DDD8D2"
+ ],
+ "fill-extrusion-height": [
+ "interpolate", ["linear"], ["zoom"],
+ 14, ["*", ["coalesce", ["to-number", ["get", "building:levels"], null], 3], 2.5],
+ 17, ["coalesce",
+ ["to-number", ["get", "height"], null],
+ ["*", ["to-number", ["get", "building:levels"], 3], 3.5],
+ 12
+ ]
+ ],
+ "fill-extrusion-base": ["coalesce", ["to-number", ["get", "min_height"], null], 0],
+ "fill-extrusion-opacity": ["interpolate", ["linear"], ["zoom"], 14, 0.6, 16, 0.88],
+ "fill-extrusion-vertical-gradient": true
+ }
+ },
+ {
+ "id": "railway-label",
"type": "symbol",
"source": "local-osm-lines",
"source-layer": "planet_osm_line",
- "minzoom": 15,
+ "filter": ["in", "railway", "rail", "subway", "light_rail", "tram"],
+ "minzoom": 13,
"layout": {
- "text-field": "{name}",
- "text-font": [
- "Noto Sans Regular"
- ],
- "text-size": 13,
+ "text-field": ["coalesce", ["get", "name:ar"], ["get", "name"], ""],
+ "text-font": ["Noto Sans Regular"],
+ "text-size": 10,
"symbol-placement": "line",
- "text-letter-spacing": 0.05,
- "text-padding": 5,
- "text-allow-overlap": false,
- "text-ignore-placement": false
+ "text-padding": 6,
+ "text-allow-overlap": false
},
"paint": {
- "text-color": "#3c4043",
- "text-halo-color": "rgba(255, 255, 255, 0.8)",
+ "text-color": ["match", ["get", "railway"], "subway", "#CC2233", "light_rail", "#0055BB", "tram", "#7722AA", "#4A5568"],
+ "text-halo-color": "rgba(255,255,255,0.9)",
"text-halo-width": 2
}
},
{
- "id": "place-labels",
+ "id": "waterway-label",
+ "type": "symbol",
+ "source": "local-osm-lines",
+ "source-layer": "planet_osm_line",
+ "filter": [
+ "all",
+ ["in", "waterway", "river", "canal"],
+ ["!=", "intermittent", "yes"],
+ ["has", "name"]
+ ],
+ "minzoom": 12,
+ "layout": {
+ "text-field": ["coalesce", ["get", "name:ar"], ["get", "name"], ""],
+ "text-font": ["Noto Sans Regular"],
+ "text-size": 11,
+ "symbol-placement": "line",
+ "text-letter-spacing": 0.1
+ },
+ "paint": {
+ "text-color": "#2E86AB",
+ "text-halo-color": "rgba(255,255,255,0.85)",
+ "text-halo-width": 2
+ }
+ },
+ {
+ "id": "building-number-polygon",
+ "type": "symbol",
+ "source": "local-osm-polygons",
+ "source-layer": "planet_osm_polygon",
+ "minzoom": 17,
+ "filter": ["has", "addr:housenumber"],
+ "layout": {
+ "text-field": "{addr:housenumber}",
+ "text-font": ["Noto Sans Regular"],
+ "text-size": 10,
+ "text-allow-overlap": false
+ },
+ "paint": {
+ "text-color": "#5A5048",
+ "text-halo-color": "rgba(255,255,255,0.95)",
+ "text-halo-width": 1.5
+ }
+ },
+ {
+ "id": "building-number-point",
+ "type": "symbol",
+ "source": "local-osm-points",
+ "source-layer": "planet_osm_point",
+ "minzoom": 17,
+ "filter": ["has", "addr:housenumber"],
+ "layout": {
+ "text-field": "{addr:housenumber}",
+ "text-font": ["Noto Sans Regular"],
+ "text-size": 10,
+ "text-allow-overlap": false
+ },
+ "paint": {
+ "text-color": "#5A5048",
+ "text-halo-color": "rgba(255,255,255,0.95)",
+ "text-halo-width": 1.5
+ }
+ },
+ {
+ "id": "road-labels-minor",
+ "type": "symbol",
+ "source": "local-osm-lines",
+ "source-layer": "planet_osm_line",
+ "filter": ["in", "highway", "residential", "service", "unclassified", "living_street"],
+ "minzoom": 16,
+ "layout": {
+ "text-field": ["coalesce", ["get", "name:ar"], ["get", "name"], ""],
+ "text-font": ["Noto Sans Regular"],
+ "text-size": 11,
+ "symbol-placement": "line",
+ "text-letter-spacing": 0.04,
+ "text-padding": 4,
+ "text-allow-overlap": false
+ },
+ "paint": {
+ "text-color": "#4A5568",
+ "text-halo-color": "rgba(255,255,255,0.85)",
+ "text-halo-width": 1.5
+ }
+ },
+ {
+ "id": "road-labels-major",
+ "type": "symbol",
+ "source": "local-osm-lines",
+ "source-layer": "planet_osm_line",
+ "filter": ["in", "highway", "primary", "secondary", "tertiary", "motorway", "trunk"],
+ "minzoom": 13,
+ "layout": {
+ "text-field": ["coalesce", ["get", "name:ar"], ["get", "name"], ""],
+ "text-font": ["Noto Sans Regular"],
+ "text-size": ["interpolate", ["linear"], ["zoom"], 13, 11, 16, 14],
+ "symbol-placement": "line",
+ "text-letter-spacing": 0.05,
+ "text-padding": 5,
+ "text-allow-overlap": false
+ },
+ "paint": {
+ "text-color": "#2D3748",
+ "text-halo-color": "rgba(255,255,255,0.9)",
+ "text-halo-width": 2
+ }
+ },
+ {
+ "id": "poi-hospital",
"type": "symbol",
"source": "local-osm-points",
"source-layer": "planet_osm_point",
"minzoom": 13,
+ "filter": ["==", "amenity", "hospital"],
"layout": {
- "text-field": "{name}",
- "text-font": [
- "Noto Sans Regular"
- ],
- "text-size": [
- "interpolate",
- [
- "linear"
- ],
- [
- "zoom"
- ],
- 13,
- 12,
- 16,
- 16
- ],
- "text-offset": [
- 0,
- 1.5
- ],
+ "icon-image": "hospital",
+ "icon-size": 1,
+ "text-field": ["coalesce", ["get", "name:ar"], ["get", "name"], ""],
+ "text-font": ["Noto Sans Regular"],
+ "text-size": 11,
+ "text-offset": [0, 1.2],
"text-anchor": "top",
- "visibility": "visible"
+ "text-allow-overlap": false
},
"paint": {
- "text-color": "#3c4043",
- "text-halo-color": "rgba(255, 255, 255, 0.9)",
+ "text-color": "#C0392B",
+ "text-halo-color": "white",
"text-halo-width": 2
}
},
{
- "id": "water-fill",
- "type": "fill",
- "source": "local-osm-polygons",
- "source-layer": "planet_osm_polygon",
- "filter": ["match", ["get", "natural"], ["water"], true, false],
- "paint": {
- "fill-color": "#bae6fd",
- "fill-opacity": 0.8
- }
- },
- {
- "id": "waterway-line",
- "type": "line",
- "source": "local-osm-lines",
- "source-layer": "planet_osm_line",
- "filter": ["has", "waterway"],
- "paint": {
- "line-color": "#7dd3fc",
- "line-width": ["interpolate", ["linear"], ["zoom"], 12, 1, 16, 4]
- }
- },
- {
- "id": "park-fill",
- "type": "fill",
- "source": "local-osm-polygons",
- "source-layer": "planet_osm_polygon",
- "filter": ["any", ["match", ["get", "leisure"], ["park", "garden", "nature_reserve"], true, false], ["match", ["get", "landuse"], ["forest", "grass", "meadow", "orchard"], true, false]],
- "paint": {
- "fill-color": "#dcfce7",
- "fill-opacity": 0.6
- }
- },
- {
- "id": "railway-line",
- "type": "line",
- "source": "local-osm-lines",
- "source-layer": "planet_osm_line",
- "filter": ["has", "railway"],
- "paint": {
- "line-color": "#94a3b8",
- "line-width": 1.5,
- "line-dasharray": [2, 1]
- }
- },
- {
- "id": "neighborhood-fill",
- "type": "fill",
- "source": "local-osm-polygons",
- "source-layer": "planet_osm_polygon",
- "filter": ["match", ["get", "landuse"], ["residential", "commercial"], true, false],
- "paint": {
- "fill-color": [
- "match",
- ["get", "landuse"],
- "residential", "#f1f5f9",
- "commercial", "#f8fafc",
- "#f1f5f9"
- ],
- "fill-opacity": 0.5
- }
- },
- {
- "id": "road-arrows",
+ "id": "poi-pharmacy",
"type": "symbol",
- "source": "local-osm-lines",
- "source-layer": "planet_osm_line",
- "minzoom": 16,
- "filter": ["==", "oneway", "yes"],
+ "source": "local-osm-points",
+ "source-layer": "planet_osm_point",
+ "minzoom": 15,
+ "filter": ["==", "amenity", "pharmacy"],
"layout": {
- "icon-image": "arrow",
- "icon-size": 0.2,
- "symbol-placement": "line",
- "symbol-spacing": 200,
- "icon-rotate": 90,
- "icon-rotation-alignment": "map"
+ "icon-image": "pharmacy",
+ "icon-size": 0.8,
+ "text-field": ["coalesce", ["get", "name:ar"], ["get", "name"], ""],
+ "text-font": ["Noto Sans Regular"],
+ "text-size": 10,
+ "text-offset": [0, 1.2],
+ "text-anchor": "top"
},
"paint": {
- "icon-opacity": 0.8
+ "text-color": "#1A7A3C",
+ "text-halo-color": "white",
+ "text-halo-width": 1.5
}
},
{
- "id": "user-landmarks-symbol",
+ "id": "poi-place-of-worship",
"type": "symbol",
- "source": "user-landmarks",
- "source-layer": "places_syria",
+ "source": "local-osm-points",
+ "source-layer": "planet_osm_point",
+ "minzoom": 14,
+ "filter": ["==", "amenity", "place_of_worship"],
"layout": {
- "text-field": "{name_ar}",
+ "icon-image": "tourist",
+ "icon-size": 0.8,
+ "text-field": ["coalesce", ["get", "name:ar"], ["get", "name"], ""],
"text-font": ["Noto Sans Regular"],
"text-size": 11,
- "text-offset": [0, 0],
- "text-anchor": "center"
+ "text-offset": [0, 1.2],
+ "text-anchor": "top"
},
"paint": {
- "text-color": "#e11d48",
- "text-halo-color": "#ffffff",
+ "text-color": "#1A6B3A",
+ "text-halo-color": "white",
+ "text-halo-width": 2
+ }
+ },
+ {
+ "id": "poi-restaurant-cafe",
+ "type": "symbol",
+ "source": "local-osm-points",
+ "source-layer": "planet_osm_point",
+ "minzoom": 16,
+ "filter": ["in", "amenity", "restaurant", "cafe", "fast_food"],
+ "layout": {
+ "icon-image": ["match", ["get", "amenity"], "cafe", "cafe", "restaurant"],
+ "icon-size": 0.8,
+ "text-field": ["coalesce", ["get", "name:ar"], ["get", "name"], ""],
+ "text-font": ["Noto Sans Regular"],
+ "text-size": 10,
+ "text-offset": [0, 1.2],
+ "text-anchor": "top"
+ },
+ "paint": {
+ "text-color": "#3D4A5C",
+ "text-halo-color": "white",
+ "text-halo-width": 1.5
+ }
+ },
+ {
+ "id": "poi-school",
+ "type": "symbol",
+ "source": "local-osm-points",
+ "source-layer": "planet_osm_point",
+ "minzoom": 14,
+ "filter": ["in", "amenity", "school", "university", "college"],
+ "layout": {
+ "icon-image": "college",
+ "icon-size": 0.8,
+ "text-field": ["coalesce", ["get", "name:ar"], ["get", "name"], ""],
+ "text-font": ["Noto Sans Regular"],
+ "text-size": 11,
+ "text-offset": [0, 1.2],
+ "text-anchor": "top"
+ },
+ "paint": {
+ "text-color": "#5A4A8A",
+ "text-halo-color": "white",
+ "text-halo-width": 2
+ }
+ },
+ {
+ "id": "poi-transit-station",
+ "type": "symbol",
+ "source": "local-osm-points",
+ "source-layer": "planet_osm_point",
+ "minzoom": 12,
+ "filter": [
+ "any",
+ ["==", "railway", "station"],
+ ["==", "railway", "halt"],
+ ["==", "railway", "tram_stop"],
+ ["==", "station", "subway"],
+ ["==", "amenity", "bus_station"]
+ ],
+ "layout": {
+ "icon-image": "rail",
+ "icon-size": 1,
+ "text-field": ["coalesce", ["get", "name:ar"], ["get", "name"], ""],
+ "text-font": ["Noto Sans Regular"],
+ "text-size": 11,
+ "text-offset": [0, 1.4],
+ "text-anchor": "top",
+ "text-allow-overlap": false
+ },
+ "paint": {
+ "text-color": "#CC2233",
+ "text-halo-color": "rgba(255,255,255,0.95)",
+ "text-halo-width": 2
+ }
+ },
+ {
+ "id": "place-labels-area",
+ "type": "symbol",
+ "source": "local-osm-polygons",
+ "source-layer": "planet_osm_polygon",
+ "minzoom": 10,
+ "filter": ["has", "name"],
+ "layout": {
+ "text-field": ["coalesce", ["get", "name:ar"], ["get", "name"], ""],
+ "text-font": ["Noto Sans Regular"],
+ "text-size": ["interpolate", ["linear"], ["zoom"], 10, 10, 14, 13],
+ "text-padding": 8,
+ "text-allow-overlap": false
+ },
+ "paint": {
+ "text-color": "#34495E",
+ "text-halo-color": "rgba(255,255,255,0.85)",
+ "text-halo-width": 2
+ }
+ },
+ {
+ "id": "place-labels-point",
+ "type": "symbol",
+ "source": "local-osm-points",
+ "source-layer": "planet_osm_point",
+ "minzoom": 10,
+ "filter": [
+ "any",
+ ["in", "place", "city", "town", "village", "suburb", "neighbourhood", "hamlet", "locality", "quarter"],
+ ["in", "natural", "peak", "spring"]
+ ],
+ "layout": {
+ "text-field": ["coalesce", ["get", "name:ar"], ["get", "name"], ""],
+ "text-font": ["Noto Sans Regular"],
+ "text-size": [
+ "interpolate", ["linear"], ["zoom"],
+ 10, ["match", ["get", "place"], "city", 16, "town", 14, 11],
+ 14, ["match", ["get", "place"], "city", 20, "town", 16, 13],
+ 17, 14
+ ],
+ "text-letter-spacing": ["match", ["get", "place"], "city", 0.08, "town", 0.05, 0.02],
+ "text-anchor": "center",
+ "text-padding": 10,
+ "text-allow-overlap": false
+ },
+ "paint": {
+ "text-color": ["match", ["get", "place"],
+ "city", "#1A2740",
+ "town", "#2C3E50",
+ "village", "#3D4F62",
+ "suburb", "#4A5568",
+ "neighbourhood","#556677",
+ "#607080"
+ ],
+ "text-halo-color": "rgba(255,255,255,0.92)",
+ "text-halo-width": ["match", ["get", "place"], "city", 3, "town", 2.5, 2]
+ }
+ },
+ {
+ "id": "places-egypt-labels",
+ "type": "symbol",
+ "source": "places_egypt",
+ "source-layer": "places_egypt",
+ "minzoom": 12,
+ "layout": {
+ "text-field": ["coalesce", ["get", "name_ar"], ["get", "name"], ""],
+ "text-font": ["Noto Sans Bold"],
+ "text-size": ["interpolate", ["linear"], ["zoom"], 12, 9, 16, 13],
+ "text-offset": [0, 1.5],
+ "text-anchor": "top",
+ "text-padding": 8,
+ "text-allow-overlap": false
+ },
+ "paint": {
+ "text-color": "#2D3748",
+ "text-halo-color": "rgba(255,255,255,0.9)",
+ "text-halo-width": 2
+ }
+ },
+ {
+ "id": "intaleq-db-label",
+ "type": "symbol",
+ "source": "intaleq_dynamic_pois",
+ "minzoom": 14,
+ "layout": {
+ "text-field": ["coalesce", ["get", "name_ar"], ["get", "name"], ""],
+ "text-font": ["Noto Sans Bold"],
+ "text-size": ["interpolate", ["linear"], ["zoom"], 14, 11, 17, 14],
+ "text-offset": [0, 1.1],
+ "text-anchor": "top",
+ "text-allow-overlap": false,
+ "text-ignore-placement": false,
+ "text-padding": 4
+ },
+ "paint": {
+ "text-color": "#7A5C10",
+ "text-halo-color": "rgba(255,255,255,0.95)",
"text-halo-width": 2
}
}
diff --git a/apps/web/public/map-demo.html b/apps/web/public/map-demo.html
index 7f9fee6..94acb02 100644
--- a/apps/web/public/map-demo.html
+++ b/apps/web/public/map-demo.html
@@ -4,7 +4,7 @@
Intaleq Premium Map V3 - خرائط انطلاقة الذكية
-
+
@@ -12,275 +12,644 @@
@import url('https://fonts.googleapis.com/css2?family=Outfit:wght@400;600;800&family=Noto+Sans+Arabic:wght@400;700&display=swap');
:root {
- --primary: #c0a048; /* Warm primary from style v3 */
- --primary-dark: #b89038;
- --accent: #2563eb;
- --bg-glass: rgba(255, 255, 255, 0.85);
- --shadow: 0 12px 40px rgba(0,0,0,0.12);
+ --primary: #c0a048;
+ --primary-dark: #8a6e20;
+ --accent: #2563eb;
+ --bg-glass: rgba(255, 255, 255, 0.88);
+ --shadow: 0 12px 40px rgba(0,0,0,0.12);
}
* { margin: 0; padding: 0; box-sizing: border-box; }
- body {
- font-family: 'Outfit', 'Noto Sans Arabic', sans-serif;
- background: #F2EDE4;
- overflow: hidden;
+
+ body {
+ font-family: 'Outfit', 'Noto Sans Arabic', sans-serif;
+ background: #F2EDE4;
+ overflow: hidden;
color: #342C20;
}
- #map { width: 100%; height: 100vh; position: absolute; top: 0; left: 0; }
+ /* ── MAP ── */
+ #map { width: 100vw; height: 100vh; position: absolute; inset: 0; }
+ /* ── LOADING OVERLAY ── */
+ #loading {
+ display: none; position: fixed; inset: 0;
+ background: rgba(242,237,228,0.6);
+ z-index: 2000; align-items: center; justify-content: center;
+ backdrop-filter: blur(6px);
+ }
+ .spinner {
+ width: 40px; height: 40px;
+ border: 4px solid rgba(192,160,72,0.15);
+ border-top: 4px solid var(--primary);
+ border-radius: 50%;
+ animation: spin 0.9s linear infinite;
+ }
+ @keyframes spin { to { transform: rotate(360deg); } }
+
+ /* ── CONTROLS PANEL ── */
.controls {
position: absolute; top: 24px; right: 24px; z-index: 100;
- background: var(--bg-glass);
- backdrop-filter: blur(12px) saturate(180%);
- -webkit-backdrop-filter: blur(12px) saturate(180%);
+ background: var(--bg-glass);
+ backdrop-filter: blur(14px) saturate(180%);
+ -webkit-backdrop-filter: blur(14px) saturate(180%);
border-radius: 24px; padding: 28px; width: 380px;
- box-shadow: var(--shadow);
- border: 1px solid rgba(255, 255, 255, 0.3);
- transition: all 0.3s ease;
- }
-
- .branding {
- position: absolute; bottom: 32px; left: 32px; z-index: 100;
- background: var(--bg-glass);
- backdrop-filter: blur(8px);
- padding: 10px 20px; border-radius: 12px;
- display: flex; align-items: center; gap: 12px;
- box-shadow: 0 8px 24px rgba(0,0,0,0.1);
- border: 1px solid rgba(255, 255, 255, 0.4);
+ box-shadow: var(--shadow);
+ border: 1px solid rgba(255,255,255,0.35);
}
.badge {
display: inline-block; padding: 4px 10px; border-radius: 6px;
- font-size: 11px; font-weight: 800; background: var(--primary); color: white;
+ font-size: 11px; font-weight: 800;
+ background: var(--primary); color: #fff;
margin-bottom: 12px; text-transform: uppercase; letter-spacing: 1px;
}
- h2 { font-size: 22px; font-weight: 800; margin-bottom: 8px; color: #18100A; }
- p { font-size: 14px; color: #584840; margin-bottom: 24px; line-height: 1.5; }
+ .panel-title { font-size: 22px; font-weight: 800; margin-bottom: 6px; color: #18100A; }
+ .panel-desc { font-size: 13px; color: #584840; margin-bottom: 20px; line-height: 1.55; }
- .btn-group { display: flex; flex-direction: column; gap: 12px; }
+ /* ── SEARCH ── */
+ .search-wrap { position: relative; margin-bottom: 20px; }
+ .search-wrap input {
+ width: 100%; padding: 13px 60px 13px 16px;
+ border-radius: 12px; border: 1.5px solid #ddd;
+ font-family: inherit; font-size: 14px;
+ outline: none; transition: border-color 0.25s;
+ background: rgba(255,255,255,0.7);
+ }
+ .search-wrap input:focus { border-color: var(--primary); }
+ .search-wrap .search-btn {
+ position: absolute; left: 8px; top: 50%; transform: translateY(-50%);
+ background: var(--primary); color: #fff;
+ border: none; padding: 6px 13px; border-radius: 8px;
+ cursor: pointer; font-size: 12px; font-weight: 700;
+ transition: background 0.2s;
+ }
+ .search-wrap .search-btn:hover { background: var(--primary-dark); }
+
+ #search-results {
+ display: none; max-height: 220px; overflow-y: auto;
+ background: #fff; border-radius: 12px;
+ border: 1px solid #eee;
+ box-shadow: 0 4px 14px rgba(0,0,0,0.06);
+ margin-top: 8px;
+ }
+ .result-item {
+ padding: 11px 16px; border-bottom: 1px solid #f5f5f5;
+ cursor: pointer; transition: background 0.15s;
+ }
+ .result-item:last-child { border-bottom: none; }
+ .result-item:hover { background: #fdfaf5; }
+ .result-item .name { font-weight: 700; font-size: 13px; }
+ .result-item .meta { font-size: 11px; color: #999; margin-top: 2px; }
+
+ /* ── ROUTE BUTTONS ── */
+ .btn-group { display: flex; flex-direction: column; gap: 10px; }
.btn {
- position: relative; padding: 16px 20px; border: none; border-radius: 16px;
- font-size: 15px; font-weight: 700; cursor: pointer;
- transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
+ padding: 15px 18px; border: none; border-radius: 14px;
+ font-size: 14px; font-weight: 700; cursor: pointer;
display: flex; align-items: center; justify-content: space-between;
- overflow: hidden;
+ transition: transform 0.25s, box-shadow 0.25s;
+ font-family: inherit;
+ }
+ .btn .label-sub { font-size: 11px; font-weight: 400; opacity: 0.75; margin-bottom: 2px; }
+ .btn .icon { font-size: 20px; }
+
+ .btn-gold {
+ background: linear-gradient(135deg, var(--primary) 0%, var(--primary-dark) 100%);
+ color: #fff;
+ box-shadow: 0 4px 16px rgba(192,160,72,0.28);
+ }
+ .btn-gold:hover { transform: translateY(-2px); box-shadow: 0 8px 22px rgba(192,160,72,0.38); }
+
+ .btn-white {
+ background: #fff; color: #342C20;
+ border: 1px solid rgba(0,0,0,0.06);
+ box-shadow: 0 3px 8px rgba(0,0,0,0.04);
+ }
+ .btn-white:hover { background: #fdfaf5; transform: translateY(-2px); }
+
+ /* ── STATUS BOX ── */
+ #status {
+ display: none; margin-top: 20px;
+ background: rgba(253,250,245,0.7);
+ border: 1.5px dashed var(--primary);
+ border-radius: 14px; padding: 16px; font-size: 13px;
+ line-height: 1.65;
+ animation: fadeUp 0.35s ease-out;
+ }
+ .status-row {
+ display: flex; justify-content: space-between; margin-top: 6px;
+ }
+ .status-row span:last-child { font-weight: 800; }
+
+ /* ── DB NAMES INDICATOR ── */
+ #db-status {
+ margin-top: 14px; padding: 10px 14px;
+ background: rgba(192,160,72,0.08);
+ border-radius: 10px; font-size: 12px; color: #7A5C10;
+ display: flex; align-items: center; gap: 8px;
+ }
+ .dot-gold {
+ width: 8px; height: 8px; border-radius: 50%;
+ background: var(--primary); flex-shrink: 0;
+ animation: pulse 2s ease-in-out infinite;
+ }
+ @keyframes pulse { 0%,100%{opacity:1} 50%{opacity:0.4} }
+
+ @keyframes fadeUp {
+ from { opacity: 0; transform: translateY(8px); }
+ to { opacity: 1; transform: translateY(0); }
}
- .btn-primary {
- background: linear-gradient(135deg, var(--primary) 0%, var(--primary-dark) 100%);
- color: white;
- box-shadow: 0 4px 15px rgba(192, 160, 72, 0.3);
+ /* ── BRANDING ── */
+ .branding {
+ position: absolute; bottom: 28px; left: 28px; z-index: 100;
+ background: var(--bg-glass); backdrop-filter: blur(8px);
+ padding: 10px 18px; border-radius: 12px;
+ display: flex; align-items: center; gap: 10px;
+ box-shadow: 0 6px 20px rgba(0,0,0,0.09);
+ border: 1px solid rgba(255,255,255,0.4);
}
- .btn-primary:hover {
- transform: translateY(-3px);
- box-shadow: 0 8px 25px rgba(192, 160, 72, 0.4);
+ .branding span { font-size: 12px; font-weight: 800; color: #18100A; letter-spacing: 0.5px; }
+
+ /* ── CONTEXT MENU ── */
+ #context-menu {
+ display: none; position: fixed; z-index: 3000;
+ background: var(--bg-glass); backdrop-filter: blur(12px);
+ border-radius: 12px; min-width: 180px;
+ box-shadow: 0 8px 30px rgba(0,0,0,0.15);
+ border: 1px solid rgba(255,255,255,0.4);
+ padding: 6px;
+ }
+ .menu-item {
+ padding: 10px 14px; border-radius: 8px; font-size: 13px; font-weight: 700;
+ cursor: pointer; display: flex; align-items: center; gap: 10px;
+ transition: background 0.2s;
+ }
+ .menu-item:hover { background: rgba(192,160,72,0.12); color: var(--primary-dark); }
+ .menu-item .icon { font-size: 16px; }
+
+ /* ── ADMIN PANEL ── */
+ #admin-panel {
+ position: absolute; bottom: 84px; right: 24px; z-index: 100;
+ background: var(--bg-glass); backdrop-filter: blur(14px);
+ border-radius: 20px; padding: 20px; width: 340px;
+ box-shadow: var(--shadow); border: 1px solid rgba(255,255,255,0.3);
+ display: none; animation: fadeUp 0.3s;
+ }
+ .admin-title { font-size: 16px; font-weight: 800; margin-bottom: 12px; display: flex; align-items: center; gap: 8px; }
+ .admin-close { cursor: pointer; opacity: 0.5; margin-right: auto; font-size: 18px; }
+
+ .delete-group { margin-top: 15px; }
+ .delete-group label { display: block; font-size: 11px; font-weight: 700; margin-bottom: 6px; opacity: 0.7; }
+ .delete-group input, .delete-group select {
+ width: 100%; padding: 10px; border-radius: 8px; border: 1.5px solid #eee;
+ font-size: 13px; margin-bottom: 10px; outline: none;
+ }
+ .delete-group input:focus { border-color: #c0392b; }
+ .btn-delete {
+ width: 100%; padding: 10px; background: #c0392b; color: #fff; border: none;
+ border-radius: 10px; font-weight: 800; cursor: pointer;
}
- .btn-secondary {
- background: white;
- color: #342C20;
- border: 1px solid rgba(0,0,0,0.05);
- box-shadow: 0 4px 10px rgba(0,0,0,0.03);
- }
- .btn-secondary:hover {
- background: #fdfaf5;
- transform: translateY(-2px);
- box-shadow: 0 6px 15px rgba(0,0,0,0.05);
+ #toggle-admin {
+ position: absolute; top: 110px; left: 10px; z-index: 100;
+ background: var(--bg-glass); border: none; border-radius: 50%;
+ width: 44px; height: 44px; display: flex; align-items: center; justify-content: center;
+ box-shadow: var(--shadow); cursor: pointer; font-size: 20px;
}
- .btn span.icon { font-size: 18px; }
-
- .info-box {
- background: rgba(253, 250, 245, 0.6);
- border-radius: 16px; padding: 18px; font-size: 14px;
- color: #342C20; margin-top: 24px;
- border: 1px dashed var(--primary);
- line-height: 1.6; display: none;
- animation: fadeIn 0.4s ease-out;
- }
-
- @keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }
-
- #loading {
- display: none; position: fixed; inset: 0;
- background: rgba(242, 237, 228, 0.6);
- z-index: 2000; align-items: center; justify-content: center;
- backdrop-filter: blur(6px);
- }
- .spinner {
- width: 40px; height: 40px; border: 4px solid rgba(192, 160, 72, 0.1);
- border-top: 4px solid var(--primary); border-radius: 50%;
- animation: spin 1s linear infinite;
- }
- @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
-
- /* Custom Scrollbar for better look */
- ::-webkit-scrollbar { width: 6px; }
+ /* ── SCROLLBAR ── */
+ ::-webkit-scrollbar { width: 5px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: var(--primary); border-radius: 10px; }
+
+
-
-
-
جاري حساب المسار الذكي...
+
+
+
جاري حساب المسار الذكي...
+
-

-
INTALEQ PREMIUM MAPS
+

+
INTALEQ PREMIUM MAPS
+
-
Version 3.0.0
-
🗺️ خرائط انطلاقة الذكية
-
حلول جغرافية متقدمة لمنطقة الشرق الأوسط وشمال أفريقيا بدقة متناهية ونظام عرض ثلاثي الأبعاد.
-
+
v3.1.0
+
🗺️ خرائط انطلاقة الذكية
+
حلول جغرافية متقدمة لمنطقة الشرق الأوسط وشمال أفريقيا — عرض ثلاثي الأبعاد وأسماء مباشرة من قاعدة البيانات.
+
+
+
+
+
-