@@ -406,16 +406,13 @@ function destinationPoint(lat: number, lng: number, bearingDeg: number, distance
406406
407407const getSuggestion = async ( ) => {
408408 setIsSuggesting ( true ) ;
409- setApiKeyMissing ( false ) ;
410-
411409 try {
412- // Use selected start point, or center from existing routes, or default to Stockholm
413410 let centerLat : number ;
414411 let centerLon : number ;
415412
416413 if ( selectedStartPoint ) {
417- centerLat = selectedStartPoint [ 1 ] ;
418414 centerLon = selectedStartPoint [ 0 ] ;
415+ centerLat = selectedStartPoint [ 1 ] ;
419416 } else if ( routes . length > 0 ) {
420417 const allCoords = routes . flatMap ( r => r . coordinates || [ ] ) ;
421418 if ( allCoords . length > 0 && Array . isArray ( allCoords [ 0 ] ) ) {
@@ -430,175 +427,33 @@ const getSuggestion = async () => {
430427 centerLon = 18.0686 ;
431428 }
432429
433- const targetMeters = suggestDistance * 1000 ;
434- const toleranceMeters = 1000 ; // ±1km tolerance
435-
436- // Generate multiple candidates and pick the best one
437- // Based on route-generator-pro algorithm
438- const candidates : any [ ] = [ ] ;
439- const roughRadiusMeters = Math . max ( 250 , targetMeters / 4.4 ) ;
440-
441- // Different shape patterns (waypoint angles)
442- const shapes = [
443- [ 0 , 120 , 240 ] , // Triangle
444- [ 0 , 90 , 180 , 270 ] , // Square
445- [ 0 , 72 , 144 , 216 , 288 ] , // Pentagon
446- [ 0 , 110 , 230 ] , // Triangle variant
447- [ 0 , 100 , 200 , 300 ] , // Diamond
448- [ 0 , 60 , 120 , 180 , 240 , 300 ] , // Hexagon
449- [ 0 , 130 , 230 ] , // Wide triangle
450- [ 0 , 80 , 180 , 280 ] , // Narrow diamond
451- [ 0 , 150 , 270 ] , // Wide offset
452- ] ;
453-
454- // Radius multipliers
455- const radiusMults = [ 0.7 , 0.8 , 0.85 , 0.9 , 0.95 , 1.0 , 1.05 , 1.1 , 1.15 , 1.2 , 1.25 , 1.3 ] ;
456-
457- // Bearing offsets
458- const bearingStep = 15 ;
459-
460- for ( let bearing = 0 ; bearing < 360 ; bearing += bearingStep ) {
461- for ( const mult of radiusMults ) {
462- for ( const shape of shapes ) {
463- const radius = roughRadiusMeters * mult ;
464- const waypoints : [ number , number ] [ ] = shape . map ( ( offset ) => {
465- const [ lng , lat ] = destinationPoint ( centerLat , centerLon , ( bearing + offset ) % 360 , radius ) ;
466- return [ lng , lat ] ;
467- } ) ;
468-
469- candidates . push ( {
470- waypoints,
471- bearing,
472- mult,
473- shape : shape . join ( '-' ) ,
474- } ) ;
475-
476- if ( candidates . length >= 30 ) break ; // Limit to 30 candidates
477- }
478- if ( candidates . length >= 30 ) break ;
479- }
480- if ( candidates . length >= 30 ) break ;
481- }
482-
483- // Shuffle candidates for variety
484- for ( let i = candidates . length - 1 ; i > 0 ; i -- ) {
485- const j = Math . floor ( Math . random ( ) * ( i + 1 ) ) ;
486- [ candidates [ i ] , candidates [ j ] ] = [ candidates [ j ] , candidates [ i ] ] ;
487- }
488-
489- // Try each candidate - now using OpenRouteService API via internal route
490- let bestRoute : any = null ;
491- let bestDistanceDiff = Infinity ;
492-
493- // Test candidates with OpenRouteService
494- for ( const candidate of candidates . slice ( 0 , 30 ) ) {
495- const coordString = [
496- `${ centerLon } ,${ centerLat } ` ,
497- ...candidate . waypoints . map ( ( w : [ number , number ] ) => `${ w [ 0 ] } ,${ w [ 1 ] } ` ) ,
498- `${ centerLon } ,${ centerLat } `
499- ] . join ( ';' ) ;
500-
501- try {
502- // Use OpenRouteService API
503- const orsResponse = await fetch (
504- `https://api.openrouteservice.org/v2/directions/foot-walking/geojson` ,
505- {
506- method : 'POST' ,
507- headers : {
508- 'Content-Type' : 'application/json' ,
509- } ,
510- body : JSON . stringify ( {
511- coordinates : coordString . split ( ';' ) . map ( c => c . split ( ',' ) )
512- } ) ,
513- signal : AbortSignal . timeout ( 8000 )
514- }
515- ) ;
516-
517- if ( ! orsResponse . ok ) continue ;
518-
519- const data = await orsResponse . json ( ) ;
520- if ( data . features ?. [ 0 ] ?. properties ?. summary ?. distance ) {
521- const route = data . features [ 0 ] ;
522- const distance = route . properties . summary . distance ;
523- const coords = route . geometry . coordinates . map ( ( c : number [ ] ) => [ c [ 0 ] , c [ 1 ] ] as [ number , number ] ) ;
524- const distanceDiff = Math . abs ( distance - targetMeters ) ;
525-
526- if ( distanceDiff < bestDistanceDiff ) {
527- bestDistanceDiff = distanceDiff ;
528- bestRoute = { coords, distance } ;
529- if ( distanceDiff <= toleranceMeters ) break ;
530- }
531- }
532- } catch ( e ) {
533- continue ;
534- }
535- }
536-
537- if ( ! bestRoute ) {
538- // Fallback - use first candidate without routing
539- const fallbackWaypoints = candidates [ 0 ] ?. waypoints || [ ] ;
540- const fallbackCoords : [ number , number ] [ ] = [
541- [ centerLon , centerLat ] ,
542- ...fallbackWaypoints ,
543- [ centerLon , centerLat ]
544- ] ;
545-
546- setSuggestedRoute ( {
547- coordinates : fallbackCoords ,
548- distance : targetMeters ,
549- elevationGain : Math . round ( suggestDistance * 10 ) ,
550- name : `Loop - ${ suggestDistance } km` ,
551- isRoundTrip : true ,
552- startPoint : [ centerLon , centerLat ] ,
553- familiarityScore : avoidFamiliar ? 0 : 100 ,
554- } ) ;
555- setIsSelectingStartPoint ( false ) ;
556- return ;
557- }
558-
559- const coords = bestRoute . coords ;
430+ const response = await fetch ( '/api/routes/suggest' , {
431+ method : 'POST' ,
432+ headers : { 'Content-Type' : 'application/json' } ,
433+ body : JSON . stringify ( {
434+ distance : suggestDistance ,
435+ avoidFamiliar,
436+ centerLat,
437+ centerLon,
438+ existingRoutes : routes . map ( ( route ) => ( { coordinates : route . coordinates || [ ] } ) ) ,
439+ } ) ,
440+ } ) ;
560441
561- // Calculate familiarity - fixed bounds: new=0-20%, familiar=80-100%
562- let familiarityScore = avoidFamiliar ? 0 : 100 ;
563-
564- if ( routes . length > 0 ) {
565- const allExistingCoords = routes . flatMap ( ( r : any ) => r . coordinates || [ ] ) ;
566- let overlapCount = 0 ;
567- const sampleSize = Math . min ( coords . length , 20 ) ;
568- const step = Math . max ( 1 , Math . floor ( coords . length / sampleSize ) ) ;
569-
570- for ( let i = 0 ; i < coords . length ; i += step ) {
571- const [ lon , lat ] = coords [ i ] ;
572- for ( const existingCoord of allExistingCoords ) {
573- const existingLon = existingCoord [ 0 ] ;
574- const existingLat = existingCoord [ 1 ] ;
575- const dist = Math . sqrt ( Math . pow ( lon - existingLon , 2 ) + Math . pow ( lat - existingLat , 2 ) ) ;
576- if ( dist < 0.005 ) {
577- overlapCount ++ ;
578- break ;
579- }
580- }
581- }
582- const actualOverlap = Math . round ( ( overlapCount / sampleSize ) * 100 ) ;
583- if ( avoidFamiliar ) {
584- familiarityScore = Math . min ( 20 , actualOverlap ) ;
585- } else {
586- familiarityScore = Math . max ( 80 , actualOverlap ) ;
587- }
442+ const data = await response . json ( ) ;
443+ if ( ! response . ok ) {
444+ throw new Error ( data ?. error || 'Failed to generate route' ) ;
588445 }
589-
590- const routeNames = [ 'Morning Loop' , 'Evening Run' , 'Park Circuit' , 'Urban Loop' , 'Nature Trail' , 'City Route' , 'Sunset Run' , 'Quick Loop' ] ;
591- const actualDistanceKm = ( bestRoute . distance / 1000 ) . toFixed ( 1 ) ;
592-
446+
593447 setSuggestedRoute ( {
594- coordinates : coords ,
595- distance : bestRoute . distance ,
596- elevationGain : Math . round ( suggestDistance * 10 ) ,
597- name : ` ${ routeNames [ Math . floor ( Math . random ( ) * routeNames . length ) ] } - ${ actualDistanceKm } km` ,
448+ coordinates : data . coordinates ,
449+ distance : data . distance ,
450+ elevationGain : data . elevationGain ,
451+ name : data . name ,
598452 isRoundTrip : true ,
599- startPoint : [ centerLon , centerLat ] ,
600- familiarityScore,
453+ startPoint : data . startPoint ,
454+ familiarityScore : data . familiarityScore ,
601455 } ) ;
456+ setSelectedRoute ( null ) ;
602457 setIsSelectingStartPoint ( false ) ;
603458 } catch ( error : any ) {
604459 console . error ( 'Suggestion error:' , error ) ;
0 commit comments