@jjlmoya/utils-nature 1.25.0 → 1.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/package.json +1 -1
  2. package/src/category/index.ts +2 -1
  3. package/src/entries.ts +2 -1
  4. package/src/index.ts +2 -0
  5. package/src/pages/[locale]/[slug].astro +5 -4
  6. package/src/tests/consumer_loader_contract.test.ts +24 -0
  7. package/src/tests/tool_validation.test.ts +2 -2
  8. package/src/tool/wildlifeCameraTrapEffortPlanner/bibliography.astro +9 -0
  9. package/src/tool/wildlifeCameraTrapEffortPlanner/bibliography.ts +16 -0
  10. package/src/tool/wildlifeCameraTrapEffortPlanner/component.astro +115 -0
  11. package/src/tool/wildlifeCameraTrapEffortPlanner/controller.ts +81 -0
  12. package/src/tool/wildlifeCameraTrapEffortPlanner/dom-views.ts +98 -0
  13. package/src/tool/wildlifeCameraTrapEffortPlanner/entry.ts +29 -0
  14. package/src/tool/wildlifeCameraTrapEffortPlanner/evaluator.test.ts +22 -0
  15. package/src/tool/wildlifeCameraTrapEffortPlanner/evaluator.ts +11 -0
  16. package/src/tool/wildlifeCameraTrapEffortPlanner/i18n/de.ts +49 -0
  17. package/src/tool/wildlifeCameraTrapEffortPlanner/i18n/en.ts +189 -0
  18. package/src/tool/wildlifeCameraTrapEffortPlanner/i18n/es.ts +46 -0
  19. package/src/tool/wildlifeCameraTrapEffortPlanner/i18n/fr.ts +44 -0
  20. package/src/tool/wildlifeCameraTrapEffortPlanner/i18n/id.ts +44 -0
  21. package/src/tool/wildlifeCameraTrapEffortPlanner/i18n/it.ts +43 -0
  22. package/src/tool/wildlifeCameraTrapEffortPlanner/i18n/ja.ts +43 -0
  23. package/src/tool/wildlifeCameraTrapEffortPlanner/i18n/ko.ts +43 -0
  24. package/src/tool/wildlifeCameraTrapEffortPlanner/i18n/nl.ts +43 -0
  25. package/src/tool/wildlifeCameraTrapEffortPlanner/i18n/pl.ts +43 -0
  26. package/src/tool/wildlifeCameraTrapEffortPlanner/i18n/pt.ts +43 -0
  27. package/src/tool/wildlifeCameraTrapEffortPlanner/i18n/ru.ts +43 -0
  28. package/src/tool/wildlifeCameraTrapEffortPlanner/i18n/sv.ts +43 -0
  29. package/src/tool/wildlifeCameraTrapEffortPlanner/i18n/tr.ts +43 -0
  30. package/src/tool/wildlifeCameraTrapEffortPlanner/i18n/zh.ts +43 -0
  31. package/src/tool/wildlifeCameraTrapEffortPlanner/index.ts +11 -0
  32. package/src/tool/wildlifeCameraTrapEffortPlanner/logic.test.ts +48 -0
  33. package/src/tool/wildlifeCameraTrapEffortPlanner/logic.ts +99 -0
  34. package/src/tool/wildlifeCameraTrapEffortPlanner/seo.astro +14 -0
  35. package/src/tool/wildlifeCameraTrapEffortPlanner/storage.ts +18 -0
  36. package/src/tool/wildlifeCameraTrapEffortPlanner/ui.ts +48 -0
  37. package/src/tool/wildlifeCameraTrapEffortPlanner/wildlife-camera-trap-effort-planner.css +442 -0
  38. package/src/tools.ts +3 -1
  39. package/src/types.ts +6 -5
@@ -0,0 +1,43 @@
1
+ import type { FAQPage, HowTo, SoftwareApplication, WithContext } from 'schema-dts';
2
+ import { bibliography } from '../bibliography';
3
+ import type { WildlifeCameraTrapEffortPlannerLocaleContent } from '../entry';
4
+
5
+ const slug = 'planer-wysilku-fotopulapek-przyrodniczych';
6
+ const title = 'Planer wysiłku fotopułapek przyrodniczych';
7
+ const description = 'Planuj wysiłek fotopułapek, szacuj dni pracy kamer i obciążenie przeglądaniem zdjęć bez udawania, że to estymacja populacji.';
8
+ const faqData = [
9
+ { question: 'Czym są dni pracy kamery?', answer: 'To liczba kamer pomnożona przez liczbę dni ich działania. Opisują wysiłek próbkowania, a nie liczbę zwierząt na obszarze.' },
10
+ { question: 'Czy planer oblicza wielkość populacji?', answer: 'Nie. Szacuje wysiłek operacyjny i nominalną powierzchnię planowania. Liczebność, zagęszczenie i zajętość wymagają zweryfikowanego projektu oraz modelu uwzględniającego niepełne wykrywanie.' },
11
+ { question: 'Co robi parametr odstępu?', answer: 'Tworzy nominalną kwadratową komórkę dla każdej kamery i porównuje ją z równym odstępem wynikającym z powierzchni i liczby kamer. Nie jest promieniem wykrywania.' },
12
+ { question: 'Jak używać harmonogramu kontroli?', answer: 'Wykorzystaj tygodniowe punkty do zaplanowania wizyt, baterii, pamięci i przeglądu zdjęć. Zastąp je rzeczywistymi wizytami z protokołu terenowego.' },
13
+ ];
14
+ const howToData = [
15
+ { name: 'Opisz obszar badania', text: 'Wpisz powierzchnię, którą ma objąć rozmieszczenie kamer. Przełącznik metryczny lub imperialny zachowuje wartość fizyczną.' },
16
+ { name: 'Rozplanuj wysiłek', text: 'Wpisz liczbę kamer, czas działania i docelowy odstęp. Szkic pokazuje względną siatkę, a nie współrzędne ani gwarantowane strefy wykrywania.' },
17
+ { name: 'Oszacuj obciążenie zdjęciami', text: 'Wpisz oczekiwaną liczbę zdarzeń na kamerę dziennie. Planer mnoży ją przez dni pracy kamer i rozdziela na kontrole.' },
18
+ { name: 'Przygotuj zapisy terenowe', text: 'Nadaj kamerom i sesjom unikalne identyfikatory, zapisz ustawienie i odnotuj każdą kontrolę, także bez wykrycia zwierzęcia.' },
19
+ ];
20
+ const faqSchema: WithContext<FAQPage> = { '@context': 'https://schema.org', '@type': 'FAQPage', mainEntity: faqData.map((item) => ({ '@type': 'Question', name: item.question, acceptedAnswer: { '@type': 'Answer', text: item.answer } })) };
21
+ const howToSchema: WithContext<HowTo> = { '@context': 'https://schema.org', '@type': 'HowTo', name: title, description, step: howToData.map((step, index) => ({ '@type': 'HowToStep', position: index + 1, name: step.name, text: step.text })) };
22
+ const appSchema: WithContext<SoftwareApplication> = { '@context': 'https://schema.org', '@type': 'SoftwareApplication', name: title, description, applicationCategory: 'UtilityApplication', operatingSystem: 'All', offers: { '@type': 'Offer', price: '0', priceCurrency: 'EUR' }, inLanguage: 'pl' };
23
+
24
+ export const content: WildlifeCameraTrapEffortPlannerLocaleContent = {
25
+ slug, title, description,
26
+ ui: {
27
+ areaPerCameraLabel: 'Powierzchnia na kamerę',
28
+ labelSurveyArea: 'Obszar badania', labelCameraCount: 'Stanowiska kamer', labelDeploymentDays: 'Dni działania', labelSpacing: 'Docelowy odstęp stanowisk', labelTriggerRate: 'Oczekiwane zdarzenia na kamerę dziennie', unitHectares: 'ha', unitAcres: 'ac', unitMeters: 'm', unitFeet: 'ft', unitCameras: 'kamer', unitDays: 'dni', unitEventsPerDay: 'zdarzeń dziennie', helpSurveyArea: 'Obszar, który ma objąć badanie.', helpCameraCount: 'Aktywne kamery w tej kampanii.', helpDeploymentDays: 'Dni od instalacji do odbioru.', helpSpacing: 'Pomoc planistyczna, nie promień wykrywania.', helpTriggerRate: 'Użyj ostrożnej wartości z podobnej kampanii lub testu.', unitSystemLabel: 'System miar', metricLabel: 'Metryczny', imperialLabel: 'Imperialny', presetsTitle: 'Punkty startowe w terenie', presetBackyard: 'Skraj lasu', presetWoodland: 'Las mieszany', presetReserve: 'Duży rezerwat', mapLabel: 'Względny szkic rozmieszczenia', mapCaption: 'Ponumerowane stanowiska na względnej siatce planistycznej. Komórki pomagają ocenić odstęp, ale nie są mapą siedliska.', stationLabel: 'stanowisko', cameraDaysLabel: 'Dni pracy kamer', expectedEventsLabel: 'Oczekiwane zdarzenia', spacingLabel: 'Odstęp równej siatki', reviewLoadLabel: 'Zdarzenia na kontrolę', reviewScheduleTitle: 'Rytm kontroli', reviewScheduleText: 'Tygodniowe punkty pokazują obciążenie zdjęciami przed instalacją. Zastąp je wizytami z własnego protokołu.', reviewDayLabel: 'Dzień', reviewEventsLabel: 'oczekiwanych zdarzeń', statusBalanced: 'Odstęp wygląda na zrównoważony', statusSparse: 'Stanowiska wyglądają na szeroko rozstawione', statusDense: 'Stanowiska wyglądają na blisko rozstawione', statusReviewHeavy: 'Obciążenie przeglądem wymaga uwagi', statusNeedsInput: 'Uzupełnij plan rozmieszczenia', statusNote: 'To plan wysiłku badawczego. Nie mówi, ile zwierząt żyje na obszarze i nie gwarantuje wykrycia gatunku.', assumptionTitle: 'Założenia modelu', assumptionText: 'Nominalna powierzchnia traktuje każde stanowisko jako kwadrat oparty na docelowym odstępie. Oczekiwane zdarzenia zakładają stałe tempo dla kamer i dni.', limitationTitle: 'Ograniczenia terenowe', limitationText: 'Wybieraj miejsca według gatunku i pytania, nie tylko geometrii. Zapisuj identyfikatory, daty, kierunek, wysokość, siedlisko i kontrole bez wykrycia.',
29
+ },
30
+ seo: [
31
+ { type: 'title', text: 'Planowanie wysiłku fotopułapek', level: 2 },
32
+ { type: 'paragraph', html: 'Projekt fotopułapek przed instalacją odpowiada na dwa pytania. Jaki wysiłek próbkowania przedstawia rozmieszczenie? Czy przewidywany strumień zdjęć można zapisać, sprawdzić i udokumentować? Ten planer zamienia je na dni pracy kamer, zdarzenia, szkic stanowisk i kontrole.' },
33
+ { type: 'title', text: 'Dni pracy kamer jako miara wysiłku', level: 3 },
34
+ { type: 'paragraph', html: 'Dni pracy kamer to <code>stanowiska kamer × dni działania</code>. Dwanaście kamer przez 28 dni daje 336 dni pracy. Ułatwia to porównywanie kampanii, ale nie jest estymacją populacji i nie koryguje awarii, siedliska ani zachowania.' },
35
+ { type: 'list', items: ['<strong>Stanowiska:</strong> urządzenia aktywne w kampanii.', '<strong>Dni działania:</strong> planowany czas od instalacji do odbioru.', '<strong>Tempo zdarzeń:</strong> zdarzenia na kamerę dziennie, nie każde zdjęcie z serii.', '<strong>Kontrole:</strong> zaplanuj pamięć, baterie, wizyty i przegląd.'] },
36
+ { type: 'title', text: 'Czytanie szkicu jako pomocy terenowej', level: 3 },
37
+ { type: 'paragraph', html: 'Szkic korzysta z powierzchni, liczby kamer i odstępu, aby pokazać względne rozmieszczenie. Nominalna powierzchnia to liczba stanowisk razy odstęp do kwadratu. Nie jest to powierzchnia czujnika, bo teren, roślinność, wysokość, kierunek i zachowanie zmieniają prawdopodobieństwo wykrycia.' },
38
+ { type: 'title', text: 'Lepsze zapisy przed instalacją', level: 3 },
39
+ { type: 'paragraph', html: 'Zdefiniuj gatunek docelowy i pytanie badawcze. Nadaj kamerom i sesjom stałe identyfikatory. Zapisuj datę, miejsce, wysokość, kierunek, siedlisko, ustawienia i działanie przy instalacji oraz kontroli. Kontrola bez wykrycia też jest obserwacją procesu.' },
40
+ { type: 'tip', title: 'Nie zamieniaj wysiłku na liczebność', html: 'Fotopułapki mają niepełne wykrywanie. Użyj planera do budżetu terenu i zdjęć, a zweryfikowanego projektu ekologicznego do zajętości, liczebności, zagęszczenia lub zmian populacji.' },
41
+ ],
42
+ faq: faqData, howTo: howToData, bibliography, schemas: [faqSchema, howToSchema, appSchema],
43
+ };
@@ -0,0 +1,43 @@
1
+ import type { FAQPage, HowTo, SoftwareApplication, WithContext } from 'schema-dts';
2
+ import { bibliography } from '../bibliography';
3
+ import type { WildlifeCameraTrapEffortPlannerLocaleContent } from '../entry';
4
+
5
+ const slug = 'planejador-esforco-armadilhas-fotograficas';
6
+ const title = 'Planejador de esforço de armadilhas fotográficas';
7
+ const description = 'Planeje o esforço de armadilhas fotográficas, estime dias de câmera e a carga de revisão sem transformar o planejamento em uma estimativa populacional.';
8
+ const faqData = [
9
+ { question: 'O que são dias de câmera?', answer: 'São o número de câmeras multiplicado pelo número de dias ativas. Descrevem o esforço de amostragem, não o número de animais em uma área.' },
10
+ { question: 'O planejador calcula o tamanho da população?', answer: 'Não. Ele estima o esforço operacional e uma área nominal de planejamento. Abundância, densidade e ocupação exigem desenho validado e modelo que considere detecção imperfeita.' },
11
+ { question: 'O que o espaçamento faz?', answer: 'Ele cria uma célula quadrada nominal para cada câmera e compara o alvo com o espaçamento de uma grade uniforme. Não é um raio de detecção.' },
12
+ { question: 'Como usar o calendário de revisão?', answer: 'Use os pontos semanais para planejar visitas, baterias, armazenamento e revisão. Troque-os pelas visitas reais do seu protocolo de campo.' },
13
+ ];
14
+ const howToData = [
15
+ { name: 'Descrever a área de estudo', text: 'Informe a área que a instalação deve amostrar. O seletor métrico ou imperial preserva o valor físico ao alternar.' },
16
+ { name: 'Planejar a disposição', text: 'Informe câmeras, duração e espaçamento-alvo. O desenho mostra uma grade relativa, não coordenadas nem zonas de detecção garantidas.' },
17
+ { name: 'Estimar o volume de imagens', text: 'Informe eventos esperados por câmera por dia. O planejador multiplica pelos dias de câmera e distribui o total nas revisões.' },
18
+ { name: 'Preparar registros de campo', text: 'Atribua identificadores únicos, registre a instalação e anote cada visita, mesmo quando nenhum animal for detectado.' },
19
+ ];
20
+ const faqSchema: WithContext<FAQPage> = { '@context': 'https://schema.org', '@type': 'FAQPage', mainEntity: faqData.map((item) => ({ '@type': 'Question', name: item.question, acceptedAnswer: { '@type': 'Answer', text: item.answer } })) };
21
+ const howToSchema: WithContext<HowTo> = { '@context': 'https://schema.org', '@type': 'HowTo', name: title, description, step: howToData.map((step, index) => ({ '@type': 'HowToStep', position: index + 1, name: step.name, text: step.text })) };
22
+ const appSchema: WithContext<SoftwareApplication> = { '@context': 'https://schema.org', '@type': 'SoftwareApplication', name: title, description, applicationCategory: 'UtilityApplication', operatingSystem: 'All', offers: { '@type': 'Offer', price: '0', priceCurrency: 'EUR' }, inLanguage: 'pt' };
23
+
24
+ export const content: WildlifeCameraTrapEffortPlannerLocaleContent = {
25
+ slug, title, description,
26
+ ui: {
27
+ areaPerCameraLabel: 'Área por câmera',
28
+ labelSurveyArea: 'Área de estudo', labelCameraCount: 'Estações de câmera', labelDeploymentDays: 'Dias de instalação', labelSpacing: 'Espaçamento-alvo', labelTriggerRate: 'Eventos esperados por câmera por dia', unitHectares: 'ha', unitAcres: 'ac', unitMeters: 'm', unitFeet: 'ft', unitCameras: 'câmeras', unitDays: 'dias', unitEventsPerDay: 'eventos por dia', helpSurveyArea: 'Área que a instalação deve amostrar.', helpCameraCount: 'Câmeras ativas nesta campanha.', helpDeploymentDays: 'Dias entre a instalação e a retirada.', helpSpacing: 'Uma célula de planejamento, não um raio de detecção.', helpTriggerRate: 'Use uma taxa cautelosa de campanha semelhante ou teste.', unitSystemLabel: 'Sistema de medidas', metricLabel: 'Métrico', imperialLabel: 'Imperial', presetsTitle: 'Pontos de partida em campo', presetBackyard: 'Borda do quintal', presetWoodland: 'Mata mista', presetReserve: 'Grande reserva', mapLabel: 'Esquema relativo da instalação', mapCaption: 'Estações numeradas em uma grade relativa. As células ajudam no espaçamento, mas não são um mapa de cobertura do habitat.', stationLabel: 'estação', cameraDaysLabel: 'Dias de câmera', expectedEventsLabel: 'Eventos esperados', spacingLabel: 'Espaçamento da grade uniforme', reviewLoadLabel: 'Eventos por revisão', reviewScheduleTitle: 'Ritmo de revisão', reviewScheduleText: 'Os pontos semanais mostram o volume antes da instalação. Substitua-os pelas visitas do seu protocolo.', reviewDayLabel: 'Dia', reviewEventsLabel: 'eventos esperados', statusBalanced: 'Espaçamento aparentemente equilibrado', statusSparse: 'Estações aparentemente distantes', statusDense: 'Estações aparentemente próximas', statusReviewHeavy: 'A carga de revisão exige atenção', statusNeedsInput: 'Complete o plano de instalação', statusNote: 'Este é um plano de esforço. Ele não informa quantos animais vivem na área nem garante a detecção de uma espécie.', assumptionTitle: 'Hipóteses do modelo', assumptionText: 'A área nominal trata cada estação como uma célula quadrada baseada no espaçamento-alvo. Os eventos esperados supõem uma taxa constante entre câmeras e dias.', limitationTitle: 'Limites de campo', limitationText: 'Escolha locais pela espécie e pela pergunta, não só pela geometria. Registre identificadores, datas, orientação, altura, habitat e revisões sem detecção.',
29
+ },
30
+ seo: [
31
+ { type: 'title', text: 'Planejar o esforço de armadilhas fotográficas', level: 2 },
32
+ { type: 'paragraph', html: 'Um projeto de armadilhas fotográficas precisa responder a duas perguntas antes da instalação. Quanto esforço de amostragem a campanha representa? O fluxo esperado de imagens pode ser armazenado, revisado e documentado? Este planejador organiza isso em dias de câmera, eventos, esquema e revisões.' },
33
+ { type: 'title', text: 'Usar dias de câmera para descrever o esforço', level: 3 },
34
+ { type: 'paragraph', html: 'Dias de câmera são <code>estações de câmera × dias de instalação</code>. Doze câmeras ativas por 28 dias produzem 336 dias de câmera. Isso compara campanhas, mas não estima população e não corrige falhas, habitat ou comportamento.' },
35
+ { type: 'list', items: ['<strong>Estações:</strong> dispositivos ativos durante a campanha.', '<strong>Dias:</strong> período previsto entre instalação e retirada.', '<strong>Taxa de eventos:</strong> eventos por câmera por dia, não cada foto de uma sequência.', '<strong>Revisões:</strong> planeje armazenamento, baterias, visitas e análise.'] },
36
+ { type: 'title', text: 'Ler o esquema como apoio de campo', level: 3 },
37
+ { type: 'paragraph', html: 'O esquema usa área, número de câmeras e espaçamento-alvo para mostrar uma disposição relativa. A área nominal é o número de estações vezes o espaçamento ao quadrado. Não é a área do sensor: terreno, vegetação, altura, orientação e comportamento mudam a probabilidade de detecção.' },
38
+ { type: 'title', text: 'Preparar registros melhores antes da instalação', level: 3 },
39
+ { type: 'paragraph', html: 'Defina a espécie-alvo e a pergunta. Dê um identificador estável a cada câmera e sessão. Registre data, local, altura, orientação, habitat, configurações e funcionamento. Uma revisão sem detecção também é uma observação do processo.' },
40
+ { type: 'tip', title: 'Não transforme esforço em abundância', html: 'Armadilhas fotográficas têm detecção imperfeita. Use o planejador para o orçamento de campo e imagens e um desenho ecológico validado para ocupação, abundância, densidade ou mudança populacional.' },
41
+ ],
42
+ faq: faqData, howTo: howToData, bibliography, schemas: [faqSchema, howToSchema, appSchema],
43
+ };
@@ -0,0 +1,43 @@
1
+ import type { FAQPage, HowTo, SoftwareApplication, WithContext } from 'schema-dts';
2
+ import { bibliography } from '../bibliography';
3
+ import type { WildlifeCameraTrapEffortPlannerLocaleContent } from '../entry';
4
+
5
+ const slug = 'planirovshchik-usilii-fotolovushek';
6
+ const title = 'Планировщик усилий фотоловушек';
7
+ const description = 'Планируйте работу фотоловушек, оценивайте дни работы камер и нагрузку проверки изображений без оценки численности животных.';
8
+ const faqData = [
9
+ { question: 'Что такое камеродни?', answer: 'Камеродни равны числу камер, умноженному на число дней их работы. Это показатель усилий выборки, а не числа животных на территории.' },
10
+ { question: 'Рассчитывает ли планировщик численность популяции?', answer: 'Нет. Он оценивает рабочие усилия и условную площадь планирования. Численность, плотность и заселённость требуют проверенного дизайна и модели неполного обнаружения.' },
11
+ { question: 'Для чего нужен интервал между станциями?', answer: 'Он создаёт условную квадратную ячейку для каждой камеры и сравнивает цель с равномерным интервалом, выведенным из площади и числа камер. Это не радиус обнаружения.' },
12
+ { question: 'Как использовать график проверок?', answer: 'Используйте еженедельные точки для планирования визитов, батарей, памяти и проверки изображений. Замените их реальными визитами из полевого протокола.' },
13
+ ];
14
+ const howToData = [
15
+ { name: 'Описать территорию исследования', text: 'Введите площадь, которую должна охватить установка. Переключение единиц сохраняет физическое значение.' },
16
+ { name: 'Наметить размещение камер', text: 'Введите число камер, длительность и целевой интервал. Схема показывает относительную сетку, а не координаты и не гарантированную зону обнаружения.' },
17
+ { name: 'Оценить поток изображений', text: 'Введите ожидаемые события на камеру в день. Планировщик умножит их на камеродни и распределит по проверкам.' },
18
+ { name: 'Подготовить полевые записи', text: 'Назначьте уникальные идентификаторы камерам и сессиям, запишите установку и каждую проверку, даже если животные не обнаружены.' },
19
+ ];
20
+ const faqSchema: WithContext<FAQPage> = { '@context': 'https://schema.org', '@type': 'FAQPage', mainEntity: faqData.map((item) => ({ '@type': 'Question', name: item.question, acceptedAnswer: { '@type': 'Answer', text: item.answer } })) };
21
+ const howToSchema: WithContext<HowTo> = { '@context': 'https://schema.org', '@type': 'HowTo', name: title, description, step: howToData.map((step, index) => ({ '@type': 'HowToStep', position: index + 1, name: step.name, text: step.text })) };
22
+ const appSchema: WithContext<SoftwareApplication> = { '@context': 'https://schema.org', '@type': 'SoftwareApplication', name: title, description, applicationCategory: 'UtilityApplication', operatingSystem: 'All', offers: { '@type': 'Offer', price: '0', priceCurrency: 'EUR' }, inLanguage: 'ru' };
23
+
24
+ export const content: WildlifeCameraTrapEffortPlannerLocaleContent = {
25
+ slug, title, description,
26
+ ui: {
27
+ areaPerCameraLabel: 'Площадь на камеру',
28
+ labelSurveyArea: 'Площадь исследования', labelCameraCount: 'Камерные станции', labelDeploymentDays: 'Дни установки', labelSpacing: 'Целевой интервал станций', labelTriggerRate: 'Ожидаемые события на камеру в день', unitHectares: 'га', unitAcres: 'акр', unitMeters: 'м', unitFeet: 'фут', unitCameras: 'камер', unitDays: 'дней', unitEventsPerDay: 'событий в день', helpSurveyArea: 'Площадь, которую должна охватить установка.', helpCameraCount: 'Активные камеры этой кампании.', helpDeploymentDays: 'Дни от установки до снятия.', helpSpacing: 'Помощь для планирования, не радиус обнаружения.', helpTriggerRate: 'Используйте осторожную оценку из похожей кампании или теста.', unitSystemLabel: 'Система измерений', metricLabel: 'Метрическая', imperialLabel: 'Имперская', presetsTitle: 'Полевые отправные точки', presetBackyard: 'Опушка у дома', presetWoodland: 'Смешанный лес', presetReserve: 'Большой заповедник', mapLabel: 'Относительная схема установки', mapCaption: 'Пронумерованные станции на относительной планировочной сетке. Это не карта покрытия местообитаний.', stationLabel: 'станция', cameraDaysLabel: 'Камеродни', expectedEventsLabel: 'Ожидаемые события', spacingLabel: 'Интервал равномерной сетки', reviewLoadLabel: 'Событий за проверку', reviewScheduleTitle: 'Ритм проверок', reviewScheduleText: 'Еженедельные точки показывают нагрузку до установки камер. Замените их визитами вашего протокола.', reviewDayLabel: 'День', reviewEventsLabel: 'ожидаемых событий', statusBalanced: 'Интервал выглядит сбалансированным', statusSparse: 'Станции выглядят широко разнесёнными', statusDense: 'Станции выглядят тесно расположенными', statusReviewHeavy: 'Нагрузка проверки требует внимания', statusNeedsInput: 'Заполните план установки', statusNote: 'Это план усилий. Он не показывает число животных и не гарантирует обнаружение вида.', assumptionTitle: 'Допущения модели', assumptionText: 'Условная площадь представляет каждую станцию квадратной ячейкой на основе целевого интервала. Ожидаемые события предполагают постоянную частоту для всех камер и дней.', limitationTitle: 'Полевые ограничения', limitationText: 'Выбирайте места по виду и вопросу исследования, а не только по геометрии. Записывайте идентификаторы, даты, направление, высоту, местообитание и проверки без обнаружений.',
29
+ },
30
+ seo: [
31
+ { type: 'title', text: 'Планирование усилий фотоловушек', level: 2 },
32
+ { type: 'paragraph', html: 'До установки фотоловушек нужно ответить на два вопроса. Какой объём выборочных усилий представляет кампания? Можно ли сохранить, проверить и описать ожидаемый поток изображений? Этот планировщик сводит ответы к камеродням, событиям, схеме станций и контрольным точкам.' },
33
+ { type: 'title', text: 'Камеродни как мера усилий', level: 3 },
34
+ { type: 'paragraph', html: 'Камеродни равны <code>камерные станции × дни установки</code>. Двенадцать камер за 28 дней дают 336 камеродней. Это помогает сравнивать кампании, но не оценивает популяцию и не исправляет отказы, различия местообитаний или поведение животных.' },
35
+ { type: 'list', items: ['<strong>Станции:</strong> устройства, работающие в кампании.', '<strong>Дни:</strong> планируемый срок от установки до снятия.', '<strong>Частота событий:</strong> события на камеру в день, а не каждый кадр серии.', '<strong>Проверки:</strong> планируйте память, батареи, визиты и просмотр изображений.'] },
36
+ { type: 'title', text: 'Схема как полевая подсказка', level: 3 },
37
+ { type: 'paragraph', html: 'Схема использует площадь, число камер и целевой интервал для относительного размещения. Условная площадь равна числу станций, умноженному на квадрат интервала. Это не площадь сенсора: рельеф, растительность, высота, направление и поведение меняют вероятность обнаружения.' },
38
+ { type: 'title', text: 'Подготовить записи до установки', level: 3 },
39
+ { type: 'paragraph', html: 'Определите целевой вид и исследовательский вопрос. Присвойте камерам и сессиям постоянные идентификаторы. Записывайте дату, место, высоту, направление, местообитание, настройки и исправность. Проверка без обнаружения тоже является наблюдением процесса.' },
40
+ { type: 'tip', title: 'Не превращайте усилия в численность', html: 'Фотоловушки дают неполное обнаружение. Используйте планировщик для бюджета поля и изображений, а проверенный экологический дизайн для заселённости, численности, плотности или изменений популяции.' },
41
+ ],
42
+ faq: faqData, howTo: howToData, bibliography, schemas: [faqSchema, howToSchema, appSchema],
43
+ };
@@ -0,0 +1,43 @@
1
+ import type { FAQPage, HowTo, SoftwareApplication, WithContext } from 'schema-dts';
2
+ import { bibliography } from '../bibliography';
3
+ import type { WildlifeCameraTrapEffortPlannerLocaleContent } from '../entry';
4
+
5
+ const slug = 'planer-for-viltkamerors-insats';
6
+ const title = 'Planerare för insats med viltkameror';
7
+ const description = 'Planera viltkamerornas insats, uppskatta kameradagar och granskningsarbete och skissa en placering utan att uppskatta en population.';
8
+ const faqData = [
9
+ { question: 'Vad är kameradagar?', answer: 'Kameradagar är antalet kameror multiplicerat med antalet aktiva dagar. De beskriver provtagningsinsatsen, inte antalet djur i området.' },
10
+ { question: 'Beräknar planeraren populationsstorlek?', answer: 'Nej. Den uppskattar arbetsinsats och en nominell planeringsyta. Täthet, antal och förekomst kräver en validerad studiedesign som tar hänsyn till ofullständig upptäckt.' },
11
+ { question: 'Vad gör avståndet mellan stationerna?', answer: 'Det skapar en nominell kvadratisk planeringscell per kamera och jämför målet med ett jämnt rutnätsavstånd. Det är inte en upptäcktsradie.' },
12
+ { question: 'Hur använder jag granskningsschemat?', answer: 'Använd veckopunkterna för att planera besök, batterier, lagring och bildgranskning. Ersätt dem med de faktiska besöken i fältprotokollet.' },
13
+ ];
14
+ const howToData = [
15
+ { name: 'Beskriv undersökningsområdet', text: 'Ange området som kamerorna ska täcka. Metrisk eller imperial växling behåller det fysiska värdet.' },
16
+ { name: 'Planera placeringen', text: 'Ange kameror, varaktighet och målavstånd. Skissen visar ett relativt planeringsrutnät, inte koordinater eller garanterade upptäcktszoner.' },
17
+ { name: 'Uppskatta bildmängden', text: 'Ange förväntade händelser per kamera och dag. Planeraren multiplicerar värdet med kameradagar och fördelar det på kontroller.' },
18
+ { name: 'Förbered fältanteckningar', text: 'Ge kameror och sessioner unika identiteter, skriv ned placeringen och registrera varje kontroll även utan upptäckt.' },
19
+ ];
20
+ const faqSchema: WithContext<FAQPage> = { '@context': 'https://schema.org', '@type': 'FAQPage', mainEntity: faqData.map((item) => ({ '@type': 'Question', name: item.question, acceptedAnswer: { '@type': 'Answer', text: item.answer } })) };
21
+ const howToSchema: WithContext<HowTo> = { '@context': 'https://schema.org', '@type': 'HowTo', name: title, description, step: howToData.map((step, index) => ({ '@type': 'HowToStep', position: index + 1, name: step.name, text: step.text })) };
22
+ const appSchema: WithContext<SoftwareApplication> = { '@context': 'https://schema.org', '@type': 'SoftwareApplication', name: title, description, applicationCategory: 'UtilityApplication', operatingSystem: 'All', offers: { '@type': 'Offer', price: '0', priceCurrency: 'EUR' }, inLanguage: 'sv' };
23
+
24
+ export const content: WildlifeCameraTrapEffortPlannerLocaleContent = {
25
+ slug, title, description,
26
+ ui: {
27
+ areaPerCameraLabel: 'Yta per kamera',
28
+ labelSurveyArea: 'Undersökningsområde', labelCameraCount: 'Kamerastationer', labelDeploymentDays: 'Utplaceringsdagar', labelSpacing: 'Målavstånd mellan stationer', labelTriggerRate: 'Förväntade händelser per kamera och dag', unitHectares: 'ha', unitAcres: 'ac', unitMeters: 'm', unitFeet: 'ft', unitCameras: 'kameror', unitDays: 'dagar', unitEventsPerDay: 'händelser per dag', helpSurveyArea: 'Området som utplaceringen ska undersöka.', helpCameraCount: 'Aktiva kameror i kampanjen.', helpDeploymentDays: 'Dagar från montering till hämtning.', helpSpacing: 'Ett planeringsmått, inte en upptäcktsradie.', helpTriggerRate: 'Använd ett försiktigt värde från en liknande kampanj eller ett test.', unitSystemLabel: 'Mätsystem', metricLabel: 'Metriskt', imperialLabel: 'Imperialt', presetsTitle: 'Startpunkter för fältet', presetBackyard: 'Skogsbryn', presetWoodland: 'Blandskog', presetReserve: 'Stort reservat', mapLabel: 'Relativ skiss av placeringen', mapCaption: 'Numrerade stationer i ett relativt planeringsrutnät. Cellerna är inte en karta över livsmiljön.', stationLabel: 'station', cameraDaysLabel: 'Kameradagar', expectedEventsLabel: 'Förväntade händelser', spacingLabel: 'Avstånd i jämnt rutnät', reviewLoadLabel: 'Händelser per kontroll', reviewScheduleTitle: 'Kontrollrytm', reviewScheduleText: 'Veckopunkterna synliggör bildmängden före utplacering. Ersätt dem med protokollets besök.', reviewDayLabel: 'Dag', reviewEventsLabel: 'förväntade händelser', statusBalanced: 'Avståndet verkar balanserat', statusSparse: 'Stationerna verkar glest placerade', statusDense: 'Stationerna verkar tätt placerade', statusReviewHeavy: 'Granskningsbördan kräver uppmärksamhet', statusNeedsInput: 'Fyll i en fullständig plan', statusNote: 'Detta är en insatsplan. Den säger inte hur många djur som finns och garanterar inte upptäckt av en art.', assumptionTitle: 'Modellens antaganden', assumptionText: 'Den nominella ytan behandlar varje station som en kvadrat baserad på målavståndet. Förväntade händelser antar en konstant takt för alla kameror och dagar.', limitationTitle: 'Begränsningar i fält', limitationText: 'Välj platser efter art och fråga, inte bara geometri. Anteckna identiteter, datum, riktning, höjd, livsmiljö och kontroller utan upptäckt.',
29
+ },
30
+ seo: [
31
+ { type: 'title', text: 'Planera insatsen med viltkameror', level: 2 },
32
+ { type: 'paragraph', html: 'Ett projekt med viltkameror måste besvara två frågor före utplaceringen. Hur stor provtagningsinsats representerar planen? Kan det förväntade bildflödet lagras, granskas och dokumenteras? Planeraren gör detta till kameradagar, händelser, stationsskiss och kontrollpunkter.' },
33
+ { type: 'title', text: 'Använd kameradagar för att beskriva insatsen', level: 3 },
34
+ { type: 'paragraph', html: 'Kameradagar är <code>kamerastationer × utplaceringsdagar</code>. Tolv kameror under 28 dagar ger 336 kameradagar. Det hjälper att jämföra kampanjer men är ingen populationsuppskattning och korrigerar inte för fel, livsmiljö eller beteende.' },
35
+ { type: 'list', items: ['<strong>Kamerastationer:</strong> enheter som är aktiva under kampanjen.', '<strong>Utplaceringsdagar:</strong> planerad tid från montering till hämtning.', '<strong>Händelsetakt:</strong> händelser per kamera och dag, inte varje bild i en serie.', '<strong>Kontroller:</strong> planera lagring, batterier, besök och granskning.'] },
36
+ { type: 'title', text: 'Läs skissen som ett fälthjälpmedel', level: 3 },
37
+ { type: 'paragraph', html: 'Skissen använder yta, antal kameror och målavstånd för en relativ placering. Den nominella ytan är antal stationer gånger avståndet i kvadrat. Det är inte sensorns yta: terräng, växtlighet, höjd, riktning och beteende ändrar upptäcktssannolikheten.' },
38
+ { type: 'title', text: 'Förbered bättre anteckningar före utplacering', level: 3 },
39
+ { type: 'paragraph', html: 'Bestäm målart och fråga. Ge varje kamera och session en stabil identitet. Registrera datum, plats, höjd, riktning, livsmiljö, inställningar och funktion vid montering och kontroll. Även en kontroll utan upptäckt är en observation.' },
40
+ { type: 'tip', title: 'Gör inte insats till populationsantal', html: 'Viltkameror har ofullständig upptäckt. Använd planeraren för fält- och bildarbete och en validerad ekologisk design för förekomst, antal, täthet eller populationsförändring.' },
41
+ ],
42
+ faq: faqData, howTo: howToData, bibliography, schemas: [faqSchema, howToSchema, appSchema],
43
+ };
@@ -0,0 +1,43 @@
1
+ import type { FAQPage, HowTo, SoftwareApplication, WithContext } from 'schema-dts';
2
+ import { bibliography } from '../bibliography';
3
+ import type { WildlifeCameraTrapEffortPlannerLocaleContent } from '../entry';
4
+
5
+ const slug = 'yaban-hayati-fotokapan-caba-planlayici';
6
+ const title = 'Yaban hayatı fotokapan çabası planlayıcısı';
7
+ const description = 'Fotokapan çalışmasının çabasını planlayın, kamera günlerini ve görüntü inceleme yükünü tahmin edin; nüfus tahmini yapmayın.';
8
+ const faqData = [
9
+ { question: 'Kamera günleri nedir?', answer: 'Kamera günleri, çalışan kamera sayısının etkin gün sayısıyla çarpımıdır. Örnekleme çabasını gösterir, alandaki hayvan sayısını göstermez.' },
10
+ { question: 'Planlayıcı yaban hayatı nüfusunu hesaplar mı?', answer: 'Hayır. İşletme çabasını ve nominal bir planlama alanını tahmin eder. Sayı, yoğunluk ve kullanım için eksik tespiti dikkate alan doğrulanmış tasarım ve model gerekir.' },
11
+ { question: 'İstasyon aralığı ne işe yarar?', answer: 'Her kamera için nominal kare hücre oluşturur ve hedefi alan ile kamera sayısından çıkan eşit ızgara aralığıyla karşılaştırır. Bu bir tespit yarıçapı değildir.' },
12
+ { question: 'İnceleme programı nasıl kullanılmalı?', answer: 'Haftalık noktaları ziyaret, pil, depolama ve görüntü incelemesini planlamak için kullanın. Bunları gerçek saha protokolünüzdeki ziyaretlerle değiştirin.' },
13
+ ];
14
+ const howToData = [
15
+ { name: 'Araştırma alanını tanımlayın', text: 'Kamera yerleşiminin örnekleyeceği alanı girin. Metrik veya emperyal seçim fiziksel değeri korur.' },
16
+ { name: 'Çabayı yerleşime dönüştürün', text: 'Kamera sayısını, süreyi ve hedef aralığı girin. Çizim göreli bir plan ızgarasıdır; koordinat veya garanti edilen tespit bölgesi değildir.' },
17
+ { name: 'Görüntü yükünü tahmin edin', text: 'Kamera başına günlük beklenen olayları girin. Planlayıcı bunları kamera günleriyle çarpar ve kontrollere dağıtır.' },
18
+ { name: 'Saha kayıtlarını hazırlayın', text: 'Kamera ve oturumlara benzersiz kimlik verin, yerleşimi yazın ve hayvan görülmese de her kontrolü kaydedin.' },
19
+ ];
20
+ const faqSchema: WithContext<FAQPage> = { '@context': 'https://schema.org', '@type': 'FAQPage', mainEntity: faqData.map((item) => ({ '@type': 'Question', name: item.question, acceptedAnswer: { '@type': 'Answer', text: item.answer } })) };
21
+ const howToSchema: WithContext<HowTo> = { '@context': 'https://schema.org', '@type': 'HowTo', name: title, description, step: howToData.map((step, index) => ({ '@type': 'HowToStep', position: index + 1, name: step.name, text: step.text })) };
22
+ const appSchema: WithContext<SoftwareApplication> = { '@context': 'https://schema.org', '@type': 'SoftwareApplication', name: title, description, applicationCategory: 'UtilityApplication', operatingSystem: 'All', offers: { '@type': 'Offer', price: '0', priceCurrency: 'EUR' }, inLanguage: 'tr' };
23
+
24
+ export const content: WildlifeCameraTrapEffortPlannerLocaleContent = {
25
+ slug, title, description,
26
+ ui: {
27
+ areaPerCameraLabel: 'Kamera başına alan',
28
+ labelSurveyArea: 'Araştırma alanı', labelCameraCount: 'Kamera istasyonları', labelDeploymentDays: 'Yerleştirme günleri', labelSpacing: 'Hedef istasyon aralığı', labelTriggerRate: 'Kamera başına günlük beklenen olay', unitHectares: 'ha', unitAcres: 'akre', unitMeters: 'm', unitFeet: 'ft', unitCameras: 'kamera', unitDays: 'gün', unitEventsPerDay: 'günlük olay', helpSurveyArea: 'Yerleşimin örnekleyeceği alan.', helpCameraCount: 'Bu çalışmadaki etkin kameralar.', helpDeploymentDays: 'Kurulumdan toplamaya kadar günler.', helpSpacing: 'Planlama hücresi; tespit yarıçapı değildir.', helpTriggerRate: 'Benzer bir çalışma veya denemeden temkinli oran kullanın.', unitSystemLabel: 'Ölçüm sistemi', metricLabel: 'Metrik', imperialLabel: 'Emperyal', presetsTitle: 'Saha başlangıçları', presetBackyard: 'Orman kenarı', presetWoodland: 'Karışık orman', presetReserve: 'Büyük koruma alanı', mapLabel: 'Göreli yerleşim çizimi', mapCaption: 'Numaralı istasyonlar göreli plan ızgarında gösterilir. Hücreler habitat kapsama haritası değildir.', stationLabel: 'istasyon', cameraDaysLabel: 'Kamera günleri', expectedEventsLabel: 'Beklenen olaylar', spacingLabel: 'Eşit ızgara aralığı', reviewLoadLabel: 'İnceleme başına olay', reviewScheduleTitle: 'İnceleme ritmi', reviewScheduleText: 'Haftalık noktalar yerleşimden önce görüntü yükünü görünür kılar. Kendi protokolünüzdeki ziyaretlerle değiştirin.', reviewDayLabel: 'Gün', reviewEventsLabel: 'beklenen olay', statusBalanced: 'Aralık dengeli görünüyor', statusSparse: 'İstasyonlar uzak görünüyor', statusDense: 'İstasyonlar yakın görünüyor', statusReviewHeavy: 'İnceleme yüküne dikkat edin', statusNeedsInput: 'Yerleşim planını tamamlayın', statusNote: 'Bu bir çaba planıdır. Alandaki hayvan sayısını söylemez ve tür tespitini garanti etmez.', assumptionTitle: 'Model varsayımları', assumptionText: 'Nominal alan her istasyonu hedef aralığa dayalı kare hücre sayar. Beklenen olaylar tüm kamera ve günlerde sabit oran varsayar.', limitationTitle: 'Saha sınırları', limitationText: 'Konumları yalnız geometriye göre değil, tür ve soruya göre seçin. Kimlikleri, tarihleri, yönü, yüksekliği, habitatı ve boş kontrolleri kaydedin.',
29
+ },
30
+ seo: [
31
+ { type: 'title', text: 'Fotokapan çalışma çabasını planlayın', level: 2 },
32
+ { type: 'paragraph', html: 'Bir fotokapan projesi yerleştirmeden önce iki soruya cevap vermelidir. Yerleşim ne kadar örnekleme çabası yaratıyor? Beklenen görüntü akışı saklanabilir, incelenebilir ve belgelenebilir mi? Bu planlayıcı bunları kamera günleri, olaylar, istasyon çizimi ve kontroller olarak düzenler.' },
33
+ { type: 'title', text: 'Çabayı kamera günleriyle anlatın', level: 3 },
34
+ { type: 'paragraph', html: 'Kamera günleri <code>kamera istasyonları × yerleştirme günleri</code> şeklinde hesaplanır. On iki kamera 28 gün çalışırsa 336 kamera günü oluşur. Bu kampanyaları karşılaştırır, ancak nüfus tahmini değildir ve arıza, habitat ya da davranışı düzeltmez.' },
35
+ { type: 'list', items: ['<strong>İstasyonlar:</strong> kampanyada çalışan cihazlar.', '<strong>Günler:</strong> kurulumdan toplamaya kadar planlanan süre.', '<strong>Olay oranı:</strong> seri çekimdeki her fotoğraf değil, kamera başına günlük olay.', '<strong>Kontroller:</strong> depolama, pil, ziyaret ve incelemeyi planlayın.'] },
36
+ { type: 'title', text: 'Çizimi saha yardımcısı olarak okuyun', level: 3 },
37
+ { type: 'paragraph', html: 'Çizim alanı, kamera sayısını ve hedef aralığı göreli bir yerleşim için kullanır. Nominal alan istasyon sayısının aralığın karesiyle çarpımıdır. Sensör alanı değildir; arazi, bitki örtüsü, yükseklik, yön ve davranış tespit olasılığını değiştirir.' },
38
+ { type: 'title', text: 'Kurulumdan önce kayıtları hazırlayın', level: 3 },
39
+ { type: 'paragraph', html: 'Hedef türü ve araştırma sorusunu belirleyin. Her kamera ve oturuma kalıcı kimlik verin. Tarih, konum, yükseklik, yön, habitat, ayarlar ve çalışma durumunu kaydedin. Tespit olmayan kontrol de gerçek bir saha gözlemidir.' },
40
+ { type: 'tip', title: 'Çabayı hayvan sayısına çevirmeyin', html: 'Fotokapanlarda eksik tespit vardır. Saha ve görüntü bütçesi için planlayıcıyı, kullanım, sayı, yoğunluk veya nüfus değişimi için doğrulanmış ekolojik tasarımı kullanın.' },
41
+ ],
42
+ faq: faqData, howTo: howToData, bibliography, schemas: [faqSchema, howToSchema, appSchema],
43
+ };
@@ -0,0 +1,43 @@
1
+ import type { FAQPage, HowTo, SoftwareApplication, WithContext } from 'schema-dts';
2
+ import { bibliography } from '../bibliography';
3
+ import type { WildlifeCameraTrapEffortPlannerLocaleContent } from '../entry';
4
+
5
+ const slug = 'wildlife-camera-trap-effort-planner';
6
+ const title = '野生动物红外相机调查工作量规划器';
7
+ const description = '规划红外相机调查工作量,估算相机日和图像审核负担,并绘制不用于种群估计的布设方案。';
8
+ const faqData = [
9
+ { question: '什么是相机日?', answer: '相机日是运行中的相机数量乘以运行天数。它表示抽样工作量,不表示区域内的动物数量。' },
10
+ { question: '规划器会计算野生动物种群数量吗?', answer: '不会。它估算运行工作量和名义规划面积。数量、密度和占域率需要经过验证的调查设计与能够处理不完全探测的统计模型。' },
11
+ { question: '相机间距有什么作用?', answer: '它为每台相机建立名义方形规划单元,并将目标间距与根据面积和相机数量得到的均匀网格间距进行比较。它不是探测半径。' },
12
+ { question: '如何使用审核计划?', answer: '用每周检查点规划访问、电池、存储和图像审核。根据实际野外协议,将检查点替换为真实访问记录。' },
13
+ ];
14
+ const howToData = [
15
+ { name: '填写调查面积', text: '输入相机布设计划覆盖的面积。切换公制或英制时,物理数值保持一致。' },
16
+ { name: '规划相机布设', text: '输入相机数量、布设时长和目标间距。示意图是相对规划网格,不是坐标或保证的探测范围。' },
17
+ { name: '估算图像工作量', text: '输入每台相机每天的预期事件数。规划器将其乘以相机日,并分配到每周检查点。' },
18
+ { name: '准备野外记录', text: '为相机和布设会话分配唯一编号,记录安装信息,并记录每次检查,包括没有发现动物的检查。' },
19
+ ];
20
+ const faqSchema: WithContext<FAQPage> = { '@context': 'https://schema.org', '@type': 'FAQPage', mainEntity: faqData.map((item) => ({ '@type': 'Question', name: item.question, acceptedAnswer: { '@type': 'Answer', text: item.answer } })) };
21
+ const howToSchema: WithContext<HowTo> = { '@context': 'https://schema.org', '@type': 'HowTo', name: title, description, step: howToData.map((step, index) => ({ '@type': 'HowToStep', position: index + 1, name: step.name, text: step.text })) };
22
+ const appSchema: WithContext<SoftwareApplication> = { '@context': 'https://schema.org', '@type': 'SoftwareApplication', name: title, description, applicationCategory: 'UtilityApplication', operatingSystem: 'All', offers: { '@type': 'Offer', price: '0', priceCurrency: 'EUR' }, inLanguage: 'zh' };
23
+
24
+ export const content: WildlifeCameraTrapEffortPlannerLocaleContent = {
25
+ slug, title, description,
26
+ ui: {
27
+ areaPerCameraLabel: '每台相机的面积',
28
+ labelSurveyArea: '调查面积', labelCameraCount: '相机点位', labelDeploymentDays: '布设天数', labelSpacing: '目标点位间距', labelTriggerRate: '每台相机每天的预期事件', unitHectares: '公顷', unitAcres: '英亩', unitMeters: '米', unitFeet: '英尺', unitCameras: '台', unitDays: '天', unitEventsPerDay: '事件/天', helpSurveyArea: '计划进行相机调查的面积。', helpCameraCount: '本次调查中运行的相机数量。', helpDeploymentDays: '从安装到回收的天数。', helpSpacing: '规划单元大小,不是探测半径。', helpTriggerRate: '参考相似调查或试布设,使用谨慎的数值。', unitSystemLabel: '测量单位', metricLabel: '公制', imperialLabel: '英制', presetsTitle: '野外起始方案', presetBackyard: '林缘', presetWoodland: '混合林', presetReserve: '大型保护区', mapLabel: '相对布设示意图', mapCaption: '编号相机排列在相对规划网格中。单元用于辅助间距规划,不是生境覆盖地图。', stationLabel: '点位', cameraDaysLabel: '相机日', expectedEventsLabel: '预期事件', spacingLabel: '均匀网格间距', reviewLoadLabel: '每次检查的事件', reviewScheduleTitle: '检查节奏', reviewScheduleText: '每周检查点能在布设前展示图像工作量。请根据实际野外协议调整。', reviewDayLabel: '第', reviewEventsLabel: '预期事件', statusBalanced: '间距看起来较均衡', statusSparse: '点位间距看起来较大', statusDense: '点位间距看起来较小', statusReviewHeavy: '图像审核负担需要关注', statusNeedsInput: '请填写完整布设计划', statusNote: '这是工作量计划,不会告诉你区域内有多少动物,也不保证发现某个物种。', assumptionTitle: '模型假设', assumptionText: '名义面积把每个点位视为由目标间距形成的正方形单元。预期事件假设所有相机和日期的触发率保持不变。', limitationTitle: '野外限制', limitationText: '根据目标物种和研究问题选择点位,而不只是追求几何整齐。记录编号、日期、方向、高度、生境以及空检结果。',
29
+ },
30
+ seo: [
31
+ { type: 'title', text: '规划红外相机调查工作量', level: 2 },
32
+ { type: 'paragraph', html: '野生动物红外相机项目在布设前需要回答两个问题:这次布设代表多少抽样工作量?预期图像流能否被存储、审核和记录?本规划器将这些问题整理为相机日、预期事件、点位示意图和检查节点。' },
33
+ { type: 'title', text: '用相机日描述调查工作量', level: 3 },
34
+ { type: 'paragraph', html: '相机日等于 <code>相机点位 × 布设天数</code>。12台相机运行28天会产生336相机日。这个数字可以比较不同调查,但不是种群估计,也不会修正设备故障、生境差异或动物行为。' },
35
+ { type: 'list', items: ['<strong>相机点位:</strong>调查期间运行的设备数量。', '<strong>布设天数:</strong>从安装到回收的计划时间。', '<strong>触发率:</strong>使用每台相机每天的事件数,而不是连拍中的每张照片。', '<strong>检查节点:</strong>据此规划存储、电池、访问和图像审核。'] },
36
+ { type: 'title', text: '把示意图当作野外规划辅助', level: 3 },
37
+ { type: 'paragraph', html: '示意图使用面积、相机数量和目标间距展示相对布设。名义面积等于点位数量乘以目标间距的平方。它不是传感器探测面积,地形、植被、道路、方向、高度和动物行为都会改变探测概率。' },
38
+ { type: 'title', text: '布设前准备更完整的记录', level: 3 },
39
+ { type: 'paragraph', html: '先确定目标物种和研究问题,为每台相机及每次布设设置稳定编号。在安装和检查时记录日期、位置、高度、方向、生境、参数和设备状态。没有拍到动物的检查也属于调查过程记录。' },
40
+ { type: 'tip', title: '不要把工作量变成动物数量', html: '红外相机存在不完全探测。用规划器预算野外和图像工作,用经过验证的生态学设计分析占域率、数量、密度或种群变化。' },
41
+ ],
42
+ faq: faqData, howTo: howToData, bibliography, schemas: [faqSchema, howToSchema, appSchema],
43
+ };
@@ -0,0 +1,11 @@
1
+ import type { ToolDefinition } from '../../types';
2
+ import { wildlifeCameraTrapEffortPlanner } from './entry';
3
+
4
+ export * from './entry';
5
+
6
+ export const WILDLIFE_CAMERA_TRAP_EFFORT_PLANNER_TOOL: ToolDefinition = {
7
+ entry: wildlifeCameraTrapEffortPlanner,
8
+ Component: () => import('./component.astro'),
9
+ SEOComponent: () => import('./seo.astro'),
10
+ BibliographyComponent: () => import('./bibliography.astro'),
11
+ };
@@ -0,0 +1,48 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { calculateSurveyPlan, DEFAULT_INPUTS, M2_PER_HECTARE } from './logic';
3
+
4
+ describe('Wildlife camera trap effort planner logic', () => {
5
+ it('calculates effort, expected events, nominal footprint, and review checkpoints', () => {
6
+ const result = calculateSurveyPlan(DEFAULT_INPUTS);
7
+
8
+ expect(result.cameraDays).toBe(336);
9
+ expect(result.expectedEvents).toBe(1344);
10
+ expect(result.areaPerCameraM2).toBe(10000);
11
+ expect(result.nominalFootprintM2).toBe(120000);
12
+ expect(result.nominalFootprintPercent).toBeCloseTo(100);
13
+ expect(result.evenGridSpacingMeters).toBeCloseTo(Math.sqrt(M2_PER_HECTARE));
14
+ expect(result.reviewCheckpoints.map((checkpoint) => checkpoint.day)).toEqual([7, 14, 21, 28]);
15
+ expect(result.expectedEventsPerReview).toBe(336);
16
+ });
17
+
18
+ it('keeps physical area calculations independent from input units', () => {
19
+ const result = calculateSurveyPlan({
20
+ areaHectares: 1,
21
+ cameraCount: 4,
22
+ deploymentDays: 10,
23
+ spacingMeters: 100,
24
+ triggerRatePerCameraDay: 2.5,
25
+ });
26
+
27
+ expect(result.areaM2).toBe(10000);
28
+ expect(result.cameraDays).toBe(40);
29
+ expect(result.expectedEvents).toBe(100);
30
+ });
31
+
32
+ it('normalizes invalid and fractional planning values safely', () => {
33
+ const result = calculateSurveyPlan({
34
+ areaHectares: -1,
35
+ cameraCount: 2.9,
36
+ deploymentDays: Number.NaN,
37
+ spacingMeters: Number.POSITIVE_INFINITY,
38
+ triggerRatePerCameraDay: -3,
39
+ });
40
+
41
+ expect(result.areaHectares).toBe(0);
42
+ expect(result.cameraCount).toBe(2);
43
+ expect(result.deploymentDays).toBe(0);
44
+ expect(result.spacingMeters).toBe(0);
45
+ expect(result.expectedEvents).toBe(0);
46
+ expect(result.reviewCheckpoints).toEqual([]);
47
+ });
48
+ });
@@ -0,0 +1,99 @@
1
+ export interface SurveyInput {
2
+ areaHectares: number;
3
+ cameraCount: number;
4
+ deploymentDays: number;
5
+ spacingMeters: number;
6
+ triggerRatePerCameraDay: number;
7
+ }
8
+
9
+ export interface ReviewCheckpoint {
10
+ day: number;
11
+ expectedEvents: number;
12
+ }
13
+
14
+ export interface SurveyPlanResult {
15
+ areaHectares: number;
16
+ areaM2: number;
17
+ cameraCount: number;
18
+ deploymentDays: number;
19
+ spacingMeters: number;
20
+ triggerRatePerCameraDay: number;
21
+ cameraDays: number;
22
+ expectedEvents: number;
23
+ areaPerCameraM2: number;
24
+ nominalFootprintM2: number;
25
+ nominalFootprintPercent: number;
26
+ evenGridSpacingMeters: number;
27
+ reviewCheckpoints: ReviewCheckpoint[];
28
+ expectedEventsPerReview: number;
29
+ }
30
+
31
+ export const DEFAULT_INPUTS: SurveyInput = {
32
+ areaHectares: 12,
33
+ cameraCount: 12,
34
+ deploymentDays: 28,
35
+ spacingMeters: 100,
36
+ triggerRatePerCameraDay: 4,
37
+ };
38
+
39
+ export const METERS_PER_FOOT = 0.3048;
40
+ export const M2_PER_HECTARE = 10000;
41
+ export const M2_PER_ACRE = 4046.8564224;
42
+ export const ACRES_PER_HECTARE = M2_PER_HECTARE / M2_PER_ACRE;
43
+
44
+ function positive(value: number): number {
45
+ return Number.isFinite(value) && value > 0 ? value : 0;
46
+ }
47
+
48
+ function wholePositive(value: number): number {
49
+ return Math.floor(positive(value));
50
+ }
51
+
52
+ function normalizeInput(input: SurveyInput): SurveyInput {
53
+ return {
54
+ areaHectares: positive(input.areaHectares),
55
+ cameraCount: wholePositive(input.cameraCount),
56
+ deploymentDays: wholePositive(input.deploymentDays),
57
+ spacingMeters: positive(input.spacingMeters),
58
+ triggerRatePerCameraDay: positive(input.triggerRatePerCameraDay),
59
+ };
60
+ }
61
+
62
+ function buildReviewCheckpoints(deploymentDays: number, expectedEventsPerDay: number): ReviewCheckpoint[] {
63
+ if (deploymentDays === 0) return [];
64
+ const checkpoints: ReviewCheckpoint[] = [];
65
+ for (let day = 7; day < deploymentDays; day += 7) {
66
+ checkpoints.push({ day, expectedEvents: day * expectedEventsPerDay });
67
+ }
68
+ checkpoints.push({ day: deploymentDays, expectedEvents: deploymentDays * expectedEventsPerDay });
69
+ return checkpoints;
70
+ }
71
+
72
+ export function calculateSurveyPlan(input: SurveyInput): SurveyPlanResult {
73
+ const { areaHectares, cameraCount, deploymentDays, spacingMeters, triggerRatePerCameraDay } = normalizeInput(input);
74
+ const areaM2 = areaHectares * M2_PER_HECTARE;
75
+ const cameraDays = cameraCount * deploymentDays;
76
+ const expectedEvents = cameraDays * triggerRatePerCameraDay;
77
+ const areaPerCameraM2 = cameraCount > 0 ? areaM2 / cameraCount : 0;
78
+ const nominalFootprintM2 = cameraCount * spacingMeters ** 2;
79
+ const nominalFootprintPercent = areaM2 > 0 ? (nominalFootprintM2 / areaM2) * 100 : 0;
80
+ const evenGridSpacingMeters = cameraCount > 0 ? Math.sqrt(areaM2 / cameraCount) : 0;
81
+ const reviewCheckpoints = buildReviewCheckpoints(deploymentDays, cameraCount * triggerRatePerCameraDay);
82
+
83
+ return {
84
+ areaHectares,
85
+ areaM2,
86
+ cameraCount,
87
+ deploymentDays,
88
+ spacingMeters,
89
+ triggerRatePerCameraDay,
90
+ cameraDays,
91
+ expectedEvents,
92
+ areaPerCameraM2,
93
+ nominalFootprintM2,
94
+ nominalFootprintPercent,
95
+ evenGridSpacingMeters,
96
+ reviewCheckpoints,
97
+ expectedEventsPerReview: reviewCheckpoints.length > 0 ? expectedEvents / reviewCheckpoints.length : 0,
98
+ };
99
+ }
@@ -0,0 +1,14 @@
1
+ ---
2
+ import { SEORenderer } from '@jjlmoya/utils-shared';
3
+ import { wildlifeCameraTrapEffortPlanner } from './index';
4
+ import type { KnownLocale } from '../../types';
5
+
6
+ interface Props {
7
+ locale?: KnownLocale;
8
+ }
9
+
10
+ const { locale = 'en' } = Astro.props;
11
+ const content = await wildlifeCameraTrapEffortPlanner.i18n[locale]?.();
12
+ ---
13
+
14
+ {content && content.seo && content.seo.length > 0 && <SEORenderer content={{ locale, sections: content.seo }} />}
@@ -0,0 +1,18 @@
1
+ export type UnitSystem = 'metric' | 'imperial';
2
+
3
+ const STORAGE_KEY = 'jjlmoya:nature:wildlife-camera-trap-effort-planner:unit-system';
4
+
5
+ export function loadUnitSystem(): UnitSystem {
6
+ try {
7
+ const stored = window.localStorage.getItem(STORAGE_KEY);
8
+ return stored === 'imperial' ? 'imperial' : 'metric';
9
+ } catch {
10
+ return 'metric';
11
+ }
12
+ }
13
+
14
+ export function saveUnitSystem(unitSystem: UnitSystem): void {
15
+ try {
16
+ window.localStorage.setItem(STORAGE_KEY, unitSystem);
17
+ } catch {}
18
+ }
@@ -0,0 +1,48 @@
1
+ export interface WildlifeCameraTrapEffortPlannerUI {
2
+ labelSurveyArea: string;
3
+ labelCameraCount: string;
4
+ labelDeploymentDays: string;
5
+ labelSpacing: string;
6
+ labelTriggerRate: string;
7
+ unitHectares: string;
8
+ unitAcres: string;
9
+ unitMeters: string;
10
+ unitFeet: string;
11
+ unitCameras: string;
12
+ unitDays: string;
13
+ unitEventsPerDay: string;
14
+ helpSurveyArea: string;
15
+ helpCameraCount: string;
16
+ helpDeploymentDays: string;
17
+ helpSpacing: string;
18
+ helpTriggerRate: string;
19
+ unitSystemLabel: string;
20
+ metricLabel: string;
21
+ imperialLabel: string;
22
+ presetsTitle: string;
23
+ presetBackyard: string;
24
+ presetWoodland: string;
25
+ presetReserve: string;
26
+ mapLabel: string;
27
+ mapCaption: string;
28
+ stationLabel: string;
29
+ cameraDaysLabel: string;
30
+ expectedEventsLabel: string;
31
+ areaPerCameraLabel: string;
32
+ spacingLabel: string;
33
+ reviewLoadLabel: string;
34
+ reviewScheduleTitle: string;
35
+ reviewScheduleText: string;
36
+ reviewDayLabel: string;
37
+ reviewEventsLabel: string;
38
+ statusBalanced: string;
39
+ statusSparse: string;
40
+ statusDense: string;
41
+ statusReviewHeavy: string;
42
+ statusNeedsInput: string;
43
+ statusNote: string;
44
+ assumptionTitle: string;
45
+ assumptionText: string;
46
+ limitationTitle: string;
47
+ limitationText: string;
48
+ }