Otvoreni kod i AGPL licenca

Svi astrološki kalkulatori i alati na ovoj stranici koriste Swiss Ephemeris © Astrodienst AG, licenciran pod AGPL v3.

U skladu s uvjetima licence, javno je dostupan izvorni kod backend dijela koji komunicira sa Swiss Ephemerisom (API i kalkulacijske funkcije).

Izvorni kod možeš pronaći u nastavku:

<?php
class Sweph {

    private static $swetest_path;
    private static $ephe_path;

    public static function init($swetest_path = null, $ephe_path = null) {
        self::$swetest_path = $swetest_path ?: __DIR__ . '/swetest';
        self::$ephe_path = $ephe_path ?: __DIR__;
    }

    /* ------------------ PLANETI ------------------ */
    public static function calc_planet($planet_code, $date, $time = '12:00') {

        if ($planet_code >= 10) {
            return self::extract_node_or_lilith($planet_code, $date, $time);
        }

        $cmd = self::$swetest_path
             . " -edir" . self::$ephe_path
             . " -b" . $date
             . " -ut" . $time
             . " -p" . $planet_code
             . " -fPls -head";

        exec($cmd, $output);

        if (empty($output)) return ['longitude' => 0, 'retrograde' => false];

        foreach ($output as $line) {
            if (preg_match('/\d/', $line)) {
                $parts = preg_split('/\s+/', trim($line));
                if (count($parts) >= 3) {
                    $longitude = (float)$parts[1];
                    break;
                }
            }
        }

        // retrogradnost – standardni izračun
$timestamp = strtotime(str_replace('.', '-', $date));
$day_before = date('d.m.Y', $timestamp - 86400);
$day_after  = date('d.m.Y', $timestamp + 86400);
$lon_before = self::get_longitude($planet_code, $day_before, $time);
$lon_after  = self::get_longitude($planet_code, $day_after, $time);

// korekcija za prijelaz preko 0°/360°
if ($lon_after < $lon_before && ($lon_before - $lon_after) > 180) {
    $lon_after += 360;
}

$retrograde = ($lon_before && $lon_after) ? ($lon_after < $lon_before) : false;


        return [
            'longitude' => $longitude ?? 0,
            'retrograde' => $retrograde
        ];
    }

    /* ------------------ HELPER ------------------ */
    private static function get_longitude($planet_code, $date, $time) {
        $cmd = self::$swetest_path
             . " -edir" . self::$ephe_path
             . " -b" . $date
             . " -ut" . $time
             . " -p" . $planet_code
             . " -fPls -head";

        exec($cmd, $out);
        foreach ($out as $line) {
            if (preg_match('/\d/', $line)) {
                $parts = preg_split('/\s+/', trim($line));
                if (count($parts) >= 3) {
                    return (float)$parts[1];
                }
            }
        }
        return null;
    }

    /* ------------------ ČVOROVI I LILITH ------------------ */
    private static function extract_node_or_lilith($planet_code, $date, $time) {
        $cmd = self::$swetest_path
             . " -edir" . self::$ephe_path
             . " -b" . $date
             . " -ut" . $time
             . " -fPls -head";

        exec($cmd, $output);
        file_put_contents(__DIR__ . '/debug_nodes.txt', "CMD: $cmd\n\nOUTPUT:\n" . implode("\n", $output));

        $longitude = 0;
        $retrograde = false;

        foreach ($output as $line) {
            $line = trim(preg_replace('/\s+/', ' ', $line));

            // mean Node
            if ($planet_code == 10 && stripos($line, 'mean Node') === 0) {
                $parts = explode(' ', $line);
                $longitude = (float)$parts[2];
                $retrograde = ($parts[3] < 0);
            }

            // južni čvor iz mean Node
            if ($planet_code == 11 && stripos($line, 'mean Node') === 0) {
                $parts = explode(' ', $line);
                $longitude = fmod((float)$parts[2] + 180, 360);
                $retrograde = true;
            }

            // Lilith (Mean Apogee)
            if ($planet_code == 12 && stripos($line, 'mean Apogee') === 0) {
                $parts = explode(' ', $line);
                $longitude = (float)$parts[2];
                $retrograde = ($parts[3] < 0);
            }
        }

        return [
            'longitude' => round($longitude, 3),
            'retrograde' => $retrograde
        ];
    }

/* ------------------ KUĆE (KOCH, full tolerant parser) ------------------ */
public static function calc_points($date, $time = '12:00', $lat = 0, $lon = 0, $country_code = 'HR') {
    setlocale(LC_ALL, 'C');

    $tz_map = [
        'HR'=>'Europe/Zagreb','SI'=>'Europe/Ljubljana','RS'=>'Europe/Belgrade',
        'BA'=>'Europe/Sarajevo','ME'=>'Europe/Podgorica','MK'=>'Europe/Skopje','XK'=>'Europe/Belgrade'
    ];
    $tz = $tz_map[$country_code] ?? 'Europe/Zagreb';

    // lokalno → UTC
    $dt_local = new DateTime("$date $time", new DateTimeZone($tz));
    $dt_utc = clone $dt_local; 
    $dt_utc->setTimezone(new DateTimeZone('UTC'));
    $ut_time = $dt_utc->format('H:i');

    // Swiss Ephemeris (lon,lat,K)
    $cmd = "LANG=C " . self::$swetest_path .
           " -edir" . self::$ephe_path .
           " -b" . $date .
           " -ut" . $ut_time .
           " -house" . $lon . "," . $lat . ",K -head";

    exec($cmd, $lines);
    file_put_contents(__DIR__.'/debug_houses.txt',"CMD: $cmd\n\nOUTPUT:\n".implode("\n",$lines));

    $houses = [];
    $asc = null;
    $mc  = null;

    foreach ($lines as $line) {
        $line = trim($line);

        // hvata i slučajeve s tabovima, razmacima, i znakovima ° ili �
        if (preg_match('/house\s+(\d+)\s+(\d+)[^0-9]+(\d+)\'\s*(\d+\.\d+)/', $line, $m)) {
            $num = (int)$m[1];
            $deg = (float)$m[2];
            $min = (float)$m[3];
            $sec = (float)$m[4];
            $houses[$num] = round($deg + $min/60 + $sec/3600, 4);
        }

        // Ascendant linija (npr. Ascendant         10°29'41.7826)
        if (stripos($line, 'Ascendant') !== false && preg_match('/(\d+)[^0-9]+(\d+)\'\s*(\d+\.\d+)/', $line, $m)) {
            $deg = (float)$m[1];
            $min = (float)$m[2];
            $sec = (float)$m[3];
            $asc = round($deg + $min/60 + $sec/3600, 4);
        }

        // MC linija
        if (preg_match('/\bMC\b/', $line) && preg_match('/(\d+)[^0-9]+(\d+)\'\s*(\d+\.\d+)/', $line, $m)) {
            $deg = (float)$m[1];
            $min = (float)$m[2];
            $sec = (float)$m[3];
            $mc = round($deg + $min/60 + $sec/3600, 4);
        }
    }

    // fallback: ako prva kuća nije eksplicitno zapisana, koristi Asc
    if (!isset($houses[1]) && $asc !== null) {
        $houses[1] = $asc;
        ksort($houses);
    }

    return [
        'houses' => $houses,
        'ASC' => $asc ?? ($houses[1] ?? null),
        'MC'  => $mc ?? ($houses[10] ?? null),
        'tz_used' => $tz,
        'ut_time' => $ut_time
    ];
}


    /* ------------------ ASPEKTI ------------------ */
    public static function calc_aspects($planets) {
    $aspects = [];
    $aspect_defs = [
        'konjunkcija' => 0,
        'sekstil' => 60,
        'kvadrat' => 90,
        'trigon' => 120,
        'opozicija' => 180
    ];

    $orb = [
        'konjunkcija' => 8,
        'sekstil' => 7,
        'kvadrat' => 7,
        'trigon' => 7,
        'opozicija' => 8
    ];

    foreach ($planets as $p1 => $data1) {
        // ⛔ preskoči čvorove i Lilith
        if ($p1 >= 10) continue;

        foreach ($planets as $p2 => $data2) {
            if ($p2 >= 10 || $p1 >= $p2) continue;

            $diff = abs($data1['longitude'] - $data2['longitude']);
            if ($diff > 180) $diff = 360 - $diff;

            foreach ($aspect_defs as $name => $angle) {
                if (abs($diff - $angle) <= $orb[$name]) {
                    $aspects[] = [
                        'aspect' => $name,
                        'planets' => [
                            self::get_planet_name($p1),
                            self::get_planet_name($p2)
                        ],
                        'difference' => round($diff, 2)
                    ];
                }
            }
        }
    }

    return $aspects;
}

/* ------------------ ASPEKTI PLANETI ↔︎ OSI (ASC / MC) ------------------ */
public static function calc_axis_aspects($planets, $points) {
    $aspects = [];
    $aspect_defs = [
        'konjunkcija' => 0,
        'sekstil' => 60,
        'kvadrat' => 90,
        'trigon' => 120,
        'opozicija' => 180
    ];

    $orb = [
        'konjunkcija' => 7,
        'sekstil' => 6,
        'kvadrat' => 6,
        'trigon' => 6,
        'opozicija' => 7
    ];

    $axes = [
        'ASC' => $points['ASC'] ?? null,
        'MC'  => $points['MC'] ?? null
    ];

    foreach ($planets as $pid => $pdata) {
        if ($pid >= 10) continue; // preskoči čvorove i Lilith

        foreach ($axes as $axis_name => $axis_lon) {
            if (!$axis_lon) continue;
            $diff = abs($pdata['longitude'] - $axis_lon);
            if ($diff > 180) $diff = 360 - $diff;

            foreach ($aspect_defs as $asp => $angle) {
                if (abs($diff - $angle) <= $orb[$asp]) {
                    $aspects[] = [
                        'planet' => self::get_planet_name($pid),
                        'axis' => $axis_name,
                        'aspect' => $asp,
                        'difference' => round($diff, 2)
                    ];
                }
            }
        }
    }

    return $aspects;
}


/* ------------------ ASTRO KONFIGURACIJE ------------------ */
public static function calc_configurations($planets) {
    $configs = [];

    // Radi samo s klasičnim planetima (Sunce–Pluton)
    $filtered = array_filter($planets, fn($p, $i) => $i < 10, ARRAY_FILTER_USE_BOTH);
    $names = array_keys($filtered);

    // --- 1. STELIJ (3+ planeta unutar 30°) ---
    $planet_list = [];
    foreach ($filtered as $id => $p) {
        $planet_list[] = ['id' => $id, 'lon' => $p['longitude']];
    }
    usort($planet_list, fn($a, $b) => $a['lon'] <=> $b['lon']);

    for ($i = 0; $i < count($planet_list); $i++) {
        $group = [$planet_list[$i]];
        for ($j = $i + 1; $j < count($planet_list); $j++) {
            $diff = abs($planet_list[$j]['lon'] - $planet_list[$i]['lon']);
            if ($diff > 180) $diff = 360 - $diff;
            if ($diff <= 15) {
                $group[] = $planet_list[$j];
            }
        }
        if (count($group) >= 3) {
            $names_in_stellium = array_map(fn($g) => self::get_planet_name($g['id']), $group);
            $longitudes = array_column($group, 'lon');
            $configs[] = [
                'type' => 'Stelij',
                'planets' => $names_in_stellium,
                'count' => count($group),
                'span' => round(max($longitudes) - min($longitudes), 2),
                'center' => round(array_sum($longitudes) / count($longitudes), 2)
            ];
        }
    }

    // --- 2. T-KVADRAT ---
    foreach ($names as $i => $a) {
        foreach ($names as $j => $b) {
            if ($i == $j) continue;
            $angle_ab = self::angle_diff($filtered[$a]['longitude'], $filtered[$b]['longitude']);
            if (abs($angle_ab - 180) > 8) continue; // mora biti opozicija

            foreach ($names as $k => $c) {
                if (in_array($k, [$i, $j])) continue;
                $ac = self::angle_diff($filtered[$a]['longitude'], $filtered[$c]['longitude']);
                $bc = self::angle_diff($filtered[$b]['longitude'], $filtered[$c]['longitude']);
                if (abs($ac - 90) <= 8 && abs($bc - 90) <= 8) {
                    $configs[] = [
                        'type' => 'T-kvadrat',
                        'planets' => [
                            self::get_planet_name($a),
                            self::get_planet_name($b),
                            self::get_planet_name($c)
                        ]
                    ];
                }
            }
        }
    }

    // --- 3. VELIKI KVADRAT (GRAND CROSS) ---
    for ($a = 0; $a < count($names); $a++) {
        for ($b = $a + 1; $b < count($names); $b++) {
            for ($c = $b + 1; $c < count($names); $c++) {
                for ($d = $c + 1; $d < count($names); $d++) {
                    $set = [$a, $b, $c, $d];
                    $ok = 0;
                    foreach ($set as $i1) {
                        foreach ($set as $i2) {
                            if ($i1 >= $i2) continue;
                            $diff = self::angle_diff($filtered[$names[$i1]]['longitude'], $filtered[$names[$i2]]['longitude']);
                            if (abs($diff - 90) <= 8 || abs($diff - 180) <= 8) $ok++;
                        }
                    }
                    if ($ok >= 10) {
                        $configs[] = [
                            'type' => 'Veliki kvadrat',
                            'planets' => array_map(fn($n) => self::get_planet_name($n), $set)
                        ];
                    }
                }
            }
        }
    }

    // --- 4. VELIKI TRIGON ---
    for ($a = 0; $a < count($names); $a++) {
        for ($b = $a + 1; $b < count($names); $b++) {
            for ($c = $b + 1; $c < count($names); $c++) {
                $λa = $filtered[$names[$a]]['longitude'];
                $λb = $filtered[$names[$b]]['longitude'];
                $λc = $filtered[$names[$c]]['longitude'];
                $ab = self::angle_diff($λa, $λb);
                $bc = self::angle_diff($λb, $λc);
                $ca = self::angle_diff($λc, $λa);
                if (abs($ab - 120) <= 8 && abs($bc - 120) <= 8 && abs($ca - 120) <= 8) {
                    $configs[] = [
                        'type' => 'Veliki trigon',
                        'planets' => [
                            self::get_planet_name($names[$a]),
                            self::get_planet_name($names[$b]),
                            self::get_planet_name($names[$c])
                        ]
                    ];
                }
            }
        }
    }

    return $configs;
}


/* ------------------ HELPER ZA KUTEVE ------------------ */
private static function angle_diff($a, $b) {
    $d = abs($a - $b);
    return ($d > 180) ? 360 - $d : $d;
}

/* ------------------ ELEMENTI, KVALITETE I ROD ------------------ */
public static function calc_elements_qualities_gender($planets) {
    $signs = ['Ovan','Bik','Blizanci','Rak','Lav','Djevica','Vaga','Škorpion','Strijelac','Jarac','Vodenjak','Ribe'];

    $elements = [
        'vatra' => ['Ovan', 'Lav', 'Strijelac'],
        'zemlja' => ['Bik', 'Djevica', 'Jarac'],
        'zrak' => ['Blizanci', 'Vaga', 'Vodenjak'],
        'voda' => ['Rak', 'Škorpion', 'Ribe']
    ];

    $qualities = [
        'kardinalni' => ['Ovan', 'Rak', 'Vaga', 'Jarac'],
        'fiksni' => ['Bik', 'Lav', 'Škorpion', 'Vodenjak'],
        'promjenjivi' => ['Blizanci', 'Djevica', 'Strijelac', 'Ribe']
    ];

    $gender = [
        'muški' => ['Ovan','Blizanci','Lav','Vaga','Strijelac','Vodenjak'],
        'ženski' => ['Bik','Rak','Djevica','Škorpion','Jarac','Ribe']
    ];

    $elem_count = ['vatra'=>0,'zemlja'=>0,'zrak'=>0,'voda'=>0];
    $qual_count = ['kardinalni'=>0,'fiksni'=>0,'promjenjivi'=>0];
    $gender_count = ['muški'=>0,'ženski'=>0];

    $total = 0;

    foreach ($planets as $pid => $pdata) {
        if ($pid >= 10) continue;
        $sign_index = floor($pdata['longitude'] / 30);
        $sign = $signs[$sign_index] ?? null;

        if ($sign) {
            foreach ($elements as $el => $list)
                if (in_array($sign, $list)) $elem_count[$el]++;

            foreach ($qualities as $q => $list)
                if (in_array($sign, $list)) $qual_count[$q]++;

            foreach ($gender as $g => $list)
                if (in_array($sign, $list)) $gender_count[$g]++;

            $total++;
        }
    }

   // postotci
$elem_pct = [];
$qual_pct = [];
$gender_pct = [];
foreach ($elem_count as $k => $v) $elem_pct[$k] = $total ? round($v / $total * 100, 1) : 0;
foreach ($qual_count as $k => $v) $qual_pct[$k] = $total ? round($v / $total * 100, 1) : 0;
foreach ($gender_count as $k => $v) $gender_pct[$k] = $total ? round($v / $total * 100, 1) : 0;

return [
    'elementi' => $elem_count,
    'kvalitete' => $qual_count,
    'rod' => $gender_count,
    'elementi_postotci' => $elem_pct,
    'kvalitete_postotci' => $qual_pct,
    'rod_postotci' => $gender_pct
];

}

/* ------------------ DOSTOJANSTVA PLANETA ------------------ */
public static function calc_dignities($planets) {
    $signs = ['Ovan','Bik','Blizanci','Rak','Lav','Djevica','Vaga','Škorpion','Strijelac','Jarac','Vodenjak','Ribe'];

    $planet_dignities = [

        'Sunce' => [
            'sjedište'     => 'Lav',
            'izgon'        => 'Vodenjak',
            'egzaltacija'  => 'Ovan',
            'pad'          => 'Vaga',
        ],

        'Mjesec' => [
            'sjedište'     => 'Rak',
            'izgon'        => 'Jarac',
            'egzaltacija'  => 'Bik',
            'pad'          => 'Škorpion',
        ],

        'Merkur' => [
            'sjedište'     => ['Blizanci', 'Djevica'],
            'izgon'        => ['Strijelac', 'Ribe'],
            'egzaltacija'  => 'Djevica',
            'pad'          => 'Ribe',
        ],

        'Venera' => [
            'sjedište'     => ['Bik', 'Vaga'],
            'izgon'        => ['Škorpion', 'Ovan'],
            'egzaltacija'  => 'Ribe',
            'pad'          => 'Djevica',
        ],

        'Mars' => [
            'sjedište'     => ['Ovan', 'Škorpion'],
            'izgon'        => ['Vaga', 'Bik'],
            'egzaltacija'  => 'Jarac',
            'pad'          => 'Rak',
        ],

        'Jupiter' => [
            'sjedište'     => ['Strijelac', 'Ribe'],
            'izgon'        => ['Blizanci', 'Djevica'],
            'egzaltacija'  => 'Rak',
            'pad'          => 'Jarac',
        ],

        'Saturn' => [
            'sjedište'     => ['Jarac', 'Vodenjak'],
            'izgon'        => ['Rak', 'Lav'],
            'egzaltacija'  => 'Vaga',
            'pad'          => 'Ovan',
        ],

        'Uran' => [
            'sjedište'     => 'Vodenjak',
            'izgon'        => 'Lav',
            'egzaltacija'  => 'Škorpion',
            'pad'          => 'Bik',
        ],

        'Neptun' => [
            'sjedište'     => 'Ribe',
            'izgon'        => 'Djevica',
            'egzaltacija'  => 'Rak',
            'pad'          => 'Jarac',
        ],

        'Pluton' => [
            'sjedište'     => 'Škorpion',
            'izgon'        => 'Bik',
            'egzaltacija'  => 'Ovan',
            'pad'          => 'Vaga',
        ],
    ];

    $results = [];

    foreach ($planets as $pid => $pdata) {
        if ($pid >= 10) continue; // preskoči čvorove i Lilith
        $planet = self::get_planet_name($pid);
        $sign_index = floor($pdata['longitude'] / 30);
        $sign = $signs[$sign_index] ?? null;

        if (!$sign || !isset($planet_dignities[$planet])) continue;

        $info = $planet_dignities[$planet];

        $match = function($value, $sign) {
            if (is_array($value)) return in_array($sign, $value);
            return $value === $sign;
        };

        $dignity = null;
        if ($match($info['sjedište'], $sign)) $dignity = 'sjedište';
        elseif ($match($info['izgon'], $sign)) $dignity = 'izgon';
        elseif ($match($info['egzaltacija'], $sign)) $dignity = 'egzaltacija';
        elseif ($match($info['pad'], $sign)) $dignity = 'pad';

        if ($dignity) {
            $results[$planet] = [
                'znak' => $sign,
                'status' => $dignity
            ];
        }
    }

    return $results;
}


/* ------------------ UMETNUTI ZNAKOVI ------------------ */
public static function calc_intercepted_signs($houses) {
    $signs = ['Ovan','Bik','Blizanci','Rak','Lav','Djevica','Vaga','Škorpion','Strijelac','Jarac','Vodenjak','Ribe'];

    // 1. vrhovi kuća u stupnjevima (1–12)
    ksort($houses);
    $cusps = array_values($houses);

    // 2. svi znakovi po 30°
    $all_signs = range(0, 11);

    // 3. znakovi na vrhovima kuća
    $cusps_signs = [];
    foreach ($cusps as $deg) {
        $cusps_signs[] = floor($deg / 30);
    }

    // 4. znakovi koji NISU na vrhu nijedne kuće
    $potential = array_diff($all_signs, array_unique($cusps_signs));

    // 5. nađi koji od tih znakova su potpuno unutar neke kuće (tj. između dva vrha iste kuće)
    $intercepted = [];

    for ($i = 0; $i < 12; $i++) {
        $start = $cusps[$i];
        $end   = $cusps[($i + 1) % 12];
        if ($end <= $start) $end += 360;

        // svi znakovi koji leže unutar tog raspona
        foreach ($potential as $s) {
            $sign_start = $s * 30;
            $sign_end   = $sign_start + 30;

            // provjera leži li cijeli znak unutar jedne kuće
            if ($sign_start >= $start && $sign_end <= $end) {
                $intercepted[] = $signs[$s];
            }
        }
    }

    // 6. ako je jedan umetnut, dodaj i njegovu opoziciju
    $final = [];
    foreach ($intercepted as $s) {
        $idx = array_search($s, $signs);
        $opp = ($idx + 6) % 12;
        $final[] = $s;
        $final[] = $signs[$opp];
    }

    return array_values(array_unique($final));
}




    /* ------------------ IMENA ------------------ */
    public static function get_planet_name($code) {
        $names = [
            0 => 'Sunce',
            1 => 'Mjesec',
            2 => 'Merkur',
            3 => 'Venera',
            4 => 'Mars',
            5 => 'Jupiter',
            6 => 'Saturn',
            7 => 'Uran',
            8 => 'Neptun',
            9 => 'Pluton',
            10 => 'Sjeverni čvor',
            11 => 'Južni čvor',
            12 => 'Lilith'
        ];
        return $names[$code] ?? 'Nepoznato';
    }
}
?>
  

README:

# Ljepota Duše – Swiss Ephemeris Backend (Open Source)

Ovaj kod koristi Swiss Ephemeris © Astrodienst AG, licenciran pod GNU Affero General Public License v3 (AGPL v3).

## Što sadrži ovaj paket
- `sweph.php`: PHP wrapper s osnovnim funkcijama za izračun planeta, kuća i aspekata
- `LICENSE`: puna AGPL v3 licenca

## Dozvoljeno
- Korištenje, izmjena i redistribucija ovog koda pod uvjetima AGPL v3
- Uključivanje u open-source projekte i besplatne alate

## Nije obuhvaćeno AGPL-om
Frontend dizajn, tumačenja, analize, PDF-izvoz i sustavi plaćanja pripadaju Ljepoti Duše i nisu dio ovog open-source paketa.

## Izvor licence
[https://www.gnu.org/licenses/agpl-3.0.html](https://www.gnu.org/licenses/agpl-3.0.html)

© Ljepota Duše – 2025.

Privacy Preference Center