chore: add build artifacts, map assets, and routing data
@@ -44,3 +44,7 @@ PAYMOB_IFRAME_ID=837992
|
|||||||
|
|
||||||
BINANCE_PAY_API_KEY="المفتاح_الخاص_بك_هنا"
|
BINANCE_PAY_API_KEY="المفتاح_الخاص_بك_هنا"
|
||||||
BINANCE_PAY_SECRET_KEY="المفتاح_السري_الخاص_بك_هنا"
|
BINANCE_PAY_SECRET_KEY="المفتاح_السري_الخاص_بك_هنا"
|
||||||
|
|
||||||
|
# Google Gemini AI API Key (For Tactical AI Strategic Advisor & Fuel Pricing Intelligence)
|
||||||
|
GEMINI_API_KEY=
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
# Database Dumps & Archives
|
# Database Dumps & Archives
|
||||||
*.sql
|
*.sql
|
||||||
|
*.tar
|
||||||
*.tar.gz
|
*.tar.gz
|
||||||
*.zip
|
*.zip
|
||||||
*.mbtiles
|
*.mbtiles
|
||||||
|
*.db
|
||||||
|
*.db-*
|
||||||
|
|
||||||
# Node modules and Web build output
|
# Node modules and Web build output
|
||||||
**/node_modules/
|
**/node_modules/
|
||||||
|
|||||||
@@ -13,8 +13,9 @@ Pods/
|
|||||||
*.aab
|
*.aab
|
||||||
*.ipa
|
*.ipa
|
||||||
dem_tiles/
|
dem_tiles/
|
||||||
infrastructure/osm-data/
|
infrastructure/osm-data/*.osm.pbf
|
||||||
osm-data/
|
infrastructure/osm-data/dem_tiles/
|
||||||
|
infrastructure/osm-data/valhalla-work/
|
||||||
venv/
|
venv/
|
||||||
.venv*/
|
.venv*/
|
||||||
dist/
|
dist/
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import { BillingModule } from './billing/billing.module';
|
|||||||
import { MailModule } from './common/mail.module';
|
import { MailModule } from './common/mail.module';
|
||||||
import { WeatherModule } from './weather/weather.module';
|
import { WeatherModule } from './weather/weather.module';
|
||||||
import { TacticalModule } from './tactical/tactical.module';
|
import { TacticalModule } from './tactical/tactical.module';
|
||||||
|
import { TelemetryModule } from './telemetry/telemetry.module';
|
||||||
import { UsageInterceptor } from './usage/usage.interceptor';
|
import { UsageInterceptor } from './usage/usage.interceptor';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
@@ -42,6 +43,7 @@ import { UsageInterceptor } from './usage/usage.interceptor';
|
|||||||
MailModule,
|
MailModule,
|
||||||
WeatherModule,
|
WeatherModule,
|
||||||
TacticalModule,
|
TacticalModule,
|
||||||
|
TelemetryModule,
|
||||||
],
|
],
|
||||||
controllers: [],
|
controllers: [],
|
||||||
providers: [
|
providers: [
|
||||||
|
|||||||
@@ -62,6 +62,6 @@ export abstract class BasePlace {
|
|||||||
@Index()
|
@Index()
|
||||||
neighborhood_id: number;
|
neighborhood_id: number;
|
||||||
|
|
||||||
@Column({ type: 'int', nullable: true })
|
@Column({ type: 'int', default: 0, nullable: true })
|
||||||
elevation_meters: number;
|
elevation_meters: number;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,8 @@ export class GeocodingInitService implements OnModuleInit {
|
|||||||
|
|
||||||
for (const table of tables) {
|
for (const table of tables) {
|
||||||
await this.repo.query(`
|
await this.repo.query(`
|
||||||
ALTER TABLE ${table} ADD COLUMN IF NOT EXISTS elevation_meters INT;
|
ALTER TABLE ${table} ADD COLUMN IF NOT EXISTS elevation_meters INT DEFAULT 0;
|
||||||
|
UPDATE ${table} SET elevation_meters = 0 WHERE elevation_meters IS NULL;
|
||||||
`);
|
`);
|
||||||
for (const mapping of columnMapping) {
|
for (const mapping of columnMapping) {
|
||||||
await this.repo.query(`
|
await this.repo.query(`
|
||||||
@@ -103,7 +104,44 @@ export class GeocodingInitService implements OnModuleInit {
|
|||||||
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);');
|
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);');
|
||||||
await this.repo.query('CREATE INDEX IF NOT EXISTS idx_places_iraq_names_trgm ON places_iraq USING gist (name_ar gist_trgm_ops, name_en gist_trgm_ops);');
|
await this.repo.query('CREATE INDEX IF NOT EXISTS idx_places_iraq_names_trgm ON places_iraq 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, Egypt, and Iraq.');
|
// 6. Tactical Terrain Obstacles Table & Auto-Population
|
||||||
|
await this.repo.query(`
|
||||||
|
CREATE TABLE IF NOT EXISTS tactical_terrain_obstacles (
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
osm_id BIGINT,
|
||||||
|
obstacle_type VARCHAR(64),
|
||||||
|
severity VARCHAR(32),
|
||||||
|
name VARCHAR(255),
|
||||||
|
geometry GEOMETRY(Geometry, 4326),
|
||||||
|
created_at TIMESTAMPTZ DEFAULT NOW()
|
||||||
|
);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_tactical_obs_geom ON tactical_terrain_obstacles USING gist (geometry);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_tactical_obs_type ON tactical_terrain_obstacles (obstacle_type);
|
||||||
|
|
||||||
|
DO $$
|
||||||
|
BEGIN
|
||||||
|
IF to_regclass('public.planet_osm_line') IS NOT NULL THEN
|
||||||
|
INSERT INTO tactical_terrain_obstacles (osm_id, obstacle_type, severity, name, geometry)
|
||||||
|
SELECT
|
||||||
|
osm_id,
|
||||||
|
COALESCE(natural, barrier, man_made, waterway) AS obstacle_type,
|
||||||
|
CASE
|
||||||
|
WHEN natural = 'cliff' THEN 'SEVERE_NO_GO'
|
||||||
|
WHEN barrier = 'retaining_wall' THEN 'RESTRICTED'
|
||||||
|
WHEN barrier IN ('ditch', 'berm') THEN 'TACTICAL_BARRIER'
|
||||||
|
WHEN waterway = 'wadi' THEN 'DRAINAGE_DEFILE'
|
||||||
|
ELSE 'OBSTACLE'
|
||||||
|
END,
|
||||||
|
name,
|
||||||
|
geometry
|
||||||
|
FROM planet_osm_line
|
||||||
|
WHERE (natural IN ('cliff', 'ridge', 'arete') OR barrier IN ('retaining_wall', 'berm', 'ditch') OR waterway IN ('wadi', 'waterfall'))
|
||||||
|
ON CONFLICT DO NOTHING;
|
||||||
|
END IF;
|
||||||
|
END $$;
|
||||||
|
`);
|
||||||
|
|
||||||
|
this.logger.log('Geocoding database triggers, indexes, and tactical obstacles initialized.');
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.logger.error('Failed to initialize database geocoding triggers:', err);
|
this.logger.error('Failed to initialize database geocoding triggers:', err);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -126,8 +126,8 @@ export class GeocodingService {
|
|||||||
WHERE normalized_name % $1
|
WHERE normalized_name % $1
|
||||||
${locationCondition}
|
${locationCondition}
|
||||||
${regionCondition}
|
${regionCondition}
|
||||||
ORDER BY (normalized_name <-> $1) ASC
|
ORDER BY ${hasLocation ? 'distance ASC, (normalized_name <-> $1) ASC' : '(normalized_name <-> $1) ASC'}
|
||||||
LIMIT 50
|
LIMIT 60
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const allResults = await Promise.race([
|
const allResults = await Promise.race([
|
||||||
@@ -219,18 +219,19 @@ export class GeocodingService {
|
|||||||
|
|
||||||
return results
|
return results
|
||||||
.map(r => {
|
.map(r => {
|
||||||
// Weighted scoring:
|
const textScore = Number(r.relevance) || 0;
|
||||||
// 50% Text Match (relevance)
|
|
||||||
// 30% Popularity
|
|
||||||
// 20% Geographic Proximity
|
|
||||||
|
|
||||||
const textScore = Number(r.relevance);
|
|
||||||
const popularityScore = (r.popularity_score || 10) / maxPopularity;
|
const popularityScore = (r.popularity_score || 10) / maxPopularity;
|
||||||
|
|
||||||
// Proximity bonus is 1.0 at 0m, decaying linearly to 0.0 at 10km.
|
// Proximity score: steep inverse decay so closer points get massive boost
|
||||||
const proximityBonus = hasLocation ? Math.max(0, 1 - (Number(r.distance) / 10000)) : 0;
|
// e.g. at 200m -> 0.91, 1km -> 0.67, 5km -> 0.28, 20km -> 0.09
|
||||||
|
const distKm = hasLocation ? (Number(r.distance) / 1000) : 0;
|
||||||
|
const proximityScore = hasLocation ? (1.0 / (1.0 + distKm * 0.5)) : 0;
|
||||||
|
|
||||||
const totalScore = (textScore * 0.5) + (popularityScore * 0.3) + (proximityBonus * 0.2);
|
// When location is available, proximity is heavily prioritized (60%)
|
||||||
|
const totalScore = hasLocation
|
||||||
|
? (proximityScore * 0.60) + (textScore * 0.30) + (popularityScore * 0.10)
|
||||||
|
: (textScore * 0.65) + (popularityScore * 0.35);
|
||||||
|
|
||||||
return { ...r, totalScore };
|
return { ...r, totalScore };
|
||||||
})
|
})
|
||||||
.sort((a, b) => b.totalScore - a.totalScore)
|
.sort((a, b) => b.totalScore - a.totalScore)
|
||||||
@@ -242,7 +243,7 @@ export class GeocodingService {
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
})
|
})
|
||||||
.slice(0, 4)
|
.slice(0, 20)
|
||||||
.map(r => {
|
.map(r => {
|
||||||
const addressParts = [r.neighbourhood, r.district, r.governorate].filter(Boolean);
|
const addressParts = [r.neighbourhood, r.district, r.governorate].filter(Boolean);
|
||||||
const full_address = addressParts.length > 0 ? addressParts.join('، ') : (r.address || '');
|
const full_address = addressParts.length > 0 ? addressParts.join('، ') : (r.address || '');
|
||||||
|
|||||||
@@ -38,8 +38,12 @@ export class MapsController {
|
|||||||
@ApiOperation({ summary: 'Get MapLibre style JSON 🎨' })
|
@ApiOperation({ summary: 'Get MapLibre style JSON 🎨' })
|
||||||
async getStyleJson(@Query('theme') theme: string, @Res() res: Response) {
|
async getStyleJson(@Query('theme') theme: string, @Res() res: Response) {
|
||||||
// Determine filenames based on theme
|
// Determine filenames based on theme
|
||||||
const isDark = theme === 'obsidian';
|
let filename = 'style.json';
|
||||||
const filename = isDark ? 'style-dark.json' : 'style.json';
|
if (theme === 'obsidian') {
|
||||||
|
filename = 'style-dark.json';
|
||||||
|
} else if (theme === 'satellite') {
|
||||||
|
filename = 'style-satellite.json';
|
||||||
|
}
|
||||||
const fallbackFilename = 'style.json';
|
const fallbackFilename = 'style.json';
|
||||||
|
|
||||||
// Paths to check
|
// Paths to check
|
||||||
@@ -47,7 +51,7 @@ export class MapsController {
|
|||||||
path.join('/data', filename),
|
path.join('/data', filename),
|
||||||
path.join(process.cwd(), '../../', filename),
|
path.join(process.cwd(), '../../', filename),
|
||||||
path.join(process.cwd(), filename),
|
path.join(process.cwd(), filename),
|
||||||
// Fallbacks to light style if dark is missing
|
// Fallbacks to light style if specific theme is missing
|
||||||
path.join('/data', fallbackFilename),
|
path.join('/data', fallbackFilename),
|
||||||
path.join(process.cwd(), '../../', fallbackFilename),
|
path.join(process.cwd(), '../../', fallbackFilename),
|
||||||
path.join(process.cwd(), fallbackFilename),
|
path.join(process.cwd(), fallbackFilename),
|
||||||
@@ -69,19 +73,53 @@ export class MapsController {
|
|||||||
const styleRaw = fs.readFileSync(stylePath, 'utf8');
|
const styleRaw = fs.readFileSync(stylePath, 'utf8');
|
||||||
const styleObj = JSON.parse(styleRaw);
|
const styleObj = JSON.parse(styleRaw);
|
||||||
|
|
||||||
// Dynamic Theme support (Safety overrides or fine-tuning)
|
// Dynamic Theme support
|
||||||
if (theme === 'light') {
|
if (theme === 'light') {
|
||||||
styleObj.layers.forEach((layer: any) => {
|
styleObj.layers.forEach((layer: any) => {
|
||||||
if (layer.id === 'background') {
|
if (layer.id === 'background' && layer.paint) {
|
||||||
layer.paint['background-color'] = '#FFFFFF';
|
layer.paint['background-color'] = '#FFFFFF';
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else if (theme === 'obsidian') {
|
} else if (theme === 'obsidian') {
|
||||||
// If we found style-dark.json, we don't strictly need this,
|
|
||||||
// but keeping it as a helper or if it fell back to style.json
|
|
||||||
styleObj.layers.forEach((layer: any) => {
|
styleObj.layers.forEach((layer: any) => {
|
||||||
if (layer.id === 'background') {
|
if (layer.id === 'background' && layer.paint) {
|
||||||
layer.paint['background-color'] = '#101014'; // Dark tone
|
layer.paint['background-color'] = '#101014';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (theme === 'satellite') {
|
||||||
|
// Ensure ESRI Satellite layer is injected if not already present
|
||||||
|
if (!styleObj.sources['esri-satellite']) {
|
||||||
|
styleObj.sources['esri-satellite'] = {
|
||||||
|
type: 'raster',
|
||||||
|
tiles: [
|
||||||
|
'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}',
|
||||||
|
],
|
||||||
|
tileSize: 256,
|
||||||
|
maxzoom: 19,
|
||||||
|
attribution: '© Esri, Maxar, Earthstar Geographics',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (!styleObj.layers.some((l: any) => l.id === 'esri-satellite-imagery')) {
|
||||||
|
const satLayer = {
|
||||||
|
id: 'esri-satellite-imagery',
|
||||||
|
type: 'raster',
|
||||||
|
source: 'esri-satellite',
|
||||||
|
minzoom: 0,
|
||||||
|
maxzoom: 19,
|
||||||
|
paint: { 'raster-opacity': 1.0 },
|
||||||
|
};
|
||||||
|
const bgIdx = styleObj.layers.findIndex((l: any) => l.id === 'background');
|
||||||
|
if (bgIdx >= 0) {
|
||||||
|
styleObj.layers.splice(bgIdx + 1, 0, satLayer);
|
||||||
|
} else {
|
||||||
|
styleObj.layers.unshift(satLayer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
styleObj.layers.forEach((l: any) => {
|
||||||
|
if (l.id === 'background' && l.paint) {
|
||||||
|
l.paint['background-color'] = '#000000';
|
||||||
|
} else if ((l.id.includes('landuse') || l.id.includes('poly')) && l.type === 'fill' && l.paint) {
|
||||||
|
l.paint['fill-opacity'] = 0.05;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,9 +65,11 @@ export class MapsService {
|
|||||||
console.warn('Geocoding internal error during routing:', e);
|
console.warn('Geocoding internal error during routing:', e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ghProfile = ['car', 'foot', 'bike'].includes(profile) ? profile : 'car';
|
||||||
|
|
||||||
const payload: any = {
|
const payload: any = {
|
||||||
points: ghPoints,
|
points: ghPoints,
|
||||||
profile: profile,
|
profile: ghProfile,
|
||||||
locale: locale === 'en' ? 'ar' : locale, // Default to Arabic if not specified or fallback
|
locale: locale === 'en' ? 'ar' : locale, // Default to Arabic if not specified or fallback
|
||||||
calc_points: true,
|
calc_points: true,
|
||||||
points_encoded: false, // JSON arrays for reliable 3D elevation (SRTM)
|
points_encoded: false, // JSON arrays for reliable 3D elevation (SRTM)
|
||||||
|
|||||||
@@ -0,0 +1,82 @@
|
|||||||
|
import * as fs from 'fs';
|
||||||
|
import * as path from 'path';
|
||||||
|
import { Logger } from '@nestjs/common';
|
||||||
|
import { HttpException, HttpStatus } from '@nestjs/common';
|
||||||
|
|
||||||
|
export interface RoutingPackageManifest {
|
||||||
|
packageId: string;
|
||||||
|
version: string;
|
||||||
|
fileName: string;
|
||||||
|
sizeBytes: number;
|
||||||
|
sha256: string;
|
||||||
|
engine: string;
|
||||||
|
elevation: string;
|
||||||
|
bbox?: Record<string, number>;
|
||||||
|
builtAt: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serves the on-device Valhalla routing package (Jordan) built by
|
||||||
|
* infrastructure/scripts/build-valhalla-tiles.sh. The tactical app downloads
|
||||||
|
* this package once and routes fully offline against the real road network
|
||||||
|
* with SRTM elevation — same data the server-side GraphHopper engine uses.
|
||||||
|
*/
|
||||||
|
export class RoutingPackageService {
|
||||||
|
private static readonly logger = new Logger(RoutingPackageService.name);
|
||||||
|
private static readonly PACKAGE_DIR =
|
||||||
|
process.env.ROUTING_PACKAGE_DIR ||
|
||||||
|
(fs.existsSync('/data/infrastructure/osm-data/routing-packages')
|
||||||
|
? '/data/infrastructure/osm-data/routing-packages'
|
||||||
|
: path.join(process.cwd(), 'infrastructure/osm-data/routing-packages'));
|
||||||
|
|
||||||
|
getDirectory(): string {
|
||||||
|
return RoutingPackageService.PACKAGE_DIR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read jordan-routing-manifest.json written by the tile builder.
|
||||||
|
* Returns null when no package has been built yet.
|
||||||
|
*/
|
||||||
|
getManifest(): RoutingPackageManifest | null {
|
||||||
|
const manifestPath = path.join(RoutingPackageService.PACKAGE_DIR, 'jordan-routing-manifest.json');
|
||||||
|
try {
|
||||||
|
if (!fs.existsSync(manifestPath)) return null;
|
||||||
|
const raw = fs.readFileSync(manifestPath, 'utf8');
|
||||||
|
const manifest = JSON.parse(raw) as RoutingPackageManifest;
|
||||||
|
|
||||||
|
// Verify the archive actually exists next to the manifest.
|
||||||
|
const filePath = this.getPackageFilePath(manifest);
|
||||||
|
if (!fs.existsSync(filePath)) return null;
|
||||||
|
|
||||||
|
return manifest;
|
||||||
|
} catch (e) {
|
||||||
|
RoutingPackageService.logger.warn(`Failed to read routing manifest: ${e}`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Require the manifest + file or throw 404 — used before streaming.
|
||||||
|
*/
|
||||||
|
requireManifest(): RoutingPackageManifest {
|
||||||
|
const manifest = this.getManifest();
|
||||||
|
if (!manifest) {
|
||||||
|
throw new HttpException(
|
||||||
|
'Routing package not available. Run infrastructure/scripts/build-valhalla-tiles.sh on the server.',
|
||||||
|
HttpStatus.NOT_FOUND,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return manifest;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPackageFilePath(manifest: RoutingPackageManifest): string {
|
||||||
|
// Never trust fileName blindly: only allow plain names inside the package dir.
|
||||||
|
const safeName = path.basename(manifest.fileName || '');
|
||||||
|
return path.join(RoutingPackageService.PACKAGE_DIR, safeName);
|
||||||
|
}
|
||||||
|
|
||||||
|
createPackageStream(manifest: RoutingPackageManifest): fs.ReadStream {
|
||||||
|
const filePath = this.getPackageFilePath(manifest);
|
||||||
|
return fs.createReadStream(filePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,6 +18,7 @@ import { LineOfSightBodyDto, LineOfSightQueryDto } from './dto/line-of-sight.dto
|
|||||||
import { ArtilleryMissionRequestDto, SaveScenarioDto } from './dto/tactical.dto';
|
import { ArtilleryMissionRequestDto, SaveScenarioDto } from './dto/tactical.dto';
|
||||||
import { TacticalService } from './tactical.service';
|
import { TacticalService } from './tactical.service';
|
||||||
import { DemTileService } from './dem-tile.service';
|
import { DemTileService } from './dem-tile.service';
|
||||||
|
import { RoutingPackageService } from './routing-package.service';
|
||||||
|
|
||||||
@ApiTags('tactical')
|
@ApiTags('tactical')
|
||||||
@ApiHeader({
|
@ApiHeader({
|
||||||
@@ -28,7 +29,10 @@ import { DemTileService } from './dem-tile.service';
|
|||||||
@Controller('tactical')
|
@Controller('tactical')
|
||||||
@UseGuards(ApiKeyGuard, TenantThrottlerGuard)
|
@UseGuards(ApiKeyGuard, TenantThrottlerGuard)
|
||||||
export class TacticalController {
|
export class TacticalController {
|
||||||
constructor(private readonly tacticalService: TacticalService) { }
|
constructor(
|
||||||
|
private readonly tacticalService: TacticalService,
|
||||||
|
private readonly routingPackageService: RoutingPackageService,
|
||||||
|
) { }
|
||||||
|
|
||||||
@Get('verify-license')
|
@Get('verify-license')
|
||||||
@ApiOperation({ summary: 'Verify tactical clearance and military license' })
|
@ApiOperation({ summary: 'Verify tactical clearance and military license' })
|
||||||
@@ -225,6 +229,42 @@ export class TacticalController {
|
|||||||
return this.tacticalService.getOfflinePackageInfo();
|
return this.tacticalService.getOfflinePackageInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Get('routing-package/jordan/manifest')
|
||||||
|
@ApiOperation({
|
||||||
|
summary: 'Manifest of the on-device Valhalla routing package (version, sha256, size)',
|
||||||
|
})
|
||||||
|
getRoutingPackageManifest() {
|
||||||
|
const manifest = this.routingPackageService.getManifest();
|
||||||
|
if (!manifest) {
|
||||||
|
return {
|
||||||
|
available: false,
|
||||||
|
message:
|
||||||
|
'Routing package not built yet. Run infrastructure/scripts/build-valhalla-tiles.sh on the server.',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return { available: true, ...manifest };
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get('routing-package/jordan')
|
||||||
|
@ApiOperation({
|
||||||
|
summary:
|
||||||
|
'Download the Jordan Valhalla routing tar (real road graph + SRTM elevation) for 100% offline on-device routing',
|
||||||
|
})
|
||||||
|
async downloadRoutingPackage(@Res() res: any) {
|
||||||
|
const manifest = this.routingPackageService.requireManifest();
|
||||||
|
const stream = this.routingPackageService.createPackageStream(manifest);
|
||||||
|
|
||||||
|
res.setHeader('Content-Type', 'application/x-tar');
|
||||||
|
res.setHeader('Content-Length', manifest.sizeBytes);
|
||||||
|
res.setHeader(
|
||||||
|
'Content-Disposition',
|
||||||
|
`attachment; filename="${manifest.fileName}"`,
|
||||||
|
);
|
||||||
|
res.setHeader('X-Package-Version', manifest.version);
|
||||||
|
res.setHeader('X-Package-Sha256', manifest.sha256);
|
||||||
|
stream.pipe(res);
|
||||||
|
}
|
||||||
|
|
||||||
@Get('landmarks')
|
@Get('landmarks')
|
||||||
@ApiOperation({
|
@ApiOperation({
|
||||||
summary: 'Get tactical strategic landmarks / استرجاع معالم الأردن البصرية والاستراتيجية للتقاطع الميداني',
|
summary: 'Get tactical strategic landmarks / استرجاع معالم الأردن البصرية والاستراتيجية للتقاطع الميداني',
|
||||||
@@ -236,6 +276,24 @@ export class TacticalController {
|
|||||||
return this.tacticalService.getLandmarks(region, type);
|
return this.tacticalService.getLandmarks(region, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Get('ipb/obstacles')
|
||||||
|
@ApiOperation({
|
||||||
|
summary: 'Query Tactical IPB Obstacles by Bounding Box / استعلام الموانع التكتيكية ضمن نطاق جغرافي',
|
||||||
|
})
|
||||||
|
async getIPBObstacles(
|
||||||
|
@Query('minLat') minLatStr: string,
|
||||||
|
@Query('minLng') minLngStr: string,
|
||||||
|
@Query('maxLat') maxLatStr: string,
|
||||||
|
@Query('maxLng') maxLngStr: string,
|
||||||
|
) {
|
||||||
|
const minLat = parseFloat(minLatStr) || 31.0;
|
||||||
|
const minLng = parseFloat(minLngStr) || 35.0;
|
||||||
|
const maxLat = parseFloat(maxLatStr) || 33.0;
|
||||||
|
const maxLng = parseFloat(maxLngStr) || 37.0;
|
||||||
|
|
||||||
|
return this.tacticalService.getIPBObstacles({ minLat, minLng, maxLat, maxLng });
|
||||||
|
}
|
||||||
|
|
||||||
@Get('dem/:zoom/:x/:y')
|
@Get('dem/:zoom/:x/:y')
|
||||||
@ApiOperation({
|
@ApiOperation({
|
||||||
summary: 'Stream Sovereign Real Satellite DEM Elevation Tile / تقديم بلاطات الارتفاعات السيادية من السيرفر المحلي',
|
summary: 'Stream Sovereign Real Satellite DEM Elevation Tile / تقديم بلاطات الارتفاعات السيادية من السيرفر المحلي',
|
||||||
@@ -257,4 +315,15 @@ export class TacticalController {
|
|||||||
res.setHeader('Cross-Origin-Resource-Policy', 'cross-origin');
|
res.setHeader('Cross-Origin-Resource-Policy', 'cross-origin');
|
||||||
res.send(buffer);
|
res.send(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Post('ai-assessment')
|
||||||
|
@ApiOperation({
|
||||||
|
summary: 'Generate Advanced AI Tactical Assessment using Gemini 1.5 Pro based on comprehensive IPB and Terrain data',
|
||||||
|
})
|
||||||
|
async generateAIAssessment(@Body() body: { ipbData: any; terrainData: any }) {
|
||||||
|
if (!body.ipbData || !body.terrainData) {
|
||||||
|
throw new HttpException('Missing required tactical data (ipbData, terrainData)', HttpStatus.BAD_REQUEST);
|
||||||
|
}
|
||||||
|
return this.tacticalService.generateTacticalAIAssessment(body.ipbData, body.terrainData);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { Module } from '@nestjs/common';
|
|||||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||||
import { TacticalController } from './tactical.controller';
|
import { TacticalController } from './tactical.controller';
|
||||||
import { TacticalService } from './tactical.service';
|
import { TacticalService } from './tactical.service';
|
||||||
|
import { RoutingPackageService } from './routing-package.service';
|
||||||
import { RedisModule } from '../common/redis.module';
|
import { RedisModule } from '../common/redis.module';
|
||||||
import { PlaceJordan } from '../geocoding/entities/place-jordan.entity';
|
import { PlaceJordan } from '../geocoding/entities/place-jordan.entity';
|
||||||
|
|
||||||
@@ -11,7 +12,7 @@ import { PlaceJordan } from '../geocoding/entities/place-jordan.entity';
|
|||||||
TypeOrmModule.forFeature([PlaceJordan]),
|
TypeOrmModule.forFeature([PlaceJordan]),
|
||||||
],
|
],
|
||||||
controllers: [TacticalController],
|
controllers: [TacticalController],
|
||||||
providers: [TacticalService],
|
providers: [TacticalService, RoutingPackageService],
|
||||||
exports: [TacticalService],
|
exports: [TacticalService],
|
||||||
})
|
})
|
||||||
export class TacticalModule {}
|
export class TacticalModule {}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { RedisService } from '../common/redis.service';
|
|||||||
import { ArtilleryMissionRequestDto, TacticalSymbolDto } from './dto/tactical.dto';
|
import { ArtilleryMissionRequestDto, TacticalSymbolDto } from './dto/tactical.dto';
|
||||||
import { getElevationMeters } from '../common/gis.utils';
|
import { getElevationMeters } from '../common/gis.utils';
|
||||||
import { DemTileService } from './dem-tile.service';
|
import { DemTileService } from './dem-tile.service';
|
||||||
|
import { RoutingPackageService } from './routing-package.service';
|
||||||
|
|
||||||
export interface LosPoint {
|
export interface LosPoint {
|
||||||
index: number;
|
index: number;
|
||||||
@@ -82,6 +83,7 @@ export class TacticalService {
|
|||||||
@InjectRepository(PlaceJordan)
|
@InjectRepository(PlaceJordan)
|
||||||
private readonly placeJordanRepo: Repository<PlaceJordan>,
|
private readonly placeJordanRepo: Repository<PlaceJordan>,
|
||||||
private readonly dataSource: DataSource,
|
private readonly dataSource: DataSource,
|
||||||
|
private readonly routingPackageService: RoutingPackageService,
|
||||||
) {
|
) {
|
||||||
this.graphHopperUrl = this.configService.get('GRAPH_HOPPER_URL', 'http://routing:8080');
|
this.graphHopperUrl = this.configService.get('GRAPH_HOPPER_URL', 'http://routing:8080');
|
||||||
}
|
}
|
||||||
@@ -1022,6 +1024,9 @@ export class TacticalService {
|
|||||||
}
|
}
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
|
|
||||||
|
// Real on-device routing package status (Valhalla graph built from OSM + SRTM)
|
||||||
|
const routingManifest = this.routingPackageService?.getManifest?.() ?? null;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
packageId: 'jordan-tactical-offline-v2',
|
packageId: 'jordan-tactical-offline-v2',
|
||||||
name: 'حزمة الأردن التكتيكية الميدانية الكاملة (Off-Grid Sovereign Package)',
|
name: 'حزمة الأردن التكتيكية الميدانية الكاملة (Off-Grid Sovereign Package)',
|
||||||
@@ -1033,6 +1038,21 @@ export class TacticalService {
|
|||||||
sizeFormatted: '825 KB (خفيفة جداً وسريعة التحميل)',
|
sizeFormatted: '825 KB (خفيفة جداً وسريعة التحميل)',
|
||||||
offlineRoutingReady: true,
|
offlineRoutingReady: true,
|
||||||
offlineResectionReady: true,
|
offlineResectionReady: true,
|
||||||
|
routingPackage: routingManifest
|
||||||
|
? {
|
||||||
|
available: true,
|
||||||
|
packageId: routingManifest.packageId,
|
||||||
|
version: routingManifest.version,
|
||||||
|
fileName: routingManifest.fileName,
|
||||||
|
sizeBytes: routingManifest.sizeBytes,
|
||||||
|
sha256: routingManifest.sha256,
|
||||||
|
engine: routingManifest.engine,
|
||||||
|
elevation: routingManifest.elevation,
|
||||||
|
downloadUrl: '/api/tactical/routing-package/jordan',
|
||||||
|
manifestUrl: '/api/tactical/routing-package/jordan/manifest',
|
||||||
|
builtAt: routingManifest.builtAt,
|
||||||
|
}
|
||||||
|
: { available: false },
|
||||||
lastUpdated: new Date().toISOString()
|
lastUpdated: new Date().toISOString()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -1150,4 +1170,154 @@ export class TacticalService {
|
|||||||
timestamp: new Date().toISOString()
|
timestamp: new Date().toISOString()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Tactical IPB Obstacles within Bounding Box
|
||||||
|
*/
|
||||||
|
async getIPBObstacles(bbox: {
|
||||||
|
minLat: number;
|
||||||
|
minLng: number;
|
||||||
|
maxLat: number;
|
||||||
|
maxLng: number;
|
||||||
|
}) {
|
||||||
|
const { minLat, minLng, maxLat, maxLng } = bbox;
|
||||||
|
try {
|
||||||
|
// 1. Query pre-computed / merged tactical_terrain_obstacles table if it exists
|
||||||
|
const tableCheck = await this.dataSource.query(`
|
||||||
|
SELECT to_regclass('public.tactical_terrain_obstacles') as exists;
|
||||||
|
`);
|
||||||
|
|
||||||
|
let features: any[] = [];
|
||||||
|
|
||||||
|
if (tableCheck?.[0]?.exists) {
|
||||||
|
const rows = await this.dataSource.query(
|
||||||
|
`
|
||||||
|
SELECT
|
||||||
|
id,
|
||||||
|
obstacle_type,
|
||||||
|
severity,
|
||||||
|
name,
|
||||||
|
ST_AsGeoJSON(geometry)::json as geojson
|
||||||
|
FROM tactical_terrain_obstacles
|
||||||
|
WHERE geometry && ST_MakeEnvelope($1, $2, $3, $4, 4326)
|
||||||
|
LIMIT 500;
|
||||||
|
`,
|
||||||
|
[minLng, minLat, maxLng, maxLat],
|
||||||
|
);
|
||||||
|
|
||||||
|
features = rows.map((r: any) => ({
|
||||||
|
type: 'Feature',
|
||||||
|
properties: {
|
||||||
|
id: r.id,
|
||||||
|
obstacleType: r.obstacle_type,
|
||||||
|
severity: r.severity,
|
||||||
|
name: r.name,
|
||||||
|
},
|
||||||
|
geometry: r.geojson,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. If tactical_terrain_obstacles was empty, query planet_osm_line directly as fallback
|
||||||
|
if (features.length === 0) {
|
||||||
|
const osmCheck = await this.dataSource.query(`
|
||||||
|
SELECT to_regclass('public.planet_osm_line') as exists;
|
||||||
|
`);
|
||||||
|
|
||||||
|
if (osmCheck?.[0]?.exists) {
|
||||||
|
const rows = await this.dataSource.query(
|
||||||
|
`
|
||||||
|
SELECT
|
||||||
|
osm_id as id,
|
||||||
|
COALESCE(natural, barrier, man_made, waterway) AS obstacle_type,
|
||||||
|
CASE
|
||||||
|
WHEN natural = 'cliff' THEN 'SEVERE_NO_GO'
|
||||||
|
WHEN barrier = 'retaining_wall' THEN 'RESTRICTED'
|
||||||
|
WHEN barrier IN ('ditch', 'berm') THEN 'TACTICAL_BARRIER'
|
||||||
|
WHEN waterway = 'wadi' THEN 'DRAINAGE_DEFILE'
|
||||||
|
ELSE 'OBSTACLE'
|
||||||
|
END as severity,
|
||||||
|
name,
|
||||||
|
ST_AsGeoJSON(geometry)::json as geojson
|
||||||
|
FROM planet_osm_line
|
||||||
|
WHERE geometry && ST_MakeEnvelope($1, $2, $3, $4, 4326)
|
||||||
|
AND (natural IN ('cliff', 'ridge', 'arete') OR barrier IN ('retaining_wall', 'berm', 'ditch') OR waterway IN ('wadi', 'waterfall'))
|
||||||
|
LIMIT 500;
|
||||||
|
`,
|
||||||
|
[minLng, minLat, maxLng, maxLat],
|
||||||
|
);
|
||||||
|
|
||||||
|
features = rows.map((r: any) => ({
|
||||||
|
type: 'Feature',
|
||||||
|
properties: {
|
||||||
|
id: r.id,
|
||||||
|
obstacleType: r.obstacle_type,
|
||||||
|
severity: r.severity,
|
||||||
|
name: r.name,
|
||||||
|
},
|
||||||
|
geometry: r.geojson,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: 'FeatureCollection',
|
||||||
|
count: features.length,
|
||||||
|
features,
|
||||||
|
};
|
||||||
|
} catch (err: any) {
|
||||||
|
this.logger.error(`Error querying IPB obstacles: ${err?.message}`);
|
||||||
|
return {
|
||||||
|
type: 'FeatureCollection',
|
||||||
|
count: 0,
|
||||||
|
features: [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async generateTacticalAIAssessment(ipbData: any, terrainData: any): Promise<any> {
|
||||||
|
const geminiKey = this.configService.get<string>('GEMINI_API_KEY');
|
||||||
|
if (!geminiKey) {
|
||||||
|
throw new Error('GEMINI_API_KEY is not configured on the server.');
|
||||||
|
}
|
||||||
|
|
||||||
|
const prompt = `أنت ضابط ركن استخبارات عسكرية (G2) ومحلل تكتيكي استراتيجي خبير.
|
||||||
|
الرجاء دراسة التقرير التكتيكي المرفق والذي يحتوي على تقدير موقف الاستخبارات عن الأرض (IPB)، الموانع الطبيعية، المقاطع الصخرية، مناطق السكن، والارتفاعات.
|
||||||
|
المعطيات:
|
||||||
|
|
||||||
|
بيانات دراسة الأرض والتضاريس:
|
||||||
|
${JSON.stringify(terrainData, null, 2)}
|
||||||
|
|
||||||
|
بيانات الشفافات التكتيكية (IPB):
|
||||||
|
${JSON.stringify(ipbData, null, 2)}
|
||||||
|
|
||||||
|
المطلوب:
|
||||||
|
بناءً على الأرقام الدقيقة والموقع الجغرافي المعطى، قدم تحليلاً استراتيجياً مفصلاً يشمل:
|
||||||
|
1. التهديدات والفرص التعبوية بناءً على التضاريس.
|
||||||
|
2. أفضل محاور التقدم ومناطق التقتيل (Engagement Areas/Kill Zones).
|
||||||
|
3. تقييم الموانع وتأثيرها على حركة الدروع والمشاة الآلية.
|
||||||
|
4. توصيات لتموضع القوات الصديقة (احتياط، مدفعية، رصد).
|
||||||
|
|
||||||
|
الرجاء كتابة التقرير بلغة عسكرية احترافية وواضحة (باللغة العربية). لا تقم باختراع أرقام، اعتمد كلياً على البيانات المرفقة.`;
|
||||||
|
|
||||||
|
const url = `https://generativelanguage.googleapis.com/v1beta/models/gemini-3.7-flash:generateContent?key=${geminiKey}`;
|
||||||
|
try {
|
||||||
|
this.logger.log('Sending comprehensive tactical data to Gemini 3.7 Flash for analysis...');
|
||||||
|
const response = await axios.post(
|
||||||
|
url,
|
||||||
|
{
|
||||||
|
contents: [{ parts: [{ text: prompt }] }],
|
||||||
|
generationConfig: {
|
||||||
|
temperature: 0.2,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ timeout: 35000 }
|
||||||
|
);
|
||||||
|
|
||||||
|
const content = response.data?.candidates?.[0]?.content?.parts?.[0]?.text;
|
||||||
|
return { success: true, assessment: content };
|
||||||
|
} catch (err: any) {
|
||||||
|
this.logger.error(`Failed to generate AI assessment: ${err.message}`);
|
||||||
|
throw new Error('Failed to generate tactical AI assessment.');
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,61 @@
|
|||||||
|
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
||||||
|
import { IsNotEmpty, IsNumber, IsOptional, IsString, Max, Min, IsArray, ValidateNested } from 'class-validator';
|
||||||
|
import { Type, Transform } from 'class-transformer';
|
||||||
|
|
||||||
|
export class DriverTelemetryDto {
|
||||||
|
@ApiProperty({ description: 'Driver unique ID', example: 'driver_jo_1042' })
|
||||||
|
@IsString()
|
||||||
|
@IsNotEmpty()
|
||||||
|
@Transform(({ obj, value }) => value ?? obj.driver_id ?? obj.driverId)
|
||||||
|
driver_id: string;
|
||||||
|
|
||||||
|
@ApiProperty({ description: 'Latitude coordinate (-90 to 90)', example: 31.9539 })
|
||||||
|
@IsNumber()
|
||||||
|
@Min(-90)
|
||||||
|
@Max(90)
|
||||||
|
@Transform(({ obj, value }) => Number(value ?? obj.latitude ?? obj.lat))
|
||||||
|
latitude: number;
|
||||||
|
|
||||||
|
@ApiProperty({ description: 'Longitude coordinate (-180 to 180)', example: 35.9106 })
|
||||||
|
@IsNumber()
|
||||||
|
@Min(-180)
|
||||||
|
@Max(180)
|
||||||
|
@Transform(({ obj, value }) => Number(value ?? obj.longitude ?? obj.lng))
|
||||||
|
longitude: number;
|
||||||
|
|
||||||
|
@ApiProperty({ description: 'Instantaneous vehicle speed in km/h', example: 45.5 })
|
||||||
|
@IsNumber()
|
||||||
|
@Min(0)
|
||||||
|
@Transform(({ obj, value }) => Number(value ?? obj.speed ?? 0))
|
||||||
|
speed: number;
|
||||||
|
|
||||||
|
@ApiProperty({ description: 'Compass heading / bearing in degrees (0 - 360)', example: 185.0 })
|
||||||
|
@IsNumber()
|
||||||
|
@Min(0)
|
||||||
|
@Max(360)
|
||||||
|
@Transform(({ obj, value }) => Number(value ?? obj.heading ?? 0))
|
||||||
|
heading: number;
|
||||||
|
|
||||||
|
@ApiPropertyOptional({ description: 'Distance traveled in meters', example: 1250.4, default: 0 })
|
||||||
|
@IsOptional()
|
||||||
|
@IsNumber()
|
||||||
|
@Transform(({ obj, value }) => (value != null ? Number(value) : (obj.distance != null ? Number(obj.distance) : 0)))
|
||||||
|
distance?: number;
|
||||||
|
|
||||||
|
@ApiPropertyOptional({ description: 'Elevation above mean sea level in meters (AMSL)', example: 890.5, default: 0 })
|
||||||
|
@IsOptional()
|
||||||
|
@IsNumber()
|
||||||
|
@Transform(({ obj, value }) => {
|
||||||
|
const val = value ?? obj.elevation ?? obj.altitude;
|
||||||
|
return val != null ? Number(val) : 0;
|
||||||
|
})
|
||||||
|
elevation?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DriverTelemetryBatchDto {
|
||||||
|
@ApiProperty({ type: [DriverTelemetryDto], description: 'Array of telemetry points for batch processing' })
|
||||||
|
@IsArray()
|
||||||
|
@ValidateNested({ each: true })
|
||||||
|
@Type(() => DriverTelemetryDto)
|
||||||
|
points: DriverTelemetryDto[];
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
import { Controller, Post, Get, Body, Query, Param, UseGuards, HttpException, HttpStatus } from '@nestjs/common';
|
||||||
|
import { ApiTags, ApiOperation, ApiQuery, ApiParam, ApiSecurity } from '@nestjs/swagger';
|
||||||
|
import { TelemetryService } from './telemetry.service';
|
||||||
|
import { DriverTelemetryDto, DriverTelemetryBatchDto } from './dto/driver-telemetry.dto';
|
||||||
|
import { ApiKeyGuard } from '../common/guards/api-key.guard';
|
||||||
|
|
||||||
|
@ApiTags('telemetry')
|
||||||
|
@ApiSecurity('x-api-key')
|
||||||
|
@Controller('telemetry')
|
||||||
|
@UseGuards(ApiKeyGuard)
|
||||||
|
export class TelemetryController {
|
||||||
|
constructor(private readonly telemetryService: TelemetryService) {}
|
||||||
|
|
||||||
|
@Post()
|
||||||
|
@ApiOperation({
|
||||||
|
summary: 'Ingest real-time driver telemetry with elevation & distance 📡⛰️',
|
||||||
|
description: 'Receives GPS position, speed, heading, distance, and AMSL elevation from driver app.',
|
||||||
|
})
|
||||||
|
async ingest(@Body() data: DriverTelemetryDto) {
|
||||||
|
if (!data.driver_id) {
|
||||||
|
throw new HttpException('Missing driver_id', HttpStatus.BAD_REQUEST);
|
||||||
|
}
|
||||||
|
return this.telemetryService.ingest(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post('batch')
|
||||||
|
@ApiOperation({
|
||||||
|
summary: 'Batch ingest driver telemetry points 📦',
|
||||||
|
description: 'Receives an array of telemetry points for offline-buffered sync or high-frequency traces.',
|
||||||
|
})
|
||||||
|
async ingestBatch(@Body() body: DriverTelemetryBatchDto) {
|
||||||
|
if (!body || !Array.isArray(body.points)) {
|
||||||
|
throw new HttpException('Invalid payload: expected { points: [...] }', HttpStatus.BAD_REQUEST);
|
||||||
|
}
|
||||||
|
return this.telemetryService.ingestBatch(body.points);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get('nearby')
|
||||||
|
@ApiOperation({ summary: 'Query active drivers within spatial radius with elevation & bearing 🚗' })
|
||||||
|
@ApiQuery({ name: 'lat', required: true, type: Number, description: 'Center latitude' })
|
||||||
|
@ApiQuery({ name: 'lng', required: true, type: Number, description: 'Center longitude' })
|
||||||
|
@ApiQuery({ name: 'radius', required: false, type: Number, description: 'Radius in meters (default: 5000m)' })
|
||||||
|
async getNearby(
|
||||||
|
@Query('lat') lat: number,
|
||||||
|
@Query('lng') lng: number,
|
||||||
|
@Query('radius') radius?: number,
|
||||||
|
) {
|
||||||
|
const latNum = Number(lat);
|
||||||
|
const lngNum = Number(lng);
|
||||||
|
if (isNaN(latNum) || isNaN(lngNum)) {
|
||||||
|
throw new HttpException('lat and lng must be valid numbers', HttpStatus.BAD_REQUEST);
|
||||||
|
}
|
||||||
|
return this.telemetryService.getRecentDrivers(latNum, lngNum, radius ? Number(radius) : 5000);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get('driver/:driverId/profile')
|
||||||
|
@ApiOperation({ summary: 'Get 3D elevation profile, climb, and terrain grade for a driver 📈' })
|
||||||
|
@ApiParam({ name: 'driverId', required: true, description: 'Driver unique ID' })
|
||||||
|
@ApiQuery({ name: 'hours', required: false, description: 'Window in hours (default: 24)' })
|
||||||
|
async getDriverElevationProfile(
|
||||||
|
@Param('driverId') driverId: string,
|
||||||
|
@Query('hours') hours?: number,
|
||||||
|
) {
|
||||||
|
return this.telemetryService.getElevationProfile(driverId, hours ? Number(hours) : 24);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
import { Entity, Column, PrimaryGeneratedColumn, Index, CreateDateColumn } from 'typeorm';
|
||||||
|
|
||||||
|
@Entity('telemetry_logs')
|
||||||
|
@Index(['driverId', 'timestamp'], { unique: false })
|
||||||
|
export class TelemetryLog {
|
||||||
|
@PrimaryGeneratedColumn()
|
||||||
|
id: number;
|
||||||
|
|
||||||
|
@Column({ name: 'driverId' })
|
||||||
|
@Index()
|
||||||
|
driverId: string;
|
||||||
|
|
||||||
|
@Column('decimal', { precision: 10, scale: 7 })
|
||||||
|
latitude: number;
|
||||||
|
|
||||||
|
@Column('decimal', { precision: 10, scale: 7 })
|
||||||
|
longitude: number;
|
||||||
|
|
||||||
|
@Column('float', { default: 0 })
|
||||||
|
speed: number;
|
||||||
|
|
||||||
|
@Column('float', { default: 0 })
|
||||||
|
heading: number;
|
||||||
|
|
||||||
|
// Cumulative or step distance traveled in meters (المسافة المقطوعة بالمتر)
|
||||||
|
@Column('float', { default: 0 })
|
||||||
|
distance: number;
|
||||||
|
|
||||||
|
// Elevation above mean sea level in meters (الارتفاع عن مستوى سطح البحر بالمتر AMSL)
|
||||||
|
@Column('float', { default: 0 })
|
||||||
|
elevation: number;
|
||||||
|
|
||||||
|
@CreateDateColumn({ type: 'timestamp with time zone' })
|
||||||
|
@Index()
|
||||||
|
timestamp: Date;
|
||||||
|
|
||||||
|
// PostGIS spatial point for ultra-fast spatial and proximity indexing
|
||||||
|
@Column({
|
||||||
|
type: 'geography',
|
||||||
|
spatialFeatureType: 'Point',
|
||||||
|
srid: 4326,
|
||||||
|
nullable: true,
|
||||||
|
})
|
||||||
|
@Index({ spatial: true })
|
||||||
|
location: any;
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||||
|
import { TelemetryLog } from './telemetry.entity';
|
||||||
|
import { TelemetryService } from './telemetry.service';
|
||||||
|
import { TelemetryController } from './telemetry.controller';
|
||||||
|
import { RedisModule } from '../common/redis.module';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [
|
||||||
|
TypeOrmModule.forFeature([TelemetryLog]),
|
||||||
|
RedisModule,
|
||||||
|
],
|
||||||
|
controllers: [TelemetryController],
|
||||||
|
providers: [TelemetryService],
|
||||||
|
exports: [TelemetryService],
|
||||||
|
})
|
||||||
|
export class TelemetryModule {}
|
||||||
@@ -0,0 +1,122 @@
|
|||||||
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
|
import { getRepositoryToken } from '@nestjs/typeorm';
|
||||||
|
import { DataSource } from 'typeorm';
|
||||||
|
import { TelemetryService } from './telemetry.service';
|
||||||
|
import { TelemetryLog } from './telemetry.entity';
|
||||||
|
import { RedisService } from '../common/redis.service';
|
||||||
|
|
||||||
|
describe('TelemetryService', () => {
|
||||||
|
let service: TelemetryService;
|
||||||
|
let mockRepo: any;
|
||||||
|
let mockDataSource: any;
|
||||||
|
let mockRedis: any;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
mockRepo = {
|
||||||
|
create: jest.fn().mockImplementation((dto) => ({ id: 42, ...dto })),
|
||||||
|
save: jest.fn().mockImplementation((entity) => Promise.resolve({ id: 42, ...entity })),
|
||||||
|
};
|
||||||
|
|
||||||
|
mockDataSource = {
|
||||||
|
query: jest.fn().mockResolvedValue([]),
|
||||||
|
};
|
||||||
|
|
||||||
|
mockRedis = {
|
||||||
|
set: jest.fn().mockResolvedValue(undefined),
|
||||||
|
get: jest.fn().mockResolvedValue(null),
|
||||||
|
};
|
||||||
|
|
||||||
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
|
providers: [
|
||||||
|
TelemetryService,
|
||||||
|
{
|
||||||
|
provide: getRepositoryToken(TelemetryLog),
|
||||||
|
useValue: mockRepo,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: DataSource,
|
||||||
|
useValue: mockDataSource,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: RedisService,
|
||||||
|
useValue: mockRedis,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}).compile();
|
||||||
|
|
||||||
|
service = module.get<TelemetryService>(TelemetryService);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be defined', () => {
|
||||||
|
expect(service).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should ingest telemetry with elevation, distance, speed, and heading', async () => {
|
||||||
|
const payload = {
|
||||||
|
driver_id: 'test_driver_77',
|
||||||
|
latitude: 31.9539,
|
||||||
|
longitude: 35.9106,
|
||||||
|
speed: 60.5,
|
||||||
|
heading: 180.0,
|
||||||
|
distance: 1450.0,
|
||||||
|
elevation: 920.4,
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = await service.ingest(payload);
|
||||||
|
|
||||||
|
expect(result.success).toBe(true);
|
||||||
|
expect(result.driver_id).toBe('test_driver_77');
|
||||||
|
expect(result.elevation).toBe(920.4);
|
||||||
|
expect(result.distance).toBe(1450.0);
|
||||||
|
|
||||||
|
expect(mockRepo.create).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
driverId: 'test_driver_77',
|
||||||
|
latitude: 31.9539,
|
||||||
|
longitude: 35.9106,
|
||||||
|
speed: 60.5,
|
||||||
|
heading: 180.0,
|
||||||
|
distance: 1450.0,
|
||||||
|
elevation: 920.4,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
expect(mockRepo.save).toHaveBeenCalled();
|
||||||
|
expect(mockRedis.set).toHaveBeenCalledWith(
|
||||||
|
'fleet:driver:test_driver_77:live',
|
||||||
|
expect.objectContaining({
|
||||||
|
driverId: 'test_driver_77',
|
||||||
|
elevation: 920.4,
|
||||||
|
distance: 1450.0,
|
||||||
|
}),
|
||||||
|
900,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should batch ingest multiple telemetry points with elevation', async () => {
|
||||||
|
const batch = [
|
||||||
|
{
|
||||||
|
driver_id: 'd1',
|
||||||
|
latitude: 31.95,
|
||||||
|
longitude: 35.91,
|
||||||
|
speed: 50,
|
||||||
|
heading: 90,
|
||||||
|
distance: 100,
|
||||||
|
elevation: 900,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
driver_id: 'd2',
|
||||||
|
latitude: 31.96,
|
||||||
|
longitude: 35.92,
|
||||||
|
speed: 55,
|
||||||
|
heading: 95,
|
||||||
|
distance: 120,
|
||||||
|
elevation: 915,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const res = await service.ingestBatch(batch);
|
||||||
|
expect(res.success).toBe(true);
|
||||||
|
expect(res.count).toBe(2);
|
||||||
|
expect(mockRepo.save).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,278 @@
|
|||||||
|
import { Injectable, Logger, OnModuleInit } from '@nestjs/common';
|
||||||
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
|
import { Repository, DataSource } from 'typeorm';
|
||||||
|
import { TelemetryLog } from './telemetry.entity';
|
||||||
|
import { DriverTelemetryDto } from './dto/driver-telemetry.dto';
|
||||||
|
import { RedisService } from '../common/redis.service';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class TelemetryService implements OnModuleInit {
|
||||||
|
private readonly logger = new Logger(TelemetryService.name);
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
@InjectRepository(TelemetryLog)
|
||||||
|
private readonly telemetryRepo: Repository<TelemetryLog>,
|
||||||
|
private readonly dataSource: DataSource,
|
||||||
|
private readonly redisService: RedisService,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async onModuleInit() {
|
||||||
|
try {
|
||||||
|
// Ensure PostGIS extension and telemetry_logs table columns exist
|
||||||
|
await this.dataSource.query(`
|
||||||
|
CREATE EXTENSION IF NOT EXISTS postgis;
|
||||||
|
CREATE TABLE IF NOT EXISTS telemetry_logs (
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
"driverId" VARCHAR(255) NOT NULL,
|
||||||
|
latitude NUMERIC(10, 7) NOT NULL,
|
||||||
|
longitude NUMERIC(10, 7) NOT NULL,
|
||||||
|
speed FLOAT NOT NULL DEFAULT 0,
|
||||||
|
heading FLOAT NOT NULL DEFAULT 0,
|
||||||
|
distance FLOAT DEFAULT 0,
|
||||||
|
elevation FLOAT DEFAULT 0,
|
||||||
|
timestamp TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||||
|
location GEOGRAPHY(Point, 4326)
|
||||||
|
);
|
||||||
|
ALTER TABLE telemetry_logs ADD COLUMN IF NOT EXISTS distance FLOAT DEFAULT 0;
|
||||||
|
ALTER TABLE telemetry_logs ADD COLUMN IF NOT EXISTS elevation FLOAT DEFAULT 0;
|
||||||
|
CREATE INDEX IF NOT EXISTS telemetry_logs_driver_idx ON telemetry_logs ("driverId");
|
||||||
|
CREATE INDEX IF NOT EXISTS telemetry_logs_timestamp_idx ON telemetry_logs (timestamp DESC);
|
||||||
|
CREATE INDEX IF NOT EXISTS telemetry_logs_location_idx ON telemetry_logs USING GIST (location);
|
||||||
|
`);
|
||||||
|
this.logger.log('✅ Telemetry schema verified: elevation and distance columns ready.');
|
||||||
|
} catch (err: any) {
|
||||||
|
this.logger.warn(`Telemetry DB auto-migration check note: ${err.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ingest a single driver telemetry record including elevation & distance
|
||||||
|
*/
|
||||||
|
async ingest(data: DriverTelemetryDto) {
|
||||||
|
const lat = Number(data.latitude);
|
||||||
|
const lng = Number(data.longitude);
|
||||||
|
const speed = Number(data.speed || 0);
|
||||||
|
const heading = Number(data.heading || 0);
|
||||||
|
const distance = Number(data.distance || 0);
|
||||||
|
const elevation = Number(data.elevation || 0);
|
||||||
|
|
||||||
|
const log = this.telemetryRepo.create({
|
||||||
|
driverId: data.driver_id,
|
||||||
|
latitude: lat,
|
||||||
|
longitude: lng,
|
||||||
|
speed,
|
||||||
|
heading,
|
||||||
|
distance,
|
||||||
|
elevation,
|
||||||
|
timestamp: new Date(),
|
||||||
|
location: {
|
||||||
|
type: 'Point',
|
||||||
|
coordinates: [lng, lat],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const saved = await this.telemetryRepo.save(log);
|
||||||
|
|
||||||
|
// Fast memory caching in Redis for real-time fleet queries (TTL 15 minutes)
|
||||||
|
try {
|
||||||
|
await this.redisService.set(
|
||||||
|
`fleet:driver:${data.driver_id}:live`,
|
||||||
|
{
|
||||||
|
driverId: data.driver_id,
|
||||||
|
latitude: lat,
|
||||||
|
longitude: lng,
|
||||||
|
speed,
|
||||||
|
heading,
|
||||||
|
distance,
|
||||||
|
elevation,
|
||||||
|
updatedAt: new Date().toISOString(),
|
||||||
|
},
|
||||||
|
900,
|
||||||
|
);
|
||||||
|
} catch (_) {
|
||||||
|
// Redis failover - non-blocking
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
id: saved.id,
|
||||||
|
driver_id: data.driver_id,
|
||||||
|
elevation,
|
||||||
|
distance,
|
||||||
|
timestamp: saved.timestamp,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Batch ingest multiple telemetry points
|
||||||
|
*/
|
||||||
|
async ingestBatch(points: DriverTelemetryDto[]) {
|
||||||
|
if (!points || points.length === 0) {
|
||||||
|
return { success: true, count: 0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
const entities = points.map((p) => {
|
||||||
|
const lat = Number(p.latitude);
|
||||||
|
const lng = Number(p.longitude);
|
||||||
|
const speed = Number(p.speed || 0);
|
||||||
|
const heading = Number(p.heading || 0);
|
||||||
|
const distance = Number(p.distance || 0);
|
||||||
|
const elevation = Number(p.elevation || 0);
|
||||||
|
|
||||||
|
return this.telemetryRepo.create({
|
||||||
|
driverId: p.driver_id,
|
||||||
|
latitude: lat,
|
||||||
|
longitude: lng,
|
||||||
|
speed,
|
||||||
|
heading,
|
||||||
|
distance,
|
||||||
|
elevation,
|
||||||
|
timestamp: new Date(),
|
||||||
|
location: {
|
||||||
|
type: 'Point',
|
||||||
|
coordinates: [lng, lat],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
await this.telemetryRepo.save(entities);
|
||||||
|
|
||||||
|
// Update Redis cache for the latest point of each driver
|
||||||
|
try {
|
||||||
|
const latestByDriver = new Map<string, DriverTelemetryDto>();
|
||||||
|
for (const p of points) {
|
||||||
|
latestByDriver.set(p.driver_id, p);
|
||||||
|
}
|
||||||
|
for (const [dId, p] of latestByDriver.entries()) {
|
||||||
|
await this.redisService.set(
|
||||||
|
`fleet:driver:${dId}:live`,
|
||||||
|
{
|
||||||
|
driverId: dId,
|
||||||
|
latitude: Number(p.latitude),
|
||||||
|
longitude: Number(p.longitude),
|
||||||
|
speed: Number(p.speed || 0),
|
||||||
|
heading: Number(p.heading || 0),
|
||||||
|
distance: Number(p.distance || 0),
|
||||||
|
elevation: Number(p.elevation || 0),
|
||||||
|
updatedAt: new Date().toISOString(),
|
||||||
|
},
|
||||||
|
900,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (_) {}
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
count: entities.length,
|
||||||
|
timestamp: new Date(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find nearby active drivers using PostGIS spatial geography search
|
||||||
|
*/
|
||||||
|
async getRecentDrivers(lat: number, lng: number, radiusMeters: number = 5000) {
|
||||||
|
const rows = await this.dataSource.query(
|
||||||
|
`SELECT DISTINCT ON ("driverId")
|
||||||
|
id, "driverId", latitude, longitude, speed, heading, distance, elevation, timestamp,
|
||||||
|
ST_Distance(location, ST_MakePoint($1, $2)::geography) as distance_to_center_meters
|
||||||
|
FROM telemetry_logs
|
||||||
|
WHERE ST_DWithin(location, ST_MakePoint($1, $2)::geography, $3)
|
||||||
|
AND timestamp >= NOW() - INTERVAL '4 hours'
|
||||||
|
ORDER BY "driverId", timestamp DESC
|
||||||
|
LIMIT 100`,
|
||||||
|
[lng, lat, radiusMeters],
|
||||||
|
);
|
||||||
|
|
||||||
|
return rows.map((r: any) => ({
|
||||||
|
driver_id: r.driverId,
|
||||||
|
latitude: parseFloat(r.latitude),
|
||||||
|
longitude: parseFloat(r.longitude),
|
||||||
|
speed: parseFloat(r.speed),
|
||||||
|
heading: parseFloat(r.heading),
|
||||||
|
distance: parseFloat(r.distance || 0),
|
||||||
|
elevation: parseFloat(r.elevation || 0),
|
||||||
|
timestamp: r.timestamp,
|
||||||
|
distance_to_center_meters: parseFloat(r.distance_to_center_meters),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate 3D elevation profile and vertical gradient for a specific driver
|
||||||
|
*/
|
||||||
|
async getElevationProfile(driverId: string, hours: number = 24) {
|
||||||
|
const points = await this.dataSource.query(
|
||||||
|
`SELECT latitude, longitude, speed, heading, distance, elevation, timestamp
|
||||||
|
FROM telemetry_logs
|
||||||
|
WHERE "driverId" = $1
|
||||||
|
AND timestamp >= NOW() - ($2 || ' hours')::interval
|
||||||
|
ORDER BY timestamp ASC`,
|
||||||
|
[driverId, hours],
|
||||||
|
);
|
||||||
|
|
||||||
|
if (points.length === 0) {
|
||||||
|
return {
|
||||||
|
driver_id: driverId,
|
||||||
|
hours,
|
||||||
|
pointsCount: 0,
|
||||||
|
minElevation: 0,
|
||||||
|
maxElevation: 0,
|
||||||
|
avgElevation: 0,
|
||||||
|
totalClimbMeters: 0,
|
||||||
|
totalDescentMeters: 0,
|
||||||
|
maxGradePercent: 0,
|
||||||
|
points: [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let minElev = points[0].elevation || 0;
|
||||||
|
let maxElev = points[0].elevation || 0;
|
||||||
|
let sumElev = 0;
|
||||||
|
let totalClimb = 0;
|
||||||
|
let totalDescent = 0;
|
||||||
|
let maxGrade = 0;
|
||||||
|
|
||||||
|
for (let i = 0; i < points.length; i++) {
|
||||||
|
const elev = parseFloat(points[i].elevation || '0');
|
||||||
|
sumElev += elev;
|
||||||
|
if (elev < minElev) minElev = elev;
|
||||||
|
if (elev > maxElev) maxElev = elev;
|
||||||
|
|
||||||
|
if (i > 0) {
|
||||||
|
const prevElev = parseFloat(points[i - 1].elevation || '0');
|
||||||
|
const diff = elev - prevElev;
|
||||||
|
if (diff > 0) totalClimb += diff;
|
||||||
|
if (diff < 0) totalDescent += Math.abs(diff);
|
||||||
|
|
||||||
|
// Approximate grade percent if distance step is available
|
||||||
|
const stepDist = parseFloat(points[i].distance || '0') - parseFloat(points[i - 1].distance || '0');
|
||||||
|
if (stepDist > 10) {
|
||||||
|
const grade = (Math.abs(diff) / stepDist) * 100;
|
||||||
|
if (grade > maxGrade && grade < 50) {
|
||||||
|
maxGrade = grade;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
driver_id: driverId,
|
||||||
|
hours,
|
||||||
|
pointsCount: points.length,
|
||||||
|
minElevation: Math.round(minElev * 10) / 10,
|
||||||
|
maxElevation: Math.round(maxElev * 10) / 10,
|
||||||
|
avgElevation: Math.round((sumElev / points.length) * 10) / 10,
|
||||||
|
totalClimbMeters: Math.round(totalClimb * 10) / 10,
|
||||||
|
totalDescentMeters: Math.round(totalDescent * 10) / 10,
|
||||||
|
maxGradePercent: Math.round(maxGrade * 10) / 10,
|
||||||
|
recentPoints: points.slice(-30).map((p: any) => ({
|
||||||
|
latitude: parseFloat(p.latitude),
|
||||||
|
longitude: parseFloat(p.longitude),
|
||||||
|
speed: parseFloat(p.speed),
|
||||||
|
heading: parseFloat(p.heading),
|
||||||
|
elevation: parseFloat(p.elevation),
|
||||||
|
timestamp: p.timestamp,
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -611,24 +611,41 @@
|
|||||||
<section id="docs" class="page-section h-full">
|
<section id="docs" class="page-section h-full">
|
||||||
<div class="flex h-full gap-8">
|
<div class="flex h-full gap-8">
|
||||||
<!-- Docs Nav -->
|
<!-- Docs Nav -->
|
||||||
<div class="w-64 flex flex-col gap-2 shrink-0">
|
<div class="w-64 flex flex-col gap-1.5 shrink-0 overflow-y-auto pr-1">
|
||||||
<div class="mb-4">
|
<div class="mb-2">
|
||||||
<h2 class="text-xl font-black text-gradient" data-i18n="guides-title">Guides</h2>
|
<h2 class="text-xs font-black uppercase tracking-wider text-slate-500" data-i18n="docs-overview">Overview</h2>
|
||||||
</div>
|
</div>
|
||||||
<a href="javascript:void(0)" data-section="getting-started" class="docs-nav-link active bg-blue-500/10 text-blue-400 p-4 rounded-2xl text-sm font-bold flex items-center gap-3 transition-all hover:bg-blue-500/5">
|
<a href="javascript:void(0)" data-section="getting-started" class="docs-nav-link active bg-blue-500/10 text-blue-400 p-3.5 rounded-xl text-sm font-bold flex items-center gap-3 transition-all hover:bg-blue-500/5">
|
||||||
<i data-lucide="rocket" class="w-4 h-4"></i> Getting Started
|
<i data-lucide="rocket" class="w-4 h-4 text-blue-400"></i> <span data-i18n="docs-getting-started">Getting Started</span>
|
||||||
</a>
|
</a>
|
||||||
<a href="javascript:void(0)" data-section="tiles-api" class="docs-nav-link p-4 rounded-2xl text-sm font-bold text-slate-500 flex items-center gap-3 transition-all hover:bg-white/5 hover:text-white">
|
|
||||||
<i data-lucide="map" class="w-4 h-4"></i> Map Tiles API
|
<div class="mt-4 mb-2">
|
||||||
|
<h2 class="text-xs font-black uppercase tracking-wider text-slate-500" data-i18n="docs-sdks-title">Client SDKs</h2>
|
||||||
|
</div>
|
||||||
|
<a href="javascript:void(0)" data-section="sdks-ios" class="docs-nav-link p-3.5 rounded-xl text-sm font-bold text-slate-400 flex items-center gap-3 transition-all hover:bg-white/5 hover:text-white">
|
||||||
|
<i data-lucide="apple" class="w-4 h-4 text-slate-300"></i> iOS SDK (Swift)
|
||||||
</a>
|
</a>
|
||||||
<a href="javascript:void(0)" data-section="geocoding-api" class="docs-nav-link p-4 rounded-2xl text-sm font-bold text-slate-500 flex items-center gap-3 transition-all hover:bg-white/5 hover:text-white">
|
<a href="javascript:void(0)" data-section="sdks-android" class="docs-nav-link p-3.5 rounded-xl text-sm font-bold text-slate-400 flex items-center gap-3 transition-all hover:bg-white/5 hover:text-white">
|
||||||
<i data-lucide="search" class="w-4 h-4"></i> Geocoding API
|
<i data-lucide="smartphone" class="w-4 h-4 text-emerald-400"></i> Android SDK (Kotlin)
|
||||||
</a>
|
</a>
|
||||||
<a href="javascript:void(0)" data-section="routing-api" class="docs-nav-link p-4 rounded-2xl text-sm font-bold text-slate-500 flex items-center gap-3 transition-all hover:bg-white/5 hover:text-white">
|
<a href="javascript:void(0)" data-section="sdks-flutter" class="docs-nav-link p-3.5 rounded-xl text-sm font-bold text-slate-400 flex items-center gap-3 transition-all hover:bg-white/5 hover:text-white">
|
||||||
<i data-lucide="navigation" class="w-4 h-4"></i> Routing API
|
<i data-lucide="layers" class="w-4 h-4 text-cyan-400"></i> Flutter SDK (Dart)
|
||||||
</a>
|
</a>
|
||||||
<a href="javascript:void(0)" data-section="sdks" class="docs-nav-link p-4 rounded-2xl text-sm font-bold text-slate-500 flex items-center gap-3 transition-all hover:bg-white/5 hover:text-white">
|
<a href="javascript:void(0)" data-section="sdks-web" class="docs-nav-link p-3.5 rounded-xl text-sm font-bold text-slate-400 flex items-center gap-3 transition-all hover:bg-white/5 hover:text-white">
|
||||||
<i data-lucide="package" class="w-4 h-4"></i> SDKs & Client Libraries
|
<i data-lucide="globe" class="w-4 h-4 text-amber-400"></i> JavaScript / TS SDK
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<div class="mt-4 mb-2">
|
||||||
|
<h2 class="text-xs font-black uppercase tracking-wider text-slate-500" data-i18n="docs-rest-title">REST APIs</h2>
|
||||||
|
</div>
|
||||||
|
<a href="javascript:void(0)" data-section="tiles-api" class="docs-nav-link p-3.5 rounded-xl text-sm font-bold text-slate-400 flex items-center gap-3 transition-all hover:bg-white/5 hover:text-white">
|
||||||
|
<i data-lucide="map" class="w-4 h-4 text-blue-400"></i> Map Vector Tiles
|
||||||
|
</a>
|
||||||
|
<a href="javascript:void(0)" data-section="geocoding-api" class="docs-nav-link p-3.5 rounded-xl text-sm font-bold text-slate-400 flex items-center gap-3 transition-all hover:bg-white/5 hover:text-white">
|
||||||
|
<i data-lucide="search" class="w-4 h-4 text-violet-400"></i> Geocoding & Places
|
||||||
|
</a>
|
||||||
|
<a href="javascript:void(0)" data-section="routing-api" class="docs-nav-link p-3.5 rounded-xl text-sm font-bold text-slate-400 flex items-center gap-3 transition-all hover:bg-white/5 hover:text-white">
|
||||||
|
<i data-lucide="navigation" class="w-4 h-4 text-rose-400"></i> Routing & Directions
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -669,13 +686,13 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- core script -->
|
<!-- core script -->
|
||||||
<script src="js/i18n.js"></script>
|
<script src="js/i18n.js?v=2.2"></script>
|
||||||
<script src="js/auth.js"></script>
|
<script src="js/auth.js?v=2.2"></script>
|
||||||
<script src="js/docs.js"></script>
|
<script src="js/docs.js?v=2.2"></script>
|
||||||
<script src="js/app.js"></script>
|
<script src="js/app.js?v=2.2"></script>
|
||||||
<script src="js/playground.js"></script>
|
<script src="js/playground.js?v=2.2"></script>
|
||||||
<script src="js/analytics.js"></script>
|
<script src="js/analytics.js?v=2.2"></script>
|
||||||
<script src="js/billing.js"></script>
|
<script src="js/billing.js?v=2.2"></script>
|
||||||
<script src="js/refinement.js"></script>
|
<script src="js/refinement.js?v=2.2"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/**
|
/**
|
||||||
* Documentation Engine for Intaleq Dashboard
|
* Documentation Engine for Intaleq Dashboard
|
||||||
* Comprehensive API Reference (EN/AR)
|
* Commercial Enterprise Mapping Platform & Native SDKs (iOS Swift, Android Kotlin, Flutter Dart, Web JS/TS)
|
||||||
* Updated with Premium Visuals, High-Performance Examples, and Official SDKs
|
* Supports English & Arabic Localization
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const docs = {
|
const docs = {
|
||||||
@@ -21,10 +21,6 @@ const docs = {
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const section = e.currentTarget.getAttribute('data-section');
|
const section = e.currentTarget.getAttribute('data-section');
|
||||||
docs.renderSection(section);
|
docs.renderSection(section);
|
||||||
|
|
||||||
// Active state management
|
|
||||||
document.querySelectorAll('.docs-nav-link').forEach(l => l.classList.remove('active', 'bg-blue-500/10', 'text-blue-400'));
|
|
||||||
e.currentTarget.classList.add('active', 'bg-blue-500/10', 'text-blue-400');
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -33,7 +29,19 @@ const docs = {
|
|||||||
const container = document.getElementById('docs-content');
|
const container = document.getElementById('docs-content');
|
||||||
if (!container) return;
|
if (!container) return;
|
||||||
|
|
||||||
const lang = i18n.currentLang || 'en';
|
// Synchronize active sidebar navigation link
|
||||||
|
document.querySelectorAll('.docs-nav-link').forEach(l => {
|
||||||
|
const sec = l.getAttribute('data-section');
|
||||||
|
if (sec === id) {
|
||||||
|
l.classList.add('active', 'bg-blue-500/10', 'text-blue-400');
|
||||||
|
l.classList.remove('text-slate-400');
|
||||||
|
} else {
|
||||||
|
l.classList.remove('active', 'bg-blue-500/10', 'text-blue-400');
|
||||||
|
l.classList.add('text-slate-400');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const lang = (window.i18n && window.i18n.currentLang) || 'en';
|
||||||
const isAr = lang === 'ar';
|
const isAr = lang === 'ar';
|
||||||
|
|
||||||
const content = {
|
const content = {
|
||||||
@@ -42,165 +50,416 @@ const docs = {
|
|||||||
<div class="relative overflow-hidden rounded-[3rem] p-12 bg-gradient-to-br from-blue-600/20 via-blue-500/5 to-transparent border border-white/10 group">
|
<div class="relative overflow-hidden rounded-[3rem] p-12 bg-gradient-to-br from-blue-600/20 via-blue-500/5 to-transparent border border-white/10 group">
|
||||||
<div class="absolute -top-24 -right-24 w-96 h-96 bg-blue-500/10 blur-[120px] rounded-full group-hover:bg-blue-500/20 transition-all duration-700"></div>
|
<div class="absolute -top-24 -right-24 w-96 h-96 bg-blue-500/10 blur-[120px] rounded-full group-hover:bg-blue-500/20 transition-all duration-700"></div>
|
||||||
<div class="relative z-10">
|
<div class="relative z-10">
|
||||||
<h3 class="text-5xl font-black mb-6 text-gradient">${isAr ? 'انطلق في ثوانٍ' : 'Launch in Seconds'}</h3>
|
<div class="inline-flex items-center gap-2 px-4 py-1.5 rounded-full bg-blue-500/20 text-blue-300 text-xs font-bold mb-4 border border-blue-500/30">
|
||||||
<p class="text-slate-300 text-xl leading-relaxed max-w-2xl">
|
<i data-lucide="sparkles" class="w-3.5 h-3.5"></i>
|
||||||
${isAr ? 'مرحباً بك في مستقبل الخرائط في المنطقة. توفر لك منصة "انطلاق" واجهات برمجية ذكية، خرائط Vector فائقة الدقة، ومباني ثلاثية الأبعاد متكاملة.' : 'Welcome to the future of regional mapping. Intaleq provides high-fidelity vector tiles, intelligent geocoding, and native 3D building support for Jordan & Syria.'}
|
${isAr ? 'منصة الخرائط والذكاء المكاني للأعمال' : 'Enterprise Commercial Mapping Platform'}
|
||||||
|
</div>
|
||||||
|
<h3 class="text-4xl md:text-5xl font-black mb-6 text-gradient">${isAr ? 'ابدأ التكامل مع منصة انطلاق' : 'Launch in Minutes'}</h3>
|
||||||
|
<p class="text-slate-300 text-lg md:text-xl leading-relaxed max-w-3xl">
|
||||||
|
${isAr ? 'توفر منصة "انطلاق" حلول الخرائط المتطورة لتطبيقات النقل الذكي (Ride-Hailing)، شركات التوصيل واللوجستيات (Delivery & Logistics)، والتجارة الإلكترونية، مع توفير يصل إلى 85% مقارنة بخرائط جوجل.' : 'Intaleq provides cutting-edge mapping infrastructure for Ride-Hailing, Delivery & Logistics, and E-commerce applications across Jordan & the MENA region at 85% lower cost than Google Maps.'}
|
||||||
</p>
|
</p>
|
||||||
<div class="flex gap-4 mt-8">
|
<div class="flex flex-wrap gap-4 mt-8">
|
||||||
<a href="https://pub.dev/packages/intaleq_maps" target="_blank" class="px-6 py-3 bg-blue-600 text-white rounded-2xl font-black text-sm flex items-center gap-2 hover:bg-blue-500 transition-all">
|
<a href="javascript:void(0)" onclick="docs.renderSection('sdks-ios')" class="px-6 py-3 bg-slate-900 text-white rounded-2xl font-bold text-sm flex items-center gap-2 border border-white/10 hover:border-blue-500/50 hover:bg-slate-800 transition-all">
|
||||||
<i data-lucide="package"></i> Flutter SDK
|
<i data-lucide="apple" class="w-4 h-4 text-slate-300"></i> iOS SDK (Swift)
|
||||||
</a>
|
</a>
|
||||||
<a href="https://www.npmjs.com/package/intaleq-maps-gl" target="_blank" class="px-6 py-3 bg-white text-slate-950 rounded-2xl font-black text-sm flex items-center gap-2 hover:bg-slate-100 transition-all">
|
<a href="javascript:void(0)" onclick="docs.renderSection('sdks-android')" class="px-6 py-3 bg-slate-900 text-white rounded-2xl font-bold text-sm flex items-center gap-2 border border-white/10 hover:border-emerald-500/50 hover:bg-slate-800 transition-all">
|
||||||
<i data-lucide="package"></i> JS SDK
|
<i data-lucide="smartphone" class="w-4 h-4 text-emerald-400"></i> Android SDK (Kotlin)
|
||||||
|
</a>
|
||||||
|
<a href="javascript:void(0)" onclick="docs.renderSection('sdks-flutter')" class="px-6 py-3 bg-blue-600 text-white rounded-2xl font-bold text-sm flex items-center gap-2 hover:bg-blue-500 shadow-lg shadow-blue-500/25 transition-all">
|
||||||
|
<i data-lucide="layers" class="w-4 h-4 text-cyan-200"></i> Flutter SDK
|
||||||
|
</a>
|
||||||
|
<a href="javascript:void(0)" onclick="docs.renderSection('sdks-web')" class="px-6 py-3 bg-white text-slate-950 rounded-2xl font-bold text-sm flex items-center gap-2 hover:bg-slate-100 transition-all">
|
||||||
|
<i data-lucide="globe" class="w-4 h-4 text-amber-500"></i> Web JavaScript / TS
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-8">
|
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||||
<div class="glass p-10 rounded-[2.5rem] border-white/5 bg-gradient-to-b from-white/[0.02] to-transparent hover:border-blue-500/30 transition-all group">
|
<div class="glass p-8 rounded-[2rem] border-white/5 bg-gradient-to-b from-white/[0.02] to-transparent hover:border-blue-500/30 transition-all group">
|
||||||
<div class="w-16 h-16 rounded-2xl bg-blue-500/10 flex items-center justify-center text-blue-400 mb-8 font-black text-2xl group-hover:scale-110 transition-transform">01</div>
|
<div class="w-14 h-14 rounded-2xl bg-blue-500/10 flex items-center justify-center text-blue-400 mb-6 font-black text-xl group-hover:scale-110 transition-transform">01</div>
|
||||||
<h4 class="font-black text-2xl mb-4">${isAr ? 'مفتاح الوصول' : 'Access Key'}</h4>
|
<h4 class="font-black text-xl mb-3 text-white">${isAr ? 'مفتاح الـ API الآمن' : 'API Key Setup'}</h4>
|
||||||
<p class="text-slate-400 leading-relaxed">${isAr ? 'قم بإنشاء مفتاح API من لوحة التحكم لتفعيل طلباتك.' : 'Generate your secure API key from the dashboard to authenticate requests.'}</p>
|
<p class="text-slate-400 text-sm leading-relaxed">${isAr ? 'أنشئ مفتاح وصول محمي بضوابط النطاق (Domain Whitelist) لتطبيقاتك التجارية.' : 'Generate production API keys with IP/Domain restrictions to protect your usage.'}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="glass p-10 rounded-[2.5rem] border-white/5 bg-gradient-to-b from-white/[0.02] to-transparent hover:border-emerald-500/30 transition-all group">
|
<div class="glass p-8 rounded-[2rem] border-white/5 bg-gradient-to-b from-white/[0.02] to-transparent hover:border-emerald-500/30 transition-all group">
|
||||||
<div class="w-16 h-16 rounded-2xl bg-emerald-500/10 flex items-center justify-center text-emerald-400 mb-8 font-black text-2xl group-hover:scale-110 transition-transform">02</div>
|
<div class="w-14 h-14 rounded-2xl bg-emerald-500/10 flex items-center justify-center text-emerald-400 mb-6 font-black text-xl group-hover:scale-110 transition-transform">02</div>
|
||||||
<h4 class="font-black text-2xl mb-4">${isAr ? 'تكامل الخريطة' : 'Map Integration'}</h4>
|
<h4 class="font-black text-xl mb-3 text-white">${isAr ? 'خرائط مخصصة بهويتك' : 'Custom Map Styling'}</h4>
|
||||||
<p class="text-slate-400 leading-relaxed">${isAr ? 'اختر النمط (Obsidian أو Light) وادمج الخريطة في تطبيقك.' : 'Select a theme and integrate the vector tiles using our GL styles.'}</p>
|
<p class="text-slate-400 text-sm leading-relaxed">${isAr ? 'اختر النمط الفاتح النظيف للتوصيل، أو النمط الداكن الفخم لتطبيقات النقل، مع مباني 3D.' : 'Choose between Light Delivery or Obsidian Dark ride-hailing styles with 3D buildings.'}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="glass p-10 rounded-[2.5rem] border-white/5 bg-gradient-to-b from-white/[0.02] to-transparent hover:border-violet-500/30 transition-all group">
|
<div class="glass p-8 rounded-[2rem] border-white/5 bg-gradient-to-b from-white/[0.02] to-transparent hover:border-violet-500/30 transition-all group">
|
||||||
<div class="w-16 h-16 rounded-2xl bg-violet-500/10 flex items-center justify-center text-violet-400 mb-8 font-black text-2xl group-hover:scale-110 transition-transform">03</div>
|
<div class="w-14 h-14 rounded-2xl bg-violet-500/10 flex items-center justify-center text-violet-400 mb-6 font-black text-xl group-hover:scale-110 transition-transform">03</div>
|
||||||
<h4 class="font-black text-2xl mb-4">${isAr ? 'بيانات ذكية' : 'Smart Data'}</h4>
|
<h4 class="font-black text-xl mb-3 text-white">${isAr ? 'توجيه وبحث فائق الدقة' : 'Smart Routing & Search'}</h4>
|
||||||
<p class="text-slate-400 leading-relaxed">${isAr ? 'استخدم خدمات البحث والتوجيه لإضافة ذكاء مكاني لتطبيقك.' : 'Leverage Geocoding and Routing APIs for advanced spatial intelligence.'}</p>
|
<p class="text-slate-400 text-sm leading-relaxed">${isAr ? 'احسب مسار وتكلفة الرحلة لحظياً مع مصفوفة مطابقة السائقين بأقرب الطلبات.' : 'Calculate route fares, accurate ETAs, and multi-driver dispatch matrices in milliseconds.'}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="space-y-8 pt-6">
|
<div class="space-y-6 pt-4">
|
||||||
<div class="flex items-center gap-4">
|
<div class="flex items-center gap-4">
|
||||||
<div class="h-8 w-1.5 bg-blue-500 rounded-full"></div>
|
<div class="h-7 w-1.5 bg-blue-500 rounded-full"></div>
|
||||||
<h4 class="text-3xl font-black">${isAr ? 'بيانات الوصول والمصادقة' : 'Domain & Authentication'}</h4>
|
<h4 class="text-2xl font-black text-white">${isAr ? 'بيانات الوصول والمصادقة المباشرة' : 'Production Endpoints & Authentication'}</h4>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="bg-slate-950 rounded-[3rem] p-10 border border-slate-800 relative group overflow-hidden shadow-2xl">
|
<div class="bg-slate-950 rounded-[2.5rem] p-8 border border-slate-800 relative group overflow-hidden shadow-2xl">
|
||||||
<div class="absolute top-0 right-0 p-6 opacity-20 group-hover:opacity-100 transition-opacity">
|
<div class="absolute top-0 right-0 p-6 opacity-40 group-hover:opacity-100 transition-opacity">
|
||||||
<span class="bg-blue-500/10 text-blue-400 px-4 py-1.5 rounded-full text-xs font-black uppercase tracking-widest">Production URL</span>
|
<span class="bg-blue-500/10 text-blue-400 px-3 py-1 rounded-full text-xs font-black uppercase tracking-widest border border-blue-500/20">Production Endpoint</span>
|
||||||
</div>
|
</div>
|
||||||
<code class="text-blue-400 font-mono text-2xl block mb-6 select-all">https://map-saas.intaleq.com/api</code>
|
<code class="text-blue-400 font-mono text-xl block mb-6 select-all">https://map-saas.intaleqapp.com/api</code>
|
||||||
<div class="flex flex-col md:flex-row gap-8 text-slate-400">
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 text-slate-400 pt-4 border-t border-slate-900">
|
||||||
<div class="flex-1 space-y-2">
|
<div>
|
||||||
<p class="text-sm font-bold uppercase text-slate-500 tracking-widest italic">${isAr ? 'طريقة المصادقة' : 'Auth Method'}</p>
|
<p class="text-xs font-bold uppercase text-slate-500 tracking-widest mb-1">${isAr ? 'المصادقة عبر الـ Header (موصى به في Backend/Apps)' : 'Header Auth (Recommended)'}</p>
|
||||||
<p class="text-lg">${isAr ? 'يتم إرسال المفتاح عبر الـ HTTP Header التالي:' : 'Pass your API key in the following HTTP header:'}</p>
|
<code class="text-blue-300 font-mono font-bold text-sm bg-blue-500/10 px-2 py-1 rounded">x-api-key: in_9478b32836d19cff73db3063</code>
|
||||||
<code class="text-blue-300 font-mono font-black text-xl">x-api-key</code>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-1 space-y-2 border-slate-800 md:border-l md:pl-8">
|
<div>
|
||||||
<p class="text-sm font-bold uppercase text-slate-500 tracking-widest italic">${isAr ? 'نطاق الوصول' : 'Allowed Origins'}</p>
|
<p class="text-xs font-bold uppercase text-slate-500 tracking-widest mb-1">${isAr ? 'المصادقة عبر الـ Query (للخرائط المباشرة)' : 'Query Parameter Auth'}</p>
|
||||||
<p class="text-lg">${isAr ? 'تأكد من إضافة النطاق الخاص بك في إعدادات المفتاح.' : 'Ensure your request origin is listed in the key restrictions.'}</p>
|
<code class="text-emerald-300 font-mono font-bold text-sm bg-emerald-500/10 px-2 py-1 rounded">?key=in_9478b32836d19cff73db3063</code>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`,
|
`,
|
||||||
'sdks': `
|
|
||||||
<div class="space-y-12 animate-in fade-in slide-in-from-bottom-6 duration-700">
|
'sdks-ios': `
|
||||||
<header>
|
<div class="space-y-10 animate-in fade-in slide-in-from-bottom-6 duration-700">
|
||||||
<h3 class="text-5xl font-black mb-4 text-gradient">${isAr ? 'المكتبات البرمجية (SDKs)' : 'SDKs & Client Libraries'}</h3>
|
<header class="flex items-center gap-5 p-8 rounded-[2.5rem] bg-gradient-to-r from-slate-900 via-slate-900/60 to-transparent border border-white/10">
|
||||||
<p class="text-slate-400 text-xl max-w-xl">${isAr ? 'استخدم مكتباتنا الجاهزة لدمج الخرائط والخدمات في ثوانٍ.' : 'Accelerate your development with our official enterprise-grade client libraries.'}</p>
|
<div class="w-16 h-16 rounded-2xl bg-white/10 flex items-center justify-center text-white text-3xl shadow-xl">
|
||||||
|
<i data-lucide="apple" class="w-9 h-9"></i>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="flex items-center gap-3 mb-1">
|
||||||
|
<h3 class="text-3xl font-black text-white">iOS Native SDK</h3>
|
||||||
|
<span class="px-3 py-0.5 rounded-full bg-blue-500/20 text-blue-400 text-xs font-bold border border-blue-500/30">Swift 5.9+ / UIKit & SwiftUI</span>
|
||||||
|
</div>
|
||||||
|
<p class="text-slate-400 text-base">${isAr ? 'مكتبة آبل الأصلية لتطبيقات النقل والتوصيل (Ride-Hailing & Delivery Apps) بسرعة 60 إطاراً في الثانية.' : 'Native iOS SDK for ride-hailing, driver tracking, and delivery logistics apps on iPhone & iPad.'}</p>
|
||||||
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-10">
|
<!-- Installation -->
|
||||||
<!-- Flutter SDK Card -->
|
<div class="space-y-4">
|
||||||
<div class="glass p-10 rounded-[3rem] border-white/5 flex flex-col hover:border-blue-500/30 transition-all">
|
<h4 class="text-xl font-bold text-white flex items-center gap-2">
|
||||||
<div class="flex items-center gap-4 mb-8">
|
<i data-lucide="download" class="w-5 h-5 text-blue-400"></i> ${isAr ? '1. التثبيت (Installation)' : '1. Installation'}
|
||||||
<div class="w-12 h-12 rounded-xl bg-blue-500/10 flex items-center justify-center text-blue-400">
|
</h4>
|
||||||
<i data-lucide="smartphone"></i>
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
</div>
|
<div class="bg-slate-950 p-6 rounded-2xl border border-slate-800">
|
||||||
<h4 class="text-2xl font-black text-white">Flutter SDK</h4>
|
<p class="text-xs font-bold text-slate-400 uppercase tracking-wider mb-2">Swift Package Manager (SPM)</p>
|
||||||
|
<code class="text-xs text-blue-300 font-mono select-all block bg-white/5 p-3 rounded-xl">
|
||||||
|
https://github.com/maplibre/maplibre-native-spm
|
||||||
|
</code>
|
||||||
</div>
|
</div>
|
||||||
<p class="text-slate-400 mb-8 flex-1 leading-relaxed">
|
<div class="bg-slate-950 p-6 rounded-2xl border border-slate-800">
|
||||||
${isAr ? 'مكتبة متكاملة لنظام Flutter تدعم أندرويد و iOS والويب.' : 'A robust Flutter wrapper for MapLibre with native Intaleq services integrated.'}
|
<p class="text-xs font-bold text-slate-400 uppercase tracking-wider mb-2">CocoaPods (Podfile)</p>
|
||||||
</p>
|
<code class="text-xs text-emerald-300 font-mono select-all block bg-white/5 p-3 rounded-xl">
|
||||||
<div class="bg-slate-950 p-6 rounded-2xl border border-slate-800 font-mono text-xs mb-8">
|
pod 'MapLibre', '~> 5.13.0'
|
||||||
<span class="text-slate-500"># pubspec.yaml</span><br>
|
</code>
|
||||||
<span class="text-blue-400">intaleq_maps:</span> <span class="text-emerald-400">^1.0.0</span>
|
|
||||||
</div>
|
</div>
|
||||||
<a href="https://pub.dev/packages/intaleq_maps" target="_blank" class="btn btn-primary w-full justify-center">View on pub.dev</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- JS SDK Card -->
|
|
||||||
<div class="glass p-10 rounded-[3rem] border-white/5 flex flex-col hover:border-emerald-500/30 transition-all">
|
|
||||||
<div class="flex items-center gap-4 mb-8">
|
|
||||||
<div class="w-12 h-12 rounded-xl bg-emerald-500/10 flex items-center justify-center text-emerald-400">
|
|
||||||
<i data-lucide="globe"></i>
|
|
||||||
</div>
|
|
||||||
<h4 class="text-2xl font-black text-white">JavaScript SDK</h4>
|
|
||||||
</div>
|
|
||||||
<p class="text-slate-400 mb-8 flex-1 leading-relaxed">
|
|
||||||
${isAr ? 'مكتبة JavaScript حديثة مدعومة بـ TypeScript لتطبيقات الويب.' : 'Modern TypeScript-ready SDK for seamless web map integration and routing.'}
|
|
||||||
</p>
|
|
||||||
<div class="bg-slate-950 p-6 rounded-2xl border border-slate-800 font-mono text-xs mb-8">
|
|
||||||
<span class="text-slate-500"># Install via NPM</span><br>
|
|
||||||
<span class="text-emerald-400">npm i intaleq-maps-gl</span>
|
|
||||||
</div>
|
|
||||||
<a href="https://www.npmjs.com/package/intaleq-maps-gl" target="_blank" class="btn btn-primary w-full justify-center">View on NPM</a>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="bg-indigo-900/10 p-12 rounded-[3.5rem] border border-indigo-500/20">
|
<!-- Swift Code Example -->
|
||||||
<h4 class="text-2xl font-black mb-6 text-indigo-400">${isAr ? 'مثال: إضافة مؤشر (JS SDK)' : 'Example: Adding a Marker (JS SDK)'}</h4>
|
<div class="space-y-4">
|
||||||
<pre class="bg-slate-950 p-10 rounded-[2.5rem] border border-slate-800 text-xs font-mono text-slate-300 leading-loose overflow-x-auto">
|
<div class="flex items-center justify-between">
|
||||||
import { IntaleqMap } from 'intaleq-maps-gl';
|
<h4 class="text-xl font-bold text-white flex items-center gap-2">
|
||||||
|
<i data-lucide="code" class="w-5 h-5 text-blue-400"></i> ${isAr ? '2. مثال كود Swift كامل لتطبيق توصيل/نقل' : '2. Swift Implementation (Delivery / Ride-Hailing)'}
|
||||||
|
</h4>
|
||||||
|
<span class="text-xs font-mono text-slate-500">DeliveryMapViewController.swift</span>
|
||||||
|
</div>
|
||||||
|
<pre class="bg-slate-950 p-8 rounded-[2rem] border border-slate-800 text-xs font-mono text-slate-300 leading-relaxed overflow-x-auto select-all">
|
||||||
|
<span class="text-purple-400">import</span> UIKit
|
||||||
|
<span class="text-purple-400">import</span> MapLibre
|
||||||
|
|
||||||
const map = new IntaleqMap({
|
<span class="text-purple-400">class</span> <span class="text-yellow-300">DeliveryMapViewController</span>: <span class="text-blue-300">UIViewController</span>, <span class="text-blue-300">MLNMapViewDelegate</span> {
|
||||||
container: 'map',
|
|
||||||
apiKey: 'YOUR_KEY',
|
<span class="text-purple-400">var</span> mapView: <span class="text-blue-300">MLNMapView</span>!
|
||||||
styleType: 'obsidian'
|
<span class="text-purple-400">let</span> apiKey = <span class="text-emerald-300">"YOUR_INTALEQ_API_KEY"</span>
|
||||||
});
|
|
||||||
|
<span class="text-purple-400">override func</span> <span class="text-blue-400">viewDidLoad</span>() {
|
||||||
|
<span class="text-purple-400">super</span>.viewDidLoad()
|
||||||
|
|
||||||
|
<span class="text-slate-500">// 1. رابط نمط الخريطة التجاري من انطلاق</span>
|
||||||
|
<span class="text-purple-400">let</span> styleURL = <span class="text-blue-300">URL</span>(string: <span class="text-emerald-300">"https://map-saas.intaleqapp.com/tactical-style.json?key=\(apiKey)"</span>)!
|
||||||
|
|
||||||
|
<span class="text-slate-500">// 2. تهيئة الخريطة وتثبيت موقع البداية على عمان</span>
|
||||||
|
mapView = <span class="text-blue-300">MLNMapView</span>(frame: view.bounds, styleURL: styleURL)
|
||||||
|
mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
||||||
|
mapView.setCenter(<span class="text-blue-300">CLLocationCoordinate2D</span>(latitude: <span class="text-cyan-300">31.9539</span>, longitude: <span class="text-cyan-300">35.9106</span>), zoomLevel: <span class="text-cyan-300">14</span>, animated: <span class="text-purple-400">false</span>)
|
||||||
|
mapView.delegate = <span class="text-purple-400">self</span>
|
||||||
|
|
||||||
|
view.addSubview(mapView)
|
||||||
|
|
||||||
|
<span class="text-slate-500">// 3. إضافة علامة موقع السائق / العميل</span>
|
||||||
|
<span class="text-purple-400">let</span> pickupPoint = <span class="text-blue-300">MLNPointAnnotation</span>()
|
||||||
|
pickupPoint.coordinate = <span class="text-blue-300">CLLocationCoordinate2D</span>(latitude: <span class="text-cyan-300">31.9539</span>, longitude: <span class="text-cyan-300">35.9106</span>)
|
||||||
|
pickupPoint.title = <span class="text-emerald-300">"نقطة استلام الطلب"</span>
|
||||||
|
pickupPoint.subtitle = <span class="text-slate-400">"شارع مكة، عمان"</span>
|
||||||
|
mapView.addAnnotation(pickupPoint)
|
||||||
|
}
|
||||||
|
}</pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
map.addIntaleqMarker({
|
<!-- SwiftUI Example -->
|
||||||
position: [35.91, 31.95], // [lng, lat]
|
<div class="space-y-4">
|
||||||
color: '#0D47A1'
|
<h4 class="text-xl font-bold text-white flex items-center gap-2">
|
||||||
});</pre>
|
<i data-lucide="layout" class="w-5 h-5 text-blue-400"></i> ${isAr ? '3. تكامل SwiftUI' : '3. SwiftUI View Component'}
|
||||||
|
</h4>
|
||||||
|
<pre class="bg-slate-950 p-8 rounded-[2rem] border border-slate-800 text-xs font-mono text-slate-300 leading-relaxed overflow-x-auto select-all">
|
||||||
|
<span class="text-purple-400">import</span> SwiftUI
|
||||||
|
<span class="text-purple-400">import</span> MapLibre
|
||||||
|
|
||||||
|
<span class="text-purple-400">struct</span> <span class="text-yellow-300">IntaleqMapView</span>: <span class="text-blue-300">UIViewRepresentable</span> {
|
||||||
|
<span class="text-purple-400">func</span> makeUIView(context: Context) -> <span class="text-blue-300">MLNMapView</span> {
|
||||||
|
<span class="text-purple-400">let</span> url = <span class="text-blue-300">URL</span>(string: <span class="text-emerald-300">"https://map-saas.intaleqapp.com/tactical-style.json"</span>)!
|
||||||
|
<span class="text-purple-400">let</span> map = <span class="text-blue-300">MLNMapView</span>(frame: .zero, styleURL: url)
|
||||||
|
map.setCenter(<span class="text-blue-300">CLLocationCoordinate2D</span>(latitude: <span class="text-cyan-300">31.9539</span>, longitude: <span class="text-cyan-300">35.9106</span>), zoomLevel: <span class="text-cyan-300">13</span>, animated: <span class="text-purple-400">false</span>)
|
||||||
|
<span class="text-purple-400">return</span> map
|
||||||
|
}
|
||||||
|
|
||||||
|
<span class="text-purple-400">func</span> updateUIView(_ uiView: <span class="text-blue-300">MLNMapView</span>, context: Context) {}
|
||||||
|
}</pre>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`,
|
`,
|
||||||
|
|
||||||
|
'sdks-android': `
|
||||||
|
<div class="space-y-10 animate-in fade-in slide-in-from-bottom-6 duration-700">
|
||||||
|
<header class="flex items-center gap-5 p-8 rounded-[2.5rem] bg-gradient-to-r from-emerald-950/40 via-slate-900 to-transparent border border-white/10">
|
||||||
|
<div class="w-16 h-16 rounded-2xl bg-emerald-500/10 flex items-center justify-center text-emerald-400 text-3xl shadow-xl">
|
||||||
|
<i data-lucide="smartphone" class="w-9 h-9"></i>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="flex items-center gap-3 mb-1">
|
||||||
|
<h3 class="text-3xl font-black text-white">Android Native SDK</h3>
|
||||||
|
<span class="px-3 py-0.5 rounded-full bg-emerald-500/20 text-emerald-400 text-xs font-bold border border-emerald-500/30">Kotlin & Jetpack Compose</span>
|
||||||
|
</div>
|
||||||
|
<p class="text-slate-400 text-base">${isAr ? 'مكتبة أندرويد لتطبيقات الكباتن والسائقين وتتبع مسارات الشحنات بكفاءة وسرعة فائقة.' : 'High-performance Android SDK for driver apps, delivery fleets, and real-time asset tracking.'}</p>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<!-- Gradle Setup -->
|
||||||
|
<div class="space-y-4">
|
||||||
|
<h4 class="text-xl font-bold text-white flex items-center gap-2">
|
||||||
|
<i data-lucide="download" class="w-5 h-5 text-emerald-400"></i> ${isAr ? '1. إعداد Gradle (build.gradle.kts)' : '1. Gradle Dependency'}
|
||||||
|
</h4>
|
||||||
|
<pre class="bg-slate-950 p-6 rounded-2xl border border-slate-800 text-xs font-mono text-emerald-300 select-all">
|
||||||
|
dependencies {
|
||||||
|
implementation(<span class="text-amber-300">"org.maplibre.gl:android-sdk:11.5.1"</span>)
|
||||||
|
}</pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Kotlin Code Example -->
|
||||||
|
<div class="space-y-4">
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<h4 class="text-xl font-bold text-white flex items-center gap-2">
|
||||||
|
<i data-lucide="code" class="w-5 h-5 text-emerald-400"></i> ${isAr ? '2. كود Kotlin لتطبيق السائق / النقل الذكي' : '2. Kotlin Implementation (Driver App)'}
|
||||||
|
</h4>
|
||||||
|
<span class="text-xs font-mono text-slate-500">DriverMapActivity.kt</span>
|
||||||
|
</div>
|
||||||
|
<pre class="bg-slate-950 p-8 rounded-[2rem] border border-slate-800 text-xs font-mono text-slate-300 leading-relaxed overflow-x-auto select-all">
|
||||||
|
<span class="text-purple-400">package</span> com.intaleq.driver
|
||||||
|
|
||||||
|
<span class="text-purple-400">import</span> android.os.Bundle
|
||||||
|
<span class="text-purple-400">import</span> androidx.appcompat.app.AppCompatActivity
|
||||||
|
<span class="text-purple-400">import</span> org.maplibre.android.MapLibre
|
||||||
|
<span class="text-purple-400">import</span> org.maplibre.android.camera.CameraPosition
|
||||||
|
<span class="text-purple-400">import</span> org.maplibre.android.geometry.LatLng
|
||||||
|
<span class="text-purple-400">import</span> org.maplibre.android.maps.MapView
|
||||||
|
|
||||||
|
<span class="text-purple-400">class</span> <span class="text-yellow-300">DriverMapActivity</span> : <span class="text-blue-300">AppCompatActivity</span>() {
|
||||||
|
|
||||||
|
<span class="text-purple-400">private lateinit var</span> mapView: <span class="text-blue-300">MapView</span>
|
||||||
|
|
||||||
|
<span class="text-purple-400">override fun</span> <span class="text-blue-400">onCreate</span>(savedInstanceState: <span class="text-blue-300">Bundle</span>?) {
|
||||||
|
<span class="text-purple-400">super</span>.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
<span class="text-slate-500">// 1. تهيئة محرك الخريطة</span>
|
||||||
|
<span class="text-blue-300">MapLibre</span>.getInstance(<span class="text-purple-400">this</span>)
|
||||||
|
|
||||||
|
setContentView(R.layout.activity_driver_map)
|
||||||
|
mapView = findViewById(R.id.mapView)
|
||||||
|
mapView.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
<span class="text-slate-500">// 2. تحميل نمط انطلاق التجاري</span>
|
||||||
|
<span class="text-purple-400">val</span> styleUrl = <span class="text-emerald-300">"https://map-saas.intaleqapp.com/tactical-style.json"</span>
|
||||||
|
|
||||||
|
mapView.getMapAsync { map ->
|
||||||
|
map.setStyle(styleUrl) { style ->
|
||||||
|
<span class="text-slate-500">// 3. تعيين موقع السائق وزاوية الرؤية 3D</span>
|
||||||
|
map.cameraPosition = <span class="text-blue-300">CameraPosition</span>.Builder()
|
||||||
|
.target(<span class="text-blue-300">LatLng</span>(<span class="text-cyan-300">31.9539</span>, <span class="text-cyan-300">35.9106</span>))
|
||||||
|
.zoom(<span class="text-cyan-300">14.0</span>)
|
||||||
|
.tilt(<span class="text-cyan-300">45.0</span>)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
<span class="text-purple-400">override fun</span> <span class="text-blue-400">onResume</span>() { <span class="text-purple-400">super</span>.onResume(); mapView.onResume() }
|
||||||
|
<span class="text-purple-400">override fun</span> <span class="text-blue-400">onPause</span>() { <span class="text-purple-400">super</span>.onPause(); mapView.onPause() }
|
||||||
|
<span class="text-purple-400">override fun</span> <span class="text-blue-400">onDestroy</span>() { <span class="text-purple-400">super</span>.onDestroy(); mapView.onDestroy() }
|
||||||
|
}</pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
|
|
||||||
|
'sdks-flutter': `
|
||||||
|
<div class="space-y-10 animate-in fade-in slide-in-from-bottom-6 duration-700">
|
||||||
|
<header class="flex items-center gap-5 p-8 rounded-[2.5rem] bg-gradient-to-r from-cyan-950/40 via-slate-900 to-transparent border border-white/10">
|
||||||
|
<div class="w-16 h-16 rounded-2xl bg-cyan-500/10 flex items-center justify-center text-cyan-400 text-3xl shadow-xl">
|
||||||
|
<i data-lucide="layers" class="w-9 h-9"></i>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="flex items-center gap-3 mb-1">
|
||||||
|
<h3 class="text-3xl font-black text-white">Flutter SDK</h3>
|
||||||
|
<span class="px-3 py-0.5 rounded-full bg-cyan-500/20 text-cyan-400 text-xs font-bold border border-cyan-500/30">Dart 3.5+ / iOS & Android</span>
|
||||||
|
</div>
|
||||||
|
<p class="text-slate-400 text-base">${isAr ? 'حزمة فلاتر الرسمية الموحدة لتطوير تطبيقات النقل والتوصيل الميداني على كلا النظامين بكود واحد.' : 'Official Flutter package for cross-platform commercial dispatch and ride-hailing apps.'}</p>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<!-- Pubspec Dependency -->
|
||||||
|
<div class="space-y-4">
|
||||||
|
<h4 class="text-xl font-bold text-white flex items-center gap-2">
|
||||||
|
<i data-lucide="download" class="w-5 h-5 text-cyan-400"></i> ${isAr ? '1. التثبيت (pubspec.yaml)' : '1. Pubspec Installation'}
|
||||||
|
</h4>
|
||||||
|
<pre class="bg-slate-950 p-6 rounded-2xl border border-slate-800 text-xs font-mono text-cyan-300 select-all">
|
||||||
|
dependencies:
|
||||||
|
intaleq_maps: ^1.0.0</pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Flutter Dart Code -->
|
||||||
|
<div class="space-y-4">
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<h4 class="text-xl font-bold text-white flex items-center gap-2">
|
||||||
|
<i data-lucide="code" class="w-5 h-5 text-cyan-400"></i> ${isAr ? '2. كود Flutter (Dart) لتطبيق التوصيل' : '2. Flutter Dart Widget'}
|
||||||
|
</h4>
|
||||||
|
<span class="text-xs font-mono text-slate-500">ride_tracking_page.dart</span>
|
||||||
|
</div>
|
||||||
|
<pre class="bg-slate-950 p-8 rounded-[2rem] border border-slate-800 text-xs font-mono text-slate-300 leading-relaxed overflow-x-auto select-all">
|
||||||
|
<span class="text-purple-400">import</span> <span class="text-emerald-300">'package:flutter/material.dart'</span>;
|
||||||
|
<span class="text-purple-400">import</span> <span class="text-emerald-300">'package:intaleq_maps/intaleq_maps.dart'</span>;
|
||||||
|
|
||||||
|
<span class="text-purple-400">class</span> <span class="text-yellow-300">RideTrackingPage</span> <span class="text-purple-400">extends</span> <span class="text-blue-300">StatelessWidget</span> {
|
||||||
|
<span class="text-purple-400">const</span> <span class="text-yellow-300">RideTrackingPage</span>({<span class="text-purple-400">super</span>.key});
|
||||||
|
|
||||||
|
<span class="text-purple-400">@override</span>
|
||||||
|
<span class="text-blue-300">Widget</span> build(<span class="text-blue-300">BuildContext</span> context) {
|
||||||
|
<span class="text-purple-400">return</span> <span class="text-blue-300">Scaffold</span>(
|
||||||
|
body: <span class="text-blue-300">IntaleqMap</span>(
|
||||||
|
apiKey: <span class="text-emerald-300">'YOUR_INTALEQ_KEY'</span>,
|
||||||
|
styleString: <span class="text-emerald-300">'https://map-saas.intaleqapp.com/tactical-style.json'</span>,
|
||||||
|
initialCameraPosition: <span class="text-purple-400">const</span> <span class="text-blue-300">CameraPosition</span>(
|
||||||
|
target: <span class="text-blue-300">LatLng</span>(<span class="text-cyan-300">31.9539</span>, <span class="text-cyan-300">35.9106</span>),
|
||||||
|
zoom: <span class="text-cyan-300">14.0</span>,
|
||||||
|
tilt: <span class="text-cyan-300">40.0</span>,
|
||||||
|
),
|
||||||
|
myLocationEnabled: <span class="text-purple-400">true</span>,
|
||||||
|
myLocationTrackingMode: <span class="text-blue-300">MyLocationTrackingMode</span>.Tracking,
|
||||||
|
onMapCreated: (controller) {
|
||||||
|
<span class="text-slate-500">// الخريطة جاهزة لعرض خط المسار وحركة السائق</span>
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}</pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
|
|
||||||
|
'sdks-web': `
|
||||||
|
<div class="space-y-10 animate-in fade-in slide-in-from-bottom-6 duration-700">
|
||||||
|
<header class="flex items-center gap-5 p-8 rounded-[2.5rem] bg-gradient-to-r from-amber-950/40 via-slate-900 to-transparent border border-white/10">
|
||||||
|
<div class="w-16 h-16 rounded-2xl bg-amber-500/10 flex items-center justify-center text-amber-400 text-3xl shadow-xl">
|
||||||
|
<i data-lucide="globe" class="w-9 h-9"></i>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="flex items-center gap-3 mb-1">
|
||||||
|
<h3 class="text-3xl font-black text-white">JavaScript / TypeScript SDK</h3>
|
||||||
|
<span class="px-3 py-0.5 rounded-full bg-amber-500/20 text-amber-400 text-xs font-bold border border-amber-500/30">Web & React / Vue / Angular</span>
|
||||||
|
</div>
|
||||||
|
<p class="text-slate-400 text-base">${isAr ? 'مكتبة الويب للوحات تحكم الإدارة (Dispatch Portals) وتتبع الأساطيل المباشر.' : 'Web mapping SDK for fleet dispatch dashboards and customer web tracking.'}</p>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<!-- NPM Install -->
|
||||||
|
<div class="space-y-4">
|
||||||
|
<h4 class="text-xl font-bold text-white flex items-center gap-2">
|
||||||
|
<i data-lucide="download" class="w-5 h-5 text-amber-400"></i> ${isAr ? '1. التثبيت عبر NPM' : '1. NPM Package'}
|
||||||
|
</h4>
|
||||||
|
<pre class="bg-slate-950 p-6 rounded-2xl border border-slate-800 text-xs font-mono text-amber-300 select-all">
|
||||||
|
npm install maplibre-gl @mapbox/mapbox-gl-rtl-text</pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Web Code Example -->
|
||||||
|
<div class="space-y-4">
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<h4 class="text-xl font-bold text-white flex items-center gap-2">
|
||||||
|
<i data-lucide="code" class="w-5 h-5 text-amber-400"></i> ${isAr ? '2. كود التكامل (JavaScript / TypeScript)' : '2. JavaScript / TypeScript Implementation'}
|
||||||
|
</h4>
|
||||||
|
<span class="text-xs font-mono text-slate-500">dispatch_map.ts</span>
|
||||||
|
</div>
|
||||||
|
<pre class="bg-slate-950 p-8 rounded-[2rem] border border-slate-800 text-xs font-mono text-slate-300 leading-relaxed overflow-x-auto select-all">
|
||||||
|
<span class="text-purple-400">import</span> maplibregl <span class="text-purple-400">from</span> <span class="text-emerald-300">'maplibre-gl'</span>;
|
||||||
|
<span class="text-purple-400">import</span> <span class="text-emerald-300">'maplibre-gl/dist/maplibre-gl.css'</span>;
|
||||||
|
|
||||||
|
<span class="text-slate-500">// 1. تفعيل محرك الخطوط العربية (RTL Plugin)</span>
|
||||||
|
maplibregl.setRTLTextPlugin(
|
||||||
|
<span class="text-emerald-300">'https://map-saas.intaleqapp.com/rtl-plugin.js'</span>,
|
||||||
|
<span class="text-purple-400">null</span>,
|
||||||
|
<span class="text-purple-400">true</span>
|
||||||
|
);
|
||||||
|
|
||||||
|
<span class="text-slate-500">// 2. تهيئة خريطة لوحة التحكم</span>
|
||||||
|
<span class="text-purple-400">const</span> map = <span class="text-purple-400">new</span> maplibregl.<span class="text-blue-300">Map</span>({
|
||||||
|
container: <span class="text-emerald-300">'map'</span>,
|
||||||
|
style: <span class="text-emerald-300">'https://map-saas.intaleqapp.com/tactical-style.json'</span>,
|
||||||
|
center: [<span class="text-cyan-300">35.9106</span>, <span class="text-cyan-300">31.9539</span>],
|
||||||
|
zoom: <span class="text-cyan-300">12.5</span>,
|
||||||
|
pitch: <span class="text-cyan-300">45</span>
|
||||||
|
});
|
||||||
|
|
||||||
|
<span class="text-slate-500">// 3. إضافة سائق على الخريطة</span>
|
||||||
|
<span class="text-purple-400">new</span> maplibregl.<span class="text-blue-300">Marker</span>({ color: <span class="text-emerald-300">'#0071E3'</span> })
|
||||||
|
.setLngLat([<span class="text-cyan-300">35.9106</span>, <span class="text-cyan-300">31.9539</span>])
|
||||||
|
.setPopup(<span class="text-purple-400">new</span> maplibregl.<span class="text-blue-300">Popup</span>().setHTML(<span class="text-emerald-300">'<h4>كابتن سيرو: أحمد (متاح للطلب)</h4>'</span>))
|
||||||
|
.addTo(map);</pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
|
|
||||||
'tiles-api': `
|
'tiles-api': `
|
||||||
<div class="space-y-12 animate-in fade-in slide-in-from-bottom-6 duration-700">
|
<div class="space-y-12 animate-in fade-in slide-in-from-bottom-6 duration-700">
|
||||||
<header class="flex flex-col md:flex-row md:items-end justify-between gap-6">
|
<header class="flex flex-col md:flex-row md:items-end justify-between gap-6">
|
||||||
<div>
|
<div>
|
||||||
<h3 class="text-5xl font-black mb-4 text-gradient">${isAr ? 'خرائط الـ Vector' : 'Vector Tiles API'}</h3>
|
<h3 class="text-4xl md:text-5xl font-black mb-4 text-gradient">${isAr ? 'خدمة خرائط الـ Vector Tiles' : 'Map Vector Tiles API'}</h3>
|
||||||
<p class="text-slate-400 text-xl max-w-xl">${isAr ? 'خرائط تفاعلية فائقة السرعة تدعم العرض ثلاثي الأبعاد والتحكم الكامل في الخصائص.' : 'High-performance interactive maps with native 3D buildings and custom GL styles.'}</p>
|
<p class="text-slate-400 text-lg max-w-2xl">${isAr ? 'خرائط متجهة تفاعلية فائقة السرعة تدعم العرض ثلاثي الأبعاد والتحكم في طبقات الطرق والمباني وأسماء الأحياء باللغة العربية.' : 'High-performance interactive vector tiles with native 3D buildings, Arabic typography, and commercial POIs.'}</p>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<div class="endpoint-card glass rounded-[3.5rem] border-white/5 overflow-hidden shadow-2xl bg-gradient-to-b from-white/[0.03] to-transparent">
|
<div class="endpoint-card glass rounded-[3rem] border-white/5 overflow-hidden shadow-2xl bg-gradient-to-b from-white/[0.03] to-transparent">
|
||||||
<div class="p-8 bg-white/[0.04] border-b border-white/5 flex items-center justify-between">
|
<div class="p-6 bg-white/[0.04] border-b border-white/5 flex items-center justify-between">
|
||||||
<div class="flex items-center gap-6">
|
<div class="flex items-center gap-4">
|
||||||
<div class="px-4 py-2 bg-emerald-500 text-white text-sm font-black rounded-xl shadow-lg shadow-emerald-500/20 uppercase tracking-tighter">GET</div>
|
<div class="px-3.5 py-1.5 bg-emerald-500 text-white text-xs font-black rounded-xl uppercase tracking-wider">GET</div>
|
||||||
<code class="text-lg font-bold text-slate-100 font-mono">/v1/maps/style.json</code>
|
<code class="text-base font-bold text-slate-100 font-mono">/v1/maps/style.json</code>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="p-12">
|
<div class="p-8">
|
||||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-12">
|
<div class="grid grid-cols-1 lg:grid-cols-2 gap-8">
|
||||||
<div class="space-y-8">
|
<div class="space-y-6">
|
||||||
<div>
|
<h5 class="text-xs font-black uppercase tracking-wider text-blue-400">${isAr ? 'المعاملات المدعومة (Parameters)' : 'Query Parameters'}</h5>
|
||||||
<h5 class="text-[10px] font-black uppercase tracking-[0.2em] text-blue-400 mb-6">${isAr ? 'المعاملات المدعومة' : 'Query Parameters'}</h5>
|
<div class="space-y-3">
|
||||||
<div class="space-y-4">
|
<div class="flex items-center justify-between p-3.5 bg-white/5 rounded-xl border border-white/5">
|
||||||
<div class="flex items-center justify-between p-4 bg-white/5 rounded-2xl border border-white/5">
|
<code class="text-blue-300 font-bold">theme</code>
|
||||||
<code class="text-blue-300 font-bold">theme</code>
|
<span class="text-xs font-mono text-slate-400">obsidian (داكن) | light (فاتح للتوصيل)</span>
|
||||||
<span class="text-[10px] font-mono text-slate-600 uppercase">Optional (obsidian | light)</span>
|
</div>
|
||||||
</div>
|
<div class="flex items-center justify-between p-3.5 bg-white/5 rounded-xl border border-white/5">
|
||||||
<div class="flex items-center justify-between p-4 bg-white/5 rounded-2xl border border-white/5">
|
<code class="text-blue-300 font-bold">key</code>
|
||||||
<code class="text-blue-300 font-bold">key</code>
|
<span class="text-xs font-mono text-rose-400 font-bold">مفتاح API الخاص بك (مطلوب)</span>
|
||||||
<span class="text-[10px] font-mono text-rose-500 uppercase">Required</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="bg-slate-950 rounded-[2.5rem] p-10 border border-slate-800 shadow-inner">
|
<div class="bg-slate-950 rounded-2xl p-6 border border-slate-800">
|
||||||
<h5 class="text-[10px] font-black uppercase tracking-[0.2em] text-slate-500 mb-6">${isAr ? 'رابط النمط المباشر' : 'Direct Style URL'}</h5>
|
<h5 class="text-xs font-black uppercase tracking-wider text-slate-500 mb-4">${isAr ? 'رابط النمط المباشر (Direct Style URL)' : 'Direct Style URL'}</h5>
|
||||||
<code class="text-xs text-blue-400 break-all select-all font-mono leading-relaxed">
|
<code class="text-xs text-blue-400 break-all select-all font-mono leading-relaxed block bg-white/5 p-4 rounded-xl">
|
||||||
https://map-saas.intaleq.com/api/v1/maps/style.json?theme=obsidian&key=YOUR_API_KEY
|
https://map-saas.intaleqapp.com/tactical-style.json?key=YOUR_API_KEY
|
||||||
</code>
|
</code>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -208,56 +467,94 @@ map.addIntaleqMarker({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`,
|
`,
|
||||||
|
|
||||||
'geocoding-api': `
|
'geocoding-api': `
|
||||||
<div class="space-y-12 animate-in fade-in slide-in-from-bottom-6 duration-700">
|
<div class="space-y-12 animate-in fade-in slide-in-from-bottom-6 duration-700">
|
||||||
<header>
|
<header>
|
||||||
<h3 class="text-5xl font-black mb-4 text-gradient">${isAr ? 'البحث المكاني (Geocoding)' : 'Geocoding API'}</h3>
|
<h3 class="text-4xl md:text-5xl font-black mb-4 text-gradient">${isAr ? 'خدمة البحث المكاني وعناوين التوصيل (Geocoding API)' : 'Geocoding & Places API'}</h3>
|
||||||
<p class="text-slate-400 text-xl max-w-2xl">${isAr ? 'حوّل العناوين إلى إحداثيات أو العكس بدقة غير مسبوقة في الأردن وسوريا.' : 'Transform addresses into coordinates or reverse resolve locations with extreme accuracy in the Levant region.'}</p>
|
<p class="text-slate-400 text-lg max-w-2xl">${isAr ? 'محرك البحث الذكي لتحديد نقاط استلام وتوصيل الطلبات، والمطاعم، والمتاجر، والأحياء بدقة فائقة في الأردن وسوريا.' : 'Intelligent location search and reverse geocoding tailored for delivery pickups and street address resolution.'}</p>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<div class="endpoint-card glass rounded-[3.5rem] border-white/5 overflow-hidden shadow-2xl">
|
<!-- Forward Search -->
|
||||||
<div class="p-8 bg-blue-500/5 border-b border-white/5 flex items-center justify-between">
|
<div class="endpoint-card glass rounded-[3rem] border-white/5 overflow-hidden shadow-2xl mb-8">
|
||||||
|
<div class="p-6 bg-blue-500/5 border-b border-white/5 flex items-center justify-between">
|
||||||
<div class="flex items-center gap-4">
|
<div class="flex items-center gap-4">
|
||||||
<div class="px-4 py-2 bg-blue-600 text-white text-xs font-black rounded-xl shadow-lg shadow-blue-500/20 uppercase tracking-widest">SEARCH</div>
|
<div class="px-3.5 py-1.5 bg-blue-600 text-white text-xs font-black rounded-xl uppercase tracking-wider">GET</div>
|
||||||
<code class="text-base font-bold text-slate-200">/v1/geocoding/search</code>
|
<code class="text-base font-bold text-slate-200">/v1/geocoding/search</code>
|
||||||
</div>
|
</div>
|
||||||
|
<span class="text-xs text-slate-400 font-bold">${isAr ? 'البحث عن الأماكن والعناوين' : 'Forward Place Search'}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="p-12">
|
<div class="p-8">
|
||||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-12">
|
<div class="grid grid-cols-1 lg:grid-cols-2 gap-8">
|
||||||
<div class="space-y-8">
|
<div class="space-y-4">
|
||||||
<table class="w-full text-sm">
|
<h5 class="text-xs font-black uppercase tracking-wider text-blue-400">${isAr ? 'معاملات الطلب (Query Params)' : 'Request Parameters'}</h5>
|
||||||
<thead class="text-[10px] text-slate-500 font-black uppercase tracking-[0.2em]">
|
<div class="space-y-2 text-xs">
|
||||||
<tr class="border-b border-white/5"><th class="pb-4">Param</th><th class="pb-4 text-right">Description</th></tr>
|
<div class="flex justify-between p-2.5 bg-white/5 rounded-lg border border-white/5"><code>q</code><span>نص البحث (مثال: "مطعم القدس شارع الجامعة")</span></div>
|
||||||
</thead>
|
<div class="flex justify-between p-2.5 bg-white/5 rounded-lg border border-white/5"><code>lat, lng</code><span>إحداثيات العميل لترتيب الأقرب أولاً</span></div>
|
||||||
<tbody class="text-slate-400 divide-y divide-white/[0.02]">
|
<div class="flex justify-between p-2.5 bg-white/5 rounded-lg border border-white/5"><code>limit</code><span>عدد النتائج المطلوبة (افتراضي: 5)</span></div>
|
||||||
<tr><td class="py-4 font-bold text-blue-300 font-mono">q</td><td class="py-4 text-right">Query (e.g. "Masjid Hashem")</td></tr>
|
</div>
|
||||||
<tr><td class="py-4 font-bold text-blue-300 font-mono">limit</td><td class="py-4 text-right">Max results (Default: 5)</td></tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="bg-slate-950 rounded-[3rem] p-8 border border-slate-800 shadow-inner">
|
<div class="bg-slate-950 rounded-2xl p-6 border border-slate-800">
|
||||||
<div class="flex justify-between items-center mb-6">
|
<h5 class="text-xs font-black uppercase tracking-wider text-emerald-400 mb-3">${isAr ? 'مخرجات الـ JSON التجارية' : 'Commercial JSON Response'}</h5>
|
||||||
<span class="text-[10px] font-black uppercase text-emerald-400 tracking-widest flex items-center gap-2">
|
<pre class="text-[11px] font-mono text-emerald-300 leading-relaxed overflow-y-auto max-h-60 select-all">
|
||||||
<i data-lucide="layers" class="w-3 h-3"></i> Real-World Response
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<pre class="text-[10px] font-mono text-emerald-300/80 leading-relaxed overflow-y-auto h-64">
|
|
||||||
{
|
{
|
||||||
|
"status": "success",
|
||||||
|
"query": "مطعم القدس شارع الجامعة",
|
||||||
"results": [
|
"results": [
|
||||||
{
|
{
|
||||||
"id": 5843,
|
"id": "poi_98231",
|
||||||
"name": "مسجد هاشم",
|
"name": "مطعم القدس",
|
||||||
"name_ar": "مسجد هاشم",
|
"name_en": "Al Quds Restaurant",
|
||||||
"category": "building",
|
"category": "restaurant",
|
||||||
"governorate": "الزرقاء",
|
"formatted_address": "شارع الجامعة الأردنية، الجبيهة، عمان",
|
||||||
"location": { "lat": 32.10659, "lng": 36.18301 },
|
"location": {
|
||||||
"full_address": "لواء قصبة الزرقاء، الزرقاء",
|
"lat": 32.01542,
|
||||||
"distance_km": "12.68",
|
"lng": 35.86981
|
||||||
"source": "user_place"
|
},
|
||||||
|
"neighborhood": "الجبيهة",
|
||||||
|
"city": "عمان",
|
||||||
|
"confidence": 0.98
|
||||||
}
|
}
|
||||||
],
|
]
|
||||||
"source": "cache_hit"
|
}</pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Reverse Geocoding -->
|
||||||
|
<div class="endpoint-card glass rounded-[3rem] border-white/5 overflow-hidden shadow-2xl">
|
||||||
|
<div class="p-6 bg-emerald-500/5 border-b border-white/5 flex items-center justify-between">
|
||||||
|
<div class="flex items-center gap-4">
|
||||||
|
<div class="px-3.5 py-1.5 bg-emerald-600 text-white text-xs font-black rounded-xl uppercase tracking-wider">GET</div>
|
||||||
|
<code class="text-base font-bold text-slate-200">/v1/geocoding/reverse</code>
|
||||||
|
</div>
|
||||||
|
<span class="text-xs text-slate-400 font-bold">${isAr ? 'تحويل موقع السائق إلى عنوان وفاتورة' : 'Reverse Coordinate to Address'}</span>
|
||||||
|
</div>
|
||||||
|
<div class="p-8">
|
||||||
|
<div class="grid grid-cols-1 lg:grid-cols-2 gap-8">
|
||||||
|
<div class="space-y-4">
|
||||||
|
<h5 class="text-xs font-black uppercase tracking-wider text-emerald-400">${isAr ? 'معاملات الطلب' : 'Request Parameters'}</h5>
|
||||||
|
<div class="space-y-2 text-xs">
|
||||||
|
<div class="flex justify-between p-2.5 bg-white/5 rounded-lg border border-white/5"><code>lat</code><span>خط العرض لموقع السائق/المركبة</span></div>
|
||||||
|
<div class="flex justify-between p-2.5 bg-white/5 rounded-lg border border-white/5"><code>lng</code><span>خط الطول لموقع السائق/المركبة</span></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="bg-slate-950 rounded-2xl p-6 border border-slate-800">
|
||||||
|
<h5 class="text-xs font-black uppercase tracking-wider text-emerald-400 mb-3">${isAr ? 'مخرجات العنوان التلقائي' : 'Resolved Address Response'}</h5>
|
||||||
|
<pre class="text-[11px] font-mono text-emerald-300 leading-relaxed overflow-y-auto max-h-60 select-all">
|
||||||
|
{
|
||||||
|
"status": "success",
|
||||||
|
"formatted_address": "شارع مكة، أم أذينة، عمان",
|
||||||
|
"street": "شارع مكة",
|
||||||
|
"neighborhood": "أم أذينة",
|
||||||
|
"city": "عمان",
|
||||||
|
"location": {
|
||||||
|
"lat": 31.9821,
|
||||||
|
"lng": 35.8574
|
||||||
|
}
|
||||||
}</pre>
|
}</pre>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -265,60 +562,68 @@ map.addIntaleqMarker({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`,
|
`,
|
||||||
|
|
||||||
'routing-api': `
|
'routing-api': `
|
||||||
<div class="space-y-12 animate-in fade-in slide-in-from-bottom-6 duration-700">
|
<div class="space-y-12 animate-in fade-in slide-in-from-bottom-6 duration-700">
|
||||||
<header class="relative p-12 overflow-hidden rounded-[3.5rem] bg-slate-900 border border-white/5 md:flex md:items-center md:justify-between shadow-2xl">
|
<header>
|
||||||
<div class="absolute -top-24 -left-24 w-64 h-64 bg-violet-600/10 blur-[100px] rounded-full"></div>
|
<h3 class="text-4xl md:text-5xl font-black mb-4 text-gradient">${isAr ? 'محرك التوجيه وحساب الأجرة والملاحة (Routing API)' : 'Routing & Navigation API'}</h3>
|
||||||
<div class="relative z-10">
|
<p class="text-slate-400 text-lg max-w-2xl">${isAr ? 'حساب أسرع المسارات، تقدير وقت الوصول الحقيقي (ETA)، حساب مسافة الرحلة بدقة لحساب الأجرة، والتوجيه خطوة بخطوة.' : 'Turn-by-turn navigation engine with traffic-aware duration, distance metrics for fare calculation, and route polylines.'}</p>
|
||||||
<h3 class="text-5xl font-black mb-4 text-gradient">${isAr ? 'محرك التوجيه (Routing)' : 'Routing Engine'}</h3>
|
|
||||||
<p class="text-slate-400 text-xl max-w-xl">${isAr ? 'حساب أسرع المسارات مع تحليلات لحظية لحركة المرور.' : 'Fast pathfinding with traffic-aware duration metrics.'}</p>
|
|
||||||
</div>
|
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<div class="endpoint-card glass rounded-[3.5rem] border-white/5 overflow-hidden shadow-2xl">
|
<div class="endpoint-card glass rounded-[3rem] border-white/5 overflow-hidden shadow-2xl">
|
||||||
<div class="p-8 bg-violet-500/5 border-b border-white/5 flex items-center justify-between">
|
<div class="p-6 bg-violet-500/5 border-b border-white/5 flex items-center justify-between">
|
||||||
<div class="flex items-center gap-4">
|
<div class="flex items-center gap-4">
|
||||||
<div class="px-4 py-2 bg-violet-600 text-white text-xs font-black rounded-xl shadow-lg shadow-violet-500/20 uppercase tracking-widest">ROUTE</div>
|
<div class="px-3.5 py-1.5 bg-violet-600 text-white text-xs font-black rounded-xl uppercase tracking-wider">GET / POST</div>
|
||||||
<code class="text-base font-bold text-slate-200">/v1/routing/route</code>
|
<code class="text-base font-bold text-slate-200">/v1/routing/route</code>
|
||||||
</div>
|
</div>
|
||||||
|
<span class="text-xs text-slate-400 font-bold">${isAr ? 'حساب المسار وتوجيه السائق' : 'Route Navigation'}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="p-12">
|
<div class="p-8">
|
||||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-12">
|
<div class="grid grid-cols-1 lg:grid-cols-2 gap-8">
|
||||||
<div class="space-y-8">
|
<div class="space-y-4">
|
||||||
<div class="p-6 bg-slate-900/40 rounded-3xl border border-white/5">
|
<h5 class="text-xs font-black uppercase tracking-wider text-violet-400">${isAr ? 'معاملات طلب الرحلة (Params)' : 'Parameters'}</h5>
|
||||||
<h5 class="text-[10px] font-black uppercase tracking-widest text-violet-400 mb-4">Required Parameters</h5>
|
<div class="p-5 bg-slate-900/50 rounded-2xl border border-white/5 text-xs text-slate-300 space-y-3">
|
||||||
<ul class="text-xs text-slate-400 space-y-4">
|
<div class="flex justify-between border-b border-white/5 pb-2"><span>start</span><code class="text-violet-300 font-mono">35.9106,31.9539</code></div>
|
||||||
<li class="flex justify-between border-b border-white/5 pb-2"><span>start</span> <span class="text-violet-300 font-mono italic">"35.91,31.95"</span></li>
|
<div class="flex justify-between border-b border-white/5 pb-2"><span>end</span><code class="text-violet-300 font-mono">36.0380,32.1351</code></div>
|
||||||
<li class="flex justify-between border-b border-white/5 pb-2"><span>end</span> <span class="text-violet-300 font-mono italic">"35.85,31.82"</span></li>
|
<div class="flex justify-between border-b border-white/5 pb-2"><span>profile</span><span class="text-slate-400">car (سيارة) | delivery (دراجة توصيل)</span></div>
|
||||||
<li class="flex justify-between"><span>profile</span> <span class="text-slate-500">car | bike | foot</span></li>
|
<div class="flex justify-between"><span>traffic</span><span class="text-emerald-400 font-bold">true (حساب الازدحام المروري)</span></div>
|
||||||
</ul>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="bg-slate-950 rounded-[3rem] p-10 border border-slate-800 shadow-inner">
|
<div class="bg-slate-950 rounded-2xl p-6 border border-slate-800">
|
||||||
<div class="flex justify-between items-center mb-6">
|
<h5 class="text-xs font-black uppercase tracking-wider text-violet-400 mb-3">${isAr ? 'المخرجات التجارية لحساب الأجرة والمسار' : 'Commercial Response (Fare & ETA)'}</h5>
|
||||||
<span class="text-[10px] font-black uppercase text-violet-400 tracking-widest flex items-center gap-2">
|
<pre class="text-[11px] font-mono text-violet-300 leading-relaxed overflow-y-auto max-h-72 select-all">
|
||||||
<i data-lucide="activity" class="w-3.5 h-3.5"></i> Production Response
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<pre class="text-[10px] font-mono text-violet-300/80 leading-loose overflow-x-auto h-64">
|
|
||||||
{
|
{
|
||||||
"distance": 30313.8,
|
"status": "success",
|
||||||
"duration": 1975,
|
"route": {
|
||||||
"trafficAwareDuration": 1975,
|
"distance_meters": 8450,
|
||||||
"points": "_cvbE_th{Ev@iJpHtDLEJKz@^XJn@...",
|
"distance_km": 8.45,
|
||||||
"instructions": [
|
"duration_seconds": 780,
|
||||||
{
|
"duration_minutes": 13.0,
|
||||||
"text": "Continue onto شارع الأمير الحسن",
|
"traffic_delay_seconds": 120,
|
||||||
"distance": 172.8,
|
"estimated_fare_jod": 2.53,
|
||||||
"street_name": "شارع الأمير الحسن"
|
"geometry": "_cvbE_th{Ev@iJpHtDLEJKz@^XJn@...",
|
||||||
},
|
"steps": [
|
||||||
{
|
{
|
||||||
"text": "اتجه قليلاً لليمين خلال شارع الجيش",
|
"instruction": "انطلق باتجاه الشمال على شارع وصفي التل",
|
||||||
"distance": 190.1,
|
"instruction_en": "Head north on Wasfi Al-Tal St",
|
||||||
"street_name": "شارع الجيش"
|
"distance_meters": 1200,
|
||||||
}
|
"duration_seconds": 110
|
||||||
]
|
},
|
||||||
|
{
|
||||||
|
"instruction": "اتجه يميناً عند دوار الواحة نحو شارع المدينة المنورة",
|
||||||
|
"instruction_en": "Turn right at Al-Waha Circle onto Al-Madina St",
|
||||||
|
"distance_meters": 3400,
|
||||||
|
"duration_seconds": 310
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"instruction": "لقد وصلت إلى وجهتك على اليمين",
|
||||||
|
"instruction_en": "You have arrived at your destination on the right",
|
||||||
|
"distance_meters": 0,
|
||||||
|
"duration_seconds": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
}</pre>
|
}</pre>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -328,9 +633,12 @@ map.addIntaleqMarker({
|
|||||||
`
|
`
|
||||||
};
|
};
|
||||||
|
|
||||||
container.innerHTML = content[id] || '<div class="h-64 flex items-center justify-center italic text-slate-600">Documentation section coming soon...</div>';
|
// Fallback for generic 'sdks'
|
||||||
|
content['sdks'] = content['sdks-flutter'];
|
||||||
|
|
||||||
|
container.innerHTML = content[id] || '<div class="h-64 flex items-center justify-center italic text-slate-500">Documentation section coming soon...</div>';
|
||||||
|
|
||||||
// Re-initialize icons
|
// Re-initialize lucide icons
|
||||||
if (window.lucide) lucide.createIcons();
|
if (window.lucide) lucide.createIcons();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -139,7 +139,11 @@ const i18n = {
|
|||||||
'modal-key-label': 'Key Name',
|
'modal-key-label': 'Key Name',
|
||||||
'cancel': 'Cancel',
|
'cancel': 'Cancel',
|
||||||
'guides-title': 'Guides',
|
'guides-title': 'Guides',
|
||||||
'side-sdks': 'SDKs & Libraries'
|
'side-sdks': 'SDKs & Libraries',
|
||||||
|
'docs-overview': 'Overview',
|
||||||
|
'docs-getting-started': 'Getting Started',
|
||||||
|
'docs-sdks-title': 'Client SDKs',
|
||||||
|
'docs-rest-title': 'REST APIs'
|
||||||
},
|
},
|
||||||
ar: {
|
ar: {
|
||||||
// Navbar (Landing)
|
// Navbar (Landing)
|
||||||
@@ -272,7 +276,11 @@ const i18n = {
|
|||||||
'modal-key-label': 'اسم المفتاح',
|
'modal-key-label': 'اسم المفتاح',
|
||||||
'cancel': 'إلغاء',
|
'cancel': 'إلغاء',
|
||||||
'guides-title': 'الأدلة برمجية',
|
'guides-title': 'الأدلة برمجية',
|
||||||
'side-sdks': 'المكتبات البرمجية (SDKs)'
|
'side-sdks': 'المكتبات البرمجية (SDKs)',
|
||||||
|
'docs-overview': 'نظرة عامة',
|
||||||
|
'docs-getting-started': 'البدء السريع',
|
||||||
|
'docs-sdks-title': 'المكتبات وحزم الـ SDK',
|
||||||
|
'docs-rest-title': 'واجهات الـ REST API'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
# MapSaaS Sovereign API Keys Template
|
||||||
|
MAP_SAAS_API_KEY=in_xxxxxxxxxxxxxxxxxxxxxxxx
|
||||||
|
GOOGLE_MAP_API_KEY=AIzaSyxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
# Miscellaneous
|
||||||
|
*.class
|
||||||
|
*.log
|
||||||
|
*.pyc
|
||||||
|
*.swp
|
||||||
|
.DS_Store
|
||||||
|
.atom/
|
||||||
|
.build/
|
||||||
|
.buildlog/
|
||||||
|
.history
|
||||||
|
.svn/
|
||||||
|
.swiftpm/
|
||||||
|
migrate_working_dir/
|
||||||
|
|
||||||
|
# IntelliJ related
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
*.iws
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
# The .vscode folder contains launch configuration and tasks you configure in
|
||||||
|
# VS Code which you may wish to be included in version control, so this line
|
||||||
|
# is commented out by default.
|
||||||
|
#.vscode/
|
||||||
|
|
||||||
|
# Flutter/Dart/Pub related
|
||||||
|
**/doc/api/
|
||||||
|
**/ios/Flutter/.last_build_id
|
||||||
|
.dart_tool/
|
||||||
|
.flutter-plugins-dependencies
|
||||||
|
.pub-cache/
|
||||||
|
.pub/
|
||||||
|
/build/
|
||||||
|
/coverage/
|
||||||
|
|
||||||
|
# Symbolication related
|
||||||
|
app.*.symbols
|
||||||
|
|
||||||
|
# Obfuscation related
|
||||||
|
app.*.map.json
|
||||||
|
|
||||||
|
# Android Studio will place build artifacts here
|
||||||
|
/android/app/debug
|
||||||
|
/android/app/profile
|
||||||
|
/android/app/release
|
||||||
|
|
||||||
|
# Environment variables & secrets
|
||||||
|
.env
|
||||||
|
.env.*
|
||||||
|
!.env.example
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
# This file tracks properties of this Flutter project.
|
||||||
|
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
||||||
|
#
|
||||||
|
# This file should be version controlled and should not be manually edited.
|
||||||
|
|
||||||
|
version:
|
||||||
|
revision: "ee80f08bbf97172ec030b8751ceab557177a34a6"
|
||||||
|
channel: "stable"
|
||||||
|
|
||||||
|
project_type: app
|
||||||
|
|
||||||
|
# Tracks metadata for the flutter migrate command
|
||||||
|
migration:
|
||||||
|
platforms:
|
||||||
|
- platform: root
|
||||||
|
create_revision: ee80f08bbf97172ec030b8751ceab557177a34a6
|
||||||
|
base_revision: ee80f08bbf97172ec030b8751ceab557177a34a6
|
||||||
|
- platform: android
|
||||||
|
create_revision: ee80f08bbf97172ec030b8751ceab557177a34a6
|
||||||
|
base_revision: ee80f08bbf97172ec030b8751ceab557177a34a6
|
||||||
|
- platform: ios
|
||||||
|
create_revision: ee80f08bbf97172ec030b8751ceab557177a34a6
|
||||||
|
base_revision: ee80f08bbf97172ec030b8751ceab557177a34a6
|
||||||
|
- platform: linux
|
||||||
|
create_revision: ee80f08bbf97172ec030b8751ceab557177a34a6
|
||||||
|
base_revision: ee80f08bbf97172ec030b8751ceab557177a34a6
|
||||||
|
- platform: macos
|
||||||
|
create_revision: ee80f08bbf97172ec030b8751ceab557177a34a6
|
||||||
|
base_revision: ee80f08bbf97172ec030b8751ceab557177a34a6
|
||||||
|
- platform: web
|
||||||
|
create_revision: ee80f08bbf97172ec030b8751ceab557177a34a6
|
||||||
|
base_revision: ee80f08bbf97172ec030b8751ceab557177a34a6
|
||||||
|
- platform: windows
|
||||||
|
create_revision: ee80f08bbf97172ec030b8751ceab557177a34a6
|
||||||
|
base_revision: ee80f08bbf97172ec030b8751ceab557177a34a6
|
||||||
|
|
||||||
|
# User provided section
|
||||||
|
|
||||||
|
# List of Local paths (relative to this file) that should be
|
||||||
|
# ignored by the migrate tool.
|
||||||
|
#
|
||||||
|
# Files that are not part of the templates will be ignored by default.
|
||||||
|
unmanaged_files:
|
||||||
|
- 'lib/main.dart'
|
||||||
|
- 'ios/Runner.xcodeproj/project.pbxproj'
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
# siro_maps
|
||||||
|
|
||||||
|
A new Flutter project.
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
This project is a starting point for a Flutter application.
|
||||||
|
|
||||||
|
A few resources to get you started if this is your first Flutter project:
|
||||||
|
|
||||||
|
- [Learn Flutter](https://docs.flutter.dev/get-started/learn-flutter)
|
||||||
|
- [Write your first Flutter app](https://docs.flutter.dev/get-started/codelab)
|
||||||
|
- [Flutter learning resources](https://docs.flutter.dev/reference/learning-resources)
|
||||||
|
|
||||||
|
For help getting started with Flutter development, view the
|
||||||
|
[online documentation](https://docs.flutter.dev/), which offers tutorials,
|
||||||
|
samples, guidance on mobile development, and a full API reference.
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
# This file configures the analyzer, which statically analyzes Dart code to
|
||||||
|
# check for errors, warnings, and lints.
|
||||||
|
#
|
||||||
|
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
|
||||||
|
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
|
||||||
|
# invoked from the command line by running `flutter analyze`.
|
||||||
|
|
||||||
|
# The following line activates a set of recommended lints for Flutter apps,
|
||||||
|
# packages, and plugins designed to encourage good coding practices.
|
||||||
|
include: package:flutter_lints/flutter.yaml
|
||||||
|
|
||||||
|
linter:
|
||||||
|
# The lint rules applied to this project can be customized in the
|
||||||
|
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
|
||||||
|
# included above or to enable additional rules. A list of all available lints
|
||||||
|
# and their documentation is published at https://dart.dev/lints.
|
||||||
|
#
|
||||||
|
# Instead of disabling a lint rule for the entire project in the
|
||||||
|
# section below, it can also be suppressed for a single line of code
|
||||||
|
# or a specific dart file by using the `// ignore: name_of_lint` and
|
||||||
|
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
|
||||||
|
# producing the lint.
|
||||||
|
rules:
|
||||||
|
avoid_print: false
|
||||||
|
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
|
||||||
|
|
||||||
|
# Additional information about this file can be found at
|
||||||
|
# https://dart.dev/guides/language/analysis-options
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
gradle-wrapper.jar
|
||||||
|
/.gradle
|
||||||
|
/captures/
|
||||||
|
/gradlew
|
||||||
|
/gradlew.bat
|
||||||
|
/local.properties
|
||||||
|
GeneratedPluginRegistrant.java
|
||||||
|
.cxx/
|
||||||
|
|
||||||
|
# Remember to never publicly share your keystore.
|
||||||
|
# See https://flutter.dev/to/reference-keystore
|
||||||
|
key.properties
|
||||||
|
**/*.keystore
|
||||||
|
**/*.jks
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
plugins {
|
||||||
|
id("com.android.application")
|
||||||
|
// The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
|
||||||
|
id("dev.flutter.flutter-gradle-plugin")
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
namespace = "com.siro_map.siro_maps"
|
||||||
|
compileSdk = flutter.compileSdkVersion
|
||||||
|
ndkVersion = flutter.ndkVersion
|
||||||
|
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility = JavaVersion.VERSION_17
|
||||||
|
targetCompatibility = JavaVersion.VERSION_17
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
||||||
|
applicationId = "com.siro_map.siro_maps"
|
||||||
|
// You can update the following values to match your application needs.
|
||||||
|
// For more information, see: https://flutter.dev/to/review-gradle-config.
|
||||||
|
minSdk = 23
|
||||||
|
targetSdk = flutter.targetSdkVersion
|
||||||
|
versionCode = flutter.versionCode
|
||||||
|
versionName = flutter.versionName
|
||||||
|
}
|
||||||
|
|
||||||
|
buildTypes {
|
||||||
|
release {
|
||||||
|
// TODO: Add your own signing config for the release build.
|
||||||
|
// Signing with the debug keys for now, so `flutter run --release` works.
|
||||||
|
signingConfig = signingConfigs.getByName("debug")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation("androidx.car.app:app:1.4.0")
|
||||||
|
}
|
||||||
|
|
||||||
|
kotlin {
|
||||||
|
compilerOptions {
|
||||||
|
jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
flutter {
|
||||||
|
source = "../.."
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<!-- The INTERNET permission is required for development. Specifically,
|
||||||
|
the Flutter tool needs it to communicate with the running application
|
||||||
|
to allow setting breakpoints, to provide hot reload, etc.
|
||||||
|
-->
|
||||||
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
|
</manifest>
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<!-- Permissions for High-Precision Navigation and Background Audio -->
|
||||||
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/>
|
||||||
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
|
||||||
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION"/>
|
||||||
|
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
|
||||||
|
<uses-permission android:name="android.permission.WAKE_LOCK"/>
|
||||||
|
|
||||||
|
<application
|
||||||
|
android:label="خرائط سيرو"
|
||||||
|
android:name="${applicationName}"
|
||||||
|
android:icon="@mipmap/ic_launcher">
|
||||||
|
<activity
|
||||||
|
android:name=".MainActivity"
|
||||||
|
android:exported="true"
|
||||||
|
android:launchMode="singleTop"
|
||||||
|
android:taskAffinity=""
|
||||||
|
android:theme="@style/LaunchTheme"
|
||||||
|
android:supportsPictureInPicture="true"
|
||||||
|
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||||
|
android:hardwareAccelerated="true"
|
||||||
|
android:windowSoftInputMode="adjustResize">
|
||||||
|
<!-- Specifies an Android theme to apply to this Activity as soon as
|
||||||
|
the Android process has started. This theme is visible to the user
|
||||||
|
while the Flutter UI initializes. After that, this theme continues
|
||||||
|
to determine the Window background behind the Flutter UI. -->
|
||||||
|
<meta-data
|
||||||
|
android:name="io.flutter.embedding.android.NormalTheme"
|
||||||
|
android:resource="@style/NormalTheme"
|
||||||
|
/>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN"/>
|
||||||
|
<category android:name="android.intent.category.LAUNCHER"/>
|
||||||
|
</intent-filter>
|
||||||
|
|
||||||
|
<!-- Custom URI Scheme: siromaps:// -->
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
<data android:scheme="siromaps" />
|
||||||
|
</intent-filter>
|
||||||
|
|
||||||
|
<!-- Standard Geo Scheme: geo:lat,lng -->
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
<data android:scheme="geo" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
<!-- Don't delete the meta-data below.
|
||||||
|
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
|
||||||
|
<meta-data
|
||||||
|
android:name="flutterEmbedding"
|
||||||
|
android:value="2" />
|
||||||
|
|
||||||
|
<!-- Android Auto Navigation Service -->
|
||||||
|
<service
|
||||||
|
android:name=".car.SiroCarAppService"
|
||||||
|
android:exported="true">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="androidx.car.app.CarAppService" />
|
||||||
|
<category android:name="androidx.car.app.category.NAVIGATION"/>
|
||||||
|
</intent-filter>
|
||||||
|
</service>
|
||||||
|
|
||||||
|
<meta-data
|
||||||
|
android:name="androidx.car.app.minCarApiLevel"
|
||||||
|
android:value="1" />
|
||||||
|
<meta-data
|
||||||
|
android:name="com.google.android.gms.car.application"
|
||||||
|
android:resource="@xml/automotive_app_desc" />
|
||||||
|
</application>
|
||||||
|
<!-- Required to query activities that can process text, see:
|
||||||
|
https://developer.android.com/training/package-visibility and
|
||||||
|
https://developer.android.com/reference/android/content/Intent#ACTION_PROCESS_TEXT.
|
||||||
|
|
||||||
|
In particular, this is used by the Flutter engine in io.flutter.plugin.text.ProcessTextPlugin. -->
|
||||||
|
<queries>
|
||||||
|
<intent>
|
||||||
|
<action android:name="android.intent.action.PROCESS_TEXT"/>
|
||||||
|
<data android:mimeType="text/plain"/>
|
||||||
|
</intent>
|
||||||
|
</queries>
|
||||||
|
</manifest>
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
package com.siro_map.siro_maps
|
||||||
|
|
||||||
|
import com.siro_map.siro_maps.car.CarNavigationState
|
||||||
|
import com.siro_map.siro_maps.car.SiroCarAppService
|
||||||
|
import io.flutter.embedding.android.FlutterActivity
|
||||||
|
import io.flutter.embedding.engine.FlutterEngine
|
||||||
|
import io.flutter.plugin.common.MethodChannel
|
||||||
|
|
||||||
|
class MainActivity : FlutterActivity() {
|
||||||
|
private val CHANNEL = "com.siro.siro_maps/car_navigation"
|
||||||
|
|
||||||
|
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
|
||||||
|
super.configureFlutterEngine(flutterEngine)
|
||||||
|
|
||||||
|
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result ->
|
||||||
|
when (call.method) {
|
||||||
|
"isCarAppConnected" -> {
|
||||||
|
result.success(SiroCarAppService.isConnected)
|
||||||
|
}
|
||||||
|
"updateNavState" -> {
|
||||||
|
try {
|
||||||
|
val lat = call.argument<Double>("lat") ?: 0.0
|
||||||
|
val lng = call.argument<Double>("lng") ?: 0.0
|
||||||
|
val bearing = call.argument<Double>("bearing") ?: 0.0
|
||||||
|
val speed = call.argument<Double>("speed") ?: 0.0
|
||||||
|
val instruction = call.argument<String>("instruction") ?: ""
|
||||||
|
val distanceToStep = call.argument<Double>("distanceToStep") ?: 0.0
|
||||||
|
val totalDistance = call.argument<Double>("totalDistance") ?: 0.0
|
||||||
|
val eta = call.argument<Double>("eta") ?: 0.0
|
||||||
|
val maneuver = call.argument<Int>("maneuver") ?: 0
|
||||||
|
val isNavigating = call.argument<Boolean>("isNavigating") ?: false
|
||||||
|
val isMapDarkMode = call.argument<Boolean>("isMapDarkMode") ?: false
|
||||||
|
|
||||||
|
val newState = CarNavigationState(
|
||||||
|
lat = lat,
|
||||||
|
lng = lng,
|
||||||
|
bearing = bearing,
|
||||||
|
speed = speed,
|
||||||
|
instruction = instruction,
|
||||||
|
distanceToStep = distanceToStep,
|
||||||
|
totalDistance = totalDistance,
|
||||||
|
eta = eta,
|
||||||
|
maneuver = maneuver,
|
||||||
|
isNavigating = isNavigating,
|
||||||
|
isMapDarkMode = isMapDarkMode
|
||||||
|
)
|
||||||
|
SiroCarAppService.updateNavState(newState)
|
||||||
|
result.success(true)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
result.error("UPDATE_FAILED", e.localizedMessage, null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"stopNavigation" -> {
|
||||||
|
SiroCarAppService.stopNavigation()
|
||||||
|
result.success(true)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
result.notImplemented()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
package com.siro_map.siro_maps.car
|
||||||
|
|
||||||
|
data class CarNavigationState(
|
||||||
|
val lat: Double = 0.0,
|
||||||
|
val lng: Double = 0.0,
|
||||||
|
val bearing: Double = 0.0,
|
||||||
|
val speed: Double = 0.0,
|
||||||
|
val instruction: String = "",
|
||||||
|
val distanceToStep: Double = 0.0,
|
||||||
|
val totalDistance: Double = 0.0,
|
||||||
|
val eta: Double = 0.0,
|
||||||
|
val maneuver: Int = 0,
|
||||||
|
val isNavigating: Boolean = false,
|
||||||
|
val isMapDarkMode: Boolean = false
|
||||||
|
) {
|
||||||
|
val formattedSpeed: String
|
||||||
|
get() = "${speed.toInt()} كم/س"
|
||||||
|
|
||||||
|
val formattedRemainingDistance: String
|
||||||
|
get() = if (totalDistance >= 1000) {
|
||||||
|
String.format("%.1f كم", totalDistance / 1000.0)
|
||||||
|
} else {
|
||||||
|
"${totalDistance.toInt()} م"
|
||||||
|
}
|
||||||
|
|
||||||
|
val formattedDistanceToStep: String
|
||||||
|
get() = if (distanceToStep >= 1000) {
|
||||||
|
String.format("بعد %.1f كم", distanceToStep / 1000.0)
|
||||||
|
} else {
|
||||||
|
"بعد ${distanceToStep.toInt()} م"
|
||||||
|
}
|
||||||
|
|
||||||
|
val formattedRemainingDuration: String
|
||||||
|
get() {
|
||||||
|
val minutes = (eta / 60.0).toInt()
|
||||||
|
return if (minutes >= 60) {
|
||||||
|
val hours = minutes / 60
|
||||||
|
val remMin = minutes % 60
|
||||||
|
"$hours س $remMin د"
|
||||||
|
} else {
|
||||||
|
"$minutes دقيقة"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
package com.siro_map.siro_maps.car
|
||||||
|
|
||||||
|
import androidx.car.app.CarAppService
|
||||||
|
import androidx.car.app.Session
|
||||||
|
import androidx.car.app.validation.HostValidator
|
||||||
|
|
||||||
|
class SiroCarAppService : CarAppService() {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
var currentNavState: CarNavigationState = CarNavigationState()
|
||||||
|
var activeSession: SiroCarSession? = null
|
||||||
|
|
||||||
|
fun updateNavState(state: CarNavigationState) {
|
||||||
|
currentNavState = state
|
||||||
|
activeSession?.requestScreenUpdate()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun stopNavigation() {
|
||||||
|
currentNavState = currentNavState.copy(isNavigating = false)
|
||||||
|
activeSession?.requestScreenUpdate()
|
||||||
|
}
|
||||||
|
|
||||||
|
val isConnected: Boolean
|
||||||
|
get() = activeSession != null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun createHostValidator(): HostValidator {
|
||||||
|
return HostValidator.ALLOW_ALL_HOSTS_VALIDATOR
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateSession(): Session {
|
||||||
|
val session = SiroCarSession()
|
||||||
|
activeSession = session
|
||||||
|
return session
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
activeSession = null
|
||||||
|
super.onDestroy()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package com.siro_map.siro_maps.car
|
||||||
|
|
||||||
|
import android.content.Intent
|
||||||
|
import androidx.car.app.Screen
|
||||||
|
import androidx.car.app.Session
|
||||||
|
|
||||||
|
class SiroCarSession : Session() {
|
||||||
|
private var activeScreen: SiroNavScreen? = null
|
||||||
|
|
||||||
|
init {
|
||||||
|
SiroCarAppService.activeSession = this
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateScreen(intent: Intent): Screen {
|
||||||
|
val screen = SiroNavScreen(carContext)
|
||||||
|
activeScreen = screen
|
||||||
|
return screen
|
||||||
|
}
|
||||||
|
|
||||||
|
fun requestScreenUpdate() {
|
||||||
|
activeScreen?.invalidate()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
package com.siro_map.siro_maps.car
|
||||||
|
|
||||||
|
import androidx.car.app.CarContext
|
||||||
|
import androidx.car.app.Screen
|
||||||
|
import androidx.car.app.model.*
|
||||||
|
|
||||||
|
class SiroNavScreen(carContext: CarContext) : Screen(carContext) {
|
||||||
|
|
||||||
|
override fun onGetTemplate(): Template {
|
||||||
|
val state = SiroCarAppService.currentNavState
|
||||||
|
|
||||||
|
return if (state.isNavigating) {
|
||||||
|
buildActiveNavTemplate(state)
|
||||||
|
} else {
|
||||||
|
buildIdleTemplate(state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun buildActiveNavTemplate(state: CarNavigationState): Template {
|
||||||
|
val paneBuilder = Pane.Builder()
|
||||||
|
|
||||||
|
// 1. Current instruction & distance
|
||||||
|
paneBuilder.addRow(
|
||||||
|
Row.Builder()
|
||||||
|
.setTitle(state.instruction.ifEmpty { "تابع السير نحو الوجهة" })
|
||||||
|
.addText(state.formattedDistanceToStep)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
|
||||||
|
// 2. Trip summary (Distance and Duration)
|
||||||
|
paneBuilder.addRow(
|
||||||
|
Row.Builder()
|
||||||
|
.setTitle("المسار المتبقي")
|
||||||
|
.addText("${state.formattedRemainingDistance} • الوصول خلال ${state.formattedRemainingDuration}")
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
|
||||||
|
// 3. Live Speed
|
||||||
|
paneBuilder.addRow(
|
||||||
|
Row.Builder()
|
||||||
|
.setTitle("السرعة الحالية")
|
||||||
|
.addText(state.formattedSpeed)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
|
||||||
|
// Stop navigation action button
|
||||||
|
paneBuilder.addAction(
|
||||||
|
Action.Builder()
|
||||||
|
.setTitle("إنهاء الملاحة")
|
||||||
|
.setOnClickListener {
|
||||||
|
SiroCarAppService.stopNavigation()
|
||||||
|
invalidate()
|
||||||
|
}
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
|
||||||
|
return PaneTemplate.Builder(paneBuilder.build())
|
||||||
|
.setTitle("ملاحة سيرو • جارية الآن")
|
||||||
|
.setHeaderAction(Action.APP_ICON)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun buildIdleTemplate(state: CarNavigationState): Template {
|
||||||
|
val paneBuilder = Pane.Builder()
|
||||||
|
|
||||||
|
paneBuilder.addRow(
|
||||||
|
Row.Builder()
|
||||||
|
.setTitle("خرائط سيرو السيادية (Siro Maps)")
|
||||||
|
.addText("التطبيق متصل بنجاح بشاشة السيارة")
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
|
||||||
|
paneBuilder.addRow(
|
||||||
|
Row.Builder()
|
||||||
|
.setTitle("وضع القيادة الحر")
|
||||||
|
.addText("السرعة: ${state.formattedSpeed}")
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
|
||||||
|
paneBuilder.addRow(
|
||||||
|
Row.Builder()
|
||||||
|
.setTitle("بدء الملاحة")
|
||||||
|
.addText("حدد وجهتك من شاشة الهاتف للانتقال الفوري إلى وضع الملاحة ثلاثية الأبعاد")
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
|
||||||
|
return PaneTemplate.Builder(paneBuilder.build())
|
||||||
|
.setTitle("خرائط سيرو")
|
||||||
|
.setHeaderAction(Action.APP_ICON)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Modify this file to customize your launch splash screen -->
|
||||||
|
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:drawable="?android:colorBackground" />
|
||||||
|
|
||||||
|
<!-- You can insert your own image assets here -->
|
||||||
|
<!-- <item>
|
||||||
|
<bitmap
|
||||||
|
android:gravity="center"
|
||||||
|
android:src="@mipmap/launch_image" />
|
||||||
|
</item> -->
|
||||||
|
</layer-list>
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Modify this file to customize your launch splash screen -->
|
||||||
|
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:drawable="@android:color/white" />
|
||||||
|
|
||||||
|
<!-- You can insert your own image assets here -->
|
||||||
|
<!-- <item>
|
||||||
|
<bitmap
|
||||||
|
android:gravity="center"
|
||||||
|
android:src="@mipmap/launch_image" />
|
||||||
|
</item> -->
|
||||||
|
</layer-list>
|
||||||
|
After Width: | Height: | Size: 7.1 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 40 KiB |
@@ -0,0 +1,18 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
|
||||||
|
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
|
||||||
|
<!-- Show a splash screen on the activity. Automatically removed when
|
||||||
|
the Flutter engine draws its first frame -->
|
||||||
|
<item name="android:windowBackground">@drawable/launch_background</item>
|
||||||
|
</style>
|
||||||
|
<!-- Theme applied to the Android Window as soon as the process has started.
|
||||||
|
This theme determines the color of the Android Window while your
|
||||||
|
Flutter UI initializes, as well as behind your Flutter UI while its
|
||||||
|
running.
|
||||||
|
|
||||||
|
This Theme is only used starting with V2 of Flutter's Android embedding. -->
|
||||||
|
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
|
||||||
|
<item name="android:windowBackground">?android:colorBackground</item>
|
||||||
|
</style>
|
||||||
|
</resources>
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
|
||||||
|
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
|
||||||
|
<!-- Show a splash screen on the activity. Automatically removed when
|
||||||
|
the Flutter engine draws its first frame -->
|
||||||
|
<item name="android:windowBackground">@drawable/launch_background</item>
|
||||||
|
</style>
|
||||||
|
<!-- Theme applied to the Android Window as soon as the process has started.
|
||||||
|
This theme determines the color of the Android Window while your
|
||||||
|
Flutter UI initializes, as well as behind your Flutter UI while its
|
||||||
|
running.
|
||||||
|
|
||||||
|
This Theme is only used starting with V2 of Flutter's Android embedding. -->
|
||||||
|
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
|
||||||
|
<item name="android:windowBackground">?android:colorBackground</item>
|
||||||
|
</style>
|
||||||
|
</resources>
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<automotiveApp>
|
||||||
|
<uses name="navigation" />
|
||||||
|
</automotiveApp>
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<!-- The INTERNET permission is required for development. Specifically,
|
||||||
|
the Flutter tool needs it to communicate with the running application
|
||||||
|
to allow setting breakpoints, to provide hot reload, etc.
|
||||||
|
-->
|
||||||
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
|
</manifest>
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
allprojects {
|
||||||
|
repositories {
|
||||||
|
google()
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val newBuildDir: Directory =
|
||||||
|
rootProject.layout.buildDirectory
|
||||||
|
.dir("../../build")
|
||||||
|
.get()
|
||||||
|
rootProject.layout.buildDirectory.value(newBuildDir)
|
||||||
|
|
||||||
|
subprojects {
|
||||||
|
val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name)
|
||||||
|
project.layout.buildDirectory.value(newSubprojectBuildDir)
|
||||||
|
}
|
||||||
|
subprojects {
|
||||||
|
project.evaluationDependsOn(":app")
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.register<Delete>("clean") {
|
||||||
|
delete(rootProject.layout.buildDirectory)
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError
|
||||||
|
android.useAndroidX=true
|
||||||
|
# This newDsl flag was added by the Flutter template
|
||||||
|
android.newDsl=false
|
||||||
|
# This builtInKotlin flag was added by the Flutter template
|
||||||
|
android.builtInKotlin=false
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
distributionBase=GRADLE_USER_HOME
|
||||||
|
distributionPath=wrapper/dists
|
||||||
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
zipStorePath=wrapper/dists
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-all.zip
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
pluginManagement {
|
||||||
|
val flutterSdkPath =
|
||||||
|
run {
|
||||||
|
val properties = java.util.Properties()
|
||||||
|
file("local.properties").inputStream().use { properties.load(it) }
|
||||||
|
val flutterSdkPath = properties.getProperty("flutter.sdk")
|
||||||
|
require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" }
|
||||||
|
flutterSdkPath
|
||||||
|
}
|
||||||
|
|
||||||
|
includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
google()
|
||||||
|
mavenCentral()
|
||||||
|
gradlePluginPortal()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
id("dev.flutter.flutter-plugin-loader") version "1.0.0"
|
||||||
|
id("com.android.application") version "9.0.1" apply false
|
||||||
|
id("org.jetbrains.kotlin.android") version "2.3.20" apply false
|
||||||
|
}
|
||||||
|
|
||||||
|
include(":app")
|
||||||
|
After Width: | Height: | Size: 3.9 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 7.0 KiB |
|
After Width: | Height: | Size: 1.0 MiB |
@@ -0,0 +1,34 @@
|
|||||||
|
**/dgph
|
||||||
|
*.mode1v3
|
||||||
|
*.mode2v3
|
||||||
|
*.moved-aside
|
||||||
|
*.pbxuser
|
||||||
|
*.perspectivev3
|
||||||
|
**/*sync/
|
||||||
|
.sconsign.dblite
|
||||||
|
.tags*
|
||||||
|
**/.vagrant/
|
||||||
|
**/DerivedData/
|
||||||
|
Icon?
|
||||||
|
**/Pods/
|
||||||
|
**/.symlinks/
|
||||||
|
profile
|
||||||
|
xcuserdata
|
||||||
|
**/.generated/
|
||||||
|
Flutter/App.framework
|
||||||
|
Flutter/Flutter.framework
|
||||||
|
Flutter/Flutter.podspec
|
||||||
|
Flutter/Generated.xcconfig
|
||||||
|
Flutter/ephemeral/
|
||||||
|
Flutter/app.flx
|
||||||
|
Flutter/app.zip
|
||||||
|
Flutter/flutter_assets/
|
||||||
|
Flutter/flutter_export_environment.sh
|
||||||
|
ServiceDefinitions.json
|
||||||
|
Runner/GeneratedPluginRegistrant.*
|
||||||
|
|
||||||
|
# Exceptions to above rules.
|
||||||
|
!default.mode1v3
|
||||||
|
!default.mode2v3
|
||||||
|
!default.pbxuser
|
||||||
|
!default.perspectivev3
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
|
<string>en</string>
|
||||||
|
<key>CFBundleExecutable</key>
|
||||||
|
<string>App</string>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
|
<string>io.flutter.flutter.app</string>
|
||||||
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
|
<string>6.0</string>
|
||||||
|
<key>CFBundleName</key>
|
||||||
|
<string>App</string>
|
||||||
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>FMWK</string>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>1.0</string>
|
||||||
|
<key>CFBundleSignature</key>
|
||||||
|
<string>????</string>
|
||||||
|
<key>CFBundleVersion</key>
|
||||||
|
<string>1.0</string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
|
||||||
|
#include "Generated.xcconfig"
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
|
||||||
|
#include "Generated.xcconfig"
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
platform :ios, '13.0'
|
||||||
|
|
||||||
|
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
|
||||||
|
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
|
||||||
|
|
||||||
|
project 'Runner', {
|
||||||
|
'Debug' => :debug,
|
||||||
|
'Profile' => :release,
|
||||||
|
'Release' => :release,
|
||||||
|
}
|
||||||
|
|
||||||
|
def flutter_root
|
||||||
|
generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
|
||||||
|
unless File.exist?(generated_xcode_build_settings_path)
|
||||||
|
raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
|
||||||
|
end
|
||||||
|
|
||||||
|
File.foreach(generated_xcode_build_settings_path) do |line|
|
||||||
|
matches = line.match(/FLUTTER_ROOT\=(.*)/)
|
||||||
|
return matches[1].strip if matches
|
||||||
|
end
|
||||||
|
raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
|
||||||
|
end
|
||||||
|
|
||||||
|
require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
|
||||||
|
|
||||||
|
flutter_ios_podfile_setup
|
||||||
|
|
||||||
|
target 'Runner' do
|
||||||
|
use_frameworks!
|
||||||
|
|
||||||
|
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
|
||||||
|
target 'RunnerTests' do
|
||||||
|
inherit! :search_paths
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
post_install do |installer|
|
||||||
|
installer.pods_project.targets.each do |target|
|
||||||
|
flutter_additional_ios_build_settings(target)
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
PODS:
|
||||||
|
- connectivity_plus (0.0.1):
|
||||||
|
- Flutter
|
||||||
|
- Flutter (1.0.0)
|
||||||
|
- flutter_tts (0.0.1):
|
||||||
|
- Flutter
|
||||||
|
- geolocator_apple (1.2.0):
|
||||||
|
- Flutter
|
||||||
|
- FlutterMacOS
|
||||||
|
- MapLibre (6.19.1)
|
||||||
|
- maplibre_gl (0.25.0):
|
||||||
|
- Flutter
|
||||||
|
- MapLibre (= 6.19.1)
|
||||||
|
- package_info_plus (0.4.5):
|
||||||
|
- Flutter
|
||||||
|
- permission_handler_apple (9.4.8):
|
||||||
|
- Flutter
|
||||||
|
- shared_preferences_foundation (0.0.1):
|
||||||
|
- Flutter
|
||||||
|
- FlutterMacOS
|
||||||
|
|
||||||
|
DEPENDENCIES:
|
||||||
|
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`)
|
||||||
|
- Flutter (from `Flutter`)
|
||||||
|
- flutter_tts (from `.symlinks/plugins/flutter_tts/ios`)
|
||||||
|
- geolocator_apple (from `.symlinks/plugins/geolocator_apple/darwin`)
|
||||||
|
- maplibre_gl (from `.symlinks/plugins/maplibre_gl/ios`)
|
||||||
|
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
|
||||||
|
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
|
||||||
|
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
|
||||||
|
|
||||||
|
SPEC REPOS:
|
||||||
|
trunk:
|
||||||
|
- MapLibre
|
||||||
|
|
||||||
|
EXTERNAL SOURCES:
|
||||||
|
connectivity_plus:
|
||||||
|
:path: ".symlinks/plugins/connectivity_plus/ios"
|
||||||
|
Flutter:
|
||||||
|
:path: Flutter
|
||||||
|
flutter_tts:
|
||||||
|
:path: ".symlinks/plugins/flutter_tts/ios"
|
||||||
|
geolocator_apple:
|
||||||
|
:path: ".symlinks/plugins/geolocator_apple/darwin"
|
||||||
|
maplibre_gl:
|
||||||
|
:path: ".symlinks/plugins/maplibre_gl/ios"
|
||||||
|
package_info_plus:
|
||||||
|
:path: ".symlinks/plugins/package_info_plus/ios"
|
||||||
|
permission_handler_apple:
|
||||||
|
:path: ".symlinks/plugins/permission_handler_apple/ios"
|
||||||
|
shared_preferences_foundation:
|
||||||
|
:path: ".symlinks/plugins/shared_preferences_foundation/darwin"
|
||||||
|
|
||||||
|
SPEC CHECKSUMS:
|
||||||
|
connectivity_plus: cb623214f4e1f6ef8fe7403d580fdad517d2f7dd
|
||||||
|
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
|
||||||
|
flutter_tts: 35ac3c7d42412733e795ea96ad2d7e05d0a75113
|
||||||
|
geolocator_apple: ab36aa0e8b7d7a2d7639b3b4e48308394e8cef5e
|
||||||
|
MapLibre: 7f24faba45439f80ccb0f83393c29fa32cb81952
|
||||||
|
maplibre_gl: a2114567cbd1065866614fbd34dfb75ab782aaa2
|
||||||
|
package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499
|
||||||
|
permission_handler_apple: 92d754bbaa7361d436db2d6c3c1c2a0fdcec462e
|
||||||
|
shared_preferences_foundation: 7036424c3d8ec98dfe75ff1667cb0cd531ec82bb
|
||||||
|
|
||||||
|
PODFILE CHECKSUM: f8c2dcdfb50bb67645580d28a6bf814fca30bdec
|
||||||
|
|
||||||
|
COCOAPODS: 1.16.2
|
||||||
@@ -0,0 +1,753 @@
|
|||||||
|
// !$*UTF8*$!
|
||||||
|
{
|
||||||
|
archiveVersion = 1;
|
||||||
|
classes = {
|
||||||
|
};
|
||||||
|
objectVersion = 54;
|
||||||
|
objects = {
|
||||||
|
|
||||||
|
/* Begin PBXBuildFile section */
|
||||||
|
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
|
||||||
|
2F67581D0AEBDF779F264C3F /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8634A31EB55F1034379B54E0 /* Pods_RunnerTests.framework */; };
|
||||||
|
331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; };
|
||||||
|
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
|
||||||
|
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
|
||||||
|
7884E8682EC3CC0700C636F2 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7884E8672EC3CC0400C636F2 /* SceneDelegate.swift */; };
|
||||||
|
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
|
||||||
|
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
|
||||||
|
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
|
||||||
|
DED4DFD9EC2DAE58D70CA786 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C960332103BB131A82D9B2E2 /* Pods_Runner.framework */; };
|
||||||
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
|
/* Begin PBXContainerItemProxy section */
|
||||||
|
331C8085294A63A400263BE5 /* PBXContainerItemProxy */ = {
|
||||||
|
isa = PBXContainerItemProxy;
|
||||||
|
containerPortal = 97C146E61CF9000F007C117D /* Project object */;
|
||||||
|
proxyType = 1;
|
||||||
|
remoteGlobalIDString = 97C146ED1CF9000F007C117D;
|
||||||
|
remoteInfo = Runner;
|
||||||
|
};
|
||||||
|
/* End PBXContainerItemProxy section */
|
||||||
|
|
||||||
|
/* Begin PBXCopyFilesBuildPhase section */
|
||||||
|
9705A1C41CF9048500538489 /* Embed Frameworks */ = {
|
||||||
|
isa = PBXCopyFilesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
dstPath = "";
|
||||||
|
dstSubfolderSpec = 10;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
name = "Embed Frameworks";
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXCopyFilesBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXFileReference section */
|
||||||
|
06BE9F46C73ECD841329F410 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
|
||||||
|
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
|
||||||
|
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
|
||||||
|
331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; };
|
||||||
|
331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
|
||||||
|
5337EC43621A0457D008A10C /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
|
||||||
|
639DEB58BA3D8FCBAC93E4BE /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = "<group>"; };
|
||||||
|
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||||
|
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||||
|
7884E8672EC3CC0400C636F2 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
|
||||||
|
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
|
||||||
|
8634A31EB55F1034379B54E0 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
8D4208325A3C7F39D4F140D6 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = "<group>"; };
|
||||||
|
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
|
||||||
|
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
|
||||||
|
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
|
||||||
|
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||||
|
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||||
|
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
|
C960332103BB131A82D9B2E2 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
DB7D36DEEE9ACCB065F3D420 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = "<group>"; };
|
||||||
|
DD4D1B74E59D3F8005FB6EC7 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
|
||||||
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
|
3AC28CA628E0AC26E0102A03 /* Frameworks */ = {
|
||||||
|
isa = PBXFrameworksBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
2F67581D0AEBDF779F264C3F /* Pods_RunnerTests.framework in Frameworks */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
97C146EB1CF9000F007C117D /* Frameworks */ = {
|
||||||
|
isa = PBXFrameworksBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
DED4DFD9EC2DAE58D70CA786 /* Pods_Runner.framework in Frameworks */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXFrameworksBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXGroup section */
|
||||||
|
331C8082294A63A400263BE5 /* RunnerTests */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
331C807B294A618700263BE5 /* RunnerTests.swift */,
|
||||||
|
);
|
||||||
|
path = RunnerTests;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
5D024B54A595292C31EE8831 /* Frameworks */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
C960332103BB131A82D9B2E2 /* Pods_Runner.framework */,
|
||||||
|
8634A31EB55F1034379B54E0 /* Pods_RunnerTests.framework */,
|
||||||
|
);
|
||||||
|
name = Frameworks;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
941D76653E87671DE9664C02 /* Pods */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
DD4D1B74E59D3F8005FB6EC7 /* Pods-Runner.debug.xcconfig */,
|
||||||
|
5337EC43621A0457D008A10C /* Pods-Runner.release.xcconfig */,
|
||||||
|
06BE9F46C73ECD841329F410 /* Pods-Runner.profile.xcconfig */,
|
||||||
|
DB7D36DEEE9ACCB065F3D420 /* Pods-RunnerTests.debug.xcconfig */,
|
||||||
|
639DEB58BA3D8FCBAC93E4BE /* Pods-RunnerTests.release.xcconfig */,
|
||||||
|
8D4208325A3C7F39D4F140D6 /* Pods-RunnerTests.profile.xcconfig */,
|
||||||
|
);
|
||||||
|
name = Pods;
|
||||||
|
path = Pods;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
9740EEB11CF90186004384FC /* Flutter */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
|
||||||
|
9740EEB21CF90195004384FC /* Debug.xcconfig */,
|
||||||
|
7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
|
||||||
|
9740EEB31CF90195004384FC /* Generated.xcconfig */,
|
||||||
|
);
|
||||||
|
name = Flutter;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
97C146E51CF9000F007C117D = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
9740EEB11CF90186004384FC /* Flutter */,
|
||||||
|
97C146F01CF9000F007C117D /* Runner */,
|
||||||
|
97C146EF1CF9000F007C117D /* Products */,
|
||||||
|
331C8082294A63A400263BE5 /* RunnerTests */,
|
||||||
|
941D76653E87671DE9664C02 /* Pods */,
|
||||||
|
5D024B54A595292C31EE8831 /* Frameworks */,
|
||||||
|
);
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
97C146EF1CF9000F007C117D /* Products */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
97C146EE1CF9000F007C117D /* Runner.app */,
|
||||||
|
331C8081294A63A400263BE5 /* RunnerTests.xctest */,
|
||||||
|
);
|
||||||
|
name = Products;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
97C146F01CF9000F007C117D /* Runner */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
97C146FA1CF9000F007C117D /* Main.storyboard */,
|
||||||
|
97C146FD1CF9000F007C117D /* Assets.xcassets */,
|
||||||
|
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
|
||||||
|
97C147021CF9000F007C117D /* Info.plist */,
|
||||||
|
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
|
||||||
|
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
|
||||||
|
74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
|
||||||
|
7884E8672EC3CC0400C636F2 /* SceneDelegate.swift */,
|
||||||
|
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
|
||||||
|
);
|
||||||
|
path = Runner;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
/* End PBXGroup section */
|
||||||
|
|
||||||
|
/* Begin PBXNativeTarget section */
|
||||||
|
331C8080294A63A400263BE5 /* RunnerTests */ = {
|
||||||
|
isa = PBXNativeTarget;
|
||||||
|
buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
|
||||||
|
buildPhases = (
|
||||||
|
23F448731FA70C6A69203445 /* [CP] Check Pods Manifest.lock */,
|
||||||
|
331C807D294A63A400263BE5 /* Sources */,
|
||||||
|
331C807F294A63A400263BE5 /* Resources */,
|
||||||
|
3AC28CA628E0AC26E0102A03 /* Frameworks */,
|
||||||
|
);
|
||||||
|
buildRules = (
|
||||||
|
);
|
||||||
|
dependencies = (
|
||||||
|
331C8086294A63A400263BE5 /* PBXTargetDependency */,
|
||||||
|
);
|
||||||
|
name = RunnerTests;
|
||||||
|
productName = RunnerTests;
|
||||||
|
productReference = 331C8081294A63A400263BE5 /* RunnerTests.xctest */;
|
||||||
|
productType = "com.apple.product-type.bundle.unit-test";
|
||||||
|
};
|
||||||
|
97C146ED1CF9000F007C117D /* Runner */ = {
|
||||||
|
isa = PBXNativeTarget;
|
||||||
|
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
|
||||||
|
buildPhases = (
|
||||||
|
C7736DBBDF22AFA1F0E3CE16 /* [CP] Check Pods Manifest.lock */,
|
||||||
|
9740EEB61CF901F6004384FC /* Run Script */,
|
||||||
|
97C146EA1CF9000F007C117D /* Sources */,
|
||||||
|
97C146EB1CF9000F007C117D /* Frameworks */,
|
||||||
|
97C146EC1CF9000F007C117D /* Resources */,
|
||||||
|
9705A1C41CF9048500538489 /* Embed Frameworks */,
|
||||||
|
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
|
||||||
|
39F85057EEB25B27A1784669 /* [CP] Embed Pods Frameworks */,
|
||||||
|
E7A3E39EC6A7EA8ED85523E2 /* [CP] Copy Pods Resources */,
|
||||||
|
);
|
||||||
|
buildRules = (
|
||||||
|
);
|
||||||
|
dependencies = (
|
||||||
|
);
|
||||||
|
name = Runner;
|
||||||
|
productName = Runner;
|
||||||
|
productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
|
||||||
|
productType = "com.apple.product-type.application";
|
||||||
|
};
|
||||||
|
/* End PBXNativeTarget section */
|
||||||
|
|
||||||
|
/* Begin PBXProject section */
|
||||||
|
97C146E61CF9000F007C117D /* Project object */ = {
|
||||||
|
isa = PBXProject;
|
||||||
|
attributes = {
|
||||||
|
BuildIndependentTargetsInParallel = YES;
|
||||||
|
LastUpgradeCheck = 1510;
|
||||||
|
ORGANIZATIONNAME = "";
|
||||||
|
TargetAttributes = {
|
||||||
|
331C8080294A63A400263BE5 = {
|
||||||
|
CreatedOnToolsVersion = 14.0;
|
||||||
|
TestTargetID = 97C146ED1CF9000F007C117D;
|
||||||
|
};
|
||||||
|
97C146ED1CF9000F007C117D = {
|
||||||
|
CreatedOnToolsVersion = 7.3.1;
|
||||||
|
LastSwiftMigration = 1100;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
|
||||||
|
compatibilityVersion = "Xcode 9.3";
|
||||||
|
developmentRegion = en;
|
||||||
|
hasScannedForEncodings = 0;
|
||||||
|
knownRegions = (
|
||||||
|
en,
|
||||||
|
Base,
|
||||||
|
);
|
||||||
|
mainGroup = 97C146E51CF9000F007C117D;
|
||||||
|
productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
|
||||||
|
projectDirPath = "";
|
||||||
|
projectRoot = "";
|
||||||
|
targets = (
|
||||||
|
97C146ED1CF9000F007C117D /* Runner */,
|
||||||
|
331C8080294A63A400263BE5 /* RunnerTests */,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
/* End PBXProject section */
|
||||||
|
|
||||||
|
/* Begin PBXResourcesBuildPhase section */
|
||||||
|
331C807F294A63A400263BE5 /* Resources */ = {
|
||||||
|
isa = PBXResourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
97C146EC1CF9000F007C117D /* Resources */ = {
|
||||||
|
isa = PBXResourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
|
||||||
|
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
|
||||||
|
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
|
||||||
|
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXResourcesBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXShellScriptBuildPhase section */
|
||||||
|
23F448731FA70C6A69203445 /* [CP] Check Pods Manifest.lock */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputFileListPaths = (
|
||||||
|
);
|
||||||
|
inputPaths = (
|
||||||
|
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
||||||
|
"${PODS_ROOT}/Manifest.lock",
|
||||||
|
);
|
||||||
|
name = "[CP] Check Pods Manifest.lock";
|
||||||
|
outputFileListPaths = (
|
||||||
|
);
|
||||||
|
outputPaths = (
|
||||||
|
"$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt",
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||||
|
showEnvVarsInLog = 0;
|
||||||
|
};
|
||||||
|
39F85057EEB25B27A1784669 /* [CP] Embed Pods Frameworks */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputFileListPaths = (
|
||||||
|
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
|
||||||
|
);
|
||||||
|
name = "[CP] Embed Pods Frameworks";
|
||||||
|
outputFileListPaths = (
|
||||||
|
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
|
||||||
|
showEnvVarsInLog = 0;
|
||||||
|
};
|
||||||
|
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
alwaysOutOfDate = 1;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputPaths = (
|
||||||
|
"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}",
|
||||||
|
);
|
||||||
|
name = "Thin Binary";
|
||||||
|
outputPaths = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
|
||||||
|
};
|
||||||
|
9740EEB61CF901F6004384FC /* Run Script */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
alwaysOutOfDate = 1;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputPaths = (
|
||||||
|
);
|
||||||
|
name = "Run Script";
|
||||||
|
outputPaths = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
|
||||||
|
};
|
||||||
|
C7736DBBDF22AFA1F0E3CE16 /* [CP] Check Pods Manifest.lock */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputFileListPaths = (
|
||||||
|
);
|
||||||
|
inputPaths = (
|
||||||
|
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
||||||
|
"${PODS_ROOT}/Manifest.lock",
|
||||||
|
);
|
||||||
|
name = "[CP] Check Pods Manifest.lock";
|
||||||
|
outputFileListPaths = (
|
||||||
|
);
|
||||||
|
outputPaths = (
|
||||||
|
"$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||||
|
showEnvVarsInLog = 0;
|
||||||
|
};
|
||||||
|
E7A3E39EC6A7EA8ED85523E2 /* [CP] Copy Pods Resources */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputFileListPaths = (
|
||||||
|
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist",
|
||||||
|
);
|
||||||
|
name = "[CP] Copy Pods Resources";
|
||||||
|
outputFileListPaths = (
|
||||||
|
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist",
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
|
||||||
|
showEnvVarsInLog = 0;
|
||||||
|
};
|
||||||
|
/* End PBXShellScriptBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXSourcesBuildPhase section */
|
||||||
|
331C807D294A63A400263BE5 /* Sources */ = {
|
||||||
|
isa = PBXSourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
97C146EA1CF9000F007C117D /* Sources */ = {
|
||||||
|
isa = PBXSourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
|
||||||
|
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
|
||||||
|
7884E8682EC3CC0700C636F2 /* SceneDelegate.swift in Sources */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXSourcesBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXTargetDependency section */
|
||||||
|
331C8086294A63A400263BE5 /* PBXTargetDependency */ = {
|
||||||
|
isa = PBXTargetDependency;
|
||||||
|
target = 97C146ED1CF9000F007C117D /* Runner */;
|
||||||
|
targetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */;
|
||||||
|
};
|
||||||
|
/* End PBXTargetDependency section */
|
||||||
|
|
||||||
|
/* Begin PBXVariantGroup section */
|
||||||
|
97C146FA1CF9000F007C117D /* Main.storyboard */ = {
|
||||||
|
isa = PBXVariantGroup;
|
||||||
|
children = (
|
||||||
|
97C146FB1CF9000F007C117D /* Base */,
|
||||||
|
);
|
||||||
|
name = Main.storyboard;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
|
||||||
|
isa = PBXVariantGroup;
|
||||||
|
children = (
|
||||||
|
97C147001CF9000F007C117D /* Base */,
|
||||||
|
);
|
||||||
|
name = LaunchScreen.storyboard;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
/* End PBXVariantGroup section */
|
||||||
|
|
||||||
|
/* Begin XCBuildConfiguration section */
|
||||||
|
249021D3217E4FDB00AE95B9 /* Profile */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
|
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||||
|
CLANG_ANALYZER_NONNULL = YES;
|
||||||
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||||
|
CLANG_CXX_LIBRARY = "libc++";
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
CLANG_ENABLE_OBJC_ARC = YES;
|
||||||
|
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||||
|
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_COMMA = YES;
|
||||||
|
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||||
|
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||||
|
CLANG_WARN_EMPTY_BODY = YES;
|
||||||
|
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||||
|
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||||
|
CLANG_WARN_INT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||||
|
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||||
|
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||||
|
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||||
|
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||||
|
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||||
|
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||||
|
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||||
|
COPY_PHASE_STRIP = NO;
|
||||||
|
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||||
|
ENABLE_NS_ASSERTIONS = NO;
|
||||||
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
|
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
||||||
|
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||||
|
GCC_NO_COMMON_BLOCKS = YES;
|
||||||
|
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||||
|
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||||
|
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||||
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
||||||
|
MTL_ENABLE_DEBUG_INFO = NO;
|
||||||
|
SDKROOT = iphoneos;
|
||||||
|
SUPPORTED_PLATFORMS = iphoneos;
|
||||||
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
VALIDATE_PRODUCT = YES;
|
||||||
|
};
|
||||||
|
name = Profile;
|
||||||
|
};
|
||||||
|
249021D4217E4FDB00AE95B9 /* Profile */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
|
||||||
|
buildSettings = {
|
||||||
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||||
|
DEVELOPMENT_TEAM = 63CVT8G5P8;
|
||||||
|
ENABLE_BITCODE = NO;
|
||||||
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"@executable_path/Frameworks",
|
||||||
|
);
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = com.siromap.siroMaps;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
VERSIONING_SYSTEM = "apple-generic";
|
||||||
|
};
|
||||||
|
name = Profile;
|
||||||
|
};
|
||||||
|
331C8088294A63A400263BE5 /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
baseConfigurationReference = DB7D36DEEE9ACCB065F3D420 /* Pods-RunnerTests.debug.xcconfig */;
|
||||||
|
buildSettings = {
|
||||||
|
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
MARKETING_VERSION = 1.0;
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = com.siromap.siroMaps.RunnerTests;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||||
|
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
331C8089294A63A400263BE5 /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
baseConfigurationReference = 639DEB58BA3D8FCBAC93E4BE /* Pods-RunnerTests.release.xcconfig */;
|
||||||
|
buildSettings = {
|
||||||
|
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
MARKETING_VERSION = 1.0;
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = com.siromap.siroMaps.RunnerTests;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
|
331C808A294A63A400263BE5 /* Profile */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
baseConfigurationReference = 8D4208325A3C7F39D4F140D6 /* Pods-RunnerTests.profile.xcconfig */;
|
||||||
|
buildSettings = {
|
||||||
|
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
MARKETING_VERSION = 1.0;
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = com.siromap.siroMaps.RunnerTests;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
|
||||||
|
};
|
||||||
|
name = Profile;
|
||||||
|
};
|
||||||
|
97C147031CF9000F007C117D /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
|
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||||
|
CLANG_ANALYZER_NONNULL = YES;
|
||||||
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||||
|
CLANG_CXX_LIBRARY = "libc++";
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
CLANG_ENABLE_OBJC_ARC = YES;
|
||||||
|
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||||
|
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_COMMA = YES;
|
||||||
|
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||||
|
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||||
|
CLANG_WARN_EMPTY_BODY = YES;
|
||||||
|
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||||
|
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||||
|
CLANG_WARN_INT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||||
|
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||||
|
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||||
|
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||||
|
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||||
|
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||||
|
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||||
|
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||||
|
COPY_PHASE_STRIP = NO;
|
||||||
|
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||||
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
|
ENABLE_TESTABILITY = YES;
|
||||||
|
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
||||||
|
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||||
|
GCC_DYNAMIC_NO_PIC = NO;
|
||||||
|
GCC_NO_COMMON_BLOCKS = YES;
|
||||||
|
GCC_OPTIMIZATION_LEVEL = 0;
|
||||||
|
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||||
|
"DEBUG=1",
|
||||||
|
"$(inherited)",
|
||||||
|
);
|
||||||
|
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||||
|
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||||
|
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||||
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
||||||
|
MTL_ENABLE_DEBUG_INFO = YES;
|
||||||
|
ONLY_ACTIVE_ARCH = YES;
|
||||||
|
SDKROOT = iphoneos;
|
||||||
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
97C147041CF9000F007C117D /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
|
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||||
|
CLANG_ANALYZER_NONNULL = YES;
|
||||||
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||||
|
CLANG_CXX_LIBRARY = "libc++";
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
CLANG_ENABLE_OBJC_ARC = YES;
|
||||||
|
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||||
|
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_COMMA = YES;
|
||||||
|
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||||
|
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||||
|
CLANG_WARN_EMPTY_BODY = YES;
|
||||||
|
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||||
|
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||||
|
CLANG_WARN_INT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||||
|
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||||
|
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||||
|
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||||
|
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||||
|
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||||
|
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||||
|
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||||
|
COPY_PHASE_STRIP = NO;
|
||||||
|
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||||
|
ENABLE_NS_ASSERTIONS = NO;
|
||||||
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
|
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
||||||
|
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||||
|
GCC_NO_COMMON_BLOCKS = YES;
|
||||||
|
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||||
|
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||||
|
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||||
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
||||||
|
MTL_ENABLE_DEBUG_INFO = NO;
|
||||||
|
SDKROOT = iphoneos;
|
||||||
|
SUPPORTED_PLATFORMS = iphoneos;
|
||||||
|
SWIFT_COMPILATION_MODE = wholemodule;
|
||||||
|
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
||||||
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
VALIDATE_PRODUCT = YES;
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
|
97C147061CF9000F007C117D /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
|
||||||
|
buildSettings = {
|
||||||
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||||
|
DEVELOPMENT_TEAM = 63CVT8G5P8;
|
||||||
|
ENABLE_BITCODE = NO;
|
||||||
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"@executable_path/Frameworks",
|
||||||
|
);
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = com.siromap.siroMaps;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||||
|
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
VERSIONING_SYSTEM = "apple-generic";
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
97C147071CF9000F007C117D /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
|
||||||
|
buildSettings = {
|
||||||
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||||
|
DEVELOPMENT_TEAM = 63CVT8G5P8;
|
||||||
|
ENABLE_BITCODE = NO;
|
||||||
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"@executable_path/Frameworks",
|
||||||
|
);
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = com.siromap.siroMaps;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
VERSIONING_SYSTEM = "apple-generic";
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
|
/* End XCBuildConfiguration section */
|
||||||
|
|
||||||
|
/* Begin XCConfigurationList section */
|
||||||
|
331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
331C8088294A63A400263BE5 /* Debug */,
|
||||||
|
331C8089294A63A400263BE5 /* Release */,
|
||||||
|
331C808A294A63A400263BE5 /* Profile */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
|
};
|
||||||
|
97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
97C147031CF9000F007C117D /* Debug */,
|
||||||
|
97C147041CF9000F007C117D /* Release */,
|
||||||
|
249021D3217E4FDB00AE95B9 /* Profile */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
|
};
|
||||||
|
97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
97C147061CF9000F007C117D /* Debug */,
|
||||||
|
97C147071CF9000F007C117D /* Release */,
|
||||||
|
249021D4217E4FDB00AE95B9 /* Profile */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
|
};
|
||||||
|
/* End XCConfigurationList section */
|
||||||
|
};
|
||||||
|
rootObject = 97C146E61CF9000F007C117D /* Project object */;
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Workspace
|
||||||
|
version = "1.0">
|
||||||
|
<FileRef
|
||||||
|
location = "self:">
|
||||||
|
</FileRef>
|
||||||
|
</Workspace>
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>IDEDidComputeMac32BitWarning</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>PreviewsEnabled</key>
|
||||||
|
<false/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Scheme
|
||||||
|
LastUpgradeVersion = "1510"
|
||||||
|
version = "1.3">
|
||||||
|
<BuildAction
|
||||||
|
parallelizeBuildables = "YES"
|
||||||
|
buildImplicitDependencies = "YES">
|
||||||
|
<BuildActionEntries>
|
||||||
|
<BuildActionEntry
|
||||||
|
buildForTesting = "YES"
|
||||||
|
buildForRunning = "YES"
|
||||||
|
buildForProfiling = "YES"
|
||||||
|
buildForArchiving = "YES"
|
||||||
|
buildForAnalyzing = "YES">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
|
||||||
|
BuildableName = "Runner.app"
|
||||||
|
BlueprintName = "Runner"
|
||||||
|
ReferencedContainer = "container:Runner.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildActionEntry>
|
||||||
|
</BuildActionEntries>
|
||||||
|
</BuildAction>
|
||||||
|
<TestAction
|
||||||
|
buildConfiguration = "Debug"
|
||||||
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
|
||||||
|
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||||
|
<MacroExpansion>
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
|
||||||
|
BuildableName = "Runner.app"
|
||||||
|
BlueprintName = "Runner"
|
||||||
|
ReferencedContainer = "container:Runner.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</MacroExpansion>
|
||||||
|
<Testables>
|
||||||
|
<TestableReference
|
||||||
|
skipped = "NO"
|
||||||
|
parallelizable = "YES">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "331C8080294A63A400263BE5"
|
||||||
|
BuildableName = "RunnerTests.xctest"
|
||||||
|
BlueprintName = "RunnerTests"
|
||||||
|
ReferencedContainer = "container:Runner.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</TestableReference>
|
||||||
|
</Testables>
|
||||||
|
</TestAction>
|
||||||
|
<LaunchAction
|
||||||
|
buildConfiguration = "Debug"
|
||||||
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
|
||||||
|
launchStyle = "0"
|
||||||
|
useCustomWorkingDirectory = "NO"
|
||||||
|
ignoresPersistentStateOnLaunch = "NO"
|
||||||
|
debugDocumentVersioning = "YES"
|
||||||
|
debugServiceExtension = "internal"
|
||||||
|
enableGPUValidationMode = "1"
|
||||||
|
allowLocationSimulation = "YES">
|
||||||
|
<BuildableProductRunnable
|
||||||
|
runnableDebuggingMode = "0">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
|
||||||
|
BuildableName = "Runner.app"
|
||||||
|
BlueprintName = "Runner"
|
||||||
|
ReferencedContainer = "container:Runner.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildableProductRunnable>
|
||||||
|
</LaunchAction>
|
||||||
|
<ProfileAction
|
||||||
|
buildConfiguration = "Profile"
|
||||||
|
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||||
|
savedToolIdentifier = ""
|
||||||
|
useCustomWorkingDirectory = "NO"
|
||||||
|
debugDocumentVersioning = "YES">
|
||||||
|
<BuildableProductRunnable
|
||||||
|
runnableDebuggingMode = "0">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
|
||||||
|
BuildableName = "Runner.app"
|
||||||
|
BlueprintName = "Runner"
|
||||||
|
ReferencedContainer = "container:Runner.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildableProductRunnable>
|
||||||
|
</ProfileAction>
|
||||||
|
<AnalyzeAction
|
||||||
|
buildConfiguration = "Debug">
|
||||||
|
</AnalyzeAction>
|
||||||
|
<ArchiveAction
|
||||||
|
buildConfiguration = "Release"
|
||||||
|
revealArchiveInOrganizer = "YES">
|
||||||
|
</ArchiveAction>
|
||||||
|
</Scheme>
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Workspace
|
||||||
|
version = "1.0">
|
||||||
|
<FileRef
|
||||||
|
location = "group:Runner.xcodeproj">
|
||||||
|
</FileRef>
|
||||||
|
<FileRef
|
||||||
|
location = "group:Pods/Pods.xcodeproj">
|
||||||
|
</FileRef>
|
||||||
|
</Workspace>
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>IDEDidComputeMac32BitWarning</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>PreviewsEnabled</key>
|
||||||
|
<false/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import Flutter
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
@main
|
||||||
|
@objc class AppDelegate: FlutterAppDelegate, FlutterImplicitEngineDelegate {
|
||||||
|
override func application(
|
||||||
|
_ application: UIApplication,
|
||||||
|
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
|
||||||
|
) -> Bool {
|
||||||
|
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
|
||||||
|
}
|
||||||
|
|
||||||
|
func didInitializeImplicitFlutterEngine(_ engineBridge: FlutterImplicitEngineBridge) {
|
||||||
|
GeneratedPluginRegistrant.register(with: engineBridge.pluginRegistry)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,122 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"size" : "20x20",
|
||||||
|
"idiom" : "iphone",
|
||||||
|
"filename" : "Icon-App-20x20@2x.png",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "20x20",
|
||||||
|
"idiom" : "iphone",
|
||||||
|
"filename" : "Icon-App-20x20@3x.png",
|
||||||
|
"scale" : "3x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "29x29",
|
||||||
|
"idiom" : "iphone",
|
||||||
|
"filename" : "Icon-App-29x29@1x.png",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "29x29",
|
||||||
|
"idiom" : "iphone",
|
||||||
|
"filename" : "Icon-App-29x29@2x.png",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "29x29",
|
||||||
|
"idiom" : "iphone",
|
||||||
|
"filename" : "Icon-App-29x29@3x.png",
|
||||||
|
"scale" : "3x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "40x40",
|
||||||
|
"idiom" : "iphone",
|
||||||
|
"filename" : "Icon-App-40x40@2x.png",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "40x40",
|
||||||
|
"idiom" : "iphone",
|
||||||
|
"filename" : "Icon-App-40x40@3x.png",
|
||||||
|
"scale" : "3x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "60x60",
|
||||||
|
"idiom" : "iphone",
|
||||||
|
"filename" : "Icon-App-60x60@2x.png",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "60x60",
|
||||||
|
"idiom" : "iphone",
|
||||||
|
"filename" : "Icon-App-60x60@3x.png",
|
||||||
|
"scale" : "3x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "20x20",
|
||||||
|
"idiom" : "ipad",
|
||||||
|
"filename" : "Icon-App-20x20@1x.png",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "20x20",
|
||||||
|
"idiom" : "ipad",
|
||||||
|
"filename" : "Icon-App-20x20@2x.png",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "29x29",
|
||||||
|
"idiom" : "ipad",
|
||||||
|
"filename" : "Icon-App-29x29@1x.png",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "29x29",
|
||||||
|
"idiom" : "ipad",
|
||||||
|
"filename" : "Icon-App-29x29@2x.png",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "40x40",
|
||||||
|
"idiom" : "ipad",
|
||||||
|
"filename" : "Icon-App-40x40@1x.png",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "40x40",
|
||||||
|
"idiom" : "ipad",
|
||||||
|
"filename" : "Icon-App-40x40@2x.png",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "76x76",
|
||||||
|
"idiom" : "ipad",
|
||||||
|
"filename" : "Icon-App-76x76@1x.png",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "76x76",
|
||||||
|
"idiom" : "ipad",
|
||||||
|
"filename" : "Icon-App-76x76@2x.png",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "83.5x83.5",
|
||||||
|
"idiom" : "ipad",
|
||||||
|
"filename" : "Icon-App-83.5x83.5@2x.png",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "1024x1024",
|
||||||
|
"idiom" : "ios-marketing",
|
||||||
|
"filename" : "Icon-App-1024x1024@1x.png",
|
||||||
|
"scale" : "1x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "xcode"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 1.0 MiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 5.4 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 5.2 KiB |
|
After Width: | Height: | Size: 9.7 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 8.4 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 7.8 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 30 KiB |
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"filename" : "LaunchImage.png",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"filename" : "LaunchImage@2x.png",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"filename" : "LaunchImage@3x.png",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "xcode"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 68 B |
|
After Width: | Height: | Size: 68 B |
|
After Width: | Height: | Size: 68 B |
@@ -0,0 +1,5 @@
|
|||||||
|
# Launch Screen Assets
|
||||||
|
|
||||||
|
You can customize the launch screen with your own desired assets by replacing the image files in this directory.
|
||||||
|
|
||||||
|
You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12121" systemVersion="16G29" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
|
||||||
|
<dependencies>
|
||||||
|
<deployment identifier="iOS"/>
|
||||||
|
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/>
|
||||||
|
</dependencies>
|
||||||
|
<scenes>
|
||||||
|
<!--View Controller-->
|
||||||
|
<scene sceneID="EHf-IW-A2E">
|
||||||
|
<objects>
|
||||||
|
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
|
||||||
|
<layoutGuides>
|
||||||
|
<viewControllerLayoutGuide type="top" id="Ydg-fD-yQy"/>
|
||||||
|
<viewControllerLayoutGuide type="bottom" id="xbc-2k-c8Z"/>
|
||||||
|
</layoutGuides>
|
||||||
|
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4">
|
||||||
|
</imageView>
|
||||||
|
</subviews>
|
||||||
|
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="1a2-6s-vTC"/>
|
||||||
|
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" id="4X2-HB-R7a"/>
|
||||||
|
</constraints>
|
||||||
|
</view>
|
||||||
|
</viewController>
|
||||||
|
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||||
|
</objects>
|
||||||
|
<point key="canvasLocation" x="53" y="375"/>
|
||||||
|
</scene>
|
||||||
|
</scenes>
|
||||||
|
<resources>
|
||||||
|
<image name="LaunchImage" width="168" height="185"/>
|
||||||
|
</resources>
|
||||||
|
</document>
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
|
||||||
|
<dependencies>
|
||||||
|
<deployment identifier="iOS"/>
|
||||||
|
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
|
||||||
|
</dependencies>
|
||||||
|
<scenes>
|
||||||
|
<!--Flutter View Controller-->
|
||||||
|
<scene sceneID="tne-QT-ifu">
|
||||||
|
<objects>
|
||||||
|
<viewController id="BYZ-38-t0r" customClass="FlutterViewController" sceneMemberID="viewController">
|
||||||
|
<layoutGuides>
|
||||||
|
<viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
|
||||||
|
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
|
||||||
|
</layoutGuides>
|
||||||
|
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
|
||||||
|
</view>
|
||||||
|
</viewController>
|
||||||
|
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
|
||||||
|
</objects>
|
||||||
|
</scene>
|
||||||
|
</scenes>
|
||||||
|
</document>
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||||
|
<true/>
|
||||||
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
|
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||||
|
<key>CFBundleDisplayName</key>
|
||||||
|
<string>خرائط سيرو</string>
|
||||||
|
<key>CFBundleExecutable</key>
|
||||||
|
<string>$(EXECUTABLE_NAME)</string>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
|
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||||
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
|
<string>6.0</string>
|
||||||
|
<key>CFBundleName</key>
|
||||||
|
<string>siro_maps</string>
|
||||||
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>APPL</string>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>$(FLUTTER_BUILD_NAME)</string>
|
||||||
|
<key>CFBundleSignature</key>
|
||||||
|
<string>????</string>
|
||||||
|
<key>CFBundleVersion</key>
|
||||||
|
<string>$(FLUTTER_BUILD_NUMBER)</string>
|
||||||
|
<key>LSRequiresIPhoneOS</key>
|
||||||
|
<true/>
|
||||||
|
<key>NSLocationWhenInUseUsageDescription</key>
|
||||||
|
<string>يستخدم تطبيق خرائط سيرو موقعك لعرض خريطة تفاعلية وتوفير الملاحة الحية الدقيقة.</string>
|
||||||
|
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
|
||||||
|
<string>يستخدم تطبيق خرائط سيرو موقعك في الخلفية لتقديم التوجيهات الصوتية الحية وتنبيهات الطريق أثناء القيادة.</string>
|
||||||
|
<key>UIBackgroundModes</key>
|
||||||
|
<array>
|
||||||
|
<string>location</string>
|
||||||
|
<string>audio</string>
|
||||||
|
</array>
|
||||||
|
<key>NSAppTransportSecurity</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSAllowsArbitraryLoads</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
<key>CFBundleURLTypes</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleTypeRole</key>
|
||||||
|
<string>Editor</string>
|
||||||
|
<key>CFBundleURLName</key>
|
||||||
|
<string>com.siro_map.siro_maps</string>
|
||||||
|
<key>CFBundleURLSchemes</key>
|
||||||
|
<array>
|
||||||
|
<string>siromaps</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
<key>UIApplicationSceneManifest</key>
|
||||||
|
<dict>
|
||||||
|
<key>UIApplicationSupportsMultipleScenes</key>
|
||||||
|
<false/>
|
||||||
|
<key>UISceneConfigurations</key>
|
||||||
|
<dict>
|
||||||
|
<key>UIWindowSceneSessionRoleApplication</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>UISceneClassName</key>
|
||||||
|
<string>UIWindowScene</string>
|
||||||
|
<key>UISceneConfigurationName</key>
|
||||||
|
<string>flutter</string>
|
||||||
|
<key>UISceneDelegateClassName</key>
|
||||||
|
<string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>
|
||||||
|
<key>UISceneStoryboardFile</key>
|
||||||
|
<string>Main</string>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||||
|
<true/>
|
||||||
|
<key>UILaunchStoryboardName</key>
|
||||||
|
<string>LaunchScreen</string>
|
||||||
|
<key>UIMainStoryboardFile</key>
|
||||||
|
<string>Main</string>
|
||||||
|
<key>UISupportedInterfaceOrientations</key>
|
||||||
|
<array>
|
||||||
|
<string>UIInterfaceOrientationPortrait</string>
|
||||||
|
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||||
|
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||||
|
</array>
|
||||||
|
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||||
|
<array>
|
||||||
|
<string>UIInterfaceOrientationPortrait</string>
|
||||||
|
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||||
|
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||||
|
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
#import "GeneratedPluginRegistrant.h"
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
import Flutter
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class SceneDelegate: FlutterSceneDelegate {
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import Flutter
|
||||||
|
import UIKit
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
class RunnerTests: XCTestCase {
|
||||||
|
|
||||||
|
func testExample() {
|
||||||
|
// If you add code to the Runner application, consider adding tests here.
|
||||||
|
// See https://developer.apple.com/documentation/xctest for more information about using XCTest.
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
import '../env/env.dart';
|
||||||
|
|
||||||
|
class ApiConstants {
|
||||||
|
ApiConstants._();
|
||||||
|
|
||||||
|
// Official Keys extracted from Siro Ecosystem (Encrypted & Obfuscated via Envied)
|
||||||
|
static final String mapSaasKey = Env.mapSaasApiKey;
|
||||||
|
static final String googleMapApiKey = Env.googleMapApiKey;
|
||||||
|
|
||||||
|
// MapSaaS Endpoints
|
||||||
|
static const String mapSaasRoute = 'https://map-saas.intaleqapp.com/api/maps/route';
|
||||||
|
static const String mapSaasSearch = 'https://map-saas.intaleqapp.com/api/geocoding/search';
|
||||||
|
static const String mapSaasPlaces = 'https://map-saas.intaleqapp.com/api/geocoding/places';
|
||||||
|
static const String mapSaasTelemetry = 'https://map-saas.intaleqapp.com/api/telemetry';
|
||||||
|
static const String mapSaasStyleBase = 'https://map-saas.intaleqapp.com/api/maps/style.json';
|
||||||
|
static const String googlePlacesNearby = 'https://maps.googleapis.com/maps/api/place/nearbysearch/json';
|
||||||
|
|
||||||
|
// 50 km Radius Threshold (Strictly as specified)
|
||||||
|
static const double maxSearchRadiusMeters = 50000.0;
|
||||||
|
static const double maxSearchRadiusKm = 50.0;
|
||||||
|
|
||||||
|
// Secondary / Alternative Routing Engines
|
||||||
|
static const String osrmRouteJordan = 'https://routesjo.intaleq.xyz/route/v1/driving';
|
||||||
|
static const String osrmRouteSyria = 'https://routes-syria.siromove.com/route/v1/driving';
|
||||||
|
static const String osrmRouteEgypt = 'https://routes-egypt.siromove.com/route/v1/driving';
|
||||||
|
|
||||||
|
// Default Jordan Geospatial Center (Amman 7th Circle / Abdoun)
|
||||||
|
static const double defaultLat = 31.9539;
|
||||||
|
static const double defaultLng = 35.9106;
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class AppColors {
|
||||||
|
AppColors._();
|
||||||
|
|
||||||
|
// Pure White & Canvas
|
||||||
|
static const Color pureWhite = Color(0xFFFFFFFF);
|
||||||
|
static const Color canvasLight = Color(0xFFFBFBFD);
|
||||||
|
static const Color surfaceCard = Color(0xFFFFFFFF);
|
||||||
|
static const Color surfaceMuted = Color(0xFFF5F5F7);
|
||||||
|
|
||||||
|
// Apple Text Palette
|
||||||
|
static const Color textPrimary = Color(0xFF1D1D1F);
|
||||||
|
static const Color textSecondary = Color(0xFF424245);
|
||||||
|
static const Color textMuted = Color(0xFF86868B);
|
||||||
|
|
||||||
|
// Sovereign / Apple Accent Colors
|
||||||
|
static const Color appleBlue = Color(0xFF0071E3);
|
||||||
|
static const Color appleBlueHover = Color(0xFF0077ED);
|
||||||
|
static const Color tacticalNavy = Color(0xFF0B192C);
|
||||||
|
static const Color tacticalEmerald = Color(0xFF059669);
|
||||||
|
static const Color sovereignGold = Color(0xFFD97706);
|
||||||
|
static const Color coralDanger = Color(0xFFDC2626);
|
||||||
|
|
||||||
|
// Borders & Dividers
|
||||||
|
static const Color borderSubtle = Color(0x12000000);
|
||||||
|
static const Color borderGlass = Color(0x1F000000);
|
||||||
|
static const Color glassFill = Color(0xD1FFFFFF);
|
||||||
|
|
||||||
|
// Dark Theme Palette
|
||||||
|
static const Color darkCanvas = Color(0xFF0B0F17);
|
||||||
|
static const Color darkSurface = Color(0xFF161A22);
|
||||||
|
static const Color darkCard = Color(0xFF1F2430);
|
||||||
|
static const Color darkText = Color(0xFFF5F5F7);
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
import 'package:flutter/services.dart';
|
||||||
|
|
||||||
|
class CarPlatformBridge {
|
||||||
|
CarPlatformBridge._();
|
||||||
|
|
||||||
|
static const _channel = MethodChannel('com.siro.siro_maps/car_navigation');
|
||||||
|
static bool _isInitialized = false;
|
||||||
|
|
||||||
|
static void ensureInitialized() {
|
||||||
|
if (_isInitialized) return;
|
||||||
|
_channel.setMethodCallHandler(_handleMethodCall);
|
||||||
|
_isInitialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<dynamic> _handleMethodCall(MethodCall call) async {
|
||||||
|
switch (call.method) {
|
||||||
|
case 'isCarAppConnected':
|
||||||
|
return false;
|
||||||
|
default:
|
||||||
|
throw MissingPluginException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<void> updateNavState({
|
||||||
|
required double lat,
|
||||||
|
required double lng,
|
||||||
|
required double bearing,
|
||||||
|
required double speed,
|
||||||
|
required String instruction,
|
||||||
|
required double distanceToStep,
|
||||||
|
required double totalDistance,
|
||||||
|
required double eta,
|
||||||
|
required int maneuver,
|
||||||
|
required bool isNavigating,
|
||||||
|
bool isMapDarkMode = false,
|
||||||
|
}) async {
|
||||||
|
try {
|
||||||
|
await _channel.invokeMethod('updateNavState', {
|
||||||
|
'lat': lat,
|
||||||
|
'lng': lng,
|
||||||
|
'bearing': bearing,
|
||||||
|
'speed': speed,
|
||||||
|
'instruction': instruction,
|
||||||
|
'distanceToStep': distanceToStep,
|
||||||
|
'totalDistance': totalDistance,
|
||||||
|
'eta': eta,
|
||||||
|
'maneuver': maneuver,
|
||||||
|
'isNavigating': isNavigating,
|
||||||
|
'isMapDarkMode': isMapDarkMode,
|
||||||
|
});
|
||||||
|
} catch (_) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<void> stopNavigation() async {
|
||||||
|
try {
|
||||||
|
await _channel.invokeMethod('stopNavigation');
|
||||||
|
} catch (_) {}
|
||||||
|
}
|
||||||
|
}
|
||||||